(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1 ) { x = utils.modulo( x, this.slideableWidth ); x = x - this.slideableWidth; this.shiftWrapCells( x ); } this.setTranslateX( x, this.isAnimating ); this.dispatchScrollEvent(); }; proto.setTranslateX = function( x, is3d ) { x += this.cursorPosition; // reverse if right-to-left and using transform x = this.options.rightToLeft ? -x : x; var translateX = this.getPositionValue( x ); // use 3D tranforms for hardware acceleration on iOS // but use 2D when settled, for better font-rendering this.slider.style.transform = is3d ? 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')'; }; proto.dispatchScrollEvent = function() { var firstSlide = this.slides[0]; if ( !firstSlide ) { return; } var positionX = -this.x - firstSlide.target; var progress = positionX / this.slidesWidth; this.dispatchEvent( 'scroll', null, [ progress, positionX ] ); }; proto.positionSliderAtSelected = function() { if ( !this.cells.length ) { return; } this.x = -this.selectedSlide.target; this.velocity = 0; // stop wobble this.positionSlider(); }; proto.getPositionValue = function( position ) { if ( this.options.percentPosition ) { // percent position, round to 2 digits, like 12.34% return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%'; } else { // pixel positioning return Math.round( position ) + 'px'; } }; proto.settle = function( previousX ) { // keep track of frames where x hasn't moved if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) { this.restingFrames++; } // stop animating if resting for 3 or more frames if ( this.restingFrames > 2 ) { this.isAnimating = false; delete this.isFreeScrolling; // render position with translateX when settled this.positionSlider(); this.dispatchEvent( 'settle', null, [ this.selectedIndex ] ); } }; proto.shiftWrapCells = function( x ) { // shift before cells var beforeGap = this.cursorPosition + x; this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); // shift after cells var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); this._shiftCells( this.afterShiftCells, afterGap, 1 ); }; proto._shiftCells = function( cells, gap, shift ) { for ( var i=0; i < cells.length; i++ ) { var cell = cells[i]; var cellShift = gap > 0 ? shift : 0; cell.wrapShift( cellShift ); gap -= cell.size.outerWidth; } }; proto._unshiftCells = function( cells ) { if ( !cells || !cells.length ) { return; } for ( var i=0; i < cells.length; i++ ) { cells[i].wrapShift( 0 ); } }; // -------------------------- physics -------------------------- // proto.integratePhysics = function() { this.x += this.velocity; this.velocity *= this.getFrictionFactor(); }; proto.applyForce = function( force ) { this.velocity += force; }; proto.getFrictionFactor = function() { return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; }; proto.getRestingPosition = function() { // my thanks to Steven Wittens, who simplified this math greatly return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); }; proto.applyDragForce = function() { if ( !this.isDraggable || !this.isPointerDown ) { return; } // change the position to drag position by applying force var dragVelocity = this.dragX - this.x; var dragForce = dragVelocity - this.velocity; this.applyForce( dragForce ); }; proto.applySelectedAttraction = function() { // do not attract if pointer down or no slides var dragDown = this.isDraggable && this.isPointerDown; if ( dragDown || this.isFreeScrolling || !this.slides.length ) { return; } var distance = this.selectedSlide.target * -1 - this.x; var force = distance * this.options.selectedAttraction; this.applyForce( force ); }; return proto; })); },{"fizzy-ui-utils":3}],6:[function(require,module,exports){ // Flickity.Cell ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'get-size/get-size' ], function( getSize ) { return factory( window, getSize ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('get-size') ); } else { // browser global window.Flickity = window.Flickity || {}; window.Flickity.Cell = factory( window, window.getSize ); } }( window, function factory( window, getSize ) { 'use strict'; function Cell( elem, parent ) { this.element = elem; this.parent = parent; this.create(); } var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; this.element.setAttribute( 'aria-hidden', 'true' ); this.x = 0; this.shift = 0; }; proto.destroy = function() { // reset style this.unselect(); this.element.style.position = ''; var side = this.parent.originSide; this.element.style[ side ] = ''; }; proto.getSize = function() { this.size = getSize( this.element ); }; proto.setPosition = function( x ) { this.x = x; this.updateTarget(); this.renderPosition( x ); }; // setDefaultTarget v1 method, backwards compatibility, remove in v3 proto.updateTarget = proto.setDefaultTarget = function() { var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight'; this.target = this.x + this.size[ marginProperty ] + this.size.width * this.parent.cellAlign; }; proto.renderPosition = function( x ) { // render position of cell with in slider var side = this.parent.originSide; this.element.style[ side ] = this.parent.getPositionValue( x ); }; proto.select = function() { this.element.classList.add('is-selected'); this.element.removeAttribute('aria-hidden'); }; proto.unselect = function() { this.element.classList.remove('is-selected'); this.element.setAttribute( 'aria-hidden', 'true' ); }; /** * @param {Integer} factor - 0, 1, or -1 **/ proto.wrapShift = function( shift ) { this.shift = shift; this.renderPosition( this.x + this.parent.slideableWidth * shift ); }; proto.remove = function() { this.element.parentNode.removeChild( this.element ); }; return Cell; })); },{"get-size":15}],7:[function(require,module,exports){ // drag ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ './flickity', 'unidragger/unidragger', 'fizzy-ui-utils/utils' ], function( Flickity, Unidragger, utils ) { return factory( window, Flickity, Unidragger, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unidragger'), require('fizzy-ui-utils') ); } else { // browser global window.Flickity = factory( window, window.Flickity, window.Unidragger, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unidragger, utils ) { 'use strict'; // ----- defaults ----- // utils.extend( Flickity.defaults, { draggable: '>1', dragThreshold: 3, }); // ----- create ----- // Flickity.createMethods.push('_createDrag'); // -------------------------- drag prototype -------------------------- // var proto = Flickity.prototype; utils.extend( proto, Unidragger.prototype ); proto._touchActionValue = 'pan-y'; // -------------------------- -------------------------- // var isTouch = 'createTouch' in document; var isTouchmoveScrollCanceled = false; proto._createDrag = function() { this.on( 'activate', this.onActivateDrag ); this.on( 'uiChange', this._uiChangeDrag ); this.on( 'deactivate', this.onDeactivateDrag ); this.on( 'cellChange', this.updateDraggable ); // TODO updateDraggable on resize? if groupCells & slides change // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior // #457, RubaXa/Sortable#973 if ( isTouch && !isTouchmoveScrollCanceled ) { window.addEventListener( 'touchmove', function() {}); isTouchmoveScrollCanceled = true; } }; proto.onActivateDrag = function() { this.handles = [ this.viewport ]; this.bindHandles(); this.updateDraggable(); }; proto.onDeactivateDrag = function() { this.unbindHandles(); this.element.classList.remove('is-draggable'); }; proto.updateDraggable = function() { // disable dragging if less than 2 slides. #278 if ( this.options.draggable == '>1' ) { this.isDraggable = this.slides.length > 1; } else { this.isDraggable = this.options.draggable; } if ( this.isDraggable ) { this.element.classList.add('is-draggable'); } else { this.element.classList.remove('is-draggable'); } }; // backwards compatibility proto.bindDrag = function() { this.options.draggable = true; this.updateDraggable(); }; proto.unbindDrag = function() { this.options.draggable = false; this.updateDraggable(); }; proto._uiChangeDrag = function() { delete this.isFreeScrolling; }; // -------------------------- pointer events -------------------------- // proto.pointerDown = function( event, pointer ) { if ( !this.isDraggable ) { this._pointerDownDefault( event, pointer ); return; } var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } this._pointerDownPreventDefault( event ); this.pointerDownFocus( event ); // blur if ( document.activeElement != this.element ) { // do not blur if already focused this.pointerDownBlur(); } // stop if it was moving this.dragX = this.x; this.viewport.classList.add('is-pointer-down'); // track scrolling this.pointerDownScroll = getScrollPosition(); window.addEventListener( 'scroll', this ); this._pointerDownDefault( event, pointer ); }; // default pointerDown logic, used for staticClick proto._pointerDownDefault = function( event, pointer ) { // track start event position // Safari 9 overrides pageX and pageY. These values needs to be copied. #779 this.pointerDownPointer = { pageX: pointer.pageX, pageY: pointer.pageY, }; // bind move and end events this._bindPostStartEvents( event ); this.dispatchEvent( 'pointerDown', event, [ pointer ] ); }; var focusNodes = { INPUT: true, TEXTAREA: true, SELECT: true, }; proto.pointerDownFocus = function( event ) { var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isFocusNode ) { this.focus(); } }; proto._pointerDownPreventDefault = function( event ) { var isTouchStart = event.type == 'touchstart'; var isTouchPointer = event.pointerType == 'touch'; var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isTouchStart && !isTouchPointer && !isFocusNode ) { event.preventDefault(); } }; // ----- move ----- // proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > this.options.dragThreshold; }; // ----- up ----- // proto.pointerUp = function( event, pointer ) { delete this.isTouchScrolling; this.viewport.classList.remove('is-pointer-down'); this.dispatchEvent( 'pointerUp', event, [ pointer ] ); this._dragPointerUp( event, pointer ); }; proto.pointerDone = function() { window.removeEventListener( 'scroll', this ); delete this.pointerDownScroll; }; // -------------------------- dragging -------------------------- // proto.dragStart = function( event, pointer ) { if ( !this.isDraggable ) { return; } this.dragStartPosition = this.x; this.startAnimation(); window.removeEventListener( 'scroll', this ); this.dispatchEvent( 'dragStart', event, [ pointer ] ); }; proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { if ( !this.isDraggable ) { return; } event.preventDefault(); this.previousDragX = this.dragX; // reverse if right-to-left var direction = this.options.rightToLeft ? -1 : 1; if ( this.options.wrapAround ) { // wrap around move. #589 moveVector.x = moveVector.x % this.slideableWidth; } var dragX = this.dragStartPosition + moveVector.x * direction; if ( !this.options.wrapAround && this.slides.length ) { // slow drag var originBound = Math.max( -this.slides[0].target, this.dragStartPosition ); dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX; var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition ); dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX; } this.dragX = dragX; this.dragMoveTime = new Date(); this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] ); }; proto.dragEnd = function( event, pointer ) { if ( !this.isDraggable ) { return; } if ( this.options.freeScroll ) { this.isFreeScrolling = true; } // set selectedIndex based on where flick will end up var index = this.dragEndRestingSelect(); if ( this.options.freeScroll && !this.options.wrapAround ) { // if free-scroll & not wrap around // do not free-scroll if going outside of bounding slides // so bounding slides can attract slider, and keep it in bounds var restingX = this.getRestingPosition(); this.isFreeScrolling = -restingX > this.slides[0].target && -restingX < this.getLastSlide().target; } else if ( !this.options.freeScroll && index == this.selectedIndex ) { // boost selection if selected index has not changed index += this.dragEndBoostSelect(); } delete this.previousDragX; // apply selection // TODO refactor this, selecting here feels weird // HACK, set flag so dragging stays in correct direction this.isDragSelect = this.options.wrapAround; this.select( index ); delete this.isDragSelect; this.dispatchEvent( 'dragEnd', event, [ pointer ] ); }; proto.dragEndRestingSelect = function() { var restingX = this.getRestingPosition(); // how far away from selected slide var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) ); // get closet resting going up and going down var positiveResting = this._getClosestResting( restingX, distance, 1 ); var negativeResting = this._getClosestResting( restingX, distance, -1 ); // use closer resting for wrap-around var index = positiveResting.distance < negativeResting.distance ? positiveResting.index : negativeResting.index; return index; }; /** * given resting X and distance to selected cell * get the distance and index of the closest cell * @param {Number} restingX - estimated post-flick resting position * @param {Number} distance - distance to selected cell * @param {Integer} increment - +1 or -1, going up or down * @returns {Object} - { distance: {Number}, index: {Integer} } */ proto._getClosestResting = function( restingX, distance, increment ) { var index = this.selectedIndex; var minDistance = Infinity; var condition = this.options.contain && !this.options.wrapAround ? // if contain, keep going if distance is equal to minDistance function( d, md ) { return d <= md; } : function( d, md ) { return d < md; }; while ( condition( distance, minDistance ) ) { // measure distance to next cell index += increment; minDistance = distance; distance = this.getSlideDistance( -restingX, index ); if ( distance === null ) { break; } distance = Math.abs( distance ); } return { distance: minDistance, // selected was previous index index: index - increment }; }; /** * measure distance between x and a slide target * @param {Number} x * @param {Integer} index - slide index */ proto.getSlideDistance = function( x, index ) { var len = this.slides.length; // wrap around if at least 2 slides var isWrapAround = this.options.wrapAround && len > 1; var slideIndex = isWrapAround ? utils.modulo( index, len ) : index; var slide = this.slides[ slideIndex ]; if ( !slide ) { return null; } // add distance for wrap-around slides var wrap = isWrapAround ? this.slideableWidth * Math.floor( index / len ) : 0; return x - ( slide.target + wrap ); }; proto.dragEndBoostSelect = function() { // do not boost if no previousDragX or dragMoveTime if ( this.previousDragX === undefined || !this.dragMoveTime || // or if drag was held for 100 ms new Date() - this.dragMoveTime > 100 ) { return 0; } var distance = this.getSlideDistance( -this.dragX, this.selectedIndex ); var delta = this.previousDragX - this.dragX; if ( distance > 0 && delta > 0 ) { // boost to next if moving towards the right, and positive velocity return 1; } else if ( distance < 0 && delta < 0 ) { // boost to previous if moving towards the left, and negative velocity return -1; } return 0; }; // ----- staticClick ----- // proto.staticClick = function( event, pointer ) { // get clickedCell, if cell was clicked var clickedCell = this.getParentCell( event.target ); var cellElem = clickedCell && clickedCell.element; var cellIndex = clickedCell && this.cells.indexOf( clickedCell ); this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] ); }; // ----- scroll ----- // proto.onscroll = function() { var scroll = getScrollPosition(); var scrollMoveX = this.pointerDownScroll.x - scroll.x; var scrollMoveY = this.pointerDownScroll.y - scroll.y; // cancel click/tap if scroll is too much if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) { this._pointerDone(); } }; // ----- utils ----- // function getScrollPosition() { return { x: window.pageXOffset, y: window.pageYOffset }; } // ----- ----- // return Flickity; })); },{"./flickity":8,"fizzy-ui-utils":3,"unidragger":19}],8:[function(require,module,exports){ // Flickity main ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'ev-emitter/ev-emitter', 'get-size/get-size', 'fizzy-ui-utils/utils', './cell', './slide', './animate' ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter'), require('get-size'), require('fizzy-ui-utils'), require('./cell'), require('./slide'), require('./animate') ); } else { // browser global var _Flickity = window.Flickity; window.Flickity = factory( window, window.EvEmitter, window.getSize, window.fizzyUIUtils, _Flickity.Cell, _Flickity.Slide, _Flickity.animatePrototype ); } }( window, function factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { 'use strict'; // vars var jQuery = window.jQuery; var getComputedStyle = window.getComputedStyle; var console = window.console; function moveElements( elems, toElem ) { elems = utils.makeArray( elems ); while ( elems.length ) { toElem.appendChild( elems.shift() ); } } // -------------------------- Flickity -------------------------- // // globally unique identifiers var GUID = 0; // internal store of all Flickity intances var instances = {}; function Flickity( element, options ) { var queryElement = utils.getQueryElement( element ); if ( !queryElement ) { if ( console ) { console.error( 'Bad element for Flickity: ' + ( queryElement || element ) ); } return; } this.element = queryElement; // do not initialize twice on same element if ( this.element.flickityGUID ) { var instance = instances[ this.element.flickityGUID ]; instance.option( options ); return instance; } // add jQuery if ( jQuery ) { this.$element = jQuery( this.element ); } // options this.options = utils.extend( {}, this.constructor.defaults ); this.option( options ); // kick things off this._create(); } Flickity.defaults = { accessibility: true, // adaptiveHeight: false, cellAlign: 'center', // cellSelector: undefined, // contain: false, freeScrollFriction: 0.075, // friction when free-scrolling friction: 0.28, // friction when selecting namespaceJQueryEvents: true, // initialIndex: 0, percentPosition: true, resize: true, selectedAttraction: 0.025, setGallerySize: true // watchCSS: false, // wrapAround: false }; // hash of methods triggered on _create() Flickity.createMethods = []; var proto = Flickity.prototype; // inherit EventEmitter utils.extend( proto, EvEmitter.prototype ); proto._create = function() { // add id for Flickity.data var id = this.guid = ++GUID; this.element.flickityGUID = id; // expando instances[ id ] = this; // associate via id // initial properties this.selectedIndex = 0; // how many frames slider has been in same position this.restingFrames = 0; // initial physics properties this.x = 0; this.velocity = 0; this.originSide = this.options.rightToLeft ? 'right' : 'left'; // create viewport & slider this.viewport = document.createElement('div'); this.viewport.className = 'flickity-viewport'; this._createSlider(); if ( this.options.resize || this.options.watchCSS ) { window.addEventListener( 'resize', this ); } // add listeners from on option for ( var eventName in this.options.on ) { var listener = this.options.on[ eventName ]; this.on( eventName, listener ); } Flickity.createMethods.forEach( function( method ) { this[ method ](); }, this ); if ( this.options.watchCSS ) { this.watchCSS(); } else { this.activate(); } }; /** * set options * @param {Object} opts */ proto.option = function( opts ) { utils.extend( this.options, opts ); }; proto.activate = function() { if ( this.isActive ) { return; } this.isActive = true; this.element.classList.add('flickity-enabled'); if ( this.options.rightToLeft ) { this.element.classList.add('flickity-rtl'); } this.getSize(); // move initial cell elements so they can be loaded as cells var cellElems = this._filterFindCellElements( this.element.children ); moveElements( cellElems, this.slider ); this.viewport.appendChild( this.slider ); this.element.appendChild( this.viewport ); // get cells from children this.reloadCells(); if ( this.options.accessibility ) { // allow element to focusable this.element.tabIndex = 0; // listen for key presses this.element.addEventListener( 'keydown', this ); } this.emitEvent('activate'); this.selectInitialIndex(); // flag for initial activation, for using initialIndex this.isInitActivated = true; // ready event. #493 this.dispatchEvent('ready'); }; // slider positions the cells proto._createSlider = function() { // slider element does all the positioning var slider = document.createElement('div'); slider.className = 'flickity-slider'; slider.style[ this.originSide ] = 0; this.slider = slider; }; proto._filterFindCellElements = function( elems ) { return utils.filterFindElements( elems, this.options.cellSelector ); }; // goes through all children proto.reloadCells = function() { // collection of item elements this.cells = this._makeCells( this.slider.children ); this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); }; /** * turn elements into Flickity.Cells * @param {Array or NodeList or HTMLElement} elems * @returns {Array} items - collection of new Flickity Cells */ proto._makeCells = function( elems ) { var cellElems = this._filterFindCellElements( elems ); // create new Flickity for collection var cells = cellElems.map( function( cellElem ) { return new Cell( cellElem, this ); }, this ); return cells; }; proto.getLastCell = function() { return this.cells[ this.cells.length - 1 ]; }; proto.getLastSlide = function() { return this.slides[ this.slides.length - 1 ]; }; // positions all cells proto.positionCells = function() { // size all cells this._sizeCells( this.cells ); // position all cells this._positionCells( 0 ); }; /** * position certain cells * @param {Integer} index - which cell to start with */ proto._positionCells = function( index ) { index = index || 0; // also measure maxCellHeight // start 0 if positioning all cells this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; var cellX = 0; // get cellX if ( index > 0 ) { var startCell = this.cells[ index - 1 ]; cellX = startCell.x + startCell.size.outerWidth; } var len = this.cells.length; for ( var i=index; i < len; i++ ) { var cell = this.cells[i]; cell.setPosition( cellX ); cellX += cell.size.outerWidth; this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); } // keep track of cellX for wrap-around this.slideableWidth = cellX; // slides this.updateSlides(); // contain slides target this._containSlides(); // update slidesWidth this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0; }; /** * cell.getSize() on multiple cells * @param {Array} cells */ proto._sizeCells = function( cells ) { cells.forEach( function( cell ) { cell.getSize(); }); }; // -------------------------- -------------------------- // proto.updateSlides = function() { this.slides = []; if ( !this.cells.length ) { return; } var slide = new Slide( this ); this.slides.push( slide ); var isOriginLeft = this.originSide == 'left'; var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft'; var canCellFit = this._getCanCellFit(); this.cells.forEach( function( cell, i ) { // just add cell if first cell in slide if ( !slide.cells.length ) { slide.addCell( cell ); return; } var slideWidth = ( slide.outerWidth - slide.firstMargin ) + ( cell.size.outerWidth - cell.size[ nextMargin ] ); if ( canCellFit.call( this, i, slideWidth ) ) { slide.addCell( cell ); } else { // doesn't fit, new slide slide.updateTarget(); slide = new Slide( this ); this.slides.push( slide ); slide.addCell( cell ); } }, this ); // last slide slide.updateTarget(); // update .selectedSlide this.updateSelectedSlide(); }; proto._getCanCellFit = function() { var groupCells = this.options.groupCells; if ( !groupCells ) { return function() { return false; }; } else if ( typeof groupCells == 'number' ) { // group by number. 3 -> [0,1,2], [3,4,5], ... var number = parseInt( groupCells, 10 ); return function( i ) { return ( i % number ) !== 0; }; } // default, group by width of slide // parse '75% var percentMatch = typeof groupCells == 'string' && groupCells.match(/^(\d+)%$/); var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1; return function( i, slideWidth ) { return slideWidth <= ( this.size.innerWidth + 1 ) * percent; }; }; // alias _init for jQuery plugin .flickity() proto._init = proto.reposition = function() { this.positionCells(); this.positionSliderAtSelected(); }; proto.getSize = function() { this.size = getSize( this.element ); this.setCellAlign(); this.cursorPosition = this.size.innerWidth * this.cellAlign; }; var cellAlignShorthands = { // cell align, then based on origin side center: { left: 0.5, right: 0.5 }, left: { left: 0, right: 1 }, right: { right: 0, left: 1 } }; proto.setCellAlign = function() { var shorthand = cellAlignShorthands[ this.options.cellAlign ]; this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign; }; proto.setGallerySize = function() { if ( this.options.setGallerySize ) { var height = this.options.adaptiveHeight && this.selectedSlide ? this.selectedSlide.height : this.maxCellHeight; this.viewport.style.height = height + 'px'; } }; proto._getWrapShiftCells = function() { // only for wrap-around if ( !this.options.wrapAround ) { return; } // unshift previous cells this._unshiftCells( this.beforeShiftCells ); this._unshiftCells( this.afterShiftCells ); // get before cells // initial gap var gapX = this.cursorPosition; var cellIndex = this.cells.length - 1; this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 ); // get after cells // ending gap between last cell and end of gallery viewport gapX = this.size.innerWidth - this.cursorPosition; // start cloning at first cell, working forwards this.afterShiftCells = this._getGapCells( gapX, 0, 1 ); }; proto._getGapCells = function( gapX, cellIndex, increment ) { // keep adding cells until the cover the initial gap var cells = []; while ( gapX > 0 ) { var cell = this.cells[ cellIndex ]; if ( !cell ) { break; } cells.push( cell ); cellIndex += increment; gapX -= cell.size.outerWidth; } return cells; }; // ----- contain ----- // // contain cell targets so no excess sliding proto._containSlides = function() { if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) { return; } var isRightToLeft = this.options.rightToLeft; var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft'; var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight'; var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ]; // content is less than gallery size var isContentSmaller = contentWidth < this.size.innerWidth; // bounds var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ]; var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); // contain each cell target this.slides.forEach( function( slide ) { if ( isContentSmaller ) { // all cells fit inside gallery slide.target = contentWidth * this.cellAlign; } else { // contain to bounds slide.target = Math.max( slide.target, beginBound ); slide.target = Math.min( slide.target, endBound ); } }, this ); }; // ----- ----- // /** * emits events via eventEmitter and jQuery events * @param {String} type - name of event * @param {Event} event - original event * @param {Array} args - extra arguments */ proto.dispatchEvent = function( type, event, args ) { var emitArgs = event ? [ event ].concat( args ) : args; this.emitEvent( type, emitArgs ); if ( jQuery && this.$element ) { // default trigger with type if no event type += this.options.namespaceJQueryEvents ? '.flickity' : ''; var $event = type; if ( event ) { // create jQuery event var jQEvent = jQuery.Event( event ); jQEvent.type = type; $event = jQEvent; } this.$element.trigger( $event, args ); } }; // -------------------------- select -------------------------- // /** * @param {Integer} index - index of the slide * @param {Boolean} isWrap - will wrap-around to last/first if at the end * @param {Boolean} isInstant - will immediately set position at selected cell */ proto.select = function( index, isWrap, isInstant ) { if ( !this.isActive ) { return; } index = parseInt( index, 10 ); this._wrapSelect( index ); if ( this.options.wrapAround || isWrap ) { index = utils.modulo( index, this.slides.length ); } // bail if invalid index if ( !this.slides[ index ] ) { return; } var prevIndex = this.selectedIndex; this.selectedIndex = index; this.updateSelectedSlide(); if ( isInstant ) { this.positionSliderAtSelected(); } else { this.startAnimation(); } if ( this.options.adaptiveHeight ) { this.setGallerySize(); } // events this.dispatchEvent( 'select', null, [ index ] ); // change event if new index if ( index != prevIndex ) { this.dispatchEvent( 'change', null, [ index ] ); } // old v1 event name, remove in v3 this.dispatchEvent('cellSelect'); }; // wraps position for wrapAround, to move to closest slide. #113 proto._wrapSelect = function( index ) { var len = this.slides.length; var isWrapping = this.options.wrapAround && len > 1; if ( !isWrapping ) { return index; } var wrapIndex = utils.modulo( index, len ); // go to shortest var delta = Math.abs( wrapIndex - this.selectedIndex ); var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex ); var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex ); if ( !this.isDragSelect && backWrapDelta < delta ) { index += len; } else if ( !this.isDragSelect && forewardWrapDelta < delta ) { index -= len; } // wrap position so slider is within normal area if ( index < 0 ) { this.x -= this.slideableWidth; } else if ( index >= len ) { this.x += this.slideableWidth; } }; proto.previous = function( isWrap, isInstant ) { this.select( this.selectedIndex - 1, isWrap, isInstant ); }; proto.next = function( isWrap, isInstant ) { this.select( this.selectedIndex + 1, isWrap, isInstant ); }; proto.updateSelectedSlide = function() { var slide = this.slides[ this.selectedIndex ]; // selectedIndex could be outside of slides, if triggered before resize() if ( !slide ) { return; } // unselect previous selected slide this.unselectSelectedSlide(); // update new selected slide this.selectedSlide = slide; slide.select(); this.selectedCells = slide.cells; this.selectedElements = slide.getCellElements(); // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility // Remove in v3? this.selectedCell = slide.cells[0]; this.selectedElement = this.selectedElements[0]; }; proto.unselectSelectedSlide = function() { if ( this.selectedSlide ) { this.selectedSlide.unselect(); } }; proto.selectInitialIndex = function() { var initialIndex = this.options.initialIndex; // already activated, select previous selectedIndex if ( this.isInitActivated ) { this.select( this.selectedIndex, false, true ); return; } // select with selector string if ( initialIndex && typeof initialIndex == 'string' ) { var cell = this.queryCell( initialIndex ); if ( cell ) { this.selectCell( initialIndex, false, true ); return; } } var index = 0; // select with number if ( initialIndex && this.slides[ initialIndex ] ) { index = initialIndex; } // select instantly this.select( index, false, true ); }; /** * select slide from number or cell element * @param {Element or Number} elem */ proto.selectCell = function( value, isWrap, isInstant ) { // get cell var cell = this.queryCell( value ); if ( !cell ) { return; } var index = this.getCellSlideIndex( cell ); this.select( index, isWrap, isInstant ); }; proto.getCellSlideIndex = function( cell ) { // get index of slides that has cell for ( var i=0; i < this.slides.length; i++ ) { var slide = this.slides[i]; var index = slide.cells.indexOf( cell ); if ( index != -1 ) { return i; } } }; // -------------------------- get cells -------------------------- // /** * get Flickity.Cell, given an Element * @param {Element} elem * @returns {Flickity.Cell} item */ proto.getCell = function( elem ) { // loop through cells to get the one that matches for ( var i=0; i < this.cells.length; i++ ) { var cell = this.cells[i]; if ( cell.element == elem ) { return cell; } } }; /** * get collection of Flickity.Cells, given Elements * @param {Element, Array, NodeList} elems * @returns {Array} cells - Flickity.Cells */ proto.getCells = function( elems ) { elems = utils.makeArray( elems ); var cells = []; elems.forEach( function( elem ) { var cell = this.getCell( elem ); if ( cell ) { cells.push( cell ); } }, this ); return cells; }; /** * get cell elements * @returns {Array} cellElems */ proto.getCellElements = function() { return this.cells.map( function( cell ) { return cell.element; }); }; /** * get parent cell from an element * @param {Element} elem * @returns {Flickit.Cell} cell */ proto.getParentCell = function( elem ) { // first check if elem is cell var cell = this.getCell( elem ); if ( cell ) { return cell; } // try to get parent cell elem elem = utils.getParent( elem, '.flickity-slider > *' ); return this.getCell( elem ); }; /** * get cells adjacent to a slide * @param {Integer} adjCount - number of adjacent slides * @param {Integer} index - index of slide to start * @returns {Array} cells - array of Flickity.Cells */ proto.getAdjacentCellElements = function( adjCount, index ) { if ( !adjCount ) { return this.selectedSlide.getCellElements(); } index = index === undefined ? this.selectedIndex : index; var len = this.slides.length; if ( 1 + ( adjCount * 2 ) >= len ) { return this.getCellElements(); } var cellElems = []; for ( var i = index - adjCount; i <= index + adjCount ; i++ ) { var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i; var slide = this.slides[ slideIndex ]; if ( slide ) { cellElems = cellElems.concat( slide.getCellElements() ); } } return cellElems; }; /** * select slide from number or cell element * @param {Element, Selector String, or Number} selector */ proto.queryCell = function( selector ) { if ( typeof selector == 'number' ) { // use number as index return this.cells[ selector ]; } if ( typeof selector == 'string' ) { // do not select invalid selectors from hash: #123, #/. #791 if ( selector.match(/^[#\.]?[\d\/]/) ) { return; } // use string as selector, get element selector = this.element.querySelector( selector ); } // get cell from element return this.getCell( selector ); }; // -------------------------- events -------------------------- // proto.uiChange = function() { this.emitEvent('uiChange'); }; // keep focus on element when child UI elements are clicked proto.childUIPointerDown = function( event ) { // HACK iOS does not allow touch events to bubble up?! if ( event.type != 'touchstart' ) { event.preventDefault(); } this.focus(); }; // ----- resize ----- // proto.onresize = function() { this.watchCSS(); this.resize(); }; utils.debounceMethod( Flickity, 'onresize', 150 ); proto.resize = function() { if ( !this.isActive ) { return; } this.getSize(); // wrap values if ( this.options.wrapAround ) { this.x = utils.modulo( this.x, this.slideableWidth ); } this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); this.emitEvent('resize'); // update selected index for group slides, instant // TODO: position can be lost between groups of various numbers var selectedElement = this.selectedElements && this.selectedElements[0]; this.selectCell( selectedElement, false, true ); }; // watches the :after property, activates/deactivates proto.watchCSS = function() { var watchOption = this.options.watchCSS; if ( !watchOption ) { return; } var afterContent = getComputedStyle( this.element, ':after' ).content; // activate if :after { content: 'flickity' } if ( afterContent.indexOf('flickity') != -1 ) { this.activate(); } else { this.deactivate(); } }; // ----- keydown ----- // // go previous/next if left/right keys pressed proto.onkeydown = function( event ) { // only work if element is in focus var isNotFocused = document.activeElement && document.activeElement != this.element; if ( !this.options.accessibility ||isNotFocused ) { return; } var handler = Flickity.keyboardHandlers[ event.keyCode ]; if ( handler ) { handler.call( this ); } }; Flickity.keyboardHandlers = { // left arrow 37: function() { var leftMethod = this.options.rightToLeft ? 'next' : 'previous'; this.uiChange(); this[ leftMethod ](); }, // right arrow 39: function() { var rightMethod = this.options.rightToLeft ? 'previous' : 'next'; this.uiChange(); this[ rightMethod ](); }, }; // ----- focus ----- // proto.focus = function() { // TODO remove scrollTo once focus options gets more support // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#Browser_compatibility var prevScrollY = window.pageYOffset; this.element.focus({ preventScroll: true }); // hack to fix scroll jump after focus, #76 if ( window.pageYOffset != prevScrollY ) { window.scrollTo( window.pageXOffset, prevScrollY ); } }; // -------------------------- destroy -------------------------- // // deactivate all Flickity functionality, but keep stuff available proto.deactivate = function() { if ( !this.isActive ) { return; } this.element.classList.remove('flickity-enabled'); this.element.classList.remove('flickity-rtl'); this.unselectSelectedSlide(); // destroy cells this.cells.forEach( function( cell ) { cell.destroy(); }); this.element.removeChild( this.viewport ); // move child elements back into element moveElements( this.slider.children, this.element ); if ( this.options.accessibility ) { this.element.removeAttribute('tabIndex'); this.element.removeEventListener( 'keydown', this ); } // set flags this.isActive = false; this.emitEvent('deactivate'); }; proto.destroy = function() { this.deactivate(); window.removeEventListener( 'resize', this ); this.allOff(); this.emitEvent('destroy'); if ( jQuery && this.$element ) { jQuery.removeData( this.element, 'flickity' ); } delete this.element.flickityGUID; delete instances[ this.guid ]; }; // -------------------------- prototype -------------------------- // utils.extend( proto, animatePrototype ); // -------------------------- extras -------------------------- // /** * get Flickity instance from element * @param {Element} elem * @returns {Flickity} */ Flickity.data = function( elem ) { elem = utils.getQueryElement( elem ); var id = elem && elem.flickityGUID; return id && instances[ id ]; }; utils.htmlInit( Flickity, 'flickity' ); if ( jQuery && jQuery.bridget ) { jQuery.bridget( 'flickity', Flickity ); } // set internal jQuery, for Webpack + jQuery v3, #478 Flickity.setJQuery = function( jq ) { jQuery = jq; }; Flickity.Cell = Cell; Flickity.Slide = Slide; return Flickity; })); },{"./animate":5,"./cell":6,"./slide":14,"ev-emitter":2,"fizzy-ui-utils":3,"get-size":15}],9:[function(require,module,exports){ /*! * Flickity v2.2.1 * Touch, responsive, flickable carousels * * Licensed GPLv3 for open source use * or Flickity Commercial License for commercial use * * https://flickity.metafizzy.co * Copyright 2015-2019 Metafizzy */ ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ './flickity', './drag', './prev-next-button', './page-dots', './player', './add-remove-cell', './lazyload' ], factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('./flickity'), require('./drag'), require('./prev-next-button'), require('./page-dots'), require('./player'), require('./add-remove-cell'), require('./lazyload') ); } })( window, function factory( Flickity ) { /*jshint strict: false*/ return Flickity; }); },{"./add-remove-cell":4,"./drag":7,"./flickity":8,"./lazyload":10,"./page-dots":11,"./player":12,"./prev-next-button":13}],10:[function(require,module,exports){ // lazyload ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ './flickity', 'fizzy-ui-utils/utils' ], function( Flickity, utils ) { return factory( window, Flickity, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, utils ) { 'use strict'; Flickity.createMethods.push('_createLazyload'); var proto = Flickity.prototype; proto._createLazyload = function() { this.on( 'select', this.lazyLoad ); }; proto.lazyLoad = function() { var lazyLoad = this.options.lazyLoad; if ( !lazyLoad ) { return; } // get adjacent cells, use lazyLoad option for adjacent count var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0; var cellElems = this.getAdjacentCellElements( adjCount ); // get lazy images in those cells var lazyImages = []; cellElems.forEach( function( cellElem ) { var lazyCellImages = getCellLazyImages( cellElem ); lazyImages = lazyImages.concat( lazyCellImages ); }); // load lazy images lazyImages.forEach( function( img ) { new LazyLoader( img, this ); }, this ); }; function getCellLazyImages( cellElem ) { // check if cell element is lazy image if ( cellElem.nodeName == 'IMG' ) { var lazyloadAttr = cellElem.getAttribute('data-flickity-lazyload'); var srcAttr = cellElem.getAttribute('data-flickity-lazyload-src'); var srcsetAttr = cellElem.getAttribute('data-flickity-lazyload-srcset'); if ( lazyloadAttr || srcAttr || srcsetAttr ) { return [ cellElem ]; } } // select lazy images in cell var lazySelector = 'img[data-flickity-lazyload], ' + 'img[data-flickity-lazyload-src], img[data-flickity-lazyload-srcset]'; var imgs = cellElem.querySelectorAll( lazySelector ); return utils.makeArray( imgs ); } // -------------------------- LazyLoader -------------------------- // /** * class to handle loading images */ function LazyLoader( img, flickity ) { this.img = img; this.flickity = flickity; this.load(); } LazyLoader.prototype.handleEvent = utils.handleEvent; LazyLoader.prototype.load = function() { this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); // get src & srcset var src = this.img.getAttribute('data-flickity-lazyload') || this.img.getAttribute('data-flickity-lazyload-src'); var srcset = this.img.getAttribute('data-flickity-lazyload-srcset'); // set src & serset this.img.src = src; if ( srcset ) { this.img.setAttribute( 'srcset', srcset ); } // remove attr this.img.removeAttribute('data-flickity-lazyload'); this.img.removeAttribute('data-flickity-lazyload-src'); this.img.removeAttribute('data-flickity-lazyload-srcset'); }; LazyLoader.prototype.onload = function( event ) { this.complete( event, 'flickity-lazyloaded' ); }; LazyLoader.prototype.onerror = function( event ) { this.complete( event, 'flickity-lazyerror' ); }; LazyLoader.prototype.complete = function( event, className ) { // unbind events this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); var cell = this.flickity.getParentCell( this.img ); var cellElem = cell && cell.element; this.flickity.cellSizeChange( cellElem ); this.img.classList.add( className ); this.flickity.dispatchEvent( 'lazyLoad', event, cellElem ); }; // ----- ----- // Flickity.LazyLoader = LazyLoader; return Flickity; })); },{"./flickity":8,"fizzy-ui-utils":3}],11:[function(require,module,exports){ // page dots ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ './flickity', 'unipointer/unipointer', 'fizzy-ui-utils/utils' ], function( Flickity, Unipointer, utils ) { return factory( window, Flickity, Unipointer, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unipointer'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.Unipointer, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unipointer, utils ) { // -------------------------- PageDots -------------------------- // 'use strict'; function PageDots( parent ) { this.parent = parent; this._create(); } PageDots.prototype = Object.create( Unipointer.prototype ); PageDots.prototype._create = function() { // create holder element this.holder = document.createElement('ol'); this.holder.className = 'flickity-page-dots'; // create dots, array of elements this.dots = []; // events this.handleClick = this.onClick.bind( this ); this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); }; PageDots.prototype.activate = function() { this.setDots(); this.holder.addEventListener( 'click', this.handleClick ); this.bindStartEvent( this.holder ); // add to DOM this.parent.element.appendChild( this.holder ); }; PageDots.prototype.deactivate = function() { this.holder.removeEventListener( 'click', this.handleClick ); this.unbindStartEvent( this.holder ); // remove from DOM this.parent.element.removeChild( this.holder ); }; PageDots.prototype.setDots = function() { // get difference between number of slides and number of dots var delta = this.parent.slides.length - this.dots.length; if ( delta > 0 ) { this.addDots( delta ); } else if ( delta < 0 ) { this.removeDots( -delta ); } }; PageDots.prototype.addDots = function( count ) { var fragment = document.createDocumentFragment(); var newDots = []; var length = this.dots.length; var max = length + count; for ( var i = length; i < max; i++ ) { var dot = document.createElement('li'); dot.className = 'dot'; dot.setAttribute( 'aria-label', 'Page dot ' + ( i + 1 ) ); fragment.appendChild( dot ); newDots.push( dot ); } this.holder.appendChild( fragment ); this.dots = this.dots.concat( newDots ); }; PageDots.prototype.removeDots = function( count ) { // remove from this.dots collection var removeDots = this.dots.splice( this.dots.length - count, count ); // remove from DOM removeDots.forEach( function( dot ) { this.holder.removeChild( dot ); }, this ); }; PageDots.prototype.updateSelected = function() { // remove selected class on previous if ( this.selectedDot ) { this.selectedDot.className = 'dot'; this.selectedDot.removeAttribute('aria-current'); } // don't proceed if no dots if ( !this.dots.length ) { return; } this.selectedDot = this.dots[ this.parent.selectedIndex ]; this.selectedDot.className = 'dot is-selected'; this.selectedDot.setAttribute( 'aria-current', 'step' ); }; PageDots.prototype.onTap = // old method name, backwards-compatible PageDots.prototype.onClick = function( event ) { var target = event.target; // only care about dot clicks if ( target.nodeName != 'LI' ) { return; } this.parent.uiChange(); var index = this.dots.indexOf( target ); this.parent.select( index ); }; PageDots.prototype.destroy = function() { this.deactivate(); this.allOff(); }; Flickity.PageDots = PageDots; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pageDots: true }); Flickity.createMethods.push('_createPageDots'); var proto = Flickity.prototype; proto._createPageDots = function() { if ( !this.options.pageDots ) { return; } this.pageDots = new PageDots( this ); // events this.on( 'activate', this.activatePageDots ); this.on( 'select', this.updateSelectedPageDots ); this.on( 'cellChange', this.updatePageDots ); this.on( 'resize', this.updatePageDots ); this.on( 'deactivate', this.deactivatePageDots ); }; proto.activatePageDots = function() { this.pageDots.activate(); }; proto.updateSelectedPageDots = function() { this.pageDots.updateSelected(); }; proto.updatePageDots = function() { this.pageDots.setDots(); }; proto.deactivatePageDots = function() { this.pageDots.deactivate(); }; // ----- ----- // Flickity.PageDots = PageDots; return Flickity; })); },{"./flickity":8,"fizzy-ui-utils":3,"unipointer":20}],12:[function(require,module,exports){ // player & autoPlay ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'ev-emitter/ev-emitter', 'fizzy-ui-utils/utils', './flickity' ], function( EvEmitter, utils, Flickity ) { return factory( EvEmitter, utils, Flickity ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('ev-emitter'), require('fizzy-ui-utils'), require('./flickity') ); } else { // browser global factory( window.EvEmitter, window.fizzyUIUtils, window.Flickity ); } }( window, function factory( EvEmitter, utils, Flickity ) { 'use strict'; // -------------------------- Player -------------------------- // function Player( parent ) { this.parent = parent; this.state = 'stopped'; // visibility change event handler this.onVisibilityChange = this.visibilityChange.bind( this ); this.onVisibilityPlay = this.visibilityPlay.bind( this ); } Player.prototype = Object.create( EvEmitter.prototype ); // start play Player.prototype.play = function() { if ( this.state == 'playing' ) { return; } // do not play if page is hidden, start playing when page is visible var isPageHidden = document.hidden; if ( isPageHidden ) { document.addEventListener( 'visibilitychange', this.onVisibilityPlay ); return; } this.state = 'playing'; // listen to visibility change document.addEventListener( 'visibilitychange', this.onVisibilityChange ); // start ticking this.tick(); }; Player.prototype.tick = function() { // do not tick if not playing if ( this.state != 'playing' ) { return; } var time = this.parent.options.autoPlay; // default to 3 seconds time = typeof time == 'number' ? time : 3000; var _this = this; // HACK: reset ticks if stopped and started within interval this.clear(); this.timeout = setTimeout( function() { _this.parent.next( true ); _this.tick(); }, time ); }; Player.prototype.stop = function() { this.state = 'stopped'; this.clear(); // remove visibility change event document.removeEventListener( 'visibilitychange', this.onVisibilityChange ); }; Player.prototype.clear = function() { clearTimeout( this.timeout ); }; Player.prototype.pause = function() { if ( this.state == 'playing' ) { this.state = 'paused'; this.clear(); } }; Player.prototype.unpause = function() { // re-start play if paused if ( this.state == 'paused' ) { this.play(); } }; // pause if page visibility is hidden, unpause if visible Player.prototype.visibilityChange = function() { var isPageHidden = document.hidden; this[ isPageHidden ? 'pause' : 'unpause' ](); }; Player.prototype.visibilityPlay = function() { this.play(); document.removeEventListener( 'visibilitychange', this.onVisibilityPlay ); }; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pauseAutoPlayOnHover: true }); Flickity.createMethods.push('_createPlayer'); var proto = Flickity.prototype; proto._createPlayer = function() { this.player = new Player( this ); this.on( 'activate', this.activatePlayer ); this.on( 'uiChange', this.stopPlayer ); this.on( 'pointerDown', this.stopPlayer ); this.on( 'deactivate', this.deactivatePlayer ); }; proto.activatePlayer = function() { if ( !this.options.autoPlay ) { return; } this.player.play(); this.element.addEventListener( 'mouseenter', this ); }; // Player API, don't hate the ... thanks I know where the door is proto.playPlayer = function() { this.player.play(); }; proto.stopPlayer = function() { this.player.stop(); }; proto.pausePlayer = function() { this.player.pause(); }; proto.unpausePlayer = function() { this.player.unpause(); }; proto.deactivatePlayer = function() { this.player.stop(); this.element.removeEventListener( 'mouseenter', this ); }; // ----- mouseenter/leave ----- // // pause auto-play on hover proto.onmouseenter = function() { if ( !this.options.pauseAutoPlayOnHover ) { return; } this.player.pause(); this.element.addEventListener( 'mouseleave', this ); }; // resume auto-play on hover off proto.onmouseleave = function() { this.player.unpause(); this.element.removeEventListener( 'mouseleave', this ); }; // ----- ----- // Flickity.Player = Player; return Flickity; })); },{"./flickity":8,"ev-emitter":2,"fizzy-ui-utils":3}],13:[function(require,module,exports){ // prev/next buttons ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ './flickity', 'unipointer/unipointer', 'fizzy-ui-utils/utils' ], function( Flickity, Unipointer, utils ) { return factory( window, Flickity, Unipointer, utils ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unipointer'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.Unipointer, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unipointer, utils ) { 'use strict'; var svgURI = 'http://www.w3.org/2000/svg'; // -------------------------- PrevNextButton -------------------------- // function PrevNextButton( direction, parent ) { this.direction = direction; this.parent = parent; this._create(); } PrevNextButton.prototype = Object.create( Unipointer.prototype ); PrevNextButton.prototype.activate = function() { this.bindStartEvent( this.element ); this.element.addEventListener( 'click', this ); // add to DOM this.parent.element.appendChild( this.element ); }; PrevNextButton.prototype.deactivate = function() { // remove from DOM this.parent.element.removeChild( this.element ); // click events this.unbindStartEvent( this.element ); this.element.removeEventListener( 'click', this ); }; PrevNextButton.prototype.createSVG = function() { var svg = document.createElementNS( svgURI, 'svg'); svg.setAttribute( 'class', 'flickity-button-icon' ); svg.setAttribute( 'viewBox', '0 0 100 100' ); var path = document.createElementNS( svgURI, 'path'); var pathMovements = getArrowMovements( this.parent.options.arrowShape ); path.setAttribute( 'd', pathMovements ); path.setAttribute( 'class', 'arrow' ); // rotate arrow if ( !this.isLeft ) { path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' ); } svg.appendChild( path ); return svg; }; // get SVG path movmement function getArrowMovements( shape ) { // use shape as movement if string if ( typeof shape == 'string' ) { return shape; } // create movement string return 'M ' + shape.x0 + ',50' + ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) + ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) + ' L ' + shape.x3 + ',50 ' + ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) + ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) + ' Z'; } PrevNextButton.prototype.handleEvent = utils.handleEvent; PrevNextButton.prototype.onclick = function() { if ( !this.isEnabled ) { return; } this.parent.uiChange(); var method = this.isPrevious ? 'previous' : 'next'; this.parent[ method ](); }; // ----- ----- // PrevNextButton.prototype.enable = function() { if ( this.isEnabled ) { return; } this.element.disabled = false; this.isEnabled = true; }; PrevNextButton.prototype.disable = function() { if ( !this.isEnabled ) { return; } this.element.disabled = true; this.isEnabled = false; }; PrevNextButton.prototype.update = function() { // index of first or last slide, if previous or next var slides = this.parent.slides; // enable is wrapAround and at least 2 slides if ( this.parent.options.wrapAround && slides.length > 1 ) { this.enable(); return; } var lastIndex = slides.length ? slides.length - 1 : 0; var boundIndex = this.isPrevious ? 0 : lastIndex; var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable'; this[ method ](); }; PrevNextButton.prototype.destroy = function() { this.deactivate(); this.allOff(); }; // -------------------------- Flickity prototype -------------------------- // utils.extend( Flickity.defaults, { prevNextButtons: true, arrowShape: { x0: 10, x1: 60, y1: 50, x2: 70, y2: 40, x3: 30 } }); Flickity.createMethods.push('_createPrevNextButtons'); var proto = Flickity.prototype; proto._createPrevNextButtons = function() { if ( !this.options.prevNextButtons ) { return; } this.prevButton = new PrevNextButton( -1, this ); this.nextButton = new PrevNextButton( 1, this ); this.on( 'activate', this.activatePrevNextButtons ); }; proto.activatePrevNextButtons = function() { this.prevButton.activate(); this.nextButton.activate(); this.on( 'deactivate', this.deactivatePrevNextButtons ); }; proto.deactivatePrevNextButtons = function() { this.prevButton.deactivate(); this.nextButton.deactivate(); this.off( 'deactivate', this.deactivatePrevNextButtons ); }; // -------------------------- -------------------------- // Flickity.PrevNextButton = PrevNextButton; return Flickity; })); },{"./flickity":8,"fizzy-ui-utils":3,"unipointer":20}],14:[function(require,module,exports){ // slide ( function( window, factory ) { // universal module definition /* jshint strict: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.Flickity = window.Flickity || {}; window.Flickity.Slide = factory(); } }( window, function factory() { 'use strict'; function Slide( parent ) { this.parent = parent; this.isOriginLeft = parent.originSide == 'left'; this.cells = []; this.outerWidth = 0; this.height = 0; } var proto = Slide.prototype; proto.addCell = function( cell ) { this.cells.push( cell ); this.outerWidth += cell.size.outerWidth; this.height = Math.max( cell.size.outerHeight, this.height ); // first cell stuff if ( this.cells.length == 1 ) { this.x = cell.x; // x comes from first cell var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight'; this.firstMargin = cell.size[ beginMargin ]; } }; proto.updateTarget = function() { var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft'; var lastCell = this.getLastCell(); var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0; var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin ); this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign; }; proto.getLastCell = function() { return this.cells[ this.cells.length - 1 ]; }; proto.select = function() { this.cells.forEach( function( cell ) { cell.select(); }); }; proto.unselect = function() { this.cells.forEach( function( cell ) { cell.unselect(); }); }; proto.getCellElements = function() { return this.cells.map( function( cell ) { return cell.element; }); }; return Slide; })); },{}],15:[function(require,module,exports){ /*! * getSize v2.0.3 * measure size of elements * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ /* globals console: false */ ( function( window, factory ) { /* jshint strict: false */ /* globals define, module */ if ( typeof define == 'function' && define.amd ) { // AMD define( factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.getSize = factory(); } })( window, function factory() { 'use strict'; // -------------------------- helpers -------------------------- // // get a number from a string, not a percentage function getStyleSize( value ) { var num = parseFloat( value ); // not a percent like '100%', and a number var isValid = value.indexOf('%') == -1 && !isNaN( num ); return isValid && num; } function noop() {} var logError = typeof console == 'undefined' ? noop : function( message ) { console.error( message ); }; // -------------------------- measurements -------------------------- // var measurements = [ 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth' ]; var measurementsLength = measurements.length; function getZeroSize() { var size = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0 }; for ( var i=0; i < measurementsLength; i++ ) { var measurement = measurements[i]; size[ measurement ] = 0; } return size; } // -------------------------- getStyle -------------------------- // /** * getStyle, get style of element, check for Firefox bug * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 */ function getStyle( elem ) { var style = getComputedStyle( elem ); if ( !style ) { logError( 'Style returned ' + style + '. Are you running this code in a hidden iframe on Firefox? ' + 'See https://bit.ly/getsizebug1' ); } return style; } // -------------------------- setup -------------------------- // var isSetup = false; var isBoxSizeOuter; /** * setup * check isBoxSizerOuter * do on first getSize() rather than on page load for Firefox bug */ function setup() { // setup once if ( isSetup ) { return; } isSetup = true; // -------------------------- box sizing -------------------------- // /** * Chrome & Safari measure the outer-width on style.width on border-box elems * IE11 & Firefox<29 measures the inner-width */ var div = document.createElement('div'); div.style.width = '200px'; div.style.padding = '1px 2px 3px 4px'; div.style.borderStyle = 'solid'; div.style.borderWidth = '1px 2px 3px 4px'; div.style.boxSizing = 'border-box'; var body = document.body || document.documentElement; body.appendChild( div ); var style = getStyle( div ); // round value for browser zoom. desandro/masonry#928 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200; getSize.isBoxSizeOuter = isBoxSizeOuter; body.removeChild( div ); } // -------------------------- getSize -------------------------- // function getSize( elem ) { setup(); // use querySeletor if elem is string if ( typeof elem == 'string' ) { elem = document.querySelector( elem ); } // do not proceed on non-objects if ( !elem || typeof elem != 'object' || !elem.nodeType ) { return; } var style = getStyle( elem ); // if hidden, everything is 0 if ( style.display == 'none' ) { return getZeroSize(); } var size = {}; size.width = elem.offsetWidth; size.height = elem.offsetHeight; var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; // get all measurements for ( var i=0; i < measurementsLength; i++ ) { var measurement = measurements[i]; var value = style[ measurement ]; var num = parseFloat( value ); // any 'auto', 'medium' value will be 0 size[ measurement ] = !isNaN( num ) ? num : 0; } var paddingWidth = size.paddingLeft + size.paddingRight; var paddingHeight = size.paddingTop + size.paddingBottom; var marginWidth = size.marginLeft + size.marginRight; var marginHeight = size.marginTop + size.marginBottom; var borderWidth = size.borderLeftWidth + size.borderRightWidth; var borderHeight = size.borderTopWidth + size.borderBottomWidth; var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; // overwrite width and height if we can get it from style var styleWidth = getStyleSize( style.width ); if ( styleWidth !== false ) { size.width = styleWidth + // add padding and border unless it's already including it ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); } var styleHeight = getStyleSize( style.height ); if ( styleHeight !== false ) { size.height = styleHeight + // add padding and border unless it's already including it ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); } size.innerWidth = size.width - ( paddingWidth + borderWidth ); size.innerHeight = size.height - ( paddingHeight + borderHeight ); size.outerWidth = size.width + marginWidth; size.outerHeight = size.height + marginHeight; return size; } return getSize; }); },{}],16:[function(require,module,exports){ typeof window !== "undefined" && (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["Hls"] = factory(); else root["Hls"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/dist/"; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/hls.ts"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/eventemitter3/index.js": /*!*********************************************!*\ !*** ./node_modules/eventemitter3/index.js ***! \*********************************************/ /*! no static exports found */ /*! ModuleConcatenation bailout: Module is not an ECMAScript module */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var has = Object.prototype.hasOwnProperty , prefix = '~'; /** * Constructor to create a storage for our `EE` objects. * An `Events` instance is a plain object whose properties are event names. * * @constructor * @private */ function Events() {} // // We try to not inherit from `Object.prototype`. In some engines creating an // instance in this way is faster than calling `Object.create(null)` directly. // If `Object.create(null)` is not supported we prefix the event names with a // character to make sure that the built-in object properties are not // overridden or used as an attack vector. // if (Object.create) { Events.prototype = Object.create(null); // // This hack is needed because the `__proto__` property is still inherited in // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. // if (!new Events().__proto__) prefix = false; } /** * Representation of a single event listener. * * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} [once=false] Specify if the listener is a one-time listener. * @constructor * @private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Add a listener for a given event. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} once Specify if the listener is a one-time listener. * @returns {EventEmitter} * @private */ function addListener(emitter, event, fn, context, once) { if (typeof fn !== 'function') { throw new TypeError('The listener must be a function'); } var listener = new EE(fn, context || emitter, once) , evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } /** * Clear event by name. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} evt The Event name. * @private */ function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } /** * Minimal `EventEmitter` interface that is molded against the Node.js * `EventEmitter` interface. * * @constructor * @public */ function EventEmitter() { this._events = new Events(); this._eventsCount = 0; } /** * Return an array listing the events for which the emitter has registered * listeners. * * @returns {Array} * @public */ EventEmitter.prototype.eventNames = function eventNames() { var names = [] , events , name; if (this._eventsCount === 0) return names; for (name in (events = this._events)) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; /** * Return the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Array} The registered listeners. * @public */ EventEmitter.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event , handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; /** * Return the number of listeners listening to a given event. * * @param {(String|Symbol)} event The event name. * @returns {Number} The number of listeners. * @public */ EventEmitter.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event , listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; /** * Calls each of the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Boolean} `true` if the event had listeners, else `false`. * @public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Add a listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; /** * Add a one-time listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; /** * Remove the listeners of a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn Only remove the listeners that match this function. * @param {*} context Only remove the listeners that have this context. * @param {Boolean} once Only remove one-time listeners. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if ( listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context) ) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; /** * Remove all listeners, or those of the specified event. * * @param {(String|Symbol)} [event] The event name. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Allow `EventEmitter` to be imported as module namespace. // EventEmitter.EventEmitter = EventEmitter; // // Expose the module. // if (true) { module.exports = EventEmitter; } /***/ }), /***/ "./node_modules/url-toolkit/src/url-toolkit.js": /*!*****************************************************!*\ !*** ./node_modules/url-toolkit/src/url-toolkit.js ***! \*****************************************************/ /*! no static exports found */ /*! ModuleConcatenation bailout: Module is not an ECMAScript module */ /***/ (function(module, exports, __webpack_require__) { // see https://tools.ietf.org/html/rfc1808 (function (root) { var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/?#]*\/)*[^;?#]*)?(;[^?#]*)?(\?[^#]*)?(#.*)?$/; var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/; var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g; var URLToolkit = { // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // // E.g // With opts.alwaysNormalize = false (default, spec compliant) // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g // With opts.alwaysNormalize = true (not spec compliant) // http://a.com/b/cd + /e/f/../g => http://a.com/e/g buildAbsoluteURL: function (baseURL, relativeURL, opts) { opts = opts || {}; // remove any remaining space and CRLF baseURL = baseURL.trim(); relativeURL = relativeURL.trim(); if (!relativeURL) { // 2a) If the embedded URL is entirely empty, it inherits the // entire base URL (i.e., is set equal to the base URL) // and we are done. if (!opts.alwaysNormalize) { return baseURL; } var basePartsForNormalise = URLToolkit.parseURL(baseURL); if (!basePartsForNormalise) { throw new Error('Error trying to parse base URL.'); } basePartsForNormalise.path = URLToolkit.normalizePath( basePartsForNormalise.path ); return URLToolkit.buildURLFromParts(basePartsForNormalise); } var relativeParts = URLToolkit.parseURL(relativeURL); if (!relativeParts) { throw new Error('Error trying to parse relative URL.'); } if (relativeParts.scheme) { // 2b) If the embedded URL starts with a scheme name, it is // interpreted as an absolute URL and we are done. if (!opts.alwaysNormalize) { return relativeURL; } relativeParts.path = URLToolkit.normalizePath(relativeParts.path); return URLToolkit.buildURLFromParts(relativeParts); } var baseParts = URLToolkit.parseURL(baseURL); if (!baseParts) { throw new Error('Error trying to parse base URL.'); } if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); baseParts.netLoc = pathParts[1]; baseParts.path = pathParts[2]; } if (baseParts.netLoc && !baseParts.path) { baseParts.path = '/'; } var builtParts = { // 2c) Otherwise, the embedded URL inherits the scheme of // the base URL. scheme: baseParts.scheme, netLoc: relativeParts.netLoc, path: null, params: relativeParts.params, query: relativeParts.query, fragment: relativeParts.fragment, }; if (!relativeParts.netLoc) { // 3) If the embedded URL's is non-empty, we skip to // Step 7. Otherwise, the embedded URL inherits the // (if any) of the base URL. builtParts.netLoc = baseParts.netLoc; // 4) If the embedded URL path is preceded by a slash "/", the // path is not relative and we skip to Step 7. if (relativeParts.path[0] !== '/') { if (!relativeParts.path) { // 5) If the embedded URL path is empty (and not preceded by a // slash), then the embedded URL inherits the base URL path builtParts.path = baseParts.path; // 5a) if the embedded URL's is non-empty, we skip to // step 7; otherwise, it inherits the of the base // URL (if any) and if (!relativeParts.params) { builtParts.params = baseParts.params; // 5b) if the embedded URL's is non-empty, we skip to // step 7; otherwise, it inherits the of the base // URL (if any) and we skip to step 7. if (!relativeParts.query) { builtParts.query = baseParts.query; } } } else { // 6) The last segment of the base URL's path (anything // following the rightmost slash "/", or the entire path if no // slash is present) is removed and the embedded URL's path is // appended in its place. var baseURLPath = baseParts.path; var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path; builtParts.path = URLToolkit.normalizePath(newPath); } } } if (builtParts.path === null) { builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path; } return URLToolkit.buildURLFromParts(builtParts); }, parseURL: function (url) { var parts = URL_REGEX.exec(url); if (!parts) { return null; } return { scheme: parts[1] || '', netLoc: parts[2] || '', path: parts[3] || '', params: parts[4] || '', query: parts[5] || '', fragment: parts[6] || '', }; }, normalizePath: function (path) { // The following operations are // then applied, in order, to the new path: // 6a) All occurrences of "./", where "." is a complete path // segment, are removed. // 6b) If the path ends with "." as a complete path segment, // that "." is removed. path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); // 6c) All occurrences of "/../", where is a // complete path segment not equal to "..", are removed. // Removal of these path segments is performed iteratively, // removing the leftmost matching pattern on each iteration, // until no matching pattern remains. // 6d) If the path ends with "/..", where is a // complete path segment not equal to "..", that // "/.." is removed. while ( path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length ) {} return path.split('').reverse().join(''); }, buildURLFromParts: function (parts) { return ( parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment ); }, }; if (true) module.exports = URLToolkit; else {} })(this); /***/ }), /***/ "./node_modules/webworkify-webpack/index.js": /*!**************************************************!*\ !*** ./node_modules/webworkify-webpack/index.js ***! \**************************************************/ /*! no static exports found */ /*! ModuleConcatenation bailout: Module is not an ECMAScript module */ /***/ (function(module, exports, __webpack_require__) { function webpackBootstrapFunc (modules) { /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.l = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/"; /******/ // on error function for async loading /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE) return f.default || f // try to call default if defined to also support babel esmodule exports } var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+' var dependencyRegExp = '\\(\\s*(\/\\*.*?\\*\/)?\\s*.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true // http://stackoverflow.com/a/2593661/130442 function quoteRegExp (str) { return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') } function isNumeric(n) { return !isNaN(1 * n); // 1 * n converts integers, integers as string ("123"), 1e3 and "1e3" to integers and strings to NaN } function getModuleDependencies (sources, module, queueName) { var retval = {} retval[queueName] = [] var fnString = module.toString() var wrapperSignature = fnString.match(/^function\s?\w*\(\w+,\s*\w+,\s*(\w+)\)/) if (!wrapperSignature) return retval var webpackRequireName = wrapperSignature[1] // main bundle deps var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g') var match while ((match = re.exec(fnString))) { if (match[3] === 'dll-reference') continue retval[queueName].push(match[3]) } // dll deps re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g') while ((match = re.exec(fnString))) { if (!sources[match[2]]) { retval[queueName].push(match[1]) sources[match[2]] = __webpack_require__(match[1]).m } retval[match[2]] = retval[match[2]] || [] retval[match[2]].push(match[4]) } // convert 1e3 back to 1000 - this can be important after uglify-js converted 1000 to 1e3 var keys = Object.keys(retval); for (var i = 0; i < keys.length; i++) { for (var j = 0; j < retval[keys[i]].length; j++) { if (isNumeric(retval[keys[i]][j])) { retval[keys[i]][j] = 1 * retval[keys[i]][j]; } } } return retval } function hasValuesInQueues (queues) { var keys = Object.keys(queues) return keys.reduce(function (hasValues, key) { return hasValues || queues[key].length > 0 }, false) } function getRequiredModules (sources, moduleId) { var modulesQueue = { main: [moduleId] } var requiredModules = { main: [] } var seenModules = { main: {} } while (hasValuesInQueues(modulesQueue)) { var queues = Object.keys(modulesQueue) for (var i = 0; i < queues.length; i++) { var queueName = queues[i] var queue = modulesQueue[queueName] var moduleToCheck = queue.pop() seenModules[queueName] = seenModules[queueName] || {} if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue seenModules[queueName][moduleToCheck] = true requiredModules[queueName] = requiredModules[queueName] || [] requiredModules[queueName].push(moduleToCheck) var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName) var newModulesKeys = Object.keys(newModules) for (var j = 0; j < newModulesKeys.length; j++) { modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || [] modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]]) } } } return requiredModules } module.exports = function (moduleId, options) { options = options || {} var sources = { main: __webpack_require__.m } var requiredModules = options.all ? { main: Object.keys(sources.main) } : getRequiredModules(sources, moduleId) var src = '' Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) { var entryModule = 0 while (requiredModules[module][entryModule]) { entryModule++ } requiredModules[module].push(entryModule) sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })' src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\n' }) src = src + 'new ((' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '}))(self);' var blob = new window.Blob([src], { type: 'text/javascript' }) if (options.bare) { return blob } var URL = window.URL || window.webkitURL || window.mozURL || window.msURL var workerUrl = URL.createObjectURL(blob) var worker = new window.Worker(workerUrl) worker.objectURL = workerUrl return worker } /***/ }), /***/ "./src/crypt/decrypter.js": /*!********************************************!*\ !*** ./src/crypt/decrypter.js + 3 modules ***! \********************************************/ /*! exports provided: default */ /*! ModuleConcatenation bailout: Cannot concat with ./src/errors.ts because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/events.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/get-self-scope.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/logger.js because of ./src/hls.ts */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // CONCATENATED MODULE: ./src/crypt/aes-crypto.js var AESCrypto = /*#__PURE__*/function () { function AESCrypto(subtle, iv) { this.subtle = subtle; this.aesIV = iv; } var _proto = AESCrypto.prototype; _proto.decrypt = function decrypt(data, key) { return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data); }; return AESCrypto; }(); // CONCATENATED MODULE: ./src/crypt/fast-aes-key.js var FastAESKey = /*#__PURE__*/function () { function FastAESKey(subtle, key) { this.subtle = subtle; this.key = key; } var _proto = FastAESKey.prototype; _proto.expandKey = function expandKey() { return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, ['encrypt', 'decrypt']); }; return FastAESKey; }(); /* harmony default export */ var fast_aes_key = (FastAESKey); // CONCATENATED MODULE: ./src/crypt/aes-decryptor.js // PKCS7 function removePadding(buffer) { var outputBytes = buffer.byteLength; var paddingBytes = outputBytes && new DataView(buffer).getUint8(outputBytes - 1); if (paddingBytes) { return buffer.slice(0, outputBytes - paddingBytes); } else { return buffer; } } var AESDecryptor = /*#__PURE__*/function () { function AESDecryptor() { // Static after running initTable this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; this.sBox = new Uint32Array(256); this.invSBox = new Uint32Array(256); // Changes during runtime this.key = new Uint32Array(0); this.initTable(); } // Using view.getUint32() also swaps the byte order. var _proto = AESDecryptor.prototype; _proto.uint8ArrayToUint32Array_ = function uint8ArrayToUint32Array_(arrayBuffer) { var view = new DataView(arrayBuffer); var newArray = new Uint32Array(4); for (var i = 0; i < 4; i++) { newArray[i] = view.getUint32(i * 4); } return newArray; }; _proto.initTable = function initTable() { var sBox = this.sBox; var invSBox = this.invSBox; var subMix = this.subMix; var subMix0 = subMix[0]; var subMix1 = subMix[1]; var subMix2 = subMix[2]; var subMix3 = subMix[3]; var invSubMix = this.invSubMix; var invSubMix0 = invSubMix[0]; var invSubMix1 = invSubMix[1]; var invSubMix2 = invSubMix[2]; var invSubMix3 = invSubMix[3]; var d = new Uint32Array(256); var x = 0; var xi = 0; var i = 0; for (i = 0; i < 256; i++) { if (i < 128) { d[i] = i << 1; } else { d[i] = i << 1 ^ 0x11b; } } for (i = 0; i < 256; i++) { var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4; sx = sx >>> 8 ^ sx & 0xff ^ 0x63; sBox[x] = sx; invSBox[sx] = x; // Compute multiplication var x2 = d[x]; var x4 = d[x2]; var x8 = d[x4]; // Compute sub/invSub bytes, mix columns tables var t = d[sx] * 0x101 ^ sx * 0x1010100; subMix0[x] = t << 24 | t >>> 8; subMix1[x] = t << 16 | t >>> 16; subMix2[x] = t << 8 | t >>> 24; subMix3[x] = t; // Compute inv sub bytes, inv mix columns tables t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; invSubMix0[sx] = t << 24 | t >>> 8; invSubMix1[sx] = t << 16 | t >>> 16; invSubMix2[sx] = t << 8 | t >>> 24; invSubMix3[sx] = t; // Compute next counter if (!x) { x = xi = 1; } else { x = x2 ^ d[d[d[x8 ^ x2]]]; xi ^= d[d[xi]]; } } }; _proto.expandKey = function expandKey(keyBuffer) { // convert keyBuffer to Uint32Array var key = this.uint8ArrayToUint32Array_(keyBuffer); var sameKey = true; var offset = 0; while (offset < key.length && sameKey) { sameKey = key[offset] === this.key[offset]; offset++; } if (sameKey) { return; } this.key = key; var keySize = this.keySize = key.length; if (keySize !== 4 && keySize !== 6 && keySize !== 8) { throw new Error('Invalid aes key size=' + keySize); } var ksRows = this.ksRows = (keySize + 6 + 1) * 4; var ksRow; var invKsRow; var keySchedule = this.keySchedule = new Uint32Array(ksRows); var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows); var sbox = this.sBox; var rcon = this.rcon; var invSubMix = this.invSubMix; var invSubMix0 = invSubMix[0]; var invSubMix1 = invSubMix[1]; var invSubMix2 = invSubMix[2]; var invSubMix3 = invSubMix[3]; var prev; var t; for (ksRow = 0; ksRow < ksRows; ksRow++) { if (ksRow < keySize) { prev = keySchedule[ksRow] = key[ksRow]; continue; } t = prev; if (ksRow % keySize === 0) { // Rot word t = t << 8 | t >>> 24; // Sub word t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; // Mix Rcon t ^= rcon[ksRow / keySize | 0] << 24; } else if (keySize > 6 && ksRow % keySize === 4) { // Sub word t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; } keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; } for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { ksRow = ksRows - invKsRow; if (invKsRow & 3) { t = keySchedule[ksRow]; } else { t = keySchedule[ksRow - 4]; } if (invKsRow < 4 || ksRow <= 4) { invKeySchedule[invKsRow] = t; } else { invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]]; } invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; } } // Adding this as a method greatly improves performance. ; _proto.networkToHostOrderSwap = function networkToHostOrderSwap(word) { return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; }; _proto.decrypt = function decrypt(inputArrayBuffer, offset, aesIV, removePKCS7Padding) { var nRounds = this.keySize + 6; var invKeySchedule = this.invKeySchedule; var invSBOX = this.invSBox; var invSubMix = this.invSubMix; var invSubMix0 = invSubMix[0]; var invSubMix1 = invSubMix[1]; var invSubMix2 = invSubMix[2]; var invSubMix3 = invSubMix[3]; var initVector = this.uint8ArrayToUint32Array_(aesIV); var initVector0 = initVector[0]; var initVector1 = initVector[1]; var initVector2 = initVector[2]; var initVector3 = initVector[3]; var inputInt32 = new Int32Array(inputArrayBuffer); var outputInt32 = new Int32Array(inputInt32.length); var t0, t1, t2, t3; var s0, s1, s2, s3; var inputWords0, inputWords1, inputWords2, inputWords3; var ksRow, i; var swapWord = this.networkToHostOrderSwap; while (offset < inputInt32.length) { inputWords0 = swapWord(inputInt32[offset]); inputWords1 = swapWord(inputInt32[offset + 1]); inputWords2 = swapWord(inputInt32[offset + 2]); inputWords3 = swapWord(inputInt32[offset + 3]); s0 = inputWords0 ^ invKeySchedule[0]; s1 = inputWords3 ^ invKeySchedule[1]; s2 = inputWords2 ^ invKeySchedule[2]; s3 = inputWords1 ^ invKeySchedule[3]; ksRow = 4; // Iterate through the rounds of decryption for (i = 1; i < nRounds; i++) { t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow]; t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; // Update state s0 = t0; s1 = t1; s2 = t2; s3 = t3; ksRow = ksRow + 4; } // Shift rows, sub bytes, add round key t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow]; t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; ksRow = ksRow + 3; // Write outputInt32[offset] = swapWord(t0 ^ initVector0); outputInt32[offset + 1] = swapWord(t3 ^ initVector1); outputInt32[offset + 2] = swapWord(t2 ^ initVector2); outputInt32[offset + 3] = swapWord(t1 ^ initVector3); // reset initVector to last 4 unsigned int initVector0 = inputWords0; initVector1 = inputWords1; initVector2 = inputWords2; initVector3 = inputWords3; offset = offset + 4; } return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer; }; _proto.destroy = function destroy() { this.key = undefined; this.keySize = undefined; this.ksRows = undefined; this.sBox = undefined; this.invSBox = undefined; this.subMix = undefined; this.invSubMix = undefined; this.keySchedule = undefined; this.invKeySchedule = undefined; this.rcon = undefined; }; return AESDecryptor; }(); /* harmony default export */ var aes_decryptor = (AESDecryptor); // EXTERNAL MODULE: ./src/errors.ts var errors = __webpack_require__("./src/errors.ts"); // EXTERNAL MODULE: ./src/utils/logger.js var logger = __webpack_require__("./src/utils/logger.js"); // EXTERNAL MODULE: ./src/events.js var events = __webpack_require__("./src/events.js"); // EXTERNAL MODULE: ./src/utils/get-self-scope.js var get_self_scope = __webpack_require__("./src/utils/get-self-scope.js"); // CONCATENATED MODULE: ./src/crypt/decrypter.js // see https://stackoverflow.com/a/11237259/589493 var global = Object(get_self_scope["getSelfScope"])(); // safeguard for code that might run both on worker and main thread var decrypter_Decrypter = /*#__PURE__*/function () { function Decrypter(observer, config, _temp) { var _ref = _temp === void 0 ? {} : _temp, _ref$removePKCS7Paddi = _ref.removePKCS7Padding, removePKCS7Padding = _ref$removePKCS7Paddi === void 0 ? true : _ref$removePKCS7Paddi; this.logEnabled = true; this.observer = observer; this.config = config; this.removePKCS7Padding = removePKCS7Padding; // built in decryptor expects PKCS7 padding if (removePKCS7Padding) { try { var browserCrypto = global.crypto; if (browserCrypto) { this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; } } catch (e) {} } this.disableWebCrypto = !this.subtle; } var _proto = Decrypter.prototype; _proto.isSync = function isSync() { return this.disableWebCrypto && this.config.enableSoftwareAES; }; _proto.decrypt = function decrypt(data, key, iv, callback) { var _this = this; if (this.disableWebCrypto && this.config.enableSoftwareAES) { if (this.logEnabled) { logger["logger"].log('JS AES decrypt'); this.logEnabled = false; } var decryptor = this.decryptor; if (!decryptor) { this.decryptor = decryptor = new aes_decryptor(); } decryptor.expandKey(key); callback(decryptor.decrypt(data, 0, iv, this.removePKCS7Padding)); } else { if (this.logEnabled) { logger["logger"].log('WebCrypto AES decrypt'); this.logEnabled = false; } var subtle = this.subtle; if (this.key !== key) { this.key = key; this.fastAesKey = new fast_aes_key(subtle, key); } this.fastAesKey.expandKey().then(function (aesKey) { // decrypt using web crypto var crypto = new AESCrypto(subtle, iv); crypto.decrypt(data, aesKey).catch(function (err) { _this.onWebCryptoError(err, data, key, iv, callback); }).then(function (result) { callback(result); }); }).catch(function (err) { _this.onWebCryptoError(err, data, key, iv, callback); }); } }; _proto.onWebCryptoError = function onWebCryptoError(err, data, key, iv, callback) { if (this.config.enableSoftwareAES) { logger["logger"].log('WebCrypto Error, disable WebCrypto API'); this.disableWebCrypto = true; this.logEnabled = true; this.decrypt(data, key, iv, callback); } else { logger["logger"].error("decrypting error : " + err.message); this.observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_DECRYPT_ERROR, fatal: true, reason: err.message }); } }; _proto.destroy = function destroy() { var decryptor = this.decryptor; if (decryptor) { decryptor.destroy(); this.decryptor = undefined; } }; return Decrypter; }(); /* harmony default export */ var decrypter = __webpack_exports__["default"] = (decrypter_Decrypter); /***/ }), /***/ "./src/demux/demuxer-inline.js": /*!**************************************************!*\ !*** ./src/demux/demuxer-inline.js + 12 modules ***! \**************************************************/ /*! exports provided: default */ /*! ModuleConcatenation bailout: Cannot concat with ./src/crypt/decrypter.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/demux/id3.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/demux/mp4demuxer.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/errors.ts because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/events.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/polyfills/number.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/get-self-scope.js because of ./src/hls.ts */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/logger.js because of ./src/hls.ts */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // EXTERNAL MODULE: ./src/events.js var events = __webpack_require__("./src/events.js"); // EXTERNAL MODULE: ./src/errors.ts var errors = __webpack_require__("./src/errors.ts"); // EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules var crypt_decrypter = __webpack_require__("./src/crypt/decrypter.js"); // EXTERNAL MODULE: ./src/polyfills/number.js var number = __webpack_require__("./src/polyfills/number.js"); // EXTERNAL MODULE: ./src/utils/logger.js var logger = __webpack_require__("./src/utils/logger.js"); // EXTERNAL MODULE: ./src/utils/get-self-scope.js var get_self_scope = __webpack_require__("./src/utils/get-self-scope.js"); // CONCATENATED MODULE: ./src/demux/adts.js /** * ADTS parser helper * @link https://wiki.multimedia.cx/index.php?title=ADTS */ function getAudioConfig(observer, data, offset, audioCodec) { var adtsObjectType, // :int adtsSampleingIndex, // :int adtsExtensionSampleingIndex, // :int adtsChanelConfig, // :int config, userAgent = navigator.userAgent.toLowerCase(), manifestCodec = audioCodec, adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; // byte 2 adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1; adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2; if (adtsSampleingIndex > adtsSampleingRates.length - 1) { observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_PARSING_ERROR, fatal: true, reason: "invalid ADTS sampling index:" + adtsSampleingIndex }); return; } adtsChanelConfig = (data[offset + 2] & 0x01) << 2; // byte 3 adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6; logger["logger"].log("manifest codec:" + audioCodec + ",ADTS data:type:" + adtsObjectType + ",sampleingIndex:" + adtsSampleingIndex + "[" + adtsSampleingRates[adtsSampleingIndex] + "Hz],channelConfig:" + adtsChanelConfig); // firefox: freq less than 24kHz = AAC SBR (HE-AAC) if (/firefox/i.test(userAgent)) { if (adtsSampleingIndex >= 6) { adtsObjectType = 5; config = new Array(4); // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies // there is a factor 2 between frame sample rate and output sample rate // multiply frequency by 2 (see table below, equivalent to substract 3) adtsExtensionSampleingIndex = adtsSampleingIndex - 3; } else { adtsObjectType = 2; config = new Array(2); adtsExtensionSampleingIndex = adtsSampleingIndex; } // Android : always use AAC } else if (userAgent.indexOf('android') !== -1) { adtsObjectType = 2; config = new Array(2); adtsExtensionSampleingIndex = adtsSampleingIndex; } else { /* for other browsers (Chrome/Vivaldi/Opera ...) always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) */ adtsObjectType = 5; config = new Array(4); // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) { // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies // there is a factor 2 between frame sample rate and output sample rate // multiply frequency by 2 (see table below, equivalent to substract 3) adtsExtensionSampleingIndex = adtsSampleingIndex - 3; } else { // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChanelConfig === 1) { adtsObjectType = 2; config = new Array(2); } adtsExtensionSampleingIndex = adtsSampleingIndex; } } /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() Audio Profile / Audio Object Type 0: Null 1: AAC Main 2: AAC LC (Low Complexity) 3: AAC SSR (Scalable Sample Rate) 4: AAC LTP (Long Term Prediction) 5: SBR (Spectral Band Replication) 6: AAC Scalable sampling freq 0: 96000 Hz 1: 88200 Hz 2: 64000 Hz 3: 48000 Hz 4: 44100 Hz 5: 32000 Hz 6: 24000 Hz 7: 22050 Hz 8: 16000 Hz 9: 12000 Hz 10: 11025 Hz 11: 8000 Hz 12: 7350 Hz 13: Reserved 14: Reserved 15: frequency is written explictly Channel Configurations These are the channel configurations: 0: Defined in AOT Specifc Config 1: 1 channel: front-center 2: 2 channels: front-left, front-right */ // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 config[0] = adtsObjectType << 3; // samplingFrequencyIndex config[0] |= (adtsSampleingIndex & 0x0E) >> 1; config[1] |= (adtsSampleingIndex & 0x01) << 7; // channelConfiguration config[1] |= adtsChanelConfig << 3; if (adtsObjectType === 5) { // adtsExtensionSampleingIndex config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1; config[2] = (adtsExtensionSampleingIndex & 0x01) << 7; // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc config[2] |= 2 << 2; config[3] = 0; } return { config: config, samplerate: adtsSampleingRates[adtsSampleingIndex], channelCount: adtsChanelConfig, codec: 'mp4a.40.' + adtsObjectType, manifestCodec: manifestCodec }; } function isHeaderPattern(data, offset) { return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0; } function getHeaderLength(data, offset) { return data[offset + 1] & 0x01 ? 7 : 9; } function getFullFrameLength(data, offset) { return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5; } function isHeader(data, offset) { // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 // Layer bits (position 14 and 15) in header should be always 0 for ADTS // More info https://wiki.multimedia.cx/index.php?title=ADTS if (offset + 1 < data.length && isHeaderPattern(data, offset)) { return true; } return false; } function adts_probe(data, offset) { // same as isHeader but we also check that ADTS frame follows last ADTS frame // or end of data is reached if (isHeader(data, offset)) { // ADTS header Length var headerLength = getHeaderLength(data, offset); if (offset + headerLength >= data.length) { return false; } // ADTS frame Length var frameLength = getFullFrameLength(data, offset); if (frameLength <= headerLength) { return false; } var newOffset = offset + frameLength; if (newOffset === data.length || newOffset + 1 < data.length && isHeaderPattern(data, newOffset)) { return true; } } return false; } function initTrackConfig(track, observer, data, offset, audioCodec) { if (!track.samplerate) { var config = getAudioConfig(observer, data, offset, audioCodec); track.config = config.config; track.samplerate = config.samplerate; track.channelCount = config.channelCount; track.codec = config.codec; track.manifestCodec = config.manifestCodec; logger["logger"].log("parsed codec:" + track.codec + ",rate:" + config.samplerate + ",nb channel:" + config.channelCount); } } function getFrameDuration(samplerate) { return 1024 * 90000 / samplerate; } function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) { var headerLength, frameLength, stamp; var length = data.length; // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header headerLength = getHeaderLength(data, offset); // retrieve frame size frameLength = getFullFrameLength(data, offset); frameLength -= headerLength; if (frameLength > 0 && offset + headerLength + frameLength <= length) { stamp = pts + frameIndex * frameDuration; // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); return { headerLength: headerLength, frameLength: frameLength, stamp: stamp }; } return undefined; } function appendFrame(track, data, offset, pts, frameIndex) { var frameDuration = getFrameDuration(track.samplerate); var header = parseFrameHeader(data, offset, pts, frameIndex, frameDuration); if (header) { var stamp = header.stamp; var headerLength = header.headerLength; var frameLength = header.frameLength; // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); var aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp }; track.samples.push(aacSample); return { sample: aacSample, length: frameLength + headerLength }; } return undefined; } // EXTERNAL MODULE: ./src/demux/id3.js var id3 = __webpack_require__("./src/demux/id3.js"); // CONCATENATED MODULE: ./src/demux/aacdemuxer.js /** * AAC demuxer */ var aacdemuxer_AACDemuxer = /*#__PURE__*/function () { function AACDemuxer(observer, remuxer, config) { this.observer = observer; this.config = config; this.remuxer = remuxer; } var _proto = AACDemuxer.prototype; _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { this._audioTrack = { container: 'audio/adts', type: 'audio', id: 0, sequenceNumber: 0, isAAC: true, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 }; }; _proto.resetTimeStamp = function resetTimeStamp() {}; AACDemuxer.probe = function probe(data) { if (!data) { return false; } // Check for the ADTS sync word // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 // Layer bits (position 14 and 15) in header should be always 0 for ADTS // More info https://wiki.multimedia.cx/index.php?title=ADTS var id3Data = id3["default"].getID3Data(data, 0) || []; var offset = id3Data.length; for (var length = data.length; offset < length; offset++) { if (adts_probe(data, offset)) { logger["logger"].log('ADTS sync word found !'); return true; } } return false; } // feed incoming data to the front of the parsing pipeline ; _proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { var track = this._audioTrack; var id3Data = id3["default"].getID3Data(data, 0) || []; var timestamp = id3["default"].getTimeStamp(id3Data); var pts = Object(number["isFiniteNumber"])(timestamp) ? timestamp * 90 : timeOffset * 90000; var frameIndex = 0; var stamp = pts; var length = data.length; var offset = id3Data.length; var id3Samples = [{ pts: stamp, dts: stamp, data: id3Data }]; while (offset < length - 1) { if (isHeader(data, offset) && offset + 5 < length) { initTrackConfig(track, this.observer, data, offset, track.manifestCodec); var frame = appendFrame(track, data, offset, pts, frameIndex); if (frame) { offset += frame.length; stamp = frame.sample.pts; frameIndex++; } else { logger["logger"].log('Unable to parse AAC frame'); break; } } else if (id3["default"].isHeader(data, offset)) { id3Data = id3["default"].getID3Data(data, offset); id3Samples.push({ pts: stamp, dts: stamp, data: id3Data }); offset += id3Data.length; } else { // nothing found, keep looking offset++; } } this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset); }; _proto.destroy = function destroy() {}; return AACDemuxer; }(); /* harmony default export */ var aacdemuxer = (aacdemuxer_AACDemuxer); // EXTERNAL MODULE: ./src/demux/mp4demuxer.js var mp4demuxer = __webpack_require__("./src/demux/mp4demuxer.js"); // CONCATENATED MODULE: ./src/demux/mpegaudio.js /** * MPEG parser helper */ var MpegAudio = { BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160], SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000], SamplesCoefficients: [// MPEG 2.5 [0, // Reserved 72, // Layer3 144, // Layer2 12 // Layer1 ], // Reserved [0, // Reserved 0, // Layer3 0, // Layer2 0 // Layer1 ], // MPEG 2 [0, // Reserved 72, // Layer3 144, // Layer2 12 // Layer1 ], // MPEG 1 [0, // Reserved 144, // Layer3 144, // Layer2 12 // Layer1 ]], BytesInSlot: [0, // Reserved 1, // Layer3 1, // Layer2 4 // Layer1 ], appendFrame: function appendFrame(track, data, offset, pts, frameIndex) { // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference if (offset + 24 > data.length) { return undefined; } var header = this.parseHeader(data, offset); if (header && offset + header.frameLength <= data.length) { var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate; var stamp = pts + frameIndex * frameDuration; var sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp }; track.config = []; track.channelCount = header.channelCount; track.samplerate = header.sampleRate; track.samples.push(sample); return { sample: sample, length: header.frameLength }; } return undefined; }, parseHeader: function parseHeader(data, offset) { var headerB = data[offset + 1] >> 3 & 3; var headerC = data[offset + 1] >> 1 & 3; var headerE = data[offset + 2] >> 4 & 15; var headerF = data[offset + 2] >> 2 & 3; var headerG = data[offset + 2] >> 1 & 1; if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { var columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4; var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF]; var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC]; var bytesInSlot = MpegAudio.BytesInSlot[headerC]; var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot; return { sampleRate: sampleRate, channelCount: channelCount, frameLength: frameLength, samplesPerFrame: samplesPerFrame }; } return undefined; }, isHeaderPattern: function isHeaderPattern(data, offset) { return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00; }, isHeader: function isHeader(data, offset) { // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) // More info http://www.mp3-tech.org/programmer/frame_header.html if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { return true; } return false; }, probe: function probe(data, offset) { // same as isHeader but we also check that MPEG frame follows last MPEG frame // or end of data is reached if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { // MPEG header Length var headerLength = 4; // MPEG frame Length var header = this.parseHeader(data, offset); var frameLength = headerLength; if (header && header.frameLength) { frameLength = header.frameLength; } var newOffset = offset + frameLength; if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) { return true; } } return false; } }; /* harmony default export */ var mpegaudio = (MpegAudio); // CONCATENATED MODULE: ./src/demux/exp-golomb.js /** * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. */ var exp_golomb_ExpGolomb = /*#__PURE__*/function () { function ExpGolomb(data) { this.data = data; // the number of bytes left to examine in this.data this.bytesAvailable = data.byteLength; // the current word being examined this.word = 0; // :uint // the number of bits left to examine in the current word this.bitsAvailable = 0; // :uint } // ():void var _proto = ExpGolomb.prototype; _proto.loadWord = function loadWord() { var data = this.data, bytesAvailable = this.bytesAvailable, position = data.byteLength - bytesAvailable, workingBytes = new Uint8Array(4), availableBytes = Math.min(4, bytesAvailable); if (availableBytes === 0) { throw new Error('no bytes available'); } workingBytes.set(data.subarray(position, position + availableBytes)); this.word = new DataView(workingBytes.buffer).getUint32(0); // track the amount of this.data that has been processed this.bitsAvailable = availableBytes * 8; this.bytesAvailable -= availableBytes; } // (count:int):void ; _proto.skipBits = function skipBits(count) { var skipBytes; // :int if (this.bitsAvailable > count) { this.word <<= count; this.bitsAvailable -= count; } else { count -= this.bitsAvailable; skipBytes = count >> 3; count -= skipBytes >> 3; this.bytesAvailable -= skipBytes; this.loadWord(); this.word <<= count; this.bitsAvailable -= count; } } // (size:int):uint ; _proto.readBits = function readBits(size) { var bits = Math.min(this.bitsAvailable, size), // :uint valu = this.word >>> 32 - bits; // :uint if (size > 32) { logger["logger"].error('Cannot read more than 32 bits at a time'); } this.bitsAvailable -= bits; if (this.bitsAvailable > 0) { this.word <<= bits; } else if (this.bytesAvailable > 0) { this.loadWord(); } bits = size - bits; if (bits > 0 && this.bitsAvailable) { return valu << bits | this.readBits(bits); } else { return valu; } } // ():uint ; _proto.skipLZ = function skipLZ() { var leadingZeroCount; // :uint for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) { // the first bit of working word is 1 this.word <<= leadingZeroCount; this.bitsAvailable -= leadingZeroCount; return leadingZeroCount; } } // we exhausted word and still have not found a 1 this.loadWord(); return leadingZeroCount + this.skipLZ(); } // ():void ; _proto.skipUEG = function skipUEG() { this.skipBits(1 + this.skipLZ()); } // ():void ; _proto.skipEG = function skipEG() { this.skipBits(1 + this.skipLZ()); } // ():uint ; _proto.readUEG = function readUEG() { var clz = this.skipLZ(); // :uint return this.readBits(clz + 1) - 1; } // ():int ; _proto.readEG = function readEG() { var valu = this.readUEG(); // :int if (0x01 & valu) { // the number is odd if the low order bit is set return 1 + valu >>> 1; // add 1 to make it even, and divide by 2 } else { return -1 * (valu >>> 1); // divide by two then make it negative } } // Some convenience functions // :Boolean ; _proto.readBoolean = function readBoolean() { return this.readBits(1) === 1; } // ():int ; _proto.readUByte = function readUByte() { return this.readBits(8); } // ():int ; _proto.readUShort = function readUShort() { return this.readBits(16); } // ():int ; _proto.readUInt = function readUInt() { return this.readBits(32); } /** * Advance the ExpGolomb decoder past a scaling list. The scaling * list is optionally transmitted as part of a sequence parameter * set and is not relevant to transmuxing. * @param count {number} the number of entries in this scaling list * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 */ ; _proto.skipScalingList = function skipScalingList(count) { var lastScale = 8, nextScale = 8, j, deltaScale; for (j = 0; j < count; j++) { if (nextScale !== 0) { deltaScale = this.readEG(); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = nextScale === 0 ? lastScale : nextScale; } } /** * Read a sequence parameter set and return some interesting video * properties. A sequence parameter set is the H264 metadata that * describes the properties of upcoming video frames. * @param data {Uint8Array} the bytes of a sequence parameter set * @return {object} an object with configuration parsed from the * sequence parameter set, including the dimensions of the * associated video frames. */ ; _proto.readSPS = function readSPS() { var frameCropLeftOffset = 0, frameCropRightOffset = 0, frameCropTopOffset = 0, frameCropBottomOffset = 0, profileIdc, profileCompat, levelIdc, numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, picHeightInMapUnitsMinus1, frameMbsOnlyFlag, scalingListCount, i, readUByte = this.readUByte.bind(this), readBits = this.readBits.bind(this), readUEG = this.readUEG.bind(this), readBoolean = this.readBoolean.bind(this), skipBits = this.skipBits.bind(this), skipEG = this.skipEG.bind(this), skipUEG = this.skipUEG.bind(this), skipScalingList = this.skipScalingList.bind(this); readUByte(); profileIdc = readUByte(); // profile_idc profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5) skipBits(3); // reserved_zero_3bits u(3), levelIdc = readUByte(); // level_idc u(8) skipUEG(); // seq_parameter_set_id // some profiles have more optional data we don't need if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { var chromaFormatIdc = readUEG(); if (chromaFormatIdc === 3) { skipBits(1); } // separate_colour_plane_flag skipUEG(); // bit_depth_luma_minus8 skipUEG(); // bit_depth_chroma_minus8 skipBits(1); // qpprime_y_zero_transform_bypass_flag if (readBoolean()) { // seq_scaling_matrix_present_flag scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; for (i = 0; i < scalingListCount; i++) { if (readBoolean()) { // seq_scaling_list_present_flag[ i ] if (i < 6) { skipScalingList(16); } else { skipScalingList(64); } } } } } skipUEG(); // log2_max_frame_num_minus4 var picOrderCntType = readUEG(); if (picOrderCntType === 0) { readUEG(); // log2_max_pic_order_cnt_lsb_minus4 } else if (picOrderCntType === 1) { skipBits(1); // delta_pic_order_always_zero_flag skipEG(); // offset_for_non_ref_pic skipEG(); // offset_for_top_to_bottom_field numRefFramesInPicOrderCntCycle = readUEG(); for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { skipEG(); } // offset_for_ref_frame[ i ] } skipUEG(); // max_num_ref_frames skipBits(1); // gaps_in_frame_num_value_allowed_flag picWidthInMbsMinus1 = readUEG(); picHeightInMapUnitsMinus1 = readUEG(); frameMbsOnlyFlag = readBits(1); if (frameMbsOnlyFlag === 0) { skipBits(1); } // mb_adaptive_frame_field_flag skipBits(1); // direct_8x8_inference_flag if (readBoolean()) { // frame_cropping_flag frameCropLeftOffset = readUEG(); frameCropRightOffset = readUEG(); frameCropTopOffset = readUEG(); frameCropBottomOffset = readUEG(); } var pixelRatio = [1, 1]; if (readBoolean()) { // vui_parameters_present_flag if (readBoolean()) { // aspect_ratio_info_present_flag var aspectRatioIdc = readUByte(); switch (aspectRatioIdc) { case 1: pixelRatio = [1, 1]; break; case 2: pixelRatio = [12, 11]; break; case 3: pixelRatio = [10, 11]; break; case 4: pixelRatio = [16, 11]; break; case 5: pixelRatio = [40, 33]; break; case 6: pixelRatio = [24, 11]; break; case 7: pixelRatio = [20, 11]; break; case 8: pixelRatio = [32, 11]; break; case 9: pixelRatio = [80, 33]; break; case 10: pixelRatio = [18, 11]; break; case 11: pixelRatio = [15, 11]; break; case 12: pixelRatio = [64, 33]; break; case 13: pixelRatio = [160, 99]; break; case 14: pixelRatio = [4, 3]; break; case 15: pixelRatio = [3, 2]; break; case 16: pixelRatio = [2, 1]; break; case 255: { pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()]; break; } } } } return { width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2), height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset), pixelRatio: pixelRatio }; }; _proto.readSliceType = function readSliceType() { // skip NALu type this.readUByte(); // discard first_mb_in_slice this.readUEG(); // return slice_type return this.readUEG(); }; return ExpGolomb; }(); /* harmony default export */ var exp_golomb = (exp_golomb_ExpGolomb); // CONCATENATED MODULE: ./src/demux/sample-aes.js /** * SAMPLE-AES decrypter */ var sample_aes_SampleAesDecrypter = /*#__PURE__*/function () { function SampleAesDecrypter(observer, config, decryptdata, discardEPB) { this.decryptdata = decryptdata; this.discardEPB = discardEPB; this.decrypter = new crypt_decrypter["default"](observer, config, { removePKCS7Padding: false }); } var _proto = SampleAesDecrypter.prototype; _proto.decryptBuffer = function decryptBuffer(encryptedData, callback) { this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback); } // AAC - encrypt all full 16 bytes blocks starting from offset 16 ; _proto.decryptAacSample = function decryptAacSample(samples, sampleIndex, callback, sync) { var curUnit = samples[sampleIndex].unit; var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); var localthis = this; this.decryptBuffer(encryptedBuffer, function (decryptedData) { decryptedData = new Uint8Array(decryptedData); curUnit.set(decryptedData, 16); if (!sync) { localthis.decryptAacSamples(samples, sampleIndex + 1, callback); } }); }; _proto.decryptAacSamples = function decryptAacSamples(samples, sampleIndex, callback) { for (;; sampleIndex++) { if (sampleIndex >= samples.length) { callback(); return; } if (samples[sampleIndex].unit.length < 32) { continue; } var sync = this.decrypter.isSync(); this.decryptAacSample(samples, sampleIndex, callback, sync); if (!sync) { return; } } } // AVC - encrypt one 16 bytes block out of ten, starting from offset 32 ; _proto.getAvcEncryptedData = function getAvcEncryptedData(decodedData) { var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; var encryptedData = new Int8Array(encryptedDataLen); var outputPos = 0; for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) { encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); } return encryptedData; }; _proto.getAvcDecryptedUnit = function getAvcDecryptedUnit(decodedData, decryptedData) { decryptedData = new Uint8Array(decryptedData); var inputPos = 0; for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) { decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos); } return decodedData; }; _proto.decryptAvcSample = function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync) { var decodedData = this.discardEPB(curUnit.data); var encryptedData = this.getAvcEncryptedData(decodedData); var localthis = this; this.decryptBuffer(encryptedData.buffer, function (decryptedData) { curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData); if (!sync) { localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); } }); }; _proto.decryptAvcSamples = function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) { for (;; sampleIndex++, unitIndex = 0) { if (sampleIndex >= samples.length) { callback(); return; } var curUnits = samples[sampleIndex].units; for (;; unitIndex++) { if (unitIndex >= curUnits.length) { break; } var curUnit = curUnits[unitIndex]; if (curUnit.data.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) { continue; } var sync = this.decrypter.isSync(); this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync); if (!sync) { return; } } } }; return SampleAesDecrypter; }(); /* harmony default export */ var sample_aes = (sample_aes_SampleAesDecrypter); // CONCATENATED MODULE: ./src/demux/tsdemuxer.js /** * highly optimized TS demuxer: * parse PAT, PMT * extract PES packet from audio and video PIDs * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet * trigger the remuxer upon parsing completion * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource. * it also controls the remuxing process : * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state. */ // import Hex from '../utils/hex'; // We are using fixed track IDs for driving the MP4 remuxer // instead of following the TS PIDs. // There is no reason not to do this and some browsers/SourceBuffer-demuxers // may not like if there are TrackID "switches" // See https://github.com/video-dev/hls.js/issues/1331 // Here we are mapping our internal track types to constant MP4 track IDs // With MSE currently one can only have one track of each, and we are muxing // whatever video/audio rendition in them. var RemuxerTrackIdConfig = { video: 1, audio: 2, id3: 3, text: 4 }; var tsdemuxer_TSDemuxer = /*#__PURE__*/function () { function TSDemuxer(observer, remuxer, config, typeSupported) { this.observer = observer; this.config = config; this.typeSupported = typeSupported; this.remuxer = remuxer; this.sampleAes = null; this.pmtUnknownTypes = {}; } var _proto = TSDemuxer.prototype; _proto.setDecryptData = function setDecryptData(decryptdata) { if (decryptdata != null && decryptdata.key != null && decryptdata.method === 'SAMPLE-AES') { this.sampleAes = new sample_aes(this.observer, this.config, decryptdata, this.discardEPB); } else { this.sampleAes = null; } }; TSDemuxer.probe = function probe(data) { var syncOffset = TSDemuxer._syncOffset(data); if (syncOffset < 0) { return false; } else { if (syncOffset) { logger["logger"].warn("MPEG2-TS detected but first sync word found @ offset " + syncOffset + ", junk ahead ?"); } return true; } }; TSDemuxer._syncOffset = function _syncOffset(data) { // scan 1000 first bytes var scanwindow = Math.min(1000, data.length - 3 * 188); var i = 0; while (i < scanwindow) { // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47 if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) { return i; } else { i++; } } return -1; } /** * Creates a track model internal to demuxer used to drive remuxing input * * @param {string} type 'audio' | 'video' | 'id3' | 'text' * @param {number} duration * @return {object} TSDemuxer's internal track model */ ; TSDemuxer.createTrack = function createTrack(type, duration) { return { container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined, type: type, id: RemuxerTrackIdConfig[type], pid: -1, inputTimeScale: 90000, sequenceNumber: 0, samples: [], dropped: type === 'video' ? 0 : undefined, isAAC: type === 'audio' ? true : undefined, duration: type === 'audio' ? duration : undefined }; } /** * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start) * Resets all internal track instances of the demuxer. * * @override Implements generic demuxing/remuxing interface (see DemuxerInline) * @param {object} initSegment * @param {string} audioCodec * @param {string} videoCodec * @param {number} duration (in TS timescale = 90kHz) */ ; _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { this.pmtParsed = false; this._pmtId = -1; this.pmtUnknownTypes = {}; this._avcTrack = TSDemuxer.createTrack('video', duration); this._audioTrack = TSDemuxer.createTrack('audio', duration); this._id3Track = TSDemuxer.createTrack('id3', duration); this._txtTrack = TSDemuxer.createTrack('text', duration); // flush any partial content this.aacOverFlow = null; this.aacLastPTS = null; this.avcSample = null; this.audioCodec = audioCodec; this.videoCodec = videoCodec; this._duration = duration; } /** * * @override */ ; _proto.resetTimeStamp = function resetTimeStamp() {} // feed incoming data to the front of the parsing pipeline ; _proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { var start, len = data.length, stt, pid, atf, offset, pes, unknownPIDs = false; this.pmtUnknownTypes = {}; this.contiguous = contiguous; var pmtParsed = this.pmtParsed, avcTrack = this._avcTrack, audioTrack = this._audioTrack, id3Track = this._id3Track, avcId = avcTrack.pid, audioId = audioTrack.pid, id3Id = id3Track.pid, pmtId = this._pmtId, avcData = avcTrack.pesData, audioData = audioTrack.pesData, id3Data = id3Track.pesData, parsePAT = this._parsePAT, parsePMT = this._parsePMT.bind(this), parsePES = this._parsePES, parseAVCPES = this._parseAVCPES.bind(this), parseAACPES = this._parseAACPES.bind(this), parseMPEGPES = this._parseMPEGPES.bind(this), parseID3PES = this._parseID3PES.bind(this); var syncOffset = TSDemuxer._syncOffset(data); // don't parse last TS packet if incomplete len -= (len + syncOffset) % 188; // loop through TS packets for (start = syncOffset; start < len; start += 188) { if (data[start] === 0x47) { stt = !!(data[start + 1] & 0x40); // pid is a 13-bit field starting at the last bit of TS[1] pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2]; atf = (data[start + 3] & 0x30) >> 4; // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. if (atf > 1) { offset = start + 5 + data[start + 4]; // continue if there is only adaptation field if (offset === start + 188) { continue; } } else { offset = start + 4; } switch (pid) { case avcId: if (stt) { if (avcData && (pes = parsePES(avcData))) { parseAVCPES(pes, false); } avcData = { data: [], size: 0 }; } if (avcData) { avcData.data.push(data.subarray(offset, start + 188)); avcData.size += start + 188 - offset; } break; case audioId: if (stt) { if (audioData && (pes = parsePES(audioData))) { if (audioTrack.isAAC) { parseAACPES(pes); } else { parseMPEGPES(pes); } } audioData = { data: [], size: 0 }; } if (audioData) { audioData.data.push(data.subarray(offset, start + 188)); audioData.size += start + 188 - offset; } break; case id3Id: if (stt) { if (id3Data && (pes = parsePES(id3Data))) { parseID3PES(pes); } id3Data = { data: [], size: 0 }; } if (id3Data) { id3Data.data.push(data.subarray(offset, start + 188)); id3Data.size += start + 188 - offset; } break; case 0: if (stt) { offset += data[offset] + 1; } pmtId = this._pmtId = parsePAT(data, offset); break; case pmtId: if (stt) { offset += data[offset] + 1; } var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null); // only update track id if track PID found while parsing PMT // this is to avoid resetting the PID to -1 in case // track PID transiently disappears from the stream // this could happen in case of transient missing audio samples for example // NOTE this is only the PID of the track as found in TS, // but we are not using this for MP4 track IDs. avcId = parsedPIDs.avc; if (avcId > 0) { avcTrack.pid = avcId; } audioId = parsedPIDs.audio; if (audioId > 0) { audioTrack.pid = audioId; audioTrack.isAAC = parsedPIDs.isAAC; } id3Id = parsedPIDs.id3; if (id3Id > 0) { id3Track.pid = id3Id; } if (unknownPIDs && !pmtParsed) { logger["logger"].log('reparse from beginning'); unknownPIDs = false; // we set it to -188, the += 188 in the for loop will reset start to 0 start = syncOffset - 188; } pmtParsed = this.pmtParsed = true; break; case 17: case 0x1fff: break; default: unknownPIDs = true; break; } } else { this.observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' }); } } // try to parse last PES packets if (avcData && (pes = parsePES(avcData))) { parseAVCPES(pes, true); avcTrack.pesData = null; } else { // either avcData null or PES truncated, keep it for next frag parsing avcTrack.pesData = avcData; } if (audioData && (pes = parsePES(audioData))) { if (audioTrack.isAAC) { parseAACPES(pes); } else { parseMPEGPES(pes); } audioTrack.pesData = null; } else { if (audioData && audioData.size) { logger["logger"].log('last AAC PES packet truncated,might overlap between fragments'); } // either audioData null or PES truncated, keep it for next frag parsing audioTrack.pesData = audioData; } if (id3Data && (pes = parsePES(id3Data))) { parseID3PES(pes); id3Track.pesData = null; } else { // either id3Data null or PES truncated, keep it for next frag parsing id3Track.pesData = id3Data; } if (this.sampleAes == null) { this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset); } else { this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset); } }; _proto.decryptAndRemux = function decryptAndRemux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { if (audioTrack.samples && audioTrack.isAAC) { var localthis = this; this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () { localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); }); } else { this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); } }; _proto.decryptAndRemuxAvc = function decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { if (videoTrack.samples) { var localthis = this; this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () { localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); }); } else { this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); } }; _proto.destroy = function destroy() { this._initPTS = this._initDTS = undefined; this._duration = 0; }; _proto._parsePAT = function _parsePAT(data, offset) { // skip the PSI header and parse the first PMT entry return (data[offset + 10] & 0x1F) << 8 | data[offset + 11]; // logger.log('PMT PID:' + this._pmtId); }; _proto._trackUnknownPmt = function _trackUnknownPmt(type, logLevel, message) { // Only log unknown and unsupported stream types once per append or stream (by resetting this.pmtUnknownTypes) // For more information on elementary stream types see: // https://en.wikipedia.org/wiki/Program-specific_information#Elementary_stream_types var result = this.pmtUnknownTypes[type] || 0; if (result === 0) { this.pmtUnknownTypes[type] = 0; logLevel.call(logger["logger"], message); } this.pmtUnknownTypes[type]++; return result; }; _proto._parsePMT = function _parsePMT(data, offset, mpegSupported, isSampleAes) { var sectionLength, tableEnd, programInfoLength, pid, result = { audio: -1, avc: -1, id3: -1, isAAC: true }; sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; tableEnd = offset + 3 + sectionLength - 4; // to determine where the table is, we have to figure out how // long the program info descriptors are programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; // advance the offset to the first entry in the mapping table offset += 12 + programInfoLength; while (offset < tableEnd) { pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2]; switch (data[offset]) { case 0xcf: // SAMPLE-AES AAC if (!isSampleAes) { this._trackUnknownPmt(data[offset], logger["logger"].warn, 'ADTS AAC with AES-128-CBC frame encryption found in unencrypted stream'); break; } /* falls through */ // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) case 0x0f: // logger.log('AAC PID:' + pid); if (result.audio === -1) { result.audio = pid; } break; // Packetized metadata (ID3) case 0x15: // logger.log('ID3 PID:' + pid); if (result.id3 === -1) { result.id3 = pid; } break; case 0xdb: // SAMPLE-AES AVC if (!isSampleAes) { this._trackUnknownPmt(data[offset], logger["logger"].warn, 'H.264 with AES-128-CBC slice encryption found in unencrypted stream'); break; } /* falls through */ // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) case 0x1b: // logger.log('AVC PID:' + pid); if (result.avc === -1) { result.avc = pid; } break; // ISO/IEC 11172-3 (MPEG-1 audio) // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) case 0x03: case 0x04: // logger.log('MPEG PID:' + pid); if (!mpegSupported) { this._trackUnknownPmt(data[offset], logger["logger"].warn, 'MPEG audio found, not supported in this browser'); } else if (result.audio === -1) { result.audio = pid; result.isAAC = false; } break; case 0x24: this._trackUnknownPmt(data[offset], logger["logger"].warn, 'Unsupported HEVC stream type found'); break; default: this._trackUnknownPmt(data[offset], logger["logger"].log, 'Unknown stream type:' + data[offset]); break; } // move to the next table entry // skip past the elementary stream descriptors, if present offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5; } return result; }; _proto._parsePES = function _parsePES(stream) { var i = 0, frag, pesFlags, pesPrefix, pesLen, pesHdrLen, pesData, pesPts, pesDts, payloadStartOffset, data = stream.data; // safety check if (!stream || stream.size === 0) { return null; } // we might need up to 19 bytes to read PES header // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes // usually only one merge is needed (and this is rare ...) while (data[0].length < 19 && data.length > 1) { var newData = new Uint8Array(data[0].length + data[1].length); newData.set(data[0]); newData.set(data[1], data[0].length); data[0] = newData; data.splice(1, 1); } // retrieve PTS/DTS from first fragment frag = data[0]; pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; if (pesPrefix === 1) { pesLen = (frag[4] << 8) + frag[5]; // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated // minus 6 : PES header size if (pesLen && pesLen > stream.size - 6) { return null; } pesFlags = frag[7]; if (pesFlags & 0xC0) { /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html as PTS / DTS is 33 bit we cannot use bitwise operator in JS, as Bitwise operators treat their operands as a sequence of 32 bits */ pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29 (frag[10] & 0xFF) * 4194304 + // 1 << 22 (frag[11] & 0xFE) * 16384 + // 1 << 14 (frag[12] & 0xFF) * 128 + // 1 << 7 (frag[13] & 0xFE) / 2; if (pesFlags & 0x40) { pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29 (frag[15] & 0xFF) * 4194304 + // 1 << 22 (frag[16] & 0xFE) * 16384 + // 1 << 14 (frag[17] & 0xFF) * 128 + // 1 << 7 (frag[18] & 0xFE) / 2; if (pesPts - pesDts > 60 * 90000) { logger["logger"].warn(Math.round((pesPts - pesDts) / 90000) + "s delta between PTS and DTS, align them"); pesPts = pesDts; } } else { pesDts = pesPts; } } pesHdrLen = frag[8]; // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension payloadStartOffset = pesHdrLen + 9; if (stream.size <= payloadStartOffset) { return null; } stream.size -= payloadStartOffset; // reassemble PES packet pesData = new Uint8Array(stream.size); for (var j = 0, dataLen = data.length; j < dataLen; j++) { frag = data[j]; var len = frag.byteLength; if (payloadStartOffset) { if (payloadStartOffset > len) { // trim full frag if PES header bigger than frag payloadStartOffset -= len; continue; } else { // trim partial frag if PES header smaller than frag frag = frag.subarray(payloadStartOffset); len -= payloadStartOffset; payloadStartOffset = 0; } } pesData.set(frag, i); i += len; } if (pesLen) { // payload size : remove PES header + PES extension pesLen -= pesHdrLen + 3; } return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen }; } else { return null; } }; _proto.pushAccesUnit = function pushAccesUnit(avcSample, avcTrack) { if (avcSample.units.length && avcSample.frame) { var samples = avcTrack.samples; var nbSamples = samples.length; // if sample does not have PTS/DTS, patch with last sample PTS/DTS if (isNaN(avcSample.pts)) { if (nbSamples) { var lastSample = samples[nbSamples - 1]; avcSample.pts = lastSample.pts; avcSample.dts = lastSample.dts; } else { // dropping samples, no timestamp found avcTrack.dropped++; return; } } // only push AVC sample if starting with a keyframe is not mandatory OR // if keyframe already found in this fragment OR // keyframe found in last fragment (track.sps) AND // samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous if (!this.config.forceKeyFrameOnDiscontinuity || avcSample.key === true || avcTrack.sps && (nbSamples || this.contiguous)) { avcSample.id = nbSamples; samples.push(avcSample); } else { // dropped samples, track it avcTrack.dropped++; } } if (avcSample.debug.length) { logger["logger"].log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug); } }; _proto._parseAVCPES = function _parseAVCPES(pes, last) { var _this = this; // logger.log('parse new PES'); var track = this._avcTrack, units = this._parseAVCNALu(pes.data), debug = false, expGolombDecoder, avcSample = this.avcSample, push, spsfound = false, i, pushAccesUnit = this.pushAccesUnit.bind(this), createAVCSample = function createAVCSample(key, pts, dts, debug) { return { key: key, pts: pts, dts: dts, units: [], debug: debug }; }; // free pes.data to save up some memory pes.data = null; // if new NAL units found and last sample still there, let's push ... // this helps parsing streams with missing AUD (only do this if AUD never found) if (avcSample && units.length && !track.audFound) { pushAccesUnit(avcSample, track); avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, ''); } units.forEach(function (unit) { switch (unit.type) { // NDR case 1: push = true; if (!avcSample) { avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, ''); } if (debug) { avcSample.debug += 'NDR '; } avcSample.frame = true; var data = unit.data; // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...) if (spsfound && data.length > 4) { // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR var sliceType = new exp_golomb(data).readSliceType(); // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. // I slice: A slice that is not an SI slice that is decoded using intra prediction only. // if (sliceType === 2 || sliceType === 7) { if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { avcSample.key = true; } } break; // IDR case 5: push = true; // handle PES not starting with AUD if (!avcSample) { avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, ''); } if (debug) { avcSample.debug += 'IDR '; } avcSample.key = true; avcSample.frame = true; break; // SEI case 6: push = true; if (debug && avcSample) { avcSample.debug += 'SEI '; } expGolombDecoder = new exp_golomb(_this.discardEPB(unit.data)); // skip frameType expGolombDecoder.readUByte(); var payloadType = 0; var payloadSize = 0; var endOfCaptions = false; var b = 0; while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) { payloadType = 0; do { b = expGolombDecoder.readUByte(); payloadType += b; } while (b === 0xFF); // Parse payload size. payloadSize = 0; do { b = expGolombDecoder.readUByte(); payloadSize += b; } while (b === 0xFF); // TODO: there can be more than one payload in an SEI packet... // TODO: need to read type and size in a while loop to get them all if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) { endOfCaptions = true; var countryCode = expGolombDecoder.readUByte(); if (countryCode === 181) { var providerCode = expGolombDecoder.readUShort(); if (providerCode === 49) { var userStructure = expGolombDecoder.readUInt(); if (userStructure === 0x47413934) { var userDataType = expGolombDecoder.readUByte(); // Raw CEA-608 bytes wrapped in CEA-708 packet if (userDataType === 3) { var firstByte = expGolombDecoder.readUByte(); var secondByte = expGolombDecoder.readUByte(); var totalCCs = 31 & firstByte; var byteArray = [firstByte, secondByte]; for (i = 0; i < totalCCs; i++) { // 3 bytes per CC byteArray.push(expGolombDecoder.readUByte()); byteArray.push(expGolombDecoder.readUByte()); byteArray.push(expGolombDecoder.readUByte()); } _this._insertSampleInOrder(_this._txtTrack.samples, { type: 3, pts: pes.pts, bytes: byteArray }); } } } } } else if (payloadType === 5 && expGolombDecoder.bytesAvailable !== 0) { endOfCaptions = true; if (payloadSize > 16) { var uuidStrArray = []; for (i = 0; i < 16; i++) { uuidStrArray.push(expGolombDecoder.readUByte().toString(16)); if (i === 3 || i === 5 || i === 7 || i === 9) { uuidStrArray.push('-'); } } var length = payloadSize - 16; var userDataPayloadBytes = new Uint8Array(length); for (i = 0; i < length; i++) { userDataPayloadBytes[i] = expGolombDecoder.readUByte(); } _this._insertSampleInOrder(_this._txtTrack.samples, { pts: pes.pts, payloadType: payloadType, uuid: uuidStrArray.join(''), userDataBytes: userDataPayloadBytes, userData: Object(id3["utf8ArrayToStr"])(userDataPayloadBytes.buffer) }); } } else if (payloadSize < expGolombDecoder.bytesAvailable) { for (i = 0; i < payloadSize; i++) { expGolombDecoder.readUByte(); } } } break; // SPS case 7: push = true; spsfound = true; if (debug && avcSample) { avcSample.debug += 'SPS '; } if (!track.sps) { expGolombDecoder = new exp_golomb(unit.data); var config = expGolombDecoder.readSPS(); track.width = config.width; track.height = config.height; track.pixelRatio = config.pixelRatio; track.sps = [unit.data]; track.duration = _this._duration; var codecarray = unit.data.subarray(1, 4); var codecstring = 'avc1.'; for (i = 0; i < 3; i++) { var h = codecarray[i].toString(16); if (h.length < 2) { h = '0' + h; } codecstring += h; } track.codec = codecstring; } break; // PPS case 8: push = true; if (debug && avcSample) { avcSample.debug += 'PPS '; } if (!track.pps) { track.pps = [unit.data]; } break; // AUD case 9: push = false; track.audFound = true; if (avcSample) { pushAccesUnit(avcSample, track); } avcSample = _this.avcSample = createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : ''); break; // Filler Data case 12: push = false; break; default: push = false; if (avcSample) { avcSample.debug += 'unknown NAL ' + unit.type + ' '; } break; } if (avcSample && push) { var _units = avcSample.units; _units.push(unit); } }); // if last PES packet, push samples if (last && avcSample) { pushAccesUnit(avcSample, track); this.avcSample = null; } }; _proto._insertSampleInOrder = function _insertSampleInOrder(arr, data) { var len = arr.length; if (len > 0) { if (data.pts >= arr[len - 1].pts) { arr.push(data); } else { for (var pos = len - 1; pos >= 0; pos--) { if (data.pts < arr[pos].pts) { arr.splice(pos, 0, data); break; } } } } else { arr.push(data); } }; _proto._getLastNalUnit = function _getLastNalUnit() { var avcSample = this.avcSample, lastUnit; // try to fallback to previous sample if current one is empty if (!avcSample || avcSample.units.length === 0) { var track = this._avcTrack, samples = track.samples; avcSample = samples[samples.length - 1]; } if (avcSample) { var units = avcSample.units; lastUnit = units[units.length - 1]; } return lastUnit; }; _proto._parseAVCNALu = function _parseAVCNALu(array) { var i = 0, len = array.byteLength, value, overflow, track = this._avcTrack, state = track.naluState || 0, lastState = state; var units = [], unit, unitType, lastUnitStart = -1, lastUnitType; // logger.log('PES:' + Hex.hexDump(array)); if (state === -1) { // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet lastUnitStart = 0; // NALu type is value read from offset 0 lastUnitType = array[0] & 0x1f; state = 0; i = 1; } while (i < len) { value = array[i++]; // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case if (!state) { state = value ? 0 : 1; continue; } if (state === 1) { state = value ? 0 : 2; continue; } // here we have state either equal to 2 or 3 if (!value) { state = 3; } else if (value === 1) { if (lastUnitStart >= 0) { unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType }; // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength); units.push(unit); } else { // lastUnitStart is undefined => this is the first start code found in this PES packet // first check if start code delimiter is overlapping between 2 PES packets, // ie it started in last packet (lastState not zero) // and ended at the beginning of this PES packet (i <= 4 - lastState) var lastUnit = this._getLastNalUnit(); if (lastUnit) { if (lastState && i <= 4 - lastState) { // start delimiter overlapping between PES packets // strip start delimiter bytes from the end of last NAL unit // check if lastUnit had a state different from zero if (lastUnit.state) { // strip last bytes lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); } } // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. overflow = i - state - 1; if (overflow > 0) { // logger.log('first NALU found with overflow:' + overflow); var tmp = new Uint8Array(lastUnit.data.byteLength + overflow); tmp.set(lastUnit.data, 0); tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength); lastUnit.data = tmp; } } } // check if we can read unit type if (i < len) { unitType = array[i] & 0x1f; // logger.log('find NALU @ offset:' + i + ',type:' + unitType); lastUnitStart = i; lastUnitType = unitType; state = 0; } else { // not enough byte to read unit type. let's read it on next PES parsing state = -1; } } else { state = 0; } } if (lastUnitStart >= 0 && state >= 0) { unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state }; units.push(unit); // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state); } // no NALu found if (units.length === 0) { // append pes.data to previous NAL unit var _lastUnit = this._getLastNalUnit(); if (_lastUnit) { var _tmp = new Uint8Array(_lastUnit.data.byteLength + array.byteLength); _tmp.set(_lastUnit.data, 0); _tmp.set(array, _lastUnit.data.byteLength); _lastUnit.data = _tmp; } } track.naluState = state; return units; } /** * remove Emulation Prevention bytes from a RBSP */ ; _proto.discardEPB = function discardEPB(data) { var length = data.byteLength, EPBPositions = [], i = 1, newLength, newData; // Find all `Emulation Prevention Bytes` while (i < length - 2) { if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { EPBPositions.push(i + 2); i += 2; } else { i++; } } // If no Emulation Prevention Bytes were found just return the original // array if (EPBPositions.length === 0) { return data; } // Create a new array to hold the NAL unit data newLength = length - EPBPositions.length; newData = new Uint8Array(newLength); var sourceIndex = 0; for (i = 0; i < newLength; sourceIndex++, i++) { if (sourceIndex === EPBPositions[0]) { // Skip this byte sourceIndex++; // Remove this position index EPBPositions.shift(); } newData[i] = data[sourceIndex]; } return newData; }; _proto._parseAACPES = function _parseAACPES(pes) { var track = this._audioTrack, data = pes.data, pts = pes.pts, startOffset = 0, aacOverFlow = this.aacOverFlow, aacLastPTS = this.aacLastPTS, frameDuration, frameIndex, offset, stamp, len; if (aacOverFlow) { var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength); tmp.set(aacOverFlow, 0); tmp.set(data, aacOverFlow.byteLength); // logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`); data = tmp; } // look for ADTS header (0xFFFx) for (offset = startOffset, len = data.length; offset < len - 1; offset++) { if (isHeader(data, offset)) { break; } } // if ADTS header does not start straight from the beginning of the PES payload, raise an error if (offset) { var reason, fatal; if (offset < len - 1) { reason = "AAC PES did not start with ADTS header,offset:" + offset; fatal = false; } else { reason = 'no ADTS header found in AAC PES'; fatal = true; } logger["logger"].warn("parsing error:" + reason); this.observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_PARSING_ERROR, fatal: fatal, reason: reason }); if (fatal) { return; } } initTrackConfig(track, this.observer, data, offset, this.audioCodec); frameIndex = 0; frameDuration = getFrameDuration(track.samplerate); // if last AAC frame is overflowing, we should ensure timestamps are contiguous: // first sample PTS should be equal to last sample PTS + frameDuration if (aacOverFlow && aacLastPTS) { var newPTS = aacLastPTS + frameDuration; if (Math.abs(newPTS - pts) > 1) { logger["logger"].log("AAC: align PTS for overlapping frames by " + Math.round((newPTS - pts) / 90)); pts = newPTS; } } // scan for aac samples while (offset < len) { if (isHeader(data, offset)) { if (offset + 5 < len) { var frame = appendFrame(track, data, offset, pts, frameIndex); if (frame) { offset += frame.length; stamp = frame.sample.pts; frameIndex++; continue; } } // We are at an ADTS header, but do not have enough data for a frame // Remaining data will be added to aacOverFlow break; } else { // nothing found, keep looking offset++; } } if (offset < len) { aacOverFlow = data.subarray(offset, len); // logger.log(`AAC: overflow detected:${len-offset}`); } else { aacOverFlow = null; } this.aacOverFlow = aacOverFlow; this.aacLastPTS = stamp; }; _proto._parseMPEGPES = function _parseMPEGPES(pes) { var data = pes.data; var length = data.length; var frameIndex = 0; var offset = 0; var pts = pes.pts; while (offset < length) { if (mpegaudio.isHeader(data, offset)) { var frame = mpegaudio.appendFrame(this._audioTrack, data, offset, pts, frameIndex); if (frame) { offset += frame.length; frameIndex++; } else { // logger.log('Unable to parse Mpeg audio frame'); break; } } else { // nothing found, keep looking offset++; } } }; _proto._parseID3PES = function _parseID3PES(pes) { this._id3Track.samples.push(pes); }; return TSDemuxer; }(); /* harmony default export */ var tsdemuxer = (tsdemuxer_TSDemuxer); // CONCATENATED MODULE: ./src/demux/mp3demuxer.js /** * MP3 demuxer */ var mp3demuxer_MP3Demuxer = /*#__PURE__*/function () { function MP3Demuxer(observer, remuxer, config) { this.observer = observer; this.config = config; this.remuxer = remuxer; } var _proto = MP3Demuxer.prototype; _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { this._audioTrack = { container: 'audio/mpeg', type: 'audio', id: -1, sequenceNumber: 0, isAAC: false, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 }; }; _proto.resetTimeStamp = function resetTimeStamp() {}; MP3Demuxer.probe = function probe(data) { // check if data contains ID3 timestamp and MPEG sync word var offset, length; var id3Data = id3["default"].getID3Data(data, 0); if (id3Data && id3["default"].getTimeStamp(id3Data) !== undefined) { // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) // More info http://www.mp3-tech.org/programmer/frame_header.html for (offset = id3Data.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { if (mpegaudio.probe(data, offset)) { logger["logger"].log('MPEG Audio sync word found !'); return true; } } } return false; } // feed incoming data to the front of the parsing pipeline ; _proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { var id3Data = id3["default"].getID3Data(data, 0) || []; var timestamp = id3["default"].getTimeStamp(id3Data); var pts = timestamp !== undefined ? 90 * timestamp : timeOffset * 90000; var offset = id3Data.length; var length = data.length; var frameIndex = 0, stamp = 0; var track = this._audioTrack; var id3Samples = [{ pts: pts, dts: pts, data: id3Data }]; while (offset < length) { if (mpegaudio.isHeader(data, offset)) { var frame = mpegaudio.appendFrame(track, data, offset, pts, frameIndex); if (frame) { offset += frame.length; stamp = frame.sample.pts; frameIndex++; } else { // logger.log('Unable to parse Mpeg audio frame'); break; } } else if (id3["default"].isHeader(data, offset)) { id3Data = id3["default"].getID3Data(data, offset); id3Samples.push({ pts: stamp, dts: stamp, data: id3Data }); offset += id3Data.length; } else { // nothing found, keep looking offset++; } } this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset); }; _proto.destroy = function destroy() {}; return MP3Demuxer; }(); /* harmony default export */ var mp3demuxer = (mp3demuxer_MP3Demuxer); // CONCATENATED MODULE: ./src/remux/aac-helper.js /** * AAC helper */ var AAC = /*#__PURE__*/function () { function AAC() {} AAC.getSilentFrame = function getSilentFrame(codec, channelCount) { switch (codec) { case 'mp4a.40.2': if (channelCount === 1) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); } else if (channelCount === 2) { return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); } else if (channelCount === 3) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); } else if (channelCount === 4) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); } else if (channelCount === 5) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); } else if (channelCount === 6) { return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]); } break; // handle HE-AAC below (mp4a.40.5 / mp4a.40.29) default: if (channelCount === 1) { // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); } else if (channelCount === 2) { // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); } else if (channelCount === 3) { // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); } break; } return null; }; return AAC; }(); /* harmony default export */ var aac_helper = (AAC); // CONCATENATED MODULE: ./src/remux/mp4-generator.js /** * Generate MP4 Box */ var UINT32_MAX = Math.pow(2, 32) - 1; var MP4 = /*#__PURE__*/function () { function MP4() {} MP4.init = function init() { MP4.types = { avc1: [], // codingname avcC: [], btrt: [], dinf: [], dref: [], esds: [], ftyp: [], hdlr: [], mdat: [], mdhd: [], mdia: [], mfhd: [], minf: [], moof: [], moov: [], mp4a: [], '.mp3': [], mvex: [], mvhd: [], pasp: [], sdtp: [], stbl: [], stco: [], stsc: [], stsd: [], stsz: [], stts: [], tfdt: [], tfhd: [], traf: [], trak: [], trun: [], trex: [], tkhd: [], vmhd: [], smhd: [] }; var i; for (i in MP4.types) { if (MP4.types.hasOwnProperty(i)) { MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; } } var videoHdlr = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' ]); var audioHdlr = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // pre_defined 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun' 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' ]); MP4.HDLR_TYPES = { 'video': videoHdlr, 'audio': audioHdlr }; var dref = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x01, // entry_count 0x00, 0x00, 0x00, 0x0c, // entry_size 0x75, 0x72, 0x6c, 0x20, // 'url' type 0x00, // version 0 0x00, 0x00, 0x01 // entry_flags ]); var stco = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00 // entry_count ]); MP4.STTS = MP4.STSC = MP4.STCO = stco; MP4.STSZ = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, // sample_size 0x00, 0x00, 0x00, 0x00 // sample_count ]); MP4.VMHD = new Uint8Array([0x00, // version 0x00, 0x00, 0x01, // flags 0x00, 0x00, // graphicsmode 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor ]); MP4.SMHD = new Uint8Array([0x00, // version 0x00, 0x00, 0x00, // flags 0x00, 0x00, // balance 0x00, 0x00 // reserved ]); MP4.STSD = new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x01]); // entry_count var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 var minorVersion = new Uint8Array([0, 0, 0, 1]); MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); }; MP4.box = function box(type) { var payload = Array.prototype.slice.call(arguments, 1), size = 8, i = payload.length, len = i, result; // calculate the total size we need to allocate while (i--) { size += payload[i].byteLength; } result = new Uint8Array(size); result[0] = size >> 24 & 0xff; result[1] = size >> 16 & 0xff; result[2] = size >> 8 & 0xff; result[3] = size & 0xff; result.set(type, 4); // copy the payload into the result for (i = 0, size = 8; i < len; i++) { // copy payload[i] array @ offset size result.set(payload[i], size); size += payload[i].byteLength; } return result; }; MP4.hdlr = function hdlr(type) { return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); }; MP4.mdat = function mdat(data) { return MP4.box(MP4.types.mdat, data); }; MP4.mdhd = function mdhd(timescale, duration) { duration *= timescale; var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); return MP4.box(MP4.types.mdhd, new Uint8Array([0x01, // version 1 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x55, 0xc4, // 'und' language (undetermined) 0x00, 0x00])); }; MP4.mdia = function mdia(track) { return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); }; MP4.mfhd = function mfhd(sequenceNumber) { return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF // sequence_number ])); }; MP4.minf = function minf(track) { if (track.type === 'audio') { return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); } else { return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); } }; MP4.moof = function moof(sn, baseMediaDecodeTime, track) { return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); } /** * @param tracks... (optional) {array} the tracks associated with this movie */ ; MP4.moov = function moov(tracks) { var i = tracks.length, boxes = []; while (i--) { boxes[i] = MP4.trak(tracks[i]); } return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks))); }; MP4.mvex = function mvex(tracks) { var i = tracks.length, boxes = []; while (i--) { boxes[i] = MP4.trex(tracks[i]); } return MP4.box.apply(null, [MP4.types.mvex].concat(boxes)); }; MP4.mvhd = function mvhd(timescale, duration) { duration *= timescale; var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); var bytes = new Uint8Array([0x01, // version 1 0x00, 0x00, 0x00, // flags 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x01, 0x00, 0x00, // 1.0 rate 0x01, 0x00, // 1.0 volume 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined 0xff, 0xff, 0xff, 0xff // next_track_ID ]); return MP4.box(MP4.types.mvhd, bytes); }; MP4.sdtp = function sdtp(track) { var samples = track.samples || [], bytes = new Uint8Array(4 + samples.length), flags, i; // leave the full box header (4 bytes) all zero // write the sample table for (i = 0; i < samples.length; i++) { flags = samples[i].flags; bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy; } return MP4.box(MP4.types.sdtp, bytes); }; MP4.stbl = function stbl(track) { return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO)); }; MP4.avc1 = function avc1(track) { var sps = [], pps = [], i, data, len; // assemble the SPSs for (i = 0; i < track.sps.length; i++) { data = track.sps[i]; len = data.byteLength; sps.push(len >>> 8 & 0xFF); sps.push(len & 0xFF); // SPS sps = sps.concat(Array.prototype.slice.call(data)); } // assemble the PPSs for (i = 0; i < track.pps.length; i++) { data = track.pps[i]; len = data.byteLength; pps.push(len >>> 8 & 0xFF); pps.push(len & 0xFF); pps = pps.concat(Array.prototype.slice.call(data)); } var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version sps[3], // profile sps[4], // profile compat sps[5], // level 0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes 0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets ].concat(sps).concat([track.pps.length // numOfPictureParameterSets ]).concat(pps))), // "PPS" width = track.width, height = track.height, hSpacing = track.pixelRatio[0], vSpacing = track.pixelRatio[1]; return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // data_reference_index 0x00, 0x00, // pre_defined 0x00, 0x00, // reserved 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined width >> 8 & 0xFF, width & 0xff, // width height >> 8 & 0xFF, height & 0xff, // height 0x00, 0x48, 0x00, 0x00, // horizresolution 0x00, 0x48, 0x00, 0x00, // vertresolution 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // frame_count 0x12, 0x64, 0x61, 0x69, 0x6C, // dailymotion/hls.js 0x79, 0x6D, 0x6F, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x68, 0x6C, 0x73, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname 0x00, 0x18, // depth = 24 0x11, 0x11]), // pre_defined = -1 avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate 0x00, 0x2d, 0xc6, 0xc0])), // avgBitrate MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, // hSpacing hSpacing >> 16 & 0xFF, hSpacing >> 8 & 0xFF, hSpacing & 0xFF, vSpacing >> 24, // vSpacing vSpacing >> 16 & 0xFF, vSpacing >> 8 & 0xFF, vSpacing & 0xFF]))); }; MP4.esds = function esds(track) { var configlen = track.config.length; return new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags 0x03, // descriptor_type 0x17 + configlen, // length 0x00, 0x01, // es_id 0x00, // stream_priority 0x04, // descriptor_type 0x0f + configlen, // length 0x40, // codec : mpeg4_audio 0x15, // stream_type 0x00, 0x00, 0x00, // buffer_size 0x00, 0x00, 0x00, 0x00, // maxBitrate 0x00, 0x00, 0x00, 0x00, // avgBitrate 0x05 // descriptor_type ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor }; MP4.mp4a = function mp4a(track) { var samplerate = track.samplerate; return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // data_reference_index 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, track.channelCount, // channelcount 0x00, 0x10, // sampleSize:16bits 0x00, 0x00, 0x00, 0x00, // reserved2 samplerate >> 8 & 0xFF, samplerate & 0xff, // 0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track))); }; MP4.mp3 = function mp3(track) { var samplerate = track.samplerate; return MP4.box(MP4.types['.mp3'], new Uint8Array([0x00, 0x00, 0x00, // reserved 0x00, 0x00, 0x00, // reserved 0x00, 0x01, // data_reference_index 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, track.channelCount, // channelcount 0x00, 0x10, // sampleSize:16bits 0x00, 0x00, 0x00, 0x00, // reserved2 samplerate >> 8 & 0xFF, samplerate & 0xff, // 0x00, 0x00])); }; MP4.stsd = function stsd(track) { if (track.type === 'audio') { if (!track.isAAC && track.codec === 'mp3') { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track)); } return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); } else { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); } }; MP4.tkhd = function tkhd(track) { var id = track.id, duration = track.duration * track.timescale, width = track.width, height = track.height, upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)), lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); return MP4.box(MP4.types.tkhd, new Uint8Array([0x01, // version 1 0x00, 0x00, 0x07, // flags 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID 0x00, 0x00, 0x00, 0x00, // reserved upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, 0x00, // layer 0x00, 0x00, // alternate_group 0x00, 0x00, // non-audio track volume 0x00, 0x00, // reserved 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height ])); }; MP4.traf = function traf(track, baseMediaDecodeTime) { var sampleDependencyTable = MP4.sdtp(track), id = track.id, upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)), lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF // track_ID ])), MP4.box(MP4.types.tfdt, new Uint8Array([0x01, // version 1 0x00, 0x00, 0x00, // flags upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0XFF, upperWordBaseMediaDecodeTime >> 8 & 0XFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0XFF, lowerWordBaseMediaDecodeTime >> 8 & 0XFF, lowerWordBaseMediaDecodeTime & 0xFF])), MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd 20 + // tfdt 8 + // traf header 16 + // mfhd 8 + // moof header 8), // mdat header sampleDependencyTable); } /** * Generate a track box. * @param track {object} a track definition * @return {Uint8Array} the track box */ ; MP4.trak = function trak(track) { track.duration = track.duration || 0xffffffff; return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); }; MP4.trex = function trex(track) { var id = track.id; return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0 0x00, 0x00, 0x00, // flags id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID 0x00, 0x00, 0x00, 0x01, // default_sample_description_index 0x00, 0x00, 0x00, 0x00, // default_sample_duration 0x00, 0x00, 0x00, 0x00, // default_sample_size 0x00, 0x01, 0x00, 0x01 // default_sample_flags ])); }; MP4.trun = function trun(track, offset) { var samples = track.samples || [], len = samples.length, arraylen = 12 + 16 * len, array = new Uint8Array(arraylen), i, sample, duration, size, flags, cts; offset += 8 + arraylen; array.set([0x00, // version 0 0x00, 0x0f, 0x01, // flags len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset ], 0); for (i = 0; i < len; i++) { sample = samples[i]; duration = sample.duration; size = sample.size; flags = sample.flags; cts = sample.cts; array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset ], 12 + 16 * i); } return MP4.box(MP4.types.trun, array); }; MP4.initSegment = function initSegment(tracks) { if (!MP4.types) { MP4.init(); } var movie = MP4.moov(tracks), result; result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength); result.set(MP4.FTYP); result.set(movie, MP4.FTYP.byteLength); return result; }; return MP4; }(); /* harmony default export */ var mp4_generator = (MP4); // CONCATENATED MODULE: ./src/utils/timescale-conversion.ts var MPEG_TS_CLOCK_FREQ_HZ = 90000; function toTimescaleFromScale(value, destScale, srcScale, round) { if (srcScale === void 0) { srcScale = 1; } if (round === void 0) { round = false; } return toTimescaleFromBase(value, destScale, 1 / srcScale); } function toTimescaleFromBase(value, destScale, srcBase, round) { if (srcBase === void 0) { srcBase = 1; } if (round === void 0) { round = false; } var result = value * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)` return round ? Math.round(result) : result; } function toMsFromMpegTsClock(value, round) { if (round === void 0) { round = false; } return toTimescaleFromBase(value, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round); } function toMpegTsClockFromTimescale(value, srcScale) { if (srcScale === void 0) { srcScale = 1; } return toTimescaleFromBase(value, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale); } // CONCATENATED MODULE: ./src/remux/mp4-remuxer.js /** * fMP4 remuxer */ var MAX_SILENT_FRAME_DURATION_90KHZ = toMpegTsClockFromTimescale(10); var PTS_DTS_SHIFT_TOLERANCE_90KHZ = toMpegTsClockFromTimescale(0.2); var chromeVersion = null; var mp4_remuxer_MP4Remuxer = /*#__PURE__*/function () { function MP4Remuxer(observer, config, typeSupported, vendor) { this.observer = observer; this.config = config; this.typeSupported = typeSupported; this.ISGenerated = false; if (chromeVersion === null) { var result = navigator.userAgent.match(/Chrome\/(\d+)/i); chromeVersion = result ? parseInt(result[1]) : 0; } } var _proto = MP4Remuxer.prototype; _proto.destroy = function destroy() {}; _proto.resetTimeStamp = function resetTimeStamp(defaultTimeStamp) { this._initPTS = this._initDTS = defaultTimeStamp; }; _proto.resetInitSegment = function resetInitSegment() { this.ISGenerated = false; }; _proto.getVideoStartPts = function getVideoStartPts(videoSamples) { var rolloverDetected = false; var startPTS = videoSamples.reduce(function (minPTS, sample) { var delta = sample.pts - minPTS; if (delta < -4294967296) { // 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation rolloverDetected = true; return PTSNormalize(minPTS, sample.pts); } else if (delta > 0) { return minPTS; } else { return sample.pts; } }, videoSamples[0].pts); if (rolloverDetected) { logger["logger"].debug('PTS rollover detected'); } return startPTS; }; _proto.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { // generate Init Segment if needed if (!this.ISGenerated) { this.generateIS(audioTrack, videoTrack, timeOffset); } if (this.ISGenerated) { var nbAudioSamples = audioTrack.samples.length; var nbVideoSamples = videoTrack.samples.length; var audioTimeOffset = timeOffset; var videoTimeOffset = timeOffset; if (nbAudioSamples && nbVideoSamples) { // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS) // if first audio DTS is not aligned with first video DTS then we need to take that into account // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small // drift between audio and video streams var startPTS = this.getVideoStartPts(videoTrack.samples); var tsDelta = PTSNormalize(audioTrack.samples[0].pts, startPTS) - startPTS; var audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale; audioTimeOffset += Math.max(0, audiovideoTimestampDelta); videoTimeOffset += Math.max(0, -audiovideoTimestampDelta); } // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is // calculated in remuxAudio. // logger.log('nb AAC samples:' + audioTrack.samples.length); if (nbAudioSamples) { // if initSegment was generated without video samples, regenerate it again if (!audioTrack.timescale) { logger["logger"].warn('regenerate InitSegment as audio detected'); this.generateIS(audioTrack, videoTrack, timeOffset); } var audioData = this.remuxAudio(audioTrack, audioTimeOffset, contiguous, accurateTimeOffset); // logger.log('nb AVC samples:' + videoTrack.samples.length); if (nbVideoSamples) { var audioTrackLength; if (audioData) { audioTrackLength = audioData.endPTS - audioData.startPTS; } // if initSegment was generated without video samples, regenerate it again if (!videoTrack.timescale) { logger["logger"].warn('regenerate InitSegment as video detected'); this.generateIS(audioTrack, videoTrack, timeOffset); } this.remuxVideo(videoTrack, videoTimeOffset, contiguous, audioTrackLength); } } else { // logger.log('nb AVC samples:' + videoTrack.samples.length); if (nbVideoSamples) { var videoData = this.remuxVideo(videoTrack, videoTimeOffset, contiguous, 0, accurateTimeOffset); if (videoData && audioTrack.codec) { this.remuxEmptyAudio(audioTrack, audioTimeOffset, contiguous, videoData); } } } } // logger.log('nb ID3 samples:' + audioTrack.samples.length); if (id3Track.samples.length) { this.remuxID3(id3Track, timeOffset); } // logger.log('nb ID3 samples:' + audioTrack.samples.length); if (textTrack.samples.length) { this.remuxText(textTrack, timeOffset); } // notify end of parsing this.observer.trigger(events["default"].FRAG_PARSED); }; _proto.generateIS = function generateIS(audioTrack, videoTrack, timeOffset) { var observer = this.observer, audioSamples = audioTrack.samples, videoSamples = videoTrack.samples, typeSupported = this.typeSupported, container = 'audio/mp4', tracks = {}, data = { tracks: tracks }, computePTSDTS = this._initPTS === undefined, initPTS, initDTS; if (computePTSDTS) { initPTS = initDTS = Infinity; } if (audioTrack.config && audioSamples.length) { // let's use audio sampling rate as MP4 time scale. // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC) // using audio sampling rate here helps having an integer MP4 frame duration // this avoids potential rounding issue and AV sync issue audioTrack.timescale = audioTrack.samplerate; logger["logger"].log("audio sampling rate : " + audioTrack.samplerate); if (!audioTrack.isAAC) { if (typeSupported.mpeg) { // Chrome and Safari container = 'audio/mpeg'; audioTrack.codec = ''; } else if (typeSupported.mp3) { // Firefox audioTrack.codec = 'mp3'; } } tracks.audio = { container: container, codec: audioTrack.codec, initSegment: !audioTrack.isAAC && typeSupported.mpeg ? new Uint8Array() : mp4_generator.initSegment([audioTrack]), metadata: { channelCount: audioTrack.channelCount } }; if (computePTSDTS) { // remember first PTS of this demuxing context. for audio, PTS = DTS initPTS = initDTS = audioSamples[0].pts - Math.round(audioTrack.inputTimeScale * timeOffset); } } if (videoTrack.sps && videoTrack.pps && videoSamples.length) { // let's use input time scale as MP4 video timescale // we use input time scale straight away to avoid rounding issues on frame duration / cts computation var inputTimeScale = videoTrack.inputTimeScale; videoTrack.timescale = inputTimeScale; tracks.video = { container: 'video/mp4', codec: videoTrack.codec, initSegment: mp4_generator.initSegment([videoTrack]), metadata: { width: videoTrack.width, height: videoTrack.height } }; if (computePTSDTS) { var startPTS = this.getVideoStartPts(videoSamples); var startOffset = Math.round(inputTimeScale * timeOffset); initDTS = Math.min(initDTS, PTSNormalize(videoSamples[0].dts, startPTS) - startOffset); initPTS = Math.min(initPTS, startPTS - startOffset); this.observer.trigger(events["default"].INIT_PTS_FOUND, { initPTS: initPTS }); } } else if (computePTSDTS && tracks.audio) { // initPTS found for audio-only stream with main and alt audio this.observer.trigger(events["default"].INIT_PTS_FOUND, { initPTS: initPTS }); } if (Object.keys(tracks).length) { observer.trigger(events["default"].FRAG_PARSING_INIT_SEGMENT, data); this.ISGenerated = true; if (computePTSDTS) { this._initPTS = initPTS; this._initDTS = initDTS; } } else { observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found' }); } }; _proto.remuxVideo = function remuxVideo(track, timeOffset, contiguous, audioTrackLength) { var timeScale = track.timescale; var inputSamples = track.samples; var outputSamples = []; var nbSamples = inputSamples.length; var initPTS = this._initPTS; var offset = 8; var mp4SampleDuration; var mdat; var moof; var firstDTS; var lastDTS; var minPTS = Number.POSITIVE_INFINITY; var maxPTS = Number.NEGATIVE_INFINITY; var ptsDtsShift = 0; var sortSamples = false; // if parsed fragment is contiguous with last one, let's use last DTS value as reference var nextAvcDts = this.nextAvcDts; if (nbSamples === 0) { return; } if (!contiguous) { var pts = timeOffset * timeScale; var cts = inputSamples[0].pts - PTSNormalize(inputSamples[0].dts, inputSamples[0].pts); // if not contiguous, let's use target timeOffset nextAvcDts = pts - cts; } // PTS is coded on 33bits, and can loop from -2^32 to 2^32 // PTSNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value for (var i = 0; i < nbSamples; i++) { var sample = inputSamples[i]; sample.pts = PTSNormalize(sample.pts - initPTS, nextAvcDts); sample.dts = PTSNormalize(sample.dts - initPTS, nextAvcDts); if (sample.dts > sample.pts) { ptsDtsShift = Math.max(Math.min(ptsDtsShift, sample.pts - sample.dts), -1 * PTS_DTS_SHIFT_TOLERANCE_90KHZ); } if (sample.dts < inputSamples[i > 0 ? i - 1 : i].dts) { sortSamples = true; } } // sort video samples by DTS then PTS then demux id order if (sortSamples) { inputSamples.sort(function (a, b) { var deltadts = a.dts - b.dts; var deltapts = a.pts - b.pts; return deltadts || deltapts || a.id - b.id; }); } // Get first/last DTS firstDTS = inputSamples[0].dts; lastDTS = inputSamples[nbSamples - 1].dts; // on Safari let's signal the same sample duration for all samples // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS // set this constant duration as being the avg delta between consecutive DTS. var averageSampleDuration = Math.round((lastDTS - firstDTS) / (nbSamples - 1)); // handle broken streams with PTS < DTS, tolerance up 0.2 seconds if (ptsDtsShift < 0) { if (ptsDtsShift < averageSampleDuration * -2) { // Fix for "CNN special report, with CC" in test-streams (including Safari browser) // With large PTS < DTS errors such as this, we want to correct CTS while maintaining increasing DTS values logger["logger"].warn("PTS < DTS detected in video samples, offsetting DTS from PTS by " + toMsFromMpegTsClock(-averageSampleDuration, true) + " ms"); var lastDts = ptsDtsShift; for (var _i = 0; _i < nbSamples; _i++) { inputSamples[_i].dts = lastDts = Math.max(lastDts, inputSamples[_i].pts - averageSampleDuration); inputSamples[_i].pts = Math.max(lastDts, inputSamples[_i].pts); } } else { // Fix for "Custom IV with bad PTS DTS" in test-streams // With smaller PTS < DTS errors we can simply move all DTS back. This increases CTS without causing buffer gaps or decode errors in Safari logger["logger"].warn("PTS < DTS detected in video samples, shifting DTS by " + toMsFromMpegTsClock(ptsDtsShift, true) + " ms to overcome this issue"); for (var _i2 = 0; _i2 < nbSamples; _i2++) { inputSamples[_i2].dts = inputSamples[_i2].dts + ptsDtsShift; } } firstDTS = inputSamples[0].dts; lastDTS = inputSamples[nbSamples - 1].dts; } // if fragment are contiguous, detect hole/overlapping between fragments if (contiguous) { // check timestamp continuity across consecutive fragments (this is to remove inter-fragment gap/hole) var delta = firstDTS - nextAvcDts; var foundHole = delta > averageSampleDuration; var foundOverlap = delta < -1; if (foundHole || foundOverlap) { if (foundHole) { logger["logger"].warn("AVC: " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected, filling it"); } else { logger["logger"].warn("AVC: " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected"); } firstDTS = nextAvcDts; var firstPTS = inputSamples[0].pts - delta; inputSamples[0].dts = firstDTS; inputSamples[0].pts = firstPTS; logger["logger"].log("Video: First PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms"); } } if (chromeVersion && chromeVersion < 75) { firstDTS = Math.max(0, firstDTS); } var nbNalu = 0; var naluLen = 0; for (var _i3 = 0; _i3 < nbSamples; _i3++) { // compute total/avc sample length and nb of NAL units var _sample = inputSamples[_i3]; var units = _sample.units; var nbUnits = units.length; var sampleLen = 0; for (var j = 0; j < nbUnits; j++) { sampleLen += units[j].data.length; } naluLen += sampleLen; nbNalu += nbUnits; _sample.length = sampleLen; // normalize PTS/DTS // ensure sample monotonic DTS _sample.dts = Math.max(_sample.dts, firstDTS); // ensure that computed value is greater or equal than sample DTS _sample.pts = Math.max(_sample.pts, _sample.dts, 0); minPTS = Math.min(_sample.pts, minPTS); maxPTS = Math.max(_sample.pts, maxPTS); } lastDTS = inputSamples[nbSamples - 1].dts; /* concatenate the video data and construct the mdat in place (need 8 more bytes to fill length and mpdat type) */ var mdatSize = naluLen + 4 * nbNalu + 8; try { mdat = new Uint8Array(mdatSize); } catch (err) { this.observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MUX_ERROR, details: errors["ErrorDetails"].REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: "fail allocating video mdat " + mdatSize }); return; } var view = new DataView(mdat.buffer); view.setUint32(0, mdatSize); mdat.set(mp4_generator.types.mdat, 4); for (var _i4 = 0; _i4 < nbSamples; _i4++) { var avcSample = inputSamples[_i4]; var avcSampleUnits = avcSample.units; var mp4SampleLength = 0; var compositionTimeOffset = void 0; // convert NALU bitstream to MP4 format (prepend NALU with size field) for (var _j = 0, _nbUnits = avcSampleUnits.length; _j < _nbUnits; _j++) { var unit = avcSampleUnits[_j]; var unitData = unit.data; var unitDataLen = unit.data.byteLength; view.setUint32(offset, unitDataLen); offset += 4; mdat.set(unitData, offset); offset += unitDataLen; mp4SampleLength += 4 + unitDataLen; } // expected sample duration is the Decoding Timestamp diff of consecutive samples if (_i4 < nbSamples - 1) { mp4SampleDuration = inputSamples[_i4 + 1].dts - avcSample.dts; } else { var config = this.config; var lastFrameDuration = avcSample.dts - inputSamples[_i4 > 0 ? _i4 - 1 : _i4].dts; if (config.stretchShortVideoTrack) { // In some cases, a segment's audio track duration may exceed the video track duration. // Since we've already remuxed audio, and we know how long the audio track is, we look to // see if the delta to the next segment is longer than maxBufferHole. // If so, playback would potentially get stuck, so we artificially inflate // the duration of the last frame to minimize any potential gap between segments. var maxBufferHole = config.maxBufferHole; var gapTolerance = Math.floor(maxBufferHole * timeScale); var deltaToFrameEnd = (audioTrackLength ? minPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts; if (deltaToFrameEnd > gapTolerance) { // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video // frame overlap. maxBufferHole should be >> lastFrameDuration anyway. mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; if (mp4SampleDuration < 0) { mp4SampleDuration = lastFrameDuration; } logger["logger"].log("It is approximately " + toMsFromMpegTsClock(deltaToFrameEnd, false) + " ms to the next segment; using duration " + toMsFromMpegTsClock(mp4SampleDuration, false) + " ms for the last video frame."); } else { mp4SampleDuration = lastFrameDuration; } } else { mp4SampleDuration = lastFrameDuration; } } compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts); // console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${avcSample.pts}/${avcSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(avcSample.pts/4294967296).toFixed(3)}'); outputSamples.push({ size: mp4SampleLength, // constant duration duration: mp4SampleDuration, cts: compositionTimeOffset, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: avcSample.key ? 2 : 1, isNonSync: avcSample.key ? 0 : 1 } }); } // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) this.nextAvcDts = lastDTS + mp4SampleDuration; var dropped = track.dropped; track.nbNalu = 0; track.dropped = 0; if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { var flags = outputSamples[0].flags; // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue // https://code.google.com/p/chromium/issues/detail?id=229412 flags.dependsOn = 2; flags.isNonSync = 0; } track.samples = outputSamples; moof = mp4_generator.moof(track.sequenceNumber++, firstDTS, track); track.samples = []; var data = { data1: moof, data2: mdat, startPTS: minPTS / timeScale, endPTS: (maxPTS + mp4SampleDuration) / timeScale, startDTS: firstDTS / timeScale, endDTS: this.nextAvcDts / timeScale, type: 'video', hasAudio: false, hasVideo: true, nb: outputSamples.length, dropped: dropped }; this.observer.trigger(events["default"].FRAG_PARSING_DATA, data); return data; }; _proto.remuxAudio = function remuxAudio(track, timeOffset, contiguous, accurateTimeOffset) { var inputTimeScale = track.inputTimeScale; var mp4timeScale = track.timescale; var scaleFactor = inputTimeScale / mp4timeScale; var mp4SampleDuration = track.isAAC ? 1024 : 1152; var inputSampleDuration = mp4SampleDuration * scaleFactor; var initPTS = this._initPTS; var rawMPEG = !track.isAAC && this.typeSupported.mpeg; var mp4Sample; var fillFrame; var mdat; var moof; var firstPTS; var lastPTS; var offset = rawMPEG ? 0 : 8; var inputSamples = track.samples; var outputSamples = []; var nextAudioPts = this.nextAudioPts; // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), // for sake of clarity: // consecutive fragments are frags with // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR // - less than 20 audio frames distance // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) // this helps ensuring audio continuity // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame contiguous |= inputSamples.length && nextAudioPts && (accurateTimeOffset && Math.abs(timeOffset - nextAudioPts / inputTimeScale) < 0.1 || Math.abs(inputSamples[0].pts - nextAudioPts - initPTS) < 20 * inputSampleDuration); // compute normalized PTS inputSamples.forEach(function (sample) { sample.pts = sample.dts = PTSNormalize(sample.pts - initPTS, timeOffset * inputTimeScale); }); // filter out sample with negative PTS that are not playable anyway // if we don't remove these negative samples, they will shift all audio samples forward. // leading to audio overlap between current / next fragment inputSamples = inputSamples.filter(function (sample) { return sample.pts >= 0; }); // in case all samples have negative PTS, and have been filtered out, return now if (inputSamples.length === 0) { return; } if (!contiguous) { if (!accurateTimeOffset) { // if frag are mot contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS nextAudioPts = inputSamples[0].pts; } else { // if timeOffset is accurate, let's use it as predicted next audio PTS nextAudioPts = Math.max(0, timeOffset * inputTimeScale); } } // If the audio track is missing samples, the frames seem to get "left-shifted" within the // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. // In an effort to prevent this from happening, we inject frames here where there are gaps. // When possible, we inject a silent frame; when that's not possible, we duplicate the last // frame. if (track.isAAC) { var maxAudioFramesDrift = this.config.maxAudioFramesDrift; for (var i = 0, nextPts = nextAudioPts; i < inputSamples.length;) { // First, let's see how far off this frame is from where we expect it to be var sample = inputSamples[i]; var pts = sample.pts; var delta = pts - nextPts; // If we're overlapping by more than a duration, drop this sample if (delta <= -maxAudioFramesDrift * inputSampleDuration) { if (contiguous || i > 0) { logger["logger"].warn("Dropping 1 audio frame @ " + toMsFromMpegTsClock(nextPts, true) / 1000 + "s due to " + toMsFromMpegTsClock(delta, true) + " ms overlap."); inputSamples.splice(i, 1); // Don't touch nextPtsNorm or i } else { // When changing qualities we can't trust that audio has been appended up to nextAudioPts // Warn about the overlap but do not drop samples as that can introduce buffer gaps logger["logger"].warn("Audio frame @ " + toMsFromMpegTsClock(pts, true) / 1000 + "s overlaps nextAudioPts by " + toMsFromMpegTsClock(delta, true) + " ms."); nextPts = pts + inputSampleDuration; i++; } } // eslint-disable-line brace-style // Insert missing frames if: // 1: We're more than maxAudioFramesDrift frame away // 2: Not more than MAX_SILENT_FRAME_DURATION away // 3: currentTime (aka nextPtsNorm) is not 0 else if (delta >= maxAudioFramesDrift * inputSampleDuration && delta < MAX_SILENT_FRAME_DURATION_90KHZ && nextPts) { var missing = Math.round(delta / inputSampleDuration); logger["logger"].warn("Injecting " + missing + " audio frames @ " + toMsFromMpegTsClock(nextPts, true) / 1000 + "s due to " + toMsFromMpegTsClock(delta, true) + " ms gap."); for (var j = 0; j < missing; j++) { var newStamp = Math.max(nextPts, 0); fillFrame = aac_helper.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); if (!fillFrame) { logger["logger"].log('Unable to get silent frame for given audio codec; duplicating last frame instead.'); fillFrame = sample.unit.subarray(); } inputSamples.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp }); nextPts += inputSampleDuration; i++; } // Adjust sample to next expected pts sample.pts = sample.dts = nextPts; nextPts += inputSampleDuration; i++; } else { // Otherwise, just adjust pts if (Math.abs(delta) > 0.1 * inputSampleDuration) {// logger.log(`Invalid frame delta ${Math.round(delta + inputSampleDuration)} at PTS ${Math.round(pts / 90)} (should be ${Math.round(inputSampleDuration)}).`); } sample.pts = sample.dts = nextPts; nextPts += inputSampleDuration; i++; } } } // compute mdat size, as we eventually filtered/added some samples var nbSamples = inputSamples.length; var mdatSize = 0; while (nbSamples--) { mdatSize += inputSamples[nbSamples].unit.byteLength; } for (var _j2 = 0, _nbSamples = inputSamples.length; _j2 < _nbSamples; _j2++) { var audioSample = inputSamples[_j2]; var unit = audioSample.unit; var _pts = audioSample.pts; // logger.log(`Audio/PTS:${toMsFromMpegTsClock(pts, true)}`); // if not first sample if (lastPTS !== undefined && mp4Sample) { mp4Sample.duration = Math.round((_pts - lastPTS) / scaleFactor); } else { var _delta = _pts - nextAudioPts; var numMissingFrames = 0; // if fragment are contiguous, detect hole/overlapping between fragments // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) if (contiguous && track.isAAC) { // log delta if (_delta) { if (_delta > 0 && _delta < MAX_SILENT_FRAME_DURATION_90KHZ) { // Q: why do we have to round here, shouldn't this always result in an integer if timestamps are correct, // and if not, shouldn't we actually Math.ceil() instead? numMissingFrames = Math.round((_pts - nextAudioPts) / inputSampleDuration); logger["logger"].log(toMsFromMpegTsClock(_delta, true) + " ms hole between AAC samples detected,filling it"); if (numMissingFrames > 0) { fillFrame = aac_helper.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); if (!fillFrame) { fillFrame = unit.subarray(); } mdatSize += numMissingFrames * fillFrame.length; } // if we have frame overlap, overlapping for more than half a frame duraion } else if (_delta < -12) { // drop overlapping audio frames... browser will deal with it logger["logger"].log("drop overlapping AAC sample, expected/parsed/delta: " + toMsFromMpegTsClock(nextAudioPts, true) + " ms / " + toMsFromMpegTsClock(_pts, true) + " ms / " + toMsFromMpegTsClock(-_delta, true) + " ms"); mdatSize -= unit.byteLength; continue; } // set PTS/DTS to expected PTS/DTS _pts = nextAudioPts; } } // remember first PTS of our audioSamples firstPTS = _pts; if (mdatSize > 0) { mdatSize += offset; try { mdat = new Uint8Array(mdatSize); } catch (err) { this.observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MUX_ERROR, details: errors["ErrorDetails"].REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: "fail allocating audio mdat " + mdatSize }); return; } if (!rawMPEG) { var view = new DataView(mdat.buffer); view.setUint32(0, mdatSize); mdat.set(mp4_generator.types.mdat, 4); } } else { // no audio samples return; } for (var _i5 = 0; _i5 < numMissingFrames; _i5++) { fillFrame = aac_helper.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); if (!fillFrame) { logger["logger"].log('Unable to get silent frame for given audio codec; duplicating this frame instead.'); fillFrame = unit.subarray(); } mdat.set(fillFrame, offset); offset += fillFrame.byteLength; mp4Sample = { size: fillFrame.byteLength, cts: 0, duration: 1024, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: 1 } }; outputSamples.push(mp4Sample); } } mdat.set(unit, offset); var unitLen = unit.byteLength; offset += unitLen; // console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${audioSample.pts}/${audioSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(audioSample.pts/4294967296).toFixed(3)}'); mp4Sample = { size: unitLen, cts: 0, duration: 0, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: 1 } }; outputSamples.push(mp4Sample); lastPTS = _pts; } var lastSampleDuration = 0; nbSamples = outputSamples.length; // set last sample duration as being identical to previous sample if (nbSamples >= 2) { lastSampleDuration = outputSamples[nbSamples - 2].duration; mp4Sample.duration = lastSampleDuration; } if (nbSamples) { // next audio sample PTS should be equal to last sample PTS + duration this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSampleDuration; // logger.log('Audio/PTS/PTSend:' + audioSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0)); track.samples = outputSamples; if (rawMPEG) { moof = new Uint8Array(); } else { moof = mp4_generator.moof(track.sequenceNumber++, firstPTS / scaleFactor, track); } track.samples = []; var start = firstPTS / inputTimeScale; var end = nextAudioPts / inputTimeScale; var audioData = { data1: moof, data2: mdat, startPTS: start, endPTS: end, startDTS: start, endDTS: end, type: 'audio', hasAudio: true, hasVideo: false, nb: nbSamples }; this.observer.trigger(events["default"].FRAG_PARSING_DATA, audioData); return audioData; } return null; }; _proto.remuxEmptyAudio = function remuxEmptyAudio(track, timeOffset, contiguous, videoData) { var inputTimeScale = track.inputTimeScale; var mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale; var scaleFactor = inputTimeScale / mp4timeScale; var nextAudioPts = this.nextAudioPts; // sync with video's timestamp var startDTS = (nextAudioPts !== undefined ? nextAudioPts : videoData.startDTS * inputTimeScale) + this._initDTS; var endDTS = videoData.endDTS * inputTimeScale + this._initDTS; // one sample's duration value var sampleDuration = 1024; var frameDuration = scaleFactor * sampleDuration; // samples count of this segment's duration var nbSamples = Math.ceil((endDTS - startDTS) / frameDuration); // silent frame var silentFrame = aac_helper.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); logger["logger"].warn('remux empty Audio'); // Can't remux if we can't generate a silent frame... if (!silentFrame) { logger["logger"].trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!'); return; } var samples = []; for (var i = 0; i < nbSamples; i++) { var stamp = startDTS + i * frameDuration; samples.push({ unit: silentFrame, pts: stamp, dts: stamp }); } track.samples = samples; this.remuxAudio(track, timeOffset, contiguous); }; _proto.remuxID3 = function remuxID3(track, timeOffset) { var length = track.samples.length; if (!length) { return; } var inputTimeScale = track.inputTimeScale; var initPTS = this._initPTS; var initDTS = this._initDTS; // consume samples for (var index = 0; index < length; index++) { var sample = track.samples[index]; // setting id3 pts, dts to relative time // using this._initPTS and this._initDTS to calculate relative time sample.pts = PTSNormalize(sample.pts - initPTS, timeOffset * inputTimeScale) / inputTimeScale; sample.dts = PTSNormalize(sample.dts - initDTS, timeOffset * inputTimeScale) / inputTimeScale; } this.observer.trigger(events["default"].FRAG_PARSING_METADATA, { samples: track.samples }); track.samples = []; }; _proto.remuxText = function remuxText(track, timeOffset) { var length = track.samples.length; var inputTimeScale = track.inputTimeScale; var initPTS = this._initPTS; // consume samples if (length) { for (var index = 0; index < length; index++) { var sample = track.samples[index]; // setting text pts, dts to relative time // using this._initPTS and this._initDTS to calculate relative time sample.pts = PTSNormalize(sample.pts - initPTS, timeOffset * inputTimeScale) / inputTimeScale; } track.samples.sort(function (a, b) { return a.pts - b.pts; }); this.observer.trigger(events["default"].FRAG_PARSING_USERDATA, { samples: track.samples }); } track.samples = []; }; return MP4Remuxer; }(); function PTSNormalize(value, reference) { var offset; if (reference === undefined) { return value; } if (reference < value) { // - 2^33 offset = -8589934592; } else { // + 2^33 offset = 8589934592; } /* PTS is 33bit (from 0 to 2^33 -1) if diff between value and reference is bigger than half of the amplitude (2^32) then it means that PTS looping occured. fill the gap */ while (Math.abs(value - reference) > 4294967296) { value += offset; } return value; } /* harmony default export */ var mp4_remuxer = (mp4_remuxer_MP4Remuxer); // CONCATENATED MODULE: ./src/remux/passthrough-remuxer.js /** * passthrough remuxer */ var passthrough_remuxer_PassThroughRemuxer = /*#__PURE__*/function () { function PassThroughRemuxer(observer) { this.observer = observer; } var _proto = PassThroughRemuxer.prototype; _proto.destroy = function destroy() {}; _proto.resetTimeStamp = function resetTimeStamp() {}; _proto.resetInitSegment = function resetInitSegment() {}; _proto.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset, rawData) { var observer = this.observer; var streamType = ''; if (audioTrack) { streamType += 'audio'; } if (videoTrack) { streamType += 'video'; } observer.trigger(events["default"].FRAG_PARSING_DATA, { data1: rawData, startPTS: timeOffset, startDTS: timeOffset, type: streamType, hasAudio: !!audioTrack, hasVideo: !!videoTrack, nb: 1, dropped: 0 }); // notify end of parsing observer.trigger(events["default"].FRAG_PARSED); }; return PassThroughRemuxer; }(); /* harmony default export */ var passthrough_remuxer = (passthrough_remuxer_PassThroughRemuxer); // CONCATENATED MODULE: ./src/demux/demuxer-inline.js /** * * inline demuxer: probe fragments and instantiate * appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...) * */ // see https://stackoverflow.com/a/11237259/589493 var global = Object(get_self_scope["getSelfScope"])(); // safeguard for code that might run both on worker and main thread var now; // performance.now() not available on WebWorker, at least on Safari Desktop try { now = global.performance.now.bind(global.performance); } catch (err) { logger["logger"].debug('Unable to use Performance API on this environment'); now = global.Date.now; } var demuxer_inline_DemuxerInline = /*#__PURE__*/function () { function DemuxerInline(observer, typeSupported, config, vendor) { this.observer = observer; this.typeSupported = typeSupported; this.config = config; this.vendor = vendor; } var _proto = DemuxerInline.prototype; _proto.destroy = function destroy() { var demuxer = this.demuxer; if (demuxer) { demuxer.destroy(); } }; _proto.push = function push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) { var _this = this; if (data.byteLength > 0 && decryptdata != null && decryptdata.key != null && decryptdata.method === 'AES-128') { var decrypter = this.decrypter; if (decrypter == null) { decrypter = this.decrypter = new crypt_decrypter["default"](this.observer, this.config); } var startTime = now(); decrypter.decrypt(data, decryptdata.key.buffer, decryptdata.iv.buffer, function (decryptedData) { var endTime = now(); _this.observer.trigger(events["default"].FRAG_DECRYPTED, { stats: { tstart: startTime, tdecrypt: endTime } }); _this.pushDecrypted(new Uint8Array(decryptedData), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); }); } else { this.pushDecrypted(new Uint8Array(data), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); } }; _proto.pushDecrypted = function pushDecrypted(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) { var demuxer = this.demuxer; var remuxer = this.remuxer; if (!demuxer || // in case of continuity change, or track switch // we might switch from content type (AAC container to TS container, or TS to fmp4 for example) discontinuity || trackSwitch) { var observer = this.observer; var typeSupported = this.typeSupported; var config = this.config; // probing order is TS/MP4/AAC/MP3 var muxConfig = [{ demux: tsdemuxer, remux: mp4_remuxer }, { demux: mp4demuxer["default"], remux: passthrough_remuxer }, { demux: aacdemuxer, remux: mp4_remuxer }, { demux: mp3demuxer, remux: mp4_remuxer }]; // probe for content type var mux; for (var i = 0, len = muxConfig.length; i < len; i++) { mux = muxConfig[i]; if (mux.demux.probe(data)) { break; } } if (!mux) { observer.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found' }); return; } // so let's check that current remuxer and demuxer are still valid if (!remuxer || !(remuxer instanceof mux.remux)) { remuxer = new mux.remux(observer, config, typeSupported, this.vendor); } if (!demuxer || !(demuxer instanceof mux.demux)) { demuxer = new mux.demux(observer, remuxer, config, typeSupported); this.probe = mux.demux.probe; } this.demuxer = demuxer; this.remuxer = remuxer; } if (discontinuity || trackSwitch) { demuxer.resetInitSegment(initSegment, audioCodec, videoCodec, duration); remuxer.resetInitSegment(); } if (discontinuity) { demuxer.resetTimeStamp(defaultInitPTS); remuxer.resetTimeStamp(defaultInitPTS); } if (typeof demuxer.setDecryptData === 'function') { demuxer.setDecryptData(decryptdata); } demuxer.append(data, timeOffset, contiguous, accurateTimeOffset); }; return DemuxerInline; }(); /* harmony default export */ var demuxer_inline = __webpack_exports__["default"] = (demuxer_inline_DemuxerInline); /***/ }), /***/ "./src/demux/demuxer-worker.js": /*!*************************************!*\ !*** ./src/demux/demuxer-worker.js ***! \*************************************/ /*! exports provided: default */ /*! ModuleConcatenation bailout: Module is referenced from these modules with unsupported syntax: ./src/demux/demuxer.js (referenced with require.resolve) */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _demux_demuxer_inline__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../demux/demuxer-inline */ "./src/demux/demuxer-inline.js"); /* harmony import */ var _events__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../events */ "./src/events.js"); /* harmony import */ var _utils_logger__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); /* harmony import */ var eventemitter3__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! eventemitter3 */ "./node_modules/eventemitter3/index.js"); /* harmony import */ var eventemitter3__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(eventemitter3__WEBPACK_IMPORTED_MODULE_3__); /* demuxer web worker. * - listen to worker message, and trigger DemuxerInline upon reception of Fragments. * - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead. */ var DemuxerWorker = function DemuxerWorker(self) { // observer setup var observer = new eventemitter3__WEBPACK_IMPORTED_MODULE_3__["EventEmitter"](); observer.trigger = function trigger(event) { for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { data[_key - 1] = arguments[_key]; } observer.emit.apply(observer, [event, event].concat(data)); }; observer.off = function off(event) { for (var _len2 = arguments.length, data = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { data[_key2 - 1] = arguments[_key2]; } observer.removeListener.apply(observer, [event].concat(data)); }; var forwardMessage = function forwardMessage(ev, data) { self.postMessage({ event: ev, data: data }); }; self.addEventListener('message', function (ev) { var data = ev.data; // console.log('demuxer cmd:' + data.cmd); switch (data.cmd) { case 'init': var config = JSON.parse(data.config); self.demuxer = new _demux_demuxer_inline__WEBPACK_IMPORTED_MODULE_0__["default"](observer, data.typeSupported, config, data.vendor); Object(_utils_logger__WEBPACK_IMPORTED_MODULE_2__["enableLogs"])(config.debug); // signal end of worker init forwardMessage('init', null); break; case 'demux': self.demuxer.push(data.data, data.decryptdata, data.initSegment, data.audioCodec, data.videoCodec, data.timeOffset, data.discontinuity, data.trackSwitch, data.contiguous, data.duration, data.accurateTimeOffset, data.defaultInitPTS); break; default: break; } }); // forward events to main thread observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_DECRYPTED, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSING_INIT_SEGMENT, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSED, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].ERROR, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSING_METADATA, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSING_USERDATA, forwardMessage); observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].INIT_PTS_FOUND, forwardMessage); // special case for FRAG_PARSING_DATA: pass data1/data2 as transferable object (no copy) observer.on(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSING_DATA, function (ev, data) { var transferable = []; var message = { event: ev, data: data }; if (data.data1) { message.data1 = data.data1.buffer; transferable.push(data.data1.buffer); delete data.data1; } if (data.data2) { message.data2 = data.data2.buffer; transferable.push(data.data2.buffer); delete data.data2; } self.postMessage(message, transferable); }); }; /* harmony default export */ __webpack_exports__["default"] = (DemuxerWorker); /***/ }), /***/ "./src/demux/id3.js": /*!**************************!*\ !*** ./src/demux/id3.js ***! \**************************/ /*! exports provided: default, utf8ArrayToStr */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "utf8ArrayToStr", function() { return utf8ArrayToStr; }); /* harmony import */ var _utils_get_self_scope__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/get-self-scope */ "./src/utils/get-self-scope.js"); /** * ID3 parser */ var ID3 = /*#__PURE__*/function () { function ID3() {} /** * Returns true if an ID3 header can be found at offset in data * @param {Uint8Array} data - The data to search in * @param {number} offset - The offset at which to start searching * @return {boolean} - True if an ID3 header is found */ ID3.isHeader = function isHeader(data, offset) { /* * http://id3.org/id3v2.3.0 * [0] = 'I' * [1] = 'D' * [2] = '3' * [3,4] = {Version} * [5] = {Flags} * [6-9] = {ID3 Size} * * An ID3v2 tag can be detected with the following pattern: * $49 44 33 yy yy xx zz zz zz zz * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80 */ if (offset + 10 <= data.length) { // look for 'ID3' identifier if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) { // check version is within range if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) { // check size is within range if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { return true; } } } } return false; } /** * Returns true if an ID3 footer can be found at offset in data * @param {Uint8Array} data - The data to search in * @param {number} offset - The offset at which to start searching * @return {boolean} - True if an ID3 footer is found */ ; ID3.isFooter = function isFooter(data, offset) { /* * The footer is a copy of the header, but with a different identifier */ if (offset + 10 <= data.length) { // look for '3DI' identifier if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) { // check version is within range if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) { // check size is within range if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { return true; } } } } return false; } /** * Returns any adjacent ID3 tags found in data starting at offset, as one block of data * @param {Uint8Array} data - The data to search in * @param {number} offset - The offset at which to start searching * @return {Uint8Array} - The block of data containing any ID3 tags found */ ; ID3.getID3Data = function getID3Data(data, offset) { var front = offset; var length = 0; while (ID3.isHeader(data, offset)) { // ID3 header is 10 bytes length += 10; var size = ID3._readSize(data, offset + 6); length += size; if (ID3.isFooter(data, offset + 10)) { // ID3 footer is 10 bytes length += 10; } offset += length; } if (length > 0) { return data.subarray(front, front + length); } return undefined; }; ID3._readSize = function _readSize(data, offset) { var size = 0; size = (data[offset] & 0x7f) << 21; size |= (data[offset + 1] & 0x7f) << 14; size |= (data[offset + 2] & 0x7f) << 7; size |= data[offset + 3] & 0x7f; return size; } /** * Searches for the Elementary Stream timestamp found in the ID3 data chunk * @param {Uint8Array} data - Block of data containing one or more ID3 tags * @return {number} - The timestamp */ ; ID3.getTimeStamp = function getTimeStamp(data) { var frames = ID3.getID3Frames(data); for (var i = 0; i < frames.length; i++) { var frame = frames[i]; if (ID3.isTimeStampFrame(frame)) { return ID3._readTimeStamp(frame); } } return undefined; } /** * Returns true if the ID3 frame is an Elementary Stream timestamp frame * @param {ID3 frame} frame */ ; ID3.isTimeStampFrame = function isTimeStampFrame(frame) { return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp'; }; ID3._getFrameData = function _getFrameData(data) { /* Frame ID $xx xx xx xx (four characters) Size $xx xx xx xx Flags $xx xx */ var type = String.fromCharCode(data[0], data[1], data[2], data[3]); var size = ID3._readSize(data, 4); // skip frame id, size, and flags var offset = 10; return { type: type, size: size, data: data.subarray(offset, offset + size) }; } /** * Returns an array of ID3 frames found in all the ID3 tags in the id3Data * @param {Uint8Array} id3Data - The ID3 data containing one or more ID3 tags * @return {ID3 frame[]} - Array of ID3 frame objects */ ; ID3.getID3Frames = function getID3Frames(id3Data) { var offset = 0; var frames = []; while (ID3.isHeader(id3Data, offset)) { var size = ID3._readSize(id3Data, offset + 6); // skip past ID3 header offset += 10; var end = offset + size; // loop through frames in the ID3 tag while (offset + 8 < end) { var frameData = ID3._getFrameData(id3Data.subarray(offset)); var frame = ID3._decodeFrame(frameData); if (frame) { frames.push(frame); } // skip frame header and frame data offset += frameData.size + 10; } if (ID3.isFooter(id3Data, offset)) { offset += 10; } } return frames; }; ID3._decodeFrame = function _decodeFrame(frame) { if (frame.type === 'PRIV') { return ID3._decodePrivFrame(frame); } else if (frame.type[0] === 'W') { return ID3._decodeURLFrame(frame); } return ID3._decodeTextFrame(frame); }; ID3._readTimeStamp = function _readTimeStamp(timeStampFrame) { if (timeStampFrame.data.byteLength === 8) { var data = new Uint8Array(timeStampFrame.data); // timestamp is 33 bit expressed as a big-endian eight-octet number, // with the upper 31 bits set to zero. var pts33Bit = data[3] & 0x1; var timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7]; timestamp /= 45; if (pts33Bit) { timestamp += 47721858.84; } // 2^32 / 90 return Math.round(timestamp); } return undefined; }; ID3._decodePrivFrame = function _decodePrivFrame(frame) { /* Format: \0 */ if (frame.size < 2) { return undefined; } var owner = ID3._utf8ArrayToStr(frame.data, true); var privateData = new Uint8Array(frame.data.subarray(owner.length + 1)); return { key: frame.type, info: owner, data: privateData.buffer }; }; ID3._decodeTextFrame = function _decodeTextFrame(frame) { if (frame.size < 2) { return undefined; } if (frame.type === 'TXXX') { /* Format: [0] = {Text Encoding} [1-?] = {Description}\0{Value} */ var index = 1; var description = ID3._utf8ArrayToStr(frame.data.subarray(index), true); index += description.length + 1; var value = ID3._utf8ArrayToStr(frame.data.subarray(index)); return { key: frame.type, info: description, data: value }; } else { /* Format: [0] = {Text Encoding} [1-?] = {Value} */ var text = ID3._utf8ArrayToStr(frame.data.subarray(1)); return { key: frame.type, data: text }; } }; ID3._decodeURLFrame = function _decodeURLFrame(frame) { if (frame.type === 'WXXX') { /* Format: [0] = {Text Encoding} [1-?] = {Description}\0{URL} */ if (frame.size < 2) { return undefined; } var index = 1; var description = ID3._utf8ArrayToStr(frame.data.subarray(index), true); index += description.length + 1; var value = ID3._utf8ArrayToStr(frame.data.subarray(index)); return { key: frame.type, info: description, data: value }; } else { /* Format: [0-?] = {URL} */ var url = ID3._utf8ArrayToStr(frame.data); return { key: frame.type, data: url }; } } // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197 // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt /* utf.js - UTF-8 <=> UTF-16 convertion * * Copyright (C) 1999 Masanao Izumo * Version: 1.0 * LastModified: Dec 25 1999 * This library is free. You can redistribute it and/or modify it. */ ; ID3._utf8ArrayToStr = function _utf8ArrayToStr(array, exitOnNull) { if (exitOnNull === void 0) { exitOnNull = false; } var decoder = getTextDecoder(); if (decoder) { var decoded = decoder.decode(array); if (exitOnNull) { // grab up to the first null var idx = decoded.indexOf('\0'); return idx !== -1 ? decoded.substring(0, idx) : decoded; } // remove any null characters return decoded.replace(/\0/g, ''); } var len = array.length; var c; var char2; var char3; var out = ''; var i = 0; while (i < len) { c = array[i++]; if (c === 0x00 && exitOnNull) { return out; } else if (c === 0x00 || c === 0x03) { // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it continue; } switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: // 0xxxxxxx out += String.fromCharCode(c); break; case 12: case 13: // 110x xxxx 10xx xxxx char2 = array[i++]; out += String.fromCharCode((c & 0x1F) << 6 | char2 & 0x3F); break; case 14: // 1110 xxxx 10xx xxxx 10xx xxxx char2 = array[i++]; char3 = array[i++]; out += String.fromCharCode((c & 0x0F) << 12 | (char2 & 0x3F) << 6 | (char3 & 0x3F) << 0); break; default: } } return out; }; return ID3; }(); var decoder; function getTextDecoder() { var global = Object(_utils_get_self_scope__WEBPACK_IMPORTED_MODULE_0__["getSelfScope"])(); // safeguard for code that might run both on worker and main thread if (!decoder && typeof global.TextDecoder !== 'undefined') { decoder = new global.TextDecoder('utf-8'); } return decoder; } var utf8ArrayToStr = ID3._utf8ArrayToStr; /* harmony default export */ __webpack_exports__["default"] = (ID3); /***/ }), /***/ "./src/demux/mp4demuxer.js": /*!*********************************!*\ !*** ./src/demux/mp4demuxer.js ***! \*********************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_logger__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); /* harmony import */ var _events__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../events */ "./src/events.js"); /** * MP4 demuxer */ var UINT32_MAX = Math.pow(2, 32) - 1; var MP4Demuxer = /*#__PURE__*/function () { function MP4Demuxer(observer, remuxer) { this.observer = observer; this.remuxer = remuxer; } var _proto = MP4Demuxer.prototype; _proto.resetTimeStamp = function resetTimeStamp(initPTS) { this.initPTS = initPTS; }; _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, duration) { // jshint unused:false if (initSegment && initSegment.byteLength) { var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment); // default audio codec if nothing specified // TODO : extract that from initsegment if (audioCodec == null) { audioCodec = 'mp4a.40.5'; } if (videoCodec == null) { videoCodec = 'avc1.42e01e'; } var tracks = {}; if (initData.audio && initData.video) { tracks.audiovideo = { container: 'video/mp4', codec: audioCodec + ',' + videoCodec, initSegment: duration ? initSegment : null }; } else { if (initData.audio) { tracks.audio = { container: 'audio/mp4', codec: audioCodec, initSegment: duration ? initSegment : null }; } if (initData.video) { tracks.video = { container: 'video/mp4', codec: videoCodec, initSegment: duration ? initSegment : null }; } } this.observer.trigger(_events__WEBPACK_IMPORTED_MODULE_1__["default"].FRAG_PARSING_INIT_SEGMENT, { tracks: tracks }); } else { if (audioCodec) { this.audioCodec = audioCodec; } if (videoCodec) { this.videoCodec = videoCodec; } } }; MP4Demuxer.probe = function probe(data) { // ensure we find a moof box in the first 16 kB return MP4Demuxer.findBox({ data: data, start: 0, end: Math.min(data.length, 16384) }, ['moof']).length > 0; }; MP4Demuxer.bin2str = function bin2str(buffer) { return String.fromCharCode.apply(null, buffer); }; MP4Demuxer.readUint16 = function readUint16(buffer, offset) { if (buffer.data) { offset += buffer.start; buffer = buffer.data; } var val = buffer[offset] << 8 | buffer[offset + 1]; return val < 0 ? 65536 + val : val; }; MP4Demuxer.readUint32 = function readUint32(buffer, offset) { if (buffer.data) { offset += buffer.start; buffer = buffer.data; } var val = buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]; return val < 0 ? 4294967296 + val : val; }; MP4Demuxer.writeUint32 = function writeUint32(buffer, offset, value) { if (buffer.data) { offset += buffer.start; buffer = buffer.data; } buffer[offset] = value >> 24; buffer[offset + 1] = value >> 16 & 0xff; buffer[offset + 2] = value >> 8 & 0xff; buffer[offset + 3] = value & 0xff; } // Find the data for a box specified by its path ; MP4Demuxer.findBox = function findBox(data, path) { var results = [], i, size, type, end, subresults, start, endbox; if (data.data) { start = data.start; end = data.end; data = data.data; } else { start = 0; end = data.byteLength; } if (!path.length) { // short-circuit the search for empty paths return null; } for (i = start; i < end;) { size = MP4Demuxer.readUint32(data, i); type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8)); endbox = size > 1 ? i + size : end; if (type === path[0]) { if (path.length === 1) { // this is the end of the path and we've found the box we were // looking for results.push({ data: data, start: i + 8, end: endbox }); } else { // recursively search for the next box along the path subresults = MP4Demuxer.findBox({ data: data, start: i + 8, end: endbox }, path.slice(1)); if (subresults.length) { results = results.concat(subresults); } } } i = endbox; } // we've finished searching all of data return results; }; MP4Demuxer.parseSegmentIndex = function parseSegmentIndex(initSegment) { var moov = MP4Demuxer.findBox(initSegment, ['moov'])[0]; var moovEndOffset = moov ? moov.end : null; // we need this in case we need to chop of garbage of the end of current data var index = 0; var sidx = MP4Demuxer.findBox(initSegment, ['sidx']); var references; if (!sidx || !sidx[0]) { return null; } references = []; sidx = sidx[0]; var version = sidx.data[0]; // set initial offset, we skip the reference ID (not needed) index = version === 0 ? 8 : 16; var timescale = MP4Demuxer.readUint32(sidx, index); index += 4; // TODO: parse earliestPresentationTime and firstOffset // usually zero in our case var earliestPresentationTime = 0; var firstOffset = 0; if (version === 0) { index += 8; } else { index += 16; } // skip reserved index += 2; var startByte = sidx.end + firstOffset; var referencesCount = MP4Demuxer.readUint16(sidx, index); index += 2; for (var i = 0; i < referencesCount; i++) { var referenceIndex = index; var referenceInfo = MP4Demuxer.readUint32(sidx, referenceIndex); referenceIndex += 4; var referenceSize = referenceInfo & 0x7FFFFFFF; var referenceType = (referenceInfo & 0x80000000) >>> 31; if (referenceType === 1) { console.warn('SIDX has hierarchical references (not supported)'); return; } var subsegmentDuration = MP4Demuxer.readUint32(sidx, referenceIndex); referenceIndex += 4; references.push({ referenceSize: referenceSize, subsegmentDuration: subsegmentDuration, // unscaled info: { duration: subsegmentDuration / timescale, start: startByte, end: startByte + referenceSize - 1 } }); startByte += referenceSize; // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits // for |sapDelta|. referenceIndex += 4; // skip to next ref index = referenceIndex; } return { earliestPresentationTime: earliestPresentationTime, timescale: timescale, version: version, referencesCount: referencesCount, references: references, moovEndOffset: moovEndOffset }; } /** * Parses an MP4 initialization segment and extracts stream type and * timescale values for any declared tracks. Timescale values indicate the * number of clock ticks per second to assume for time-based values * elsewhere in the MP4. * * To determine the start time of an MP4, you need two pieces of * information: the timescale unit and the earliest base media decode * time. Multiple timescales can be specified within an MP4 but the * base media decode time is always expressed in the timescale from * the media header box for the track: * ``` * moov > trak > mdia > mdhd.timescale * moov > trak > mdia > hdlr * ``` * @param init {Uint8Array} the bytes of the init segment * @return {object} a hash of track type to timescale values or null if * the init segment is malformed. */ ; MP4Demuxer.parseInitSegment = function parseInitSegment(initSegment) { var result = []; var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']); traks.forEach(function (trak) { var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0]; if (tkhd) { var version = tkhd.data[tkhd.start]; var index = version === 0 ? 12 : 20; var trackId = MP4Demuxer.readUint32(tkhd, index); var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0]; if (mdhd) { version = mdhd.data[mdhd.start]; index = version === 0 ? 12 : 20; var timescale = MP4Demuxer.readUint32(mdhd, index); var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0]; if (hdlr) { var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12)); var type = { 'soun': 'audio', 'vide': 'video' }[hdlrType]; if (type) { // extract codec info. TODO : parse codec details to be able to build MIME type var codecBox = MP4Demuxer.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd']); if (codecBox.length) { codecBox = codecBox[0]; var codecType = MP4Demuxer.bin2str(codecBox.data.subarray(codecBox.start + 12, codecBox.start + 16)); _utils_logger__WEBPACK_IMPORTED_MODULE_0__["logger"].log("MP4Demuxer:" + type + ":" + codecType + " found"); } result[trackId] = { timescale: timescale, type: type }; result[type] = { timescale: timescale, id: trackId }; } } } } }); return result; } /** * Determine the base media decode start time, in seconds, for an MP4 * fragment. If multiple fragments are specified, the earliest time is * returned. * * The base media decode time can be parsed from track fragment * metadata: * ``` * moof > traf > tfdt.baseMediaDecodeTime * ``` * It requires the timescale value from the mdhd to interpret. * * @param timescale {object} a hash of track ids to timescale values. * @return {number} the earliest base media decode start time for the * fragment, in seconds */ ; MP4Demuxer.getStartDTS = function getStartDTS(initData, fragment) { var trafs, baseTimes, result; // we need info from two childrend of each track fragment box trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']); // determine the start times for each track baseTimes = [].concat.apply([], trafs.map(function (traf) { return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) { var id, scale, baseTime; // get the track id from the tfhd id = MP4Demuxer.readUint32(tfhd, 4); // assume a 90kHz clock if no timescale was specified scale = initData[id].timescale || 90e3; // get the base media decode time from the tfdt baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) { var version, result; version = tfdt.data[tfdt.start]; result = MP4Demuxer.readUint32(tfdt, 4); if (version === 1) { result *= Math.pow(2, 32); result += MP4Demuxer.readUint32(tfdt, 8); } return result; })[0]; // convert base time to seconds return baseTime / scale; }); })); // return the minimum result = Math.min.apply(null, baseTimes); return isFinite(result) ? result : 0; }; MP4Demuxer.offsetStartDTS = function offsetStartDTS(initData, fragment, timeOffset) { MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) { return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) { // get the track id from the tfhd var id = MP4Demuxer.readUint32(tfhd, 4); // assume a 90kHz clock if no timescale was specified var timescale = initData[id].timescale || 90e3; // get the base media decode time from the tfdt MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) { var version = tfdt.data[tfdt.start]; var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4); if (version === 0) { MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale); } else { baseMediaDecodeTime *= Math.pow(2, 32); baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8); baseMediaDecodeTime -= timeOffset * timescale; baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); MP4Demuxer.writeUint32(tfdt, 4, upper); MP4Demuxer.writeUint32(tfdt, 8, lower); } }); }); }); } // feed incoming data to the front of the parsing pipeline ; _proto.append = function append(data, timeOffset, contiguous, accurateTimeOffset) { var initData = this.initData; if (!initData) { this.resetInitSegment(data, this.audioCodec, this.videoCodec, false); initData = this.initData; } var startDTS, initPTS = this.initPTS; if (initPTS === undefined) { var _startDTS = MP4Demuxer.getStartDTS(initData, data); this.initPTS = initPTS = _startDTS - timeOffset; this.observer.trigger(_events__WEBPACK_IMPORTED_MODULE_1__["default"].INIT_PTS_FOUND, { initPTS: initPTS }); } MP4Demuxer.offsetStartDTS(initData, data, initPTS); startDTS = MP4Demuxer.getStartDTS(initData, data); this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data); }; _proto.destroy = function destroy() {}; return MP4Demuxer; }(); /* harmony default export */ __webpack_exports__["default"] = (MP4Demuxer); /***/ }), /***/ "./src/errors.ts": /*!***********************!*\ !*** ./src/errors.ts ***! \***********************/ /*! exports provided: ErrorTypes, ErrorDetails */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorTypes", function() { return ErrorTypes; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ErrorDetails", function() { return ErrorDetails; }); var ErrorTypes; /** * @enum {ErrorDetails} * @typedef {string} ErrorDetail */ (function (ErrorTypes) { ErrorTypes["NETWORK_ERROR"] = "networkError"; ErrorTypes["MEDIA_ERROR"] = "mediaError"; ErrorTypes["KEY_SYSTEM_ERROR"] = "keySystemError"; ErrorTypes["MUX_ERROR"] = "muxError"; ErrorTypes["OTHER_ERROR"] = "otherError"; })(ErrorTypes || (ErrorTypes = {})); var ErrorDetails; (function (ErrorDetails) { ErrorDetails["KEY_SYSTEM_NO_KEYS"] = "keySystemNoKeys"; ErrorDetails["KEY_SYSTEM_NO_ACCESS"] = "keySystemNoAccess"; ErrorDetails["KEY_SYSTEM_NO_SESSION"] = "keySystemNoSession"; ErrorDetails["KEY_SYSTEM_LICENSE_REQUEST_FAILED"] = "keySystemLicenseRequestFailed"; ErrorDetails["KEY_SYSTEM_NO_INIT_DATA"] = "keySystemNoInitData"; ErrorDetails["MANIFEST_LOAD_ERROR"] = "manifestLoadError"; ErrorDetails["MANIFEST_LOAD_TIMEOUT"] = "manifestLoadTimeOut"; ErrorDetails["MANIFEST_PARSING_ERROR"] = "manifestParsingError"; ErrorDetails["MANIFEST_INCOMPATIBLE_CODECS_ERROR"] = "manifestIncompatibleCodecsError"; ErrorDetails["LEVEL_EMPTY_ERROR"] = "levelEmptyError"; ErrorDetails["LEVEL_LOAD_ERROR"] = "levelLoadError"; ErrorDetails["LEVEL_LOAD_TIMEOUT"] = "levelLoadTimeOut"; ErrorDetails["LEVEL_SWITCH_ERROR"] = "levelSwitchError"; ErrorDetails["AUDIO_TRACK_LOAD_ERROR"] = "audioTrackLoadError"; ErrorDetails["AUDIO_TRACK_LOAD_TIMEOUT"] = "audioTrackLoadTimeOut"; ErrorDetails["FRAG_LOAD_ERROR"] = "fragLoadError"; ErrorDetails["FRAG_LOAD_TIMEOUT"] = "fragLoadTimeOut"; ErrorDetails["FRAG_DECRYPT_ERROR"] = "fragDecryptError"; ErrorDetails["FRAG_PARSING_ERROR"] = "fragParsingError"; ErrorDetails["REMUX_ALLOC_ERROR"] = "remuxAllocError"; ErrorDetails["KEY_LOAD_ERROR"] = "keyLoadError"; ErrorDetails["KEY_LOAD_TIMEOUT"] = "keyLoadTimeOut"; ErrorDetails["BUFFER_ADD_CODEC_ERROR"] = "bufferAddCodecError"; ErrorDetails["BUFFER_APPEND_ERROR"] = "bufferAppendError"; ErrorDetails["BUFFER_APPENDING_ERROR"] = "bufferAppendingError"; ErrorDetails["BUFFER_STALLED_ERROR"] = "bufferStalledError"; ErrorDetails["BUFFER_FULL_ERROR"] = "bufferFullError"; ErrorDetails["BUFFER_SEEK_OVER_HOLE"] = "bufferSeekOverHole"; ErrorDetails["BUFFER_NUDGE_ON_STALL"] = "bufferNudgeOnStall"; ErrorDetails["INTERNAL_EXCEPTION"] = "internalException"; })(ErrorDetails || (ErrorDetails = {})); /***/ }), /***/ "./src/events.js": /*!***********************!*\ !*** ./src/events.js ***! \***********************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /** * @readonly * @enum {string} */ var HlsEvents = { // fired before MediaSource is attaching to media element - data: { media } MEDIA_ATTACHING: 'hlsMediaAttaching', // fired when MediaSource has been succesfully attached to media element - data: { } MEDIA_ATTACHED: 'hlsMediaAttached', // fired before detaching MediaSource from media element - data: { } MEDIA_DETACHING: 'hlsMediaDetaching', // fired when MediaSource has been detached from media element - data: { } MEDIA_DETACHED: 'hlsMediaDetached', // fired when we buffer is going to be reset - data: { } BUFFER_RESET: 'hlsBufferReset', // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }} BUFFER_CODECS: 'hlsBufferCodecs', // fired when sourcebuffers have been created - data: { tracks : tracks } BUFFER_CREATED: 'hlsBufferCreated', // fired when we append a segment to the buffer - data: { segment: segment object } BUFFER_APPENDING: 'hlsBufferAppending', // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent} BUFFER_APPENDED: 'hlsBufferAppended', // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { } BUFFER_EOS: 'hlsBufferEos', // fired when the media buffer should be flushed - data { startOffset, endOffset } BUFFER_FLUSHING: 'hlsBufferFlushing', // fired when the media buffer has been flushed - data: { } BUFFER_FLUSHED: 'hlsBufferFlushed', // fired to signal that a manifest loading starts - data: { url : manifestURL} MANIFEST_LOADING: 'hlsManifestLoading', // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}} MANIFEST_LOADED: 'hlsManifestLoaded', // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest} MANIFEST_PARSED: 'hlsManifestParsed', // fired when a level switch is requested - data: { level : id of new level } LEVEL_SWITCHING: 'hlsLevelSwitching', // fired when a level switch is effective - data: { level : id of new level } LEVEL_SWITCHED: 'hlsLevelSwitched', // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded} LEVEL_LOADING: 'hlsLevelLoading', // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} } LEVEL_LOADED: 'hlsLevelLoaded', // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level } LEVEL_UPDATED: 'hlsLevelUpdated', // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated', // fired to notify that levels have changed after removing a level - data: { levels : [available quality levels] } LEVELS_UPDATED: 'hlsLevelsUpdated', // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks } AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated', // fired when an audio track switching is requested - data: { id : audio track id } AUDIO_TRACK_SWITCHING: 'hlsAudioTrackSwitching', // fired when an audio track switch actually occurs - data: { id : audio track id } AUDIO_TRACK_SWITCHED: 'hlsAudioTrackSwitched', // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id } AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading', // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } } AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded', // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks } SUBTITLE_TRACKS_UPDATED: 'hlsSubtitleTracksUpdated', // fired when an subtitle track switch occurs - data: { id : subtitle track id } SUBTITLE_TRACK_SWITCH: 'hlsSubtitleTrackSwitch', // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id } SUBTITLE_TRACK_LOADING: 'hlsSubtitleTrackLoading', // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : { trequest, tfirst, tload, mtime } } SUBTITLE_TRACK_LOADED: 'hlsSubtitleTrackLoaded', // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag } SUBTITLE_FRAG_PROCESSED: 'hlsSubtitleFragProcessed', // fired when a set of VTTCues to be managed externally has been parsed - data: { type: string, track: string, cues: [ VTTCue ] } CUES_PARSED: 'hlsCuesParsed', // fired when a text track to be managed externally is found - data: { tracks: [ { label: string, kind: string, default: boolean } ] } NON_NATIVE_TEXT_TRACKS_FOUND: 'hlsNonNativeTextTracksFound', // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, frag : fragment object } INIT_PTS_FOUND: 'hlsInitPtsFound', // fired when a fragment loading starts - data: { frag : fragment object } FRAG_LOADING: 'hlsFragLoading', // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } } FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress', // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object } FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted', // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } } FRAG_LOADED: 'hlsFragLoaded', // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, payload : fragment payload, stats : { tstart, tdecrypt } } FRAG_DECRYPTED: 'hlsFragDecrypted', // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment } FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment', // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] } FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata', // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] } FRAG_PARSING_METADATA: 'hlsFragParsingMetadata', // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null} FRAG_PARSING_DATA: 'hlsFragParsingData', // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object } FRAG_PARSED: 'hlsFragParsed', // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length, bwEstimate } } FRAG_BUFFERED: 'hlsFragBuffered', // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object } FRAG_CHANGED: 'hlsFragChanged', // Identifier for a FPS drop event - data: { curentDropped, currentDecoded, totalDroppedFrames } FPS_DROP: 'hlsFpsDrop', // triggered when FPS drop triggers auto level capping - data: { level, droppedlevel } FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping', // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data } ERROR: 'hlsError', // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { } DESTROYING: 'hlsDestroying', // fired when a decrypt key loading starts - data: { frag : fragment object } KEY_LOADING: 'hlsKeyLoading', // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length } } KEY_LOADED: 'hlsKeyLoaded', // fired upon stream controller state transitions - data: { previousState, nextState } STREAM_STATE_TRANSITION: 'hlsStreamStateTransition', // fired when the live back buffer is reached defined by the liveBackBufferLength config option - data : { bufferEnd: number } LIVE_BACK_BUFFER_REACHED: 'hlsLiveBackBufferReached' }; /* harmony default export */ __webpack_exports__["default"] = (HlsEvents); /***/ }), /***/ "./src/hls.ts": /*!*********************************!*\ !*** ./src/hls.ts + 50 modules ***! \*********************************/ /*! exports provided: default */ /*! ModuleConcatenation bailout: Cannot concat with ./src/crypt/decrypter.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/demux/demuxer-inline.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/demux/id3.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/demux/mp4demuxer.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/errors.ts because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/events.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/polyfills/number.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/get-self-scope.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./src/utils/logger.js because of ./src/demux/demuxer-worker.js */ /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/eventemitter3/index.js (<- Module is not an ECMAScript module) */ /*! ModuleConcatenation bailout: Cannot concat with ./node_modules/url-toolkit/src/url-toolkit.js (<- Module is not an ECMAScript module) */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // EXPORTS __webpack_require__.d(__webpack_exports__, "default", function() { return /* binding */ hls_Hls; }); // NAMESPACE OBJECT: ./src/utils/cues.ts var cues_namespaceObject = {}; __webpack_require__.r(cues_namespaceObject); __webpack_require__.d(cues_namespaceObject, "newCue", function() { return newCue; }); // EXTERNAL MODULE: ./node_modules/url-toolkit/src/url-toolkit.js var url_toolkit = __webpack_require__("./node_modules/url-toolkit/src/url-toolkit.js"); // EXTERNAL MODULE: ./src/errors.ts var errors = __webpack_require__("./src/errors.ts"); // EXTERNAL MODULE: ./src/polyfills/number.js var number = __webpack_require__("./src/polyfills/number.js"); // EXTERNAL MODULE: ./src/events.js var events = __webpack_require__("./src/events.js"); // EXTERNAL MODULE: ./src/utils/logger.js var logger = __webpack_require__("./src/utils/logger.js"); // CONCATENATED MODULE: ./src/event-handler.ts /* * * All objects in the event handling chain should inherit from this class * */ var FORBIDDEN_EVENT_NAMES = { 'hlsEventGeneric': true, 'hlsHandlerDestroying': true, 'hlsHandlerDestroyed': true }; var event_handler_EventHandler = /*#__PURE__*/function () { function EventHandler(hls) { this.hls = void 0; this.handledEvents = void 0; this.useGenericHandler = void 0; this.hls = hls; this.onEvent = this.onEvent.bind(this); for (var _len = arguments.length, events = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { events[_key - 1] = arguments[_key]; } this.handledEvents = events; this.useGenericHandler = true; this.registerListeners(); } var _proto = EventHandler.prototype; _proto.destroy = function destroy() { this.onHandlerDestroying(); this.unregisterListeners(); this.onHandlerDestroyed(); }; _proto.onHandlerDestroying = function onHandlerDestroying() {}; _proto.onHandlerDestroyed = function onHandlerDestroyed() {}; _proto.isEventHandler = function isEventHandler() { return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function'; }; _proto.registerListeners = function registerListeners() { if (this.isEventHandler()) { this.handledEvents.forEach(function (event) { if (FORBIDDEN_EVENT_NAMES[event]) { throw new Error('Forbidden event-name: ' + event); } this.hls.on(event, this.onEvent); }, this); } }; _proto.unregisterListeners = function unregisterListeners() { if (this.isEventHandler()) { this.handledEvents.forEach(function (event) { this.hls.off(event, this.onEvent); }, this); } } /** * arguments: event (string), data (any) */ ; _proto.onEvent = function onEvent(event, data) { this.onEventGeneric(event, data); }; _proto.onEventGeneric = function onEventGeneric(event, data) { var eventToFunction = function eventToFunction(event, data) { var funcName = 'on' + event.replace('hls', ''); if (typeof this[funcName] !== 'function') { throw new Error("Event " + event + " has no generic handler in this " + this.constructor.name + " class (tried " + funcName + ")"); } return this[funcName].bind(this, data); }; try { eventToFunction.call(this, event, data).call(); } catch (err) { logger["logger"].error("An internal error happened while handling event " + event + ". Error message: \"" + err.message + "\". Here is a stacktrace:", err); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].OTHER_ERROR, details: errors["ErrorDetails"].INTERNAL_EXCEPTION, fatal: false, event: event, err: err }); } }; return EventHandler; }(); /* harmony default export */ var event_handler = (event_handler_EventHandler); // CONCATENATED MODULE: ./src/types/loader.ts /** * `type` property values for this loaders' context object * @enum * */ var PlaylistContextType; /** * @enum {string} */ (function (PlaylistContextType) { PlaylistContextType["MANIFEST"] = "manifest"; PlaylistContextType["LEVEL"] = "level"; PlaylistContextType["AUDIO_TRACK"] = "audioTrack"; PlaylistContextType["SUBTITLE_TRACK"] = "subtitleTrack"; })(PlaylistContextType || (PlaylistContextType = {})); var PlaylistLevelType; (function (PlaylistLevelType) { PlaylistLevelType["MAIN"] = "main"; PlaylistLevelType["AUDIO"] = "audio"; PlaylistLevelType["SUBTITLE"] = "subtitle"; })(PlaylistLevelType || (PlaylistLevelType = {})); // EXTERNAL MODULE: ./src/demux/mp4demuxer.js var mp4demuxer = __webpack_require__("./src/demux/mp4demuxer.js"); // CONCATENATED MODULE: ./src/loader/level-key.ts function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var level_key_LevelKey = /*#__PURE__*/function () { function LevelKey(baseURI, relativeURI) { this._uri = null; this.baseuri = void 0; this.reluri = void 0; this.method = null; this.key = null; this.iv = null; this.baseuri = baseURI; this.reluri = relativeURI; } _createClass(LevelKey, [{ key: "uri", get: function get() { if (!this._uri && this.reluri) { this._uri = Object(url_toolkit["buildAbsoluteURL"])(this.baseuri, this.reluri, { alwaysNormalize: true }); } return this._uri; } }]); return LevelKey; }(); // CONCATENATED MODULE: ./src/loader/fragment.ts function fragment_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function fragment_createClass(Constructor, protoProps, staticProps) { if (protoProps) fragment_defineProperties(Constructor.prototype, protoProps); if (staticProps) fragment_defineProperties(Constructor, staticProps); return Constructor; } var ElementaryStreamTypes; (function (ElementaryStreamTypes) { ElementaryStreamTypes["AUDIO"] = "audio"; ElementaryStreamTypes["VIDEO"] = "video"; })(ElementaryStreamTypes || (ElementaryStreamTypes = {})); var fragment_Fragment = /*#__PURE__*/function () { function Fragment() { var _this$_elementaryStre; this._url = null; this._byteRange = null; this._decryptdata = null; this._elementaryStreams = (_this$_elementaryStre = {}, _this$_elementaryStre[ElementaryStreamTypes.AUDIO] = false, _this$_elementaryStre[ElementaryStreamTypes.VIDEO] = false, _this$_elementaryStre); this.deltaPTS = 0; this.rawProgramDateTime = null; this.programDateTime = null; this.title = null; this.tagList = []; this.cc = void 0; this.type = void 0; this.relurl = void 0; this.baseurl = void 0; this.duration = void 0; this.start = void 0; this.sn = 0; this.urlId = 0; this.level = 0; this.levelkey = void 0; this.loader = void 0; } var _proto = Fragment.prototype; // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array _proto.setByteRange = function setByteRange(value, previousFrag) { var params = value.split('@', 2); var byteRange = []; if (params.length === 1) { byteRange[0] = previousFrag ? previousFrag.byteRangeEndOffset : 0; } else { byteRange[0] = parseInt(params[1]); } byteRange[1] = parseInt(params[0]) + byteRange[0]; this._byteRange = byteRange; }; /** * @param {ElementaryStreamTypes} type */ _proto.addElementaryStream = function addElementaryStream(type) { this._elementaryStreams[type] = true; } /** * @param {ElementaryStreamTypes} type */ ; _proto.hasElementaryStream = function hasElementaryStream(type) { return this._elementaryStreams[type] === true; } /** * Utility method for parseLevelPlaylist to create an initialization vector for a given segment * @param {number} segmentNumber - segment number to generate IV with * @returns {Uint8Array} */ ; _proto.createInitializationVector = function createInitializationVector(segmentNumber) { var uint8View = new Uint8Array(16); for (var i = 12; i < 16; i++) { uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff; } return uint8View; } /** * Utility method for parseLevelPlaylist to get a fragment's decryption data from the currently parsed encryption key data * @param levelkey - a playlist's encryption info * @param segmentNumber - the fragment's segment number * @returns {LevelKey} - an object to be applied as a fragment's decryptdata */ ; _proto.setDecryptDataFromLevelKey = function setDecryptDataFromLevelKey(levelkey, segmentNumber) { var decryptdata = levelkey; if ((levelkey === null || levelkey === void 0 ? void 0 : levelkey.method) && levelkey.uri && !levelkey.iv) { decryptdata = new level_key_LevelKey(levelkey.baseuri, levelkey.reluri); decryptdata.method = levelkey.method; decryptdata.iv = this.createInitializationVector(segmentNumber); } return decryptdata; }; fragment_createClass(Fragment, [{ key: "url", get: function get() { if (!this._url && this.relurl) { this._url = Object(url_toolkit["buildAbsoluteURL"])(this.baseurl, this.relurl, { alwaysNormalize: true }); } return this._url; }, set: function set(value) { this._url = value; } }, { key: "byteRange", get: function get() { if (!this._byteRange) { return []; } return this._byteRange; } /** * @type {number} */ }, { key: "byteRangeStartOffset", get: function get() { return this.byteRange[0]; } }, { key: "byteRangeEndOffset", get: function get() { return this.byteRange[1]; } }, { key: "decryptdata", get: function get() { if (!this.levelkey && !this._decryptdata) { return null; } if (!this._decryptdata && this.levelkey) { var sn = this.sn; if (typeof sn !== 'number') { // We are fetching decryption data for a initialization segment // If the segment was encrypted with AES-128 // It must have an IV defined. We cannot substitute the Segment Number in. if (this.levelkey && this.levelkey.method === 'AES-128' && !this.levelkey.iv) { logger["logger"].warn("missing IV for initialization segment with method=\"" + this.levelkey.method + "\" - compliance issue"); } /* Be converted to a Number. 'initSegment' will become NaN. NaN, which when converted through ToInt32() -> +0. --- Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation. */ sn = 0; } this._decryptdata = this.setDecryptDataFromLevelKey(this.levelkey, sn); } return this._decryptdata; } }, { key: "endProgramDateTime", get: function get() { if (this.programDateTime === null) { return null; } if (!Object(number["isFiniteNumber"])(this.programDateTime)) { return null; } var duration = !Object(number["isFiniteNumber"])(this.duration) ? 0 : this.duration; return this.programDateTime + duration * 1000; } }, { key: "encrypted", get: function get() { return !!(this.decryptdata && this.decryptdata.uri !== null && this.decryptdata.key === null); } }]); return Fragment; }(); // CONCATENATED MODULE: ./src/loader/level.js function level_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function level_createClass(Constructor, protoProps, staticProps) { if (protoProps) level_defineProperties(Constructor.prototype, protoProps); if (staticProps) level_defineProperties(Constructor, staticProps); return Constructor; } var level_Level = /*#__PURE__*/function () { function Level(baseUrl) { // Please keep properties in alphabetical order this.endCC = 0; this.endSN = 0; this.fragments = []; this.initSegment = null; this.live = true; this.needSidxRanges = false; this.startCC = 0; this.startSN = 0; this.startTimeOffset = null; this.targetduration = 0; this.totalduration = 0; this.type = null; this.url = baseUrl; this.version = null; } level_createClass(Level, [{ key: "hasProgramDateTime", get: function get() { return !!(this.fragments[0] && Object(number["isFiniteNumber"])(this.fragments[0].programDateTime)); } }]); return Level; }(); // CONCATENATED MODULE: ./src/utils/attr-list.js var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; // eslint-disable-line no-useless-escape var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; // eslint-disable-line no-useless-escape // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js var AttrList = /*#__PURE__*/function () { function AttrList(attrs) { if (typeof attrs === 'string') { attrs = AttrList.parseAttrList(attrs); } for (var attr in attrs) { if (attrs.hasOwnProperty(attr)) { this[attr] = attrs[attr]; } } } var _proto = AttrList.prototype; _proto.decimalInteger = function decimalInteger(attrName) { var intValue = parseInt(this[attrName], 10); if (intValue > Number.MAX_SAFE_INTEGER) { return Infinity; } return intValue; }; _proto.hexadecimalInteger = function hexadecimalInteger(attrName) { if (this[attrName]) { var stringValue = (this[attrName] || '0x').slice(2); stringValue = (stringValue.length & 1 ? '0' : '') + stringValue; var value = new Uint8Array(stringValue.length / 2); for (var i = 0; i < stringValue.length / 2; i++) { value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); } return value; } else { return null; } }; _proto.hexadecimalIntegerAsNumber = function hexadecimalIntegerAsNumber(attrName) { var intValue = parseInt(this[attrName], 16); if (intValue > Number.MAX_SAFE_INTEGER) { return Infinity; } return intValue; }; _proto.decimalFloatingPoint = function decimalFloatingPoint(attrName) { return parseFloat(this[attrName]); }; _proto.enumeratedString = function enumeratedString(attrName) { return this[attrName]; }; _proto.decimalResolution = function decimalResolution(attrName) { var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]); if (res === null) { return undefined; } return { width: parseInt(res[1], 10), height: parseInt(res[2], 10) }; }; AttrList.parseAttrList = function parseAttrList(input) { var match, attrs = {}; ATTR_LIST_REGEX.lastIndex = 0; while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { var value = match[2], quote = '"'; if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) { value = value.slice(1, -1); } attrs[match[1]] = value; } return attrs; }; return AttrList; }(); /* harmony default export */ var attr_list = (AttrList); // CONCATENATED MODULE: ./src/utils/codecs.ts // from http://mp4ra.org/codecs.html var sampleEntryCodesISO = { audio: { 'a3ds': true, 'ac-3': true, 'ac-4': true, 'alac': true, 'alaw': true, 'dra1': true, 'dts+': true, 'dts-': true, 'dtsc': true, 'dtse': true, 'dtsh': true, 'ec-3': true, 'enca': true, 'g719': true, 'g726': true, 'm4ae': true, 'mha1': true, 'mha2': true, 'mhm1': true, 'mhm2': true, 'mlpa': true, 'mp4a': true, 'raw ': true, 'Opus': true, 'samr': true, 'sawb': true, 'sawp': true, 'sevc': true, 'sqcp': true, 'ssmv': true, 'twos': true, 'ulaw': true }, video: { 'avc1': true, 'avc2': true, 'avc3': true, 'avc4': true, 'avcp': true, 'drac': true, 'dvav': true, 'dvhe': true, 'encv': true, 'hev1': true, 'hvc1': true, 'mjp2': true, 'mp4v': true, 'mvc1': true, 'mvc2': true, 'mvc3': true, 'mvc4': true, 'resv': true, 'rv60': true, 's263': true, 'svc1': true, 'svc2': true, 'vc-1': true, 'vp08': true, 'vp09': true } }; function isCodecType(codec, type) { var typeCodes = sampleEntryCodesISO[type]; return !!typeCodes && typeCodes[codec.slice(0, 4)] === true; } function isCodecSupportedInMp4(codec, type) { return MediaSource.isTypeSupported((type || 'video') + "/mp4;codecs=\"" + codec + "\""); } // CONCATENATED MODULE: ./src/loader/m3u8-parser.ts /** * M3U8 parser * @module */ // https://regex101.com is your friend var MASTER_PLAYLIST_REGEX = /(?:#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)|#EXT-X-SESSION-DATA:([^\n\r]*)[\r\n]+)/g; var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, // duration (#EXTINF:,), group 1 => duration, group 2 => title /|(?!#)([\S+ ?]+)/.source, // segment URI, group 3 => the URI (note newline is not eaten) /|#EXT-X-BYTERANGE:*(.+)/.source, // next segment's byterange, group 4 => range spec (x@y) /|#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, // next segment's program date/time group 5 => the datetime spec /|#.*/.source // All other non-segment oriented tags will match with all groups empty ].join(''), 'g'); var LEVEL_PLAYLIST_REGEX_SLOW = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:(#)([^:]*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/; var MP4_REGEX_SUFFIX = /\.(mp4|m4s|m4v|m4a)$/i; var m3u8_parser_M3U8Parser = /*#__PURE__*/function () { function M3U8Parser() {} M3U8Parser.findGroup = function findGroup(groups, mediaGroupId) { for (var i = 0; i < groups.length; i++) { var group = groups[i]; if (group.id === mediaGroupId) { return group; } } }; M3U8Parser.convertAVC1ToAVCOTI = function convertAVC1ToAVCOTI(codec) { var avcdata = codec.split('.'); var result; if (avcdata.length > 2) { result = avcdata.shift() + '.'; result += parseInt(avcdata.shift()).toString(16); result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4); } else { result = codec; } return result; }; M3U8Parser.resolve = function resolve(url, baseUrl) { return url_toolkit["buildAbsoluteURL"](baseUrl, url, { alwaysNormalize: true }); }; M3U8Parser.parseMasterPlaylist = function parseMasterPlaylist(string, baseurl) { // TODO(typescript-level) var levels = []; var sessionData = {}; var hasSessionData = false; MASTER_PLAYLIST_REGEX.lastIndex = 0; // TODO(typescript-level) function setCodecs(codecs, level) { ['video', 'audio'].forEach(function (type) { var filtered = codecs.filter(function (codec) { return isCodecType(codec, type); }); if (filtered.length) { var preferred = filtered.filter(function (codec) { return codec.lastIndexOf('avc1', 0) === 0 || codec.lastIndexOf('mp4a', 0) === 0; }); level[type + "Codec"] = preferred.length > 0 ? preferred[0] : filtered[0]; // remove from list codecs = codecs.filter(function (codec) { return filtered.indexOf(codec) === -1; }); } }); level.unknownCodecs = codecs; } var result; while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { if (result[1]) { // '#EXT-X-STREAM-INF' is found, parse level tag in group 1 // TODO(typescript-level) var level = {}; var attrs = level.attrs = new attr_list(result[1]); level.url = M3U8Parser.resolve(result[2], baseurl); var resolution = attrs.decimalResolution('RESOLUTION'); if (resolution) { level.width = resolution.width; level.height = resolution.height; } level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH'); level.name = attrs.NAME; setCodecs([].concat((attrs.CODECS || '').split(/[ ,]+/)), level); if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) { level.videoCodec = M3U8Parser.convertAVC1ToAVCOTI(level.videoCodec); } levels.push(level); } else if (result[3]) { // '#EXT-X-SESSION-DATA' is found, parse session data in group 3 var sessionAttrs = new attr_list(result[3]); if (sessionAttrs['DATA-ID']) { hasSessionData = true; sessionData[sessionAttrs['DATA-ID']] = sessionAttrs; } } } return { levels: levels, sessionData: hasSessionData ? sessionData : null }; }; M3U8Parser.parseMasterPlaylistMedia = function parseMasterPlaylistMedia(string, baseurl, type, audioGroups) { if (audioGroups === void 0) { audioGroups = []; } var result; var medias = []; var id = 0; MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { var attrs = new attr_list(result[1]); if (attrs.TYPE === type) { var media = { attrs: attrs, id: id++, groupId: attrs['GROUP-ID'], instreamId: attrs['INSTREAM-ID'], name: attrs.NAME || attrs.LANGUAGE, type: type, default: attrs.DEFAULT === 'YES', autoselect: attrs.AUTOSELECT === 'YES', forced: attrs.FORCED === 'YES', lang: attrs.LANGUAGE }; if (attrs.URI) { media.url = M3U8Parser.resolve(attrs.URI, baseurl); } if (audioGroups.length) { // If there are audio groups signalled in the manifest, let's look for a matching codec string for this track var groupCodec = M3U8Parser.findGroup(audioGroups, media.groupId); // If we don't find the track signalled, lets use the first audio groups codec we have // Acting as a best guess media.audioCodec = groupCodec ? groupCodec.codec : audioGroups[0].codec; } medias.push(media); } } return medias; }; M3U8Parser.parseLevelPlaylist = function parseLevelPlaylist(string, baseurl, id, type, levelUrlId) { var currentSN = 0; var totalduration = 0; var level = new level_Level(baseurl); var discontinuityCounter = 0; var prevFrag = null; var frag = new fragment_Fragment(); var result; var i; var levelkey; var firstPdtIndex = null; LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0; while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) { var duration = result[1]; if (duration) { // INF frag.duration = parseFloat(duration); // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 var title = (' ' + result[2]).slice(1); frag.title = title || null; frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); } else if (result[3]) { // url if (Object(number["isFiniteNumber"])(frag.duration)) { var sn = currentSN++; frag.type = type; frag.start = totalduration; if (levelkey) { frag.levelkey = levelkey; } frag.sn = sn; frag.level = id; frag.cc = discontinuityCounter; frag.urlId = levelUrlId; frag.baseurl = baseurl; // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 frag.relurl = (' ' + result[3]).slice(1); assignProgramDateTime(frag, prevFrag); level.fragments.push(frag); prevFrag = frag; totalduration += frag.duration; frag = new fragment_Fragment(); } } else if (result[4]) { // X-BYTERANGE var data = (' ' + result[4]).slice(1); if (prevFrag) { frag.setByteRange(data, prevFrag); } else { frag.setByteRange(data); } } else if (result[5]) { // PROGRAM-DATE-TIME // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 frag.rawProgramDateTime = (' ' + result[5]).slice(1); frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]); if (firstPdtIndex === null) { firstPdtIndex = level.fragments.length; } } else { result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); if (!result) { logger["logger"].warn('No matches on slow regex match for level playlist!'); continue; } for (i = 1; i < result.length; i++) { if (typeof result[i] !== 'undefined') { break; } } // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 var value1 = (' ' + result[i + 1]).slice(1); var value2 = (' ' + result[i + 2]).slice(1); switch (result[i]) { case '#': frag.tagList.push(value2 ? [value1, value2] : [value1]); break; case 'PLAYLIST-TYPE': level.type = value1.toUpperCase(); break; case 'MEDIA-SEQUENCE': currentSN = level.startSN = parseInt(value1); break; case 'TARGETDURATION': level.targetduration = parseFloat(value1); break; case 'VERSION': level.version = parseInt(value1); break; case 'EXTM3U': break; case 'ENDLIST': level.live = false; break; case 'DIS': discontinuityCounter++; frag.tagList.push(['DIS']); break; case 'DISCONTINUITY-SEQ': discontinuityCounter = parseInt(value1); break; case 'KEY': { // https://tools.ietf.org/html/rfc8216#section-4.3.2.4 var decryptparams = value1; var keyAttrs = new attr_list(decryptparams); var decryptmethod = keyAttrs.enumeratedString('METHOD'); var decrypturi = keyAttrs.URI; var decryptiv = keyAttrs.hexadecimalInteger('IV'); // From RFC: This attribute is OPTIONAL; its absence indicates an implicit value of "identity". var decryptkeyformat = keyAttrs.KEYFORMAT || 'identity'; if (decryptkeyformat === 'com.apple.streamingkeydelivery') { logger["logger"].warn('Keyformat com.apple.streamingkeydelivery is not supported'); continue; } if (decryptmethod) { levelkey = new level_key_LevelKey(baseurl, decrypturi); if (decrypturi && ['AES-128', 'SAMPLE-AES', 'SAMPLE-AES-CENC'].indexOf(decryptmethod) >= 0) { levelkey.method = decryptmethod; levelkey.key = null; // Initialization Vector (IV) levelkey.iv = decryptiv; } } break; } case 'START': { var startAttrs = new attr_list(value1); var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); // TIME-OFFSET can be 0 if (Object(number["isFiniteNumber"])(startTimeOffset)) { level.startTimeOffset = startTimeOffset; } break; } case 'MAP': { var mapAttrs = new attr_list(value1); frag.relurl = mapAttrs.URI; if (mapAttrs.BYTERANGE) { frag.setByteRange(mapAttrs.BYTERANGE); } frag.baseurl = baseurl; frag.level = id; frag.type = type; frag.sn = 'initSegment'; level.initSegment = frag; frag = new fragment_Fragment(); frag.rawProgramDateTime = level.initSegment.rawProgramDateTime; break; } default: logger["logger"].warn("line parsed but not handled: " + result); break; } } } frag = prevFrag; // logger.log('found ' + level.fragments.length + ' fragments'); if (frag && !frag.relurl) { level.fragments.pop(); totalduration -= frag.duration; } level.totalduration = totalduration; level.averagetargetduration = totalduration / level.fragments.length; level.endSN = currentSN - 1; level.startCC = level.fragments[0] ? level.fragments[0].cc : 0; level.endCC = discontinuityCounter; if (!level.initSegment && level.fragments.length) { // this is a bit lurky but HLS really has no other way to tell us // if the fragments are TS or MP4, except if we download them :/ // but this is to be able to handle SIDX. if (level.fragments.every(function (frag) { return MP4_REGEX_SUFFIX.test(frag.relurl); })) { logger["logger"].warn('MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX'); frag = new fragment_Fragment(); frag.relurl = level.fragments[0].relurl; frag.baseurl = baseurl; frag.level = id; frag.type = type; frag.sn = 'initSegment'; level.initSegment = frag; level.needSidxRanges = true; } } /** * Backfill any missing PDT values "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after one or more Media Segment URIs, the client SHOULD extrapolate backward from that tag (using EXTINF durations and/or media timestamps) to associate dates with those segments." * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs * computed. */ if (firstPdtIndex) { backfillProgramDateTimes(level.fragments, firstPdtIndex); } return level; }; return M3U8Parser; }(); function backfillProgramDateTimes(fragments, startIndex) { var fragPrev = fragments[startIndex]; for (var i = startIndex - 1; i >= 0; i--) { var frag = fragments[i]; frag.programDateTime = fragPrev.programDateTime - frag.duration * 1000; fragPrev = frag; } } function assignProgramDateTime(frag, prevFrag) { if (frag.rawProgramDateTime) { frag.programDateTime = Date.parse(frag.rawProgramDateTime); } else if (prevFrag === null || prevFrag === void 0 ? void 0 : prevFrag.programDateTime) { frag.programDateTime = prevFrag.endProgramDateTime; } if (!Object(number["isFiniteNumber"])(frag.programDateTime)) { frag.programDateTime = null; frag.rawProgramDateTime = null; } } // CONCATENATED MODULE: ./src/loader/playlist-loader.ts function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * PlaylistLoader - delegate for media manifest/playlist loading tasks. Takes care of parsing media to internal data-models. * * Once loaded, dispatches events with parsed data-models of manifest/levels/audio/subtitle tracks. * * Uses loader(s) set in config to do actual internal loading of resource tasks. * * @module * */ var _window = window, performance = _window.performance; /** * @constructor */ var playlist_loader_PlaylistLoader = /*#__PURE__*/function (_EventHandler) { _inheritsLoose(PlaylistLoader, _EventHandler); /** * @constructs * @param {Hls} hls */ function PlaylistLoader(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MANIFEST_LOADING, events["default"].LEVEL_LOADING, events["default"].AUDIO_TRACK_LOADING, events["default"].SUBTITLE_TRACK_LOADING) || this; _this.loaders = {}; return _this; } /** * @param {PlaylistContextType} type * @returns {boolean} */ PlaylistLoader.canHaveQualityLevels = function canHaveQualityLevels(type) { return type !== PlaylistContextType.AUDIO_TRACK && type !== PlaylistContextType.SUBTITLE_TRACK; } /** * Map context.type to LevelType * @param {PlaylistLoaderContext} context * @returns {LevelType} */ ; PlaylistLoader.mapContextToLevelType = function mapContextToLevelType(context) { var type = context.type; switch (type) { case PlaylistContextType.AUDIO_TRACK: return PlaylistLevelType.AUDIO; case PlaylistContextType.SUBTITLE_TRACK: return PlaylistLevelType.SUBTITLE; default: return PlaylistLevelType.MAIN; } }; PlaylistLoader.getResponseUrl = function getResponseUrl(response, context) { var url = response.url; // responseURL not supported on some browsers (it is used to detect URL redirection) // data-uri mode also not supported (but no need to detect redirection) if (url === undefined || url.indexOf('data:') === 0) { // fallback to initial URL url = context.url; } return url; } /** * Returns defaults or configured loader-type overloads (pLoader and loader config params) * Default loader is XHRLoader (see utils) * @param {PlaylistLoaderContext} context * @returns {Loader} or other compatible configured overload */ ; var _proto = PlaylistLoader.prototype; _proto.createInternalLoader = function createInternalLoader(context) { var config = this.hls.config; var PLoader = config.pLoader; var Loader = config.loader; // TODO(typescript-config): Verify once config is typed that InternalLoader always returns a Loader var InternalLoader = PLoader || Loader; var loader = new InternalLoader(config); // TODO - Do we really need to assign the instance or if the dep has been lost context.loader = loader; this.loaders[context.type] = loader; return loader; }; _proto.getInternalLoader = function getInternalLoader(context) { return this.loaders[context.type]; }; _proto.resetInternalLoader = function resetInternalLoader(contextType) { if (this.loaders[contextType]) { delete this.loaders[contextType]; } } /** * Call `destroy` on all internal loader instances mapped (one per context type) */ ; _proto.destroyInternalLoaders = function destroyInternalLoaders() { for (var contextType in this.loaders) { var loader = this.loaders[contextType]; if (loader) { loader.destroy(); } this.resetInternalLoader(contextType); } }; _proto.destroy = function destroy() { this.destroyInternalLoaders(); _EventHandler.prototype.destroy.call(this); }; _proto.onManifestLoading = function onManifestLoading(data) { this.load({ url: data.url, type: PlaylistContextType.MANIFEST, level: 0, id: null, responseType: 'text' }); }; _proto.onLevelLoading = function onLevelLoading(data) { this.load({ url: data.url, type: PlaylistContextType.LEVEL, level: data.level, id: data.id, responseType: 'text' }); }; _proto.onAudioTrackLoading = function onAudioTrackLoading(data) { this.load({ url: data.url, type: PlaylistContextType.AUDIO_TRACK, level: null, id: data.id, responseType: 'text' }); }; _proto.onSubtitleTrackLoading = function onSubtitleTrackLoading(data) { this.load({ url: data.url, type: PlaylistContextType.SUBTITLE_TRACK, level: null, id: data.id, responseType: 'text' }); }; _proto.load = function load(context) { var config = this.hls.config; logger["logger"].debug("Loading playlist of type " + context.type + ", level: " + context.level + ", id: " + context.id); // Check if a loader for this context already exists var loader = this.getInternalLoader(context); if (loader) { var loaderContext = loader.context; if (loaderContext && loaderContext.url === context.url) { // same URL can't overlap logger["logger"].trace('playlist request ongoing'); return false; } else { logger["logger"].warn("aborting previous loader for type: " + context.type); loader.abort(); } } var maxRetry; var timeout; var retryDelay; var maxRetryDelay; // apply different configs for retries depending on // context (manifest, level, audio/subs playlist) switch (context.type) { case PlaylistContextType.MANIFEST: maxRetry = config.manifestLoadingMaxRetry; timeout = config.manifestLoadingTimeOut; retryDelay = config.manifestLoadingRetryDelay; maxRetryDelay = config.manifestLoadingMaxRetryTimeout; break; case PlaylistContextType.LEVEL: // Disable internal loader retry logic, since we are managing retries in Level Controller maxRetry = 0; maxRetryDelay = 0; retryDelay = 0; timeout = config.levelLoadingTimeOut; // TODO Introduce retry settings for audio-track and subtitle-track, it should not use level retry config break; default: maxRetry = config.levelLoadingMaxRetry; timeout = config.levelLoadingTimeOut; retryDelay = config.levelLoadingRetryDelay; maxRetryDelay = config.levelLoadingMaxRetryTimeout; break; } loader = this.createInternalLoader(context); var loaderConfig = { timeout: timeout, maxRetry: maxRetry, retryDelay: retryDelay, maxRetryDelay: maxRetryDelay }; var loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) }; logger["logger"].debug("Calling internal loader delegate for URL: " + context.url); loader.load(context, loaderConfig, loaderCallbacks); return true; }; _proto.loadsuccess = function loadsuccess(response, stats, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } if (context.isSidxRequest) { this._handleSidxRequest(response, context); this._handlePlaylistLoaded(response, stats, context, networkDetails); return; } this.resetInternalLoader(context.type); if (typeof response.data !== 'string') { throw new Error('expected responseType of "text" for PlaylistLoader'); } var string = response.data; stats.tload = performance.now(); // stats.mtime = new Date(target.getResponseHeader('Last-Modified')); // Validate if it is an M3U8 at all if (string.indexOf('#EXTM3U') !== 0) { this._handleManifestParsingError(response, context, 'no EXTM3U delimiter', networkDetails); return; } // Check if chunk-list or master. handle empty chunk list case (first EXTINF not signaled, but TARGETDURATION present) if (string.indexOf('#EXTINF:') > 0 || string.indexOf('#EXT-X-TARGETDURATION:') > 0) { this._handleTrackOrLevelPlaylist(response, stats, context, networkDetails); } else { this._handleMasterPlaylist(response, stats, context, networkDetails); } }; _proto.loaderror = function loaderror(response, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } this._handleNetworkError(context, networkDetails, false, response); }; _proto.loadtimeout = function loadtimeout(stats, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } this._handleNetworkError(context, networkDetails, true); } // TODO(typescript-config): networkDetails can currently be a XHR or Fetch impl, // but with custom loaders it could be generic investigate this further when config is typed ; _proto._handleMasterPlaylist = function _handleMasterPlaylist(response, stats, context, networkDetails) { var hls = this.hls; var string = response.data; var url = PlaylistLoader.getResponseUrl(response, context); var _M3U8Parser$parseMast = m3u8_parser_M3U8Parser.parseMasterPlaylist(string, url), levels = _M3U8Parser$parseMast.levels, sessionData = _M3U8Parser$parseMast.sessionData; if (!levels.length) { this._handleManifestParsingError(response, context, 'no level found in manifest', networkDetails); return; } // multi level playlist, parse level info var audioGroups = levels.map(function (level) { return { id: level.attrs.AUDIO, codec: level.audioCodec }; }); var audioTracks = m3u8_parser_M3U8Parser.parseMasterPlaylistMedia(string, url, 'AUDIO', audioGroups); var subtitles = m3u8_parser_M3U8Parser.parseMasterPlaylistMedia(string, url, 'SUBTITLES'); var captions = m3u8_parser_M3U8Parser.parseMasterPlaylistMedia(string, url, 'CLOSED-CAPTIONS'); if (audioTracks.length) { // check if we have found an audio track embedded in main playlist (audio track without URI attribute) var embeddedAudioFound = false; audioTracks.forEach(function (audioTrack) { if (!audioTrack.url) { embeddedAudioFound = true; } }); // if no embedded audio track defined, but audio codec signaled in quality level, // we need to signal this main audio track this could happen with playlists with // alt audio rendition in which quality levels (main) // contains both audio+video. but with mixed audio track not signaled if (embeddedAudioFound === false && levels[0].audioCodec && !levels[0].attrs.AUDIO) { logger["logger"].log('audio codec signaled in quality level, but no embedded audio track signaled, create one'); audioTracks.unshift({ type: 'main', name: 'main', default: false, autoselect: false, forced: false, id: -1, attrs: {}, url: '' }); } } hls.trigger(events["default"].MANIFEST_LOADED, { levels: levels, audioTracks: audioTracks, subtitles: subtitles, captions: captions, url: url, stats: stats, networkDetails: networkDetails, sessionData: sessionData }); }; _proto._handleTrackOrLevelPlaylist = function _handleTrackOrLevelPlaylist(response, stats, context, networkDetails) { var hls = this.hls; var id = context.id, level = context.level, type = context.type; var url = PlaylistLoader.getResponseUrl(response, context); // if the values are null, they will result in the else conditional var levelUrlId = Object(number["isFiniteNumber"])(id) ? id : 0; var levelId = Object(number["isFiniteNumber"])(level) ? level : levelUrlId; var levelType = PlaylistLoader.mapContextToLevelType(context); var levelDetails = m3u8_parser_M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, levelUrlId); // set stats on level structure // TODO(jstackhouse): why? mixing concerns, is it just treated as value bag? levelDetails.tload = stats.tload; if (!levelDetails.fragments.length) { hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].LEVEL_EMPTY_ERROR, fatal: false, url: url, reason: 'no fragments found in level', level: typeof context.level === 'number' ? context.level : undefined }); return; } // We have done our first request (Manifest-type) and receive // not a master playlist but a chunk-list (track/level) // We fire the manifest-loaded event anyway with the parsed level-details // by creating a single-level structure for it. if (type === PlaylistContextType.MANIFEST) { var singleLevel = { url: url, details: levelDetails }; hls.trigger(events["default"].MANIFEST_LOADED, { levels: [singleLevel], audioTracks: [], url: url, stats: stats, networkDetails: networkDetails, sessionData: null }); } // save parsing time stats.tparsed = performance.now(); // in case we need SIDX ranges // return early after calling load for // the SIDX box. if (levelDetails.needSidxRanges) { var sidxUrl = levelDetails.initSegment.url; this.load({ url: sidxUrl, isSidxRequest: true, type: type, level: level, levelDetails: levelDetails, id: id, rangeStart: 0, rangeEnd: 2048, responseType: 'arraybuffer' }); return; } // extend the context with the new levelDetails property context.levelDetails = levelDetails; this._handlePlaylistLoaded(response, stats, context, networkDetails); }; _proto._handleSidxRequest = function _handleSidxRequest(response, context) { if (typeof response.data === 'string') { throw new Error('sidx request must be made with responseType of array buffer'); } var sidxInfo = mp4demuxer["default"].parseSegmentIndex(new Uint8Array(response.data)); // if provided fragment does not contain sidx, early return if (!sidxInfo) { return; } var sidxReferences = sidxInfo.references; var levelDetails = context.levelDetails; sidxReferences.forEach(function (segmentRef, index) { var segRefInfo = segmentRef.info; if (!levelDetails) { return; } var frag = levelDetails.fragments[index]; if (frag.byteRange.length === 0) { frag.setByteRange(String(1 + segRefInfo.end - segRefInfo.start) + '@' + String(segRefInfo.start)); } }); if (levelDetails) { levelDetails.initSegment.setByteRange(String(sidxInfo.moovEndOffset) + '@0'); } }; _proto._handleManifestParsingError = function _handleManifestParsingError(response, context, reason, networkDetails) { this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].MANIFEST_PARSING_ERROR, fatal: true, url: response.url, reason: reason, networkDetails: networkDetails }); }; _proto._handleNetworkError = function _handleNetworkError(context, networkDetails, timeout, response) { if (timeout === void 0) { timeout = false; } if (response === void 0) { response = null; } logger["logger"].info("A network error occured while loading a " + context.type + "-type playlist"); var details; var fatal; var loader = this.getInternalLoader(context); switch (context.type) { case PlaylistContextType.MANIFEST: details = timeout ? errors["ErrorDetails"].MANIFEST_LOAD_TIMEOUT : errors["ErrorDetails"].MANIFEST_LOAD_ERROR; fatal = true; break; case PlaylistContextType.LEVEL: details = timeout ? errors["ErrorDetails"].LEVEL_LOAD_TIMEOUT : errors["ErrorDetails"].LEVEL_LOAD_ERROR; fatal = false; break; case PlaylistContextType.AUDIO_TRACK: details = timeout ? errors["ErrorDetails"].AUDIO_TRACK_LOAD_TIMEOUT : errors["ErrorDetails"].AUDIO_TRACK_LOAD_ERROR; fatal = false; break; default: // details = ...? fatal = false; } if (loader) { loader.abort(); this.resetInternalLoader(context.type); } // TODO(typescript-events): when error events are handled, type this var errorData = { type: errors["ErrorTypes"].NETWORK_ERROR, details: details, fatal: fatal, url: context.url, loader: loader, context: context, networkDetails: networkDetails }; if (response) { errorData.response = response; } this.hls.trigger(events["default"].ERROR, errorData); }; _proto._handlePlaylistLoaded = function _handlePlaylistLoaded(response, stats, context, networkDetails) { var type = context.type, level = context.level, id = context.id, levelDetails = context.levelDetails; if (!levelDetails || !levelDetails.targetduration) { this._handleManifestParsingError(response, context, 'invalid target duration', networkDetails); return; } var canHaveLevels = PlaylistLoader.canHaveQualityLevels(context.type); if (canHaveLevels) { this.hls.trigger(events["default"].LEVEL_LOADED, { details: levelDetails, level: level || 0, id: id || 0, stats: stats, networkDetails: networkDetails }); } else { switch (type) { case PlaylistContextType.AUDIO_TRACK: this.hls.trigger(events["default"].AUDIO_TRACK_LOADED, { details: levelDetails, id: id, stats: stats, networkDetails: networkDetails }); break; case PlaylistContextType.SUBTITLE_TRACK: this.hls.trigger(events["default"].SUBTITLE_TRACK_LOADED, { details: levelDetails, id: id, stats: stats, networkDetails: networkDetails }); break; } } }; return PlaylistLoader; }(event_handler); /* harmony default export */ var playlist_loader = (playlist_loader_PlaylistLoader); // CONCATENATED MODULE: ./src/loader/fragment-loader.js function fragment_loader_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Fragment Loader */ var fragment_loader_FragmentLoader = /*#__PURE__*/function (_EventHandler) { fragment_loader_inheritsLoose(FragmentLoader, _EventHandler); function FragmentLoader(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].FRAG_LOADING) || this; _this.loaders = {}; return _this; } var _proto = FragmentLoader.prototype; _proto.destroy = function destroy() { var loaders = this.loaders; for (var loaderName in loaders) { var loader = loaders[loaderName]; if (loader) { loader.destroy(); } } this.loaders = {}; _EventHandler.prototype.destroy.call(this); }; _proto.onFragLoading = function onFragLoading(data) { var frag = data.frag, type = frag.type, loaders = this.loaders, config = this.hls.config, FragmentILoader = config.fLoader, DefaultILoader = config.loader; // reset fragment state frag.loaded = 0; var loader = loaders[type]; if (loader) { logger["logger"].warn("abort previous fragment loader for type: " + type); loader.abort(); } loader = loaders[type] = frag.loader = config.fLoader ? new FragmentILoader(config) : new DefaultILoader(config); var loaderContext, loaderConfig, loaderCallbacks; loaderContext = { url: frag.url, frag: frag, responseType: 'arraybuffer', progressData: false }; var start = frag.byteRangeStartOffset, end = frag.byteRangeEndOffset; if (Object(number["isFiniteNumber"])(start) && Object(number["isFiniteNumber"])(end)) { loaderContext.rangeStart = start; loaderContext.rangeEnd = end; } loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: 0, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this), onProgress: this.loadprogress.bind(this) }; loader.load(loaderContext, loaderConfig, loaderCallbacks); }; _proto.loadsuccess = function loadsuccess(response, stats, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } var payload = response.data, frag = context.frag; // detach fragment loader on load success frag.loader = undefined; this.loaders[frag.type] = undefined; this.hls.trigger(events["default"].FRAG_LOADED, { payload: payload, frag: frag, stats: stats, networkDetails: networkDetails }); }; _proto.loaderror = function loaderror(response, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } var frag = context.frag; var loader = frag.loader; if (loader) { loader.abort(); } this.loaders[frag.type] = undefined; this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: response, networkDetails: networkDetails }); }; _proto.loadtimeout = function loadtimeout(stats, context, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } var frag = context.frag; var loader = frag.loader; if (loader) { loader.abort(); } this.loaders[frag.type] = undefined; this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].FRAG_LOAD_TIMEOUT, fatal: false, frag: context.frag, networkDetails: networkDetails }); } // data will be used for progressive parsing ; _proto.loadprogress = function loadprogress(stats, context, data, networkDetails) { if (networkDetails === void 0) { networkDetails = null; } // jshint ignore:line var frag = context.frag; frag.loaded = stats.loaded; this.hls.trigger(events["default"].FRAG_LOAD_PROGRESS, { frag: frag, stats: stats, networkDetails: networkDetails }); }; return FragmentLoader; }(event_handler); /* harmony default export */ var fragment_loader = (fragment_loader_FragmentLoader); // CONCATENATED MODULE: ./src/loader/key-loader.ts function key_loader_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Decrypt key Loader */ var key_loader_KeyLoader = /*#__PURE__*/function (_EventHandler) { key_loader_inheritsLoose(KeyLoader, _EventHandler); function KeyLoader(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].KEY_LOADING) || this; _this.loaders = {}; _this.decryptkey = null; _this.decrypturl = null; return _this; } var _proto = KeyLoader.prototype; _proto.destroy = function destroy() { for (var loaderName in this.loaders) { var loader = this.loaders[loaderName]; if (loader) { loader.destroy(); } } this.loaders = {}; _EventHandler.prototype.destroy.call(this); }; _proto.onKeyLoading = function onKeyLoading(data) { var frag = data.frag; var type = frag.type; var loader = this.loaders[type]; if (!frag.decryptdata) { logger["logger"].warn('Missing decryption data on fragment in onKeyLoading'); return; } // Load the key if the uri is different from previous one, or if the decrypt key has not yet been retrieved var uri = frag.decryptdata.uri; if (uri !== this.decrypturl || this.decryptkey === null) { var config = this.hls.config; if (loader) { logger["logger"].warn("abort previous key loader for type:" + type); loader.abort(); } if (!uri) { logger["logger"].warn('key uri is falsy'); return; } frag.loader = this.loaders[type] = new config.loader(config); this.decrypturl = uri; this.decryptkey = null; var loaderContext = { url: uri, frag: frag, responseType: 'arraybuffer' }; // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times, // key-loader will trigger an error and rely on stream-controller to handle retry logic. // this will also align retry logic with fragment-loader var loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: config.fragLoadingRetryDelay, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; var loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) }; frag.loader.load(loaderContext, loaderConfig, loaderCallbacks); } else if (this.decryptkey) { // Return the key if it's already been loaded frag.decryptdata.key = this.decryptkey; this.hls.trigger(events["default"].KEY_LOADED, { frag: frag }); } }; _proto.loadsuccess = function loadsuccess(response, stats, context) { var frag = context.frag; if (!frag.decryptdata) { logger["logger"].error('after key load, decryptdata unset'); return; } this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data); // detach fragment loader on load success frag.loader = undefined; delete this.loaders[frag.type]; this.hls.trigger(events["default"].KEY_LOADED, { frag: frag }); }; _proto.loaderror = function loaderror(response, context) { var frag = context.frag; var loader = frag.loader; if (loader) { loader.abort(); } delete this.loaders[frag.type]; this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].KEY_LOAD_ERROR, fatal: false, frag: frag, response: response }); }; _proto.loadtimeout = function loadtimeout(stats, context) { var frag = context.frag; var loader = frag.loader; if (loader) { loader.abort(); } delete this.loaders[frag.type]; this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].NETWORK_ERROR, details: errors["ErrorDetails"].KEY_LOAD_TIMEOUT, fatal: false, frag: frag }); }; return KeyLoader; }(event_handler); /* harmony default export */ var key_loader = (key_loader_KeyLoader); // CONCATENATED MODULE: ./src/controller/fragment-tracker.js function fragment_tracker_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var FragmentState = { NOT_LOADED: 'NOT_LOADED', APPENDING: 'APPENDING', PARTIAL: 'PARTIAL', OK: 'OK' }; var fragment_tracker_FragmentTracker = /*#__PURE__*/function (_EventHandler) { fragment_tracker_inheritsLoose(FragmentTracker, _EventHandler); function FragmentTracker(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].BUFFER_APPENDED, events["default"].FRAG_BUFFERED, events["default"].FRAG_LOADED) || this; _this.bufferPadding = 0.2; _this.fragments = Object.create(null); _this.timeRanges = Object.create(null); _this.config = hls.config; return _this; } var _proto = FragmentTracker.prototype; _proto.destroy = function destroy() { this.fragments = Object.create(null); this.timeRanges = Object.create(null); this.config = null; event_handler.prototype.destroy.call(this); _EventHandler.prototype.destroy.call(this); } /** * Return a Fragment that match the position and levelType. * If not found any Fragment, return null * @param {number} position * @param {LevelType} levelType * @returns {Fragment|null} */ ; _proto.getBufferedFrag = function getBufferedFrag(position, levelType) { var fragments = this.fragments; var bufferedFrags = Object.keys(fragments).filter(function (key) { var fragmentEntity = fragments[key]; if (fragmentEntity.body.type !== levelType) { return false; } if (!fragmentEntity.buffered) { return false; } var frag = fragmentEntity.body; return frag.startPTS <= position && position <= frag.endPTS; }); if (bufferedFrags.length === 0) { return null; } else { // https://github.com/video-dev/hls.js/pull/1545#discussion_r166229566 var bufferedFragKey = bufferedFrags.pop(); return fragments[bufferedFragKey].body; } } /** * Partial fragments effected by coded frame eviction will be removed * The browser will unload parts of the buffer to free up memory for new buffer data * Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable) * @param {String} elementaryStream The elementaryStream of media this is (eg. video/audio) * @param {TimeRanges} timeRange TimeRange object from a sourceBuffer */ ; _proto.detectEvictedFragments = function detectEvictedFragments(elementaryStream, timeRange) { var _this2 = this; // Check if any flagged fragments have been unloaded Object.keys(this.fragments).forEach(function (key) { var fragmentEntity = _this2.fragments[key]; if (!fragmentEntity || !fragmentEntity.buffered) { return; } var esData = fragmentEntity.range[elementaryStream]; if (!esData) { return; } var fragmentTimes = esData.time; for (var i = 0; i < fragmentTimes.length; i++) { var time = fragmentTimes[i]; if (!_this2.isTimeBuffered(time.startPTS, time.endPTS, timeRange)) { // Unregister partial fragment as it needs to load again to be reused _this2.removeFragment(fragmentEntity.body); break; } } }); } /** * Checks if the fragment passed in is loaded in the buffer properly * Partially loaded fragments will be registered as a partial fragment * @param {Object} fragment Check the fragment against all sourceBuffers loaded */ ; _proto.detectPartialFragments = function detectPartialFragments(fragment) { var _this3 = this; var fragKey = this.getFragmentKey(fragment); var fragmentEntity = this.fragments[fragKey]; if (fragmentEntity) { fragmentEntity.buffered = true; Object.keys(this.timeRanges).forEach(function (elementaryStream) { if (fragment.hasElementaryStream(elementaryStream)) { var timeRange = _this3.timeRanges[elementaryStream]; // Check for malformed fragments // Gaps need to be calculated for each elementaryStream fragmentEntity.range[elementaryStream] = _this3.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange); } }); } }; _proto.getBufferedTimes = function getBufferedTimes(startPTS, endPTS, timeRange) { var fragmentTimes = []; var startTime, endTime; var fragmentPartial = false; for (var i = 0; i < timeRange.length; i++) { startTime = timeRange.start(i) - this.bufferPadding; endTime = timeRange.end(i) + this.bufferPadding; if (startPTS >= startTime && endPTS <= endTime) { // Fragment is entirely contained in buffer // No need to check the other timeRange times since it's completely playable fragmentTimes.push({ startPTS: Math.max(startPTS, timeRange.start(i)), endPTS: Math.min(endPTS, timeRange.end(i)) }); break; } else if (startPTS < endTime && endPTS > startTime) { // Check for intersection with buffer // Get playable sections of the fragment fragmentTimes.push({ startPTS: Math.max(startPTS, timeRange.start(i)), endPTS: Math.min(endPTS, timeRange.end(i)) }); fragmentPartial = true; } else if (endPTS <= startTime) { // No need to check the rest of the timeRange as it is in order break; } } return { time: fragmentTimes, partial: fragmentPartial }; }; _proto.getFragmentKey = function getFragmentKey(fragment) { return fragment.type + "_" + fragment.level + "_" + fragment.urlId + "_" + fragment.sn; } /** * Gets the partial fragment for a certain time * @param {Number} time * @returns {Object} fragment Returns a partial fragment at a time or null if there is no partial fragment */ ; _proto.getPartialFragment = function getPartialFragment(time) { var _this4 = this; var timePadding, startTime, endTime; var bestFragment = null; var bestOverlap = 0; Object.keys(this.fragments).forEach(function (key) { var fragmentEntity = _this4.fragments[key]; if (_this4.isPartial(fragmentEntity)) { startTime = fragmentEntity.body.startPTS - _this4.bufferPadding; endTime = fragmentEntity.body.endPTS + _this4.bufferPadding; if (time >= startTime && time <= endTime) { // Use the fragment that has the most padding from start and end time timePadding = Math.min(time - startTime, endTime - time); if (bestOverlap <= timePadding) { bestFragment = fragmentEntity.body; bestOverlap = timePadding; } } } }); return bestFragment; } /** * @param {Object} fragment The fragment to check * @returns {String} Returns the fragment state when a fragment never loaded or if it partially loaded */ ; _proto.getState = function getState(fragment) { var fragKey = this.getFragmentKey(fragment); var fragmentEntity = this.fragments[fragKey]; var state = FragmentState.NOT_LOADED; if (fragmentEntity !== undefined) { if (!fragmentEntity.buffered) { state = FragmentState.APPENDING; } else if (this.isPartial(fragmentEntity) === true) { state = FragmentState.PARTIAL; } else { state = FragmentState.OK; } } return state; }; _proto.isPartial = function isPartial(fragmentEntity) { return fragmentEntity.buffered === true && (fragmentEntity.range.video !== undefined && fragmentEntity.range.video.partial === true || fragmentEntity.range.audio !== undefined && fragmentEntity.range.audio.partial === true); }; _proto.isTimeBuffered = function isTimeBuffered(startPTS, endPTS, timeRange) { var startTime, endTime; for (var i = 0; i < timeRange.length; i++) { startTime = timeRange.start(i) - this.bufferPadding; endTime = timeRange.end(i) + this.bufferPadding; if (startPTS >= startTime && endPTS <= endTime) { return true; } if (endPTS <= startTime) { // No need to check the rest of the timeRange as it is in order return false; } } return false; } /** * Fires when a fragment loading is completed */ ; _proto.onFragLoaded = function onFragLoaded(e) { var fragment = e.frag; // don't track initsegment (for which sn is not a number) // don't track frags used for bitrateTest, they're irrelevant. if (!Object(number["isFiniteNumber"])(fragment.sn) || fragment.bitrateTest) { return; } this.fragments[this.getFragmentKey(fragment)] = { body: fragment, range: Object.create(null), buffered: false }; } /** * Fires when the buffer is updated */ ; _proto.onBufferAppended = function onBufferAppended(e) { var _this5 = this; // Store the latest timeRanges loaded in the buffer this.timeRanges = e.timeRanges; Object.keys(this.timeRanges).forEach(function (elementaryStream) { var timeRange = _this5.timeRanges[elementaryStream]; _this5.detectEvictedFragments(elementaryStream, timeRange); }); } /** * Fires after a fragment has been loaded into the source buffer */ ; _proto.onFragBuffered = function onFragBuffered(e) { this.detectPartialFragments(e.frag); } /** * Return true if fragment tracker has the fragment. * @param {Object} fragment * @returns {boolean} */ ; _proto.hasFragment = function hasFragment(fragment) { var fragKey = this.getFragmentKey(fragment); return this.fragments[fragKey] !== undefined; } /** * Remove a fragment from fragment tracker until it is loaded again * @param {Object} fragment The fragment to remove */ ; _proto.removeFragment = function removeFragment(fragment) { var fragKey = this.getFragmentKey(fragment); delete this.fragments[fragKey]; } /** * Remove all fragments from fragment tracker. */ ; _proto.removeAllFragments = function removeAllFragments() { this.fragments = Object.create(null); }; return FragmentTracker; }(event_handler); // CONCATENATED MODULE: ./src/utils/binary-search.ts var BinarySearch = { /** * Searches for an item in an array which matches a certain condition. * This requires the condition to only match one item in the array, * and for the array to be ordered. * * @param {Array<T>} list The array to search. * @param {BinarySearchComparison<T>} comparisonFn * Called and provided a candidate item as the first argument. * Should return: * > -1 if the item should be located at a lower index than the provided item. * > 1 if the item should be located at a higher index than the provided item. * > 0 if the item is the item you're looking for. * * @return {T | null} The object if it is found or null otherwise. */ search: function search(list, comparisonFn) { var minIndex = 0; var maxIndex = list.length - 1; var currentIndex = null; var currentElement = null; while (minIndex <= maxIndex) { currentIndex = (minIndex + maxIndex) / 2 | 0; currentElement = list[currentIndex]; var comparisonResult = comparisonFn(currentElement); if (comparisonResult > 0) { minIndex = currentIndex + 1; } else if (comparisonResult < 0) { maxIndex = currentIndex - 1; } else { return currentElement; } } return null; } }; /* harmony default export */ var binary_search = (BinarySearch); // CONCATENATED MODULE: ./src/utils/buffer-helper.ts /** * @module BufferHelper * * Providing methods dealing with buffer length retrieval for example. * * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property. * * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered */ var BufferHelper = /*#__PURE__*/function () { function BufferHelper() {} /** * Return true if `media`'s buffered include `position` * @param {Bufferable} media * @param {number} position * @returns {boolean} */ BufferHelper.isBuffered = function isBuffered(media, position) { try { if (media) { var buffered = media.buffered; for (var i = 0; i < buffered.length; i++) { if (position >= buffered.start(i) && position <= buffered.end(i)) { return true; } } } } catch (error) {// this is to catch // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': // This SourceBuffer has been removed from the parent media source } return false; }; BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) { try { if (media) { var vbuffered = media.buffered; var buffered = []; var i; for (i = 0; i < vbuffered.length; i++) { buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) }); } return this.bufferedInfo(buffered, pos, maxHoleDuration); } } catch (error) {// this is to catch // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': // This SourceBuffer has been removed from the parent media source } return { len: 0, start: pos, end: pos, nextStart: undefined }; }; BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) { // sort on buffer.start/smaller end (IE does not always return sorted buffered range) buffered.sort(function (a, b) { var diff = a.start - b.start; if (diff) { return diff; } else { return b.end - a.end; } }); var buffered2 = []; if (maxHoleDuration) { // there might be some small holes between buffer time range // consider that holes smaller than maxHoleDuration are irrelevant and build another // buffer time range representations that discards those holes for (var i = 0; i < buffered.length; i++) { var buf2len = buffered2.length; if (buf2len) { var buf2end = buffered2[buf2len - 1].end; // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) if (buffered[i].start - buf2end < maxHoleDuration) { // merge overlapping time ranges // update lastRange.end only if smaller than item.end // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) if (buffered[i].end > buf2end) { buffered2[buf2len - 1].end = buffered[i].end; } } else { // big hole buffered2.push(buffered[i]); } } else { // first value buffered2.push(buffered[i]); } } } else { buffered2 = buffered; } var bufferLen = 0; // bufferStartNext can possibly be undefined based on the conditional logic below var bufferStartNext; // bufferStart and bufferEnd are buffer boundaries around current video position var bufferStart = pos; var bufferEnd = pos; for (var _i = 0; _i < buffered2.length; _i++) { var start = buffered2[_i].start, end = buffered2[_i].end; // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i)); if (pos + maxHoleDuration >= start && pos < end) { // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length bufferStart = start; bufferEnd = end; bufferLen = bufferEnd - pos; } else if (pos + maxHoleDuration < start) { bufferStartNext = start; break; } } return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext }; }; return BufferHelper; }(); // EXTERNAL MODULE: ./node_modules/eventemitter3/index.js var eventemitter3 = __webpack_require__("./node_modules/eventemitter3/index.js"); // EXTERNAL MODULE: ./node_modules/webworkify-webpack/index.js var webworkify_webpack = __webpack_require__("./node_modules/webworkify-webpack/index.js"); // EXTERNAL MODULE: ./src/demux/demuxer-inline.js + 12 modules var demuxer_inline = __webpack_require__("./src/demux/demuxer-inline.js"); // CONCATENATED MODULE: ./src/utils/mediasource-helper.ts /** * MediaSource helper */ function getMediaSource() { return window.MediaSource || window.WebKitMediaSource; } // EXTERNAL MODULE: ./src/utils/get-self-scope.js var get_self_scope = __webpack_require__("./src/utils/get-self-scope.js"); // CONCATENATED MODULE: ./src/observer.ts function observer_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * Simple adapter sub-class of Nodejs-like EventEmitter. */ var Observer = /*#__PURE__*/function (_EventEmitter) { observer_inheritsLoose(Observer, _EventEmitter); function Observer() { return _EventEmitter.apply(this, arguments) || this; } var _proto = Observer.prototype; /** * We simply want to pass along the event-name itself * in every call to a handler, which is the purpose of our `trigger` method * extending the standard API. */ _proto.trigger = function trigger(event) { for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { data[_key - 1] = arguments[_key]; } this.emit.apply(this, [event, event].concat(data)); }; return Observer; }(eventemitter3["EventEmitter"]); // CONCATENATED MODULE: ./src/demux/demuxer.js // see https://stackoverflow.com/a/11237259/589493 var global = Object(get_self_scope["getSelfScope"])(); // safeguard for code that might run both on worker and main thread var demuxer_MediaSource = getMediaSource() || { isTypeSupported: function isTypeSupported() { return false; } }; var demuxer_Demuxer = /*#__PURE__*/function () { function Demuxer(hls, id) { var _this = this; this.hls = hls; this.id = id; var observer = this.observer = new Observer(); var config = hls.config; var forwardMessage = function forwardMessage(ev, data) { data = data || {}; data.frag = _this.frag; data.id = _this.id; hls.trigger(ev, data); }; // forward events to main thread observer.on(events["default"].FRAG_DECRYPTED, forwardMessage); observer.on(events["default"].FRAG_PARSING_INIT_SEGMENT, forwardMessage); observer.on(events["default"].FRAG_PARSING_DATA, forwardMessage); observer.on(events["default"].FRAG_PARSED, forwardMessage); observer.on(events["default"].ERROR, forwardMessage); observer.on(events["default"].FRAG_PARSING_METADATA, forwardMessage); observer.on(events["default"].FRAG_PARSING_USERDATA, forwardMessage); observer.on(events["default"].INIT_PTS_FOUND, forwardMessage); var typeSupported = { mp4: demuxer_MediaSource.isTypeSupported('video/mp4'), mpeg: demuxer_MediaSource.isTypeSupported('audio/mpeg'), mp3: demuxer_MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') }; // navigator.vendor is not always available in Web Worker // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator var vendor = navigator.vendor; if (config.enableWorker && typeof Worker !== 'undefined') { logger["logger"].log('demuxing in webworker'); var w; try { w = this.w = webworkify_webpack(/*require.resolve*/(/*! ../demux/demuxer-worker.js */ "./src/demux/demuxer-worker.js")); this.onwmsg = this.onWorkerMessage.bind(this); w.addEventListener('message', this.onwmsg); w.onerror = function (event) { hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].OTHER_ERROR, details: errors["ErrorDetails"].INTERNAL_EXCEPTION, fatal: true, event: 'demuxerWorker', err: { message: event.message + ' (' + event.filename + ':' + event.lineno + ')' } }); }; w.postMessage({ cmd: 'init', typeSupported: typeSupported, vendor: vendor, id: id, config: JSON.stringify(config) }); } catch (err) { logger["logger"].warn('Error in worker:', err); logger["logger"].error('Error while initializing DemuxerWorker, fallback on DemuxerInline'); if (w) { // revoke the Object URL that was used to create demuxer worker, so as not to leak it global.URL.revokeObjectURL(w.objectURL); } this.demuxer = new demuxer_inline["default"](observer, typeSupported, config, vendor); this.w = undefined; } } else { this.demuxer = new demuxer_inline["default"](observer, typeSupported, config, vendor); } } var _proto = Demuxer.prototype; _proto.destroy = function destroy() { var w = this.w; if (w) { w.removeEventListener('message', this.onwmsg); w.terminate(); this.w = null; } else { var demuxer = this.demuxer; if (demuxer) { demuxer.destroy(); this.demuxer = null; } } var observer = this.observer; if (observer) { observer.removeAllListeners(); this.observer = null; } }; _proto.push = function push(data, initSegment, audioCodec, videoCodec, frag, duration, accurateTimeOffset, defaultInitPTS) { var w = this.w; var timeOffset = Object(number["isFiniteNumber"])(frag.startPTS) ? frag.startPTS : frag.start; var decryptdata = frag.decryptdata; var lastFrag = this.frag; var discontinuity = !(lastFrag && frag.cc === lastFrag.cc); var trackSwitch = !(lastFrag && frag.level === lastFrag.level); var nextSN = lastFrag && frag.sn === lastFrag.sn + 1; var contiguous = !trackSwitch && nextSN; if (discontinuity) { logger["logger"].log(this.id + ":discontinuity detected"); } if (trackSwitch) { logger["logger"].log(this.id + ":switch detected"); } this.frag = frag; if (w) { // post fragment payload as transferable objects for ArrayBuffer (no copy) w.postMessage({ cmd: 'demux', data: data, decryptdata: decryptdata, initSegment: initSegment, audioCodec: audioCodec, videoCodec: videoCodec, timeOffset: timeOffset, discontinuity: discontinuity, trackSwitch: trackSwitch, contiguous: contiguous, duration: duration, accurateTimeOffset: accurateTimeOffset, defaultInitPTS: defaultInitPTS }, data instanceof ArrayBuffer ? [data] : []); } else { var demuxer = this.demuxer; if (demuxer) { demuxer.push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); } } }; _proto.onWorkerMessage = function onWorkerMessage(ev) { var data = ev.data, hls = this.hls; switch (data.event) { case 'init': // revoke the Object URL that was used to create demuxer worker, so as not to leak it global.URL.revokeObjectURL(this.w.objectURL); break; // special case for FRAG_PARSING_DATA: data1 and data2 are transferable objects case events["default"].FRAG_PARSING_DATA: data.data.data1 = new Uint8Array(data.data1); if (data.data2) { data.data.data2 = new Uint8Array(data.data2); } /* falls through */ default: data.data = data.data || {}; data.data.frag = this.frag; data.data.id = this.id; hls.trigger(data.event, data.data); break; } }; return Demuxer; }(); /* harmony default export */ var demux_demuxer = (demuxer_Demuxer); // CONCATENATED MODULE: ./src/controller/level-helper.js /** * @module LevelHelper * * Providing methods dealing with playlist sliding and drift * * TODO: Create an actual `Level` class/model that deals with all this logic in an object-oriented-manner. * * */ function addGroupId(level, type, id) { switch (type) { case 'audio': if (!level.audioGroupIds) { level.audioGroupIds = []; } level.audioGroupIds.push(id); break; case 'text': if (!level.textGroupIds) { level.textGroupIds = []; } level.textGroupIds.push(id); break; } } function updatePTS(fragments, fromIdx, toIdx) { var fragFrom = fragments[fromIdx], fragTo = fragments[toIdx], fragToPTS = fragTo.startPTS; // if we know startPTS[toIdx] if (Object(number["isFiniteNumber"])(fragToPTS)) { // update fragment duration. // it helps to fix drifts between playlist reported duration and fragment real duration if (toIdx > fromIdx) { fragFrom.duration = fragToPTS - fragFrom.start; if (fragFrom.duration < 0) { logger["logger"].warn("negative duration computed for frag " + fragFrom.sn + ",level " + fragFrom.level + ", there should be some duration drift between playlist and fragment!"); } } else { fragTo.duration = fragFrom.start - fragToPTS; if (fragTo.duration < 0) { logger["logger"].warn("negative duration computed for frag " + fragTo.sn + ",level " + fragTo.level + ", there should be some duration drift between playlist and fragment!"); } } } else { // we dont know startPTS[toIdx] if (toIdx > fromIdx) { var contiguous = fragFrom.cc === fragTo.cc; fragTo.start = fragFrom.start + (contiguous && fragFrom.minEndPTS ? fragFrom.minEndPTS - fragFrom.start : fragFrom.duration); } else { fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0); } } } function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) { // update frag PTS/DTS var maxStartPTS = startPTS; var minEndPTS = endPTS; if (Object(number["isFiniteNumber"])(frag.startPTS)) { // delta PTS between audio and video var deltaPTS = Math.abs(frag.startPTS - startPTS); if (!Object(number["isFiniteNumber"])(frag.deltaPTS)) { frag.deltaPTS = deltaPTS; } else { frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); } maxStartPTS = Math.max(startPTS, frag.startPTS); startPTS = Math.min(startPTS, frag.startPTS); minEndPTS = Math.min(endPTS, frag.endPTS); endPTS = Math.max(endPTS, frag.endPTS); startDTS = Math.min(startDTS, frag.startDTS); endDTS = Math.max(endDTS, frag.endDTS); } var drift = startPTS - frag.start; frag.start = frag.startPTS = startPTS; frag.maxStartPTS = maxStartPTS; frag.endPTS = endPTS; frag.minEndPTS = minEndPTS; frag.startDTS = startDTS; frag.endDTS = endDTS; frag.duration = endPTS - startPTS; var sn = frag.sn; // exit if sn out of range if (!details || sn < details.startSN || sn > details.endSN) { return 0; } var fragIdx, fragments, i; fragIdx = sn - details.startSN; fragments = details.fragments; // update frag reference in fragments array // rationale is that fragments array might not contain this frag object. // this will happen if playlist has been refreshed between frag loading and call to updateFragPTSDTS() // if we don't update frag, we won't be able to propagate PTS info on the playlist // resulting in invalid sliding computation fragments[fragIdx] = frag; // adjust fragment PTS/duration from seqnum-1 to frag 0 for (i = fragIdx; i > 0; i--) { updatePTS(fragments, i, i - 1); } // adjust fragment PTS/duration from seqnum to last frag for (i = fragIdx; i < fragments.length - 1; i++) { updatePTS(fragments, i, i + 1); } details.PTSKnown = true; return drift; } function mergeDetails(oldDetails, newDetails) { // potentially retrieve cached initsegment if (newDetails.initSegment && oldDetails.initSegment) { newDetails.initSegment = oldDetails.initSegment; } // check if old/new playlists have fragments in common // loop through overlapping SN and update startPTS , cc, and duration if any found var ccOffset = 0; var PTSFrag; mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag) { ccOffset = oldFrag.cc - newFrag.cc; if (Object(number["isFiniteNumber"])(oldFrag.startPTS)) { newFrag.start = newFrag.startPTS = oldFrag.startPTS; newFrag.endPTS = oldFrag.endPTS; newFrag.duration = oldFrag.duration; newFrag.backtracked = oldFrag.backtracked; newFrag.dropped = oldFrag.dropped; PTSFrag = newFrag; } // PTS is known when there are overlapping segments newDetails.PTSKnown = true; }); if (!newDetails.PTSKnown) { return; } if (ccOffset) { logger["logger"].log('discontinuity sliding from playlist, take drift into account'); var newFragments = newDetails.fragments; for (var i = 0; i < newFragments.length; i++) { newFragments[i].cc += ccOffset; } } // if at least one fragment contains PTS info, recompute PTS information for all fragments if (PTSFrag) { updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); } else { // ensure that delta is within oldFragments range // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) // in that case we also need to adjust start offset of all fragments adjustSliding(oldDetails, newDetails); } // if we are here, it means we have fragments overlapping between // old and new level. reliable PTS info is thus relying on old level newDetails.PTSKnown = oldDetails.PTSKnown; } function mergeSubtitlePlaylists(oldPlaylist, newPlaylist, referenceStart) { if (referenceStart === void 0) { referenceStart = 0; } var lastIndex = -1; mapFragmentIntersection(oldPlaylist, newPlaylist, function (oldFrag, newFrag, index) { newFrag.start = oldFrag.start; lastIndex = index; }); var frags = newPlaylist.fragments; if (lastIndex < 0) { frags.forEach(function (frag) { frag.start += referenceStart; }); return; } for (var i = lastIndex + 1; i < frags.length; i++) { frags[i].start = frags[i - 1].start + frags[i - 1].duration; } } function mapFragmentIntersection(oldPlaylist, newPlaylist, intersectionFn) { if (!oldPlaylist || !newPlaylist) { return; } var start = Math.max(oldPlaylist.startSN, newPlaylist.startSN) - newPlaylist.startSN; var end = Math.min(oldPlaylist.endSN, newPlaylist.endSN) - newPlaylist.startSN; var delta = newPlaylist.startSN - oldPlaylist.startSN; for (var i = start; i <= end; i++) { var oldFrag = oldPlaylist.fragments[delta + i]; var newFrag = newPlaylist.fragments[i]; if (!oldFrag || !newFrag) { break; } intersectionFn(oldFrag, newFrag, i); } } function adjustSliding(oldPlaylist, newPlaylist) { var delta = newPlaylist.startSN - oldPlaylist.startSN; var oldFragments = oldPlaylist.fragments; var newFragments = newPlaylist.fragments; if (delta < 0 || delta > oldFragments.length) { return; } for (var i = 0; i < newFragments.length; i++) { newFragments[i].start += oldFragments[delta].start; } } function computeReloadInterval(currentPlaylist, newPlaylist, lastRequestTime) { var reloadInterval = 1000 * (newPlaylist.averagetargetduration ? newPlaylist.averagetargetduration : newPlaylist.targetduration); var minReloadInterval = reloadInterval / 2; if (currentPlaylist && newPlaylist.endSN === currentPlaylist.endSN) { // follow HLS Spec, If the client reloads a Playlist file and finds that it has not // changed then it MUST wait for a period of one-half the target // duration before retrying. reloadInterval = minReloadInterval; } if (lastRequestTime) { reloadInterval = Math.max(minReloadInterval, reloadInterval - (window.performance.now() - lastRequestTime)); } // in any case, don't reload more than half of target duration return Math.round(reloadInterval); } // CONCATENATED MODULE: ./src/utils/time-ranges.ts /** * TimeRanges to string helper */ var TimeRanges = { toString: function toString(r) { var log = ''; var len = r.length; for (var i = 0; i < len; i++) { log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']'; } return log; } }; /* harmony default export */ var time_ranges = (TimeRanges); // CONCATENATED MODULE: ./src/utils/discontinuities.js function findFirstFragWithCC(fragments, cc) { var firstFrag = null; for (var i = 0; i < fragments.length; i += 1) { var currentFrag = fragments[i]; if (currentFrag && currentFrag.cc === cc) { firstFrag = currentFrag; break; } } return firstFrag; } function findFragWithCC(fragments, CC) { return binary_search.search(fragments, function (candidate) { if (candidate.cc < CC) { return 1; } else if (candidate.cc > CC) { return -1; } else { return 0; } }); } function shouldAlignOnDiscontinuities(lastFrag, lastLevel, details) { var shouldAlign = false; if (lastLevel && lastLevel.details && details) { if (details.endCC > details.startCC || lastFrag && lastFrag.cc < details.startCC) { shouldAlign = true; } } return shouldAlign; } // Find the first frag in the previous level which matches the CC of the first frag of the new level function findDiscontinuousReferenceFrag(prevDetails, curDetails) { var prevFrags = prevDetails.fragments; var curFrags = curDetails.fragments; if (!curFrags.length || !prevFrags.length) { logger["logger"].log('No fragments to align'); return; } var prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc); if (!prevStartFrag || prevStartFrag && !prevStartFrag.startPTS) { logger["logger"].log('No frag in previous level to align on'); return; } return prevStartFrag; } function adjustPts(sliding, details) { details.fragments.forEach(function (frag) { if (frag) { var start = frag.start + sliding; frag.start = frag.startPTS = start; frag.endPTS = start + frag.duration; } }); details.PTSKnown = true; } /** * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a * contiguous stream with the last fragments. * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time * and an extra download. * @param lastFrag * @param lastLevel * @param details */ function alignStream(lastFrag, lastLevel, details) { alignDiscontinuities(lastFrag, details, lastLevel); if (!details.PTSKnown && lastLevel) { // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level. // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same // discontinuity sequence. alignPDT(details, lastLevel.details); } } /** * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same * discontinuity sequence. * @param lastLevel - The details of the last loaded level * @param details - The details of the new level */ function alignDiscontinuities(lastFrag, details, lastLevel) { if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) { var referenceFrag = findDiscontinuousReferenceFrag(lastLevel.details, details); if (referenceFrag) { logger["logger"].log('Adjusting PTS using last level due to CC increase within current level'); adjustPts(referenceFrag.start, details); } } } /** * Computes the PTS of a new level's fragments using the difference in Program Date Time from the last level. * @param details - The details of the new level * @param lastDetails - The details of the last loaded level */ function alignPDT(details, lastDetails) { if (lastDetails && lastDetails.fragments.length) { if (!details.hasProgramDateTime || !lastDetails.hasProgramDateTime) { return; } // if last level sliding is 1000 and its first frag PROGRAM-DATE-TIME is 2017-08-20 1:10:00 AM // and if new details first frag PROGRAM DATE-TIME is 2017-08-20 1:10:08 AM // then we can deduce that playlist B sliding is 1000+8 = 1008s var lastPDT = lastDetails.fragments[0].programDateTime; var newPDT = details.fragments[0].programDateTime; // date diff is in ms. frag.start is in seconds var sliding = (newPDT - lastPDT) / 1000 + lastDetails.fragments[0].start; if (Object(number["isFiniteNumber"])(sliding)) { logger["logger"].log("adjusting PTS using programDateTime delta, sliding:" + sliding.toFixed(3)); adjustPts(sliding, details); } } } // CONCATENATED MODULE: ./src/controller/fragment-finders.ts /** * Returns first fragment whose endPdt value exceeds the given PDT. * @param {Array<Fragment>} fragments - The array of candidate fragments * @param {number|null} [PDTValue = null] - The PDT value which must be exceeded * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start/end can be within in order to be considered contiguous * @returns {*|null} fragment - The best matching fragment */ function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) { if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !Object(number["isFiniteNumber"])(PDTValue)) { return null; } // if less than start var startPDT = fragments[0].programDateTime; if (PDTValue < (startPDT || 0)) { return null; } var endPDT = fragments[fragments.length - 1].endProgramDateTime; if (PDTValue >= (endPDT || 0)) { return null; } maxFragLookUpTolerance = maxFragLookUpTolerance || 0; for (var seg = 0; seg < fragments.length; ++seg) { var frag = fragments[seg]; if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) { return frag; } } return null; } /** * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer. * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus * breaking any traps which would cause the same fragment to be continuously selected within a small range. * @param {*} fragPrevious - The last frag successfully appended * @param {Array<Fragment>} fragments - The array of candidate fragments * @param {number} [bufferEnd = 0] - The end of the contiguous buffered range the playhead is currently within * @param {number} maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous * @returns {*} foundFrag - The best matching fragment */ function findFragmentByPTS(fragPrevious, fragments, bufferEnd, maxFragLookUpTolerance) { if (bufferEnd === void 0) { bufferEnd = 0; } if (maxFragLookUpTolerance === void 0) { maxFragLookUpTolerance = 0; } var fragNext = null; if (fragPrevious) { fragNext = fragments[fragPrevious.sn - fragments[0].sn + 1]; } else if (bufferEnd === 0 && fragments[0].start === 0) { fragNext = fragments[0]; } // Prefer the next fragment if it's within tolerance if (fragNext && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0) { return fragNext; } // We might be seeking past the tolerance so find the best match var foundFragment = binary_search.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); if (foundFragment) { return foundFragment; } // If no match was found return the next fragment after fragPrevious, or null return fragNext; } /** * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions. * @param {*} candidate - The fragment to test * @param {number} [bufferEnd = 0] - The end of the current buffered range the playhead is currently within * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous * @returns {number} - 0 if it matches, 1 if too low, -1 if too high */ function fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, candidate) { if (bufferEnd === void 0) { bufferEnd = 0; } if (maxFragLookUpTolerance === void 0) { maxFragLookUpTolerance = 0; } // offset should be within fragment boundary - config.maxFragLookUpTolerance // this is to cope with situations like // bufferEnd = 9.991 // frag[Ø] : [0,10] // frag[1] : [10,20] // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here // frag start frag start+duration // |-----------------------------| // <---> <---> // ...--------><-----------------------------><---------.... // previous frag matching fragment next frag // return -1 return 0 return 1 // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)); if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { return 1; } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { // if maxFragLookUpTolerance will have negative value then don't return -1 for first element return -1; } return 0; } /** * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions. * This function tests the candidate's program date time values, as represented in Unix time * @param {*} candidate - The fragment to test * @param {number} [pdtBufferEnd = 0] - The Unix time representing the end of the current buffered range * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous * @returns {boolean} True if contiguous, false otherwise */ function pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) { var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000; // endProgramDateTime can be null, default to zero var endProgramDateTime = candidate.endProgramDateTime || 0; return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd; } // CONCATENATED MODULE: ./src/controller/gap-controller.js var STALL_MINIMUM_DURATION_MS = 250; var MAX_START_GAP_JUMP = 2.0; var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1; var SKIP_BUFFER_RANGE_START = 0.05; var gap_controller_GapController = /*#__PURE__*/function () { function GapController(config, media, fragmentTracker, hls) { this.config = config; this.media = media; this.fragmentTracker = fragmentTracker; this.hls = hls; this.nudgeRetry = 0; this.stallReported = false; this.stalled = null; this.moved = false; this.seeking = false; } /** * Checks if the playhead is stuck within a gap, and if so, attempts to free it. * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range). * * @param {number} lastCurrentTime Previously read playhead position */ var _proto = GapController.prototype; _proto.poll = function poll(lastCurrentTime) { var config = this.config, media = this.media, stalled = this.stalled; var currentTime = media.currentTime, seeking = media.seeking; var seeked = this.seeking && !seeking; var beginSeek = !this.seeking && seeking; this.seeking = seeking; // The playhead is moving, no-op if (currentTime !== lastCurrentTime) { this.moved = true; if (stalled !== null) { // The playhead is now moving, but was previously stalled if (this.stallReported) { var _stalledDuration = self.performance.now() - stalled; logger["logger"].warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms"); this.stallReported = false; } this.stalled = null; this.nudgeRetry = 0; } return; } // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek if (beginSeek || seeked) { this.stalled = null; } // The playhead should not be moving if (media.paused || media.ended || media.playbackRate === 0 || !media.buffered.length) { return; } var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); var isBuffered = bufferInfo.len > 0; var nextStart = bufferInfo.nextStart || 0; // There is no playable buffer (waiting for buffer append) if (!isBuffered && !nextStart) { return; } if (seeking) { // Waiting for seeking in a buffered range to complete var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP; // Next buffered range is too far ahead to jump to while still seeking var noBufferGap = !nextStart || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime); if (hasEnoughBuffer || noBufferGap) { return; } // Reset moved state when seeking to a point in or before a gap this.moved = false; } // Skip start gaps if we haven't played, but the last poll detected the start of a stall // The addition poll gives the browser a chance to jump the gap for us if (!this.moved && this.stalled) { // Jump start gaps within jump threshold var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime; if (startJump > 0 && startJump <= MAX_START_GAP_JUMP) { this._trySkipBufferHole(null); return; } } // Start tracking stall time var tnow = self.performance.now(); if (stalled === null) { this.stalled = tnow; return; } var stalledDuration = tnow - stalled; if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) { // Report stalling after trying to fix this._reportStall(bufferInfo.len); } var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole); this._tryFixBufferStall(bufferedWithHoles, stalledDuration); } /** * Detects and attempts to fix known buffer stalling issues. * @param bufferInfo - The properties of the current buffer. * @param stalledDurationMs - The amount of time Hls.js has been stalling for. * @private */ ; _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) { var config = this.config, fragmentTracker = this.fragmentTracker, media = this.media; var currentTime = media.currentTime; var partial = fragmentTracker.getPartialFragment(currentTime); if (partial) { // Try to skip over the buffer hole caused by a partial fragment // This method isn't limited by the size of the gap between buffered ranges var targetTime = this._trySkipBufferHole(partial); // we return here in this case, meaning // the branch below only executes when we don't handle a partial fragment if (targetTime) { return; } } // if we haven't had to skip over a buffer hole of a partial fragment // we may just have to "nudge" the playlist as the browser decoding/rendering engine // needs to cross some sort of threshold covering all source-buffers content // to start playing properly. if (bufferInfo.len > config.maxBufferHole && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) { logger["logger"].warn('Trying to nudge playhead over buffer-hole'); // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds // We only try to jump the hole if it's under the configured size // Reset stalled so to rearm watchdog timer this.stalled = null; this._tryNudgeBuffer(); } } /** * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period. * @param bufferLen - The playhead distance from the end of the current buffer segment. * @private */ ; _proto._reportStall = function _reportStall(bufferLen) { var hls = this.hls, media = this.media, stallReported = this.stallReported; if (!stallReported) { // Report stalled error once this.stallReported = true; logger["logger"].warn("Playback stalling at @" + media.currentTime + " due to low buffer (buffer=" + bufferLen + ")"); hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_STALLED_ERROR, fatal: false, buffer: bufferLen }); } } /** * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments * @param partial - The partial fragment found at the current time (where playback is stalling). * @private */ ; _proto._trySkipBufferHole = function _trySkipBufferHole(partial) { var config = this.config, hls = this.hls, media = this.media; var currentTime = media.currentTime; var lastEndTime = 0; // Check if currentTime is between unbuffered regions of partial fragments for (var i = 0; i < media.buffered.length; i++) { var startTime = media.buffered.start(i); if (currentTime + config.maxBufferHole >= lastEndTime && currentTime < startTime) { var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, media.currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS); logger["logger"].warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime); this.moved = true; this.stalled = null; media.currentTime = targetTime; if (partial) { hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_SEEK_OVER_HOLE, fatal: false, reason: "fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime, frag: partial }); } return targetTime; } lastEndTime = media.buffered.end(i); } return 0; } /** * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount. * @private */ ; _proto._tryNudgeBuffer = function _tryNudgeBuffer() { var config = this.config, hls = this.hls, media = this.media; var currentTime = media.currentTime; var nudgeRetry = (this.nudgeRetry || 0) + 1; this.nudgeRetry = nudgeRetry; if (nudgeRetry < config.nudgeMaxRetry) { var targetTime = currentTime + nudgeRetry * config.nudgeOffset; // playback stalled in buffered area ... let's nudge currentTime to try to overcome this logger["logger"].warn("Nudging 'currentTime' from " + currentTime + " to " + targetTime); media.currentTime = targetTime; hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_NUDGE_ON_STALL, fatal: false }); } else { logger["logger"].error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges"); hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_STALLED_ERROR, fatal: true }); } }; return GapController; }(); // CONCATENATED MODULE: ./src/task-loop.ts function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function task_loop_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * Sub-class specialization of EventHandler base class. * * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop, * scheduled asynchroneously, avoiding recursive calls in the same tick. * * The task itself is implemented in `doTick`. It can be requested and called for single execution * using the `tick` method. * * It will be assured that the task execution method (`tick`) only gets called once per main loop "tick", * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly. * * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`, * and cancelled with `clearNextTick`. * * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`). * * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine. * * Further explanations: * * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks. * * When the task execution (`tick` method) is called in re-entrant way this is detected and * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo). */ var TaskLoop = /*#__PURE__*/function (_EventHandler) { task_loop_inheritsLoose(TaskLoop, _EventHandler); function TaskLoop(hls) { var _this; for (var _len = arguments.length, events = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { events[_key - 1] = arguments[_key]; } _this = _EventHandler.call.apply(_EventHandler, [this, hls].concat(events)) || this; _this._boundTick = void 0; _this._tickTimer = null; _this._tickInterval = null; _this._tickCallCount = 0; _this._boundTick = _this.tick.bind(_assertThisInitialized(_this)); return _this; } /** * @override */ var _proto = TaskLoop.prototype; _proto.onHandlerDestroying = function onHandlerDestroying() { // clear all timers before unregistering from event bus this.clearNextTick(); this.clearInterval(); } /** * @returns {boolean} */ ; _proto.hasInterval = function hasInterval() { return !!this._tickInterval; } /** * @returns {boolean} */ ; _proto.hasNextTick = function hasNextTick() { return !!this._tickTimer; } /** * @param {number} millis Interval time (ms) * @returns {boolean} True when interval has been scheduled, false when already scheduled (no effect) */ ; _proto.setInterval = function setInterval(millis) { if (!this._tickInterval) { this._tickInterval = self.setInterval(this._boundTick, millis); return true; } return false; } /** * @returns {boolean} True when interval was cleared, false when none was set (no effect) */ ; _proto.clearInterval = function clearInterval() { if (this._tickInterval) { self.clearInterval(this._tickInterval); this._tickInterval = null; return true; } return false; } /** * @returns {boolean} True when timeout was cleared, false when none was set (no effect) */ ; _proto.clearNextTick = function clearNextTick() { if (this._tickTimer) { self.clearTimeout(this._tickTimer); this._tickTimer = null; return true; } return false; } /** * Will call the subclass doTick implementation in this main loop tick * or in the next one (via setTimeout(,0)) in case it has already been called * in this tick (in case this is a re-entrant call). */ ; _proto.tick = function tick() { this._tickCallCount++; if (this._tickCallCount === 1) { this.doTick(); // re-entrant call to tick from previous doTick call stack // -> schedule a call on the next main loop iteration to process this task processing request if (this._tickCallCount > 1) { // make sure only one timer exists at any time at max this.clearNextTick(); this._tickTimer = self.setTimeout(this._boundTick, 0); } this._tickCallCount = 0; } } /** * For subclass to implement task logic * @abstract */ ; _proto.doTick = function doTick() {}; return TaskLoop; }(event_handler); // CONCATENATED MODULE: ./src/controller/base-stream-controller.js function base_stream_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var State = { STOPPED: 'STOPPED', STARTING: 'STARTING', IDLE: 'IDLE', PAUSED: 'PAUSED', KEY_LOADING: 'KEY_LOADING', FRAG_LOADING: 'FRAG_LOADING', FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', WAITING_TRACK: 'WAITING_TRACK', PARSING: 'PARSING', PARSED: 'PARSED', BUFFER_FLUSHING: 'BUFFER_FLUSHING', ENDED: 'ENDED', ERROR: 'ERROR', WAITING_INIT_PTS: 'WAITING_INIT_PTS', WAITING_LEVEL: 'WAITING_LEVEL' }; var base_stream_controller_BaseStreamController = /*#__PURE__*/function (_TaskLoop) { base_stream_controller_inheritsLoose(BaseStreamController, _TaskLoop); function BaseStreamController() { return _TaskLoop.apply(this, arguments) || this; } var _proto = BaseStreamController.prototype; _proto.doTick = function doTick() {}; _proto.startLoad = function startLoad() {}; _proto.stopLoad = function stopLoad() { var frag = this.fragCurrent; if (frag) { if (frag.loader) { frag.loader.abort(); } this.fragmentTracker.removeFragment(frag); } if (this.demuxer) { this.demuxer.destroy(); this.demuxer = null; } this.fragCurrent = null; this.fragPrevious = null; this.clearInterval(); this.clearNextTick(); this.state = State.STOPPED; }; _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) { var fragCurrent = this.fragCurrent, fragmentTracker = this.fragmentTracker; // we just got done loading the final fragment and there is no other buffered range after ... // rationale is that in case there are any buffered ranges after, it means that there are unbuffered portion in between // so we should not switch to ENDED in that case, to be able to buffer them // dont switch to ENDED if we need to backtrack last fragment if (!levelDetails.live && fragCurrent && !fragCurrent.backtracked && fragCurrent.sn === levelDetails.endSN && !bufferInfo.nextStart) { var fragState = fragmentTracker.getState(fragCurrent); return fragState === FragmentState.PARTIAL || fragState === FragmentState.OK; } return false; }; _proto.onMediaSeeking = function onMediaSeeking() { var config = this.config, media = this.media, mediaBuffer = this.mediaBuffer, state = this.state; var currentTime = media ? media.currentTime : null; var bufferInfo = BufferHelper.bufferInfo(mediaBuffer || media, currentTime, this.config.maxBufferHole); logger["logger"].log("media seeking to " + (Object(number["isFiniteNumber"])(currentTime) ? currentTime.toFixed(3) : currentTime)); if (state === State.FRAG_LOADING) { var fragCurrent = this.fragCurrent; // check if we are seeking to a unbuffered area AND if frag loading is in progress if (bufferInfo.len === 0 && fragCurrent) { var tolerance = config.maxFragLookUpTolerance; var fragStartOffset = fragCurrent.start - tolerance; var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything if (currentTime < fragStartOffset || currentTime > fragEndOffset) { if (fragCurrent.loader) { logger["logger"].log('seeking outside of buffer while fragment load in progress, cancel fragment load'); fragCurrent.loader.abort(); } this.fragCurrent = null; this.fragPrevious = null; // switch to IDLE state to load new fragment this.state = State.IDLE; } else { logger["logger"].log('seeking outside of buffer but within currently loaded fragment range'); } } } else if (state === State.ENDED) { // if seeking to unbuffered area, clean up fragPrevious if (bufferInfo.len === 0) { this.fragPrevious = null; this.fragCurrent = null; } // switch to IDLE state to check for potential new fragment this.state = State.IDLE; } if (media) { this.lastCurrentTime = currentTime; } // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target if (!this.loadedmetadata) { this.nextLoadPosition = this.startPosition = currentTime; } // tick to speed up processing this.tick(); }; _proto.onMediaEnded = function onMediaEnded() { // reset startPosition and lastCurrentTime to restart playback @ stream beginning this.startPosition = this.lastCurrentTime = 0; }; _proto.onHandlerDestroying = function onHandlerDestroying() { this.stopLoad(); _TaskLoop.prototype.onHandlerDestroying.call(this); }; _proto.onHandlerDestroyed = function onHandlerDestroyed() { this.state = State.STOPPED; this.fragmentTracker = null; }; _proto.computeLivePosition = function computeLivePosition(sliding, levelDetails) { var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration; return sliding + Math.max(0, levelDetails.totalduration - targetLatency); }; return BaseStreamController; }(TaskLoop); // CONCATENATED MODULE: ./src/controller/stream-controller.js function stream_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function stream_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) stream_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) stream_controller_defineProperties(Constructor, staticProps); return Constructor; } function stream_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Stream Controller */ var TICK_INTERVAL = 100; // how often to tick in ms var stream_controller_StreamController = /*#__PURE__*/function (_BaseStreamController) { stream_controller_inheritsLoose(StreamController, _BaseStreamController); function StreamController(hls, fragmentTracker) { var _this; _this = _BaseStreamController.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHING, events["default"].MANIFEST_LOADING, events["default"].MANIFEST_PARSED, events["default"].LEVEL_LOADED, events["default"].LEVELS_UPDATED, events["default"].KEY_LOADED, events["default"].FRAG_LOADED, events["default"].FRAG_LOAD_EMERGENCY_ABORTED, events["default"].FRAG_PARSING_INIT_SEGMENT, events["default"].FRAG_PARSING_DATA, events["default"].FRAG_PARSED, events["default"].ERROR, events["default"].AUDIO_TRACK_SWITCHING, events["default"].AUDIO_TRACK_SWITCHED, events["default"].BUFFER_CREATED, events["default"].BUFFER_APPENDED, events["default"].BUFFER_FLUSHED) || this; _this.fragmentTracker = fragmentTracker; _this.config = hls.config; _this.audioCodecSwap = false; _this._state = State.STOPPED; _this.stallReported = false; _this.gapController = null; _this.altAudio = false; _this.audioOnly = false; _this.bitrateTest = false; return _this; } var _proto = StreamController.prototype; _proto.startLoad = function startLoad(startPosition) { if (this.levels) { var lastCurrentTime = this.lastCurrentTime, hls = this.hls; this.stopLoad(); this.setInterval(TICK_INTERVAL); this.level = -1; this.fragLoadError = 0; if (!this.startFragRequested) { // determine load level var startLevel = hls.startLevel; if (startLevel === -1) { if (hls.config.testBandwidth) { // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level startLevel = 0; this.bitrateTest = true; } else { startLevel = hls.nextAutoLevel; } } // set new level to playlist loader : this will trigger start level load // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded this.level = hls.nextLoadLevel = startLevel; this.loadedmetadata = false; } // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime if (lastCurrentTime > 0 && startPosition === -1) { logger["logger"].log("override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3)); startPosition = lastCurrentTime; } this.state = State.IDLE; this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; this.tick(); } else { this.forceStartLoad = true; this.state = State.STOPPED; } }; _proto.stopLoad = function stopLoad() { this.forceStartLoad = false; _BaseStreamController.prototype.stopLoad.call(this); }; _proto.doTick = function doTick() { switch (this.state) { case State.BUFFER_FLUSHING: // in buffer flushing state, reset fragLoadError counter this.fragLoadError = 0; break; case State.IDLE: this._doTickIdle(); break; case State.WAITING_LEVEL: var level = this.levels[this.level]; // check if playlist is already loaded if (level && level.details) { this.state = State.IDLE; } break; case State.FRAG_LOADING_WAITING_RETRY: var now = window.performance.now(); var retryDate = this.retryDate; // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading if (!retryDate || now >= retryDate || this.media && this.media.seeking) { logger["logger"].log('mediaController: retryDate reached, switch back to IDLE state'); this.state = State.IDLE; } break; case State.ERROR: case State.STOPPED: case State.FRAG_LOADING: case State.PARSING: case State.PARSED: case State.ENDED: break; default: break; } // check buffer this._checkBuffer(); // check/update current fragment this._checkFragmentChanged(); } // Ironically the "idle" state is the on we do the most logic in it seems .... // NOTE: Maybe we could rather schedule a check for buffer length after half of the currently // played segment, or on pause/play/seek instead of naively checking every 100ms? ; _proto._doTickIdle = function _doTickIdle() { var hls = this.hls, config = hls.config, media = this.media; // if start level not parsed yet OR // if video not attached AND start fragment already requested OR start frag prefetch disable // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment if (this.levelLastLoaded === undefined || !media && (this.startFragRequested || !config.startFragPrefetch)) { return; } // If the "main" level is audio-only but we are loading an alternate track in the same group, do not load anything if (this.altAudio && this.audioOnly) { // Clear audio demuxer state so when switching back to main audio we're not still appending where we left off this.demuxer.frag = null; return; } // if we have not yet loaded any fragment, start loading from start position var pos; if (this.loadedmetadata) { pos = media.currentTime; } else { pos = this.nextLoadPosition; } // determine next load level var level = hls.nextLoadLevel, levelInfo = this.levels[level]; if (!levelInfo) { return; } var levelBitrate = levelInfo.bitrate, maxBufLen; // compute max Buffer Length that we could get from this load level, based on level bitrate. if (levelBitrate) { maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength); } else { maxBufLen = config.maxBufferLength; } maxBufLen = Math.min(maxBufLen, config.maxMaxBufferLength); // determine next candidate fragment to be loaded, based on current position and end of buffer position // ensure up to `config.maxMaxBufferLength` of buffer upfront var maxBufferHole = pos < config.maxBufferHole ? Math.max(MAX_START_GAP_JUMP, config.maxBufferHole) : config.maxBufferHole; var bufferInfo = BufferHelper.bufferInfo(this.mediaBuffer ? this.mediaBuffer : media, pos, maxBufferHole); var bufferLen = bufferInfo.len; // Stay idle if we are still with buffer margins if (bufferLen >= maxBufLen) { return; } // if buffer length is less than maxBufLen try to load a new fragment ... logger["logger"].trace("buffer length of " + bufferLen.toFixed(3) + " is below max of " + maxBufLen.toFixed(3) + ". checking for more payload ..."); // set next load level : this will trigger a playlist load if needed this.level = hls.nextLoadLevel = level; var levelDetails = levelInfo.details; // if level info not retrieved yet, switch state and wait for level retrieval // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist) if (!levelDetails || levelDetails.live && this.levelLastLoaded !== level) { this.state = State.WAITING_LEVEL; return; } if (this._streamEnded(bufferInfo, levelDetails)) { var data = {}; if (this.altAudio) { data.type = 'video'; } this.hls.trigger(events["default"].BUFFER_EOS, data); this.state = State.ENDED; return; } // if we have the levelDetails for the selected variant, lets continue enrichen our stream (load keys/fragments or trigger EOS, etc..) this._fetchPayloadOrEos(pos, bufferInfo, levelDetails); }; _proto._fetchPayloadOrEos = function _fetchPayloadOrEos(pos, bufferInfo, levelDetails) { var fragPrevious = this.fragPrevious, level = this.level, fragments = levelDetails.fragments, fragLen = fragments.length; // empty playlist if (fragLen === 0) { return; } // find fragment index, contiguous with end of buffer position var start = fragments[0].start, end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, bufferEnd = bufferInfo.end, frag; if (levelDetails.initSegment && !levelDetails.initSegment.data) { frag = levelDetails.initSegment; } else { // in case of live playlist we need to ensure that requested position is not located before playlist start if (levelDetails.live) { var initialLiveManifestSize = this.config.initialLiveManifestSize; if (fragLen < initialLiveManifestSize) { logger["logger"].warn("Can not start playback of a level, reason: not enough fragments " + fragLen + " < " + initialLiveManifestSize); return; } frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments); // if it explicitely returns null don't load any fragment and exit function now if (frag === null) { return; } } else { // VoD playlist: if bufferEnd before start of playlist, load first fragment if (bufferEnd < start) { frag = fragments[0]; } } } if (!frag) { frag = this._findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails); } if (frag) { if (frag.encrypted) { this._loadKey(frag, levelDetails); } else { this._loadFragment(frag, levelDetails, pos, bufferEnd); } } }; _proto._ensureFragmentAtLivePoint = function _ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments) { var config = this.hls.config, media = this.media; var frag; // check if requested position is within seekable boundaries : // logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`); var maxLatency = Infinity; if (config.liveMaxLatencyDuration !== undefined) { maxLatency = config.liveMaxLatencyDuration; } else if (Object(number["isFiniteNumber"])(config.liveMaxLatencyDurationCount)) { maxLatency = config.liveMaxLatencyDurationCount * levelDetails.targetduration; } if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) { var liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails); bufferEnd = liveSyncPosition; if (media && !media.paused && media.readyState && media.duration > liveSyncPosition && liveSyncPosition > media.currentTime) { logger["logger"].log("buffer end: " + bufferEnd.toFixed(3) + " is located too far from the end of live sliding playlist, reset currentTime to : " + liveSyncPosition.toFixed(3)); media.currentTime = liveSyncPosition; } this.nextLoadPosition = liveSyncPosition; } // if end of buffer greater than live edge, don't load any fragment // this could happen if live playlist intermittently slides in the past. // level 1 loaded [182580161,182580167] // level 1 loaded [182580162,182580169] // Loading 182580168 of [182580162 ,182580169],level 1 .. // Loading 182580169 of [182580162 ,182580169],level 1 .. // level 1 loaded [182580162,182580168] <============= here we should have bufferEnd > end. in that case break to avoid reloading 182580168 // level 1 loaded [182580164,182580171] // // don't return null in case media not loaded yet (readystate === 0) if (levelDetails.PTSKnown && bufferEnd > end && media && media.readyState) { return null; } if (this.startFragRequested && !levelDetails.PTSKnown) { /* we are switching level on live playlist, but we don't have any PTS info for that quality level ... try to load frag matching with next SN. even if SN are not synchronized between playlists, loading this frag will help us compute playlist sliding and find the right one after in case it was not the right consecutive one */ if (fragPrevious) { if (levelDetails.hasProgramDateTime) { // Relies on PDT in order to switch bitrates (Support EXT-X-DISCONTINUITY without EXT-X-DISCONTINUITY-SEQUENCE) logger["logger"].log("live playlist, switching playlist, load frag with same PDT: " + fragPrevious.programDateTime); frag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, config.maxFragLookUpTolerance); } else { // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) var targetSN = fragPrevious.sn + 1; if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { var fragNext = fragments[targetSN - levelDetails.startSN]; if (fragPrevious.cc === fragNext.cc) { frag = fragNext; logger["logger"].log("live playlist, switching playlist, load frag with next SN: " + frag.sn); } } // next frag SN not available (or not with same continuity counter) // look for a frag sharing the same CC if (!frag) { frag = binary_search.search(fragments, function (frag) { return fragPrevious.cc - frag.cc; }); if (frag) { logger["logger"].log("live playlist, switching playlist, load frag with same CC: " + frag.sn); } } } } } return frag; }; _proto._findFragment = function _findFragment(start, fragPreviousLoad, fragmentIndexRange, fragments, bufferEnd, end, levelDetails) { var config = this.hls.config; var fragNextLoad; if (bufferEnd < end) { var lookupTolerance = bufferEnd > end - config.maxFragLookUpTolerance ? 0 : config.maxFragLookUpTolerance; // Remove the tolerance if it would put the bufferEnd past the actual end of stream // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) fragNextLoad = findFragmentByPTS(fragPreviousLoad, fragments, bufferEnd, lookupTolerance); } else { // reach end of playlist fragNextLoad = fragments[fragmentIndexRange - 1]; } if (fragNextLoad) { var curSNIdx = fragNextLoad.sn - levelDetails.startSN; var sameLevel = fragPreviousLoad && fragNextLoad.level === fragPreviousLoad.level; var prevSnFrag = fragments[curSNIdx - 1]; var nextSnFrag = fragments[curSNIdx + 1]; // logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); if (fragPreviousLoad && fragNextLoad.sn === fragPreviousLoad.sn) { if (sameLevel && !fragNextLoad.backtracked) { if (fragNextLoad.sn < levelDetails.endSN) { var deltaPTS = fragPreviousLoad.deltaPTS; // if there is a significant delta between audio and video, larger than max allowed hole, // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped) // let's try to load previous fragment again to get last keyframe // then we will reload again current fragment (that way we should be able to fill the buffer hole ...) if (deltaPTS && deltaPTS > config.maxBufferHole && fragPreviousLoad.dropped && curSNIdx) { fragNextLoad = prevSnFrag; logger["logger"].warn('Previous fragment was dropped with large PTS gap between audio and video. Maybe fragment is not starting with a keyframe? Loading previous one to try to overcome this'); } else { fragNextLoad = nextSnFrag; if (this.fragmentTracker.getState(fragNextLoad) !== FragmentState.OK) { logger["logger"].log("Re-loading fragment with SN: " + fragNextLoad.sn); } } } else { fragNextLoad = null; } } else if (fragNextLoad.backtracked) { // Only backtrack a max of 1 consecutive fragment to prevent sliding back too far when little or no frags start with keyframes if (nextSnFrag && nextSnFrag.backtracked) { logger["logger"].warn("Already backtracked from fragment " + nextSnFrag.sn + ", will not backtrack to fragment " + fragNextLoad.sn + ". Loading fragment " + nextSnFrag.sn); fragNextLoad = nextSnFrag; } else { // If a fragment has dropped frames and it's in a same level/sequence, load the previous fragment to try and find the keyframe // Reset the dropped count now since it won't be reset until we parse the fragment again, which prevents infinite backtracking on the same segment logger["logger"].warn('Loaded fragment with dropped frames, backtracking 1 segment to find a keyframe'); fragNextLoad.dropped = 0; if (prevSnFrag) { fragNextLoad = prevSnFrag; fragNextLoad.backtracked = true; } else if (curSNIdx) { // can't backtrack on very first fragment fragNextLoad = null; } } } } } return fragNextLoad; }; _proto._loadKey = function _loadKey(frag, levelDetails) { logger["logger"].log("Loading key for " + frag.sn + " of [" + levelDetails.startSN + "-" + levelDetails.endSN + "], level " + this.level); this.state = State.KEY_LOADING; this.hls.trigger(events["default"].KEY_LOADING, { frag: frag }); }; _proto._loadFragment = function _loadFragment(frag, levelDetails, pos, bufferEnd) { // Check if fragment is not loaded var fragState = this.fragmentTracker.getState(frag); this.fragCurrent = frag; if (frag.sn !== 'initSegment') { this.startFragRequested = true; } // Don't update nextLoadPosition for fragments which are not buffered if (Object(number["isFiniteNumber"])(frag.sn) && !frag.bitrateTest) { this.nextLoadPosition = frag.start + frag.duration; } // Allow backtracked fragments to load if (frag.backtracked || fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) { frag.autoLevel = this.hls.autoLevelEnabled; frag.bitrateTest = this.bitrateTest; logger["logger"].log("Loading " + frag.sn + " of [" + levelDetails.startSN + "-" + levelDetails.endSN + "], level " + this.level + ", " + (this.loadedmetadata ? 'currentTime' : 'nextLoadPosition') + ": " + parseFloat(pos.toFixed(3)) + ", bufferEnd: " + parseFloat(bufferEnd.toFixed(3))); this.hls.trigger(events["default"].FRAG_LOADING, { frag: frag }); // lazy demuxer init, as this could take some time ... do it during frag loading if (!this.demuxer) { this.demuxer = new demux_demuxer(this.hls, 'main'); } this.state = State.FRAG_LOADING; } else if (fragState === FragmentState.APPENDING) { // Lower the buffer size and try again if (this._reduceMaxBufferLength(frag.duration)) { this.fragmentTracker.removeFragment(frag); } } }; _proto.getBufferedFrag = function getBufferedFrag(position) { return this.fragmentTracker.getBufferedFrag(position, PlaylistLevelType.MAIN); }; _proto.followingBufferedFrag = function followingBufferedFrag(frag) { if (frag) { // try to get range of next fragment (500ms after this range) return this.getBufferedFrag(frag.endPTS + 0.5); } return null; }; _proto._checkFragmentChanged = function _checkFragmentChanged() { var fragPlayingCurrent, currentTime, video = this.media; if (video && video.readyState && video.seeking === false) { currentTime = video.currentTime; /* if video element is in seeked state, currentTime can only increase. (assuming that playback rate is positive ...) As sometimes currentTime jumps back to zero after a media decode error, check this, to avoid seeking back to wrong position after a media decode error */ if (currentTime > this.lastCurrentTime) { this.lastCurrentTime = currentTime; } if (BufferHelper.isBuffered(video, currentTime)) { fragPlayingCurrent = this.getBufferedFrag(currentTime); } else if (BufferHelper.isBuffered(video, currentTime + 0.1)) { /* ensure that FRAG_CHANGED event is triggered at startup, when first video frame is displayed and playback is paused. add a tolerance of 100ms, in case current position is not buffered, check if current pos+100ms is buffered and use that buffer range for FRAG_CHANGED event reporting */ fragPlayingCurrent = this.getBufferedFrag(currentTime + 0.1); } if (fragPlayingCurrent) { var fragPlaying = fragPlayingCurrent; if (fragPlaying !== this.fragPlaying) { this.hls.trigger(events["default"].FRAG_CHANGED, { frag: fragPlaying }); var fragPlayingLevel = fragPlaying.level; if (!this.fragPlaying || this.fragPlaying.level !== fragPlayingLevel) { this.hls.trigger(events["default"].LEVEL_SWITCHED, { level: fragPlayingLevel }); } this.fragPlaying = fragPlaying; } } } } /* on immediate level switch : - pause playback if playing - cancel any pending load request - and trigger a buffer flush */ ; _proto.immediateLevelSwitch = function immediateLevelSwitch() { logger["logger"].log('immediateLevelSwitch'); if (!this.immediateSwitch) { this.immediateSwitch = true; var media = this.media, previouslyPaused; if (media) { previouslyPaused = media.paused; if (!previouslyPaused) { media.pause(); } } else { // don't restart playback after instant level switch in case media not attached previouslyPaused = true; } this.previouslyPaused = previouslyPaused; } var fragCurrent = this.fragCurrent; if (fragCurrent && fragCurrent.loader) { fragCurrent.loader.abort(); } this.fragCurrent = null; // flush everything this.flushMainBuffer(0, Number.POSITIVE_INFINITY); } /** * on immediate level switch end, after new fragment has been buffered: * - nudge video decoder by slightly adjusting video currentTime (if currentTime buffered) * - resume the playback if needed */ ; _proto.immediateLevelSwitchEnd = function immediateLevelSwitchEnd() { var media = this.media; if (media && media.buffered.length) { this.immediateSwitch = false; if (media.currentTime > 0 && BufferHelper.isBuffered(media, media.currentTime)) { // only nudge if currentTime is buffered media.currentTime -= 0.0001; } if (!this.previouslyPaused) { media.play(); } } } /** * try to switch ASAP without breaking video playback: * in order to ensure smooth but quick level switching, * we need to find the next flushable buffer range * we should take into account new segment fetch time */ ; _proto.nextLevelSwitch = function nextLevelSwitch() { var media = this.media; // ensure that media is defined and that metadata are available (to retrieve currentTime) if (media && media.readyState) { var fetchdelay; var fragPlayingCurrent = this.getBufferedFrag(media.currentTime); if (fragPlayingCurrent && fragPlayingCurrent.startPTS > 1) { // flush buffer preceding current fragment (flush until current fragment start offset) // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ... this.flushMainBuffer(0, fragPlayingCurrent.startPTS - 1); } if (!media.paused) { // add a safety delay of 1s var nextLevelId = this.hls.nextLoadLevel, nextLevel = this.levels[nextLevelId], fragLastKbps = this.fragLastKbps; if (fragLastKbps && this.fragCurrent) { fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1; } else { fetchdelay = 0; } } else { fetchdelay = 0; } // logger.log('fetchdelay:'+fetchdelay); // find buffer range that will be reached once new fragment will be fetched var bufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay); if (bufferedFrag) { // we can flush buffer range following this one without stalling playback var nextBufferedFrag = this.followingBufferedFrag(bufferedFrag); if (nextBufferedFrag) { // if we are here, we can also cancel any loading/demuxing in progress, as they are useless var fragCurrent = this.fragCurrent; if (fragCurrent && fragCurrent.loader) { fragCurrent.loader.abort(); } this.fragCurrent = null; // start flush position is the start PTS of next buffered frag. // we use frag.naxStartPTS which is max(audio startPTS, video startPTS). // in case there is a small PTS Delta between audio and video, using maxStartPTS avoids flushing last samples from current fragment var startPts = Math.max(bufferedFrag.endPTS, nextBufferedFrag.maxStartPTS + Math.min(this.config.maxFragLookUpTolerance, nextBufferedFrag.duration)); this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY); } } } }; _proto.flushMainBuffer = function flushMainBuffer(startOffset, endOffset) { this.state = State.BUFFER_FLUSHING; var flushScope = { startOffset: startOffset, endOffset: endOffset }; // if alternate audio tracks are used, only flush video, otherwise flush everything if (this.altAudio) { flushScope.type = 'video'; } this.hls.trigger(events["default"].BUFFER_FLUSHING, flushScope); }; _proto.onMediaAttached = function onMediaAttached(data) { var media = this.media = this.mediaBuffer = data.media; this.onvseeking = this.onMediaSeeking.bind(this); this.onvseeked = this.onMediaSeeked.bind(this); this.onvended = this.onMediaEnded.bind(this); media.addEventListener('seeking', this.onvseeking); media.addEventListener('seeked', this.onvseeked); media.addEventListener('ended', this.onvended); var config = this.config; if (this.levels && config.autoStartLoad) { this.hls.startLoad(config.startPosition); } this.gapController = new gap_controller_GapController(config, media, this.fragmentTracker, this.hls); }; _proto.onMediaDetaching = function onMediaDetaching() { var media = this.media; if (media && media.ended) { logger["logger"].log('MSE detaching and video ended, reset startPosition'); this.startPosition = this.lastCurrentTime = 0; } // reset fragment backtracked flag var levels = this.levels; if (levels) { levels.forEach(function (level) { if (level.details) { level.details.fragments.forEach(function (fragment) { fragment.backtracked = undefined; }); } }); } // remove video listeners if (media) { media.removeEventListener('seeking', this.onvseeking); media.removeEventListener('seeked', this.onvseeked); media.removeEventListener('ended', this.onvended); this.onvseeking = this.onvseeked = this.onvended = null; } this.fragmentTracker.removeAllFragments(); this.media = this.mediaBuffer = null; this.loadedmetadata = false; this.stopLoad(); }; _proto.onMediaSeeked = function onMediaSeeked() { var media = this.media; var currentTime = media ? media.currentTime : undefined; if (Object(number["isFiniteNumber"])(currentTime)) { logger["logger"].log("media seeked to " + currentTime.toFixed(3)); } // tick to speed up FRAGMENT_PLAYING triggering this.tick(); }; _proto.onManifestLoading = function onManifestLoading() { // reset buffer on manifest loading logger["logger"].log('trigger BUFFER_RESET'); this.hls.trigger(events["default"].BUFFER_RESET); this.fragmentTracker.removeAllFragments(); this.stalled = false; this.startPosition = this.lastCurrentTime = 0; }; _proto.onManifestParsed = function onManifestParsed(data) { var aac = false, heaac = false, codec; data.levels.forEach(function (level) { // detect if we have different kind of audio codecs used amongst playlists codec = level.audioCodec; if (codec) { if (codec.indexOf('mp4a.40.2') !== -1) { aac = true; } if (codec.indexOf('mp4a.40.5') !== -1) { heaac = true; } } }); this.audioCodecSwitch = aac && heaac; if (this.audioCodecSwitch) { logger["logger"].log('both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'); } this.altAudio = data.altAudio; this.levels = data.levels; this.startFragRequested = false; var config = this.config; if (config.autoStartLoad || this.forceStartLoad) { this.hls.startLoad(config.startPosition); } }; _proto.onLevelLoaded = function onLevelLoaded(data) { var newDetails = data.details; var newLevelId = data.level; var lastLevel = this.levels[this.levelLastLoaded]; var curLevel = this.levels[newLevelId]; var duration = newDetails.totalduration; var sliding = 0; logger["logger"].log("level " + newLevelId + " loaded [" + newDetails.startSN + "," + newDetails.endSN + "],duration:" + duration); if (newDetails.live || curLevel.details && curLevel.details.live) { var curDetails = curLevel.details; if (curDetails && newDetails.fragments.length > 0) { // we already have details for that level, merge them mergeDetails(curDetails, newDetails); sliding = newDetails.fragments[0].start; this.liveSyncPosition = this.computeLivePosition(sliding, curDetails); if (newDetails.PTSKnown && Object(number["isFiniteNumber"])(sliding)) { logger["logger"].log("live playlist sliding:" + sliding.toFixed(3)); } else { logger["logger"].log('live playlist - outdated PTS, unknown sliding'); alignStream(this.fragPrevious, lastLevel, newDetails); } } else { logger["logger"].log('live playlist - first load, unknown sliding'); newDetails.PTSKnown = false; alignStream(this.fragPrevious, lastLevel, newDetails); } } else { newDetails.PTSKnown = false; } // override level info curLevel.details = newDetails; this.levelLastLoaded = newLevelId; this.hls.trigger(events["default"].LEVEL_UPDATED, { details: newDetails, level: newLevelId }); if (this.startFragRequested === false) { // compute start position if set to -1. use it straight away if value is defined if (this.startPosition === -1 || this.lastCurrentTime === -1) { // first, check if start time offset has been set in playlist, if yes, use this value var startTimeOffset = newDetails.startTimeOffset; if (Object(number["isFiniteNumber"])(startTimeOffset)) { if (startTimeOffset < 0) { logger["logger"].log("negative start time offset " + startTimeOffset + ", count from end of last fragment"); startTimeOffset = sliding + duration + startTimeOffset; } logger["logger"].log("start time offset found in playlist, adjust startPosition to " + startTimeOffset); this.startPosition = startTimeOffset; } else { // if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3) if (newDetails.live) { this.startPosition = this.computeLivePosition(sliding, newDetails); logger["logger"].log("configure startPosition to " + this.startPosition); } else { this.startPosition = 0; } } this.lastCurrentTime = this.startPosition; } this.nextLoadPosition = this.startPosition; } // only switch batck to IDLE state if we were waiting for level to start downloading a new fragment if (this.state === State.WAITING_LEVEL) { this.state = State.IDLE; } // trigger handler right now this.tick(); }; _proto.onKeyLoaded = function onKeyLoaded() { if (this.state === State.KEY_LOADING) { this.state = State.IDLE; this.tick(); } }; _proto.onFragLoaded = function onFragLoaded(data) { var fragCurrent = this.fragCurrent, hls = this.hls, levels = this.levels, media = this.media; var fragLoaded = data.frag; if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'main' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) { var stats = data.stats; var currentLevel = levels[fragCurrent.level]; var details = currentLevel.details; // reset frag bitrate test in any case after frag loaded event // if this frag was loaded to perform a bitrate test AND if hls.nextLoadLevel is greater than 0 // then this means that we should be able to load a fragment at a higher quality level this.bitrateTest = false; this.stats = stats; logger["logger"].log("Loaded " + fragCurrent.sn + " of [" + details.startSN + " ," + details.endSN + "],level " + fragCurrent.level); if (fragLoaded.bitrateTest && hls.nextLoadLevel) { // switch back to IDLE state ... we just loaded a fragment to determine adequate start bitrate and initialize autoswitch algo this.state = State.IDLE; this.startFragRequested = false; stats.tparsed = stats.tbuffered = window.performance.now(); hls.trigger(events["default"].FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' }); this.tick(); } else if (fragLoaded.sn === 'initSegment') { this.state = State.IDLE; stats.tparsed = stats.tbuffered = window.performance.now(); details.initSegment.data = data.payload; hls.trigger(events["default"].FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' }); this.tick(); } else { logger["logger"].log("Parsing " + fragCurrent.sn + " of [" + details.startSN + " ," + details.endSN + "],level " + fragCurrent.level + ", cc " + fragCurrent.cc); this.state = State.PARSING; this.pendingBuffering = true; this.appended = false; // Bitrate test frags are not usually buffered so the fragment tracker ignores them. If Hls.js decides to buffer // it (and therefore ends up at this line), then the fragment tracker needs to be manually informed. if (fragLoaded.bitrateTest) { fragLoaded.bitrateTest = false; this.fragmentTracker.onFragLoaded({ frag: fragLoaded }); } // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) and if media is not seeking (this is to overcome potential timestamp drifts between playlists and fragments) var accurateTimeOffset = !(media && media.seeking) && (details.PTSKnown || !details.live); var initSegmentData = details.initSegment ? details.initSegment.data : []; var audioCodec = this._getAudioCodec(currentLevel); // transmux the MPEG-TS data to ISO-BMFF segments var demuxer = this.demuxer = this.demuxer || new demux_demuxer(this.hls, 'main'); demuxer.push(data.payload, initSegmentData, audioCodec, currentLevel.videoCodec, fragCurrent, details.totalduration, accurateTimeOffset); } } this.fragLoadError = 0; }; _proto.onFragParsingInitSegment = function onFragParsingInitSegment(data) { var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) { var tracks = data.tracks, trackName, track; this.audioOnly = tracks.audio && !tracks.video; // if audio track is expected to come from audio stream controller, discard any coming from main if (this.altAudio && !this.audioOnly) { delete tracks.audio; } // include levelCodec in audio and video tracks track = tracks.audio; if (track) { var audioCodec = this.levels[this.level].audioCodec, ua = navigator.userAgent.toLowerCase(); if (audioCodec && this.audioCodecSwap) { logger["logger"].log('swapping playlist audio codec'); if (audioCodec.indexOf('mp4a.40.5') !== -1) { audioCodec = 'mp4a.40.2'; } else { audioCodec = 'mp4a.40.5'; } } // in case AAC and HE-AAC audio codecs are signalled in manifest // force HE-AAC , as it seems that most browsers prefers that way, // except for mono streams OR on FF // these conditions might need to be reviewed ... if (this.audioCodecSwitch) { // don't force HE-AAC if mono stream if (track.metadata.channelCount !== 1 && // don't force HE-AAC if firefox ua.indexOf('firefox') === -1) { audioCodec = 'mp4a.40.5'; } } // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise if (ua.indexOf('android') !== -1 && track.container !== 'audio/mpeg') { // Exclude mpeg audio audioCodec = 'mp4a.40.2'; logger["logger"].log("Android: force audio codec to " + audioCodec); } track.levelCodec = audioCodec; track.id = data.id; } track = tracks.video; if (track) { track.levelCodec = this.levels[this.level].videoCodec; track.id = data.id; } this.hls.trigger(events["default"].BUFFER_CODECS, tracks); // loop through tracks that are going to be provided to bufferController for (trackName in tracks) { track = tracks[trackName]; logger["logger"].log("main track:" + trackName + ",container:" + track.container + ",codecs[level/parsed]=[" + track.levelCodec + "/" + track.codec + "]"); var initSegment = track.initSegment; if (initSegment) { this.appended = true; // arm pending Buffering flag before appending a segment this.pendingBuffering = true; this.hls.trigger(events["default"].BUFFER_APPENDING, { type: trackName, data: initSegment, parent: 'main', content: 'initSegment' }); } } // trigger handler right now this.tick(); } }; _proto.onFragParsingData = function onFragParsingData(data) { var _this2 = this; var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && !(data.type === 'audio' && this.altAudio) && // filter out main audio if audio track is loaded through audio stream controller this.state === State.PARSING) { var level = this.levels[this.level], frag = fragCurrent; if (!Object(number["isFiniteNumber"])(data.endPTS)) { data.endPTS = data.startPTS + fragCurrent.duration; data.endDTS = data.startDTS + fragCurrent.duration; } if (data.hasAudio === true) { frag.addElementaryStream(ElementaryStreamTypes.AUDIO); } if (data.hasVideo === true) { frag.addElementaryStream(ElementaryStreamTypes.VIDEO); } logger["logger"].log("Parsed " + data.type + ",PTS:[" + data.startPTS.toFixed(3) + "," + data.endPTS.toFixed(3) + "],DTS:[" + data.startDTS.toFixed(3) + "/" + data.endDTS.toFixed(3) + "],nb:" + data.nb + ",dropped:" + (data.dropped || 0)); // Detect gaps in a fragment and try to fix it by finding a keyframe in the previous fragment (see _findFragments) if (data.type === 'video') { frag.dropped = data.dropped; if (frag.dropped) { if (!frag.backtracked) { var levelDetails = level.details; if (levelDetails && frag.sn === levelDetails.startSN) { logger["logger"].warn('missing video frame(s) on first frag, appending with gap', frag.sn); } else { logger["logger"].warn('missing video frame(s), backtracking fragment', frag.sn); // Return back to the IDLE state without appending to buffer // Causes findFragments to backtrack a segment and find the keyframe // Audio fragments arriving before video sets the nextLoadPosition, causing _findFragments to skip the backtracked fragment this.fragmentTracker.removeFragment(frag); frag.backtracked = true; this.nextLoadPosition = data.startPTS; this.state = State.IDLE; this.fragPrevious = frag; if (this.demuxer) { this.demuxer.destroy(); this.demuxer = null; } this.tick(); return; } } else { logger["logger"].warn('Already backtracked on this fragment, appending with the gap', frag.sn); } } else { // Only reset the backtracked flag if we've loaded the frag without any dropped frames frag.backtracked = false; } } var drift = updateFragPTSDTS(level.details, frag, data.startPTS, data.endPTS, data.startDTS, data.endDTS), hls = this.hls; hls.trigger(events["default"].LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift, type: data.type, start: data.startPTS, end: data.endPTS }); // has remuxer dropped video frames located before first keyframe ? [data.data1, data.data2].forEach(function (buffer) { // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending) // in that case it is useless to append following segments if (buffer && buffer.length && _this2.state === State.PARSING) { _this2.appended = true; // arm pending Buffering flag before appending a segment _this2.pendingBuffering = true; hls.trigger(events["default"].BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'main', content: 'data' }); } }); // trigger handler right now this.tick(); } }; _proto.onFragParsed = function onFragParsed(data) { var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) { this.stats.tparsed = window.performance.now(); this.state = State.PARSED; this._checkAppendedParsed(); } }; _proto.onAudioTrackSwitching = function onAudioTrackSwitching(data) { // if any URL found on new audio track, it is an alternate audio track var fromAltAudio = this.altAudio; var altAudio = !!data.url; var trackId = data.id; // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered // don't do anything if we switch to alt audio: audio stream controller is handling it. // we will just have to change buffer scheduling on audioTrackSwitched if (!altAudio) { if (this.mediaBuffer !== this.media) { logger["logger"].log('switching on main audio, use media.buffered to schedule main fragment loading'); this.mediaBuffer = this.media; var fragCurrent = this.fragCurrent; // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch if (fragCurrent.loader) { logger["logger"].log('switching to main audio track, cancel main fragment load'); fragCurrent.loader.abort(); } this.fragCurrent = null; this.fragPrevious = null; // destroy demuxer to force init segment generation (following audio switch) if (this.demuxer) { this.demuxer.destroy(); this.demuxer = null; } // switch to IDLE state to load new fragment this.state = State.IDLE; } var hls = this.hls; // If switching from alt to main audio, flush all audio and trigger track switched if (fromAltAudio) { hls.trigger(events["default"].BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); } hls.trigger(events["default"].AUDIO_TRACK_SWITCHED, { id: trackId }); } }; _proto.onAudioTrackSwitched = function onAudioTrackSwitched(data) { var trackId = data.id, altAudio = !!this.hls.audioTracks[trackId].url; if (altAudio) { var videoBuffer = this.videoBuffer; // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered if (videoBuffer && this.mediaBuffer !== videoBuffer) { logger["logger"].log('switching on alternate audio, use video.buffered to schedule main fragment loading'); this.mediaBuffer = videoBuffer; } } this.altAudio = altAudio; this.tick(); }; _proto.onBufferCreated = function onBufferCreated(data) { var tracks = data.tracks, mediaTrack, name, alternate = false; for (var type in tracks) { var track = tracks[type]; if (track.id === 'main') { name = type; mediaTrack = track; // keep video source buffer reference if (type === 'video') { this.videoBuffer = tracks[type].buffer; } } else { alternate = true; } } if (alternate && mediaTrack) { logger["logger"].log("alternate track found, use " + name + ".buffered to schedule main fragment loading"); this.mediaBuffer = mediaTrack.buffer; } else { this.mediaBuffer = this.media; } }; _proto.onBufferAppended = function onBufferAppended(data) { if (data.parent === 'main') { var state = this.state; if (state === State.PARSING || state === State.PARSED) { // check if all buffers have been appended this.pendingBuffering = data.pending > 0; this._checkAppendedParsed(); } } }; _proto._checkAppendedParsed = function _checkAppendedParsed() { // trigger handler right now if (this.state === State.PARSED && (!this.appended || !this.pendingBuffering)) { var frag = this.fragCurrent; if (frag) { var media = this.mediaBuffer ? this.mediaBuffer : this.media; logger["logger"].log("main buffered : " + time_ranges.toString(media.buffered)); this.fragPrevious = frag; var stats = this.stats; stats.tbuffered = window.performance.now(); // we should get rid of this.fragLastKbps this.fragLastKbps = Math.round(8 * stats.total / (stats.tbuffered - stats.tfirst)); this.hls.trigger(events["default"].FRAG_BUFFERED, { stats: stats, frag: frag, id: 'main' }); this.state = State.IDLE; } // Do not tick when _seekToStartPos needs to be called as seeking to the start can fail on live streams at this point if (this.loadedmetadata || this.startPosition <= 0) { this.tick(); } } }; _proto.onError = function onError(data) { var frag = data.frag || this.fragCurrent; // don't handle frag error not related to main fragment if (frag && frag.type !== 'main') { return; } // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end var mediaBuffered = !!this.media && BufferHelper.isBuffered(this.media, this.media.currentTime) && BufferHelper.isBuffered(this.media, this.media.currentTime + 0.5); switch (data.details) { case errors["ErrorDetails"].FRAG_LOAD_ERROR: case errors["ErrorDetails"].FRAG_LOAD_TIMEOUT: case errors["ErrorDetails"].KEY_LOAD_ERROR: case errors["ErrorDetails"].KEY_LOAD_TIMEOUT: if (!data.fatal) { // keep retrying until the limit will be reached if (this.fragLoadError + 1 <= this.config.fragLoadingMaxRetry) { // exponential backoff capped to config.fragLoadingMaxRetryTimeout var delay = Math.min(Math.pow(2, this.fragLoadError) * this.config.fragLoadingRetryDelay, this.config.fragLoadingMaxRetryTimeout); logger["logger"].warn("mediaController: frag loading failed, retry in " + delay + " ms"); this.retryDate = window.performance.now() + delay; // retry loading state // if loadedmetadata is not set, it means that we are emergency switch down on first frag // in that case, reset startFragRequested flag if (!this.loadedmetadata) { this.startFragRequested = false; this.nextLoadPosition = this.startPosition; } this.fragLoadError++; this.state = State.FRAG_LOADING_WAITING_RETRY; } else { logger["logger"].error("mediaController: " + data.details + " reaches max retry, redispatch as fatal ..."); // switch error to fatal data.fatal = true; this.state = State.ERROR; } } break; case errors["ErrorDetails"].LEVEL_LOAD_ERROR: case errors["ErrorDetails"].LEVEL_LOAD_TIMEOUT: if (this.state !== State.ERROR) { if (data.fatal) { // if fatal error, stop processing this.state = State.ERROR; logger["logger"].warn("streamController: " + data.details + ",switch to " + this.state + " state ..."); } else { // in case of non fatal error while loading level, if level controller is not retrying to load level , switch back to IDLE if (!data.levelRetry && this.state === State.WAITING_LEVEL) { this.state = State.IDLE; } } } break; case errors["ErrorDetails"].BUFFER_FULL_ERROR: // if in appending state if (data.parent === 'main' && (this.state === State.PARSING || this.state === State.PARSED)) { // reduce max buf len if current position is buffered if (mediaBuffered) { this._reduceMaxBufferLength(this.config.maxBufferLength); this.state = State.IDLE; } else { // current position is not buffered, but browser is still complaining about buffer full error // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 // in that case flush the whole buffer to recover logger["logger"].warn('buffer full error also media.currentTime is not buffered, flush everything'); this.fragCurrent = null; // flush everything this.flushMainBuffer(0, Number.POSITIVE_INFINITY); } } break; default: break; } }; _proto._reduceMaxBufferLength = function _reduceMaxBufferLength(minLength) { var config = this.config; if (config.maxMaxBufferLength >= minLength) { // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... config.maxMaxBufferLength /= 2; logger["logger"].warn("main:reduce max buffer length to " + config.maxMaxBufferLength + "s"); return true; } return false; } /** * Checks the health of the buffer and attempts to resolve playback stalls. * @private */ ; _proto._checkBuffer = function _checkBuffer() { var media = this.media; if (!media || media.readyState === 0) { // Exit early if we don't have media or if the media hasn't bufferd anything yet (readyState 0) return; } var mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media; var buffered = mediaBuffer.buffered; if (!this.loadedmetadata && buffered.length) { this.loadedmetadata = true; this._seekToStartPos(); } else if (this.immediateSwitch) { this.immediateLevelSwitchEnd(); } else { this.gapController.poll(this.lastCurrentTime, buffered); } }; _proto.onFragLoadEmergencyAborted = function onFragLoadEmergencyAborted() { this.state = State.IDLE; // if loadedmetadata is not set, it means that we are emergency switch down on first frag // in that case, reset startFragRequested flag if (!this.loadedmetadata) { this.startFragRequested = false; this.nextLoadPosition = this.startPosition; } this.tick(); }; _proto.onBufferFlushed = function onBufferFlushed() { /* after successful buffer flushing, filter flushed fragments from bufferedFrags use mediaBuffered instead of media (so that we will check against video.buffered ranges in case of alt audio track) */ var media = this.mediaBuffer ? this.mediaBuffer : this.media; if (media) { // filter fragments potentially evicted from buffer. this is to avoid memleak on live streams var elementaryStreamType = this.audioOnly ? ElementaryStreamTypes.AUDIO : ElementaryStreamTypes.VIDEO; this.fragmentTracker.detectEvictedFragments(elementaryStreamType, media.buffered); } // move to IDLE once flush complete. this should trigger new fragment loading this.state = State.IDLE; // reset reference to frag this.fragPrevious = null; }; _proto.onLevelsUpdated = function onLevelsUpdated(data) { this.levels = data.levels; }; _proto.swapAudioCodec = function swapAudioCodec() { this.audioCodecSwap = !this.audioCodecSwap; } /** * Seeks to the set startPosition if not equal to the mediaElement's current time. * @private */ ; _proto._seekToStartPos = function _seekToStartPos() { var media = this.media; var currentTime = media.currentTime; var startPosition = this.startPosition; // only adjust currentTime if different from startPosition or if startPosition not buffered // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered if (currentTime !== startPosition && startPosition >= 0) { if (media.seeking) { logger["logger"].log("could not seek to " + startPosition + ", already seeking at " + currentTime); return; } var bufferStart = media.buffered.length ? media.buffered.start(0) : 0; var delta = bufferStart - startPosition; if (delta > 0 && delta < this.config.maxBufferHole) { logger["logger"].log("adjusting start position by " + delta + " to match buffer start"); startPosition += delta; this.startPosition = startPosition; } logger["logger"].log("seek to target start position " + startPosition + " from current time " + currentTime + ". ready state " + media.readyState); media.currentTime = startPosition; } }; _proto._getAudioCodec = function _getAudioCodec(currentLevel) { var audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec; if (this.audioCodecSwap) { logger["logger"].log('swapping playlist audio codec'); if (audioCodec) { if (audioCodec.indexOf('mp4a.40.5') !== -1) { audioCodec = 'mp4a.40.2'; } else { audioCodec = 'mp4a.40.5'; } } } return audioCodec; }; stream_controller_createClass(StreamController, [{ key: "state", set: function set(nextState) { if (this.state !== nextState) { var previousState = this.state; this._state = nextState; logger["logger"].log("main stream-controller: " + previousState + "->" + nextState); this.hls.trigger(events["default"].STREAM_STATE_TRANSITION, { previousState: previousState, nextState: nextState }); } }, get: function get() { return this._state; } }, { key: "currentLevel", get: function get() { var media = this.media; if (media) { var frag = this.getBufferedFrag(media.currentTime); if (frag) { return frag.level; } } return -1; } }, { key: "nextBufferedFrag", get: function get() { var media = this.media; if (media) { // first get end range of current fragment return this.followingBufferedFrag(this.getBufferedFrag(media.currentTime)); } else { return null; } } }, { key: "nextLevel", get: function get() { var frag = this.nextBufferedFrag; if (frag) { return frag.level; } else { return -1; } } }, { key: "liveSyncPosition", get: function get() { return this._liveSyncPosition; }, set: function set(value) { this._liveSyncPosition = value; } }]); return StreamController; }(base_stream_controller_BaseStreamController); /* harmony default export */ var stream_controller = (stream_controller_StreamController); // CONCATENATED MODULE: ./src/controller/level-controller.js function level_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function level_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) level_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) level_controller_defineProperties(Constructor, staticProps); return Constructor; } function level_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Level Controller */ var chromeOrFirefox; var level_controller_LevelController = /*#__PURE__*/function (_EventHandler) { level_controller_inheritsLoose(LevelController, _EventHandler); function LevelController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MANIFEST_LOADED, events["default"].LEVEL_LOADED, events["default"].AUDIO_TRACK_SWITCHED, events["default"].FRAG_LOADED, events["default"].ERROR) || this; _this.canload = false; _this.currentLevelIndex = null; _this.manualLevelIndex = -1; _this.timer = null; chromeOrFirefox = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); return _this; } var _proto = LevelController.prototype; _proto.onHandlerDestroying = function onHandlerDestroying() { this.clearTimer(); this.manualLevelIndex = -1; }; _proto.clearTimer = function clearTimer() { if (this.timer !== null) { clearTimeout(this.timer); this.timer = null; } }; _proto.startLoad = function startLoad() { var levels = this._levels; this.canload = true; this.levelRetryCount = 0; // clean up live level details to force reload them, and reset load errors if (levels) { levels.forEach(function (level) { level.loadError = 0; var levelDetails = level.details; if (levelDetails && levelDetails.live) { level.details = undefined; } }); } // speed up live playlist refresh if timer exists if (this.timer !== null) { this.loadLevel(); } }; _proto.stopLoad = function stopLoad() { this.canload = false; }; _proto.onManifestLoaded = function onManifestLoaded(data) { var levels = []; var audioTracks = []; var bitrateStart; var levelSet = {}; var levelFromSet = null; var videoCodecFound = false; var audioCodecFound = false; // regroup redundant levels together data.levels.forEach(function (level) { var attributes = level.attrs; level.loadError = 0; level.fragmentError = false; videoCodecFound = videoCodecFound || !!level.videoCodec; audioCodecFound = audioCodecFound || !!level.audioCodec; // erase audio codec info if browser does not support mp4a.40.34. // demuxer will autodetect codec and fallback to mpeg/audio if (chromeOrFirefox && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { level.audioCodec = undefined; } levelFromSet = levelSet[level.bitrate]; // FIXME: we would also have to match the resolution here if (!levelFromSet) { level.url = [level.url]; level.urlId = 0; levelSet[level.bitrate] = level; levels.push(level); } else { levelFromSet.url.push(level.url); } if (attributes) { if (attributes.AUDIO) { addGroupId(levelFromSet || level, 'audio', attributes.AUDIO); } if (attributes.SUBTITLES) { addGroupId(levelFromSet || level, 'text', attributes.SUBTITLES); } } }); // remove audio-only level if we also have levels with audio+video codecs signalled if (videoCodecFound && audioCodecFound) { levels = levels.filter(function (_ref) { var videoCodec = _ref.videoCodec; return !!videoCodec; }); } // only keep levels with supported audio/video codecs levels = levels.filter(function (_ref2) { var audioCodec = _ref2.audioCodec, videoCodec = _ref2.videoCodec; return (!audioCodec || isCodecSupportedInMp4(audioCodec, 'audio')) && (!videoCodec || isCodecSupportedInMp4(videoCodec, 'video')); }); if (data.audioTracks) { audioTracks = data.audioTracks.filter(function (track) { return !track.audioCodec || isCodecSupportedInMp4(track.audioCodec, 'audio'); }); // Reassign id's after filtering since they're used as array indices audioTracks.forEach(function (track, index) { track.id = index; }); } if (levels.length > 0) { // start bitrate is the first bitrate of the manifest bitrateStart = levels[0].bitrate; // sort level on bitrate levels.sort(function (a, b) { return a.bitrate - b.bitrate; }); this._levels = levels; // find index of first level in sorted levels for (var i = 0; i < levels.length; i++) { if (levels[i].bitrate === bitrateStart) { this._firstLevel = i; logger["logger"].log("manifest loaded," + levels.length + " level(s) found, first bitrate:" + bitrateStart); break; } } // Audio is only alternate if manifest include a URI along with the audio group tag, // and this is not an audio-only stream where levels contain audio-only var audioOnly = audioCodecFound && !videoCodecFound; this.hls.trigger(events["default"].MANIFEST_PARSED, { levels: levels, audioTracks: audioTracks, firstLevel: this._firstLevel, stats: data.stats, audio: audioCodecFound, video: videoCodecFound, altAudio: !audioOnly && audioTracks.some(function (t) { return !!t.url; }) }); } else { this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].MANIFEST_INCOMPATIBLE_CODECS_ERROR, fatal: true, url: this.hls.url, reason: 'no level with compatible codecs found in manifest' }); } }; _proto.setLevelInternal = function setLevelInternal(newLevel) { var levels = this._levels; var hls = this.hls; // check if level idx is valid if (newLevel >= 0 && newLevel < levels.length) { // stopping live reloading timer if any this.clearTimer(); if (this.currentLevelIndex !== newLevel) { logger["logger"].log("switching to level " + newLevel); this.currentLevelIndex = newLevel; var levelProperties = levels[newLevel]; levelProperties.level = newLevel; hls.trigger(events["default"].LEVEL_SWITCHING, levelProperties); } var level = levels[newLevel]; var levelDetails = level.details; // check if we need to load playlist for this level if (!levelDetails || levelDetails.live) { // level not retrieved yet, or live playlist we need to (re)load it var urlId = level.urlId; hls.trigger(events["default"].LEVEL_LOADING, { url: level.url[urlId], level: newLevel, id: urlId }); } } else { // invalid level id given, trigger error hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].OTHER_ERROR, details: errors["ErrorDetails"].LEVEL_SWITCH_ERROR, level: newLevel, fatal: false, reason: 'invalid level idx' }); } }; _proto.onError = function onError(data) { if (data.fatal) { if (data.type === errors["ErrorTypes"].NETWORK_ERROR) { this.clearTimer(); } return; } var levelError = false, fragmentError = false; var levelIndex; // try to recover not fatal errors switch (data.details) { case errors["ErrorDetails"].FRAG_LOAD_ERROR: case errors["ErrorDetails"].FRAG_LOAD_TIMEOUT: case errors["ErrorDetails"].KEY_LOAD_ERROR: case errors["ErrorDetails"].KEY_LOAD_TIMEOUT: levelIndex = data.frag.level; fragmentError = true; break; case errors["ErrorDetails"].LEVEL_LOAD_ERROR: case errors["ErrorDetails"].LEVEL_LOAD_TIMEOUT: levelIndex = data.context.level; levelError = true; break; case errors["ErrorDetails"].REMUX_ALLOC_ERROR: levelIndex = data.level; levelError = true; break; } if (levelIndex !== undefined) { this.recoverLevel(data, levelIndex, levelError, fragmentError); } } /** * Switch to a redundant stream if any available. * If redundant stream is not available, emergency switch down if ABR mode is enabled. * * @param {Object} errorEvent * @param {Number} levelIndex current level index * @param {Boolean} levelError * @param {Boolean} fragmentError */ // FIXME Find a better abstraction where fragment/level retry management is well decoupled ; _proto.recoverLevel = function recoverLevel(errorEvent, levelIndex, levelError, fragmentError) { var _this2 = this; var config = this.hls.config; var errorDetails = errorEvent.details; var level = this._levels[levelIndex]; var redundantLevels, delay, nextLevel; level.loadError++; level.fragmentError = fragmentError; if (levelError) { if (this.levelRetryCount + 1 <= config.levelLoadingMaxRetry) { // exponential backoff capped to max retry timeout delay = Math.min(Math.pow(2, this.levelRetryCount) * config.levelLoadingRetryDelay, config.levelLoadingMaxRetryTimeout); // Schedule level reload this.timer = setTimeout(function () { return _this2.loadLevel(); }, delay); // boolean used to inform stream controller not to switch back to IDLE on non fatal error errorEvent.levelRetry = true; this.levelRetryCount++; logger["logger"].warn("level controller, " + errorDetails + ", retry in " + delay + " ms, current retry count is " + this.levelRetryCount); } else { logger["logger"].error("level controller, cannot recover from " + errorDetails + " error"); this.currentLevelIndex = null; // stopping live reloading timer if any this.clearTimer(); // switch error to fatal errorEvent.fatal = true; return; } } // Try any redundant streams if available for both errors: level and fragment // If level.loadError reaches redundantLevels it means that we tried them all, no hope => let's switch down if (levelError || fragmentError) { redundantLevels = level.url.length; if (redundantLevels > 1 && level.loadError < redundantLevels) { level.urlId = (level.urlId + 1) % redundantLevels; level.details = undefined; logger["logger"].warn("level controller, " + errorDetails + " for level " + levelIndex + ": switching to redundant URL-id " + level.urlId); // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); // console.log('New video quality level audio group id:', level.attrs.AUDIO); } else { // Search for available level if (this.manualLevelIndex === -1) { // When lowest level has been reached, let's start hunt from the top nextLevel = levelIndex === 0 ? this._levels.length - 1 : levelIndex - 1; logger["logger"].warn("level controller, " + errorDetails + ": switch to " + nextLevel); this.hls.nextAutoLevel = this.currentLevelIndex = nextLevel; } else if (fragmentError) { // Allow fragment retry as long as configuration allows. // reset this._level so that another call to set level() will trigger again a frag load logger["logger"].warn("level controller, " + errorDetails + ": reload a fragment"); this.currentLevelIndex = null; } } } } // reset errors on the successful load of a fragment ; _proto.onFragLoaded = function onFragLoaded(_ref3) { var frag = _ref3.frag; if (frag !== undefined && frag.type === 'main') { var level = this._levels[frag.level]; if (level !== undefined) { level.fragmentError = false; level.loadError = 0; this.levelRetryCount = 0; } } }; _proto.onLevelLoaded = function onLevelLoaded(data) { var _this3 = this; var level = data.level, details = data.details; // only process level loaded events matching with expected level if (level !== this.currentLevelIndex) { return; } var curLevel = this._levels[level]; // reset level load error counter on successful level loaded only if there is no issues with fragments if (!curLevel.fragmentError) { curLevel.loadError = 0; this.levelRetryCount = 0; } // if current playlist is a live playlist, arm a timer to reload it if (details.live) { var reloadInterval = computeReloadInterval(curLevel.details, details, data.stats.trequest); logger["logger"].log("live playlist, reload in " + Math.round(reloadInterval) + " ms"); this.timer = setTimeout(function () { return _this3.loadLevel(); }, reloadInterval); } else { this.clearTimer(); } }; _proto.onAudioTrackSwitched = function onAudioTrackSwitched(data) { var audioGroupId = this.hls.audioTracks[data.id].groupId; var currentLevel = this.hls.levels[this.currentLevelIndex]; if (!currentLevel) { return; } if (currentLevel.audioGroupIds) { var urlId = -1; for (var i = 0; i < currentLevel.audioGroupIds.length; i++) { if (currentLevel.audioGroupIds[i] === audioGroupId) { urlId = i; break; } } if (urlId !== currentLevel.urlId) { currentLevel.urlId = urlId; this.startLoad(); } } }; _proto.loadLevel = function loadLevel() { logger["logger"].debug('call to loadLevel'); if (this.currentLevelIndex !== null && this.canload) { var levelObject = this._levels[this.currentLevelIndex]; if (typeof levelObject === 'object' && levelObject.url.length > 0) { var level = this.currentLevelIndex; var id = levelObject.urlId; var url = levelObject.url[id]; logger["logger"].log("Attempt loading level index " + level + " with URL-id " + id); // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level); this.hls.trigger(events["default"].LEVEL_LOADING, { url: url, level: level, id: id }); } } }; _proto.removeLevel = function removeLevel(levelIndex, urlId) { var levels = this.levels.filter(function (level, index) { if (index !== levelIndex) { return true; } if (level.url.length > 1 && urlId !== undefined) { level.url = level.url.filter(function (url, id) { return id !== urlId; }); level.urlId = 0; return true; } return false; }).map(function (level, index) { var details = level.details; if (details && details.fragments) { details.fragments.forEach(function (fragment) { fragment.level = index; }); } return level; }); this._levels = levels; this.hls.trigger(events["default"].LEVELS_UPDATED, { levels: levels }); }; level_controller_createClass(LevelController, [{ key: "levels", get: function get() { return this._levels; } }, { key: "level", get: function get() { return this.currentLevelIndex; }, set: function set(newLevel) { var levels = this._levels; if (levels) { newLevel = Math.min(newLevel, levels.length - 1); if (this.currentLevelIndex !== newLevel || !levels[newLevel].details) { this.setLevelInternal(newLevel); } } } }, { key: "manualLevel", get: function get() { return this.manualLevelIndex; }, set: function set(newLevel) { this.manualLevelIndex = newLevel; if (this._startLevel === undefined) { this._startLevel = newLevel; } if (newLevel !== -1) { this.level = newLevel; } } }, { key: "firstLevel", get: function get() { return this._firstLevel; }, set: function set(newLevel) { this._firstLevel = newLevel; } }, { key: "startLevel", get: function get() { // hls.startLevel takes precedence over config.startLevel // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest) if (this._startLevel === undefined) { var configStartLevel = this.hls.config.startLevel; if (configStartLevel !== undefined) { return configStartLevel; } else { return this._firstLevel; } } else { return this._startLevel; } }, set: function set(newLevel) { this._startLevel = newLevel; } }, { key: "nextLoadLevel", get: function get() { if (this.manualLevelIndex !== -1) { return this.manualLevelIndex; } else { return this.hls.nextAutoLevel; } }, set: function set(nextLevel) { this.level = nextLevel; if (this.manualLevelIndex === -1) { this.hls.nextAutoLevel = nextLevel; } } }]); return LevelController; }(event_handler); // EXTERNAL MODULE: ./src/demux/id3.js var id3 = __webpack_require__("./src/demux/id3.js"); // CONCATENATED MODULE: ./src/utils/texttrack-utils.ts function sendAddTrackEvent(track, videoEl) { var event; try { event = new Event('addtrack'); } catch (err) { // for IE11 event = document.createEvent('Event'); event.initEvent('addtrack', false, false); } event.track = track; videoEl.dispatchEvent(event); } function clearCurrentCues(track) { if (track === null || track === void 0 ? void 0 : track.cues) { while (track.cues.length > 0) { track.removeCue(track.cues[0]); } } } /** * Given a list of Cues, finds the closest cue matching the given time. * Modified verison of binary search O(log(n)). * * @export * @param {(TextTrackCueList | TextTrackCue[])} cues - List of cues. * @param {number} time - Target time, to find closest cue to. * @returns {TextTrackCue} */ function getClosestCue(cues, time) { // If the offset is less than the first element, the first element is the closest. if (time < cues[0].endTime) { return cues[0]; } // If the offset is greater than the last cue, the last is the closest. if (time > cues[cues.length - 1].endTime) { return cues[cues.length - 1]; } var left = 0; var right = cues.length - 1; while (left <= right) { var mid = Math.floor((right + left) / 2); if (time < cues[mid].endTime) { right = mid - 1; } else if (time > cues[mid].endTime) { left = mid + 1; } else { // If it's not lower or higher, it must be equal. return cues[mid]; } } // At this point, left and right have swapped. // No direct match was found, left or right element must be the closest. Check which one has the smallest diff. return cues[left].endTime - time < time - cues[right].endTime ? cues[left] : cues[right]; } // CONCATENATED MODULE: ./src/controller/id3-track-controller.js function id3_track_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * id3 metadata track controller */ var MIN_CUE_DURATION = 0.25; var id3_track_controller_ID3TrackController = /*#__PURE__*/function (_EventHandler) { id3_track_controller_inheritsLoose(ID3TrackController, _EventHandler); function ID3TrackController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHING, events["default"].FRAG_PARSING_METADATA, events["default"].LIVE_BACK_BUFFER_REACHED) || this; _this.id3Track = undefined; _this.media = undefined; return _this; } var _proto = ID3TrackController.prototype; _proto.destroy = function destroy() { event_handler.prototype.destroy.call(this); } // Add ID3 metatadata text track. ; _proto.onMediaAttached = function onMediaAttached(data) { this.media = data.media; if (!this.media) {} }; _proto.onMediaDetaching = function onMediaDetaching() { clearCurrentCues(this.id3Track); this.id3Track = undefined; this.media = undefined; }; _proto.getID3Track = function getID3Track(textTracks) { for (var i = 0; i < textTracks.length; i++) { var textTrack = textTracks[i]; if (textTrack.kind === 'metadata' && textTrack.label === 'id3') { // send 'addtrack' when reusing the textTrack for metadata, // same as what we do for captions sendAddTrackEvent(textTrack, this.media); return textTrack; } } return this.media.addTextTrack('metadata', 'id3'); }; _proto.onFragParsingMetadata = function onFragParsingMetadata(data) { var fragment = data.frag; var samples = data.samples; // create track dynamically if (!this.id3Track) { this.id3Track = this.getID3Track(this.media.textTracks); this.id3Track.mode = 'hidden'; } // Attempt to recreate Safari functionality by creating // WebKitDataCue objects when available and store the decoded // ID3 data in the value property of the cue var Cue = window.WebKitDataCue || window.VTTCue || window.TextTrackCue; for (var i = 0; i < samples.length; i++) { var frames = id3["default"].getID3Frames(samples[i].data); if (frames) { // Ensure the pts is positive - sometimes it's reported as a small negative number var startTime = Math.max(samples[i].pts, 0); var endTime = i < samples.length - 1 ? samples[i + 1].pts : fragment.endPTS; if (!endTime) { endTime = fragment.start + fragment.duration; } var timeDiff = endTime - startTime; if (timeDiff <= 0) { endTime = startTime + MIN_CUE_DURATION; } for (var j = 0; j < frames.length; j++) { var frame = frames[j]; // Safari doesn't put the timestamp frame in the TextTrack if (!id3["default"].isTimeStampFrame(frame)) { var cue = new Cue(startTime, endTime, ''); cue.value = frame; this.id3Track.addCue(cue); } } } } }; _proto.onLiveBackBufferReached = function onLiveBackBufferReached(_ref) { var bufferEnd = _ref.bufferEnd; var id3Track = this.id3Track; if (!id3Track || !id3Track.cues || !id3Track.cues.length) { return; } var foundCue = getClosestCue(id3Track.cues, bufferEnd); if (!foundCue) { return; } while (id3Track.cues[0] !== foundCue) { id3Track.removeCue(id3Track.cues[0]); } }; return ID3TrackController; }(event_handler); /* harmony default export */ var id3_track_controller = (id3_track_controller_ID3TrackController); // CONCATENATED MODULE: ./src/is-supported.ts function is_supported_isSupported() { var mediaSource = getMediaSource(); if (!mediaSource) { return false; } var sourceBuffer = self.SourceBuffer || self.WebKitSourceBuffer; var isTypeSupported = mediaSource && typeof mediaSource.isTypeSupported === 'function' && mediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); // if SourceBuffer is exposed ensure its API is valid // safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible var sourceBufferValidAPI = !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function'; return !!isTypeSupported && !!sourceBufferValidAPI; } // CONCATENATED MODULE: ./src/utils/ewma.ts /* * compute an Exponential Weighted moving average * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average * - heavily inspired from shaka-player */ var EWMA = /*#__PURE__*/function () { // About half of the estimated value will be from the last |halfLife| samples by weight. function EWMA(halfLife) { this.alpha_ = void 0; this.estimate_ = void 0; this.totalWeight_ = void 0; // Larger values of alpha expire historical data more slowly. this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; this.estimate_ = 0; this.totalWeight_ = 0; } var _proto = EWMA.prototype; _proto.sample = function sample(weight, value) { var adjAlpha = Math.pow(this.alpha_, weight); this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; this.totalWeight_ += weight; }; _proto.getTotalWeight = function getTotalWeight() { return this.totalWeight_; }; _proto.getEstimate = function getEstimate() { if (this.alpha_) { var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); return this.estimate_ / zeroFactor; } else { return this.estimate_; } }; return EWMA; }(); /* harmony default export */ var ewma = (EWMA); // CONCATENATED MODULE: ./src/utils/ewma-bandwidth-estimator.ts /* * EWMA Bandwidth Estimator * - heavily inspired from shaka-player * Tracks bandwidth samples and estimates available bandwidth. * Based on the minimum of two exponentially-weighted moving averages with * different half-lives. */ var ewma_bandwidth_estimator_EwmaBandWidthEstimator = /*#__PURE__*/function () { // TODO(typescript-hls) function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) { this.hls = void 0; this.defaultEstimate_ = void 0; this.minWeight_ = void 0; this.minDelayMs_ = void 0; this.slow_ = void 0; this.fast_ = void 0; this.hls = hls; this.defaultEstimate_ = defaultEstimate; this.minWeight_ = 0.001; this.minDelayMs_ = 50; this.slow_ = new ewma(slow); this.fast_ = new ewma(fast); } var _proto = EwmaBandWidthEstimator.prototype; _proto.sample = function sample(durationMs, numBytes) { durationMs = Math.max(durationMs, this.minDelayMs_); var numBits = 8 * numBytes, // weight is duration in seconds durationS = durationMs / 1000, // value is bandwidth in bits/s bandwidthInBps = numBits / durationS; this.fast_.sample(durationS, bandwidthInBps); this.slow_.sample(durationS, bandwidthInBps); }; _proto.canEstimate = function canEstimate() { var fast = this.fast_; return fast && fast.getTotalWeight() >= this.minWeight_; }; _proto.getEstimate = function getEstimate() { if (this.canEstimate()) { // console.log('slow estimate:'+ Math.round(this.slow_.getEstimate())); // console.log('fast estimate:'+ Math.round(this.fast_.getEstimate())); // Take the minimum of these two estimates. This should have the effect of // adapting down quickly, but up more slowly. return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); } else { return this.defaultEstimate_; } }; _proto.destroy = function destroy() {}; return EwmaBandWidthEstimator; }(); /* harmony default export */ var ewma_bandwidth_estimator = (ewma_bandwidth_estimator_EwmaBandWidthEstimator); // CONCATENATED MODULE: ./src/controller/abr-controller.js function abr_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function abr_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) abr_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) abr_controller_defineProperties(Constructor, staticProps); return Constructor; } function abr_controller_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function abr_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * simple ABR Controller * - compute next level based on last fragment bw heuristics * - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling */ var abr_controller_window = window, abr_controller_performance = abr_controller_window.performance; var abr_controller_AbrController = /*#__PURE__*/function (_EventHandler) { abr_controller_inheritsLoose(AbrController, _EventHandler); function AbrController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].FRAG_LOADING, events["default"].FRAG_LOADED, events["default"].FRAG_BUFFERED, events["default"].ERROR) || this; _this.lastLoadedFragLevel = 0; _this._nextAutoLevel = -1; _this.hls = hls; _this.timer = null; _this._bwEstimator = null; _this.onCheck = _this._abandonRulesCheck.bind(abr_controller_assertThisInitialized(_this)); return _this; } var _proto = AbrController.prototype; _proto.destroy = function destroy() { this.clearTimer(); event_handler.prototype.destroy.call(this); }; _proto.onFragLoading = function onFragLoading(data) { var frag = data.frag; if (frag.type === 'main') { if (!this.timer) { this.fragCurrent = frag; this.timer = setInterval(this.onCheck, 100); } // lazy init of BwEstimator, rationale is that we use different params for Live/VoD // so we need to wait for stream manifest / playlist type to instantiate it. if (!this._bwEstimator) { var hls = this.hls; var config = hls.config; var level = frag.level; var isLive = hls.levels[level].details.live; var ewmaFast; var ewmaSlow; if (isLive) { ewmaFast = config.abrEwmaFastLive; ewmaSlow = config.abrEwmaSlowLive; } else { ewmaFast = config.abrEwmaFastVoD; ewmaSlow = config.abrEwmaSlowVoD; } this._bwEstimator = new ewma_bandwidth_estimator(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate); } } }; _proto._abandonRulesCheck = function _abandonRulesCheck() { /* monitor fragment retrieval time... we compute expected time of arrival of the complete fragment. we compare it to expected time of buffer starvation */ var hls = this.hls; var video = hls.media; var frag = this.fragCurrent; if (!frag) { return; } var loader = frag.loader; // if loader has been destroyed or loading has been aborted, stop timer and return if (!loader || loader.stats && loader.stats.aborted) { logger["logger"].warn('frag loader destroy or aborted, disarm abandonRules'); this.clearTimer(); // reset forced auto level value so that next level will be selected this._nextAutoLevel = -1; return; } var stats = loader.stats; /* only monitor frag retrieval time if (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */ if (video && stats && (!video.paused && video.playbackRate !== 0 || !video.readyState) && frag.autoLevel && frag.level) { var requestDelay = abr_controller_performance.now() - stats.trequest; var playbackRate = Math.abs(video.playbackRate); // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate if (requestDelay > 500 * frag.duration / playbackRate) { var levels = hls.levels; var loadRate = Math.max(1, stats.bw ? stats.bw / 8 : stats.loaded * 1000 / requestDelay); // byte/s; at least 1 byte/s to avoid division by zero // compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size var level = levels[frag.level]; if (!level) { return; } var levelBitrate = level.realBitrate ? Math.max(level.realBitrate, level.bitrate) : level.bitrate; var expectedLen = stats.total ? stats.total : Math.max(stats.loaded, Math.round(frag.duration * levelBitrate / 8)); var pos = video.currentTime; var fragLoadedDelay = (expectedLen - stats.loaded) / loadRate; var bufferStarvationDelay = (BufferHelper.bufferInfo(video, pos, hls.config.maxBufferHole).end - pos) / playbackRate; // consider emergency switch down only if we have less than 2 frag buffered AND // time to finish loading current fragment is bigger than buffer starvation delay // ie if we risk buffer starvation if bw does not increase quickly if (bufferStarvationDelay < 2 * frag.duration / playbackRate && fragLoadedDelay > bufferStarvationDelay) { var minAutoLevel = hls.minAutoLevel; var fragLevelNextLoadedDelay; var nextLoadLevel; // lets iterate through lower level and try to find the biggest one that could avoid rebuffering // we start from current level - 1 and we step down , until we find a matching level for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) { // compute time to load next fragment at lower level // 0.8 : consider only 80% of current bw to be conservative // 8 = bits per byte (bps/Bps) var levelNextBitrate = levels[nextLoadLevel].realBitrate ? Math.max(levels[nextLoadLevel].realBitrate, levels[nextLoadLevel].bitrate) : levels[nextLoadLevel].bitrate; var _fragLevelNextLoadedDelay = frag.duration * levelNextBitrate / (8 * 0.8 * loadRate); if (_fragLevelNextLoadedDelay < bufferStarvationDelay) { // we found a lower level that be rebuffering free with current estimated bw ! break; } } // only emergency switch down if it takes less time to load new fragment at lowest level instead // of finishing loading current one ... if (fragLevelNextLoadedDelay < fragLoadedDelay) { logger["logger"].warn("loading too slow, abort fragment loading and switch to level " + nextLoadLevel + ":fragLoadedDelay[" + nextLoadLevel + "]<fragLoadedDelay[" + (frag.level - 1) + "];bufferStarvationDelay:" + fragLevelNextLoadedDelay.toFixed(1) + "<" + fragLoadedDelay.toFixed(1) + ":" + bufferStarvationDelay.toFixed(1)); // force next load level in auto mode hls.nextLoadLevel = nextLoadLevel; // update bw estimate for this fragment before cancelling load (this will help reducing the bw) this._bwEstimator.sample(requestDelay, stats.loaded); // abort fragment loading loader.abort(); // stop abandon rules timer this.clearTimer(); hls.trigger(events["default"].FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats }); } } } } }; _proto.onFragLoaded = function onFragLoaded(data) { var frag = data.frag; if (frag.type === 'main' && Object(number["isFiniteNumber"])(frag.sn)) { // stop monitoring bw once frag loaded this.clearTimer(); // store level id after successful fragment load this.lastLoadedFragLevel = frag.level; // reset forced auto level value so that next level will be selected this._nextAutoLevel = -1; // compute level average bitrate if (this.hls.config.abrMaxWithRealBitrate) { var level = this.hls.levels[frag.level]; var loadedBytes = (level.loaded ? level.loaded.bytes : 0) + data.stats.loaded; var loadedDuration = (level.loaded ? level.loaded.duration : 0) + data.frag.duration; level.loaded = { bytes: loadedBytes, duration: loadedDuration }; level.realBitrate = Math.round(8 * loadedBytes / loadedDuration); } // if fragment has been loaded to perform a bitrate test, if (data.frag.bitrateTest) { var stats = data.stats; stats.tparsed = stats.tbuffered = stats.tload; this.onFragBuffered(data); } } }; _proto.onFragBuffered = function onFragBuffered(data) { var stats = data.stats; var frag = data.frag; // only update stats on first frag buffering // if same frag is loaded multiple times, it might be in browser cache, and loaded quickly // and leading to wrong bw estimation // on bitrate test, also only update stats once (if tload = tbuffered == on FRAG_LOADED) if (stats.aborted !== true && frag.type === 'main' && Object(number["isFiniteNumber"])(frag.sn) && (!frag.bitrateTest || stats.tload === stats.tbuffered)) { // use tparsed-trequest instead of tbuffered-trequest to compute fragLoadingProcessing; rationale is that buffer appending only happens once media is attached // in case we use config.startFragPrefetch while media is not attached yet, fragment might be parsed while media not attached yet, but it will only be buffered on media attached // as a consequence it could happen really late in the process. meaning that appending duration might appears huge ... leading to underestimated throughput estimation var fragLoadingProcessingMs = stats.tparsed - stats.trequest; logger["logger"].log("latency/loading/parsing/append/kbps:" + Math.round(stats.tfirst - stats.trequest) + "/" + Math.round(stats.tload - stats.tfirst) + "/" + Math.round(stats.tparsed - stats.tload) + "/" + Math.round(stats.tbuffered - stats.tparsed) + "/" + Math.round(8 * stats.loaded / (stats.tbuffered - stats.trequest))); this._bwEstimator.sample(fragLoadingProcessingMs, stats.loaded); stats.bwEstimate = this._bwEstimator.getEstimate(); // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration if (frag.bitrateTest) { this.bitrateTestDelay = fragLoadingProcessingMs / 1000; } else { this.bitrateTestDelay = 0; } } }; _proto.onError = function onError(data) { // stop timer in case of frag loading error switch (data.details) { case errors["ErrorDetails"].FRAG_LOAD_ERROR: case errors["ErrorDetails"].FRAG_LOAD_TIMEOUT: this.clearTimer(); break; default: break; } }; _proto.clearTimer = function clearTimer() { clearInterval(this.timer); this.timer = null; } // return next auto level ; _proto._findBestLevel = function _findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) { for (var i = maxAutoLevel; i >= minAutoLevel; i--) { var levelInfo = levels[i]; if (!levelInfo) { continue; } var levelDetails = levelInfo.details; var avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration; var live = levelDetails ? levelDetails.live : false; var adjustedbw = void 0; // follow algorithm captured from stagefright : // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp // Pick the highest bandwidth stream below or equal to estimated bandwidth. // consider only 80% of the available bandwidth, but if we are switching up, // be even more conservative (70%) to avoid overestimating and immediately // switching back. if (i <= currentLevel) { adjustedbw = bwFactor * currentBw; } else { adjustedbw = bwUpFactor * currentBw; } var bitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate; var fetchDuration = bitrate * avgDuration / adjustedbw; logger["logger"].trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: " + i + "/" + Math.round(adjustedbw) + "/" + bitrate + "/" + avgDuration + "/" + maxFetchDuration + "/" + fetchDuration); // if adjusted bw is greater than level bitrate AND if (adjustedbw > bitrate && ( // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ... // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that _findBestLevel will return -1 !fetchDuration || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration)) { // as we are looping from highest to lowest, this will return the best achievable quality level return i; } } // not enough time budget even with quality level 0 ... rebuffering might happen return -1; }; abr_controller_createClass(AbrController, [{ key: "nextAutoLevel", get: function get() { var forcedAutoLevel = this._nextAutoLevel; var bwEstimator = this._bwEstimator; // in case next auto level has been forced, and bw not available or not reliable, return forced value if (forcedAutoLevel !== -1 && (!bwEstimator || !bwEstimator.canEstimate())) { return forcedAutoLevel; } // compute next level using ABR logic var nextABRAutoLevel = this._nextABRAutoLevel; // if forced auto level has been defined, use it to cap ABR computed quality level if (forcedAutoLevel !== -1) { nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel); } return nextABRAutoLevel; }, set: function set(nextLevel) { this._nextAutoLevel = nextLevel; } }, { key: "_nextABRAutoLevel", get: function get() { var hls = this.hls; var maxAutoLevel = hls.maxAutoLevel, levels = hls.levels, config = hls.config, minAutoLevel = hls.minAutoLevel; var video = hls.media; var currentLevel = this.lastLoadedFragLevel; var currentFragDuration = this.fragCurrent ? this.fragCurrent.duration : 0; var pos = video ? video.currentTime : 0; // playbackRate is the absolute value of the playback rate; if video.playbackRate is 0, we use 1 to load as // if we're playing back at the normal rate. var playbackRate = video && video.playbackRate !== 0 ? Math.abs(video.playbackRate) : 1.0; var avgbw = this._bwEstimator ? this._bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate; // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted. var bufferStarvationDelay = (BufferHelper.bufferInfo(video, pos, config.maxBufferHole).end - pos) / playbackRate; // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all var bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor, levels); if (bestLevel >= 0) { return bestLevel; } else { logger["logger"].trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering'); // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering // if no matching level found, logic will return 0 var maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay; var bwFactor = config.abrBandWidthFactor; var bwUpFactor = config.abrBandWidthUpFactor; if (bufferStarvationDelay === 0) { // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test var bitrateTestDelay = this.bitrateTestDelay; if (bitrateTestDelay) { // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value // max video loading delay used in automatic start level selection : // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level + // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` ) // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay; maxStarvationDelay = maxLoadingDelay - bitrateTestDelay; logger["logger"].trace("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms"); // don't use conservative factor on bitrate test bwFactor = bwUpFactor = 1; } } bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor, levels); return Math.max(bestLevel, 0); } } }]); return AbrController; }(event_handler); /* harmony default export */ var abr_controller = (abr_controller_AbrController); // CONCATENATED MODULE: ./src/controller/buffer-controller.ts function buffer_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Buffer Controller */ var buffer_controller_MediaSource = getMediaSource(); var buffer_controller_BufferController = /*#__PURE__*/function (_EventHandler) { buffer_controller_inheritsLoose(BufferController, _EventHandler); // the value that we have set mediasource.duration to // (the actual duration may be tweaked slighly by the browser) // the value that we want to set mediaSource.duration to // the target duration of the current media playlist // current stream state: true - for live broadcast, false - for VoD content // cache the self generated object url to detect hijack of video tag // signals that the sourceBuffers need to be flushed // signals that mediaSource should have endOfStream called // this is optional because this property is removed from the class sometimes // The number of BUFFER_CODEC events received before any sourceBuffers are created // The total number of BUFFER_CODEC events received // A reference to the attached media element // A reference to the active media source // List of pending segments to be appended to source buffer // A guard to see if we are currently appending to the source buffer // counters function BufferController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHING, events["default"].MEDIA_DETACHING, events["default"].MANIFEST_PARSED, events["default"].BUFFER_RESET, events["default"].BUFFER_APPENDING, events["default"].BUFFER_CODECS, events["default"].BUFFER_EOS, events["default"].BUFFER_FLUSHING, events["default"].LEVEL_PTS_UPDATED, events["default"].LEVEL_UPDATED) || this; _this._msDuration = null; _this._levelDuration = null; _this._levelTargetDuration = 10; _this._live = null; _this._objectUrl = null; _this._needsFlush = false; _this._needsEos = false; _this.config = void 0; _this.audioTimestampOffset = void 0; _this.bufferCodecEventsExpected = 0; _this._bufferCodecEventsTotal = 0; _this.media = null; _this.mediaSource = null; _this.segments = []; _this.parent = void 0; _this.appending = false; _this.appended = 0; _this.appendError = 0; _this.flushBufferCounter = 0; _this.tracks = {}; _this.pendingTracks = {}; _this.sourceBuffer = {}; _this.flushRange = []; _this._onMediaSourceOpen = function () { logger["logger"].log('media source opened'); _this.hls.trigger(events["default"].MEDIA_ATTACHED, { media: _this.media }); var mediaSource = _this.mediaSource; if (mediaSource) { // once received, don't listen anymore to sourceopen event mediaSource.removeEventListener('sourceopen', _this._onMediaSourceOpen); } _this.checkPendingTracks(); }; _this._onMediaSourceClose = function () { logger["logger"].log('media source closed'); }; _this._onMediaSourceEnded = function () { logger["logger"].log('media source ended'); }; _this._onSBUpdateEnd = function () { // update timestampOffset if (_this.audioTimestampOffset && _this.sourceBuffer.audio) { var audioBuffer = _this.sourceBuffer.audio; logger["logger"].warn("change mpeg audio timestamp offset from " + audioBuffer.timestampOffset + " to " + _this.audioTimestampOffset); audioBuffer.timestampOffset = _this.audioTimestampOffset; delete _this.audioTimestampOffset; } if (_this._needsFlush) { _this.doFlush(); } if (_this._needsEos) { _this.checkEos(); } _this.appending = false; var parent = _this.parent; // count nb of pending segments waiting for appending on this sourcebuffer var pending = _this.segments.reduce(function (counter, segment) { return segment.parent === parent ? counter + 1 : counter; }, 0); // this.sourceBuffer is better to use than media.buffered as it is closer to the PTS data from the fragments var timeRanges = {}; var sbSet = _this.sourceBuffer; for (var streamType in sbSet) { var sb = sbSet[streamType]; if (!sb) { throw Error("handling source buffer update end error: source buffer for " + streamType + " uninitilized and unable to update buffered TimeRanges."); } timeRanges[streamType] = sb.buffered; } _this.hls.trigger(events["default"].BUFFER_APPENDED, { parent: parent, pending: pending, timeRanges: timeRanges }); // don't append in flushing mode if (!_this._needsFlush) { _this.doAppending(); } _this.updateMediaElementDuration(); // appending goes first if (pending === 0) { _this.flushLiveBackBuffer(); } }; _this._onSBUpdateError = function (event) { logger["logger"].error('sourceBuffer error:', event); // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error // this error might not always be fatal (it is fatal if decode error is set, in that case // it will be followed by a mediaElement error ...) _this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_APPENDING_ERROR, fatal: false }); // we don't need to do more than that, as accordin to the spec, updateend will be fired just after }; _this.config = hls.config; return _this; } var _proto = BufferController.prototype; _proto.destroy = function destroy() { event_handler.prototype.destroy.call(this); }; _proto.onLevelPtsUpdated = function onLevelPtsUpdated(data) { var type = data.type; var audioTrack = this.tracks.audio; // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended) // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset` // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). At the time of change we issue // `SourceBuffer.abort()` and adjusting `SourceBuffer.timestampOffset` if `SourceBuffer.updating` is false or awaiting `updateend` // event if SB is in updating state. // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486 if (type === 'audio' && audioTrack && audioTrack.container === 'audio/mpeg') { // Chrome audio mp3 track var audioBuffer = this.sourceBuffer.audio; if (!audioBuffer) { throw Error('Level PTS Updated and source buffer for audio uninitalized'); } var delta = Math.abs(audioBuffer.timestampOffset - data.start); // adjust timestamp offset if time delta is greater than 100ms if (delta > 0.1) { var updating = audioBuffer.updating; try { audioBuffer.abort(); } catch (err) { logger["logger"].warn('can not abort audio buffer: ' + err); } if (!updating) { logger["logger"].warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + data.start); audioBuffer.timestampOffset = data.start; } else { this.audioTimestampOffset = data.start; } } } }; _proto.onManifestParsed = function onManifestParsed(data) { // in case of alt audio (where all tracks have urls) 2 BUFFER_CODECS events will be triggered, one per stream controller // sourcebuffers will be created all at once when the expected nb of tracks will be reached // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller // it will contain the expected nb of source buffers, no need to compute it var codecEvents = 2; if (data.audio && !data.video || !data.altAudio) { codecEvents = 1; } this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = codecEvents; logger["logger"].log(this.bufferCodecEventsExpected + " bufferCodec event(s) expected"); }; _proto.onMediaAttaching = function onMediaAttaching(data) { var media = this.media = data.media; if (media && buffer_controller_MediaSource) { // setup the media source var ms = this.mediaSource = new buffer_controller_MediaSource(); // Media Source listeners ms.addEventListener('sourceopen', this._onMediaSourceOpen); ms.addEventListener('sourceended', this._onMediaSourceEnded); ms.addEventListener('sourceclose', this._onMediaSourceClose); // link video and media Source media.src = window.URL.createObjectURL(ms); // cache the locally generated object url this._objectUrl = media.src; } }; _proto.onMediaDetaching = function onMediaDetaching() { logger["logger"].log('media source detaching'); var ms = this.mediaSource; if (ms) { if (ms.readyState === 'open') { try { // endOfStream could trigger exception if any sourcebuffer is in updating state // we don't really care about checking sourcebuffer state here, // as we are anyway detaching the MediaSource // let's just avoid this exception to propagate ms.endOfStream(); } catch (err) { logger["logger"].warn("onMediaDetaching:" + err.message + " while calling endOfStream"); } } ms.removeEventListener('sourceopen', this._onMediaSourceOpen); ms.removeEventListener('sourceended', this._onMediaSourceEnded); ms.removeEventListener('sourceclose', this._onMediaSourceClose); // Detach properly the MediaSource from the HTMLMediaElement as // suggested in https://github.com/w3c/media-source/issues/53. if (this.media) { if (this._objectUrl) { window.URL.revokeObjectURL(this._objectUrl); } // clean up video tag src only if it's our own url. some external libraries might // hijack the video tag and change its 'src' without destroying the Hls instance first if (this.media.src === this._objectUrl) { this.media.removeAttribute('src'); this.media.load(); } else { logger["logger"].warn('media.src was changed by a third party - skip cleanup'); } } this.mediaSource = null; this.media = null; this._objectUrl = null; this.bufferCodecEventsExpected = this._bufferCodecEventsTotal; this.pendingTracks = {}; this.tracks = {}; this.sourceBuffer = {}; this.flushRange = []; this.segments = []; this.appended = 0; } this.hls.trigger(events["default"].MEDIA_DETACHED); }; _proto.checkPendingTracks = function checkPendingTracks() { var bufferCodecEventsExpected = this.bufferCodecEventsExpected, pendingTracks = this.pendingTracks; // Check if we've received all of the expected bufferCodec events. When none remain, create all the sourceBuffers at once. // This is important because the MSE spec allows implementations to throw QuotaExceededErrors if creating new sourceBuffers after // data has been appended to existing ones. // 2 tracks is the max (one for audio, one for video). If we've reach this max go ahead and create the buffers. var pendingTracksCount = Object.keys(pendingTracks).length; if (pendingTracksCount && !bufferCodecEventsExpected || pendingTracksCount === 2) { // ok, let's create them now ! this.createSourceBuffers(pendingTracks); this.pendingTracks = {}; // append any pending segments now ! this.doAppending(); } }; _proto.onBufferReset = function onBufferReset() { var sourceBuffer = this.sourceBuffer; for (var type in sourceBuffer) { var sb = sourceBuffer[type]; try { if (sb) { if (this.mediaSource) { this.mediaSource.removeSourceBuffer(sb); } sb.removeEventListener('updateend', this._onSBUpdateEnd); sb.removeEventListener('error', this._onSBUpdateError); } } catch (err) {} } this.sourceBuffer = {}; this.flushRange = []; this.segments = []; this.appended = 0; }; _proto.onBufferCodecs = function onBufferCodecs(tracks) { var _this2 = this; // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks // if sourcebuffers already created, do nothing ... if (Object.keys(this.sourceBuffer).length) { return; } Object.keys(tracks).forEach(function (trackName) { _this2.pendingTracks[trackName] = tracks[trackName]; }); this.bufferCodecEventsExpected = Math.max(this.bufferCodecEventsExpected - 1, 0); if (this.mediaSource && this.mediaSource.readyState === 'open') { this.checkPendingTracks(); } }; _proto.createSourceBuffers = function createSourceBuffers(tracks) { var sourceBuffer = this.sourceBuffer, mediaSource = this.mediaSource; if (!mediaSource) { throw Error('createSourceBuffers called when mediaSource was null'); } for (var trackName in tracks) { if (!sourceBuffer[trackName]) { var track = tracks[trackName]; if (!track) { throw Error("source buffer exists for track " + trackName + ", however track does not"); } // use levelCodec as first priority var codec = track.levelCodec || track.codec; var mimeType = track.container + ";codecs=" + codec; logger["logger"].log("creating sourceBuffer(" + mimeType + ")"); try { var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); sb.addEventListener('updateend', this._onSBUpdateEnd); sb.addEventListener('error', this._onSBUpdateError); this.tracks[trackName] = { buffer: sb, codec: codec, id: track.id, container: track.container, levelCodec: track.levelCodec }; } catch (err) { logger["logger"].error("error while trying to add sourceBuffer:" + err.message); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].BUFFER_ADD_CODEC_ERROR, fatal: false, err: err, mimeType: mimeType }); } } } this.hls.trigger(events["default"].BUFFER_CREATED, { tracks: this.tracks }); }; _proto.onBufferAppending = function onBufferAppending(data) { if (!this._needsFlush) { if (!this.segments) { this.segments = [data]; } else { this.segments.push(data); } this.doAppending(); } } // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos() // an undefined data.type will mark all buffers as EOS. ; _proto.onBufferEos = function onBufferEos(data) { for (var type in this.sourceBuffer) { if (!data.type || data.type === type) { var sb = this.sourceBuffer[type]; if (sb && !sb.ended) { sb.ended = true; logger["logger"].log(type + " sourceBuffer now EOS"); } } } this.checkEos(); } // if all source buffers are marked as ended, signal endOfStream() to MediaSource. ; _proto.checkEos = function checkEos() { var sourceBuffer = this.sourceBuffer, mediaSource = this.mediaSource; if (!mediaSource || mediaSource.readyState !== 'open') { this._needsEos = false; return; } for (var type in sourceBuffer) { var sb = sourceBuffer[type]; if (!sb) continue; if (!sb.ended) { return; } if (sb.updating) { this._needsEos = true; return; } } logger["logger"].log('all media data are available, signal endOfStream() to MediaSource and stop loading fragment'); // Notify the media element that it now has all of the media data try { mediaSource.endOfStream(); } catch (e) { logger["logger"].warn('exception while calling mediaSource.endOfStream()'); } this._needsEos = false; }; _proto.onBufferFlushing = function onBufferFlushing(data) { if (data.type) { this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: data.type }); } else { this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: 'video' }); this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: 'audio' }); } // attempt flush immediately this.flushBufferCounter = 0; this.doFlush(); }; _proto.flushLiveBackBuffer = function flushLiveBackBuffer() { // clear back buffer for live only if (!this._live) { return; } var liveBackBufferLength = this.config.liveBackBufferLength; if (!isFinite(liveBackBufferLength) || liveBackBufferLength < 0) { return; } if (!this.media) { logger["logger"].error('flushLiveBackBuffer called without attaching media'); return; } var currentTime = this.media.currentTime; var sourceBuffer = this.sourceBuffer; var bufferTypes = Object.keys(sourceBuffer); var targetBackBufferPosition = currentTime - Math.max(liveBackBufferLength, this._levelTargetDuration); for (var index = bufferTypes.length - 1; index >= 0; index--) { var bufferType = bufferTypes[index]; var sb = sourceBuffer[bufferType]; if (sb) { var buffered = sb.buffered; // when target buffer start exceeds actual buffer start if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) { // remove buffer up until current time minus minimum back buffer length (removing buffer too close to current // time will lead to playback freezing) // credits for level target duration - https://github.com/videojs/http-streaming/blob/3132933b6aa99ddefab29c10447624efd6fd6e52/src/segment-loader.js#L91 if (this.removeBufferRange(bufferType, sb, 0, targetBackBufferPosition)) { this.hls.trigger(events["default"].LIVE_BACK_BUFFER_REACHED, { bufferEnd: targetBackBufferPosition }); } } } } }; _proto.onLevelUpdated = function onLevelUpdated(_ref) { var details = _ref.details; if (details.fragments.length > 0) { this._levelDuration = details.totalduration + details.fragments[0].start; this._levelTargetDuration = details.averagetargetduration || details.targetduration || 10; this._live = details.live; this.updateMediaElementDuration(); } } /** * Update Media Source duration to current level duration or override to Infinity if configuration parameter * 'liveDurationInfinity` is set to `true` * More details: https://github.com/video-dev/hls.js/issues/355 */ ; _proto.updateMediaElementDuration = function updateMediaElementDuration() { var config = this.config; var duration; if (this._levelDuration === null || !this.media || !this.mediaSource || !this.sourceBuffer || this.media.readyState === 0 || this.mediaSource.readyState !== 'open') { return; } for (var type in this.sourceBuffer) { var sb = this.sourceBuffer[type]; if (sb && sb.updating === true) { // can't set duration whilst a buffer is updating return; } } duration = this.media.duration; // initialise to the value that the media source is reporting if (this._msDuration === null) { this._msDuration = this.mediaSource.duration; } if (this._live === true && config.liveDurationInfinity === true) { // Override duration to Infinity logger["logger"].log('Media Source duration is set to Infinity'); this._msDuration = this.mediaSource.duration = Infinity; } else if (this._levelDuration > this._msDuration && this._levelDuration > duration || !Object(number["isFiniteNumber"])(duration)) { // levelDuration was the last value we set. // not using mediaSource.duration as the browser may tweak this value // only update Media Source duration if its value increase, this is to avoid // flushing already buffered portion when switching between quality level logger["logger"].log("Updating Media Source duration to " + this._levelDuration.toFixed(3)); this._msDuration = this.mediaSource.duration = this._levelDuration; } }; _proto.doFlush = function doFlush() { // loop through all buffer ranges to flush while (this.flushRange.length) { var range = this.flushRange[0]; // flushBuffer will abort any buffer append in progress and flush Audio/Video Buffer if (this.flushBuffer(range.start, range.end, range.type)) { // range flushed, remove from flush array this.flushRange.shift(); this.flushBufferCounter = 0; } else { this._needsFlush = true; // avoid looping, wait for SB update end to retrigger a flush return; } } if (this.flushRange.length === 0) { // everything flushed this._needsFlush = false; // let's recompute this.appended, which is used to avoid flush looping var appended = 0; var sourceBuffer = this.sourceBuffer; try { for (var type in sourceBuffer) { var sb = sourceBuffer[type]; if (sb) { appended += sb.buffered.length; } } } catch (error) { // error could be thrown while accessing buffered, in case sourcebuffer has already been removed from MediaSource // this is harmess at this stage, catch this to avoid reporting an internal exception logger["logger"].error('error while accessing sourceBuffer.buffered'); } this.appended = appended; this.hls.trigger(events["default"].BUFFER_FLUSHED); } }; _proto.doAppending = function doAppending() { var config = this.config, hls = this.hls, segments = this.segments, sourceBuffer = this.sourceBuffer; if (!Object.keys(sourceBuffer).length) { // early exit if no source buffers have been initialized yet return; } if (!this.media || this.media.error) { this.segments = []; logger["logger"].error('trying to append although a media error occured, flush segment and abort'); return; } if (this.appending) { // logger.log(`sb appending in progress`); return; } var segment = segments.shift(); if (!segment) { // handle undefined shift return; } try { var sb = sourceBuffer[segment.type]; if (!sb) { // in case we don't have any source buffer matching with this segment type, // it means that Mediasource fails to create sourcebuffer // discard this segment, and trigger update end this._onSBUpdateEnd(); return; } if (sb.updating) { // if we are still updating the source buffer from the last segment, place this back at the front of the queue segments.unshift(segment); return; } // reset sourceBuffer ended flag before appending segment sb.ended = false; // logger.log(`appending ${segment.content} ${type} SB, size:${segment.data.length}, ${segment.parent}`); this.parent = segment.parent; sb.appendBuffer(segment.data); this.appendError = 0; this.appended++; this.appending = true; } catch (err) { // in case any error occured while appending, put back segment in segments table logger["logger"].error("error while trying to append buffer:" + err.message); segments.unshift(segment); var event = { type: errors["ErrorTypes"].MEDIA_ERROR, parent: segment.parent, details: '', fatal: false }; if (err.code === 22) { // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror // let's stop appending any segments, and report BUFFER_FULL_ERROR error this.segments = []; event.details = errors["ErrorDetails"].BUFFER_FULL_ERROR; } else { this.appendError++; event.details = errors["ErrorDetails"].BUFFER_APPEND_ERROR; /* with UHD content, we could get loop of quota exceeded error until browser is able to evict some data from sourcebuffer. retrying help recovering this */ if (this.appendError > config.appendErrorMaxRetry) { logger["logger"].log("fail " + config.appendErrorMaxRetry + " times to append segment in sourceBuffer"); this.segments = []; event.fatal = true; } } hls.trigger(events["default"].ERROR, event); } } /* flush specified buffered range, return true once range has been flushed. as sourceBuffer.remove() is asynchronous, flushBuffer will be retriggered on sourceBuffer update end */ ; _proto.flushBuffer = function flushBuffer(startOffset, endOffset, sbType) { var sourceBuffer = this.sourceBuffer; // exit if no sourceBuffers are initialized if (!Object.keys(sourceBuffer).length) { return true; } var currentTime = 'null'; if (this.media) { currentTime = this.media.currentTime.toFixed(3); } logger["logger"].log("flushBuffer,pos/start/end: " + currentTime + "/" + startOffset + "/" + endOffset); // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments if (this.flushBufferCounter >= this.appended) { logger["logger"].warn('abort flushing too many retries'); return true; } var sb = sourceBuffer[sbType]; // we are going to flush buffer, mark source buffer as 'not ended' if (sb) { sb.ended = false; if (!sb.updating) { if (this.removeBufferRange(sbType, sb, startOffset, endOffset)) { this.flushBufferCounter++; return false; } } else { logger["logger"].warn('cannot flush, sb updating in progress'); return false; } } logger["logger"].log('buffer flushed'); // everything flushed ! return true; } /** * Removes first buffered range from provided source buffer that lies within given start and end offsets. * * @param {string} type Type of the source buffer, logging purposes only. * @param {SourceBuffer} sb Target SourceBuffer instance. * @param {number} startOffset * @param {number} endOffset * * @returns {boolean} True when source buffer remove requested. */ ; _proto.removeBufferRange = function removeBufferRange(type, sb, startOffset, endOffset) { try { for (var i = 0; i < sb.buffered.length; i++) { var bufStart = sb.buffered.start(i); var bufEnd = sb.buffered.end(i); var removeStart = Math.max(bufStart, startOffset); var removeEnd = Math.min(bufEnd, endOffset); /* sometimes sourcebuffer.remove() does not flush the exact expected time range. to avoid rounding issues/infinite loop, only flush buffer range of length greater than 500ms. */ if (Math.min(removeEnd, bufEnd) - removeStart > 0.5) { var currentTime = 'null'; if (this.media) { currentTime = this.media.currentTime.toString(); } logger["logger"].log("sb remove " + type + " [" + removeStart + "," + removeEnd + "], of [" + bufStart + "," + bufEnd + "], pos:" + currentTime); sb.remove(removeStart, removeEnd); return true; } } } catch (error) { logger["logger"].warn('removeBufferRange failed', error); } return false; }; return BufferController; }(event_handler); /* harmony default export */ var buffer_controller = (buffer_controller_BufferController); // CONCATENATED MODULE: ./src/controller/cap-level-controller.js function cap_level_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function cap_level_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) cap_level_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) cap_level_controller_defineProperties(Constructor, staticProps); return Constructor; } function cap_level_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * cap stream level to media size dimension controller */ var cap_level_controller_CapLevelController = /*#__PURE__*/function (_EventHandler) { cap_level_controller_inheritsLoose(CapLevelController, _EventHandler); function CapLevelController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].FPS_DROP_LEVEL_CAPPING, events["default"].MEDIA_ATTACHING, events["default"].MANIFEST_PARSED, events["default"].LEVELS_UPDATED, events["default"].BUFFER_CODECS, events["default"].MEDIA_DETACHING) || this; _this.autoLevelCapping = Number.POSITIVE_INFINITY; _this.firstLevel = null; _this.levels = []; _this.media = null; _this.restrictedLevels = []; _this.timer = null; _this.clientRect = null; return _this; } var _proto = CapLevelController.prototype; _proto.destroy = function destroy() { if (this.hls.config.capLevelToPlayerSize) { this.media = null; this.clientRect = null; this.stopCapping(); } }; _proto.onFpsDropLevelCapping = function onFpsDropLevelCapping(data) { // Don't add a restricted level more than once if (CapLevelController.isLevelAllowed(data.droppedLevel, this.restrictedLevels)) { this.restrictedLevels.push(data.droppedLevel); } }; _proto.onMediaAttaching = function onMediaAttaching(data) { this.media = data.media instanceof window.HTMLVideoElement ? data.media : null; }; _proto.onManifestParsed = function onManifestParsed(data) { var hls = this.hls; this.restrictedLevels = []; this.levels = data.levels; this.firstLevel = data.firstLevel; if (hls.config.capLevelToPlayerSize && data.video) { // Start capping immediately if the manifest has signaled video codecs this.startCapping(); } } // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted // to the first level ; _proto.onBufferCodecs = function onBufferCodecs(data) { var hls = this.hls; if (hls.config.capLevelToPlayerSize && data.video) { // If the manifest did not signal a video codec capping has been deferred until we're certain video is present this.startCapping(); } }; _proto.onLevelsUpdated = function onLevelsUpdated(data) { this.levels = data.levels; }; _proto.onMediaDetaching = function onMediaDetaching() { this.stopCapping(); }; _proto.detectPlayerSize = function detectPlayerSize() { if (this.media) { var levelsLength = this.levels ? this.levels.length : 0; if (levelsLength) { var hls = this.hls; hls.autoLevelCapping = this.getMaxLevel(levelsLength - 1); if (hls.autoLevelCapping > this.autoLevelCapping) { // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch // usually happen when the user go to the fullscreen mode. hls.streamController.nextLevelSwitch(); } this.autoLevelCapping = hls.autoLevelCapping; } } } /* * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled) */ ; _proto.getMaxLevel = function getMaxLevel(capLevelIndex) { var _this2 = this; if (!this.levels) { return -1; } var validLevels = this.levels.filter(function (level, index) { return CapLevelController.isLevelAllowed(index, _this2.restrictedLevels) && index <= capLevelIndex; }); this.clientRect = null; return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight); }; _proto.startCapping = function startCapping() { if (this.timer) { // Don't reset capping if started twice; this can happen if the manifest signals a video codec return; } this.autoLevelCapping = Number.POSITIVE_INFINITY; this.hls.firstLevel = this.getMaxLevel(this.firstLevel); clearInterval(this.timer); this.timer = setInterval(this.detectPlayerSize.bind(this), 1000); this.detectPlayerSize(); }; _proto.stopCapping = function stopCapping() { this.restrictedLevels = []; this.firstLevel = null; this.autoLevelCapping = Number.POSITIVE_INFINITY; if (this.timer) { this.timer = clearInterval(this.timer); this.timer = null; } }; _proto.getDimensions = function getDimensions() { if (this.clientRect) { return this.clientRect; } var media = this.media; var boundsRect = { width: 0, height: 0 }; if (media) { var clientRect = media.getBoundingClientRect(); boundsRect.width = clientRect.width; boundsRect.height = clientRect.height; if (!boundsRect.width && !boundsRect.height) { // When the media element has no width or height (equivalent to not being in the DOM), // then use its width and height attributes (media.width, media.height) boundsRect.width = clientRect.right - clientRect.left || media.width || 0; boundsRect.height = clientRect.bottom - clientRect.top || media.height || 0; } } this.clientRect = boundsRect; return boundsRect; }; CapLevelController.isLevelAllowed = function isLevelAllowed(level, restrictedLevels) { if (restrictedLevels === void 0) { restrictedLevels = []; } return restrictedLevels.indexOf(level) === -1; }; CapLevelController.getMaxLevelByMediaSize = function getMaxLevelByMediaSize(levels, width, height) { if (!levels || levels && !levels.length) { return -1; } // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next // to determine whether we've chosen the greatest bandwidth for the media's dimensions var atGreatestBandiwdth = function atGreatestBandiwdth(curLevel, nextLevel) { if (!nextLevel) { return true; } return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height; }; // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to // the max level var maxLevelIndex = levels.length - 1; for (var i = 0; i < levels.length; i += 1) { var level = levels[i]; if ((level.width >= width || level.height >= height) && atGreatestBandiwdth(level, levels[i + 1])) { maxLevelIndex = i; break; } } return maxLevelIndex; }; cap_level_controller_createClass(CapLevelController, [{ key: "mediaWidth", get: function get() { return this.getDimensions().width * CapLevelController.contentScaleFactor; } }, { key: "mediaHeight", get: function get() { return this.getDimensions().height * CapLevelController.contentScaleFactor; } }], [{ key: "contentScaleFactor", get: function get() { var pixelRatio = 1; try { pixelRatio = window.devicePixelRatio; } catch (e) {} return pixelRatio; } }]); return CapLevelController; }(event_handler); /* harmony default export */ var cap_level_controller = (cap_level_controller_CapLevelController); // CONCATENATED MODULE: ./src/controller/fps-controller.js function fps_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * FPS Controller */ var fps_controller_window = window, fps_controller_performance = fps_controller_window.performance; var fps_controller_FPSController = /*#__PURE__*/function (_EventHandler) { fps_controller_inheritsLoose(FPSController, _EventHandler); function FPSController(hls) { return _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHING) || this; } var _proto = FPSController.prototype; _proto.destroy = function destroy() { if (this.timer) { clearInterval(this.timer); } this.isVideoPlaybackQualityAvailable = false; }; _proto.onMediaAttaching = function onMediaAttaching(data) { var config = this.hls.config; if (config.capLevelOnFPSDrop) { var video = this.video = data.media instanceof window.HTMLVideoElement ? data.media : null; if (typeof video.getVideoPlaybackQuality === 'function') { this.isVideoPlaybackQualityAvailable = true; } clearInterval(this.timer); this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod); } }; _proto.checkFPS = function checkFPS(video, decodedFrames, droppedFrames) { var currentTime = fps_controller_performance.now(); if (decodedFrames) { if (this.lastTime) { var currentPeriod = currentTime - this.lastTime, currentDropped = droppedFrames - this.lastDroppedFrames, currentDecoded = decodedFrames - this.lastDecodedFrames, droppedFPS = 1000 * currentDropped / currentPeriod, hls = this.hls; hls.trigger(events["default"].FPS_DROP, { currentDropped: currentDropped, currentDecoded: currentDecoded, totalDroppedFrames: droppedFrames }); if (droppedFPS > 0) { // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod)); if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) { var currentLevel = hls.currentLevel; logger["logger"].warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel); if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) { currentLevel = currentLevel - 1; hls.trigger(events["default"].FPS_DROP_LEVEL_CAPPING, { level: currentLevel, droppedLevel: hls.currentLevel }); hls.autoLevelCapping = currentLevel; hls.streamController.nextLevelSwitch(); } } } } this.lastTime = currentTime; this.lastDroppedFrames = droppedFrames; this.lastDecodedFrames = decodedFrames; } }; _proto.checkFPSInterval = function checkFPSInterval() { var video = this.video; if (video) { if (this.isVideoPlaybackQualityAvailable) { var videoPlaybackQuality = video.getVideoPlaybackQuality(); this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); } else { this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount); } } }; return FPSController; }(event_handler); /* harmony default export */ var fps_controller = (fps_controller_FPSController); // CONCATENATED MODULE: ./src/utils/xhr-loader.js /** * XHR based logger */ var xhr_loader_XhrLoader = /*#__PURE__*/function () { function XhrLoader(config) { if (config && config.xhrSetup) { this.xhrSetup = config.xhrSetup; } } var _proto = XhrLoader.prototype; _proto.destroy = function destroy() { this.abort(); this.loader = null; }; _proto.abort = function abort() { var loader = this.loader; if (loader && loader.readyState !== 4) { this.stats.aborted = true; loader.abort(); } window.clearTimeout(this.requestTimeout); this.requestTimeout = null; window.clearTimeout(this.retryTimeout); this.retryTimeout = null; }; _proto.load = function load(context, config, callbacks) { this.context = context; this.config = config; this.callbacks = callbacks; this.stats = { trequest: window.performance.now(), retry: 0 }; this.retryDelay = config.retryDelay; this.loadInternal(); }; _proto.loadInternal = function loadInternal() { var xhr, context = this.context; xhr = this.loader = new window.XMLHttpRequest(); var stats = this.stats; stats.tfirst = 0; stats.loaded = 0; var xhrSetup = this.xhrSetup; try { if (xhrSetup) { try { xhrSetup(xhr, context.url); } catch (e) { // fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");} // not working, as xhr.setRequestHeader expects xhr.readyState === OPEN xhr.open('GET', context.url, true); xhrSetup(xhr, context.url); } } if (!xhr.readyState) { xhr.open('GET', context.url, true); } } catch (e) { // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS this.callbacks.onError({ code: xhr.status, text: e.message }, context, xhr); return; } if (context.rangeEnd) { xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); } xhr.onreadystatechange = this.readystatechange.bind(this); xhr.onprogress = this.loadprogress.bind(this); xhr.responseType = context.responseType; // setup timeout before we perform request this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout); xhr.send(); }; _proto.readystatechange = function readystatechange(event) { var xhr = event.currentTarget, readyState = xhr.readyState, stats = this.stats, context = this.context, config = this.config; // don't proceed if xhr has been aborted if (stats.aborted) { return; } // >= HEADERS_RECEIVED if (readyState >= 2) { // clear xhr timeout and rearm it if readyState less than 4 window.clearTimeout(this.requestTimeout); if (stats.tfirst === 0) { stats.tfirst = Math.max(window.performance.now(), stats.trequest); } if (readyState === 4) { var status = xhr.status; // http status between 200 to 299 are all successful if (status >= 200 && status < 300) { stats.tload = Math.max(stats.tfirst, window.performance.now()); var data, len; if (context.responseType === 'arraybuffer') { data = xhr.response; len = data.byteLength; } else { data = xhr.responseText; len = data.length; } stats.loaded = stats.total = len; var response = { url: xhr.responseURL, data: data }; this.callbacks.onSuccess(response, stats, context, xhr); } else { // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error if (stats.retry >= config.maxRetry || status >= 400 && status < 499) { logger["logger"].error(status + " while loading " + context.url); this.callbacks.onError({ code: status, text: xhr.statusText }, context, xhr); } else { // retry logger["logger"].warn(status + " while loading " + context.url + ", retrying in " + this.retryDelay + "..."); // aborts and resets internal state this.destroy(); // schedule retry this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay); // set exponential backoff this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay); stats.retry++; } } } else { // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout); } } }; _proto.loadtimeout = function loadtimeout() { logger["logger"].warn("timeout while loading " + this.context.url); this.callbacks.onTimeout(this.stats, this.context, null); }; _proto.loadprogress = function loadprogress(event) { var xhr = event.currentTarget, stats = this.stats; stats.loaded = event.loaded; if (event.lengthComputable) { stats.total = event.total; } var onProgress = this.callbacks.onProgress; if (onProgress) { // third arg is to provide on progress data onProgress(stats, this.context, null, xhr); } }; return XhrLoader; }(); /* harmony default export */ var xhr_loader = (xhr_loader_XhrLoader); // CONCATENATED MODULE: ./src/controller/audio-track-controller.js function audio_track_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function audio_track_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) audio_track_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) audio_track_controller_defineProperties(Constructor, staticProps); return Constructor; } function audio_track_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * @class AudioTrackController * @implements {EventHandler} * * Handles main manifest and audio-track metadata loaded, * owns and exposes the selectable audio-tracks data-models. * * Exposes internal interface to select available audio-tracks. * * Handles errors on loading audio-track playlists. Manages fallback mechanism * with redundants tracks (group-IDs). * * Handles level-loading and group-ID switches for video (fallback on video levels), * and eventually adapts the audio-track group-ID to match. * * @fires AUDIO_TRACK_LOADING * @fires AUDIO_TRACK_SWITCHING * @fires AUDIO_TRACKS_UPDATED * @fires ERROR * */ var audio_track_controller_AudioTrackController = /*#__PURE__*/function (_TaskLoop) { audio_track_controller_inheritsLoose(AudioTrackController, _TaskLoop); function AudioTrackController(hls) { var _this; _this = _TaskLoop.call(this, hls, events["default"].MANIFEST_LOADING, events["default"].MANIFEST_PARSED, events["default"].AUDIO_TRACK_LOADED, events["default"].AUDIO_TRACK_SWITCHED, events["default"].LEVEL_LOADED, events["default"].ERROR) || this; /** * @private * Currently selected index in `tracks` * @member {number} trackId */ _this._trackId = -1; /** * @private * If should select tracks according to default track attribute * @member {boolean} _selectDefaultTrack */ _this._selectDefaultTrack = true; /** * @public * All tracks available * @member {AudioTrack[]} */ _this.tracks = []; /** * @public * List of blacklisted audio track IDs (that have caused failure) * @member {number[]} */ _this.trackIdBlacklist = Object.create(null); /** * @public * The currently running group ID for audio * (we grab this on manifest-parsed and new level-loaded) * @member {string} */ _this.audioGroupId = null; return _this; } /** * Reset audio tracks on new manifest loading. */ var _proto = AudioTrackController.prototype; _proto.onManifestLoading = function onManifestLoading() { this.tracks = []; this._trackId = -1; this._selectDefaultTrack = true; } /** * Store tracks data from manifest parsed data. * * Trigger AUDIO_TRACKS_UPDATED event. * * @param {*} data */ ; _proto.onManifestParsed = function onManifestParsed(data) { var tracks = this.tracks = data.audioTracks || []; this.hls.trigger(events["default"].AUDIO_TRACKS_UPDATED, { audioTracks: tracks }); this._selectAudioGroup(this.hls.nextLoadLevel); } /** * Store track details of loaded track in our data-model. * * Set-up metadata update interval task for live-mode streams. * * @param {*} data */ ; _proto.onAudioTrackLoaded = function onAudioTrackLoaded(data) { if (data.id >= this.tracks.length) { logger["logger"].warn('Invalid audio track id:', data.id); return; } logger["logger"].log("audioTrack " + data.id + " loaded"); this.tracks[data.id].details = data.details; // check if current playlist is a live playlist // and if we have already our reload interval setup if (data.details.live && !this.hasInterval()) { // if live playlist we will have to reload it periodically // set reload period to playlist target duration var updatePeriodMs = data.details.targetduration * 1000; this.setInterval(updatePeriodMs); } if (!data.details.live && this.hasInterval()) { // playlist is not live and timer is scheduled: cancel it this.clearInterval(); } } /** * Update the internal group ID to any audio-track we may have set manually * or because of a failure-handling fallback. * * Quality-levels should update to that group ID in this case. * * @param {*} data */ ; _proto.onAudioTrackSwitched = function onAudioTrackSwitched(data) { var audioGroupId = this.tracks[data.id].groupId; if (audioGroupId && this.audioGroupId !== audioGroupId) { this.audioGroupId = audioGroupId; } } /** * When a level gets loaded, if it has redundant audioGroupIds (in the same ordinality as it's redundant URLs) * we are setting our audio-group ID internally to the one set, if it is different from the group ID currently set. * * If group-ID got update, we re-select the appropriate audio-track with this group-ID matching the currently * selected one (based on NAME property). * * @param {*} data */ ; _proto.onLevelLoaded = function onLevelLoaded(data) { this._selectAudioGroup(data.level); } /** * Handle network errors loading audio track manifests * and also pausing on any netwok errors. * * @param {ErrorEventData} data */ ; _proto.onError = function onError(data) { // Only handle network errors if (data.type !== errors["ErrorTypes"].NETWORK_ERROR) { return; } // If fatal network error, cancel update task if (data.fatal) { this.clearInterval(); } // If not an audio-track loading error don't handle further if (data.details !== errors["ErrorDetails"].AUDIO_TRACK_LOAD_ERROR) { return; } logger["logger"].warn('Network failure on audio-track id:', data.context.id); this._handleLoadError(); } /** * @type {AudioTrack[]} Audio-track list we own */ ; /** * @private * @param {number} newId */ _proto._setAudioTrack = function _setAudioTrack(newId) { // noop on same audio track id as already set if (this._trackId === newId && this.tracks[this._trackId].details) { logger["logger"].debug('Same id as current audio-track passed, and track details available -> no-op'); return; } // check if level idx is valid if (newId < 0 || newId >= this.tracks.length) { logger["logger"].warn('Invalid id passed to audio-track controller'); return; } var audioTrack = this.tracks[newId]; logger["logger"].log("Now switching to audio-track index " + newId); // stopping live reloading timer if any this.clearInterval(); this._trackId = newId; var url = audioTrack.url, type = audioTrack.type, id = audioTrack.id; this.hls.trigger(events["default"].AUDIO_TRACK_SWITCHING, { id: id, type: type, url: url }); this._loadTrackDetailsIfNeeded(audioTrack); } /** * @override */ ; _proto.doTick = function doTick() { this._updateTrack(this._trackId); } /** * @param levelId * @private */ ; _proto._selectAudioGroup = function _selectAudioGroup(levelId) { var levelInfo = this.hls.levels[levelId]; if (!levelInfo || !levelInfo.audioGroupIds) { return; } var audioGroupId = levelInfo.audioGroupIds[levelInfo.urlId]; if (this.audioGroupId !== audioGroupId) { this.audioGroupId = audioGroupId; this._selectInitialAudioTrack(); } } /** * Select initial track * @private */ ; _proto._selectInitialAudioTrack = function _selectInitialAudioTrack() { var _this2 = this; var tracks = this.tracks; if (!tracks.length) { return; } var currentAudioTrack = this.tracks[this._trackId]; var name = null; if (currentAudioTrack) { name = currentAudioTrack.name; } // Pre-select default tracks if there are any if (this._selectDefaultTrack) { var defaultTracks = tracks.filter(function (track) { return track.default; }); if (defaultTracks.length) { tracks = defaultTracks; } else { logger["logger"].warn('No default audio tracks defined'); } } var trackFound = false; var traverseTracks = function traverseTracks() { // Select track with right group ID tracks.forEach(function (track) { if (trackFound) { return; } // We need to match the (pre-)selected group ID // and the NAME of the current track. if ((!_this2.audioGroupId || track.groupId === _this2.audioGroupId) && (!name || name === track.name)) { // If there was a previous track try to stay with the same `NAME`. // It should be unique across tracks of same group, and consistent through redundant track groups. _this2._setAudioTrack(track.id); trackFound = true; } }); }; traverseTracks(); if (!trackFound) { name = null; traverseTracks(); } if (!trackFound) { logger["logger"].error("No track found for running audio group-ID: " + this.audioGroupId); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: errors["ErrorDetails"].AUDIO_TRACK_LOAD_ERROR, fatal: true }); } } /** * @private * @param {AudioTrack} audioTrack * @returns {boolean} */ ; _proto._needsTrackLoading = function _needsTrackLoading(audioTrack) { var details = audioTrack.details, url = audioTrack.url; if (!details || details.live) { // check if we face an audio track embedded in main playlist (audio track without URI attribute) return !!url; } return false; } /** * @private * @param {AudioTrack} audioTrack */ ; _proto._loadTrackDetailsIfNeeded = function _loadTrackDetailsIfNeeded(audioTrack) { if (this._needsTrackLoading(audioTrack)) { var url = audioTrack.url, id = audioTrack.id; // track not retrieved yet, or live playlist we need to (re)load it logger["logger"].log("loading audio-track playlist for id: " + id); this.hls.trigger(events["default"].AUDIO_TRACK_LOADING, { url: url, id: id }); } } /** * @private * @param {number} newId */ ; _proto._updateTrack = function _updateTrack(newId) { // check if level idx is valid if (newId < 0 || newId >= this.tracks.length) { return; } // stopping live reloading timer if any this.clearInterval(); this._trackId = newId; logger["logger"].log("trying to update audio-track " + newId); var audioTrack = this.tracks[newId]; this._loadTrackDetailsIfNeeded(audioTrack); } /** * @private */ ; _proto._handleLoadError = function _handleLoadError() { // First, let's black list current track id this.trackIdBlacklist[this._trackId] = true; // Let's try to fall back on a functional audio-track with the same group ID var previousId = this._trackId; var _this$tracks$previous = this.tracks[previousId], name = _this$tracks$previous.name, language = _this$tracks$previous.language, groupId = _this$tracks$previous.groupId; logger["logger"].warn("Loading failed on audio track id: " + previousId + ", group-id: " + groupId + ", name/language: \"" + name + "\" / \"" + language + "\""); // Find a non-blacklisted track ID with the same NAME // At least a track that is not blacklisted, thus on another group-ID. var newId = previousId; for (var i = 0; i < this.tracks.length; i++) { if (this.trackIdBlacklist[i]) { continue; } var newTrack = this.tracks[i]; if (newTrack.name === name) { newId = i; break; } } if (newId === previousId) { logger["logger"].warn("No fallback audio-track found for name/language: \"" + name + "\" / \"" + language + "\""); return; } logger["logger"].log('Attempting audio-track fallback id:', newId, 'group-id:', this.tracks[newId].groupId); this._setAudioTrack(newId); }; audio_track_controller_createClass(AudioTrackController, [{ key: "audioTracks", get: function get() { return this.tracks; } /** * @type {number} Index into audio-tracks list of currently selected track. */ }, { key: "audioTrack", get: function get() { return this._trackId; } /** * Select current track by index */ , set: function set(newId) { this._setAudioTrack(newId); // If audio track is selected from API then don't choose from the manifest default track this._selectDefaultTrack = false; } }]); return AudioTrackController; }(TaskLoop); /* harmony default export */ var audio_track_controller = (audio_track_controller_AudioTrackController); // CONCATENATED MODULE: ./src/controller/audio-stream-controller.js function audio_stream_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function audio_stream_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) audio_stream_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) audio_stream_controller_defineProperties(Constructor, staticProps); return Constructor; } function audio_stream_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /* * Audio Stream Controller */ var audio_stream_controller_window = window, audio_stream_controller_performance = audio_stream_controller_window.performance; var audio_stream_controller_TICK_INTERVAL = 100; // how often to tick in ms var audio_stream_controller_AudioStreamController = /*#__PURE__*/function (_BaseStreamController) { audio_stream_controller_inheritsLoose(AudioStreamController, _BaseStreamController); function AudioStreamController(hls, fragmentTracker) { var _this; _this = _BaseStreamController.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHING, events["default"].AUDIO_TRACKS_UPDATED, events["default"].AUDIO_TRACK_SWITCHING, events["default"].AUDIO_TRACK_LOADED, events["default"].KEY_LOADED, events["default"].FRAG_LOADED, events["default"].FRAG_PARSING_INIT_SEGMENT, events["default"].FRAG_PARSING_DATA, events["default"].FRAG_PARSED, events["default"].ERROR, events["default"].BUFFER_RESET, events["default"].BUFFER_CREATED, events["default"].BUFFER_APPENDED, events["default"].BUFFER_FLUSHED, events["default"].INIT_PTS_FOUND) || this; _this.fragmentTracker = fragmentTracker; _this.config = hls.config; _this.audioCodecSwap = false; _this._state = State.STOPPED; _this.initPTS = []; _this.waitingFragment = null; _this.videoTrackCC = null; _this.waitingVideoCC = null; return _this; } // Signal that video PTS was found var _proto = AudioStreamController.prototype; _proto.onInitPtsFound = function onInitPtsFound(data) { var demuxerId = data.id, cc = data.frag.cc, initPTS = data.initPTS; if (demuxerId === 'main') { // Always update the new INIT PTS // Can change due level switch this.initPTS[cc] = initPTS; this.videoTrackCC = cc; logger["logger"].log("InitPTS for cc: " + cc + " found from main: " + initPTS); // If we are waiting we need to demux/remux the waiting frag // With the new initPTS if (this.state === State.WAITING_INIT_PTS) { this.tick(); } } }; _proto.startLoad = function startLoad(startPosition) { if (this.tracks) { var lastCurrentTime = this.lastCurrentTime; this.stopLoad(); this.setInterval(audio_stream_controller_TICK_INTERVAL); this.fragLoadError = 0; if (lastCurrentTime > 0 && startPosition === -1) { logger["logger"].log("audio:override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3)); this.state = State.IDLE; } else { this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition; this.state = State.STARTING; } this.nextLoadPosition = this.startPosition = this.lastCurrentTime; this.tick(); } else { this.startPosition = startPosition; this.state = State.STOPPED; } }; _proto.doTick = function doTick() { var pos, track, trackDetails, hls = this.hls, config = hls.config; // logger.log('audioStream:' + this.state); switch (this.state) { case State.ERROR: // don't do anything in error state to avoid breaking further ... case State.PAUSED: // don't do anything in paused state either ... case State.BUFFER_FLUSHING: break; case State.STARTING: this.state = State.WAITING_TRACK; this.loadedmetadata = false; break; case State.IDLE: var tracks = this.tracks; // audio tracks not received => exit loop if (!tracks) { break; } // if video not attached AND // start fragment already requested OR start frag prefetch disable // exit loop // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop if (!this.media && (this.startFragRequested || !config.startFragPrefetch)) { break; } // determine next candidate fragment to be loaded, based on current position and // end of buffer position // if we have not yet loaded any fragment, start loading from start position if (this.loadedmetadata) { pos = this.media.currentTime; } else { pos = this.nextLoadPosition; if (pos === undefined) { break; } } var media = this.mediaBuffer ? this.mediaBuffer : this.media; var videoBuffer = this.videoBuffer ? this.videoBuffer : this.media; var maxBufferHole = pos < config.maxBufferHole ? Math.max(MAX_START_GAP_JUMP, config.maxBufferHole) : config.maxBufferHole; var bufferInfo = BufferHelper.bufferInfo(media, pos, maxBufferHole); var mainBufferInfo = BufferHelper.bufferInfo(videoBuffer, pos, maxBufferHole); var bufferLen = bufferInfo.len; var bufferEnd = bufferInfo.end; var fragPrevious = this.fragPrevious; // ensure we buffer at least config.maxBufferLength (default 30s) or config.maxMaxBufferLength (default: 600s) // whichever is smaller. // once we reach that threshold, don't buffer more than video (mainBufferInfo.len) var maxConfigBuffer = Math.min(config.maxBufferLength, config.maxMaxBufferLength); var maxBufLen = Math.max(maxConfigBuffer, mainBufferInfo.len); var audioSwitch = this.audioSwitch; var trackId = this.trackId; // if buffer length is less than maxBufLen try to load a new fragment if ((bufferLen < maxBufLen || audioSwitch) && trackId < tracks.length) { trackDetails = tracks[trackId].details; // if track info not retrieved yet, switch state and wait for track retrieval if (typeof trackDetails === 'undefined') { this.state = State.WAITING_TRACK; break; } if (!audioSwitch && this._streamEnded(bufferInfo, trackDetails)) { this.hls.trigger(events["default"].BUFFER_EOS, { type: 'audio' }); this.state = State.ENDED; return; } // find fragment index, contiguous with end of buffer position var fragments = trackDetails.fragments, fragLen = fragments.length, start = fragments[0].start, end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, frag; // When switching audio track, reload audio as close as possible to currentTime if (audioSwitch) { if (trackDetails.live && !trackDetails.PTSKnown) { logger["logger"].log('switching audiotrack, live stream, unknown PTS,load first fragment'); bufferEnd = 0; } else { bufferEnd = pos; // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime if (trackDetails.PTSKnown && pos < start) { // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start if (bufferInfo.end > start || bufferInfo.nextStart) { logger["logger"].log('alt audio track ahead of main track, seek to start of alt audio track'); this.media.currentTime = start + 0.05; } else { return; } } } } if (trackDetails.initSegment && !trackDetails.initSegment.data) { frag = trackDetails.initSegment; } // eslint-disable-line brace-style // if bufferEnd before start of playlist, load first fragment else if (bufferEnd <= start) { frag = fragments[0]; if (this.videoTrackCC !== null && frag.cc !== this.videoTrackCC) { // Ensure we find a fragment which matches the continuity of the video track frag = findFragWithCC(fragments, this.videoTrackCC); } if (trackDetails.live && frag.loadIdx && frag.loadIdx === this.fragLoadIdx) { // we just loaded this first fragment, and we are still lagging behind the start of the live playlist // let's force seek to start var nextBuffered = bufferInfo.nextStart ? bufferInfo.nextStart : start; logger["logger"].log("no alt audio available @currentTime:" + this.media.currentTime + ", seeking @" + (nextBuffered + 0.05)); this.media.currentTime = nextBuffered + 0.05; return; } } else { var foundFrag; var maxFragLookUpTolerance = config.maxFragLookUpTolerance; var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined; if (bufferEnd < end) { if (bufferEnd > end - maxFragLookUpTolerance) { maxFragLookUpTolerance = 0; } // Prefer the next fragment if it's within tolerance if (fragNext && !fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext)) { foundFrag = fragNext; } else { foundFrag = binary_search.search(fragments, function (frag) { return fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, frag); }); } } else { // reach end of playlist foundFrag = fragments[fragLen - 1]; } if (foundFrag) { frag = foundFrag; start = foundFrag.start; // logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) { if (frag.sn < trackDetails.endSN) { frag = fragments[frag.sn + 1 - trackDetails.startSN]; if (this.fragmentTracker.getState(frag) !== FragmentState.OK) { logger["logger"].log("SN just loaded, load next one: " + frag.sn); } } else { frag = null; } } } } if (frag) { // logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3)); if (frag.encrypted) { logger["logger"].log("Loading key for " + frag.sn + " of [" + trackDetails.startSN + " ," + trackDetails.endSN + "],track " + trackId); this.state = State.KEY_LOADING; hls.trigger(events["default"].KEY_LOADING, { frag: frag }); } else { // only load if fragment is not loaded or if in audio switch // we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch this.fragCurrent = frag; if (audioSwitch || this.fragmentTracker.getState(frag) === FragmentState.NOT_LOADED) { logger["logger"].log("Loading " + frag.sn + ", cc: " + frag.cc + " of [" + trackDetails.startSN + " ," + trackDetails.endSN + "],track " + trackId + ", " + (this.loadedmetadata ? 'currentTime' : 'nextLoadPosition') + ": " + pos + ", bufferEnd: " + bufferEnd.toFixed(3)); if (frag.sn !== 'initSegment') { this.startFragRequested = true; } if (Object(number["isFiniteNumber"])(frag.sn)) { this.nextLoadPosition = frag.start + frag.duration; } hls.trigger(events["default"].FRAG_LOADING, { frag: frag }); this.state = State.FRAG_LOADING; } } } } break; case State.WAITING_TRACK: track = this.tracks[this.trackId]; // check if playlist is already loaded if (track && track.details) { this.state = State.IDLE; } break; case State.FRAG_LOADING_WAITING_RETRY: var now = audio_stream_controller_performance.now(); var retryDate = this.retryDate; media = this.media; var isSeeking = media && media.seeking; // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading if (!retryDate || now >= retryDate || isSeeking) { logger["logger"].log('audioStreamController: retryDate reached, switch back to IDLE state'); this.state = State.IDLE; } break; case State.WAITING_INIT_PTS: // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS var waitingFrag = this.waitingFragment; if (waitingFrag) { var waitingFragCC = waitingFrag.frag.cc; if (this.initPTS[waitingFragCC] !== undefined) { this.waitingFragment = null; this.state = State.FRAG_LOADING; this.onFragLoaded(waitingFrag); } else if (this.videoTrackCC !== this.waitingVideoCC) { // Drop waiting fragment if videoTrackCC has changed since waitingFragment was set and initPTS was not found logger["logger"].log("Waiting fragment cc (" + waitingFragCC + ") cancelled because video is at cc " + this.videoTrackCC); this.clearWaitingFragment(); } else { // Drop waiting fragment if an earlier fragment is needed var _bufferInfo = BufferHelper.bufferInfo(this.mediaBuffer, this.media.currentTime, config.maxBufferHole); var waitingFragmentAtPosition = fragmentWithinToleranceTest(_bufferInfo.end, config.maxFragLookUpTolerance, waitingFrag.frag); if (waitingFragmentAtPosition < 0) { logger["logger"].log("Waiting fragment cc (" + waitingFragCC + ") @ " + waitingFrag.frag.start + " cancelled because another fragment at " + _bufferInfo.end + " is needed"); this.clearWaitingFragment(); } } } else { this.state = State.IDLE; } break; case State.STOPPED: case State.FRAG_LOADING: case State.PARSING: case State.PARSED: case State.ENDED: break; default: break; } }; _proto.clearWaitingFragment = function clearWaitingFragment() { var waitingFrag = this.waitingFragment; if (waitingFrag) { this.fragmentTracker.removeFragment(waitingFrag.frag); this.waitingFragment = null; this.waitingVideoCC = null; this.state = State.IDLE; } }; _proto.onMediaAttached = function onMediaAttached(data) { var media = this.media = this.mediaBuffer = data.media; this.onvseeking = this.onMediaSeeking.bind(this); this.onvended = this.onMediaEnded.bind(this); media.addEventListener('seeking', this.onvseeking); media.addEventListener('ended', this.onvended); var config = this.config; if (this.tracks && config.autoStartLoad) { this.startLoad(config.startPosition); } }; _proto.onMediaDetaching = function onMediaDetaching() { var media = this.media; if (media && media.ended) { logger["logger"].log('MSE detaching and video ended, reset startPosition'); this.startPosition = this.lastCurrentTime = 0; } // remove video listeners if (media) { media.removeEventListener('seeking', this.onvseeking); media.removeEventListener('ended', this.onvended); this.onvseeking = this.onvseeked = this.onvended = null; } this.media = this.mediaBuffer = this.videoBuffer = null; this.loadedmetadata = false; this.fragmentTracker.removeAllFragments(); this.stopLoad(); }; _proto.onAudioTracksUpdated = function onAudioTracksUpdated(data) { logger["logger"].log('audio tracks updated'); this.tracks = data.audioTracks; }; _proto.onAudioTrackSwitching = function onAudioTrackSwitching(data) { // if any URL found on new audio track, it is an alternate audio track var altAudio = !!data.url; this.trackId = data.id; this.fragCurrent = null; this.clearWaitingFragment(); this.state = State.PAUSED; // destroy useless demuxer when switching audio to main if (!altAudio) { if (this.demuxer) { this.demuxer.destroy(); this.demuxer = null; } } else { // switching to audio track, start timer if not already started this.setInterval(audio_stream_controller_TICK_INTERVAL); } // should we switch tracks ? if (altAudio) { this.audioSwitch = true; // main audio track are handled by stream-controller, just do something if switching to alt audio track this.state = State.IDLE; } this.tick(); }; _proto.onAudioTrackLoaded = function onAudioTrackLoaded(data) { var newDetails = data.details, trackId = data.id, track = this.tracks[trackId], curDetails = track.details, duration = newDetails.totalduration, sliding = 0; logger["logger"].log("track " + trackId + " loaded [" + newDetails.startSN + "," + newDetails.endSN + "],duration:" + duration); if (newDetails.live || curDetails && curDetails.live) { if (curDetails && newDetails.fragments.length > 0) { // we already have details for that level, merge them mergeDetails(curDetails, newDetails); sliding = newDetails.fragments[0].start; // TODO // this.liveSyncPosition = this.computeLivePosition(sliding, curDetails); if (newDetails.PTSKnown) { logger["logger"].log("live audio playlist sliding:" + sliding.toFixed(3)); } else { logger["logger"].log('live audio playlist - outdated PTS, unknown sliding'); } } else { newDetails.PTSKnown = false; logger["logger"].log('live audio playlist - first load, unknown sliding'); } } else { newDetails.PTSKnown = false; } track.details = newDetails; // compute start position if (!this.startFragRequested) { // compute start position if set to -1. use it straight away if value is defined if (this.startPosition === -1) { // first, check if start time offset has been set in playlist, if yes, use this value var startTimeOffset = newDetails.startTimeOffset; if (Object(number["isFiniteNumber"])(startTimeOffset)) { logger["logger"].log("start time offset found in playlist, adjust startPosition to " + startTimeOffset); this.startPosition = startTimeOffset; } else { if (newDetails.live) { this.startPosition = this.computeLivePosition(sliding, newDetails); logger["logger"].log("compute startPosition for audio-track to " + this.startPosition); } else { this.startPosition = 0; } } } this.nextLoadPosition = this.startPosition; } // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment if (this.state === State.WAITING_TRACK) { this.state = State.IDLE; } // trigger handler right now this.tick(); }; _proto.onKeyLoaded = function onKeyLoaded() { if (this.state === State.KEY_LOADING) { this.state = State.IDLE; this.tick(); } }; _proto.onFragLoaded = function onFragLoaded(data) { var fragCurrent = this.fragCurrent, fragLoaded = data.frag; if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'audio' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) { var track = this.tracks[this.trackId], details = track.details, duration = details.totalduration, trackId = fragCurrent.level, sn = fragCurrent.sn, cc = fragCurrent.cc, audioCodec = this.config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2', stats = this.stats = data.stats; if (sn === 'initSegment') { this.state = State.IDLE; stats.tparsed = stats.tbuffered = audio_stream_controller_performance.now(); details.initSegment.data = data.payload; this.hls.trigger(events["default"].FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'audio' }); this.tick(); } else { this.state = State.PARSING; // transmux the MPEG-TS data to ISO-BMFF segments this.appended = false; if (!this.demuxer) { this.demuxer = new demux_demuxer(this.hls, 'audio'); } // Check if we have video initPTS // If not we need to wait for it var initPTS = this.initPTS[cc]; var initSegmentData = details.initSegment ? details.initSegment.data : []; if (initPTS !== undefined) { this.pendingBuffering = true; logger["logger"].log("Demuxing " + sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId); // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) var accurateTimeOffset = false; // details.PTSKnown || !details.live; this.demuxer.push(data.payload, initSegmentData, audioCodec, null, fragCurrent, duration, accurateTimeOffset, initPTS); } else { logger["logger"].log("Unknown video PTS for cc " + cc + ", waiting for video PTS before demuxing audio frag " + sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId); this.waitingFragment = data; this.waitingVideoCC = this.videoTrackCC; this.state = State.WAITING_INIT_PTS; } } } this.fragLoadError = 0; }; _proto.onFragParsingInitSegment = function onFragParsingInitSegment(data) { var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) { var tracks = data.tracks, track; // delete any video track found on audio demuxer if (tracks.video) { delete tracks.video; } // include levelCodec in audio and video tracks track = tracks.audio; if (track) { track.levelCodec = track.codec; track.id = data.id; this.hls.trigger(events["default"].BUFFER_CODECS, tracks); logger["logger"].log("audio track:audio,container:" + track.container + ",codecs[level/parsed]=[" + track.levelCodec + "/" + track.codec + "]"); var initSegment = track.initSegment; if (initSegment) { var appendObj = { type: 'audio', data: initSegment, parent: 'audio', content: 'initSegment' }; if (this.audioSwitch) { this.pendingData = [appendObj]; } else { this.appended = true; // arm pending Buffering flag before appending a segment this.pendingBuffering = true; this.hls.trigger(events["default"].BUFFER_APPENDING, appendObj); } } // trigger handler right now this.tick(); } } }; _proto.onFragParsingData = function onFragParsingData(data) { var _this2 = this; var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'audio' && data.type === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) { var trackId = this.trackId, track = this.tracks[trackId], hls = this.hls; if (!Object(number["isFiniteNumber"])(data.endPTS)) { data.endPTS = data.startPTS + fragCurrent.duration; data.endDTS = data.startDTS + fragCurrent.duration; } fragCurrent.addElementaryStream(ElementaryStreamTypes.AUDIO); logger["logger"].log("parsed " + data.type + ",PTS:[" + data.startPTS.toFixed(3) + "," + data.endPTS.toFixed(3) + "],DTS:[" + data.startDTS.toFixed(3) + "/" + data.endDTS.toFixed(3) + "],nb:" + data.nb); updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS); var media = this.media; var appendOnBufferFlush = false; // Only flush audio from old audio tracks when PTS is known on new audio track if (this.audioSwitch) { if (media && media.readyState) { var currentTime = media.currentTime; logger["logger"].log('switching audio track : currentTime:' + currentTime); if (currentTime >= data.startPTS) { logger["logger"].log('switching audio track : flushing all audio'); this.state = State.BUFFER_FLUSHING; hls.trigger(events["default"].BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); appendOnBufferFlush = true; // Lets announce that the initial audio track switch flush occur this.audioSwitch = false; hls.trigger(events["default"].AUDIO_TRACK_SWITCHED, { id: trackId }); } } else { // Lets announce that the initial audio track switch flush occur this.audioSwitch = false; hls.trigger(events["default"].AUDIO_TRACK_SWITCHED, { id: trackId }); } } var pendingData = this.pendingData; if (!pendingData) { logger["logger"].warn('Apparently attempt to enqueue media payload without codec initialization data upfront'); hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].MEDIA_ERROR, details: null, fatal: true }); return; } if (!this.audioSwitch) { [data.data1, data.data2].forEach(function (buffer) { if (buffer && buffer.length) { pendingData.push({ type: data.type, data: buffer, parent: 'audio', content: 'data' }); } }); if (!appendOnBufferFlush && pendingData.length) { pendingData.forEach(function (appendObj) { // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending) // in that case it is useless to append following segments if (_this2.state === State.PARSING) { // arm pending Buffering flag before appending a segment _this2.pendingBuffering = true; _this2.hls.trigger(events["default"].BUFFER_APPENDING, appendObj); } }); this.pendingData = []; this.appended = true; } } // trigger handler right now this.tick(); } }; _proto.onFragParsed = function onFragParsed(data) { var fragCurrent = this.fragCurrent; var fragNew = data.frag; if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) { this.stats.tparsed = audio_stream_controller_performance.now(); this.state = State.PARSED; this._checkAppendedParsed(); } }; _proto.onBufferReset = function onBufferReset() { // reset reference to sourcebuffers this.mediaBuffer = this.videoBuffer = null; this.loadedmetadata = false; }; _proto.onBufferCreated = function onBufferCreated(data) { var audioTrack = data.tracks.audio; if (audioTrack) { this.mediaBuffer = audioTrack.buffer; this.loadedmetadata = true; } if (data.tracks.video) { this.videoBuffer = data.tracks.video.buffer; } }; _proto.onBufferAppended = function onBufferAppended(data) { if (data.parent === 'audio') { var state = this.state; if (state === State.PARSING || state === State.PARSED) { // check if all buffers have been appended this.pendingBuffering = data.pending > 0; this._checkAppendedParsed(); } } }; _proto._checkAppendedParsed = function _checkAppendedParsed() { // trigger handler right now if (this.state === State.PARSED && (!this.appended || !this.pendingBuffering)) { var frag = this.fragCurrent, stats = this.stats, hls = this.hls; if (frag) { this.fragPrevious = frag; stats.tbuffered = audio_stream_controller_performance.now(); hls.trigger(events["default"].FRAG_BUFFERED, { stats: stats, frag: frag, id: 'audio' }); var media = this.mediaBuffer ? this.mediaBuffer : this.media; if (media) { logger["logger"].log("audio buffered : " + time_ranges.toString(media.buffered)); } if (this.audioSwitch && this.appended) { this.audioSwitch = false; hls.trigger(events["default"].AUDIO_TRACK_SWITCHED, { id: this.trackId }); } this.state = State.IDLE; } this.tick(); } }; _proto.onError = function onError(data) { var frag = data.frag; // don't handle frag error not related to audio fragment if (frag && frag.type !== 'audio') { return; } switch (data.details) { case errors["ErrorDetails"].FRAG_LOAD_ERROR: case errors["ErrorDetails"].FRAG_LOAD_TIMEOUT: var _frag = data.frag; // don't handle frag error not related to audio fragment if (_frag && _frag.type !== 'audio') { break; } if (!data.fatal) { var loadError = this.fragLoadError; if (loadError) { loadError++; } else { loadError = 1; } var config = this.config; if (loadError <= config.fragLoadingMaxRetry) { this.fragLoadError = loadError; // exponential backoff capped to config.fragLoadingMaxRetryTimeout var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout); logger["logger"].warn("AudioStreamController: frag loading failed, retry in " + delay + " ms"); this.retryDate = audio_stream_controller_performance.now() + delay; // retry loading state this.state = State.FRAG_LOADING_WAITING_RETRY; } else { logger["logger"].error("AudioStreamController: " + data.details + " reaches max retry, redispatch as fatal ..."); // switch error to fatal data.fatal = true; this.state = State.ERROR; } } break; case errors["ErrorDetails"].AUDIO_TRACK_LOAD_ERROR: case errors["ErrorDetails"].AUDIO_TRACK_LOAD_TIMEOUT: case errors["ErrorDetails"].KEY_LOAD_ERROR: case errors["ErrorDetails"].KEY_LOAD_TIMEOUT: // when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received if (this.state !== State.ERROR) { // if fatal error, stop processing, otherwise move to IDLE to retry loading this.state = data.fatal ? State.ERROR : State.IDLE; logger["logger"].warn("AudioStreamController: " + data.details + " while loading frag, now switching to " + this.state + " state ..."); } break; case errors["ErrorDetails"].BUFFER_FULL_ERROR: // if in appending state if (data.parent === 'audio' && (this.state === State.PARSING || this.state === State.PARSED)) { var media = this.mediaBuffer, currentTime = this.media.currentTime, mediaBuffered = media && BufferHelper.isBuffered(media, currentTime) && BufferHelper.isBuffered(media, currentTime + 0.5); // reduce max buf len if current position is buffered if (mediaBuffered) { var _config = this.config; if (_config.maxMaxBufferLength >= _config.maxBufferLength) { // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... _config.maxMaxBufferLength /= 2; logger["logger"].warn("AudioStreamController: reduce max buffer length to " + _config.maxMaxBufferLength + "s"); } this.state = State.IDLE; } else { // current position is not buffered, but browser is still complaining about buffer full error // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 // in that case flush the whole audio buffer to recover logger["logger"].warn('AudioStreamController: buffer full error also media.currentTime is not buffered, flush audio buffer'); this.fragCurrent = null; // flush everything this.state = State.BUFFER_FLUSHING; this.hls.trigger(events["default"].BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); } } break; default: break; } }; _proto.onBufferFlushed = function onBufferFlushed() { var _this3 = this; var pendingData = this.pendingData; if (pendingData && pendingData.length) { logger["logger"].log('AudioStreamController: appending pending audio data after buffer flushed'); pendingData.forEach(function (appendObj) { _this3.hls.trigger(events["default"].BUFFER_APPENDING, appendObj); }); this.appended = true; this.pendingData = []; this.state = State.PARSED; } else { // move to IDLE once flush complete. this should trigger new fragment loading this.state = State.IDLE; // reset reference to frag this.fragPrevious = null; this.tick(); } }; audio_stream_controller_createClass(AudioStreamController, [{ key: "state", set: function set(nextState) { if (this.state !== nextState) { var previousState = this.state; this._state = nextState; logger["logger"].log("audio stream:" + previousState + "->" + nextState); } }, get: function get() { return this._state; } }]); return AudioStreamController; }(base_stream_controller_BaseStreamController); /* harmony default export */ var audio_stream_controller = (audio_stream_controller_AudioStreamController); // CONCATENATED MODULE: ./src/utils/vttcue.js /** * Copyright 2013 vtt.js Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* harmony default export */ var vttcue = ((function () { if (typeof window !== 'undefined' && window.VTTCue) { return window.VTTCue; } var autoKeyword = 'auto'; var directionSetting = { '': true, lr: true, rl: true }; var alignSetting = { start: true, middle: true, end: true, left: true, right: true }; function findDirectionSetting(value) { if (typeof value !== 'string') { return false; } var dir = directionSetting[value.toLowerCase()]; return dir ? value.toLowerCase() : false; } function findAlignSetting(value) { if (typeof value !== 'string') { return false; } var align = alignSetting[value.toLowerCase()]; return align ? value.toLowerCase() : false; } function extend(obj) { var i = 1; for (; i < arguments.length; i++) { var cobj = arguments[i]; for (var p in cobj) { obj[p] = cobj[p]; } } return obj; } function VTTCue(startTime, endTime, text) { var cue = this; var baseObj = {}; baseObj.enumerable = true; /** * Shim implementation specific properties. These properties are not in * the spec. */ // Lets us know when the VTTCue's data has changed in such a way that we need // to recompute its display state. This lets us compute its display state // lazily. cue.hasBeenReset = false; /** * VTTCue and TextTrackCue properties * http://dev.w3.org/html5/webvtt/#vttcue-interface */ var _id = ''; var _pauseOnExit = false; var _startTime = startTime; var _endTime = endTime; var _text = text; var _region = null; var _vertical = ''; var _snapToLines = true; var _line = 'auto'; var _lineAlign = 'start'; var _position = 50; var _positionAlign = 'middle'; var _size = 50; var _align = 'middle'; Object.defineProperty(cue, 'id', extend({}, baseObj, { get: function get() { return _id; }, set: function set(value) { _id = '' + value; } })); Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, { get: function get() { return _pauseOnExit; }, set: function set(value) { _pauseOnExit = !!value; } })); Object.defineProperty(cue, 'startTime', extend({}, baseObj, { get: function get() { return _startTime; }, set: function set(value) { if (typeof value !== 'number') { throw new TypeError('Start time must be set to a number.'); } _startTime = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'endTime', extend({}, baseObj, { get: function get() { return _endTime; }, set: function set(value) { if (typeof value !== 'number') { throw new TypeError('End time must be set to a number.'); } _endTime = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'text', extend({}, baseObj, { get: function get() { return _text; }, set: function set(value) { _text = '' + value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'region', extend({}, baseObj, { get: function get() { return _region; }, set: function set(value) { _region = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'vertical', extend({}, baseObj, { get: function get() { return _vertical; }, set: function set(value) { var setting = findDirectionSetting(value); // Have to check for false because the setting an be an empty string. if (setting === false) { throw new SyntaxError('An invalid or illegal string was specified.'); } _vertical = setting; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, { get: function get() { return _snapToLines; }, set: function set(value) { _snapToLines = !!value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'line', extend({}, baseObj, { get: function get() { return _line; }, set: function set(value) { if (typeof value !== 'number' && value !== autoKeyword) { throw new SyntaxError('An invalid number or illegal string was specified.'); } _line = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, { get: function get() { return _lineAlign; }, set: function set(value) { var setting = findAlignSetting(value); if (!setting) { throw new SyntaxError('An invalid or illegal string was specified.'); } _lineAlign = setting; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'position', extend({}, baseObj, { get: function get() { return _position; }, set: function set(value) { if (value < 0 || value > 100) { throw new Error('Position must be between 0 and 100.'); } _position = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, { get: function get() { return _positionAlign; }, set: function set(value) { var setting = findAlignSetting(value); if (!setting) { throw new SyntaxError('An invalid or illegal string was specified.'); } _positionAlign = setting; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'size', extend({}, baseObj, { get: function get() { return _size; }, set: function set(value) { if (value < 0 || value > 100) { throw new Error('Size must be between 0 and 100.'); } _size = value; this.hasBeenReset = true; } })); Object.defineProperty(cue, 'align', extend({}, baseObj, { get: function get() { return _align; }, set: function set(value) { var setting = findAlignSetting(value); if (!setting) { throw new SyntaxError('An invalid or illegal string was specified.'); } _align = setting; this.hasBeenReset = true; } })); /** * Other <track> spec defined properties */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state cue.displayState = void 0; } /** * VTTCue methods */ VTTCue.prototype.getCueAsHTML = function () { // Assume WebVTT.convertCueToDOMTree is on the global. var WebVTT = window.WebVTT; return WebVTT.convertCueToDOMTree(window, this.text); }; return VTTCue; })()); // CONCATENATED MODULE: ./src/utils/vttparser.js /* * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js#L1716 */ var StringDecoder = function StringDecoder() { return { decode: function decode(data) { if (!data) { return ''; } if (typeof data !== 'string') { throw new Error('Error - expected string data.'); } return decodeURIComponent(encodeURIComponent(data)); } }; }; function VTTParser() { this.window = window; this.state = 'INITIAL'; this.buffer = ''; this.decoder = new StringDecoder(); this.regionList = []; } // Try to parse input as a time stamp. function parseTimeStamp(input) { function computeSeconds(h, m, s, f) { return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000; } var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/); if (!m) { return null; } if (m[3]) { // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] return computeSeconds(m[1], m[2], m[3].replace(':', ''), m[4]); } else if (m[1] > 59) { // Timestamp takes the form of [hours]:[minutes].[milliseconds] // First position is hours as it's over 59. return computeSeconds(m[1], m[2], 0, m[4]); } else { // Timestamp takes the form of [minutes]:[seconds].[milliseconds] return computeSeconds(0, m[1], m[2], m[4]); } } // A settings object holds key/value pairs and will ignore anything but the first // assignment to a specific key. function Settings() { this.values = Object.create(null); } Settings.prototype = { // Only accept the first assignment to any key. set: function set(k, v) { if (!this.get(k) && v !== '') { this.values[k] = v; } }, // Return the value for a key, or a default value. // If 'defaultKey' is passed then 'dflt' is assumed to be an object with // a number of possible default values as properties where 'defaultKey' is // the key of the property that will be chosen; otherwise it's assumed to be // a single value. get: function get(k, dflt, defaultKey) { if (defaultKey) { return this.has(k) ? this.values[k] : dflt[defaultKey]; } return this.has(k) ? this.values[k] : dflt; }, // Check whether we have a value for a key. has: function has(k) { return k in this.values; }, // Accept a setting if its one of the given alternatives. alt: function alt(k, v, a) { for (var n = 0; n < a.length; ++n) { if (v === a[n]) { this.set(k, v); break; } } }, // Accept a setting if its a valid (signed) integer. integer: function integer(k, v) { if (/^-?\d+$/.test(v)) { // integer this.set(k, parseInt(v, 10)); } }, // Accept a setting if its a valid percentage. percent: function percent(k, v) { var m; if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) { v = parseFloat(v); if (v >= 0 && v <= 100) { this.set(k, v); return true; } } return false; } }; // Helper function to parse input into groups separated by 'groupDelim', and // interprete each group as a key/value pair separated by 'keyValueDelim'. function parseOptions(input, callback, keyValueDelim, groupDelim) { var groups = groupDelim ? input.split(groupDelim) : [input]; for (var i in groups) { if (typeof groups[i] !== 'string') { continue; } var kv = groups[i].split(keyValueDelim); if (kv.length !== 2) { continue; } var k = kv[0]; var v = kv[1]; callback(k, v); } } var defaults = new vttcue(0, 0, 0); // 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244 // Safari doesn't yet support this change, but FF and Chrome do. var center = defaults.align === 'middle' ? 'middle' : 'center'; function parseCue(input, cue, regionList) { // Remember the original input if we need to throw an error. var oInput = input; // 4.1 WebVTT timestamp function consumeTimeStamp() { var ts = parseTimeStamp(input); if (ts === null) { throw new Error('Malformed timestamp: ' + oInput); } // Remove time stamp from input. input = input.replace(/^[^\sa-zA-Z-]+/, ''); return ts; } // 4.4.2 WebVTT cue settings function consumeCueSettings(input, cue) { var settings = new Settings(); parseOptions(input, function (k, v) { switch (k) { case 'region': // Find the last region we parsed with the same region id. for (var i = regionList.length - 1; i >= 0; i--) { if (regionList[i].id === v) { settings.set(k, regionList[i].region); break; } } break; case 'vertical': settings.alt(k, v, ['rl', 'lr']); break; case 'line': var vals = v.split(','), vals0 = vals[0]; settings.integer(k, vals0); if (settings.percent(k, vals0)) { settings.set('snapToLines', false); } settings.alt(k, vals0, ['auto']); if (vals.length === 2) { settings.alt('lineAlign', vals[1], ['start', center, 'end']); } break; case 'position': vals = v.split(','); settings.percent(k, vals[0]); if (vals.length === 2) { settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']); } break; case 'size': settings.percent(k, v); break; case 'align': settings.alt(k, v, ['start', center, 'end', 'left', 'right']); break; } }, /:/, /\s/); // Apply default values for any missing fields. cue.region = settings.get('region', null); cue.vertical = settings.get('vertical', ''); var line = settings.get('line', 'auto'); if (line === 'auto' && defaults.line === -1) { // set numeric line number for Safari line = -1; } cue.line = line; cue.lineAlign = settings.get('lineAlign', 'start'); cue.snapToLines = settings.get('snapToLines', true); cue.size = settings.get('size', 100); cue.align = settings.get('align', center); var position = settings.get('position', 'auto'); if (position === 'auto' && defaults.position === 50) { // set numeric position for Safari position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50; } cue.position = position; } function skipWhitespace() { input = input.replace(/^\s+/, ''); } // 4.1 WebVTT cue timings. skipWhitespace(); cue.startTime = consumeTimeStamp(); // (1) collect cue start time skipWhitespace(); if (input.substr(0, 3) !== '-->') { // (3) next characters must match '-->' throw new Error('Malformed time stamp (time stamps must be separated by \'-->\'): ' + oInput); } input = input.substr(3); skipWhitespace(); cue.endTime = consumeTimeStamp(); // (5) collect cue end time // 4.1 WebVTT cue settings list. skipWhitespace(); consumeCueSettings(input, cue); } function fixLineBreaks(input) { return input.replace(/<br(?: \/)?>/gi, '\n'); } VTTParser.prototype = { parse: function parse(data) { var self = this; // If there is no data then we won't decode it, but will just try to parse // whatever is in buffer already. This may occur in circumstances, for // example when flush() is called. if (data) { // Try to decode the data that we received. self.buffer += self.decoder.decode(data, { stream: true }); } function collectNextLine() { var buffer = self.buffer; var pos = 0; buffer = fixLineBreaks(buffer); while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { ++pos; } var line = buffer.substr(0, pos); // Advance the buffer early in case we fail below. if (buffer[pos] === '\r') { ++pos; } if (buffer[pos] === '\n') { ++pos; } self.buffer = buffer.substr(pos); return line; } // 3.2 WebVTT metadata header syntax function parseHeader(input) { parseOptions(input, function (k, v) { switch (k) { case 'Region': // 3.3 WebVTT region metadata header syntax // console.log('parse region', v); // parseRegion(v); break; } }, /:/); } // 5.1 WebVTT file parsing. try { var line; if (self.state === 'INITIAL') { // We can't start parsing until we have the first line. if (!/\r\n|\n/.test(self.buffer)) { return this; } line = collectNextLine(); // strip of UTF-8 BOM if any // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 var m = line.match(/^()?WEBVTT([ \t].*)?$/); if (!m || !m[0]) { throw new Error('Malformed WebVTT signature.'); } self.state = 'HEADER'; } var alreadyCollectedLine = false; while (self.buffer) { // We can't parse a line until we have the full line. if (!/\r\n|\n/.test(self.buffer)) { return this; } if (!alreadyCollectedLine) { line = collectNextLine(); } else { alreadyCollectedLine = false; } switch (self.state) { case 'HEADER': // 13-18 - Allow a header (metadata) under the WEBVTT line. if (/:/.test(line)) { parseHeader(line); } else if (!line) { // An empty line terminates the header and starts the body (cues). self.state = 'ID'; } continue; case 'NOTE': // Ignore NOTE blocks. if (!line) { self.state = 'ID'; } continue; case 'ID': // Check for the start of NOTE blocks. if (/^NOTE($|[ \t])/.test(line)) { self.state = 'NOTE'; break; } // 19-29 - Allow any number of line terminators, then initialize new cue values. if (!line) { continue; } self.cue = new vttcue(0, 0, ''); self.state = 'CUE'; // 30-39 - Check if self line contains an optional identifier or timing data. if (line.indexOf('-->') === -1) { self.cue.id = line; continue; } // Process line as start of a cue. /* falls through */ case 'CUE': // 40 - Collect cue timings and settings. try { parseCue(line, self.cue, self.regionList); } catch (e) { // In case of an error ignore rest of the cue. self.cue = null; self.state = 'BADCUE'; continue; } self.state = 'CUETEXT'; continue; case 'CUETEXT': var hasSubstring = line.indexOf('-->') !== -1; // 34 - If we have an empty line then report the cue. // 35 - If we have the special substring '-->' then report the cue, // but do not collect the line as we need to process the current // one as a new cue. if (!line || hasSubstring && (alreadyCollectedLine = true)) { // We are done parsing self cue. if (self.oncue) { self.oncue(self.cue); } self.cue = null; self.state = 'ID'; continue; } if (self.cue.text) { self.cue.text += '\n'; } self.cue.text += line; continue; case 'BADCUE': // BADCUE // 54-62 - Collect and discard the remaining cue. if (!line) { self.state = 'ID'; } continue; } } } catch (e) { // If we are currently parsing a cue, report what we have. if (self.state === 'CUETEXT' && self.cue && self.oncue) { self.oncue(self.cue); } self.cue = null; // Enter BADWEBVTT state if header was not parsed correctly otherwise // another exception occurred so enter BADCUE state. self.state = self.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE'; } return this; }, flush: function flush() { var self = this; try { // Finish decoding the stream. self.buffer += self.decoder.decode(); // Synthesize the end of the current cue or region. if (self.cue || self.state === 'HEADER') { self.buffer += '\n\n'; self.parse(); } // If we've flushed, parsed, and we're still on the INITIAL state then // that means we don't have enough of the stream to parse the first // line. if (self.state === 'INITIAL') { throw new Error('Malformed WebVTT signature.'); } } catch (e) { throw e; } if (self.onflush) { self.onflush(); } return this; } }; /* harmony default export */ var vttparser = (VTTParser); // CONCATENATED MODULE: ./src/utils/cues.ts function newCue(track, startTime, endTime, captionScreen) { var result = []; var row; // the type data states this is VTTCue, but it can potentially be a TextTrackCue on old browsers var cue; var indenting; var indent; var text; var VTTCue = window.VTTCue || TextTrackCue; for (var r = 0; r < captionScreen.rows.length; r++) { row = captionScreen.rows[r]; indenting = true; indent = 0; text = ''; if (!row.isEmpty()) { for (var c = 0; c < row.chars.length; c++) { if (row.chars[c].uchar.match(/\s/) && indenting) { indent++; } else { text += row.chars[c].uchar; indenting = false; } } // To be used for cleaning-up orphaned roll-up captions row.cueStartTime = startTime; // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE if (startTime === endTime) { endTime += 0.0001; } cue = new VTTCue(startTime, endTime, fixLineBreaks(text.trim())); if (indent >= 16) { indent--; } else { indent++; } // VTTCue.line get's flakey when using controls, so let's now include line 13&14 // also, drop line 1 since it's to close to the top if (navigator.userAgent.match(/Firefox\//)) { cue.line = r + 1; } else { cue.line = r > 7 ? r - 2 : r + 1; } cue.align = 'left'; // Clamp the position between 0 and 100 - if out of these bounds, Firefox throws an exception and captions break cue.position = Math.max(0, Math.min(100, 100 * (indent / 32))); result.push(cue); if (track) { track.addCue(cue); } } } return result; } // CONCATENATED MODULE: ./src/utils/cea-608-parser.ts /** * * This code was ported from the dash.js project at: * https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js * https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2 * * The original copyright appears below: * * The copyright in this software is being made available under the BSD License, * included below. This software may be subject to other third party and contributor * rights, including patent rights, and no such rights are granted under this license. * * Copyright (c) 2015-2016, DASH Industry Forum. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * 2. Neither the name of Dash Industry Forum nor the names of its * contributors may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes */ var specialCea608CharsCodes = { 0x2a: 0xe1, // lowercase a, acute accent 0x5c: 0xe9, // lowercase e, acute accent 0x5e: 0xed, // lowercase i, acute accent 0x5f: 0xf3, // lowercase o, acute accent 0x60: 0xfa, // lowercase u, acute accent 0x7b: 0xe7, // lowercase c with cedilla 0x7c: 0xf7, // division symbol 0x7d: 0xd1, // uppercase N tilde 0x7e: 0xf1, // lowercase n tilde 0x7f: 0x2588, // Full block // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES 0x80: 0xae, // Registered symbol (R) 0x81: 0xb0, // degree sign 0x82: 0xbd, // 1/2 symbol 0x83: 0xbf, // Inverted (open) question mark 0x84: 0x2122, // Trademark symbol (TM) 0x85: 0xa2, // Cents symbol 0x86: 0xa3, // Pounds sterling 0x87: 0x266a, // Music 8'th note 0x88: 0xe0, // lowercase a, grave accent 0x89: 0x20, // transparent space (regular) 0x8a: 0xe8, // lowercase e, grave accent 0x8b: 0xe2, // lowercase a, circumflex accent 0x8c: 0xea, // lowercase e, circumflex accent 0x8d: 0xee, // lowercase i, circumflex accent 0x8e: 0xf4, // lowercase o, circumflex accent 0x8f: 0xfb, // lowercase u, circumflex accent // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F 0x90: 0xc1, // capital letter A with acute 0x91: 0xc9, // capital letter E with acute 0x92: 0xd3, // capital letter O with acute 0x93: 0xda, // capital letter U with acute 0x94: 0xdc, // capital letter U with diaresis 0x95: 0xfc, // lowercase letter U with diaeresis 0x96: 0x2018, // opening single quote 0x97: 0xa1, // inverted exclamation mark 0x98: 0x2a, // asterisk 0x99: 0x2019, // closing single quote 0x9a: 0x2501, // box drawings heavy horizontal 0x9b: 0xa9, // copyright sign 0x9c: 0x2120, // Service mark 0x9d: 0x2022, // (round) bullet 0x9e: 0x201c, // Left double quotation mark 0x9f: 0x201d, // Right double quotation mark 0xa0: 0xc0, // uppercase A, grave accent 0xa1: 0xc2, // uppercase A, circumflex 0xa2: 0xc7, // uppercase C with cedilla 0xa3: 0xc8, // uppercase E, grave accent 0xa4: 0xca, // uppercase E, circumflex 0xa5: 0xcb, // capital letter E with diaresis 0xa6: 0xeb, // lowercase letter e with diaresis 0xa7: 0xce, // uppercase I, circumflex 0xa8: 0xcf, // uppercase I, with diaresis 0xa9: 0xef, // lowercase i, with diaresis 0xaa: 0xd4, // uppercase O, circumflex 0xab: 0xd9, // uppercase U, grave accent 0xac: 0xf9, // lowercase u, grave accent 0xad: 0xdb, // uppercase U, circumflex 0xae: 0xab, // left-pointing double angle quotation mark 0xaf: 0xbb, // right-pointing double angle quotation mark // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F 0xb0: 0xc3, // Uppercase A, tilde 0xb1: 0xe3, // Lowercase a, tilde 0xb2: 0xcd, // Uppercase I, acute accent 0xb3: 0xcc, // Uppercase I, grave accent 0xb4: 0xec, // Lowercase i, grave accent 0xb5: 0xd2, // Uppercase O, grave accent 0xb6: 0xf2, // Lowercase o, grave accent 0xb7: 0xd5, // Uppercase O, tilde 0xb8: 0xf5, // Lowercase o, tilde 0xb9: 0x7b, // Open curly brace 0xba: 0x7d, // Closing curly brace 0xbb: 0x5c, // Backslash 0xbc: 0x5e, // Caret 0xbd: 0x5f, // Underscore 0xbe: 0x7c, // Pipe (vertical line) 0xbf: 0x223c, // Tilde operator 0xc0: 0xc4, // Uppercase A, umlaut 0xc1: 0xe4, // Lowercase A, umlaut 0xc2: 0xd6, // Uppercase O, umlaut 0xc3: 0xf6, // Lowercase o, umlaut 0xc4: 0xdf, // Esszett (sharp S) 0xc5: 0xa5, // Yen symbol 0xc6: 0xa4, // Generic currency sign 0xc7: 0x2503, // Box drawings heavy vertical 0xc8: 0xc5, // Uppercase A, ring 0xc9: 0xe5, // Lowercase A, ring 0xca: 0xd8, // Uppercase O, stroke 0xcb: 0xf8, // Lowercase o, strok 0xcc: 0x250f, // Box drawings heavy down and right 0xcd: 0x2513, // Box drawings heavy down and left 0xce: 0x2517, // Box drawings heavy up and right 0xcf: 0x251b // Box drawings heavy up and left }; /** * Utils */ var getCharForByte = function getCharForByte(_byte) { var charCode = _byte; if (specialCea608CharsCodes.hasOwnProperty(_byte)) { charCode = specialCea608CharsCodes[_byte]; } return String.fromCharCode(charCode); }; var NR_ROWS = 15; var NR_COLS = 100; // Tables to look up row from PAC data var rowsLowCh1 = { 0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14 }; var rowsHighCh1 = { 0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15 }; var rowsLowCh2 = { 0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14 }; var rowsHighCh2 = { 0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15 }; var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent']; var VerboseLevel; (function (VerboseLevel) { VerboseLevel[VerboseLevel["ERROR"] = 0] = "ERROR"; VerboseLevel[VerboseLevel["TEXT"] = 1] = "TEXT"; VerboseLevel[VerboseLevel["WARNING"] = 2] = "WARNING"; VerboseLevel[VerboseLevel["INFO"] = 2] = "INFO"; VerboseLevel[VerboseLevel["DEBUG"] = 3] = "DEBUG"; VerboseLevel[VerboseLevel["DATA"] = 3] = "DATA"; })(VerboseLevel || (VerboseLevel = {})); var cea_608_parser_CaptionsLogger = /*#__PURE__*/function () { function CaptionsLogger() { this.time = null; this.verboseLevel = VerboseLevel.ERROR; } var _proto = CaptionsLogger.prototype; _proto.log = function log(severity, msg) { if (this.verboseLevel >= severity) { logger["logger"].log(this.time + " [" + severity + "] " + msg); } }; return CaptionsLogger; }(); var numArrayToHexArray = function numArrayToHexArray(numArray) { var hexArray = []; for (var j = 0; j < numArray.length; j++) { hexArray.push(numArray[j].toString(16)); } return hexArray; }; var PenState = /*#__PURE__*/function () { function PenState(foreground, underline, italics, background, flash) { this.foreground = void 0; this.underline = void 0; this.italics = void 0; this.background = void 0; this.flash = void 0; this.foreground = foreground || 'white'; this.underline = underline || false; this.italics = italics || false; this.background = background || 'black'; this.flash = flash || false; } var _proto2 = PenState.prototype; _proto2.reset = function reset() { this.foreground = 'white'; this.underline = false; this.italics = false; this.background = 'black'; this.flash = false; }; _proto2.setStyles = function setStyles(styles) { var attribs = ['foreground', 'underline', 'italics', 'background', 'flash']; for (var i = 0; i < attribs.length; i++) { var style = attribs[i]; if (styles.hasOwnProperty(style)) { this[style] = styles[style]; } } }; _proto2.isDefault = function isDefault() { return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash; }; _proto2.equals = function equals(other) { return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash; }; _proto2.copy = function copy(newPenState) { this.foreground = newPenState.foreground; this.underline = newPenState.underline; this.italics = newPenState.italics; this.background = newPenState.background; this.flash = newPenState.flash; }; _proto2.toString = function toString() { return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash; }; return PenState; }(); /** * Unicode character with styling and background. * @constructor */ var StyledUnicodeChar = /*#__PURE__*/function () { function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) { this.uchar = void 0; this.penState = void 0; this.uchar = uchar || ' '; // unicode character this.penState = new PenState(foreground, underline, italics, background, flash); } var _proto3 = StyledUnicodeChar.prototype; _proto3.reset = function reset() { this.uchar = ' '; this.penState.reset(); }; _proto3.setChar = function setChar(uchar, newPenState) { this.uchar = uchar; this.penState.copy(newPenState); }; _proto3.setPenState = function setPenState(newPenState) { this.penState.copy(newPenState); }; _proto3.equals = function equals(other) { return this.uchar === other.uchar && this.penState.equals(other.penState); }; _proto3.copy = function copy(newChar) { this.uchar = newChar.uchar; this.penState.copy(newChar.penState); }; _proto3.isEmpty = function isEmpty() { return this.uchar === ' ' && this.penState.isDefault(); }; return StyledUnicodeChar; }(); /** * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar. * @constructor */ var Row = /*#__PURE__*/function () { function Row(logger) { this.chars = void 0; this.pos = void 0; this.currPenState = void 0; this.cueStartTime = void 0; this.logger = void 0; this.chars = []; for (var i = 0; i < NR_COLS; i++) { this.chars.push(new StyledUnicodeChar()); } this.logger = logger; this.pos = 0; this.currPenState = new PenState(); } var _proto4 = Row.prototype; _proto4.equals = function equals(other) { var equal = true; for (var i = 0; i < NR_COLS; i++) { if (!this.chars[i].equals(other.chars[i])) { equal = false; break; } } return equal; }; _proto4.copy = function copy(other) { for (var i = 0; i < NR_COLS; i++) { this.chars[i].copy(other.chars[i]); } }; _proto4.isEmpty = function isEmpty() { var empty = true; for (var i = 0; i < NR_COLS; i++) { if (!this.chars[i].isEmpty()) { empty = false; break; } } return empty; } /** * Set the cursor to a valid column. */ ; _proto4.setCursor = function setCursor(absPos) { if (this.pos !== absPos) { this.pos = absPos; } if (this.pos < 0) { this.logger.log(VerboseLevel.DEBUG, 'Negative cursor position ' + this.pos); this.pos = 0; } else if (this.pos > NR_COLS) { this.logger.log(VerboseLevel.DEBUG, 'Too large cursor position ' + this.pos); this.pos = NR_COLS; } } /** * Move the cursor relative to current position. */ ; _proto4.moveCursor = function moveCursor(relPos) { var newPos = this.pos + relPos; if (relPos > 1) { for (var i = this.pos + 1; i < newPos + 1; i++) { this.chars[i].setPenState(this.currPenState); } } this.setCursor(newPos); } /** * Backspace, move one step back and clear character. */ ; _proto4.backSpace = function backSpace() { this.moveCursor(-1); this.chars[this.pos].setChar(' ', this.currPenState); }; _proto4.insertChar = function insertChar(_byte2) { if (_byte2 >= 0x90) { // Extended char this.backSpace(); } var _char = getCharForByte(_byte2); if (this.pos >= NR_COLS) { this.logger.log(VerboseLevel.ERROR, 'Cannot insert ' + _byte2.toString(16) + ' (' + _char + ') at position ' + this.pos + '. Skipping it!'); return; } this.chars[this.pos].setChar(_char, this.currPenState); this.moveCursor(1); }; _proto4.clearFromPos = function clearFromPos(startPos) { var i; for (i = startPos; i < NR_COLS; i++) { this.chars[i].reset(); } }; _proto4.clear = function clear() { this.clearFromPos(0); this.pos = 0; this.currPenState.reset(); }; _proto4.clearToEndOfRow = function clearToEndOfRow() { this.clearFromPos(this.pos); }; _proto4.getTextString = function getTextString() { var chars = []; var empty = true; for (var i = 0; i < NR_COLS; i++) { var _char2 = this.chars[i].uchar; if (_char2 !== ' ') { empty = false; } chars.push(_char2); } if (empty) { return ''; } else { return chars.join(''); } }; _proto4.setPenStyles = function setPenStyles(styles) { this.currPenState.setStyles(styles); var currChar = this.chars[this.pos]; currChar.setPenState(this.currPenState); }; return Row; }(); /** * Keep a CEA-608 screen of 32x15 styled characters * @constructor */ var CaptionScreen = /*#__PURE__*/function () { function CaptionScreen(logger) { this.rows = void 0; this.currRow = void 0; this.nrRollUpRows = void 0; this.lastOutputScreen = void 0; this.logger = void 0; this.rows = []; for (var i = 0; i < NR_ROWS; i++) { this.rows.push(new Row(logger)); } // Note that we use zero-based numbering (0-14) this.logger = logger; this.currRow = NR_ROWS - 1; this.nrRollUpRows = null; this.lastOutputScreen = null; this.reset(); } var _proto5 = CaptionScreen.prototype; _proto5.reset = function reset() { for (var i = 0; i < NR_ROWS; i++) { this.rows[i].clear(); } this.currRow = NR_ROWS - 1; }; _proto5.equals = function equals(other) { var equal = true; for (var i = 0; i < NR_ROWS; i++) { if (!this.rows[i].equals(other.rows[i])) { equal = false; break; } } return equal; }; _proto5.copy = function copy(other) { for (var i = 0; i < NR_ROWS; i++) { this.rows[i].copy(other.rows[i]); } }; _proto5.isEmpty = function isEmpty() { var empty = true; for (var i = 0; i < NR_ROWS; i++) { if (!this.rows[i].isEmpty()) { empty = false; break; } } return empty; }; _proto5.backSpace = function backSpace() { var row = this.rows[this.currRow]; row.backSpace(); }; _proto5.clearToEndOfRow = function clearToEndOfRow() { var row = this.rows[this.currRow]; row.clearToEndOfRow(); } /** * Insert a character (without styling) in the current row. */ ; _proto5.insertChar = function insertChar(_char3) { var row = this.rows[this.currRow]; row.insertChar(_char3); }; _proto5.setPen = function setPen(styles) { var row = this.rows[this.currRow]; row.setPenStyles(styles); }; _proto5.moveCursor = function moveCursor(relPos) { var row = this.rows[this.currRow]; row.moveCursor(relPos); }; _proto5.setCursor = function setCursor(absPos) { this.logger.log(VerboseLevel.INFO, 'setCursor: ' + absPos); var row = this.rows[this.currRow]; row.setCursor(absPos); }; _proto5.setPAC = function setPAC(pacData) { this.logger.log(VerboseLevel.INFO, 'pacData = ' + JSON.stringify(pacData)); var newRow = pacData.row - 1; if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) { newRow = this.nrRollUpRows - 1; } // Make sure this only affects Roll-up Captions by checking this.nrRollUpRows if (this.nrRollUpRows && this.currRow !== newRow) { // clear all rows first for (var i = 0; i < NR_ROWS; i++) { this.rows[i].clear(); } // Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location // topRowIndex - the start of rows to copy (inclusive index) var topRowIndex = this.currRow + 1 - this.nrRollUpRows; // We only copy if the last position was already shown. // We use the cueStartTime value to check this. var lastOutputScreen = this.lastOutputScreen; if (lastOutputScreen) { var prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime; var time = this.logger.time; if (prevLineTime && time !== null && prevLineTime < time) { for (var _i = 0; _i < this.nrRollUpRows; _i++) { this.rows[newRow - this.nrRollUpRows + _i + 1].copy(lastOutputScreen.rows[topRowIndex + _i]); } } } } this.currRow = newRow; var row = this.rows[this.currRow]; if (pacData.indent !== null) { var indent = pacData.indent; var prevPos = Math.max(indent - 1, 0); row.setCursor(pacData.indent); pacData.color = row.chars[prevPos].penState.foreground; } var styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false }; this.setPen(styles); } /** * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility). */ ; _proto5.setBkgData = function setBkgData(bkgData) { this.logger.log(VerboseLevel.INFO, 'bkgData = ' + JSON.stringify(bkgData)); this.backSpace(); this.setPen(bkgData); this.insertChar(0x20); // Space }; _proto5.setRollUpRows = function setRollUpRows(nrRows) { this.nrRollUpRows = nrRows; }; _proto5.rollUp = function rollUp() { if (this.nrRollUpRows === null) { this.logger.log(VerboseLevel.DEBUG, 'roll_up but nrRollUpRows not set yet'); return; // Not properly setup } this.logger.log(VerboseLevel.TEXT, this.getDisplayText()); var topRowIndex = this.currRow + 1 - this.nrRollUpRows; var topRow = this.rows.splice(topRowIndex, 1)[0]; topRow.clear(); this.rows.splice(this.currRow, 0, topRow); this.logger.log(VerboseLevel.INFO, 'Rolling up'); // this.logger.log(VerboseLevel.TEXT, this.get_display_text()) } /** * Get all non-empty rows with as unicode text. */ ; _proto5.getDisplayText = function getDisplayText(asOneRow) { asOneRow = asOneRow || false; var displayText = []; var text = ''; var rowNr = -1; for (var i = 0; i < NR_ROWS; i++) { var rowText = this.rows[i].getTextString(); if (rowText) { rowNr = i + 1; if (asOneRow) { displayText.push('Row ' + rowNr + ': \'' + rowText + '\''); } else { displayText.push(rowText.trim()); } } } if (displayText.length > 0) { if (asOneRow) { text = '[' + displayText.join(' | ') + ']'; } else { text = displayText.join('\n'); } } return text; }; _proto5.getTextAndFormat = function getTextAndFormat() { return this.rows; }; return CaptionScreen; }(); // var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT']; var Cea608Channel = /*#__PURE__*/function () { function Cea608Channel(channelNumber, outputFilter, logger) { this.chNr = void 0; this.outputFilter = void 0; this.mode = void 0; this.verbose = void 0; this.displayedMemory = void 0; this.nonDisplayedMemory = void 0; this.lastOutputScreen = void 0; this.currRollUpRow = void 0; this.writeScreen = void 0; this.cueStartTime = void 0; this.logger = void 0; this.chNr = channelNumber; this.outputFilter = outputFilter; this.mode = null; this.verbose = 0; this.displayedMemory = new CaptionScreen(logger); this.nonDisplayedMemory = new CaptionScreen(logger); this.lastOutputScreen = new CaptionScreen(logger); this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; this.writeScreen = this.displayedMemory; this.mode = null; this.cueStartTime = null; // Keeps track of where a cue started. this.logger = logger; } var _proto6 = Cea608Channel.prototype; _proto6.reset = function reset() { this.mode = null; this.displayedMemory.reset(); this.nonDisplayedMemory.reset(); this.lastOutputScreen.reset(); this.outputFilter.reset(); this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; this.writeScreen = this.displayedMemory; this.mode = null; this.cueStartTime = null; }; _proto6.getHandler = function getHandler() { return this.outputFilter; }; _proto6.setHandler = function setHandler(newHandler) { this.outputFilter = newHandler; }; _proto6.setPAC = function setPAC(pacData) { this.writeScreen.setPAC(pacData); }; _proto6.setBkgData = function setBkgData(bkgData) { this.writeScreen.setBkgData(bkgData); }; _proto6.setMode = function setMode(newMode) { if (newMode === this.mode) { return; } this.mode = newMode; this.logger.log(VerboseLevel.INFO, 'MODE=' + newMode); if (this.mode === 'MODE_POP-ON') { this.writeScreen = this.nonDisplayedMemory; } else { this.writeScreen = this.displayedMemory; this.writeScreen.reset(); } if (this.mode !== 'MODE_ROLL-UP') { this.displayedMemory.nrRollUpRows = null; this.nonDisplayedMemory.nrRollUpRows = null; } this.mode = newMode; }; _proto6.insertChars = function insertChars(chars) { for (var i = 0; i < chars.length; i++) { this.writeScreen.insertChar(chars[i]); } var screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP'; this.logger.log(VerboseLevel.INFO, screen + ': ' + this.writeScreen.getDisplayText(true)); if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') { this.logger.log(VerboseLevel.TEXT, 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)); this.outputDataUpdate(); } }; _proto6.ccRCL = function ccRCL() { // Resume Caption Loading (switch mode to Pop On) this.logger.log(VerboseLevel.INFO, 'RCL - Resume Caption Loading'); this.setMode('MODE_POP-ON'); }; _proto6.ccBS = function ccBS() { // BackSpace this.logger.log(VerboseLevel.INFO, 'BS - BackSpace'); if (this.mode === 'MODE_TEXT') { return; } this.writeScreen.backSpace(); if (this.writeScreen === this.displayedMemory) { this.outputDataUpdate(); } }; _proto6.ccAOF = function ccAOF() {// Reserved (formerly Alarm Off) }; _proto6.ccAON = function ccAON() {// Reserved (formerly Alarm On) }; _proto6.ccDER = function ccDER() { // Delete to End of Row this.logger.log(VerboseLevel.INFO, 'DER- Delete to End of Row'); this.writeScreen.clearToEndOfRow(); this.outputDataUpdate(); }; _proto6.ccRU = function ccRU(nrRows) { // Roll-Up Captions-2,3,or 4 Rows this.logger.log(VerboseLevel.INFO, 'RU(' + nrRows + ') - Roll Up'); this.writeScreen = this.displayedMemory; this.setMode('MODE_ROLL-UP'); this.writeScreen.setRollUpRows(nrRows); }; _proto6.ccFON = function ccFON() { // Flash On this.logger.log(VerboseLevel.INFO, 'FON - Flash On'); this.writeScreen.setPen({ flash: true }); }; _proto6.ccRDC = function ccRDC() { // Resume Direct Captioning (switch mode to PaintOn) this.logger.log(VerboseLevel.INFO, 'RDC - Resume Direct Captioning'); this.setMode('MODE_PAINT-ON'); }; _proto6.ccTR = function ccTR() { // Text Restart in text mode (not supported, however) this.logger.log(VerboseLevel.INFO, 'TR'); this.setMode('MODE_TEXT'); }; _proto6.ccRTD = function ccRTD() { // Resume Text Display in Text mode (not supported, however) this.logger.log(VerboseLevel.INFO, 'RTD'); this.setMode('MODE_TEXT'); }; _proto6.ccEDM = function ccEDM() { // Erase Displayed Memory this.logger.log(VerboseLevel.INFO, 'EDM - Erase Displayed Memory'); this.displayedMemory.reset(); this.outputDataUpdate(true); }; _proto6.ccCR = function ccCR() { // Carriage Return this.logger.log(VerboseLevel.INFO, 'CR - Carriage Return'); this.writeScreen.rollUp(); this.outputDataUpdate(true); }; _proto6.ccENM = function ccENM() { // Erase Non-Displayed Memory this.logger.log(VerboseLevel.INFO, 'ENM - Erase Non-displayed Memory'); this.nonDisplayedMemory.reset(); }; _proto6.ccEOC = function ccEOC() { // End of Caption (Flip Memories) this.logger.log(VerboseLevel.INFO, 'EOC - End Of Caption'); if (this.mode === 'MODE_POP-ON') { var tmp = this.displayedMemory; this.displayedMemory = this.nonDisplayedMemory; this.nonDisplayedMemory = tmp; this.writeScreen = this.nonDisplayedMemory; this.logger.log(VerboseLevel.TEXT, 'DISP: ' + this.displayedMemory.getDisplayText()); } this.outputDataUpdate(true); }; _proto6.ccTO = function ccTO(nrCols) { // Tab Offset 1,2, or 3 columns this.logger.log(VerboseLevel.INFO, 'TO(' + nrCols + ') - Tab Offset'); this.writeScreen.moveCursor(nrCols); }; _proto6.ccMIDROW = function ccMIDROW(secondByte) { // Parse MIDROW command var styles = { flash: false }; styles.underline = secondByte % 2 === 1; styles.italics = secondByte >= 0x2e; if (!styles.italics) { var colorIndex = Math.floor(secondByte / 2) - 0x10; var colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta']; styles.foreground = colors[colorIndex]; } else { styles.foreground = 'white'; } this.logger.log(VerboseLevel.INFO, 'MIDROW: ' + JSON.stringify(styles)); this.writeScreen.setPen(styles); }; _proto6.outputDataUpdate = function outputDataUpdate(dispatch) { if (dispatch === void 0) { dispatch = false; } var time = this.logger.time; if (time === null) { return; } if (this.outputFilter) { if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) { // Start of a new cue this.cueStartTime = time; } else { if (!this.displayedMemory.equals(this.lastOutputScreen)) { this.outputFilter.newCue(this.cueStartTime, time, this.lastOutputScreen); if (dispatch && this.outputFilter.dispatchCue) { this.outputFilter.dispatchCue(); } this.cueStartTime = this.displayedMemory.isEmpty() ? null : time; } } this.lastOutputScreen.copy(this.displayedMemory); } }; _proto6.cueSplitAtTime = function cueSplitAtTime(t) { if (this.outputFilter) { if (!this.displayedMemory.isEmpty()) { if (this.outputFilter.newCue) { this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory); } this.cueStartTime = t; } } }; return Cea608Channel; }(); var Cea608Parser = /*#__PURE__*/function () { function Cea608Parser(field, out1, out2) { this.channels = void 0; this.currentChannel = 0; this.cmdHistory = void 0; this.logger = void 0; var logger = new cea_608_parser_CaptionsLogger(); this.channels = [null, new Cea608Channel(field, out1, logger), new Cea608Channel(field + 1, out2, logger)]; this.cmdHistory = createCmdHistory(); this.logger = logger; } var _proto7 = Cea608Parser.prototype; _proto7.getHandler = function getHandler(channel) { return this.channels[channel].getHandler(); }; _proto7.setHandler = function setHandler(channel, newHandler) { this.channels[channel].setHandler(newHandler); } /** * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs. */ ; _proto7.addData = function addData(time, byteList) { var cmdFound; var a; var b; var charsFound = false; this.logger.time = time; for (var i = 0; i < byteList.length; i += 2) { a = byteList[i] & 0x7f; b = byteList[i + 1] & 0x7f; if (a === 0 && b === 0) { continue; } else { this.logger.log(VerboseLevel.DATA, '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')'); } cmdFound = this.parseCmd(a, b); if (!cmdFound) { cmdFound = this.parseMidrow(a, b); } if (!cmdFound) { cmdFound = this.parsePAC(a, b); } if (!cmdFound) { cmdFound = this.parseBackgroundAttributes(a, b); } if (!cmdFound) { charsFound = this.parseChars(a, b); if (charsFound) { var currChNr = this.currentChannel; if (currChNr && currChNr > 0) { var channel = this.channels[currChNr]; channel.insertChars(charsFound); } else { this.logger.log(VerboseLevel.WARNING, 'No channel found yet. TEXT-MODE?'); } } } if (!cmdFound && !charsFound) { this.logger.log(VerboseLevel.WARNING, 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]])); } } } /** * Parse Command. * @returns {Boolean} Tells if a command was found */ ; _proto7.parseCmd = function parseCmd(a, b) { var cmdHistory = this.cmdHistory; var cond1 = (a === 0x14 || a === 0x1C || a === 0x15 || a === 0x1D) && b >= 0x20 && b <= 0x2F; var cond2 = (a === 0x17 || a === 0x1F) && b >= 0x21 && b <= 0x23; if (!(cond1 || cond2)) { return false; } if (hasCmdRepeated(a, b, cmdHistory)) { setLastCmd(null, null, cmdHistory); this.logger.log(VerboseLevel.DEBUG, 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'); return true; } var chNr = a === 0x14 || a === 0x15 || a === 0x17 ? 1 : 2; var channel = this.channels[chNr]; if (a === 0x14 || a === 0x15 || a === 0x1C || a === 0x1D) { if (b === 0x20) { channel.ccRCL(); } else if (b === 0x21) { channel.ccBS(); } else if (b === 0x22) { channel.ccAOF(); } else if (b === 0x23) { channel.ccAON(); } else if (b === 0x24) { channel.ccDER(); } else if (b === 0x25) { channel.ccRU(2); } else if (b === 0x26) { channel.ccRU(3); } else if (b === 0x27) { channel.ccRU(4); } else if (b === 0x28) { channel.ccFON(); } else if (b === 0x29) { channel.ccRDC(); } else if (b === 0x2A) { channel.ccTR(); } else if (b === 0x2B) { channel.ccRTD(); } else if (b === 0x2C) { channel.ccEDM(); } else if (b === 0x2D) { channel.ccCR(); } else if (b === 0x2E) { channel.ccENM(); } else if (b === 0x2F) { channel.ccEOC(); } } else { // a == 0x17 || a == 0x1F channel.ccTO(b - 0x20); } setLastCmd(a, b, cmdHistory); this.currentChannel = chNr; return true; } /** * Parse midrow styling command * @returns {Boolean} */ ; _proto7.parseMidrow = function parseMidrow(a, b) { var chNr = 0; if ((a === 0x11 || a === 0x19) && b >= 0x20 && b <= 0x2f) { if (a === 0x11) { chNr = 1; } else { chNr = 2; } if (chNr !== this.currentChannel) { this.logger.log(VerboseLevel.ERROR, 'Mismatch channel in midrow parsing'); return false; } var channel = this.channels[chNr]; if (!channel) { return false; } channel.ccMIDROW(b); this.logger.log(VerboseLevel.DEBUG, 'MIDROW (' + numArrayToHexArray([a, b]) + ')'); return true; } return false; } /** * Parse Preable Access Codes (Table 53). * @returns {Boolean} Tells if PAC found */ ; _proto7.parsePAC = function parsePAC(a, b) { var row; var cmdHistory = this.cmdHistory; var case1 = (a >= 0x11 && a <= 0x17 || a >= 0x19 && a <= 0x1F) && b >= 0x40 && b <= 0x7F; var case2 = (a === 0x10 || a === 0x18) && b >= 0x40 && b <= 0x5F; if (!(case1 || case2)) { return false; } if (hasCmdRepeated(a, b, cmdHistory)) { setLastCmd(null, null, cmdHistory); return true; // Repeated commands are dropped (once) } var chNr = a <= 0x17 ? 1 : 2; if (b >= 0x40 && b <= 0x5F) { row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a]; } else { // 0x60 <= b <= 0x7F row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a]; } var channel = this.channels[chNr]; if (!channel) { return false; } channel.setPAC(this.interpretPAC(row, b)); setLastCmd(a, b, cmdHistory); this.currentChannel = chNr; return true; } /** * Interpret the second byte of the pac, and return the information. * @returns {Object} pacData with style parameters. */ ; _proto7.interpretPAC = function interpretPAC(row, _byte3) { var pacIndex = _byte3; var pacData = { color: null, italics: false, indent: null, underline: false, row: row }; if (_byte3 > 0x5F) { pacIndex = _byte3 - 0x60; } else { pacIndex = _byte3 - 0x40; } pacData.underline = (pacIndex & 1) === 1; if (pacIndex <= 0xd) { pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)]; } else if (pacIndex <= 0xf) { pacData.italics = true; pacData.color = 'white'; } else { pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4; } return pacData; // Note that row has zero offset. The spec uses 1. } /** * Parse characters. * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise. */ ; _proto7.parseChars = function parseChars(a, b) { var channelNr; var charCodes = null; var charCode1 = null; if (a >= 0x19) { channelNr = 2; charCode1 = a - 8; } else { channelNr = 1; charCode1 = a; } if (charCode1 >= 0x11 && charCode1 <= 0x13) { // Special character var oneCode = b; if (charCode1 === 0x11) { oneCode = b + 0x50; } else if (charCode1 === 0x12) { oneCode = b + 0x70; } else { oneCode = b + 0x90; } this.logger.log(VerboseLevel.INFO, 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr); charCodes = [oneCode]; } else if (a >= 0x20 && a <= 0x7f) { charCodes = b === 0 ? [a] : [a, b]; } if (charCodes) { var hexCodes = numArrayToHexArray(charCodes); this.logger.log(VerboseLevel.DEBUG, 'Char codes = ' + hexCodes.join(',')); setLastCmd(a, b, this.cmdHistory); } return charCodes; } /** * Parse extended background attributes as well as new foreground color black. * @returns {Boolean} Tells if background attributes are found */ ; _proto7.parseBackgroundAttributes = function parseBackgroundAttributes(a, b) { var case1 = (a === 0x10 || a === 0x18) && b >= 0x20 && b <= 0x2f; var case2 = (a === 0x17 || a === 0x1f) && b >= 0x2d && b <= 0x2f; if (!(case1 || case2)) { return false; } var index; var bkgData = {}; if (a === 0x10 || a === 0x18) { index = Math.floor((b - 0x20) / 2); bkgData.background = backgroundColors[index]; if (b % 2 === 1) { bkgData.background = bkgData.background + '_semi'; } } else if (b === 0x2d) { bkgData.background = 'transparent'; } else { bkgData.foreground = 'black'; if (b === 0x2f) { bkgData.underline = true; } } var chNr = a <= 0x17 ? 1 : 2; var channel = this.channels[chNr]; channel.setBkgData(bkgData); setLastCmd(a, b, this.cmdHistory); return true; } /** * Reset state of parser and its channels. */ ; _proto7.reset = function reset() { for (var i = 0; i < Object.keys(this.channels).length; i++) { var channel = this.channels[i]; if (channel) { channel.reset(); } } this.cmdHistory = createCmdHistory(); } /** * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty. */ ; _proto7.cueSplitAtTime = function cueSplitAtTime(t) { for (var i = 0; i < this.channels.length; i++) { var channel = this.channels[i]; if (channel) { channel.cueSplitAtTime(t); } } }; return Cea608Parser; }(); function setLastCmd(a, b, cmdHistory) { cmdHistory.a = a; cmdHistory.b = b; } function hasCmdRepeated(a, b, cmdHistory) { return cmdHistory.a === a && cmdHistory.b === b; } function createCmdHistory() { return { a: null, b: null }; } /* harmony default export */ var cea_608_parser = (Cea608Parser); // CONCATENATED MODULE: ./src/utils/output-filter.ts var OutputFilter = /*#__PURE__*/function () { function OutputFilter(timelineController, trackName) { this.timelineController = void 0; this.cueRanges = []; this.trackName = void 0; this.startTime = null; this.endTime = null; this.screen = null; this.timelineController = timelineController; this.trackName = trackName; } var _proto = OutputFilter.prototype; _proto.dispatchCue = function dispatchCue() { if (this.startTime === null) { return; } this.timelineController.addCues(this.trackName, this.startTime, this.endTime, this.screen, this.cueRanges); this.startTime = null; }; _proto.newCue = function newCue(startTime, endTime, screen) { if (this.startTime === null || this.startTime > startTime) { this.startTime = startTime; } this.endTime = endTime; this.screen = screen; this.timelineController.createCaptionsTrack(this.trackName); }; _proto.reset = function reset() { this.cueRanges = []; }; return OutputFilter; }(); // CONCATENATED MODULE: ./src/utils/webvtt-parser.js // String.prototype.startsWith is not supported in IE11 var startsWith = function startsWith(inputString, searchString, position) { return inputString.substr(position || 0, searchString.length) === searchString; }; var webvtt_parser_cueString2millis = function cueString2millis(timeString) { var ts = parseInt(timeString.substr(-3)); var secs = parseInt(timeString.substr(-6, 2)); var mins = parseInt(timeString.substr(-9, 2)); var hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0; if (!Object(number["isFiniteNumber"])(ts) || !Object(number["isFiniteNumber"])(secs) || !Object(number["isFiniteNumber"])(mins) || !Object(number["isFiniteNumber"])(hours)) { throw Error("Malformed X-TIMESTAMP-MAP: Local:" + timeString); } ts += 1000 * secs; ts += 60 * 1000 * mins; ts += 60 * 60 * 1000 * hours; return ts; }; // From https://github.com/darkskyapp/string-hash var hash = function hash(text) { var hash = 5381; var i = text.length; while (i) { hash = hash * 33 ^ text.charCodeAt(--i); } return (hash >>> 0).toString(); }; var calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) { var currCC = vttCCs[cc]; var prevCC = vttCCs[currCC.prevCC]; // This is the first discontinuity or cues have been processed since the last discontinuity // Offset = current discontinuity time if (!prevCC || !prevCC.new && currCC.new) { vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start; currCC.new = false; return; } // There have been discontinuities since cues were last parsed. // Offset = time elapsed while (prevCC && prevCC.new) { vttCCs.ccOffset += currCC.start - prevCC.start; currCC.new = false; currCC = prevCC; prevCC = vttCCs[currCC.prevCC]; } vttCCs.presentationOffset = presentationTime; }; var WebVTTParser = { parse: function parse(vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) { // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. var re = /\r\n|\n\r|\n|\r/g; // Uint8Array.prototype.reduce is not implemented in IE11 var vttLines = Object(id3["utf8ArrayToStr"])(new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n'); var cueTime = '00:00.000'; var mpegTs = 0; var localTime = 0; var presentationTime = 0; var cues = []; var parsingError; var inHeader = true; var timestampMap = false; // let VTTCue = VTTCue || window.TextTrackCue; // Create parser object using VTTCue with TextTrackCue fallback on certain browsers. var parser = new vttparser(); parser.oncue = function (cue) { // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline. var currCC = vttCCs[cc]; var cueOffset = vttCCs.ccOffset; // Update offsets for new discontinuities if (currCC && currCC.new) { if (localTime !== undefined) { // When local time is provided, offset = discontinuity start time - local time cueOffset = vttCCs.ccOffset = currCC.start; } else { calculateOffset(vttCCs, cc, presentationTime); } } if (presentationTime) { // If we have MPEGTS, offset = presentation time + discontinuity offset cueOffset = presentationTime - vttCCs.presentationOffset; } if (timestampMap) { cue.startTime += cueOffset - localTime; cue.endTime += cueOffset - localTime; } // Create a unique hash id for a cue based on start/end times and text. // This helps timeline-controller to avoid showing repeated captions. cue.id = hash(cue.startTime.toString()) + hash(cue.endTime.toString()) + hash(cue.text); // Fix encoding of special characters. TODO: Test with all sorts of weird characters. cue.text = decodeURIComponent(encodeURIComponent(cue.text)); if (cue.endTime > 0) { cues.push(cue); } }; parser.onparsingerror = function (e) { parsingError = e; }; parser.onflush = function () { if (parsingError && errorCallBack) { errorCallBack(parsingError); return; } callBack(cues); }; // Go through contents line by line. vttLines.forEach(function (line) { if (inHeader) { // Look for X-TIMESTAMP-MAP in header. if (startsWith(line, 'X-TIMESTAMP-MAP=')) { // Once found, no more are allowed anyway, so stop searching. inHeader = false; timestampMap = true; // Extract LOCAL and MPEGTS. line.substr(16).split(',').forEach(function (timestamp) { if (startsWith(timestamp, 'LOCAL:')) { cueTime = timestamp.substr(6); } else if (startsWith(timestamp, 'MPEGTS:')) { mpegTs = parseInt(timestamp.substr(7)); } }); try { // Calculate subtitle offset in milliseconds. if (syncPTS + (vttCCs[cc].start * 90000 || 0) < 0) { syncPTS += 8589934592; } // Adjust MPEGTS by sync PTS. mpegTs -= syncPTS; // Convert cue time to seconds localTime = webvtt_parser_cueString2millis(cueTime) / 1000; // Convert MPEGTS to seconds from 90kHz. presentationTime = mpegTs / 90000; } catch (e) { timestampMap = false; parsingError = e; } // Return without parsing X-TIMESTAMP-MAP line. return; } else if (line === '') { inHeader = false; } } // Parse line by default. parser.parse(line + '\n'); }); parser.flush(); } }; /* harmony default export */ var webvtt_parser = (WebVTTParser); // CONCATENATED MODULE: ./src/controller/timeline-controller.ts function timeline_controller_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function timeline_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var timeline_controller_TimelineController = /*#__PURE__*/function (_EventHandler) { timeline_controller_inheritsLoose(TimelineController, _EventHandler); function TimelineController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHING, events["default"].MEDIA_DETACHING, events["default"].FRAG_PARSING_USERDATA, events["default"].FRAG_DECRYPTED, events["default"].MANIFEST_LOADING, events["default"].MANIFEST_LOADED, events["default"].FRAG_LOADED, events["default"].INIT_PTS_FOUND) || this; _this.media = null; _this.config = void 0; _this.enabled = true; _this.Cues = void 0; _this.textTracks = []; _this.tracks = []; _this.initPTS = []; _this.unparsedVttFrags = []; _this.captionsTracks = {}; _this.nonNativeCaptionsTracks = {}; _this.captionsProperties = void 0; _this.cea608Parser1 = void 0; _this.cea608Parser2 = void 0; _this.lastSn = -1; _this.prevCC = -1; _this.vttCCs = newVTTCCs(); _this.hls = hls; _this.config = hls.config; _this.Cues = hls.config.cueHandler; _this.captionsProperties = { textTrack1: { label: _this.config.captionsTextTrack1Label, languageCode: _this.config.captionsTextTrack1LanguageCode }, textTrack2: { label: _this.config.captionsTextTrack2Label, languageCode: _this.config.captionsTextTrack2LanguageCode }, textTrack3: { label: _this.config.captionsTextTrack3Label, languageCode: _this.config.captionsTextTrack3LanguageCode }, textTrack4: { label: _this.config.captionsTextTrack4Label, languageCode: _this.config.captionsTextTrack4LanguageCode } }; if (_this.config.enableCEA708Captions) { var channel1 = new OutputFilter(timeline_controller_assertThisInitialized(_this), 'textTrack1'); var channel2 = new OutputFilter(timeline_controller_assertThisInitialized(_this), 'textTrack2'); var channel3 = new OutputFilter(timeline_controller_assertThisInitialized(_this), 'textTrack3'); var channel4 = new OutputFilter(timeline_controller_assertThisInitialized(_this), 'textTrack4'); _this.cea608Parser1 = new cea_608_parser(1, channel1, channel2); _this.cea608Parser2 = new cea_608_parser(3, channel3, channel4); } return _this; } var _proto = TimelineController.prototype; _proto.addCues = function addCues(trackName, startTime, endTime, screen, cueRanges) { // skip cues which overlap more than 50% with previously parsed time ranges var merged = false; for (var i = cueRanges.length; i--;) { var cueRange = cueRanges[i]; var overlap = intersection(cueRange[0], cueRange[1], startTime, endTime); if (overlap >= 0) { cueRange[0] = Math.min(cueRange[0], startTime); cueRange[1] = Math.max(cueRange[1], endTime); merged = true; if (overlap / (endTime - startTime) > 0.5) { return; } } } if (!merged) { cueRanges.push([startTime, endTime]); } if (this.config.renderTextTracksNatively) { this.Cues.newCue(this.captionsTracks[trackName], startTime, endTime, screen); } else { var cues = this.Cues.newCue(null, startTime, endTime, screen); this.hls.trigger(events["default"].CUES_PARSED, { type: 'captions', cues: cues, track: trackName }); } } // Triggered when an initial PTS is found; used for synchronisation of WebVTT. ; _proto.onInitPtsFound = function onInitPtsFound(data) { var _this2 = this; var frag = data.frag, id = data.id, initPTS = data.initPTS; var unparsedVttFrags = this.unparsedVttFrags; if (id === 'main') { this.initPTS[frag.cc] = initPTS; } // Due to asynchronous processing, initial PTS may arrive later than the first VTT fragments are loaded. // Parse any unparsed fragments upon receiving the initial PTS. if (unparsedVttFrags.length) { this.unparsedVttFrags = []; unparsedVttFrags.forEach(function (frag) { _this2.onFragLoaded(frag); }); } }; _proto.getExistingTrack = function getExistingTrack(trackName) { var media = this.media; if (media) { for (var i = 0; i < media.textTracks.length; i++) { var textTrack = media.textTracks[i]; if (textTrack[trackName]) { return textTrack; } } } return null; }; _proto.createCaptionsTrack = function createCaptionsTrack(trackName) { if (this.config.renderTextTracksNatively) { this.createNativeTrack(trackName); } else { this.createNonNativeTrack(trackName); } }; _proto.createNativeTrack = function createNativeTrack(trackName) { if (this.captionsTracks[trackName]) { return; } var captionsProperties = this.captionsProperties, captionsTracks = this.captionsTracks, media = this.media; var _captionsProperties$t = captionsProperties[trackName], label = _captionsProperties$t.label, languageCode = _captionsProperties$t.languageCode; // Enable reuse of existing text track. var existingTrack = this.getExistingTrack(trackName); if (!existingTrack) { var textTrack = this.createTextTrack('captions', label, languageCode); if (textTrack) { // Set a special property on the track so we know it's managed by Hls.js textTrack[trackName] = true; captionsTracks[trackName] = textTrack; } } else { captionsTracks[trackName] = existingTrack; clearCurrentCues(captionsTracks[trackName]); sendAddTrackEvent(captionsTracks[trackName], media); } }; _proto.createNonNativeTrack = function createNonNativeTrack(trackName) { if (this.nonNativeCaptionsTracks[trackName]) { return; } // Create a list of a single track for the provider to consume var trackProperties = this.captionsProperties[trackName]; if (!trackProperties) { return; } var label = trackProperties.label; var track = { _id: trackName, label: label, kind: 'captions', default: trackProperties.media ? !!trackProperties.media.default : false, closedCaptions: trackProperties.media }; this.nonNativeCaptionsTracks[trackName] = track; this.hls.trigger(events["default"].NON_NATIVE_TEXT_TRACKS_FOUND, { tracks: [track] }); }; _proto.createTextTrack = function createTextTrack(kind, label, lang) { var media = this.media; if (!media) { return; } return media.addTextTrack(kind, label, lang); }; _proto.destroy = function destroy() { _EventHandler.prototype.destroy.call(this); }; _proto.onMediaAttaching = function onMediaAttaching(data) { this.media = data.media; this._cleanTracks(); }; _proto.onMediaDetaching = function onMediaDetaching() { var captionsTracks = this.captionsTracks; Object.keys(captionsTracks).forEach(function (trackName) { clearCurrentCues(captionsTracks[trackName]); delete captionsTracks[trackName]; }); this.nonNativeCaptionsTracks = {}; }; _proto.onManifestLoading = function onManifestLoading() { this.lastSn = -1; // Detect discontiguity in fragment parsing this.prevCC = -1; this.vttCCs = newVTTCCs(); // Detect discontinuity in subtitle manifests this._cleanTracks(); this.tracks = []; this.captionsTracks = {}; this.nonNativeCaptionsTracks = {}; }; _proto._cleanTracks = function _cleanTracks() { // clear outdated subtitles var media = this.media; if (!media) { return; } var textTracks = media.textTracks; if (textTracks) { for (var i = 0; i < textTracks.length; i++) { clearCurrentCues(textTracks[i]); } } }; _proto.onManifestLoaded = function onManifestLoaded(data) { var _this3 = this; this.textTracks = []; this.unparsedVttFrags = this.unparsedVttFrags || []; this.initPTS = []; if (this.cea608Parser1 && this.cea608Parser2) { this.cea608Parser1.reset(); this.cea608Parser2.reset(); } if (this.config.enableWebVTT) { var tracks = data.subtitles || []; var sameTracks = this.tracks && tracks && this.tracks.length === tracks.length; this.tracks = data.subtitles || []; if (this.config.renderTextTracksNatively) { var inUseTracks = this.media ? this.media.textTracks : []; this.tracks.forEach(function (track, index) { var textTrack; if (index < inUseTracks.length) { var inUseTrack = null; for (var i = 0; i < inUseTracks.length; i++) { if (canReuseVttTextTrack(inUseTracks[i], track)) { inUseTrack = inUseTracks[i]; break; } } // Reuse tracks with the same label, but do not reuse 608/708 tracks if (inUseTrack) { textTrack = inUseTrack; } } if (!textTrack) { textTrack = _this3.createTextTrack('subtitles', track.name, track.lang); } if (track.default) { textTrack.mode = _this3.hls.subtitleDisplay ? 'showing' : 'hidden'; } else { textTrack.mode = 'disabled'; } _this3.textTracks.push(textTrack); }); } else if (!sameTracks && this.tracks && this.tracks.length) { // Create a list of tracks for the provider to consume var tracksList = this.tracks.map(function (track) { return { label: track.name, kind: track.type.toLowerCase(), default: track.default, subtitleTrack: track }; }); this.hls.trigger(events["default"].NON_NATIVE_TEXT_TRACKS_FOUND, { tracks: tracksList }); } } if (this.config.enableCEA708Captions && data.captions) { data.captions.forEach(function (captionsTrack) { var instreamIdMatch = /(?:CC|SERVICE)([1-4])/.exec(captionsTrack.instreamId); if (!instreamIdMatch) { return; } var trackName = "textTrack" + instreamIdMatch[1]; var trackProperties = _this3.captionsProperties[trackName]; if (!trackProperties) { return; } trackProperties.label = captionsTrack.name; if (captionsTrack.lang) { // optional attribute trackProperties.languageCode = captionsTrack.lang; } trackProperties.media = captionsTrack; }); } }; _proto.onFragLoaded = function onFragLoaded(data) { var frag = data.frag, payload = data.payload; var cea608Parser1 = this.cea608Parser1, cea608Parser2 = this.cea608Parser2, initPTS = this.initPTS, lastSn = this.lastSn, unparsedVttFrags = this.unparsedVttFrags; if (frag.type === 'main') { var sn = frag.sn; // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack if (frag.sn !== lastSn + 1) { if (cea608Parser1 && cea608Parser2) { cea608Parser1.reset(); cea608Parser2.reset(); } } this.lastSn = sn; } // eslint-disable-line brace-style // If fragment is subtitle type, parse as WebVTT. else if (frag.type === 'subtitle') { if (payload.byteLength) { // We need an initial synchronisation PTS. Store fragments as long as none has arrived. if (!Object(number["isFiniteNumber"])(initPTS[frag.cc])) { unparsedVttFrags.push(data); if (initPTS.length) { // finish unsuccessfully, otherwise the subtitle-stream-controller could be blocked from loading new frags. this.hls.trigger(events["default"].SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); } return; } var decryptData = frag.decryptdata; // If the subtitles are not encrypted, parse VTTs now. Otherwise, we need to wait. if (decryptData == null || decryptData.key == null || decryptData.method !== 'AES-128') { this._parseVTTs(frag, payload); } } else { // In case there is no payload, finish unsuccessfully. this.hls.trigger(events["default"].SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); } } }; _proto._parseVTTs = function _parseVTTs(frag, payload) { var _this4 = this; var hls = this.hls, prevCC = this.prevCC, textTracks = this.textTracks, vttCCs = this.vttCCs; if (!vttCCs[frag.cc]) { vttCCs[frag.cc] = { start: frag.start, prevCC: prevCC, new: true }; this.prevCC = frag.cc; } // Parse the WebVTT file contents. webvtt_parser.parse(payload, this.initPTS[frag.cc], vttCCs, frag.cc, function (cues) { if (_this4.config.renderTextTracksNatively) { var currentTrack = textTracks[frag.level]; // WebVTTParser.parse is an async method and if the currently selected text track mode is set to "disabled" // before parsing is done then don't try to access currentTrack.cues.getCueById as cues will be null // and trying to access getCueById method of cues will throw an exception // Because we check if the mode is diabled, we can force check `cues` below. They can't be null. if (currentTrack.mode === 'disabled') { hls.trigger(events["default"].SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); return; } // Add cues and trigger event with success true. cues.forEach(function (cue) { // Sometimes there are cue overlaps on segmented vtts so the same // cue can appear more than once in different vtt files. // This avoid showing duplicated cues with same timecode and text. if (!currentTrack.cues.getCueById(cue.id)) { try { currentTrack.addCue(cue); if (!currentTrack.cues.getCueById(cue.id)) { throw new Error("addCue is failed for: " + cue); } } catch (err) { logger["logger"].debug("Failed occurred on adding cues: " + err); var textTrackCue = new window.TextTrackCue(cue.startTime, cue.endTime, cue.text); textTrackCue.id = cue.id; currentTrack.addCue(textTrackCue); } } }); } else { var trackId = _this4.tracks[frag.level].default ? 'default' : 'subtitles' + frag.level; hls.trigger(events["default"].CUES_PARSED, { type: 'subtitles', cues: cues, track: trackId }); } hls.trigger(events["default"].SUBTITLE_FRAG_PROCESSED, { success: true, frag: frag }); }, function (e) { // Something went wrong while parsing. Trigger event with success false. logger["logger"].log("Failed to parse VTT cue: " + e); hls.trigger(events["default"].SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); }); }; _proto.onFragDecrypted = function onFragDecrypted(data) { var frag = data.frag, payload = data.payload; if (frag.type === 'subtitle') { if (!Object(number["isFiniteNumber"])(this.initPTS[frag.cc])) { this.unparsedVttFrags.push(data); return; } this._parseVTTs(frag, payload); } }; _proto.onFragParsingUserdata = function onFragParsingUserdata(data) { var cea608Parser1 = this.cea608Parser1, cea608Parser2 = this.cea608Parser2; if (!this.enabled || !(cea608Parser1 && cea608Parser2)) { return; } // If the event contains captions (found in the bytes property), push all bytes into the parser immediately // It will create the proper timestamps based on the PTS value for (var i = 0; i < data.samples.length; i++) { var ccBytes = data.samples[i].bytes; if (ccBytes) { var ccdatas = this.extractCea608Data(ccBytes); cea608Parser1.addData(data.samples[i].pts, ccdatas[0]); cea608Parser2.addData(data.samples[i].pts, ccdatas[1]); } } }; _proto.extractCea608Data = function extractCea608Data(byteArray) { var count = byteArray[0] & 31; var position = 2; var actualCCBytes = [[], []]; for (var j = 0; j < count; j++) { var tmpByte = byteArray[position++]; var ccbyte1 = 0x7F & byteArray[position++]; var ccbyte2 = 0x7F & byteArray[position++]; var ccValid = (4 & tmpByte) !== 0; var ccType = 3 & tmpByte; if (ccbyte1 === 0 && ccbyte2 === 0) { continue; } if (ccValid) { if (ccType === 0 || ccType === 1) { actualCCBytes[ccType].push(ccbyte1); actualCCBytes[ccType].push(ccbyte2); } } } return actualCCBytes; }; return TimelineController; }(event_handler); function canReuseVttTextTrack(inUseTrack, manifestTrack) { return inUseTrack && inUseTrack.label === manifestTrack.name && !(inUseTrack.textTrack1 || inUseTrack.textTrack2); } function intersection(x1, x2, y1, y2) { return Math.min(x2, y2) - Math.max(x1, y1); } function newVTTCCs() { return { ccOffset: 0, presentationOffset: 0, 0: { start: 0, prevCC: -1, new: false } }; } /* harmony default export */ var timeline_controller = (timeline_controller_TimelineController); // CONCATENATED MODULE: ./src/controller/subtitle-track-controller.js function subtitle_track_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function subtitle_track_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) subtitle_track_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) subtitle_track_controller_defineProperties(Constructor, staticProps); return Constructor; } function subtitle_track_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } var subtitle_track_controller_SubtitleTrackController = /*#__PURE__*/function (_EventHandler) { subtitle_track_controller_inheritsLoose(SubtitleTrackController, _EventHandler); function SubtitleTrackController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHING, events["default"].MANIFEST_LOADED, events["default"].SUBTITLE_TRACK_LOADED) || this; _this.tracks = []; _this.trackId = -1; _this.media = null; _this.stopped = true; /** * @member {boolean} subtitleDisplay Enable/disable subtitle display rendering */ _this.subtitleDisplay = true; /** * Keeps reference to a default track id when media has not been attached yet * @member {number} */ _this.queuedDefaultTrack = null; return _this; } var _proto = SubtitleTrackController.prototype; _proto.destroy = function destroy() { event_handler.prototype.destroy.call(this); } // Listen for subtitle track change, then extract the current track ID. ; _proto.onMediaAttached = function onMediaAttached(data) { var _this2 = this; this.media = data.media; if (!this.media) { return; } if (Object(number["isFiniteNumber"])(this.queuedDefaultTrack)) { this.subtitleTrack = this.queuedDefaultTrack; this.queuedDefaultTrack = null; } this.trackChangeListener = this._onTextTracksChanged.bind(this); this.useTextTrackPolling = !(this.media.textTracks && 'onchange' in this.media.textTracks); if (this.useTextTrackPolling) { this.subtitlePollingInterval = setInterval(function () { _this2.trackChangeListener(); }, 500); } else { this.media.textTracks.addEventListener('change', this.trackChangeListener); } }; _proto.onMediaDetaching = function onMediaDetaching() { if (!this.media) { return; } if (this.useTextTrackPolling) { clearInterval(this.subtitlePollingInterval); } else { this.media.textTracks.removeEventListener('change', this.trackChangeListener); } if (Object(number["isFiniteNumber"])(this.subtitleTrack)) { this.queuedDefaultTrack = this.subtitleTrack; } var textTracks = filterSubtitleTracks(this.media.textTracks); // Clear loaded cues on media detachment from tracks textTracks.forEach(function (track) { clearCurrentCues(track); }); // Disable all subtitle tracks before detachment so when reattached only tracks in that content are enabled. this.subtitleTrack = -1; this.media = null; } // Fired whenever a new manifest is loaded. ; _proto.onManifestLoaded = function onManifestLoaded(data) { var _this3 = this; var tracks = data.subtitles || []; this.tracks = tracks; this.hls.trigger(events["default"].SUBTITLE_TRACKS_UPDATED, { subtitleTracks: tracks }); // loop through available subtitle tracks and autoselect default if needed // TODO: improve selection logic to handle forced, etc tracks.forEach(function (track) { if (track.default) { // setting this.subtitleTrack will trigger internal logic // if media has not been attached yet, it will fail // we keep a reference to the default track id // and we'll set subtitleTrack when onMediaAttached is triggered if (_this3.media) { _this3.subtitleTrack = track.id; } else { _this3.queuedDefaultTrack = track.id; } } }); }; _proto.onSubtitleTrackLoaded = function onSubtitleTrackLoaded(data) { var _this4 = this; var id = data.id, details = data.details; var trackId = this.trackId, tracks = this.tracks; var currentTrack = tracks[trackId]; if (id >= tracks.length || id !== trackId || !currentTrack || this.stopped) { this._clearReloadTimer(); return; } logger["logger"].log("subtitle track " + id + " loaded"); if (details.live) { var reloadInterval = computeReloadInterval(currentTrack.details, details, data.stats.trequest); logger["logger"].log("Reloading live subtitle playlist in " + reloadInterval + "ms"); this.timer = setTimeout(function () { _this4._loadCurrentTrack(); }, reloadInterval); } else { this._clearReloadTimer(); } }; _proto.startLoad = function startLoad() { this.stopped = false; this._loadCurrentTrack(); }; _proto.stopLoad = function stopLoad() { this.stopped = true; this._clearReloadTimer(); } /** get alternate subtitle tracks list from playlist **/ ; _proto._clearReloadTimer = function _clearReloadTimer() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } }; _proto._loadCurrentTrack = function _loadCurrentTrack() { var trackId = this.trackId, tracks = this.tracks, hls = this.hls; var currentTrack = tracks[trackId]; if (trackId < 0 || !currentTrack || currentTrack.details && !currentTrack.details.live) { return; } logger["logger"].log("Loading subtitle track " + trackId); hls.trigger(events["default"].SUBTITLE_TRACK_LOADING, { url: currentTrack.url, id: trackId }); } /** * Disables the old subtitleTrack and sets current mode on the next subtitleTrack. * This operates on the DOM textTracks. * A value of -1 will disable all subtitle tracks. * @param newId - The id of the next track to enable * @private */ ; _proto._toggleTrackModes = function _toggleTrackModes(newId) { var media = this.media, subtitleDisplay = this.subtitleDisplay, trackId = this.trackId; if (!media) { return; } var textTracks = filterSubtitleTracks(media.textTracks); if (newId === -1) { [].slice.call(textTracks).forEach(function (track) { track.mode = 'disabled'; }); } else { var oldTrack = textTracks[trackId]; if (oldTrack) { oldTrack.mode = 'disabled'; } } var nextTrack = textTracks[newId]; if (nextTrack) { nextTrack.mode = subtitleDisplay ? 'showing' : 'hidden'; } } /** * This method is responsible for validating the subtitle index and periodically reloading if live. * Dispatches the SUBTITLE_TRACK_SWITCH event, which instructs the subtitle-stream-controller to load the selected track. * @param newId - The id of the subtitle track to activate. */ ; _proto._setSubtitleTrackInternal = function _setSubtitleTrackInternal(newId) { var hls = this.hls, tracks = this.tracks; if (!Object(number["isFiniteNumber"])(newId) || newId < -1 || newId >= tracks.length) { return; } this.trackId = newId; logger["logger"].log("Switching to subtitle track " + newId); hls.trigger(events["default"].SUBTITLE_TRACK_SWITCH, { id: newId }); this._loadCurrentTrack(); }; _proto._onTextTracksChanged = function _onTextTracksChanged() { // Media is undefined when switching streams via loadSource() if (!this.media || !this.hls.config.renderTextTracksNatively) { return; } var trackId = -1; var tracks = filterSubtitleTracks(this.media.textTracks); for (var id = 0; id < tracks.length; id++) { if (tracks[id].mode === 'hidden') { // Do not break in case there is a following track with showing. trackId = id; } else if (tracks[id].mode === 'showing') { trackId = id; break; } } // Setting current subtitleTrack will invoke code. this.subtitleTrack = trackId; }; subtitle_track_controller_createClass(SubtitleTrackController, [{ key: "subtitleTracks", get: function get() { return this.tracks; } /** get index of the selected subtitle track (index in subtitle track lists) **/ }, { key: "subtitleTrack", get: function get() { return this.trackId; } /** select a subtitle track, based on its index in subtitle track lists**/ , set: function set(subtitleTrackId) { if (this.trackId !== subtitleTrackId) { this._toggleTrackModes(subtitleTrackId); this._setSubtitleTrackInternal(subtitleTrackId); } } }]); return SubtitleTrackController; }(event_handler); function filterSubtitleTracks(textTrackList) { var tracks = []; for (var i = 0; i < textTrackList.length; i++) { var track = textTrackList[i]; // Edge adds a track without a label; we don't want to use it if (track.kind === 'subtitles' && track.label) { tracks.push(textTrackList[i]); } } return tracks; } /* harmony default export */ var subtitle_track_controller = (subtitle_track_controller_SubtitleTrackController); // EXTERNAL MODULE: ./src/crypt/decrypter.js + 3 modules var decrypter = __webpack_require__("./src/crypt/decrypter.js"); // CONCATENATED MODULE: ./src/controller/subtitle-stream-controller.js function subtitle_stream_controller_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function subtitle_stream_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * @class SubtitleStreamController */ var subtitle_stream_controller_window = window, subtitle_stream_controller_performance = subtitle_stream_controller_window.performance; var subtitle_stream_controller_TICK_INTERVAL = 500; // how often to tick in ms var subtitle_stream_controller_SubtitleStreamController = /*#__PURE__*/function (_BaseStreamController) { subtitle_stream_controller_inheritsLoose(SubtitleStreamController, _BaseStreamController); function SubtitleStreamController(hls, fragmentTracker) { var _this; _this = _BaseStreamController.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHING, events["default"].ERROR, events["default"].KEY_LOADED, events["default"].FRAG_LOADED, events["default"].SUBTITLE_TRACKS_UPDATED, events["default"].SUBTITLE_TRACK_SWITCH, events["default"].SUBTITLE_TRACK_LOADED, events["default"].SUBTITLE_FRAG_PROCESSED, events["default"].LEVEL_UPDATED) || this; _this.fragmentTracker = fragmentTracker; _this.config = hls.config; _this.state = State.STOPPED; _this.tracks = []; _this.tracksBuffered = []; _this.currentTrackId = -1; _this.decrypter = new decrypter["default"](hls, hls.config); // lastAVStart stores the time in seconds for the start time of a level load _this.lastAVStart = 0; _this._onMediaSeeking = _this.onMediaSeeking.bind(subtitle_stream_controller_assertThisInitialized(_this)); return _this; } var _proto = SubtitleStreamController.prototype; _proto.startLoad = function startLoad() { this.stopLoad(); this.state = State.IDLE; // Check if we already have a track with necessary details to load fragments var currentTrack = this.tracks[this.currentTrackId]; if (currentTrack && currentTrack.details) { this.setInterval(subtitle_stream_controller_TICK_INTERVAL); this.tick(); } }; _proto.onSubtitleFragProcessed = function onSubtitleFragProcessed(data) { var frag = data.frag, success = data.success; this.fragPrevious = frag; this.state = State.IDLE; if (!success) { return; } var buffered = this.tracksBuffered[this.currentTrackId]; if (!buffered) { return; } // Create/update a buffered array matching the interface used by BufferHelper.bufferedInfo // so we can re-use the logic used to detect how much have been buffered var timeRange; var fragStart = frag.start; for (var i = 0; i < buffered.length; i++) { if (fragStart >= buffered[i].start && fragStart <= buffered[i].end) { timeRange = buffered[i]; break; } } var fragEnd = frag.start + frag.duration; if (timeRange) { timeRange.end = fragEnd; } else { timeRange = { start: fragStart, end: fragEnd }; buffered.push(timeRange); } }; _proto.onMediaAttached = function onMediaAttached(_ref) { var media = _ref.media; this.media = media; media.addEventListener('seeking', this._onMediaSeeking); this.state = State.IDLE; }; _proto.onMediaDetaching = function onMediaDetaching() { var _this2 = this; if (!this.media) { return; } this.media.removeEventListener('seeking', this._onMediaSeeking); this.fragmentTracker.removeAllFragments(); this.currentTrackId = -1; this.tracks.forEach(function (track) { _this2.tracksBuffered[track.id] = []; }); this.media = null; this.state = State.STOPPED; } // If something goes wrong, proceed to next frag, if we were processing one. ; _proto.onError = function onError(data) { var frag = data.frag; // don't handle error not related to subtitle fragment if (!frag || frag.type !== 'subtitle') { return; } if (this.fragCurrent && this.fragCurrent.loader) { this.fragCurrent.loader.abort(); } this.state = State.IDLE; } // Got all new subtitle tracks. ; _proto.onSubtitleTracksUpdated = function onSubtitleTracksUpdated(data) { var _this3 = this; logger["logger"].log('subtitle tracks updated'); this.tracksBuffered = []; this.tracks = data.subtitleTracks; this.tracks.forEach(function (track) { _this3.tracksBuffered[track.id] = []; }); }; _proto.onSubtitleTrackSwitch = function onSubtitleTrackSwitch(data) { this.currentTrackId = data.id; if (!this.tracks || !this.tracks.length || this.currentTrackId === -1) { this.clearInterval(); return; } // Check if track has the necessary details to load fragments var currentTrack = this.tracks[this.currentTrackId]; if (currentTrack && currentTrack.details) { this.setInterval(subtitle_stream_controller_TICK_INTERVAL); } } // Got a new set of subtitle fragments. ; _proto.onSubtitleTrackLoaded = function onSubtitleTrackLoaded(data) { var id = data.id, details = data.details; var currentTrackId = this.currentTrackId, tracks = this.tracks; var currentTrack = tracks[currentTrackId]; if (id >= tracks.length || id !== currentTrackId || !currentTrack) { return; } if (details.live) { mergeSubtitlePlaylists(currentTrack.details, details, this.lastAVStart); } currentTrack.details = details; this.setInterval(subtitle_stream_controller_TICK_INTERVAL); }; _proto.onKeyLoaded = function onKeyLoaded() { if (this.state === State.KEY_LOADING) { this.state = State.IDLE; } }; _proto.onFragLoaded = function onFragLoaded(data) { var fragCurrent = this.fragCurrent; var decryptData = data.frag.decryptdata; var fragLoaded = data.frag; var hls = this.hls; if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.type === 'subtitle' && fragCurrent.sn === data.frag.sn) { // check to see if the payload needs to be decrypted if (data.payload.byteLength > 0 && decryptData && decryptData.key && decryptData.method === 'AES-128') { var startTime = subtitle_stream_controller_performance.now(); // decrypt the subtitles this.decrypter.decrypt(data.payload, decryptData.key.buffer, decryptData.iv.buffer, function (decryptedData) { var endTime = subtitle_stream_controller_performance.now(); hls.trigger(events["default"].FRAG_DECRYPTED, { frag: fragLoaded, payload: decryptedData, stats: { tstart: startTime, tdecrypt: endTime } }); }); } } }; _proto.onLevelUpdated = function onLevelUpdated(_ref2) { var details = _ref2.details; var frags = details.fragments; this.lastAVStart = frags.length ? frags[0].start : 0; }; _proto.doTick = function doTick() { if (!this.media) { this.state = State.IDLE; return; } switch (this.state) { case State.IDLE: { var config = this.config, currentTrackId = this.currentTrackId, fragmentTracker = this.fragmentTracker, media = this.media, tracks = this.tracks; if (!tracks || !tracks[currentTrackId] || !tracks[currentTrackId].details) { break; } var maxBufferHole = config.maxBufferHole, maxFragLookUpTolerance = config.maxFragLookUpTolerance; var maxConfigBuffer = Math.min(config.maxBufferLength, config.maxMaxBufferLength); var bufferedInfo = BufferHelper.bufferedInfo(this._getBuffered(), media.currentTime, maxBufferHole); var bufferEnd = bufferedInfo.end, bufferLen = bufferedInfo.len; var trackDetails = tracks[currentTrackId].details; var fragments = trackDetails.fragments; var fragLen = fragments.length; var end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration; if (bufferLen > maxConfigBuffer) { return; } var foundFrag; var fragPrevious = this.fragPrevious; if (bufferEnd < end) { if (fragPrevious && trackDetails.hasProgramDateTime) { foundFrag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, maxFragLookUpTolerance); } if (!foundFrag) { foundFrag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, maxFragLookUpTolerance); } } else { foundFrag = fragments[fragLen - 1]; } if (foundFrag && foundFrag.encrypted) { logger["logger"].log("Loading key for " + foundFrag.sn); this.state = State.KEY_LOADING; this.hls.trigger(events["default"].KEY_LOADING, { frag: foundFrag }); } else if (foundFrag && fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) { // only load if fragment is not loaded this.fragCurrent = foundFrag; this.state = State.FRAG_LOADING; this.hls.trigger(events["default"].FRAG_LOADING, { frag: foundFrag }); } } } }; _proto.stopLoad = function stopLoad() { this.lastAVStart = 0; this.fragPrevious = null; _BaseStreamController.prototype.stopLoad.call(this); }; _proto._getBuffered = function _getBuffered() { return this.tracksBuffered[this.currentTrackId] || []; }; _proto.onMediaSeeking = function onMediaSeeking() { if (this.fragCurrent) { var currentTime = this.media ? this.media.currentTime : 0; var tolerance = this.config.maxFragLookUpTolerance; var fragStartOffset = this.fragCurrent.start - tolerance; var fragEndOffset = this.fragCurrent.start + this.fragCurrent.duration + tolerance; // check if position will be out of currently loaded frag range after seeking : if out, cancel frag load, if in, don't do anything if (currentTime < fragStartOffset || currentTime > fragEndOffset) { if (this.fragCurrent.loader) { this.fragCurrent.loader.abort(); } this.fragmentTracker.removeFragment(this.fragCurrent); this.fragCurrent = null; this.fragPrevious = null; // switch to IDLE state to load new fragment this.state = State.IDLE; // speed up things this.tick(); } } }; return SubtitleStreamController; }(base_stream_controller_BaseStreamController); // CONCATENATED MODULE: ./src/utils/mediakeys-helper.ts /** * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess */ var KeySystems; (function (KeySystems) { KeySystems["WIDEVINE"] = "com.widevine.alpha"; KeySystems["PLAYREADY"] = "com.microsoft.playready"; })(KeySystems || (KeySystems = {})); var requestMediaKeySystemAccess = function () { if (typeof window !== 'undefined' && window.navigator && window.navigator.requestMediaKeySystemAccess) { return window.navigator.requestMediaKeySystemAccess.bind(window.navigator); } else { return null; } }(); // CONCATENATED MODULE: ./src/controller/eme-controller.ts function eme_controller_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function eme_controller_createClass(Constructor, protoProps, staticProps) { if (protoProps) eme_controller_defineProperties(Constructor.prototype, protoProps); if (staticProps) eme_controller_defineProperties(Constructor, staticProps); return Constructor; } function eme_controller_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * @author Stephan Hesse <disparat@gmail.com> | <tchakabam@gmail.com> * * DRM support for Hls.js */ var MAX_LICENSE_REQUEST_FAILURES = 3; /** * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration * @param {Array<string>} audioCodecs List of required audio codecs to support * @param {Array<string>} videoCodecs List of required video codecs to support * @param {object} drmSystemOptions Optional parameters/requirements for the key-system * @returns {Array<MediaSystemConfiguration>} An array of supported configurations */ var createWidevineMediaKeySystemConfigurations = function createWidevineMediaKeySystemConfigurations(audioCodecs, videoCodecs, drmSystemOptions) { /* jshint ignore:line */ var baseConfig = { // initDataTypes: ['keyids', 'mp4'], // label: "", // persistentState: "not-allowed", // or "required" ? // distinctiveIdentifier: "not-allowed", // or "required" ? // sessionTypes: ['temporary'], audioCapabilities: [], // { contentType: 'audio/mp4; codecs="mp4a.40.2"' } videoCapabilities: [] // { contentType: 'video/mp4; codecs="avc1.42E01E"' } }; audioCodecs.forEach(function (codec) { baseConfig.audioCapabilities.push({ contentType: "audio/mp4; codecs=\"" + codec + "\"", robustness: drmSystemOptions.audioRobustness || '' }); }); videoCodecs.forEach(function (codec) { baseConfig.videoCapabilities.push({ contentType: "video/mp4; codecs=\"" + codec + "\"", robustness: drmSystemOptions.videoRobustness || '' }); }); return [baseConfig]; }; /** * The idea here is to handle key-system (and their respective platforms) specific configuration differences * in order to work with the local requestMediaKeySystemAccess method. * * We can also rule-out platform-related key-system support at this point by throwing an error. * * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum * @param {Array<string>} audioCodecs List of required audio codecs to support * @param {Array<string>} videoCodecs List of required video codecs to support * @throws will throw an error if a unknown key system is passed * @returns {Array<MediaSystemConfiguration>} A non-empty Array of MediaKeySystemConfiguration objects */ var eme_controller_getSupportedMediaKeySystemConfigurations = function getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, drmSystemOptions) { switch (keySystem) { case KeySystems.WIDEVINE: return createWidevineMediaKeySystemConfigurations(audioCodecs, videoCodecs, drmSystemOptions); default: throw new Error("Unknown key-system: " + keySystem); } }; /** * Controller to deal with encrypted media extensions (EME) * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API * * @class * @constructor */ var eme_controller_EMEController = /*#__PURE__*/function (_EventHandler) { eme_controller_inheritsLoose(EMEController, _EventHandler); /** * @constructs * @param {Hls} hls Our Hls.js instance */ function EMEController(hls) { var _this; _this = _EventHandler.call(this, hls, events["default"].MEDIA_ATTACHED, events["default"].MEDIA_DETACHED, events["default"].MANIFEST_PARSED) || this; _this._widevineLicenseUrl = void 0; _this._licenseXhrSetup = void 0; _this._emeEnabled = void 0; _this._requestMediaKeySystemAccess = void 0; _this._drmSystemOptions = void 0; _this._config = void 0; _this._mediaKeysList = []; _this._media = null; _this._hasSetMediaKeys = false; _this._requestLicenseFailureCount = 0; _this.mediaKeysPromise = null; _this._onMediaEncrypted = function (e) { logger["logger"].log("Media is encrypted using \"" + e.initDataType + "\" init data type"); if (!_this.mediaKeysPromise) { logger["logger"].error('Fatal: Media is encrypted but no CDM access or no keys have been requested'); _this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_KEYS, fatal: true }); return; } var finallySetKeyAndStartSession = function finallySetKeyAndStartSession(mediaKeys) { if (!_this._media) { return; } _this._attemptSetMediaKeys(mediaKeys); _this._generateRequestWithPreferredKeySession(e.initDataType, e.initData); }; // Could use `Promise.finally` but some Promise polyfills are missing it _this.mediaKeysPromise.then(finallySetKeyAndStartSession).catch(finallySetKeyAndStartSession); }; _this._config = hls.config; _this._widevineLicenseUrl = _this._config.widevineLicenseUrl; _this._licenseXhrSetup = _this._config.licenseXhrSetup; _this._emeEnabled = _this._config.emeEnabled; _this._requestMediaKeySystemAccess = _this._config.requestMediaKeySystemAccessFunc; _this._drmSystemOptions = hls.config.drmSystemOptions; return _this; } /** * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum * @returns {string} License server URL for key-system (if any configured, otherwise causes error) * @throws if a unsupported keysystem is passed */ var _proto = EMEController.prototype; _proto.getLicenseServerUrl = function getLicenseServerUrl(keySystem) { switch (keySystem) { case KeySystems.WIDEVINE: if (!this._widevineLicenseUrl) { break; } return this._widevineLicenseUrl; } throw new Error("no license server URL configured for key-system \"" + keySystem + "\""); } /** * Requests access object and adds it to our list upon success * @private * @param {string} keySystem System ID (see `KeySystems`) * @param {Array<string>} audioCodecs List of required audio codecs to support * @param {Array<string>} videoCodecs List of required video codecs to support * @throws When a unsupported KeySystem is passed */ ; _proto._attemptKeySystemAccess = function _attemptKeySystemAccess(keySystem, audioCodecs, videoCodecs) { var _this2 = this; // This can throw, but is caught in event handler callpath var mediaKeySystemConfigs = eme_controller_getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, this._drmSystemOptions); logger["logger"].log('Requesting encrypted media key-system access'); // expecting interface like window.navigator.requestMediaKeySystemAccess var keySystemAccessPromise = this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs); this.mediaKeysPromise = keySystemAccessPromise.then(function (mediaKeySystemAccess) { return _this2._onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess); }); keySystemAccessPromise.catch(function (err) { logger["logger"].error("Failed to obtain key-system \"" + keySystem + "\" access:", err); }); }; /** * Handles obtaining access to a key-system * @private * @param {string} keySystem * @param {MediaKeySystemAccess} mediaKeySystemAccess https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemAccess */ _proto._onMediaKeySystemAccessObtained = function _onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess) { var _this3 = this; logger["logger"].log("Access for key-system \"" + keySystem + "\" obtained"); var mediaKeysListItem = { mediaKeysSessionInitialized: false, mediaKeySystemAccess: mediaKeySystemAccess, mediaKeySystemDomain: keySystem }; this._mediaKeysList.push(mediaKeysListItem); var mediaKeysPromise = Promise.resolve().then(function () { return mediaKeySystemAccess.createMediaKeys(); }).then(function (mediaKeys) { mediaKeysListItem.mediaKeys = mediaKeys; logger["logger"].log("Media-keys created for key-system \"" + keySystem + "\""); _this3._onMediaKeysCreated(); return mediaKeys; }); mediaKeysPromise.catch(function (err) { logger["logger"].error('Failed to create media-keys:', err); }); return mediaKeysPromise; } /** * Handles key-creation (represents access to CDM). We are going to create key-sessions upon this * for all existing keys where no session exists yet. * * @private */ ; _proto._onMediaKeysCreated = function _onMediaKeysCreated() { var _this4 = this; // check for all key-list items if a session exists, otherwise, create one this._mediaKeysList.forEach(function (mediaKeysListItem) { if (!mediaKeysListItem.mediaKeysSession) { // mediaKeys is definitely initialized here mediaKeysListItem.mediaKeysSession = mediaKeysListItem.mediaKeys.createSession(); _this4._onNewMediaKeySession(mediaKeysListItem.mediaKeysSession); } }); } /** * @private * @param {*} keySession */ ; _proto._onNewMediaKeySession = function _onNewMediaKeySession(keySession) { var _this5 = this; logger["logger"].log("New key-system session " + keySession.sessionId); keySession.addEventListener('message', function (event) { _this5._onKeySessionMessage(keySession, event.message); }, false); } /** * @private * @param {MediaKeySession} keySession * @param {ArrayBuffer} message */ ; _proto._onKeySessionMessage = function _onKeySessionMessage(keySession, message) { logger["logger"].log('Got EME message event, creating license request'); this._requestLicense(message, function (data) { logger["logger"].log("Received license data (length: " + (data ? data.byteLength : data) + "), updating key-session"); keySession.update(data); }); } /** * @private * @param e {MediaEncryptedEvent} */ ; /** * @private */ _proto._attemptSetMediaKeys = function _attemptSetMediaKeys(mediaKeys) { if (!this._media) { throw new Error('Attempted to set mediaKeys without first attaching a media element'); } if (!this._hasSetMediaKeys) { // FIXME: see if we can/want/need-to really to deal with several potential key-sessions? var keysListItem = this._mediaKeysList[0]; if (!keysListItem || !keysListItem.mediaKeys) { logger["logger"].error('Fatal: Media is encrypted but no CDM access or no keys have been obtained yet'); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_KEYS, fatal: true }); return; } logger["logger"].log('Setting keys for encrypted media'); this._media.setMediaKeys(keysListItem.mediaKeys); this._hasSetMediaKeys = true; } } /** * @private */ ; _proto._generateRequestWithPreferredKeySession = function _generateRequestWithPreferredKeySession(initDataType, initData) { var _this6 = this; // FIXME: see if we can/want/need-to really to deal with several potential key-sessions? var keysListItem = this._mediaKeysList[0]; if (!keysListItem) { logger["logger"].error('Fatal: Media is encrypted but not any key-system access has been obtained yet'); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_ACCESS, fatal: true }); return; } if (keysListItem.mediaKeysSessionInitialized) { logger["logger"].warn('Key-Session already initialized but requested again'); return; } var keySession = keysListItem.mediaKeysSession; if (!keySession) { logger["logger"].error('Fatal: Media is encrypted but no key-session existing'); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_SESSION, fatal: true }); return; } // initData is null if the media is not CORS-same-origin if (!initData) { logger["logger"].warn('Fatal: initData required for generating a key session is null'); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_INIT_DATA, fatal: true }); return; } logger["logger"].log("Generating key-session request for \"" + initDataType + "\" init data type"); keysListItem.mediaKeysSessionInitialized = true; keySession.generateRequest(initDataType, initData).then(function () { logger["logger"].debug('Key-session generation succeeded'); }).catch(function (err) { logger["logger"].error('Error generating key-session request:', err); _this6.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_SESSION, fatal: false }); }); } /** * @private * @param {string} url License server URL * @param {ArrayBuffer} keyMessage Message data issued by key-system * @param {function} callback Called when XHR has succeeded * @returns {XMLHttpRequest} Unsent (but opened state) XHR object * @throws if XMLHttpRequest construction failed */ ; _proto._createLicenseXhr = function _createLicenseXhr(url, keyMessage, callback) { var xhr = new XMLHttpRequest(); var licenseXhrSetup = this._licenseXhrSetup; try { if (licenseXhrSetup) { try { licenseXhrSetup(xhr, url); } catch (e) { // let's try to open before running setup xhr.open('POST', url, true); licenseXhrSetup(xhr, url); } } // if licenseXhrSetup did not yet call open, let's do it now if (!xhr.readyState) { xhr.open('POST', url, true); } } catch (e) { // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS throw new Error("issue setting up KeySystem license XHR " + e); } // Because we set responseType to ArrayBuffer here, callback is typed as handling only array buffers xhr.responseType = 'arraybuffer'; xhr.onreadystatechange = this._onLicenseRequestReadyStageChange.bind(this, xhr, url, keyMessage, callback); return xhr; } /** * @private * @param {XMLHttpRequest} xhr * @param {string} url License server URL * @param {ArrayBuffer} keyMessage Message data issued by key-system * @param {function} callback Called when XHR has succeeded */ ; _proto._onLicenseRequestReadyStageChange = function _onLicenseRequestReadyStageChange(xhr, url, keyMessage, callback) { switch (xhr.readyState) { case 4: if (xhr.status === 200) { this._requestLicenseFailureCount = 0; logger["logger"].log('License request succeeded'); if (xhr.responseType !== 'arraybuffer') { logger["logger"].warn('xhr response type was not set to the expected arraybuffer for license request'); } callback(xhr.response); } else { logger["logger"].error("License Request XHR failed (" + url + "). Status: " + xhr.status + " (" + xhr.statusText + ")"); this._requestLicenseFailureCount++; if (this._requestLicenseFailureCount > MAX_LICENSE_REQUEST_FAILURES) { this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_LICENSE_REQUEST_FAILED, fatal: true }); return; } var attemptsLeft = MAX_LICENSE_REQUEST_FAILURES - this._requestLicenseFailureCount + 1; logger["logger"].warn("Retrying license request, " + attemptsLeft + " attempts left"); this._requestLicense(keyMessage, callback); } break; } } /** * @private * @param {MediaKeysListItem} keysListItem * @param {ArrayBuffer} keyMessage * @returns {ArrayBuffer} Challenge data posted to license server * @throws if KeySystem is unsupported */ ; _proto._generateLicenseRequestChallenge = function _generateLicenseRequestChallenge(keysListItem, keyMessage) { switch (keysListItem.mediaKeySystemDomain) { // case KeySystems.PLAYREADY: // from https://github.com/MicrosoftEdge/Demos/blob/master/eme/scripts/demo.js /* if (this.licenseType !== this.LICENSE_TYPE_WIDEVINE) { // For PlayReady CDMs, we need to dig the Challenge out of the XML. var keyMessageXml = new DOMParser().parseFromString(String.fromCharCode.apply(null, new Uint16Array(keyMessage)), 'application/xml'); if (keyMessageXml.getElementsByTagName('Challenge')[0]) { challenge = atob(keyMessageXml.getElementsByTagName('Challenge')[0].childNodes[0].nodeValue); } else { throw 'Cannot find <Challenge> in key message'; } var headerNames = keyMessageXml.getElementsByTagName('name'); var headerValues = keyMessageXml.getElementsByTagName('value'); if (headerNames.length !== headerValues.length) { throw 'Mismatched header <name>/<value> pair in key message'; } for (var i = 0; i < headerNames.length; i++) { xhr.setRequestHeader(headerNames[i].childNodes[0].nodeValue, headerValues[i].childNodes[0].nodeValue); } } break; */ case KeySystems.WIDEVINE: // For Widevine CDMs, the challenge is the keyMessage. return keyMessage; } throw new Error("unsupported key-system: " + keysListItem.mediaKeySystemDomain); } /** * @private * @param keyMessage * @param callback */ ; _proto._requestLicense = function _requestLicense(keyMessage, callback) { logger["logger"].log('Requesting content license for key-system'); var keysListItem = this._mediaKeysList[0]; if (!keysListItem) { logger["logger"].error('Fatal error: Media is encrypted but no key-system access has been obtained yet'); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_NO_ACCESS, fatal: true }); return; } try { var _url = this.getLicenseServerUrl(keysListItem.mediaKeySystemDomain); var _xhr = this._createLicenseXhr(_url, keyMessage, callback); logger["logger"].log("Sending license request to URL: " + _url); var challenge = this._generateLicenseRequestChallenge(keysListItem, keyMessage); _xhr.send(challenge); } catch (e) { logger["logger"].error("Failure requesting DRM license: " + e); this.hls.trigger(events["default"].ERROR, { type: errors["ErrorTypes"].KEY_SYSTEM_ERROR, details: errors["ErrorDetails"].KEY_SYSTEM_LICENSE_REQUEST_FAILED, fatal: true }); } }; _proto.onMediaAttached = function onMediaAttached(data) { if (!this._emeEnabled) { return; } var media = data.media; // keep reference of media this._media = media; media.addEventListener('encrypted', this._onMediaEncrypted); }; _proto.onMediaDetached = function onMediaDetached() { var media = this._media; var mediaKeysList = this._mediaKeysList; if (!media) { return; } media.removeEventListener('encrypted', this._onMediaEncrypted); this._media = null; this._mediaKeysList = []; // Close all sessions and remove media keys from the video element. Promise.all(mediaKeysList.map(function (mediaKeysListItem) { if (mediaKeysListItem.mediaKeysSession) { return mediaKeysListItem.mediaKeysSession.close().catch(function () {// Ignore errors when closing the sessions. Closing a session that // generated no key requests will throw an error. }); } })).then(function () { return media.setMediaKeys(null); }).catch(function () {// Ignore any failures while removing media keys from the video element. }); } // TODO: Use manifest types here when they are defined ; _proto.onManifestParsed = function onManifestParsed(data) { if (!this._emeEnabled) { return; } var audioCodecs = data.levels.map(function (level) { return level.audioCodec; }); var videoCodecs = data.levels.map(function (level) { return level.videoCodec; }); this._attemptKeySystemAccess(KeySystems.WIDEVINE, audioCodecs, videoCodecs); }; eme_controller_createClass(EMEController, [{ key: "requestMediaKeySystemAccess", get: function get() { if (!this._requestMediaKeySystemAccess) { throw new Error('No requestMediaKeySystemAccess function configured'); } return this._requestMediaKeySystemAccess; } }]); return EMEController; }(event_handler); /* harmony default export */ var eme_controller = (eme_controller_EMEController); // CONCATENATED MODULE: ./src/config.ts function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * HLS config */ // import FetchLoader from './utils/fetch-loader'; // If possible, keep hlsDefaultConfig shallow // It is cloned whenever a new Hls instance is created, by keeping the config // shallow the properties are cloned, and we don't end up manipulating the default var hlsDefaultConfig = _objectSpread(_objectSpread({ autoStartLoad: true, // used by stream-controller startPosition: -1, // used by stream-controller defaultAudioCodec: void 0, // used by stream-controller debug: false, // used by logger capLevelOnFPSDrop: false, // used by fps-controller capLevelToPlayerSize: false, // used by cap-level-controller initialLiveManifestSize: 1, // used by stream-controller maxBufferLength: 30, // used by stream-controller maxBufferSize: 60 * 1000 * 1000, // used by stream-controller maxBufferHole: 0.5, // used by stream-controller lowBufferWatchdogPeriod: 0.5, // used by stream-controller highBufferWatchdogPeriod: 3, // used by stream-controller nudgeOffset: 0.1, // used by stream-controller nudgeMaxRetry: 3, // used by stream-controller maxFragLookUpTolerance: 0.25, // used by stream-controller liveSyncDurationCount: 3, // used by stream-controller liveMaxLatencyDurationCount: Infinity, // used by stream-controller liveSyncDuration: void 0, // used by stream-controller liveMaxLatencyDuration: void 0, // used by stream-controller liveDurationInfinity: false, // used by buffer-controller liveBackBufferLength: Infinity, // used by buffer-controller maxMaxBufferLength: 600, // used by stream-controller enableWorker: true, // used by demuxer enableSoftwareAES: true, // used by decrypter manifestLoadingTimeOut: 10000, // used by playlist-loader manifestLoadingMaxRetry: 1, // used by playlist-loader manifestLoadingRetryDelay: 1000, // used by playlist-loader manifestLoadingMaxRetryTimeout: 64000, // used by playlist-loader startLevel: void 0, // used by level-controller levelLoadingTimeOut: 10000, // used by playlist-loader levelLoadingMaxRetry: 4, // used by playlist-loader levelLoadingRetryDelay: 1000, // used by playlist-loader levelLoadingMaxRetryTimeout: 64000, // used by playlist-loader fragLoadingTimeOut: 20000, // used by fragment-loader fragLoadingMaxRetry: 6, // used by fragment-loader fragLoadingRetryDelay: 1000, // used by fragment-loader fragLoadingMaxRetryTimeout: 64000, // used by fragment-loader startFragPrefetch: false, // used by stream-controller fpsDroppedMonitoringPeriod: 5000, // used by fps-controller fpsDroppedMonitoringThreshold: 0.2, // used by fps-controller appendErrorMaxRetry: 3, // used by buffer-controller loader: xhr_loader, // loader: FetchLoader, fLoader: void 0, // used by fragment-loader pLoader: void 0, // used by playlist-loader xhrSetup: void 0, // used by xhr-loader licenseXhrSetup: void 0, // used by eme-controller // fetchSetup: void 0, abrController: abr_controller, bufferController: buffer_controller, capLevelController: cap_level_controller, fpsController: fps_controller, stretchShortVideoTrack: false, // used by mp4-remuxer maxAudioFramesDrift: 1, // used by mp4-remuxer forceKeyFrameOnDiscontinuity: true, // used by ts-demuxer abrEwmaFastLive: 3, // used by abr-controller abrEwmaSlowLive: 9, // used by abr-controller abrEwmaFastVoD: 3, // used by abr-controller abrEwmaSlowVoD: 9, // used by abr-controller abrEwmaDefaultEstimate: 5e5, // 500 kbps // used by abr-controller abrBandWidthFactor: 0.95, // used by abr-controller abrBandWidthUpFactor: 0.7, // used by abr-controller abrMaxWithRealBitrate: false, // used by abr-controller maxStarvationDelay: 4, // used by abr-controller maxLoadingDelay: 4, // used by abr-controller minAutoBitrate: 0, // used by hls emeEnabled: false, // used by eme-controller widevineLicenseUrl: void 0, // used by eme-controller drmSystemOptions: {}, // used by eme-controller requestMediaKeySystemAccessFunc: requestMediaKeySystemAccess, // used by eme-controller testBandwidth: true }, timelineConfig()), {}, { subtitleStreamController: true ? subtitle_stream_controller_SubtitleStreamController : undefined, subtitleTrackController: true ? subtitle_track_controller : undefined, timelineController: true ? timeline_controller : undefined, audioStreamController: true ? audio_stream_controller : undefined, audioTrackController: true ? audio_track_controller : undefined, emeController: true ? eme_controller : undefined }); function timelineConfig() { return { cueHandler: cues_namespaceObject, // used by timeline-controller enableCEA708Captions: true, // used by timeline-controller enableWebVTT: true, // used by timeline-controller captionsTextTrack1Label: 'English', // used by timeline-controller captionsTextTrack1LanguageCode: 'en', // used by timeline-controller captionsTextTrack2Label: 'Spanish', // used by timeline-controller captionsTextTrack2LanguageCode: 'es', // used by timeline-controller captionsTextTrack3Label: 'Unknown CC', // used by timeline-controller captionsTextTrack3LanguageCode: '', // used by timeline-controller captionsTextTrack4Label: 'Unknown CC', // used by timeline-controller captionsTextTrack4LanguageCode: '', // used by timeline-controller renderTextTracksNatively: true }; } // CONCATENATED MODULE: ./src/hls.ts function hls_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function hls_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { hls_ownKeys(Object(source), true).forEach(function (key) { hls_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { hls_ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function hls_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function hls_assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function hls_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function hls_createClass(Constructor, protoProps, staticProps) { if (protoProps) hls_defineProperties(Constructor.prototype, protoProps); if (staticProps) hls_defineProperties(Constructor, staticProps); return Constructor; } function hls_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } /** * @module Hls * @class * @constructor */ var hls_Hls = /*#__PURE__*/function (_Observer) { hls_inheritsLoose(Hls, _Observer); /** * @type {boolean} */ Hls.isSupported = function isSupported() { return is_supported_isSupported(); } /** * @type {HlsEvents} */ ; hls_createClass(Hls, null, [{ key: "version", /** * @type {string} */ get: function get() { return "0.14.17"; } }, { key: "Events", get: function get() { return events["default"]; } /** * @type {HlsErrorTypes} */ }, { key: "ErrorTypes", get: function get() { return errors["ErrorTypes"]; } /** * @type {HlsErrorDetails} */ }, { key: "ErrorDetails", get: function get() { return errors["ErrorDetails"]; } /** * @type {HlsConfig} */ }, { key: "DefaultConfig", get: function get() { if (!Hls.defaultConfig) { return hlsDefaultConfig; } return Hls.defaultConfig; } /** * @type {HlsConfig} */ , set: function set(defaultConfig) { Hls.defaultConfig = defaultConfig; } /** * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`. * * @constructs Hls * @param {HlsConfig} config */ }]); function Hls(userConfig) { var _this; if (userConfig === void 0) { userConfig = {}; } _this = _Observer.call(this) || this; _this.config = void 0; _this._autoLevelCapping = void 0; _this.abrController = void 0; _this.capLevelController = void 0; _this.levelController = void 0; _this.streamController = void 0; _this.networkControllers = void 0; _this.audioTrackController = void 0; _this.subtitleTrackController = void 0; _this.emeController = void 0; _this.coreComponents = void 0; _this.media = null; _this.url = null; var defaultConfig = Hls.DefaultConfig; if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) { throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration'); } // Shallow clone _this.config = hls_objectSpread(hls_objectSpread({}, defaultConfig), userConfig); var _assertThisInitialize = hls_assertThisInitialized(_this), config = _assertThisInitialize.config; if (config.liveMaxLatencyDurationCount !== void 0 && config.liveMaxLatencyDurationCount <= config.liveSyncDurationCount) { throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); } if (config.liveMaxLatencyDuration !== void 0 && (config.liveSyncDuration === void 0 || config.liveMaxLatencyDuration <= config.liveSyncDuration)) { throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); } Object(logger["enableLogs"])(config.debug); _this._autoLevelCapping = -1; // core controllers and network loaders /** * @member {AbrController} abrController */ var abrController = _this.abrController = new config.abrController(hls_assertThisInitialized(_this)); // eslint-disable-line new-cap var bufferController = new config.bufferController(hls_assertThisInitialized(_this)); // eslint-disable-line new-cap var capLevelController = _this.capLevelController = new config.capLevelController(hls_assertThisInitialized(_this)); // eslint-disable-line new-cap var fpsController = new config.fpsController(hls_assertThisInitialized(_this)); // eslint-disable-line new-cap var playListLoader = new playlist_loader(hls_assertThisInitialized(_this)); var fragmentLoader = new fragment_loader(hls_assertThisInitialized(_this)); var keyLoader = new key_loader(hls_assertThisInitialized(_this)); var id3TrackController = new id3_track_controller(hls_assertThisInitialized(_this)); // network controllers /** * @member {LevelController} levelController */ var levelController = _this.levelController = new level_controller_LevelController(hls_assertThisInitialized(_this)); // FIXME: FragmentTracker must be defined before StreamController because the order of event handling is important var fragmentTracker = new fragment_tracker_FragmentTracker(hls_assertThisInitialized(_this)); /** * @member {StreamController} streamController */ var streamController = _this.streamController = new stream_controller(hls_assertThisInitialized(_this), fragmentTracker); var networkControllers = [levelController, streamController]; // optional audio stream controller /** * @var {ICoreComponent | Controller} */ var Controller = config.audioStreamController; if (Controller) { networkControllers.push(new Controller(hls_assertThisInitialized(_this), fragmentTracker)); } /** * @member {INetworkController[]} networkControllers */ _this.networkControllers = networkControllers; /** * @var {ICoreComponent[]} */ var coreComponents = [playListLoader, fragmentLoader, keyLoader, abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker]; // optional audio track and subtitle controller Controller = config.audioTrackController; if (Controller) { var audioTrackController = new Controller(hls_assertThisInitialized(_this)); /** * @member {AudioTrackController} audioTrackController */ _this.audioTrackController = audioTrackController; coreComponents.push(audioTrackController); } Controller = config.subtitleTrackController; if (Controller) { var subtitleTrackController = new Controller(hls_assertThisInitialized(_this)); /** * @member {SubtitleTrackController} subtitleTrackController */ _this.subtitleTrackController = subtitleTrackController; networkControllers.push(subtitleTrackController); } Controller = config.emeController; if (Controller) { var emeController = new Controller(hls_assertThisInitialized(_this)); /** * @member {EMEController} emeController */ _this.emeController = emeController; coreComponents.push(emeController); } // optional subtitle controllers Controller = config.subtitleStreamController; if (Controller) { networkControllers.push(new Controller(hls_assertThisInitialized(_this), fragmentTracker)); } Controller = config.timelineController; if (Controller) { coreComponents.push(new Controller(hls_assertThisInitialized(_this))); } /** * @member {ICoreComponent[]} */ _this.coreComponents = coreComponents; return _this; } /** * Dispose of the instance */ var _proto = Hls.prototype; _proto.destroy = function destroy() { logger["logger"].log('destroy'); this.trigger(events["default"].DESTROYING); this.detachMedia(); this.coreComponents.concat(this.networkControllers).forEach(function (component) { component.destroy(); }); this.url = null; this.removeAllListeners(); this._autoLevelCapping = -1; } /** * Attach a media element * @param {HTMLMediaElement} media */ ; _proto.attachMedia = function attachMedia(media) { logger["logger"].log('attachMedia'); this.media = media; this.trigger(events["default"].MEDIA_ATTACHING, { media: media }); } /** * Detach from the media */ ; _proto.detachMedia = function detachMedia() { logger["logger"].log('detachMedia'); this.trigger(events["default"].MEDIA_DETACHING); this.media = null; } /** * Set the source URL. Can be relative or absolute. * @param {string} url */ ; _proto.loadSource = function loadSource(url) { url = url_toolkit["buildAbsoluteURL"](window.location.href, url, { alwaysNormalize: true }); logger["logger"].log("loadSource:" + url); this.url = url; // when attaching to a source URL, trigger a playlist load this.trigger(events["default"].MANIFEST_LOADING, { url: url }); } /** * Start loading data from the stream source. * Depending on default config, client starts loading automatically when a source is set. * * @param {number} startPosition Set the start position to stream from * @default -1 None (from earliest point) */ ; _proto.startLoad = function startLoad(startPosition) { if (startPosition === void 0) { startPosition = -1; } logger["logger"].log("startLoad(" + startPosition + ")"); this.networkControllers.forEach(function (controller) { controller.startLoad(startPosition); }); } /** * Stop loading of any stream data. */ ; _proto.stopLoad = function stopLoad() { logger["logger"].log('stopLoad'); this.networkControllers.forEach(function (controller) { controller.stopLoad(); }); } /** * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1) */ ; _proto.swapAudioCodec = function swapAudioCodec() { logger["logger"].log('swapAudioCodec'); this.streamController.swapAudioCodec(); } /** * When the media-element fails, this allows to detach and then re-attach it * as one call (convenience method). * * Automatic recovery of media-errors by this process is configurable. */ ; _proto.recoverMediaError = function recoverMediaError() { logger["logger"].log('recoverMediaError'); var media = this.media; this.detachMedia(); if (media) { this.attachMedia(media); } } /** * Remove a loaded level from the list of levels, or a level url in from a list of redundant level urls. * This can be used to remove a rendition or playlist url that errors frequently from the list of levels that a user * or hls.js can choose from. * * @param levelIndex {number} The quality level index to of the level to remove * @param urlId {number} The quality level url index in the case that fallback levels are available. Defaults to 0. */ ; _proto.removeLevel = function removeLevel(levelIndex, urlId) { if (urlId === void 0) { urlId = 0; } this.levelController.removeLevel(levelIndex, urlId); } /** * @type {QualityLevel[]} */ // todo(typescript-levelController) ; hls_createClass(Hls, [{ key: "levels", get: function get() { return this.levelController.levels; } /** * Index of quality level currently played * @type {number} */ }, { key: "currentLevel", get: function get() { return this.streamController.currentLevel; } /** * Set quality level index immediately . * This will flush the current buffer to replace the quality asap. * That means playback will interrupt at least shortly to re-buffer and re-sync eventually. * @param newLevel {number} -1 for automatic level selection */ , set: function set(newLevel) { logger["logger"].log("set currentLevel:" + newLevel); this.loadLevel = newLevel; this.streamController.immediateLevelSwitch(); } /** * Index of next quality level loaded as scheduled by stream controller. * @type {number} */ }, { key: "nextLevel", get: function get() { return this.streamController.nextLevel; } /** * Set quality level index for next loaded data. * This will switch the video quality asap, without interrupting playback. * May abort current loading of data, and flush parts of buffer (outside currently played fragment region). * @type {number} -1 for automatic level selection */ , set: function set(newLevel) { logger["logger"].log("set nextLevel:" + newLevel); this.levelController.manualLevel = newLevel; this.streamController.nextLevelSwitch(); } /** * Return the quality level of the currently or last (of none is loaded currently) segment * @type {number} */ }, { key: "loadLevel", get: function get() { return this.levelController.level; } /** * Set quality level index for next loaded data in a conservative way. * This will switch the quality without flushing, but interrupt current loading. * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer. * @type {number} newLevel -1 for automatic level selection */ , set: function set(newLevel) { logger["logger"].log("set loadLevel:" + newLevel); this.levelController.manualLevel = newLevel; } /** * get next quality level loaded * @type {number} */ }, { key: "nextLoadLevel", get: function get() { return this.levelController.nextLoadLevel; } /** * Set quality level of next loaded segment in a fully "non-destructive" way. * Same as `loadLevel` but will wait for next switch (until current loading is done). * @type {number} level */ , set: function set(level) { this.levelController.nextLoadLevel = level; } /** * Return "first level": like a default level, if not set, * falls back to index of first level referenced in manifest * @type {number} */ }, { key: "firstLevel", get: function get() { return Math.max(this.levelController.firstLevel, this.minAutoLevel); } /** * Sets "first-level", see getter. * @type {number} */ , set: function set(newLevel) { logger["logger"].log("set firstLevel:" + newLevel); this.levelController.firstLevel = newLevel; } /** * Return start level (level of first fragment that will be played back) * if not overrided by user, first level appearing in manifest will be used as start level * if -1 : automatic start level selection, playback will start from level matching download bandwidth * (determined from download of first segment) * @type {number} */ }, { key: "startLevel", get: function get() { return this.levelController.startLevel; } /** * set start level (level of first fragment that will be played back) * if not overrided by user, first level appearing in manifest will be used as start level * if -1 : automatic start level selection, playback will start from level matching download bandwidth * (determined from download of first segment) * @type {number} newLevel */ , set: function set(newLevel) { logger["logger"].log("set startLevel:" + newLevel); // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel if (newLevel !== -1) { newLevel = Math.max(newLevel, this.minAutoLevel); } this.levelController.startLevel = newLevel; } /** * set dynamically set capLevelToPlayerSize against (`CapLevelController`) * * @type {boolean} */ }, { key: "capLevelToPlayerSize", set: function set(shouldStartCapping) { var newCapLevelToPlayerSize = !!shouldStartCapping; if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) { if (newCapLevelToPlayerSize) { this.capLevelController.startCapping(); // If capping occurs, nextLevelSwitch will happen based on size. } else { this.capLevelController.stopCapping(); this.autoLevelCapping = -1; this.streamController.nextLevelSwitch(); // Now we're uncapped, get the next level asap. } this.config.capLevelToPlayerSize = newCapLevelToPlayerSize; } } /** * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) * @type {number} */ }, { key: "autoLevelCapping", get: function get() { return this._autoLevelCapping; } /** * get bandwidth estimate * @type {number} */ , /** * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) * @type {number} */ set: function set(newLevel) { logger["logger"].log("set autoLevelCapping:" + newLevel); this._autoLevelCapping = newLevel; } /** * True when automatic level selection enabled * @type {boolean} */ }, { key: "bandwidthEstimate", get: function get() { var bwEstimator = this.abrController._bwEstimator; return bwEstimator ? bwEstimator.getEstimate() : NaN; } }, { key: "autoLevelEnabled", get: function get() { return this.levelController.manualLevel === -1; } /** * Level set manually (if any) * @type {number} */ }, { key: "manualLevel", get: function get() { return this.levelController.manualLevel; } /** * min level selectable in auto mode according to config.minAutoBitrate * @type {number} */ }, { key: "minAutoLevel", get: function get() { var levels = this.levels, minAutoBitrate = this.config.minAutoBitrate; var len = levels ? levels.length : 0; for (var i = 0; i < len; i++) { var levelNextBitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate; if (levelNextBitrate > minAutoBitrate) { return i; } } return 0; } /** * max level selectable in auto mode according to autoLevelCapping * @type {number} */ }, { key: "maxAutoLevel", get: function get() { var levels = this.levels, autoLevelCapping = this.autoLevelCapping; var maxAutoLevel; if (autoLevelCapping === -1 && levels && levels.length) { maxAutoLevel = levels.length - 1; } else { maxAutoLevel = autoLevelCapping; } return maxAutoLevel; } /** * next automatically selected quality level * @type {number} */ }, { key: "nextAutoLevel", get: function get() { // ensure next auto level is between min and max auto level return Math.min(Math.max(this.abrController.nextAutoLevel, this.minAutoLevel), this.maxAutoLevel); } /** * this setter is used to force next auto level. * this is useful to force a switch down in auto mode: * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example) * forced value is valid for one fragment. upon succesful frag loading at forced level, * this value will be resetted to -1 by ABR controller. * @type {number} */ , set: function set(nextLevel) { this.abrController.nextAutoLevel = Math.max(this.minAutoLevel, nextLevel); } /** * @type {AudioTrack[]} */ // todo(typescript-audioTrackController) }, { key: "audioTracks", get: function get() { var audioTrackController = this.audioTrackController; return audioTrackController ? audioTrackController.audioTracks : []; } /** * index of the selected audio track (index in audio track lists) * @type {number} */ }, { key: "audioTrack", get: function get() { var audioTrackController = this.audioTrackController; return audioTrackController ? audioTrackController.audioTrack : -1; } /** * selects an audio track, based on its index in audio track lists * @type {number} */ , set: function set(audioTrackId) { var audioTrackController = this.audioTrackController; if (audioTrackController) { audioTrackController.audioTrack = audioTrackId; } } /** * @type {Seconds} */ }, { key: "liveSyncPosition", get: function get() { return this.streamController.liveSyncPosition; } /** * get alternate subtitle tracks list from playlist * @type {SubtitleTrack[]} */ // todo(typescript-subtitleTrackController) }, { key: "subtitleTracks", get: function get() { var subtitleTrackController = this.subtitleTrackController; return subtitleTrackController ? subtitleTrackController.subtitleTracks : []; } /** * index of the selected subtitle track (index in subtitle track lists) * @type {number} */ }, { key: "subtitleTrack", get: function get() { var subtitleTrackController = this.subtitleTrackController; return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1; } /** * select an subtitle track, based on its index in subtitle track lists * @type {number} */ , set: function set(subtitleTrackId) { var subtitleTrackController = this.subtitleTrackController; if (subtitleTrackController) { subtitleTrackController.subtitleTrack = subtitleTrackId; } } /** * @type {boolean} */ }, { key: "subtitleDisplay", get: function get() { var subtitleTrackController = this.subtitleTrackController; return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false; } /** * Enable/disable subtitle display rendering * @type {boolean} */ , set: function set(value) { var subtitleTrackController = this.subtitleTrackController; if (subtitleTrackController) { subtitleTrackController.subtitleDisplay = value; } } }]); return Hls; }(Observer); hls_Hls.defaultConfig = void 0; /***/ }), /***/ "./src/polyfills/number.js": /*!*********************************!*\ !*** ./src/polyfills/number.js ***! \*********************************/ /*! exports provided: isFiniteNumber, MAX_SAFE_INTEGER */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFiniteNumber", function() { return isFiniteNumber; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MAX_SAFE_INTEGER", function() { return MAX_SAFE_INTEGER; }); var isFiniteNumber = Number.isFinite || function (value) { return typeof value === 'number' && isFinite(value); }; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; /***/ }), /***/ "./src/utils/get-self-scope.js": /*!*************************************!*\ !*** ./src/utils/get-self-scope.js ***! \*************************************/ /*! exports provided: getSelfScope */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getSelfScope", function() { return getSelfScope; }); function getSelfScope() { // see https://stackoverflow.com/a/11237259/589493 if (typeof window === 'undefined') { /* eslint-disable-next-line no-undef */ return self; } else { return window; } } /***/ }), /***/ "./src/utils/logger.js": /*!*****************************!*\ !*** ./src/utils/logger.js ***! \*****************************/ /*! exports provided: enableLogs, logger */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "enableLogs", function() { return enableLogs; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "logger", function() { return logger; }); /* harmony import */ var _get_self_scope__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./get-self-scope */ "./src/utils/get-self-scope.js"); function noop() {} var fakeLogger = { trace: noop, debug: noop, log: noop, warn: noop, info: noop, error: noop }; var exportedLogger = fakeLogger; // let lastCallTime; // function formatMsgWithTimeInfo(type, msg) { // const now = Date.now(); // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; // lastCallTime = now; // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; // return msg; // } function formatMsg(type, msg) { msg = '[' + type + '] > ' + msg; return msg; } var global = Object(_get_self_scope__WEBPACK_IMPORTED_MODULE_0__["getSelfScope"])(); function consolePrintFn(type) { var func = global.console[type]; if (func) { return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (args[0]) { args[0] = formatMsg(type, args[0]); } func.apply(global.console, args); }; } return noop; } function exportLoggerFunctions(debugConfig) { for (var _len2 = arguments.length, functions = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { functions[_key2 - 1] = arguments[_key2]; } functions.forEach(function (type) { exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type); }); } var enableLogs = function enableLogs(debugConfig) { // check that console is available if (global.console && debugConfig === true || typeof debugConfig === 'object') { exportLoggerFunctions(debugConfig, // Remove out from list here to hard-disable a log-level // 'trace', 'debug', 'log', 'info', 'warn', 'error'); // Some browsers don't allow to use bind on console object anyway // fallback to default if needed try { exportedLogger.log(); } catch (e) { exportedLogger = fakeLogger; } } else { exportedLogger = fakeLogger; } }; var logger = exportedLogger; /***/ }) /******/ })["default"]; }); },{}],17:[function(require,module,exports){ (function(window, factory) { var lazySizes = factory(window, window.document, Date); window.lazySizes = lazySizes; if(typeof module == 'object' && module.exports){ module.exports = lazySizes; } }(typeof window != 'undefined' ? window : {}, function l(window, document, Date) { // Pass in the windoe Date function also for SSR because the Date class can be lost 'use strict'; /*jshint eqnull:true */ var lazysizes, lazySizesCfg; (function(){ var prop; var lazySizesDefaults = { lazyClass: 'lazyload', loadedClass: 'lazyloaded', loadingClass: 'lazyloading', preloadClass: 'lazypreload', errorClass: 'lazyerror', //strictClass: 'lazystrict', autosizesClass: 'lazyautosizes', srcAttr: 'data-src', srcsetAttr: 'data-srcset', sizesAttr: 'data-sizes', //preloadAfterLoad: false, minSize: 40, customMedia: {}, init: true, expFactor: 1.5, hFac: 0.8, loadMode: 2, loadHidden: true, ricTimeout: 0, throttleDelay: 125, }; lazySizesCfg = window.lazySizesConfig || window.lazysizesConfig || {}; for(prop in lazySizesDefaults){ if(!(prop in lazySizesCfg)){ lazySizesCfg[prop] = lazySizesDefaults[prop]; } } })(); if (!document || !document.getElementsByClassName) { return { init: function () {}, cfg: lazySizesCfg, noSupport: true, }; } var docElem = document.documentElement; var supportPicture = window.HTMLPictureElement; var _addEventListener = 'addEventListener'; var _getAttribute = 'getAttribute'; /** * Update to bind to window because 'this' becomes null during SSR * builds. */ var addEventListener = window[_addEventListener].bind(window); var setTimeout = window.setTimeout; var requestAnimationFrame = window.requestAnimationFrame || setTimeout; var requestIdleCallback = window.requestIdleCallback; var regPicture = /^picture$/i; var loadEvents = ['load', 'error', 'lazyincluded', '_lazyloaded']; var regClassCache = {}; var forEach = Array.prototype.forEach; var hasClass = function(ele, cls) { if(!regClassCache[cls]){ regClassCache[cls] = new RegExp('(\\s|^)'+cls+'(\\s|$)'); } return regClassCache[cls].test(ele[_getAttribute]('class') || '') && regClassCache[cls]; }; var addClass = function(ele, cls) { if (!hasClass(ele, cls)){ ele.setAttribute('class', (ele[_getAttribute]('class') || '').trim() + ' ' + cls); } }; var removeClass = function(ele, cls) { var reg; if ((reg = hasClass(ele,cls))) { ele.setAttribute('class', (ele[_getAttribute]('class') || '').replace(reg, ' ')); } }; var addRemoveLoadEvents = function(dom, fn, add){ var action = add ? _addEventListener : 'removeEventListener'; if(add){ addRemoveLoadEvents(dom, fn); } loadEvents.forEach(function(evt){ dom[action](evt, fn); }); }; var triggerEvent = function(elem, name, detail, noBubbles, noCancelable){ var event = document.createEvent('Event'); if(!detail){ detail = {}; } detail.instance = lazysizes; event.initEvent(name, !noBubbles, !noCancelable); event.detail = detail; elem.dispatchEvent(event); return event; }; var updatePolyfill = function (el, full){ var polyfill; if( !supportPicture && ( polyfill = (window.picturefill || lazySizesCfg.pf) ) ){ if(full && full.src && !el[_getAttribute]('srcset')){ el.setAttribute('srcset', full.src); } polyfill({reevaluate: true, elements: [el]}); } else if(full && full.src){ el.src = full.src; } }; var getCSS = function (elem, style){ return (getComputedStyle(elem, null) || {})[style]; }; var getWidth = function(elem, parent, width){ width = width || elem.offsetWidth; while(width < lazySizesCfg.minSize && parent && !elem._lazysizesWidth){ width = parent.offsetWidth; parent = parent.parentNode; } return width; }; var rAF = (function(){ var running, waiting; var firstFns = []; var secondFns = []; var fns = firstFns; var run = function(){ var runFns = fns; fns = firstFns.length ? secondFns : firstFns; running = true; waiting = false; while(runFns.length){ runFns.shift()(); } running = false; }; var rafBatch = function(fn, queue){ if(running && !queue){ fn.apply(this, arguments); } else { fns.push(fn); if(!waiting){ waiting = true; (document.hidden ? setTimeout : requestAnimationFrame)(run); } } }; rafBatch._lsFlush = run; return rafBatch; })(); var rAFIt = function(fn, simple){ return simple ? function() { rAF(fn); } : function(){ var that = this; var args = arguments; rAF(function(){ fn.apply(that, args); }); } ; }; var throttle = function(fn){ var running; var lastTime = 0; var gDelay = lazySizesCfg.throttleDelay; var rICTimeout = lazySizesCfg.ricTimeout; var run = function(){ running = false; lastTime = Date.now(); fn(); }; var idleCallback = requestIdleCallback && rICTimeout > 49 ? function(){ requestIdleCallback(run, {timeout: rICTimeout}); if(rICTimeout !== lazySizesCfg.ricTimeout){ rICTimeout = lazySizesCfg.ricTimeout; } } : rAFIt(function(){ setTimeout(run); }, true) ; return function(isPriority){ var delay; if((isPriority = isPriority === true)){ rICTimeout = 33; } if(running){ return; } running = true; delay = gDelay - (Date.now() - lastTime); if(delay < 0){ delay = 0; } if(isPriority || delay < 9){ idleCallback(); } else { setTimeout(idleCallback, delay); } }; }; //based on http://modernjavascript.blogspot.de/2013/08/building-better-debounce.html var debounce = function(func) { var timeout, timestamp; var wait = 99; var run = function(){ timeout = null; func(); }; var later = function() { var last = Date.now() - timestamp; if (last < wait) { setTimeout(later, wait - last); } else { (requestIdleCallback || run)(run); } }; return function() { timestamp = Date.now(); if (!timeout) { timeout = setTimeout(later, wait); } }; }; var loader = (function(){ var preloadElems, isCompleted, resetPreloadingTimer, loadMode, started; var eLvW, elvH, eLtop, eLleft, eLright, eLbottom, isBodyHidden; var regImg = /^img$/i; var regIframe = /^iframe$/i; var supportScroll = ('onscroll' in window) && !(/(gle|ing)bot/.test(navigator.userAgent)); var shrinkExpand = 0; var currentExpand = 0; var isLoading = 0; var lowRuns = -1; var resetPreloading = function(e){ isLoading--; if(!e || isLoading < 0 || !e.target){ isLoading = 0; } }; var isVisible = function (elem) { if (isBodyHidden == null) { isBodyHidden = getCSS(document.body, 'visibility') == 'hidden'; } return isBodyHidden || !(getCSS(elem.parentNode, 'visibility') == 'hidden' && getCSS(elem, 'visibility') == 'hidden'); }; var isNestedVisible = function(elem, elemExpand){ var outerRect; var parent = elem; var visible = isVisible(elem); eLtop -= elemExpand; eLbottom += elemExpand; eLleft -= elemExpand; eLright += elemExpand; while(visible && (parent = parent.offsetParent) && parent != document.body && parent != docElem){ visible = ((getCSS(parent, 'opacity') || 1) > 0); if(visible && getCSS(parent, 'overflow') != 'visible'){ outerRect = parent.getBoundingClientRect(); visible = eLright > outerRect.left && eLleft < outerRect.right && eLbottom > outerRect.top - 1 && eLtop < outerRect.bottom + 1 ; } } return visible; }; var checkElements = function() { var eLlen, i, rect, autoLoadElem, loadedSomething, elemExpand, elemNegativeExpand, elemExpandVal, beforeExpandVal, defaultExpand, preloadExpand, hFac; var lazyloadElems = lazysizes.elements; if((loadMode = lazySizesCfg.loadMode) && isLoading < 8 && (eLlen = lazyloadElems.length)){ i = 0; lowRuns++; for(; i < eLlen; i++){ if(!lazyloadElems[i] || lazyloadElems[i]._lazyRace){continue;} if(!supportScroll || (lazysizes.prematureUnveil && lazysizes.prematureUnveil(lazyloadElems[i]))){unveilElement(lazyloadElems[i]);continue;} if(!(elemExpandVal = lazyloadElems[i][_getAttribute]('data-expand')) || !(elemExpand = elemExpandVal * 1)){ elemExpand = currentExpand; } if (!defaultExpand) { defaultExpand = (!lazySizesCfg.expand || lazySizesCfg.expand < 1) ? docElem.clientHeight > 500 && docElem.clientWidth > 500 ? 500 : 370 : lazySizesCfg.expand; lazysizes._defEx = defaultExpand; preloadExpand = defaultExpand * lazySizesCfg.expFactor; hFac = lazySizesCfg.hFac; isBodyHidden = null; if(currentExpand < preloadExpand && isLoading < 1 && lowRuns > 2 && loadMode > 2 && !document.hidden){ currentExpand = preloadExpand; lowRuns = 0; } else if(loadMode > 1 && lowRuns > 1 && isLoading < 6){ currentExpand = defaultExpand; } else { currentExpand = shrinkExpand; } } if(beforeExpandVal !== elemExpand){ eLvW = innerWidth + (elemExpand * hFac); elvH = innerHeight + elemExpand; elemNegativeExpand = elemExpand * -1; beforeExpandVal = elemExpand; } rect = lazyloadElems[i].getBoundingClientRect(); if ((eLbottom = rect.bottom) >= elemNegativeExpand && (eLtop = rect.top) <= elvH && (eLright = rect.right) >= elemNegativeExpand * hFac && (eLleft = rect.left) <= eLvW && (eLbottom || eLright || eLleft || eLtop) && (lazySizesCfg.loadHidden || isVisible(lazyloadElems[i])) && ((isCompleted && isLoading < 3 && !elemExpandVal && (loadMode < 3 || lowRuns < 4)) || isNestedVisible(lazyloadElems[i], elemExpand))){ unveilElement(lazyloadElems[i]); loadedSomething = true; if(isLoading > 9){break;} } else if(!loadedSomething && isCompleted && !autoLoadElem && isLoading < 4 && lowRuns < 4 && loadMode > 2 && (preloadElems[0] || lazySizesCfg.preloadAfterLoad) && (preloadElems[0] || (!elemExpandVal && ((eLbottom || eLright || eLleft || eLtop) || lazyloadElems[i][_getAttribute](lazySizesCfg.sizesAttr) != 'auto')))){ autoLoadElem = preloadElems[0] || lazyloadElems[i]; } } if(autoLoadElem && !loadedSomething){ unveilElement(autoLoadElem); } } }; var throttledCheckElements = throttle(checkElements); var switchLoadingClass = function(e){ var elem = e.target; if (elem._lazyCache) { delete elem._lazyCache; return; } resetPreloading(e); addClass(elem, lazySizesCfg.loadedClass); removeClass(elem, lazySizesCfg.loadingClass); addRemoveLoadEvents(elem, rafSwitchLoadingClass); triggerEvent(elem, 'lazyloaded'); }; var rafedSwitchLoadingClass = rAFIt(switchLoadingClass); var rafSwitchLoadingClass = function(e){ rafedSwitchLoadingClass({target: e.target}); }; var changeIframeSrc = function(elem, src){ try { elem.contentWindow.location.replace(src); } catch(e){ elem.src = src; } }; var handleSources = function(source){ var customMedia; var sourceSrcset = source[_getAttribute](lazySizesCfg.srcsetAttr); if( (customMedia = lazySizesCfg.customMedia[source[_getAttribute]('data-media') || source[_getAttribute]('media')]) ){ source.setAttribute('media', customMedia); } if(sourceSrcset){ source.setAttribute('srcset', sourceSrcset); } }; var lazyUnveil = rAFIt(function (elem, detail, isAuto, sizes, isImg){ var src, srcset, parent, isPicture, event, firesLoad; if(!(event = triggerEvent(elem, 'lazybeforeunveil', detail)).defaultPrevented){ if(sizes){ if(isAuto){ addClass(elem, lazySizesCfg.autosizesClass); } else { elem.setAttribute('sizes', sizes); } } srcset = elem[_getAttribute](lazySizesCfg.srcsetAttr); src = elem[_getAttribute](lazySizesCfg.srcAttr); if(isImg) { parent = elem.parentNode; isPicture = parent && regPicture.test(parent.nodeName || ''); } firesLoad = detail.firesLoad || (('src' in elem) && (srcset || src || isPicture)); event = {target: elem}; addClass(elem, lazySizesCfg.loadingClass); if(firesLoad){ clearTimeout(resetPreloadingTimer); resetPreloadingTimer = setTimeout(resetPreloading, 2500); addRemoveLoadEvents(elem, rafSwitchLoadingClass, true); } if(isPicture){ forEach.call(parent.getElementsByTagName('source'), handleSources); } if(srcset){ elem.setAttribute('srcset', srcset); } else if(src && !isPicture){ if(regIframe.test(elem.nodeName)){ changeIframeSrc(elem, src); } else { elem.src = src; } } if(isImg && (srcset || isPicture)){ updatePolyfill(elem, {src: src}); } } if(elem._lazyRace){ delete elem._lazyRace; } removeClass(elem, lazySizesCfg.lazyClass); rAF(function(){ // Part of this can be removed as soon as this fix is older: https://bugs.chromium.org/p/chromium/issues/detail?id=7731 (2015) var isLoaded = elem.complete && elem.naturalWidth > 1; if( !firesLoad || isLoaded){ if (isLoaded) { addClass(elem, 'ls-is-cached'); } switchLoadingClass(event); elem._lazyCache = true; setTimeout(function(){ if ('_lazyCache' in elem) { delete elem._lazyCache; } }, 9); } if (elem.loading == 'lazy') { isLoading--; } }, true); }); var unveilElement = function (elem){ if (elem._lazyRace) {return;} var detail; var isImg = regImg.test(elem.nodeName); //allow using sizes="auto", but don't use. it's invalid. Use data-sizes="auto" or a valid value for sizes instead (i.e.: sizes="80vw") var sizes = isImg && (elem[_getAttribute](lazySizesCfg.sizesAttr) || elem[_getAttribute]('sizes')); var isAuto = sizes == 'auto'; if( (isAuto || !isCompleted) && isImg && (elem[_getAttribute]('src') || elem.srcset) && !elem.complete && !hasClass(elem, lazySizesCfg.errorClass) && hasClass(elem, lazySizesCfg.lazyClass)){return;} detail = triggerEvent(elem, 'lazyunveilread').detail; if(isAuto){ autoSizer.updateElem(elem, true, elem.offsetWidth); } elem._lazyRace = true; isLoading++; lazyUnveil(elem, detail, isAuto, sizes, isImg); }; var afterScroll = debounce(function(){ lazySizesCfg.loadMode = 3; throttledCheckElements(); }); var altLoadmodeScrollListner = function(){ if(lazySizesCfg.loadMode == 3){ lazySizesCfg.loadMode = 2; } afterScroll(); }; var onload = function(){ if(isCompleted){return;} if(Date.now() - started < 999){ setTimeout(onload, 999); return; } isCompleted = true; lazySizesCfg.loadMode = 3; throttledCheckElements(); addEventListener('scroll', altLoadmodeScrollListner, true); }; return { _: function(){ started = Date.now(); lazysizes.elements = document.getElementsByClassName(lazySizesCfg.lazyClass); preloadElems = document.getElementsByClassName(lazySizesCfg.lazyClass + ' ' + lazySizesCfg.preloadClass); addEventListener('scroll', throttledCheckElements, true); addEventListener('resize', throttledCheckElements, true); addEventListener('pageshow', function (e) { if (e.persisted) { var loadingElements = document.querySelectorAll('.' + lazySizesCfg.loadingClass); if (loadingElements.length && loadingElements.forEach) { requestAnimationFrame(function () { loadingElements.forEach( function (img) { if (img.complete) { unveilElement(img); } }); }); } } }); if(window.MutationObserver){ new MutationObserver( throttledCheckElements ).observe( docElem, {childList: true, subtree: true, attributes: true} ); } else { docElem[_addEventListener]('DOMNodeInserted', throttledCheckElements, true); docElem[_addEventListener]('DOMAttrModified', throttledCheckElements, true); setInterval(throttledCheckElements, 999); } addEventListener('hashchange', throttledCheckElements, true); //, 'fullscreenchange' ['focus', 'mouseover', 'click', 'load', 'transitionend', 'animationend'].forEach(function(name){ document[_addEventListener](name, throttledCheckElements, true); }); if((/d$|^c/.test(document.readyState))){ onload(); } else { addEventListener('load', onload); document[_addEventListener]('DOMContentLoaded', throttledCheckElements); setTimeout(onload, 20000); } if(lazysizes.elements.length){ checkElements(); rAF._lsFlush(); } else { throttledCheckElements(); } }, checkElems: throttledCheckElements, unveil: unveilElement, _aLSL: altLoadmodeScrollListner, }; })(); var autoSizer = (function(){ var autosizesElems; var sizeElement = rAFIt(function(elem, parent, event, width){ var sources, i, len; elem._lazysizesWidth = width; width += 'px'; elem.setAttribute('sizes', width); if(regPicture.test(parent.nodeName || '')){ sources = parent.getElementsByTagName('source'); for(i = 0, len = sources.length; i < len; i++){ sources[i].setAttribute('sizes', width); } } if(!event.detail.dataAttr){ updatePolyfill(elem, event.detail); } }); var getSizeElement = function (elem, dataAttr, width){ var event; var parent = elem.parentNode; if(parent){ width = getWidth(elem, parent, width); event = triggerEvent(elem, 'lazybeforesizes', {width: width, dataAttr: !!dataAttr}); if(!event.defaultPrevented){ width = event.detail.width; if(width && width !== elem._lazysizesWidth){ sizeElement(elem, parent, event, width); } } } }; var updateElementsSizes = function(){ var i; var len = autosizesElems.length; if(len){ i = 0; for(; i < len; i++){ getSizeElement(autosizesElems[i]); } } }; var debouncedUpdateElementsSizes = debounce(updateElementsSizes); return { _: function(){ autosizesElems = document.getElementsByClassName(lazySizesCfg.autosizesClass); addEventListener('resize', debouncedUpdateElementsSizes); }, checkElems: debouncedUpdateElementsSizes, updateElem: getSizeElement }; })(); var init = function(){ if(!init.i && document.getElementsByClassName){ init.i = true; autoSizer._(); loader._(); } }; setTimeout(function(){ if(lazySizesCfg.init){ init(); } }); lazysizes = { cfg: lazySizesCfg, autoSizer: autoSizer, loader: loader, init: init, uP: updatePolyfill, aC: addClass, rC: removeClass, hC: hasClass, fire: triggerEvent, gW: getWidth, rAF: rAF, }; return lazysizes; } )); },{}],18:[function(require,module,exports){ (function (global){(function (){ "object"==typeof navigator&&function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Plyr",t):(e="undefined"!=typeof globalThis?globalThis:e||self).Plyr=t()}(this,(function(){"use strict";function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function i(e,i,n){return i&&t(e.prototype,i),n&&t(e,n),e}function n(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function a(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function s(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?a(Object(i),!0).forEach((function(t){n(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):a(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}function r(e,t){if(null==e)return{};var i,n,a=function(e,t){if(null==e)return{};var i,n,a={},s=Object.keys(e);for(n=0;n<s.length;n++)i=s[n],t.indexOf(i)>=0||(a[i]=e[i]);return a}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n<s.length;n++)i=s[n],t.indexOf(i)>=0||Object.prototype.propertyIsEnumerable.call(e,i)&&(a[i]=e[i])}return a}function o(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(e)))return;var i=[],n=!0,a=!1,s=void 0;try{for(var r,o=e[Symbol.iterator]();!(n=(r=o.next()).done)&&(i.push(r.value),!t||i.length!==t);n=!0);}catch(e){a=!0,s=e}finally{try{n||null==o.return||o.return()}finally{if(a)throw s}}return i}(e,t)||c(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(e){return function(e){if(Array.isArray(e))return u(e)}(e)||function(e){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e))return Array.from(e)}(e)||c(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(e,t){if(e){if("string"==typeof e)return u(e,t);var i=Object.prototype.toString.call(e).slice(8,-1);return"Object"===i&&e.constructor&&(i=e.constructor.name),"Map"===i||"Set"===i?Array.from(e):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?u(e,t):void 0}}function u(e,t){(null==t||t>e.length)&&(t=e.length);for(var i=0,n=new Array(t);i<t;i++)n[i]=e[i];return n}function d(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function h(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function m(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function p(e){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?m(Object(i),!0).forEach((function(t){h(e,t,i[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(i)):m(Object(i)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(i,t))}))}return e}var f={addCSS:!0,thumbWidth:15,watch:!0};function g(e,t){return function(){return Array.from(document.querySelectorAll(t)).includes(this)}.call(e,t)}var y=function(e){return null!=e?e.constructor:null},v=function(e,t){return!!(e&&t&&e instanceof t)},b=function(e){return null==e},w=function(e){return y(e)===Object},k=function(e){return y(e)===String},T=function(e){return Array.isArray(e)},C=function(e){return v(e,NodeList)},A=k,S=T,P=C,E=function(e){return v(e,Element)},N=function(e){return v(e,Event)},M=function(e){return b(e)||(k(e)||T(e)||C(e))&&!e.length||w(e)&&!Object.keys(e).length};function x(e,t){if(1>t){var i=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(i))}return Math.round(e/t)*t}var I,L,O,_=function(){function e(t,i){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),E(t)?this.element=t:A(t)&&(this.element=document.querySelector(t)),E(this.element)&&M(this.element.rangeTouch)&&(this.config=p({},f,{},i),this.init())}return function(e,t,i){t&&d(e.prototype,t),i&&d(e,i)}(e,[{key:"init",value:function(){e.enabled&&(this.config.addCSS&&(this.element.style.userSelect="none",this.element.style.webKitUserSelect="none",this.element.style.touchAction="manipulation"),this.listeners(!0),this.element.rangeTouch=this)}},{key:"destroy",value:function(){e.enabled&&(this.config.addCSS&&(this.element.style.userSelect="",this.element.style.webKitUserSelect="",this.element.style.touchAction=""),this.listeners(!1),this.element.rangeTouch=null)}},{key:"listeners",value:function(e){var t=this,i=e?"addEventListener":"removeEventListener";["touchstart","touchmove","touchend"].forEach((function(e){t.element[i](e,(function(e){return t.set(e)}),!1)}))}},{key:"get",value:function(t){if(!e.enabled||!N(t))return null;var i,n=t.target,a=t.changedTouches[0],s=parseFloat(n.getAttribute("min"))||0,r=parseFloat(n.getAttribute("max"))||100,o=parseFloat(n.getAttribute("step"))||1,l=n.getBoundingClientRect(),c=100/l.width*(this.config.thumbWidth/2)/100;return 0>(i=100/l.width*(a.clientX-l.left))?i=0:100<i&&(i=100),50>i?i-=(100-2*i)*c:50<i&&(i+=2*(i-50)*c),s+x(i/100*(r-s),o)}},{key:"set",value:function(t){e.enabled&&N(t)&&!t.target.disabled&&(t.preventDefault(),t.target.value=this.get(t),function(e,t){if(e&&t){var i=new Event(t,{bubbles:!0});e.dispatchEvent(i)}}(t.target,"touchend"===t.type?"change":"input"))}}],[{key:"setup",value:function(t){var i=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=null;if(M(t)||A(t)?n=Array.from(document.querySelectorAll(A(t)?t:'input[type="range"]')):E(t)?n=[t]:P(t)?n=Array.from(t):S(t)&&(n=t.filter(E)),M(n))return null;var a=p({},f,{},i);if(A(t)&&a.watch){var s=new MutationObserver((function(i){Array.from(i).forEach((function(i){Array.from(i.addedNodes).forEach((function(i){E(i)&&g(i,t)&&new e(i,a)}))}))}));s.observe(document.body,{childList:!0,subtree:!0})}return n.map((function(t){return new e(t,i)}))}},{key:"enabled",get:function(){return"ontouchstart"in document.documentElement}}]),e}(),j=function(e){return null!=e?e.constructor:null},D=function(e,t){return Boolean(e&&t&&e instanceof t)},q=function(e){return null==e},H=function(e){return j(e)===Object},F=function(e){return j(e)===String},R=function(e){return j(e)===Function},V=function(e){return Array.isArray(e)},B=function(e){return D(e,NodeList)},U=function(e){return q(e)||(F(e)||V(e)||B(e))&&!e.length||H(e)&&!Object.keys(e).length},W=q,z=H,K=function(e){return j(e)===Number&&!Number.isNaN(e)},Y=F,Q=function(e){return j(e)===Boolean},X=R,$=V,J=B,G=function(e){return D(e,Element)},Z=function(e){return D(e,Event)},ee=function(e){return D(e,KeyboardEvent)},te=function(e){return D(e,TextTrack)||!q(e)&&F(e.kind)},ie=function(e){return D(e,Promise)&&R(e.then)},ne=function(e){if(D(e,window.URL))return!0;if(!F(e))return!1;var t=e;e.startsWith("http://")&&e.startsWith("https://")||(t="http://".concat(e));try{return!U(new URL(t).hostname)}catch(e){return!1}},ae=U,se=(I=document.createElement("span"),L={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},O=Object.keys(L).find((function(e){return void 0!==I.style[e]})),!!Y(O)&&L[O]);function re(e,t){setTimeout((function(){try{e.hidden=!0,e.offsetHeight,e.hidden=!1}catch(e){}}),t)}var oe={isIE: /* @cc_on!@ */ !!document.documentMode,isEdge:window.navigator.userAgent.includes("Edge"),isWebkit:"WebkitAppearance"in document.documentElement.style&&!/Edge/.test(navigator.userAgent),isIPhone:/(iPhone|iPod)/gi.test(navigator.platform),isIos:/(iPad|iPhone|iPod)/gi.test(navigator.platform)};function le(e,t){return t.split(".").reduce((function(e,t){return e&&e[t]}),e)}function ce(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length,i=new Array(t>1?t-1:0),a=1;a<t;a++)i[a-1]=arguments[a];if(!i.length)return e;var s=i.shift();return z(s)?(Object.keys(s).forEach((function(t){z(s[t])?(Object.keys(e).includes(t)||Object.assign(e,n({},t,{})),ce(e[t],s[t])):Object.assign(e,n({},t,s[t]))})),ce.apply(void 0,[e].concat(i))):e}function ue(e,t){var i=e.length?e:[e];Array.from(i).reverse().forEach((function(e,i){var n=i>0?t.cloneNode(!0):t,a=e.parentNode,s=e.nextSibling;n.appendChild(e),s?a.insertBefore(n,s):a.appendChild(n)}))}function de(e,t){G(e)&&!ae(t)&&Object.entries(t).filter((function(e){var t=o(e,2)[1];return!W(t)})).forEach((function(t){var i=o(t,2),n=i[0],a=i[1];return e.setAttribute(n,a)}))}function he(e,t,i){var n=document.createElement(e);return z(t)&&de(n,t),Y(i)&&(n.innerText=i),n}function me(e,t,i,n){G(t)&&t.appendChild(he(e,i,n))}function pe(e){J(e)||$(e)?Array.from(e).forEach(pe):G(e)&&G(e.parentNode)&&e.parentNode.removeChild(e)}function fe(e){if(G(e))for(var t=e.childNodes.length;t>0;)e.removeChild(e.lastChild),t-=1}function ge(e,t){return G(t)&&G(t.parentNode)&&G(e)?(t.parentNode.replaceChild(e,t),e):null}function ye(e,t){if(!Y(e)||ae(e))return{};var i={},n=ce({},t);return e.split(",").forEach((function(e){var t=e.trim(),a=t.replace(".",""),s=t.replace(/[[\]]/g,"").split("="),r=o(s,1)[0],l=s.length>1?s[1].replace(/["']/g,""):"";switch(t.charAt(0)){case".":Y(n.class)?i.class="".concat(n.class," ").concat(a):i.class=a;break;case"#":i.id=t.replace("#","");break;case"[":i[r]=l}})),ce(n,i)}function ve(e,t){if(G(e)){var i=t;Q(i)||(i=!e.hidden),e.hidden=i}}function be(e,t,i){if(J(e))return Array.from(e).map((function(e){return be(e,t,i)}));if(G(e)){var n="toggle";return void 0!==i&&(n=i?"add":"remove"),e.classList[n](t),e.classList.contains(t)}return!1}function we(e,t){return G(e)&&e.classList.contains(t)}function ke(e,t){var i=Element.prototype;return(i.matches||i.webkitMatchesSelector||i.mozMatchesSelector||i.msMatchesSelector||function(){return Array.from(document.querySelectorAll(t)).includes(this)}).call(e,t)}function Te(e){return this.elements.container.querySelectorAll(e)}function Ce(e){return this.elements.container.querySelector(e)}function Ae(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];G(e)&&(e.focus({preventScroll:!0}),t&&be(e,this.config.classNames.tabFocus))}var Se,Pe={"audio/ogg":"vorbis","audio/wav":"1","video/webm":"vp8, vorbis","video/mp4":"avc1.42E01E, mp4a.40.2","video/ogg":"theora"},Ee={audio:"canPlayType"in document.createElement("audio"),video:"canPlayType"in document.createElement("video"),check:function(e,t,i){var n=oe.isIPhone&&i&&Ee.playsinline,a=Ee[e]||"html5"!==t;return{api:a,ui:a&&Ee.rangeInput&&("video"!==e||!oe.isIPhone||n)}},pip:!(oe.isIPhone||!X(he("video").webkitSetPresentationMode)&&(!document.pictureInPictureEnabled||he("video").disablePictureInPicture)),airplay:X(window.WebKitPlaybackTargetAvailabilityEvent),playsinline:"playsInline"in document.createElement("video"),mime:function(e){if(ae(e))return!1;var t=o(e.split("/"),1)[0],i=e;if(!this.isHTML5||t!==this.type)return!1;Object.keys(Pe).includes(i)&&(i+='; codecs="'.concat(Pe[e],'"'));try{return Boolean(i&&this.media.canPlayType(i).replace(/no/,""))}catch(e){return!1}},textTracks:"textTracks"in document.createElement("video"),rangeInput:(Se=document.createElement("input"),Se.type="range","range"===Se.type),touch:"ontouchstart"in document.documentElement,transitions:!1!==se,reducedMotion:"matchMedia"in window&&window.matchMedia("(prefers-reduced-motion)").matches},Ne=function(){var e=!1;try{var t=Object.defineProperty({},"passive",{get:function(){return e=!0,null}});window.addEventListener("test",null,t),window.removeEventListener("test",null,t)}catch(e){}return e}();function Me(e,t,i){var n=this,a=arguments.length>3&&void 0!==arguments[3]&&arguments[3],s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=arguments.length>5&&void 0!==arguments[5]&&arguments[5];if(e&&"addEventListener"in e&&!ae(t)&&X(i)){var o=t.split(" "),l=r;Ne&&(l={passive:s,capture:r}),o.forEach((function(t){n&&n.eventListeners&&a&&n.eventListeners.push({element:e,type:t,callback:i,options:l}),e[a?"addEventListener":"removeEventListener"](t,i,l)}))}}function xe(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2?arguments[2]:void 0,n=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];Me.call(this,e,t,i,!0,n,a)}function Ie(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2?arguments[2]:void 0,n=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];Me.call(this,e,t,i,!1,n,a)}function Le(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,a=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],s=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r=function r(){Ie(e,i,r,a,s);for(var o=arguments.length,l=new Array(o),c=0;c<o;c++)l[c]=arguments[c];n.apply(t,l)};Me.call(this,e,i,r,!0,a,s)}function Oe(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};if(G(e)&&!ae(t)){var a=new CustomEvent(t,{bubbles:i,detail:s(s({},n),{},{plyr:this})});e.dispatchEvent(a)}}function _e(){this&&this.eventListeners&&(this.eventListeners.forEach((function(e){var t=e.element,i=e.type,n=e.callback,a=e.options;t.removeEventListener(i,n,a)})),this.eventListeners=[])}function je(){var e=this;return new Promise((function(t){return e.ready?setTimeout(t,0):xe.call(e,e.elements.container,"ready",t)})).then((function(){}))}function De(e){ie(e)&&e.then(null,(function(){}))}function qe(e){return!!($(e)||Y(e)&&e.includes(":"))&&($(e)?e:e.split(":")).map(Number).every(K)}function He(e){if(!$(e)||!e.every(K))return null;var t=o(e,2),i=t[0],n=t[1],a=function e(t,i){return 0===i?t:e(i,t%i)}(i,n);return[i/a,n/a]}function Fe(e){var t=function(e){return qe(e)?e.split(":").map(Number):null},i=t(e);if(null===i&&(i=t(this.config.ratio)),null===i&&!ae(this.embed)&&$(this.embed.ratio)&&(i=this.embed.ratio),null===i&&this.isHTML5){var n=this.media;i=He([n.videoWidth,n.videoHeight])}return i}function Re(e){if(!this.isVideo)return{};var t=this.elements.wrapper,i=Fe.call(this,e),n=o($(i)?i:[0,0],2),a=100/n[0]*n[1];if(t.style.paddingBottom="".concat(a,"%"),this.isVimeo&&!this.config.vimeo.premium&&this.supported.ui){var s=100/this.media.offsetWidth*parseInt(window.getComputedStyle(this.media).paddingBottom,10),r=(s-a)/(s/50);this.fullscreen.active?t.style.paddingBottom=null:this.media.style.transform="translateY(-".concat(r,"%)")}else this.isHTML5&&t.classList.toggle(this.config.classNames.videoFixedRatio,null!==i);return{padding:a,ratio:i}}var Ve={getSources:function(){var e=this;return this.isHTML5?Array.from(this.media.querySelectorAll("source")).filter((function(t){var i=t.getAttribute("type");return!!ae(i)||Ee.mime.call(e,i)})):[]},getQualityOptions:function(){return this.config.quality.forced?this.config.quality.options:Ve.getSources.call(this).map((function(e){return Number(e.getAttribute("size"))})).filter(Boolean)},setup:function(){if(this.isHTML5){var e=this;e.options.speed=e.config.speed.options,ae(this.config.ratio)||Re.call(e),Object.defineProperty(e.media,"quality",{get:function(){var t=Ve.getSources.call(e).find((function(t){return t.getAttribute("src")===e.source}));return t&&Number(t.getAttribute("size"))},set:function(t){if(e.quality!==t){if(e.config.quality.forced&&X(e.config.quality.onChange))e.config.quality.onChange(t);else{var i=Ve.getSources.call(e).find((function(e){return Number(e.getAttribute("size"))===t}));if(!i)return;var n=e.media,a=n.currentTime,s=n.paused,r=n.preload,o=n.readyState,l=n.playbackRate;e.media.src=i.getAttribute("src"),("none"!==r||o)&&(e.once("loadedmetadata",(function(){e.speed=l,e.currentTime=a,s||De(e.play())})),e.media.load())}Oe.call(e,e.media,"qualitychange",!1,{quality:t})}}})}},cancelRequests:function(){this.isHTML5&&(pe(Ve.getSources.call(this)),this.media.setAttribute("src",this.config.blankVideo),this.media.load(),this.debug.log("Cancelled network requests"))}};function Be(e){return $(e)?e.filter((function(t,i){return e.indexOf(t)===i})):e}function Ue(e){for(var t=arguments.length,i=new Array(t>1?t-1:0),n=1;n<t;n++)i[n-1]=arguments[n];return ae(e)?e:e.toString().replace(/{(\d+)}/g,(function(e,t){return i[t].toString()}))}var We=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return e.replace(new RegExp(t.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g,"\\$1"),"g"),i.toString())},ze=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return e.toString().replace(/\w\S*/g,(function(e){return e.charAt(0).toUpperCase()+e.substr(1).toLowerCase()}))};function Ke(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=e.toString();return t=We(t,"-"," "),t=We(t,"_"," "),t=ze(t),We(t," ","")}function Ye(e){var t=document.createElement("div");return t.appendChild(e),t.innerHTML}var Qe={pip:"PIP",airplay:"AirPlay",html5:"HTML5",vimeo:"Vimeo",youtube:"YouTube"},Xe=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(ae(e)||ae(t))return"";var i=le(t.i18n,e);if(ae(i))return Object.keys(Qe).includes(e)?Qe[e]:"";var n={"{seektime}":t.seekTime,"{title}":t.title};return Object.entries(n).forEach((function(e){var t=o(e,2),n=t[0],a=t[1];i=We(i,n,a)})),i},$e=function(){function t(i){e(this,t),this.enabled=i.config.storage.enabled,this.key=i.config.storage.key}return i(t,[{key:"get",value:function(e){if(!t.supported||!this.enabled)return null;var i=window.localStorage.getItem(this.key);if(ae(i))return null;var n=JSON.parse(i);return Y(e)&&e.length?n[e]:n}},{key:"set",value:function(e){if(t.supported&&this.enabled&&z(e)){var i=this.get();ae(i)&&(i={}),ce(i,e),window.localStorage.setItem(this.key,JSON.stringify(i))}}}],[{key:"supported",get:function(){try{if(!("localStorage"in window))return!1;var e="___test";return window.localStorage.setItem(e,e),window.localStorage.removeItem(e),!0}catch(e){return!1}}}]),t}();function Je(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"text";return new Promise((function(i,n){try{var a=new XMLHttpRequest;if(!("withCredentials"in a))return;a.addEventListener("load",(function(){if("text"===t)try{i(JSON.parse(a.responseText))}catch(e){i(a.responseText)}else i(a.response)})),a.addEventListener("error",(function(){throw new Error(a.status)})),a.open("GET",e,!0),a.responseType=t,a.send()}catch(e){n(e)}}))}function Ge(e,t){if(Y(e)){var i="cache",n=Y(t),a=function(){return null!==document.getElementById(t)},s=function(e,t){e.innerHTML=t,n&&a()||document.body.insertAdjacentElement("afterbegin",e)};if(!n||!a()){var r=$e.supported,o=document.createElement("div");if(o.setAttribute("hidden",""),n&&o.setAttribute("id",t),r){var l=window.localStorage.getItem("".concat(i,"-").concat(t));if(null!==l){var c=JSON.parse(l);s(o,c.content)}}Je(e).then((function(e){ae(e)||(r&&window.localStorage.setItem("".concat(i,"-").concat(t),JSON.stringify({content:e})),s(o,e))})).catch((function(){}))}}}var Ze=function(e){return Math.trunc(e/60/60%60,10)},et=function(e){return Math.trunc(e/60%60,10)},tt=function(e){return Math.trunc(e%60,10)};function it(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(!K(e))return it(void 0,t,i);var n=function(e){return"0".concat(e).slice(-2)},a=Ze(e),s=et(e),r=tt(e);return a=t||a>0?"".concat(a,":"):"","".concat(i&&e>0?"-":"").concat(a).concat(n(s),":").concat(n(r))}var nt={getIconUrl:function(){var e=new URL(this.config.iconUrl,window.location).host!==window.location.host||oe.isIE&&!window.svg4everybody;return{url:this.config.iconUrl,cors:e}},findElements:function(){try{return this.elements.controls=Ce.call(this,this.config.selectors.controls.wrapper),this.elements.buttons={play:Te.call(this,this.config.selectors.buttons.play),pause:Ce.call(this,this.config.selectors.buttons.pause),restart:Ce.call(this,this.config.selectors.buttons.restart),rewind:Ce.call(this,this.config.selectors.buttons.rewind),fastForward:Ce.call(this,this.config.selectors.buttons.fastForward),mute:Ce.call(this,this.config.selectors.buttons.mute),pip:Ce.call(this,this.config.selectors.buttons.pip),airplay:Ce.call(this,this.config.selectors.buttons.airplay),settings:Ce.call(this,this.config.selectors.buttons.settings),captions:Ce.call(this,this.config.selectors.buttons.captions),fullscreen:Ce.call(this,this.config.selectors.buttons.fullscreen)},this.elements.progress=Ce.call(this,this.config.selectors.progress),this.elements.inputs={seek:Ce.call(this,this.config.selectors.inputs.seek),volume:Ce.call(this,this.config.selectors.inputs.volume)},this.elements.display={buffer:Ce.call(this,this.config.selectors.display.buffer),currentTime:Ce.call(this,this.config.selectors.display.currentTime),duration:Ce.call(this,this.config.selectors.display.duration)},G(this.elements.progress)&&(this.elements.display.seekTooltip=this.elements.progress.querySelector(".".concat(this.config.classNames.tooltip))),!0}catch(e){return this.debug.warn("It looks like there is a problem with your custom controls HTML",e),this.toggleNativeControls(!0),!1}},createIcon:function(e,t){var i="http://www.w3.org/2000/svg",n=nt.getIconUrl.call(this),a="".concat(n.cors?"":n.url,"#").concat(this.config.iconPrefix),s=document.createElementNS(i,"svg");de(s,ce(t,{"aria-hidden":"true",focusable:"false"}));var r=document.createElementNS(i,"use"),o="".concat(a,"-").concat(e);return"href"in r&&r.setAttributeNS("http://www.w3.org/1999/xlink","href",o),r.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",o),s.appendChild(r),s},createLabel:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=Xe(e,this.config),n=s(s({},t),{},{class:[t.class,this.config.classNames.hidden].filter(Boolean).join(" ")});return he("span",n,i)},createBadge:function(e){if(ae(e))return null;var t=he("span",{class:this.config.classNames.menu.value});return t.appendChild(he("span",{class:this.config.classNames.menu.badge},e)),t},createButton:function(e,t){var i=this,n=ce({},t),a=function(){var e=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").toString();return(e=Ke(e)).charAt(0).toLowerCase()+e.slice(1)}(e),s={element:"button",toggle:!1,label:null,icon:null,labelPressed:null,iconPressed:null};switch(["element","icon","label"].forEach((function(e){Object.keys(n).includes(e)&&(s[e]=n[e],delete n[e])})),"button"!==s.element||Object.keys(n).includes("type")||(n.type="button"),Object.keys(n).includes("class")?n.class.split(" ").some((function(e){return e===i.config.classNames.control}))||ce(n,{class:"".concat(n.class," ").concat(this.config.classNames.control)}):n.class=this.config.classNames.control,e){case"play":s.toggle=!0,s.label="play",s.labelPressed="pause",s.icon="play",s.iconPressed="pause";break;case"mute":s.toggle=!0,s.label="mute",s.labelPressed="unmute",s.icon="volume",s.iconPressed="muted";break;case"captions":s.toggle=!0,s.label="enableCaptions",s.labelPressed="disableCaptions",s.icon="captions-off",s.iconPressed="captions-on";break;case"fullscreen":s.toggle=!0,s.label="enterFullscreen",s.labelPressed="exitFullscreen",s.icon="enter-fullscreen",s.iconPressed="exit-fullscreen";break;case"play-large":n.class+=" ".concat(this.config.classNames.control,"--overlaid"),a="play",s.label="play",s.icon="play";break;default:ae(s.label)&&(s.label=a),ae(s.icon)&&(s.icon=e)}var r=he(s.element);return s.toggle?(r.appendChild(nt.createIcon.call(this,s.iconPressed,{class:"icon--pressed"})),r.appendChild(nt.createIcon.call(this,s.icon,{class:"icon--not-pressed"})),r.appendChild(nt.createLabel.call(this,s.labelPressed,{class:"label--pressed"})),r.appendChild(nt.createLabel.call(this,s.label,{class:"label--not-pressed"}))):(r.appendChild(nt.createIcon.call(this,s.icon)),r.appendChild(nt.createLabel.call(this,s.label))),ce(n,ye(this.config.selectors.buttons[a],n)),de(r,n),"play"===a?($(this.elements.buttons[a])||(this.elements.buttons[a]=[]),this.elements.buttons[a].push(r)):this.elements.buttons[a]=r,r},createRange:function(e,t){var i=he("input",ce(ye(this.config.selectors.inputs[e]),{type:"range",min:0,max:100,step:.01,value:0,autocomplete:"off",role:"slider","aria-label":Xe(e,this.config),"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":0},t));return this.elements.inputs[e]=i,nt.updateRangeFill.call(this,i),_.setup(i),i},createProgress:function(e,t){var i=he("progress",ce(ye(this.config.selectors.display[e]),{min:0,max:100,value:0,role:"progressbar","aria-hidden":!0},t));if("volume"!==e){i.appendChild(he("span",null,"0"));var n={played:"played",buffer:"buffered"}[e],a=n?Xe(n,this.config):"";i.innerText="% ".concat(a.toLowerCase())}return this.elements.display[e]=i,i},createTime:function(e,t){var i=ye(this.config.selectors.display[e],t),n=he("div",ce(i,{class:"".concat(i.class?i.class:""," ").concat(this.config.classNames.display.time," ").trim(),"aria-label":Xe(e,this.config)}),"00:00");return this.elements.display[e]=n,n},bindMenuItemShortcuts:function(e,t){var i=this;xe.call(this,e,"keydown keyup",(function(n){if([32,38,39,40].includes(n.which)&&(n.preventDefault(),n.stopPropagation(),"keydown"!==n.type)){var a,s=ke(e,'[role="menuitemradio"]');if(!s&&[32,39].includes(n.which))nt.showMenuPanel.call(i,t,!0);else 32!==n.which&&(40===n.which||s&&39===n.which?(a=e.nextElementSibling,G(a)||(a=e.parentNode.firstElementChild)):(a=e.previousElementSibling,G(a)||(a=e.parentNode.lastElementChild)),Ae.call(i,a,!0))}}),!1),xe.call(this,e,"keyup",(function(e){13===e.which&&nt.focusFirstMenuItem.call(i,null,!0)}))},createMenuItem:function(e){var t=this,i=e.value,n=e.list,a=e.type,s=e.title,r=e.badge,o=void 0===r?null:r,l=e.checked,c=void 0!==l&&l,u=ye(this.config.selectors.inputs[a]),d=he("button",ce(u,{type:"button",role:"menuitemradio",class:"".concat(this.config.classNames.control," ").concat(u.class?u.class:"").trim(),"aria-checked":c,value:i})),h=he("span");h.innerHTML=s,G(o)&&h.appendChild(o),d.appendChild(h),Object.defineProperty(d,"checked",{enumerable:!0,get:function(){return"true"===d.getAttribute("aria-checked")},set:function(e){e&&Array.from(d.parentNode.children).filter((function(e){return ke(e,'[role="menuitemradio"]')})).forEach((function(e){return e.setAttribute("aria-checked","false")})),d.setAttribute("aria-checked",e?"true":"false")}}),this.listeners.bind(d,"click keyup",(function(e){if(!ee(e)||32===e.which){switch(e.preventDefault(),e.stopPropagation(),d.checked=!0,a){case"language":t.currentTrack=Number(i);break;case"quality":t.quality=i;break;case"speed":t.speed=parseFloat(i)}nt.showMenuPanel.call(t,"home",ee(e))}}),a,!1),nt.bindMenuItemShortcuts.call(this,d,a),n.appendChild(d)},formatTime:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(!K(e))return e;var i=Ze(this.duration)>0;return it(e,i,t)},updateTimeDisplay:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];G(e)&&K(t)&&(e.innerText=nt.formatTime(t,i))},updateVolume:function(){this.supported.ui&&(G(this.elements.inputs.volume)&&nt.setRange.call(this,this.elements.inputs.volume,this.muted?0:this.volume),G(this.elements.buttons.mute)&&(this.elements.buttons.mute.pressed=this.muted||0===this.volume))},setRange:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;G(e)&&(e.value=t,nt.updateRangeFill.call(this,e))},updateProgress:function(e){var t=this;if(this.supported.ui&&Z(e)){var i,n,a=0;if(e)switch(e.type){case"timeupdate":case"seeking":case"seeked":i=this.currentTime,n=this.duration,a=0===i||0===n||Number.isNaN(i)||Number.isNaN(n)?0:(i/n*100).toFixed(2),"timeupdate"===e.type&&nt.setRange.call(this,this.elements.inputs.seek,a);break;case"playing":case"progress":!function(e,i){var n=K(i)?i:0,a=G(e)?e:t.elements.display.buffer;if(G(a)){a.value=n;var s=a.getElementsByTagName("span")[0];G(s)&&(s.childNodes[0].nodeValue=n)}}(this.elements.display.buffer,100*this.buffered)}}},updateRangeFill:function(e){var t=Z(e)?e.target:e;if(G(t)&&"range"===t.getAttribute("type")){if(ke(t,this.config.selectors.inputs.seek)){t.setAttribute("aria-valuenow",this.currentTime);var i=nt.formatTime(this.currentTime),n=nt.formatTime(this.duration),a=Xe("seekLabel",this.config);t.setAttribute("aria-valuetext",a.replace("{currentTime}",i).replace("{duration}",n))}else if(ke(t,this.config.selectors.inputs.volume)){var s=100*t.value;t.setAttribute("aria-valuenow",s),t.setAttribute("aria-valuetext","".concat(s.toFixed(1),"%"))}else t.setAttribute("aria-valuenow",t.value);oe.isWebkit&&t.style.setProperty("--value","".concat(t.value/t.max*100,"%"))}},updateSeekTooltip:function(e){var t=this;if(this.config.tooltips.seek&&G(this.elements.inputs.seek)&&G(this.elements.display.seekTooltip)&&0!==this.duration){var i="".concat(this.config.classNames.tooltip,"--visible"),n=function(e){return be(t.elements.display.seekTooltip,i,e)};if(this.touch)n(!1);else{var a=0,s=this.elements.progress.getBoundingClientRect();if(Z(e))a=100/s.width*(e.pageX-s.left);else{if(!we(this.elements.display.seekTooltip,i))return;a=parseFloat(this.elements.display.seekTooltip.style.left,10)}a<0?a=0:a>100&&(a=100),nt.updateTimeDisplay.call(this,this.elements.display.seekTooltip,this.duration/100*a),this.elements.display.seekTooltip.style.left="".concat(a,"%"),Z(e)&&["mouseenter","mouseleave"].includes(e.type)&&n("mouseenter"===e.type)}}},timeUpdate:function(e){var t=!G(this.elements.display.duration)&&this.config.invertTime;nt.updateTimeDisplay.call(this,this.elements.display.currentTime,t?this.duration-this.currentTime:this.currentTime,t),e&&"timeupdate"===e.type&&this.media.seeking||nt.updateProgress.call(this,e)},durationUpdate:function(){if(this.supported.ui&&(this.config.invertTime||!this.currentTime)){if(this.duration>=Math.pow(2,32))return ve(this.elements.display.currentTime,!0),void ve(this.elements.progress,!0);G(this.elements.inputs.seek)&&this.elements.inputs.seek.setAttribute("aria-valuemax",this.duration);var e=G(this.elements.display.duration);!e&&this.config.displayDuration&&this.paused&&nt.updateTimeDisplay.call(this,this.elements.display.currentTime,this.duration),e&&nt.updateTimeDisplay.call(this,this.elements.display.duration,this.duration),nt.updateSeekTooltip.call(this)}},toggleMenuButton:function(e,t){ve(this.elements.settings.buttons[e],!t)},updateSetting:function(e,t,i){var n=this.elements.settings.panels[e],a=null,s=t;if("captions"===e)a=this.currentTrack;else{if(a=ae(i)?this[e]:i,ae(a)&&(a=this.config[e].default),!ae(this.options[e])&&!this.options[e].includes(a))return void this.debug.warn("Unsupported value of '".concat(a,"' for ").concat(e));if(!this.config[e].options.includes(a))return void this.debug.warn("Disabled value of '".concat(a,"' for ").concat(e))}if(G(s)||(s=n&&n.querySelector('[role="menu"]')),G(s)){this.elements.settings.buttons[e].querySelector(".".concat(this.config.classNames.menu.value)).innerHTML=nt.getLabel.call(this,e,a);var r=s&&s.querySelector('[value="'.concat(a,'"]'));G(r)&&(r.checked=!0)}},getLabel:function(e,t){switch(e){case"speed":return 1===t?Xe("normal",this.config):"".concat(t,"×");case"quality":if(K(t)){var i=Xe("qualityLabel.".concat(t),this.config);return i.length?i:"".concat(t,"p")}return ze(t);case"captions":return rt.getLabel.call(this);default:return null}},setQualityMenu:function(e){var t=this;if(G(this.elements.settings.panels.quality)){var i="quality",n=this.elements.settings.panels.quality.querySelector('[role="menu"]');$(e)&&(this.options.quality=Be(e).filter((function(e){return t.config.quality.options.includes(e)})));var a=!ae(this.options.quality)&&this.options.quality.length>1;if(nt.toggleMenuButton.call(this,i,a),fe(n),nt.checkMenu.call(this),a){var s=function(e){var i=Xe("qualityBadge.".concat(e),t.config);return i.length?nt.createBadge.call(t,i):null};this.options.quality.sort((function(e,i){var n=t.config.quality.options;return n.indexOf(e)>n.indexOf(i)?1:-1})).forEach((function(e){nt.createMenuItem.call(t,{value:e,list:n,type:i,title:nt.getLabel.call(t,"quality",e),badge:s(e)})})),nt.updateSetting.call(this,i,n)}}},setCaptionsMenu:function(){var e=this;if(G(this.elements.settings.panels.captions)){var t="captions",i=this.elements.settings.panels.captions.querySelector('[role="menu"]'),n=rt.getTracks.call(this),a=Boolean(n.length);if(nt.toggleMenuButton.call(this,t,a),fe(i),nt.checkMenu.call(this),a){var s=n.map((function(t,n){return{value:n,checked:e.captions.toggled&&e.currentTrack===n,title:rt.getLabel.call(e,t),badge:t.language&&nt.createBadge.call(e,t.language.toUpperCase()),list:i,type:"language"}}));s.unshift({value:-1,checked:!this.captions.toggled,title:Xe("disabled",this.config),list:i,type:"language"}),s.forEach(nt.createMenuItem.bind(this)),nt.updateSetting.call(this,t,i)}}},setSpeedMenu:function(){var e=this;if(G(this.elements.settings.panels.speed)){var t="speed",i=this.elements.settings.panels.speed.querySelector('[role="menu"]');this.options.speed=this.options.speed.filter((function(t){return t>=e.minimumSpeed&&t<=e.maximumSpeed}));var n=!ae(this.options.speed)&&this.options.speed.length>1;nt.toggleMenuButton.call(this,t,n),fe(i),nt.checkMenu.call(this),n&&(this.options.speed.forEach((function(n){nt.createMenuItem.call(e,{value:n,list:i,type:t,title:nt.getLabel.call(e,"speed",n)})})),nt.updateSetting.call(this,t,i))}},checkMenu:function(){var e=this.elements.settings.buttons,t=!ae(e)&&Object.values(e).some((function(e){return!e.hidden}));ve(this.elements.settings.menu,!t)},focusFirstMenuItem:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(!this.elements.settings.popup.hidden){var i=e;G(i)||(i=Object.values(this.elements.settings.panels).find((function(e){return!e.hidden})));var n=i.querySelector('[role^="menuitem"]');Ae.call(this,n,t)}},toggleMenu:function(e){var t=this.elements.settings.popup,i=this.elements.buttons.settings;if(G(t)&&G(i)){var n=t.hidden,a=n;if(Q(e))a=e;else if(ee(e)&&27===e.which)a=!1;else if(Z(e)){var s=X(e.composedPath)?e.composedPath()[0]:e.target,r=t.contains(s);if(r||!r&&e.target!==i&&a)return}i.setAttribute("aria-expanded",a),ve(t,!a),be(this.elements.container,this.config.classNames.menu.open,a),a&&ee(e)?nt.focusFirstMenuItem.call(this,null,!0):a||n||Ae.call(this,i,ee(e))}},getMenuSize:function(e){var t=e.cloneNode(!0);t.style.position="absolute",t.style.opacity=0,t.removeAttribute("hidden"),e.parentNode.appendChild(t);var i=t.scrollWidth,n=t.scrollHeight;return pe(t),{width:i,height:n}},showMenuPanel:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.elements.container.querySelector("#plyr-settings-".concat(this.id,"-").concat(t));if(G(n)){var a=n.parentNode,s=Array.from(a.children).find((function(e){return!e.hidden}));if(Ee.transitions&&!Ee.reducedMotion){a.style.width="".concat(s.scrollWidth,"px"),a.style.height="".concat(s.scrollHeight,"px");var r=nt.getMenuSize.call(this,n),o=function t(i){i.target===a&&["width","height"].includes(i.propertyName)&&(a.style.width="",a.style.height="",Ie.call(e,a,se,t))};xe.call(this,a,se,o),a.style.width="".concat(r.width,"px"),a.style.height="".concat(r.height,"px")}ve(s,!0),ve(n,!1),nt.focusFirstMenuItem.call(this,n,i)}},setDownloadUrl:function(){var e=this.elements.buttons.download;G(e)&&e.setAttribute("href",this.download)},create:function(e){var t=this,i=nt.bindMenuItemShortcuts,n=nt.createButton,a=nt.createProgress,s=nt.createRange,r=nt.createTime,o=nt.setQualityMenu,l=nt.setSpeedMenu,c=nt.showMenuPanel;this.elements.controls=null,$(this.config.controls)&&this.config.controls.includes("play-large")&&this.elements.container.appendChild(n.call(this,"play-large"));var u=he("div",ye(this.config.selectors.controls.wrapper));this.elements.controls=u;var d={class:"plyr__controls__item"};return Be($(this.config.controls)?this.config.controls:[]).forEach((function(o){if("restart"===o&&u.appendChild(n.call(t,"restart",d)),"rewind"===o&&u.appendChild(n.call(t,"rewind",d)),"play"===o&&u.appendChild(n.call(t,"play",d)),"fast-forward"===o&&u.appendChild(n.call(t,"fast-forward",d)),"progress"===o){var l=he("div",{class:"".concat(d.class," plyr__progress__container")}),h=he("div",ye(t.config.selectors.progress));if(h.appendChild(s.call(t,"seek",{id:"plyr-seek-".concat(e.id)})),h.appendChild(a.call(t,"buffer")),t.config.tooltips.seek){var m=he("span",{class:t.config.classNames.tooltip},"00:00");h.appendChild(m),t.elements.display.seekTooltip=m}t.elements.progress=h,l.appendChild(t.elements.progress),u.appendChild(l)}if("current-time"===o&&u.appendChild(r.call(t,"currentTime",d)),"duration"===o&&u.appendChild(r.call(t,"duration",d)),"mute"===o||"volume"===o){var p=t.elements.volume;if(G(p)&&u.contains(p)||(p=he("div",ce({},d,{class:"".concat(d.class," plyr__volume").trim()})),t.elements.volume=p,u.appendChild(p)),"mute"===o&&p.appendChild(n.call(t,"mute")),"volume"===o&&!oe.isIos){var f={max:1,step:.05,value:t.config.volume};p.appendChild(s.call(t,"volume",ce(f,{id:"plyr-volume-".concat(e.id)})))}}if("captions"===o&&u.appendChild(n.call(t,"captions",d)),"settings"===o&&!ae(t.config.settings)){var g=he("div",ce({},d,{class:"".concat(d.class," plyr__menu").trim(),hidden:""}));g.appendChild(n.call(t,"settings",{"aria-haspopup":!0,"aria-controls":"plyr-settings-".concat(e.id),"aria-expanded":!1}));var y=he("div",{class:"plyr__menu__container",id:"plyr-settings-".concat(e.id),hidden:""}),v=he("div"),b=he("div",{id:"plyr-settings-".concat(e.id,"-home")}),w=he("div",{role:"menu"});b.appendChild(w),v.appendChild(b),t.elements.settings.panels.home=b,t.config.settings.forEach((function(n){var a=he("button",ce(ye(t.config.selectors.buttons.settings),{type:"button",class:"".concat(t.config.classNames.control," ").concat(t.config.classNames.control,"--forward"),role:"menuitem","aria-haspopup":!0,hidden:""}));i.call(t,a,n),xe.call(t,a,"click",(function(){c.call(t,n,!1)}));var s=he("span",null,Xe(n,t.config)),r=he("span",{class:t.config.classNames.menu.value});r.innerHTML=e[n],s.appendChild(r),a.appendChild(s),w.appendChild(a);var o=he("div",{id:"plyr-settings-".concat(e.id,"-").concat(n),hidden:""}),l=he("button",{type:"button",class:"".concat(t.config.classNames.control," ").concat(t.config.classNames.control,"--back")});l.appendChild(he("span",{"aria-hidden":!0},Xe(n,t.config))),l.appendChild(he("span",{class:t.config.classNames.hidden},Xe("menuBack",t.config))),xe.call(t,o,"keydown",(function(e){37===e.which&&(e.preventDefault(),e.stopPropagation(),c.call(t,"home",!0))}),!1),xe.call(t,l,"click",(function(){c.call(t,"home",!1)})),o.appendChild(l),o.appendChild(he("div",{role:"menu"})),v.appendChild(o),t.elements.settings.buttons[n]=a,t.elements.settings.panels[n]=o})),y.appendChild(v),g.appendChild(y),u.appendChild(g),t.elements.settings.popup=y,t.elements.settings.menu=g}if("pip"===o&&Ee.pip&&u.appendChild(n.call(t,"pip",d)),"airplay"===o&&Ee.airplay&&u.appendChild(n.call(t,"airplay",d)),"download"===o){var k=ce({},d,{element:"a",href:t.download,target:"_blank"});t.isHTML5&&(k.download="");var T=t.config.urls.download;!ne(T)&&t.isEmbed&&ce(k,{icon:"logo-".concat(t.provider),label:t.provider}),u.appendChild(n.call(t,"download",k))}"fullscreen"===o&&u.appendChild(n.call(t,"fullscreen",d))})),this.isHTML5&&o.call(this,Ve.getQualityOptions.call(this)),l.call(this),u},inject:function(){var e=this;if(this.config.loadSprite){var t=nt.getIconUrl.call(this);t.cors&&Ge(t.url,"sprite-plyr")}this.id=Math.floor(1e4*Math.random());var i=null;this.elements.controls=null;var n={id:this.id,seektime:this.config.seekTime,title:this.config.title},a=!0;X(this.config.controls)&&(this.config.controls=this.config.controls.call(this,n)),this.config.controls||(this.config.controls=[]),G(this.config.controls)||Y(this.config.controls)?i=this.config.controls:(i=nt.create.call(this,{id:this.id,seektime:this.config.seekTime,speed:this.speed,quality:this.quality,captions:rt.getLabel.call(this)}),a=!1);var s,r;if(a&&Y(this.config.controls)&&(s=i,Object.entries(n).forEach((function(e){var t=o(e,2),i=t[0],n=t[1];s=We(s,"{".concat(i,"}"),n)})),i=s),Y(this.config.selectors.controls.container)&&(r=document.querySelector(this.config.selectors.controls.container)),G(r)||(r=this.elements.container),r[G(i)?"insertAdjacentElement":"insertAdjacentHTML"]("afterbegin",i),G(this.elements.controls)||nt.findElements.call(this),!ae(this.elements.buttons)){var l=function(t){var i=e.config.classNames.controlPressed;Object.defineProperty(t,"pressed",{enumerable:!0,get:function(){return we(t,i)},set:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];be(t,i,e)}})};Object.values(this.elements.buttons).filter(Boolean).forEach((function(e){$(e)||J(e)?Array.from(e).filter(Boolean).forEach(l):l(e)}))}if(oe.isEdge&&re(r),this.config.tooltips.controls){var c=this.config,u=c.classNames,d=c.selectors,h="".concat(d.controls.wrapper," ").concat(d.labels," .").concat(u.hidden),m=Te.call(this,h);Array.from(m).forEach((function(t){be(t,e.config.classNames.hidden,!1),be(t,e.config.classNames.tooltip,!0)}))}}};function at(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=e;if(t){var n=document.createElement("a");n.href=i,i=n.href}try{return new URL(i)}catch(e){return null}}function st(e){var t=new URLSearchParams;return z(e)&&Object.entries(e).forEach((function(e){var i=o(e,2),n=i[0],a=i[1];t.set(n,a)})),t}var rt={setup:function(){if(this.supported.ui)if(!this.isVideo||this.isYouTube||this.isHTML5&&!Ee.textTracks)$(this.config.controls)&&this.config.controls.includes("settings")&&this.config.settings.includes("captions")&&nt.setCaptionsMenu.call(this);else{if(G(this.elements.captions)||(this.elements.captions=he("div",ye(this.config.selectors.captions)),function(e,t){G(e)&&G(t)&&t.parentNode.insertBefore(e,t.nextSibling)}(this.elements.captions,this.elements.wrapper)),oe.isIE&&window.URL){var e=this.media.querySelectorAll("track");Array.from(e).forEach((function(e){var t=e.getAttribute("src"),i=at(t);null!==i&&i.hostname!==window.location.href.hostname&&["http:","https:"].includes(i.protocol)&&Je(t,"blob").then((function(t){e.setAttribute("src",window.URL.createObjectURL(t))})).catch((function(){pe(e)}))}))}var t=Be((navigator.languages||[navigator.language||navigator.userLanguage||"en"]).map((function(e){return e.split("-")[0]}))),i=(this.storage.get("language")||this.config.captions.language||"auto").toLowerCase();if("auto"===i)i=o(t,1)[0];var n=this.storage.get("captions");if(Q(n)||(n=this.config.captions.active),Object.assign(this.captions,{toggled:!1,active:n,language:i,languages:t}),this.isHTML5){var a=this.config.captions.update?"addtrack removetrack":"removetrack";xe.call(this,this.media.textTracks,a,rt.update.bind(this))}setTimeout(rt.update.bind(this),0)}},update:function(){var e=this,t=rt.getTracks.call(this,!0),i=this.captions,n=i.active,a=i.language,s=i.meta,r=i.currentTrackNode,o=Boolean(t.find((function(e){return e.language===a})));this.isHTML5&&this.isVideo&&t.filter((function(e){return!s.get(e)})).forEach((function(t){e.debug.log("Track added",t),s.set(t,{default:"showing"===t.mode}),"showing"===t.mode&&(t.mode="hidden"),xe.call(e,t,"cuechange",(function(){return rt.updateCues.call(e)}))})),(o&&this.language!==a||!t.includes(r))&&(rt.setLanguage.call(this,a),rt.toggle.call(this,n&&o)),be(this.elements.container,this.config.classNames.captions.enabled,!ae(t)),$(this.config.controls)&&this.config.controls.includes("settings")&&this.config.settings.includes("captions")&&nt.setCaptionsMenu.call(this)},toggle:function(e){var t=this,i=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(this.supported.ui){var n=this.captions.toggled,a=this.config.classNames.captions.active,s=W(e)?!n:e;if(s!==n){if(i||(this.captions.active=s,this.storage.set({captions:s})),!this.language&&s&&!i){var r=rt.getTracks.call(this),o=rt.findTrack.call(this,[this.captions.language].concat(l(this.captions.languages)),!0);return this.captions.language=o.language,void rt.set.call(this,r.indexOf(o))}this.elements.buttons.captions&&(this.elements.buttons.captions.pressed=s),be(this.elements.container,a,s),this.captions.toggled=s,nt.updateSetting.call(this,"captions"),Oe.call(this,this.media,s?"captionsenabled":"captionsdisabled")}setTimeout((function(){s&&t.captions.toggled&&(t.captions.currentTrackNode.mode="hidden")}))}},set:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=rt.getTracks.call(this);if(-1!==e)if(K(e))if(e in i){if(this.captions.currentTrack!==e){this.captions.currentTrack=e;var n=i[e],a=n||{},s=a.language;this.captions.currentTrackNode=n,nt.updateSetting.call(this,"captions"),t||(this.captions.language=s,this.storage.set({language:s})),this.isVimeo&&this.embed.enableTextTrack(s),Oe.call(this,this.media,"languagechange")}rt.toggle.call(this,!0,t),this.isHTML5&&this.isVideo&&rt.updateCues.call(this)}else this.debug.warn("Track not found",e);else this.debug.warn("Invalid caption argument",e);else rt.toggle.call(this,!1,t)},setLanguage:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(Y(e)){var i=e.toLowerCase();this.captions.language=i;var n=rt.getTracks.call(this),a=rt.findTrack.call(this,[i]);rt.set.call(this,n.indexOf(a),t)}else this.debug.warn("Invalid language argument",e)},getTracks:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],i=Array.from((this.media||{}).textTracks||[]);return i.filter((function(i){return!e.isHTML5||t||e.captions.meta.has(i)})).filter((function(e){return["captions","subtitles"].includes(e.kind)}))},findTrack:function(e){var t,i=this,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],a=rt.getTracks.call(this),s=function(e){return Number((i.captions.meta.get(e)||{}).default)},r=Array.from(a).sort((function(e,t){return s(t)-s(e)}));return e.every((function(e){return!(t=r.find((function(t){return t.language===e})))})),t||(n?r[0]:void 0)},getCurrentTrack:function(){return rt.getTracks.call(this)[this.currentTrack]},getLabel:function(e){var t=e;return!te(t)&&Ee.textTracks&&this.captions.toggled&&(t=rt.getCurrentTrack.call(this)),te(t)?ae(t.label)?ae(t.language)?Xe("enabled",this.config):e.language.toUpperCase():t.label:Xe("disabled",this.config)},updateCues:function(e){if(this.supported.ui)if(G(this.elements.captions))if(W(e)||Array.isArray(e)){var t=e;if(!t){var i=rt.getCurrentTrack.call(this);t=Array.from((i||{}).activeCues||[]).map((function(e){return e.getCueAsHTML()})).map(Ye)}var n=t.map((function(e){return e.trim()})).join("\n");if(n!==this.elements.captions.innerHTML){fe(this.elements.captions);var a=he("span",ye(this.config.selectors.caption));a.innerHTML=n,this.elements.captions.appendChild(a),Oe.call(this,this.media,"cuechange")}}else this.debug.warn("updateCues: Invalid input",e);else this.debug.warn("No captions element to render to")}},ot={enabled:!0,title:"",debug:!1,autoplay:!1,autopause:!0,playsinline:!0,seekTime:10,volume:1,muted:!1,duration:null,displayDuration:!0,invertTime:!0,toggleInvert:!0,ratio:null,clickToPlay:!0,hideControls:!0,resetOnEnd:!1,disableContextMenu:!0,loadSprite:!0,iconPrefix:"plyr",iconUrl:"https://cdn.plyr.io/3.6.3/plyr.svg",blankVideo:"https://cdn.plyr.io/static/blank.mp4",quality:{default:576,options:[4320,2880,2160,1440,1080,720,576,480,360,240],forced:!1,onChange:null},loop:{active:!1},speed:{selected:1,options:[.5,.75,1,1.25,1.5,1.75,2,4]},keyboard:{focused:!0,global:!1},tooltips:{controls:!1,seek:!0},captions:{active:!1,language:"auto",update:!1},fullscreen:{enabled:!0,fallback:!0,iosNative:!1},storage:{enabled:!0,key:"plyr"},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","pip","airplay","fullscreen"],settings:["captions","quality","speed"],i18n:{restart:"Restart",rewind:"Rewind {seektime}s",play:"Play",pause:"Pause",fastForward:"Forward {seektime}s",seek:"Seek",seekLabel:"{currentTime} of {duration}",played:"Played",buffered:"Buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",mute:"Mute",unmute:"Unmute",enableCaptions:"Enable captions",disableCaptions:"Disable captions",download:"Download",enterFullscreen:"Enter fullscreen",exitFullscreen:"Exit fullscreen",frameTitle:"Player for {title}",captions:"Captions",settings:"Settings",pip:"PIP",menuBack:"Go back to previous menu",speed:"Speed",normal:"Normal",quality:"Quality",loop:"Loop",start:"Start",end:"End",all:"All",reset:"Reset",disabled:"Disabled",enabled:"Enabled",advertisement:"Ad",qualityBadge:{2160:"4K",1440:"HD",1080:"HD",720:"HD",576:"SD",480:"SD"}},urls:{download:null,vimeo:{sdk:"https://player.vimeo.com/api/player.js",iframe:"https://player.vimeo.com/video/{0}?{1}",api:"https://vimeo.com/api/oembed.json?url={0}"},youtube:{sdk:"https://www.youtube.com/iframe_api",api:"https://noembed.com/embed?url=https://www.youtube.com/watch?v={0}"},googleIMA:{sdk:"https://imasdk.googleapis.com/js/sdkloader/ima3.js"}},listeners:{seek:null,play:null,pause:null,restart:null,rewind:null,fastForward:null,mute:null,volume:null,captions:null,download:null,fullscreen:null,pip:null,airplay:null,speed:null,quality:null,loop:null,language:null},events:["ended","progress","stalled","playing","waiting","canplay","canplaythrough","loadstart","loadeddata","loadedmetadata","timeupdate","volumechange","play","pause","error","seeking","seeked","emptied","ratechange","cuechange","download","enterfullscreen","exitfullscreen","captionsenabled","captionsdisabled","languagechange","controlshidden","controlsshown","ready","statechange","qualitychange","adsloaded","adscontentpause","adscontentresume","adstarted","adsmidpoint","adscomplete","adsallcomplete","adsimpression","adsclick"],selectors:{editable:"input, textarea, select, [contenteditable]",container:".plyr",controls:{container:null,wrapper:".plyr__controls"},labels:"[data-plyr]",buttons:{play:'[data-plyr="play"]',pause:'[data-plyr="pause"]',restart:'[data-plyr="restart"]',rewind:'[data-plyr="rewind"]',fastForward:'[data-plyr="fast-forward"]',mute:'[data-plyr="mute"]',captions:'[data-plyr="captions"]',download:'[data-plyr="download"]',fullscreen:'[data-plyr="fullscreen"]',pip:'[data-plyr="pip"]',airplay:'[data-plyr="airplay"]',settings:'[data-plyr="settings"]',loop:'[data-plyr="loop"]'},inputs:{seek:'[data-plyr="seek"]',volume:'[data-plyr="volume"]',speed:'[data-plyr="speed"]',language:'[data-plyr="language"]',quality:'[data-plyr="quality"]'},display:{currentTime:".plyr__time--current",duration:".plyr__time--duration",buffer:".plyr__progress__buffer",loop:".plyr__progress__loop",volume:".plyr__volume--display"},progress:".plyr__progress",captions:".plyr__captions",caption:".plyr__caption"},classNames:{type:"plyr--{0}",provider:"plyr--{0}",video:"plyr__video-wrapper",embed:"plyr__video-embed",videoFixedRatio:"plyr__video-wrapper--fixed-ratio",embedContainer:"plyr__video-embed__container",poster:"plyr__poster",posterEnabled:"plyr__poster-enabled",ads:"plyr__ads",control:"plyr__control",controlPressed:"plyr__control--pressed",playing:"plyr--playing",paused:"plyr--paused",stopped:"plyr--stopped",loading:"plyr--loading",hover:"plyr--hover",tooltip:"plyr__tooltip",cues:"plyr__cues",hidden:"plyr__sr-only",hideControls:"plyr--hide-controls",isIos:"plyr--is-ios",isTouch:"plyr--is-touch",uiSupported:"plyr--full-ui",noTransition:"plyr--no-transition",display:{time:"plyr__time"},menu:{value:"plyr__menu__value",badge:"plyr__badge",open:"plyr--menu-open"},captions:{enabled:"plyr--captions-enabled",active:"plyr--captions-active"},fullscreen:{enabled:"plyr--fullscreen-enabled",fallback:"plyr--fullscreen-fallback"},pip:{supported:"plyr--pip-supported",active:"plyr--pip-active"},airplay:{supported:"plyr--airplay-supported",active:"plyr--airplay-active"},tabFocus:"plyr__tab-focus",previewThumbnails:{thumbContainer:"plyr__preview-thumb",thumbContainerShown:"plyr__preview-thumb--is-shown",imageContainer:"plyr__preview-thumb__image-container",timeContainer:"plyr__preview-thumb__time-container",scrubbingContainer:"plyr__preview-scrubbing",scrubbingContainerShown:"plyr__preview-scrubbing--is-shown"}},attributes:{embed:{provider:"data-plyr-provider",id:"data-plyr-embed-id"}},ads:{enabled:!1,publisherId:"",tagUrl:""},previewThumbnails:{enabled:!1,src:""},vimeo:{byline:!1,portrait:!1,title:!1,speed:!0,transparent:!1,customControls:!0,referrerPolicy:null,premium:!1},youtube:{rel:0,showinfo:0,iv_load_policy:3,modestbranding:1,customControls:!0,noCookie:!1}},lt="picture-in-picture",ct="inline",ut={html5:"html5",youtube:"youtube",vimeo:"vimeo"},dt="audio",ht="video";var mt=function(){},pt=function(){function t(){var i=arguments.length>0&&void 0!==arguments[0]&&arguments[0];e(this,t),this.enabled=window.console&&i,this.enabled&&this.log("Debugging enabled")}return i(t,[{key:"log",get:function(){return this.enabled?Function.prototype.bind.call(console.log,console):mt}},{key:"warn",get:function(){return this.enabled?Function.prototype.bind.call(console.warn,console):mt}},{key:"error",get:function(){return this.enabled?Function.prototype.bind.call(console.error,console):mt}}]),t}(),ft=function(){function t(i){var n=this;e(this,t),this.player=i,this.prefix=t.prefix,this.property=t.property,this.scrollPosition={x:0,y:0},this.forceFallback="force"===i.config.fullscreen.fallback,this.player.elements.fullscreen=i.config.fullscreen.container&&function(e,t){return(Element.prototype.closest||function(){var e=this;do{if(ke.matches(e,t))return e;e=e.parentElement||e.parentNode}while(null!==e&&1===e.nodeType);return null}).call(e,t)}(this.player.elements.container,i.config.fullscreen.container),xe.call(this.player,document,"ms"===this.prefix?"MSFullscreenChange":"".concat(this.prefix,"fullscreenchange"),(function(){n.onChange()})),xe.call(this.player,this.player.elements.container,"dblclick",(function(e){G(n.player.elements.controls)&&n.player.elements.controls.contains(e.target)||n.player.listeners.proxy(e,n.toggle,"fullscreen")})),xe.call(this,this.player.elements.container,"keydown",(function(e){return n.trapFocus(e)})),this.update()}return i(t,[{key:"onChange",value:function(){if(this.enabled){var e=this.player.elements.buttons.fullscreen;G(e)&&(e.pressed=this.active);var t=this.target===this.player.media?this.target:this.player.elements.container;Oe.call(this.player,t,this.active?"enterfullscreen":"exitfullscreen",!0)}}},{key:"toggleFallback",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(e?this.scrollPosition={x:window.scrollX||0,y:window.scrollY||0}:window.scrollTo(this.scrollPosition.x,this.scrollPosition.y),document.body.style.overflow=e?"hidden":"",be(this.target,this.player.config.classNames.fullscreen.fallback,e),oe.isIos){var t=document.head.querySelector('meta[name="viewport"]'),i="viewport-fit=cover";t||(t=document.createElement("meta")).setAttribute("name","viewport");var n=Y(t.content)&&t.content.includes(i);e?(this.cleanupViewport=!n,n||(t.content+=",".concat(i))):this.cleanupViewport&&(t.content=t.content.split(",").filter((function(e){return e.trim()!==i})).join(","))}this.onChange()}},{key:"trapFocus",value:function(e){if(!oe.isIos&&this.active&&"Tab"===e.key&&9===e.keyCode){var t=document.activeElement,i=Te.call(this.player,"a[href], button:not(:disabled), input:not(:disabled), [tabindex]"),n=o(i,1)[0],a=i[i.length-1];t!==a||e.shiftKey?t===n&&e.shiftKey&&(a.focus(),e.preventDefault()):(n.focus(),e.preventDefault())}}},{key:"update",value:function(){var e;this.enabled?(e=this.forceFallback?"Fallback (forced)":t.native?"Native":"Fallback",this.player.debug.log("".concat(e," fullscreen enabled"))):this.player.debug.log("Fullscreen not supported and fallback disabled");be(this.player.elements.container,this.player.config.classNames.fullscreen.enabled,this.enabled)}},{key:"enter",value:function(){this.enabled&&(oe.isIos&&this.player.config.fullscreen.iosNative?this.target.webkitEnterFullscreen():!t.native||this.forceFallback?this.toggleFallback(!0):this.prefix?ae(this.prefix)||this.target["".concat(this.prefix,"Request").concat(this.property)]():this.target.requestFullscreen({navigationUI:"hide"}))}},{key:"exit",value:function(){if(this.enabled)if(oe.isIos&&this.player.config.fullscreen.iosNative)this.target.webkitExitFullscreen(),De(this.player.play());else if(!t.native||this.forceFallback)this.toggleFallback(!1);else if(this.prefix){if(!ae(this.prefix)){var e="moz"===this.prefix?"Cancel":"Exit";document["".concat(this.prefix).concat(e).concat(this.property)]()}}else(document.cancelFullScreen||document.exitFullscreen).call(document)}},{key:"toggle",value:function(){this.active?this.exit():this.enter()}},{key:"usingNative",get:function(){return t.native&&!this.forceFallback}},{key:"enabled",get:function(){return(t.native||this.player.config.fullscreen.fallback)&&this.player.config.fullscreen.enabled&&this.player.supported.ui&&this.player.isVideo}},{key:"active",get:function(){if(!this.enabled)return!1;if(!t.native||this.forceFallback)return we(this.target,this.player.config.classNames.fullscreen.fallback);var e=this.prefix?document["".concat(this.prefix).concat(this.property,"Element")]:document.fullscreenElement;return e&&e.shadowRoot?e===this.target.getRootNode().host:e===this.target}},{key:"target",get:function(){return oe.isIos&&this.player.config.fullscreen.iosNative?this.player.media:this.player.elements.fullscreen||this.player.elements.container}}],[{key:"native",get:function(){return!!(document.fullscreenEnabled||document.webkitFullscreenEnabled||document.mozFullScreenEnabled||document.msFullscreenEnabled)}},{key:"prefix",get:function(){if(X(document.exitFullscreen))return"";var e="";return["webkit","moz","ms"].some((function(t){return!(!X(document["".concat(t,"ExitFullscreen")])&&!X(document["".concat(t,"CancelFullScreen")]))&&(e=t,!0)})),e}},{key:"property",get:function(){return"moz"===this.prefix?"FullScreen":"Fullscreen"}}]),t}();function gt(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;return new Promise((function(i,n){var a=new Image,s=function(){delete a.onload,delete a.onerror,(a.naturalWidth>=t?i:n)(a)};Object.assign(a,{onload:s,onerror:s,src:e})}))}var yt={addStyleHook:function(){be(this.elements.container,this.config.selectors.container.replace(".",""),!0),be(this.elements.container,this.config.classNames.uiSupported,this.supported.ui)},toggleNativeControls:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];e&&this.isHTML5?this.media.setAttribute("controls",""):this.media.removeAttribute("controls")},build:function(){var e=this;if(this.listeners.media(),!this.supported.ui)return this.debug.warn("Basic support only for ".concat(this.provider," ").concat(this.type)),void yt.toggleNativeControls.call(this,!0);G(this.elements.controls)||(nt.inject.call(this),this.listeners.controls()),yt.toggleNativeControls.call(this),this.isHTML5&&rt.setup.call(this),this.volume=null,this.muted=null,this.loop=null,this.quality=null,this.speed=null,nt.updateVolume.call(this),nt.timeUpdate.call(this),yt.checkPlaying.call(this),be(this.elements.container,this.config.classNames.pip.supported,Ee.pip&&this.isHTML5&&this.isVideo),be(this.elements.container,this.config.classNames.airplay.supported,Ee.airplay&&this.isHTML5),be(this.elements.container,this.config.classNames.isIos,oe.isIos),be(this.elements.container,this.config.classNames.isTouch,this.touch),this.ready=!0,setTimeout((function(){Oe.call(e,e.media,"ready")}),0),yt.setTitle.call(this),this.poster&&yt.setPoster.call(this,this.poster,!1).catch((function(){})),this.config.duration&&nt.durationUpdate.call(this)},setTitle:function(){var e=Xe("play",this.config);if(Y(this.config.title)&&!ae(this.config.title)&&(e+=", ".concat(this.config.title)),Array.from(this.elements.buttons.play||[]).forEach((function(t){t.setAttribute("aria-label",e)})),this.isEmbed){var t=Ce.call(this,"iframe");if(!G(t))return;var i=ae(this.config.title)?"video":this.config.title,n=Xe("frameTitle",this.config);t.setAttribute("title",n.replace("{title}",i))}},togglePoster:function(e){be(this.elements.container,this.config.classNames.posterEnabled,e)},setPoster:function(e){var t=this,i=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return i&&this.poster?Promise.reject(new Error("Poster already set")):(this.media.setAttribute("data-poster",e),this.elements.poster.removeAttribute("hidden"),je.call(this).then((function(){return gt(e)})).catch((function(i){throw e===t.poster&&yt.togglePoster.call(t,!1),i})).then((function(){if(e!==t.poster)throw new Error("setPoster cancelled by later call to setPoster")})).then((function(){return Object.assign(t.elements.poster.style,{backgroundImage:"url('".concat(e,"')"),backgroundSize:""}),yt.togglePoster.call(t,!0),e})))},checkPlaying:function(e){var t=this;be(this.elements.container,this.config.classNames.playing,this.playing),be(this.elements.container,this.config.classNames.paused,this.paused),be(this.elements.container,this.config.classNames.stopped,this.stopped),Array.from(this.elements.buttons.play||[]).forEach((function(e){Object.assign(e,{pressed:t.playing}),e.setAttribute("aria-label",Xe(t.playing?"pause":"play",t.config))})),Z(e)&&"timeupdate"===e.type||yt.toggleControls.call(this)},checkLoading:function(e){var t=this;this.loading=["stalled","waiting"].includes(e.type),clearTimeout(this.timers.loading),this.timers.loading=setTimeout((function(){be(t.elements.container,t.config.classNames.loading,t.loading),yt.toggleControls.call(t)}),this.loading?250:0)},toggleControls:function(e){var t=this.elements.controls;if(t&&this.config.hideControls){var i=this.touch&&this.lastSeekTime+2e3>Date.now();this.toggleControls(Boolean(e||this.loading||this.paused||t.pressed||t.hover||i))}},migrateStyles:function(){var e=this;Object.values(s({},this.media.style)).filter((function(e){return!ae(e)&&Y(e)&&e.startsWith("--plyr")})).forEach((function(t){e.elements.container.style.setProperty(t,e.media.style.getPropertyValue(t)),e.media.style.removeProperty(t)})),ae(this.media.style)&&this.media.removeAttribute("style")}},vt=function(){function t(i){e(this,t),this.player=i,this.lastKey=null,this.focusTimer=null,this.lastKeyDown=null,this.handleKey=this.handleKey.bind(this),this.toggleMenu=this.toggleMenu.bind(this),this.setTabFocus=this.setTabFocus.bind(this),this.firstTouch=this.firstTouch.bind(this)}return i(t,[{key:"handleKey",value:function(e){var t=this.player,i=t.elements,n=e.keyCode?e.keyCode:e.which,a="keydown"===e.type,s=a&&n===this.lastKey;if(!(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey)&&K(n)){if(a){var r=document.activeElement;if(G(r)){var o=t.config.selectors.editable;if(r!==i.inputs.seek&&ke(r,o))return;if(32===e.which&&ke(r,'button, [role^="menuitem"]'))return}switch([32,37,38,39,40,48,49,50,51,52,53,54,56,57,67,70,73,75,76,77,79].includes(n)&&(e.preventDefault(),e.stopPropagation()),n){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:s||(t.currentTime=t.duration/10*(n-48));break;case 32:case 75:s||De(t.togglePlay());break;case 38:t.increaseVolume(.1);break;case 40:t.decreaseVolume(.1);break;case 77:s||(t.muted=!t.muted);break;case 39:t.forward();break;case 37:t.rewind();break;case 70:t.fullscreen.toggle();break;case 67:s||t.toggleCaptions();break;case 76:t.loop=!t.loop}27===n&&!t.fullscreen.usingNative&&t.fullscreen.active&&t.fullscreen.toggle(),this.lastKey=n}else this.lastKey=null}}},{key:"toggleMenu",value:function(e){nt.toggleMenu.call(this.player,e)}},{key:"firstTouch",value:function(){var e=this.player,t=e.elements;e.touch=!0,be(t.container,e.config.classNames.isTouch,!0)}},{key:"setTabFocus",value:function(e){var t=this.player,i=t.elements;if(clearTimeout(this.focusTimer),"keydown"!==e.type||9===e.which){"keydown"===e.type&&(this.lastKeyDown=e.timeStamp);var n,a=e.timeStamp-this.lastKeyDown<=20;if("focus"!==e.type||a)n=t.config.classNames.tabFocus,be(Te.call(t,".".concat(n)),n,!1),"focusout"!==e.type&&(this.focusTimer=setTimeout((function(){var e=document.activeElement;i.container.contains(e)&&be(document.activeElement,t.config.classNames.tabFocus,!0)}),10))}}},{key:"global",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],t=this.player;t.config.keyboard.global&&Me.call(t,window,"keydown keyup",this.handleKey,e,!1),Me.call(t,document.body,"click",this.toggleMenu,e),Le.call(t,document.body,"touchstart",this.firstTouch),Me.call(t,document.body,"keydown focus blur focusout",this.setTabFocus,e,!1,!0)}},{key:"container",value:function(){var e=this.player,t=e.config,i=e.elements,n=e.timers;!t.keyboard.global&&t.keyboard.focused&&xe.call(e,i.container,"keydown keyup",this.handleKey,!1),xe.call(e,i.container,"mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen",(function(t){var a=i.controls;a&&"enterfullscreen"===t.type&&(a.pressed=!1,a.hover=!1);var s=0;["touchstart","touchmove","mousemove"].includes(t.type)&&(yt.toggleControls.call(e,!0),s=e.touch?3e3:2e3),clearTimeout(n.controls),n.controls=setTimeout((function(){return yt.toggleControls.call(e,!1)}),s)}));var a=function(t){if(!t)return Re.call(e);var n=i.container.getBoundingClientRect(),a=n.width,s=n.height;return Re.call(e,"".concat(a,":").concat(s))},s=function(){clearTimeout(n.resized),n.resized=setTimeout(a,50)};xe.call(e,i.container,"enterfullscreen exitfullscreen",(function(t){var n=e.fullscreen,r=n.target,l=n.usingNative;if(r===i.container&&(e.isEmbed||!ae(e.config.ratio))){var c="enterfullscreen"===t.type,u=a(c);u.padding;!function(t,i,n){if(e.isVimeo&&!e.config.vimeo.premium){var a=e.elements.wrapper.firstChild,s=o(t,2)[1],r=o(Fe.call(e),2),l=r[0],c=r[1];a.style.maxWidth=n?"".concat(s/c*l,"px"):null,a.style.margin=n?"0 auto":null}}(u.ratio,0,c),c&&setTimeout((function(){return re(i.container)}),100),l||(c?xe.call(e,window,"resize",s):Ie.call(e,window,"resize",s))}}))}},{key:"media",value:function(){var e=this,t=this.player,i=t.elements;if(xe.call(t,t.media,"timeupdate seeking seeked",(function(e){return nt.timeUpdate.call(t,e)})),xe.call(t,t.media,"durationchange loadeddata loadedmetadata",(function(e){return nt.durationUpdate.call(t,e)})),xe.call(t,t.media,"ended",(function(){t.isHTML5&&t.isVideo&&t.config.resetOnEnd&&(t.restart(),t.pause())})),xe.call(t,t.media,"progress playing seeking seeked",(function(e){return nt.updateProgress.call(t,e)})),xe.call(t,t.media,"volumechange",(function(e){return nt.updateVolume.call(t,e)})),xe.call(t,t.media,"playing play pause ended emptied timeupdate",(function(e){return yt.checkPlaying.call(t,e)})),xe.call(t,t.media,"waiting canplay seeked playing",(function(e){return yt.checkLoading.call(t,e)})),t.supported.ui&&t.config.clickToPlay&&!t.isAudio){var n=Ce.call(t,".".concat(t.config.classNames.video));if(!G(n))return;xe.call(t,i.container,"click",(function(a){([i.container,n].includes(a.target)||n.contains(a.target))&&(t.touch&&t.config.hideControls||(t.ended?(e.proxy(a,t.restart,"restart"),e.proxy(a,(function(){De(t.play())}),"play")):e.proxy(a,(function(){De(t.togglePlay())}),"play")))}))}t.supported.ui&&t.config.disableContextMenu&&xe.call(t,i.wrapper,"contextmenu",(function(e){e.preventDefault()}),!1),xe.call(t,t.media,"volumechange",(function(){t.storage.set({volume:t.volume,muted:t.muted})})),xe.call(t,t.media,"ratechange",(function(){nt.updateSetting.call(t,"speed"),t.storage.set({speed:t.speed})})),xe.call(t,t.media,"qualitychange",(function(e){nt.updateSetting.call(t,"quality",null,e.detail.quality)})),xe.call(t,t.media,"ready qualitychange",(function(){nt.setDownloadUrl.call(t)}));var a=t.config.events.concat(["keyup","keydown"]).join(" ");xe.call(t,t.media,a,(function(e){var n=e.detail,a=void 0===n?{}:n;"error"===e.type&&(a=t.media.error),Oe.call(t,i.container,e.type,!0,a)}))}},{key:"proxy",value:function(e,t,i){var n=this.player,a=n.config.listeners[i],s=!0;X(a)&&(s=a.call(n,e)),!1!==s&&X(t)&&t.call(n,e)}},{key:"bind",value:function(e,t,i,n){var a=this,s=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],r=this.player,o=r.config.listeners[n],l=X(o);xe.call(r,e,t,(function(e){return a.proxy(e,i,n)}),s&&!l)}},{key:"controls",value:function(){var e=this,t=this.player,i=t.elements,n=oe.isIE?"change":"input";if(i.buttons.play&&Array.from(i.buttons.play).forEach((function(i){e.bind(i,"click",(function(){De(t.togglePlay())}),"play")})),this.bind(i.buttons.restart,"click",t.restart,"restart"),this.bind(i.buttons.rewind,"click",(function(){t.lastSeekTime=Date.now(),t.rewind()}),"rewind"),this.bind(i.buttons.fastForward,"click",(function(){t.lastSeekTime=Date.now(),t.forward()}),"fastForward"),this.bind(i.buttons.mute,"click",(function(){t.muted=!t.muted}),"mute"),this.bind(i.buttons.captions,"click",(function(){return t.toggleCaptions()})),this.bind(i.buttons.download,"click",(function(){Oe.call(t,t.media,"download")}),"download"),this.bind(i.buttons.fullscreen,"click",(function(){t.fullscreen.toggle()}),"fullscreen"),this.bind(i.buttons.pip,"click",(function(){t.pip="toggle"}),"pip"),this.bind(i.buttons.airplay,"click",t.airplay,"airplay"),this.bind(i.buttons.settings,"click",(function(e){e.stopPropagation(),e.preventDefault(),nt.toggleMenu.call(t,e)}),null,!1),this.bind(i.buttons.settings,"keyup",(function(e){var i=e.which;[13,32].includes(i)&&(13!==i?(e.preventDefault(),e.stopPropagation(),nt.toggleMenu.call(t,e)):nt.focusFirstMenuItem.call(t,null,!0))}),null,!1),this.bind(i.settings.menu,"keydown",(function(e){27===e.which&&nt.toggleMenu.call(t,e)})),this.bind(i.inputs.seek,"mousedown mousemove",(function(e){var t=i.progress.getBoundingClientRect(),n=100/t.width*(e.pageX-t.left);e.currentTarget.setAttribute("seek-value",n)})),this.bind(i.inputs.seek,"mousedown mouseup keydown keyup touchstart touchend",(function(e){var i=e.currentTarget,n=e.keyCode?e.keyCode:e.which,a="play-on-seeked";if(!ee(e)||39===n||37===n){t.lastSeekTime=Date.now();var s=i.hasAttribute(a),r=["mouseup","touchend","keyup"].includes(e.type);s&&r?(i.removeAttribute(a),De(t.play())):!r&&t.playing&&(i.setAttribute(a,""),t.pause())}})),oe.isIos){var a=Te.call(t,'input[type="range"]');Array.from(a).forEach((function(t){return e.bind(t,n,(function(e){return re(e.target)}))}))}this.bind(i.inputs.seek,n,(function(e){var i=e.currentTarget,n=i.getAttribute("seek-value");ae(n)&&(n=i.value),i.removeAttribute("seek-value"),t.currentTime=n/i.max*t.duration}),"seek"),this.bind(i.progress,"mouseenter mouseleave mousemove",(function(e){return nt.updateSeekTooltip.call(t,e)})),this.bind(i.progress,"mousemove touchmove",(function(e){var i=t.previewThumbnails;i&&i.loaded&&i.startMove(e)})),this.bind(i.progress,"mouseleave touchend click",(function(){var e=t.previewThumbnails;e&&e.loaded&&e.endMove(!1,!0)})),this.bind(i.progress,"mousedown touchstart",(function(e){var i=t.previewThumbnails;i&&i.loaded&&i.startScrubbing(e)})),this.bind(i.progress,"mouseup touchend",(function(e){var i=t.previewThumbnails;i&&i.loaded&&i.endScrubbing(e)})),oe.isWebkit&&Array.from(Te.call(t,'input[type="range"]')).forEach((function(i){e.bind(i,"input",(function(e){return nt.updateRangeFill.call(t,e.target)}))})),t.config.toggleInvert&&!G(i.display.duration)&&this.bind(i.display.currentTime,"click",(function(){0!==t.currentTime&&(t.config.invertTime=!t.config.invertTime,nt.timeUpdate.call(t))})),this.bind(i.inputs.volume,n,(function(e){t.volume=e.target.value}),"volume"),this.bind(i.controls,"mouseenter mouseleave",(function(e){i.controls.hover=!t.touch&&"mouseenter"===e.type})),i.fullscreen&&Array.from(i.fullscreen.children).filter((function(e){return!e.contains(i.container)})).forEach((function(n){e.bind(n,"mouseenter mouseleave",(function(e){i.controls.hover=!t.touch&&"mouseenter"===e.type}))})),this.bind(i.controls,"mousedown mouseup touchstart touchend touchcancel",(function(e){i.controls.pressed=["mousedown","touchstart"].includes(e.type)})),this.bind(i.controls,"focusin",(function(){var n=t.config,a=t.timers;be(i.controls,n.classNames.noTransition,!0),yt.toggleControls.call(t,!0),setTimeout((function(){be(i.controls,n.classNames.noTransition,!1)}),0);var s=e.touch?3e3:4e3;clearTimeout(a.controls),a.controls=setTimeout((function(){return yt.toggleControls.call(t,!1)}),s)})),this.bind(i.inputs.volume,"wheel",(function(e){var i=e.webkitDirectionInvertedFromDevice,n=o([e.deltaX,-e.deltaY].map((function(e){return i?-e:e})),2),a=n[0],s=n[1],r=Math.sign(Math.abs(a)>Math.abs(s)?a:s);t.increaseVolume(r/50);var l=t.media.volume;(1===r&&l<1||-1===r&&l>0)&&e.preventDefault()}),"volume",!1)}}]),t}();"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;var bt=function(e,t){return e(t={exports:{}},t.exports),t.exports}((function(e,t){e.exports=function(){var e=function(){},t={},i={},n={};function a(e,t){e=e.push?e:[e];var a,s,r,o=[],l=e.length,c=l;for(a=function(e,i){i.length&&o.push(e),--c||t(o)};l--;)s=e[l],(r=i[s])?a(s,r):(n[s]=n[s]||[]).push(a)}function s(e,t){if(e){var a=n[e];if(i[e]=t,a)for(;a.length;)a[0](e,t),a.splice(0,1)}}function r(t,i){t.call&&(t={success:t}),i.length?(t.error||e)(i):(t.success||e)(t)}function o(t,i,n,a){var s,r,l=document,c=n.async,u=(n.numRetries||0)+1,d=n.before||e,h=t.replace(/[\?|#].*$/,""),m=t.replace(/^(css|img)!/,"");a=a||0,/(^css!|\.css$)/.test(h)?((r=l.createElement("link")).rel="stylesheet",r.href=m,(s="hideFocus"in r)&&r.relList&&(s=0,r.rel="preload",r.as="style")):/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(h)?(r=l.createElement("img")).src=m:((r=l.createElement("script")).src=t,r.async=void 0===c||c),r.onload=r.onerror=r.onbeforeload=function(e){var l=e.type[0];if(s)try{r.sheet.cssText.length||(l="e")}catch(e){18!=e.code&&(l="e")}if("e"==l){if((a+=1)<u)return o(t,i,n,a)}else if("preload"==r.rel&&"style"==r.as)return r.rel="stylesheet";i(t,l,e.defaultPrevented)},!1!==d(t,r)&&l.head.appendChild(r)}function l(e,t,i){var n,a,s=(e=e.push?e:[e]).length,r=s,l=[];for(n=function(e,i,n){if("e"==i&&l.push(e),"b"==i){if(!n)return;l.push(e)}--s||t(l)},a=0;a<r;a++)o(e[a],n,i)}function c(e,i,n){var a,o;if(i&&i.trim&&(a=i),o=(a?n:i)||{},a){if(a in t)throw"LoadJS";t[a]=!0}function c(t,i){l(e,(function(e){r(o,e),t&&r({success:t,error:i},e),s(a,e)}),o)}if(o.returnPromise)return new Promise(c);c()}return c.ready=function(e,t){return a(e,(function(e){r(t,e)})),c},c.done=function(e){s(e,[])},c.reset=function(){t={},i={},n={}},c.isDefined=function(e){return e in t},c}()}));function wt(e){return new Promise((function(t,i){bt(e,{success:t,error:i})}))}function kt(e){e&&!this.embed.hasPlayed&&(this.embed.hasPlayed=!0),this.media.paused===e&&(this.media.paused=!e,Oe.call(this,this.media,e?"play":"pause"))}var Tt={setup:function(){var e=this;be(e.elements.wrapper,e.config.classNames.embed,!0),e.options.speed=e.config.speed.options,Re.call(e),z(window.Vimeo)?Tt.ready.call(e):wt(e.config.urls.vimeo.sdk).then((function(){Tt.ready.call(e)})).catch((function(t){e.debug.warn("Vimeo SDK (player.js) failed to load",t)}))},ready:function(){var e=this,t=this,i=t.config.vimeo,n=i.premium,a=i.referrerPolicy,l=r(i,["premium","referrerPolicy"]);n&&Object.assign(l,{controls:!1,sidedock:!1});var c=st(s({loop:t.config.loop.active,autoplay:t.autoplay,muted:t.muted,gesture:"media",playsinline:!this.config.fullscreen.iosNative},l)),u=t.media.getAttribute("src");ae(u)&&(u=t.media.getAttribute(t.config.attributes.embed.id));var d,h=ae(d=u)?null:K(Number(d))?d:d.match(/^.*(vimeo.com\/|video\/)(\d+).*/)?RegExp.$2:d,m=he("iframe"),p=Ue(t.config.urls.vimeo.iframe,h,c);if(m.setAttribute("src",p),m.setAttribute("allowfullscreen",""),m.setAttribute("allow","autoplay,fullscreen,picture-in-picture"),ae(a)||m.setAttribute("referrerPolicy",a),n||!i.customControls)m.setAttribute("data-poster",t.poster),t.media=ge(m,t.media);else{var f=he("div",{class:t.config.classNames.embedContainer,"data-poster":t.poster});f.appendChild(m),t.media=ge(f,t.media)}i.customControls||Je(Ue(t.config.urls.vimeo.api,p)).then((function(e){!ae(e)&&e.thumbnail_url&&yt.setPoster.call(t,e.thumbnail_url).catch((function(){}))})),t.embed=new window.Vimeo.Player(m,{autopause:t.config.autopause,muted:t.muted}),t.media.paused=!0,t.media.currentTime=0,t.supported.ui&&t.embed.disableTextTrack(),t.media.play=function(){return kt.call(t,!0),t.embed.play()},t.media.pause=function(){return kt.call(t,!1),t.embed.pause()},t.media.stop=function(){t.pause(),t.currentTime=0};var g=t.media.currentTime;Object.defineProperty(t.media,"currentTime",{get:function(){return g},set:function(e){var i=t.embed,n=t.media,a=t.paused,s=t.volume,r=a&&!i.hasPlayed;n.seeking=!0,Oe.call(t,n,"seeking"),Promise.resolve(r&&i.setVolume(0)).then((function(){return i.setCurrentTime(e)})).then((function(){return r&&i.pause()})).then((function(){return r&&i.setVolume(s)})).catch((function(){}))}});var y=t.config.speed.selected;Object.defineProperty(t.media,"playbackRate",{get:function(){return y},set:function(e){t.embed.setPlaybackRate(e).then((function(){y=e,Oe.call(t,t.media,"ratechange")})).catch((function(){t.options.speed=[1]}))}});var v=t.config.volume;Object.defineProperty(t.media,"volume",{get:function(){return v},set:function(e){t.embed.setVolume(e).then((function(){v=e,Oe.call(t,t.media,"volumechange")}))}});var b=t.config.muted;Object.defineProperty(t.media,"muted",{get:function(){return b},set:function(e){var i=!!Q(e)&&e;t.embed.setVolume(i?0:t.config.volume).then((function(){b=i,Oe.call(t,t.media,"volumechange")}))}});var w,k=t.config.loop;Object.defineProperty(t.media,"loop",{get:function(){return k},set:function(e){var i=Q(e)?e:t.config.loop.active;t.embed.setLoop(i).then((function(){k=i}))}}),t.embed.getVideoUrl().then((function(e){w=e,nt.setDownloadUrl.call(t)})).catch((function(t){e.debug.warn(t)})),Object.defineProperty(t.media,"currentSrc",{get:function(){return w}}),Object.defineProperty(t.media,"ended",{get:function(){return t.currentTime===t.duration}}),Promise.all([t.embed.getVideoWidth(),t.embed.getVideoHeight()]).then((function(i){var n=o(i,2),a=n[0],s=n[1];t.embed.ratio=[a,s],Re.call(e)})),t.embed.setAutopause(t.config.autopause).then((function(e){t.config.autopause=e})),t.embed.getVideoTitle().then((function(i){t.config.title=i,yt.setTitle.call(e)})),t.embed.getCurrentTime().then((function(e){g=e,Oe.call(t,t.media,"timeupdate")})),t.embed.getDuration().then((function(e){t.media.duration=e,Oe.call(t,t.media,"durationchange")})),t.embed.getTextTracks().then((function(e){t.media.textTracks=e,rt.setup.call(t)})),t.embed.on("cuechange",(function(e){var i=e.cues,n=(void 0===i?[]:i).map((function(e){return function(e){var t=document.createDocumentFragment(),i=document.createElement("div");return t.appendChild(i),i.innerHTML=e,t.firstChild.innerText}(e.text)}));rt.updateCues.call(t,n)})),t.embed.on("loaded",(function(){(t.embed.getPaused().then((function(e){kt.call(t,!e),e||Oe.call(t,t.media,"playing")})),G(t.embed.element)&&t.supported.ui)&&t.embed.element.setAttribute("tabindex",-1)})),t.embed.on("bufferstart",(function(){Oe.call(t,t.media,"waiting")})),t.embed.on("bufferend",(function(){Oe.call(t,t.media,"playing")})),t.embed.on("play",(function(){kt.call(t,!0),Oe.call(t,t.media,"playing")})),t.embed.on("pause",(function(){kt.call(t,!1)})),t.embed.on("timeupdate",(function(e){t.media.seeking=!1,g=e.seconds,Oe.call(t,t.media,"timeupdate")})),t.embed.on("progress",(function(e){t.media.buffered=e.percent,Oe.call(t,t.media,"progress"),1===parseInt(e.percent,10)&&Oe.call(t,t.media,"canplaythrough"),t.embed.getDuration().then((function(e){e!==t.media.duration&&(t.media.duration=e,Oe.call(t,t.media,"durationchange"))}))})),t.embed.on("seeked",(function(){t.media.seeking=!1,Oe.call(t,t.media,"seeked")})),t.embed.on("ended",(function(){t.media.paused=!0,Oe.call(t,t.media,"ended")})),t.embed.on("error",(function(e){t.media.error=e,Oe.call(t,t.media,"error")})),i.customControls&&setTimeout((function(){return yt.build.call(t)}),0)}};function Ct(e){e&&!this.embed.hasPlayed&&(this.embed.hasPlayed=!0),this.media.paused===e&&(this.media.paused=!e,Oe.call(this,this.media,e?"play":"pause"))}function At(e){return e.noCookie?"https://www.youtube-nocookie.com":"http:"===window.location.protocol?"http://www.youtube.com":void 0}var St={setup:function(){var e=this;if(be(this.elements.wrapper,this.config.classNames.embed,!0),z(window.YT)&&X(window.YT.Player))St.ready.call(this);else{var t=window.onYouTubeIframeAPIReady;window.onYouTubeIframeAPIReady=function(){X(t)&&t(),St.ready.call(e)},wt(this.config.urls.youtube.sdk).catch((function(t){e.debug.warn("YouTube API failed to load",t)}))}},getTitle:function(e){var t=this;Je(Ue(this.config.urls.youtube.api,e)).then((function(e){if(z(e)){var i=e.title,n=e.height,a=e.width;t.config.title=i,yt.setTitle.call(t),t.embed.ratio=[a,n]}Re.call(t)})).catch((function(){Re.call(t)}))},ready:function(){var e=this,t=e.config.youtube,i=e.media&&e.media.getAttribute("id");if(ae(i)||!i.startsWith("youtube-")){var n=e.media.getAttribute("src");ae(n)&&(n=e.media.getAttribute(this.config.attributes.embed.id));var a,s,r=ae(a=n)?null:a.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/)?RegExp.$2:a,o=he("div",{id:(s=e.provider,"".concat(s,"-").concat(Math.floor(1e4*Math.random()))),"data-poster":t.customControls?e.poster:void 0});if(e.media=ge(o,e.media),t.customControls){var l=function(e){return"https://i.ytimg.com/vi/".concat(r,"/").concat(e,"default.jpg")};gt(l("maxres"),121).catch((function(){return gt(l("sd"),121)})).catch((function(){return gt(l("hq"))})).then((function(t){return yt.setPoster.call(e,t.src)})).then((function(t){t.includes("maxres")||(e.elements.poster.style.backgroundSize="cover")})).catch((function(){}))}e.embed=new window.YT.Player(e.media,{videoId:r,host:At(t),playerVars:ce({},{autoplay:e.config.autoplay?1:0,hl:e.config.hl,controls:e.supported.ui&&t.customControls?0:1,disablekb:1,playsinline:e.config.fullscreen.iosNative?0:1,cc_load_policy:e.captions.active?1:0,cc_lang_pref:e.config.captions.language,widget_referrer:window?window.location.href:null},t),events:{onError:function(t){if(!e.media.error){var i=t.data,n={2:"The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.",5:"The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.",100:"The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.",101:"The owner of the requested video does not allow it to be played in embedded players.",150:"The owner of the requested video does not allow it to be played in embedded players."}[i]||"An unknown error occured";e.media.error={code:i,message:n},Oe.call(e,e.media,"error")}},onPlaybackRateChange:function(t){var i=t.target;e.media.playbackRate=i.getPlaybackRate(),Oe.call(e,e.media,"ratechange")},onReady:function(i){if(!X(e.media.play)){var n=i.target;St.getTitle.call(e,r),e.media.play=function(){Ct.call(e,!0),n.playVideo()},e.media.pause=function(){Ct.call(e,!1),n.pauseVideo()},e.media.stop=function(){n.stopVideo()},e.media.duration=n.getDuration(),e.media.paused=!0,e.media.currentTime=0,Object.defineProperty(e.media,"currentTime",{get:function(){return Number(n.getCurrentTime())},set:function(t){e.paused&&!e.embed.hasPlayed&&e.embed.mute(),e.media.seeking=!0,Oe.call(e,e.media,"seeking"),n.seekTo(t)}}),Object.defineProperty(e.media,"playbackRate",{get:function(){return n.getPlaybackRate()},set:function(e){n.setPlaybackRate(e)}});var a=e.config.volume;Object.defineProperty(e.media,"volume",{get:function(){return a},set:function(t){a=t,n.setVolume(100*a),Oe.call(e,e.media,"volumechange")}});var s=e.config.muted;Object.defineProperty(e.media,"muted",{get:function(){return s},set:function(t){var i=Q(t)?t:s;s=i,n[i?"mute":"unMute"](),n.setVolume(100*a),Oe.call(e,e.media,"volumechange")}}),Object.defineProperty(e.media,"currentSrc",{get:function(){return n.getVideoUrl()}}),Object.defineProperty(e.media,"ended",{get:function(){return e.currentTime===e.duration}});var o=n.getAvailablePlaybackRates();e.options.speed=o.filter((function(t){return e.config.speed.options.includes(t)})),e.supported.ui&&t.customControls&&e.media.setAttribute("tabindex",-1),Oe.call(e,e.media,"timeupdate"),Oe.call(e,e.media,"durationchange"),clearInterval(e.timers.buffering),e.timers.buffering=setInterval((function(){e.media.buffered=n.getVideoLoadedFraction(),(null===e.media.lastBuffered||e.media.lastBuffered<e.media.buffered)&&Oe.call(e,e.media,"progress"),e.media.lastBuffered=e.media.buffered,1===e.media.buffered&&(clearInterval(e.timers.buffering),Oe.call(e,e.media,"canplaythrough"))}),200),t.customControls&&setTimeout((function(){return yt.build.call(e)}),50)}},onStateChange:function(i){var n=i.target;switch(clearInterval(e.timers.playing),e.media.seeking&&[1,2].includes(i.data)&&(e.media.seeking=!1,Oe.call(e,e.media,"seeked")),i.data){case-1:Oe.call(e,e.media,"timeupdate"),e.media.buffered=n.getVideoLoadedFraction(),Oe.call(e,e.media,"progress");break;case 0:Ct.call(e,!1),e.media.loop?(n.stopVideo(),n.playVideo()):Oe.call(e,e.media,"ended");break;case 1:t.customControls&&!e.config.autoplay&&e.media.paused&&!e.embed.hasPlayed?e.media.pause():(Ct.call(e,!0),Oe.call(e,e.media,"playing"),e.timers.playing=setInterval((function(){Oe.call(e,e.media,"timeupdate")}),50),e.media.duration!==n.getDuration()&&(e.media.duration=n.getDuration(),Oe.call(e,e.media,"durationchange")));break;case 2:e.muted||e.embed.unMute(),Ct.call(e,!1);break;case 3:Oe.call(e,e.media,"waiting")}Oe.call(e,e.elements.container,"statechange",!1,{code:i.data})}}})}}},Pt={setup:function(){this.media?(be(this.elements.container,this.config.classNames.type.replace("{0}",this.type),!0),be(this.elements.container,this.config.classNames.provider.replace("{0}",this.provider),!0),this.isEmbed&&be(this.elements.container,this.config.classNames.type.replace("{0}","video"),!0),this.isVideo&&(this.elements.wrapper=he("div",{class:this.config.classNames.video}),ue(this.media,this.elements.wrapper),this.elements.poster=he("div",{class:this.config.classNames.poster,hidden:""}),this.elements.wrapper.appendChild(this.elements.poster)),this.isHTML5?Ve.setup.call(this):this.isYouTube?St.setup.call(this):this.isVimeo&&Tt.setup.call(this)):this.debug.warn("No media element found!")}},Et=function(){function t(i){var n=this;e(this,t),this.player=i,this.config=i.config.ads,this.playing=!1,this.initialized=!1,this.elements={container:null,displayContainer:null},this.manager=null,this.loader=null,this.cuePoints=null,this.events={},this.safetyTimer=null,this.countdownTimer=null,this.managerPromise=new Promise((function(e,t){n.on("loaded",e),n.on("error",t)})),this.load()}return i(t,[{key:"load",value:function(){var e=this;this.enabled&&(z(window.google)&&z(window.google.ima)?this.ready():wt(this.player.config.urls.googleIMA.sdk).then((function(){e.ready()})).catch((function(){e.trigger("error",new Error("Google IMA SDK failed to load"))})))}},{key:"ready",value:function(){var e,t=this;this.enabled||((e=this).manager&&e.manager.destroy(),e.elements.displayContainer&&e.elements.displayContainer.destroy(),e.elements.container.remove()),this.startSafetyTimer(12e3,"ready()"),this.managerPromise.then((function(){t.clearSafetyTimer("onAdsManagerLoaded()")})),this.listeners(),this.setupIMA()}},{key:"setupIMA",value:function(){var e=this;this.elements.container=he("div",{class:this.player.config.classNames.ads}),this.player.elements.container.appendChild(this.elements.container),google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED),google.ima.settings.setLocale(this.player.config.ads.language),google.ima.settings.setDisableCustomPlaybackForIOS10Plus(this.player.config.playsinline),this.elements.displayContainer=new google.ima.AdDisplayContainer(this.elements.container,this.player.media),this.loader=new google.ima.AdsLoader(this.elements.displayContainer),this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,(function(t){return e.onAdsManagerLoaded(t)}),!1),this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,(function(t){return e.onAdError(t)}),!1),this.requestAds()}},{key:"requestAds",value:function(){var e=this.player.elements.container;try{var t=new google.ima.AdsRequest;t.adTagUrl=this.tagUrl,t.linearAdSlotWidth=e.offsetWidth,t.linearAdSlotHeight=e.offsetHeight,t.nonLinearAdSlotWidth=e.offsetWidth,t.nonLinearAdSlotHeight=e.offsetHeight,t.forceNonLinearFullSlot=!1,t.setAdWillPlayMuted(!this.player.muted),this.loader.requestAds(t)}catch(e){this.onAdError(e)}}},{key:"pollCountdown",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!t)return clearInterval(this.countdownTimer),void this.elements.container.removeAttribute("data-badge-text");var i=function(){var t=it(Math.max(e.manager.getRemainingTime(),0)),i="".concat(Xe("advertisement",e.player.config)," - ").concat(t);e.elements.container.setAttribute("data-badge-text",i)};this.countdownTimer=setInterval(i,100)}},{key:"onAdsManagerLoaded",value:function(e){var t=this;if(this.enabled){var i=new google.ima.AdsRenderingSettings;i.restoreCustomPlaybackStateOnAdBreakComplete=!0,i.enablePreloading=!0,this.manager=e.getAdsManager(this.player,i),this.cuePoints=this.manager.getCuePoints(),this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,(function(e){return t.onAdError(e)})),Object.keys(google.ima.AdEvent.Type).forEach((function(e){t.manager.addEventListener(google.ima.AdEvent.Type[e],(function(e){return t.onAdEvent(e)}))})),this.trigger("loaded")}}},{key:"addCuePoints",value:function(){var e=this;ae(this.cuePoints)||this.cuePoints.forEach((function(t){if(0!==t&&-1!==t&&t<e.player.duration){var i=e.player.elements.progress;if(G(i)){var n=100/e.player.duration*t,a=he("span",{class:e.player.config.classNames.cues});a.style.left="".concat(n.toString(),"%"),i.appendChild(a)}}}))}},{key:"onAdEvent",value:function(e){var t=this,i=this.player.elements.container,n=e.getAd(),a=e.getAdData();switch(function(e){Oe.call(t.player,t.player.media,"ads".concat(e.replace(/_/g,"").toLowerCase()))}(e.type),e.type){case google.ima.AdEvent.Type.LOADED:this.trigger("loaded"),this.pollCountdown(!0),n.isLinear()||(n.width=i.offsetWidth,n.height=i.offsetHeight);break;case google.ima.AdEvent.Type.STARTED:this.manager.setVolume(this.player.volume);break;case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:this.player.ended?this.loadAds():this.loader.contentComplete();break;case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:this.pauseContent();break;case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED:this.pollCountdown(),this.resumeContent();break;case google.ima.AdEvent.Type.LOG:a.adError&&this.player.debug.warn("Non-fatal ad error: ".concat(a.adError.getMessage()))}}},{key:"onAdError",value:function(e){this.cancel(),this.player.debug.warn("Ads error",e)}},{key:"listeners",value:function(){var e,t=this,i=this.player.elements.container;this.player.on("canplay",(function(){t.addCuePoints()})),this.player.on("ended",(function(){t.loader.contentComplete()})),this.player.on("timeupdate",(function(){e=t.player.currentTime})),this.player.on("seeked",(function(){var i=t.player.currentTime;ae(t.cuePoints)||t.cuePoints.forEach((function(n,a){e<n&&n<i&&(t.manager.discardAdBreak(),t.cuePoints.splice(a,1))}))})),window.addEventListener("resize",(function(){t.manager&&t.manager.resize(i.offsetWidth,i.offsetHeight,google.ima.ViewMode.NORMAL)}))}},{key:"play",value:function(){var e=this,t=this.player.elements.container;this.managerPromise||this.resumeContent(),this.managerPromise.then((function(){e.manager.setVolume(e.player.volume),e.elements.displayContainer.initialize();try{e.initialized||(e.manager.init(t.offsetWidth,t.offsetHeight,google.ima.ViewMode.NORMAL),e.manager.start()),e.initialized=!0}catch(t){e.onAdError(t)}})).catch((function(){}))}},{key:"resumeContent",value:function(){this.elements.container.style.zIndex="",this.playing=!1,De(this.player.media.play())}},{key:"pauseContent",value:function(){this.elements.container.style.zIndex=3,this.playing=!0,this.player.media.pause()}},{key:"cancel",value:function(){this.initialized&&this.resumeContent(),this.trigger("error"),this.loadAds()}},{key:"loadAds",value:function(){var e=this;this.managerPromise.then((function(){e.manager&&e.manager.destroy(),e.managerPromise=new Promise((function(t){e.on("loaded",t),e.player.debug.log(e.manager)})),e.initialized=!1,e.requestAds()})).catch((function(){}))}},{key:"trigger",value:function(e){for(var t=this,i=arguments.length,n=new Array(i>1?i-1:0),a=1;a<i;a++)n[a-1]=arguments[a];var s=this.events[e];$(s)&&s.forEach((function(e){X(e)&&e.apply(t,n)}))}},{key:"on",value:function(e,t){return $(this.events[e])||(this.events[e]=[]),this.events[e].push(t),this}},{key:"startSafetyTimer",value:function(e,t){var i=this;this.player.debug.log("Safety timer invoked from: ".concat(t)),this.safetyTimer=setTimeout((function(){i.cancel(),i.clearSafetyTimer("startSafetyTimer()")}),e)}},{key:"clearSafetyTimer",value:function(e){W(this.safetyTimer)||(this.player.debug.log("Safety timer cleared from: ".concat(e)),clearTimeout(this.safetyTimer),this.safetyTimer=null)}},{key:"enabled",get:function(){var e=this.config;return this.player.isHTML5&&this.player.isVideo&&e.enabled&&(!ae(e.publisherId)||ne(e.tagUrl))}},{key:"tagUrl",get:function(){var e=this.config;if(ne(e.tagUrl))return e.tagUrl;var t={AV_PUBLISHERID:"58c25bb0073ef448b1087ad6",AV_CHANNELID:"5a0458dc28a06145e4519d21",AV_URL:window.location.hostname,cb:Date.now(),AV_WIDTH:640,AV_HEIGHT:480,AV_CDIM2:e.publisherId};return"".concat("https://go.aniview.com/api/adserver6/vast/","?").concat(st(t))}}]),t}(),Nt=function(e,t){var i={};return e>t.width/t.height?(i.width=t.width,i.height=1/e*t.width):(i.height=t.height,i.width=e*t.height),i},Mt=function(){function t(i){e(this,t),this.player=i,this.thumbnails=[],this.loaded=!1,this.lastMouseMoveTime=Date.now(),this.mouseDown=!1,this.loadedImages=[],this.elements={thumb:{},scrubbing:{}},this.load()}return i(t,[{key:"load",value:function(){var e=this;this.player.elements.display.seekTooltip&&(this.player.elements.display.seekTooltip.hidden=this.enabled),this.enabled&&this.getThumbnails().then((function(){e.enabled&&(e.render(),e.determineContainerAutoSizing(),e.loaded=!0)}))}},{key:"getThumbnails",value:function(){var e=this;return new Promise((function(t){var i=e.player.config.previewThumbnails.src;if(ae(i))throw new Error("Missing previewThumbnails.src config attribute");var n=function(){e.thumbnails.sort((function(e,t){return e.height-t.height})),e.player.debug.log("Preview thumbnails",e.thumbnails),t()};if(X(i))i((function(t){e.thumbnails=t,n()}));else{var a=(Y(i)?[i]:i).map((function(t){return e.getThumbnail(t)}));Promise.all(a).then(n)}}))}},{key:"getThumbnail",value:function(e){var t=this;return new Promise((function(i){Je(e).then((function(n){var a,s,r={frames:(a=n,s=[],a.split(/\r\n\r\n|\n\n|\r\r/).forEach((function(e){var t={};e.split(/\r\n|\n|\r/).forEach((function(e){if(K(t.startTime)){if(!ae(e.trim())&&ae(t.text)){var i=e.trim().split("#xywh="),n=o(i,1);if(t.text=n[0],i[1]){var a=o(i[1].split(","),4);t.x=a[0],t.y=a[1],t.w=a[2],t.h=a[3]}}}else{var s=e.match(/([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})/);s&&(t.startTime=60*Number(s[1]||0)*60+60*Number(s[2])+Number(s[3])+Number("0.".concat(s[4])),t.endTime=60*Number(s[6]||0)*60+60*Number(s[7])+Number(s[8])+Number("0.".concat(s[9])))}})),t.text&&s.push(t)})),s),height:null,urlPrefix:""};r.frames[0].text.startsWith("/")||r.frames[0].text.startsWith("http://")||r.frames[0].text.startsWith("https://")||(r.urlPrefix=e.substring(0,e.lastIndexOf("/")+1));var l=new Image;l.onload=function(){r.height=l.naturalHeight,r.width=l.naturalWidth,t.thumbnails.push(r),i()},l.src=r.urlPrefix+r.frames[0].text}))}))}},{key:"startMove",value:function(e){if(this.loaded&&Z(e)&&["touchmove","mousemove"].includes(e.type)&&this.player.media.duration){if("touchmove"===e.type)this.seekTime=this.player.media.duration*(this.player.elements.inputs.seek.value/100);else{var t=this.player.elements.progress.getBoundingClientRect(),i=100/t.width*(e.pageX-t.left);this.seekTime=this.player.media.duration*(i/100),this.seekTime<0&&(this.seekTime=0),this.seekTime>this.player.media.duration-1&&(this.seekTime=this.player.media.duration-1),this.mousePosX=e.pageX,this.elements.thumb.time.innerText=it(this.seekTime)}this.showImageAtCurrentTime()}}},{key:"endMove",value:function(){this.toggleThumbContainer(!1,!0)}},{key:"startScrubbing",value:function(e){(W(e.button)||!1===e.button||0===e.button)&&(this.mouseDown=!0,this.player.media.duration&&(this.toggleScrubbingContainer(!0),this.toggleThumbContainer(!1,!0),this.showImageAtCurrentTime()))}},{key:"endScrubbing",value:function(){var e=this;this.mouseDown=!1,Math.ceil(this.lastTime)===Math.ceil(this.player.media.currentTime)?this.toggleScrubbingContainer(!1):Le.call(this.player,this.player.media,"timeupdate",(function(){e.mouseDown||e.toggleScrubbingContainer(!1)}))}},{key:"listeners",value:function(){var e=this;this.player.on("play",(function(){e.toggleThumbContainer(!1,!0)})),this.player.on("seeked",(function(){e.toggleThumbContainer(!1)})),this.player.on("timeupdate",(function(){e.lastTime=e.player.media.currentTime}))}},{key:"render",value:function(){this.elements.thumb.container=he("div",{class:this.player.config.classNames.previewThumbnails.thumbContainer}),this.elements.thumb.imageContainer=he("div",{class:this.player.config.classNames.previewThumbnails.imageContainer}),this.elements.thumb.container.appendChild(this.elements.thumb.imageContainer);var e=he("div",{class:this.player.config.classNames.previewThumbnails.timeContainer});this.elements.thumb.time=he("span",{},"00:00"),e.appendChild(this.elements.thumb.time),this.elements.thumb.container.appendChild(e),G(this.player.elements.progress)&&this.player.elements.progress.appendChild(this.elements.thumb.container),this.elements.scrubbing.container=he("div",{class:this.player.config.classNames.previewThumbnails.scrubbingContainer}),this.player.elements.wrapper.appendChild(this.elements.scrubbing.container)}},{key:"destroy",value:function(){this.elements.thumb.container&&this.elements.thumb.container.remove(),this.elements.scrubbing.container&&this.elements.scrubbing.container.remove()}},{key:"showImageAtCurrentTime",value:function(){var e=this;this.mouseDown?this.setScrubbingContainerSize():this.setThumbContainerSizeAndPos();var t=this.thumbnails[0].frames.findIndex((function(t){return e.seekTime>=t.startTime&&e.seekTime<=t.endTime})),i=t>=0,n=0;this.mouseDown||this.toggleThumbContainer(i),i&&(this.thumbnails.forEach((function(i,a){e.loadedImages.includes(i.frames[t].text)&&(n=a)})),t!==this.showingThumb&&(this.showingThumb=t,this.loadImage(n)))}},{key:"loadImage",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,i=this.showingThumb,n=this.thumbnails[t],a=n.urlPrefix,s=n.frames[i],r=n.frames[i].text,o=a+r;if(this.currentImageElement&&this.currentImageElement.dataset.filename===r)this.showImage(this.currentImageElement,s,t,i,r,!1),this.currentImageElement.dataset.index=i,this.removeOldImages(this.currentImageElement);else{this.loadingImage&&this.usingSprites&&(this.loadingImage.onload=null);var l=new Image;l.src=o,l.dataset.index=i,l.dataset.filename=r,this.showingThumbFilename=r,this.player.debug.log("Loading image: ".concat(o)),l.onload=function(){return e.showImage(l,s,t,i,r,!0)},this.loadingImage=l,this.removeOldImages(l)}}},{key:"showImage",value:function(e,t,i,n,a){var s=!(arguments.length>5&&void 0!==arguments[5])||arguments[5];this.player.debug.log("Showing thumb: ".concat(a,". num: ").concat(n,". qual: ").concat(i,". newimg: ").concat(s)),this.setImageSizeAndOffset(e,t),s&&(this.currentImageContainer.appendChild(e),this.currentImageElement=e,this.loadedImages.includes(a)||this.loadedImages.push(a)),this.preloadNearby(n,!0).then(this.preloadNearby(n,!1)).then(this.getHigherQuality(i,e,t,a))}},{key:"removeOldImages",value:function(e){var t=this;Array.from(this.currentImageContainer.children).forEach((function(i){if("img"===i.tagName.toLowerCase()){var n=t.usingSprites?500:1e3;if(i.dataset.index!==e.dataset.index&&!i.dataset.deleting){i.dataset.deleting=!0;var a=t.currentImageContainer;setTimeout((function(){a.removeChild(i),t.player.debug.log("Removing thumb: ".concat(i.dataset.filename))}),n)}}}))}},{key:"preloadNearby",value:function(e){var t=this,i=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return new Promise((function(n){setTimeout((function(){var a=t.thumbnails[0].frames[e].text;if(t.showingThumbFilename===a){var s;s=i?t.thumbnails[0].frames.slice(e):t.thumbnails[0].frames.slice(0,e).reverse();var r=!1;s.forEach((function(e){var i=e.text;if(i!==a&&!t.loadedImages.includes(i)){r=!0,t.player.debug.log("Preloading thumb filename: ".concat(i));var s=t.thumbnails[0].urlPrefix+i,o=new Image;o.src=s,o.onload=function(){t.player.debug.log("Preloaded thumb filename: ".concat(i)),t.loadedImages.includes(i)||t.loadedImages.push(i),n()}}})),r||n()}}),300)}))}},{key:"getHigherQuality",value:function(e,t,i,n){var a=this;if(e<this.thumbnails.length-1){var s=t.naturalHeight;this.usingSprites&&(s=i.h),s<this.thumbContainerHeight&&setTimeout((function(){a.showingThumbFilename===n&&(a.player.debug.log("Showing higher quality thumb for: ".concat(n)),a.loadImage(e+1))}),300)}}},{key:"toggleThumbContainer",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=this.player.config.classNames.previewThumbnails.thumbContainerShown;this.elements.thumb.container.classList.toggle(i,e),!e&&t&&(this.showingThumb=null,this.showingThumbFilename=null)}},{key:"toggleScrubbingContainer",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=this.player.config.classNames.previewThumbnails.scrubbingContainerShown;this.elements.scrubbing.container.classList.toggle(t,e),e||(this.showingThumb=null,this.showingThumbFilename=null)}},{key:"determineContainerAutoSizing",value:function(){(this.elements.thumb.imageContainer.clientHeight>20||this.elements.thumb.imageContainer.clientWidth>20)&&(this.sizeSpecifiedInCSS=!0)}},{key:"setThumbContainerSizeAndPos",value:function(){if(this.sizeSpecifiedInCSS){if(this.elements.thumb.imageContainer.clientHeight>20&&this.elements.thumb.imageContainer.clientWidth<20){var e=Math.floor(this.elements.thumb.imageContainer.clientHeight*this.thumbAspectRatio);this.elements.thumb.imageContainer.style.width="".concat(e,"px")}else if(this.elements.thumb.imageContainer.clientHeight<20&&this.elements.thumb.imageContainer.clientWidth>20){var t=Math.floor(this.elements.thumb.imageContainer.clientWidth/this.thumbAspectRatio);this.elements.thumb.imageContainer.style.height="".concat(t,"px")}}else{var i=Math.floor(this.thumbContainerHeight*this.thumbAspectRatio);this.elements.thumb.imageContainer.style.height="".concat(this.thumbContainerHeight,"px"),this.elements.thumb.imageContainer.style.width="".concat(i,"px")}this.setThumbContainerPos()}},{key:"setThumbContainerPos",value:function(){var e=this.player.elements.progress.getBoundingClientRect(),t=this.player.elements.container.getBoundingClientRect(),i=this.elements.thumb.container,n=t.left-e.left+10,a=t.right-e.left-i.clientWidth-10,s=this.mousePosX-e.left-i.clientWidth/2;s<n&&(s=n),s>a&&(s=a),i.style.left="".concat(s,"px")}},{key:"setScrubbingContainerSize",value:function(){var e=Nt(this.thumbAspectRatio,{width:this.player.media.clientWidth,height:this.player.media.clientHeight}),t=e.width,i=e.height;this.elements.scrubbing.container.style.width="".concat(t,"px"),this.elements.scrubbing.container.style.height="".concat(i,"px")}},{key:"setImageSizeAndOffset",value:function(e,t){if(this.usingSprites){var i=this.thumbContainerHeight/t.h;e.style.height="".concat(e.naturalHeight*i,"px"),e.style.width="".concat(e.naturalWidth*i,"px"),e.style.left="-".concat(t.x*i,"px"),e.style.top="-".concat(t.y*i,"px")}}},{key:"enabled",get:function(){return this.player.isHTML5&&this.player.isVideo&&this.player.config.previewThumbnails.enabled}},{key:"currentImageContainer",get:function(){return this.mouseDown?this.elements.scrubbing.container:this.elements.thumb.imageContainer}},{key:"usingSprites",get:function(){return Object.keys(this.thumbnails[0].frames[0]).includes("w")}},{key:"thumbAspectRatio",get:function(){return this.usingSprites?this.thumbnails[0].frames[0].w/this.thumbnails[0].frames[0].h:this.thumbnails[0].width/this.thumbnails[0].height}},{key:"thumbContainerHeight",get:function(){return this.mouseDown?Nt(this.thumbAspectRatio,{width:this.player.media.clientWidth,height:this.player.media.clientHeight}).height:this.sizeSpecifiedInCSS?this.elements.thumb.imageContainer.clientHeight:Math.floor(this.player.media.clientWidth/this.thumbAspectRatio/4)}},{key:"currentImageElement",get:function(){return this.mouseDown?this.currentScrubbingImageElement:this.currentThumbnailImageElement},set:function(e){this.mouseDown?this.currentScrubbingImageElement=e:this.currentThumbnailImageElement=e}}]),t}(),xt={insertElements:function(e,t){var i=this;Y(t)?me(e,this.media,{src:t}):$(t)&&t.forEach((function(t){me(e,i.media,t)}))},change:function(e){var t=this;le(e,"sources.length")?(Ve.cancelRequests.call(this),this.destroy.call(this,(function(){t.options.quality=[],pe(t.media),t.media=null,G(t.elements.container)&&t.elements.container.removeAttribute("class");var i=e.sources,n=e.type,a=o(i,1)[0],s=a.provider,r=void 0===s?ut.html5:s,l=a.src,c="html5"===r?n:"div",u="html5"===r?{}:{src:l};Object.assign(t,{provider:r,type:n,supported:Ee.check(n,r,t.config.playsinline),media:he(c,u)}),t.elements.container.appendChild(t.media),Q(e.autoplay)&&(t.config.autoplay=e.autoplay),t.isHTML5&&(t.config.crossorigin&&t.media.setAttribute("crossorigin",""),t.config.autoplay&&t.media.setAttribute("autoplay",""),ae(e.poster)||(t.poster=e.poster),t.config.loop.active&&t.media.setAttribute("loop",""),t.config.muted&&t.media.setAttribute("muted",""),t.config.playsinline&&t.media.setAttribute("playsinline","")),yt.addStyleHook.call(t),t.isHTML5&&xt.insertElements.call(t,"source",i),t.config.title=e.title,Pt.setup.call(t),t.isHTML5&&Object.keys(e).includes("tracks")&&xt.insertElements.call(t,"track",e.tracks),(t.isHTML5||t.isEmbed&&!t.supported.ui)&&yt.build.call(t),t.isHTML5&&t.media.load(),ae(e.previewThumbnails)||(Object.assign(t.config.previewThumbnails,e.previewThumbnails),t.previewThumbnails&&t.previewThumbnails.loaded&&(t.previewThumbnails.destroy(),t.previewThumbnails=null),t.config.previewThumbnails.enabled&&(t.previewThumbnails=new Mt(t))),t.fullscreen.update()}),!0)):this.debug.warn("Invalid source format")}};var It,Lt=function(){function t(i,n){var a=this;if(e(this,t),this.timers={},this.ready=!1,this.loading=!1,this.failed=!1,this.touch=Ee.touch,this.media=i,Y(this.media)&&(this.media=document.querySelectorAll(this.media)),(window.jQuery&&this.media instanceof jQuery||J(this.media)||$(this.media))&&(this.media=this.media[0]),this.config=ce({},ot,t.defaults,n||{},function(){try{return JSON.parse(a.media.getAttribute("data-plyr-config"))}catch(e){return{}}}()),this.elements={container:null,fullscreen:null,captions:null,buttons:{},display:{},progress:{},inputs:{},settings:{popup:null,menu:null,panels:{},buttons:{}}},this.captions={active:null,currentTrack:-1,meta:new WeakMap},this.fullscreen={active:!1},this.options={speed:[],quality:[]},this.debug=new pt(this.config.debug),this.debug.log("Config",this.config),this.debug.log("Support",Ee),!W(this.media)&&G(this.media))if(this.media.plyr)this.debug.warn("Target already setup");else if(this.config.enabled)if(Ee.check().api){var s=this.media.cloneNode(!0);s.autoplay=!1,this.elements.original=s;var r=this.media.tagName.toLowerCase(),o=null,l=null;switch(r){case"div":if(o=this.media.querySelector("iframe"),G(o)){if(l=at(o.getAttribute("src")),this.provider=function(e){return/^(https?:\/\/)?(www\.)?(youtube\.com|youtube-nocookie\.com|youtu\.?be)\/.+$/.test(e)?ut.youtube:/^https?:\/\/player.vimeo.com\/video\/\d{0,9}(?=\b|\/)/.test(e)?ut.vimeo:null}(l.toString()),this.elements.container=this.media,this.media=o,this.elements.container.className="",l.search.length){var c=["1","true"];c.includes(l.searchParams.get("autoplay"))&&(this.config.autoplay=!0),c.includes(l.searchParams.get("loop"))&&(this.config.loop.active=!0),this.isYouTube?(this.config.playsinline=c.includes(l.searchParams.get("playsinline")),this.config.youtube.hl=l.searchParams.get("hl")):this.config.playsinline=!0}}else this.provider=this.media.getAttribute(this.config.attributes.embed.provider),this.media.removeAttribute(this.config.attributes.embed.provider);if(ae(this.provider)||!Object.keys(ut).includes(this.provider))return void this.debug.error("Setup failed: Invalid provider");this.type=ht;break;case"video":case"audio":this.type=r,this.provider=ut.html5,this.media.hasAttribute("crossorigin")&&(this.config.crossorigin=!0),this.media.hasAttribute("autoplay")&&(this.config.autoplay=!0),(this.media.hasAttribute("playsinline")||this.media.hasAttribute("webkit-playsinline"))&&(this.config.playsinline=!0),this.media.hasAttribute("muted")&&(this.config.muted=!0),this.media.hasAttribute("loop")&&(this.config.loop.active=!0);break;default:return void this.debug.error("Setup failed: unsupported type")}this.supported=Ee.check(this.type,this.provider,this.config.playsinline),this.supported.api?(this.eventListeners=[],this.listeners=new vt(this),this.storage=new $e(this),this.media.plyr=this,G(this.elements.container)||(this.elements.container=he("div",{tabindex:0}),ue(this.media,this.elements.container)),yt.migrateStyles.call(this),yt.addStyleHook.call(this),Pt.setup.call(this),this.config.debug&&xe.call(this,this.elements.container,this.config.events.join(" "),(function(e){a.debug.log("event: ".concat(e.type))})),this.fullscreen=new ft(this),(this.isHTML5||this.isEmbed&&!this.supported.ui)&&yt.build.call(this),this.listeners.container(),this.listeners.global(),this.config.ads.enabled&&(this.ads=new Et(this)),this.isHTML5&&this.config.autoplay&&this.once("canplay",(function(){return De(a.play())})),this.lastSeekTime=0,this.config.previewThumbnails.enabled&&(this.previewThumbnails=new Mt(this))):this.debug.error("Setup failed: no support")}else this.debug.error("Setup failed: no support");else this.debug.error("Setup failed: disabled by config");else this.debug.error("Setup failed: no suitable element passed")}return i(t,[{key:"play",value:function(){var e=this;return X(this.media.play)?(this.ads&&this.ads.enabled&&this.ads.managerPromise.then((function(){return e.ads.play()})).catch((function(){return De(e.media.play())})),this.media.play()):null}},{key:"pause",value:function(){return this.playing&&X(this.media.pause)?this.media.pause():null}},{key:"togglePlay",value:function(e){return(Q(e)?e:!this.playing)?this.play():this.pause()}},{key:"stop",value:function(){this.isHTML5?(this.pause(),this.restart()):X(this.media.stop)&&this.media.stop()}},{key:"restart",value:function(){this.currentTime=0}},{key:"rewind",value:function(e){this.currentTime-=K(e)?e:this.config.seekTime}},{key:"forward",value:function(e){this.currentTime+=K(e)?e:this.config.seekTime}},{key:"increaseVolume",value:function(e){var t=this.media.muted?0:this.volume;this.volume=t+(K(e)?e:0)}},{key:"decreaseVolume",value:function(e){this.increaseVolume(-e)}},{key:"toggleCaptions",value:function(e){rt.toggle.call(this,e,!1)}},{key:"airplay",value:function(){Ee.airplay&&this.media.webkitShowPlaybackTargetPicker()}},{key:"toggleControls",value:function(e){if(this.supported.ui&&!this.isAudio){var t=we(this.elements.container,this.config.classNames.hideControls),i=void 0===e?void 0:!e,n=be(this.elements.container,this.config.classNames.hideControls,i);if(n&&$(this.config.controls)&&this.config.controls.includes("settings")&&!ae(this.config.settings)&&nt.toggleMenu.call(this,!1),n!==t){var a=n?"controlshidden":"controlsshown";Oe.call(this,this.media,a)}return!n}return!1}},{key:"on",value:function(e,t){xe.call(this,this.elements.container,e,t)}},{key:"once",value:function(e,t){Le.call(this,this.elements.container,e,t)}},{key:"off",value:function(e,t){Ie(this.elements.container,e,t)}},{key:"destroy",value:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(this.ready){var n=function(){document.body.style.overflow="",t.embed=null,i?(Object.keys(t.elements).length&&(pe(t.elements.buttons.play),pe(t.elements.captions),pe(t.elements.controls),pe(t.elements.wrapper),t.elements.buttons.play=null,t.elements.captions=null,t.elements.controls=null,t.elements.wrapper=null),X(e)&&e()):(_e.call(t),Ve.cancelRequests.call(t),ge(t.elements.original,t.elements.container),Oe.call(t,t.elements.original,"destroyed",!0),X(e)&&e.call(t.elements.original),t.ready=!1,setTimeout((function(){t.elements=null,t.media=null}),200))};this.stop(),clearTimeout(this.timers.loading),clearTimeout(this.timers.controls),clearTimeout(this.timers.resized),this.isHTML5?(yt.toggleNativeControls.call(this,!0),n()):this.isYouTube?(clearInterval(this.timers.buffering),clearInterval(this.timers.playing),null!==this.embed&&X(this.embed.destroy)&&this.embed.destroy(),n()):this.isVimeo&&(null!==this.embed&&this.embed.unload().then(n),setTimeout(n,200))}}},{key:"supports",value:function(e){return Ee.mime.call(this,e)}},{key:"isHTML5",get:function(){return this.provider===ut.html5}},{key:"isEmbed",get:function(){return this.isYouTube||this.isVimeo}},{key:"isYouTube",get:function(){return this.provider===ut.youtube}},{key:"isVimeo",get:function(){return this.provider===ut.vimeo}},{key:"isVideo",get:function(){return this.type===ht}},{key:"isAudio",get:function(){return this.type===dt}},{key:"playing",get:function(){return Boolean(this.ready&&!this.paused&&!this.ended)}},{key:"paused",get:function(){return Boolean(this.media.paused)}},{key:"stopped",get:function(){return Boolean(this.paused&&0===this.currentTime)}},{key:"ended",get:function(){return Boolean(this.media.ended)}},{key:"currentTime",set:function(e){if(this.duration){var t=K(e)&&e>0;this.media.currentTime=t?Math.min(e,this.duration):0,this.debug.log("Seeking to ".concat(this.currentTime," seconds"))}},get:function(){return Number(this.media.currentTime)}},{key:"buffered",get:function(){var e=this.media.buffered;return K(e)?e:e&&e.length&&this.duration>0?e.end(0)/this.duration:0}},{key:"seeking",get:function(){return Boolean(this.media.seeking)}},{key:"duration",get:function(){var e=parseFloat(this.config.duration),t=(this.media||{}).duration,i=K(t)&&t!==1/0?t:0;return e||i}},{key:"volume",set:function(e){var t=e;Y(t)&&(t=Number(t)),K(t)||(t=this.storage.get("volume")),K(t)||(t=this.config.volume),t>1&&(t=1),t<0&&(t=0),this.config.volume=t,this.media.volume=t,!ae(e)&&this.muted&&t>0&&(this.muted=!1)},get:function(){return Number(this.media.volume)}},{key:"muted",set:function(e){var t=e;Q(t)||(t=this.storage.get("muted")),Q(t)||(t=this.config.muted),this.config.muted=t,this.media.muted=t},get:function(){return Boolean(this.media.muted)}},{key:"hasAudio",get:function(){return!this.isHTML5||(!!this.isAudio||(Boolean(this.media.mozHasAudio)||Boolean(this.media.webkitAudioDecodedByteCount)||Boolean(this.media.audioTracks&&this.media.audioTracks.length)))}},{key:"speed",set:function(e){var t=this,i=null;K(e)&&(i=e),K(i)||(i=this.storage.get("speed")),K(i)||(i=this.config.speed.selected);var n=this.minimumSpeed,a=this.maximumSpeed;i=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:255;return Math.min(Math.max(e,t),i)}(i,n,a),this.config.speed.selected=i,setTimeout((function(){t.media.playbackRate=i}),0)},get:function(){return Number(this.media.playbackRate)}},{key:"minimumSpeed",get:function(){return this.isYouTube?Math.min.apply(Math,l(this.options.speed)):this.isVimeo?.5:.0625}},{key:"maximumSpeed",get:function(){return this.isYouTube?Math.max.apply(Math,l(this.options.speed)):this.isVimeo?2:16}},{key:"quality",set:function(e){var t=this.config.quality,i=this.options.quality;if(i.length){var n=[!ae(e)&&Number(e),this.storage.get("quality"),t.selected,t.default].find(K),a=!0;if(!i.includes(n)){var s=function(e,t){return $(e)&&e.length?e.reduce((function(e,i){return Math.abs(i-t)<Math.abs(e-t)?i:e})):null}(i,n);this.debug.warn("Unsupported quality option: ".concat(n,", using ").concat(s," instead")),n=s,a=!1}t.selected=n,this.media.quality=n,a&&this.storage.set({quality:n})}},get:function(){return this.media.quality}},{key:"loop",set:function(e){var t=Q(e)?e:this.config.loop.active;this.config.loop.active=t,this.media.loop=t},get:function(){return Boolean(this.media.loop)}},{key:"source",set:function(e){xt.change.call(this,e)},get:function(){return this.media.currentSrc}},{key:"download",get:function(){var e=this.config.urls.download;return ne(e)?e:this.source},set:function(e){ne(e)&&(this.config.urls.download=e,nt.setDownloadUrl.call(this))}},{key:"poster",set:function(e){this.isVideo?yt.setPoster.call(this,e,!1).catch((function(){})):this.debug.warn("Poster can only be set for video")},get:function(){return this.isVideo?this.media.getAttribute("poster")||this.media.getAttribute("data-poster"):null}},{key:"ratio",get:function(){if(!this.isVideo)return null;var e=He(Fe.call(this));return $(e)?e.join(":"):e},set:function(e){this.isVideo?Y(e)&&qe(e)?(this.config.ratio=e,Re.call(this)):this.debug.error("Invalid aspect ratio specified (".concat(e,")")):this.debug.warn("Aspect ratio can only be set for video")}},{key:"autoplay",set:function(e){var t=Q(e)?e:this.config.autoplay;this.config.autoplay=t},get:function(){return Boolean(this.config.autoplay)}},{key:"currentTrack",set:function(e){rt.set.call(this,e,!1)},get:function(){var e=this.captions,t=e.toggled,i=e.currentTrack;return t?i:-1}},{key:"language",set:function(e){rt.setLanguage.call(this,e,!1)},get:function(){return(rt.getCurrentTrack.call(this)||{}).language}},{key:"pip",set:function(e){if(Ee.pip){var t=Q(e)?e:!this.pip;X(this.media.webkitSetPresentationMode)&&this.media.webkitSetPresentationMode(t?lt:ct),X(this.media.requestPictureInPicture)&&(!this.pip&&t?this.media.requestPictureInPicture():this.pip&&!t&&document.exitPictureInPicture())}},get:function(){return Ee.pip?ae(this.media.webkitPresentationMode)?this.media===document.pictureInPictureElement:this.media.webkitPresentationMode===lt:null}}],[{key:"supported",value:function(e,t,i){return Ee.check(e,t,i)}},{key:"loadSprite",value:function(e,t){return Ge(e,t)}},{key:"setup",value:function(e){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null;return Y(e)?n=Array.from(document.querySelectorAll(e)):J(e)?n=Array.from(e):$(e)&&(n=e.filter(G)),ae(n)?null:n.map((function(e){return new t(e,i)}))}}]),t}();return Lt.defaults=(It=ot,JSON.parse(JSON.stringify(It))),Lt})); }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],19:[function(require,module,exports){ /*! * Unidragger v2.3.0 * Draggable base class * MIT license */ /*jshint browser: true, unused: true, undef: true, strict: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'unipointer/unipointer' ], function( Unipointer ) { return factory( window, Unipointer ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('unipointer') ); } else { // browser global window.Unidragger = factory( window, window.Unipointer ); } }( window, function factory( window, Unipointer ) { 'use strict'; // -------------------------- Unidragger -------------------------- // function Unidragger() {} // inherit Unipointer & EvEmitter var proto = Unidragger.prototype = Object.create( Unipointer.prototype ); // ----- bind start ----- // proto.bindHandles = function() { this._bindHandles( true ); }; proto.unbindHandles = function() { this._bindHandles( false ); }; /** * Add or remove start event * @param {Boolean} isAdd */ proto._bindHandles = function( isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; // bind each handle var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; var touchAction = isAdd ? this._touchActionValue : ''; for ( var i=0; i < this.handles.length; i++ ) { var handle = this.handles[i]; this._bindStartEvent( handle, isAdd ); handle[ bindMethod ]( 'click', this ); // touch-action: none to override browser touch gestures. metafizzy/flickity#540 if ( window.PointerEvent ) { handle.style.touchAction = touchAction; } } }; // prototype so it can be overwriteable by Flickity proto._touchActionValue = 'none'; // ----- start event ----- // /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerDown = function( event, pointer ) { var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } // track start event position this.pointerDownPointer = pointer; event.preventDefault(); this.pointerDownBlur(); // bind move and end events this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // nodes that have text fields var cursorNodes = { TEXTAREA: true, INPUT: true, SELECT: true, OPTION: true, }; // dismiss inputs with text fields. flickity#403, flickity#404 proto.okayPointerDown = function( event ) { var isCursorNode = cursorNodes[ event.target.nodeName ]; var isClickType = clickTypes[ event.target.type ]; var isOkay = !isCursorNode || isClickType; if ( !isOkay ) { this._pointerReset(); } return isOkay; }; // kludge to blur previously focused input proto.pointerDownBlur = function() { var focused = document.activeElement; // do not blur body for IE10, metafizzy/flickity#117 var canBlur = focused && focused.blur && focused != document.body; if ( canBlur ) { focused.blur(); } }; // ----- move event ----- // /** * drag move * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; // base pointer move logic proto._dragPointerMove = function( event, pointer ) { var moveVector = { x: pointer.pageX - this.pointerDownPointer.pageX, y: pointer.pageY - this.pointerDownPointer.pageY }; // start drag if pointer has moved far enough to start drag if ( !this.isDragging && this.hasDragStarted( moveVector ) ) { this._dragStart( event, pointer ); } return moveVector; }; // condition if pointer has moved far enough to start drag proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; }; // ----- end event ----- // /** * pointer up * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); this._dragPointerUp( event, pointer ); }; proto._dragPointerUp = function( event, pointer ) { if ( this.isDragging ) { this._dragEnd( event, pointer ); } else { // pointer didn't move enough for drag to start this._staticClick( event, pointer ); } }; // -------------------------- drag -------------------------- // // dragStart proto._dragStart = function( event, pointer ) { this.isDragging = true; // prevent clicks this.isPreventingClicks = true; this.dragStart( event, pointer ); }; proto.dragStart = function( event, pointer ) { this.emitEvent( 'dragStart', [ event, pointer ] ); }; // dragMove proto._dragMove = function( event, pointer, moveVector ) { // do not drag if not dragging yet if ( !this.isDragging ) { return; } this.dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { event.preventDefault(); this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); }; // dragEnd proto._dragEnd = function( event, pointer ) { // set flags this.isDragging = false; // re-enable clicking async setTimeout( function() { delete this.isPreventingClicks; }.bind( this ) ); this.dragEnd( event, pointer ); }; proto.dragEnd = function( event, pointer ) { this.emitEvent( 'dragEnd', [ event, pointer ] ); }; // ----- onclick ----- // // handle all clicks and prevent clicks when dragging proto.onclick = function( event ) { if ( this.isPreventingClicks ) { event.preventDefault(); } }; // ----- staticClick ----- // // triggered after pointer down & up with no/tiny movement proto._staticClick = function( event, pointer ) { // ignore emulated mouse up clicks if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { return; } this.staticClick( event, pointer ); // set flag for emulated clicks 300ms after touchend if ( event.type != 'mouseup' ) { this.isIgnoringMouseUp = true; // reset flag after 300ms setTimeout( function() { delete this.isIgnoringMouseUp; }.bind( this ), 400 ); } }; proto.staticClick = function( event, pointer ) { this.emitEvent( 'staticClick', [ event, pointer ] ); }; // ----- utils ----- // Unidragger.getPointerPoint = Unipointer.getPointerPoint; // ----- ----- // return Unidragger; })); },{"unipointer":20}],20:[function(require,module,exports){ /*! * Unipointer v2.3.0 * base class for doing one thing with pointer event * MIT license */ /*jshint browser: true, undef: true, unused: true, strict: true */ ( function( window, factory ) { // universal module definition /* jshint strict: false */ /*global define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'ev-emitter/ev-emitter' ], function( EvEmitter ) { return factory( window, EvEmitter ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter') ); } else { // browser global window.Unipointer = factory( window, window.EvEmitter ); } }( window, function factory( window, EvEmitter ) { 'use strict'; function noop() {} function Unipointer() {} // inherit EvEmitter var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); proto.bindStartEvent = function( elem ) { this._bindStartEvent( elem, true ); }; proto.unbindStartEvent = function( elem ) { this._bindStartEvent( elem, false ); }; /** * Add or remove start event * @param {Boolean} isAdd - remove if falsey */ proto._bindStartEvent = function( elem, isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; // default to mouse events var startEvent = 'mousedown'; if ( window.PointerEvent ) { // Pointer Events startEvent = 'pointerdown'; } else if ( 'ontouchstart' in window ) { // Touch Events. iOS Safari startEvent = 'touchstart'; } elem[ bindMethod ]( startEvent, this ); }; // trigger handler methods for events proto.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; // returns the touch that we're keeping track of proto.getTouch = function( touches ) { for ( var i=0; i < touches.length; i++ ) { var touch = touches[i]; if ( touch.identifier == this.pointerIdentifier ) { return touch; } } }; // ----- start event ----- // proto.onmousedown = function( event ) { // dismiss clicks from right or middle buttons var button = event.button; if ( button && ( button !== 0 && button !== 1 ) ) { return; } this._pointerDown( event, event ); }; proto.ontouchstart = function( event ) { this._pointerDown( event, event.changedTouches[0] ); }; proto.onpointerdown = function( event ) { this._pointerDown( event, event ); }; /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto._pointerDown = function( event, pointer ) { // dismiss right click and other pointers // button = 0 is okay, 1-4 not if ( event.button || this.isPointerDown ) { return; } this.isPointerDown = true; // save pointer identifier to match up touch events this.pointerIdentifier = pointer.pointerId !== undefined ? // pointerId for pointer events, touch.indentifier for touch events pointer.pointerId : pointer.identifier; this.pointerDown( event, pointer ); }; proto.pointerDown = function( event, pointer ) { this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // hash of events to be bound after start event var postStartEvents = { mousedown: [ 'mousemove', 'mouseup' ], touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], }; proto._bindPostStartEvents = function( event ) { if ( !event ) { return; } // get proper events to match start event var events = postStartEvents[ event.type ]; // bind events to node events.forEach( function( eventName ) { window.addEventListener( eventName, this ); }, this ); // save these arguments this._boundPointerEvents = events; }; proto._unbindPostStartEvents = function() { // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) if ( !this._boundPointerEvents ) { return; } this._boundPointerEvents.forEach( function( eventName ) { window.removeEventListener( eventName, this ); }, this ); delete this._boundPointerEvents; }; // ----- move event ----- // proto.onmousemove = function( event ) { this._pointerMove( event, event ); }; proto.onpointermove = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerMove( event, event ); } }; proto.ontouchmove = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerMove( event, touch ); } }; /** * pointer move * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerMove = function( event, pointer ) { this.pointerMove( event, pointer ); }; // public proto.pointerMove = function( event, pointer ) { this.emitEvent( 'pointerMove', [ event, pointer ] ); }; // ----- end event ----- // proto.onmouseup = function( event ) { this._pointerUp( event, event ); }; proto.onpointerup = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerUp( event, event ); } }; proto.ontouchend = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerUp( event, touch ); } }; /** * pointer up * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerUp = function( event, pointer ) { this._pointerDone(); this.pointerUp( event, pointer ); }; // public proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); }; // ----- pointer done ----- // // triggered on pointer up & pointer cancel proto._pointerDone = function() { this._pointerReset(); this._unbindPostStartEvents(); this.pointerDone(); }; proto._pointerReset = function() { // reset properties this.isPointerDown = false; delete this.pointerIdentifier; }; proto.pointerDone = noop; // ----- pointer cancel ----- // proto.onpointercancel = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerCancel( event, event ); } }; proto.ontouchcancel = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerCancel( event, touch ); } }; /** * pointer cancel * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerCancel = function( event, pointer ) { this._pointerDone(); this.pointerCancel( event, pointer ); }; // public proto.pointerCancel = function( event, pointer ) { this.emitEvent( 'pointerCancel', [ event, pointer ] ); }; // ----- ----- // // utility function for getting x/y coords from event Unipointer.getPointerPoint = function( pointer ) { return { x: pointer.pageX, y: pointer.pageY }; }; // ----- ----- // return Unipointer; })); },{"ev-emitter":2}],21:[function(require,module,exports){ "use strict"; var _flickity = _interopRequireDefault(require("flickity")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /*------------------------------------------------------------------------*/ /* Carousel /*------------------------------------------------------------------------*/ var Carousel = function Carousel() { var carousels = document.querySelectorAll('.js-carousel-container'); var slideshows = document.querySelectorAll('.js-slideshow-container'); // Set up carousels [].forEach.call(carousels, function (elem) { var carouselElem = elem.querySelector('.js-carousel'); var nextButton = elem.querySelector('.js-carousel-next'); var prevButton = elem.querySelector('.js-carousel-prev'); var carousel = new _flickity.default(carouselElem, { prevNextButtons: false, pageDots: false, initialIndex: 1, lazyLoad: 1, wrapAround: true, autoPlay: 2000 }); nextButton.addEventListener('click', function () { carousel.next(); }); prevButton.addEventListener('click', function () { carousel.previous(); }); }); // Set up slideshows [].forEach.call(slideshows, function (elem) { var slideshowElem = elem.querySelector('.js-slideshow'); var nextButton = elem.querySelector('.js-slideshow-next'); var prevButton = elem.querySelector('.js-slideshow-prev'); var indicators = elem.querySelectorAll('.js-slideshow-indicator'); var slideshow = new _flickity.default(slideshowElem, { prevNextButtons: false, pageDots: false, wrapAround: true, autoPlay: 3000 }); slideshow.on('dragStart.flickity', function (event, pointer) { document.ontouchmove = function (e) { e.preventDefault(); }; }); slideshow.on('dragEnd.flickity', function (event, pointer) { document.ontouchmove = function (e) { return true; }; }); slideshow.on('select', function () { var selectedButton = indicators[slideshow.selectedIndex]; var previousSelectedButton = elem.querySelector('.slideshow-indicators__item--active'); previousSelectedButton.classList.remove('slideshow-indicators__item--active'); selectedButton.classList.add('slideshow-indicators__item--active'); }); nextButton.addEventListener('click', function () { slideshow.next(); }); prevButton.addEventListener('click', function () { slideshow.previous(); }); [].forEach.call(indicators, function (indicator) { indicator.addEventListener('click', function (e) { e.preventDefault(); var index = indicator.dataset.id; slideshow.select(index); }); }); }); }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { Carousel(); }); },{"flickity":9}],22:[function(require,module,exports){ "use strict"; /*------------------------------------------------------------------------*/ /* Cover /*------------------------------------------------------------------------*/ var Cover = function Cover() { var vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', "".concat(vh, "px")); }; var ParallaxCover = function ParallaxCover() { var cover = document.querySelector('.cover__content'); if (cover) { var scrollTop = 0; var tweened = 0; scrollTop = Math.max(0, document.documentElement.scrollTop || window.pageYOffset || 0); if (Math.abs(scrollTop - tweened) > 0) { // you can change the number for the acceleration of scroll var top = tweened += 0.1 * (scrollTop - tweened); // update value of Y translation cover.style.transform = "translateY(".concat(top * -1, "px)"); } else { cover.style.transform = "translateY(0px)"; } } }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { ParallaxCover(); Cover(); }); window.addEventListener('resize', function () { ParallaxCover(); Cover(); }); window.addEventListener('scroll', function () { ParallaxCover(); }); },{}],23:[function(require,module,exports){ "use strict"; function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } /*------------------------------------------------------------------------*/ /* Filter /*------------------------------------------------------------------------*/ var getPosts = function getPosts() { var pagination = document.querySelector('.js-pagination'); var postArea = document.querySelector('.js-posts'); var posts = document.querySelector('#js-posts-container'); var postType = posts.querySelector('#js-post-type').value; var filters = document.querySelectorAll('.js-filter'); var taxonomies = {}; var meta = {}; var formData = new FormData(); var urlPath = jsData.currentUrl; formData.append('action', 'filter_posts'); formData.append('post_type', postType); formData.append('query', jsData.posts); // this is how we get params from wp_localize_script() function formData.append('page', jsData.currentPage); [].forEach.call(filters, function (filter) { if (filter.dataset.type === 'taxonomy') { taxonomies[filter.dataset.taxonomy] = {}; taxonomies[filter.dataset.taxonomy]['type'] = filter.dataset.taxonomy; taxonomies[filter.dataset.taxonomy]['value'] = filter.value; taxonomies[filter.dataset.taxonomy]['slug'] = filter.dataset.slug; } if (filter.dataset.type === 'meta') { meta[filter.dataset.meta] = {}; meta[filter.dataset.meta]['type'] = filter.dataset.meta; meta[filter.dataset.meta]['value'] = filter.value; // Append data to the form formData.append('meta', JSON.stringify(meta)); } if (filter.dataset.type === 'search') { var search = filter.value; // Append data to the form formData.append('search', search); } }); if (Object.keys(taxonomies).length !== 0 && taxonomies.constructor === Object) { var urlOperator = ''; formData.append('taxonomies', JSON.stringify(taxonomies)); for (var _i = 0, _Object$entries = Object.entries(taxonomies); _i < _Object$entries.length; _i++) { var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), key = _Object$entries$_i[0], value = _Object$entries$_i[1]; if (urlPath.includes('?')) { urlOperator = '&'; } else { urlOperator = '?'; } if (value.value !== '') { urlPath = urlPath + urlOperator + value.slug + '=' + value.value; } } } var formDataString = new URLSearchParams(formData).toString(); fetch(jsData.ajaxUrl, { method: 'POST', credentials: 'same-origin', headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }), body: formDataString }).then(function (resp) { return resp.json(); }).then(function (data) { postArea.innerHTML = data.posts; // Update the pagination if there is any if (pagination) { pagination.innerHTML = data.pagination; if (data.paged !== 1) { var _urlOperator = ''; if (urlPath.contains('?')) { _urlOperator = '&'; } else { _urlOperator = '?'; } urlPath = urlPath + _urlOperator + 'page=' + data.paged; } window.history.pushState(null, null, urlPath); posts.scrollIntoView(); Pagination(); } }).catch(function (error) { console.log(error); }); }; var Filter = function Filter() { var filters = document.querySelectorAll('.js-filter'); var searchFields = document.querySelectorAll('.js-search'); [].forEach.call(filters, function (filter) { filter.addEventListener('change', function (e) { e.preventDefault(); jsData.currentPage = 1; getPosts(); }); }); [].forEach.call(searchFields, function (searchField) { searchField.addEventListener('input', function () { jsData.currentPage = 1; getPosts(); }); }); }; var Pagination = function Pagination() { var paginationLinks = document.querySelectorAll('a.js-pagination-link'); var paginationPrev = document.querySelector('.js-pagination-prev-link'); var paginationNext = document.querySelector('.js-pagination-next-link'); [].forEach.call(paginationLinks, function (paginationLink) { paginationLink.addEventListener('click', function (e) { e.preventDefault(); // Update the current page if (paginationLink.innerHTML !== jsData.currentPage) { jsData.currentPage = paginationLink.innerHTML; } getPosts(); }); }); if (paginationNext) { paginationNext.addEventListener('click', function (e) { e.preventDefault(); jsData.currentPage++; getPosts(); }); } if (paginationPrev) { paginationPrev.addEventListener('click', function (e) { e.preventDefault(); jsData.currentPage--; getPosts(); }); } }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { Filter(); Pagination(); }); },{}],24:[function(require,module,exports){ "use strict"; /*------------------------------------------------------------------------*/ /* Toggle menu /*------------------------------------------------------------------------*/ var toggleMainMenu = function toggleMainMenu() { var body = document.getElementsByTagName('BODY')[0]; var toggleMenuBtns = document.querySelectorAll('.js-toggle-menu'); [].forEach.call(toggleMenuBtns, function (toggleMenuBtn) { toggleMenuBtn.addEventListener('click', function (e) { e.preventDefault(); if (body.classList.contains('search-open')) { body.classList.toggle('search-open'); } body.classList.toggle('menu-open'); }); }); }; /*------------------------------------------------------------------------*/ /* Sub menu /*------------------------------------------------------------------------*/ var subMenu = function subMenu() { var subMenuItems = document.querySelectorAll('.menu-item-has-children'); [].forEach.call(subMenuItems, function (subMenuItem) { var subMenuChevron = document.createElement('span'); var subMenuLink = subMenuItem.querySelector('a'); subMenuChevron.className = 'chevron'; subMenuItem.appendChild(subMenuChevron); subMenuChevron.addEventListener('click', function (e) { e.preventDefault(); subMenuItem.classList.toggle('sub-menu-open'); }); }); }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { toggleMainMenu(); subMenu(); }); },{}],25:[function(require,module,exports){ "use strict"; /*------------------------------------------------------------------------*/ /* Reach /*------------------------------------------------------------------------*/ var reach = function reach() { var body = document.getElementsByTagName('BODY')[0]; var elems = document.querySelectorAll('.js-reach'); [].forEach.call(elems, function (elem) { var reachToggle = elem.querySelector('.js-reach-toggle'); reachToggle.addEventListener('click', function (e) { e.preventDefault(); elem.classList.toggle('reach--open'); body.classList.toggle('reach-open'); }); }); }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { reach(); }); },{}],26:[function(require,module,exports){ "use strict"; /*------------------------------------------------------------------------*/ /* Toggle searchoverlay /*------------------------------------------------------------------------*/ var toggleSearchMenu = function toggleSearchMenu() { var body = document.getElementsByTagName('BODY')[0]; var toggleSearchBtns = document.querySelectorAll('.js-toggle-search'); var searchInput = document.querySelector('.search-input'); [].forEach.call(toggleSearchBtns, function (toggleSearchBtn) { toggleSearchBtn.addEventListener('click', function (e) { e.preventDefault(); if (body.classList.contains('menu-open')) { body.classList.toggle('menu-open'); } body.classList.toggle('search-open'); searchInput.focus(); }); }); }; /*------------------------------------------------------------------------*/ /* DOMContentLoaded EventListener /*------------------------------------------------------------------------*/ document.addEventListener('DOMContentLoaded', function () { toggleSearchMenu(); }); },{}],27:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isMSIE = isMSIE; /** * Detects if the visitor is using a MSIE browser * * @returns {true|false} True if MSIE, false if not */ function isMSIE() { return ( /* @cc_on!@ */ false || !!document.documentMode ); } },{}],28:[function(require,module,exports){ "use strict"; require("./vendor/lazysizes.js"); require("./vendor/plyr.js"); require("./components/cover.js"); require("./components/navigation.js"); require("./components/reach.js"); require("./components/carousel.js"); require("./components/filter.js"); require("./components/search.js"); },{"./components/carousel.js":21,"./components/cover.js":22,"./components/filter.js":23,"./components/navigation.js":24,"./components/reach.js":25,"./components/search.js":26,"./vendor/lazysizes.js":29,"./vendor/plyr.js":30}],29:[function(require,module,exports){ "use strict"; require("lazysizes"); if ('loading' in HTMLImageElement.prototype) { var images = document.querySelectorAll('img.lazyload'); images.forEach(function (img) { if (typeof img.dataset.src !== 'undefined') { img.src = img.dataset.src; } if (typeof img.dataset.srcset !== 'undefined') { img.srcset = img.dataset.srcset; } }); } else { // Initiate LazySizes (reads data-src & class=lazyload) lazySizes.init(); // lazySizes works off a global. } },{"lazysizes":17}],30:[function(require,module,exports){ "use strict"; var _plyr = _interopRequireDefault(require("plyr")); var _hls = _interopRequireDefault(require("hls.js")); var _functions = require("../helpers/functions.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // missing forEach on NodeList for IE11 if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = Array.prototype.forEach; } var videoPlayerHolders = document.querySelectorAll('.js-video-player-holder'); /*------------------------------------------------------------------------*/ /* Video Players Background /*------------------------------------------------------------------------*/ if (videoPlayerHolders) { videoPlayerHolders.forEach(function (videoPlayerHolder) { var videoBgPlayer = videoPlayerHolder.querySelector('.js-background-video'); var videoSource = videoBgPlayer.dataset.video; var hlsType = videoBgPlayer.dataset.hls; if (hlsType === 'true' && _hls.default.isSupported()) { var hls = new _hls.default(); hls.loadSource(videoSource); hls.attachMedia(videoBgPlayer); } var plyrInstance = new _plyr.default(videoBgPlayer, { loadSprite: (0, _functions.isMSIE)(), clickToPlay: false, volume: 0, controls: false }); plyrInstance.on('ready', function () { plyrInstance.play(); }); plyrInstance.on('playing', function () { videoPlayerHolder.classList.add('cover--video-ready'); }); plyrInstance.on('ended', function () { plyrInstance.play(); }); }); } },{"../helpers/functions.js":27,"hls.js":16,"plyr":18}]},{},[28]) //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvZGVzYW5kcm8tbWF0Y2hlcy1zZWxlY3Rvci9tYXRjaGVzLXNlbGVjdG9yLmpzIiwibm9kZV9tb2R1bGVzL2V2LWVtaXR0ZXIvZXYtZW1pdHRlci5qcyIsIm5vZGVfbW9kdWxlcy9maXp6eS11aS11dGlscy91dGlscy5qcyIsIm5vZGVfbW9kdWxlcy9mbGlja2l0eS9qcy9hZGQtcmVtb3ZlLWNlbGwuanMiLCJub2RlX21vZHVsZXMvZmxpY2tpdHkvanMvYW5pbWF0ZS5qcyIsIm5vZGVfbW9kdWxlcy9mbGlja2l0eS9qcy9jZWxsLmpzIiwibm9kZV9tb2R1bGVzL2ZsaWNraXR5L2pzL2RyYWcuanMiLCJub2RlX21vZHVsZXMvZmxpY2tpdHkvanMvZmxpY2tpdHkuanMiLCJub2RlX21vZHVsZXMvZmxpY2tpdHkvanMvaW5kZXguanMiLCJub2RlX21vZHVsZXMvZmxpY2tpdHkvanMvbGF6eWxvYWQuanMiLCJub2RlX21vZHVsZXMvZmxpY2tpdHkvanMvcGFnZS1kb3RzLmpzIiwibm9kZV9tb2R1bGVzL2ZsaWNraXR5L2pzL3BsYXllci5qcyIsIm5vZGVfbW9kdWxlcy9mbGlja2l0eS9qcy9wcmV2LW5leHQtYnV0dG9uLmpzIiwibm9kZV9tb2R1bGVzL2ZsaWNraXR5L2pzL3NsaWRlLmpzIiwibm9kZV9tb2R1bGVzL2dldC1zaXplL2dldC1zaXplLmpzIiwibm9kZV9tb2R1bGVzL2hscy5qcy9kaXN0L2hscy5qcyIsIm5vZGVfbW9kdWxlcy9sYXp5c2l6ZXMvbGF6eXNpemVzLmpzIiwibm9kZV9tb2R1bGVzL3BseXIvZGlzdC9wbHlyLm1pbi5qcyIsIm5vZGVfbW9kdWxlcy91bmlkcmFnZ2VyL3VuaWRyYWdnZXIuanMiLCJub2RlX21vZHVsZXMvdW5pcG9pbnRlci91bmlwb2ludGVyLmpzIiwid2ViL2FwcC90aGVtZXMvbWVkaWFodWlzL2Fzc2V0cy9zcmMvanMvY29tcG9uZW50cy9jYXJvdXNlbC5qcyIsIndlYi9hcHAvdGhlbWVzL21lZGlhaHVpcy9hc3NldHMvc3JjL2pzL2NvbXBvbmVudHMvY292ZXIuanMiLCJ3ZWIvYXBwL3RoZW1lcy9tZWRpYWh1aXMvYXNzZXRzL3NyYy9qcy9jb21wb25lbnRzL2ZpbHRlci5qcyIsIndlYi9hcHAvdGhlbWVzL21lZGlhaHVpcy9hc3NldHMvc3JjL2pzL2NvbXBvbmVudHMvbmF2aWdhdGlvbi5qcyIsIndlYi9hcHAvdGhlbWVzL21lZGlhaHVpcy9hc3NldHMvc3JjL2pzL2NvbXBvbmVudHMvcmVhY2guanMiLCJ3ZWIvYXBwL3RoZW1lcy9tZWRpYWh1aXMvYXNzZXRzL3NyYy9qcy9jb21wb25lbnRzL3NlYXJjaC5qcyIsIndlYi9hcHAvdGhlbWVzL21lZGlhaHVpcy9hc3NldHMvc3JjL2pzL2hlbHBlcnMvZnVuY3Rpb25zLmpzIiwid2ViL2FwcC90aGVtZXMvbWVkaWFodWlzL2Fzc2V0cy9zcmMvanMvbWFpbi5qcyIsIndlYi9hcHAvdGhlbWVzL21lZGlhaHVpcy9hc3NldHMvc3JjL2pzL3ZlbmRvci9sYXp5c2l6ZXMuanMiLCJ3ZWIvYXBwL3RoZW1lcy9tZWRpYWh1aXMvYXNzZXRzL3NyYy9qcy92ZW5kb3IvcGx5ci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoSEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqUEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6TUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pZQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDajZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3RJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUxBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvTEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuTkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMvTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN2lxQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDbHZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDSkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdlJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUM3U0E7Ozs7QUFFQTs7QUFDQTtBQUNBO0FBQ0EsSUFBTSxRQUFRLEdBQUcsU0FBWCxRQUFXLEdBQVk7QUFDM0IsTUFBTSxTQUFTLEdBQUcsUUFBUSxDQUFDLGdCQUFULENBQTBCLHdCQUExQixDQUFsQjtBQUNBLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxnQkFBVCxDQUEwQix5QkFBMUIsQ0FBbkIsQ0FGMkIsQ0FJM0I7O0FBQ0EsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixTQUFoQixFQUEyQixVQUFVLElBQVYsRUFBZ0I7QUFDekMsUUFBSSxZQUFZLEdBQUcsSUFBSSxDQUFDLGFBQUwsQ0FBbUIsY0FBbkIsQ0FBbkI7QUFDQSxRQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBTCxDQUFtQixtQkFBbkIsQ0FBakI7QUFDQSxRQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBTCxDQUFtQixtQkFBbkIsQ0FBakI7QUFFQSxRQUFJLFFBQVEsR0FBRyxJQUFJLGlCQUFKLENBQWEsWUFBYixFQUEyQjtBQUN4QyxNQUFBLGVBQWUsRUFBRSxLQUR1QjtBQUV4QyxNQUFBLFFBQVEsRUFBRSxLQUY4QjtBQUd4QyxNQUFBLFlBQVksRUFBRSxDQUgwQjtBQUl4QyxNQUFBLFFBQVEsRUFBRSxDQUo4QjtBQUt4QyxNQUFBLFVBQVUsRUFBRSxJQUw0QjtBQU14QyxNQUFBLFFBQVEsRUFBRTtBQU44QixLQUEzQixDQUFmO0FBU0EsSUFBQSxVQUFVLENBQUMsZ0JBQVgsQ0FBNEIsT0FBNUIsRUFBcUMsWUFBWTtBQUMvQyxNQUFBLFFBQVEsQ0FBQyxJQUFUO0FBQ0QsS0FGRDtBQUlBLElBQUEsVUFBVSxDQUFDLGdCQUFYLENBQTRCLE9BQTVCLEVBQXFDLFlBQVk7QUFDL0MsTUFBQSxRQUFRLENBQUMsUUFBVDtBQUNELEtBRkQ7QUFHRCxHQXJCRCxFQUwyQixDQTRCM0I7O0FBQ0EsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixVQUFoQixFQUE0QixVQUFVLElBQVYsRUFBZ0I7QUFDMUMsUUFBSSxhQUFhLEdBQUcsSUFBSSxDQUFDLGFBQUwsQ0FBbUIsZUFBbkIsQ0FBcEI7QUFDQSxRQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBTCxDQUFtQixvQkFBbkIsQ0FBakI7QUFDQSxRQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsYUFBTCxDQUFtQixvQkFBbkIsQ0FBakI7QUFDQSxRQUFJLFVBQVUsR0FBRyxJQUFJLENBQUMsZ0JBQUwsQ0FBc0IseUJBQXRCLENBQWpCO0FBRUEsUUFBSSxTQUFTLEdBQUcsSUFBSSxpQkFBSixDQUFhLGFBQWIsRUFBNEI7QUFDMUMsTUFBQSxlQUFlLEVBQUUsS0FEeUI7QUFFMUMsTUFBQSxRQUFRLEVBQUUsS0FGZ0M7QUFHMUMsTUFBQSxVQUFVLEVBQUUsSUFIOEI7QUFJMUMsTUFBQSxRQUFRLEVBQUU7QUFKZ0MsS0FBNUIsQ0FBaEI7QUFPQSxJQUFBLFNBQVMsQ0FBQyxFQUFWLENBQWEsb0JBQWIsRUFBbUMsVUFBVSxLQUFWLEVBQWlCLE9BQWpCLEVBQTBCO0FBQzNELE1BQUEsUUFBUSxDQUFDLFdBQVQsR0FBdUIsVUFBVSxDQUFWLEVBQWE7QUFDbEMsUUFBQSxDQUFDLENBQUMsY0FBRjtBQUNELE9BRkQ7QUFHRCxLQUpEO0FBTUEsSUFBQSxTQUFTLENBQUMsRUFBVixDQUFhLGtCQUFiLEVBQWlDLFVBQVUsS0FBVixFQUFpQixPQUFqQixFQUEwQjtBQUN6RCxNQUFBLFFBQVEsQ0FBQyxXQUFULEdBQXVCLFVBQVUsQ0FBVixFQUFhO0FBQ2xDLGVBQU8sSUFBUDtBQUNELE9BRkQ7QUFHRCxLQUpEO0FBTUEsSUFBQSxTQUFTLENBQUMsRUFBVixDQUFhLFFBQWIsRUFBdUIsWUFBWTtBQUNqQyxVQUFJLGNBQWMsR0FBRyxVQUFVLENBQUMsU0FBUyxDQUFDLGFBQVgsQ0FBL0I7QUFDQSxVQUFJLHNCQUFzQixHQUFHLElBQUksQ0FBQyxhQUFMLENBQW1CLHFDQUFuQixDQUE3QjtBQUVBLE1BQUEsc0JBQXNCLENBQUMsU0FBdkIsQ0FBaUMsTUFBakMsQ0FBd0Msb0NBQXhDO0FBQ0EsTUFBQSxjQUFjLENBQUMsU0FBZixDQUF5QixHQUF6QixDQUE2QixvQ0FBN0I7QUFDRCxLQU5EO0FBUUEsSUFBQSxVQUFVLENBQUMsZ0JBQVgsQ0FBNEIsT0FBNUIsRUFBcUMsWUFBWTtBQUMvQyxNQUFBLFNBQVMsQ0FBQyxJQUFWO0FBQ0QsS0FGRDtBQUlBLElBQUEsVUFBVSxDQUFDLGdCQUFYLENBQTRCLE9BQTVCLEVBQXFDLFlBQVk7QUFDL0MsTUFBQSxTQUFTLENBQUMsUUFBVjtBQUNELEtBRkQ7QUFJQSxPQUFHLE9BQUgsQ0FBVyxJQUFYLENBQWdCLFVBQWhCLEVBQTRCLFVBQVUsU0FBVixFQUFxQjtBQUMvQyxNQUFBLFNBQVMsQ0FBQyxnQkFBVixDQUEyQixPQUEzQixFQUFvQyxVQUFTLENBQVQsRUFBWTtBQUM5QyxRQUFBLENBQUMsQ0FBQyxjQUFGO0FBQ0EsWUFBSSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQVYsQ0FBa0IsRUFBOUI7QUFDQSxRQUFBLFNBQVMsQ0FBQyxNQUFWLENBQWlCLEtBQWpCO0FBQ0QsT0FKRDtBQUtELEtBTkQ7QUFPRCxHQWhERDtBQWlERCxDQTlFRDtBQWdGQTs7QUFDQTtBQUNBOzs7QUFDQSxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsa0JBQTFCLEVBQThDLFlBQVk7QUFDeEQsRUFBQSxRQUFRO0FBQ1QsQ0FGRDs7Ozs7QUN4RkE7O0FBQ0E7QUFDQTtBQUNBLElBQU0sS0FBSyxHQUFHLFNBQVIsS0FBUSxHQUFZO0FBQ3hCLE1BQUksRUFBRSxHQUFHLE1BQU0sQ0FBQyxXQUFQLEdBQXFCLElBQTlCO0FBQ0EsRUFBQSxRQUFRLENBQUMsZUFBVCxDQUF5QixLQUF6QixDQUErQixXQUEvQixDQUEyQyxNQUEzQyxZQUFzRCxFQUF0RDtBQUNELENBSEQ7O0FBS0EsSUFBTSxhQUFhLEdBQUcsU0FBaEIsYUFBZ0IsR0FBWTtBQUNoQyxNQUFJLEtBQUssR0FBRyxRQUFRLENBQUMsYUFBVCxDQUF1QixpQkFBdkIsQ0FBWjs7QUFFQSxNQUFJLEtBQUosRUFBVztBQUNULFFBQUksU0FBUyxHQUFHLENBQWhCO0FBQ0EsUUFBSSxPQUFPLEdBQUcsQ0FBZDtBQUNBLElBQUEsU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFMLENBQVMsQ0FBVCxFQUFZLFFBQVEsQ0FBQyxlQUFULENBQXlCLFNBQXpCLElBQXNDLE1BQU0sQ0FBQyxXQUE3QyxJQUE0RCxDQUF4RSxDQUFaOztBQUVBLFFBQUksSUFBSSxDQUFDLEdBQUwsQ0FBUyxTQUFTLEdBQUcsT0FBckIsSUFBZ0MsQ0FBcEMsRUFBdUM7QUFDckM7QUFDQSxVQUFJLEdBQUcsR0FBRyxPQUFPLElBQUksT0FBTyxTQUFTLEdBQUcsT0FBbkIsQ0FBckIsQ0FGcUMsQ0FFYTs7QUFFbEQsTUFBQSxLQUFLLENBQUMsS0FBTixDQUFZLFNBQVosd0JBQXVDLEdBQUcsR0FBRyxDQUFDLENBQTlDO0FBQ0QsS0FMRCxNQUtPO0FBQ0wsTUFBQSxLQUFLLENBQUMsS0FBTixDQUFZLFNBQVo7QUFDRDtBQUNGO0FBQ0YsQ0FqQkQ7QUFtQkE7O0FBQ0E7QUFDQTs7O0FBQ0EsUUFBUSxDQUFDLGdCQUFULENBQTBCLGtCQUExQixFQUE4QyxZQUFZO0FBQ3hELEVBQUEsYUFBYTtBQUNiLEVBQUEsS0FBSztBQUNOLENBSEQ7QUFLQSxNQUFNLENBQUMsZ0JBQVAsQ0FBd0IsUUFBeEIsRUFBa0MsWUFBWTtBQUM1QyxFQUFBLGFBQWE7QUFDYixFQUFBLEtBQUs7QUFDTixDQUhEO0FBS0EsTUFBTSxDQUFDLGdCQUFQLENBQXdCLFFBQXhCLEVBQWtDLFlBQVk7QUFDNUMsRUFBQSxhQUFhO0FBQ2QsQ0FGRDs7Ozs7Ozs7Ozs7Ozs7Ozs7QUN4Q0E7O0FBQ0E7QUFDQTtBQUNBLElBQU0sUUFBUSxHQUFHLFNBQVgsUUFBVyxHQUFNO0FBQ3JCLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxhQUFULENBQXVCLGdCQUF2QixDQUFuQjtBQUNBLE1BQU0sUUFBUSxHQUFHLFFBQVEsQ0FBQyxhQUFULENBQXVCLFdBQXZCLENBQWpCO0FBQ0EsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLGFBQVQsQ0FBdUIscUJBQXZCLENBQWQ7QUFDQSxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsYUFBTixDQUFvQixlQUFwQixFQUFxQyxLQUF0RDtBQUNBLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxnQkFBVCxDQUEwQixZQUExQixDQUFoQjtBQUNBLE1BQUksVUFBVSxHQUFHLEVBQWpCO0FBQ0EsTUFBSSxJQUFJLEdBQUcsRUFBWDtBQUNBLE1BQUksUUFBUSxHQUFHLElBQUksUUFBSixFQUFmO0FBQ0EsTUFBSSxPQUFPLEdBQUcsTUFBTSxDQUFDLFVBQXJCO0FBRUEsRUFBQSxRQUFRLENBQUMsTUFBVCxDQUFnQixRQUFoQixFQUEwQixjQUExQjtBQUNBLEVBQUEsUUFBUSxDQUFDLE1BQVQsQ0FBZ0IsV0FBaEIsRUFBNkIsUUFBN0I7QUFDQSxFQUFBLFFBQVEsQ0FBQyxNQUFULENBQWdCLE9BQWhCLEVBQXlCLE1BQU0sQ0FBQyxLQUFoQyxFQWJxQixDQWFtQjs7QUFDeEMsRUFBQSxRQUFRLENBQUMsTUFBVCxDQUFnQixNQUFoQixFQUF3QixNQUFNLENBQUMsV0FBL0I7QUFFQSxLQUFHLE9BQUgsQ0FBVyxJQUFYLENBQWdCLE9BQWhCLEVBQXlCLFVBQUMsTUFBRCxFQUFZO0FBQ25DLFFBQUksTUFBTSxDQUFDLE9BQVAsQ0FBZSxJQUFmLEtBQXdCLFVBQTVCLEVBQXdDO0FBQ3RDLE1BQUEsVUFBVSxDQUFDLE1BQU0sQ0FBQyxPQUFQLENBQWUsUUFBaEIsQ0FBVixHQUFzQyxFQUF0QztBQUNBLE1BQUEsVUFBVSxDQUFDLE1BQU0sQ0FBQyxPQUFQLENBQWUsUUFBaEIsQ0FBVixDQUFvQyxNQUFwQyxJQUE4QyxNQUFNLENBQUMsT0FBUCxDQUFlLFFBQTdEO0FBQ0EsTUFBQSxVQUFVLENBQUMsTUFBTSxDQUFDLE9BQVAsQ0FBZSxRQUFoQixDQUFWLENBQW9DLE9BQXBDLElBQStDLE1BQU0sQ0FBQyxLQUF0RDtBQUNBLE1BQUEsVUFBVSxDQUFDLE1BQU0sQ0FBQyxPQUFQLENBQWUsUUFBaEIsQ0FBVixDQUFvQyxNQUFwQyxJQUE4QyxNQUFNLENBQUMsT0FBUCxDQUFlLElBQTdEO0FBQ0Q7O0FBRUQsUUFBSSxNQUFNLENBQUMsT0FBUCxDQUFlLElBQWYsS0FBd0IsTUFBNUIsRUFBb0M7QUFDbEMsTUFBQSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQVAsQ0FBZSxJQUFoQixDQUFKLEdBQTRCLEVBQTVCO0FBQ0EsTUFBQSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQVAsQ0FBZSxJQUFoQixDQUFKLENBQTBCLE1BQTFCLElBQW9DLE1BQU0sQ0FBQyxPQUFQLENBQWUsSUFBbkQ7QUFDQSxNQUFBLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBUCxDQUFlLElBQWhCLENBQUosQ0FBMEIsT0FBMUIsSUFBcUMsTUFBTSxDQUFDLEtBQTVDLENBSGtDLENBS2xDOztBQUNBLE1BQUEsUUFBUSxDQUFDLE1BQVQsQ0FBZ0IsTUFBaEIsRUFBd0IsSUFBSSxDQUFDLFNBQUwsQ0FBZSxJQUFmLENBQXhCO0FBQ0Q7O0FBRUQsUUFBSSxNQUFNLENBQUMsT0FBUCxDQUFlLElBQWYsS0FBd0IsUUFBNUIsRUFBc0M7QUFDcEMsVUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQXRCLENBRG9DLENBR3BDOztBQUNBLE1BQUEsUUFBUSxDQUFDLE1BQVQsQ0FBZ0IsUUFBaEIsRUFBMEIsTUFBMUI7QUFDRDtBQUNGLEdBdkJEOztBQXlCQSxNQUFJLE1BQU0sQ0FBQyxJQUFQLENBQVksVUFBWixFQUF3QixNQUF4QixLQUFtQyxDQUFuQyxJQUF3QyxVQUFVLENBQUMsV0FBWCxLQUEyQixNQUF2RSxFQUErRTtBQUM3RSxRQUFJLFdBQVcsR0FBRyxFQUFsQjtBQUVBLElBQUEsUUFBUSxDQUFDLE1BQVQsQ0FBZ0IsWUFBaEIsRUFBOEIsSUFBSSxDQUFDLFNBQUwsQ0FBZSxVQUFmLENBQTlCOztBQUVBLHVDQUEyQixNQUFNLENBQUMsT0FBUCxDQUFlLFVBQWYsQ0FBM0IscUNBQXVEO0FBQUE7QUFBQSxVQUEzQyxHQUEyQztBQUFBLFVBQXRDLEtBQXNDOztBQUNyRCxVQUFJLE9BQU8sQ0FBQyxRQUFSLENBQWlCLEdBQWpCLENBQUosRUFBMkI7QUFDekIsUUFBQSxXQUFXLEdBQUcsR0FBZDtBQUNELE9BRkQsTUFFTztBQUNMLFFBQUEsV0FBVyxHQUFHLEdBQWQ7QUFDRDs7QUFFRCxVQUFJLEtBQUssQ0FBQyxLQUFOLEtBQWdCLEVBQXBCLEVBQXdCO0FBQ3RCLFFBQUEsT0FBTyxHQUFHLE9BQU8sR0FBRyxXQUFWLEdBQXdCLEtBQUssQ0FBQyxJQUE5QixHQUFxQyxHQUFyQyxHQUEyQyxLQUFLLENBQUMsS0FBM0Q7QUFDRDtBQUNGO0FBQ0Y7O0FBRUQsTUFBSSxjQUFjLEdBQUcsSUFBSSxlQUFKLENBQW9CLFFBQXBCLEVBQThCLFFBQTlCLEVBQXJCO0FBRUEsRUFBQSxLQUFLLENBQUMsTUFBTSxDQUFDLE9BQVIsRUFBaUI7QUFDcEIsSUFBQSxNQUFNLEVBQUUsTUFEWTtBQUVwQixJQUFBLFdBQVcsRUFBRSxhQUZPO0FBR3BCLElBQUEsT0FBTyxFQUFFLElBQUksT0FBSixDQUFZO0FBQ25CLHNCQUFnQjtBQURHLEtBQVosQ0FIVztBQU1wQixJQUFBLElBQUksRUFBRTtBQU5jLEdBQWpCLENBQUwsQ0FRRyxJQVJILENBUVEsVUFBQSxJQUFJO0FBQUEsV0FBSSxJQUFJLENBQUMsSUFBTCxFQUFKO0FBQUEsR0FSWixFQVNHLElBVEgsQ0FTUSxVQUFDLElBQUQsRUFBVTtBQUNkLElBQUEsUUFBUSxDQUFDLFNBQVQsR0FBcUIsSUFBSSxDQUFDLEtBQTFCLENBRGMsQ0FHZDs7QUFDQSxRQUFJLFVBQUosRUFBZ0I7QUFDZCxNQUFBLFVBQVUsQ0FBQyxTQUFYLEdBQXVCLElBQUksQ0FBQyxVQUE1Qjs7QUFFQSxVQUFJLElBQUksQ0FBQyxLQUFMLEtBQWUsQ0FBbkIsRUFBc0I7QUFDcEIsWUFBSSxZQUFXLEdBQUcsRUFBbEI7O0FBRUEsWUFBSSxPQUFPLENBQUMsUUFBUixDQUFpQixHQUFqQixDQUFKLEVBQTJCO0FBQ3pCLFVBQUEsWUFBVyxHQUFHLEdBQWQ7QUFDRCxTQUZELE1BRU87QUFDTCxVQUFBLFlBQVcsR0FBRyxHQUFkO0FBQ0Q7O0FBRUQsUUFBQSxPQUFPLEdBQUcsT0FBTyxHQUFHLFlBQVYsR0FBd0IsT0FBeEIsR0FBa0MsSUFBSSxDQUFDLEtBQWpEO0FBQ0Q7O0FBRUQsTUFBQSxNQUFNLENBQUMsT0FBUCxDQUFlLFNBQWYsQ0FBeUIsSUFBekIsRUFBK0IsSUFBL0IsRUFBcUMsT0FBckM7QUFFQSxNQUFBLEtBQUssQ0FBQyxjQUFOO0FBQ0EsTUFBQSxVQUFVO0FBQ1g7QUFDRixHQWpDSCxFQWtDRyxLQWxDSCxDQWtDUyxVQUFDLEtBQUQsRUFBVztBQUNoQixJQUFBLE9BQU8sQ0FBQyxHQUFSLENBQVksS0FBWjtBQUNELEdBcENIO0FBcUNELENBbEdEOztBQW9HQSxJQUFNLE1BQU0sR0FBRyxTQUFULE1BQVMsR0FBTTtBQUNuQixNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsWUFBMUIsQ0FBaEI7QUFDQSxNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsWUFBMUIsQ0FBckI7QUFFQSxLQUFHLE9BQUgsQ0FBVyxJQUFYLENBQWdCLE9BQWhCLEVBQXlCLFVBQUMsTUFBRCxFQUFZO0FBQ25DLElBQUEsTUFBTSxDQUFDLGdCQUFQLENBQXdCLFFBQXhCLEVBQWtDLFVBQUMsQ0FBRCxFQUFPO0FBQ3ZDLE1BQUEsQ0FBQyxDQUFDLGNBQUY7QUFFQSxNQUFBLE1BQU0sQ0FBQyxXQUFQLEdBQXFCLENBQXJCO0FBRUEsTUFBQSxRQUFRO0FBQ1QsS0FORDtBQU9ELEdBUkQ7QUFVQSxLQUFHLE9BQUgsQ0FBVyxJQUFYLENBQWdCLFlBQWhCLEVBQThCLFVBQUMsV0FBRCxFQUFpQjtBQUM3QyxJQUFBLFdBQVcsQ0FBQyxnQkFBWixDQUE2QixPQUE3QixFQUFzQyxZQUFNO0FBQzFDLE1BQUEsTUFBTSxDQUFDLFdBQVAsR0FBcUIsQ0FBckI7QUFFQSxNQUFBLFFBQVE7QUFDVCxLQUpEO0FBS0QsR0FORDtBQU9ELENBckJEOztBQXVCQSxJQUFNLFVBQVUsR0FBRyxTQUFiLFVBQWEsR0FBTTtBQUN2QixNQUFNLGVBQWUsR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsc0JBQTFCLENBQXhCO0FBQ0EsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGFBQVQsQ0FBdUIsMEJBQXZCLENBQXZCO0FBQ0EsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGFBQVQsQ0FBdUIsMEJBQXZCLENBQXZCO0FBRUEsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixlQUFoQixFQUFpQyxVQUFDLGNBQUQsRUFBb0I7QUFDbkQsSUFBQSxjQUFjLENBQUMsZ0JBQWYsQ0FBZ0MsT0FBaEMsRUFBeUMsVUFBQyxDQUFELEVBQU87QUFDOUMsTUFBQSxDQUFDLENBQUMsY0FBRixHQUQ4QyxDQUc5Qzs7QUFDQSxVQUFJLGNBQWMsQ0FBQyxTQUFmLEtBQTZCLE1BQU0sQ0FBQyxXQUF4QyxFQUFxRDtBQUNuRCxRQUFBLE1BQU0sQ0FBQyxXQUFQLEdBQXFCLGNBQWMsQ0FBQyxTQUFwQztBQUNEOztBQUVELE1BQUEsUUFBUTtBQUNULEtBVEQ7QUFVRCxHQVhEOztBQWFBLE1BQUksY0FBSixFQUFvQjtBQUNsQixJQUFBLGNBQWMsQ0FBQyxnQkFBZixDQUFnQyxPQUFoQyxFQUF5QyxVQUFDLENBQUQsRUFBTztBQUM5QyxNQUFBLENBQUMsQ0FBQyxjQUFGO0FBRUEsTUFBQSxNQUFNLENBQUMsV0FBUDtBQUVBLE1BQUEsUUFBUTtBQUNULEtBTkQ7QUFPRDs7QUFFRCxNQUFJLGNBQUosRUFBb0I7QUFDbEIsSUFBQSxjQUFjLENBQUMsZ0JBQWYsQ0FBZ0MsT0FBaEMsRUFBeUMsVUFBQyxDQUFELEVBQU87QUFDOUMsTUFBQSxDQUFDLENBQUMsY0FBRjtBQUVBLE1BQUEsTUFBTSxDQUFDLFdBQVA7QUFFQSxNQUFBLFFBQVE7QUFDVCxLQU5EO0FBT0Q7QUFDRixDQXJDRDtBQXVDQTs7QUFDQTtBQUNBOzs7QUFDQSxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsa0JBQTFCLEVBQThDLFlBQU07QUFDbEQsRUFBQSxNQUFNO0FBQ04sRUFBQSxVQUFVO0FBQ1gsQ0FIRDs7Ozs7QUN4S0E7O0FBQ0E7QUFDQTtBQUNBLElBQU0sY0FBYyxHQUFHLFNBQWpCLGNBQWlCLEdBQVk7QUFDakMsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLG9CQUFULENBQThCLE1BQTlCLEVBQXNDLENBQXRDLENBQWI7QUFDQSxNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsaUJBQTFCLENBQXZCO0FBRUEsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixjQUFoQixFQUFnQyxVQUFVLGFBQVYsRUFBeUI7QUFDdkQsSUFBQSxhQUFhLENBQUMsZ0JBQWQsQ0FBK0IsT0FBL0IsRUFBd0MsVUFBVSxDQUFWLEVBQWE7QUFDbkQsTUFBQSxDQUFDLENBQUMsY0FBRjs7QUFDQSxVQUFJLElBQUksQ0FBQyxTQUFMLENBQWUsUUFBZixDQUF3QixhQUF4QixDQUFKLEVBQTRDO0FBQzFDLFFBQUEsSUFBSSxDQUFDLFNBQUwsQ0FBZSxNQUFmLENBQXNCLGFBQXRCO0FBQ0Q7O0FBQ0QsTUFBQSxJQUFJLENBQUMsU0FBTCxDQUFlLE1BQWYsQ0FBc0IsV0FBdEI7QUFDRCxLQU5EO0FBT0QsR0FSRDtBQVNELENBYkQ7QUFlQTs7QUFDQTtBQUNBOzs7QUFDQSxJQUFNLE9BQU8sR0FBRyxTQUFWLE9BQVUsR0FBWTtBQUMxQixNQUFNLFlBQVksR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIseUJBQTFCLENBQXJCO0FBRUEsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixZQUFoQixFQUE4QixVQUFVLFdBQVYsRUFBdUI7QUFDbkQsUUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLGFBQVQsQ0FBdUIsTUFBdkIsQ0FBdkI7QUFDQSxRQUFNLFdBQVcsR0FBRyxXQUFXLENBQUMsYUFBWixDQUEwQixHQUExQixDQUFwQjtBQUVBLElBQUEsY0FBYyxDQUFDLFNBQWYsR0FBMkIsU0FBM0I7QUFDQSxJQUFBLFdBQVcsQ0FBQyxXQUFaLENBQXdCLGNBQXhCO0FBRUEsSUFBQSxjQUFjLENBQUMsZ0JBQWYsQ0FBZ0MsT0FBaEMsRUFBeUMsVUFBVSxDQUFWLEVBQWE7QUFDcEQsTUFBQSxDQUFDLENBQUMsY0FBRjtBQUNBLE1BQUEsV0FBVyxDQUFDLFNBQVosQ0FBc0IsTUFBdEIsQ0FBNkIsZUFBN0I7QUFDRCxLQUhEO0FBSUQsR0FYRDtBQVlELENBZkQ7QUFpQkE7O0FBQ0E7QUFDQTs7O0FBQ0EsUUFBUSxDQUFDLGdCQUFULENBQTBCLGtCQUExQixFQUE4QyxZQUFZO0FBQ3hELEVBQUEsY0FBYztBQUNkLEVBQUEsT0FBTztBQUNSLENBSEQ7Ozs7O0FDekNBOztBQUNBO0FBQ0E7QUFDQSxJQUFNLEtBQUssR0FBRyxTQUFSLEtBQVEsR0FBWTtBQUN4QixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsb0JBQVQsQ0FBOEIsTUFBOUIsRUFBc0MsQ0FBdEMsQ0FBYjtBQUNBLE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxnQkFBVCxDQUEwQixXQUExQixDQUFkO0FBRUEsS0FBRyxPQUFILENBQVcsSUFBWCxDQUFnQixLQUFoQixFQUF1QixVQUFVLElBQVYsRUFBZ0I7QUFDckMsUUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLGFBQUwsQ0FBbUIsa0JBQW5CLENBQWxCO0FBRUEsSUFBQSxXQUFXLENBQUMsZ0JBQVosQ0FBNkIsT0FBN0IsRUFBc0MsVUFBVSxDQUFWLEVBQWE7QUFDakQsTUFBQSxDQUFDLENBQUMsY0FBRjtBQUNBLE1BQUEsSUFBSSxDQUFDLFNBQUwsQ0FBZSxNQUFmLENBQXNCLGFBQXRCO0FBQ0EsTUFBQSxJQUFJLENBQUMsU0FBTCxDQUFlLE1BQWYsQ0FBc0IsWUFBdEI7QUFDRCxLQUpEO0FBS0QsR0FSRDtBQVNELENBYkQ7QUFlQTs7QUFDQTtBQUNBOzs7QUFDQSxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsa0JBQTFCLEVBQThDLFlBQVk7QUFDeEQsRUFBQSxLQUFLO0FBQ04sQ0FGRDs7Ozs7QUNyQkE7O0FBQ0E7QUFDQTtBQUNBLElBQU0sZ0JBQWdCLEdBQUcsU0FBbkIsZ0JBQW1CLEdBQVk7QUFDbkMsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLG9CQUFULENBQThCLE1BQTlCLEVBQXNDLENBQXRDLENBQWI7QUFDQSxNQUFNLGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxnQkFBVCxDQUEwQixtQkFBMUIsQ0FBekI7QUFDQSxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQUMsYUFBVCxDQUF1QixlQUF2QixDQUFwQjtBQUVBLEtBQUcsT0FBSCxDQUFXLElBQVgsQ0FBZ0IsZ0JBQWhCLEVBQWtDLFVBQVUsZUFBVixFQUEyQjtBQUMzRCxJQUFBLGVBQWUsQ0FBQyxnQkFBaEIsQ0FBaUMsT0FBakMsRUFBMEMsVUFBVSxDQUFWLEVBQWE7QUFDckQsTUFBQSxDQUFDLENBQUMsY0FBRjs7QUFDQSxVQUFJLElBQUksQ0FBQyxTQUFMLENBQWUsUUFBZixDQUF3QixXQUF4QixDQUFKLEVBQTBDO0FBQ3hDLFFBQUEsSUFBSSxDQUFDLFNBQUwsQ0FBZSxNQUFmLENBQXNCLFdBQXRCO0FBQ0Q7O0FBQ0QsTUFBQSxJQUFJLENBQUMsU0FBTCxDQUFlLE1BQWYsQ0FBc0IsYUFBdEI7QUFDQSxNQUFBLFdBQVcsQ0FBQyxLQUFaO0FBQ0QsS0FQRDtBQVFELEdBVEQ7QUFVRCxDQWZEO0FBaUJBOztBQUNBO0FBQ0E7OztBQUNBLFFBQVEsQ0FBQyxnQkFBVCxDQUEwQixrQkFBMUIsRUFBOEMsWUFBWTtBQUN4RCxFQUFBLGdCQUFnQjtBQUNqQixDQUZEOzs7Ozs7Ozs7O0FDdkJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDTyxTQUFTLE1BQVQsR0FBa0I7QUFDeEI7QUFBTztBQUFlLGFBQVMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztBQUExQztBQUNBOzs7OztBQ1BEOztBQUNBOztBQUVBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOzs7OztBQ1JBOztBQUVBLElBQUksYUFBYSxnQkFBZ0IsQ0FBQyxTQUFsQyxFQUE2QztBQUMzQyxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIsY0FBMUIsQ0FBZjtBQUVBLEVBQUEsTUFBTSxDQUFDLE9BQVAsQ0FBZSxVQUFBLEdBQUcsRUFBSTtBQUNwQixRQUFJLE9BQU8sR0FBRyxDQUFDLE9BQUosQ0FBWSxHQUFuQixLQUEyQixXQUEvQixFQUE0QztBQUMxQyxNQUFBLEdBQUcsQ0FBQyxHQUFKLEdBQVUsR0FBRyxDQUFDLE9BQUosQ0FBWSxHQUF0QjtBQUNEOztBQUVELFFBQUksT0FBTyxHQUFHLENBQUMsT0FBSixDQUFZLE1BQW5CLEtBQThCLFdBQWxDLEVBQStDO0FBQzdDLE1BQUEsR0FBRyxDQUFDLE1BQUosR0FBYSxHQUFHLENBQUMsT0FBSixDQUFZLE1BQXpCO0FBQ0Q7QUFDRixHQVJEO0FBU0QsQ0FaRCxNQVlPO0FBQ0w7QUFDQSxFQUFBLFNBQVMsQ0FBQyxJQUFWLEdBRkssQ0FFYTtBQUNuQjs7Ozs7QUNqQkQ7O0FBQ0E7O0FBQ0E7Ozs7QUFFQTtBQUNBLElBQUksTUFBTSxDQUFDLFFBQVAsSUFBbUIsQ0FBQyxRQUFRLENBQUMsU0FBVCxDQUFtQixPQUEzQyxFQUFvRDtBQUNsRCxFQUFBLFFBQVEsQ0FBQyxTQUFULENBQW1CLE9BQW5CLEdBQTZCLEtBQUssQ0FBQyxTQUFOLENBQWdCLE9BQTdDO0FBQ0Q7O0FBRUQsSUFBTSxrQkFBa0IsR0FBRyxRQUFRLENBQUMsZ0JBQVQsQ0FBMEIseUJBQTFCLENBQTNCO0FBRUE7O0FBQ0E7QUFDQTs7QUFDQSxJQUFJLGtCQUFKLEVBQXdCO0FBRXRCLEVBQUEsa0JBQWtCLENBQUMsT0FBbkIsQ0FBMkIsVUFBQSxpQkFBaUIsRUFBSTtBQUM5QyxRQUFJLGFBQWEsR0FBRyxpQkFBaUIsQ0FBQyxhQUFsQixDQUFnQyxzQkFBaEMsQ0FBcEI7QUFDQSxRQUFJLFdBQVcsR0FBRyxhQUFhLENBQUMsT0FBZCxDQUFzQixLQUF4QztBQUNBLFFBQUksT0FBTyxHQUFHLGFBQWEsQ0FBQyxPQUFkLENBQXNCLEdBQXBDOztBQUVBLFFBQUksT0FBTyxLQUFLLE1BQVosSUFBc0IsYUFBSSxXQUFKLEVBQTFCLEVBQTZDO0FBQzNDLFVBQUksR0FBRyxHQUFHLElBQUksWUFBSixFQUFWO0FBQ0EsTUFBQSxHQUFHLENBQUMsVUFBSixDQUFlLFdBQWY7QUFDQSxNQUFBLEdBQUcsQ0FBQyxXQUFKLENBQWdCLGFBQWhCO0FBQ0Q7O0FBRUQsUUFBTSxZQUFZLEdBQUcsSUFBSSxhQUFKLENBQVMsYUFBVCxFQUF3QjtBQUMzQyxNQUFBLFVBQVUsRUFBRSx3QkFEK0I7QUFFM0MsTUFBQSxXQUFXLEVBQUUsS0FGOEI7QUFHM0MsTUFBQSxNQUFNLEVBQUUsQ0FIbUM7QUFJM0MsTUFBQSxRQUFRLEVBQUU7QUFKaUMsS0FBeEIsQ0FBckI7QUFPQSxJQUFBLFlBQVksQ0FBQyxFQUFiLENBQWdCLE9BQWhCLEVBQXlCLFlBQU07QUFDN0IsTUFBQSxZQUFZLENBQUMsSUFBYjtBQUNELEtBRkQ7QUFJQSxJQUFBLFlBQVksQ0FBQyxFQUFiLENBQWdCLFNBQWhCLEVBQTJCLFlBQU07QUFDL0IsTUFBQSxpQkFBaUIsQ0FBQyxTQUFsQixDQUE0QixHQUE1QixDQUFnQyxvQkFBaEM7QUFDRCxLQUZEO0FBSUEsSUFBQSxZQUFZLENBQUMsRUFBYixDQUFnQixPQUFoQixFQUF5QixZQUFNO0FBQzdCLE1BQUEsWUFBWSxDQUFDLElBQWI7QUFDRCxLQUZEO0FBR0QsR0E3QkQ7QUE4QkQiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbigpe2Z1bmN0aW9uIHIoZSxuLHQpe2Z1bmN0aW9uIG8oaSxmKXtpZighbltpXSl7aWYoIWVbaV0pe3ZhciBjPVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmU7aWYoIWYmJmMpcmV0dXJuIGMoaSwhMCk7aWYodSlyZXR1cm4gdShpLCEwKTt2YXIgYT1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK2krXCInXCIpO3Rocm93IGEuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixhfXZhciBwPW5baV09e2V4cG9ydHM6e319O2VbaV1bMF0uY2FsbChwLmV4cG9ydHMsZnVuY3Rpb24ocil7dmFyIG49ZVtpXVsxXVtyXTtyZXR1cm4gbyhufHxyKX0scCxwLmV4cG9ydHMscixlLG4sdCl9cmV0dXJuIG5baV0uZXhwb3J0c31mb3IodmFyIHU9XCJmdW5jdGlvblwiPT10eXBlb2YgcmVxdWlyZSYmcmVxdWlyZSxpPTA7aTx0Lmxlbmd0aDtpKyspbyh0W2ldKTtyZXR1cm4gb31yZXR1cm4gcn0pKCkiLCIvKipcbiAqIG1hdGNoZXNTZWxlY3RvciB2Mi4wLjJcbiAqIG1hdGNoZXNTZWxlY3RvciggZWxlbWVudCwgJy5zZWxlY3RvcicgKVxuICogTUlUIGxpY2Vuc2VcbiAqL1xuXG4vKmpzaGludCBicm93c2VyOiB0cnVlLCBzdHJpY3Q6IHRydWUsIHVuZGVmOiB0cnVlLCB1bnVzZWQ6IHRydWUgKi9cblxuKCBmdW5jdGlvbiggd2luZG93LCBmYWN0b3J5ICkge1xuICAvKmdsb2JhbCBkZWZpbmU6IGZhbHNlLCBtb2R1bGU6IGZhbHNlICovXG4gICd1c2Ugc3RyaWN0JztcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBmYWN0b3J5ICk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIHdpbmRvdy5tYXRjaGVzU2VsZWN0b3IgPSBmYWN0b3J5KCk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCkge1xuICAndXNlIHN0cmljdCc7XG5cbiAgdmFyIG1hdGNoZXNNZXRob2QgPSAoIGZ1bmN0aW9uKCkge1xuICAgIHZhciBFbGVtUHJvdG8gPSB3aW5kb3cuRWxlbWVudC5wcm90b3R5cGU7XG4gICAgLy8gY2hlY2sgZm9yIHRoZSBzdGFuZGFyZCBtZXRob2QgbmFtZSBmaXJzdFxuICAgIGlmICggRWxlbVByb3RvLm1hdGNoZXMgKSB7XG4gICAgICByZXR1cm4gJ21hdGNoZXMnO1xuICAgIH1cbiAgICAvLyBjaGVjayB1bi1wcmVmaXhlZFxuICAgIGlmICggRWxlbVByb3RvLm1hdGNoZXNTZWxlY3RvciApIHtcbiAgICAgIHJldHVybiAnbWF0Y2hlc1NlbGVjdG9yJztcbiAgICB9XG4gICAgLy8gY2hlY2sgdmVuZG9yIHByZWZpeGVzXG4gICAgdmFyIHByZWZpeGVzID0gWyAnd2Via2l0JywgJ21veicsICdtcycsICdvJyBdO1xuXG4gICAgZm9yICggdmFyIGk9MDsgaSA8IHByZWZpeGVzLmxlbmd0aDsgaSsrICkge1xuICAgICAgdmFyIHByZWZpeCA9IHByZWZpeGVzW2ldO1xuICAgICAgdmFyIG1ldGhvZCA9IHByZWZpeCArICdNYXRjaGVzU2VsZWN0b3InO1xuICAgICAgaWYgKCBFbGVtUHJvdG9bIG1ldGhvZCBdICkge1xuICAgICAgICByZXR1cm4gbWV0aG9kO1xuICAgICAgfVxuICAgIH1cbiAgfSkoKTtcblxuICByZXR1cm4gZnVuY3Rpb24gbWF0Y2hlc1NlbGVjdG9yKCBlbGVtLCBzZWxlY3RvciApIHtcbiAgICByZXR1cm4gZWxlbVsgbWF0Y2hlc01ldGhvZCBdKCBzZWxlY3RvciApO1xuICB9O1xuXG59KSk7XG4iLCIvKipcbiAqIEV2RW1pdHRlciB2MS4xLjBcbiAqIExpbCcgZXZlbnQgZW1pdHRlclxuICogTUlUIExpY2Vuc2VcbiAqL1xuXG4vKiBqc2hpbnQgdW51c2VkOiB0cnVlLCB1bmRlZjogdHJ1ZSwgc3RyaWN0OiB0cnVlICovXG5cbiggZnVuY3Rpb24oIGdsb2JhbCwgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovIC8qIGdsb2JhbHMgZGVmaW5lLCBtb2R1bGUsIHdpbmRvdyAqL1xuICBpZiAoIHR5cGVvZiBkZWZpbmUgPT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kICkge1xuICAgIC8vIEFNRCAtIFJlcXVpcmVKU1xuICAgIGRlZmluZSggZmFjdG9yeSApO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTIC0gQnJvd3NlcmlmeSwgV2VicGFja1xuICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeSgpO1xuICB9IGVsc2Uge1xuICAgIC8vIEJyb3dzZXIgZ2xvYmFsc1xuICAgIGdsb2JhbC5FdkVtaXR0ZXIgPSBmYWN0b3J5KCk7XG4gIH1cblxufSggdHlwZW9mIHdpbmRvdyAhPSAndW5kZWZpbmVkJyA/IHdpbmRvdyA6IHRoaXMsIGZ1bmN0aW9uKCkge1xuXG5cInVzZSBzdHJpY3RcIjtcblxuZnVuY3Rpb24gRXZFbWl0dGVyKCkge31cblxudmFyIHByb3RvID0gRXZFbWl0dGVyLnByb3RvdHlwZTtcblxucHJvdG8ub24gPSBmdW5jdGlvbiggZXZlbnROYW1lLCBsaXN0ZW5lciApIHtcbiAgaWYgKCAhZXZlbnROYW1lIHx8ICFsaXN0ZW5lciApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgLy8gc2V0IGV2ZW50cyBoYXNoXG4gIHZhciBldmVudHMgPSB0aGlzLl9ldmVudHMgPSB0aGlzLl9ldmVudHMgfHwge307XG4gIC8vIHNldCBsaXN0ZW5lcnMgYXJyYXlcbiAgdmFyIGxpc3RlbmVycyA9IGV2ZW50c1sgZXZlbnROYW1lIF0gPSBldmVudHNbIGV2ZW50TmFtZSBdIHx8IFtdO1xuICAvLyBvbmx5IGFkZCBvbmNlXG4gIGlmICggbGlzdGVuZXJzLmluZGV4T2YoIGxpc3RlbmVyICkgPT0gLTEgKSB7XG4gICAgbGlzdGVuZXJzLnB1c2goIGxpc3RlbmVyICk7XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbnByb3RvLm9uY2UgPSBmdW5jdGlvbiggZXZlbnROYW1lLCBsaXN0ZW5lciApIHtcbiAgaWYgKCAhZXZlbnROYW1lIHx8ICFsaXN0ZW5lciApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgLy8gYWRkIGV2ZW50XG4gIHRoaXMub24oIGV2ZW50TmFtZSwgbGlzdGVuZXIgKTtcbiAgLy8gc2V0IG9uY2UgZmxhZ1xuICAvLyBzZXQgb25jZUV2ZW50cyBoYXNoXG4gIHZhciBvbmNlRXZlbnRzID0gdGhpcy5fb25jZUV2ZW50cyA9IHRoaXMuX29uY2VFdmVudHMgfHwge307XG4gIC8vIHNldCBvbmNlTGlzdGVuZXJzIG9iamVjdFxuICB2YXIgb25jZUxpc3RlbmVycyA9IG9uY2VFdmVudHNbIGV2ZW50TmFtZSBdID0gb25jZUV2ZW50c1sgZXZlbnROYW1lIF0gfHwge307XG4gIC8vIHNldCBmbGFnXG4gIG9uY2VMaXN0ZW5lcnNbIGxpc3RlbmVyIF0gPSB0cnVlO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxucHJvdG8ub2ZmID0gZnVuY3Rpb24oIGV2ZW50TmFtZSwgbGlzdGVuZXIgKSB7XG4gIHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHMgJiYgdGhpcy5fZXZlbnRzWyBldmVudE5hbWUgXTtcbiAgaWYgKCAhbGlzdGVuZXJzIHx8ICFsaXN0ZW5lcnMubGVuZ3RoICkge1xuICAgIHJldHVybjtcbiAgfVxuICB2YXIgaW5kZXggPSBsaXN0ZW5lcnMuaW5kZXhPZiggbGlzdGVuZXIgKTtcbiAgaWYgKCBpbmRleCAhPSAtMSApIHtcbiAgICBsaXN0ZW5lcnMuc3BsaWNlKCBpbmRleCwgMSApO1xuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5wcm90by5lbWl0RXZlbnQgPSBmdW5jdGlvbiggZXZlbnROYW1lLCBhcmdzICkge1xuICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzICYmIHRoaXMuX2V2ZW50c1sgZXZlbnROYW1lIF07XG4gIGlmICggIWxpc3RlbmVycyB8fCAhbGlzdGVuZXJzLmxlbmd0aCApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgLy8gY29weSBvdmVyIHRvIGF2b2lkIGludGVyZmVyZW5jZSBpZiAub2ZmKCkgaW4gbGlzdGVuZXJcbiAgbGlzdGVuZXJzID0gbGlzdGVuZXJzLnNsaWNlKDApO1xuICBhcmdzID0gYXJncyB8fCBbXTtcbiAgLy8gb25jZSBzdHVmZlxuICB2YXIgb25jZUxpc3RlbmVycyA9IHRoaXMuX29uY2VFdmVudHMgJiYgdGhpcy5fb25jZUV2ZW50c1sgZXZlbnROYW1lIF07XG5cbiAgZm9yICggdmFyIGk9MDsgaSA8IGxpc3RlbmVycy5sZW5ndGg7IGkrKyApIHtcbiAgICB2YXIgbGlzdGVuZXIgPSBsaXN0ZW5lcnNbaV1cbiAgICB2YXIgaXNPbmNlID0gb25jZUxpc3RlbmVycyAmJiBvbmNlTGlzdGVuZXJzWyBsaXN0ZW5lciBdO1xuICAgIGlmICggaXNPbmNlICkge1xuICAgICAgLy8gcmVtb3ZlIGxpc3RlbmVyXG4gICAgICAvLyByZW1vdmUgYmVmb3JlIHRyaWdnZXIgdG8gcHJldmVudCByZWN1cnNpb25cbiAgICAgIHRoaXMub2ZmKCBldmVudE5hbWUsIGxpc3RlbmVyICk7XG4gICAgICAvLyB1bnNldCBvbmNlIGZsYWdcbiAgICAgIGRlbGV0ZSBvbmNlTGlzdGVuZXJzWyBsaXN0ZW5lciBdO1xuICAgIH1cbiAgICAvLyB0cmlnZ2VyIGxpc3RlbmVyXG4gICAgbGlzdGVuZXIuYXBwbHkoIHRoaXMsIGFyZ3MgKTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxucHJvdG8uYWxsT2ZmID0gZnVuY3Rpb24oKSB7XG4gIGRlbGV0ZSB0aGlzLl9ldmVudHM7XG4gIGRlbGV0ZSB0aGlzLl9vbmNlRXZlbnRzO1xufTtcblxucmV0dXJuIEV2RW1pdHRlcjtcblxufSkpO1xuIiwiLyoqXG4gKiBGaXp6eSBVSSB1dGlscyB2Mi4wLjdcbiAqIE1JVCBsaWNlbnNlXG4gKi9cblxuLypqc2hpbnQgYnJvd3NlcjogdHJ1ZSwgdW5kZWY6IHRydWUsIHVudXNlZDogdHJ1ZSwgc3RyaWN0OiB0cnVlICovXG5cbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qanNoaW50IHN0cmljdDogZmFsc2UgKi8gLypnbG9iYWxzIGRlZmluZSwgbW9kdWxlLCByZXF1aXJlICovXG5cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICdkZXNhbmRyby1tYXRjaGVzLXNlbGVjdG9yL21hdGNoZXMtc2VsZWN0b3InXG4gICAgXSwgZnVuY3Rpb24oIG1hdGNoZXNTZWxlY3RvciApIHtcbiAgICAgIHJldHVybiBmYWN0b3J5KCB3aW5kb3csIG1hdGNoZXNTZWxlY3RvciApO1xuICAgIH0pO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgcmVxdWlyZSgnZGVzYW5kcm8tbWF0Y2hlcy1zZWxlY3RvcicpXG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIHdpbmRvdy5maXp6eVVJVXRpbHMgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgd2luZG93Lm1hdGNoZXNTZWxlY3RvclxuICAgICk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCB3aW5kb3csIG1hdGNoZXNTZWxlY3RvciApIHtcblxuJ3VzZSBzdHJpY3QnO1xuXG52YXIgdXRpbHMgPSB7fTtcblxuLy8gLS0tLS0gZXh0ZW5kIC0tLS0tIC8vXG5cbi8vIGV4dGVuZHMgb2JqZWN0c1xudXRpbHMuZXh0ZW5kID0gZnVuY3Rpb24oIGEsIGIgKSB7XG4gIGZvciAoIHZhciBwcm9wIGluIGIgKSB7XG4gICAgYVsgcHJvcCBdID0gYlsgcHJvcCBdO1xuICB9XG4gIHJldHVybiBhO1xufTtcblxuLy8gLS0tLS0gbW9kdWxvIC0tLS0tIC8vXG5cbnV0aWxzLm1vZHVsbyA9IGZ1bmN0aW9uKCBudW0sIGRpdiApIHtcbiAgcmV0dXJuICggKCBudW0gJSBkaXYgKSArIGRpdiApICUgZGl2O1xufTtcblxuLy8gLS0tLS0gbWFrZUFycmF5IC0tLS0tIC8vXG5cbnZhciBhcnJheVNsaWNlID0gQXJyYXkucHJvdG90eXBlLnNsaWNlO1xuXG4vLyB0dXJuIGVsZW1lbnQgb3Igbm9kZUxpc3QgaW50byBhbiBhcnJheVxudXRpbHMubWFrZUFycmF5ID0gZnVuY3Rpb24oIG9iaiApIHtcbiAgaWYgKCBBcnJheS5pc0FycmF5KCBvYmogKSApIHtcbiAgICAvLyB1c2Ugb2JqZWN0IGlmIGFscmVhZHkgYW4gYXJyYXlcbiAgICByZXR1cm4gb2JqO1xuICB9XG4gIC8vIHJldHVybiBlbXB0eSBhcnJheSBpZiB1bmRlZmluZWQgb3IgbnVsbC4gIzZcbiAgaWYgKCBvYmogPT09IG51bGwgfHwgb2JqID09PSB1bmRlZmluZWQgKSB7XG4gICAgcmV0dXJuIFtdO1xuICB9XG5cbiAgdmFyIGlzQXJyYXlMaWtlID0gdHlwZW9mIG9iaiA9PSAnb2JqZWN0JyAmJiB0eXBlb2Ygb2JqLmxlbmd0aCA9PSAnbnVtYmVyJztcbiAgaWYgKCBpc0FycmF5TGlrZSApIHtcbiAgICAvLyBjb252ZXJ0IG5vZGVMaXN0IHRvIGFycmF5XG4gICAgcmV0dXJuIGFycmF5U2xpY2UuY2FsbCggb2JqICk7XG4gIH1cblxuICAvLyBhcnJheSBvZiBzaW5nbGUgaW5kZXhcbiAgcmV0dXJuIFsgb2JqIF07XG59O1xuXG4vLyAtLS0tLSByZW1vdmVGcm9tIC0tLS0tIC8vXG5cbnV0aWxzLnJlbW92ZUZyb20gPSBmdW5jdGlvbiggYXJ5LCBvYmogKSB7XG4gIHZhciBpbmRleCA9IGFyeS5pbmRleE9mKCBvYmogKTtcbiAgaWYgKCBpbmRleCAhPSAtMSApIHtcbiAgICBhcnkuc3BsaWNlKCBpbmRleCwgMSApO1xuICB9XG59O1xuXG4vLyAtLS0tLSBnZXRQYXJlbnQgLS0tLS0gLy9cblxudXRpbHMuZ2V0UGFyZW50ID0gZnVuY3Rpb24oIGVsZW0sIHNlbGVjdG9yICkge1xuICB3aGlsZSAoIGVsZW0ucGFyZW50Tm9kZSAmJiBlbGVtICE9IGRvY3VtZW50LmJvZHkgKSB7XG4gICAgZWxlbSA9IGVsZW0ucGFyZW50Tm9kZTtcbiAgICBpZiAoIG1hdGNoZXNTZWxlY3RvciggZWxlbSwgc2VsZWN0b3IgKSApIHtcbiAgICAgIHJldHVybiBlbGVtO1xuICAgIH1cbiAgfVxufTtcblxuLy8gLS0tLS0gZ2V0UXVlcnlFbGVtZW50IC0tLS0tIC8vXG5cbi8vIHVzZSBlbGVtZW50IGFzIHNlbGVjdG9yIHN0cmluZ1xudXRpbHMuZ2V0UXVlcnlFbGVtZW50ID0gZnVuY3Rpb24oIGVsZW0gKSB7XG4gIGlmICggdHlwZW9mIGVsZW0gPT0gJ3N0cmluZycgKSB7XG4gICAgcmV0dXJuIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIGVsZW0gKTtcbiAgfVxuICByZXR1cm4gZWxlbTtcbn07XG5cbi8vIC0tLS0tIGhhbmRsZUV2ZW50IC0tLS0tIC8vXG5cbi8vIGVuYWJsZSAub250eXBlIHRvIHRyaWdnZXIgZnJvbSAuYWRkRXZlbnRMaXN0ZW5lciggZWxlbSwgJ3R5cGUnIClcbnV0aWxzLmhhbmRsZUV2ZW50ID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICB2YXIgbWV0aG9kID0gJ29uJyArIGV2ZW50LnR5cGU7XG4gIGlmICggdGhpc1sgbWV0aG9kIF0gKSB7XG4gICAgdGhpc1sgbWV0aG9kIF0oIGV2ZW50ICk7XG4gIH1cbn07XG5cbi8vIC0tLS0tIGZpbHRlckZpbmRFbGVtZW50cyAtLS0tLSAvL1xuXG51dGlscy5maWx0ZXJGaW5kRWxlbWVudHMgPSBmdW5jdGlvbiggZWxlbXMsIHNlbGVjdG9yICkge1xuICAvLyBtYWtlIGFycmF5IG9mIGVsZW1zXG4gIGVsZW1zID0gdXRpbHMubWFrZUFycmF5KCBlbGVtcyApO1xuICB2YXIgZmZFbGVtcyA9IFtdO1xuXG4gIGVsZW1zLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtICkge1xuICAgIC8vIGNoZWNrIHRoYXQgZWxlbSBpcyBhbiBhY3R1YWwgZWxlbWVudFxuICAgIGlmICggISggZWxlbSBpbnN0YW5jZW9mIEhUTUxFbGVtZW50ICkgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIGFkZCBlbGVtIGlmIG5vIHNlbGVjdG9yXG4gICAgaWYgKCAhc2VsZWN0b3IgKSB7XG4gICAgICBmZkVsZW1zLnB1c2goIGVsZW0gKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gZmlsdGVyICYgZmluZCBpdGVtcyBpZiB3ZSBoYXZlIGEgc2VsZWN0b3JcbiAgICAvLyBmaWx0ZXJcbiAgICBpZiAoIG1hdGNoZXNTZWxlY3RvciggZWxlbSwgc2VsZWN0b3IgKSApIHtcbiAgICAgIGZmRWxlbXMucHVzaCggZWxlbSApO1xuICAgIH1cbiAgICAvLyBmaW5kIGNoaWxkcmVuXG4gICAgdmFyIGNoaWxkRWxlbXMgPSBlbGVtLnF1ZXJ5U2VsZWN0b3JBbGwoIHNlbGVjdG9yICk7XG4gICAgLy8gY29uY2F0IGNoaWxkRWxlbXMgdG8gZmlsdGVyRm91bmQgYXJyYXlcbiAgICBmb3IgKCB2YXIgaT0wOyBpIDwgY2hpbGRFbGVtcy5sZW5ndGg7IGkrKyApIHtcbiAgICAgIGZmRWxlbXMucHVzaCggY2hpbGRFbGVtc1tpXSApO1xuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIGZmRWxlbXM7XG59O1xuXG4vLyAtLS0tLSBkZWJvdW5jZU1ldGhvZCAtLS0tLSAvL1xuXG51dGlscy5kZWJvdW5jZU1ldGhvZCA9IGZ1bmN0aW9uKCBfY2xhc3MsIG1ldGhvZE5hbWUsIHRocmVzaG9sZCApIHtcbiAgdGhyZXNob2xkID0gdGhyZXNob2xkIHx8IDEwMDtcbiAgLy8gb3JpZ2luYWwgbWV0aG9kXG4gIHZhciBtZXRob2QgPSBfY2xhc3MucHJvdG90eXBlWyBtZXRob2ROYW1lIF07XG4gIHZhciB0aW1lb3V0TmFtZSA9IG1ldGhvZE5hbWUgKyAnVGltZW91dCc7XG5cbiAgX2NsYXNzLnByb3RvdHlwZVsgbWV0aG9kTmFtZSBdID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIHRpbWVvdXQgPSB0aGlzWyB0aW1lb3V0TmFtZSBdO1xuICAgIGNsZWFyVGltZW91dCggdGltZW91dCApO1xuXG4gICAgdmFyIGFyZ3MgPSBhcmd1bWVudHM7XG4gICAgdmFyIF90aGlzID0gdGhpcztcbiAgICB0aGlzWyB0aW1lb3V0TmFtZSBdID0gc2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG4gICAgICBtZXRob2QuYXBwbHkoIF90aGlzLCBhcmdzICk7XG4gICAgICBkZWxldGUgX3RoaXNbIHRpbWVvdXROYW1lIF07XG4gICAgfSwgdGhyZXNob2xkICk7XG4gIH07XG59O1xuXG4vLyAtLS0tLSBkb2NSZWFkeSAtLS0tLSAvL1xuXG51dGlscy5kb2NSZWFkeSA9IGZ1bmN0aW9uKCBjYWxsYmFjayApIHtcbiAgdmFyIHJlYWR5U3RhdGUgPSBkb2N1bWVudC5yZWFkeVN0YXRlO1xuICBpZiAoIHJlYWR5U3RhdGUgPT0gJ2NvbXBsZXRlJyB8fCByZWFkeVN0YXRlID09ICdpbnRlcmFjdGl2ZScgKSB7XG4gICAgLy8gZG8gYXN5bmMgdG8gYWxsb3cgZm9yIG90aGVyIHNjcmlwdHMgdG8gcnVuLiBtZXRhZml6enkvZmxpY2tpdHkjNDQxXG4gICAgc2V0VGltZW91dCggY2FsbGJhY2sgKTtcbiAgfSBlbHNlIHtcbiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCAnRE9NQ29udGVudExvYWRlZCcsIGNhbGxiYWNrICk7XG4gIH1cbn07XG5cbi8vIC0tLS0tIGh0bWxJbml0IC0tLS0tIC8vXG5cbi8vIGh0dHA6Ly9qYW1lc3JvYmVydHMubmFtZS9ibG9nLzIwMTAvMDIvMjIvc3RyaW5nLWZ1bmN0aW9ucy1mb3ItamF2YXNjcmlwdC10cmltLXRvLWNhbWVsLWNhc2UtdG8tZGFzaGVkLWFuZC10by11bmRlcnNjb3JlL1xudXRpbHMudG9EYXNoZWQgPSBmdW5jdGlvbiggc3RyICkge1xuICByZXR1cm4gc3RyLnJlcGxhY2UoIC8oLikoW0EtWl0pL2csIGZ1bmN0aW9uKCBtYXRjaCwgJDEsICQyICkge1xuICAgIHJldHVybiAkMSArICctJyArICQyO1xuICB9KS50b0xvd2VyQ2FzZSgpO1xufTtcblxudmFyIGNvbnNvbGUgPSB3aW5kb3cuY29uc29sZTtcbi8qKlxuICogYWxsb3cgdXNlciB0byBpbml0aWFsaXplIGNsYXNzZXMgdmlhIFtkYXRhLW5hbWVzcGFjZV0gb3IgLmpzLW5hbWVzcGFjZSBjbGFzc1xuICogaHRtbEluaXQoIFdpZGdldCwgJ3dpZGdldE5hbWUnIClcbiAqIG9wdGlvbnMgYXJlIHBhcnNlZCBmcm9tIGRhdGEtbmFtZXNwYWNlLW9wdGlvbnNcbiAqL1xudXRpbHMuaHRtbEluaXQgPSBmdW5jdGlvbiggV2lkZ2V0Q2xhc3MsIG5hbWVzcGFjZSApIHtcbiAgdXRpbHMuZG9jUmVhZHkoIGZ1bmN0aW9uKCkge1xuICAgIHZhciBkYXNoZWROYW1lc3BhY2UgPSB1dGlscy50b0Rhc2hlZCggbmFtZXNwYWNlICk7XG4gICAgdmFyIGRhdGFBdHRyID0gJ2RhdGEtJyArIGRhc2hlZE5hbWVzcGFjZTtcbiAgICB2YXIgZGF0YUF0dHJFbGVtcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICdbJyArIGRhdGFBdHRyICsgJ10nICk7XG4gICAgdmFyIGpzRGFzaEVsZW1zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJy5qcy0nICsgZGFzaGVkTmFtZXNwYWNlICk7XG4gICAgdmFyIGVsZW1zID0gdXRpbHMubWFrZUFycmF5KCBkYXRhQXR0ckVsZW1zIClcbiAgICAgIC5jb25jYXQoIHV0aWxzLm1ha2VBcnJheSgganNEYXNoRWxlbXMgKSApO1xuICAgIHZhciBkYXRhT3B0aW9uc0F0dHIgPSBkYXRhQXR0ciArICctb3B0aW9ucyc7XG4gICAgdmFyIGpRdWVyeSA9IHdpbmRvdy5qUXVlcnk7XG5cbiAgICBlbGVtcy5mb3JFYWNoKCBmdW5jdGlvbiggZWxlbSApIHtcbiAgICAgIHZhciBhdHRyID0gZWxlbS5nZXRBdHRyaWJ1dGUoIGRhdGFBdHRyICkgfHxcbiAgICAgICAgZWxlbS5nZXRBdHRyaWJ1dGUoIGRhdGFPcHRpb25zQXR0ciApO1xuICAgICAgdmFyIG9wdGlvbnM7XG4gICAgICB0cnkge1xuICAgICAgICBvcHRpb25zID0gYXR0ciAmJiBKU09OLnBhcnNlKCBhdHRyICk7XG4gICAgICB9IGNhdGNoICggZXJyb3IgKSB7XG4gICAgICAgIC8vIGxvZyBlcnJvciwgZG8gbm90IGluaXRpYWxpemVcbiAgICAgICAgaWYgKCBjb25zb2xlICkge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoICdFcnJvciBwYXJzaW5nICcgKyBkYXRhQXR0ciArICcgb24gJyArIGVsZW0uY2xhc3NOYW1lICtcbiAgICAgICAgICAnOiAnICsgZXJyb3IgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBpbml0aWFsaXplXG4gICAgICB2YXIgaW5zdGFuY2UgPSBuZXcgV2lkZ2V0Q2xhc3MoIGVsZW0sIG9wdGlvbnMgKTtcbiAgICAgIC8vIG1ha2UgYXZhaWxhYmxlIHZpYSAkKCkuZGF0YSgnbmFtZXNwYWNlJylcbiAgICAgIGlmICggalF1ZXJ5ICkge1xuICAgICAgICBqUXVlcnkuZGF0YSggZWxlbSwgbmFtZXNwYWNlLCBpbnN0YW5jZSApO1xuICAgICAgfVxuICAgIH0pO1xuXG4gIH0pO1xufTtcblxuLy8gLS0tLS0gIC0tLS0tIC8vXG5cbnJldHVybiB1dGlscztcblxufSkpO1xuIiwiLy8gYWRkLCByZW1vdmUgY2VsbFxuKCBmdW5jdGlvbiggd2luZG93LCBmYWN0b3J5ICkge1xuICAvLyB1bml2ZXJzYWwgbW9kdWxlIGRlZmluaXRpb25cbiAgLyoganNoaW50IHN0cmljdDogZmFsc2UgKi9cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICcuL2ZsaWNraXR5JyxcbiAgICAgICdmaXp6eS11aS11dGlscy91dGlscydcbiAgICBdLCBmdW5jdGlvbiggRmxpY2tpdHksIHV0aWxzICkge1xuICAgICAgcmV0dXJuIGZhY3RvcnkoIHdpbmRvdywgRmxpY2tpdHksIHV0aWxzICk7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICByZXF1aXJlKCcuL2ZsaWNraXR5JyksXG4gICAgICByZXF1aXJlKCdmaXp6eS11aS11dGlscycpXG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICB3aW5kb3cuRmxpY2tpdHksXG4gICAgICB3aW5kb3cuZml6enlVSVV0aWxzXG4gICAgKTtcbiAgfVxuXG59KCB3aW5kb3csIGZ1bmN0aW9uIGZhY3RvcnkoIHdpbmRvdywgRmxpY2tpdHksIHV0aWxzICkge1xuXG4ndXNlIHN0cmljdCc7XG5cbi8vIGFwcGVuZCBjZWxscyB0byBhIGRvY3VtZW50IGZyYWdtZW50XG5mdW5jdGlvbiBnZXRDZWxsc0ZyYWdtZW50KCBjZWxscyApIHtcbiAgdmFyIGZyYWdtZW50ID0gZG9jdW1lbnQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpO1xuICBjZWxscy5mb3JFYWNoKCBmdW5jdGlvbiggY2VsbCApIHtcbiAgICBmcmFnbWVudC5hcHBlbmRDaGlsZCggY2VsbC5lbGVtZW50ICk7XG4gIH0pO1xuICByZXR1cm4gZnJhZ21lbnQ7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGFkZC9yZW1vdmUgY2VsbCBwcm90b3R5cGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudmFyIHByb3RvID0gRmxpY2tpdHkucHJvdG90eXBlO1xuXG4vKipcbiAqIEluc2VydCwgcHJlcGVuZCwgb3IgYXBwZW5kIGNlbGxzXG4gKiBAcGFyYW0ge0VsZW1lbnQsIEFycmF5LCBOb2RlTGlzdH0gZWxlbXNcbiAqIEBwYXJhbSB7SW50ZWdlcn0gaW5kZXhcbiAqL1xucHJvdG8uaW5zZXJ0ID0gZnVuY3Rpb24oIGVsZW1zLCBpbmRleCApIHtcbiAgdmFyIGNlbGxzID0gdGhpcy5fbWFrZUNlbGxzKCBlbGVtcyApO1xuICBpZiAoICFjZWxscyB8fCAhY2VsbHMubGVuZ3RoICkge1xuICAgIHJldHVybjtcbiAgfVxuICB2YXIgbGVuID0gdGhpcy5jZWxscy5sZW5ndGg7XG4gIC8vIGRlZmF1bHQgdG8gYXBwZW5kXG4gIGluZGV4ID0gaW5kZXggPT09IHVuZGVmaW5lZCA/IGxlbiA6IGluZGV4O1xuICAvLyBhZGQgY2VsbHMgd2l0aCBkb2N1bWVudCBmcmFnbWVudFxuICB2YXIgZnJhZ21lbnQgPSBnZXRDZWxsc0ZyYWdtZW50KCBjZWxscyApO1xuICAvLyBhcHBlbmQgdG8gc2xpZGVyXG4gIHZhciBpc0FwcGVuZCA9IGluZGV4ID09IGxlbjtcbiAgaWYgKCBpc0FwcGVuZCApIHtcbiAgICB0aGlzLnNsaWRlci5hcHBlbmRDaGlsZCggZnJhZ21lbnQgKTtcbiAgfSBlbHNlIHtcbiAgICB2YXIgaW5zZXJ0Q2VsbEVsZW1lbnQgPSB0aGlzLmNlbGxzWyBpbmRleCBdLmVsZW1lbnQ7XG4gICAgdGhpcy5zbGlkZXIuaW5zZXJ0QmVmb3JlKCBmcmFnbWVudCwgaW5zZXJ0Q2VsbEVsZW1lbnQgKTtcbiAgfVxuICAvLyBhZGQgdG8gdGhpcy5jZWxsc1xuICBpZiAoIGluZGV4ID09PSAwICkge1xuICAgIC8vIHByZXBlbmQsIGFkZCB0byBzdGFydFxuICAgIHRoaXMuY2VsbHMgPSBjZWxscy5jb25jYXQoIHRoaXMuY2VsbHMgKTtcbiAgfSBlbHNlIGlmICggaXNBcHBlbmQgKSB7XG4gICAgLy8gYXBwZW5kLCBhZGQgdG8gZW5kXG4gICAgdGhpcy5jZWxscyA9IHRoaXMuY2VsbHMuY29uY2F0KCBjZWxscyApO1xuICB9IGVsc2Uge1xuICAgIC8vIGluc2VydCBpbiB0aGlzLmNlbGxzXG4gICAgdmFyIGVuZENlbGxzID0gdGhpcy5jZWxscy5zcGxpY2UoIGluZGV4LCBsZW4gLSBpbmRleCApO1xuICAgIHRoaXMuY2VsbHMgPSB0aGlzLmNlbGxzLmNvbmNhdCggY2VsbHMgKS5jb25jYXQoIGVuZENlbGxzICk7XG4gIH1cblxuICB0aGlzLl9zaXplQ2VsbHMoIGNlbGxzICk7XG4gIHRoaXMuY2VsbENoYW5nZSggaW5kZXgsIHRydWUgKTtcbn07XG5cbnByb3RvLmFwcGVuZCA9IGZ1bmN0aW9uKCBlbGVtcyApIHtcbiAgdGhpcy5pbnNlcnQoIGVsZW1zLCB0aGlzLmNlbGxzLmxlbmd0aCApO1xufTtcblxucHJvdG8ucHJlcGVuZCA9IGZ1bmN0aW9uKCBlbGVtcyApIHtcbiAgdGhpcy5pbnNlcnQoIGVsZW1zLCAwICk7XG59O1xuXG4vKipcbiAqIFJlbW92ZSBjZWxsc1xuICogQHBhcmFtIHtFbGVtZW50LCBBcnJheSwgTm9kZUxpc3R9IGVsZW1zXG4gKi9cbnByb3RvLnJlbW92ZSA9IGZ1bmN0aW9uKCBlbGVtcyApIHtcbiAgdmFyIGNlbGxzID0gdGhpcy5nZXRDZWxscyggZWxlbXMgKTtcbiAgaWYgKCAhY2VsbHMgfHwgIWNlbGxzLmxlbmd0aCApIHtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgbWluQ2VsbEluZGV4ID0gdGhpcy5jZWxscy5sZW5ndGggLSAxO1xuICAvLyByZW1vdmUgY2VsbHMgZnJvbSBjb2xsZWN0aW9uICYgRE9NXG4gIGNlbGxzLmZvckVhY2goIGZ1bmN0aW9uKCBjZWxsICkge1xuICAgIGNlbGwucmVtb3ZlKCk7XG4gICAgdmFyIGluZGV4ID0gdGhpcy5jZWxscy5pbmRleE9mKCBjZWxsICk7XG4gICAgbWluQ2VsbEluZGV4ID0gTWF0aC5taW4oIGluZGV4LCBtaW5DZWxsSW5kZXggKTtcbiAgICB1dGlscy5yZW1vdmVGcm9tKCB0aGlzLmNlbGxzLCBjZWxsICk7XG4gIH0sIHRoaXMgKTtcblxuICB0aGlzLmNlbGxDaGFuZ2UoIG1pbkNlbGxJbmRleCwgdHJ1ZSApO1xufTtcblxuLyoqXG4gKiBsb2dpYyB0byBiZSBydW4gYWZ0ZXIgYSBjZWxsJ3Mgc2l6ZSBjaGFuZ2VzXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsZW0gLSBjZWxsJ3MgZWxlbWVudFxuICovXG5wcm90by5jZWxsU2l6ZUNoYW5nZSA9IGZ1bmN0aW9uKCBlbGVtICkge1xuICB2YXIgY2VsbCA9IHRoaXMuZ2V0Q2VsbCggZWxlbSApO1xuICBpZiAoICFjZWxsICkge1xuICAgIHJldHVybjtcbiAgfVxuICBjZWxsLmdldFNpemUoKTtcblxuICB2YXIgaW5kZXggPSB0aGlzLmNlbGxzLmluZGV4T2YoIGNlbGwgKTtcbiAgdGhpcy5jZWxsQ2hhbmdlKCBpbmRleCApO1xufTtcblxuLyoqXG4gKiBsb2dpYyBhbnkgdGltZSBhIGNlbGwgaXMgY2hhbmdlZDogYWRkZWQsIHJlbW92ZWQsIG9yIHNpemUgY2hhbmdlZFxuICogQHBhcmFtIHtJbnRlZ2VyfSBjaGFuZ2VkQ2VsbEluZGV4IC0gaW5kZXggb2YgdGhlIGNoYW5nZWQgY2VsbCwgb3B0aW9uYWxcbiAqL1xucHJvdG8uY2VsbENoYW5nZSA9IGZ1bmN0aW9uKCBjaGFuZ2VkQ2VsbEluZGV4LCBpc1Bvc2l0aW9uaW5nU2xpZGVyICkge1xuICB2YXIgcHJldlNlbGVjdGVkRWxlbSA9IHRoaXMuc2VsZWN0ZWRFbGVtZW50O1xuICB0aGlzLl9wb3NpdGlvbkNlbGxzKCBjaGFuZ2VkQ2VsbEluZGV4ICk7XG4gIHRoaXMuX2dldFdyYXBTaGlmdENlbGxzKCk7XG4gIHRoaXMuc2V0R2FsbGVyeVNpemUoKTtcbiAgLy8gdXBkYXRlIHNlbGVjdGVkSW5kZXhcbiAgLy8gdHJ5IHRvIG1haW50YWluIHBvc2l0aW9uICYgc2VsZWN0IHByZXZpb3VzIHNlbGVjdGVkIGVsZW1lbnRcbiAgdmFyIGNlbGwgPSB0aGlzLmdldENlbGwoIHByZXZTZWxlY3RlZEVsZW0gKTtcbiAgaWYgKCBjZWxsICkge1xuICAgIHRoaXMuc2VsZWN0ZWRJbmRleCA9IHRoaXMuZ2V0Q2VsbFNsaWRlSW5kZXgoIGNlbGwgKTtcbiAgfVxuICB0aGlzLnNlbGVjdGVkSW5kZXggPSBNYXRoLm1pbiggdGhpcy5zbGlkZXMubGVuZ3RoIC0gMSwgdGhpcy5zZWxlY3RlZEluZGV4ICk7XG5cbiAgdGhpcy5lbWl0RXZlbnQoICdjZWxsQ2hhbmdlJywgWyBjaGFuZ2VkQ2VsbEluZGV4IF0gKTtcbiAgLy8gcG9zaXRpb24gc2xpZGVyXG4gIHRoaXMuc2VsZWN0KCB0aGlzLnNlbGVjdGVkSW5kZXggKTtcbiAgLy8gZG8gbm90IHBvc2l0aW9uIHNsaWRlciBhZnRlciBsYXp5IGxvYWRcbiAgaWYgKCBpc1Bvc2l0aW9uaW5nU2xpZGVyICkge1xuICAgIHRoaXMucG9zaXRpb25TbGlkZXJBdFNlbGVjdGVkKCk7XG4gIH1cbn07XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG5yZXR1cm4gRmxpY2tpdHk7XG5cbn0pKTtcbiIsIi8vIGFuaW1hdGVcbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnZml6enktdWktdXRpbHMvdXRpbHMnXG4gICAgXSwgZnVuY3Rpb24oIHV0aWxzICkge1xuICAgICAgcmV0dXJuIGZhY3RvcnkoIHdpbmRvdywgdXRpbHMgKTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmICggdHlwZW9mIG1vZHVsZSA9PSAnb2JqZWN0JyAmJiBtb2R1bGUuZXhwb3J0cyApIHtcbiAgICAvLyBDb21tb25KU1xuICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeShcbiAgICAgIHdpbmRvdyxcbiAgICAgIHJlcXVpcmUoJ2Zpenp5LXVpLXV0aWxzJylcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIC8vIGJyb3dzZXIgZ2xvYmFsXG4gICAgd2luZG93LkZsaWNraXR5ID0gd2luZG93LkZsaWNraXR5IHx8IHt9O1xuICAgIHdpbmRvdy5GbGlja2l0eS5hbmltYXRlUHJvdG90eXBlID0gZmFjdG9yeShcbiAgICAgIHdpbmRvdyxcbiAgICAgIHdpbmRvdy5maXp6eVVJVXRpbHNcbiAgICApO1xuICB9XG5cbn0oIHdpbmRvdywgZnVuY3Rpb24gZmFjdG9yeSggd2luZG93LCB1dGlscyApIHtcblxuJ3VzZSBzdHJpY3QnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBhbmltYXRlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbnZhciBwcm90byA9IHt9O1xuXG5wcm90by5zdGFydEFuaW1hdGlvbiA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIHRoaXMuaXNBbmltYXRpbmcgKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdGhpcy5pc0FuaW1hdGluZyA9IHRydWU7XG4gIHRoaXMucmVzdGluZ0ZyYW1lcyA9IDA7XG4gIHRoaXMuYW5pbWF0ZSgpO1xufTtcblxucHJvdG8uYW5pbWF0ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmFwcGx5RHJhZ0ZvcmNlKCk7XG4gIHRoaXMuYXBwbHlTZWxlY3RlZEF0dHJhY3Rpb24oKTtcblxuICB2YXIgcHJldmlvdXNYID0gdGhpcy54O1xuXG4gIHRoaXMuaW50ZWdyYXRlUGh5c2ljcygpO1xuICB0aGlzLnBvc2l0aW9uU2xpZGVyKCk7XG4gIHRoaXMuc2V0dGxlKCBwcmV2aW91c1ggKTtcbiAgLy8gYW5pbWF0ZSBuZXh0IGZyYW1lXG4gIGlmICggdGhpcy5pc0FuaW1hdGluZyApIHtcbiAgICB2YXIgX3RoaXMgPSB0aGlzO1xuICAgIHJlcXVlc3RBbmltYXRpb25GcmFtZSggZnVuY3Rpb24gYW5pbWF0ZUZyYW1lKCkge1xuICAgICAgX3RoaXMuYW5pbWF0ZSgpO1xuICAgIH0pO1xuICB9XG59O1xuXG5wcm90by5wb3NpdGlvblNsaWRlciA9IGZ1bmN0aW9uKCkge1xuICB2YXIgeCA9IHRoaXMueDtcbiAgLy8gd3JhcCBwb3NpdGlvbiBhcm91bmRcbiAgaWYgKCB0aGlzLm9wdGlvbnMud3JhcEFyb3VuZCAmJiB0aGlzLmNlbGxzLmxlbmd0aCA+IDEgKSB7XG4gICAgeCA9IHV0aWxzLm1vZHVsbyggeCwgdGhpcy5zbGlkZWFibGVXaWR0aCApO1xuICAgIHggPSB4IC0gdGhpcy5zbGlkZWFibGVXaWR0aDtcbiAgICB0aGlzLnNoaWZ0V3JhcENlbGxzKCB4ICk7XG4gIH1cblxuICB0aGlzLnNldFRyYW5zbGF0ZVgoIHgsIHRoaXMuaXNBbmltYXRpbmcgKTtcbiAgdGhpcy5kaXNwYXRjaFNjcm9sbEV2ZW50KCk7XG59O1xuXG5wcm90by5zZXRUcmFuc2xhdGVYID0gZnVuY3Rpb24oIHgsIGlzM2QgKSB7XG4gIHggKz0gdGhpcy5jdXJzb3JQb3NpdGlvbjtcbiAgLy8gcmV2ZXJzZSBpZiByaWdodC10by1sZWZ0IGFuZCB1c2luZyB0cmFuc2Zvcm1cbiAgeCA9IHRoaXMub3B0aW9ucy5yaWdodFRvTGVmdCA/IC14IDogeDtcbiAgdmFyIHRyYW5zbGF0ZVggPSB0aGlzLmdldFBvc2l0aW9uVmFsdWUoIHggKTtcbiAgLy8gdXNlIDNEIHRyYW5mb3JtcyBmb3IgaGFyZHdhcmUgYWNjZWxlcmF0aW9uIG9uIGlPU1xuICAvLyBidXQgdXNlIDJEIHdoZW4gc2V0dGxlZCwgZm9yIGJldHRlciBmb250LXJlbmRlcmluZ1xuICB0aGlzLnNsaWRlci5zdHlsZS50cmFuc2Zvcm0gPSBpczNkID9cbiAgICAndHJhbnNsYXRlM2QoJyArIHRyYW5zbGF0ZVggKyAnLDAsMCknIDogJ3RyYW5zbGF0ZVgoJyArIHRyYW5zbGF0ZVggKyAnKSc7XG59O1xuXG5wcm90by5kaXNwYXRjaFNjcm9sbEV2ZW50ID0gZnVuY3Rpb24oKSB7XG4gIHZhciBmaXJzdFNsaWRlID0gdGhpcy5zbGlkZXNbMF07XG4gIGlmICggIWZpcnN0U2xpZGUgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHZhciBwb3NpdGlvblggPSAtdGhpcy54IC0gZmlyc3RTbGlkZS50YXJnZXQ7XG4gIHZhciBwcm9ncmVzcyA9IHBvc2l0aW9uWCAvIHRoaXMuc2xpZGVzV2lkdGg7XG4gIHRoaXMuZGlzcGF0Y2hFdmVudCggJ3Njcm9sbCcsIG51bGwsIFsgcHJvZ3Jlc3MsIHBvc2l0aW9uWCBdICk7XG59O1xuXG5wcm90by5wb3NpdGlvblNsaWRlckF0U2VsZWN0ZWQgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCAhdGhpcy5jZWxscy5sZW5ndGggKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMueCA9IC10aGlzLnNlbGVjdGVkU2xpZGUudGFyZ2V0O1xuICB0aGlzLnZlbG9jaXR5ID0gMDsgLy8gc3RvcCB3b2JibGVcbiAgdGhpcy5wb3NpdGlvblNsaWRlcigpO1xufTtcblxucHJvdG8uZ2V0UG9zaXRpb25WYWx1ZSA9IGZ1bmN0aW9uKCBwb3NpdGlvbiApIHtcbiAgaWYgKCB0aGlzLm9wdGlvbnMucGVyY2VudFBvc2l0aW9uICkge1xuICAgIC8vIHBlcmNlbnQgcG9zaXRpb24sIHJvdW5kIHRvIDIgZGlnaXRzLCBsaWtlIDEyLjM0JVxuICAgIHJldHVybiAoIE1hdGgucm91bmQoICggcG9zaXRpb24gLyB0aGlzLnNpemUuaW5uZXJXaWR0aCApICogMTAwMDAgKSAqIDAuMDEgKSsgJyUnO1xuICB9IGVsc2Uge1xuICAgIC8vIHBpeGVsIHBvc2l0aW9uaW5nXG4gICAgcmV0dXJuIE1hdGgucm91bmQoIHBvc2l0aW9uICkgKyAncHgnO1xuICB9XG59O1xuXG5wcm90by5zZXR0bGUgPSBmdW5jdGlvbiggcHJldmlvdXNYICkge1xuICAvLyBrZWVwIHRyYWNrIG9mIGZyYW1lcyB3aGVyZSB4IGhhc24ndCBtb3ZlZFxuICBpZiAoICF0aGlzLmlzUG9pbnRlckRvd24gJiYgTWF0aC5yb3VuZCggdGhpcy54ICogMTAwICkgPT0gTWF0aC5yb3VuZCggcHJldmlvdXNYICogMTAwICkgKSB7XG4gICAgdGhpcy5yZXN0aW5nRnJhbWVzKys7XG4gIH1cbiAgLy8gc3RvcCBhbmltYXRpbmcgaWYgcmVzdGluZyBmb3IgMyBvciBtb3JlIGZyYW1lc1xuICBpZiAoIHRoaXMucmVzdGluZ0ZyYW1lcyA+IDIgKSB7XG4gICAgdGhpcy5pc0FuaW1hdGluZyA9IGZhbHNlO1xuICAgIGRlbGV0ZSB0aGlzLmlzRnJlZVNjcm9sbGluZztcbiAgICAvLyByZW5kZXIgcG9zaXRpb24gd2l0aCB0cmFuc2xhdGVYIHdoZW4gc2V0dGxlZFxuICAgIHRoaXMucG9zaXRpb25TbGlkZXIoKTtcbiAgICB0aGlzLmRpc3BhdGNoRXZlbnQoICdzZXR0bGUnLCBudWxsLCBbIHRoaXMuc2VsZWN0ZWRJbmRleCBdICk7XG4gIH1cbn07XG5cbnByb3RvLnNoaWZ0V3JhcENlbGxzID0gZnVuY3Rpb24oIHggKSB7XG4gIC8vIHNoaWZ0IGJlZm9yZSBjZWxsc1xuICB2YXIgYmVmb3JlR2FwID0gdGhpcy5jdXJzb3JQb3NpdGlvbiArIHg7XG4gIHRoaXMuX3NoaWZ0Q2VsbHMoIHRoaXMuYmVmb3JlU2hpZnRDZWxscywgYmVmb3JlR2FwLCAtMSApO1xuICAvLyBzaGlmdCBhZnRlciBjZWxsc1xuICB2YXIgYWZ0ZXJHYXAgPSB0aGlzLnNpemUuaW5uZXJXaWR0aCAtICggeCArIHRoaXMuc2xpZGVhYmxlV2lkdGggKyB0aGlzLmN1cnNvclBvc2l0aW9uICk7XG4gIHRoaXMuX3NoaWZ0Q2VsbHMoIHRoaXMuYWZ0ZXJTaGlmdENlbGxzLCBhZnRlckdhcCwgMSApO1xufTtcblxucHJvdG8uX3NoaWZ0Q2VsbHMgPSBmdW5jdGlvbiggY2VsbHMsIGdhcCwgc2hpZnQgKSB7XG4gIGZvciAoIHZhciBpPTA7IGkgPCBjZWxscy5sZW5ndGg7IGkrKyApIHtcbiAgICB2YXIgY2VsbCA9IGNlbGxzW2ldO1xuICAgIHZhciBjZWxsU2hpZnQgPSBnYXAgPiAwID8gc2hpZnQgOiAwO1xuICAgIGNlbGwud3JhcFNoaWZ0KCBjZWxsU2hpZnQgKTtcbiAgICBnYXAgLT0gY2VsbC5zaXplLm91dGVyV2lkdGg7XG4gIH1cbn07XG5cbnByb3RvLl91bnNoaWZ0Q2VsbHMgPSBmdW5jdGlvbiggY2VsbHMgKSB7XG4gIGlmICggIWNlbGxzIHx8ICFjZWxscy5sZW5ndGggKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGZvciAoIHZhciBpPTA7IGkgPCBjZWxscy5sZW5ndGg7IGkrKyApIHtcbiAgICBjZWxsc1tpXS53cmFwU2hpZnQoIDAgKTtcbiAgfVxufTtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gcGh5c2ljcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5wcm90by5pbnRlZ3JhdGVQaHlzaWNzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMueCArPSB0aGlzLnZlbG9jaXR5O1xuICB0aGlzLnZlbG9jaXR5ICo9IHRoaXMuZ2V0RnJpY3Rpb25GYWN0b3IoKTtcbn07XG5cbnByb3RvLmFwcGx5Rm9yY2UgPSBmdW5jdGlvbiggZm9yY2UgKSB7XG4gIHRoaXMudmVsb2NpdHkgKz0gZm9yY2U7XG59O1xuXG5wcm90by5nZXRGcmljdGlvbkZhY3RvciA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gMSAtIHRoaXMub3B0aW9uc1sgdGhpcy5pc0ZyZWVTY3JvbGxpbmcgPyAnZnJlZVNjcm9sbEZyaWN0aW9uJyA6ICdmcmljdGlvbicgXTtcbn07XG5cbnByb3RvLmdldFJlc3RpbmdQb3NpdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAvLyBteSB0aGFua3MgdG8gU3RldmVuIFdpdHRlbnMsIHdobyBzaW1wbGlmaWVkIHRoaXMgbWF0aCBncmVhdGx5XG4gIHJldHVybiB0aGlzLnggKyB0aGlzLnZlbG9jaXR5IC8gKCAxIC0gdGhpcy5nZXRGcmljdGlvbkZhY3RvcigpICk7XG59O1xuXG5wcm90by5hcHBseURyYWdGb3JjZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoICF0aGlzLmlzRHJhZ2dhYmxlIHx8ICF0aGlzLmlzUG9pbnRlckRvd24gKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIGNoYW5nZSB0aGUgcG9zaXRpb24gdG8gZHJhZyBwb3NpdGlvbiBieSBhcHBseWluZyBmb3JjZVxuICB2YXIgZHJhZ1ZlbG9jaXR5ID0gdGhpcy5kcmFnWCAtIHRoaXMueDtcbiAgdmFyIGRyYWdGb3JjZSA9IGRyYWdWZWxvY2l0eSAtIHRoaXMudmVsb2NpdHk7XG4gIHRoaXMuYXBwbHlGb3JjZSggZHJhZ0ZvcmNlICk7XG59O1xuXG5wcm90by5hcHBseVNlbGVjdGVkQXR0cmFjdGlvbiA9IGZ1bmN0aW9uKCkge1xuICAvLyBkbyBub3QgYXR0cmFjdCBpZiBwb2ludGVyIGRvd24gb3Igbm8gc2xpZGVzXG4gIHZhciBkcmFnRG93biA9IHRoaXMuaXNEcmFnZ2FibGUgJiYgdGhpcy5pc1BvaW50ZXJEb3duO1xuICBpZiAoIGRyYWdEb3duIHx8IHRoaXMuaXNGcmVlU2Nyb2xsaW5nIHx8ICF0aGlzLnNsaWRlcy5sZW5ndGggKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHZhciBkaXN0YW5jZSA9IHRoaXMuc2VsZWN0ZWRTbGlkZS50YXJnZXQgKiAtMSAtIHRoaXMueDtcbiAgdmFyIGZvcmNlID0gZGlzdGFuY2UgKiB0aGlzLm9wdGlvbnMuc2VsZWN0ZWRBdHRyYWN0aW9uO1xuICB0aGlzLmFwcGx5Rm9yY2UoIGZvcmNlICk7XG59O1xuXG5yZXR1cm4gcHJvdG87XG5cbn0pKTtcbiIsIi8vIEZsaWNraXR5LkNlbGxcbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnZ2V0LXNpemUvZ2V0LXNpemUnXG4gICAgXSwgZnVuY3Rpb24oIGdldFNpemUgKSB7XG4gICAgICByZXR1cm4gZmFjdG9yeSggd2luZG93LCBnZXRTaXplICk7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICByZXF1aXJlKCdnZXQtc2l6ZScpXG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIHdpbmRvdy5GbGlja2l0eSA9IHdpbmRvdy5GbGlja2l0eSB8fCB7fTtcbiAgICB3aW5kb3cuRmxpY2tpdHkuQ2VsbCA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICB3aW5kb3cuZ2V0U2l6ZVxuICAgICk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCB3aW5kb3csIGdldFNpemUgKSB7XG5cbid1c2Ugc3RyaWN0JztcblxuZnVuY3Rpb24gQ2VsbCggZWxlbSwgcGFyZW50ICkge1xuICB0aGlzLmVsZW1lbnQgPSBlbGVtO1xuICB0aGlzLnBhcmVudCA9IHBhcmVudDtcblxuICB0aGlzLmNyZWF0ZSgpO1xufVxuXG52YXIgcHJvdG8gPSBDZWxsLnByb3RvdHlwZTtcblxucHJvdG8uY3JlYXRlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuZWxlbWVudC5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XG4gIHRoaXMuZWxlbWVudC5zZXRBdHRyaWJ1dGUoICdhcmlhLWhpZGRlbicsICd0cnVlJyApO1xuICB0aGlzLnggPSAwO1xuICB0aGlzLnNoaWZ0ID0gMDtcbn07XG5cbnByb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgLy8gcmVzZXQgc3R5bGVcbiAgdGhpcy51bnNlbGVjdCgpO1xuICB0aGlzLmVsZW1lbnQuc3R5bGUucG9zaXRpb24gPSAnJztcbiAgdmFyIHNpZGUgPSB0aGlzLnBhcmVudC5vcmlnaW5TaWRlO1xuICB0aGlzLmVsZW1lbnQuc3R5bGVbIHNpZGUgXSA9ICcnO1xufTtcblxucHJvdG8uZ2V0U2l6ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnNpemUgPSBnZXRTaXplKCB0aGlzLmVsZW1lbnQgKTtcbn07XG5cbnByb3RvLnNldFBvc2l0aW9uID0gZnVuY3Rpb24oIHggKSB7XG4gIHRoaXMueCA9IHg7XG4gIHRoaXMudXBkYXRlVGFyZ2V0KCk7XG4gIHRoaXMucmVuZGVyUG9zaXRpb24oIHggKTtcbn07XG5cbi8vIHNldERlZmF1bHRUYXJnZXQgdjEgbWV0aG9kLCBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSwgcmVtb3ZlIGluIHYzXG5wcm90by51cGRhdGVUYXJnZXQgPSBwcm90by5zZXREZWZhdWx0VGFyZ2V0ID0gZnVuY3Rpb24oKSB7XG4gIHZhciBtYXJnaW5Qcm9wZXJ0eSA9IHRoaXMucGFyZW50Lm9yaWdpblNpZGUgPT0gJ2xlZnQnID8gJ21hcmdpbkxlZnQnIDogJ21hcmdpblJpZ2h0JztcbiAgdGhpcy50YXJnZXQgPSB0aGlzLnggKyB0aGlzLnNpemVbIG1hcmdpblByb3BlcnR5IF0gK1xuICAgIHRoaXMuc2l6ZS53aWR0aCAqIHRoaXMucGFyZW50LmNlbGxBbGlnbjtcbn07XG5cbnByb3RvLnJlbmRlclBvc2l0aW9uID0gZnVuY3Rpb24oIHggKSB7XG4gIC8vIHJlbmRlciBwb3NpdGlvbiBvZiBjZWxsIHdpdGggaW4gc2xpZGVyXG4gIHZhciBzaWRlID0gdGhpcy5wYXJlbnQub3JpZ2luU2lkZTtcbiAgdGhpcy5lbGVtZW50LnN0eWxlWyBzaWRlIF0gPSB0aGlzLnBhcmVudC5nZXRQb3NpdGlvblZhbHVlKCB4ICk7XG59O1xuXG5wcm90by5zZWxlY3QgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5lbGVtZW50LmNsYXNzTGlzdC5hZGQoJ2lzLXNlbGVjdGVkJyk7XG4gIHRoaXMuZWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoJ2FyaWEtaGlkZGVuJyk7XG59O1xuXG5wcm90by51bnNlbGVjdCA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnaXMtc2VsZWN0ZWQnKTtcbiAgdGhpcy5lbGVtZW50LnNldEF0dHJpYnV0ZSggJ2FyaWEtaGlkZGVuJywgJ3RydWUnICk7XG59O1xuXG4vKipcbiAqIEBwYXJhbSB7SW50ZWdlcn0gZmFjdG9yIC0gMCwgMSwgb3IgLTFcbioqL1xucHJvdG8ud3JhcFNoaWZ0ID0gZnVuY3Rpb24oIHNoaWZ0ICkge1xuICB0aGlzLnNoaWZ0ID0gc2hpZnQ7XG4gIHRoaXMucmVuZGVyUG9zaXRpb24oIHRoaXMueCArIHRoaXMucGFyZW50LnNsaWRlYWJsZVdpZHRoICogc2hpZnQgKTtcbn07XG5cbnByb3RvLnJlbW92ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmVsZW1lbnQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggdGhpcy5lbGVtZW50ICk7XG59O1xuXG5yZXR1cm4gQ2VsbDtcblxufSkpO1xuIiwiLy8gZHJhZ1xuKCBmdW5jdGlvbiggd2luZG93LCBmYWN0b3J5ICkge1xuICAvLyB1bml2ZXJzYWwgbW9kdWxlIGRlZmluaXRpb25cbiAgLyoganNoaW50IHN0cmljdDogZmFsc2UgKi9cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICcuL2ZsaWNraXR5JyxcbiAgICAgICd1bmlkcmFnZ2VyL3VuaWRyYWdnZXInLFxuICAgICAgJ2Zpenp5LXVpLXV0aWxzL3V0aWxzJ1xuICAgIF0sIGZ1bmN0aW9uKCBGbGlja2l0eSwgVW5pZHJhZ2dlciwgdXRpbHMgKSB7XG4gICAgICByZXR1cm4gZmFjdG9yeSggd2luZG93LCBGbGlja2l0eSwgVW5pZHJhZ2dlciwgdXRpbHMgKTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmICggdHlwZW9mIG1vZHVsZSA9PSAnb2JqZWN0JyAmJiBtb2R1bGUuZXhwb3J0cyApIHtcbiAgICAvLyBDb21tb25KU1xuICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeShcbiAgICAgIHdpbmRvdyxcbiAgICAgIHJlcXVpcmUoJy4vZmxpY2tpdHknKSxcbiAgICAgIHJlcXVpcmUoJ3VuaWRyYWdnZXInKSxcbiAgICAgIHJlcXVpcmUoJ2Zpenp5LXVpLXV0aWxzJylcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIC8vIGJyb3dzZXIgZ2xvYmFsXG4gICAgd2luZG93LkZsaWNraXR5ID0gZmFjdG9yeShcbiAgICAgIHdpbmRvdyxcbiAgICAgIHdpbmRvdy5GbGlja2l0eSxcbiAgICAgIHdpbmRvdy5VbmlkcmFnZ2VyLFxuICAgICAgd2luZG93LmZpenp5VUlVdGlsc1xuICAgICk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCB3aW5kb3csIEZsaWNraXR5LCBVbmlkcmFnZ2VyLCB1dGlscyApIHtcblxuJ3VzZSBzdHJpY3QnO1xuXG4vLyAtLS0tLSBkZWZhdWx0cyAtLS0tLSAvL1xuXG51dGlscy5leHRlbmQoIEZsaWNraXR5LmRlZmF1bHRzLCB7XG4gIGRyYWdnYWJsZTogJz4xJyxcbiAgZHJhZ1RocmVzaG9sZDogMyxcbn0pO1xuXG4vLyAtLS0tLSBjcmVhdGUgLS0tLS0gLy9cblxuRmxpY2tpdHkuY3JlYXRlTWV0aG9kcy5wdXNoKCdfY3JlYXRlRHJhZycpO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBkcmFnIHByb3RvdHlwZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG52YXIgcHJvdG8gPSBGbGlja2l0eS5wcm90b3R5cGU7XG51dGlscy5leHRlbmQoIHByb3RvLCBVbmlkcmFnZ2VyLnByb3RvdHlwZSApO1xucHJvdG8uX3RvdWNoQWN0aW9uVmFsdWUgPSAncGFuLXknO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudmFyIGlzVG91Y2ggPSAnY3JlYXRlVG91Y2gnIGluIGRvY3VtZW50O1xudmFyIGlzVG91Y2htb3ZlU2Nyb2xsQ2FuY2VsZWQgPSBmYWxzZTtcblxucHJvdG8uX2NyZWF0ZURyYWcgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5vbiggJ2FjdGl2YXRlJywgdGhpcy5vbkFjdGl2YXRlRHJhZyApO1xuICB0aGlzLm9uKCAndWlDaGFuZ2UnLCB0aGlzLl91aUNoYW5nZURyYWcgKTtcbiAgdGhpcy5vbiggJ2RlYWN0aXZhdGUnLCB0aGlzLm9uRGVhY3RpdmF0ZURyYWcgKTtcbiAgdGhpcy5vbiggJ2NlbGxDaGFuZ2UnLCB0aGlzLnVwZGF0ZURyYWdnYWJsZSApO1xuICAvLyBUT0RPIHVwZGF0ZURyYWdnYWJsZSBvbiByZXNpemU/IGlmIGdyb3VwQ2VsbHMgJiBzbGlkZXMgY2hhbmdlXG4gIC8vIEhBQ0sgLSBhZGQgc2VlbWluZ2x5IGlubm9jdW91cyBoYW5kbGVyIHRvIGZpeCBpT1MgMTAgc2Nyb2xsIGJlaGF2aW9yXG4gIC8vICM0NTcsIFJ1YmFYYS9Tb3J0YWJsZSM5NzNcbiAgaWYgKCBpc1RvdWNoICYmICFpc1RvdWNobW92ZVNjcm9sbENhbmNlbGVkICkge1xuICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCAndG91Y2htb3ZlJywgZnVuY3Rpb24oKSB7fSk7XG4gICAgaXNUb3VjaG1vdmVTY3JvbGxDYW5jZWxlZCA9IHRydWU7XG4gIH1cbn07XG5cbnByb3RvLm9uQWN0aXZhdGVEcmFnID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuaGFuZGxlcyA9IFsgdGhpcy52aWV3cG9ydCBdO1xuICB0aGlzLmJpbmRIYW5kbGVzKCk7XG4gIHRoaXMudXBkYXRlRHJhZ2dhYmxlKCk7XG59O1xuXG5wcm90by5vbkRlYWN0aXZhdGVEcmFnID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMudW5iaW5kSGFuZGxlcygpO1xuICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnaXMtZHJhZ2dhYmxlJyk7XG59O1xuXG5wcm90by51cGRhdGVEcmFnZ2FibGUgPSBmdW5jdGlvbigpIHtcbiAgLy8gZGlzYWJsZSBkcmFnZ2luZyBpZiBsZXNzIHRoYW4gMiBzbGlkZXMuICMyNzhcbiAgaWYgKCB0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlID09ICc+MScgKSB7XG4gICAgdGhpcy5pc0RyYWdnYWJsZSA9IHRoaXMuc2xpZGVzLmxlbmd0aCA+IDE7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5pc0RyYWdnYWJsZSA9IHRoaXMub3B0aW9ucy5kcmFnZ2FibGU7XG4gIH1cbiAgaWYgKCB0aGlzLmlzRHJhZ2dhYmxlICkge1xuICAgIHRoaXMuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCdpcy1kcmFnZ2FibGUnKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgnaXMtZHJhZ2dhYmxlJyk7XG4gIH1cbn07XG5cbi8vIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XG5wcm90by5iaW5kRHJhZyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLm9wdGlvbnMuZHJhZ2dhYmxlID0gdHJ1ZTtcbiAgdGhpcy51cGRhdGVEcmFnZ2FibGUoKTtcbn07XG5cbnByb3RvLnVuYmluZERyYWcgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5vcHRpb25zLmRyYWdnYWJsZSA9IGZhbHNlO1xuICB0aGlzLnVwZGF0ZURyYWdnYWJsZSgpO1xufTtcblxucHJvdG8uX3VpQ2hhbmdlRHJhZyA9IGZ1bmN0aW9uKCkge1xuICBkZWxldGUgdGhpcy5pc0ZyZWVTY3JvbGxpbmc7XG59O1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBwb2ludGVyIGV2ZW50cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5wcm90by5wb2ludGVyRG93biA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgaWYgKCAhdGhpcy5pc0RyYWdnYWJsZSApIHtcbiAgICB0aGlzLl9wb2ludGVyRG93bkRlZmF1bHQoIGV2ZW50LCBwb2ludGVyICk7XG4gICAgcmV0dXJuO1xuICB9XG4gIHZhciBpc09rYXkgPSB0aGlzLm9rYXlQb2ludGVyRG93biggZXZlbnQgKTtcbiAgaWYgKCAhaXNPa2F5ICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMuX3BvaW50ZXJEb3duUHJldmVudERlZmF1bHQoIGV2ZW50ICk7XG4gIHRoaXMucG9pbnRlckRvd25Gb2N1cyggZXZlbnQgKTtcbiAgLy8gYmx1clxuICBpZiAoIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgIT0gdGhpcy5lbGVtZW50ICkge1xuICAgIC8vIGRvIG5vdCBibHVyIGlmIGFscmVhZHkgZm9jdXNlZFxuICAgIHRoaXMucG9pbnRlckRvd25CbHVyKCk7XG4gIH1cblxuICAvLyBzdG9wIGlmIGl0IHdhcyBtb3ZpbmdcbiAgdGhpcy5kcmFnWCA9IHRoaXMueDtcbiAgdGhpcy52aWV3cG9ydC5jbGFzc0xpc3QuYWRkKCdpcy1wb2ludGVyLWRvd24nKTtcbiAgLy8gdHJhY2sgc2Nyb2xsaW5nXG4gIHRoaXMucG9pbnRlckRvd25TY3JvbGwgPSBnZXRTY3JvbGxQb3NpdGlvbigpO1xuICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lciggJ3Njcm9sbCcsIHRoaXMgKTtcblxuICB0aGlzLl9wb2ludGVyRG93bkRlZmF1bHQoIGV2ZW50LCBwb2ludGVyICk7XG59O1xuXG4vLyBkZWZhdWx0IHBvaW50ZXJEb3duIGxvZ2ljLCB1c2VkIGZvciBzdGF0aWNDbGlja1xucHJvdG8uX3BvaW50ZXJEb3duRGVmYXVsdCA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgLy8gdHJhY2sgc3RhcnQgZXZlbnQgcG9zaXRpb25cbiAgLy8gU2FmYXJpIDkgb3ZlcnJpZGVzIHBhZ2VYIGFuZCBwYWdlWS4gVGhlc2UgdmFsdWVzIG5lZWRzIHRvIGJlIGNvcGllZC4gIzc3OVxuICB0aGlzLnBvaW50ZXJEb3duUG9pbnRlciA9IHtcbiAgICBwYWdlWDogcG9pbnRlci5wYWdlWCxcbiAgICBwYWdlWTogcG9pbnRlci5wYWdlWSxcbiAgfTtcbiAgLy8gYmluZCBtb3ZlIGFuZCBlbmQgZXZlbnRzXG4gIHRoaXMuX2JpbmRQb3N0U3RhcnRFdmVudHMoIGV2ZW50ICk7XG4gIHRoaXMuZGlzcGF0Y2hFdmVudCggJ3BvaW50ZXJEb3duJywgZXZlbnQsIFsgcG9pbnRlciBdICk7XG59O1xuXG52YXIgZm9jdXNOb2RlcyA9IHtcbiAgSU5QVVQ6IHRydWUsXG4gIFRFWFRBUkVBOiB0cnVlLFxuICBTRUxFQ1Q6IHRydWUsXG59O1xuXG5wcm90by5wb2ludGVyRG93bkZvY3VzID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICB2YXIgaXNGb2N1c05vZGUgPSBmb2N1c05vZGVzWyBldmVudC50YXJnZXQubm9kZU5hbWUgXTtcbiAgaWYgKCAhaXNGb2N1c05vZGUgKSB7XG4gICAgdGhpcy5mb2N1cygpO1xuICB9XG59O1xuXG5wcm90by5fcG9pbnRlckRvd25QcmV2ZW50RGVmYXVsdCA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgdmFyIGlzVG91Y2hTdGFydCA9IGV2ZW50LnR5cGUgPT0gJ3RvdWNoc3RhcnQnO1xuICB2YXIgaXNUb3VjaFBvaW50ZXIgPSBldmVudC5wb2ludGVyVHlwZSA9PSAndG91Y2gnO1xuICB2YXIgaXNGb2N1c05vZGUgPSBmb2N1c05vZGVzWyBldmVudC50YXJnZXQubm9kZU5hbWUgXTtcbiAgaWYgKCAhaXNUb3VjaFN0YXJ0ICYmICFpc1RvdWNoUG9pbnRlciAmJiAhaXNGb2N1c05vZGUgKSB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgfVxufTtcblxuLy8gLS0tLS0gbW92ZSAtLS0tLSAvL1xuXG5wcm90by5oYXNEcmFnU3RhcnRlZCA9IGZ1bmN0aW9uKCBtb3ZlVmVjdG9yICkge1xuICByZXR1cm4gTWF0aC5hYnMoIG1vdmVWZWN0b3IueCApID4gdGhpcy5vcHRpb25zLmRyYWdUaHJlc2hvbGQ7XG59O1xuXG4vLyAtLS0tLSB1cCAtLS0tLSAvL1xuXG5wcm90by5wb2ludGVyVXAgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIGRlbGV0ZSB0aGlzLmlzVG91Y2hTY3JvbGxpbmc7XG4gIHRoaXMudmlld3BvcnQuY2xhc3NMaXN0LnJlbW92ZSgnaXMtcG9pbnRlci1kb3duJyk7XG4gIHRoaXMuZGlzcGF0Y2hFdmVudCggJ3BvaW50ZXJVcCcsIGV2ZW50LCBbIHBvaW50ZXIgXSApO1xuICB0aGlzLl9kcmFnUG9pbnRlclVwKCBldmVudCwgcG9pbnRlciApO1xufTtcblxucHJvdG8ucG9pbnRlckRvbmUgPSBmdW5jdGlvbigpIHtcbiAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdzY3JvbGwnLCB0aGlzICk7XG4gIGRlbGV0ZSB0aGlzLnBvaW50ZXJEb3duU2Nyb2xsO1xufTtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gZHJhZ2dpbmcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxucHJvdG8uZHJhZ1N0YXJ0ID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICBpZiAoICF0aGlzLmlzRHJhZ2dhYmxlICkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLmRyYWdTdGFydFBvc2l0aW9uID0gdGhpcy54O1xuICB0aGlzLnN0YXJ0QW5pbWF0aW9uKCk7XG4gIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCAnc2Nyb2xsJywgdGhpcyApO1xuICB0aGlzLmRpc3BhdGNoRXZlbnQoICdkcmFnU3RhcnQnLCBldmVudCwgWyBwb2ludGVyIF0gKTtcbn07XG5cbnByb3RvLnBvaW50ZXJNb3ZlID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB2YXIgbW92ZVZlY3RvciA9IHRoaXMuX2RyYWdQb2ludGVyTW92ZSggZXZlbnQsIHBvaW50ZXIgKTtcbiAgdGhpcy5kaXNwYXRjaEV2ZW50KCAncG9pbnRlck1vdmUnLCBldmVudCwgWyBwb2ludGVyLCBtb3ZlVmVjdG9yIF0gKTtcbiAgdGhpcy5fZHJhZ01vdmUoIGV2ZW50LCBwb2ludGVyLCBtb3ZlVmVjdG9yICk7XG59O1xuXG5wcm90by5kcmFnTW92ZSA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciwgbW92ZVZlY3RvciApIHtcbiAgaWYgKCAhdGhpcy5pc0RyYWdnYWJsZSApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcblxuICB0aGlzLnByZXZpb3VzRHJhZ1ggPSB0aGlzLmRyYWdYO1xuICAvLyByZXZlcnNlIGlmIHJpZ2h0LXRvLWxlZnRcbiAgdmFyIGRpcmVjdGlvbiA9IHRoaXMub3B0aW9ucy5yaWdodFRvTGVmdCA/IC0xIDogMTtcbiAgaWYgKCB0aGlzLm9wdGlvbnMud3JhcEFyb3VuZCApIHtcbiAgICAvLyB3cmFwIGFyb3VuZCBtb3ZlLiAjNTg5XG4gICAgbW92ZVZlY3Rvci54ID0gbW92ZVZlY3Rvci54ICUgdGhpcy5zbGlkZWFibGVXaWR0aDtcbiAgfVxuICB2YXIgZHJhZ1ggPSB0aGlzLmRyYWdTdGFydFBvc2l0aW9uICsgbW92ZVZlY3Rvci54ICogZGlyZWN0aW9uO1xuXG4gIGlmICggIXRoaXMub3B0aW9ucy53cmFwQXJvdW5kICYmIHRoaXMuc2xpZGVzLmxlbmd0aCApIHtcbiAgICAvLyBzbG93IGRyYWdcbiAgICB2YXIgb3JpZ2luQm91bmQgPSBNYXRoLm1heCggLXRoaXMuc2xpZGVzWzBdLnRhcmdldCwgdGhpcy5kcmFnU3RhcnRQb3NpdGlvbiApO1xuICAgIGRyYWdYID0gZHJhZ1ggPiBvcmlnaW5Cb3VuZCA/ICggZHJhZ1ggKyBvcmlnaW5Cb3VuZCApICogMC41IDogZHJhZ1g7XG4gICAgdmFyIGVuZEJvdW5kID0gTWF0aC5taW4oIC10aGlzLmdldExhc3RTbGlkZSgpLnRhcmdldCwgdGhpcy5kcmFnU3RhcnRQb3NpdGlvbiApO1xuICAgIGRyYWdYID0gZHJhZ1ggPCBlbmRCb3VuZCA/ICggZHJhZ1ggKyBlbmRCb3VuZCApICogMC41IDogZHJhZ1g7XG4gIH1cblxuICB0aGlzLmRyYWdYID0gZHJhZ1g7XG5cbiAgdGhpcy5kcmFnTW92ZVRpbWUgPSBuZXcgRGF0ZSgpO1xuICB0aGlzLmRpc3BhdGNoRXZlbnQoICdkcmFnTW92ZScsIGV2ZW50LCBbIHBvaW50ZXIsIG1vdmVWZWN0b3IgXSApO1xufTtcblxucHJvdG8uZHJhZ0VuZCA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgaWYgKCAhdGhpcy5pc0RyYWdnYWJsZSApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKCB0aGlzLm9wdGlvbnMuZnJlZVNjcm9sbCApIHtcbiAgICB0aGlzLmlzRnJlZVNjcm9sbGluZyA9IHRydWU7XG4gIH1cbiAgLy8gc2V0IHNlbGVjdGVkSW5kZXggYmFzZWQgb24gd2hlcmUgZmxpY2sgd2lsbCBlbmQgdXBcbiAgdmFyIGluZGV4ID0gdGhpcy5kcmFnRW5kUmVzdGluZ1NlbGVjdCgpO1xuXG4gIGlmICggdGhpcy5vcHRpb25zLmZyZWVTY3JvbGwgJiYgIXRoaXMub3B0aW9ucy53cmFwQXJvdW5kICkge1xuICAgIC8vIGlmIGZyZWUtc2Nyb2xsICYgbm90IHdyYXAgYXJvdW5kXG4gICAgLy8gZG8gbm90IGZyZWUtc2Nyb2xsIGlmIGdvaW5nIG91dHNpZGUgb2YgYm91bmRpbmcgc2xpZGVzXG4gICAgLy8gc28gYm91bmRpbmcgc2xpZGVzIGNhbiBhdHRyYWN0IHNsaWRlciwgYW5kIGtlZXAgaXQgaW4gYm91bmRzXG4gICAgdmFyIHJlc3RpbmdYID0gdGhpcy5nZXRSZXN0aW5nUG9zaXRpb24oKTtcbiAgICB0aGlzLmlzRnJlZVNjcm9sbGluZyA9IC1yZXN0aW5nWCA+IHRoaXMuc2xpZGVzWzBdLnRhcmdldCAmJlxuICAgICAgLXJlc3RpbmdYIDwgdGhpcy5nZXRMYXN0U2xpZGUoKS50YXJnZXQ7XG4gIH0gZWxzZSBpZiAoICF0aGlzLm9wdGlvbnMuZnJlZVNjcm9sbCAmJiBpbmRleCA9PSB0aGlzLnNlbGVjdGVkSW5kZXggKSB7XG4gICAgLy8gYm9vc3Qgc2VsZWN0aW9uIGlmIHNlbGVjdGVkIGluZGV4IGhhcyBub3QgY2hhbmdlZFxuICAgIGluZGV4ICs9IHRoaXMuZHJhZ0VuZEJvb3N0U2VsZWN0KCk7XG4gIH1cbiAgZGVsZXRlIHRoaXMucHJldmlvdXNEcmFnWDtcbiAgLy8gYXBwbHkgc2VsZWN0aW9uXG4gIC8vIFRPRE8gcmVmYWN0b3IgdGhpcywgc2VsZWN0aW5nIGhlcmUgZmVlbHMgd2VpcmRcbiAgLy8gSEFDSywgc2V0IGZsYWcgc28gZHJhZ2dpbmcgc3RheXMgaW4gY29ycmVjdCBkaXJlY3Rpb25cbiAgdGhpcy5pc0RyYWdTZWxlY3QgPSB0aGlzLm9wdGlvbnMud3JhcEFyb3VuZDtcbiAgdGhpcy5zZWxlY3QoIGluZGV4ICk7XG4gIGRlbGV0ZSB0aGlzLmlzRHJhZ1NlbGVjdDtcbiAgdGhpcy5kaXNwYXRjaEV2ZW50KCAnZHJhZ0VuZCcsIGV2ZW50LCBbIHBvaW50ZXIgXSApO1xufTtcblxucHJvdG8uZHJhZ0VuZFJlc3RpbmdTZWxlY3QgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHJlc3RpbmdYID0gdGhpcy5nZXRSZXN0aW5nUG9zaXRpb24oKTtcbiAgLy8gaG93IGZhciBhd2F5IGZyb20gc2VsZWN0ZWQgc2xpZGVcbiAgdmFyIGRpc3RhbmNlID0gTWF0aC5hYnMoIHRoaXMuZ2V0U2xpZGVEaXN0YW5jZSggLXJlc3RpbmdYLCB0aGlzLnNlbGVjdGVkSW5kZXggKSApO1xuICAvLyBnZXQgY2xvc2V0IHJlc3RpbmcgZ29pbmcgdXAgYW5kIGdvaW5nIGRvd25cbiAgdmFyIHBvc2l0aXZlUmVzdGluZyA9IHRoaXMuX2dldENsb3Nlc3RSZXN0aW5nKCByZXN0aW5nWCwgZGlzdGFuY2UsIDEgKTtcbiAgdmFyIG5lZ2F0aXZlUmVzdGluZyA9IHRoaXMuX2dldENsb3Nlc3RSZXN0aW5nKCByZXN0aW5nWCwgZGlzdGFuY2UsIC0xICk7XG4gIC8vIHVzZSBjbG9zZXIgcmVzdGluZyBmb3Igd3JhcC1hcm91bmRcbiAgdmFyIGluZGV4ID0gcG9zaXRpdmVSZXN0aW5nLmRpc3RhbmNlIDwgbmVnYXRpdmVSZXN0aW5nLmRpc3RhbmNlID9cbiAgICBwb3NpdGl2ZVJlc3RpbmcuaW5kZXggOiBuZWdhdGl2ZVJlc3RpbmcuaW5kZXg7XG4gIHJldHVybiBpbmRleDtcbn07XG5cbi8qKlxuICogZ2l2ZW4gcmVzdGluZyBYIGFuZCBkaXN0YW5jZSB0byBzZWxlY3RlZCBjZWxsXG4gKiBnZXQgdGhlIGRpc3RhbmNlIGFuZCBpbmRleCBvZiB0aGUgY2xvc2VzdCBjZWxsXG4gKiBAcGFyYW0ge051bWJlcn0gcmVzdGluZ1ggLSBlc3RpbWF0ZWQgcG9zdC1mbGljayByZXN0aW5nIHBvc2l0aW9uXG4gKiBAcGFyYW0ge051bWJlcn0gZGlzdGFuY2UgLSBkaXN0YW5jZSB0byBzZWxlY3RlZCBjZWxsXG4gKiBAcGFyYW0ge0ludGVnZXJ9IGluY3JlbWVudCAtICsxIG9yIC0xLCBnb2luZyB1cCBvciBkb3duXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIHsgZGlzdGFuY2U6IHtOdW1iZXJ9LCBpbmRleDoge0ludGVnZXJ9IH1cbiAqL1xucHJvdG8uX2dldENsb3Nlc3RSZXN0aW5nID0gZnVuY3Rpb24oIHJlc3RpbmdYLCBkaXN0YW5jZSwgaW5jcmVtZW50ICkge1xuICB2YXIgaW5kZXggPSB0aGlzLnNlbGVjdGVkSW5kZXg7XG4gIHZhciBtaW5EaXN0YW5jZSA9IEluZmluaXR5O1xuICB2YXIgY29uZGl0aW9uID0gdGhpcy5vcHRpb25zLmNvbnRhaW4gJiYgIXRoaXMub3B0aW9ucy53cmFwQXJvdW5kID9cbiAgICAvLyBpZiBjb250YWluLCBrZWVwIGdvaW5nIGlmIGRpc3RhbmNlIGlzIGVxdWFsIHRvIG1pbkRpc3RhbmNlXG4gICAgZnVuY3Rpb24oIGQsIG1kICkgeyByZXR1cm4gZCA8PSBtZDsgfSA6IGZ1bmN0aW9uKCBkLCBtZCApIHsgcmV0dXJuIGQgPCBtZDsgfTtcbiAgd2hpbGUgKCBjb25kaXRpb24oIGRpc3RhbmNlLCBtaW5EaXN0YW5jZSApICkge1xuICAgIC8vIG1lYXN1cmUgZGlzdGFuY2UgdG8gbmV4dCBjZWxsXG4gICAgaW5kZXggKz0gaW5jcmVtZW50O1xuICAgIG1pbkRpc3RhbmNlID0gZGlzdGFuY2U7XG4gICAgZGlzdGFuY2UgPSB0aGlzLmdldFNsaWRlRGlzdGFuY2UoIC1yZXN0aW5nWCwgaW5kZXggKTtcbiAgICBpZiAoIGRpc3RhbmNlID09PSBudWxsICkge1xuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGRpc3RhbmNlID0gTWF0aC5hYnMoIGRpc3RhbmNlICk7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBkaXN0YW5jZTogbWluRGlzdGFuY2UsXG4gICAgLy8gc2VsZWN0ZWQgd2FzIHByZXZpb3VzIGluZGV4XG4gICAgaW5kZXg6IGluZGV4IC0gaW5jcmVtZW50XG4gIH07XG59O1xuXG4vKipcbiAqIG1lYXN1cmUgZGlzdGFuY2UgYmV0d2VlbiB4IGFuZCBhIHNsaWRlIHRhcmdldFxuICogQHBhcmFtIHtOdW1iZXJ9IHhcbiAqIEBwYXJhbSB7SW50ZWdlcn0gaW5kZXggLSBzbGlkZSBpbmRleFxuICovXG5wcm90by5nZXRTbGlkZURpc3RhbmNlID0gZnVuY3Rpb24oIHgsIGluZGV4ICkge1xuICB2YXIgbGVuID0gdGhpcy5zbGlkZXMubGVuZ3RoO1xuICAvLyB3cmFwIGFyb3VuZCBpZiBhdCBsZWFzdCAyIHNsaWRlc1xuICB2YXIgaXNXcmFwQXJvdW5kID0gdGhpcy5vcHRpb25zLndyYXBBcm91bmQgJiYgbGVuID4gMTtcbiAgdmFyIHNsaWRlSW5kZXggPSBpc1dyYXBBcm91bmQgPyB1dGlscy5tb2R1bG8oIGluZGV4LCBsZW4gKSA6IGluZGV4O1xuICB2YXIgc2xpZGUgPSB0aGlzLnNsaWRlc1sgc2xpZGVJbmRleCBdO1xuICBpZiAoICFzbGlkZSApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICAvLyBhZGQgZGlzdGFuY2UgZm9yIHdyYXAtYXJvdW5kIHNsaWRlc1xuICB2YXIgd3JhcCA9IGlzV3JhcEFyb3VuZCA/IHRoaXMuc2xpZGVhYmxlV2lkdGggKiBNYXRoLmZsb29yKCBpbmRleCAvIGxlbiApIDogMDtcbiAgcmV0dXJuIHggLSAoIHNsaWRlLnRhcmdldCArIHdyYXAgKTtcbn07XG5cbnByb3RvLmRyYWdFbmRCb29zdFNlbGVjdCA9IGZ1bmN0aW9uKCkge1xuICAvLyBkbyBub3QgYm9vc3QgaWYgbm8gcHJldmlvdXNEcmFnWCBvciBkcmFnTW92ZVRpbWVcbiAgaWYgKCB0aGlzLnByZXZpb3VzRHJhZ1ggPT09IHVuZGVmaW5lZCB8fCAhdGhpcy5kcmFnTW92ZVRpbWUgfHxcbiAgICAvLyBvciBpZiBkcmFnIHdhcyBoZWxkIGZvciAxMDAgbXNcbiAgICBuZXcgRGF0ZSgpIC0gdGhpcy5kcmFnTW92ZVRpbWUgPiAxMDAgKSB7XG4gICAgcmV0dXJuIDA7XG4gIH1cblxuICB2YXIgZGlzdGFuY2UgPSB0aGlzLmdldFNsaWRlRGlzdGFuY2UoIC10aGlzLmRyYWdYLCB0aGlzLnNlbGVjdGVkSW5kZXggKTtcbiAgdmFyIGRlbHRhID0gdGhpcy5wcmV2aW91c0RyYWdYIC0gdGhpcy5kcmFnWDtcbiAgaWYgKCBkaXN0YW5jZSA+IDAgJiYgZGVsdGEgPiAwICkge1xuICAgIC8vIGJvb3N0IHRvIG5leHQgaWYgbW92aW5nIHRvd2FyZHMgdGhlIHJpZ2h0LCBhbmQgcG9zaXRpdmUgdmVsb2NpdHlcbiAgICByZXR1cm4gMTtcbiAgfSBlbHNlIGlmICggZGlzdGFuY2UgPCAwICYmIGRlbHRhIDwgMCApIHtcbiAgICAvLyBib29zdCB0byBwcmV2aW91cyBpZiBtb3ZpbmcgdG93YXJkcyB0aGUgbGVmdCwgYW5kIG5lZ2F0aXZlIHZlbG9jaXR5XG4gICAgcmV0dXJuIC0xO1xuICB9XG4gIHJldHVybiAwO1xufTtcblxuLy8gLS0tLS0gc3RhdGljQ2xpY2sgLS0tLS0gLy9cblxucHJvdG8uc3RhdGljQ2xpY2sgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIC8vIGdldCBjbGlja2VkQ2VsbCwgaWYgY2VsbCB3YXMgY2xpY2tlZFxuICB2YXIgY2xpY2tlZENlbGwgPSB0aGlzLmdldFBhcmVudENlbGwoIGV2ZW50LnRhcmdldCApO1xuICB2YXIgY2VsbEVsZW0gPSBjbGlja2VkQ2VsbCAmJiBjbGlja2VkQ2VsbC5lbGVtZW50O1xuICB2YXIgY2VsbEluZGV4ID0gY2xpY2tlZENlbGwgJiYgdGhpcy5jZWxscy5pbmRleE9mKCBjbGlja2VkQ2VsbCApO1xuICB0aGlzLmRpc3BhdGNoRXZlbnQoICdzdGF0aWNDbGljaycsIGV2ZW50LCBbIHBvaW50ZXIsIGNlbGxFbGVtLCBjZWxsSW5kZXggXSApO1xufTtcblxuLy8gLS0tLS0gc2Nyb2xsIC0tLS0tIC8vXG5cbnByb3RvLm9uc2Nyb2xsID0gZnVuY3Rpb24oKSB7XG4gIHZhciBzY3JvbGwgPSBnZXRTY3JvbGxQb3NpdGlvbigpO1xuICB2YXIgc2Nyb2xsTW92ZVggPSB0aGlzLnBvaW50ZXJEb3duU2Nyb2xsLnggLSBzY3JvbGwueDtcbiAgdmFyIHNjcm9sbE1vdmVZID0gdGhpcy5wb2ludGVyRG93blNjcm9sbC55IC0gc2Nyb2xsLnk7XG4gIC8vIGNhbmNlbCBjbGljay90YXAgaWYgc2Nyb2xsIGlzIHRvbyBtdWNoXG4gIGlmICggTWF0aC5hYnMoIHNjcm9sbE1vdmVYICkgPiAzIHx8IE1hdGguYWJzKCBzY3JvbGxNb3ZlWSApID4gMyApIHtcbiAgICB0aGlzLl9wb2ludGVyRG9uZSgpO1xuICB9XG59O1xuXG4vLyAtLS0tLSB1dGlscyAtLS0tLSAvL1xuXG5mdW5jdGlvbiBnZXRTY3JvbGxQb3NpdGlvbigpIHtcbiAgcmV0dXJuIHtcbiAgICB4OiB3aW5kb3cucGFnZVhPZmZzZXQsXG4gICAgeTogd2luZG93LnBhZ2VZT2Zmc2V0XG4gIH07XG59XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG5yZXR1cm4gRmxpY2tpdHk7XG5cbn0pKTtcbiIsIi8vIEZsaWNraXR5IG1haW5cbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnZXYtZW1pdHRlci9ldi1lbWl0dGVyJyxcbiAgICAgICdnZXQtc2l6ZS9nZXQtc2l6ZScsXG4gICAgICAnZml6enktdWktdXRpbHMvdXRpbHMnLFxuICAgICAgJy4vY2VsbCcsXG4gICAgICAnLi9zbGlkZScsXG4gICAgICAnLi9hbmltYXRlJ1xuICAgIF0sIGZ1bmN0aW9uKCBFdkVtaXR0ZXIsIGdldFNpemUsIHV0aWxzLCBDZWxsLCBTbGlkZSwgYW5pbWF0ZVByb3RvdHlwZSApIHtcbiAgICAgIHJldHVybiBmYWN0b3J5KCB3aW5kb3csIEV2RW1pdHRlciwgZ2V0U2l6ZSwgdXRpbHMsIENlbGwsIFNsaWRlLCBhbmltYXRlUHJvdG90eXBlICk7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICByZXF1aXJlKCdldi1lbWl0dGVyJyksXG4gICAgICByZXF1aXJlKCdnZXQtc2l6ZScpLFxuICAgICAgcmVxdWlyZSgnZml6enktdWktdXRpbHMnKSxcbiAgICAgIHJlcXVpcmUoJy4vY2VsbCcpLFxuICAgICAgcmVxdWlyZSgnLi9zbGlkZScpLFxuICAgICAgcmVxdWlyZSgnLi9hbmltYXRlJylcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIC8vIGJyb3dzZXIgZ2xvYmFsXG4gICAgdmFyIF9GbGlja2l0eSA9IHdpbmRvdy5GbGlja2l0eTtcblxuICAgIHdpbmRvdy5GbGlja2l0eSA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICB3aW5kb3cuRXZFbWl0dGVyLFxuICAgICAgd2luZG93LmdldFNpemUsXG4gICAgICB3aW5kb3cuZml6enlVSVV0aWxzLFxuICAgICAgX0ZsaWNraXR5LkNlbGwsXG4gICAgICBfRmxpY2tpdHkuU2xpZGUsXG4gICAgICBfRmxpY2tpdHkuYW5pbWF0ZVByb3RvdHlwZVxuICAgICk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCB3aW5kb3csIEV2RW1pdHRlciwgZ2V0U2l6ZSxcbiAgdXRpbHMsIENlbGwsIFNsaWRlLCBhbmltYXRlUHJvdG90eXBlICkge1xuXG4ndXNlIHN0cmljdCc7XG5cbi8vIHZhcnNcbnZhciBqUXVlcnkgPSB3aW5kb3cualF1ZXJ5O1xudmFyIGdldENvbXB1dGVkU3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZTtcbnZhciBjb25zb2xlID0gd2luZG93LmNvbnNvbGU7XG5cbmZ1bmN0aW9uIG1vdmVFbGVtZW50cyggZWxlbXMsIHRvRWxlbSApIHtcbiAgZWxlbXMgPSB1dGlscy5tYWtlQXJyYXkoIGVsZW1zICk7XG4gIHdoaWxlICggZWxlbXMubGVuZ3RoICkge1xuICAgIHRvRWxlbS5hcHBlbmRDaGlsZCggZWxlbXMuc2hpZnQoKSApO1xuICB9XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEZsaWNraXR5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbi8vIGdsb2JhbGx5IHVuaXF1ZSBpZGVudGlmaWVyc1xudmFyIEdVSUQgPSAwO1xuLy8gaW50ZXJuYWwgc3RvcmUgb2YgYWxsIEZsaWNraXR5IGludGFuY2VzXG52YXIgaW5zdGFuY2VzID0ge307XG5cbmZ1bmN0aW9uIEZsaWNraXR5KCBlbGVtZW50LCBvcHRpb25zICkge1xuICB2YXIgcXVlcnlFbGVtZW50ID0gdXRpbHMuZ2V0UXVlcnlFbGVtZW50KCBlbGVtZW50ICk7XG4gIGlmICggIXF1ZXJ5RWxlbWVudCApIHtcbiAgICBpZiAoIGNvbnNvbGUgKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCAnQmFkIGVsZW1lbnQgZm9yIEZsaWNraXR5OiAnICsgKCBxdWVyeUVsZW1lbnQgfHwgZWxlbWVudCApICk7XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLmVsZW1lbnQgPSBxdWVyeUVsZW1lbnQ7XG4gIC8vIGRvIG5vdCBpbml0aWFsaXplIHR3aWNlIG9uIHNhbWUgZWxlbWVudFxuICBpZiAoIHRoaXMuZWxlbWVudC5mbGlja2l0eUdVSUQgKSB7XG4gICAgdmFyIGluc3RhbmNlID0gaW5zdGFuY2VzWyB0aGlzLmVsZW1lbnQuZmxpY2tpdHlHVUlEIF07XG4gICAgaW5zdGFuY2Uub3B0aW9uKCBvcHRpb25zICk7XG4gICAgcmV0dXJuIGluc3RhbmNlO1xuICB9XG5cbiAgLy8gYWRkIGpRdWVyeVxuICBpZiAoIGpRdWVyeSApIHtcbiAgICB0aGlzLiRlbGVtZW50ID0galF1ZXJ5KCB0aGlzLmVsZW1lbnQgKTtcbiAgfVxuICAvLyBvcHRpb25zXG4gIHRoaXMub3B0aW9ucyA9IHV0aWxzLmV4dGVuZCgge30sIHRoaXMuY29uc3RydWN0b3IuZGVmYXVsdHMgKTtcbiAgdGhpcy5vcHRpb24oIG9wdGlvbnMgKTtcblxuICAvLyBraWNrIHRoaW5ncyBvZmZcbiAgdGhpcy5fY3JlYXRlKCk7XG59XG5cbkZsaWNraXR5LmRlZmF1bHRzID0ge1xuICBhY2Nlc3NpYmlsaXR5OiB0cnVlLFxuICAvLyBhZGFwdGl2ZUhlaWdodDogZmFsc2UsXG4gIGNlbGxBbGlnbjogJ2NlbnRlcicsXG4gIC8vIGNlbGxTZWxlY3RvcjogdW5kZWZpbmVkLFxuICAvLyBjb250YWluOiBmYWxzZSxcbiAgZnJlZVNjcm9sbEZyaWN0aW9uOiAwLjA3NSwgLy8gZnJpY3Rpb24gd2hlbiBmcmVlLXNjcm9sbGluZ1xuICBmcmljdGlvbjogMC4yOCwgLy8gZnJpY3Rpb24gd2hlbiBzZWxlY3RpbmdcbiAgbmFtZXNwYWNlSlF1ZXJ5RXZlbnRzOiB0cnVlLFxuICAvLyBpbml0aWFsSW5kZXg6IDAsXG4gIHBlcmNlbnRQb3NpdGlvbjogdHJ1ZSxcbiAgcmVzaXplOiB0cnVlLFxuICBzZWxlY3RlZEF0dHJhY3Rpb246IDAuMDI1LFxuICBzZXRHYWxsZXJ5U2l6ZTogdHJ1ZVxuICAvLyB3YXRjaENTUzogZmFsc2UsXG4gIC8vIHdyYXBBcm91bmQ6IGZhbHNlXG59O1xuXG4vLyBoYXNoIG9mIG1ldGhvZHMgdHJpZ2dlcmVkIG9uIF9jcmVhdGUoKVxuRmxpY2tpdHkuY3JlYXRlTWV0aG9kcyA9IFtdO1xuXG52YXIgcHJvdG8gPSBGbGlja2l0eS5wcm90b3R5cGU7XG4vLyBpbmhlcml0IEV2ZW50RW1pdHRlclxudXRpbHMuZXh0ZW5kKCBwcm90bywgRXZFbWl0dGVyLnByb3RvdHlwZSApO1xuXG5wcm90by5fY3JlYXRlID0gZnVuY3Rpb24oKSB7XG4gIC8vIGFkZCBpZCBmb3IgRmxpY2tpdHkuZGF0YVxuICB2YXIgaWQgPSB0aGlzLmd1aWQgPSArK0dVSUQ7XG4gIHRoaXMuZWxlbWVudC5mbGlja2l0eUdVSUQgPSBpZDsgLy8gZXhwYW5kb1xuICBpbnN0YW5jZXNbIGlkIF0gPSB0aGlzOyAvLyBhc3NvY2lhdGUgdmlhIGlkXG4gIC8vIGluaXRpYWwgcHJvcGVydGllc1xuICB0aGlzLnNlbGVjdGVkSW5kZXggPSAwO1xuICAvLyBob3cgbWFueSBmcmFtZXMgc2xpZGVyIGhhcyBiZWVuIGluIHNhbWUgcG9zaXRpb25cbiAgdGhpcy5yZXN0aW5nRnJhbWVzID0gMDtcbiAgLy8gaW5pdGlhbCBwaHlzaWNzIHByb3BlcnRpZXNcbiAgdGhpcy54ID0gMDtcbiAgdGhpcy52ZWxvY2l0eSA9IDA7XG4gIHRoaXMub3JpZ2luU2lkZSA9IHRoaXMub3B0aW9ucy5yaWdodFRvTGVmdCA/ICdyaWdodCcgOiAnbGVmdCc7XG4gIC8vIGNyZWF0ZSB2aWV3cG9ydCAmIHNsaWRlclxuICB0aGlzLnZpZXdwb3J0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gIHRoaXMudmlld3BvcnQuY2xhc3NOYW1lID0gJ2ZsaWNraXR5LXZpZXdwb3J0JztcbiAgdGhpcy5fY3JlYXRlU2xpZGVyKCk7XG5cbiAgaWYgKCB0aGlzLm9wdGlvbnMucmVzaXplIHx8IHRoaXMub3B0aW9ucy53YXRjaENTUyApIHtcbiAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lciggJ3Jlc2l6ZScsIHRoaXMgKTtcbiAgfVxuXG4gIC8vIGFkZCBsaXN0ZW5lcnMgZnJvbSBvbiBvcHRpb25cbiAgZm9yICggdmFyIGV2ZW50TmFtZSBpbiB0aGlzLm9wdGlvbnMub24gKSB7XG4gICAgdmFyIGxpc3RlbmVyID0gdGhpcy5vcHRpb25zLm9uWyBldmVudE5hbWUgXTtcbiAgICB0aGlzLm9uKCBldmVudE5hbWUsIGxpc3RlbmVyICk7XG4gIH1cblxuICBGbGlja2l0eS5jcmVhdGVNZXRob2RzLmZvckVhY2goIGZ1bmN0aW9uKCBtZXRob2QgKSB7XG4gICAgdGhpc1sgbWV0aG9kIF0oKTtcbiAgfSwgdGhpcyApO1xuXG4gIGlmICggdGhpcy5vcHRpb25zLndhdGNoQ1NTICkge1xuICAgIHRoaXMud2F0Y2hDU1MoKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLmFjdGl2YXRlKCk7XG4gIH1cblxufTtcblxuLyoqXG4gKiBzZXQgb3B0aW9uc1xuICogQHBhcmFtIHtPYmplY3R9IG9wdHNcbiAqL1xucHJvdG8ub3B0aW9uID0gZnVuY3Rpb24oIG9wdHMgKSB7XG4gIHV0aWxzLmV4dGVuZCggdGhpcy5vcHRpb25zLCBvcHRzICk7XG59O1xuXG5wcm90by5hY3RpdmF0ZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIHRoaXMuaXNBY3RpdmUgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMuaXNBY3RpdmUgPSB0cnVlO1xuICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZmxpY2tpdHktZW5hYmxlZCcpO1xuICBpZiAoIHRoaXMub3B0aW9ucy5yaWdodFRvTGVmdCApIHtcbiAgICB0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgnZmxpY2tpdHktcnRsJyk7XG4gIH1cblxuICB0aGlzLmdldFNpemUoKTtcbiAgLy8gbW92ZSBpbml0aWFsIGNlbGwgZWxlbWVudHMgc28gdGhleSBjYW4gYmUgbG9hZGVkIGFzIGNlbGxzXG4gIHZhciBjZWxsRWxlbXMgPSB0aGlzLl9maWx0ZXJGaW5kQ2VsbEVsZW1lbnRzKCB0aGlzLmVsZW1lbnQuY2hpbGRyZW4gKTtcbiAgbW92ZUVsZW1lbnRzKCBjZWxsRWxlbXMsIHRoaXMuc2xpZGVyICk7XG4gIHRoaXMudmlld3BvcnQuYXBwZW5kQ2hpbGQoIHRoaXMuc2xpZGVyICk7XG4gIHRoaXMuZWxlbWVudC5hcHBlbmRDaGlsZCggdGhpcy52aWV3cG9ydCApO1xuICAvLyBnZXQgY2VsbHMgZnJvbSBjaGlsZHJlblxuICB0aGlzLnJlbG9hZENlbGxzKCk7XG5cbiAgaWYgKCB0aGlzLm9wdGlvbnMuYWNjZXNzaWJpbGl0eSApIHtcbiAgICAvLyBhbGxvdyBlbGVtZW50IHRvIGZvY3VzYWJsZVxuICAgIHRoaXMuZWxlbWVudC50YWJJbmRleCA9IDA7XG4gICAgLy8gbGlzdGVuIGZvciBrZXkgcHJlc3Nlc1xuICAgIHRoaXMuZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCAna2V5ZG93bicsIHRoaXMgKTtcbiAgfVxuXG4gIHRoaXMuZW1pdEV2ZW50KCdhY3RpdmF0ZScpO1xuICB0aGlzLnNlbGVjdEluaXRpYWxJbmRleCgpO1xuICAvLyBmbGFnIGZvciBpbml0aWFsIGFjdGl2YXRpb24sIGZvciB1c2luZyBpbml0aWFsSW5kZXhcbiAgdGhpcy5pc0luaXRBY3RpdmF0ZWQgPSB0cnVlO1xuICAvLyByZWFkeSBldmVudC4gIzQ5M1xuICB0aGlzLmRpc3BhdGNoRXZlbnQoJ3JlYWR5Jyk7XG59O1xuXG4vLyBzbGlkZXIgcG9zaXRpb25zIHRoZSBjZWxsc1xucHJvdG8uX2NyZWF0ZVNsaWRlciA9IGZ1bmN0aW9uKCkge1xuICAvLyBzbGlkZXIgZWxlbWVudCBkb2VzIGFsbCB0aGUgcG9zaXRpb25pbmdcbiAgdmFyIHNsaWRlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICBzbGlkZXIuY2xhc3NOYW1lID0gJ2ZsaWNraXR5LXNsaWRlcic7XG4gIHNsaWRlci5zdHlsZVsgdGhpcy5vcmlnaW5TaWRlIF0gPSAwO1xuICB0aGlzLnNsaWRlciA9IHNsaWRlcjtcbn07XG5cbnByb3RvLl9maWx0ZXJGaW5kQ2VsbEVsZW1lbnRzID0gZnVuY3Rpb24oIGVsZW1zICkge1xuICByZXR1cm4gdXRpbHMuZmlsdGVyRmluZEVsZW1lbnRzKCBlbGVtcywgdGhpcy5vcHRpb25zLmNlbGxTZWxlY3RvciApO1xufTtcblxuLy8gZ29lcyB0aHJvdWdoIGFsbCBjaGlsZHJlblxucHJvdG8ucmVsb2FkQ2VsbHMgPSBmdW5jdGlvbigpIHtcbiAgLy8gY29sbGVjdGlvbiBvZiBpdGVtIGVsZW1lbnRzXG4gIHRoaXMuY2VsbHMgPSB0aGlzLl9tYWtlQ2VsbHMoIHRoaXMuc2xpZGVyLmNoaWxkcmVuICk7XG4gIHRoaXMucG9zaXRpb25DZWxscygpO1xuICB0aGlzLl9nZXRXcmFwU2hpZnRDZWxscygpO1xuICB0aGlzLnNldEdhbGxlcnlTaXplKCk7XG59O1xuXG4vKipcbiAqIHR1cm4gZWxlbWVudHMgaW50byBGbGlja2l0eS5DZWxsc1xuICogQHBhcmFtIHtBcnJheSBvciBOb2RlTGlzdCBvciBIVE1MRWxlbWVudH0gZWxlbXNcbiAqIEByZXR1cm5zIHtBcnJheX0gaXRlbXMgLSBjb2xsZWN0aW9uIG9mIG5ldyBGbGlja2l0eSBDZWxsc1xuICovXG5wcm90by5fbWFrZUNlbGxzID0gZnVuY3Rpb24oIGVsZW1zICkge1xuICB2YXIgY2VsbEVsZW1zID0gdGhpcy5fZmlsdGVyRmluZENlbGxFbGVtZW50cyggZWxlbXMgKTtcblxuICAvLyBjcmVhdGUgbmV3IEZsaWNraXR5IGZvciBjb2xsZWN0aW9uXG4gIHZhciBjZWxscyA9IGNlbGxFbGVtcy5tYXAoIGZ1bmN0aW9uKCBjZWxsRWxlbSApIHtcbiAgICByZXR1cm4gbmV3IENlbGwoIGNlbGxFbGVtLCB0aGlzICk7XG4gIH0sIHRoaXMgKTtcblxuICByZXR1cm4gY2VsbHM7XG59O1xuXG5wcm90by5nZXRMYXN0Q2VsbCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5jZWxsc1sgdGhpcy5jZWxscy5sZW5ndGggLSAxIF07XG59O1xuXG5wcm90by5nZXRMYXN0U2xpZGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuc2xpZGVzWyB0aGlzLnNsaWRlcy5sZW5ndGggLSAxIF07XG59O1xuXG4vLyBwb3NpdGlvbnMgYWxsIGNlbGxzXG5wcm90by5wb3NpdGlvbkNlbGxzID0gZnVuY3Rpb24oKSB7XG4gIC8vIHNpemUgYWxsIGNlbGxzXG4gIHRoaXMuX3NpemVDZWxscyggdGhpcy5jZWxscyApO1xuICAvLyBwb3NpdGlvbiBhbGwgY2VsbHNcbiAgdGhpcy5fcG9zaXRpb25DZWxscyggMCApO1xufTtcblxuLyoqXG4gKiBwb3NpdGlvbiBjZXJ0YWluIGNlbGxzXG4gKiBAcGFyYW0ge0ludGVnZXJ9IGluZGV4IC0gd2hpY2ggY2VsbCB0byBzdGFydCB3aXRoXG4gKi9cbnByb3RvLl9wb3NpdGlvbkNlbGxzID0gZnVuY3Rpb24oIGluZGV4ICkge1xuICBpbmRleCA9IGluZGV4IHx8IDA7XG4gIC8vIGFsc28gbWVhc3VyZSBtYXhDZWxsSGVpZ2h0XG4gIC8vIHN0YXJ0IDAgaWYgcG9zaXRpb25pbmcgYWxsIGNlbGxzXG4gIHRoaXMubWF4Q2VsbEhlaWdodCA9IGluZGV4ID8gdGhpcy5tYXhDZWxsSGVpZ2h0IHx8IDAgOiAwO1xuICB2YXIgY2VsbFggPSAwO1xuICAvLyBnZXQgY2VsbFhcbiAgaWYgKCBpbmRleCA+IDAgKSB7XG4gICAgdmFyIHN0YXJ0Q2VsbCA9IHRoaXMuY2VsbHNbIGluZGV4IC0gMSBdO1xuICAgIGNlbGxYID0gc3RhcnRDZWxsLnggKyBzdGFydENlbGwuc2l6ZS5vdXRlcldpZHRoO1xuICB9XG4gIHZhciBsZW4gPSB0aGlzLmNlbGxzLmxlbmd0aDtcbiAgZm9yICggdmFyIGk9aW5kZXg7IGkgPCBsZW47IGkrKyApIHtcbiAgICB2YXIgY2VsbCA9IHRoaXMuY2VsbHNbaV07XG4gICAgY2VsbC5zZXRQb3NpdGlvbiggY2VsbFggKTtcbiAgICBjZWxsWCArPSBjZWxsLnNpemUub3V0ZXJXaWR0aDtcbiAgICB0aGlzLm1heENlbGxIZWlnaHQgPSBNYXRoLm1heCggY2VsbC5zaXplLm91dGVySGVpZ2h0LCB0aGlzLm1heENlbGxIZWlnaHQgKTtcbiAgfVxuICAvLyBrZWVwIHRyYWNrIG9mIGNlbGxYIGZvciB3cmFwLWFyb3VuZFxuICB0aGlzLnNsaWRlYWJsZVdpZHRoID0gY2VsbFg7XG4gIC8vIHNsaWRlc1xuICB0aGlzLnVwZGF0ZVNsaWRlcygpO1xuICAvLyBjb250YWluIHNsaWRlcyB0YXJnZXRcbiAgdGhpcy5fY29udGFpblNsaWRlcygpO1xuICAvLyB1cGRhdGUgc2xpZGVzV2lkdGhcbiAgdGhpcy5zbGlkZXNXaWR0aCA9IGxlbiA/IHRoaXMuZ2V0TGFzdFNsaWRlKCkudGFyZ2V0IC0gdGhpcy5zbGlkZXNbMF0udGFyZ2V0IDogMDtcbn07XG5cbi8qKlxuICogY2VsbC5nZXRTaXplKCkgb24gbXVsdGlwbGUgY2VsbHNcbiAqIEBwYXJhbSB7QXJyYXl9IGNlbGxzXG4gKi9cbnByb3RvLl9zaXplQ2VsbHMgPSBmdW5jdGlvbiggY2VsbHMgKSB7XG4gIGNlbGxzLmZvckVhY2goIGZ1bmN0aW9uKCBjZWxsICkge1xuICAgIGNlbGwuZ2V0U2l6ZSgpO1xuICB9KTtcbn07XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5wcm90by51cGRhdGVTbGlkZXMgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5zbGlkZXMgPSBbXTtcbiAgaWYgKCAhdGhpcy5jZWxscy5sZW5ndGggKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIHNsaWRlID0gbmV3IFNsaWRlKCB0aGlzICk7XG4gIHRoaXMuc2xpZGVzLnB1c2goIHNsaWRlICk7XG4gIHZhciBpc09yaWdpbkxlZnQgPSB0aGlzLm9yaWdpblNpZGUgPT0gJ2xlZnQnO1xuICB2YXIgbmV4dE1hcmdpbiA9IGlzT3JpZ2luTGVmdCA/ICdtYXJnaW5SaWdodCcgOiAnbWFyZ2luTGVmdCc7XG5cbiAgdmFyIGNhbkNlbGxGaXQgPSB0aGlzLl9nZXRDYW5DZWxsRml0KCk7XG5cbiAgdGhpcy5jZWxscy5mb3JFYWNoKCBmdW5jdGlvbiggY2VsbCwgaSApIHtcbiAgICAvLyBqdXN0IGFkZCBjZWxsIGlmIGZpcnN0IGNlbGwgaW4gc2xpZGVcbiAgICBpZiAoICFzbGlkZS5jZWxscy5sZW5ndGggKSB7XG4gICAgICBzbGlkZS5hZGRDZWxsKCBjZWxsICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHNsaWRlV2lkdGggPSAoIHNsaWRlLm91dGVyV2lkdGggLSBzbGlkZS5maXJzdE1hcmdpbiApICtcbiAgICAgICggY2VsbC5zaXplLm91dGVyV2lkdGggLSBjZWxsLnNpemVbIG5leHRNYXJnaW4gXSApO1xuXG4gICAgaWYgKCBjYW5DZWxsRml0LmNhbGwoIHRoaXMsIGksIHNsaWRlV2lkdGggKSApIHtcbiAgICAgIHNsaWRlLmFkZENlbGwoIGNlbGwgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gZG9lc24ndCBmaXQsIG5ldyBzbGlkZVxuICAgICAgc2xpZGUudXBkYXRlVGFyZ2V0KCk7XG5cbiAgICAgIHNsaWRlID0gbmV3IFNsaWRlKCB0aGlzICk7XG4gICAgICB0aGlzLnNsaWRlcy5wdXNoKCBzbGlkZSApO1xuICAgICAgc2xpZGUuYWRkQ2VsbCggY2VsbCApO1xuICAgIH1cbiAgfSwgdGhpcyApO1xuICAvLyBsYXN0IHNsaWRlXG4gIHNsaWRlLnVwZGF0ZVRhcmdldCgpO1xuICAvLyB1cGRhdGUgLnNlbGVjdGVkU2xpZGVcbiAgdGhpcy51cGRhdGVTZWxlY3RlZFNsaWRlKCk7XG59O1xuXG5wcm90by5fZ2V0Q2FuQ2VsbEZpdCA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ3JvdXBDZWxscyA9IHRoaXMub3B0aW9ucy5ncm91cENlbGxzO1xuICBpZiAoICFncm91cENlbGxzICkge1xuICAgIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9O1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgZ3JvdXBDZWxscyA9PSAnbnVtYmVyJyApIHtcbiAgICAvLyBncm91cCBieSBudW1iZXIuIDMgLT4gWzAsMSwyXSwgWzMsNCw1XSwgLi4uXG4gICAgdmFyIG51bWJlciA9IHBhcnNlSW50KCBncm91cENlbGxzLCAxMCApO1xuICAgIHJldHVybiBmdW5jdGlvbiggaSApIHtcbiAgICAgIHJldHVybiAoIGkgJSBudW1iZXIgKSAhPT0gMDtcbiAgICB9O1xuICB9XG4gIC8vIGRlZmF1bHQsIGdyb3VwIGJ5IHdpZHRoIG9mIHNsaWRlXG4gIC8vIHBhcnNlICc3NSVcbiAgdmFyIHBlcmNlbnRNYXRjaCA9IHR5cGVvZiBncm91cENlbGxzID09ICdzdHJpbmcnICYmXG4gICAgZ3JvdXBDZWxscy5tYXRjaCgvXihcXGQrKSUkLyk7XG4gIHZhciBwZXJjZW50ID0gcGVyY2VudE1hdGNoID8gcGFyc2VJbnQoIHBlcmNlbnRNYXRjaFsxXSwgMTAgKSAvIDEwMCA6IDE7XG4gIHJldHVybiBmdW5jdGlvbiggaSwgc2xpZGVXaWR0aCApIHtcbiAgICByZXR1cm4gc2xpZGVXaWR0aCA8PSAoIHRoaXMuc2l6ZS5pbm5lcldpZHRoICsgMSApICogcGVyY2VudDtcbiAgfTtcbn07XG5cbi8vIGFsaWFzIF9pbml0IGZvciBqUXVlcnkgcGx1Z2luIC5mbGlja2l0eSgpXG5wcm90by5faW5pdCA9XG5wcm90by5yZXBvc2l0aW9uID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucG9zaXRpb25DZWxscygpO1xuICB0aGlzLnBvc2l0aW9uU2xpZGVyQXRTZWxlY3RlZCgpO1xufTtcblxucHJvdG8uZ2V0U2l6ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnNpemUgPSBnZXRTaXplKCB0aGlzLmVsZW1lbnQgKTtcbiAgdGhpcy5zZXRDZWxsQWxpZ24oKTtcbiAgdGhpcy5jdXJzb3JQb3NpdGlvbiA9IHRoaXMuc2l6ZS5pbm5lcldpZHRoICogdGhpcy5jZWxsQWxpZ247XG59O1xuXG52YXIgY2VsbEFsaWduU2hvcnRoYW5kcyA9IHtcbiAgLy8gY2VsbCBhbGlnbiwgdGhlbiBiYXNlZCBvbiBvcmlnaW4gc2lkZVxuICBjZW50ZXI6IHtcbiAgICBsZWZ0OiAwLjUsXG4gICAgcmlnaHQ6IDAuNVxuICB9LFxuICBsZWZ0OiB7XG4gICAgbGVmdDogMCxcbiAgICByaWdodDogMVxuICB9LFxuICByaWdodDoge1xuICAgIHJpZ2h0OiAwLFxuICAgIGxlZnQ6IDFcbiAgfVxufTtcblxucHJvdG8uc2V0Q2VsbEFsaWduID0gZnVuY3Rpb24oKSB7XG4gIHZhciBzaG9ydGhhbmQgPSBjZWxsQWxpZ25TaG9ydGhhbmRzWyB0aGlzLm9wdGlvbnMuY2VsbEFsaWduIF07XG4gIHRoaXMuY2VsbEFsaWduID0gc2hvcnRoYW5kID8gc2hvcnRoYW5kWyB0aGlzLm9yaWdpblNpZGUgXSA6IHRoaXMub3B0aW9ucy5jZWxsQWxpZ247XG59O1xuXG5wcm90by5zZXRHYWxsZXJ5U2l6ZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIHRoaXMub3B0aW9ucy5zZXRHYWxsZXJ5U2l6ZSApIHtcbiAgICB2YXIgaGVpZ2h0ID0gdGhpcy5vcHRpb25zLmFkYXB0aXZlSGVpZ2h0ICYmIHRoaXMuc2VsZWN0ZWRTbGlkZSA/XG4gICAgICB0aGlzLnNlbGVjdGVkU2xpZGUuaGVpZ2h0IDogdGhpcy5tYXhDZWxsSGVpZ2h0O1xuICAgIHRoaXMudmlld3BvcnQuc3R5bGUuaGVpZ2h0ID0gaGVpZ2h0ICsgJ3B4JztcbiAgfVxufTtcblxucHJvdG8uX2dldFdyYXBTaGlmdENlbGxzID0gZnVuY3Rpb24oKSB7XG4gIC8vIG9ubHkgZm9yIHdyYXAtYXJvdW5kXG4gIGlmICggIXRoaXMub3B0aW9ucy53cmFwQXJvdW5kICkge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyB1bnNoaWZ0IHByZXZpb3VzIGNlbGxzXG4gIHRoaXMuX3Vuc2hpZnRDZWxscyggdGhpcy5iZWZvcmVTaGlmdENlbGxzICk7XG4gIHRoaXMuX3Vuc2hpZnRDZWxscyggdGhpcy5hZnRlclNoaWZ0Q2VsbHMgKTtcbiAgLy8gZ2V0IGJlZm9yZSBjZWxsc1xuICAvLyBpbml0aWFsIGdhcFxuICB2YXIgZ2FwWCA9IHRoaXMuY3Vyc29yUG9zaXRpb247XG4gIHZhciBjZWxsSW5kZXggPSB0aGlzLmNlbGxzLmxlbmd0aCAtIDE7XG4gIHRoaXMuYmVmb3JlU2hpZnRDZWxscyA9IHRoaXMuX2dldEdhcENlbGxzKCBnYXBYLCBjZWxsSW5kZXgsIC0xICk7XG4gIC8vIGdldCBhZnRlciBjZWxsc1xuICAvLyBlbmRpbmcgZ2FwIGJldHdlZW4gbGFzdCBjZWxsIGFuZCBlbmQgb2YgZ2FsbGVyeSB2aWV3cG9ydFxuICBnYXBYID0gdGhpcy5zaXplLmlubmVyV2lkdGggLSB0aGlzLmN1cnNvclBvc2l0aW9uO1xuICAvLyBzdGFydCBjbG9uaW5nIGF0IGZpcnN0IGNlbGwsIHdvcmtpbmcgZm9yd2FyZHNcbiAgdGhpcy5hZnRlclNoaWZ0Q2VsbHMgPSB0aGlzLl9nZXRHYXBDZWxscyggZ2FwWCwgMCwgMSApO1xufTtcblxucHJvdG8uX2dldEdhcENlbGxzID0gZnVuY3Rpb24oIGdhcFgsIGNlbGxJbmRleCwgaW5jcmVtZW50ICkge1xuICAvLyBrZWVwIGFkZGluZyBjZWxscyB1bnRpbCB0aGUgY292ZXIgdGhlIGluaXRpYWwgZ2FwXG4gIHZhciBjZWxscyA9IFtdO1xuICB3aGlsZSAoIGdhcFggPiAwICkge1xuICAgIHZhciBjZWxsID0gdGhpcy5jZWxsc1sgY2VsbEluZGV4IF07XG4gICAgaWYgKCAhY2VsbCApIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBjZWxscy5wdXNoKCBjZWxsICk7XG4gICAgY2VsbEluZGV4ICs9IGluY3JlbWVudDtcbiAgICBnYXBYIC09IGNlbGwuc2l6ZS5vdXRlcldpZHRoO1xuICB9XG4gIHJldHVybiBjZWxscztcbn07XG5cbi8vIC0tLS0tIGNvbnRhaW4gLS0tLS0gLy9cblxuLy8gY29udGFpbiBjZWxsIHRhcmdldHMgc28gbm8gZXhjZXNzIHNsaWRpbmdcbnByb3RvLl9jb250YWluU2xpZGVzID0gZnVuY3Rpb24oKSB7XG4gIGlmICggIXRoaXMub3B0aW9ucy5jb250YWluIHx8IHRoaXMub3B0aW9ucy53cmFwQXJvdW5kIHx8ICF0aGlzLmNlbGxzLmxlbmd0aCApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdmFyIGlzUmlnaHRUb0xlZnQgPSB0aGlzLm9wdGlvbnMucmlnaHRUb0xlZnQ7XG4gIHZhciBiZWdpbk1hcmdpbiA9IGlzUmlnaHRUb0xlZnQgPyAnbWFyZ2luUmlnaHQnIDogJ21hcmdpbkxlZnQnO1xuICB2YXIgZW5kTWFyZ2luID0gaXNSaWdodFRvTGVmdCA/ICdtYXJnaW5MZWZ0JyA6ICdtYXJnaW5SaWdodCc7XG4gIHZhciBjb250ZW50V2lkdGggPSB0aGlzLnNsaWRlYWJsZVdpZHRoIC0gdGhpcy5nZXRMYXN0Q2VsbCgpLnNpemVbIGVuZE1hcmdpbiBdO1xuICAvLyBjb250ZW50IGlzIGxlc3MgdGhhbiBnYWxsZXJ5IHNpemVcbiAgdmFyIGlzQ29udGVudFNtYWxsZXIgPSBjb250ZW50V2lkdGggPCB0aGlzLnNpemUuaW5uZXJXaWR0aDtcbiAgLy8gYm91bmRzXG4gIHZhciBiZWdpbkJvdW5kID0gdGhpcy5jdXJzb3JQb3NpdGlvbiArIHRoaXMuY2VsbHNbMF0uc2l6ZVsgYmVnaW5NYXJnaW4gXTtcbiAgdmFyIGVuZEJvdW5kID0gY29udGVudFdpZHRoIC0gdGhpcy5zaXplLmlubmVyV2lkdGggKiAoIDEgLSB0aGlzLmNlbGxBbGlnbiApO1xuICAvLyBjb250YWluIGVhY2ggY2VsbCB0YXJnZXRcbiAgdGhpcy5zbGlkZXMuZm9yRWFjaCggZnVuY3Rpb24oIHNsaWRlICkge1xuICAgIGlmICggaXNDb250ZW50U21hbGxlciApIHtcbiAgICAgIC8vIGFsbCBjZWxscyBmaXQgaW5zaWRlIGdhbGxlcnlcbiAgICAgIHNsaWRlLnRhcmdldCA9IGNvbnRlbnRXaWR0aCAqIHRoaXMuY2VsbEFsaWduO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBjb250YWluIHRvIGJvdW5kc1xuICAgICAgc2xpZGUudGFyZ2V0ID0gTWF0aC5tYXgoIHNsaWRlLnRhcmdldCwgYmVnaW5Cb3VuZCApO1xuICAgICAgc2xpZGUudGFyZ2V0ID0gTWF0aC5taW4oIHNsaWRlLnRhcmdldCwgZW5kQm91bmQgKTtcbiAgICB9XG4gIH0sIHRoaXMgKTtcbn07XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG4vKipcbiAqIGVtaXRzIGV2ZW50cyB2aWEgZXZlbnRFbWl0dGVyIGFuZCBqUXVlcnkgZXZlbnRzXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZSAtIG5hbWUgb2YgZXZlbnRcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50IC0gb3JpZ2luYWwgZXZlbnRcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBleHRyYSBhcmd1bWVudHNcbiAqL1xucHJvdG8uZGlzcGF0Y2hFdmVudCA9IGZ1bmN0aW9uKCB0eXBlLCBldmVudCwgYXJncyApIHtcbiAgdmFyIGVtaXRBcmdzID0gZXZlbnQgPyBbIGV2ZW50IF0uY29uY2F0KCBhcmdzICkgOiBhcmdzO1xuICB0aGlzLmVtaXRFdmVudCggdHlwZSwgZW1pdEFyZ3MgKTtcblxuICBpZiAoIGpRdWVyeSAmJiB0aGlzLiRlbGVtZW50ICkge1xuICAgIC8vIGRlZmF1bHQgdHJpZ2dlciB3aXRoIHR5cGUgaWYgbm8gZXZlbnRcbiAgICB0eXBlICs9IHRoaXMub3B0aW9ucy5uYW1lc3BhY2VKUXVlcnlFdmVudHMgPyAnLmZsaWNraXR5JyA6ICcnO1xuICAgIHZhciAkZXZlbnQgPSB0eXBlO1xuICAgIGlmICggZXZlbnQgKSB7XG4gICAgICAvLyBjcmVhdGUgalF1ZXJ5IGV2ZW50XG4gICAgICB2YXIgalFFdmVudCA9IGpRdWVyeS5FdmVudCggZXZlbnQgKTtcbiAgICAgIGpRRXZlbnQudHlwZSA9IHR5cGU7XG4gICAgICAkZXZlbnQgPSBqUUV2ZW50O1xuICAgIH1cbiAgICB0aGlzLiRlbGVtZW50LnRyaWdnZXIoICRldmVudCwgYXJncyApO1xuICB9XG59O1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBzZWxlY3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuLyoqXG4gKiBAcGFyYW0ge0ludGVnZXJ9IGluZGV4IC0gaW5kZXggb2YgdGhlIHNsaWRlXG4gKiBAcGFyYW0ge0Jvb2xlYW59IGlzV3JhcCAtIHdpbGwgd3JhcC1hcm91bmQgdG8gbGFzdC9maXJzdCBpZiBhdCB0aGUgZW5kXG4gKiBAcGFyYW0ge0Jvb2xlYW59IGlzSW5zdGFudCAtIHdpbGwgaW1tZWRpYXRlbHkgc2V0IHBvc2l0aW9uIGF0IHNlbGVjdGVkIGNlbGxcbiAqL1xucHJvdG8uc2VsZWN0ID0gZnVuY3Rpb24oIGluZGV4LCBpc1dyYXAsIGlzSW5zdGFudCApIHtcbiAgaWYgKCAhdGhpcy5pc0FjdGl2ZSApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaW5kZXggPSBwYXJzZUludCggaW5kZXgsIDEwICk7XG4gIHRoaXMuX3dyYXBTZWxlY3QoIGluZGV4ICk7XG5cbiAgaWYgKCB0aGlzLm9wdGlvbnMud3JhcEFyb3VuZCB8fCBpc1dyYXAgKSB7XG4gICAgaW5kZXggPSB1dGlscy5tb2R1bG8oIGluZGV4LCB0aGlzLnNsaWRlcy5sZW5ndGggKTtcbiAgfVxuICAvLyBiYWlsIGlmIGludmFsaWQgaW5kZXhcbiAgaWYgKCAhdGhpcy5zbGlkZXNbIGluZGV4IF0gKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHZhciBwcmV2SW5kZXggPSB0aGlzLnNlbGVjdGVkSW5kZXg7XG4gIHRoaXMuc2VsZWN0ZWRJbmRleCA9IGluZGV4O1xuICB0aGlzLnVwZGF0ZVNlbGVjdGVkU2xpZGUoKTtcbiAgaWYgKCBpc0luc3RhbnQgKSB7XG4gICAgdGhpcy5wb3NpdGlvblNsaWRlckF0U2VsZWN0ZWQoKTtcbiAgfSBlbHNlIHtcbiAgICB0aGlzLnN0YXJ0QW5pbWF0aW9uKCk7XG4gIH1cbiAgaWYgKCB0aGlzLm9wdGlvbnMuYWRhcHRpdmVIZWlnaHQgKSB7XG4gICAgdGhpcy5zZXRHYWxsZXJ5U2l6ZSgpO1xuICB9XG4gIC8vIGV2ZW50c1xuICB0aGlzLmRpc3BhdGNoRXZlbnQoICdzZWxlY3QnLCBudWxsLCBbIGluZGV4IF0gKTtcbiAgLy8gY2hhbmdlIGV2ZW50IGlmIG5ldyBpbmRleFxuICBpZiAoIGluZGV4ICE9IHByZXZJbmRleCApIHtcbiAgICB0aGlzLmRpc3BhdGNoRXZlbnQoICdjaGFuZ2UnLCBudWxsLCBbIGluZGV4IF0gKTtcbiAgfVxuICAvLyBvbGQgdjEgZXZlbnQgbmFtZSwgcmVtb3ZlIGluIHYzXG4gIHRoaXMuZGlzcGF0Y2hFdmVudCgnY2VsbFNlbGVjdCcpO1xufTtcblxuLy8gd3JhcHMgcG9zaXRpb24gZm9yIHdyYXBBcm91bmQsIHRvIG1vdmUgdG8gY2xvc2VzdCBzbGlkZS4gIzExM1xucHJvdG8uX3dyYXBTZWxlY3QgPSBmdW5jdGlvbiggaW5kZXggKSB7XG4gIHZhciBsZW4gPSB0aGlzLnNsaWRlcy5sZW5ndGg7XG4gIHZhciBpc1dyYXBwaW5nID0gdGhpcy5vcHRpb25zLndyYXBBcm91bmQgJiYgbGVuID4gMTtcbiAgaWYgKCAhaXNXcmFwcGluZyApIHtcbiAgICByZXR1cm4gaW5kZXg7XG4gIH1cbiAgdmFyIHdyYXBJbmRleCA9IHV0aWxzLm1vZHVsbyggaW5kZXgsIGxlbiApO1xuICAvLyBnbyB0byBzaG9ydGVzdFxuICB2YXIgZGVsdGEgPSBNYXRoLmFicyggd3JhcEluZGV4IC0gdGhpcy5zZWxlY3RlZEluZGV4ICk7XG4gIHZhciBiYWNrV3JhcERlbHRhID0gTWF0aC5hYnMoICggd3JhcEluZGV4ICsgbGVuICkgLSB0aGlzLnNlbGVjdGVkSW5kZXggKTtcbiAgdmFyIGZvcmV3YXJkV3JhcERlbHRhID0gTWF0aC5hYnMoICggd3JhcEluZGV4IC0gbGVuICkgLSB0aGlzLnNlbGVjdGVkSW5kZXggKTtcbiAgaWYgKCAhdGhpcy5pc0RyYWdTZWxlY3QgJiYgYmFja1dyYXBEZWx0YSA8IGRlbHRhICkge1xuICAgIGluZGV4ICs9IGxlbjtcbiAgfSBlbHNlIGlmICggIXRoaXMuaXNEcmFnU2VsZWN0ICYmIGZvcmV3YXJkV3JhcERlbHRhIDwgZGVsdGEgKSB7XG4gICAgaW5kZXggLT0gbGVuO1xuICB9XG4gIC8vIHdyYXAgcG9zaXRpb24gc28gc2xpZGVyIGlzIHdpdGhpbiBub3JtYWwgYXJlYVxuICBpZiAoIGluZGV4IDwgMCApIHtcbiAgICB0aGlzLnggLT0gdGhpcy5zbGlkZWFibGVXaWR0aDtcbiAgfSBlbHNlIGlmICggaW5kZXggPj0gbGVuICkge1xuICAgIHRoaXMueCArPSB0aGlzLnNsaWRlYWJsZVdpZHRoO1xuICB9XG59O1xuXG5wcm90by5wcmV2aW91cyA9IGZ1bmN0aW9uKCBpc1dyYXAsIGlzSW5zdGFudCApIHtcbiAgdGhpcy5zZWxlY3QoIHRoaXMuc2VsZWN0ZWRJbmRleCAtIDEsIGlzV3JhcCwgaXNJbnN0YW50ICk7XG59O1xuXG5wcm90by5uZXh0ID0gZnVuY3Rpb24oIGlzV3JhcCwgaXNJbnN0YW50ICkge1xuICB0aGlzLnNlbGVjdCggdGhpcy5zZWxlY3RlZEluZGV4ICsgMSwgaXNXcmFwLCBpc0luc3RhbnQgKTtcbn07XG5cbnByb3RvLnVwZGF0ZVNlbGVjdGVkU2xpZGUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIHNsaWRlID0gdGhpcy5zbGlkZXNbIHRoaXMuc2VsZWN0ZWRJbmRleCBdO1xuICAvLyBzZWxlY3RlZEluZGV4IGNvdWxkIGJlIG91dHNpZGUgb2Ygc2xpZGVzLCBpZiB0cmlnZ2VyZWQgYmVmb3JlIHJlc2l6ZSgpXG4gIGlmICggIXNsaWRlICkge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyB1bnNlbGVjdCBwcmV2aW91cyBzZWxlY3RlZCBzbGlkZVxuICB0aGlzLnVuc2VsZWN0U2VsZWN0ZWRTbGlkZSgpO1xuICAvLyB1cGRhdGUgbmV3IHNlbGVjdGVkIHNsaWRlXG4gIHRoaXMuc2VsZWN0ZWRTbGlkZSA9IHNsaWRlO1xuICBzbGlkZS5zZWxlY3QoKTtcbiAgdGhpcy5zZWxlY3RlZENlbGxzID0gc2xpZGUuY2VsbHM7XG4gIHRoaXMuc2VsZWN0ZWRFbGVtZW50cyA9IHNsaWRlLmdldENlbGxFbGVtZW50cygpO1xuICAvLyBIQUNLOiBzZWxlY3RlZENlbGwgJiBzZWxlY3RlZEVsZW1lbnQgaXMgZmlyc3QgY2VsbCBpbiBzbGlkZSwgYmFja3dhcmRzIGNvbXBhdGliaWxpdHlcbiAgLy8gUmVtb3ZlIGluIHYzP1xuICB0aGlzLnNlbGVjdGVkQ2VsbCA9IHNsaWRlLmNlbGxzWzBdO1xuICB0aGlzLnNlbGVjdGVkRWxlbWVudCA9IHRoaXMuc2VsZWN0ZWRFbGVtZW50c1swXTtcbn07XG5cbnByb3RvLnVuc2VsZWN0U2VsZWN0ZWRTbGlkZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoIHRoaXMuc2VsZWN0ZWRTbGlkZSApIHtcbiAgICB0aGlzLnNlbGVjdGVkU2xpZGUudW5zZWxlY3QoKTtcbiAgfVxufTtcblxucHJvdG8uc2VsZWN0SW5pdGlhbEluZGV4ID0gZnVuY3Rpb24oKSB7XG4gIHZhciBpbml0aWFsSW5kZXggPSB0aGlzLm9wdGlvbnMuaW5pdGlhbEluZGV4O1xuICAvLyBhbHJlYWR5IGFjdGl2YXRlZCwgc2VsZWN0IHByZXZpb3VzIHNlbGVjdGVkSW5kZXhcbiAgaWYgKCB0aGlzLmlzSW5pdEFjdGl2YXRlZCApIHtcbiAgICB0aGlzLnNlbGVjdCggdGhpcy5zZWxlY3RlZEluZGV4LCBmYWxzZSwgdHJ1ZSApO1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBzZWxlY3Qgd2l0aCBzZWxlY3RvciBzdHJpbmdcbiAgaWYgKCBpbml0aWFsSW5kZXggJiYgdHlwZW9mIGluaXRpYWxJbmRleCA9PSAnc3RyaW5nJyApIHtcbiAgICB2YXIgY2VsbCA9IHRoaXMucXVlcnlDZWxsKCBpbml0aWFsSW5kZXggKTtcbiAgICBpZiAoIGNlbGwgKSB7XG4gICAgICB0aGlzLnNlbGVjdENlbGwoIGluaXRpYWxJbmRleCwgZmFsc2UsIHRydWUgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gIH1cblxuICB2YXIgaW5kZXggPSAwO1xuICAvLyBzZWxlY3Qgd2l0aCBudW1iZXJcbiAgaWYgKCBpbml0aWFsSW5kZXggJiYgdGhpcy5zbGlkZXNbIGluaXRpYWxJbmRleCBdICkge1xuICAgIGluZGV4ID0gaW5pdGlhbEluZGV4O1xuICB9XG4gIC8vIHNlbGVjdCBpbnN0YW50bHlcbiAgdGhpcy5zZWxlY3QoIGluZGV4LCBmYWxzZSwgdHJ1ZSApO1xufTtcblxuLyoqXG4gKiBzZWxlY3Qgc2xpZGUgZnJvbSBudW1iZXIgb3IgY2VsbCBlbGVtZW50XG4gKiBAcGFyYW0ge0VsZW1lbnQgb3IgTnVtYmVyfSBlbGVtXG4gKi9cbnByb3RvLnNlbGVjdENlbGwgPSBmdW5jdGlvbiggdmFsdWUsIGlzV3JhcCwgaXNJbnN0YW50ICkge1xuICAvLyBnZXQgY2VsbFxuICB2YXIgY2VsbCA9IHRoaXMucXVlcnlDZWxsKCB2YWx1ZSApO1xuICBpZiAoICFjZWxsICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciBpbmRleCA9IHRoaXMuZ2V0Q2VsbFNsaWRlSW5kZXgoIGNlbGwgKTtcbiAgdGhpcy5zZWxlY3QoIGluZGV4LCBpc1dyYXAsIGlzSW5zdGFudCApO1xufTtcblxucHJvdG8uZ2V0Q2VsbFNsaWRlSW5kZXggPSBmdW5jdGlvbiggY2VsbCApIHtcbiAgLy8gZ2V0IGluZGV4IG9mIHNsaWRlcyB0aGF0IGhhcyBjZWxsXG4gIGZvciAoIHZhciBpPTA7IGkgPCB0aGlzLnNsaWRlcy5sZW5ndGg7IGkrKyApIHtcbiAgICB2YXIgc2xpZGUgPSB0aGlzLnNsaWRlc1tpXTtcbiAgICB2YXIgaW5kZXggPSBzbGlkZS5jZWxscy5pbmRleE9mKCBjZWxsICk7XG4gICAgaWYgKCBpbmRleCAhPSAtMSApIHtcbiAgICAgIHJldHVybiBpO1xuICAgIH1cbiAgfVxufTtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gZ2V0IGNlbGxzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbi8qKlxuICogZ2V0IEZsaWNraXR5LkNlbGwsIGdpdmVuIGFuIEVsZW1lbnRcbiAqIEBwYXJhbSB7RWxlbWVudH0gZWxlbVxuICogQHJldHVybnMge0ZsaWNraXR5LkNlbGx9IGl0ZW1cbiAqL1xucHJvdG8uZ2V0Q2VsbCA9IGZ1bmN0aW9uKCBlbGVtICkge1xuICAvLyBsb29wIHRocm91Z2ggY2VsbHMgdG8gZ2V0IHRoZSBvbmUgdGhhdCBtYXRjaGVzXG4gIGZvciAoIHZhciBpPTA7IGkgPCB0aGlzLmNlbGxzLmxlbmd0aDsgaSsrICkge1xuICAgIHZhciBjZWxsID0gdGhpcy5jZWxsc1tpXTtcbiAgICBpZiAoIGNlbGwuZWxlbWVudCA9PSBlbGVtICkge1xuICAgICAgcmV0dXJuIGNlbGw7XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIGdldCBjb2xsZWN0aW9uIG9mIEZsaWNraXR5LkNlbGxzLCBnaXZlbiBFbGVtZW50c1xuICogQHBhcmFtIHtFbGVtZW50LCBBcnJheSwgTm9kZUxpc3R9IGVsZW1zXG4gKiBAcmV0dXJucyB7QXJyYXl9IGNlbGxzIC0gRmxpY2tpdHkuQ2VsbHNcbiAqL1xucHJvdG8uZ2V0Q2VsbHMgPSBmdW5jdGlvbiggZWxlbXMgKSB7XG4gIGVsZW1zID0gdXRpbHMubWFrZUFycmF5KCBlbGVtcyApO1xuICB2YXIgY2VsbHMgPSBbXTtcbiAgZWxlbXMuZm9yRWFjaCggZnVuY3Rpb24oIGVsZW0gKSB7XG4gICAgdmFyIGNlbGwgPSB0aGlzLmdldENlbGwoIGVsZW0gKTtcbiAgICBpZiAoIGNlbGwgKSB7XG4gICAgICBjZWxscy5wdXNoKCBjZWxsICk7XG4gICAgfVxuICB9LCB0aGlzICk7XG4gIHJldHVybiBjZWxscztcbn07XG5cbi8qKlxuICogZ2V0IGNlbGwgZWxlbWVudHNcbiAqIEByZXR1cm5zIHtBcnJheX0gY2VsbEVsZW1zXG4gKi9cbnByb3RvLmdldENlbGxFbGVtZW50cyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5jZWxscy5tYXAoIGZ1bmN0aW9uKCBjZWxsICkge1xuICAgIHJldHVybiBjZWxsLmVsZW1lbnQ7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBnZXQgcGFyZW50IGNlbGwgZnJvbSBhbiBlbGVtZW50XG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsZW1cbiAqIEByZXR1cm5zIHtGbGlja2l0LkNlbGx9IGNlbGxcbiAqL1xucHJvdG8uZ2V0UGFyZW50Q2VsbCA9IGZ1bmN0aW9uKCBlbGVtICkge1xuICAvLyBmaXJzdCBjaGVjayBpZiBlbGVtIGlzIGNlbGxcbiAgdmFyIGNlbGwgPSB0aGlzLmdldENlbGwoIGVsZW0gKTtcbiAgaWYgKCBjZWxsICkge1xuICAgIHJldHVybiBjZWxsO1xuICB9XG4gIC8vIHRyeSB0byBnZXQgcGFyZW50IGNlbGwgZWxlbVxuICBlbGVtID0gdXRpbHMuZ2V0UGFyZW50KCBlbGVtLCAnLmZsaWNraXR5LXNsaWRlciA+IConICk7XG4gIHJldHVybiB0aGlzLmdldENlbGwoIGVsZW0gKTtcbn07XG5cbi8qKlxuICogZ2V0IGNlbGxzIGFkamFjZW50IHRvIGEgc2xpZGVcbiAqIEBwYXJhbSB7SW50ZWdlcn0gYWRqQ291bnQgLSBudW1iZXIgb2YgYWRqYWNlbnQgc2xpZGVzXG4gKiBAcGFyYW0ge0ludGVnZXJ9IGluZGV4IC0gaW5kZXggb2Ygc2xpZGUgdG8gc3RhcnRcbiAqIEByZXR1cm5zIHtBcnJheX0gY2VsbHMgLSBhcnJheSBvZiBGbGlja2l0eS5DZWxsc1xuICovXG5wcm90by5nZXRBZGphY2VudENlbGxFbGVtZW50cyA9IGZ1bmN0aW9uKCBhZGpDb3VudCwgaW5kZXggKSB7XG4gIGlmICggIWFkakNvdW50ICkge1xuICAgIHJldHVybiB0aGlzLnNlbGVjdGVkU2xpZGUuZ2V0Q2VsbEVsZW1lbnRzKCk7XG4gIH1cbiAgaW5kZXggPSBpbmRleCA9PT0gdW5kZWZpbmVkID8gdGhpcy5zZWxlY3RlZEluZGV4IDogaW5kZXg7XG5cbiAgdmFyIGxlbiA9IHRoaXMuc2xpZGVzLmxlbmd0aDtcbiAgaWYgKCAxICsgKCBhZGpDb3VudCAqIDIgKSA+PSBsZW4gKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0Q2VsbEVsZW1lbnRzKCk7XG4gIH1cblxuICB2YXIgY2VsbEVsZW1zID0gW107XG4gIGZvciAoIHZhciBpID0gaW5kZXggLSBhZGpDb3VudDsgaSA8PSBpbmRleCArIGFkakNvdW50IDsgaSsrICkge1xuICAgIHZhciBzbGlkZUluZGV4ID0gdGhpcy5vcHRpb25zLndyYXBBcm91bmQgPyB1dGlscy5tb2R1bG8oIGksIGxlbiApIDogaTtcbiAgICB2YXIgc2xpZGUgPSB0aGlzLnNsaWRlc1sgc2xpZGVJbmRleCBdO1xuICAgIGlmICggc2xpZGUgKSB7XG4gICAgICBjZWxsRWxlbXMgPSBjZWxsRWxlbXMuY29uY2F0KCBzbGlkZS5nZXRDZWxsRWxlbWVudHMoKSApO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2VsbEVsZW1zO1xufTtcblxuLyoqXG4gKiBzZWxlY3Qgc2xpZGUgZnJvbSBudW1iZXIgb3IgY2VsbCBlbGVtZW50XG4gKiBAcGFyYW0ge0VsZW1lbnQsIFNlbGVjdG9yIFN0cmluZywgb3IgTnVtYmVyfSBzZWxlY3RvclxuICovXG5wcm90by5xdWVyeUNlbGwgPSBmdW5jdGlvbiggc2VsZWN0b3IgKSB7XG4gIGlmICggdHlwZW9mIHNlbGVjdG9yID09ICdudW1iZXInICkge1xuICAgIC8vIHVzZSBudW1iZXIgYXMgaW5kZXhcbiAgICByZXR1cm4gdGhpcy5jZWxsc1sgc2VsZWN0b3IgXTtcbiAgfVxuICBpZiAoIHR5cGVvZiBzZWxlY3RvciA9PSAnc3RyaW5nJyApIHtcbiAgICAvLyBkbyBub3Qgc2VsZWN0IGludmFsaWQgc2VsZWN0b3JzIGZyb20gaGFzaDogIzEyMywgIy8uICM3OTFcbiAgICBpZiAoIHNlbGVjdG9yLm1hdGNoKC9eWyNcXC5dP1tcXGRcXC9dLykgKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIHVzZSBzdHJpbmcgYXMgc2VsZWN0b3IsIGdldCBlbGVtZW50XG4gICAgc2VsZWN0b3IgPSB0aGlzLmVsZW1lbnQucXVlcnlTZWxlY3Rvciggc2VsZWN0b3IgKTtcbiAgfVxuICAvLyBnZXQgY2VsbCBmcm9tIGVsZW1lbnRcbiAgcmV0dXJuIHRoaXMuZ2V0Q2VsbCggc2VsZWN0b3IgKTtcbn07XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGV2ZW50cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5wcm90by51aUNoYW5nZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmVtaXRFdmVudCgndWlDaGFuZ2UnKTtcbn07XG5cbi8vIGtlZXAgZm9jdXMgb24gZWxlbWVudCB3aGVuIGNoaWxkIFVJIGVsZW1lbnRzIGFyZSBjbGlja2VkXG5wcm90by5jaGlsZFVJUG9pbnRlckRvd24gPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIC8vIEhBQ0sgaU9TIGRvZXMgbm90IGFsbG93IHRvdWNoIGV2ZW50cyB0byBidWJibGUgdXA/IVxuICBpZiAoIGV2ZW50LnR5cGUgIT0gJ3RvdWNoc3RhcnQnICkge1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gIH1cbiAgdGhpcy5mb2N1cygpO1xufTtcblxuLy8gLS0tLS0gcmVzaXplIC0tLS0tIC8vXG5cbnByb3RvLm9ucmVzaXplID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMud2F0Y2hDU1MoKTtcbiAgdGhpcy5yZXNpemUoKTtcbn07XG5cbnV0aWxzLmRlYm91bmNlTWV0aG9kKCBGbGlja2l0eSwgJ29ucmVzaXplJywgMTUwICk7XG5cbnByb3RvLnJlc2l6ZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoICF0aGlzLmlzQWN0aXZlICkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLmdldFNpemUoKTtcbiAgLy8gd3JhcCB2YWx1ZXNcbiAgaWYgKCB0aGlzLm9wdGlvbnMud3JhcEFyb3VuZCApIHtcbiAgICB0aGlzLnggPSB1dGlscy5tb2R1bG8oIHRoaXMueCwgdGhpcy5zbGlkZWFibGVXaWR0aCApO1xuICB9XG4gIHRoaXMucG9zaXRpb25DZWxscygpO1xuICB0aGlzLl9nZXRXcmFwU2hpZnRDZWxscygpO1xuICB0aGlzLnNldEdhbGxlcnlTaXplKCk7XG4gIHRoaXMuZW1pdEV2ZW50KCdyZXNpemUnKTtcbiAgLy8gdXBkYXRlIHNlbGVjdGVkIGluZGV4IGZvciBncm91cCBzbGlkZXMsIGluc3RhbnRcbiAgLy8gVE9ETzogcG9zaXRpb24gY2FuIGJlIGxvc3QgYmV0d2VlbiBncm91cHMgb2YgdmFyaW91cyBudW1iZXJzXG4gIHZhciBzZWxlY3RlZEVsZW1lbnQgPSB0aGlzLnNlbGVjdGVkRWxlbWVudHMgJiYgdGhpcy5zZWxlY3RlZEVsZW1lbnRzWzBdO1xuICB0aGlzLnNlbGVjdENlbGwoIHNlbGVjdGVkRWxlbWVudCwgZmFsc2UsIHRydWUgKTtcbn07XG5cbi8vIHdhdGNoZXMgdGhlIDphZnRlciBwcm9wZXJ0eSwgYWN0aXZhdGVzL2RlYWN0aXZhdGVzXG5wcm90by53YXRjaENTUyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgd2F0Y2hPcHRpb24gPSB0aGlzLm9wdGlvbnMud2F0Y2hDU1M7XG4gIGlmICggIXdhdGNoT3B0aW9uICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciBhZnRlckNvbnRlbnQgPSBnZXRDb21wdXRlZFN0eWxlKCB0aGlzLmVsZW1lbnQsICc6YWZ0ZXInICkuY29udGVudDtcbiAgLy8gYWN0aXZhdGUgaWYgOmFmdGVyIHsgY29udGVudDogJ2ZsaWNraXR5JyB9XG4gIGlmICggYWZ0ZXJDb250ZW50LmluZGV4T2YoJ2ZsaWNraXR5JykgIT0gLTEgKSB7XG4gICAgdGhpcy5hY3RpdmF0ZSgpO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuZGVhY3RpdmF0ZSgpO1xuICB9XG59O1xuXG4vLyAtLS0tLSBrZXlkb3duIC0tLS0tIC8vXG5cbi8vIGdvIHByZXZpb3VzL25leHQgaWYgbGVmdC9yaWdodCBrZXlzIHByZXNzZWRcbnByb3RvLm9ua2V5ZG93biA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgLy8gb25seSB3b3JrIGlmIGVsZW1lbnQgaXMgaW4gZm9jdXNcbiAgdmFyIGlzTm90Rm9jdXNlZCA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgJiYgZG9jdW1lbnQuYWN0aXZlRWxlbWVudCAhPSB0aGlzLmVsZW1lbnQ7XG4gIGlmICggIXRoaXMub3B0aW9ucy5hY2Nlc3NpYmlsaXR5IHx8aXNOb3RGb2N1c2VkICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciBoYW5kbGVyID0gRmxpY2tpdHkua2V5Ym9hcmRIYW5kbGVyc1sgZXZlbnQua2V5Q29kZSBdO1xuICBpZiAoIGhhbmRsZXIgKSB7XG4gICAgaGFuZGxlci5jYWxsKCB0aGlzICk7XG4gIH1cbn07XG5cbkZsaWNraXR5LmtleWJvYXJkSGFuZGxlcnMgPSB7XG4gIC8vIGxlZnQgYXJyb3dcbiAgMzc6IGZ1bmN0aW9uKCkge1xuICAgIHZhciBsZWZ0TWV0aG9kID0gdGhpcy5vcHRpb25zLnJpZ2h0VG9MZWZ0ID8gJ25leHQnIDogJ3ByZXZpb3VzJztcbiAgICB0aGlzLnVpQ2hhbmdlKCk7XG4gICAgdGhpc1sgbGVmdE1ldGhvZCBdKCk7XG4gIH0sXG4gIC8vIHJpZ2h0IGFycm93XG4gIDM5OiBmdW5jdGlvbigpIHtcbiAgICB2YXIgcmlnaHRNZXRob2QgPSB0aGlzLm9wdGlvbnMucmlnaHRUb0xlZnQgPyAncHJldmlvdXMnIDogJ25leHQnO1xuICAgIHRoaXMudWlDaGFuZ2UoKTtcbiAgICB0aGlzWyByaWdodE1ldGhvZCBdKCk7XG4gIH0sXG59O1xuXG4vLyAtLS0tLSBmb2N1cyAtLS0tLSAvL1xuXG5wcm90by5mb2N1cyA9IGZ1bmN0aW9uKCkge1xuICAvLyBUT0RPIHJlbW92ZSBzY3JvbGxUbyBvbmNlIGZvY3VzIG9wdGlvbnMgZ2V0cyBtb3JlIHN1cHBvcnRcbiAgLy8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0hUTUxFbGVtZW50L2ZvY3VzI0Jyb3dzZXJfY29tcGF0aWJpbGl0eVxuICB2YXIgcHJldlNjcm9sbFkgPSB3aW5kb3cucGFnZVlPZmZzZXQ7XG4gIHRoaXMuZWxlbWVudC5mb2N1cyh7IHByZXZlbnRTY3JvbGw6IHRydWUgfSk7XG4gIC8vIGhhY2sgdG8gZml4IHNjcm9sbCBqdW1wIGFmdGVyIGZvY3VzLCAjNzZcbiAgaWYgKCB3aW5kb3cucGFnZVlPZmZzZXQgIT0gcHJldlNjcm9sbFkgKSB7XG4gICAgd2luZG93LnNjcm9sbFRvKCB3aW5kb3cucGFnZVhPZmZzZXQsIHByZXZTY3JvbGxZICk7XG4gIH1cbn07XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGRlc3Ryb3kgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuLy8gZGVhY3RpdmF0ZSBhbGwgRmxpY2tpdHkgZnVuY3Rpb25hbGl0eSwgYnV0IGtlZXAgc3R1ZmYgYXZhaWxhYmxlXG5wcm90by5kZWFjdGl2YXRlID0gZnVuY3Rpb24oKSB7XG4gIGlmICggIXRoaXMuaXNBY3RpdmUgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMuZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdmbGlja2l0eS1lbmFibGVkJyk7XG4gIHRoaXMuZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCdmbGlja2l0eS1ydGwnKTtcbiAgdGhpcy51bnNlbGVjdFNlbGVjdGVkU2xpZGUoKTtcbiAgLy8gZGVzdHJveSBjZWxsc1xuICB0aGlzLmNlbGxzLmZvckVhY2goIGZ1bmN0aW9uKCBjZWxsICkge1xuICAgIGNlbGwuZGVzdHJveSgpO1xuICB9KTtcbiAgdGhpcy5lbGVtZW50LnJlbW92ZUNoaWxkKCB0aGlzLnZpZXdwb3J0ICk7XG4gIC8vIG1vdmUgY2hpbGQgZWxlbWVudHMgYmFjayBpbnRvIGVsZW1lbnRcbiAgbW92ZUVsZW1lbnRzKCB0aGlzLnNsaWRlci5jaGlsZHJlbiwgdGhpcy5lbGVtZW50ICk7XG4gIGlmICggdGhpcy5vcHRpb25zLmFjY2Vzc2liaWxpdHkgKSB7XG4gICAgdGhpcy5lbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSgndGFiSW5kZXgnKTtcbiAgICB0aGlzLmVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2tleWRvd24nLCB0aGlzICk7XG4gIH1cbiAgLy8gc2V0IGZsYWdzXG4gIHRoaXMuaXNBY3RpdmUgPSBmYWxzZTtcbiAgdGhpcy5lbWl0RXZlbnQoJ2RlYWN0aXZhdGUnKTtcbn07XG5cbnByb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5kZWFjdGl2YXRlKCk7XG4gIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCAncmVzaXplJywgdGhpcyApO1xuICB0aGlzLmFsbE9mZigpO1xuICB0aGlzLmVtaXRFdmVudCgnZGVzdHJveScpO1xuICBpZiAoIGpRdWVyeSAmJiB0aGlzLiRlbGVtZW50ICkge1xuICAgIGpRdWVyeS5yZW1vdmVEYXRhKCB0aGlzLmVsZW1lbnQsICdmbGlja2l0eScgKTtcbiAgfVxuICBkZWxldGUgdGhpcy5lbGVtZW50LmZsaWNraXR5R1VJRDtcbiAgZGVsZXRlIGluc3RhbmNlc1sgdGhpcy5ndWlkIF07XG59O1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBwcm90b3R5cGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudXRpbHMuZXh0ZW5kKCBwcm90bywgYW5pbWF0ZVByb3RvdHlwZSApO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBleHRyYXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuLyoqXG4gKiBnZXQgRmxpY2tpdHkgaW5zdGFuY2UgZnJvbSBlbGVtZW50XG4gKiBAcGFyYW0ge0VsZW1lbnR9IGVsZW1cbiAqIEByZXR1cm5zIHtGbGlja2l0eX1cbiAqL1xuRmxpY2tpdHkuZGF0YSA9IGZ1bmN0aW9uKCBlbGVtICkge1xuICBlbGVtID0gdXRpbHMuZ2V0UXVlcnlFbGVtZW50KCBlbGVtICk7XG4gIHZhciBpZCA9IGVsZW0gJiYgZWxlbS5mbGlja2l0eUdVSUQ7XG4gIHJldHVybiBpZCAmJiBpbnN0YW5jZXNbIGlkIF07XG59O1xuXG51dGlscy5odG1sSW5pdCggRmxpY2tpdHksICdmbGlja2l0eScgKTtcblxuaWYgKCBqUXVlcnkgJiYgalF1ZXJ5LmJyaWRnZXQgKSB7XG4gIGpRdWVyeS5icmlkZ2V0KCAnZmxpY2tpdHknLCBGbGlja2l0eSApO1xufVxuXG4vLyBzZXQgaW50ZXJuYWwgalF1ZXJ5LCBmb3IgV2VicGFjayArIGpRdWVyeSB2MywgIzQ3OFxuRmxpY2tpdHkuc2V0SlF1ZXJ5ID0gZnVuY3Rpb24oIGpxICkge1xuICBqUXVlcnkgPSBqcTtcbn07XG5cbkZsaWNraXR5LkNlbGwgPSBDZWxsO1xuRmxpY2tpdHkuU2xpZGUgPSBTbGlkZTtcblxucmV0dXJuIEZsaWNraXR5O1xuXG59KSk7XG4iLCIvKiFcbiAqIEZsaWNraXR5IHYyLjIuMVxuICogVG91Y2gsIHJlc3BvbnNpdmUsIGZsaWNrYWJsZSBjYXJvdXNlbHNcbiAqXG4gKiBMaWNlbnNlZCBHUEx2MyBmb3Igb3BlbiBzb3VyY2UgdXNlXG4gKiBvciBGbGlja2l0eSBDb21tZXJjaWFsIExpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlXG4gKlxuICogaHR0cHM6Ly9mbGlja2l0eS5tZXRhZml6enkuY29cbiAqIENvcHlyaWdodCAyMDE1LTIwMTkgTWV0YWZpenp5XG4gKi9cblxuKCBmdW5jdGlvbiggd2luZG93LCBmYWN0b3J5ICkge1xuICAvLyB1bml2ZXJzYWwgbW9kdWxlIGRlZmluaXRpb25cbiAgLyoganNoaW50IHN0cmljdDogZmFsc2UgKi9cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICcuL2ZsaWNraXR5JyxcbiAgICAgICcuL2RyYWcnLFxuICAgICAgJy4vcHJldi1uZXh0LWJ1dHRvbicsXG4gICAgICAnLi9wYWdlLWRvdHMnLFxuICAgICAgJy4vcGxheWVyJyxcbiAgICAgICcuL2FkZC1yZW1vdmUtY2VsbCcsXG4gICAgICAnLi9sYXp5bG9hZCdcbiAgICBdLCBmYWN0b3J5ICk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICByZXF1aXJlKCcuL2ZsaWNraXR5JyksXG4gICAgICByZXF1aXJlKCcuL2RyYWcnKSxcbiAgICAgIHJlcXVpcmUoJy4vcHJldi1uZXh0LWJ1dHRvbicpLFxuICAgICAgcmVxdWlyZSgnLi9wYWdlLWRvdHMnKSxcbiAgICAgIHJlcXVpcmUoJy4vcGxheWVyJyksXG4gICAgICByZXF1aXJlKCcuL2FkZC1yZW1vdmUtY2VsbCcpLFxuICAgICAgcmVxdWlyZSgnLi9sYXp5bG9hZCcpXG4gICAgKTtcbiAgfVxuXG59KSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCBGbGlja2l0eSApIHtcbiAgLypqc2hpbnQgc3RyaWN0OiBmYWxzZSovXG4gIHJldHVybiBGbGlja2l0eTtcbn0pO1xuIiwiLy8gbGF6eWxvYWRcbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnLi9mbGlja2l0eScsXG4gICAgICAnZml6enktdWktdXRpbHMvdXRpbHMnXG4gICAgXSwgZnVuY3Rpb24oIEZsaWNraXR5LCB1dGlscyApIHtcbiAgICAgIHJldHVybiBmYWN0b3J5KCB3aW5kb3csIEZsaWNraXR5LCB1dGlscyApO1xuICAgIH0pO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgcmVxdWlyZSgnLi9mbGlja2l0eScpLFxuICAgICAgcmVxdWlyZSgnZml6enktdWktdXRpbHMnKVxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgLy8gYnJvd3NlciBnbG9iYWxcbiAgICBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgd2luZG93LkZsaWNraXR5LFxuICAgICAgd2luZG93LmZpenp5VUlVdGlsc1xuICAgICk7XG4gIH1cblxufSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCB3aW5kb3csIEZsaWNraXR5LCB1dGlscyApIHtcbid1c2Ugc3RyaWN0JztcblxuRmxpY2tpdHkuY3JlYXRlTWV0aG9kcy5wdXNoKCdfY3JlYXRlTGF6eWxvYWQnKTtcbnZhciBwcm90byA9IEZsaWNraXR5LnByb3RvdHlwZTtcblxucHJvdG8uX2NyZWF0ZUxhenlsb2FkID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMub24oICdzZWxlY3QnLCB0aGlzLmxhenlMb2FkICk7XG59O1xuXG5wcm90by5sYXp5TG9hZCA9IGZ1bmN0aW9uKCkge1xuICB2YXIgbGF6eUxvYWQgPSB0aGlzLm9wdGlvbnMubGF6eUxvYWQ7XG4gIGlmICggIWxhenlMb2FkICkge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBnZXQgYWRqYWNlbnQgY2VsbHMsIHVzZSBsYXp5TG9hZCBvcHRpb24gZm9yIGFkamFjZW50IGNvdW50XG4gIHZhciBhZGpDb3VudCA9IHR5cGVvZiBsYXp5TG9hZCA9PSAnbnVtYmVyJyA/IGxhenlMb2FkIDogMDtcbiAgdmFyIGNlbGxFbGVtcyA9IHRoaXMuZ2V0QWRqYWNlbnRDZWxsRWxlbWVudHMoIGFkakNvdW50ICk7XG4gIC8vIGdldCBsYXp5IGltYWdlcyBpbiB0aG9zZSBjZWxsc1xuICB2YXIgbGF6eUltYWdlcyA9IFtdO1xuICBjZWxsRWxlbXMuZm9yRWFjaCggZnVuY3Rpb24oIGNlbGxFbGVtICkge1xuICAgIHZhciBsYXp5Q2VsbEltYWdlcyA9IGdldENlbGxMYXp5SW1hZ2VzKCBjZWxsRWxlbSApO1xuICAgIGxhenlJbWFnZXMgPSBsYXp5SW1hZ2VzLmNvbmNhdCggbGF6eUNlbGxJbWFnZXMgKTtcbiAgfSk7XG4gIC8vIGxvYWQgbGF6eSBpbWFnZXNcbiAgbGF6eUltYWdlcy5mb3JFYWNoKCBmdW5jdGlvbiggaW1nICkge1xuICAgIG5ldyBMYXp5TG9hZGVyKCBpbWcsIHRoaXMgKTtcbiAgfSwgdGhpcyApO1xufTtcblxuZnVuY3Rpb24gZ2V0Q2VsbExhenlJbWFnZXMoIGNlbGxFbGVtICkge1xuICAvLyBjaGVjayBpZiBjZWxsIGVsZW1lbnQgaXMgbGF6eSBpbWFnZVxuICBpZiAoIGNlbGxFbGVtLm5vZGVOYW1lID09ICdJTUcnICkge1xuICAgIHZhciBsYXp5bG9hZEF0dHIgPSBjZWxsRWxlbS5nZXRBdHRyaWJ1dGUoJ2RhdGEtZmxpY2tpdHktbGF6eWxvYWQnKTtcbiAgICB2YXIgc3JjQXR0ciA9IGNlbGxFbGVtLmdldEF0dHJpYnV0ZSgnZGF0YS1mbGlja2l0eS1sYXp5bG9hZC1zcmMnKTtcbiAgICB2YXIgc3Jjc2V0QXR0ciA9IGNlbGxFbGVtLmdldEF0dHJpYnV0ZSgnZGF0YS1mbGlja2l0eS1sYXp5bG9hZC1zcmNzZXQnKTtcbiAgICBpZiAoIGxhenlsb2FkQXR0ciB8fCBzcmNBdHRyIHx8IHNyY3NldEF0dHIgKSB7XG4gICAgICByZXR1cm4gWyBjZWxsRWxlbSBdO1xuICAgIH1cbiAgfVxuICAvLyBzZWxlY3QgbGF6eSBpbWFnZXMgaW4gY2VsbFxuICB2YXIgbGF6eVNlbGVjdG9yID0gJ2ltZ1tkYXRhLWZsaWNraXR5LWxhenlsb2FkXSwgJyArXG4gICAgJ2ltZ1tkYXRhLWZsaWNraXR5LWxhenlsb2FkLXNyY10sIGltZ1tkYXRhLWZsaWNraXR5LWxhenlsb2FkLXNyY3NldF0nO1xuICB2YXIgaW1ncyA9IGNlbGxFbGVtLnF1ZXJ5U2VsZWN0b3JBbGwoIGxhenlTZWxlY3RvciApO1xuICByZXR1cm4gdXRpbHMubWFrZUFycmF5KCBpbWdzICk7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIExhenlMb2FkZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuLyoqXG4gKiBjbGFzcyB0byBoYW5kbGUgbG9hZGluZyBpbWFnZXNcbiAqL1xuZnVuY3Rpb24gTGF6eUxvYWRlciggaW1nLCBmbGlja2l0eSApIHtcbiAgdGhpcy5pbWcgPSBpbWc7XG4gIHRoaXMuZmxpY2tpdHkgPSBmbGlja2l0eTtcbiAgdGhpcy5sb2FkKCk7XG59XG5cbkxhenlMb2FkZXIucHJvdG90eXBlLmhhbmRsZUV2ZW50ID0gdXRpbHMuaGFuZGxlRXZlbnQ7XG5cbkxhenlMb2FkZXIucHJvdG90eXBlLmxvYWQgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5pbWcuYWRkRXZlbnRMaXN0ZW5lciggJ2xvYWQnLCB0aGlzICk7XG4gIHRoaXMuaW1nLmFkZEV2ZW50TGlzdGVuZXIoICdlcnJvcicsIHRoaXMgKTtcbiAgLy8gZ2V0IHNyYyAmIHNyY3NldFxuICB2YXIgc3JjID0gdGhpcy5pbWcuZ2V0QXR0cmlidXRlKCdkYXRhLWZsaWNraXR5LWxhenlsb2FkJykgfHxcbiAgICB0aGlzLmltZy5nZXRBdHRyaWJ1dGUoJ2RhdGEtZmxpY2tpdHktbGF6eWxvYWQtc3JjJyk7XG4gIHZhciBzcmNzZXQgPSB0aGlzLmltZy5nZXRBdHRyaWJ1dGUoJ2RhdGEtZmxpY2tpdHktbGF6eWxvYWQtc3Jjc2V0Jyk7XG4gIC8vIHNldCBzcmMgJiBzZXJzZXRcbiAgdGhpcy5pbWcuc3JjID0gc3JjO1xuICBpZiAoIHNyY3NldCApIHtcbiAgICB0aGlzLmltZy5zZXRBdHRyaWJ1dGUoICdzcmNzZXQnLCBzcmNzZXQgKTtcbiAgfVxuICAvLyByZW1vdmUgYXR0clxuICB0aGlzLmltZy5yZW1vdmVBdHRyaWJ1dGUoJ2RhdGEtZmxpY2tpdHktbGF6eWxvYWQnKTtcbiAgdGhpcy5pbWcucmVtb3ZlQXR0cmlidXRlKCdkYXRhLWZsaWNraXR5LWxhenlsb2FkLXNyYycpO1xuICB0aGlzLmltZy5yZW1vdmVBdHRyaWJ1dGUoJ2RhdGEtZmxpY2tpdHktbGF6eWxvYWQtc3Jjc2V0Jyk7XG59O1xuXG5MYXp5TG9hZGVyLnByb3RvdHlwZS5vbmxvYWQgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIHRoaXMuY29tcGxldGUoIGV2ZW50LCAnZmxpY2tpdHktbGF6eWxvYWRlZCcgKTtcbn07XG5cbkxhenlMb2FkZXIucHJvdG90eXBlLm9uZXJyb3IgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIHRoaXMuY29tcGxldGUoIGV2ZW50LCAnZmxpY2tpdHktbGF6eWVycm9yJyApO1xufTtcblxuTGF6eUxvYWRlci5wcm90b3R5cGUuY29tcGxldGUgPSBmdW5jdGlvbiggZXZlbnQsIGNsYXNzTmFtZSApIHtcbiAgLy8gdW5iaW5kIGV2ZW50c1xuICB0aGlzLmltZy5yZW1vdmVFdmVudExpc3RlbmVyKCAnbG9hZCcsIHRoaXMgKTtcbiAgdGhpcy5pbWcucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2Vycm9yJywgdGhpcyApO1xuXG4gIHZhciBjZWxsID0gdGhpcy5mbGlja2l0eS5nZXRQYXJlbnRDZWxsKCB0aGlzLmltZyApO1xuICB2YXIgY2VsbEVsZW0gPSBjZWxsICYmIGNlbGwuZWxlbWVudDtcbiAgdGhpcy5mbGlja2l0eS5jZWxsU2l6ZUNoYW5nZSggY2VsbEVsZW0gKTtcblxuICB0aGlzLmltZy5jbGFzc0xpc3QuYWRkKCBjbGFzc05hbWUgKTtcbiAgdGhpcy5mbGlja2l0eS5kaXNwYXRjaEV2ZW50KCAnbGF6eUxvYWQnLCBldmVudCwgY2VsbEVsZW0gKTtcbn07XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG5GbGlja2l0eS5MYXp5TG9hZGVyID0gTGF6eUxvYWRlcjtcblxucmV0dXJuIEZsaWNraXR5O1xuXG59KSk7XG4iLCIvLyBwYWdlIGRvdHNcbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnLi9mbGlja2l0eScsXG4gICAgICAndW5pcG9pbnRlci91bmlwb2ludGVyJyxcbiAgICAgICdmaXp6eS11aS11dGlscy91dGlscydcbiAgICBdLCBmdW5jdGlvbiggRmxpY2tpdHksIFVuaXBvaW50ZXIsIHV0aWxzICkge1xuICAgICAgcmV0dXJuIGZhY3RvcnkoIHdpbmRvdywgRmxpY2tpdHksIFVuaXBvaW50ZXIsIHV0aWxzICk7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICByZXF1aXJlKCcuL2ZsaWNraXR5JyksXG4gICAgICByZXF1aXJlKCd1bmlwb2ludGVyJyksXG4gICAgICByZXF1aXJlKCdmaXp6eS11aS11dGlscycpXG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICB3aW5kb3cuRmxpY2tpdHksXG4gICAgICB3aW5kb3cuVW5pcG9pbnRlcixcbiAgICAgIHdpbmRvdy5maXp6eVVJVXRpbHNcbiAgICApO1xuICB9XG5cbn0oIHdpbmRvdywgZnVuY3Rpb24gZmFjdG9yeSggd2luZG93LCBGbGlja2l0eSwgVW5pcG9pbnRlciwgdXRpbHMgKSB7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFBhZ2VEb3RzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbid1c2Ugc3RyaWN0JztcblxuZnVuY3Rpb24gUGFnZURvdHMoIHBhcmVudCApIHtcbiAgdGhpcy5wYXJlbnQgPSBwYXJlbnQ7XG4gIHRoaXMuX2NyZWF0ZSgpO1xufVxuXG5QYWdlRG90cy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBVbmlwb2ludGVyLnByb3RvdHlwZSApO1xuXG5QYWdlRG90cy5wcm90b3R5cGUuX2NyZWF0ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBjcmVhdGUgaG9sZGVyIGVsZW1lbnRcbiAgdGhpcy5ob2xkZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdvbCcpO1xuICB0aGlzLmhvbGRlci5jbGFzc05hbWUgPSAnZmxpY2tpdHktcGFnZS1kb3RzJztcbiAgLy8gY3JlYXRlIGRvdHMsIGFycmF5IG9mIGVsZW1lbnRzXG4gIHRoaXMuZG90cyA9IFtdO1xuICAvLyBldmVudHNcbiAgdGhpcy5oYW5kbGVDbGljayA9IHRoaXMub25DbGljay5iaW5kKCB0aGlzICk7XG4gIHRoaXMub24oICdwb2ludGVyRG93bicsIHRoaXMucGFyZW50LmNoaWxkVUlQb2ludGVyRG93bi5iaW5kKCB0aGlzLnBhcmVudCApICk7XG59O1xuXG5QYWdlRG90cy5wcm90b3R5cGUuYWN0aXZhdGUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5zZXREb3RzKCk7XG4gIHRoaXMuaG9sZGVyLmFkZEV2ZW50TGlzdGVuZXIoICdjbGljaycsIHRoaXMuaGFuZGxlQ2xpY2sgKTtcbiAgdGhpcy5iaW5kU3RhcnRFdmVudCggdGhpcy5ob2xkZXIgKTtcbiAgLy8gYWRkIHRvIERPTVxuICB0aGlzLnBhcmVudC5lbGVtZW50LmFwcGVuZENoaWxkKCB0aGlzLmhvbGRlciApO1xufTtcblxuUGFnZURvdHMucHJvdG90eXBlLmRlYWN0aXZhdGUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5ob2xkZXIucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcy5oYW5kbGVDbGljayApO1xuICB0aGlzLnVuYmluZFN0YXJ0RXZlbnQoIHRoaXMuaG9sZGVyICk7XG4gIC8vIHJlbW92ZSBmcm9tIERPTVxuICB0aGlzLnBhcmVudC5lbGVtZW50LnJlbW92ZUNoaWxkKCB0aGlzLmhvbGRlciApO1xufTtcblxuUGFnZURvdHMucHJvdG90eXBlLnNldERvdHMgPSBmdW5jdGlvbigpIHtcbiAgLy8gZ2V0IGRpZmZlcmVuY2UgYmV0d2VlbiBudW1iZXIgb2Ygc2xpZGVzIGFuZCBudW1iZXIgb2YgZG90c1xuICB2YXIgZGVsdGEgPSB0aGlzLnBhcmVudC5zbGlkZXMubGVuZ3RoIC0gdGhpcy5kb3RzLmxlbmd0aDtcbiAgaWYgKCBkZWx0YSA+IDAgKSB7XG4gICAgdGhpcy5hZGREb3RzKCBkZWx0YSApO1xuICB9IGVsc2UgaWYgKCBkZWx0YSA8IDAgKSB7XG4gICAgdGhpcy5yZW1vdmVEb3RzKCAtZGVsdGEgKTtcbiAgfVxufTtcblxuUGFnZURvdHMucHJvdG90eXBlLmFkZERvdHMgPSBmdW5jdGlvbiggY291bnQgKSB7XG4gIHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtcbiAgdmFyIG5ld0RvdHMgPSBbXTtcbiAgdmFyIGxlbmd0aCA9IHRoaXMuZG90cy5sZW5ndGg7XG4gIHZhciBtYXggPSBsZW5ndGggKyBjb3VudDtcblxuICBmb3IgKCB2YXIgaSA9IGxlbmd0aDsgaSA8IG1heDsgaSsrICkge1xuICAgIHZhciBkb3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpO1xuICAgIGRvdC5jbGFzc05hbWUgPSAnZG90JztcbiAgICBkb3Quc2V0QXR0cmlidXRlKCAnYXJpYS1sYWJlbCcsICdQYWdlIGRvdCAnICsgKCBpICsgMSApICk7XG4gICAgZnJhZ21lbnQuYXBwZW5kQ2hpbGQoIGRvdCApO1xuICAgIG5ld0RvdHMucHVzaCggZG90ICk7XG4gIH1cblxuICB0aGlzLmhvbGRlci5hcHBlbmRDaGlsZCggZnJhZ21lbnQgKTtcbiAgdGhpcy5kb3RzID0gdGhpcy5kb3RzLmNvbmNhdCggbmV3RG90cyApO1xufTtcblxuUGFnZURvdHMucHJvdG90eXBlLnJlbW92ZURvdHMgPSBmdW5jdGlvbiggY291bnQgKSB7XG4gIC8vIHJlbW92ZSBmcm9tIHRoaXMuZG90cyBjb2xsZWN0aW9uXG4gIHZhciByZW1vdmVEb3RzID0gdGhpcy5kb3RzLnNwbGljZSggdGhpcy5kb3RzLmxlbmd0aCAtIGNvdW50LCBjb3VudCApO1xuICAvLyByZW1vdmUgZnJvbSBET01cbiAgcmVtb3ZlRG90cy5mb3JFYWNoKCBmdW5jdGlvbiggZG90ICkge1xuICAgIHRoaXMuaG9sZGVyLnJlbW92ZUNoaWxkKCBkb3QgKTtcbiAgfSwgdGhpcyApO1xufTtcblxuUGFnZURvdHMucHJvdG90eXBlLnVwZGF0ZVNlbGVjdGVkID0gZnVuY3Rpb24oKSB7XG4gIC8vIHJlbW92ZSBzZWxlY3RlZCBjbGFzcyBvbiBwcmV2aW91c1xuICBpZiAoIHRoaXMuc2VsZWN0ZWREb3QgKSB7XG4gICAgdGhpcy5zZWxlY3RlZERvdC5jbGFzc05hbWUgPSAnZG90JztcbiAgICB0aGlzLnNlbGVjdGVkRG90LnJlbW92ZUF0dHJpYnV0ZSgnYXJpYS1jdXJyZW50Jyk7XG4gIH1cbiAgLy8gZG9uJ3QgcHJvY2VlZCBpZiBubyBkb3RzXG4gIGlmICggIXRoaXMuZG90cy5sZW5ndGggKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMuc2VsZWN0ZWREb3QgPSB0aGlzLmRvdHNbIHRoaXMucGFyZW50LnNlbGVjdGVkSW5kZXggXTtcbiAgdGhpcy5zZWxlY3RlZERvdC5jbGFzc05hbWUgPSAnZG90IGlzLXNlbGVjdGVkJztcbiAgdGhpcy5zZWxlY3RlZERvdC5zZXRBdHRyaWJ1dGUoICdhcmlhLWN1cnJlbnQnLCAnc3RlcCcgKTtcbn07XG5cblBhZ2VEb3RzLnByb3RvdHlwZS5vblRhcCA9IC8vIG9sZCBtZXRob2QgbmFtZSwgYmFja3dhcmRzLWNvbXBhdGlibGVcblBhZ2VEb3RzLnByb3RvdHlwZS5vbkNsaWNrID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICB2YXIgdGFyZ2V0ID0gZXZlbnQudGFyZ2V0O1xuICAvLyBvbmx5IGNhcmUgYWJvdXQgZG90IGNsaWNrc1xuICBpZiAoIHRhcmdldC5ub2RlTmFtZSAhPSAnTEknICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMucGFyZW50LnVpQ2hhbmdlKCk7XG4gIHZhciBpbmRleCA9IHRoaXMuZG90cy5pbmRleE9mKCB0YXJnZXQgKTtcbiAgdGhpcy5wYXJlbnQuc2VsZWN0KCBpbmRleCApO1xufTtcblxuUGFnZURvdHMucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5kZWFjdGl2YXRlKCk7XG4gIHRoaXMuYWxsT2ZmKCk7XG59O1xuXG5GbGlja2l0eS5QYWdlRG90cyA9IFBhZ2VEb3RzO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBGbGlja2l0eSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG51dGlscy5leHRlbmQoIEZsaWNraXR5LmRlZmF1bHRzLCB7XG4gIHBhZ2VEb3RzOiB0cnVlXG59KTtcblxuRmxpY2tpdHkuY3JlYXRlTWV0aG9kcy5wdXNoKCdfY3JlYXRlUGFnZURvdHMnKTtcblxudmFyIHByb3RvID0gRmxpY2tpdHkucHJvdG90eXBlO1xuXG5wcm90by5fY3JlYXRlUGFnZURvdHMgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCAhdGhpcy5vcHRpb25zLnBhZ2VEb3RzICkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLnBhZ2VEb3RzID0gbmV3IFBhZ2VEb3RzKCB0aGlzICk7XG4gIC8vIGV2ZW50c1xuICB0aGlzLm9uKCAnYWN0aXZhdGUnLCB0aGlzLmFjdGl2YXRlUGFnZURvdHMgKTtcbiAgdGhpcy5vbiggJ3NlbGVjdCcsIHRoaXMudXBkYXRlU2VsZWN0ZWRQYWdlRG90cyApO1xuICB0aGlzLm9uKCAnY2VsbENoYW5nZScsIHRoaXMudXBkYXRlUGFnZURvdHMgKTtcbiAgdGhpcy5vbiggJ3Jlc2l6ZScsIHRoaXMudXBkYXRlUGFnZURvdHMgKTtcbiAgdGhpcy5vbiggJ2RlYWN0aXZhdGUnLCB0aGlzLmRlYWN0aXZhdGVQYWdlRG90cyApO1xufTtcblxucHJvdG8uYWN0aXZhdGVQYWdlRG90cyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBhZ2VEb3RzLmFjdGl2YXRlKCk7XG59O1xuXG5wcm90by51cGRhdGVTZWxlY3RlZFBhZ2VEb3RzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGFnZURvdHMudXBkYXRlU2VsZWN0ZWQoKTtcbn07XG5cbnByb3RvLnVwZGF0ZVBhZ2VEb3RzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGFnZURvdHMuc2V0RG90cygpO1xufTtcblxucHJvdG8uZGVhY3RpdmF0ZVBhZ2VEb3RzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGFnZURvdHMuZGVhY3RpdmF0ZSgpO1xufTtcblxuLy8gLS0tLS0gIC0tLS0tIC8vXG5cbkZsaWNraXR5LlBhZ2VEb3RzID0gUGFnZURvdHM7XG5cbnJldHVybiBGbGlja2l0eTtcblxufSkpO1xuIiwiLy8gcGxheWVyICYgYXV0b1BsYXlcbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qIGpzaGludCBzdHJpY3Q6IGZhbHNlICovXG4gIGlmICggdHlwZW9mIGRlZmluZSA9PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG4gICAgLy8gQU1EXG4gICAgZGVmaW5lKCBbXG4gICAgICAnZXYtZW1pdHRlci9ldi1lbWl0dGVyJyxcbiAgICAgICdmaXp6eS11aS11dGlscy91dGlscycsXG4gICAgICAnLi9mbGlja2l0eSdcbiAgICBdLCBmdW5jdGlvbiggRXZFbWl0dGVyLCB1dGlscywgRmxpY2tpdHkgKSB7XG4gICAgICByZXR1cm4gZmFjdG9yeSggRXZFbWl0dGVyLCB1dGlscywgRmxpY2tpdHkgKTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmICggdHlwZW9mIG1vZHVsZSA9PSAnb2JqZWN0JyAmJiBtb2R1bGUuZXhwb3J0cyApIHtcbiAgICAvLyBDb21tb25KU1xuICAgIG1vZHVsZS5leHBvcnRzID0gZmFjdG9yeShcbiAgICAgIHJlcXVpcmUoJ2V2LWVtaXR0ZXInKSxcbiAgICAgIHJlcXVpcmUoJ2Zpenp5LXVpLXV0aWxzJyksXG4gICAgICByZXF1aXJlKCcuL2ZsaWNraXR5JylcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIC8vIGJyb3dzZXIgZ2xvYmFsXG4gICAgZmFjdG9yeShcbiAgICAgIHdpbmRvdy5FdkVtaXR0ZXIsXG4gICAgICB3aW5kb3cuZml6enlVSVV0aWxzLFxuICAgICAgd2luZG93LkZsaWNraXR5XG4gICAgKTtcbiAgfVxuXG59KCB3aW5kb3csIGZ1bmN0aW9uIGZhY3RvcnkoIEV2RW1pdHRlciwgdXRpbHMsIEZsaWNraXR5ICkge1xuXG4ndXNlIHN0cmljdCc7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFBsYXllciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5mdW5jdGlvbiBQbGF5ZXIoIHBhcmVudCApIHtcbiAgdGhpcy5wYXJlbnQgPSBwYXJlbnQ7XG4gIHRoaXMuc3RhdGUgPSAnc3RvcHBlZCc7XG4gIC8vIHZpc2liaWxpdHkgY2hhbmdlIGV2ZW50IGhhbmRsZXJcbiAgdGhpcy5vblZpc2liaWxpdHlDaGFuZ2UgPSB0aGlzLnZpc2liaWxpdHlDaGFuZ2UuYmluZCggdGhpcyApO1xuICB0aGlzLm9uVmlzaWJpbGl0eVBsYXkgPSB0aGlzLnZpc2liaWxpdHlQbGF5LmJpbmQoIHRoaXMgKTtcbn1cblxuUGxheWVyLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoIEV2RW1pdHRlci5wcm90b3R5cGUgKTtcblxuLy8gc3RhcnQgcGxheVxuUGxheWVyLnByb3RvdHlwZS5wbGF5ID0gZnVuY3Rpb24oKSB7XG4gIGlmICggdGhpcy5zdGF0ZSA9PSAncGxheWluZycgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIGRvIG5vdCBwbGF5IGlmIHBhZ2UgaXMgaGlkZGVuLCBzdGFydCBwbGF5aW5nIHdoZW4gcGFnZSBpcyB2aXNpYmxlXG4gIHZhciBpc1BhZ2VIaWRkZW4gPSBkb2N1bWVudC5oaWRkZW47XG4gIGlmICggaXNQYWdlSGlkZGVuICkge1xuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICd2aXNpYmlsaXR5Y2hhbmdlJywgdGhpcy5vblZpc2liaWxpdHlQbGF5ICk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdGhpcy5zdGF0ZSA9ICdwbGF5aW5nJztcbiAgLy8gbGlzdGVuIHRvIHZpc2liaWxpdHkgY2hhbmdlXG4gIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICd2aXNpYmlsaXR5Y2hhbmdlJywgdGhpcy5vblZpc2liaWxpdHlDaGFuZ2UgKTtcbiAgLy8gc3RhcnQgdGlja2luZ1xuICB0aGlzLnRpY2soKTtcbn07XG5cblBsYXllci5wcm90b3R5cGUudGljayA9IGZ1bmN0aW9uKCkge1xuICAvLyBkbyBub3QgdGljayBpZiBub3QgcGxheWluZ1xuICBpZiAoIHRoaXMuc3RhdGUgIT0gJ3BsYXlpbmcnICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciB0aW1lID0gdGhpcy5wYXJlbnQub3B0aW9ucy5hdXRvUGxheTtcbiAgLy8gZGVmYXVsdCB0byAzIHNlY29uZHNcbiAgdGltZSA9IHR5cGVvZiB0aW1lID09ICdudW1iZXInID8gdGltZSA6IDMwMDA7XG4gIHZhciBfdGhpcyA9IHRoaXM7XG4gIC8vIEhBQ0s6IHJlc2V0IHRpY2tzIGlmIHN0b3BwZWQgYW5kIHN0YXJ0ZWQgd2l0aGluIGludGVydmFsXG4gIHRoaXMuY2xlYXIoKTtcbiAgdGhpcy50aW1lb3V0ID0gc2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG4gICAgX3RoaXMucGFyZW50Lm5leHQoIHRydWUgKTtcbiAgICBfdGhpcy50aWNrKCk7XG4gIH0sIHRpbWUgKTtcbn07XG5cblBsYXllci5wcm90b3R5cGUuc3RvcCA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnN0YXRlID0gJ3N0b3BwZWQnO1xuICB0aGlzLmNsZWFyKCk7XG4gIC8vIHJlbW92ZSB2aXNpYmlsaXR5IGNoYW5nZSBldmVudFxuICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAndmlzaWJpbGl0eWNoYW5nZScsIHRoaXMub25WaXNpYmlsaXR5Q2hhbmdlICk7XG59O1xuXG5QbGF5ZXIucHJvdG90eXBlLmNsZWFyID0gZnVuY3Rpb24oKSB7XG4gIGNsZWFyVGltZW91dCggdGhpcy50aW1lb3V0ICk7XG59O1xuXG5QbGF5ZXIucHJvdG90eXBlLnBhdXNlID0gZnVuY3Rpb24oKSB7XG4gIGlmICggdGhpcy5zdGF0ZSA9PSAncGxheWluZycgKSB7XG4gICAgdGhpcy5zdGF0ZSA9ICdwYXVzZWQnO1xuICAgIHRoaXMuY2xlYXIoKTtcbiAgfVxufTtcblxuUGxheWVyLnByb3RvdHlwZS51bnBhdXNlID0gZnVuY3Rpb24oKSB7XG4gIC8vIHJlLXN0YXJ0IHBsYXkgaWYgcGF1c2VkXG4gIGlmICggdGhpcy5zdGF0ZSA9PSAncGF1c2VkJyApIHtcbiAgICB0aGlzLnBsYXkoKTtcbiAgfVxufTtcblxuLy8gcGF1c2UgaWYgcGFnZSB2aXNpYmlsaXR5IGlzIGhpZGRlbiwgdW5wYXVzZSBpZiB2aXNpYmxlXG5QbGF5ZXIucHJvdG90eXBlLnZpc2liaWxpdHlDaGFuZ2UgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGlzUGFnZUhpZGRlbiA9IGRvY3VtZW50LmhpZGRlbjtcbiAgdGhpc1sgaXNQYWdlSGlkZGVuID8gJ3BhdXNlJyA6ICd1bnBhdXNlJyBdKCk7XG59O1xuXG5QbGF5ZXIucHJvdG90eXBlLnZpc2liaWxpdHlQbGF5ID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGxheSgpO1xuICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAndmlzaWJpbGl0eWNoYW5nZScsIHRoaXMub25WaXNpYmlsaXR5UGxheSApO1xufTtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gRmxpY2tpdHkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudXRpbHMuZXh0ZW5kKCBGbGlja2l0eS5kZWZhdWx0cywge1xuICBwYXVzZUF1dG9QbGF5T25Ib3ZlcjogdHJ1ZVxufSk7XG5cbkZsaWNraXR5LmNyZWF0ZU1ldGhvZHMucHVzaCgnX2NyZWF0ZVBsYXllcicpO1xudmFyIHByb3RvID0gRmxpY2tpdHkucHJvdG90eXBlO1xuXG5wcm90by5fY3JlYXRlUGxheWVyID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGxheWVyID0gbmV3IFBsYXllciggdGhpcyApO1xuXG4gIHRoaXMub24oICdhY3RpdmF0ZScsIHRoaXMuYWN0aXZhdGVQbGF5ZXIgKTtcbiAgdGhpcy5vbiggJ3VpQ2hhbmdlJywgdGhpcy5zdG9wUGxheWVyICk7XG4gIHRoaXMub24oICdwb2ludGVyRG93bicsIHRoaXMuc3RvcFBsYXllciApO1xuICB0aGlzLm9uKCAnZGVhY3RpdmF0ZScsIHRoaXMuZGVhY3RpdmF0ZVBsYXllciApO1xufTtcblxucHJvdG8uYWN0aXZhdGVQbGF5ZXIgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCAhdGhpcy5vcHRpb25zLmF1dG9QbGF5ICkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLnBsYXllci5wbGF5KCk7XG4gIHRoaXMuZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCAnbW91c2VlbnRlcicsIHRoaXMgKTtcbn07XG5cbi8vIFBsYXllciBBUEksIGRvbid0IGhhdGUgdGhlIC4uLiB0aGFua3MgSSBrbm93IHdoZXJlIHRoZSBkb29yIGlzXG5cbnByb3RvLnBsYXlQbGF5ZXIgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wbGF5ZXIucGxheSgpO1xufTtcblxucHJvdG8uc3RvcFBsYXllciA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBsYXllci5zdG9wKCk7XG59O1xuXG5wcm90by5wYXVzZVBsYXllciA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBsYXllci5wYXVzZSgpO1xufTtcblxucHJvdG8udW5wYXVzZVBsYXllciA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnBsYXllci51bnBhdXNlKCk7XG59O1xuXG5wcm90by5kZWFjdGl2YXRlUGxheWVyID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMucGxheWVyLnN0b3AoKTtcbiAgdGhpcy5lbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdtb3VzZWVudGVyJywgdGhpcyApO1xufTtcblxuLy8gLS0tLS0gbW91c2VlbnRlci9sZWF2ZSAtLS0tLSAvL1xuXG4vLyBwYXVzZSBhdXRvLXBsYXkgb24gaG92ZXJcbnByb3RvLm9ubW91c2VlbnRlciA9IGZ1bmN0aW9uKCkge1xuICBpZiAoICF0aGlzLm9wdGlvbnMucGF1c2VBdXRvUGxheU9uSG92ZXIgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMucGxheWVyLnBhdXNlKCk7XG4gIHRoaXMuZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCAnbW91c2VsZWF2ZScsIHRoaXMgKTtcbn07XG5cbi8vIHJlc3VtZSBhdXRvLXBsYXkgb24gaG92ZXIgb2ZmXG5wcm90by5vbm1vdXNlbGVhdmUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wbGF5ZXIudW5wYXVzZSgpO1xuICB0aGlzLmVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ21vdXNlbGVhdmUnLCB0aGlzICk7XG59O1xuXG4vLyAtLS0tLSAgLS0tLS0gLy9cblxuRmxpY2tpdHkuUGxheWVyID0gUGxheWVyO1xuXG5yZXR1cm4gRmxpY2tpdHk7XG5cbn0pKTtcbiIsIi8vIHByZXYvbmV4dCBidXR0b25zXG4oIGZ1bmN0aW9uKCB3aW5kb3csIGZhY3RvcnkgKSB7XG4gIC8vIHVuaXZlcnNhbCBtb2R1bGUgZGVmaW5pdGlvblxuICAvKiBqc2hpbnQgc3RyaWN0OiBmYWxzZSAqL1xuICBpZiAoIHR5cGVvZiBkZWZpbmUgPT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kICkge1xuICAgIC8vIEFNRFxuICAgIGRlZmluZSggW1xuICAgICAgJy4vZmxpY2tpdHknLFxuICAgICAgJ3VuaXBvaW50ZXIvdW5pcG9pbnRlcicsXG4gICAgICAnZml6enktdWktdXRpbHMvdXRpbHMnXG4gICAgXSwgZnVuY3Rpb24oIEZsaWNraXR5LCBVbmlwb2ludGVyLCB1dGlscyApIHtcbiAgICAgIHJldHVybiBmYWN0b3J5KCB3aW5kb3csIEZsaWNraXR5LCBVbmlwb2ludGVyLCB1dGlscyApO1xuICAgIH0pO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgcmVxdWlyZSgnLi9mbGlja2l0eScpLFxuICAgICAgcmVxdWlyZSgndW5pcG9pbnRlcicpLFxuICAgICAgcmVxdWlyZSgnZml6enktdWktdXRpbHMnKVxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgLy8gYnJvd3NlciBnbG9iYWxcbiAgICBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgd2luZG93LkZsaWNraXR5LFxuICAgICAgd2luZG93LlVuaXBvaW50ZXIsXG4gICAgICB3aW5kb3cuZml6enlVSVV0aWxzXG4gICAgKTtcbiAgfVxuXG59KCB3aW5kb3csIGZ1bmN0aW9uIGZhY3RvcnkoIHdpbmRvdywgRmxpY2tpdHksIFVuaXBvaW50ZXIsIHV0aWxzICkge1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgc3ZnVVJJID0gJ2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJztcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gUHJldk5leHRCdXR0b24gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuZnVuY3Rpb24gUHJldk5leHRCdXR0b24oIGRpcmVjdGlvbiwgcGFyZW50ICkge1xuICB0aGlzLmRpcmVjdGlvbiA9IGRpcmVjdGlvbjtcbiAgdGhpcy5wYXJlbnQgPSBwYXJlbnQ7XG4gIHRoaXMuX2NyZWF0ZSgpO1xufVxuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBVbmlwb2ludGVyLnByb3RvdHlwZSApO1xuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuX2NyZWF0ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyBwcm9wZXJ0aWVzXG4gIHRoaXMuaXNFbmFibGVkID0gdHJ1ZTtcbiAgdGhpcy5pc1ByZXZpb3VzID0gdGhpcy5kaXJlY3Rpb24gPT0gLTE7XG4gIHZhciBsZWZ0RGlyZWN0aW9uID0gdGhpcy5wYXJlbnQub3B0aW9ucy5yaWdodFRvTGVmdCA/IDEgOiAtMTtcbiAgdGhpcy5pc0xlZnQgPSB0aGlzLmRpcmVjdGlvbiA9PSBsZWZ0RGlyZWN0aW9uO1xuXG4gIHZhciBlbGVtZW50ID0gdGhpcy5lbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7XG4gIGVsZW1lbnQuY2xhc3NOYW1lID0gJ2ZsaWNraXR5LWJ1dHRvbiBmbGlja2l0eS1wcmV2LW5leHQtYnV0dG9uJztcbiAgZWxlbWVudC5jbGFzc05hbWUgKz0gdGhpcy5pc1ByZXZpb3VzID8gJyBwcmV2aW91cycgOiAnIG5leHQnO1xuICAvLyBwcmV2ZW50IGJ1dHRvbiBmcm9tIHN1Ym1pdHRpbmcgZm9ybSBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xMDgzNjA3Ni8xODIxODNcbiAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoICd0eXBlJywgJ2J1dHRvbicgKTtcbiAgLy8gaW5pdCBhcyBkaXNhYmxlZFxuICB0aGlzLmRpc2FibGUoKTtcblxuICBlbGVtZW50LnNldEF0dHJpYnV0ZSggJ2FyaWEtbGFiZWwnLCB0aGlzLmlzUHJldmlvdXMgPyAnUHJldmlvdXMnIDogJ05leHQnICk7XG5cbiAgLy8gY3JlYXRlIGFycm93XG4gIHZhciBzdmcgPSB0aGlzLmNyZWF0ZVNWRygpO1xuICBlbGVtZW50LmFwcGVuZENoaWxkKCBzdmcgKTtcbiAgLy8gZXZlbnRzXG4gIHRoaXMucGFyZW50Lm9uKCAnc2VsZWN0JywgdGhpcy51cGRhdGUuYmluZCggdGhpcyApICk7XG4gIHRoaXMub24oICdwb2ludGVyRG93bicsIHRoaXMucGFyZW50LmNoaWxkVUlQb2ludGVyRG93bi5iaW5kKCB0aGlzLnBhcmVudCApICk7XG59O1xuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuYWN0aXZhdGUgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5iaW5kU3RhcnRFdmVudCggdGhpcy5lbGVtZW50ICk7XG4gIHRoaXMuZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCB0aGlzICk7XG4gIC8vIGFkZCB0byBET01cbiAgdGhpcy5wYXJlbnQuZWxlbWVudC5hcHBlbmRDaGlsZCggdGhpcy5lbGVtZW50ICk7XG59O1xuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuZGVhY3RpdmF0ZSA9IGZ1bmN0aW9uKCkge1xuICAvLyByZW1vdmUgZnJvbSBET01cbiAgdGhpcy5wYXJlbnQuZWxlbWVudC5yZW1vdmVDaGlsZCggdGhpcy5lbGVtZW50ICk7XG4gIC8vIGNsaWNrIGV2ZW50c1xuICB0aGlzLnVuYmluZFN0YXJ0RXZlbnQoIHRoaXMuZWxlbWVudCApO1xuICB0aGlzLmVsZW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgdGhpcyApO1xufTtcblxuUHJldk5leHRCdXR0b24ucHJvdG90eXBlLmNyZWF0ZVNWRyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgc3ZnID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKCBzdmdVUkksICdzdmcnKTtcbiAgc3ZnLnNldEF0dHJpYnV0ZSggJ2NsYXNzJywgJ2ZsaWNraXR5LWJ1dHRvbi1pY29uJyApO1xuICBzdmcuc2V0QXR0cmlidXRlKCAndmlld0JveCcsICcwIDAgMTAwIDEwMCcgKTtcbiAgdmFyIHBhdGggPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoIHN2Z1VSSSwgJ3BhdGgnKTtcbiAgdmFyIHBhdGhNb3ZlbWVudHMgPSBnZXRBcnJvd01vdmVtZW50cyggdGhpcy5wYXJlbnQub3B0aW9ucy5hcnJvd1NoYXBlICk7XG4gIHBhdGguc2V0QXR0cmlidXRlKCAnZCcsIHBhdGhNb3ZlbWVudHMgKTtcbiAgcGF0aC5zZXRBdHRyaWJ1dGUoICdjbGFzcycsICdhcnJvdycgKTtcbiAgLy8gcm90YXRlIGFycm93XG4gIGlmICggIXRoaXMuaXNMZWZ0ICkge1xuICAgIHBhdGguc2V0QXR0cmlidXRlKCAndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgxMDAsIDEwMCkgcm90YXRlKDE4MCkgJyApO1xuICB9XG4gIHN2Zy5hcHBlbmRDaGlsZCggcGF0aCApO1xuICByZXR1cm4gc3ZnO1xufTtcblxuLy8gZ2V0IFNWRyBwYXRoIG1vdm1lbWVudFxuZnVuY3Rpb24gZ2V0QXJyb3dNb3ZlbWVudHMoIHNoYXBlICkge1xuICAvLyB1c2Ugc2hhcGUgYXMgbW92ZW1lbnQgaWYgc3RyaW5nXG4gIGlmICggdHlwZW9mIHNoYXBlID09ICdzdHJpbmcnICkge1xuICAgIHJldHVybiBzaGFwZTtcbiAgfVxuICAvLyBjcmVhdGUgbW92ZW1lbnQgc3RyaW5nXG4gIHJldHVybiAnTSAnICsgc2hhcGUueDAgKyAnLDUwJyArXG4gICAgJyBMICcgKyBzaGFwZS54MSArICcsJyArICggc2hhcGUueTEgKyA1MCApICtcbiAgICAnIEwgJyArIHNoYXBlLngyICsgJywnICsgKCBzaGFwZS55MiArIDUwICkgK1xuICAgICcgTCAnICsgc2hhcGUueDMgKyAnLDUwICcgK1xuICAgICcgTCAnICsgc2hhcGUueDIgKyAnLCcgKyAoIDUwIC0gc2hhcGUueTIgKSArXG4gICAgJyBMICcgKyBzaGFwZS54MSArICcsJyArICggNTAgLSBzaGFwZS55MSApICtcbiAgICAnIFonO1xufVxuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuaGFuZGxlRXZlbnQgPSB1dGlscy5oYW5kbGVFdmVudDtcblxuUHJldk5leHRCdXR0b24ucHJvdG90eXBlLm9uY2xpY2sgPSBmdW5jdGlvbigpIHtcbiAgaWYgKCAhdGhpcy5pc0VuYWJsZWQgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMucGFyZW50LnVpQ2hhbmdlKCk7XG4gIHZhciBtZXRob2QgPSB0aGlzLmlzUHJldmlvdXMgPyAncHJldmlvdXMnIDogJ25leHQnO1xuICB0aGlzLnBhcmVudFsgbWV0aG9kIF0oKTtcbn07XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuZW5hYmxlID0gZnVuY3Rpb24oKSB7XG4gIGlmICggdGhpcy5pc0VuYWJsZWQgKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHRoaXMuZWxlbWVudC5kaXNhYmxlZCA9IGZhbHNlO1xuICB0aGlzLmlzRW5hYmxlZCA9IHRydWU7XG59O1xuXG5QcmV2TmV4dEJ1dHRvbi5wcm90b3R5cGUuZGlzYWJsZSA9IGZ1bmN0aW9uKCkge1xuICBpZiAoICF0aGlzLmlzRW5hYmxlZCApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdGhpcy5lbGVtZW50LmRpc2FibGVkID0gdHJ1ZTtcbiAgdGhpcy5pc0VuYWJsZWQgPSBmYWxzZTtcbn07XG5cblByZXZOZXh0QnV0dG9uLnByb3RvdHlwZS51cGRhdGUgPSBmdW5jdGlvbigpIHtcbiAgLy8gaW5kZXggb2YgZmlyc3Qgb3IgbGFzdCBzbGlkZSwgaWYgcHJldmlvdXMgb3IgbmV4dFxuICB2YXIgc2xpZGVzID0gdGhpcy5wYXJlbnQuc2xpZGVzO1xuICAvLyBlbmFibGUgaXMgd3JhcEFyb3VuZCBhbmQgYXQgbGVhc3QgMiBzbGlkZXNcbiAgaWYgKCB0aGlzLnBhcmVudC5vcHRpb25zLndyYXBBcm91bmQgJiYgc2xpZGVzLmxlbmd0aCA+IDEgKSB7XG4gICAgdGhpcy5lbmFibGUoKTtcbiAgICByZXR1cm47XG4gIH1cbiAgdmFyIGxhc3RJbmRleCA9IHNsaWRlcy5sZW5ndGggPyBzbGlkZXMubGVuZ3RoIC0gMSA6IDA7XG4gIHZhciBib3VuZEluZGV4ID0gdGhpcy5pc1ByZXZpb3VzID8gMCA6IGxhc3RJbmRleDtcbiAgdmFyIG1ldGhvZCA9IHRoaXMucGFyZW50LnNlbGVjdGVkSW5kZXggPT0gYm91bmRJbmRleCA/ICdkaXNhYmxlJyA6ICdlbmFibGUnO1xuICB0aGlzWyBtZXRob2QgXSgpO1xufTtcblxuUHJldk5leHRCdXR0b24ucHJvdG90eXBlLmRlc3Ryb3kgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5kZWFjdGl2YXRlKCk7XG4gIHRoaXMuYWxsT2ZmKCk7XG59O1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBGbGlja2l0eSBwcm90b3R5cGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudXRpbHMuZXh0ZW5kKCBGbGlja2l0eS5kZWZhdWx0cywge1xuICBwcmV2TmV4dEJ1dHRvbnM6IHRydWUsXG4gIGFycm93U2hhcGU6IHtcbiAgICB4MDogMTAsXG4gICAgeDE6IDYwLCB5MTogNTAsXG4gICAgeDI6IDcwLCB5MjogNDAsXG4gICAgeDM6IDMwXG4gIH1cbn0pO1xuXG5GbGlja2l0eS5jcmVhdGVNZXRob2RzLnB1c2goJ19jcmVhdGVQcmV2TmV4dEJ1dHRvbnMnKTtcbnZhciBwcm90byA9IEZsaWNraXR5LnByb3RvdHlwZTtcblxucHJvdG8uX2NyZWF0ZVByZXZOZXh0QnV0dG9ucyA9IGZ1bmN0aW9uKCkge1xuICBpZiAoICF0aGlzLm9wdGlvbnMucHJldk5leHRCdXR0b25zICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMucHJldkJ1dHRvbiA9IG5ldyBQcmV2TmV4dEJ1dHRvbiggLTEsIHRoaXMgKTtcbiAgdGhpcy5uZXh0QnV0dG9uID0gbmV3IFByZXZOZXh0QnV0dG9uKCAxLCB0aGlzICk7XG5cbiAgdGhpcy5vbiggJ2FjdGl2YXRlJywgdGhpcy5hY3RpdmF0ZVByZXZOZXh0QnV0dG9ucyApO1xufTtcblxucHJvdG8uYWN0aXZhdGVQcmV2TmV4dEJ1dHRvbnMgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5wcmV2QnV0dG9uLmFjdGl2YXRlKCk7XG4gIHRoaXMubmV4dEJ1dHRvbi5hY3RpdmF0ZSgpO1xuICB0aGlzLm9uKCAnZGVhY3RpdmF0ZScsIHRoaXMuZGVhY3RpdmF0ZVByZXZOZXh0QnV0dG9ucyApO1xufTtcblxucHJvdG8uZGVhY3RpdmF0ZVByZXZOZXh0QnV0dG9ucyA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLnByZXZCdXR0b24uZGVhY3RpdmF0ZSgpO1xuICB0aGlzLm5leHRCdXR0b24uZGVhY3RpdmF0ZSgpO1xuICB0aGlzLm9mZiggJ2RlYWN0aXZhdGUnLCB0aGlzLmRlYWN0aXZhdGVQcmV2TmV4dEJ1dHRvbnMgKTtcbn07XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5GbGlja2l0eS5QcmV2TmV4dEJ1dHRvbiA9IFByZXZOZXh0QnV0dG9uO1xuXG5yZXR1cm4gRmxpY2tpdHk7XG5cbn0pKTtcbiIsIi8vIHNsaWRlXG4oIGZ1bmN0aW9uKCB3aW5kb3csIGZhY3RvcnkgKSB7XG4gIC8vIHVuaXZlcnNhbCBtb2R1bGUgZGVmaW5pdGlvblxuICAvKiBqc2hpbnQgc3RyaWN0OiBmYWxzZSAqL1xuICBpZiAoIHR5cGVvZiBkZWZpbmUgPT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kICkge1xuICAgIC8vIEFNRFxuICAgIGRlZmluZSggZmFjdG9yeSApO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG4gIH0gZWxzZSB7XG4gICAgLy8gYnJvd3NlciBnbG9iYWxcbiAgICB3aW5kb3cuRmxpY2tpdHkgPSB3aW5kb3cuRmxpY2tpdHkgfHwge307XG4gICAgd2luZG93LkZsaWNraXR5LlNsaWRlID0gZmFjdG9yeSgpO1xuICB9XG5cbn0oIHdpbmRvdywgZnVuY3Rpb24gZmFjdG9yeSgpIHtcbid1c2Ugc3RyaWN0JztcblxuZnVuY3Rpb24gU2xpZGUoIHBhcmVudCApIHtcbiAgdGhpcy5wYXJlbnQgPSBwYXJlbnQ7XG4gIHRoaXMuaXNPcmlnaW5MZWZ0ID0gcGFyZW50Lm9yaWdpblNpZGUgPT0gJ2xlZnQnO1xuICB0aGlzLmNlbGxzID0gW107XG4gIHRoaXMub3V0ZXJXaWR0aCA9IDA7XG4gIHRoaXMuaGVpZ2h0ID0gMDtcbn1cblxudmFyIHByb3RvID0gU2xpZGUucHJvdG90eXBlO1xuXG5wcm90by5hZGRDZWxsID0gZnVuY3Rpb24oIGNlbGwgKSB7XG4gIHRoaXMuY2VsbHMucHVzaCggY2VsbCApO1xuICB0aGlzLm91dGVyV2lkdGggKz0gY2VsbC5zaXplLm91dGVyV2lkdGg7XG4gIHRoaXMuaGVpZ2h0ID0gTWF0aC5tYXgoIGNlbGwuc2l6ZS5vdXRlckhlaWdodCwgdGhpcy5oZWlnaHQgKTtcbiAgLy8gZmlyc3QgY2VsbCBzdHVmZlxuICBpZiAoIHRoaXMuY2VsbHMubGVuZ3RoID09IDEgKSB7XG4gICAgdGhpcy54ID0gY2VsbC54OyAvLyB4IGNvbWVzIGZyb20gZmlyc3QgY2VsbFxuICAgIHZhciBiZWdpbk1hcmdpbiA9IHRoaXMuaXNPcmlnaW5MZWZ0ID8gJ21hcmdpbkxlZnQnIDogJ21hcmdpblJpZ2h0JztcbiAgICB0aGlzLmZpcnN0TWFyZ2luID0gY2VsbC5zaXplWyBiZWdpbk1hcmdpbiBdO1xuICB9XG59O1xuXG5wcm90by51cGRhdGVUYXJnZXQgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGVuZE1hcmdpbiA9IHRoaXMuaXNPcmlnaW5MZWZ0ID8gJ21hcmdpblJpZ2h0JyA6ICdtYXJnaW5MZWZ0JztcbiAgdmFyIGxhc3RDZWxsID0gdGhpcy5nZXRMYXN0Q2VsbCgpO1xuICB2YXIgbGFzdE1hcmdpbiA9IGxhc3RDZWxsID8gbGFzdENlbGwuc2l6ZVsgZW5kTWFyZ2luIF0gOiAwO1xuICB2YXIgc2xpZGVXaWR0aCA9IHRoaXMub3V0ZXJXaWR0aCAtICggdGhpcy5maXJzdE1hcmdpbiArIGxhc3RNYXJnaW4gKTtcbiAgdGhpcy50YXJnZXQgPSB0aGlzLnggKyB0aGlzLmZpcnN0TWFyZ2luICsgc2xpZGVXaWR0aCAqIHRoaXMucGFyZW50LmNlbGxBbGlnbjtcbn07XG5cbnByb3RvLmdldExhc3RDZWxsID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmNlbGxzWyB0aGlzLmNlbGxzLmxlbmd0aCAtIDEgXTtcbn07XG5cbnByb3RvLnNlbGVjdCA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLmNlbGxzLmZvckVhY2goIGZ1bmN0aW9uKCBjZWxsICkge1xuICAgIGNlbGwuc2VsZWN0KCk7XG4gIH0pO1xufTtcblxucHJvdG8udW5zZWxlY3QgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5jZWxscy5mb3JFYWNoKCBmdW5jdGlvbiggY2VsbCApIHtcbiAgICBjZWxsLnVuc2VsZWN0KCk7XG4gIH0pO1xufTtcblxucHJvdG8uZ2V0Q2VsbEVsZW1lbnRzID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLmNlbGxzLm1hcCggZnVuY3Rpb24oIGNlbGwgKSB7XG4gICAgcmV0dXJuIGNlbGwuZWxlbWVudDtcbiAgfSk7XG59O1xuXG5yZXR1cm4gU2xpZGU7XG5cbn0pKTtcbiIsIi8qIVxuICogZ2V0U2l6ZSB2Mi4wLjNcbiAqIG1lYXN1cmUgc2l6ZSBvZiBlbGVtZW50c1xuICogTUlUIGxpY2Vuc2VcbiAqL1xuXG4vKiBqc2hpbnQgYnJvd3NlcjogdHJ1ZSwgc3RyaWN0OiB0cnVlLCB1bmRlZjogdHJ1ZSwgdW51c2VkOiB0cnVlICovXG4vKiBnbG9iYWxzIGNvbnNvbGU6IGZhbHNlICovXG5cbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLyoganNoaW50IHN0cmljdDogZmFsc2UgKi8gLyogZ2xvYmFscyBkZWZpbmUsIG1vZHVsZSAqL1xuICBpZiAoIHR5cGVvZiBkZWZpbmUgPT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kICkge1xuICAgIC8vIEFNRFxuICAgIGRlZmluZSggZmFjdG9yeSApO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG4gIH0gZWxzZSB7XG4gICAgLy8gYnJvd3NlciBnbG9iYWxcbiAgICB3aW5kb3cuZ2V0U2l6ZSA9IGZhY3RvcnkoKTtcbiAgfVxuXG59KSggd2luZG93LCBmdW5jdGlvbiBmYWN0b3J5KCkge1xuJ3VzZSBzdHJpY3QnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBoZWxwZXJzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbi8vIGdldCBhIG51bWJlciBmcm9tIGEgc3RyaW5nLCBub3QgYSBwZXJjZW50YWdlXG5mdW5jdGlvbiBnZXRTdHlsZVNpemUoIHZhbHVlICkge1xuICB2YXIgbnVtID0gcGFyc2VGbG9hdCggdmFsdWUgKTtcbiAgLy8gbm90IGEgcGVyY2VudCBsaWtlICcxMDAlJywgYW5kIGEgbnVtYmVyXG4gIHZhciBpc1ZhbGlkID0gdmFsdWUuaW5kZXhPZignJScpID09IC0xICYmICFpc05hTiggbnVtICk7XG4gIHJldHVybiBpc1ZhbGlkICYmIG51bTtcbn1cblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbnZhciBsb2dFcnJvciA9IHR5cGVvZiBjb25zb2xlID09ICd1bmRlZmluZWQnID8gbm9vcCA6XG4gIGZ1bmN0aW9uKCBtZXNzYWdlICkge1xuICAgIGNvbnNvbGUuZXJyb3IoIG1lc3NhZ2UgKTtcbiAgfTtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gbWVhc3VyZW1lbnRzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC8vXG5cbnZhciBtZWFzdXJlbWVudHMgPSBbXG4gICdwYWRkaW5nTGVmdCcsXG4gICdwYWRkaW5nUmlnaHQnLFxuICAncGFkZGluZ1RvcCcsXG4gICdwYWRkaW5nQm90dG9tJyxcbiAgJ21hcmdpbkxlZnQnLFxuICAnbWFyZ2luUmlnaHQnLFxuICAnbWFyZ2luVG9wJyxcbiAgJ21hcmdpbkJvdHRvbScsXG4gICdib3JkZXJMZWZ0V2lkdGgnLFxuICAnYm9yZGVyUmlnaHRXaWR0aCcsXG4gICdib3JkZXJUb3BXaWR0aCcsXG4gICdib3JkZXJCb3R0b21XaWR0aCdcbl07XG5cbnZhciBtZWFzdXJlbWVudHNMZW5ndGggPSBtZWFzdXJlbWVudHMubGVuZ3RoO1xuXG5mdW5jdGlvbiBnZXRaZXJvU2l6ZSgpIHtcbiAgdmFyIHNpemUgPSB7XG4gICAgd2lkdGg6IDAsXG4gICAgaGVpZ2h0OiAwLFxuICAgIGlubmVyV2lkdGg6IDAsXG4gICAgaW5uZXJIZWlnaHQ6IDAsXG4gICAgb3V0ZXJXaWR0aDogMCxcbiAgICBvdXRlckhlaWdodDogMFxuICB9O1xuICBmb3IgKCB2YXIgaT0wOyBpIDwgbWVhc3VyZW1lbnRzTGVuZ3RoOyBpKysgKSB7XG4gICAgdmFyIG1lYXN1cmVtZW50ID0gbWVhc3VyZW1lbnRzW2ldO1xuICAgIHNpemVbIG1lYXN1cmVtZW50IF0gPSAwO1xuICB9XG4gIHJldHVybiBzaXplO1xufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBnZXRTdHlsZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG4vKipcbiAqIGdldFN0eWxlLCBnZXQgc3R5bGUgb2YgZWxlbWVudCwgY2hlY2sgZm9yIEZpcmVmb3ggYnVnXG4gKiBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD01NDgzOTdcbiAqL1xuZnVuY3Rpb24gZ2V0U3R5bGUoIGVsZW0gKSB7XG4gIHZhciBzdHlsZSA9IGdldENvbXB1dGVkU3R5bGUoIGVsZW0gKTtcbiAgaWYgKCAhc3R5bGUgKSB7XG4gICAgbG9nRXJyb3IoICdTdHlsZSByZXR1cm5lZCAnICsgc3R5bGUgK1xuICAgICAgJy4gQXJlIHlvdSBydW5uaW5nIHRoaXMgY29kZSBpbiBhIGhpZGRlbiBpZnJhbWUgb24gRmlyZWZveD8gJyArXG4gICAgICAnU2VlIGh0dHBzOi8vYml0Lmx5L2dldHNpemVidWcxJyApO1xuICB9XG4gIHJldHVybiBzdHlsZTtcbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gc2V0dXAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxudmFyIGlzU2V0dXAgPSBmYWxzZTtcblxudmFyIGlzQm94U2l6ZU91dGVyO1xuXG4vKipcbiAqIHNldHVwXG4gKiBjaGVjayBpc0JveFNpemVyT3V0ZXJcbiAqIGRvIG9uIGZpcnN0IGdldFNpemUoKSByYXRoZXIgdGhhbiBvbiBwYWdlIGxvYWQgZm9yIEZpcmVmb3ggYnVnXG4gKi9cbmZ1bmN0aW9uIHNldHVwKCkge1xuICAvLyBzZXR1cCBvbmNlXG4gIGlmICggaXNTZXR1cCApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaXNTZXR1cCA9IHRydWU7XG5cbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gYm94IHNpemluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG4gIC8qKlxuICAgKiBDaHJvbWUgJiBTYWZhcmkgbWVhc3VyZSB0aGUgb3V0ZXItd2lkdGggb24gc3R5bGUud2lkdGggb24gYm9yZGVyLWJveCBlbGVtc1xuICAgKiBJRTExICYgRmlyZWZveDwyOSBtZWFzdXJlcyB0aGUgaW5uZXItd2lkdGhcbiAgICovXG4gIHZhciBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgZGl2LnN0eWxlLndpZHRoID0gJzIwMHB4JztcbiAgZGl2LnN0eWxlLnBhZGRpbmcgPSAnMXB4IDJweCAzcHggNHB4JztcbiAgZGl2LnN0eWxlLmJvcmRlclN0eWxlID0gJ3NvbGlkJztcbiAgZGl2LnN0eWxlLmJvcmRlcldpZHRoID0gJzFweCAycHggM3B4IDRweCc7XG4gIGRpdi5zdHlsZS5ib3hTaXppbmcgPSAnYm9yZGVyLWJveCc7XG5cbiAgdmFyIGJvZHkgPSBkb2N1bWVudC5ib2R5IHx8IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcbiAgYm9keS5hcHBlbmRDaGlsZCggZGl2ICk7XG4gIHZhciBzdHlsZSA9IGdldFN0eWxlKCBkaXYgKTtcbiAgLy8gcm91bmQgdmFsdWUgZm9yIGJyb3dzZXIgem9vbS4gZGVzYW5kcm8vbWFzb25yeSM5MjhcbiAgaXNCb3hTaXplT3V0ZXIgPSBNYXRoLnJvdW5kKCBnZXRTdHlsZVNpemUoIHN0eWxlLndpZHRoICkgKSA9PSAyMDA7XG4gIGdldFNpemUuaXNCb3hTaXplT3V0ZXIgPSBpc0JveFNpemVPdXRlcjtcblxuICBib2R5LnJlbW92ZUNoaWxkKCBkaXYgKTtcbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gZ2V0U2l6ZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAvL1xuXG5mdW5jdGlvbiBnZXRTaXplKCBlbGVtICkge1xuICBzZXR1cCgpO1xuXG4gIC8vIHVzZSBxdWVyeVNlbGV0b3IgaWYgZWxlbSBpcyBzdHJpbmdcbiAgaWYgKCB0eXBlb2YgZWxlbSA9PSAnc3RyaW5nJyApIHtcbiAgICBlbGVtID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggZWxlbSApO1xuICB9XG5cbiAgLy8gZG8gbm90IHByb2NlZWQgb24gbm9uLW9iamVjdHNcbiAgaWYgKCAhZWxlbSB8fCB0eXBlb2YgZWxlbSAhPSAnb2JqZWN0JyB8fCAhZWxlbS5ub2RlVHlwZSApIHtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgc3R5bGUgPSBnZXRTdHlsZSggZWxlbSApO1xuXG4gIC8vIGlmIGhpZGRlbiwgZXZlcnl0aGluZyBpcyAwXG4gIGlmICggc3R5bGUuZGlzcGxheSA9PSAnbm9uZScgKSB7XG4gICAgcmV0dXJuIGdldFplcm9TaXplKCk7XG4gIH1cblxuICB2YXIgc2l6ZSA9IHt9O1xuICBzaXplLndpZHRoID0gZWxlbS5vZmZzZXRXaWR0aDtcbiAgc2l6ZS5oZWlnaHQgPSBlbGVtLm9mZnNldEhlaWdodDtcblxuICB2YXIgaXNCb3JkZXJCb3ggPSBzaXplLmlzQm9yZGVyQm94ID0gc3R5bGUuYm94U2l6aW5nID09ICdib3JkZXItYm94JztcblxuICAvLyBnZXQgYWxsIG1lYXN1cmVtZW50c1xuICBmb3IgKCB2YXIgaT0wOyBpIDwgbWVhc3VyZW1lbnRzTGVuZ3RoOyBpKysgKSB7XG4gICAgdmFyIG1lYXN1cmVtZW50ID0gbWVhc3VyZW1lbnRzW2ldO1xuICAgIHZhciB2YWx1ZSA9IHN0eWxlWyBtZWFzdXJlbWVudCBdO1xuICAgIHZhciBudW0gPSBwYXJzZUZsb2F0KCB2YWx1ZSApO1xuICAgIC8vIGFueSAnYXV0bycsICdtZWRpdW0nIHZhbHVlIHdpbGwgYmUgMFxuICAgIHNpemVbIG1lYXN1cmVtZW50IF0gPSAhaXNOYU4oIG51bSApID8gbnVtIDogMDtcbiAgfVxuXG4gIHZhciBwYWRkaW5nV2lkdGggPSBzaXplLnBhZGRpbmdMZWZ0ICsgc2l6ZS5wYWRkaW5nUmlnaHQ7XG4gIHZhciBwYWRkaW5nSGVpZ2h0ID0gc2l6ZS5wYWRkaW5nVG9wICsgc2l6ZS5wYWRkaW5nQm90dG9tO1xuICB2YXIgbWFyZ2luV2lkdGggPSBzaXplLm1hcmdpbkxlZnQgKyBzaXplLm1hcmdpblJpZ2h0O1xuICB2YXIgbWFyZ2luSGVpZ2h0ID0gc2l6ZS5tYXJnaW5Ub3AgKyBzaXplLm1hcmdpbkJvdHRvbTtcbiAgdmFyIGJvcmRlcldpZHRoID0gc2l6ZS5ib3JkZXJMZWZ0V2lkdGggKyBzaXplLmJvcmRlclJpZ2h0V2lkdGg7XG4gIHZhciBib3JkZXJIZWlnaHQgPSBzaXplLmJvcmRlclRvcFdpZHRoICsgc2l6ZS5ib3JkZXJCb3R0b21XaWR0aDtcblxuICB2YXIgaXNCb3JkZXJCb3hTaXplT3V0ZXIgPSBpc0JvcmRlckJveCAmJiBpc0JveFNpemVPdXRlcjtcblxuICAvLyBvdmVyd3JpdGUgd2lkdGggYW5kIGhlaWdodCBpZiB3ZSBjYW4gZ2V0IGl0IGZyb20gc3R5bGVcbiAgdmFyIHN0eWxlV2lkdGggPSBnZXRTdHlsZVNpemUoIHN0eWxlLndpZHRoICk7XG4gIGlmICggc3R5bGVXaWR0aCAhPT0gZmFsc2UgKSB7XG4gICAgc2l6ZS53aWR0aCA9IHN0eWxlV2lkdGggK1xuICAgICAgLy8gYWRkIHBhZGRpbmcgYW5kIGJvcmRlciB1bmxlc3MgaXQncyBhbHJlYWR5IGluY2x1ZGluZyBpdFxuICAgICAgKCBpc0JvcmRlckJveFNpemVPdXRlciA/IDAgOiBwYWRkaW5nV2lkdGggKyBib3JkZXJXaWR0aCApO1xuICB9XG5cbiAgdmFyIHN0eWxlSGVpZ2h0ID0gZ2V0U3R5bGVTaXplKCBzdHlsZS5oZWlnaHQgKTtcbiAgaWYgKCBzdHlsZUhlaWdodCAhPT0gZmFsc2UgKSB7XG4gICAgc2l6ZS5oZWlnaHQgPSBzdHlsZUhlaWdodCArXG4gICAgICAvLyBhZGQgcGFkZGluZyBhbmQgYm9yZGVyIHVubGVzcyBpdCdzIGFscmVhZHkgaW5jbHVkaW5nIGl0XG4gICAgICAoIGlzQm9yZGVyQm94U2l6ZU91dGVyID8gMCA6IHBhZGRpbmdIZWlnaHQgKyBib3JkZXJIZWlnaHQgKTtcbiAgfVxuXG4gIHNpemUuaW5uZXJXaWR0aCA9IHNpemUud2lkdGggLSAoIHBhZGRpbmdXaWR0aCArIGJvcmRlcldpZHRoICk7XG4gIHNpemUuaW5uZXJIZWlnaHQgPSBzaXplLmhlaWdodCAtICggcGFkZGluZ0hlaWdodCArIGJvcmRlckhlaWdodCApO1xuXG4gIHNpemUub3V0ZXJXaWR0aCA9IHNpemUud2lkdGggKyBtYXJnaW5XaWR0aDtcbiAgc2l6ZS5vdXRlckhlaWdodCA9IHNpemUuaGVpZ2h0ICsgbWFyZ2luSGVpZ2h0O1xuXG4gIHJldHVybiBzaXplO1xufVxuXG5yZXR1cm4gZ2V0U2l6ZTtcblxufSk7XG4iLCJ0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiICYmXG4oZnVuY3Rpb24gd2VicGFja1VuaXZlcnNhbE1vZHVsZURlZmluaXRpb24ocm9vdCwgZmFjdG9yeSkge1xuXHRpZih0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIG1vZHVsZSA9PT0gJ29iamVjdCcpXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG5cdGVsc2UgaWYodHlwZW9mIGRlZmluZSA9PT0gJ2Z1bmN0aW9uJyAmJiBkZWZpbmUuYW1kKVxuXHRcdGRlZmluZShbXSwgZmFjdG9yeSk7XG5cdGVsc2UgaWYodHlwZW9mIGV4cG9ydHMgPT09ICdvYmplY3QnKVxuXHRcdGV4cG9ydHNbXCJIbHNcIl0gPSBmYWN0b3J5KCk7XG5cdGVsc2Vcblx0XHRyb290W1wiSGxzXCJdID0gZmFjdG9yeSgpO1xufSkodGhpcywgZnVuY3Rpb24oKSB7XG5yZXR1cm4gLyoqKioqKi8gKGZ1bmN0aW9uKG1vZHVsZXMpIHsgLy8gd2VicGFja0Jvb3RzdHJhcFxuLyoqKioqKi8gXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4vKioqKioqLyBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG4vKioqKioqL1xuLyoqKioqKi8gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuLyoqKioqKi8gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG4vKioqKioqL1xuLyoqKioqKi8gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuLyoqKioqKi8gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4vKioqKioqLyBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbi8qKioqKiovIFx0XHR9XG4vKioqKioqLyBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbi8qKioqKiovIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4vKioqKioqLyBcdFx0XHRpOiBtb2R1bGVJZCxcbi8qKioqKiovIFx0XHRcdGw6IGZhbHNlLFxuLyoqKioqKi8gXHRcdFx0ZXhwb3J0czoge31cbi8qKioqKiovIFx0XHR9O1xuLyoqKioqKi9cbi8qKioqKiovIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbi8qKioqKiovIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcbi8qKioqKiovXG4vKioqKioqLyBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuLyoqKioqKi8gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcbi8qKioqKiovXG4vKioqKioqLyBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbi8qKioqKiovIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4vKioqKioqLyBcdH1cbi8qKioqKiovXG4vKioqKioqL1xuLyoqKioqKi8gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuLyoqKioqKi9cbi8qKioqKiovIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbi8qKioqKiovIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcbi8qKioqKiovXG4vKioqKioqLyBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbi8qKioqKiovIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4vKioqKioqLyBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGdldHRlciB9KTtcbi8qKioqKiovIFx0XHR9XG4vKioqKioqLyBcdH07XG4vKioqKioqL1xuLyoqKioqKi8gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4vKioqKioqLyBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbi8qKioqKiovIFx0XHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcbi8qKioqKiovIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuLyoqKioqKi8gXHRcdH1cbi8qKioqKiovIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuLyoqKioqKi8gXHR9O1xuLyoqKioqKi9cbi8qKioqKiovIFx0Ly8gY3JlYXRlIGEgZmFrZSBuYW1lc3BhY2Ugb2JqZWN0XG4vKioqKioqLyBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuLyoqKioqKi8gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbi8qKioqKiovIFx0Ly8gbW9kZSAmIDQ6IHJldHVybiB2YWx1ZSB3aGVuIGFscmVhZHkgbnMgb2JqZWN0XG4vKioqKioqLyBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbi8qKioqKiovIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbi8qKioqKiovIFx0XHRpZihtb2RlICYgMSkgdmFsdWUgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKHZhbHVlKTtcbi8qKioqKiovIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuLyoqKioqKi8gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4vKioqKioqLyBcdFx0dmFyIG5zID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbi8qKioqKiovIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuLyoqKioqKi8gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbi8qKioqKiovIFx0XHRpZihtb2RlICYgMiAmJiB0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIGZvcih2YXIga2V5IGluIHZhbHVlKSBfX3dlYnBhY2tfcmVxdWlyZV9fLmQobnMsIGtleSwgZnVuY3Rpb24oa2V5KSB7IHJldHVybiB2YWx1ZVtrZXldOyB9LmJpbmQobnVsbCwga2V5KSk7XG4vKioqKioqLyBcdFx0cmV0dXJuIG5zO1xuLyoqKioqKi8gXHR9O1xuLyoqKioqKi9cbi8qKioqKiovIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbi8qKioqKiovIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4vKioqKioqLyBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4vKioqKioqLyBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuLyoqKioqKi8gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbi8qKioqKiovIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4vKioqKioqLyBcdFx0cmV0dXJuIGdldHRlcjtcbi8qKioqKiovIFx0fTtcbi8qKioqKiovXG4vKioqKioqLyBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG4vKioqKioqL1xuLyoqKioqKi8gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuLyoqKioqKi8gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIi9kaXN0L1wiO1xuLyoqKioqKi9cbi8qKioqKiovXG4vKioqKioqLyBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuLyoqKioqKi8gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vc3JjL2hscy50c1wiKTtcbi8qKioqKiovIH0pXG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqKioqKi8gKHtcblxuLyoqKi8gXCIuL25vZGVfbW9kdWxlcy9ldmVudGVtaXR0ZXIzL2luZGV4LmpzXCI6XG4vKiEqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiohKlxcXG4gICEqKiogLi9ub2RlX21vZHVsZXMvZXZlbnRlbWl0dGVyMy9pbmRleC5qcyAqKiohXG4gIFxcKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyohIG5vIHN0YXRpYyBleHBvcnRzIGZvdW5kICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBNb2R1bGUgaXMgbm90IGFuIEVDTUFTY3JpcHQgbW9kdWxlICovXG4vKioqLyAoZnVuY3Rpb24obW9kdWxlLCBleHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7XG5cblwidXNlIHN0cmljdFwiO1xuXG5cbnZhciBoYXMgPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5XG4gICwgcHJlZml4ID0gJ34nO1xuXG4vKipcbiAqIENvbnN0cnVjdG9yIHRvIGNyZWF0ZSBhIHN0b3JhZ2UgZm9yIG91ciBgRUVgIG9iamVjdHMuXG4gKiBBbiBgRXZlbnRzYCBpbnN0YW5jZSBpcyBhIHBsYWluIG9iamVjdCB3aG9zZSBwcm9wZXJ0aWVzIGFyZSBldmVudCBuYW1lcy5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIEV2ZW50cygpIHt9XG5cbi8vXG4vLyBXZSB0cnkgdG8gbm90IGluaGVyaXQgZnJvbSBgT2JqZWN0LnByb3RvdHlwZWAuIEluIHNvbWUgZW5naW5lcyBjcmVhdGluZyBhblxuLy8gaW5zdGFuY2UgaW4gdGhpcyB3YXkgaXMgZmFzdGVyIHRoYW4gY2FsbGluZyBgT2JqZWN0LmNyZWF0ZShudWxsKWAgZGlyZWN0bHkuXG4vLyBJZiBgT2JqZWN0LmNyZWF0ZShudWxsKWAgaXMgbm90IHN1cHBvcnRlZCB3ZSBwcmVmaXggdGhlIGV2ZW50IG5hbWVzIHdpdGggYVxuLy8gY2hhcmFjdGVyIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBidWlsdC1pbiBvYmplY3QgcHJvcGVydGllcyBhcmUgbm90XG4vLyBvdmVycmlkZGVuIG9yIHVzZWQgYXMgYW4gYXR0YWNrIHZlY3Rvci5cbi8vXG5pZiAoT2JqZWN0LmNyZWF0ZSkge1xuICBFdmVudHMucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcblxuICAvL1xuICAvLyBUaGlzIGhhY2sgaXMgbmVlZGVkIGJlY2F1c2UgdGhlIGBfX3Byb3RvX19gIHByb3BlcnR5IGlzIHN0aWxsIGluaGVyaXRlZCBpblxuICAvLyBzb21lIG9sZCBicm93c2VycyBsaWtlIEFuZHJvaWQgNCwgaVBob25lIDUuMSwgT3BlcmEgMTEgYW5kIFNhZmFyaSA1LlxuICAvL1xuICBpZiAoIW5ldyBFdmVudHMoKS5fX3Byb3RvX18pIHByZWZpeCA9IGZhbHNlO1xufVxuXG4vKipcbiAqIFJlcHJlc2VudGF0aW9uIG9mIGEgc2luZ2xlIGV2ZW50IGxpc3RlbmVyLlxuICpcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBsaXN0ZW5lciBmdW5jdGlvbi5cbiAqIEBwYXJhbSB7Kn0gY29udGV4dCBUaGUgY29udGV4dCB0byBpbnZva2UgdGhlIGxpc3RlbmVyIHdpdGguXG4gKiBAcGFyYW0ge0Jvb2xlYW59IFtvbmNlPWZhbHNlXSBTcGVjaWZ5IGlmIHRoZSBsaXN0ZW5lciBpcyBhIG9uZS10aW1lIGxpc3RlbmVyLlxuICogQGNvbnN0cnVjdG9yXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBFRShmbiwgY29udGV4dCwgb25jZSkge1xuICB0aGlzLmZuID0gZm47XG4gIHRoaXMuY29udGV4dCA9IGNvbnRleHQ7XG4gIHRoaXMub25jZSA9IG9uY2UgfHwgZmFsc2U7XG59XG5cbi8qKlxuICogQWRkIGEgbGlzdGVuZXIgZm9yIGEgZ2l2ZW4gZXZlbnQuXG4gKlxuICogQHBhcmFtIHtFdmVudEVtaXR0ZXJ9IGVtaXR0ZXIgUmVmZXJlbmNlIHRvIHRoZSBgRXZlbnRFbWl0dGVyYCBpbnN0YW5jZS5cbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBsaXN0ZW5lciBmdW5jdGlvbi5cbiAqIEBwYXJhbSB7Kn0gY29udGV4dCBUaGUgY29udGV4dCB0byBpbnZva2UgdGhlIGxpc3RlbmVyIHdpdGguXG4gKiBAcGFyYW0ge0Jvb2xlYW59IG9uY2UgU3BlY2lmeSBpZiB0aGUgbGlzdGVuZXIgaXMgYSBvbmUtdGltZSBsaXN0ZW5lci5cbiAqIEByZXR1cm5zIHtFdmVudEVtaXR0ZXJ9XG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBhZGRMaXN0ZW5lcihlbWl0dGVyLCBldmVudCwgZm4sIGNvbnRleHQsIG9uY2UpIHtcbiAgaWYgKHR5cGVvZiBmbiAhPT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1RoZSBsaXN0ZW5lciBtdXN0IGJlIGEgZnVuY3Rpb24nKTtcbiAgfVxuXG4gIHZhciBsaXN0ZW5lciA9IG5ldyBFRShmbiwgY29udGV4dCB8fCBlbWl0dGVyLCBvbmNlKVxuICAgICwgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudDtcblxuICBpZiAoIWVtaXR0ZXIuX2V2ZW50c1tldnRdKSBlbWl0dGVyLl9ldmVudHNbZXZ0XSA9IGxpc3RlbmVyLCBlbWl0dGVyLl9ldmVudHNDb3VudCsrO1xuICBlbHNlIGlmICghZW1pdHRlci5fZXZlbnRzW2V2dF0uZm4pIGVtaXR0ZXIuX2V2ZW50c1tldnRdLnB1c2gobGlzdGVuZXIpO1xuICBlbHNlIGVtaXR0ZXIuX2V2ZW50c1tldnRdID0gW2VtaXR0ZXIuX2V2ZW50c1tldnRdLCBsaXN0ZW5lcl07XG5cbiAgcmV0dXJuIGVtaXR0ZXI7XG59XG5cbi8qKlxuICogQ2xlYXIgZXZlbnQgYnkgbmFtZS5cbiAqXG4gKiBAcGFyYW0ge0V2ZW50RW1pdHRlcn0gZW1pdHRlciBSZWZlcmVuY2UgdG8gdGhlIGBFdmVudEVtaXR0ZXJgIGluc3RhbmNlLlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2dCBUaGUgRXZlbnQgbmFtZS5cbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIGNsZWFyRXZlbnQoZW1pdHRlciwgZXZ0KSB7XG4gIGlmICgtLWVtaXR0ZXIuX2V2ZW50c0NvdW50ID09PSAwKSBlbWl0dGVyLl9ldmVudHMgPSBuZXcgRXZlbnRzKCk7XG4gIGVsc2UgZGVsZXRlIGVtaXR0ZXIuX2V2ZW50c1tldnRdO1xufVxuXG4vKipcbiAqIE1pbmltYWwgYEV2ZW50RW1pdHRlcmAgaW50ZXJmYWNlIHRoYXQgaXMgbW9sZGVkIGFnYWluc3QgdGhlIE5vZGUuanNcbiAqIGBFdmVudEVtaXR0ZXJgIGludGVyZmFjZS5cbiAqXG4gKiBAY29uc3RydWN0b3JcbiAqIEBwdWJsaWNcbiAqL1xuZnVuY3Rpb24gRXZlbnRFbWl0dGVyKCkge1xuICB0aGlzLl9ldmVudHMgPSBuZXcgRXZlbnRzKCk7XG4gIHRoaXMuX2V2ZW50c0NvdW50ID0gMDtcbn1cblxuLyoqXG4gKiBSZXR1cm4gYW4gYXJyYXkgbGlzdGluZyB0aGUgZXZlbnRzIGZvciB3aGljaCB0aGUgZW1pdHRlciBoYXMgcmVnaXN0ZXJlZFxuICogbGlzdGVuZXJzLlxuICpcbiAqIEByZXR1cm5zIHtBcnJheX1cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5ldmVudE5hbWVzID0gZnVuY3Rpb24gZXZlbnROYW1lcygpIHtcbiAgdmFyIG5hbWVzID0gW11cbiAgICAsIGV2ZW50c1xuICAgICwgbmFtZTtcblxuICBpZiAodGhpcy5fZXZlbnRzQ291bnQgPT09IDApIHJldHVybiBuYW1lcztcblxuICBmb3IgKG5hbWUgaW4gKGV2ZW50cyA9IHRoaXMuX2V2ZW50cykpIHtcbiAgICBpZiAoaGFzLmNhbGwoZXZlbnRzLCBuYW1lKSkgbmFtZXMucHVzaChwcmVmaXggPyBuYW1lLnNsaWNlKDEpIDogbmFtZSk7XG4gIH1cblxuICBpZiAoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scykge1xuICAgIHJldHVybiBuYW1lcy5jb25jYXQoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhldmVudHMpKTtcbiAgfVxuXG4gIHJldHVybiBuYW1lcztcbn07XG5cbi8qKlxuICogUmV0dXJuIHRoZSBsaXN0ZW5lcnMgcmVnaXN0ZXJlZCBmb3IgYSBnaXZlbiBldmVudC5cbiAqXG4gKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gZXZlbnQgVGhlIGV2ZW50IG5hbWUuXG4gKiBAcmV0dXJucyB7QXJyYXl9IFRoZSByZWdpc3RlcmVkIGxpc3RlbmVycy5cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbiBsaXN0ZW5lcnMoZXZlbnQpIHtcbiAgdmFyIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnRcbiAgICAsIGhhbmRsZXJzID0gdGhpcy5fZXZlbnRzW2V2dF07XG5cbiAgaWYgKCFoYW5kbGVycykgcmV0dXJuIFtdO1xuICBpZiAoaGFuZGxlcnMuZm4pIHJldHVybiBbaGFuZGxlcnMuZm5dO1xuXG4gIGZvciAodmFyIGkgPSAwLCBsID0gaGFuZGxlcnMubGVuZ3RoLCBlZSA9IG5ldyBBcnJheShsKTsgaSA8IGw7IGkrKykge1xuICAgIGVlW2ldID0gaGFuZGxlcnNbaV0uZm47XG4gIH1cblxuICByZXR1cm4gZWU7XG59O1xuXG4vKipcbiAqIFJldHVybiB0aGUgbnVtYmVyIG9mIGxpc3RlbmVycyBsaXN0ZW5pbmcgdG8gYSBnaXZlbiBldmVudC5cbiAqXG4gKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gZXZlbnQgVGhlIGV2ZW50IG5hbWUuXG4gKiBAcmV0dXJucyB7TnVtYmVyfSBUaGUgbnVtYmVyIG9mIGxpc3RlbmVycy5cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lckNvdW50ID0gZnVuY3Rpb24gbGlzdGVuZXJDb3VudChldmVudCkge1xuICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudFxuICAgICwgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW2V2dF07XG5cbiAgaWYgKCFsaXN0ZW5lcnMpIHJldHVybiAwO1xuICBpZiAobGlzdGVuZXJzLmZuKSByZXR1cm4gMTtcbiAgcmV0dXJuIGxpc3RlbmVycy5sZW5ndGg7XG59O1xuXG4vKipcbiAqIENhbGxzIGVhY2ggb2YgdGhlIGxpc3RlbmVycyByZWdpc3RlcmVkIGZvciBhIGdpdmVuIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEByZXR1cm5zIHtCb29sZWFufSBgdHJ1ZWAgaWYgdGhlIGV2ZW50IGhhZCBsaXN0ZW5lcnMsIGVsc2UgYGZhbHNlYC5cbiAqIEBwdWJsaWNcbiAqL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5lbWl0ID0gZnVuY3Rpb24gZW1pdChldmVudCwgYTEsIGEyLCBhMywgYTQsIGE1KSB7XG4gIHZhciBldnQgPSBwcmVmaXggPyBwcmVmaXggKyBldmVudCA6IGV2ZW50O1xuXG4gIGlmICghdGhpcy5fZXZlbnRzW2V2dF0pIHJldHVybiBmYWxzZTtcblxuICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW2V2dF1cbiAgICAsIGxlbiA9IGFyZ3VtZW50cy5sZW5ndGhcbiAgICAsIGFyZ3NcbiAgICAsIGk7XG5cbiAgaWYgKGxpc3RlbmVycy5mbikge1xuICAgIGlmIChsaXN0ZW5lcnMub25jZSkgdGhpcy5yZW1vdmVMaXN0ZW5lcihldmVudCwgbGlzdGVuZXJzLmZuLCB1bmRlZmluZWQsIHRydWUpO1xuXG4gICAgc3dpdGNoIChsZW4pIHtcbiAgICAgIGNhc2UgMTogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0KSwgdHJ1ZTtcbiAgICAgIGNhc2UgMjogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSksIHRydWU7XG4gICAgICBjYXNlIDM6IHJldHVybiBsaXN0ZW5lcnMuZm4uY2FsbChsaXN0ZW5lcnMuY29udGV4dCwgYTEsIGEyKSwgdHJ1ZTtcbiAgICAgIGNhc2UgNDogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIsIGEzKSwgdHJ1ZTtcbiAgICAgIGNhc2UgNTogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIsIGEzLCBhNCksIHRydWU7XG4gICAgICBjYXNlIDY6IHJldHVybiBsaXN0ZW5lcnMuZm4uY2FsbChsaXN0ZW5lcnMuY29udGV4dCwgYTEsIGEyLCBhMywgYTQsIGE1KSwgdHJ1ZTtcbiAgICB9XG5cbiAgICBmb3IgKGkgPSAxLCBhcmdzID0gbmV3IEFycmF5KGxlbiAtMSk7IGkgPCBsZW47IGkrKykge1xuICAgICAgYXJnc1tpIC0gMV0gPSBhcmd1bWVudHNbaV07XG4gICAgfVxuXG4gICAgbGlzdGVuZXJzLmZuLmFwcGx5KGxpc3RlbmVycy5jb250ZXh0LCBhcmdzKTtcbiAgfSBlbHNlIHtcbiAgICB2YXIgbGVuZ3RoID0gbGlzdGVuZXJzLmxlbmd0aFxuICAgICAgLCBqO1xuXG4gICAgZm9yIChpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAobGlzdGVuZXJzW2ldLm9uY2UpIHRoaXMucmVtb3ZlTGlzdGVuZXIoZXZlbnQsIGxpc3RlbmVyc1tpXS5mbiwgdW5kZWZpbmVkLCB0cnVlKTtcblxuICAgICAgc3dpdGNoIChsZW4pIHtcbiAgICAgICAgY2FzZSAxOiBsaXN0ZW5lcnNbaV0uZm4uY2FsbChsaXN0ZW5lcnNbaV0uY29udGV4dCk7IGJyZWFrO1xuICAgICAgICBjYXNlIDI6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhMSk7IGJyZWFrO1xuICAgICAgICBjYXNlIDM6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhMSwgYTIpOyBicmVhaztcbiAgICAgICAgY2FzZSA0OiBsaXN0ZW5lcnNbaV0uZm4uY2FsbChsaXN0ZW5lcnNbaV0uY29udGV4dCwgYTEsIGEyLCBhMyk7IGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIGlmICghYXJncykgZm9yIChqID0gMSwgYXJncyA9IG5ldyBBcnJheShsZW4gLTEpOyBqIDwgbGVuOyBqKyspIHtcbiAgICAgICAgICAgIGFyZ3NbaiAtIDFdID0gYXJndW1lbnRzW2pdO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGxpc3RlbmVyc1tpXS5mbi5hcHBseShsaXN0ZW5lcnNbaV0uY29udGV4dCwgYXJncyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59O1xuXG4vKipcbiAqIEFkZCBhIGxpc3RlbmVyIGZvciBhIGdpdmVuIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBsaXN0ZW5lciBmdW5jdGlvbi5cbiAqIEBwYXJhbSB7Kn0gW2NvbnRleHQ9dGhpc10gVGhlIGNvbnRleHQgdG8gaW52b2tlIHRoZSBsaXN0ZW5lciB3aXRoLlxuICogQHJldHVybnMge0V2ZW50RW1pdHRlcn0gYHRoaXNgLlxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLm9uID0gZnVuY3Rpb24gb24oZXZlbnQsIGZuLCBjb250ZXh0KSB7XG4gIHJldHVybiBhZGRMaXN0ZW5lcih0aGlzLCBldmVudCwgZm4sIGNvbnRleHQsIGZhbHNlKTtcbn07XG5cbi8qKlxuICogQWRkIGEgb25lLXRpbWUgbGlzdGVuZXIgZm9yIGEgZ2l2ZW4gZXZlbnQuXG4gKlxuICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uLlxuICogQHBhcmFtIHsqfSBbY29udGV4dD10aGlzXSBUaGUgY29udGV4dCB0byBpbnZva2UgdGhlIGxpc3RlbmVyIHdpdGguXG4gKiBAcmV0dXJucyB7RXZlbnRFbWl0dGVyfSBgdGhpc2AuXG4gKiBAcHVibGljXG4gKi9cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUub25jZSA9IGZ1bmN0aW9uIG9uY2UoZXZlbnQsIGZuLCBjb250ZXh0KSB7XG4gIHJldHVybiBhZGRMaXN0ZW5lcih0aGlzLCBldmVudCwgZm4sIGNvbnRleHQsIHRydWUpO1xufTtcblxuLyoqXG4gKiBSZW1vdmUgdGhlIGxpc3RlbmVycyBvZiBhIGdpdmVuIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIE9ubHkgcmVtb3ZlIHRoZSBsaXN0ZW5lcnMgdGhhdCBtYXRjaCB0aGlzIGZ1bmN0aW9uLlxuICogQHBhcmFtIHsqfSBjb250ZXh0IE9ubHkgcmVtb3ZlIHRoZSBsaXN0ZW5lcnMgdGhhdCBoYXZlIHRoaXMgY29udGV4dC5cbiAqIEBwYXJhbSB7Qm9vbGVhbn0gb25jZSBPbmx5IHJlbW92ZSBvbmUtdGltZSBsaXN0ZW5lcnMuXG4gKiBAcmV0dXJucyB7RXZlbnRFbWl0dGVyfSBgdGhpc2AuXG4gKiBAcHVibGljXG4gKi9cbkV2ZW50RW1pdHRlci5wcm90b3R5cGUucmVtb3ZlTGlzdGVuZXIgPSBmdW5jdGlvbiByZW1vdmVMaXN0ZW5lcihldmVudCwgZm4sIGNvbnRleHQsIG9uY2UpIHtcbiAgdmFyIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnQ7XG5cbiAgaWYgKCF0aGlzLl9ldmVudHNbZXZ0XSkgcmV0dXJuIHRoaXM7XG4gIGlmICghZm4pIHtcbiAgICBjbGVhckV2ZW50KHRoaXMsIGV2dCk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICB2YXIgbGlzdGVuZXJzID0gdGhpcy5fZXZlbnRzW2V2dF07XG5cbiAgaWYgKGxpc3RlbmVycy5mbikge1xuICAgIGlmIChcbiAgICAgIGxpc3RlbmVycy5mbiA9PT0gZm4gJiZcbiAgICAgICghb25jZSB8fCBsaXN0ZW5lcnMub25jZSkgJiZcbiAgICAgICghY29udGV4dCB8fCBsaXN0ZW5lcnMuY29udGV4dCA9PT0gY29udGV4dClcbiAgICApIHtcbiAgICAgIGNsZWFyRXZlbnQodGhpcywgZXZ0KTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgZm9yICh2YXIgaSA9IDAsIGV2ZW50cyA9IFtdLCBsZW5ndGggPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChcbiAgICAgICAgbGlzdGVuZXJzW2ldLmZuICE9PSBmbiB8fFxuICAgICAgICAob25jZSAmJiAhbGlzdGVuZXJzW2ldLm9uY2UpIHx8XG4gICAgICAgIChjb250ZXh0ICYmIGxpc3RlbmVyc1tpXS5jb250ZXh0ICE9PSBjb250ZXh0KVxuICAgICAgKSB7XG4gICAgICAgIGV2ZW50cy5wdXNoKGxpc3RlbmVyc1tpXSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy9cbiAgICAvLyBSZXNldCB0aGUgYXJyYXksIG9yIHJlbW92ZSBpdCBjb21wbGV0ZWx5IGlmIHdlIGhhdmUgbm8gbW9yZSBsaXN0ZW5lcnMuXG4gICAgLy9cbiAgICBpZiAoZXZlbnRzLmxlbmd0aCkgdGhpcy5fZXZlbnRzW2V2dF0gPSBldmVudHMubGVuZ3RoID09PSAxID8gZXZlbnRzWzBdIDogZXZlbnRzO1xuICAgIGVsc2UgY2xlYXJFdmVudCh0aGlzLCBldnQpO1xuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIFJlbW92ZSBhbGwgbGlzdGVuZXJzLCBvciB0aG9zZSBvZiB0aGUgc3BlY2lmaWVkIGV2ZW50LlxuICpcbiAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBbZXZlbnRdIFRoZSBldmVudCBuYW1lLlxuICogQHJldHVybnMge0V2ZW50RW1pdHRlcn0gYHRoaXNgLlxuICogQHB1YmxpY1xuICovXG5FdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUFsbExpc3RlbmVycyA9IGZ1bmN0aW9uIHJlbW92ZUFsbExpc3RlbmVycyhldmVudCkge1xuICB2YXIgZXZ0O1xuXG4gIGlmIChldmVudCkge1xuICAgIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnQ7XG4gICAgaWYgKHRoaXMuX2V2ZW50c1tldnRdKSBjbGVhckV2ZW50KHRoaXMsIGV2dCk7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5fZXZlbnRzID0gbmV3IEV2ZW50cygpO1xuICAgIHRoaXMuX2V2ZW50c0NvdW50ID0gMDtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuLy9cbi8vIEFsaWFzIG1ldGhvZHMgbmFtZXMgYmVjYXVzZSBwZW9wbGUgcm9sbCBsaWtlIHRoYXQuXG4vL1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5vZmYgPSBFdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUxpc3RlbmVyO1xuRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lciA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUub247XG5cbi8vXG4vLyBFeHBvc2UgdGhlIHByZWZpeC5cbi8vXG5FdmVudEVtaXR0ZXIucHJlZml4ZWQgPSBwcmVmaXg7XG5cbi8vXG4vLyBBbGxvdyBgRXZlbnRFbWl0dGVyYCB0byBiZSBpbXBvcnRlZCBhcyBtb2R1bGUgbmFtZXNwYWNlLlxuLy9cbkV2ZW50RW1pdHRlci5FdmVudEVtaXR0ZXIgPSBFdmVudEVtaXR0ZXI7XG5cbi8vXG4vLyBFeHBvc2UgdGhlIG1vZHVsZS5cbi8vXG5pZiAodHJ1ZSkge1xuICBtb2R1bGUuZXhwb3J0cyA9IEV2ZW50RW1pdHRlcjtcbn1cblxuXG4vKioqLyB9KSxcblxuLyoqKi8gXCIuL25vZGVfbW9kdWxlcy91cmwtdG9vbGtpdC9zcmMvdXJsLXRvb2xraXQuanNcIjpcbi8qISoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqISpcXFxuICAhKioqIC4vbm9kZV9tb2R1bGVzL3VybC10b29sa2l0L3NyYy91cmwtdG9vbGtpdC5qcyAqKiohXG4gIFxcKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKiEgbm8gc3RhdGljIGV4cG9ydHMgZm91bmQgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IE1vZHVsZSBpcyBub3QgYW4gRUNNQVNjcmlwdCBtb2R1bGUgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIGV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuLy8gc2VlIGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9yZmMxODA4XG5cbihmdW5jdGlvbiAocm9vdCkge1xuICB2YXIgVVJMX1JFR0VYID0gL14oKD86W2EtekEtWjAtOStcXC0uXSs6KT8pKFxcL1xcL1teXFwvPyNdKik/KCg/OlteXFwvPyNdKlxcLykqW147PyNdKik/KDtbXj8jXSopPyhcXD9bXiNdKik/KCMuKik/JC87XG4gIHZhciBGSVJTVF9TRUdNRU5UX1JFR0VYID0gL14oW15cXC8/I10qKSguKikkLztcbiAgdmFyIFNMQVNIX0RPVF9SRUdFWCA9IC8oPzpcXC98XilcXC4oPz1cXC8pL2c7XG4gIHZhciBTTEFTSF9ET1RfRE9UX1JFR0VYID0gLyg/OlxcL3xeKVxcLlxcLlxcLyg/IVxcLlxcLlxcLylbXlxcL10qKD89XFwvKS9nO1xuXG4gIHZhciBVUkxUb29sa2l0ID0ge1xuICAgIC8vIElmIG9wdHMuYWx3YXlzTm9ybWFsaXplIGlzIHRydWUgdGhlbiB0aGUgcGF0aCB3aWxsIGFsd2F5cyBiZSBub3JtYWxpemVkIGV2ZW4gd2hlbiBpdCBzdGFydHMgd2l0aCAvIG9yIC8vXG4gICAgLy8gRS5nXG4gICAgLy8gV2l0aCBvcHRzLmFsd2F5c05vcm1hbGl6ZSA9IGZhbHNlIChkZWZhdWx0LCBzcGVjIGNvbXBsaWFudClcbiAgICAvLyBodHRwOi8vYS5jb20vYi9jZCArIC9lL2YvLi4vZyA9PiBodHRwOi8vYS5jb20vZS9mLy4uL2dcbiAgICAvLyBXaXRoIG9wdHMuYWx3YXlzTm9ybWFsaXplID0gdHJ1ZSAobm90IHNwZWMgY29tcGxpYW50KVxuICAgIC8vIGh0dHA6Ly9hLmNvbS9iL2NkICsgL2UvZi8uLi9nID0+IGh0dHA6Ly9hLmNvbS9lL2dcbiAgICBidWlsZEFic29sdXRlVVJMOiBmdW5jdGlvbiAoYmFzZVVSTCwgcmVsYXRpdmVVUkwsIG9wdHMpIHtcbiAgICAgIG9wdHMgPSBvcHRzIHx8IHt9O1xuICAgICAgLy8gcmVtb3ZlIGFueSByZW1haW5pbmcgc3BhY2UgYW5kIENSTEZcbiAgICAgIGJhc2VVUkwgPSBiYXNlVVJMLnRyaW0oKTtcbiAgICAgIHJlbGF0aXZlVVJMID0gcmVsYXRpdmVVUkwudHJpbSgpO1xuICAgICAgaWYgKCFyZWxhdGl2ZVVSTCkge1xuICAgICAgICAvLyAyYSkgSWYgdGhlIGVtYmVkZGVkIFVSTCBpcyBlbnRpcmVseSBlbXB0eSwgaXQgaW5oZXJpdHMgdGhlXG4gICAgICAgIC8vIGVudGlyZSBiYXNlIFVSTCAoaS5lLiwgaXMgc2V0IGVxdWFsIHRvIHRoZSBiYXNlIFVSTClcbiAgICAgICAgLy8gYW5kIHdlIGFyZSBkb25lLlxuICAgICAgICBpZiAoIW9wdHMuYWx3YXlzTm9ybWFsaXplKSB7XG4gICAgICAgICAgcmV0dXJuIGJhc2VVUkw7XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGJhc2VQYXJ0c0Zvck5vcm1hbGlzZSA9IFVSTFRvb2xraXQucGFyc2VVUkwoYmFzZVVSTCk7XG4gICAgICAgIGlmICghYmFzZVBhcnRzRm9yTm9ybWFsaXNlKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFcnJvciB0cnlpbmcgdG8gcGFyc2UgYmFzZSBVUkwuJyk7XG4gICAgICAgIH1cbiAgICAgICAgYmFzZVBhcnRzRm9yTm9ybWFsaXNlLnBhdGggPSBVUkxUb29sa2l0Lm5vcm1hbGl6ZVBhdGgoXG4gICAgICAgICAgYmFzZVBhcnRzRm9yTm9ybWFsaXNlLnBhdGhcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIFVSTFRvb2xraXQuYnVpbGRVUkxGcm9tUGFydHMoYmFzZVBhcnRzRm9yTm9ybWFsaXNlKTtcbiAgICAgIH1cbiAgICAgIHZhciByZWxhdGl2ZVBhcnRzID0gVVJMVG9vbGtpdC5wYXJzZVVSTChyZWxhdGl2ZVVSTCk7XG4gICAgICBpZiAoIXJlbGF0aXZlUGFydHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFcnJvciB0cnlpbmcgdG8gcGFyc2UgcmVsYXRpdmUgVVJMLicpO1xuICAgICAgfVxuICAgICAgaWYgKHJlbGF0aXZlUGFydHMuc2NoZW1lKSB7XG4gICAgICAgIC8vIDJiKSBJZiB0aGUgZW1iZWRkZWQgVVJMIHN0YXJ0cyB3aXRoIGEgc2NoZW1lIG5hbWUsIGl0IGlzXG4gICAgICAgIC8vIGludGVycHJldGVkIGFzIGFuIGFic29sdXRlIFVSTCBhbmQgd2UgYXJlIGRvbmUuXG4gICAgICAgIGlmICghb3B0cy5hbHdheXNOb3JtYWxpemUpIHtcbiAgICAgICAgICByZXR1cm4gcmVsYXRpdmVVUkw7XG4gICAgICAgIH1cbiAgICAgICAgcmVsYXRpdmVQYXJ0cy5wYXRoID0gVVJMVG9vbGtpdC5ub3JtYWxpemVQYXRoKHJlbGF0aXZlUGFydHMucGF0aCk7XG4gICAgICAgIHJldHVybiBVUkxUb29sa2l0LmJ1aWxkVVJMRnJvbVBhcnRzKHJlbGF0aXZlUGFydHMpO1xuICAgICAgfVxuICAgICAgdmFyIGJhc2VQYXJ0cyA9IFVSTFRvb2xraXQucGFyc2VVUkwoYmFzZVVSTCk7XG4gICAgICBpZiAoIWJhc2VQYXJ0cykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Vycm9yIHRyeWluZyB0byBwYXJzZSBiYXNlIFVSTC4nKTtcbiAgICAgIH1cbiAgICAgIGlmICghYmFzZVBhcnRzLm5ldExvYyAmJiBiYXNlUGFydHMucGF0aCAmJiBiYXNlUGFydHMucGF0aFswXSAhPT0gJy8nKSB7XG4gICAgICAgIC8vIElmIG5ldExvYyBtaXNzaW5nIGFuZCBwYXRoIGRvZXNuJ3Qgc3RhcnQgd2l0aCAnLycsIGFzc3VtZSBldmVydGhpbmcgYmVmb3JlIHRoZSBmaXJzdCAnLycgaXMgdGhlIG5ldExvY1xuICAgICAgICAvLyBUaGlzIGNhdXNlcyAnZXhhbXBsZS5jb20vYScgdG8gYmUgaGFuZGxlZCBhcyAnLy9leGFtcGxlLmNvbS9hJyBpbnN0ZWFkIG9mICcvZXhhbXBsZS5jb20vYSdcbiAgICAgICAgdmFyIHBhdGhQYXJ0cyA9IEZJUlNUX1NFR01FTlRfUkVHRVguZXhlYyhiYXNlUGFydHMucGF0aCk7XG4gICAgICAgIGJhc2VQYXJ0cy5uZXRMb2MgPSBwYXRoUGFydHNbMV07XG4gICAgICAgIGJhc2VQYXJ0cy5wYXRoID0gcGF0aFBhcnRzWzJdO1xuICAgICAgfVxuICAgICAgaWYgKGJhc2VQYXJ0cy5uZXRMb2MgJiYgIWJhc2VQYXJ0cy5wYXRoKSB7XG4gICAgICAgIGJhc2VQYXJ0cy5wYXRoID0gJy8nO1xuICAgICAgfVxuICAgICAgdmFyIGJ1aWx0UGFydHMgPSB7XG4gICAgICAgIC8vIDJjKSBPdGhlcndpc2UsIHRoZSBlbWJlZGRlZCBVUkwgaW5oZXJpdHMgdGhlIHNjaGVtZSBvZlxuICAgICAgICAvLyB0aGUgYmFzZSBVUkwuXG4gICAgICAgIHNjaGVtZTogYmFzZVBhcnRzLnNjaGVtZSxcbiAgICAgICAgbmV0TG9jOiByZWxhdGl2ZVBhcnRzLm5ldExvYyxcbiAgICAgICAgcGF0aDogbnVsbCxcbiAgICAgICAgcGFyYW1zOiByZWxhdGl2ZVBhcnRzLnBhcmFtcyxcbiAgICAgICAgcXVlcnk6IHJlbGF0aXZlUGFydHMucXVlcnksXG4gICAgICAgIGZyYWdtZW50OiByZWxhdGl2ZVBhcnRzLmZyYWdtZW50LFxuICAgICAgfTtcbiAgICAgIGlmICghcmVsYXRpdmVQYXJ0cy5uZXRMb2MpIHtcbiAgICAgICAgLy8gMykgSWYgdGhlIGVtYmVkZGVkIFVSTCdzIDxuZXRfbG9jPiBpcyBub24tZW1wdHksIHdlIHNraXAgdG9cbiAgICAgICAgLy8gU3RlcCA3LiAgT3RoZXJ3aXNlLCB0aGUgZW1iZWRkZWQgVVJMIGluaGVyaXRzIHRoZSA8bmV0X2xvYz5cbiAgICAgICAgLy8gKGlmIGFueSkgb2YgdGhlIGJhc2UgVVJMLlxuICAgICAgICBidWlsdFBhcnRzLm5ldExvYyA9IGJhc2VQYXJ0cy5uZXRMb2M7XG4gICAgICAgIC8vIDQpIElmIHRoZSBlbWJlZGRlZCBVUkwgcGF0aCBpcyBwcmVjZWRlZCBieSBhIHNsYXNoIFwiL1wiLCB0aGVcbiAgICAgICAgLy8gcGF0aCBpcyBub3QgcmVsYXRpdmUgYW5kIHdlIHNraXAgdG8gU3RlcCA3LlxuICAgICAgICBpZiAocmVsYXRpdmVQYXJ0cy5wYXRoWzBdICE9PSAnLycpIHtcbiAgICAgICAgICBpZiAoIXJlbGF0aXZlUGFydHMucGF0aCkge1xuICAgICAgICAgICAgLy8gNSkgSWYgdGhlIGVtYmVkZGVkIFVSTCBwYXRoIGlzIGVtcHR5IChhbmQgbm90IHByZWNlZGVkIGJ5IGFcbiAgICAgICAgICAgIC8vIHNsYXNoKSwgdGhlbiB0aGUgZW1iZWRkZWQgVVJMIGluaGVyaXRzIHRoZSBiYXNlIFVSTCBwYXRoXG4gICAgICAgICAgICBidWlsdFBhcnRzLnBhdGggPSBiYXNlUGFydHMucGF0aDtcbiAgICAgICAgICAgIC8vIDVhKSBpZiB0aGUgZW1iZWRkZWQgVVJMJ3MgPHBhcmFtcz4gaXMgbm9uLWVtcHR5LCB3ZSBza2lwIHRvXG4gICAgICAgICAgICAvLyBzdGVwIDc7IG90aGVyd2lzZSwgaXQgaW5oZXJpdHMgdGhlIDxwYXJhbXM+IG9mIHRoZSBiYXNlXG4gICAgICAgICAgICAvLyBVUkwgKGlmIGFueSkgYW5kXG4gICAgICAgICAgICBpZiAoIXJlbGF0aXZlUGFydHMucGFyYW1zKSB7XG4gICAgICAgICAgICAgIGJ1aWx0UGFydHMucGFyYW1zID0gYmFzZVBhcnRzLnBhcmFtcztcbiAgICAgICAgICAgICAgLy8gNWIpIGlmIHRoZSBlbWJlZGRlZCBVUkwncyA8cXVlcnk+IGlzIG5vbi1lbXB0eSwgd2Ugc2tpcCB0b1xuICAgICAgICAgICAgICAvLyBzdGVwIDc7IG90aGVyd2lzZSwgaXQgaW5oZXJpdHMgdGhlIDxxdWVyeT4gb2YgdGhlIGJhc2VcbiAgICAgICAgICAgICAgLy8gVVJMIChpZiBhbnkpIGFuZCB3ZSBza2lwIHRvIHN0ZXAgNy5cbiAgICAgICAgICAgICAgaWYgKCFyZWxhdGl2ZVBhcnRzLnF1ZXJ5KSB7XG4gICAgICAgICAgICAgICAgYnVpbHRQYXJ0cy5xdWVyeSA9IGJhc2VQYXJ0cy5xdWVyeTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyA2KSBUaGUgbGFzdCBzZWdtZW50IG9mIHRoZSBiYXNlIFVSTCdzIHBhdGggKGFueXRoaW5nXG4gICAgICAgICAgICAvLyBmb2xsb3dpbmcgdGhlIHJpZ2h0bW9zdCBzbGFzaCBcIi9cIiwgb3IgdGhlIGVudGlyZSBwYXRoIGlmIG5vXG4gICAgICAgICAgICAvLyBzbGFzaCBpcyBwcmVzZW50KSBpcyByZW1vdmVkIGFuZCB0aGUgZW1iZWRkZWQgVVJMJ3MgcGF0aCBpc1xuICAgICAgICAgICAgLy8gYXBwZW5kZWQgaW4gaXRzIHBsYWNlLlxuICAgICAgICAgICAgdmFyIGJhc2VVUkxQYXRoID0gYmFzZVBhcnRzLnBhdGg7XG4gICAgICAgICAgICB2YXIgbmV3UGF0aCA9XG4gICAgICAgICAgICAgIGJhc2VVUkxQYXRoLnN1YnN0cmluZygwLCBiYXNlVVJMUGF0aC5sYXN0SW5kZXhPZignLycpICsgMSkgK1xuICAgICAgICAgICAgICByZWxhdGl2ZVBhcnRzLnBhdGg7XG4gICAgICAgICAgICBidWlsdFBhcnRzLnBhdGggPSBVUkxUb29sa2l0Lm5vcm1hbGl6ZVBhdGgobmV3UGF0aCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoYnVpbHRQYXJ0cy5wYXRoID09PSBudWxsKSB7XG4gICAgICAgIGJ1aWx0UGFydHMucGF0aCA9IG9wdHMuYWx3YXlzTm9ybWFsaXplXG4gICAgICAgICAgPyBVUkxUb29sa2l0Lm5vcm1hbGl6ZVBhdGgocmVsYXRpdmVQYXJ0cy5wYXRoKVxuICAgICAgICAgIDogcmVsYXRpdmVQYXJ0cy5wYXRoO1xuICAgICAgfVxuICAgICAgcmV0dXJuIFVSTFRvb2xraXQuYnVpbGRVUkxGcm9tUGFydHMoYnVpbHRQYXJ0cyk7XG4gICAgfSxcbiAgICBwYXJzZVVSTDogZnVuY3Rpb24gKHVybCkge1xuICAgICAgdmFyIHBhcnRzID0gVVJMX1JFR0VYLmV4ZWModXJsKTtcbiAgICAgIGlmICghcGFydHMpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzY2hlbWU6IHBhcnRzWzFdIHx8ICcnLFxuICAgICAgICBuZXRMb2M6IHBhcnRzWzJdIHx8ICcnLFxuICAgICAgICBwYXRoOiBwYXJ0c1szXSB8fCAnJyxcbiAgICAgICAgcGFyYW1zOiBwYXJ0c1s0XSB8fCAnJyxcbiAgICAgICAgcXVlcnk6IHBhcnRzWzVdIHx8ICcnLFxuICAgICAgICBmcmFnbWVudDogcGFydHNbNl0gfHwgJycsXG4gICAgICB9O1xuICAgIH0sXG4gICAgbm9ybWFsaXplUGF0aDogZnVuY3Rpb24gKHBhdGgpIHtcbiAgICAgIC8vIFRoZSBmb2xsb3dpbmcgb3BlcmF0aW9ucyBhcmVcbiAgICAgIC8vIHRoZW4gYXBwbGllZCwgaW4gb3JkZXIsIHRvIHRoZSBuZXcgcGF0aDpcbiAgICAgIC8vIDZhKSBBbGwgb2NjdXJyZW5jZXMgb2YgXCIuL1wiLCB3aGVyZSBcIi5cIiBpcyBhIGNvbXBsZXRlIHBhdGhcbiAgICAgIC8vIHNlZ21lbnQsIGFyZSByZW1vdmVkLlxuICAgICAgLy8gNmIpIElmIHRoZSBwYXRoIGVuZHMgd2l0aCBcIi5cIiBhcyBhIGNvbXBsZXRlIHBhdGggc2VnbWVudCxcbiAgICAgIC8vIHRoYXQgXCIuXCIgaXMgcmVtb3ZlZC5cbiAgICAgIHBhdGggPSBwYXRoLnNwbGl0KCcnKS5yZXZlcnNlKCkuam9pbignJykucmVwbGFjZShTTEFTSF9ET1RfUkVHRVgsICcnKTtcbiAgICAgIC8vIDZjKSBBbGwgb2NjdXJyZW5jZXMgb2YgXCI8c2VnbWVudD4vLi4vXCIsIHdoZXJlIDxzZWdtZW50PiBpcyBhXG4gICAgICAvLyBjb21wbGV0ZSBwYXRoIHNlZ21lbnQgbm90IGVxdWFsIHRvIFwiLi5cIiwgYXJlIHJlbW92ZWQuXG4gICAgICAvLyBSZW1vdmFsIG9mIHRoZXNlIHBhdGggc2VnbWVudHMgaXMgcGVyZm9ybWVkIGl0ZXJhdGl2ZWx5LFxuICAgICAgLy8gcmVtb3ZpbmcgdGhlIGxlZnRtb3N0IG1hdGNoaW5nIHBhdHRlcm4gb24gZWFjaCBpdGVyYXRpb24sXG4gICAgICAvLyB1bnRpbCBubyBtYXRjaGluZyBwYXR0ZXJuIHJlbWFpbnMuXG4gICAgICAvLyA2ZCkgSWYgdGhlIHBhdGggZW5kcyB3aXRoIFwiPHNlZ21lbnQ+Ly4uXCIsIHdoZXJlIDxzZWdtZW50PiBpcyBhXG4gICAgICAvLyBjb21wbGV0ZSBwYXRoIHNlZ21lbnQgbm90IGVxdWFsIHRvIFwiLi5cIiwgdGhhdFxuICAgICAgLy8gXCI8c2VnbWVudD4vLi5cIiBpcyByZW1vdmVkLlxuICAgICAgd2hpbGUgKFxuICAgICAgICBwYXRoLmxlbmd0aCAhPT0gKHBhdGggPSBwYXRoLnJlcGxhY2UoU0xBU0hfRE9UX0RPVF9SRUdFWCwgJycpKS5sZW5ndGhcbiAgICAgICkge31cbiAgICAgIHJldHVybiBwYXRoLnNwbGl0KCcnKS5yZXZlcnNlKCkuam9pbignJyk7XG4gICAgfSxcbiAgICBidWlsZFVSTEZyb21QYXJ0czogZnVuY3Rpb24gKHBhcnRzKSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICBwYXJ0cy5zY2hlbWUgK1xuICAgICAgICBwYXJ0cy5uZXRMb2MgK1xuICAgICAgICBwYXJ0cy5wYXRoICtcbiAgICAgICAgcGFydHMucGFyYW1zICtcbiAgICAgICAgcGFydHMucXVlcnkgK1xuICAgICAgICBwYXJ0cy5mcmFnbWVudFxuICAgICAgKTtcbiAgICB9LFxuICB9O1xuXG4gIGlmICh0cnVlKVxuICAgIG1vZHVsZS5leHBvcnRzID0gVVJMVG9vbGtpdDtcbiAgZWxzZSB7fVxufSkodGhpcyk7XG5cblxuLyoqKi8gfSksXG5cbi8qKiovIFwiLi9ub2RlX21vZHVsZXMvd2Vid29ya2lmeS13ZWJwYWNrL2luZGV4LmpzXCI6XG4vKiEqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL25vZGVfbW9kdWxlcy93ZWJ3b3JraWZ5LXdlYnBhY2svaW5kZXguanMgKioqIVxuICBcXCoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyohIG5vIHN0YXRpYyBleHBvcnRzIGZvdW5kICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBNb2R1bGUgaXMgbm90IGFuIEVDTUFTY3JpcHQgbW9kdWxlICovXG4vKioqLyAoZnVuY3Rpb24obW9kdWxlLCBleHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7XG5cbmZ1bmN0aW9uIHdlYnBhY2tCb290c3RyYXBGdW5jIChtb2R1bGVzKSB7XG4vKioqKioqLyAgLy8gVGhlIG1vZHVsZSBjYWNoZVxuLyoqKioqKi8gIHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbi8qKioqKiovICAvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuLyoqKioqKi8gIGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuLyoqKioqKi8gICAgLy8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4vKioqKioqLyAgICBpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSlcbi8qKioqKiovICAgICAgcmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG5cbi8qKioqKiovICAgIC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4vKioqKioqLyAgICB2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4vKioqKioqLyAgICAgIGk6IG1vZHVsZUlkLFxuLyoqKioqKi8gICAgICBsOiBmYWxzZSxcbi8qKioqKiovICAgICAgZXhwb3J0czoge31cbi8qKioqKiovICAgIH07XG5cbi8qKioqKiovICAgIC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuLyoqKioqKi8gICAgbW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbi8qKioqKiovICAgIC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbi8qKioqKiovICAgIG1vZHVsZS5sID0gdHJ1ZTtcblxuLyoqKioqKi8gICAgLy8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbi8qKioqKiovICAgIHJldHVybiBtb2R1bGUuZXhwb3J0cztcbi8qKioqKiovICB9XG5cbi8qKioqKiovICAvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuLyoqKioqKi8gIF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbi8qKioqKiovICAvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuLyoqKioqKi8gIF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbi8qKioqKiovICAvLyBpZGVudGl0eSBmdW5jdGlvbiBmb3IgY2FsbGluZyBoYXJtb255IGltcG9ydHMgd2l0aCB0aGUgY29ycmVjdCBjb250ZXh0XG4vKioqKioqLyAgX193ZWJwYWNrX3JlcXVpcmVfXy5pID0gZnVuY3Rpb24odmFsdWUpIHsgcmV0dXJuIHZhbHVlOyB9O1xuXG4vKioqKioqLyAgLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4vKioqKioqLyAgX193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4vKioqKioqLyAgICBpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4vKioqKioqLyAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7XG4vKioqKioqLyAgICAgICAgY29uZmlndXJhYmxlOiBmYWxzZSxcbi8qKioqKiovICAgICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuLyoqKioqKi8gICAgICAgIGdldDogZ2V0dGVyXG4vKioqKioqLyAgICAgIH0pO1xuLyoqKioqKi8gICAgfVxuLyoqKioqKi8gIH07XG5cbi8qKioqKiovICAvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4vKioqKioqLyAgX193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuLyoqKioqKi8gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbi8qKioqKiovICB9O1xuXG4vKioqKioqLyAgLy8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbi8qKioqKiovICBfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbi8qKioqKiovICAgIHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuLyoqKioqKi8gICAgICBmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuLyoqKioqKi8gICAgICBmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuLyoqKioqKi8gICAgX193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuLyoqKioqKi8gICAgcmV0dXJuIGdldHRlcjtcbi8qKioqKiovICB9O1xuXG4vKioqKioqLyAgLy8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4vKioqKioqLyAgX193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4vKioqKioqLyAgLy8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbi8qKioqKiovICBfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIi9cIjtcblxuLyoqKioqKi8gIC8vIG9uIGVycm9yIGZ1bmN0aW9uIGZvciBhc3luYyBsb2FkaW5nXG4vKioqKioqLyAgX193ZWJwYWNrX3JlcXVpcmVfXy5vZSA9IGZ1bmN0aW9uKGVycikgeyBjb25zb2xlLmVycm9yKGVycik7IHRocm93IGVycjsgfTtcblxuICB2YXIgZiA9IF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gRU5UUllfTU9EVUxFKVxuICByZXR1cm4gZi5kZWZhdWx0IHx8IGYgLy8gdHJ5IHRvIGNhbGwgZGVmYXVsdCBpZiBkZWZpbmVkIHRvIGFsc28gc3VwcG9ydCBiYWJlbCBlc21vZHVsZSBleHBvcnRzXG59XG5cbnZhciBtb2R1bGVOYW1lUmVxRXhwID0gJ1tcXFxcLnxcXFxcLXxcXFxcK3xcXFxcd3xcXC98QF0rJ1xudmFyIGRlcGVuZGVuY3lSZWdFeHAgPSAnXFxcXChcXFxccyooXFwvXFxcXCouKj9cXFxcKlxcLyk/XFxcXHMqLio/KCcgKyBtb2R1bGVOYW1lUmVxRXhwICsgJykuKj9cXFxcKScgLy8gYWRkaXRpb25hbCBjaGFycyB3aGVuIG91dHB1dC5wYXRoaW5mbyBpcyB0cnVlXG5cbi8vIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzI1OTM2NjEvMTMwNDQyXG5mdW5jdGlvbiBxdW90ZVJlZ0V4cCAoc3RyKSB7XG4gIHJldHVybiAoc3RyICsgJycpLnJlcGxhY2UoL1suPyorXiRbXFxdXFxcXCgpe318LV0vZywgJ1xcXFwkJicpXG59XG5cbmZ1bmN0aW9uIGlzTnVtZXJpYyhuKSB7XG4gIHJldHVybiAhaXNOYU4oMSAqIG4pOyAvLyAxICogbiBjb252ZXJ0cyBpbnRlZ2VycywgaW50ZWdlcnMgYXMgc3RyaW5nIChcIjEyM1wiKSwgMWUzIGFuZCBcIjFlM1wiIHRvIGludGVnZXJzIGFuZCBzdHJpbmdzIHRvIE5hTlxufVxuXG5mdW5jdGlvbiBnZXRNb2R1bGVEZXBlbmRlbmNpZXMgKHNvdXJjZXMsIG1vZHVsZSwgcXVldWVOYW1lKSB7XG4gIHZhciByZXR2YWwgPSB7fVxuICByZXR2YWxbcXVldWVOYW1lXSA9IFtdXG5cbiAgdmFyIGZuU3RyaW5nID0gbW9kdWxlLnRvU3RyaW5nKClcbiAgdmFyIHdyYXBwZXJTaWduYXR1cmUgPSBmblN0cmluZy5tYXRjaCgvXmZ1bmN0aW9uXFxzP1xcdypcXChcXHcrLFxccypcXHcrLFxccyooXFx3KylcXCkvKVxuICBpZiAoIXdyYXBwZXJTaWduYXR1cmUpIHJldHVybiByZXR2YWxcbiAgdmFyIHdlYnBhY2tSZXF1aXJlTmFtZSA9IHdyYXBwZXJTaWduYXR1cmVbMV1cblxuICAvLyBtYWluIGJ1bmRsZSBkZXBzXG4gIHZhciByZSA9IG5ldyBSZWdFeHAoJyhcXFxcXFxcXG58XFxcXFcpJyArIHF1b3RlUmVnRXhwKHdlYnBhY2tSZXF1aXJlTmFtZSkgKyBkZXBlbmRlbmN5UmVnRXhwLCAnZycpXG4gIHZhciBtYXRjaFxuICB3aGlsZSAoKG1hdGNoID0gcmUuZXhlYyhmblN0cmluZykpKSB7XG4gICAgaWYgKG1hdGNoWzNdID09PSAnZGxsLXJlZmVyZW5jZScpIGNvbnRpbnVlXG4gICAgcmV0dmFsW3F1ZXVlTmFtZV0ucHVzaChtYXRjaFszXSlcbiAgfVxuXG4gIC8vIGRsbCBkZXBzXG4gIHJlID0gbmV3IFJlZ0V4cCgnXFxcXCgnICsgcXVvdGVSZWdFeHAod2VicGFja1JlcXVpcmVOYW1lKSArICdcXFxcKFwiKGRsbC1yZWZlcmVuY2VcXFxccygnICsgbW9kdWxlTmFtZVJlcUV4cCArICcpKVwiXFxcXClcXFxcKScgKyBkZXBlbmRlbmN5UmVnRXhwLCAnZycpXG4gIHdoaWxlICgobWF0Y2ggPSByZS5leGVjKGZuU3RyaW5nKSkpIHtcbiAgICBpZiAoIXNvdXJjZXNbbWF0Y2hbMl1dKSB7XG4gICAgICByZXR2YWxbcXVldWVOYW1lXS5wdXNoKG1hdGNoWzFdKVxuICAgICAgc291cmNlc1ttYXRjaFsyXV0gPSBfX3dlYnBhY2tfcmVxdWlyZV9fKG1hdGNoWzFdKS5tXG4gICAgfVxuICAgIHJldHZhbFttYXRjaFsyXV0gPSByZXR2YWxbbWF0Y2hbMl1dIHx8IFtdXG4gICAgcmV0dmFsW21hdGNoWzJdXS5wdXNoKG1hdGNoWzRdKVxuICB9XG5cbiAgLy8gY29udmVydCAxZTMgYmFjayB0byAxMDAwIC0gdGhpcyBjYW4gYmUgaW1wb3J0YW50IGFmdGVyIHVnbGlmeS1qcyBjb252ZXJ0ZWQgMTAwMCB0byAxZTNcbiAgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhyZXR2YWwpO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IGtleXMubGVuZ3RoOyBpKyspIHtcbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IHJldHZhbFtrZXlzW2ldXS5sZW5ndGg7IGorKykge1xuICAgICAgaWYgKGlzTnVtZXJpYyhyZXR2YWxba2V5c1tpXV1bal0pKSB7XG4gICAgICAgIHJldHZhbFtrZXlzW2ldXVtqXSA9IDEgKiByZXR2YWxba2V5c1tpXV1bal07XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHJldHZhbFxufVxuXG5mdW5jdGlvbiBoYXNWYWx1ZXNJblF1ZXVlcyAocXVldWVzKSB7XG4gIHZhciBrZXlzID0gT2JqZWN0LmtleXMocXVldWVzKVxuICByZXR1cm4ga2V5cy5yZWR1Y2UoZnVuY3Rpb24gKGhhc1ZhbHVlcywga2V5KSB7XG4gICAgcmV0dXJuIGhhc1ZhbHVlcyB8fCBxdWV1ZXNba2V5XS5sZW5ndGggPiAwXG4gIH0sIGZhbHNlKVxufVxuXG5mdW5jdGlvbiBnZXRSZXF1aXJlZE1vZHVsZXMgKHNvdXJjZXMsIG1vZHVsZUlkKSB7XG4gIHZhciBtb2R1bGVzUXVldWUgPSB7XG4gICAgbWFpbjogW21vZHVsZUlkXVxuICB9XG4gIHZhciByZXF1aXJlZE1vZHVsZXMgPSB7XG4gICAgbWFpbjogW11cbiAgfVxuICB2YXIgc2Vlbk1vZHVsZXMgPSB7XG4gICAgbWFpbjoge31cbiAgfVxuXG4gIHdoaWxlIChoYXNWYWx1ZXNJblF1ZXVlcyhtb2R1bGVzUXVldWUpKSB7XG4gICAgdmFyIHF1ZXVlcyA9IE9iamVjdC5rZXlzKG1vZHVsZXNRdWV1ZSlcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHF1ZXVlcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIHF1ZXVlTmFtZSA9IHF1ZXVlc1tpXVxuICAgICAgdmFyIHF1ZXVlID0gbW9kdWxlc1F1ZXVlW3F1ZXVlTmFtZV1cbiAgICAgIHZhciBtb2R1bGVUb0NoZWNrID0gcXVldWUucG9wKClcbiAgICAgIHNlZW5Nb2R1bGVzW3F1ZXVlTmFtZV0gPSBzZWVuTW9kdWxlc1txdWV1ZU5hbWVdIHx8IHt9XG4gICAgICBpZiAoc2Vlbk1vZHVsZXNbcXVldWVOYW1lXVttb2R1bGVUb0NoZWNrXSB8fCAhc291cmNlc1txdWV1ZU5hbWVdW21vZHVsZVRvQ2hlY2tdKSBjb250aW51ZVxuICAgICAgc2Vlbk1vZHVsZXNbcXVldWVOYW1lXVttb2R1bGVUb0NoZWNrXSA9IHRydWVcbiAgICAgIHJlcXVpcmVkTW9kdWxlc1txdWV1ZU5hbWVdID0gcmVxdWlyZWRNb2R1bGVzW3F1ZXVlTmFtZV0gfHwgW11cbiAgICAgIHJlcXVpcmVkTW9kdWxlc1txdWV1ZU5hbWVdLnB1c2gobW9kdWxlVG9DaGVjaylcbiAgICAgIHZhciBuZXdNb2R1bGVzID0gZ2V0TW9kdWxlRGVwZW5kZW5jaWVzKHNvdXJjZXMsIHNvdXJjZXNbcXVldWVOYW1lXVttb2R1bGVUb0NoZWNrXSwgcXVldWVOYW1lKVxuICAgICAgdmFyIG5ld01vZHVsZXNLZXlzID0gT2JqZWN0LmtleXMobmV3TW9kdWxlcylcbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbmV3TW9kdWxlc0tleXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgbW9kdWxlc1F1ZXVlW25ld01vZHVsZXNLZXlzW2pdXSA9IG1vZHVsZXNRdWV1ZVtuZXdNb2R1bGVzS2V5c1tqXV0gfHwgW11cbiAgICAgICAgbW9kdWxlc1F1ZXVlW25ld01vZHVsZXNLZXlzW2pdXSA9IG1vZHVsZXNRdWV1ZVtuZXdNb2R1bGVzS2V5c1tqXV0uY29uY2F0KG5ld01vZHVsZXNbbmV3TW9kdWxlc0tleXNbal1dKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXF1aXJlZE1vZHVsZXNcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAobW9kdWxlSWQsIG9wdGlvbnMpIHtcbiAgb3B0aW9ucyA9IG9wdGlvbnMgfHwge31cbiAgdmFyIHNvdXJjZXMgPSB7XG4gICAgbWFpbjogX193ZWJwYWNrX3JlcXVpcmVfXy5tXG4gIH1cblxuICB2YXIgcmVxdWlyZWRNb2R1bGVzID0gb3B0aW9ucy5hbGwgPyB7IG1haW46IE9iamVjdC5rZXlzKHNvdXJjZXMubWFpbikgfSA6IGdldFJlcXVpcmVkTW9kdWxlcyhzb3VyY2VzLCBtb2R1bGVJZClcblxuICB2YXIgc3JjID0gJydcblxuICBPYmplY3Qua2V5cyhyZXF1aXJlZE1vZHVsZXMpLmZpbHRlcihmdW5jdGlvbiAobSkgeyByZXR1cm4gbSAhPT0gJ21haW4nIH0pLmZvckVhY2goZnVuY3Rpb24gKG1vZHVsZSkge1xuICAgIHZhciBlbnRyeU1vZHVsZSA9IDBcbiAgICB3aGlsZSAocmVxdWlyZWRNb2R1bGVzW21vZHVsZV1bZW50cnlNb2R1bGVdKSB7XG4gICAgICBlbnRyeU1vZHVsZSsrXG4gICAgfVxuICAgIHJlcXVpcmVkTW9kdWxlc1ttb2R1bGVdLnB1c2goZW50cnlNb2R1bGUpXG4gICAgc291cmNlc1ttb2R1bGVdW2VudHJ5TW9kdWxlXSA9ICcoZnVuY3Rpb24obW9kdWxlLCBleHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7IG1vZHVsZS5leHBvcnRzID0gX193ZWJwYWNrX3JlcXVpcmVfXzsgfSknXG4gICAgc3JjID0gc3JjICsgJ3ZhciAnICsgbW9kdWxlICsgJyA9ICgnICsgd2VicGFja0Jvb3RzdHJhcEZ1bmMudG9TdHJpbmcoKS5yZXBsYWNlKCdFTlRSWV9NT0RVTEUnLCBKU09OLnN0cmluZ2lmeShlbnRyeU1vZHVsZSkpICsgJykoeycgKyByZXF1aXJlZE1vZHVsZXNbbW9kdWxlXS5tYXAoZnVuY3Rpb24gKGlkKSB7IHJldHVybiAnJyArIEpTT04uc3RyaW5naWZ5KGlkKSArICc6ICcgKyBzb3VyY2VzW21vZHVsZV1baWRdLnRvU3RyaW5nKCkgfSkuam9pbignLCcpICsgJ30pO1xcbidcbiAgfSlcblxuICBzcmMgPSBzcmMgKyAnbmV3ICgoJyArIHdlYnBhY2tCb290c3RyYXBGdW5jLnRvU3RyaW5nKCkucmVwbGFjZSgnRU5UUllfTU9EVUxFJywgSlNPTi5zdHJpbmdpZnkobW9kdWxlSWQpKSArICcpKHsnICsgcmVxdWlyZWRNb2R1bGVzLm1haW4ubWFwKGZ1bmN0aW9uIChpZCkgeyByZXR1cm4gJycgKyBKU09OLnN0cmluZ2lmeShpZCkgKyAnOiAnICsgc291cmNlcy5tYWluW2lkXS50b1N0cmluZygpIH0pLmpvaW4oJywnKSArICd9KSkoc2VsZik7J1xuXG4gIHZhciBibG9iID0gbmV3IHdpbmRvdy5CbG9iKFtzcmNdLCB7IHR5cGU6ICd0ZXh0L2phdmFzY3JpcHQnIH0pXG4gIGlmIChvcHRpb25zLmJhcmUpIHsgcmV0dXJuIGJsb2IgfVxuXG4gIHZhciBVUkwgPSB3aW5kb3cuVVJMIHx8IHdpbmRvdy53ZWJraXRVUkwgfHwgd2luZG93Lm1velVSTCB8fCB3aW5kb3cubXNVUkxcblxuICB2YXIgd29ya2VyVXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKVxuICB2YXIgd29ya2VyID0gbmV3IHdpbmRvdy5Xb3JrZXIod29ya2VyVXJsKVxuICB3b3JrZXIub2JqZWN0VVJMID0gd29ya2VyVXJsXG5cbiAgcmV0dXJuIHdvcmtlclxufVxuXG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2NyeXB0L2RlY3J5cHRlci5qc1wiOlxuLyohKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiohKlxcXG4gICEqKiogLi9zcmMvY3J5cHQvZGVjcnlwdGVyLmpzICsgMyBtb2R1bGVzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qISBleHBvcnRzIHByb3ZpZGVkOiBkZWZhdWx0ICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZXJyb3JzLnRzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZXZlbnRzLmpzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvdXRpbHMvZ2V0LXNlbGYtc2NvcGUuanMgYmVjYXVzZSBvZiAuL3NyYy9obHMudHMgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy91dGlscy9sb2dnZXIuanMgYmVjYXVzZSBvZiAuL3NyYy9obHMudHMgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIF9fd2VicGFja19leHBvcnRzX18sIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG4vLyBFU00gQ09NUEFUIEZMQUdcbl9fd2VicGFja19yZXF1aXJlX18ucihfX3dlYnBhY2tfZXhwb3J0c19fKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY3J5cHQvYWVzLWNyeXB0by5qc1xudmFyIEFFU0NyeXB0byA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIEFFU0NyeXB0byhzdWJ0bGUsIGl2KSB7XG4gICAgdGhpcy5zdWJ0bGUgPSBzdWJ0bGU7XG4gICAgdGhpcy5hZXNJViA9IGl2O1xuICB9XG5cbiAgdmFyIF9wcm90byA9IEFFU0NyeXB0by5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlY3J5cHQgPSBmdW5jdGlvbiBkZWNyeXB0KGRhdGEsIGtleSkge1xuICAgIHJldHVybiB0aGlzLnN1YnRsZS5kZWNyeXB0KHtcbiAgICAgIG5hbWU6ICdBRVMtQ0JDJyxcbiAgICAgIGl2OiB0aGlzLmFlc0lWXG4gICAgfSwga2V5LCBkYXRhKTtcbiAgfTtcblxuICByZXR1cm4gQUVTQ3J5cHRvO1xufSgpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NyeXB0L2Zhc3QtYWVzLWtleS5qc1xudmFyIEZhc3RBRVNLZXkgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBGYXN0QUVTS2V5KHN1YnRsZSwga2V5KSB7XG4gICAgdGhpcy5zdWJ0bGUgPSBzdWJ0bGU7XG4gICAgdGhpcy5rZXkgPSBrZXk7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gRmFzdEFFU0tleS5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmV4cGFuZEtleSA9IGZ1bmN0aW9uIGV4cGFuZEtleSgpIHtcbiAgICByZXR1cm4gdGhpcy5zdWJ0bGUuaW1wb3J0S2V5KCdyYXcnLCB0aGlzLmtleSwge1xuICAgICAgbmFtZTogJ0FFUy1DQkMnXG4gICAgfSwgZmFsc2UsIFsnZW5jcnlwdCcsICdkZWNyeXB0J10pO1xuICB9O1xuXG4gIHJldHVybiBGYXN0QUVTS2V5O1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBmYXN0X2Flc19rZXkgPSAoRmFzdEFFU0tleSk7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9jcnlwdC9hZXMtZGVjcnlwdG9yLmpzXG4vLyBQS0NTN1xuZnVuY3Rpb24gcmVtb3ZlUGFkZGluZyhidWZmZXIpIHtcbiAgdmFyIG91dHB1dEJ5dGVzID0gYnVmZmVyLmJ5dGVMZW5ndGg7XG4gIHZhciBwYWRkaW5nQnl0ZXMgPSBvdXRwdXRCeXRlcyAmJiBuZXcgRGF0YVZpZXcoYnVmZmVyKS5nZXRVaW50OChvdXRwdXRCeXRlcyAtIDEpO1xuXG4gIGlmIChwYWRkaW5nQnl0ZXMpIHtcbiAgICByZXR1cm4gYnVmZmVyLnNsaWNlKDAsIG91dHB1dEJ5dGVzIC0gcGFkZGluZ0J5dGVzKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gYnVmZmVyO1xuICB9XG59XG5cbnZhciBBRVNEZWNyeXB0b3IgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBBRVNEZWNyeXB0b3IoKSB7XG4gICAgLy8gU3RhdGljIGFmdGVyIHJ1bm5pbmcgaW5pdFRhYmxlXG4gICAgdGhpcy5yY29uID0gWzB4MCwgMHgxLCAweDIsIDB4NCwgMHg4LCAweDEwLCAweDIwLCAweDQwLCAweDgwLCAweDFiLCAweDM2XTtcbiAgICB0aGlzLnN1Yk1peCA9IFtuZXcgVWludDMyQXJyYXkoMjU2KSwgbmV3IFVpbnQzMkFycmF5KDI1NiksIG5ldyBVaW50MzJBcnJheSgyNTYpLCBuZXcgVWludDMyQXJyYXkoMjU2KV07XG4gICAgdGhpcy5pbnZTdWJNaXggPSBbbmV3IFVpbnQzMkFycmF5KDI1NiksIG5ldyBVaW50MzJBcnJheSgyNTYpLCBuZXcgVWludDMyQXJyYXkoMjU2KSwgbmV3IFVpbnQzMkFycmF5KDI1NildO1xuICAgIHRoaXMuc0JveCA9IG5ldyBVaW50MzJBcnJheSgyNTYpO1xuICAgIHRoaXMuaW52U0JveCA9IG5ldyBVaW50MzJBcnJheSgyNTYpOyAvLyBDaGFuZ2VzIGR1cmluZyBydW50aW1lXG5cbiAgICB0aGlzLmtleSA9IG5ldyBVaW50MzJBcnJheSgwKTtcbiAgICB0aGlzLmluaXRUYWJsZSgpO1xuICB9IC8vIFVzaW5nIHZpZXcuZ2V0VWludDMyKCkgYWxzbyBzd2FwcyB0aGUgYnl0ZSBvcmRlci5cblxuXG4gIHZhciBfcHJvdG8gPSBBRVNEZWNyeXB0b3IucHJvdG90eXBlO1xuXG4gIF9wcm90by51aW50OEFycmF5VG9VaW50MzJBcnJheV8gPSBmdW5jdGlvbiB1aW50OEFycmF5VG9VaW50MzJBcnJheV8oYXJyYXlCdWZmZXIpIHtcbiAgICB2YXIgdmlldyA9IG5ldyBEYXRhVmlldyhhcnJheUJ1ZmZlcik7XG4gICAgdmFyIG5ld0FycmF5ID0gbmV3IFVpbnQzMkFycmF5KDQpO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCA0OyBpKyspIHtcbiAgICAgIG5ld0FycmF5W2ldID0gdmlldy5nZXRVaW50MzIoaSAqIDQpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXdBcnJheTtcbiAgfTtcblxuICBfcHJvdG8uaW5pdFRhYmxlID0gZnVuY3Rpb24gaW5pdFRhYmxlKCkge1xuICAgIHZhciBzQm94ID0gdGhpcy5zQm94O1xuICAgIHZhciBpbnZTQm94ID0gdGhpcy5pbnZTQm94O1xuICAgIHZhciBzdWJNaXggPSB0aGlzLnN1Yk1peDtcbiAgICB2YXIgc3ViTWl4MCA9IHN1Yk1peFswXTtcbiAgICB2YXIgc3ViTWl4MSA9IHN1Yk1peFsxXTtcbiAgICB2YXIgc3ViTWl4MiA9IHN1Yk1peFsyXTtcbiAgICB2YXIgc3ViTWl4MyA9IHN1Yk1peFszXTtcbiAgICB2YXIgaW52U3ViTWl4ID0gdGhpcy5pbnZTdWJNaXg7XG4gICAgdmFyIGludlN1Yk1peDAgPSBpbnZTdWJNaXhbMF07XG4gICAgdmFyIGludlN1Yk1peDEgPSBpbnZTdWJNaXhbMV07XG4gICAgdmFyIGludlN1Yk1peDIgPSBpbnZTdWJNaXhbMl07XG4gICAgdmFyIGludlN1Yk1peDMgPSBpbnZTdWJNaXhbM107XG4gICAgdmFyIGQgPSBuZXcgVWludDMyQXJyYXkoMjU2KTtcbiAgICB2YXIgeCA9IDA7XG4gICAgdmFyIHhpID0gMDtcbiAgICB2YXIgaSA9IDA7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgMjU2OyBpKyspIHtcbiAgICAgIGlmIChpIDwgMTI4KSB7XG4gICAgICAgIGRbaV0gPSBpIDw8IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkW2ldID0gaSA8PCAxIF4gMHgxMWI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgZm9yIChpID0gMDsgaSA8IDI1NjsgaSsrKSB7XG4gICAgICB2YXIgc3ggPSB4aSBeIHhpIDw8IDEgXiB4aSA8PCAyIF4geGkgPDwgMyBeIHhpIDw8IDQ7XG4gICAgICBzeCA9IHN4ID4+PiA4IF4gc3ggJiAweGZmIF4gMHg2MztcbiAgICAgIHNCb3hbeF0gPSBzeDtcbiAgICAgIGludlNCb3hbc3hdID0geDsgLy8gQ29tcHV0ZSBtdWx0aXBsaWNhdGlvblxuXG4gICAgICB2YXIgeDIgPSBkW3hdO1xuICAgICAgdmFyIHg0ID0gZFt4Ml07XG4gICAgICB2YXIgeDggPSBkW3g0XTsgLy8gQ29tcHV0ZSBzdWIvaW52U3ViIGJ5dGVzLCBtaXggY29sdW1ucyB0YWJsZXNcblxuICAgICAgdmFyIHQgPSBkW3N4XSAqIDB4MTAxIF4gc3ggKiAweDEwMTAxMDA7XG4gICAgICBzdWJNaXgwW3hdID0gdCA8PCAyNCB8IHQgPj4+IDg7XG4gICAgICBzdWJNaXgxW3hdID0gdCA8PCAxNiB8IHQgPj4+IDE2O1xuICAgICAgc3ViTWl4Mlt4XSA9IHQgPDwgOCB8IHQgPj4+IDI0O1xuICAgICAgc3ViTWl4M1t4XSA9IHQ7IC8vIENvbXB1dGUgaW52IHN1YiBieXRlcywgaW52IG1peCBjb2x1bW5zIHRhYmxlc1xuXG4gICAgICB0ID0geDggKiAweDEwMTAxMDEgXiB4NCAqIDB4MTAwMDEgXiB4MiAqIDB4MTAxIF4geCAqIDB4MTAxMDEwMDtcbiAgICAgIGludlN1Yk1peDBbc3hdID0gdCA8PCAyNCB8IHQgPj4+IDg7XG4gICAgICBpbnZTdWJNaXgxW3N4XSA9IHQgPDwgMTYgfCB0ID4+PiAxNjtcbiAgICAgIGludlN1Yk1peDJbc3hdID0gdCA8PCA4IHwgdCA+Pj4gMjQ7XG4gICAgICBpbnZTdWJNaXgzW3N4XSA9IHQ7IC8vIENvbXB1dGUgbmV4dCBjb3VudGVyXG5cbiAgICAgIGlmICgheCkge1xuICAgICAgICB4ID0geGkgPSAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgeCA9IHgyIF4gZFtkW2RbeDggXiB4Ml1dXTtcbiAgICAgICAgeGkgXj0gZFtkW3hpXV07XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5leHBhbmRLZXkgPSBmdW5jdGlvbiBleHBhbmRLZXkoa2V5QnVmZmVyKSB7XG4gICAgLy8gY29udmVydCBrZXlCdWZmZXIgdG8gVWludDMyQXJyYXlcbiAgICB2YXIga2V5ID0gdGhpcy51aW50OEFycmF5VG9VaW50MzJBcnJheV8oa2V5QnVmZmVyKTtcbiAgICB2YXIgc2FtZUtleSA9IHRydWU7XG4gICAgdmFyIG9mZnNldCA9IDA7XG5cbiAgICB3aGlsZSAob2Zmc2V0IDwga2V5Lmxlbmd0aCAmJiBzYW1lS2V5KSB7XG4gICAgICBzYW1lS2V5ID0ga2V5W29mZnNldF0gPT09IHRoaXMua2V5W29mZnNldF07XG4gICAgICBvZmZzZXQrKztcbiAgICB9XG5cbiAgICBpZiAoc2FtZUtleSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMua2V5ID0ga2V5O1xuICAgIHZhciBrZXlTaXplID0gdGhpcy5rZXlTaXplID0ga2V5Lmxlbmd0aDtcblxuICAgIGlmIChrZXlTaXplICE9PSA0ICYmIGtleVNpemUgIT09IDYgJiYga2V5U2l6ZSAhPT0gOCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFlcyBrZXkgc2l6ZT0nICsga2V5U2l6ZSk7XG4gICAgfVxuXG4gICAgdmFyIGtzUm93cyA9IHRoaXMua3NSb3dzID0gKGtleVNpemUgKyA2ICsgMSkgKiA0O1xuICAgIHZhciBrc1JvdztcbiAgICB2YXIgaW52S3NSb3c7XG4gICAgdmFyIGtleVNjaGVkdWxlID0gdGhpcy5rZXlTY2hlZHVsZSA9IG5ldyBVaW50MzJBcnJheShrc1Jvd3MpO1xuICAgIHZhciBpbnZLZXlTY2hlZHVsZSA9IHRoaXMuaW52S2V5U2NoZWR1bGUgPSBuZXcgVWludDMyQXJyYXkoa3NSb3dzKTtcbiAgICB2YXIgc2JveCA9IHRoaXMuc0JveDtcbiAgICB2YXIgcmNvbiA9IHRoaXMucmNvbjtcbiAgICB2YXIgaW52U3ViTWl4ID0gdGhpcy5pbnZTdWJNaXg7XG4gICAgdmFyIGludlN1Yk1peDAgPSBpbnZTdWJNaXhbMF07XG4gICAgdmFyIGludlN1Yk1peDEgPSBpbnZTdWJNaXhbMV07XG4gICAgdmFyIGludlN1Yk1peDIgPSBpbnZTdWJNaXhbMl07XG4gICAgdmFyIGludlN1Yk1peDMgPSBpbnZTdWJNaXhbM107XG4gICAgdmFyIHByZXY7XG4gICAgdmFyIHQ7XG5cbiAgICBmb3IgKGtzUm93ID0gMDsga3NSb3cgPCBrc1Jvd3M7IGtzUm93KyspIHtcbiAgICAgIGlmIChrc1JvdyA8IGtleVNpemUpIHtcbiAgICAgICAgcHJldiA9IGtleVNjaGVkdWxlW2tzUm93XSA9IGtleVtrc1Jvd107XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB0ID0gcHJldjtcblxuICAgICAgaWYgKGtzUm93ICUga2V5U2l6ZSA9PT0gMCkge1xuICAgICAgICAvLyBSb3Qgd29yZFxuICAgICAgICB0ID0gdCA8PCA4IHwgdCA+Pj4gMjQ7IC8vIFN1YiB3b3JkXG5cbiAgICAgICAgdCA9IHNib3hbdCA+Pj4gMjRdIDw8IDI0IHwgc2JveFt0ID4+PiAxNiAmIDB4ZmZdIDw8IDE2IHwgc2JveFt0ID4+PiA4ICYgMHhmZl0gPDwgOCB8IHNib3hbdCAmIDB4ZmZdOyAvLyBNaXggUmNvblxuXG4gICAgICAgIHQgXj0gcmNvbltrc1JvdyAvIGtleVNpemUgfCAwXSA8PCAyNDtcbiAgICAgIH0gZWxzZSBpZiAoa2V5U2l6ZSA+IDYgJiYga3NSb3cgJSBrZXlTaXplID09PSA0KSB7XG4gICAgICAgIC8vIFN1YiB3b3JkXG4gICAgICAgIHQgPSBzYm94W3QgPj4+IDI0XSA8PCAyNCB8IHNib3hbdCA+Pj4gMTYgJiAweGZmXSA8PCAxNiB8IHNib3hbdCA+Pj4gOCAmIDB4ZmZdIDw8IDggfCBzYm94W3QgJiAweGZmXTtcbiAgICAgIH1cblxuICAgICAga2V5U2NoZWR1bGVba3NSb3ddID0gcHJldiA9IChrZXlTY2hlZHVsZVtrc1JvdyAtIGtleVNpemVdIF4gdCkgPj4+IDA7XG4gICAgfVxuXG4gICAgZm9yIChpbnZLc1JvdyA9IDA7IGludktzUm93IDwga3NSb3dzOyBpbnZLc1JvdysrKSB7XG4gICAgICBrc1JvdyA9IGtzUm93cyAtIGludktzUm93O1xuXG4gICAgICBpZiAoaW52S3NSb3cgJiAzKSB7XG4gICAgICAgIHQgPSBrZXlTY2hlZHVsZVtrc1Jvd107XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ID0ga2V5U2NoZWR1bGVba3NSb3cgLSA0XTtcbiAgICAgIH1cblxuICAgICAgaWYgKGludktzUm93IDwgNCB8fCBrc1JvdyA8PSA0KSB7XG4gICAgICAgIGludktleVNjaGVkdWxlW2ludktzUm93XSA9IHQ7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbnZLZXlTY2hlZHVsZVtpbnZLc1Jvd10gPSBpbnZTdWJNaXgwW3Nib3hbdCA+Pj4gMjRdXSBeIGludlN1Yk1peDFbc2JveFt0ID4+PiAxNiAmIDB4ZmZdXSBeIGludlN1Yk1peDJbc2JveFt0ID4+PiA4ICYgMHhmZl1dIF4gaW52U3ViTWl4M1tzYm94W3QgJiAweGZmXV07XG4gICAgICB9XG5cbiAgICAgIGludktleVNjaGVkdWxlW2ludktzUm93XSA9IGludktleVNjaGVkdWxlW2ludktzUm93XSA+Pj4gMDtcbiAgICB9XG4gIH0gLy8gQWRkaW5nIHRoaXMgYXMgYSBtZXRob2QgZ3JlYXRseSBpbXByb3ZlcyBwZXJmb3JtYW5jZS5cbiAgO1xuXG4gIF9wcm90by5uZXR3b3JrVG9Ib3N0T3JkZXJTd2FwID0gZnVuY3Rpb24gbmV0d29ya1RvSG9zdE9yZGVyU3dhcCh3b3JkKSB7XG4gICAgcmV0dXJuIHdvcmQgPDwgMjQgfCAod29yZCAmIDB4ZmYwMCkgPDwgOCB8ICh3b3JkICYgMHhmZjAwMDApID4+IDggfCB3b3JkID4+PiAyNDtcbiAgfTtcblxuICBfcHJvdG8uZGVjcnlwdCA9IGZ1bmN0aW9uIGRlY3J5cHQoaW5wdXRBcnJheUJ1ZmZlciwgb2Zmc2V0LCBhZXNJViwgcmVtb3ZlUEtDUzdQYWRkaW5nKSB7XG4gICAgdmFyIG5Sb3VuZHMgPSB0aGlzLmtleVNpemUgKyA2O1xuICAgIHZhciBpbnZLZXlTY2hlZHVsZSA9IHRoaXMuaW52S2V5U2NoZWR1bGU7XG4gICAgdmFyIGludlNCT1ggPSB0aGlzLmludlNCb3g7XG4gICAgdmFyIGludlN1Yk1peCA9IHRoaXMuaW52U3ViTWl4O1xuICAgIHZhciBpbnZTdWJNaXgwID0gaW52U3ViTWl4WzBdO1xuICAgIHZhciBpbnZTdWJNaXgxID0gaW52U3ViTWl4WzFdO1xuICAgIHZhciBpbnZTdWJNaXgyID0gaW52U3ViTWl4WzJdO1xuICAgIHZhciBpbnZTdWJNaXgzID0gaW52U3ViTWl4WzNdO1xuICAgIHZhciBpbml0VmVjdG9yID0gdGhpcy51aW50OEFycmF5VG9VaW50MzJBcnJheV8oYWVzSVYpO1xuICAgIHZhciBpbml0VmVjdG9yMCA9IGluaXRWZWN0b3JbMF07XG4gICAgdmFyIGluaXRWZWN0b3IxID0gaW5pdFZlY3RvclsxXTtcbiAgICB2YXIgaW5pdFZlY3RvcjIgPSBpbml0VmVjdG9yWzJdO1xuICAgIHZhciBpbml0VmVjdG9yMyA9IGluaXRWZWN0b3JbM107XG4gICAgdmFyIGlucHV0SW50MzIgPSBuZXcgSW50MzJBcnJheShpbnB1dEFycmF5QnVmZmVyKTtcbiAgICB2YXIgb3V0cHV0SW50MzIgPSBuZXcgSW50MzJBcnJheShpbnB1dEludDMyLmxlbmd0aCk7XG4gICAgdmFyIHQwLCB0MSwgdDIsIHQzO1xuICAgIHZhciBzMCwgczEsIHMyLCBzMztcbiAgICB2YXIgaW5wdXRXb3JkczAsIGlucHV0V29yZHMxLCBpbnB1dFdvcmRzMiwgaW5wdXRXb3JkczM7XG4gICAgdmFyIGtzUm93LCBpO1xuICAgIHZhciBzd2FwV29yZCA9IHRoaXMubmV0d29ya1RvSG9zdE9yZGVyU3dhcDtcblxuICAgIHdoaWxlIChvZmZzZXQgPCBpbnB1dEludDMyLmxlbmd0aCkge1xuICAgICAgaW5wdXRXb3JkczAgPSBzd2FwV29yZChpbnB1dEludDMyW29mZnNldF0pO1xuICAgICAgaW5wdXRXb3JkczEgPSBzd2FwV29yZChpbnB1dEludDMyW29mZnNldCArIDFdKTtcbiAgICAgIGlucHV0V29yZHMyID0gc3dhcFdvcmQoaW5wdXRJbnQzMltvZmZzZXQgKyAyXSk7XG4gICAgICBpbnB1dFdvcmRzMyA9IHN3YXBXb3JkKGlucHV0SW50MzJbb2Zmc2V0ICsgM10pO1xuICAgICAgczAgPSBpbnB1dFdvcmRzMCBeIGludktleVNjaGVkdWxlWzBdO1xuICAgICAgczEgPSBpbnB1dFdvcmRzMyBeIGludktleVNjaGVkdWxlWzFdO1xuICAgICAgczIgPSBpbnB1dFdvcmRzMiBeIGludktleVNjaGVkdWxlWzJdO1xuICAgICAgczMgPSBpbnB1dFdvcmRzMSBeIGludktleVNjaGVkdWxlWzNdO1xuICAgICAga3NSb3cgPSA0OyAvLyBJdGVyYXRlIHRocm91Z2ggdGhlIHJvdW5kcyBvZiBkZWNyeXB0aW9uXG5cbiAgICAgIGZvciAoaSA9IDE7IGkgPCBuUm91bmRzOyBpKyspIHtcbiAgICAgICAgdDAgPSBpbnZTdWJNaXgwW3MwID4+PiAyNF0gXiBpbnZTdWJNaXgxW3MxID4+IDE2ICYgMHhmZl0gXiBpbnZTdWJNaXgyW3MyID4+IDggJiAweGZmXSBeIGludlN1Yk1peDNbczMgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93XTtcbiAgICAgICAgdDEgPSBpbnZTdWJNaXgwW3MxID4+PiAyNF0gXiBpbnZTdWJNaXgxW3MyID4+IDE2ICYgMHhmZl0gXiBpbnZTdWJNaXgyW3MzID4+IDggJiAweGZmXSBeIGludlN1Yk1peDNbczAgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93ICsgMV07XG4gICAgICAgIHQyID0gaW52U3ViTWl4MFtzMiA+Pj4gMjRdIF4gaW52U3ViTWl4MVtzMyA+PiAxNiAmIDB4ZmZdIF4gaW52U3ViTWl4MltzMCA+PiA4ICYgMHhmZl0gXiBpbnZTdWJNaXgzW3MxICYgMHhmZl0gXiBpbnZLZXlTY2hlZHVsZVtrc1JvdyArIDJdO1xuICAgICAgICB0MyA9IGludlN1Yk1peDBbczMgPj4+IDI0XSBeIGludlN1Yk1peDFbczAgPj4gMTYgJiAweGZmXSBeIGludlN1Yk1peDJbczEgPj4gOCAmIDB4ZmZdIF4gaW52U3ViTWl4M1tzMiAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3cgKyAzXTsgLy8gVXBkYXRlIHN0YXRlXG5cbiAgICAgICAgczAgPSB0MDtcbiAgICAgICAgczEgPSB0MTtcbiAgICAgICAgczIgPSB0MjtcbiAgICAgICAgczMgPSB0MztcbiAgICAgICAga3NSb3cgPSBrc1JvdyArIDQ7XG4gICAgICB9IC8vIFNoaWZ0IHJvd3MsIHN1YiBieXRlcywgYWRkIHJvdW5kIGtleVxuXG5cbiAgICAgIHQwID0gaW52U0JPWFtzMCA+Pj4gMjRdIDw8IDI0IF4gaW52U0JPWFtzMSA+PiAxNiAmIDB4ZmZdIDw8IDE2IF4gaW52U0JPWFtzMiA+PiA4ICYgMHhmZl0gPDwgOCBeIGludlNCT1hbczMgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93XTtcbiAgICAgIHQxID0gaW52U0JPWFtzMSA+Pj4gMjRdIDw8IDI0IF4gaW52U0JPWFtzMiA+PiAxNiAmIDB4ZmZdIDw8IDE2IF4gaW52U0JPWFtzMyA+PiA4ICYgMHhmZl0gPDwgOCBeIGludlNCT1hbczAgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93ICsgMV07XG4gICAgICB0MiA9IGludlNCT1hbczIgPj4+IDI0XSA8PCAyNCBeIGludlNCT1hbczMgPj4gMTYgJiAweGZmXSA8PCAxNiBeIGludlNCT1hbczAgPj4gOCAmIDB4ZmZdIDw8IDggXiBpbnZTQk9YW3MxICYgMHhmZl0gXiBpbnZLZXlTY2hlZHVsZVtrc1JvdyArIDJdO1xuICAgICAgdDMgPSBpbnZTQk9YW3MzID4+PiAyNF0gPDwgMjQgXiBpbnZTQk9YW3MwID4+IDE2ICYgMHhmZl0gPDwgMTYgXiBpbnZTQk9YW3MxID4+IDggJiAweGZmXSA8PCA4IF4gaW52U0JPWFtzMiAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3cgKyAzXTtcbiAgICAgIGtzUm93ID0ga3NSb3cgKyAzOyAvLyBXcml0ZVxuXG4gICAgICBvdXRwdXRJbnQzMltvZmZzZXRdID0gc3dhcFdvcmQodDAgXiBpbml0VmVjdG9yMCk7XG4gICAgICBvdXRwdXRJbnQzMltvZmZzZXQgKyAxXSA9IHN3YXBXb3JkKHQzIF4gaW5pdFZlY3RvcjEpO1xuICAgICAgb3V0cHV0SW50MzJbb2Zmc2V0ICsgMl0gPSBzd2FwV29yZCh0MiBeIGluaXRWZWN0b3IyKTtcbiAgICAgIG91dHB1dEludDMyW29mZnNldCArIDNdID0gc3dhcFdvcmQodDEgXiBpbml0VmVjdG9yMyk7IC8vIHJlc2V0IGluaXRWZWN0b3IgdG8gbGFzdCA0IHVuc2lnbmVkIGludFxuXG4gICAgICBpbml0VmVjdG9yMCA9IGlucHV0V29yZHMwO1xuICAgICAgaW5pdFZlY3RvcjEgPSBpbnB1dFdvcmRzMTtcbiAgICAgIGluaXRWZWN0b3IyID0gaW5wdXRXb3JkczI7XG4gICAgICBpbml0VmVjdG9yMyA9IGlucHV0V29yZHMzO1xuICAgICAgb2Zmc2V0ID0gb2Zmc2V0ICsgNDtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVtb3ZlUEtDUzdQYWRkaW5nID8gcmVtb3ZlUGFkZGluZyhvdXRwdXRJbnQzMi5idWZmZXIpIDogb3V0cHV0SW50MzIuYnVmZmVyO1xuICB9O1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHtcbiAgICB0aGlzLmtleSA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmtleVNpemUgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5rc1Jvd3MgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5zQm94ID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuaW52U0JveCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnN1Yk1peCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmludlN1Yk1peCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmtleVNjaGVkdWxlID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuaW52S2V5U2NoZWR1bGUgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5yY29uID0gdW5kZWZpbmVkO1xuICB9O1xuXG4gIHJldHVybiBBRVNEZWNyeXB0b3I7XG59KCk7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGFlc19kZWNyeXB0b3IgPSAoQUVTRGVjcnlwdG9yKTtcbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvZXJyb3JzLnRzXG52YXIgZXJyb3JzID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL2Vycm9ycy50c1wiKTtcblxuLy8gRVhURVJOQUwgTU9EVUxFOiAuL3NyYy91dGlscy9sb2dnZXIuanNcbnZhciBsb2dnZXIgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvdXRpbHMvbG9nZ2VyLmpzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2V2ZW50cy5qc1xudmFyIGV2ZW50cyA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9ldmVudHMuanNcIik7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvdXRpbHMvZ2V0LXNlbGYtc2NvcGUuanNcbnZhciBnZXRfc2VsZl9zY29wZSA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy91dGlscy9nZXQtc2VsZi1zY29wZS5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY3J5cHQvZGVjcnlwdGVyLmpzXG5cblxuXG5cblxuXG4gLy8gc2VlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xMTIzNzI1OS81ODk0OTNcblxudmFyIGdsb2JhbCA9IE9iamVjdChnZXRfc2VsZl9zY29wZVtcImdldFNlbGZTY29wZVwiXSkoKTsgLy8gc2FmZWd1YXJkIGZvciBjb2RlIHRoYXQgbWlnaHQgcnVuIGJvdGggb24gd29ya2VyIGFuZCBtYWluIHRocmVhZFxuXG52YXIgZGVjcnlwdGVyX0RlY3J5cHRlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIERlY3J5cHRlcihvYnNlcnZlciwgY29uZmlnLCBfdGVtcCkge1xuICAgIHZhciBfcmVmID0gX3RlbXAgPT09IHZvaWQgMCA/IHt9IDogX3RlbXAsXG4gICAgICAgIF9yZWYkcmVtb3ZlUEtDUzdQYWRkaSA9IF9yZWYucmVtb3ZlUEtDUzdQYWRkaW5nLFxuICAgICAgICByZW1vdmVQS0NTN1BhZGRpbmcgPSBfcmVmJHJlbW92ZVBLQ1M3UGFkZGkgPT09IHZvaWQgMCA/IHRydWUgOiBfcmVmJHJlbW92ZVBLQ1M3UGFkZGk7XG5cbiAgICB0aGlzLmxvZ0VuYWJsZWQgPSB0cnVlO1xuICAgIHRoaXMub2JzZXJ2ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnJlbW92ZVBLQ1M3UGFkZGluZyA9IHJlbW92ZVBLQ1M3UGFkZGluZzsgLy8gYnVpbHQgaW4gZGVjcnlwdG9yIGV4cGVjdHMgUEtDUzcgcGFkZGluZ1xuXG4gICAgaWYgKHJlbW92ZVBLQ1M3UGFkZGluZykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgdmFyIGJyb3dzZXJDcnlwdG8gPSBnbG9iYWwuY3J5cHRvO1xuXG4gICAgICAgIGlmIChicm93c2VyQ3J5cHRvKSB7XG4gICAgICAgICAgdGhpcy5zdWJ0bGUgPSBicm93c2VyQ3J5cHRvLnN1YnRsZSB8fCBicm93c2VyQ3J5cHRvLndlYmtpdFN1YnRsZTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZSkge31cbiAgICB9XG5cbiAgICB0aGlzLmRpc2FibGVXZWJDcnlwdG8gPSAhdGhpcy5zdWJ0bGU7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gRGVjcnlwdGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uaXNTeW5jID0gZnVuY3Rpb24gaXNTeW5jKCkge1xuICAgIHJldHVybiB0aGlzLmRpc2FibGVXZWJDcnlwdG8gJiYgdGhpcy5jb25maWcuZW5hYmxlU29mdHdhcmVBRVM7XG4gIH07XG5cbiAgX3Byb3RvLmRlY3J5cHQgPSBmdW5jdGlvbiBkZWNyeXB0KGRhdGEsIGtleSwgaXYsIGNhbGxiYWNrKSB7XG4gICAgdmFyIF90aGlzID0gdGhpcztcblxuICAgIGlmICh0aGlzLmRpc2FibGVXZWJDcnlwdG8gJiYgdGhpcy5jb25maWcuZW5hYmxlU29mdHdhcmVBRVMpIHtcbiAgICAgIGlmICh0aGlzLmxvZ0VuYWJsZWQpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnSlMgQUVTIGRlY3J5cHQnKTtcbiAgICAgICAgdGhpcy5sb2dFbmFibGVkID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIHZhciBkZWNyeXB0b3IgPSB0aGlzLmRlY3J5cHRvcjtcblxuICAgICAgaWYgKCFkZWNyeXB0b3IpIHtcbiAgICAgICAgdGhpcy5kZWNyeXB0b3IgPSBkZWNyeXB0b3IgPSBuZXcgYWVzX2RlY3J5cHRvcigpO1xuICAgICAgfVxuXG4gICAgICBkZWNyeXB0b3IuZXhwYW5kS2V5KGtleSk7XG4gICAgICBjYWxsYmFjayhkZWNyeXB0b3IuZGVjcnlwdChkYXRhLCAwLCBpdiwgdGhpcy5yZW1vdmVQS0NTN1BhZGRpbmcpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKHRoaXMubG9nRW5hYmxlZCkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdXZWJDcnlwdG8gQUVTIGRlY3J5cHQnKTtcbiAgICAgICAgdGhpcy5sb2dFbmFibGVkID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIHZhciBzdWJ0bGUgPSB0aGlzLnN1YnRsZTtcblxuICAgICAgaWYgKHRoaXMua2V5ICE9PSBrZXkpIHtcbiAgICAgICAgdGhpcy5rZXkgPSBrZXk7XG4gICAgICAgIHRoaXMuZmFzdEFlc0tleSA9IG5ldyBmYXN0X2Flc19rZXkoc3VidGxlLCBrZXkpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmZhc3RBZXNLZXkuZXhwYW5kS2V5KCkudGhlbihmdW5jdGlvbiAoYWVzS2V5KSB7XG4gICAgICAgIC8vIGRlY3J5cHQgdXNpbmcgd2ViIGNyeXB0b1xuICAgICAgICB2YXIgY3J5cHRvID0gbmV3IEFFU0NyeXB0byhzdWJ0bGUsIGl2KTtcbiAgICAgICAgY3J5cHRvLmRlY3J5cHQoZGF0YSwgYWVzS2V5KS5jYXRjaChmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgICAgX3RoaXMub25XZWJDcnlwdG9FcnJvcihlcnIsIGRhdGEsIGtleSwgaXYsIGNhbGxiYWNrKTtcbiAgICAgICAgfSkudGhlbihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgICAgY2FsbGJhY2socmVzdWx0KTtcbiAgICAgICAgfSk7XG4gICAgICB9KS5jYXRjaChmdW5jdGlvbiAoZXJyKSB7XG4gICAgICAgIF90aGlzLm9uV2ViQ3J5cHRvRXJyb3IoZXJyLCBkYXRhLCBrZXksIGl2LCBjYWxsYmFjayk7XG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uV2ViQ3J5cHRvRXJyb3IgPSBmdW5jdGlvbiBvbldlYkNyeXB0b0Vycm9yKGVyciwgZGF0YSwga2V5LCBpdiwgY2FsbGJhY2spIHtcbiAgICBpZiAodGhpcy5jb25maWcuZW5hYmxlU29mdHdhcmVBRVMpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ1dlYkNyeXB0byBFcnJvciwgZGlzYWJsZSBXZWJDcnlwdG8gQVBJJyk7XG4gICAgICB0aGlzLmRpc2FibGVXZWJDcnlwdG8gPSB0cnVlO1xuICAgICAgdGhpcy5sb2dFbmFibGVkID0gdHJ1ZTtcbiAgICAgIHRoaXMuZGVjcnlwdChkYXRhLCBrZXksIGl2LCBjYWxsYmFjayk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcihcImRlY3J5cHRpbmcgZXJyb3IgOiBcIiArIGVyci5tZXNzYWdlKTtcbiAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTUVESUFfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5GUkFHX0RFQ1JZUFRfRVJST1IsXG4gICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICByZWFzb246IGVyci5tZXNzYWdlXG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHZhciBkZWNyeXB0b3IgPSB0aGlzLmRlY3J5cHRvcjtcblxuICAgIGlmIChkZWNyeXB0b3IpIHtcbiAgICAgIGRlY3J5cHRvci5kZXN0cm95KCk7XG4gICAgICB0aGlzLmRlY3J5cHRvciA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIERlY3J5cHRlcjtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgZGVjcnlwdGVyID0gX193ZWJwYWNrX2V4cG9ydHNfX1tcImRlZmF1bHRcIl0gPSAoZGVjcnlwdGVyX0RlY3J5cHRlcik7XG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2RlbXV4L2RlbXV4ZXItaW5saW5lLmpzXCI6XG4vKiEqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL3NyYy9kZW11eC9kZW11eGVyLWlubGluZS5qcyArIDEyIG1vZHVsZXMgKioqIVxuICBcXCoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyohIGV4cG9ydHMgcHJvdmlkZWQ6IGRlZmF1bHQgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy9jcnlwdC9kZWNyeXB0ZXIuanMgYmVjYXVzZSBvZiAuL3NyYy9obHMudHMgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy9kZW11eC9pZDMuanMgYmVjYXVzZSBvZiAuL3NyYy9obHMudHMgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy9kZW11eC9tcDRkZW11eGVyLmpzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZXJyb3JzLnRzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZXZlbnRzLmpzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvcG9seWZpbGxzL251bWJlci5qcyBiZWNhdXNlIG9mIC4vc3JjL2hscy50cyAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vc3JjL3V0aWxzL2dldC1zZWxmLXNjb3BlLmpzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvdXRpbHMvbG9nZ2VyLmpzIGJlY2F1c2Ugb2YgLi9zcmMvaGxzLnRzICovXG4vKioqLyAoZnVuY3Rpb24obW9kdWxlLCBfX3dlYnBhY2tfZXhwb3J0c19fLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7XG5cblwidXNlIHN0cmljdFwiO1xuLy8gRVNNIENPTVBBVCBGTEFHXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvZXZlbnRzLmpzXG52YXIgZXZlbnRzID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL2V2ZW50cy5qc1wiKTtcblxuLy8gRVhURVJOQUwgTU9EVUxFOiAuL3NyYy9lcnJvcnMudHNcbnZhciBlcnJvcnMgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvZXJyb3JzLnRzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2NyeXB0L2RlY3J5cHRlci5qcyArIDMgbW9kdWxlc1xudmFyIGNyeXB0X2RlY3J5cHRlciA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9jcnlwdC9kZWNyeXB0ZXIuanNcIik7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvcG9seWZpbGxzL251bWJlci5qc1xudmFyIG51bWJlciA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9wb2x5ZmlsbHMvbnVtYmVyLmpzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL3V0aWxzL2xvZ2dlci5qc1xudmFyIGxvZ2dlciA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy91dGlscy9sb2dnZXIuanNcIik7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvdXRpbHMvZ2V0LXNlbGYtc2NvcGUuanNcbnZhciBnZXRfc2VsZl9zY29wZSA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy91dGlscy9nZXQtc2VsZi1zY29wZS5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvZGVtdXgvYWR0cy5qc1xuLyoqXG4gKiBBRFRTIHBhcnNlciBoZWxwZXJcbiAqIEBsaW5rIGh0dHBzOi8vd2lraS5tdWx0aW1lZGlhLmN4L2luZGV4LnBocD90aXRsZT1BRFRTXG4gKi9cblxuXG5cblxuZnVuY3Rpb24gZ2V0QXVkaW9Db25maWcob2JzZXJ2ZXIsIGRhdGEsIG9mZnNldCwgYXVkaW9Db2RlYykge1xuICB2YXIgYWR0c09iamVjdFR5cGUsXG4gICAgICAvLyA6aW50XG4gIGFkdHNTYW1wbGVpbmdJbmRleCxcbiAgICAgIC8vIDppbnRcbiAgYWR0c0V4dGVuc2lvblNhbXBsZWluZ0luZGV4LFxuICAgICAgLy8gOmludFxuICBhZHRzQ2hhbmVsQ29uZmlnLFxuICAgICAgLy8gOmludFxuICBjb25maWcsXG4gICAgICB1c2VyQWdlbnQgPSBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCksXG4gICAgICBtYW5pZmVzdENvZGVjID0gYXVkaW9Db2RlYyxcbiAgICAgIGFkdHNTYW1wbGVpbmdSYXRlcyA9IFs5NjAwMCwgODgyMDAsIDY0MDAwLCA0ODAwMCwgNDQxMDAsIDMyMDAwLCAyNDAwMCwgMjIwNTAsIDE2MDAwLCAxMjAwMCwgMTEwMjUsIDgwMDAsIDczNTBdOyAvLyBieXRlIDJcblxuICBhZHRzT2JqZWN0VHlwZSA9ICgoZGF0YVtvZmZzZXQgKyAyXSAmIDB4QzApID4+PiA2KSArIDE7XG4gIGFkdHNTYW1wbGVpbmdJbmRleCA9IChkYXRhW29mZnNldCArIDJdICYgMHgzQykgPj4+IDI7XG5cbiAgaWYgKGFkdHNTYW1wbGVpbmdJbmRleCA+IGFkdHNTYW1wbGVpbmdSYXRlcy5sZW5ndGggLSAxKSB7XG4gICAgb2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkZSQUdfUEFSU0lOR19FUlJPUixcbiAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgcmVhc29uOiBcImludmFsaWQgQURUUyBzYW1wbGluZyBpbmRleDpcIiArIGFkdHNTYW1wbGVpbmdJbmRleFxuICAgIH0pO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGFkdHNDaGFuZWxDb25maWcgPSAoZGF0YVtvZmZzZXQgKyAyXSAmIDB4MDEpIDw8IDI7IC8vIGJ5dGUgM1xuXG4gIGFkdHNDaGFuZWxDb25maWcgfD0gKGRhdGFbb2Zmc2V0ICsgM10gJiAweEMwKSA+Pj4gNjtcbiAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIm1hbmlmZXN0IGNvZGVjOlwiICsgYXVkaW9Db2RlYyArIFwiLEFEVFMgZGF0YTp0eXBlOlwiICsgYWR0c09iamVjdFR5cGUgKyBcIixzYW1wbGVpbmdJbmRleDpcIiArIGFkdHNTYW1wbGVpbmdJbmRleCArIFwiW1wiICsgYWR0c1NhbXBsZWluZ1JhdGVzW2FkdHNTYW1wbGVpbmdJbmRleF0gKyBcIkh6XSxjaGFubmVsQ29uZmlnOlwiICsgYWR0c0NoYW5lbENvbmZpZyk7IC8vIGZpcmVmb3g6IGZyZXEgbGVzcyB0aGFuIDI0a0h6ID0gQUFDIFNCUiAoSEUtQUFDKVxuXG4gIGlmICgvZmlyZWZveC9pLnRlc3QodXNlckFnZW50KSkge1xuICAgIGlmIChhZHRzU2FtcGxlaW5nSW5kZXggPj0gNikge1xuICAgICAgYWR0c09iamVjdFR5cGUgPSA1O1xuICAgICAgY29uZmlnID0gbmV3IEFycmF5KDQpOyAvLyBIRS1BQUMgdXNlcyBTQlIgKFNwZWN0cmFsIEJhbmQgUmVwbGljYXRpb24pICwgaGlnaCBmcmVxdWVuY2llcyBhcmUgY29uc3RydWN0ZWQgZnJvbSBsb3cgZnJlcXVlbmNpZXNcbiAgICAgIC8vIHRoZXJlIGlzIGEgZmFjdG9yIDIgYmV0d2VlbiBmcmFtZSBzYW1wbGUgcmF0ZSBhbmQgb3V0cHV0IHNhbXBsZSByYXRlXG4gICAgICAvLyBtdWx0aXBseSBmcmVxdWVuY3kgYnkgMiAoc2VlIHRhYmxlIGJlbG93LCBlcXVpdmFsZW50IHRvIHN1YnN0cmFjdCAzKVxuXG4gICAgICBhZHRzRXh0ZW5zaW9uU2FtcGxlaW5nSW5kZXggPSBhZHRzU2FtcGxlaW5nSW5kZXggLSAzO1xuICAgIH0gZWxzZSB7XG4gICAgICBhZHRzT2JqZWN0VHlwZSA9IDI7XG4gICAgICBjb25maWcgPSBuZXcgQXJyYXkoMik7XG4gICAgICBhZHRzRXh0ZW5zaW9uU2FtcGxlaW5nSW5kZXggPSBhZHRzU2FtcGxlaW5nSW5kZXg7XG4gICAgfSAvLyBBbmRyb2lkIDogYWx3YXlzIHVzZSBBQUNcblxuICB9IGVsc2UgaWYgKHVzZXJBZ2VudC5pbmRleE9mKCdhbmRyb2lkJykgIT09IC0xKSB7XG4gICAgYWR0c09iamVjdFR5cGUgPSAyO1xuICAgIGNvbmZpZyA9IG5ldyBBcnJheSgyKTtcbiAgICBhZHRzRXh0ZW5zaW9uU2FtcGxlaW5nSW5kZXggPSBhZHRzU2FtcGxlaW5nSW5kZXg7XG4gIH0gZWxzZSB7XG4gICAgLyogIGZvciBvdGhlciBicm93c2VycyAoQ2hyb21lL1ZpdmFsZGkvT3BlcmEgLi4uKVxuICAgICAgICBhbHdheXMgZm9yY2UgYXVkaW8gdHlwZSB0byBiZSBIRS1BQUMgU0JSLCBhcyBzb21lIGJyb3dzZXJzIGRvIG5vdCBzdXBwb3J0IGF1ZGlvIGNvZGVjIHN3aXRjaCBwcm9wZXJseSAobGlrZSBDaHJvbWUgLi4uKVxuICAgICovXG4gICAgYWR0c09iamVjdFR5cGUgPSA1O1xuICAgIGNvbmZpZyA9IG5ldyBBcnJheSg0KTsgLy8gaWYgKG1hbmlmZXN0IGNvZGVjIGlzIEhFLUFBQyBvciBIRS1BQUN2MikgT1IgKG1hbmlmZXN0IGNvZGVjIG5vdCBzcGVjaWZpZWQgQU5EIGZyZXF1ZW5jeSBsZXNzIHRoYW4gMjRrSHopXG5cbiAgICBpZiAoYXVkaW9Db2RlYyAmJiAoYXVkaW9Db2RlYy5pbmRleE9mKCdtcDRhLjQwLjI5JykgIT09IC0xIHx8IGF1ZGlvQ29kZWMuaW5kZXhPZignbXA0YS40MC41JykgIT09IC0xKSB8fCAhYXVkaW9Db2RlYyAmJiBhZHRzU2FtcGxlaW5nSW5kZXggPj0gNikge1xuICAgICAgLy8gSEUtQUFDIHVzZXMgU0JSIChTcGVjdHJhbCBCYW5kIFJlcGxpY2F0aW9uKSAsIGhpZ2ggZnJlcXVlbmNpZXMgYXJlIGNvbnN0cnVjdGVkIGZyb20gbG93IGZyZXF1ZW5jaWVzXG4gICAgICAvLyB0aGVyZSBpcyBhIGZhY3RvciAyIGJldHdlZW4gZnJhbWUgc2FtcGxlIHJhdGUgYW5kIG91dHB1dCBzYW1wbGUgcmF0ZVxuICAgICAgLy8gbXVsdGlwbHkgZnJlcXVlbmN5IGJ5IDIgKHNlZSB0YWJsZSBiZWxvdywgZXF1aXZhbGVudCB0byBzdWJzdHJhY3QgMylcbiAgICAgIGFkdHNFeHRlbnNpb25TYW1wbGVpbmdJbmRleCA9IGFkdHNTYW1wbGVpbmdJbmRleCAtIDM7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGlmIChtYW5pZmVzdCBjb2RlYyBpcyBBQUMpIEFORCAoZnJlcXVlbmN5IGxlc3MgdGhhbiAyNGtIeiBBTkQgbmIgY2hhbm5lbCBpcyAxKSBPUiAobWFuaWZlc3QgY29kZWMgbm90IHNwZWNpZmllZCBhbmQgbW9ubyBhdWRpbylcbiAgICAgIC8vIENocm9tZSBmYWlscyB0byBwbGF5IGJhY2sgd2l0aCBsb3cgZnJlcXVlbmN5IEFBQyBMQyBtb25vIHdoZW4gaW5pdGlhbGl6ZWQgd2l0aCBIRS1BQUMuICBUaGlzIGlzIG5vdCBhIHByb2JsZW0gd2l0aCBzdGVyZW8uXG4gICAgICBpZiAoYXVkaW9Db2RlYyAmJiBhdWRpb0NvZGVjLmluZGV4T2YoJ21wNGEuNDAuMicpICE9PSAtMSAmJiAoYWR0c1NhbXBsZWluZ0luZGV4ID49IDYgJiYgYWR0c0NoYW5lbENvbmZpZyA9PT0gMSB8fCAvdml2YWxkaS9pLnRlc3QodXNlckFnZW50KSkgfHwgIWF1ZGlvQ29kZWMgJiYgYWR0c0NoYW5lbENvbmZpZyA9PT0gMSkge1xuICAgICAgICBhZHRzT2JqZWN0VHlwZSA9IDI7XG4gICAgICAgIGNvbmZpZyA9IG5ldyBBcnJheSgyKTtcbiAgICAgIH1cblxuICAgICAgYWR0c0V4dGVuc2lvblNhbXBsZWluZ0luZGV4ID0gYWR0c1NhbXBsZWluZ0luZGV4O1xuICAgIH1cbiAgfVxuICAvKiByZWZlciB0byBodHRwOi8vd2lraS5tdWx0aW1lZGlhLmN4L2luZGV4LnBocD90aXRsZT1NUEVHLTRfQXVkaW8jQXVkaW9fU3BlY2lmaWNfQ29uZmlnXG4gICAgICBJU08gMTQ0OTYtMyAoQUFDKS5wZGYgLSBUYWJsZSAxLjEzIOKAlCBTeW50YXggb2YgQXVkaW9TcGVjaWZpY0NvbmZpZygpXG4gICAgQXVkaW8gUHJvZmlsZSAvIEF1ZGlvIE9iamVjdCBUeXBlXG4gICAgMDogTnVsbFxuICAgIDE6IEFBQyBNYWluXG4gICAgMjogQUFDIExDIChMb3cgQ29tcGxleGl0eSlcbiAgICAzOiBBQUMgU1NSIChTY2FsYWJsZSBTYW1wbGUgUmF0ZSlcbiAgICA0OiBBQUMgTFRQIChMb25nIFRlcm0gUHJlZGljdGlvbilcbiAgICA1OiBTQlIgKFNwZWN0cmFsIEJhbmQgUmVwbGljYXRpb24pXG4gICAgNjogQUFDIFNjYWxhYmxlXG4gICBzYW1wbGluZyBmcmVxXG4gICAgMDogOTYwMDAgSHpcbiAgICAxOiA4ODIwMCBIelxuICAgIDI6IDY0MDAwIEh6XG4gICAgMzogNDgwMDAgSHpcbiAgICA0OiA0NDEwMCBIelxuICAgIDU6IDMyMDAwIEh6XG4gICAgNjogMjQwMDAgSHpcbiAgICA3OiAyMjA1MCBIelxuICAgIDg6IDE2MDAwIEh6XG4gICAgOTogMTIwMDAgSHpcbiAgICAxMDogMTEwMjUgSHpcbiAgICAxMTogODAwMCBIelxuICAgIDEyOiA3MzUwIEh6XG4gICAgMTM6IFJlc2VydmVkXG4gICAgMTQ6IFJlc2VydmVkXG4gICAgMTU6IGZyZXF1ZW5jeSBpcyB3cml0dGVuIGV4cGxpY3RseVxuICAgIENoYW5uZWwgQ29uZmlndXJhdGlvbnNcbiAgICBUaGVzZSBhcmUgdGhlIGNoYW5uZWwgY29uZmlndXJhdGlvbnM6XG4gICAgMDogRGVmaW5lZCBpbiBBT1QgU3BlY2lmYyBDb25maWdcbiAgICAxOiAxIGNoYW5uZWw6IGZyb250LWNlbnRlclxuICAgIDI6IDIgY2hhbm5lbHM6IGZyb250LWxlZnQsIGZyb250LXJpZ2h0XG4gICovXG4gIC8vIGF1ZGlvT2JqZWN0VHlwZSA9IHByb2ZpbGUgPT4gcHJvZmlsZSwgdGhlIE1QRUctNCBBdWRpbyBPYmplY3QgVHlwZSBtaW51cyAxXG5cblxuICBjb25maWdbMF0gPSBhZHRzT2JqZWN0VHlwZSA8PCAzOyAvLyBzYW1wbGluZ0ZyZXF1ZW5jeUluZGV4XG5cbiAgY29uZmlnWzBdIHw9IChhZHRzU2FtcGxlaW5nSW5kZXggJiAweDBFKSA+PiAxO1xuICBjb25maWdbMV0gfD0gKGFkdHNTYW1wbGVpbmdJbmRleCAmIDB4MDEpIDw8IDc7IC8vIGNoYW5uZWxDb25maWd1cmF0aW9uXG5cbiAgY29uZmlnWzFdIHw9IGFkdHNDaGFuZWxDb25maWcgPDwgMztcblxuICBpZiAoYWR0c09iamVjdFR5cGUgPT09IDUpIHtcbiAgICAvLyBhZHRzRXh0ZW5zaW9uU2FtcGxlaW5nSW5kZXhcbiAgICBjb25maWdbMV0gfD0gKGFkdHNFeHRlbnNpb25TYW1wbGVpbmdJbmRleCAmIDB4MEUpID4+IDE7XG4gICAgY29uZmlnWzJdID0gKGFkdHNFeHRlbnNpb25TYW1wbGVpbmdJbmRleCAmIDB4MDEpIDw8IDc7IC8vIGFkdHNPYmplY3RUeXBlIChmb3JjZSB0byAyLCBjaHJvbWUgaXMgY2hlY2tpbmcgdGhhdCBvYmplY3QgdHlwZSBpcyBsZXNzIHRoYW4gNSA/Pz9cbiAgICAvLyAgICBodHRwczovL2Nocm9taXVtLmdvb2dsZXNvdXJjZS5jb20vY2hyb21pdW0vc3JjLmdpdC8rL21hc3Rlci9tZWRpYS9mb3JtYXRzL21wNC9hYWMuY2NcblxuICAgIGNvbmZpZ1syXSB8PSAyIDw8IDI7XG4gICAgY29uZmlnWzNdID0gMDtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgY29uZmlnOiBjb25maWcsXG4gICAgc2FtcGxlcmF0ZTogYWR0c1NhbXBsZWluZ1JhdGVzW2FkdHNTYW1wbGVpbmdJbmRleF0sXG4gICAgY2hhbm5lbENvdW50OiBhZHRzQ2hhbmVsQ29uZmlnLFxuICAgIGNvZGVjOiAnbXA0YS40MC4nICsgYWR0c09iamVjdFR5cGUsXG4gICAgbWFuaWZlc3RDb2RlYzogbWFuaWZlc3RDb2RlY1xuICB9O1xufVxuZnVuY3Rpb24gaXNIZWFkZXJQYXR0ZXJuKGRhdGEsIG9mZnNldCkge1xuICByZXR1cm4gZGF0YVtvZmZzZXRdID09PSAweGZmICYmIChkYXRhW29mZnNldCArIDFdICYgMHhmNikgPT09IDB4ZjA7XG59XG5mdW5jdGlvbiBnZXRIZWFkZXJMZW5ndGgoZGF0YSwgb2Zmc2V0KSB7XG4gIHJldHVybiBkYXRhW29mZnNldCArIDFdICYgMHgwMSA/IDcgOiA5O1xufVxuZnVuY3Rpb24gZ2V0RnVsbEZyYW1lTGVuZ3RoKGRhdGEsIG9mZnNldCkge1xuICByZXR1cm4gKGRhdGFbb2Zmc2V0ICsgM10gJiAweDAzKSA8PCAxMSB8IGRhdGFbb2Zmc2V0ICsgNF0gPDwgMyB8IChkYXRhW29mZnNldCArIDVdICYgMHhFMCkgPj4+IDU7XG59XG5mdW5jdGlvbiBpc0hlYWRlcihkYXRhLCBvZmZzZXQpIHtcbiAgLy8gTG9vayBmb3IgQURUUyBoZWFkZXIgfCAxMTExIDExMTEgfCAxMTExIFgwMFggfCB3aGVyZSBYIGNhbiBiZSBlaXRoZXIgMCBvciAxXG4gIC8vIExheWVyIGJpdHMgKHBvc2l0aW9uIDE0IGFuZCAxNSkgaW4gaGVhZGVyIHNob3VsZCBiZSBhbHdheXMgMCBmb3IgQURUU1xuICAvLyBNb3JlIGluZm8gaHR0cHM6Ly93aWtpLm11bHRpbWVkaWEuY3gvaW5kZXgucGhwP3RpdGxlPUFEVFNcbiAgaWYgKG9mZnNldCArIDEgPCBkYXRhLmxlbmd0aCAmJiBpc0hlYWRlclBhdHRlcm4oZGF0YSwgb2Zmc2V0KSkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcmV0dXJuIGZhbHNlO1xufVxuZnVuY3Rpb24gYWR0c19wcm9iZShkYXRhLCBvZmZzZXQpIHtcbiAgLy8gc2FtZSBhcyBpc0hlYWRlciBidXQgd2UgYWxzbyBjaGVjayB0aGF0IEFEVFMgZnJhbWUgZm9sbG93cyBsYXN0IEFEVFMgZnJhbWVcbiAgLy8gb3IgZW5kIG9mIGRhdGEgaXMgcmVhY2hlZFxuICBpZiAoaXNIZWFkZXIoZGF0YSwgb2Zmc2V0KSkge1xuICAgIC8vIEFEVFMgaGVhZGVyIExlbmd0aFxuICAgIHZhciBoZWFkZXJMZW5ndGggPSBnZXRIZWFkZXJMZW5ndGgoZGF0YSwgb2Zmc2V0KTtcblxuICAgIGlmIChvZmZzZXQgKyBoZWFkZXJMZW5ndGggPj0gZGF0YS5sZW5ndGgpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9IC8vIEFEVFMgZnJhbWUgTGVuZ3RoXG5cblxuICAgIHZhciBmcmFtZUxlbmd0aCA9IGdldEZ1bGxGcmFtZUxlbmd0aChkYXRhLCBvZmZzZXQpO1xuXG4gICAgaWYgKGZyYW1lTGVuZ3RoIDw9IGhlYWRlckxlbmd0aCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHZhciBuZXdPZmZzZXQgPSBvZmZzZXQgKyBmcmFtZUxlbmd0aDtcblxuICAgIGlmIChuZXdPZmZzZXQgPT09IGRhdGEubGVuZ3RoIHx8IG5ld09mZnNldCArIDEgPCBkYXRhLmxlbmd0aCAmJiBpc0hlYWRlclBhdHRlcm4oZGF0YSwgbmV3T2Zmc2V0KSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGZhbHNlO1xufVxuZnVuY3Rpb24gaW5pdFRyYWNrQ29uZmlnKHRyYWNrLCBvYnNlcnZlciwgZGF0YSwgb2Zmc2V0LCBhdWRpb0NvZGVjKSB7XG4gIGlmICghdHJhY2suc2FtcGxlcmF0ZSkge1xuICAgIHZhciBjb25maWcgPSBnZXRBdWRpb0NvbmZpZyhvYnNlcnZlciwgZGF0YSwgb2Zmc2V0LCBhdWRpb0NvZGVjKTtcbiAgICB0cmFjay5jb25maWcgPSBjb25maWcuY29uZmlnO1xuICAgIHRyYWNrLnNhbXBsZXJhdGUgPSBjb25maWcuc2FtcGxlcmF0ZTtcbiAgICB0cmFjay5jaGFubmVsQ291bnQgPSBjb25maWcuY2hhbm5lbENvdW50O1xuICAgIHRyYWNrLmNvZGVjID0gY29uZmlnLmNvZGVjO1xuICAgIHRyYWNrLm1hbmlmZXN0Q29kZWMgPSBjb25maWcubWFuaWZlc3RDb2RlYztcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwicGFyc2VkIGNvZGVjOlwiICsgdHJhY2suY29kZWMgKyBcIixyYXRlOlwiICsgY29uZmlnLnNhbXBsZXJhdGUgKyBcIixuYiBjaGFubmVsOlwiICsgY29uZmlnLmNoYW5uZWxDb3VudCk7XG4gIH1cbn1cbmZ1bmN0aW9uIGdldEZyYW1lRHVyYXRpb24oc2FtcGxlcmF0ZSkge1xuICByZXR1cm4gMTAyNCAqIDkwMDAwIC8gc2FtcGxlcmF0ZTtcbn1cbmZ1bmN0aW9uIHBhcnNlRnJhbWVIZWFkZXIoZGF0YSwgb2Zmc2V0LCBwdHMsIGZyYW1lSW5kZXgsIGZyYW1lRHVyYXRpb24pIHtcbiAgdmFyIGhlYWRlckxlbmd0aCwgZnJhbWVMZW5ndGgsIHN0YW1wO1xuICB2YXIgbGVuZ3RoID0gZGF0YS5sZW5ndGg7IC8vIFRoZSBwcm90ZWN0aW9uIHNraXAgYml0IHRlbGxzIHVzIGlmIHdlIGhhdmUgMiBieXRlcyBvZiBDUkMgZGF0YSBhdCB0aGUgZW5kIG9mIHRoZSBBRFRTIGhlYWRlclxuXG4gIGhlYWRlckxlbmd0aCA9IGdldEhlYWRlckxlbmd0aChkYXRhLCBvZmZzZXQpOyAvLyByZXRyaWV2ZSBmcmFtZSBzaXplXG5cbiAgZnJhbWVMZW5ndGggPSBnZXRGdWxsRnJhbWVMZW5ndGgoZGF0YSwgb2Zmc2V0KTtcbiAgZnJhbWVMZW5ndGggLT0gaGVhZGVyTGVuZ3RoO1xuXG4gIGlmIChmcmFtZUxlbmd0aCA+IDAgJiYgb2Zmc2V0ICsgaGVhZGVyTGVuZ3RoICsgZnJhbWVMZW5ndGggPD0gbGVuZ3RoKSB7XG4gICAgc3RhbXAgPSBwdHMgKyBmcmFtZUluZGV4ICogZnJhbWVEdXJhdGlvbjsgLy8gbG9nZ2VyLmxvZyhgQUFDIGZyYW1lLCBvZmZzZXQvbGVuZ3RoL3RvdGFsL3B0czoke29mZnNldCtoZWFkZXJMZW5ndGh9LyR7ZnJhbWVMZW5ndGh9LyR7ZGF0YS5ieXRlTGVuZ3RofS8keyhzdGFtcC85MCkudG9GaXhlZCgwKX1gKTtcblxuICAgIHJldHVybiB7XG4gICAgICBoZWFkZXJMZW5ndGg6IGhlYWRlckxlbmd0aCxcbiAgICAgIGZyYW1lTGVuZ3RoOiBmcmFtZUxlbmd0aCxcbiAgICAgIHN0YW1wOiBzdGFtcFxuICAgIH07XG4gIH1cblxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuZnVuY3Rpb24gYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIG9mZnNldCwgcHRzLCBmcmFtZUluZGV4KSB7XG4gIHZhciBmcmFtZUR1cmF0aW9uID0gZ2V0RnJhbWVEdXJhdGlvbih0cmFjay5zYW1wbGVyYXRlKTtcbiAgdmFyIGhlYWRlciA9IHBhcnNlRnJhbWVIZWFkZXIoZGF0YSwgb2Zmc2V0LCBwdHMsIGZyYW1lSW5kZXgsIGZyYW1lRHVyYXRpb24pO1xuXG4gIGlmIChoZWFkZXIpIHtcbiAgICB2YXIgc3RhbXAgPSBoZWFkZXIuc3RhbXA7XG4gICAgdmFyIGhlYWRlckxlbmd0aCA9IGhlYWRlci5oZWFkZXJMZW5ndGg7XG4gICAgdmFyIGZyYW1lTGVuZ3RoID0gaGVhZGVyLmZyYW1lTGVuZ3RoOyAvLyBsb2dnZXIubG9nKGBBQUMgZnJhbWUsIG9mZnNldC9sZW5ndGgvdG90YWwvcHRzOiR7b2Zmc2V0K2hlYWRlckxlbmd0aH0vJHtmcmFtZUxlbmd0aH0vJHtkYXRhLmJ5dGVMZW5ndGh9LyR7KHN0YW1wLzkwKS50b0ZpeGVkKDApfWApO1xuXG4gICAgdmFyIGFhY1NhbXBsZSA9IHtcbiAgICAgIHVuaXQ6IGRhdGEuc3ViYXJyYXkob2Zmc2V0ICsgaGVhZGVyTGVuZ3RoLCBvZmZzZXQgKyBoZWFkZXJMZW5ndGggKyBmcmFtZUxlbmd0aCksXG4gICAgICBwdHM6IHN0YW1wLFxuICAgICAgZHRzOiBzdGFtcFxuICAgIH07XG4gICAgdHJhY2suc2FtcGxlcy5wdXNoKGFhY1NhbXBsZSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHNhbXBsZTogYWFjU2FtcGxlLFxuICAgICAgbGVuZ3RoOiBmcmFtZUxlbmd0aCArIGhlYWRlckxlbmd0aFxuICAgIH07XG4gIH1cblxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuLy8gRVhURVJOQUwgTU9EVUxFOiAuL3NyYy9kZW11eC9pZDMuanNcbnZhciBpZDMgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvZGVtdXgvaWQzLmpzXCIpO1xuXG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9kZW11eC9hYWNkZW11eGVyLmpzXG5cblxuLyoqXG4gKiBBQUMgZGVtdXhlclxuICovXG5cblxuXG5cbnZhciBhYWNkZW11eGVyX0FBQ0RlbXV4ZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBBQUNEZW11eGVyKG9ic2VydmVyLCByZW11eGVyLCBjb25maWcpIHtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy5yZW11eGVyID0gcmVtdXhlcjtcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBBQUNEZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8ucmVzZXRJbml0U2VnbWVudCA9IGZ1bmN0aW9uIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGR1cmF0aW9uKSB7XG4gICAgdGhpcy5fYXVkaW9UcmFjayA9IHtcbiAgICAgIGNvbnRhaW5lcjogJ2F1ZGlvL2FkdHMnLFxuICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgIGlkOiAwLFxuICAgICAgc2VxdWVuY2VOdW1iZXI6IDAsXG4gICAgICBpc0FBQzogdHJ1ZSxcbiAgICAgIHNhbXBsZXM6IFtdLFxuICAgICAgbGVuOiAwLFxuICAgICAgbWFuaWZlc3RDb2RlYzogYXVkaW9Db2RlYyxcbiAgICAgIGR1cmF0aW9uOiBkdXJhdGlvbixcbiAgICAgIGlucHV0VGltZVNjYWxlOiA5MDAwMFxuICAgIH07XG4gIH07XG5cbiAgX3Byb3RvLnJlc2V0VGltZVN0YW1wID0gZnVuY3Rpb24gcmVzZXRUaW1lU3RhbXAoKSB7fTtcblxuICBBQUNEZW11eGVyLnByb2JlID0gZnVuY3Rpb24gcHJvYmUoZGF0YSkge1xuICAgIGlmICghZGF0YSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH0gLy8gQ2hlY2sgZm9yIHRoZSBBRFRTIHN5bmMgd29yZFxuICAgIC8vIExvb2sgZm9yIEFEVFMgaGVhZGVyIHwgMTExMSAxMTExIHwgMTExMSBYMDBYIHwgd2hlcmUgWCBjYW4gYmUgZWl0aGVyIDAgb3IgMVxuICAgIC8vIExheWVyIGJpdHMgKHBvc2l0aW9uIDE0IGFuZCAxNSkgaW4gaGVhZGVyIHNob3VsZCBiZSBhbHdheXMgMCBmb3IgQURUU1xuICAgIC8vIE1vcmUgaW5mbyBodHRwczovL3dpa2kubXVsdGltZWRpYS5jeC9pbmRleC5waHA/dGl0bGU9QURUU1xuXG5cbiAgICB2YXIgaWQzRGF0YSA9IGlkM1tcImRlZmF1bHRcIl0uZ2V0SUQzRGF0YShkYXRhLCAwKSB8fCBbXTtcbiAgICB2YXIgb2Zmc2V0ID0gaWQzRGF0YS5sZW5ndGg7XG5cbiAgICBmb3IgKHZhciBsZW5ndGggPSBkYXRhLmxlbmd0aDsgb2Zmc2V0IDwgbGVuZ3RoOyBvZmZzZXQrKykge1xuICAgICAgaWYgKGFkdHNfcHJvYmUoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdBRFRTIHN5bmMgd29yZCBmb3VuZCAhJyk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfSAvLyBmZWVkIGluY29taW5nIGRhdGEgdG8gdGhlIGZyb250IG9mIHRoZSBwYXJzaW5nIHBpcGVsaW5lXG4gIDtcblxuICBfcHJvdG8uYXBwZW5kID0gZnVuY3Rpb24gYXBwZW5kKGRhdGEsIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCkge1xuICAgIHZhciB0cmFjayA9IHRoaXMuX2F1ZGlvVHJhY2s7XG4gICAgdmFyIGlkM0RhdGEgPSBpZDNbXCJkZWZhdWx0XCJdLmdldElEM0RhdGEoZGF0YSwgMCkgfHwgW107XG4gICAgdmFyIHRpbWVzdGFtcCA9IGlkM1tcImRlZmF1bHRcIl0uZ2V0VGltZVN0YW1wKGlkM0RhdGEpO1xuICAgIHZhciBwdHMgPSBPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHRpbWVzdGFtcCkgPyB0aW1lc3RhbXAgKiA5MCA6IHRpbWVPZmZzZXQgKiA5MDAwMDtcbiAgICB2YXIgZnJhbWVJbmRleCA9IDA7XG4gICAgdmFyIHN0YW1wID0gcHRzO1xuICAgIHZhciBsZW5ndGggPSBkYXRhLmxlbmd0aDtcbiAgICB2YXIgb2Zmc2V0ID0gaWQzRGF0YS5sZW5ndGg7XG4gICAgdmFyIGlkM1NhbXBsZXMgPSBbe1xuICAgICAgcHRzOiBzdGFtcCxcbiAgICAgIGR0czogc3RhbXAsXG4gICAgICBkYXRhOiBpZDNEYXRhXG4gICAgfV07XG5cbiAgICB3aGlsZSAob2Zmc2V0IDwgbGVuZ3RoIC0gMSkge1xuICAgICAgaWYgKGlzSGVhZGVyKGRhdGEsIG9mZnNldCkgJiYgb2Zmc2V0ICsgNSA8IGxlbmd0aCkge1xuICAgICAgICBpbml0VHJhY2tDb25maWcodHJhY2ssIHRoaXMub2JzZXJ2ZXIsIGRhdGEsIG9mZnNldCwgdHJhY2subWFuaWZlc3RDb2RlYyk7XG4gICAgICAgIHZhciBmcmFtZSA9IGFwcGVuZEZyYW1lKHRyYWNrLCBkYXRhLCBvZmZzZXQsIHB0cywgZnJhbWVJbmRleCk7XG5cbiAgICAgICAgaWYgKGZyYW1lKSB7XG4gICAgICAgICAgb2Zmc2V0ICs9IGZyYW1lLmxlbmd0aDtcbiAgICAgICAgICBzdGFtcCA9IGZyYW1lLnNhbXBsZS5wdHM7XG4gICAgICAgICAgZnJhbWVJbmRleCsrO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ1VuYWJsZSB0byBwYXJzZSBBQUMgZnJhbWUnKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChpZDNbXCJkZWZhdWx0XCJdLmlzSGVhZGVyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgaWQzRGF0YSA9IGlkM1tcImRlZmF1bHRcIl0uZ2V0SUQzRGF0YShkYXRhLCBvZmZzZXQpO1xuICAgICAgICBpZDNTYW1wbGVzLnB1c2goe1xuICAgICAgICAgIHB0czogc3RhbXAsXG4gICAgICAgICAgZHRzOiBzdGFtcCxcbiAgICAgICAgICBkYXRhOiBpZDNEYXRhXG4gICAgICAgIH0pO1xuICAgICAgICBvZmZzZXQgKz0gaWQzRGF0YS5sZW5ndGg7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBub3RoaW5nIGZvdW5kLCBrZWVwIGxvb2tpbmdcbiAgICAgICAgb2Zmc2V0Kys7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5yZW11eGVyLnJlbXV4KHRyYWNrLCB7XG4gICAgICBzYW1wbGVzOiBbXVxuICAgIH0sIHtcbiAgICAgIHNhbXBsZXM6IGlkM1NhbXBsZXMsXG4gICAgICBpbnB1dFRpbWVTY2FsZTogOTAwMDBcbiAgICB9LCB7XG4gICAgICBzYW1wbGVzOiBbXVxuICAgIH0sIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCk7XG4gIH07XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge307XG5cbiAgcmV0dXJuIEFBQ0RlbXV4ZXI7XG59KCk7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGFhY2RlbXV4ZXIgPSAoYWFjZGVtdXhlcl9BQUNEZW11eGVyKTtcbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvZGVtdXgvbXA0ZGVtdXhlci5qc1xudmFyIG1wNGRlbXV4ZXIgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvZGVtdXgvbXA0ZGVtdXhlci5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvZGVtdXgvbXBlZ2F1ZGlvLmpzXG4vKipcbiAqICBNUEVHIHBhcnNlciBoZWxwZXJcbiAqL1xudmFyIE1wZWdBdWRpbyA9IHtcbiAgQml0cmF0ZXNNYXA6IFszMiwgNjQsIDk2LCAxMjgsIDE2MCwgMTkyLCAyMjQsIDI1NiwgMjg4LCAzMjAsIDM1MiwgMzg0LCA0MTYsIDQ0OCwgMzIsIDQ4LCA1NiwgNjQsIDgwLCA5NiwgMTEyLCAxMjgsIDE2MCwgMTkyLCAyMjQsIDI1NiwgMzIwLCAzODQsIDMyLCA0MCwgNDgsIDU2LCA2NCwgODAsIDk2LCAxMTIsIDEyOCwgMTYwLCAxOTIsIDIyNCwgMjU2LCAzMjAsIDMyLCA0OCwgNTYsIDY0LCA4MCwgOTYsIDExMiwgMTI4LCAxNDQsIDE2MCwgMTc2LCAxOTIsIDIyNCwgMjU2LCA4LCAxNiwgMjQsIDMyLCA0MCwgNDgsIDU2LCA2NCwgODAsIDk2LCAxMTIsIDEyOCwgMTQ0LCAxNjBdLFxuICBTYW1wbGluZ1JhdGVNYXA6IFs0NDEwMCwgNDgwMDAsIDMyMDAwLCAyMjA1MCwgMjQwMDAsIDE2MDAwLCAxMTAyNSwgMTIwMDAsIDgwMDBdLFxuICBTYW1wbGVzQ29lZmZpY2llbnRzOiBbLy8gTVBFRyAyLjVcbiAgWzAsIC8vIFJlc2VydmVkXG4gIDcyLCAvLyBMYXllcjNcbiAgMTQ0LCAvLyBMYXllcjJcbiAgMTIgLy8gTGF5ZXIxXG4gIF0sIC8vIFJlc2VydmVkXG4gIFswLCAvLyBSZXNlcnZlZFxuICAwLCAvLyBMYXllcjNcbiAgMCwgLy8gTGF5ZXIyXG4gIDAgLy8gTGF5ZXIxXG4gIF0sIC8vIE1QRUcgMlxuICBbMCwgLy8gUmVzZXJ2ZWRcbiAgNzIsIC8vIExheWVyM1xuICAxNDQsIC8vIExheWVyMlxuICAxMiAvLyBMYXllcjFcbiAgXSwgLy8gTVBFRyAxXG4gIFswLCAvLyBSZXNlcnZlZFxuICAxNDQsIC8vIExheWVyM1xuICAxNDQsIC8vIExheWVyMlxuICAxMiAvLyBMYXllcjFcbiAgXV0sXG4gIEJ5dGVzSW5TbG90OiBbMCwgLy8gUmVzZXJ2ZWRcbiAgMSwgLy8gTGF5ZXIzXG4gIDEsIC8vIExheWVyMlxuICA0IC8vIExheWVyMVxuICBdLFxuICBhcHBlbmRGcmFtZTogZnVuY3Rpb24gYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIG9mZnNldCwgcHRzLCBmcmFtZUluZGV4KSB7XG4gICAgLy8gVXNpbmcgaHR0cDovL3d3dy5kYXRhdm95YWdlLmNvbS9tcGdzY3JpcHQvbXBlZ2hkci5odG0gYXMgYSByZWZlcmVuY2VcbiAgICBpZiAob2Zmc2V0ICsgMjQgPiBkYXRhLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICB2YXIgaGVhZGVyID0gdGhpcy5wYXJzZUhlYWRlcihkYXRhLCBvZmZzZXQpO1xuXG4gICAgaWYgKGhlYWRlciAmJiBvZmZzZXQgKyBoZWFkZXIuZnJhbWVMZW5ndGggPD0gZGF0YS5sZW5ndGgpIHtcbiAgICAgIHZhciBmcmFtZUR1cmF0aW9uID0gaGVhZGVyLnNhbXBsZXNQZXJGcmFtZSAqIDkwMDAwIC8gaGVhZGVyLnNhbXBsZVJhdGU7XG4gICAgICB2YXIgc3RhbXAgPSBwdHMgKyBmcmFtZUluZGV4ICogZnJhbWVEdXJhdGlvbjtcbiAgICAgIHZhciBzYW1wbGUgPSB7XG4gICAgICAgIHVuaXQ6IGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyBoZWFkZXIuZnJhbWVMZW5ndGgpLFxuICAgICAgICBwdHM6IHN0YW1wLFxuICAgICAgICBkdHM6IHN0YW1wXG4gICAgICB9O1xuICAgICAgdHJhY2suY29uZmlnID0gW107XG4gICAgICB0cmFjay5jaGFubmVsQ291bnQgPSBoZWFkZXIuY2hhbm5lbENvdW50O1xuICAgICAgdHJhY2suc2FtcGxlcmF0ZSA9IGhlYWRlci5zYW1wbGVSYXRlO1xuICAgICAgdHJhY2suc2FtcGxlcy5wdXNoKHNhbXBsZSk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzYW1wbGU6IHNhbXBsZSxcbiAgICAgICAgbGVuZ3RoOiBoZWFkZXIuZnJhbWVMZW5ndGhcbiAgICAgIH07XG4gICAgfVxuXG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfSxcbiAgcGFyc2VIZWFkZXI6IGZ1bmN0aW9uIHBhcnNlSGVhZGVyKGRhdGEsIG9mZnNldCkge1xuICAgIHZhciBoZWFkZXJCID0gZGF0YVtvZmZzZXQgKyAxXSA+PiAzICYgMztcbiAgICB2YXIgaGVhZGVyQyA9IGRhdGFbb2Zmc2V0ICsgMV0gPj4gMSAmIDM7XG4gICAgdmFyIGhlYWRlckUgPSBkYXRhW29mZnNldCArIDJdID4+IDQgJiAxNTtcbiAgICB2YXIgaGVhZGVyRiA9IGRhdGFbb2Zmc2V0ICsgMl0gPj4gMiAmIDM7XG4gICAgdmFyIGhlYWRlckcgPSBkYXRhW29mZnNldCArIDJdID4+IDEgJiAxO1xuXG4gICAgaWYgKGhlYWRlckIgIT09IDEgJiYgaGVhZGVyRSAhPT0gMCAmJiBoZWFkZXJFICE9PSAxNSAmJiBoZWFkZXJGICE9PSAzKSB7XG4gICAgICB2YXIgY29sdW1uSW5CaXRyYXRlcyA9IGhlYWRlckIgPT09IDMgPyAzIC0gaGVhZGVyQyA6IGhlYWRlckMgPT09IDMgPyAzIDogNDtcbiAgICAgIHZhciBiaXRSYXRlID0gTXBlZ0F1ZGlvLkJpdHJhdGVzTWFwW2NvbHVtbkluQml0cmF0ZXMgKiAxNCArIGhlYWRlckUgLSAxXSAqIDEwMDA7XG4gICAgICB2YXIgY29sdW1uSW5TYW1wbGVSYXRlcyA9IGhlYWRlckIgPT09IDMgPyAwIDogaGVhZGVyQiA9PT0gMiA/IDEgOiAyO1xuICAgICAgdmFyIHNhbXBsZVJhdGUgPSBNcGVnQXVkaW8uU2FtcGxpbmdSYXRlTWFwW2NvbHVtbkluU2FtcGxlUmF0ZXMgKiAzICsgaGVhZGVyRl07XG4gICAgICB2YXIgY2hhbm5lbENvdW50ID0gZGF0YVtvZmZzZXQgKyAzXSA+PiA2ID09PSAzID8gMSA6IDI7IC8vIElmIGJpdHMgb2YgY2hhbm5lbCBtb2RlIGFyZSBgMTFgIHRoZW4gaXQgaXMgYSBzaW5nbGUgY2hhbm5lbCAoTW9ubylcblxuICAgICAgdmFyIHNhbXBsZUNvZWZmaWNpZW50ID0gTXBlZ0F1ZGlvLlNhbXBsZXNDb2VmZmljaWVudHNbaGVhZGVyQl1baGVhZGVyQ107XG4gICAgICB2YXIgYnl0ZXNJblNsb3QgPSBNcGVnQXVkaW8uQnl0ZXNJblNsb3RbaGVhZGVyQ107XG4gICAgICB2YXIgc2FtcGxlc1BlckZyYW1lID0gc2FtcGxlQ29lZmZpY2llbnQgKiA4ICogYnl0ZXNJblNsb3Q7XG4gICAgICB2YXIgZnJhbWVMZW5ndGggPSBwYXJzZUludChzYW1wbGVDb2VmZmljaWVudCAqIGJpdFJhdGUgLyBzYW1wbGVSYXRlICsgaGVhZGVyRywgMTApICogYnl0ZXNJblNsb3Q7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzYW1wbGVSYXRlOiBzYW1wbGVSYXRlLFxuICAgICAgICBjaGFubmVsQ291bnQ6IGNoYW5uZWxDb3VudCxcbiAgICAgICAgZnJhbWVMZW5ndGg6IGZyYW1lTGVuZ3RoLFxuICAgICAgICBzYW1wbGVzUGVyRnJhbWU6IHNhbXBsZXNQZXJGcmFtZVxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9LFxuICBpc0hlYWRlclBhdHRlcm46IGZ1bmN0aW9uIGlzSGVhZGVyUGF0dGVybihkYXRhLCBvZmZzZXQpIHtcbiAgICByZXR1cm4gZGF0YVtvZmZzZXRdID09PSAweGZmICYmIChkYXRhW29mZnNldCArIDFdICYgMHhlMCkgPT09IDB4ZTAgJiYgKGRhdGFbb2Zmc2V0ICsgMV0gJiAweDA2KSAhPT0gMHgwMDtcbiAgfSxcbiAgaXNIZWFkZXI6IGZ1bmN0aW9uIGlzSGVhZGVyKGRhdGEsIG9mZnNldCkge1xuICAgIC8vIExvb2sgZm9yIE1QRUcgaGVhZGVyIHwgMTExMSAxMTExIHwgMTExWCBYWVpYIHwgd2hlcmUgWCBjYW4gYmUgZWl0aGVyIDAgb3IgMSBhbmQgWSBvciBaIHNob3VsZCBiZSAxXG4gICAgLy8gTGF5ZXIgYml0cyAocG9zaXRpb24gMTQgYW5kIDE1KSBpbiBoZWFkZXIgc2hvdWxkIGJlIGFsd2F5cyBkaWZmZXJlbnQgZnJvbSAwIChMYXllciBJIG9yIExheWVyIElJIG9yIExheWVyIElJSSlcbiAgICAvLyBNb3JlIGluZm8gaHR0cDovL3d3dy5tcDMtdGVjaC5vcmcvcHJvZ3JhbW1lci9mcmFtZV9oZWFkZXIuaHRtbFxuICAgIGlmIChvZmZzZXQgKyAxIDwgZGF0YS5sZW5ndGggJiYgdGhpcy5pc0hlYWRlclBhdHRlcm4oZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9LFxuICBwcm9iZTogZnVuY3Rpb24gcHJvYmUoZGF0YSwgb2Zmc2V0KSB7XG4gICAgLy8gc2FtZSBhcyBpc0hlYWRlciBidXQgd2UgYWxzbyBjaGVjayB0aGF0IE1QRUcgZnJhbWUgZm9sbG93cyBsYXN0IE1QRUcgZnJhbWVcbiAgICAvLyBvciBlbmQgb2YgZGF0YSBpcyByZWFjaGVkXG4gICAgaWYgKG9mZnNldCArIDEgPCBkYXRhLmxlbmd0aCAmJiB0aGlzLmlzSGVhZGVyUGF0dGVybihkYXRhLCBvZmZzZXQpKSB7XG4gICAgICAvLyBNUEVHIGhlYWRlciBMZW5ndGhcbiAgICAgIHZhciBoZWFkZXJMZW5ndGggPSA0OyAvLyBNUEVHIGZyYW1lIExlbmd0aFxuXG4gICAgICB2YXIgaGVhZGVyID0gdGhpcy5wYXJzZUhlYWRlcihkYXRhLCBvZmZzZXQpO1xuICAgICAgdmFyIGZyYW1lTGVuZ3RoID0gaGVhZGVyTGVuZ3RoO1xuXG4gICAgICBpZiAoaGVhZGVyICYmIGhlYWRlci5mcmFtZUxlbmd0aCkge1xuICAgICAgICBmcmFtZUxlbmd0aCA9IGhlYWRlci5mcmFtZUxlbmd0aDtcbiAgICAgIH1cblxuICAgICAgdmFyIG5ld09mZnNldCA9IG9mZnNldCArIGZyYW1lTGVuZ3RoO1xuXG4gICAgICBpZiAobmV3T2Zmc2V0ID09PSBkYXRhLmxlbmd0aCB8fCBuZXdPZmZzZXQgKyAxIDwgZGF0YS5sZW5ndGggJiYgdGhpcy5pc0hlYWRlclBhdHRlcm4oZGF0YSwgbmV3T2Zmc2V0KSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn07XG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBtcGVnYXVkaW8gPSAoTXBlZ0F1ZGlvKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2RlbXV4L2V4cC1nb2xvbWIuanNcbi8qKlxuICogUGFyc2VyIGZvciBleHBvbmVudGlhbCBHb2xvbWIgY29kZXMsIGEgdmFyaWFibGUtYml0d2lkdGggbnVtYmVyIGVuY29kaW5nIHNjaGVtZSB1c2VkIGJ5IGgyNjQuXG4qL1xuXG5cbnZhciBleHBfZ29sb21iX0V4cEdvbG9tYiA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIEV4cEdvbG9tYihkYXRhKSB7XG4gICAgdGhpcy5kYXRhID0gZGF0YTsgLy8gdGhlIG51bWJlciBvZiBieXRlcyBsZWZ0IHRvIGV4YW1pbmUgaW4gdGhpcy5kYXRhXG5cbiAgICB0aGlzLmJ5dGVzQXZhaWxhYmxlID0gZGF0YS5ieXRlTGVuZ3RoOyAvLyB0aGUgY3VycmVudCB3b3JkIGJlaW5nIGV4YW1pbmVkXG5cbiAgICB0aGlzLndvcmQgPSAwOyAvLyA6dWludFxuICAgIC8vIHRoZSBudW1iZXIgb2YgYml0cyBsZWZ0IHRvIGV4YW1pbmUgaW4gdGhlIGN1cnJlbnQgd29yZFxuXG4gICAgdGhpcy5iaXRzQXZhaWxhYmxlID0gMDsgLy8gOnVpbnRcbiAgfSAvLyAoKTp2b2lkXG5cblxuICB2YXIgX3Byb3RvID0gRXhwR29sb21iLnByb3RvdHlwZTtcblxuICBfcHJvdG8ubG9hZFdvcmQgPSBmdW5jdGlvbiBsb2FkV29yZCgpIHtcbiAgICB2YXIgZGF0YSA9IHRoaXMuZGF0YSxcbiAgICAgICAgYnl0ZXNBdmFpbGFibGUgPSB0aGlzLmJ5dGVzQXZhaWxhYmxlLFxuICAgICAgICBwb3NpdGlvbiA9IGRhdGEuYnl0ZUxlbmd0aCAtIGJ5dGVzQXZhaWxhYmxlLFxuICAgICAgICB3b3JraW5nQnl0ZXMgPSBuZXcgVWludDhBcnJheSg0KSxcbiAgICAgICAgYXZhaWxhYmxlQnl0ZXMgPSBNYXRoLm1pbig0LCBieXRlc0F2YWlsYWJsZSk7XG5cbiAgICBpZiAoYXZhaWxhYmxlQnl0ZXMgPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbm8gYnl0ZXMgYXZhaWxhYmxlJyk7XG4gICAgfVxuXG4gICAgd29ya2luZ0J5dGVzLnNldChkYXRhLnN1YmFycmF5KHBvc2l0aW9uLCBwb3NpdGlvbiArIGF2YWlsYWJsZUJ5dGVzKSk7XG4gICAgdGhpcy53b3JkID0gbmV3IERhdGFWaWV3KHdvcmtpbmdCeXRlcy5idWZmZXIpLmdldFVpbnQzMigwKTsgLy8gdHJhY2sgdGhlIGFtb3VudCBvZiB0aGlzLmRhdGEgdGhhdCBoYXMgYmVlbiBwcm9jZXNzZWRcblxuICAgIHRoaXMuYml0c0F2YWlsYWJsZSA9IGF2YWlsYWJsZUJ5dGVzICogODtcbiAgICB0aGlzLmJ5dGVzQXZhaWxhYmxlIC09IGF2YWlsYWJsZUJ5dGVzO1xuICB9IC8vIChjb3VudDppbnQpOnZvaWRcbiAgO1xuXG4gIF9wcm90by5za2lwQml0cyA9IGZ1bmN0aW9uIHNraXBCaXRzKGNvdW50KSB7XG4gICAgdmFyIHNraXBCeXRlczsgLy8gOmludFxuXG4gICAgaWYgKHRoaXMuYml0c0F2YWlsYWJsZSA+IGNvdW50KSB7XG4gICAgICB0aGlzLndvcmQgPDw9IGNvdW50O1xuICAgICAgdGhpcy5iaXRzQXZhaWxhYmxlIC09IGNvdW50O1xuICAgIH0gZWxzZSB7XG4gICAgICBjb3VudCAtPSB0aGlzLmJpdHNBdmFpbGFibGU7XG4gICAgICBza2lwQnl0ZXMgPSBjb3VudCA+PiAzO1xuICAgICAgY291bnQgLT0gc2tpcEJ5dGVzID4+IDM7XG4gICAgICB0aGlzLmJ5dGVzQXZhaWxhYmxlIC09IHNraXBCeXRlcztcbiAgICAgIHRoaXMubG9hZFdvcmQoKTtcbiAgICAgIHRoaXMud29yZCA8PD0gY291bnQ7XG4gICAgICB0aGlzLmJpdHNBdmFpbGFibGUgLT0gY291bnQ7XG4gICAgfVxuICB9IC8vIChzaXplOmludCk6dWludFxuICA7XG5cbiAgX3Byb3RvLnJlYWRCaXRzID0gZnVuY3Rpb24gcmVhZEJpdHMoc2l6ZSkge1xuICAgIHZhciBiaXRzID0gTWF0aC5taW4odGhpcy5iaXRzQXZhaWxhYmxlLCBzaXplKSxcbiAgICAgICAgLy8gOnVpbnRcbiAgICB2YWx1ID0gdGhpcy53b3JkID4+PiAzMiAtIGJpdHM7IC8vIDp1aW50XG5cbiAgICBpZiAoc2l6ZSA+IDMyKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoJ0Nhbm5vdCByZWFkIG1vcmUgdGhhbiAzMiBiaXRzIGF0IGEgdGltZScpO1xuICAgIH1cblxuICAgIHRoaXMuYml0c0F2YWlsYWJsZSAtPSBiaXRzO1xuXG4gICAgaWYgKHRoaXMuYml0c0F2YWlsYWJsZSA+IDApIHtcbiAgICAgIHRoaXMud29yZCA8PD0gYml0cztcbiAgICB9IGVsc2UgaWYgKHRoaXMuYnl0ZXNBdmFpbGFibGUgPiAwKSB7XG4gICAgICB0aGlzLmxvYWRXb3JkKCk7XG4gICAgfVxuXG4gICAgYml0cyA9IHNpemUgLSBiaXRzO1xuXG4gICAgaWYgKGJpdHMgPiAwICYmIHRoaXMuYml0c0F2YWlsYWJsZSkge1xuICAgICAgcmV0dXJuIHZhbHUgPDwgYml0cyB8IHRoaXMucmVhZEJpdHMoYml0cyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB2YWx1O1xuICAgIH1cbiAgfSAvLyAoKTp1aW50XG4gIDtcblxuICBfcHJvdG8uc2tpcExaID0gZnVuY3Rpb24gc2tpcExaKCkge1xuICAgIHZhciBsZWFkaW5nWmVyb0NvdW50OyAvLyA6dWludFxuXG4gICAgZm9yIChsZWFkaW5nWmVyb0NvdW50ID0gMDsgbGVhZGluZ1plcm9Db3VudCA8IHRoaXMuYml0c0F2YWlsYWJsZTsgKytsZWFkaW5nWmVyb0NvdW50KSB7XG4gICAgICBpZiAoKHRoaXMud29yZCAmIDB4ODAwMDAwMDAgPj4+IGxlYWRpbmdaZXJvQ291bnQpICE9PSAwKSB7XG4gICAgICAgIC8vIHRoZSBmaXJzdCBiaXQgb2Ygd29ya2luZyB3b3JkIGlzIDFcbiAgICAgICAgdGhpcy53b3JkIDw8PSBsZWFkaW5nWmVyb0NvdW50O1xuICAgICAgICB0aGlzLmJpdHNBdmFpbGFibGUgLT0gbGVhZGluZ1plcm9Db3VudDtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdaZXJvQ291bnQ7XG4gICAgICB9XG4gICAgfSAvLyB3ZSBleGhhdXN0ZWQgd29yZCBhbmQgc3RpbGwgaGF2ZSBub3QgZm91bmQgYSAxXG5cblxuICAgIHRoaXMubG9hZFdvcmQoKTtcbiAgICByZXR1cm4gbGVhZGluZ1plcm9Db3VudCArIHRoaXMuc2tpcExaKCk7XG4gIH0gLy8gKCk6dm9pZFxuICA7XG5cbiAgX3Byb3RvLnNraXBVRUcgPSBmdW5jdGlvbiBza2lwVUVHKCkge1xuICAgIHRoaXMuc2tpcEJpdHMoMSArIHRoaXMuc2tpcExaKCkpO1xuICB9IC8vICgpOnZvaWRcbiAgO1xuXG4gIF9wcm90by5za2lwRUcgPSBmdW5jdGlvbiBza2lwRUcoKSB7XG4gICAgdGhpcy5za2lwQml0cygxICsgdGhpcy5za2lwTFooKSk7XG4gIH0gLy8gKCk6dWludFxuICA7XG5cbiAgX3Byb3RvLnJlYWRVRUcgPSBmdW5jdGlvbiByZWFkVUVHKCkge1xuICAgIHZhciBjbHogPSB0aGlzLnNraXBMWigpOyAvLyA6dWludFxuXG4gICAgcmV0dXJuIHRoaXMucmVhZEJpdHMoY2x6ICsgMSkgLSAxO1xuICB9IC8vICgpOmludFxuICA7XG5cbiAgX3Byb3RvLnJlYWRFRyA9IGZ1bmN0aW9uIHJlYWRFRygpIHtcbiAgICB2YXIgdmFsdSA9IHRoaXMucmVhZFVFRygpOyAvLyA6aW50XG5cbiAgICBpZiAoMHgwMSAmIHZhbHUpIHtcbiAgICAgIC8vIHRoZSBudW1iZXIgaXMgb2RkIGlmIHRoZSBsb3cgb3JkZXIgYml0IGlzIHNldFxuICAgICAgcmV0dXJuIDEgKyB2YWx1ID4+PiAxOyAvLyBhZGQgMSB0byBtYWtlIGl0IGV2ZW4sIGFuZCBkaXZpZGUgYnkgMlxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gLTEgKiAodmFsdSA+Pj4gMSk7IC8vIGRpdmlkZSBieSB0d28gdGhlbiBtYWtlIGl0IG5lZ2F0aXZlXG4gICAgfVxuICB9IC8vIFNvbWUgY29udmVuaWVuY2UgZnVuY3Rpb25zXG4gIC8vIDpCb29sZWFuXG4gIDtcblxuICBfcHJvdG8ucmVhZEJvb2xlYW4gPSBmdW5jdGlvbiByZWFkQm9vbGVhbigpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkQml0cygxKSA9PT0gMTtcbiAgfSAvLyAoKTppbnRcbiAgO1xuXG4gIF9wcm90by5yZWFkVUJ5dGUgPSBmdW5jdGlvbiByZWFkVUJ5dGUoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZEJpdHMoOCk7XG4gIH0gLy8gKCk6aW50XG4gIDtcblxuICBfcHJvdG8ucmVhZFVTaG9ydCA9IGZ1bmN0aW9uIHJlYWRVU2hvcnQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZEJpdHMoMTYpO1xuICB9IC8vICgpOmludFxuICA7XG5cbiAgX3Byb3RvLnJlYWRVSW50ID0gZnVuY3Rpb24gcmVhZFVJbnQoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVhZEJpdHMoMzIpO1xuICB9XG4gIC8qKlxuICAgKiBBZHZhbmNlIHRoZSBFeHBHb2xvbWIgZGVjb2RlciBwYXN0IGEgc2NhbGluZyBsaXN0LiBUaGUgc2NhbGluZ1xuICAgKiBsaXN0IGlzIG9wdGlvbmFsbHkgdHJhbnNtaXR0ZWQgYXMgcGFydCBvZiBhIHNlcXVlbmNlIHBhcmFtZXRlclxuICAgKiBzZXQgYW5kIGlzIG5vdCByZWxldmFudCB0byB0cmFuc211eGluZy5cbiAgICogQHBhcmFtIGNvdW50IHtudW1iZXJ9IHRoZSBudW1iZXIgb2YgZW50cmllcyBpbiB0aGlzIHNjYWxpbmcgbGlzdFxuICAgKiBAc2VlIFJlY29tbWVuZGF0aW9uIElUVS1UIEguMjY0LCBTZWN0aW9uIDcuMy4yLjEuMS4xXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnNraXBTY2FsaW5nTGlzdCA9IGZ1bmN0aW9uIHNraXBTY2FsaW5nTGlzdChjb3VudCkge1xuICAgIHZhciBsYXN0U2NhbGUgPSA4LFxuICAgICAgICBuZXh0U2NhbGUgPSA4LFxuICAgICAgICBqLFxuICAgICAgICBkZWx0YVNjYWxlO1xuXG4gICAgZm9yIChqID0gMDsgaiA8IGNvdW50OyBqKyspIHtcbiAgICAgIGlmIChuZXh0U2NhbGUgIT09IDApIHtcbiAgICAgICAgZGVsdGFTY2FsZSA9IHRoaXMucmVhZEVHKCk7XG4gICAgICAgIG5leHRTY2FsZSA9IChsYXN0U2NhbGUgKyBkZWx0YVNjYWxlICsgMjU2KSAlIDI1NjtcbiAgICAgIH1cblxuICAgICAgbGFzdFNjYWxlID0gbmV4dFNjYWxlID09PSAwID8gbGFzdFNjYWxlIDogbmV4dFNjYWxlO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogUmVhZCBhIHNlcXVlbmNlIHBhcmFtZXRlciBzZXQgYW5kIHJldHVybiBzb21lIGludGVyZXN0aW5nIHZpZGVvXG4gICAqIHByb3BlcnRpZXMuIEEgc2VxdWVuY2UgcGFyYW1ldGVyIHNldCBpcyB0aGUgSDI2NCBtZXRhZGF0YSB0aGF0XG4gICAqIGRlc2NyaWJlcyB0aGUgcHJvcGVydGllcyBvZiB1cGNvbWluZyB2aWRlbyBmcmFtZXMuXG4gICAqIEBwYXJhbSBkYXRhIHtVaW50OEFycmF5fSB0aGUgYnl0ZXMgb2YgYSBzZXF1ZW5jZSBwYXJhbWV0ZXIgc2V0XG4gICAqIEByZXR1cm4ge29iamVjdH0gYW4gb2JqZWN0IHdpdGggY29uZmlndXJhdGlvbiBwYXJzZWQgZnJvbSB0aGVcbiAgICogc2VxdWVuY2UgcGFyYW1ldGVyIHNldCwgaW5jbHVkaW5nIHRoZSBkaW1lbnNpb25zIG9mIHRoZVxuICAgKiBhc3NvY2lhdGVkIHZpZGVvIGZyYW1lcy5cbiAgICovXG4gIDtcblxuICBfcHJvdG8ucmVhZFNQUyA9IGZ1bmN0aW9uIHJlYWRTUFMoKSB7XG4gICAgdmFyIGZyYW1lQ3JvcExlZnRPZmZzZXQgPSAwLFxuICAgICAgICBmcmFtZUNyb3BSaWdodE9mZnNldCA9IDAsXG4gICAgICAgIGZyYW1lQ3JvcFRvcE9mZnNldCA9IDAsXG4gICAgICAgIGZyYW1lQ3JvcEJvdHRvbU9mZnNldCA9IDAsXG4gICAgICAgIHByb2ZpbGVJZGMsXG4gICAgICAgIHByb2ZpbGVDb21wYXQsXG4gICAgICAgIGxldmVsSWRjLFxuICAgICAgICBudW1SZWZGcmFtZXNJblBpY09yZGVyQ250Q3ljbGUsXG4gICAgICAgIHBpY1dpZHRoSW5NYnNNaW51czEsXG4gICAgICAgIHBpY0hlaWdodEluTWFwVW5pdHNNaW51czEsXG4gICAgICAgIGZyYW1lTWJzT25seUZsYWcsXG4gICAgICAgIHNjYWxpbmdMaXN0Q291bnQsXG4gICAgICAgIGksXG4gICAgICAgIHJlYWRVQnl0ZSA9IHRoaXMucmVhZFVCeXRlLmJpbmQodGhpcyksXG4gICAgICAgIHJlYWRCaXRzID0gdGhpcy5yZWFkQml0cy5iaW5kKHRoaXMpLFxuICAgICAgICByZWFkVUVHID0gdGhpcy5yZWFkVUVHLmJpbmQodGhpcyksXG4gICAgICAgIHJlYWRCb29sZWFuID0gdGhpcy5yZWFkQm9vbGVhbi5iaW5kKHRoaXMpLFxuICAgICAgICBza2lwQml0cyA9IHRoaXMuc2tpcEJpdHMuYmluZCh0aGlzKSxcbiAgICAgICAgc2tpcEVHID0gdGhpcy5za2lwRUcuYmluZCh0aGlzKSxcbiAgICAgICAgc2tpcFVFRyA9IHRoaXMuc2tpcFVFRy5iaW5kKHRoaXMpLFxuICAgICAgICBza2lwU2NhbGluZ0xpc3QgPSB0aGlzLnNraXBTY2FsaW5nTGlzdC5iaW5kKHRoaXMpO1xuICAgIHJlYWRVQnl0ZSgpO1xuICAgIHByb2ZpbGVJZGMgPSByZWFkVUJ5dGUoKTsgLy8gcHJvZmlsZV9pZGNcblxuICAgIHByb2ZpbGVDb21wYXQgPSByZWFkQml0cyg1KTsgLy8gY29uc3RyYWludF9zZXRbMC00XV9mbGFnLCB1KDUpXG5cbiAgICBza2lwQml0cygzKTsgLy8gcmVzZXJ2ZWRfemVyb18zYml0cyB1KDMpLFxuXG4gICAgbGV2ZWxJZGMgPSByZWFkVUJ5dGUoKTsgLy8gbGV2ZWxfaWRjIHUoOClcblxuICAgIHNraXBVRUcoKTsgLy8gc2VxX3BhcmFtZXRlcl9zZXRfaWRcbiAgICAvLyBzb21lIHByb2ZpbGVzIGhhdmUgbW9yZSBvcHRpb25hbCBkYXRhIHdlIGRvbid0IG5lZWRcblxuICAgIGlmIChwcm9maWxlSWRjID09PSAxMDAgfHwgcHJvZmlsZUlkYyA9PT0gMTEwIHx8IHByb2ZpbGVJZGMgPT09IDEyMiB8fCBwcm9maWxlSWRjID09PSAyNDQgfHwgcHJvZmlsZUlkYyA9PT0gNDQgfHwgcHJvZmlsZUlkYyA9PT0gODMgfHwgcHJvZmlsZUlkYyA9PT0gODYgfHwgcHJvZmlsZUlkYyA9PT0gMTE4IHx8IHByb2ZpbGVJZGMgPT09IDEyOCkge1xuICAgICAgdmFyIGNocm9tYUZvcm1hdElkYyA9IHJlYWRVRUcoKTtcblxuICAgICAgaWYgKGNocm9tYUZvcm1hdElkYyA9PT0gMykge1xuICAgICAgICBza2lwQml0cygxKTtcbiAgICAgIH0gLy8gc2VwYXJhdGVfY29sb3VyX3BsYW5lX2ZsYWdcblxuXG4gICAgICBza2lwVUVHKCk7IC8vIGJpdF9kZXB0aF9sdW1hX21pbnVzOFxuXG4gICAgICBza2lwVUVHKCk7IC8vIGJpdF9kZXB0aF9jaHJvbWFfbWludXM4XG5cbiAgICAgIHNraXBCaXRzKDEpOyAvLyBxcHByaW1lX3lfemVyb190cmFuc2Zvcm1fYnlwYXNzX2ZsYWdcblxuICAgICAgaWYgKHJlYWRCb29sZWFuKCkpIHtcbiAgICAgICAgLy8gc2VxX3NjYWxpbmdfbWF0cml4X3ByZXNlbnRfZmxhZ1xuICAgICAgICBzY2FsaW5nTGlzdENvdW50ID0gY2hyb21hRm9ybWF0SWRjICE9PSAzID8gOCA6IDEyO1xuXG4gICAgICAgIGZvciAoaSA9IDA7IGkgPCBzY2FsaW5nTGlzdENvdW50OyBpKyspIHtcbiAgICAgICAgICBpZiAocmVhZEJvb2xlYW4oKSkge1xuICAgICAgICAgICAgLy8gc2VxX3NjYWxpbmdfbGlzdF9wcmVzZW50X2ZsYWdbIGkgXVxuICAgICAgICAgICAgaWYgKGkgPCA2KSB7XG4gICAgICAgICAgICAgIHNraXBTY2FsaW5nTGlzdCgxNik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBza2lwU2NhbGluZ0xpc3QoNjQpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHNraXBVRUcoKTsgLy8gbG9nMl9tYXhfZnJhbWVfbnVtX21pbnVzNFxuXG4gICAgdmFyIHBpY09yZGVyQ250VHlwZSA9IHJlYWRVRUcoKTtcblxuICAgIGlmIChwaWNPcmRlckNudFR5cGUgPT09IDApIHtcbiAgICAgIHJlYWRVRUcoKTsgLy8gbG9nMl9tYXhfcGljX29yZGVyX2NudF9sc2JfbWludXM0XG4gICAgfSBlbHNlIGlmIChwaWNPcmRlckNudFR5cGUgPT09IDEpIHtcbiAgICAgIHNraXBCaXRzKDEpOyAvLyBkZWx0YV9waWNfb3JkZXJfYWx3YXlzX3plcm9fZmxhZ1xuXG4gICAgICBza2lwRUcoKTsgLy8gb2Zmc2V0X2Zvcl9ub25fcmVmX3BpY1xuXG4gICAgICBza2lwRUcoKTsgLy8gb2Zmc2V0X2Zvcl90b3BfdG9fYm90dG9tX2ZpZWxkXG5cbiAgICAgIG51bVJlZkZyYW1lc0luUGljT3JkZXJDbnRDeWNsZSA9IHJlYWRVRUcoKTtcblxuICAgICAgZm9yIChpID0gMDsgaSA8IG51bVJlZkZyYW1lc0luUGljT3JkZXJDbnRDeWNsZTsgaSsrKSB7XG4gICAgICAgIHNraXBFRygpO1xuICAgICAgfSAvLyBvZmZzZXRfZm9yX3JlZl9mcmFtZVsgaSBdXG5cbiAgICB9XG5cbiAgICBza2lwVUVHKCk7IC8vIG1heF9udW1fcmVmX2ZyYW1lc1xuXG4gICAgc2tpcEJpdHMoMSk7IC8vIGdhcHNfaW5fZnJhbWVfbnVtX3ZhbHVlX2FsbG93ZWRfZmxhZ1xuXG4gICAgcGljV2lkdGhJbk1ic01pbnVzMSA9IHJlYWRVRUcoKTtcbiAgICBwaWNIZWlnaHRJbk1hcFVuaXRzTWludXMxID0gcmVhZFVFRygpO1xuICAgIGZyYW1lTWJzT25seUZsYWcgPSByZWFkQml0cygxKTtcblxuICAgIGlmIChmcmFtZU1ic09ubHlGbGFnID09PSAwKSB7XG4gICAgICBza2lwQml0cygxKTtcbiAgICB9IC8vIG1iX2FkYXB0aXZlX2ZyYW1lX2ZpZWxkX2ZsYWdcblxuXG4gICAgc2tpcEJpdHMoMSk7IC8vIGRpcmVjdF84eDhfaW5mZXJlbmNlX2ZsYWdcblxuICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAvLyBmcmFtZV9jcm9wcGluZ19mbGFnXG4gICAgICBmcmFtZUNyb3BMZWZ0T2Zmc2V0ID0gcmVhZFVFRygpO1xuICAgICAgZnJhbWVDcm9wUmlnaHRPZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgICBmcmFtZUNyb3BUb3BPZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgICBmcmFtZUNyb3BCb3R0b21PZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgfVxuXG4gICAgdmFyIHBpeGVsUmF0aW8gPSBbMSwgMV07XG5cbiAgICBpZiAocmVhZEJvb2xlYW4oKSkge1xuICAgICAgLy8gdnVpX3BhcmFtZXRlcnNfcHJlc2VudF9mbGFnXG4gICAgICBpZiAocmVhZEJvb2xlYW4oKSkge1xuICAgICAgICAvLyBhc3BlY3RfcmF0aW9faW5mb19wcmVzZW50X2ZsYWdcbiAgICAgICAgdmFyIGFzcGVjdFJhdGlvSWRjID0gcmVhZFVCeXRlKCk7XG5cbiAgICAgICAgc3dpdGNoIChhc3BlY3RSYXRpb0lkYykge1xuICAgICAgICAgIGNhc2UgMTpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMSwgMV07XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgMjpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMTIsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAzOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxMCwgMTFdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDQ6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzE2LCAxMV07XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgNTpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbNDAsIDMzXTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSA2OlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsyNCwgMTFdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDc6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzIwLCAxMV07XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgODpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMzIsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSA5OlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFs4MCwgMzNdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDEwOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxOCwgMTFdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDExOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxNSwgMTFdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDEyOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFs2NCwgMzNdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDEzOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxNjAsIDk5XTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAxNDpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbNCwgM107XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgMTU6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzMsIDJdO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDE2OlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsyLCAxXTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAyNTU6XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbcmVhZFVCeXRlKCkgPDwgOCB8IHJlYWRVQnl0ZSgpLCByZWFkVUJ5dGUoKSA8PCA4IHwgcmVhZFVCeXRlKCldO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICB3aWR0aDogTWF0aC5jZWlsKChwaWNXaWR0aEluTWJzTWludXMxICsgMSkgKiAxNiAtIGZyYW1lQ3JvcExlZnRPZmZzZXQgKiAyIC0gZnJhbWVDcm9wUmlnaHRPZmZzZXQgKiAyKSxcbiAgICAgIGhlaWdodDogKDIgLSBmcmFtZU1ic09ubHlGbGFnKSAqIChwaWNIZWlnaHRJbk1hcFVuaXRzTWludXMxICsgMSkgKiAxNiAtIChmcmFtZU1ic09ubHlGbGFnID8gMiA6IDQpICogKGZyYW1lQ3JvcFRvcE9mZnNldCArIGZyYW1lQ3JvcEJvdHRvbU9mZnNldCksXG4gICAgICBwaXhlbFJhdGlvOiBwaXhlbFJhdGlvXG4gICAgfTtcbiAgfTtcblxuICBfcHJvdG8ucmVhZFNsaWNlVHlwZSA9IGZ1bmN0aW9uIHJlYWRTbGljZVR5cGUoKSB7XG4gICAgLy8gc2tpcCBOQUx1IHR5cGVcbiAgICB0aGlzLnJlYWRVQnl0ZSgpOyAvLyBkaXNjYXJkIGZpcnN0X21iX2luX3NsaWNlXG5cbiAgICB0aGlzLnJlYWRVRUcoKTsgLy8gcmV0dXJuIHNsaWNlX3R5cGVcblxuICAgIHJldHVybiB0aGlzLnJlYWRVRUcoKTtcbiAgfTtcblxuICByZXR1cm4gRXhwR29sb21iO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBleHBfZ29sb21iID0gKGV4cF9nb2xvbWJfRXhwR29sb21iKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2RlbXV4L3NhbXBsZS1hZXMuanNcbi8qKlxuICogU0FNUExFLUFFUyBkZWNyeXB0ZXJcbiovXG5cblxudmFyIHNhbXBsZV9hZXNfU2FtcGxlQWVzRGVjcnlwdGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gU2FtcGxlQWVzRGVjcnlwdGVyKG9ic2VydmVyLCBjb25maWcsIGRlY3J5cHRkYXRhLCBkaXNjYXJkRVBCKSB7XG4gICAgdGhpcy5kZWNyeXB0ZGF0YSA9IGRlY3J5cHRkYXRhO1xuICAgIHRoaXMuZGlzY2FyZEVQQiA9IGRpc2NhcmRFUEI7XG4gICAgdGhpcy5kZWNyeXB0ZXIgPSBuZXcgY3J5cHRfZGVjcnlwdGVyW1wiZGVmYXVsdFwiXShvYnNlcnZlciwgY29uZmlnLCB7XG4gICAgICByZW1vdmVQS0NTN1BhZGRpbmc6IGZhbHNlXG4gICAgfSk7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gU2FtcGxlQWVzRGVjcnlwdGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVjcnlwdEJ1ZmZlciA9IGZ1bmN0aW9uIGRlY3J5cHRCdWZmZXIoZW5jcnlwdGVkRGF0YSwgY2FsbGJhY2spIHtcbiAgICB0aGlzLmRlY3J5cHRlci5kZWNyeXB0KGVuY3J5cHRlZERhdGEsIHRoaXMuZGVjcnlwdGRhdGEua2V5LmJ1ZmZlciwgdGhpcy5kZWNyeXB0ZGF0YS5pdi5idWZmZXIsIGNhbGxiYWNrKTtcbiAgfSAvLyBBQUMgLSBlbmNyeXB0IGFsbCBmdWxsIDE2IGJ5dGVzIGJsb2NrcyBzdGFydGluZyBmcm9tIG9mZnNldCAxNlxuICA7XG5cbiAgX3Byb3RvLmRlY3J5cHRBYWNTYW1wbGUgPSBmdW5jdGlvbiBkZWNyeXB0QWFjU2FtcGxlKHNhbXBsZXMsIHNhbXBsZUluZGV4LCBjYWxsYmFjaywgc3luYykge1xuICAgIHZhciBjdXJVbml0ID0gc2FtcGxlc1tzYW1wbGVJbmRleF0udW5pdDtcbiAgICB2YXIgZW5jcnlwdGVkRGF0YSA9IGN1clVuaXQuc3ViYXJyYXkoMTYsIGN1clVuaXQubGVuZ3RoIC0gY3VyVW5pdC5sZW5ndGggJSAxNik7XG4gICAgdmFyIGVuY3J5cHRlZEJ1ZmZlciA9IGVuY3J5cHRlZERhdGEuYnVmZmVyLnNsaWNlKGVuY3J5cHRlZERhdGEuYnl0ZU9mZnNldCwgZW5jcnlwdGVkRGF0YS5ieXRlT2Zmc2V0ICsgZW5jcnlwdGVkRGF0YS5sZW5ndGgpO1xuICAgIHZhciBsb2NhbHRoaXMgPSB0aGlzO1xuICAgIHRoaXMuZGVjcnlwdEJ1ZmZlcihlbmNyeXB0ZWRCdWZmZXIsIGZ1bmN0aW9uIChkZWNyeXB0ZWREYXRhKSB7XG4gICAgICBkZWNyeXB0ZWREYXRhID0gbmV3IFVpbnQ4QXJyYXkoZGVjcnlwdGVkRGF0YSk7XG4gICAgICBjdXJVbml0LnNldChkZWNyeXB0ZWREYXRhLCAxNik7XG5cbiAgICAgIGlmICghc3luYykge1xuICAgICAgICBsb2NhbHRoaXMuZGVjcnlwdEFhY1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXggKyAxLCBjYWxsYmFjayk7XG4gICAgICB9XG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLmRlY3J5cHRBYWNTYW1wbGVzID0gZnVuY3Rpb24gZGVjcnlwdEFhY1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXgsIGNhbGxiYWNrKSB7XG4gICAgZm9yICg7OyBzYW1wbGVJbmRleCsrKSB7XG4gICAgICBpZiAoc2FtcGxlSW5kZXggPj0gc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2FtcGxlc1tzYW1wbGVJbmRleF0udW5pdC5sZW5ndGggPCAzMikge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgdmFyIHN5bmMgPSB0aGlzLmRlY3J5cHRlci5pc1N5bmMoKTtcbiAgICAgIHRoaXMuZGVjcnlwdEFhY1NhbXBsZShzYW1wbGVzLCBzYW1wbGVJbmRleCwgY2FsbGJhY2ssIHN5bmMpO1xuXG4gICAgICBpZiAoIXN5bmMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgfSAvLyBBVkMgLSBlbmNyeXB0IG9uZSAxNiBieXRlcyBibG9jayBvdXQgb2YgdGVuLCBzdGFydGluZyBmcm9tIG9mZnNldCAzMlxuICA7XG5cbiAgX3Byb3RvLmdldEF2Y0VuY3J5cHRlZERhdGEgPSBmdW5jdGlvbiBnZXRBdmNFbmNyeXB0ZWREYXRhKGRlY29kZWREYXRhKSB7XG4gICAgdmFyIGVuY3J5cHRlZERhdGFMZW4gPSBNYXRoLmZsb29yKChkZWNvZGVkRGF0YS5sZW5ndGggLSA0OCkgLyAxNjApICogMTYgKyAxNjtcbiAgICB2YXIgZW5jcnlwdGVkRGF0YSA9IG5ldyBJbnQ4QXJyYXkoZW5jcnlwdGVkRGF0YUxlbik7XG4gICAgdmFyIG91dHB1dFBvcyA9IDA7XG5cbiAgICBmb3IgKHZhciBpbnB1dFBvcyA9IDMyOyBpbnB1dFBvcyA8PSBkZWNvZGVkRGF0YS5sZW5ndGggLSAxNjsgaW5wdXRQb3MgKz0gMTYwLCBvdXRwdXRQb3MgKz0gMTYpIHtcbiAgICAgIGVuY3J5cHRlZERhdGEuc2V0KGRlY29kZWREYXRhLnN1YmFycmF5KGlucHV0UG9zLCBpbnB1dFBvcyArIDE2KSwgb3V0cHV0UG9zKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZW5jcnlwdGVkRGF0YTtcbiAgfTtcblxuICBfcHJvdG8uZ2V0QXZjRGVjcnlwdGVkVW5pdCA9IGZ1bmN0aW9uIGdldEF2Y0RlY3J5cHRlZFVuaXQoZGVjb2RlZERhdGEsIGRlY3J5cHRlZERhdGEpIHtcbiAgICBkZWNyeXB0ZWREYXRhID0gbmV3IFVpbnQ4QXJyYXkoZGVjcnlwdGVkRGF0YSk7XG4gICAgdmFyIGlucHV0UG9zID0gMDtcblxuICAgIGZvciAodmFyIG91dHB1dFBvcyA9IDMyOyBvdXRwdXRQb3MgPD0gZGVjb2RlZERhdGEubGVuZ3RoIC0gMTY7IG91dHB1dFBvcyArPSAxNjAsIGlucHV0UG9zICs9IDE2KSB7XG4gICAgICBkZWNvZGVkRGF0YS5zZXQoZGVjcnlwdGVkRGF0YS5zdWJhcnJheShpbnB1dFBvcywgaW5wdXRQb3MgKyAxNiksIG91dHB1dFBvcyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGRlY29kZWREYXRhO1xuICB9O1xuXG4gIF9wcm90by5kZWNyeXB0QXZjU2FtcGxlID0gZnVuY3Rpb24gZGVjcnlwdEF2Y1NhbXBsZShzYW1wbGVzLCBzYW1wbGVJbmRleCwgdW5pdEluZGV4LCBjYWxsYmFjaywgY3VyVW5pdCwgc3luYykge1xuICAgIHZhciBkZWNvZGVkRGF0YSA9IHRoaXMuZGlzY2FyZEVQQihjdXJVbml0LmRhdGEpO1xuICAgIHZhciBlbmNyeXB0ZWREYXRhID0gdGhpcy5nZXRBdmNFbmNyeXB0ZWREYXRhKGRlY29kZWREYXRhKTtcbiAgICB2YXIgbG9jYWx0aGlzID0gdGhpcztcbiAgICB0aGlzLmRlY3J5cHRCdWZmZXIoZW5jcnlwdGVkRGF0YS5idWZmZXIsIGZ1bmN0aW9uIChkZWNyeXB0ZWREYXRhKSB7XG4gICAgICBjdXJVbml0LmRhdGEgPSBsb2NhbHRoaXMuZ2V0QXZjRGVjcnlwdGVkVW5pdChkZWNvZGVkRGF0YSwgZGVjcnlwdGVkRGF0YSk7XG5cbiAgICAgIGlmICghc3luYykge1xuICAgICAgICBsb2NhbHRoaXMuZGVjcnlwdEF2Y1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXgsIHVuaXRJbmRleCArIDEsIGNhbGxiYWNrKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8uZGVjcnlwdEF2Y1NhbXBsZXMgPSBmdW5jdGlvbiBkZWNyeXB0QXZjU2FtcGxlcyhzYW1wbGVzLCBzYW1wbGVJbmRleCwgdW5pdEluZGV4LCBjYWxsYmFjaykge1xuICAgIGZvciAoOzsgc2FtcGxlSW5kZXgrKywgdW5pdEluZGV4ID0gMCkge1xuICAgICAgaWYgKHNhbXBsZUluZGV4ID49IHNhbXBsZXMubGVuZ3RoKSB7XG4gICAgICAgIGNhbGxiYWNrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGN1clVuaXRzID0gc2FtcGxlc1tzYW1wbGVJbmRleF0udW5pdHM7XG5cbiAgICAgIGZvciAoOzsgdW5pdEluZGV4KyspIHtcbiAgICAgICAgaWYgKHVuaXRJbmRleCA+PSBjdXJVbml0cy5sZW5ndGgpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBjdXJVbml0ID0gY3VyVW5pdHNbdW5pdEluZGV4XTtcblxuICAgICAgICBpZiAoY3VyVW5pdC5kYXRhLmxlbmd0aCA8PSA0OCB8fCBjdXJVbml0LnR5cGUgIT09IDEgJiYgY3VyVW5pdC50eXBlICE9PSA1KSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgc3luYyA9IHRoaXMuZGVjcnlwdGVyLmlzU3luYygpO1xuICAgICAgICB0aGlzLmRlY3J5cHRBdmNTYW1wbGUoc2FtcGxlcywgc2FtcGxlSW5kZXgsIHVuaXRJbmRleCwgY2FsbGJhY2ssIGN1clVuaXQsIHN5bmMpO1xuXG4gICAgICAgIGlmICghc3luYykge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICByZXR1cm4gU2FtcGxlQWVzRGVjcnlwdGVyO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBzYW1wbGVfYWVzID0gKHNhbXBsZV9hZXNfU2FtcGxlQWVzRGVjcnlwdGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2RlbXV4L3RzZGVtdXhlci5qc1xuLyoqXG4gKiBoaWdobHkgb3B0aW1pemVkIFRTIGRlbXV4ZXI6XG4gKiBwYXJzZSBQQVQsIFBNVFxuICogZXh0cmFjdCBQRVMgcGFja2V0IGZyb20gYXVkaW8gYW5kIHZpZGVvIFBJRHNcbiAqIGV4dHJhY3QgQVZDL0gyNjQgTkFMIHVuaXRzIGFuZCBBQUMvQURUUyBzYW1wbGVzIGZyb20gUEVTIHBhY2tldFxuICogdHJpZ2dlciB0aGUgcmVtdXhlciB1cG9uIHBhcnNpbmcgY29tcGxldGlvblxuICogaXQgYWxzbyB0cmllcyB0byB3b3JrYXJvdW5kIGFzIGJlc3QgYXMgaXQgY2FuIGF1ZGlvIGNvZGVjIHN3aXRjaCAoSEUtQUFDIHRvIEFBQyBhbmQgdmljZSB2ZXJzYSksIHdpdGhvdXQgaGF2aW5nIHRvIHJlc3RhcnQgdGhlIE1lZGlhU291cmNlLlxuICogaXQgYWxzbyBjb250cm9scyB0aGUgcmVtdXhpbmcgcHJvY2VzcyA6XG4gKiB1cG9uIGRpc2NvbnRpbnVpdHkgb3IgbGV2ZWwgc3dpdGNoIGRldGVjdGlvbiwgaXQgd2lsbCBhbHNvIG5vdGlmaWVzIHRoZSByZW11eGVyIHNvIHRoYXQgaXQgY2FuIHJlc2V0IGl0cyBzdGF0ZS5cbiovXG5cblxuXG5cbiAvLyBpbXBvcnQgSGV4IGZyb20gJy4uL3V0aWxzL2hleCc7XG5cblxuXG4gLy8gV2UgYXJlIHVzaW5nIGZpeGVkIHRyYWNrIElEcyBmb3IgZHJpdmluZyB0aGUgTVA0IHJlbXV4ZXJcbi8vIGluc3RlYWQgb2YgZm9sbG93aW5nIHRoZSBUUyBQSURzLlxuLy8gVGhlcmUgaXMgbm8gcmVhc29uIG5vdCB0byBkbyB0aGlzIGFuZCBzb21lIGJyb3dzZXJzL1NvdXJjZUJ1ZmZlci1kZW11eGVyc1xuLy8gbWF5IG5vdCBsaWtlIGlmIHRoZXJlIGFyZSBUcmFja0lEIFwic3dpdGNoZXNcIlxuLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy8xMzMxXG4vLyBIZXJlIHdlIGFyZSBtYXBwaW5nIG91ciBpbnRlcm5hbCB0cmFjayB0eXBlcyB0byBjb25zdGFudCBNUDQgdHJhY2sgSURzXG4vLyBXaXRoIE1TRSBjdXJyZW50bHkgb25lIGNhbiBvbmx5IGhhdmUgb25lIHRyYWNrIG9mIGVhY2gsIGFuZCB3ZSBhcmUgbXV4aW5nXG4vLyB3aGF0ZXZlciB2aWRlby9hdWRpbyByZW5kaXRpb24gaW4gdGhlbS5cblxudmFyIFJlbXV4ZXJUcmFja0lkQ29uZmlnID0ge1xuICB2aWRlbzogMSxcbiAgYXVkaW86IDIsXG4gIGlkMzogMyxcbiAgdGV4dDogNFxufTtcblxudmFyIHRzZGVtdXhlcl9UU0RlbXV4ZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBUU0RlbXV4ZXIob2JzZXJ2ZXIsIHJlbXV4ZXIsIGNvbmZpZywgdHlwZVN1cHBvcnRlZCkge1xuICAgIHRoaXMub2JzZXJ2ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnR5cGVTdXBwb3J0ZWQgPSB0eXBlU3VwcG9ydGVkO1xuICAgIHRoaXMucmVtdXhlciA9IHJlbXV4ZXI7XG4gICAgdGhpcy5zYW1wbGVBZXMgPSBudWxsO1xuICAgIHRoaXMucG10VW5rbm93blR5cGVzID0ge307XG4gIH1cblxuICB2YXIgX3Byb3RvID0gVFNEZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uc2V0RGVjcnlwdERhdGEgPSBmdW5jdGlvbiBzZXREZWNyeXB0RGF0YShkZWNyeXB0ZGF0YSkge1xuICAgIGlmIChkZWNyeXB0ZGF0YSAhPSBudWxsICYmIGRlY3J5cHRkYXRhLmtleSAhPSBudWxsICYmIGRlY3J5cHRkYXRhLm1ldGhvZCA9PT0gJ1NBTVBMRS1BRVMnKSB7XG4gICAgICB0aGlzLnNhbXBsZUFlcyA9IG5ldyBzYW1wbGVfYWVzKHRoaXMub2JzZXJ2ZXIsIHRoaXMuY29uZmlnLCBkZWNyeXB0ZGF0YSwgdGhpcy5kaXNjYXJkRVBCKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5zYW1wbGVBZXMgPSBudWxsO1xuICAgIH1cbiAgfTtcblxuICBUU0RlbXV4ZXIucHJvYmUgPSBmdW5jdGlvbiBwcm9iZShkYXRhKSB7XG4gICAgdmFyIHN5bmNPZmZzZXQgPSBUU0RlbXV4ZXIuX3N5bmNPZmZzZXQoZGF0YSk7XG5cbiAgICBpZiAoc3luY09mZnNldCA8IDApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKHN5bmNPZmZzZXQpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJNUEVHMi1UUyBkZXRlY3RlZCBidXQgZmlyc3Qgc3luYyB3b3JkIGZvdW5kIEAgb2Zmc2V0IFwiICsgc3luY09mZnNldCArIFwiLCBqdW5rIGFoZWFkID9cIik7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfTtcblxuICBUU0RlbXV4ZXIuX3N5bmNPZmZzZXQgPSBmdW5jdGlvbiBfc3luY09mZnNldChkYXRhKSB7XG4gICAgLy8gc2NhbiAxMDAwIGZpcnN0IGJ5dGVzXG4gICAgdmFyIHNjYW53aW5kb3cgPSBNYXRoLm1pbigxMDAwLCBkYXRhLmxlbmd0aCAtIDMgKiAxODgpO1xuICAgIHZhciBpID0gMDtcblxuICAgIHdoaWxlIChpIDwgc2NhbndpbmRvdykge1xuICAgICAgLy8gYSBUUyBmcmFnbWVudCBzaG91bGQgY29udGFpbiBhdCBsZWFzdCAzIFRTIHBhY2tldHMsIGEgUEFULCBhIFBNVCwgYW5kIG9uZSBQSUQsIGVhY2ggc3RhcnRpbmcgd2l0aCAweDQ3XG4gICAgICBpZiAoZGF0YVtpXSA9PT0gMHg0NyAmJiBkYXRhW2kgKyAxODhdID09PSAweDQ3ICYmIGRhdGFbaSArIDIgKiAxODhdID09PSAweDQ3KSB7XG4gICAgICAgIHJldHVybiBpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaSsrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiAtMTtcbiAgfVxuICAvKipcbiAgICogQ3JlYXRlcyBhIHRyYWNrIG1vZGVsIGludGVybmFsIHRvIGRlbXV4ZXIgdXNlZCB0byBkcml2ZSByZW11eGluZyBpbnB1dFxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAnYXVkaW8nIHwgJ3ZpZGVvJyB8ICdpZDMnIHwgJ3RleHQnXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBkdXJhdGlvblxuICAgKiBAcmV0dXJuIHtvYmplY3R9IFRTRGVtdXhlcidzIGludGVybmFsIHRyYWNrIG1vZGVsXG4gICAqL1xuICA7XG5cbiAgVFNEZW11eGVyLmNyZWF0ZVRyYWNrID0gZnVuY3Rpb24gY3JlYXRlVHJhY2sodHlwZSwgZHVyYXRpb24pIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29udGFpbmVyOiB0eXBlID09PSAndmlkZW8nIHx8IHR5cGUgPT09ICdhdWRpbycgPyAndmlkZW8vbXAydCcgOiB1bmRlZmluZWQsXG4gICAgICB0eXBlOiB0eXBlLFxuICAgICAgaWQ6IFJlbXV4ZXJUcmFja0lkQ29uZmlnW3R5cGVdLFxuICAgICAgcGlkOiAtMSxcbiAgICAgIGlucHV0VGltZVNjYWxlOiA5MDAwMCxcbiAgICAgIHNlcXVlbmNlTnVtYmVyOiAwLFxuICAgICAgc2FtcGxlczogW10sXG4gICAgICBkcm9wcGVkOiB0eXBlID09PSAndmlkZW8nID8gMCA6IHVuZGVmaW5lZCxcbiAgICAgIGlzQUFDOiB0eXBlID09PSAnYXVkaW8nID8gdHJ1ZSA6IHVuZGVmaW5lZCxcbiAgICAgIGR1cmF0aW9uOiB0eXBlID09PSAnYXVkaW8nID8gZHVyYXRpb24gOiB1bmRlZmluZWRcbiAgICB9O1xuICB9XG4gIC8qKlxuICAgKiBJbml0aWFsaXplcyBhIG5ldyBpbml0IHNlZ21lbnQgb24gdGhlIGRlbXV4ZXIvcmVtdXhlciBpbnRlcmZhY2UuIE5lZWRlZCBmb3IgZGlzY29udGludWl0aWVzL3RyYWNrLXN3aXRjaGVzIChvciBhdCBzdHJlYW0gc3RhcnQpXG4gICAqIFJlc2V0cyBhbGwgaW50ZXJuYWwgdHJhY2sgaW5zdGFuY2VzIG9mIHRoZSBkZW11eGVyLlxuICAgKlxuICAgKiBAb3ZlcnJpZGUgSW1wbGVtZW50cyBnZW5lcmljIGRlbXV4aW5nL3JlbXV4aW5nIGludGVyZmFjZSAoc2VlIERlbXV4ZXJJbmxpbmUpXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBpbml0U2VnbWVudFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXVkaW9Db2RlY1xuICAgKiBAcGFyYW0ge3N0cmluZ30gdmlkZW9Db2RlY1xuICAgKiBAcGFyYW0ge251bWJlcn0gZHVyYXRpb24gKGluIFRTIHRpbWVzY2FsZSA9IDkwa0h6KVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5yZXNldEluaXRTZWdtZW50ID0gZnVuY3Rpb24gcmVzZXRJbml0U2VnbWVudChpbml0U2VnbWVudCwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgZHVyYXRpb24pIHtcbiAgICB0aGlzLnBtdFBhcnNlZCA9IGZhbHNlO1xuICAgIHRoaXMuX3BtdElkID0gLTE7XG4gICAgdGhpcy5wbXRVbmtub3duVHlwZXMgPSB7fTtcbiAgICB0aGlzLl9hdmNUcmFjayA9IFRTRGVtdXhlci5jcmVhdGVUcmFjaygndmlkZW8nLCBkdXJhdGlvbik7XG4gICAgdGhpcy5fYXVkaW9UcmFjayA9IFRTRGVtdXhlci5jcmVhdGVUcmFjaygnYXVkaW8nLCBkdXJhdGlvbik7XG4gICAgdGhpcy5faWQzVHJhY2sgPSBUU0RlbXV4ZXIuY3JlYXRlVHJhY2soJ2lkMycsIGR1cmF0aW9uKTtcbiAgICB0aGlzLl90eHRUcmFjayA9IFRTRGVtdXhlci5jcmVhdGVUcmFjaygndGV4dCcsIGR1cmF0aW9uKTsgLy8gZmx1c2ggYW55IHBhcnRpYWwgY29udGVudFxuXG4gICAgdGhpcy5hYWNPdmVyRmxvdyA9IG51bGw7XG4gICAgdGhpcy5hYWNMYXN0UFRTID0gbnVsbDtcbiAgICB0aGlzLmF2Y1NhbXBsZSA9IG51bGw7XG4gICAgdGhpcy5hdWRpb0NvZGVjID0gYXVkaW9Db2RlYztcbiAgICB0aGlzLnZpZGVvQ29kZWMgPSB2aWRlb0NvZGVjO1xuICAgIHRoaXMuX2R1cmF0aW9uID0gZHVyYXRpb247XG4gIH1cbiAgLyoqXG4gICAqXG4gICAqIEBvdmVycmlkZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5yZXNldFRpbWVTdGFtcCA9IGZ1bmN0aW9uIHJlc2V0VGltZVN0YW1wKCkge30gLy8gZmVlZCBpbmNvbWluZyBkYXRhIHRvIHRoZSBmcm9udCBvZiB0aGUgcGFyc2luZyBwaXBlbGluZVxuICA7XG5cbiAgX3Byb3RvLmFwcGVuZCA9IGZ1bmN0aW9uIGFwcGVuZChkYXRhLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICB2YXIgc3RhcnQsXG4gICAgICAgIGxlbiA9IGRhdGEubGVuZ3RoLFxuICAgICAgICBzdHQsXG4gICAgICAgIHBpZCxcbiAgICAgICAgYXRmLFxuICAgICAgICBvZmZzZXQsXG4gICAgICAgIHBlcyxcbiAgICAgICAgdW5rbm93blBJRHMgPSBmYWxzZTtcbiAgICB0aGlzLnBtdFVua25vd25UeXBlcyA9IHt9O1xuICAgIHRoaXMuY29udGlndW91cyA9IGNvbnRpZ3VvdXM7XG5cbiAgICB2YXIgcG10UGFyc2VkID0gdGhpcy5wbXRQYXJzZWQsXG4gICAgICAgIGF2Y1RyYWNrID0gdGhpcy5fYXZjVHJhY2ssXG4gICAgICAgIGF1ZGlvVHJhY2sgPSB0aGlzLl9hdWRpb1RyYWNrLFxuICAgICAgICBpZDNUcmFjayA9IHRoaXMuX2lkM1RyYWNrLFxuICAgICAgICBhdmNJZCA9IGF2Y1RyYWNrLnBpZCxcbiAgICAgICAgYXVkaW9JZCA9IGF1ZGlvVHJhY2sucGlkLFxuICAgICAgICBpZDNJZCA9IGlkM1RyYWNrLnBpZCxcbiAgICAgICAgcG10SWQgPSB0aGlzLl9wbXRJZCxcbiAgICAgICAgYXZjRGF0YSA9IGF2Y1RyYWNrLnBlc0RhdGEsXG4gICAgICAgIGF1ZGlvRGF0YSA9IGF1ZGlvVHJhY2sucGVzRGF0YSxcbiAgICAgICAgaWQzRGF0YSA9IGlkM1RyYWNrLnBlc0RhdGEsXG4gICAgICAgIHBhcnNlUEFUID0gdGhpcy5fcGFyc2VQQVQsXG4gICAgICAgIHBhcnNlUE1UID0gdGhpcy5fcGFyc2VQTVQuYmluZCh0aGlzKSxcbiAgICAgICAgcGFyc2VQRVMgPSB0aGlzLl9wYXJzZVBFUyxcbiAgICAgICAgcGFyc2VBVkNQRVMgPSB0aGlzLl9wYXJzZUFWQ1BFUy5iaW5kKHRoaXMpLFxuICAgICAgICBwYXJzZUFBQ1BFUyA9IHRoaXMuX3BhcnNlQUFDUEVTLmJpbmQodGhpcyksXG4gICAgICAgIHBhcnNlTVBFR1BFUyA9IHRoaXMuX3BhcnNlTVBFR1BFUy5iaW5kKHRoaXMpLFxuICAgICAgICBwYXJzZUlEM1BFUyA9IHRoaXMuX3BhcnNlSUQzUEVTLmJpbmQodGhpcyk7XG5cbiAgICB2YXIgc3luY09mZnNldCA9IFRTRGVtdXhlci5fc3luY09mZnNldChkYXRhKTsgLy8gZG9uJ3QgcGFyc2UgbGFzdCBUUyBwYWNrZXQgaWYgaW5jb21wbGV0ZVxuXG5cbiAgICBsZW4gLT0gKGxlbiArIHN5bmNPZmZzZXQpICUgMTg4OyAvLyBsb29wIHRocm91Z2ggVFMgcGFja2V0c1xuXG4gICAgZm9yIChzdGFydCA9IHN5bmNPZmZzZXQ7IHN0YXJ0IDwgbGVuOyBzdGFydCArPSAxODgpIHtcbiAgICAgIGlmIChkYXRhW3N0YXJ0XSA9PT0gMHg0Nykge1xuICAgICAgICBzdHQgPSAhIShkYXRhW3N0YXJ0ICsgMV0gJiAweDQwKTsgLy8gcGlkIGlzIGEgMTMtYml0IGZpZWxkIHN0YXJ0aW5nIGF0IHRoZSBsYXN0IGJpdCBvZiBUU1sxXVxuXG4gICAgICAgIHBpZCA9ICgoZGF0YVtzdGFydCArIDFdICYgMHgxZikgPDwgOCkgKyBkYXRhW3N0YXJ0ICsgMl07XG4gICAgICAgIGF0ZiA9IChkYXRhW3N0YXJ0ICsgM10gJiAweDMwKSA+PiA0OyAvLyBpZiBhbiBhZGFwdGlvbiBmaWVsZCBpcyBwcmVzZW50LCBpdHMgbGVuZ3RoIGlzIHNwZWNpZmllZCBieSB0aGUgZmlmdGggYnl0ZSBvZiB0aGUgVFMgcGFja2V0IGhlYWRlci5cblxuICAgICAgICBpZiAoYXRmID4gMSkge1xuICAgICAgICAgIG9mZnNldCA9IHN0YXJ0ICsgNSArIGRhdGFbc3RhcnQgKyA0XTsgLy8gY29udGludWUgaWYgdGhlcmUgaXMgb25seSBhZGFwdGF0aW9uIGZpZWxkXG5cbiAgICAgICAgICBpZiAob2Zmc2V0ID09PSBzdGFydCArIDE4OCkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9mZnNldCA9IHN0YXJ0ICsgNDtcbiAgICAgICAgfVxuXG4gICAgICAgIHN3aXRjaCAocGlkKSB7XG4gICAgICAgICAgY2FzZSBhdmNJZDpcbiAgICAgICAgICAgIGlmIChzdHQpIHtcbiAgICAgICAgICAgICAgaWYgKGF2Y0RhdGEgJiYgKHBlcyA9IHBhcnNlUEVTKGF2Y0RhdGEpKSkge1xuICAgICAgICAgICAgICAgIHBhcnNlQVZDUEVTKHBlcywgZmFsc2UpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgYXZjRGF0YSA9IHtcbiAgICAgICAgICAgICAgICBkYXRhOiBbXSxcbiAgICAgICAgICAgICAgICBzaXplOiAwXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChhdmNEYXRhKSB7XG4gICAgICAgICAgICAgIGF2Y0RhdGEuZGF0YS5wdXNoKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBzdGFydCArIDE4OCkpO1xuICAgICAgICAgICAgICBhdmNEYXRhLnNpemUgKz0gc3RhcnQgKyAxODggLSBvZmZzZXQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSBhdWRpb0lkOlxuICAgICAgICAgICAgaWYgKHN0dCkge1xuICAgICAgICAgICAgICBpZiAoYXVkaW9EYXRhICYmIChwZXMgPSBwYXJzZVBFUyhhdWRpb0RhdGEpKSkge1xuICAgICAgICAgICAgICAgIGlmIChhdWRpb1RyYWNrLmlzQUFDKSB7XG4gICAgICAgICAgICAgICAgICBwYXJzZUFBQ1BFUyhwZXMpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICBwYXJzZU1QRUdQRVMocGVzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBhdWRpb0RhdGEgPSB7XG4gICAgICAgICAgICAgICAgZGF0YTogW10sXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoYXVkaW9EYXRhKSB7XG4gICAgICAgICAgICAgIGF1ZGlvRGF0YS5kYXRhLnB1c2goZGF0YS5zdWJhcnJheShvZmZzZXQsIHN0YXJ0ICsgMTg4KSk7XG4gICAgICAgICAgICAgIGF1ZGlvRGF0YS5zaXplICs9IHN0YXJ0ICsgMTg4IC0gb2Zmc2V0O1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgaWQzSWQ6XG4gICAgICAgICAgICBpZiAoc3R0KSB7XG4gICAgICAgICAgICAgIGlmIChpZDNEYXRhICYmIChwZXMgPSBwYXJzZVBFUyhpZDNEYXRhKSkpIHtcbiAgICAgICAgICAgICAgICBwYXJzZUlEM1BFUyhwZXMpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgaWQzRGF0YSA9IHtcbiAgICAgICAgICAgICAgICBkYXRhOiBbXSxcbiAgICAgICAgICAgICAgICBzaXplOiAwXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChpZDNEYXRhKSB7XG4gICAgICAgICAgICAgIGlkM0RhdGEuZGF0YS5wdXNoKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBzdGFydCArIDE4OCkpO1xuICAgICAgICAgICAgICBpZDNEYXRhLnNpemUgKz0gc3RhcnQgKyAxODggLSBvZmZzZXQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAwOlxuICAgICAgICAgICAgaWYgKHN0dCkge1xuICAgICAgICAgICAgICBvZmZzZXQgKz0gZGF0YVtvZmZzZXRdICsgMTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcG10SWQgPSB0aGlzLl9wbXRJZCA9IHBhcnNlUEFUKGRhdGEsIG9mZnNldCk7XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgcG10SWQ6XG4gICAgICAgICAgICBpZiAoc3R0KSB7XG4gICAgICAgICAgICAgIG9mZnNldCArPSBkYXRhW29mZnNldF0gKyAxO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB2YXIgcGFyc2VkUElEcyA9IHBhcnNlUE1UKGRhdGEsIG9mZnNldCwgdGhpcy50eXBlU3VwcG9ydGVkLm1wZWcgPT09IHRydWUgfHwgdGhpcy50eXBlU3VwcG9ydGVkLm1wMyA9PT0gdHJ1ZSwgdGhpcy5zYW1wbGVBZXMgIT0gbnVsbCk7IC8vIG9ubHkgdXBkYXRlIHRyYWNrIGlkIGlmIHRyYWNrIFBJRCBmb3VuZCB3aGlsZSBwYXJzaW5nIFBNVFxuICAgICAgICAgICAgLy8gdGhpcyBpcyB0byBhdm9pZCByZXNldHRpbmcgdGhlIFBJRCB0byAtMSBpbiBjYXNlXG4gICAgICAgICAgICAvLyB0cmFjayBQSUQgdHJhbnNpZW50bHkgZGlzYXBwZWFycyBmcm9tIHRoZSBzdHJlYW1cbiAgICAgICAgICAgIC8vIHRoaXMgY291bGQgaGFwcGVuIGluIGNhc2Ugb2YgdHJhbnNpZW50IG1pc3NpbmcgYXVkaW8gc2FtcGxlcyBmb3IgZXhhbXBsZVxuICAgICAgICAgICAgLy8gTk9URSB0aGlzIGlzIG9ubHkgdGhlIFBJRCBvZiB0aGUgdHJhY2sgYXMgZm91bmQgaW4gVFMsXG4gICAgICAgICAgICAvLyBidXQgd2UgYXJlIG5vdCB1c2luZyB0aGlzIGZvciBNUDQgdHJhY2sgSURzLlxuXG4gICAgICAgICAgICBhdmNJZCA9IHBhcnNlZFBJRHMuYXZjO1xuXG4gICAgICAgICAgICBpZiAoYXZjSWQgPiAwKSB7XG4gICAgICAgICAgICAgIGF2Y1RyYWNrLnBpZCA9IGF2Y0lkO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBhdWRpb0lkID0gcGFyc2VkUElEcy5hdWRpbztcblxuICAgICAgICAgICAgaWYgKGF1ZGlvSWQgPiAwKSB7XG4gICAgICAgICAgICAgIGF1ZGlvVHJhY2sucGlkID0gYXVkaW9JZDtcbiAgICAgICAgICAgICAgYXVkaW9UcmFjay5pc0FBQyA9IHBhcnNlZFBJRHMuaXNBQUM7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlkM0lkID0gcGFyc2VkUElEcy5pZDM7XG5cbiAgICAgICAgICAgIGlmIChpZDNJZCA+IDApIHtcbiAgICAgICAgICAgICAgaWQzVHJhY2sucGlkID0gaWQzSWQ7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICh1bmtub3duUElEcyAmJiAhcG10UGFyc2VkKSB7XG4gICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3JlcGFyc2UgZnJvbSBiZWdpbm5pbmcnKTtcbiAgICAgICAgICAgICAgdW5rbm93blBJRHMgPSBmYWxzZTsgLy8gd2Ugc2V0IGl0IHRvIC0xODgsIHRoZSArPSAxODggaW4gdGhlIGZvciBsb29wIHdpbGwgcmVzZXQgc3RhcnQgdG8gMFxuXG4gICAgICAgICAgICAgIHN0YXJ0ID0gc3luY09mZnNldCAtIDE4ODtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcG10UGFyc2VkID0gdGhpcy5wbXRQYXJzZWQgPSB0cnVlO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlIDE3OlxuICAgICAgICAgIGNhc2UgMHgxZmZmOlxuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgdW5rbm93blBJRHMgPSB0cnVlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5NRURJQV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19QQVJTSU5HX0VSUk9SLFxuICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICByZWFzb246ICdUUyBwYWNrZXQgZGlkIG5vdCBzdGFydCB3aXRoIDB4NDcnXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gLy8gdHJ5IHRvIHBhcnNlIGxhc3QgUEVTIHBhY2tldHNcblxuXG4gICAgaWYgKGF2Y0RhdGEgJiYgKHBlcyA9IHBhcnNlUEVTKGF2Y0RhdGEpKSkge1xuICAgICAgcGFyc2VBVkNQRVMocGVzLCB0cnVlKTtcbiAgICAgIGF2Y1RyYWNrLnBlc0RhdGEgPSBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBlaXRoZXIgYXZjRGF0YSBudWxsIG9yIFBFUyB0cnVuY2F0ZWQsIGtlZXAgaXQgZm9yIG5leHQgZnJhZyBwYXJzaW5nXG4gICAgICBhdmNUcmFjay5wZXNEYXRhID0gYXZjRGF0YTtcbiAgICB9XG5cbiAgICBpZiAoYXVkaW9EYXRhICYmIChwZXMgPSBwYXJzZVBFUyhhdWRpb0RhdGEpKSkge1xuICAgICAgaWYgKGF1ZGlvVHJhY2suaXNBQUMpIHtcbiAgICAgICAgcGFyc2VBQUNQRVMocGVzKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHBhcnNlTVBFR1BFUyhwZXMpO1xuICAgICAgfVxuXG4gICAgICBhdWRpb1RyYWNrLnBlc0RhdGEgPSBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoYXVkaW9EYXRhICYmIGF1ZGlvRGF0YS5zaXplKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ2xhc3QgQUFDIFBFUyBwYWNrZXQgdHJ1bmNhdGVkLG1pZ2h0IG92ZXJsYXAgYmV0d2VlbiBmcmFnbWVudHMnKTtcbiAgICAgIH0gLy8gZWl0aGVyIGF1ZGlvRGF0YSBudWxsIG9yIFBFUyB0cnVuY2F0ZWQsIGtlZXAgaXQgZm9yIG5leHQgZnJhZyBwYXJzaW5nXG5cblxuICAgICAgYXVkaW9UcmFjay5wZXNEYXRhID0gYXVkaW9EYXRhO1xuICAgIH1cblxuICAgIGlmIChpZDNEYXRhICYmIChwZXMgPSBwYXJzZVBFUyhpZDNEYXRhKSkpIHtcbiAgICAgIHBhcnNlSUQzUEVTKHBlcyk7XG4gICAgICBpZDNUcmFjay5wZXNEYXRhID0gbnVsbDtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gZWl0aGVyIGlkM0RhdGEgbnVsbCBvciBQRVMgdHJ1bmNhdGVkLCBrZWVwIGl0IGZvciBuZXh0IGZyYWcgcGFyc2luZ1xuICAgICAgaWQzVHJhY2sucGVzRGF0YSA9IGlkM0RhdGE7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuc2FtcGxlQWVzID09IG51bGwpIHtcbiAgICAgIHRoaXMucmVtdXhlci5yZW11eChhdWRpb1RyYWNrLCBhdmNUcmFjaywgaWQzVHJhY2ssIHRoaXMuX3R4dFRyYWNrLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmRlY3J5cHRBbmRSZW11eChhdWRpb1RyYWNrLCBhdmNUcmFjaywgaWQzVHJhY2ssIHRoaXMuX3R4dFRyYWNrLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uZGVjcnlwdEFuZFJlbXV4ID0gZnVuY3Rpb24gZGVjcnlwdEFuZFJlbXV4KGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIGlkM1RyYWNrLCB0ZXh0VHJhY2ssIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCkge1xuICAgIGlmIChhdWRpb1RyYWNrLnNhbXBsZXMgJiYgYXVkaW9UcmFjay5pc0FBQykge1xuICAgICAgdmFyIGxvY2FsdGhpcyA9IHRoaXM7XG4gICAgICB0aGlzLnNhbXBsZUFlcy5kZWNyeXB0QWFjU2FtcGxlcyhhdWRpb1RyYWNrLnNhbXBsZXMsIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbG9jYWx0aGlzLmRlY3J5cHRBbmRSZW11eEF2YyhhdWRpb1RyYWNrLCB2aWRlb1RyYWNrLCBpZDNUcmFjaywgdGV4dFRyYWNrLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZGVjcnlwdEFuZFJlbXV4QXZjKGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIGlkM1RyYWNrLCB0ZXh0VHJhY2ssIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5kZWNyeXB0QW5kUmVtdXhBdmMgPSBmdW5jdGlvbiBkZWNyeXB0QW5kUmVtdXhBdmMoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgaWQzVHJhY2ssIHRleHRUcmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0KSB7XG4gICAgaWYgKHZpZGVvVHJhY2suc2FtcGxlcykge1xuICAgICAgdmFyIGxvY2FsdGhpcyA9IHRoaXM7XG4gICAgICB0aGlzLnNhbXBsZUFlcy5kZWNyeXB0QXZjU2FtcGxlcyh2aWRlb1RyYWNrLnNhbXBsZXMsIDAsIDAsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgbG9jYWx0aGlzLnJlbXV4ZXIucmVtdXgoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgaWQzVHJhY2ssIHRleHRUcmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0KTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlbXV4ZXIucmVtdXgoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgaWQzVHJhY2ssIHRleHRUcmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0KTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHRoaXMuX2luaXRQVFMgPSB0aGlzLl9pbml0RFRTID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuX2R1cmF0aW9uID0gMDtcbiAgfTtcblxuICBfcHJvdG8uX3BhcnNlUEFUID0gZnVuY3Rpb24gX3BhcnNlUEFUKGRhdGEsIG9mZnNldCkge1xuICAgIC8vIHNraXAgdGhlIFBTSSBoZWFkZXIgYW5kIHBhcnNlIHRoZSBmaXJzdCBQTVQgZW50cnlcbiAgICByZXR1cm4gKGRhdGFbb2Zmc2V0ICsgMTBdICYgMHgxRikgPDwgOCB8IGRhdGFbb2Zmc2V0ICsgMTFdOyAvLyBsb2dnZXIubG9nKCdQTVQgUElEOicgICsgdGhpcy5fcG10SWQpO1xuICB9O1xuXG4gIF9wcm90by5fdHJhY2tVbmtub3duUG10ID0gZnVuY3Rpb24gX3RyYWNrVW5rbm93blBtdCh0eXBlLCBsb2dMZXZlbCwgbWVzc2FnZSkge1xuICAgIC8vIE9ubHkgbG9nIHVua25vd24gYW5kIHVuc3VwcG9ydGVkIHN0cmVhbSB0eXBlcyBvbmNlIHBlciBhcHBlbmQgb3Igc3RyZWFtIChieSByZXNldHRpbmcgdGhpcy5wbXRVbmtub3duVHlwZXMpXG4gICAgLy8gRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gZWxlbWVudGFyeSBzdHJlYW0gdHlwZXMgc2VlOlxuICAgIC8vIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1Byb2dyYW0tc3BlY2lmaWNfaW5mb3JtYXRpb24jRWxlbWVudGFyeV9zdHJlYW1fdHlwZXNcbiAgICB2YXIgcmVzdWx0ID0gdGhpcy5wbXRVbmtub3duVHlwZXNbdHlwZV0gfHwgMDtcblxuICAgIGlmIChyZXN1bHQgPT09IDApIHtcbiAgICAgIHRoaXMucG10VW5rbm93blR5cGVzW3R5cGVdID0gMDtcbiAgICAgIGxvZ0xldmVsLmNhbGwobG9nZ2VyW1wibG9nZ2VyXCJdLCBtZXNzYWdlKTtcbiAgICB9XG5cbiAgICB0aGlzLnBtdFVua25vd25UeXBlc1t0eXBlXSsrO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG5cbiAgX3Byb3RvLl9wYXJzZVBNVCA9IGZ1bmN0aW9uIF9wYXJzZVBNVChkYXRhLCBvZmZzZXQsIG1wZWdTdXBwb3J0ZWQsIGlzU2FtcGxlQWVzKSB7XG4gICAgdmFyIHNlY3Rpb25MZW5ndGgsXG4gICAgICAgIHRhYmxlRW5kLFxuICAgICAgICBwcm9ncmFtSW5mb0xlbmd0aCxcbiAgICAgICAgcGlkLFxuICAgICAgICByZXN1bHQgPSB7XG4gICAgICBhdWRpbzogLTEsXG4gICAgICBhdmM6IC0xLFxuICAgICAgaWQzOiAtMSxcbiAgICAgIGlzQUFDOiB0cnVlXG4gICAgfTtcbiAgICBzZWN0aW9uTGVuZ3RoID0gKGRhdGFbb2Zmc2V0ICsgMV0gJiAweDBmKSA8PCA4IHwgZGF0YVtvZmZzZXQgKyAyXTtcbiAgICB0YWJsZUVuZCA9IG9mZnNldCArIDMgKyBzZWN0aW9uTGVuZ3RoIC0gNDsgLy8gdG8gZGV0ZXJtaW5lIHdoZXJlIHRoZSB0YWJsZSBpcywgd2UgaGF2ZSB0byBmaWd1cmUgb3V0IGhvd1xuICAgIC8vIGxvbmcgdGhlIHByb2dyYW0gaW5mbyBkZXNjcmlwdG9ycyBhcmVcblxuICAgIHByb2dyYW1JbmZvTGVuZ3RoID0gKGRhdGFbb2Zmc2V0ICsgMTBdICYgMHgwZikgPDwgOCB8IGRhdGFbb2Zmc2V0ICsgMTFdOyAvLyBhZHZhbmNlIHRoZSBvZmZzZXQgdG8gdGhlIGZpcnN0IGVudHJ5IGluIHRoZSBtYXBwaW5nIHRhYmxlXG5cbiAgICBvZmZzZXQgKz0gMTIgKyBwcm9ncmFtSW5mb0xlbmd0aDtcblxuICAgIHdoaWxlIChvZmZzZXQgPCB0YWJsZUVuZCkge1xuICAgICAgcGlkID0gKGRhdGFbb2Zmc2V0ICsgMV0gJiAweDFGKSA8PCA4IHwgZGF0YVtvZmZzZXQgKyAyXTtcblxuICAgICAgc3dpdGNoIChkYXRhW29mZnNldF0pIHtcbiAgICAgICAgY2FzZSAweGNmOlxuICAgICAgICAgIC8vIFNBTVBMRS1BRVMgQUFDXG4gICAgICAgICAgaWYgKCFpc1NhbXBsZUFlcykge1xuICAgICAgICAgICAgdGhpcy5fdHJhY2tVbmtub3duUG10KGRhdGFbb2Zmc2V0XSwgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4sICdBRFRTIEFBQyB3aXRoIEFFUy0xMjgtQ0JDIGZyYW1lIGVuY3J5cHRpb24gZm91bmQgaW4gdW5lbmNyeXB0ZWQgc3RyZWFtJyk7XG5cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cblxuICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICAgIC8vIElTTy9JRUMgMTM4MTgtNyBBRFRTIEFBQyAoTVBFRy0yIGxvd2VyIGJpdC1yYXRlIGF1ZGlvKVxuXG4gICAgICAgIGNhc2UgMHgwZjpcbiAgICAgICAgICAvLyBsb2dnZXIubG9nKCdBQUMgUElEOicgICsgcGlkKTtcbiAgICAgICAgICBpZiAocmVzdWx0LmF1ZGlvID09PSAtMSkge1xuICAgICAgICAgICAgcmVzdWx0LmF1ZGlvID0gcGlkO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICAvLyBQYWNrZXRpemVkIG1ldGFkYXRhIChJRDMpXG5cbiAgICAgICAgY2FzZSAweDE1OlxuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ0lEMyBQSUQ6JyAgKyBwaWQpO1xuICAgICAgICAgIGlmIChyZXN1bHQuaWQzID09PSAtMSkge1xuICAgICAgICAgICAgcmVzdWx0LmlkMyA9IHBpZDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlIDB4ZGI6XG4gICAgICAgICAgLy8gU0FNUExFLUFFUyBBVkNcbiAgICAgICAgICBpZiAoIWlzU2FtcGxlQWVzKSB7XG4gICAgICAgICAgICB0aGlzLl90cmFja1Vua25vd25QbXQoZGF0YVtvZmZzZXRdLCBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybiwgJ0guMjY0IHdpdGggQUVTLTEyOC1DQkMgc2xpY2UgZW5jcnlwdGlvbiBmb3VuZCBpbiB1bmVuY3J5cHRlZCBzdHJlYW0nKTtcblxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuXG4gICAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cbiAgICAgICAgLy8gSVRVLVQgUmVjLiBILjI2NCBhbmQgSVNPL0lFQyAxNDQ5Ni0xMCAobG93ZXIgYml0LXJhdGUgdmlkZW8pXG5cbiAgICAgICAgY2FzZSAweDFiOlxuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ0FWQyBQSUQ6JyAgKyBwaWQpO1xuICAgICAgICAgIGlmIChyZXN1bHQuYXZjID09PSAtMSkge1xuICAgICAgICAgICAgcmVzdWx0LmF2YyA9IHBpZDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgLy8gSVNPL0lFQyAxMTE3Mi0zIChNUEVHLTEgYXVkaW8pXG4gICAgICAgIC8vIG9yIElTTy9JRUMgMTM4MTgtMyAoTVBFRy0yIGhhbHZlZCBzYW1wbGUgcmF0ZSBhdWRpbylcblxuICAgICAgICBjYXNlIDB4MDM6XG4gICAgICAgIGNhc2UgMHgwNDpcbiAgICAgICAgICAvLyBsb2dnZXIubG9nKCdNUEVHIFBJRDonICArIHBpZCk7XG4gICAgICAgICAgaWYgKCFtcGVnU3VwcG9ydGVkKSB7XG4gICAgICAgICAgICB0aGlzLl90cmFja1Vua25vd25QbXQoZGF0YVtvZmZzZXRdLCBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybiwgJ01QRUcgYXVkaW8gZm91bmQsIG5vdCBzdXBwb3J0ZWQgaW4gdGhpcyBicm93c2VyJyk7XG4gICAgICAgICAgfSBlbHNlIGlmIChyZXN1bHQuYXVkaW8gPT09IC0xKSB7XG4gICAgICAgICAgICByZXN1bHQuYXVkaW8gPSBwaWQ7XG4gICAgICAgICAgICByZXN1bHQuaXNBQUMgPSBmYWxzZTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlIDB4MjQ6XG4gICAgICAgICAgdGhpcy5fdHJhY2tVbmtub3duUG10KGRhdGFbb2Zmc2V0XSwgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4sICdVbnN1cHBvcnRlZCBIRVZDIHN0cmVhbSB0eXBlIGZvdW5kJyk7XG5cbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMuX3RyYWNrVW5rbm93blBtdChkYXRhW29mZnNldF0sIGxvZ2dlcltcImxvZ2dlclwiXS5sb2csICdVbmtub3duIHN0cmVhbSB0eXBlOicgKyBkYXRhW29mZnNldF0pO1xuXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9IC8vIG1vdmUgdG8gdGhlIG5leHQgdGFibGUgZW50cnlcbiAgICAgIC8vIHNraXAgcGFzdCB0aGUgZWxlbWVudGFyeSBzdHJlYW0gZGVzY3JpcHRvcnMsIGlmIHByZXNlbnRcblxuXG4gICAgICBvZmZzZXQgKz0gKChkYXRhW29mZnNldCArIDNdICYgMHgwRikgPDwgOCB8IGRhdGFbb2Zmc2V0ICsgNF0pICsgNTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9O1xuXG4gIF9wcm90by5fcGFyc2VQRVMgPSBmdW5jdGlvbiBfcGFyc2VQRVMoc3RyZWFtKSB7XG4gICAgdmFyIGkgPSAwLFxuICAgICAgICBmcmFnLFxuICAgICAgICBwZXNGbGFncyxcbiAgICAgICAgcGVzUHJlZml4LFxuICAgICAgICBwZXNMZW4sXG4gICAgICAgIHBlc0hkckxlbixcbiAgICAgICAgcGVzRGF0YSxcbiAgICAgICAgcGVzUHRzLFxuICAgICAgICBwZXNEdHMsXG4gICAgICAgIHBheWxvYWRTdGFydE9mZnNldCxcbiAgICAgICAgZGF0YSA9IHN0cmVhbS5kYXRhOyAvLyBzYWZldHkgY2hlY2tcblxuICAgIGlmICghc3RyZWFtIHx8IHN0cmVhbS5zaXplID09PSAwKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IC8vIHdlIG1pZ2h0IG5lZWQgdXAgdG8gMTkgYnl0ZXMgdG8gcmVhZCBQRVMgaGVhZGVyXG4gICAgLy8gaWYgZmlyc3QgY2h1bmsgb2YgZGF0YSBpcyBsZXNzIHRoYW4gMTkgYnl0ZXMsIGxldCdzIG1lcmdlIGl0IHdpdGggZm9sbG93aW5nIG9uZXMgdW50aWwgd2UgZ2V0IDE5IGJ5dGVzXG4gICAgLy8gdXN1YWxseSBvbmx5IG9uZSBtZXJnZSBpcyBuZWVkZWQgKGFuZCB0aGlzIGlzIHJhcmUgLi4uKVxuXG5cbiAgICB3aGlsZSAoZGF0YVswXS5sZW5ndGggPCAxOSAmJiBkYXRhLmxlbmd0aCA+IDEpIHtcbiAgICAgIHZhciBuZXdEYXRhID0gbmV3IFVpbnQ4QXJyYXkoZGF0YVswXS5sZW5ndGggKyBkYXRhWzFdLmxlbmd0aCk7XG4gICAgICBuZXdEYXRhLnNldChkYXRhWzBdKTtcbiAgICAgIG5ld0RhdGEuc2V0KGRhdGFbMV0sIGRhdGFbMF0ubGVuZ3RoKTtcbiAgICAgIGRhdGFbMF0gPSBuZXdEYXRhO1xuICAgICAgZGF0YS5zcGxpY2UoMSwgMSk7XG4gICAgfSAvLyByZXRyaWV2ZSBQVFMvRFRTIGZyb20gZmlyc3QgZnJhZ21lbnRcblxuXG4gICAgZnJhZyA9IGRhdGFbMF07XG4gICAgcGVzUHJlZml4ID0gKGZyYWdbMF0gPDwgMTYpICsgKGZyYWdbMV0gPDwgOCkgKyBmcmFnWzJdO1xuXG4gICAgaWYgKHBlc1ByZWZpeCA9PT0gMSkge1xuICAgICAgcGVzTGVuID0gKGZyYWdbNF0gPDwgOCkgKyBmcmFnWzVdOyAvLyBpZiBQRVMgcGFyc2VkIGxlbmd0aCBpcyBub3QgemVybyBhbmQgZ3JlYXRlciB0aGFuIHRvdGFsIHJlY2VpdmVkIGxlbmd0aCwgc3RvcCBwYXJzaW5nLiBQRVMgbWlnaHQgYmUgdHJ1bmNhdGVkXG4gICAgICAvLyBtaW51cyA2IDogUEVTIGhlYWRlciBzaXplXG5cbiAgICAgIGlmIChwZXNMZW4gJiYgcGVzTGVuID4gc3RyZWFtLnNpemUgLSA2KSB7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBwZXNGbGFncyA9IGZyYWdbN107XG5cbiAgICAgIGlmIChwZXNGbGFncyAmIDB4QzApIHtcbiAgICAgICAgLyogUEVTIGhlYWRlciBkZXNjcmliZWQgaGVyZSA6IGh0dHA6Ly9kdmQuc291cmNlZm9yZ2UubmV0L2R2ZGluZm8vcGVzLWhkci5odG1sXG4gICAgICAgICAgICBhcyBQVFMgLyBEVFMgaXMgMzMgYml0IHdlIGNhbm5vdCB1c2UgYml0d2lzZSBvcGVyYXRvciBpbiBKUyxcbiAgICAgICAgICAgIGFzIEJpdHdpc2Ugb3BlcmF0b3JzIHRyZWF0IHRoZWlyIG9wZXJhbmRzIGFzIGEgc2VxdWVuY2Ugb2YgMzIgYml0cyAqL1xuICAgICAgICBwZXNQdHMgPSAoZnJhZ1s5XSAmIDB4MEUpICogNTM2ODcwOTEyICsgLy8gMSA8PCAyOVxuICAgICAgICAoZnJhZ1sxMF0gJiAweEZGKSAqIDQxOTQzMDQgKyAvLyAxIDw8IDIyXG4gICAgICAgIChmcmFnWzExXSAmIDB4RkUpICogMTYzODQgKyAvLyAxIDw8IDE0XG4gICAgICAgIChmcmFnWzEyXSAmIDB4RkYpICogMTI4ICsgLy8gMSA8PCA3XG4gICAgICAgIChmcmFnWzEzXSAmIDB4RkUpIC8gMjtcblxuICAgICAgICBpZiAocGVzRmxhZ3MgJiAweDQwKSB7XG4gICAgICAgICAgcGVzRHRzID0gKGZyYWdbMTRdICYgMHgwRSkgKiA1MzY4NzA5MTIgKyAvLyAxIDw8IDI5XG4gICAgICAgICAgKGZyYWdbMTVdICYgMHhGRikgKiA0MTk0MzA0ICsgLy8gMSA8PCAyMlxuICAgICAgICAgIChmcmFnWzE2XSAmIDB4RkUpICogMTYzODQgKyAvLyAxIDw8IDE0XG4gICAgICAgICAgKGZyYWdbMTddICYgMHhGRikgKiAxMjggKyAvLyAxIDw8IDdcbiAgICAgICAgICAoZnJhZ1sxOF0gJiAweEZFKSAvIDI7XG5cbiAgICAgICAgICBpZiAocGVzUHRzIC0gcGVzRHRzID4gNjAgKiA5MDAwMCkge1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oTWF0aC5yb3VuZCgocGVzUHRzIC0gcGVzRHRzKSAvIDkwMDAwKSArIFwicyBkZWx0YSBiZXR3ZWVuIFBUUyBhbmQgRFRTLCBhbGlnbiB0aGVtXCIpO1xuICAgICAgICAgICAgcGVzUHRzID0gcGVzRHRzO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBwZXNEdHMgPSBwZXNQdHM7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcGVzSGRyTGVuID0gZnJhZ1s4XTsgLy8gOSBieXRlcyA6IDYgYnl0ZXMgZm9yIFBFUyBoZWFkZXIgKyAzIGJ5dGVzIGZvciBQRVMgZXh0ZW5zaW9uXG5cbiAgICAgIHBheWxvYWRTdGFydE9mZnNldCA9IHBlc0hkckxlbiArIDk7XG5cbiAgICAgIGlmIChzdHJlYW0uc2l6ZSA8PSBwYXlsb2FkU3RhcnRPZmZzZXQpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIHN0cmVhbS5zaXplIC09IHBheWxvYWRTdGFydE9mZnNldDsgLy8gcmVhc3NlbWJsZSBQRVMgcGFja2V0XG5cbiAgICAgIHBlc0RhdGEgPSBuZXcgVWludDhBcnJheShzdHJlYW0uc2l6ZSk7XG5cbiAgICAgIGZvciAodmFyIGogPSAwLCBkYXRhTGVuID0gZGF0YS5sZW5ndGg7IGogPCBkYXRhTGVuOyBqKyspIHtcbiAgICAgICAgZnJhZyA9IGRhdGFbal07XG4gICAgICAgIHZhciBsZW4gPSBmcmFnLmJ5dGVMZW5ndGg7XG5cbiAgICAgICAgaWYgKHBheWxvYWRTdGFydE9mZnNldCkge1xuICAgICAgICAgIGlmIChwYXlsb2FkU3RhcnRPZmZzZXQgPiBsZW4pIHtcbiAgICAgICAgICAgIC8vIHRyaW0gZnVsbCBmcmFnIGlmIFBFUyBoZWFkZXIgYmlnZ2VyIHRoYW4gZnJhZ1xuICAgICAgICAgICAgcGF5bG9hZFN0YXJ0T2Zmc2V0IC09IGxlbjtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyB0cmltIHBhcnRpYWwgZnJhZyBpZiBQRVMgaGVhZGVyIHNtYWxsZXIgdGhhbiBmcmFnXG4gICAgICAgICAgICBmcmFnID0gZnJhZy5zdWJhcnJheShwYXlsb2FkU3RhcnRPZmZzZXQpO1xuICAgICAgICAgICAgbGVuIC09IHBheWxvYWRTdGFydE9mZnNldDtcbiAgICAgICAgICAgIHBheWxvYWRTdGFydE9mZnNldCA9IDA7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgcGVzRGF0YS5zZXQoZnJhZywgaSk7XG4gICAgICAgIGkgKz0gbGVuO1xuICAgICAgfVxuXG4gICAgICBpZiAocGVzTGVuKSB7XG4gICAgICAgIC8vIHBheWxvYWQgc2l6ZSA6IHJlbW92ZSBQRVMgaGVhZGVyICsgUEVTIGV4dGVuc2lvblxuICAgICAgICBwZXNMZW4gLT0gcGVzSGRyTGVuICsgMztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZGF0YTogcGVzRGF0YSxcbiAgICAgICAgcHRzOiBwZXNQdHMsXG4gICAgICAgIGR0czogcGVzRHRzLFxuICAgICAgICBsZW46IHBlc0xlblxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5wdXNoQWNjZXNVbml0ID0gZnVuY3Rpb24gcHVzaEFjY2VzVW5pdChhdmNTYW1wbGUsIGF2Y1RyYWNrKSB7XG4gICAgaWYgKGF2Y1NhbXBsZS51bml0cy5sZW5ndGggJiYgYXZjU2FtcGxlLmZyYW1lKSB7XG4gICAgICB2YXIgc2FtcGxlcyA9IGF2Y1RyYWNrLnNhbXBsZXM7XG4gICAgICB2YXIgbmJTYW1wbGVzID0gc2FtcGxlcy5sZW5ndGg7IC8vIGlmIHNhbXBsZSBkb2VzIG5vdCBoYXZlIFBUUy9EVFMsIHBhdGNoIHdpdGggbGFzdCBzYW1wbGUgUFRTL0RUU1xuXG4gICAgICBpZiAoaXNOYU4oYXZjU2FtcGxlLnB0cykpIHtcbiAgICAgICAgaWYgKG5iU2FtcGxlcykge1xuICAgICAgICAgIHZhciBsYXN0U2FtcGxlID0gc2FtcGxlc1tuYlNhbXBsZXMgLSAxXTtcbiAgICAgICAgICBhdmNTYW1wbGUucHRzID0gbGFzdFNhbXBsZS5wdHM7XG4gICAgICAgICAgYXZjU2FtcGxlLmR0cyA9IGxhc3RTYW1wbGUuZHRzO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGRyb3BwaW5nIHNhbXBsZXMsIG5vIHRpbWVzdGFtcCBmb3VuZFxuICAgICAgICAgIGF2Y1RyYWNrLmRyb3BwZWQrKztcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH0gLy8gb25seSBwdXNoIEFWQyBzYW1wbGUgaWYgc3RhcnRpbmcgd2l0aCBhIGtleWZyYW1lIGlzIG5vdCBtYW5kYXRvcnkgT1JcbiAgICAgIC8vICAgIGlmIGtleWZyYW1lIGFscmVhZHkgZm91bmQgaW4gdGhpcyBmcmFnbWVudCBPUlxuICAgICAgLy8gICAgICAga2V5ZnJhbWUgZm91bmQgaW4gbGFzdCBmcmFnbWVudCAodHJhY2suc3BzKSBBTkRcbiAgICAgIC8vICAgICAgICAgIHNhbXBsZXMgYWxyZWFkeSBhcHBlbmRlZCAod2UgYWxyZWFkeSBmb3VuZCBhIGtleWZyYW1lIGluIHRoaXMgZnJhZ21lbnQpIE9SIGZyYWdtZW50IGlzIGNvbnRpZ3VvdXNcblxuXG4gICAgICBpZiAoIXRoaXMuY29uZmlnLmZvcmNlS2V5RnJhbWVPbkRpc2NvbnRpbnVpdHkgfHwgYXZjU2FtcGxlLmtleSA9PT0gdHJ1ZSB8fCBhdmNUcmFjay5zcHMgJiYgKG5iU2FtcGxlcyB8fCB0aGlzLmNvbnRpZ3VvdXMpKSB7XG4gICAgICAgIGF2Y1NhbXBsZS5pZCA9IG5iU2FtcGxlcztcbiAgICAgICAgc2FtcGxlcy5wdXNoKGF2Y1NhbXBsZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBkcm9wcGVkIHNhbXBsZXMsIHRyYWNrIGl0XG4gICAgICAgIGF2Y1RyYWNrLmRyb3BwZWQrKztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoYXZjU2FtcGxlLmRlYnVnLmxlbmd0aCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhhdmNTYW1wbGUucHRzICsgJy8nICsgYXZjU2FtcGxlLmR0cyArICc6JyArIGF2Y1NhbXBsZS5kZWJ1Zyk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5fcGFyc2VBVkNQRVMgPSBmdW5jdGlvbiBfcGFyc2VBVkNQRVMocGVzLCBsYXN0KSB7XG4gICAgdmFyIF90aGlzID0gdGhpcztcblxuICAgIC8vIGxvZ2dlci5sb2coJ3BhcnNlIG5ldyBQRVMnKTtcbiAgICB2YXIgdHJhY2sgPSB0aGlzLl9hdmNUcmFjayxcbiAgICAgICAgdW5pdHMgPSB0aGlzLl9wYXJzZUFWQ05BTHUocGVzLmRhdGEpLFxuICAgICAgICBkZWJ1ZyA9IGZhbHNlLFxuICAgICAgICBleHBHb2xvbWJEZWNvZGVyLFxuICAgICAgICBhdmNTYW1wbGUgPSB0aGlzLmF2Y1NhbXBsZSxcbiAgICAgICAgcHVzaCxcbiAgICAgICAgc3BzZm91bmQgPSBmYWxzZSxcbiAgICAgICAgaSxcbiAgICAgICAgcHVzaEFjY2VzVW5pdCA9IHRoaXMucHVzaEFjY2VzVW5pdC5iaW5kKHRoaXMpLFxuICAgICAgICBjcmVhdGVBVkNTYW1wbGUgPSBmdW5jdGlvbiBjcmVhdGVBVkNTYW1wbGUoa2V5LCBwdHMsIGR0cywgZGVidWcpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGtleToga2V5LFxuICAgICAgICBwdHM6IHB0cyxcbiAgICAgICAgZHRzOiBkdHMsXG4gICAgICAgIHVuaXRzOiBbXSxcbiAgICAgICAgZGVidWc6IGRlYnVnXG4gICAgICB9O1xuICAgIH07IC8vIGZyZWUgcGVzLmRhdGEgdG8gc2F2ZSB1cCBzb21lIG1lbW9yeVxuXG5cbiAgICBwZXMuZGF0YSA9IG51bGw7IC8vIGlmIG5ldyBOQUwgdW5pdHMgZm91bmQgYW5kIGxhc3Qgc2FtcGxlIHN0aWxsIHRoZXJlLCBsZXQncyBwdXNoIC4uLlxuICAgIC8vIHRoaXMgaGVscHMgcGFyc2luZyBzdHJlYW1zIHdpdGggbWlzc2luZyBBVUQgKG9ubHkgZG8gdGhpcyBpZiBBVUQgbmV2ZXIgZm91bmQpXG5cbiAgICBpZiAoYXZjU2FtcGxlICYmIHVuaXRzLmxlbmd0aCAmJiAhdHJhY2suYXVkRm91bmQpIHtcbiAgICAgIHB1c2hBY2Nlc1VuaXQoYXZjU2FtcGxlLCB0cmFjayk7XG4gICAgICBhdmNTYW1wbGUgPSB0aGlzLmF2Y1NhbXBsZSA9IGNyZWF0ZUFWQ1NhbXBsZShmYWxzZSwgcGVzLnB0cywgcGVzLmR0cywgJycpO1xuICAgIH1cblxuICAgIHVuaXRzLmZvckVhY2goZnVuY3Rpb24gKHVuaXQpIHtcbiAgICAgIHN3aXRjaCAodW5pdC50eXBlKSB7XG4gICAgICAgIC8vIE5EUlxuICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgcHVzaCA9IHRydWU7XG5cbiAgICAgICAgICBpZiAoIWF2Y1NhbXBsZSkge1xuICAgICAgICAgICAgYXZjU2FtcGxlID0gX3RoaXMuYXZjU2FtcGxlID0gY3JlYXRlQVZDU2FtcGxlKHRydWUsIHBlcy5wdHMsIHBlcy5kdHMsICcnKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZGVidWcpIHtcbiAgICAgICAgICAgIGF2Y1NhbXBsZS5kZWJ1ZyArPSAnTkRSICc7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYXZjU2FtcGxlLmZyYW1lID0gdHJ1ZTtcbiAgICAgICAgICB2YXIgZGF0YSA9IHVuaXQuZGF0YTsgLy8gb25seSBjaGVjayBzbGljZSB0eXBlIHRvIGRldGVjdCBLRiBpbiBjYXNlIFNQUyBmb3VuZCBpbiBzYW1lIHBhY2tldCAoYW55IGtleWZyYW1lIGlzIHByZWNlZGVkIGJ5IFNQUyAuLi4pXG5cbiAgICAgICAgICBpZiAoc3BzZm91bmQgJiYgZGF0YS5sZW5ndGggPiA0KSB7XG4gICAgICAgICAgICAvLyByZXRyaWV2ZSBzbGljZSB0eXBlIGJ5IHBhcnNpbmcgYmVnaW5uaW5nIG9mIE5BTCB1bml0IChmb2xsb3cgSDI2NCBzcGVjLCBzbGljZV9oZWFkZXIgZGVmaW5pdGlvbikgdG8gZGV0ZWN0IGtleWZyYW1lIGVtYmVkZGVkIGluIE5EUlxuICAgICAgICAgICAgdmFyIHNsaWNlVHlwZSA9IG5ldyBleHBfZ29sb21iKGRhdGEpLnJlYWRTbGljZVR5cGUoKTsgLy8gMiA6IEkgc2xpY2UsIDQgOiBTSSBzbGljZSwgNyA6IEkgc2xpY2UsIDk6IFNJIHNsaWNlXG4gICAgICAgICAgICAvLyBTSSBzbGljZSA6IEEgc2xpY2UgdGhhdCBpcyBjb2RlZCB1c2luZyBpbnRyYSBwcmVkaWN0aW9uIG9ubHkgYW5kIHVzaW5nIHF1YW50aXNhdGlvbiBvZiB0aGUgcHJlZGljdGlvbiBzYW1wbGVzLlxuICAgICAgICAgICAgLy8gQW4gU0kgc2xpY2UgY2FuIGJlIGNvZGVkIHN1Y2ggdGhhdCBpdHMgZGVjb2RlZCBzYW1wbGVzIGNhbiBiZSBjb25zdHJ1Y3RlZCBpZGVudGljYWxseSB0byBhbiBTUCBzbGljZS5cbiAgICAgICAgICAgIC8vIEkgc2xpY2U6IEEgc2xpY2UgdGhhdCBpcyBub3QgYW4gU0kgc2xpY2UgdGhhdCBpcyBkZWNvZGVkIHVzaW5nIGludHJhIHByZWRpY3Rpb24gb25seS5cbiAgICAgICAgICAgIC8vIGlmIChzbGljZVR5cGUgPT09IDIgfHwgc2xpY2VUeXBlID09PSA3KSB7XG5cbiAgICAgICAgICAgIGlmIChzbGljZVR5cGUgPT09IDIgfHwgc2xpY2VUeXBlID09PSA0IHx8IHNsaWNlVHlwZSA9PT0gNyB8fCBzbGljZVR5cGUgPT09IDkpIHtcbiAgICAgICAgICAgICAgYXZjU2FtcGxlLmtleSA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIC8vIElEUlxuXG4gICAgICAgIGNhc2UgNTpcbiAgICAgICAgICBwdXNoID0gdHJ1ZTsgLy8gaGFuZGxlIFBFUyBub3Qgc3RhcnRpbmcgd2l0aCBBVURcblxuICAgICAgICAgIGlmICghYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBhdmNTYW1wbGUgPSBfdGhpcy5hdmNTYW1wbGUgPSBjcmVhdGVBVkNTYW1wbGUodHJ1ZSwgcGVzLnB0cywgcGVzLmR0cywgJycpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChkZWJ1Zykge1xuICAgICAgICAgICAgYXZjU2FtcGxlLmRlYnVnICs9ICdJRFIgJztcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBhdmNTYW1wbGUua2V5ID0gdHJ1ZTtcbiAgICAgICAgICBhdmNTYW1wbGUuZnJhbWUgPSB0cnVlO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICAvLyBTRUlcblxuICAgICAgICBjYXNlIDY6XG4gICAgICAgICAgcHVzaCA9IHRydWU7XG5cbiAgICAgICAgICBpZiAoZGVidWcgJiYgYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBhdmNTYW1wbGUuZGVidWcgKz0gJ1NFSSAnO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGV4cEdvbG9tYkRlY29kZXIgPSBuZXcgZXhwX2dvbG9tYihfdGhpcy5kaXNjYXJkRVBCKHVuaXQuZGF0YSkpOyAvLyBza2lwIGZyYW1lVHlwZVxuXG4gICAgICAgICAgZXhwR29sb21iRGVjb2Rlci5yZWFkVUJ5dGUoKTtcbiAgICAgICAgICB2YXIgcGF5bG9hZFR5cGUgPSAwO1xuICAgICAgICAgIHZhciBwYXlsb2FkU2l6ZSA9IDA7XG4gICAgICAgICAgdmFyIGVuZE9mQ2FwdGlvbnMgPSBmYWxzZTtcbiAgICAgICAgICB2YXIgYiA9IDA7XG5cbiAgICAgICAgICB3aGlsZSAoIWVuZE9mQ2FwdGlvbnMgJiYgZXhwR29sb21iRGVjb2Rlci5ieXRlc0F2YWlsYWJsZSA+IDEpIHtcbiAgICAgICAgICAgIHBheWxvYWRUeXBlID0gMDtcblxuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICBiID0gZXhwR29sb21iRGVjb2Rlci5yZWFkVUJ5dGUoKTtcbiAgICAgICAgICAgICAgcGF5bG9hZFR5cGUgKz0gYjtcbiAgICAgICAgICAgIH0gd2hpbGUgKGIgPT09IDB4RkYpOyAvLyBQYXJzZSBwYXlsb2FkIHNpemUuXG5cblxuICAgICAgICAgICAgcGF5bG9hZFNpemUgPSAwO1xuXG4gICAgICAgICAgICBkbyB7XG4gICAgICAgICAgICAgIGIgPSBleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpO1xuICAgICAgICAgICAgICBwYXlsb2FkU2l6ZSArPSBiO1xuICAgICAgICAgICAgfSB3aGlsZSAoYiA9PT0gMHhGRik7IC8vIFRPRE86IHRoZXJlIGNhbiBiZSBtb3JlIHRoYW4gb25lIHBheWxvYWQgaW4gYW4gU0VJIHBhY2tldC4uLlxuICAgICAgICAgICAgLy8gVE9ETzogbmVlZCB0byByZWFkIHR5cGUgYW5kIHNpemUgaW4gYSB3aGlsZSBsb29wIHRvIGdldCB0aGVtIGFsbFxuXG5cbiAgICAgICAgICAgIGlmIChwYXlsb2FkVHlwZSA9PT0gNCAmJiBleHBHb2xvbWJEZWNvZGVyLmJ5dGVzQXZhaWxhYmxlICE9PSAwKSB7XG4gICAgICAgICAgICAgIGVuZE9mQ2FwdGlvbnMgPSB0cnVlO1xuICAgICAgICAgICAgICB2YXIgY291bnRyeUNvZGUgPSBleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpO1xuXG4gICAgICAgICAgICAgIGlmIChjb3VudHJ5Q29kZSA9PT0gMTgxKSB7XG4gICAgICAgICAgICAgICAgdmFyIHByb3ZpZGVyQ29kZSA9IGV4cEdvbG9tYkRlY29kZXIucmVhZFVTaG9ydCgpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHByb3ZpZGVyQ29kZSA9PT0gNDkpIHtcbiAgICAgICAgICAgICAgICAgIHZhciB1c2VyU3RydWN0dXJlID0gZXhwR29sb21iRGVjb2Rlci5yZWFkVUludCgpO1xuXG4gICAgICAgICAgICAgICAgICBpZiAodXNlclN0cnVjdHVyZSA9PT0gMHg0NzQxMzkzNCkge1xuICAgICAgICAgICAgICAgICAgICB2YXIgdXNlckRhdGFUeXBlID0gZXhwR29sb21iRGVjb2Rlci5yZWFkVUJ5dGUoKTsgLy8gUmF3IENFQS02MDggYnl0ZXMgd3JhcHBlZCBpbiBDRUEtNzA4IHBhY2tldFxuXG4gICAgICAgICAgICAgICAgICAgIGlmICh1c2VyRGF0YVR5cGUgPT09IDMpIHtcbiAgICAgICAgICAgICAgICAgICAgICB2YXIgZmlyc3RCeXRlID0gZXhwR29sb21iRGVjb2Rlci5yZWFkVUJ5dGUoKTtcbiAgICAgICAgICAgICAgICAgICAgICB2YXIgc2Vjb25kQnl0ZSA9IGV4cEdvbG9tYkRlY29kZXIucmVhZFVCeXRlKCk7XG4gICAgICAgICAgICAgICAgICAgICAgdmFyIHRvdGFsQ0NzID0gMzEgJiBmaXJzdEJ5dGU7XG4gICAgICAgICAgICAgICAgICAgICAgdmFyIGJ5dGVBcnJheSA9IFtmaXJzdEJ5dGUsIHNlY29uZEJ5dGVdO1xuXG4gICAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHRvdGFsQ0NzOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIDMgYnl0ZXMgcGVyIENDXG4gICAgICAgICAgICAgICAgICAgICAgICBieXRlQXJyYXkucHVzaChleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ5dGVBcnJheS5wdXNoKGV4cEdvbG9tYkRlY29kZXIucmVhZFVCeXRlKCkpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnl0ZUFycmF5LnB1c2goZXhwR29sb21iRGVjb2Rlci5yZWFkVUJ5dGUoKSk7XG4gICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgX3RoaXMuX2luc2VydFNhbXBsZUluT3JkZXIoX3RoaXMuX3R4dFRyYWNrLnNhbXBsZXMsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU6IDMsXG4gICAgICAgICAgICAgICAgICAgICAgICBwdHM6IHBlcy5wdHMsXG4gICAgICAgICAgICAgICAgICAgICAgICBieXRlczogYnl0ZUFycmF5XG4gICAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAocGF5bG9hZFR5cGUgPT09IDUgJiYgZXhwR29sb21iRGVjb2Rlci5ieXRlc0F2YWlsYWJsZSAhPT0gMCkge1xuICAgICAgICAgICAgICBlbmRPZkNhcHRpb25zID0gdHJ1ZTtcblxuICAgICAgICAgICAgICBpZiAocGF5bG9hZFNpemUgPiAxNikge1xuICAgICAgICAgICAgICAgIHZhciB1dWlkU3RyQXJyYXkgPSBbXTtcblxuICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCAxNjsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICB1dWlkU3RyQXJyYXkucHVzaChleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpLnRvU3RyaW5nKDE2KSk7XG5cbiAgICAgICAgICAgICAgICAgIGlmIChpID09PSAzIHx8IGkgPT09IDUgfHwgaSA9PT0gNyB8fCBpID09PSA5KSB7XG4gICAgICAgICAgICAgICAgICAgIHV1aWRTdHJBcnJheS5wdXNoKCctJyk7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgdmFyIGxlbmd0aCA9IHBheWxvYWRTaXplIC0gMTY7XG4gICAgICAgICAgICAgICAgdmFyIHVzZXJEYXRhUGF5bG9hZEJ5dGVzID0gbmV3IFVpbnQ4QXJyYXkobGVuZ3RoKTtcblxuICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgdXNlckRhdGFQYXlsb2FkQnl0ZXNbaV0gPSBleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIF90aGlzLl9pbnNlcnRTYW1wbGVJbk9yZGVyKF90aGlzLl90eHRUcmFjay5zYW1wbGVzLCB7XG4gICAgICAgICAgICAgICAgICBwdHM6IHBlcy5wdHMsXG4gICAgICAgICAgICAgICAgICBwYXlsb2FkVHlwZTogcGF5bG9hZFR5cGUsXG4gICAgICAgICAgICAgICAgICB1dWlkOiB1dWlkU3RyQXJyYXkuam9pbignJyksXG4gICAgICAgICAgICAgICAgICB1c2VyRGF0YUJ5dGVzOiB1c2VyRGF0YVBheWxvYWRCeXRlcyxcbiAgICAgICAgICAgICAgICAgIHVzZXJEYXRhOiBPYmplY3QoaWQzW1widXRmOEFycmF5VG9TdHJcIl0pKHVzZXJEYXRhUGF5bG9hZEJ5dGVzLmJ1ZmZlcilcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChwYXlsb2FkU2l6ZSA8IGV4cEdvbG9tYkRlY29kZXIuYnl0ZXNBdmFpbGFibGUpIHtcbiAgICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IHBheWxvYWRTaXplOyBpKyspIHtcbiAgICAgICAgICAgICAgICBleHBHb2xvbWJEZWNvZGVyLnJlYWRVQnl0ZSgpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIC8vIFNQU1xuXG4gICAgICAgIGNhc2UgNzpcbiAgICAgICAgICBwdXNoID0gdHJ1ZTtcbiAgICAgICAgICBzcHNmb3VuZCA9IHRydWU7XG5cbiAgICAgICAgICBpZiAoZGVidWcgJiYgYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBhdmNTYW1wbGUuZGVidWcgKz0gJ1NQUyAnO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghdHJhY2suc3BzKSB7XG4gICAgICAgICAgICBleHBHb2xvbWJEZWNvZGVyID0gbmV3IGV4cF9nb2xvbWIodW5pdC5kYXRhKTtcbiAgICAgICAgICAgIHZhciBjb25maWcgPSBleHBHb2xvbWJEZWNvZGVyLnJlYWRTUFMoKTtcbiAgICAgICAgICAgIHRyYWNrLndpZHRoID0gY29uZmlnLndpZHRoO1xuICAgICAgICAgICAgdHJhY2suaGVpZ2h0ID0gY29uZmlnLmhlaWdodDtcbiAgICAgICAgICAgIHRyYWNrLnBpeGVsUmF0aW8gPSBjb25maWcucGl4ZWxSYXRpbztcbiAgICAgICAgICAgIHRyYWNrLnNwcyA9IFt1bml0LmRhdGFdO1xuICAgICAgICAgICAgdHJhY2suZHVyYXRpb24gPSBfdGhpcy5fZHVyYXRpb247XG4gICAgICAgICAgICB2YXIgY29kZWNhcnJheSA9IHVuaXQuZGF0YS5zdWJhcnJheSgxLCA0KTtcbiAgICAgICAgICAgIHZhciBjb2RlY3N0cmluZyA9ICdhdmMxLic7XG5cbiAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCAzOyBpKyspIHtcbiAgICAgICAgICAgICAgdmFyIGggPSBjb2RlY2FycmF5W2ldLnRvU3RyaW5nKDE2KTtcblxuICAgICAgICAgICAgICBpZiAoaC5sZW5ndGggPCAyKSB7XG4gICAgICAgICAgICAgICAgaCA9ICcwJyArIGg7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBjb2RlY3N0cmluZyArPSBoO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0cmFjay5jb2RlYyA9IGNvZGVjc3RyaW5nO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICAvLyBQUFNcblxuICAgICAgICBjYXNlIDg6XG4gICAgICAgICAgcHVzaCA9IHRydWU7XG5cbiAgICAgICAgICBpZiAoZGVidWcgJiYgYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBhdmNTYW1wbGUuZGVidWcgKz0gJ1BQUyAnO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghdHJhY2sucHBzKSB7XG4gICAgICAgICAgICB0cmFjay5wcHMgPSBbdW5pdC5kYXRhXTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgLy8gQVVEXG5cbiAgICAgICAgY2FzZSA5OlxuICAgICAgICAgIHB1c2ggPSBmYWxzZTtcbiAgICAgICAgICB0cmFjay5hdWRGb3VuZCA9IHRydWU7XG5cbiAgICAgICAgICBpZiAoYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBwdXNoQWNjZXNVbml0KGF2Y1NhbXBsZSwgdHJhY2spO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGF2Y1NhbXBsZSA9IF90aGlzLmF2Y1NhbXBsZSA9IGNyZWF0ZUFWQ1NhbXBsZShmYWxzZSwgcGVzLnB0cywgcGVzLmR0cywgZGVidWcgPyAnQVVEICcgOiAnJyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIC8vIEZpbGxlciBEYXRhXG5cbiAgICAgICAgY2FzZSAxMjpcbiAgICAgICAgICBwdXNoID0gZmFsc2U7XG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBwdXNoID0gZmFsc2U7XG5cbiAgICAgICAgICBpZiAoYXZjU2FtcGxlKSB7XG4gICAgICAgICAgICBhdmNTYW1wbGUuZGVidWcgKz0gJ3Vua25vd24gTkFMICcgKyB1bml0LnR5cGUgKyAnICc7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIGlmIChhdmNTYW1wbGUgJiYgcHVzaCkge1xuICAgICAgICB2YXIgX3VuaXRzID0gYXZjU2FtcGxlLnVuaXRzO1xuXG4gICAgICAgIF91bml0cy5wdXNoKHVuaXQpO1xuICAgICAgfVxuICAgIH0pOyAvLyBpZiBsYXN0IFBFUyBwYWNrZXQsIHB1c2ggc2FtcGxlc1xuXG4gICAgaWYgKGxhc3QgJiYgYXZjU2FtcGxlKSB7XG4gICAgICBwdXNoQWNjZXNVbml0KGF2Y1NhbXBsZSwgdHJhY2spO1xuICAgICAgdGhpcy5hdmNTYW1wbGUgPSBudWxsO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uX2luc2VydFNhbXBsZUluT3JkZXIgPSBmdW5jdGlvbiBfaW5zZXJ0U2FtcGxlSW5PcmRlcihhcnIsIGRhdGEpIHtcbiAgICB2YXIgbGVuID0gYXJyLmxlbmd0aDtcblxuICAgIGlmIChsZW4gPiAwKSB7XG4gICAgICBpZiAoZGF0YS5wdHMgPj0gYXJyW2xlbiAtIDFdLnB0cykge1xuICAgICAgICBhcnIucHVzaChkYXRhKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZvciAodmFyIHBvcyA9IGxlbiAtIDE7IHBvcyA+PSAwOyBwb3MtLSkge1xuICAgICAgICAgIGlmIChkYXRhLnB0cyA8IGFycltwb3NdLnB0cykge1xuICAgICAgICAgICAgYXJyLnNwbGljZShwb3MsIDAsIGRhdGEpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGFyci5wdXNoKGRhdGEpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uX2dldExhc3ROYWxVbml0ID0gZnVuY3Rpb24gX2dldExhc3ROYWxVbml0KCkge1xuICAgIHZhciBhdmNTYW1wbGUgPSB0aGlzLmF2Y1NhbXBsZSxcbiAgICAgICAgbGFzdFVuaXQ7IC8vIHRyeSB0byBmYWxsYmFjayB0byBwcmV2aW91cyBzYW1wbGUgaWYgY3VycmVudCBvbmUgaXMgZW1wdHlcblxuICAgIGlmICghYXZjU2FtcGxlIHx8IGF2Y1NhbXBsZS51bml0cy5sZW5ndGggPT09IDApIHtcbiAgICAgIHZhciB0cmFjayA9IHRoaXMuX2F2Y1RyYWNrLFxuICAgICAgICAgIHNhbXBsZXMgPSB0cmFjay5zYW1wbGVzO1xuICAgICAgYXZjU2FtcGxlID0gc2FtcGxlc1tzYW1wbGVzLmxlbmd0aCAtIDFdO1xuICAgIH1cblxuICAgIGlmIChhdmNTYW1wbGUpIHtcbiAgICAgIHZhciB1bml0cyA9IGF2Y1NhbXBsZS51bml0cztcbiAgICAgIGxhc3RVbml0ID0gdW5pdHNbdW5pdHMubGVuZ3RoIC0gMV07XG4gICAgfVxuXG4gICAgcmV0dXJuIGxhc3RVbml0O1xuICB9O1xuXG4gIF9wcm90by5fcGFyc2VBVkNOQUx1ID0gZnVuY3Rpb24gX3BhcnNlQVZDTkFMdShhcnJheSkge1xuICAgIHZhciBpID0gMCxcbiAgICAgICAgbGVuID0gYXJyYXkuYnl0ZUxlbmd0aCxcbiAgICAgICAgdmFsdWUsXG4gICAgICAgIG92ZXJmbG93LFxuICAgICAgICB0cmFjayA9IHRoaXMuX2F2Y1RyYWNrLFxuICAgICAgICBzdGF0ZSA9IHRyYWNrLm5hbHVTdGF0ZSB8fCAwLFxuICAgICAgICBsYXN0U3RhdGUgPSBzdGF0ZTtcbiAgICB2YXIgdW5pdHMgPSBbXSxcbiAgICAgICAgdW5pdCxcbiAgICAgICAgdW5pdFR5cGUsXG4gICAgICAgIGxhc3RVbml0U3RhcnQgPSAtMSxcbiAgICAgICAgbGFzdFVuaXRUeXBlOyAvLyBsb2dnZXIubG9nKCdQRVM6JyArIEhleC5oZXhEdW1wKGFycmF5KSk7XG5cbiAgICBpZiAoc3RhdGUgPT09IC0xKSB7XG4gICAgICAvLyBzcGVjaWFsIHVzZSBjYXNlIHdoZXJlIHdlIGZvdW5kIDMgb3IgNC1ieXRlIHN0YXJ0IGNvZGVzIGV4YWN0bHkgYXQgdGhlIGVuZCBvZiBwcmV2aW91cyBQRVMgcGFja2V0XG4gICAgICBsYXN0VW5pdFN0YXJ0ID0gMDsgLy8gTkFMdSB0eXBlIGlzIHZhbHVlIHJlYWQgZnJvbSBvZmZzZXQgMFxuXG4gICAgICBsYXN0VW5pdFR5cGUgPSBhcnJheVswXSAmIDB4MWY7XG4gICAgICBzdGF0ZSA9IDA7XG4gICAgICBpID0gMTtcbiAgICB9XG5cbiAgICB3aGlsZSAoaSA8IGxlbikge1xuICAgICAgdmFsdWUgPSBhcnJheVtpKytdOyAvLyBvcHRpbWl6YXRpb24uIHN0YXRlIDAgYW5kIDEgYXJlIHRoZSBwcmVkb21pbmFudCBjYXNlLiBsZXQncyBoYW5kbGUgdGhlbSBvdXRzaWRlIG9mIHRoZSBzd2l0Y2gvY2FzZVxuXG4gICAgICBpZiAoIXN0YXRlKSB7XG4gICAgICAgIHN0YXRlID0gdmFsdWUgPyAwIDogMTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChzdGF0ZSA9PT0gMSkge1xuICAgICAgICBzdGF0ZSA9IHZhbHVlID8gMCA6IDI7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfSAvLyBoZXJlIHdlIGhhdmUgc3RhdGUgZWl0aGVyIGVxdWFsIHRvIDIgb3IgM1xuXG5cbiAgICAgIGlmICghdmFsdWUpIHtcbiAgICAgICAgc3RhdGUgPSAzO1xuICAgICAgfSBlbHNlIGlmICh2YWx1ZSA9PT0gMSkge1xuICAgICAgICBpZiAobGFzdFVuaXRTdGFydCA+PSAwKSB7XG4gICAgICAgICAgdW5pdCA9IHtcbiAgICAgICAgICAgIGRhdGE6IGFycmF5LnN1YmFycmF5KGxhc3RVbml0U3RhcnQsIGkgLSBzdGF0ZSAtIDEpLFxuICAgICAgICAgICAgdHlwZTogbGFzdFVuaXRUeXBlXG4gICAgICAgICAgfTsgLy8gbG9nZ2VyLmxvZygncHVzaGluZyBOQUxVLCB0eXBlL3NpemU6JyArIHVuaXQudHlwZSArICcvJyArIHVuaXQuZGF0YS5ieXRlTGVuZ3RoKTtcblxuICAgICAgICAgIHVuaXRzLnB1c2godW5pdCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gbGFzdFVuaXRTdGFydCBpcyB1bmRlZmluZWQgPT4gdGhpcyBpcyB0aGUgZmlyc3Qgc3RhcnQgY29kZSBmb3VuZCBpbiB0aGlzIFBFUyBwYWNrZXRcbiAgICAgICAgICAvLyBmaXJzdCBjaGVjayBpZiBzdGFydCBjb2RlIGRlbGltaXRlciBpcyBvdmVybGFwcGluZyBiZXR3ZWVuIDIgUEVTIHBhY2tldHMsXG4gICAgICAgICAgLy8gaWUgaXQgc3RhcnRlZCBpbiBsYXN0IHBhY2tldCAobGFzdFN0YXRlIG5vdCB6ZXJvKVxuICAgICAgICAgIC8vIGFuZCBlbmRlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoaXMgUEVTIHBhY2tldCAoaSA8PSA0IC0gbGFzdFN0YXRlKVxuICAgICAgICAgIHZhciBsYXN0VW5pdCA9IHRoaXMuX2dldExhc3ROYWxVbml0KCk7XG5cbiAgICAgICAgICBpZiAobGFzdFVuaXQpIHtcbiAgICAgICAgICAgIGlmIChsYXN0U3RhdGUgJiYgaSA8PSA0IC0gbGFzdFN0YXRlKSB7XG4gICAgICAgICAgICAgIC8vIHN0YXJ0IGRlbGltaXRlciBvdmVybGFwcGluZyBiZXR3ZWVuIFBFUyBwYWNrZXRzXG4gICAgICAgICAgICAgIC8vIHN0cmlwIHN0YXJ0IGRlbGltaXRlciBieXRlcyBmcm9tIHRoZSBlbmQgb2YgbGFzdCBOQUwgdW5pdFxuICAgICAgICAgICAgICAvLyBjaGVjayBpZiBsYXN0VW5pdCBoYWQgYSBzdGF0ZSBkaWZmZXJlbnQgZnJvbSB6ZXJvXG4gICAgICAgICAgICAgIGlmIChsYXN0VW5pdC5zdGF0ZSkge1xuICAgICAgICAgICAgICAgIC8vIHN0cmlwIGxhc3QgYnl0ZXNcbiAgICAgICAgICAgICAgICBsYXN0VW5pdC5kYXRhID0gbGFzdFVuaXQuZGF0YS5zdWJhcnJheSgwLCBsYXN0VW5pdC5kYXRhLmJ5dGVMZW5ndGggLSBsYXN0U3RhdGUpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IC8vIElmIE5BTCB1bml0cyBhcmUgbm90IHN0YXJ0aW5nIHJpZ2h0IGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIFBFUyBwYWNrZXQsIHB1c2ggcHJlY2VkaW5nIGRhdGEgaW50byBwcmV2aW91cyBOQUwgdW5pdC5cblxuXG4gICAgICAgICAgICBvdmVyZmxvdyA9IGkgLSBzdGF0ZSAtIDE7XG5cbiAgICAgICAgICAgIGlmIChvdmVyZmxvdyA+IDApIHtcbiAgICAgICAgICAgICAgLy8gbG9nZ2VyLmxvZygnZmlyc3QgTkFMVSBmb3VuZCB3aXRoIG92ZXJmbG93OicgKyBvdmVyZmxvdyk7XG4gICAgICAgICAgICAgIHZhciB0bXAgPSBuZXcgVWludDhBcnJheShsYXN0VW5pdC5kYXRhLmJ5dGVMZW5ndGggKyBvdmVyZmxvdyk7XG4gICAgICAgICAgICAgIHRtcC5zZXQobGFzdFVuaXQuZGF0YSwgMCk7XG4gICAgICAgICAgICAgIHRtcC5zZXQoYXJyYXkuc3ViYXJyYXkoMCwgb3ZlcmZsb3cpLCBsYXN0VW5pdC5kYXRhLmJ5dGVMZW5ndGgpO1xuICAgICAgICAgICAgICBsYXN0VW5pdC5kYXRhID0gdG1wO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSAvLyBjaGVjayBpZiB3ZSBjYW4gcmVhZCB1bml0IHR5cGVcblxuXG4gICAgICAgIGlmIChpIDwgbGVuKSB7XG4gICAgICAgICAgdW5pdFR5cGUgPSBhcnJheVtpXSAmIDB4MWY7IC8vIGxvZ2dlci5sb2coJ2ZpbmQgTkFMVSBAIG9mZnNldDonICsgaSArICcsdHlwZTonICsgdW5pdFR5cGUpO1xuXG4gICAgICAgICAgbGFzdFVuaXRTdGFydCA9IGk7XG4gICAgICAgICAgbGFzdFVuaXRUeXBlID0gdW5pdFR5cGU7XG4gICAgICAgICAgc3RhdGUgPSAwO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIG5vdCBlbm91Z2ggYnl0ZSB0byByZWFkIHVuaXQgdHlwZS4gbGV0J3MgcmVhZCBpdCBvbiBuZXh0IFBFUyBwYXJzaW5nXG4gICAgICAgICAgc3RhdGUgPSAtMTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3RhdGUgPSAwO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChsYXN0VW5pdFN0YXJ0ID49IDAgJiYgc3RhdGUgPj0gMCkge1xuICAgICAgdW5pdCA9IHtcbiAgICAgICAgZGF0YTogYXJyYXkuc3ViYXJyYXkobGFzdFVuaXRTdGFydCwgbGVuKSxcbiAgICAgICAgdHlwZTogbGFzdFVuaXRUeXBlLFxuICAgICAgICBzdGF0ZTogc3RhdGVcbiAgICAgIH07XG4gICAgICB1bml0cy5wdXNoKHVuaXQpOyAvLyBsb2dnZXIubG9nKCdwdXNoaW5nIE5BTFUsIHR5cGUvc2l6ZS9zdGF0ZTonICsgdW5pdC50eXBlICsgJy8nICsgdW5pdC5kYXRhLmJ5dGVMZW5ndGggKyAnLycgKyBzdGF0ZSk7XG4gICAgfSAvLyBubyBOQUx1IGZvdW5kXG5cblxuICAgIGlmICh1bml0cy5sZW5ndGggPT09IDApIHtcbiAgICAgIC8vIGFwcGVuZCBwZXMuZGF0YSB0byBwcmV2aW91cyBOQUwgdW5pdFxuICAgICAgdmFyIF9sYXN0VW5pdCA9IHRoaXMuX2dldExhc3ROYWxVbml0KCk7XG5cbiAgICAgIGlmIChfbGFzdFVuaXQpIHtcbiAgICAgICAgdmFyIF90bXAgPSBuZXcgVWludDhBcnJheShfbGFzdFVuaXQuZGF0YS5ieXRlTGVuZ3RoICsgYXJyYXkuYnl0ZUxlbmd0aCk7XG5cbiAgICAgICAgX3RtcC5zZXQoX2xhc3RVbml0LmRhdGEsIDApO1xuXG4gICAgICAgIF90bXAuc2V0KGFycmF5LCBfbGFzdFVuaXQuZGF0YS5ieXRlTGVuZ3RoKTtcblxuICAgICAgICBfbGFzdFVuaXQuZGF0YSA9IF90bXA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdHJhY2submFsdVN0YXRlID0gc3RhdGU7XG4gICAgcmV0dXJuIHVuaXRzO1xuICB9XG4gIC8qKlxuICAgKiByZW1vdmUgRW11bGF0aW9uIFByZXZlbnRpb24gYnl0ZXMgZnJvbSBhIFJCU1BcbiAgICovXG4gIDtcblxuICBfcHJvdG8uZGlzY2FyZEVQQiA9IGZ1bmN0aW9uIGRpc2NhcmRFUEIoZGF0YSkge1xuICAgIHZhciBsZW5ndGggPSBkYXRhLmJ5dGVMZW5ndGgsXG4gICAgICAgIEVQQlBvc2l0aW9ucyA9IFtdLFxuICAgICAgICBpID0gMSxcbiAgICAgICAgbmV3TGVuZ3RoLFxuICAgICAgICBuZXdEYXRhOyAvLyBGaW5kIGFsbCBgRW11bGF0aW9uIFByZXZlbnRpb24gQnl0ZXNgXG5cbiAgICB3aGlsZSAoaSA8IGxlbmd0aCAtIDIpIHtcbiAgICAgIGlmIChkYXRhW2ldID09PSAwICYmIGRhdGFbaSArIDFdID09PSAwICYmIGRhdGFbaSArIDJdID09PSAweDAzKSB7XG4gICAgICAgIEVQQlBvc2l0aW9ucy5wdXNoKGkgKyAyKTtcbiAgICAgICAgaSArPSAyO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaSsrO1xuICAgICAgfVxuICAgIH0gLy8gSWYgbm8gRW11bGF0aW9uIFByZXZlbnRpb24gQnl0ZXMgd2VyZSBmb3VuZCBqdXN0IHJldHVybiB0aGUgb3JpZ2luYWxcbiAgICAvLyBhcnJheVxuXG5cbiAgICBpZiAoRVBCUG9zaXRpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSAvLyBDcmVhdGUgYSBuZXcgYXJyYXkgdG8gaG9sZCB0aGUgTkFMIHVuaXQgZGF0YVxuXG5cbiAgICBuZXdMZW5ndGggPSBsZW5ndGggLSBFUEJQb3NpdGlvbnMubGVuZ3RoO1xuICAgIG5ld0RhdGEgPSBuZXcgVWludDhBcnJheShuZXdMZW5ndGgpO1xuICAgIHZhciBzb3VyY2VJbmRleCA9IDA7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbmV3TGVuZ3RoOyBzb3VyY2VJbmRleCsrLCBpKyspIHtcbiAgICAgIGlmIChzb3VyY2VJbmRleCA9PT0gRVBCUG9zaXRpb25zWzBdKSB7XG4gICAgICAgIC8vIFNraXAgdGhpcyBieXRlXG4gICAgICAgIHNvdXJjZUluZGV4Kys7IC8vIFJlbW92ZSB0aGlzIHBvc2l0aW9uIGluZGV4XG5cbiAgICAgICAgRVBCUG9zaXRpb25zLnNoaWZ0KCk7XG4gICAgICB9XG5cbiAgICAgIG5ld0RhdGFbaV0gPSBkYXRhW3NvdXJjZUluZGV4XTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3RGF0YTtcbiAgfTtcblxuICBfcHJvdG8uX3BhcnNlQUFDUEVTID0gZnVuY3Rpb24gX3BhcnNlQUFDUEVTKHBlcykge1xuICAgIHZhciB0cmFjayA9IHRoaXMuX2F1ZGlvVHJhY2ssXG4gICAgICAgIGRhdGEgPSBwZXMuZGF0YSxcbiAgICAgICAgcHRzID0gcGVzLnB0cyxcbiAgICAgICAgc3RhcnRPZmZzZXQgPSAwLFxuICAgICAgICBhYWNPdmVyRmxvdyA9IHRoaXMuYWFjT3ZlckZsb3csXG4gICAgICAgIGFhY0xhc3RQVFMgPSB0aGlzLmFhY0xhc3RQVFMsXG4gICAgICAgIGZyYW1lRHVyYXRpb24sXG4gICAgICAgIGZyYW1lSW5kZXgsXG4gICAgICAgIG9mZnNldCxcbiAgICAgICAgc3RhbXAsXG4gICAgICAgIGxlbjtcblxuICAgIGlmIChhYWNPdmVyRmxvdykge1xuICAgICAgdmFyIHRtcCA9IG5ldyBVaW50OEFycmF5KGFhY092ZXJGbG93LmJ5dGVMZW5ndGggKyBkYXRhLmJ5dGVMZW5ndGgpO1xuICAgICAgdG1wLnNldChhYWNPdmVyRmxvdywgMCk7XG4gICAgICB0bXAuc2V0KGRhdGEsIGFhY092ZXJGbG93LmJ5dGVMZW5ndGgpOyAvLyBsb2dnZXIubG9nKGBBQUM6IGFwcGVuZCBvdmVyZmxvd2luZyAke2FhY092ZXJGbG93LmJ5dGVMZW5ndGh9IGJ5dGVzIHRvIGJlZ2lubmluZyBvZiBuZXcgUEVTYCk7XG5cbiAgICAgIGRhdGEgPSB0bXA7XG4gICAgfSAvLyBsb29rIGZvciBBRFRTIGhlYWRlciAoMHhGRkZ4KVxuXG5cbiAgICBmb3IgKG9mZnNldCA9IHN0YXJ0T2Zmc2V0LCBsZW4gPSBkYXRhLmxlbmd0aDsgb2Zmc2V0IDwgbGVuIC0gMTsgb2Zmc2V0KyspIHtcbiAgICAgIGlmIChpc0hlYWRlcihkYXRhLCBvZmZzZXQpKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH0gLy8gaWYgQURUUyBoZWFkZXIgZG9lcyBub3Qgc3RhcnQgc3RyYWlnaHQgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIHRoZSBQRVMgcGF5bG9hZCwgcmFpc2UgYW4gZXJyb3JcblxuXG4gICAgaWYgKG9mZnNldCkge1xuICAgICAgdmFyIHJlYXNvbiwgZmF0YWw7XG5cbiAgICAgIGlmIChvZmZzZXQgPCBsZW4gLSAxKSB7XG4gICAgICAgIHJlYXNvbiA9IFwiQUFDIFBFUyBkaWQgbm90IHN0YXJ0IHdpdGggQURUUyBoZWFkZXIsb2Zmc2V0OlwiICsgb2Zmc2V0O1xuICAgICAgICBmYXRhbCA9IGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVhc29uID0gJ25vIEFEVFMgaGVhZGVyIGZvdW5kIGluIEFBQyBQRVMnO1xuICAgICAgICBmYXRhbCA9IHRydWU7XG4gICAgICB9XG5cbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwicGFyc2luZyBlcnJvcjpcIiArIHJlYXNvbik7XG4gICAgICB0aGlzLm9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19QQVJTSU5HX0VSUk9SLFxuICAgICAgICBmYXRhbDogZmF0YWwsXG4gICAgICAgIHJlYXNvbjogcmVhc29uXG4gICAgICB9KTtcblxuICAgICAgaWYgKGZhdGFsKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpbml0VHJhY2tDb25maWcodHJhY2ssIHRoaXMub2JzZXJ2ZXIsIGRhdGEsIG9mZnNldCwgdGhpcy5hdWRpb0NvZGVjKTtcbiAgICBmcmFtZUluZGV4ID0gMDtcbiAgICBmcmFtZUR1cmF0aW9uID0gZ2V0RnJhbWVEdXJhdGlvbih0cmFjay5zYW1wbGVyYXRlKTsgLy8gaWYgbGFzdCBBQUMgZnJhbWUgaXMgb3ZlcmZsb3dpbmcsIHdlIHNob3VsZCBlbnN1cmUgdGltZXN0YW1wcyBhcmUgY29udGlndW91czpcbiAgICAvLyBmaXJzdCBzYW1wbGUgUFRTIHNob3VsZCBiZSBlcXVhbCB0byBsYXN0IHNhbXBsZSBQVFMgKyBmcmFtZUR1cmF0aW9uXG5cbiAgICBpZiAoYWFjT3ZlckZsb3cgJiYgYWFjTGFzdFBUUykge1xuICAgICAgdmFyIG5ld1BUUyA9IGFhY0xhc3RQVFMgKyBmcmFtZUR1cmF0aW9uO1xuXG4gICAgICBpZiAoTWF0aC5hYnMobmV3UFRTIC0gcHRzKSA+IDEpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIkFBQzogYWxpZ24gUFRTIGZvciBvdmVybGFwcGluZyBmcmFtZXMgYnkgXCIgKyBNYXRoLnJvdW5kKChuZXdQVFMgLSBwdHMpIC8gOTApKTtcbiAgICAgICAgcHRzID0gbmV3UFRTO1xuICAgICAgfVxuICAgIH0gLy8gc2NhbiBmb3IgYWFjIHNhbXBsZXNcblxuXG4gICAgd2hpbGUgKG9mZnNldCA8IGxlbikge1xuICAgICAgaWYgKGlzSGVhZGVyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgaWYgKG9mZnNldCArIDUgPCBsZW4pIHtcbiAgICAgICAgICB2YXIgZnJhbWUgPSBhcHBlbmRGcmFtZSh0cmFjaywgZGF0YSwgb2Zmc2V0LCBwdHMsIGZyYW1lSW5kZXgpO1xuXG4gICAgICAgICAgaWYgKGZyYW1lKSB7XG4gICAgICAgICAgICBvZmZzZXQgKz0gZnJhbWUubGVuZ3RoO1xuICAgICAgICAgICAgc3RhbXAgPSBmcmFtZS5zYW1wbGUucHRzO1xuICAgICAgICAgICAgZnJhbWVJbmRleCsrO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IC8vIFdlIGFyZSBhdCBhbiBBRFRTIGhlYWRlciwgYnV0IGRvIG5vdCBoYXZlIGVub3VnaCBkYXRhIGZvciBhIGZyYW1lXG4gICAgICAgIC8vIFJlbWFpbmluZyBkYXRhIHdpbGwgYmUgYWRkZWQgdG8gYWFjT3ZlckZsb3dcblxuXG4gICAgICAgIGJyZWFrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gbm90aGluZyBmb3VuZCwga2VlcCBsb29raW5nXG4gICAgICAgIG9mZnNldCsrO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChvZmZzZXQgPCBsZW4pIHtcbiAgICAgIGFhY092ZXJGbG93ID0gZGF0YS5zdWJhcnJheShvZmZzZXQsIGxlbik7IC8vIGxvZ2dlci5sb2coYEFBQzogb3ZlcmZsb3cgZGV0ZWN0ZWQ6JHtsZW4tb2Zmc2V0fWApO1xuICAgIH0gZWxzZSB7XG4gICAgICBhYWNPdmVyRmxvdyA9IG51bGw7XG4gICAgfVxuXG4gICAgdGhpcy5hYWNPdmVyRmxvdyA9IGFhY092ZXJGbG93O1xuICAgIHRoaXMuYWFjTGFzdFBUUyA9IHN0YW1wO1xuICB9O1xuXG4gIF9wcm90by5fcGFyc2VNUEVHUEVTID0gZnVuY3Rpb24gX3BhcnNlTVBFR1BFUyhwZXMpIHtcbiAgICB2YXIgZGF0YSA9IHBlcy5kYXRhO1xuICAgIHZhciBsZW5ndGggPSBkYXRhLmxlbmd0aDtcbiAgICB2YXIgZnJhbWVJbmRleCA9IDA7XG4gICAgdmFyIG9mZnNldCA9IDA7XG4gICAgdmFyIHB0cyA9IHBlcy5wdHM7XG5cbiAgICB3aGlsZSAob2Zmc2V0IDwgbGVuZ3RoKSB7XG4gICAgICBpZiAobXBlZ2F1ZGlvLmlzSGVhZGVyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgdmFyIGZyYW1lID0gbXBlZ2F1ZGlvLmFwcGVuZEZyYW1lKHRoaXMuX2F1ZGlvVHJhY2ssIGRhdGEsIG9mZnNldCwgcHRzLCBmcmFtZUluZGV4KTtcblxuICAgICAgICBpZiAoZnJhbWUpIHtcbiAgICAgICAgICBvZmZzZXQgKz0gZnJhbWUubGVuZ3RoO1xuICAgICAgICAgIGZyYW1lSW5kZXgrKztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBsb2dnZXIubG9nKCdVbmFibGUgdG8gcGFyc2UgTXBlZyBhdWRpbyBmcmFtZScpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBub3RoaW5nIGZvdW5kLCBrZWVwIGxvb2tpbmdcbiAgICAgICAgb2Zmc2V0Kys7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5fcGFyc2VJRDNQRVMgPSBmdW5jdGlvbiBfcGFyc2VJRDNQRVMocGVzKSB7XG4gICAgdGhpcy5faWQzVHJhY2suc2FtcGxlcy5wdXNoKHBlcyk7XG4gIH07XG5cbiAgcmV0dXJuIFRTRGVtdXhlcjtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgdHNkZW11eGVyID0gKHRzZGVtdXhlcl9UU0RlbXV4ZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvZGVtdXgvbXAzZGVtdXhlci5qc1xuLyoqXG4gKiBNUDMgZGVtdXhlclxuICovXG5cblxuXG5cbnZhciBtcDNkZW11eGVyX01QM0RlbXV4ZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBNUDNEZW11eGVyKG9ic2VydmVyLCByZW11eGVyLCBjb25maWcpIHtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy5yZW11eGVyID0gcmVtdXhlcjtcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBNUDNEZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8ucmVzZXRJbml0U2VnbWVudCA9IGZ1bmN0aW9uIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGR1cmF0aW9uKSB7XG4gICAgdGhpcy5fYXVkaW9UcmFjayA9IHtcbiAgICAgIGNvbnRhaW5lcjogJ2F1ZGlvL21wZWcnLFxuICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgIGlkOiAtMSxcbiAgICAgIHNlcXVlbmNlTnVtYmVyOiAwLFxuICAgICAgaXNBQUM6IGZhbHNlLFxuICAgICAgc2FtcGxlczogW10sXG4gICAgICBsZW46IDAsXG4gICAgICBtYW5pZmVzdENvZGVjOiBhdWRpb0NvZGVjLFxuICAgICAgZHVyYXRpb246IGR1cmF0aW9uLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwXG4gICAgfTtcbiAgfTtcblxuICBfcHJvdG8ucmVzZXRUaW1lU3RhbXAgPSBmdW5jdGlvbiByZXNldFRpbWVTdGFtcCgpIHt9O1xuXG4gIE1QM0RlbXV4ZXIucHJvYmUgPSBmdW5jdGlvbiBwcm9iZShkYXRhKSB7XG4gICAgLy8gY2hlY2sgaWYgZGF0YSBjb250YWlucyBJRDMgdGltZXN0YW1wIGFuZCBNUEVHIHN5bmMgd29yZFxuICAgIHZhciBvZmZzZXQsIGxlbmd0aDtcbiAgICB2YXIgaWQzRGF0YSA9IGlkM1tcImRlZmF1bHRcIl0uZ2V0SUQzRGF0YShkYXRhLCAwKTtcblxuICAgIGlmIChpZDNEYXRhICYmIGlkM1tcImRlZmF1bHRcIl0uZ2V0VGltZVN0YW1wKGlkM0RhdGEpICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIExvb2sgZm9yIE1QRUcgaGVhZGVyIHwgMTExMSAxMTExIHwgMTExWCBYWVpYIHwgd2hlcmUgWCBjYW4gYmUgZWl0aGVyIDAgb3IgMSBhbmQgWSBvciBaIHNob3VsZCBiZSAxXG4gICAgICAvLyBMYXllciBiaXRzIChwb3NpdGlvbiAxNCBhbmQgMTUpIGluIGhlYWRlciBzaG91bGQgYmUgYWx3YXlzIGRpZmZlcmVudCBmcm9tIDAgKExheWVyIEkgb3IgTGF5ZXIgSUkgb3IgTGF5ZXIgSUlJKVxuICAgICAgLy8gTW9yZSBpbmZvIGh0dHA6Ly93d3cubXAzLXRlY2gub3JnL3Byb2dyYW1tZXIvZnJhbWVfaGVhZGVyLmh0bWxcbiAgICAgIGZvciAob2Zmc2V0ID0gaWQzRGF0YS5sZW5ndGgsIGxlbmd0aCA9IE1hdGgubWluKGRhdGEubGVuZ3RoIC0gMSwgb2Zmc2V0ICsgMTAwKTsgb2Zmc2V0IDwgbGVuZ3RoOyBvZmZzZXQrKykge1xuICAgICAgICBpZiAobXBlZ2F1ZGlvLnByb2JlKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdNUEVHIEF1ZGlvIHN5bmMgd29yZCBmb3VuZCAhJyk7XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH0gLy8gZmVlZCBpbmNvbWluZyBkYXRhIHRvIHRoZSBmcm9udCBvZiB0aGUgcGFyc2luZyBwaXBlbGluZVxuICA7XG5cbiAgX3Byb3RvLmFwcGVuZCA9IGZ1bmN0aW9uIGFwcGVuZChkYXRhLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICB2YXIgaWQzRGF0YSA9IGlkM1tcImRlZmF1bHRcIl0uZ2V0SUQzRGF0YShkYXRhLCAwKSB8fCBbXTtcbiAgICB2YXIgdGltZXN0YW1wID0gaWQzW1wiZGVmYXVsdFwiXS5nZXRUaW1lU3RhbXAoaWQzRGF0YSk7XG4gICAgdmFyIHB0cyA9IHRpbWVzdGFtcCAhPT0gdW5kZWZpbmVkID8gOTAgKiB0aW1lc3RhbXAgOiB0aW1lT2Zmc2V0ICogOTAwMDA7XG4gICAgdmFyIG9mZnNldCA9IGlkM0RhdGEubGVuZ3RoO1xuICAgIHZhciBsZW5ndGggPSBkYXRhLmxlbmd0aDtcbiAgICB2YXIgZnJhbWVJbmRleCA9IDAsXG4gICAgICAgIHN0YW1wID0gMDtcbiAgICB2YXIgdHJhY2sgPSB0aGlzLl9hdWRpb1RyYWNrO1xuICAgIHZhciBpZDNTYW1wbGVzID0gW3tcbiAgICAgIHB0czogcHRzLFxuICAgICAgZHRzOiBwdHMsXG4gICAgICBkYXRhOiBpZDNEYXRhXG4gICAgfV07XG5cbiAgICB3aGlsZSAob2Zmc2V0IDwgbGVuZ3RoKSB7XG4gICAgICBpZiAobXBlZ2F1ZGlvLmlzSGVhZGVyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgdmFyIGZyYW1lID0gbXBlZ2F1ZGlvLmFwcGVuZEZyYW1lKHRyYWNrLCBkYXRhLCBvZmZzZXQsIHB0cywgZnJhbWVJbmRleCk7XG5cbiAgICAgICAgaWYgKGZyYW1lKSB7XG4gICAgICAgICAgb2Zmc2V0ICs9IGZyYW1lLmxlbmd0aDtcbiAgICAgICAgICBzdGFtcCA9IGZyYW1lLnNhbXBsZS5wdHM7XG4gICAgICAgICAgZnJhbWVJbmRleCsrO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ1VuYWJsZSB0byBwYXJzZSBNcGVnIGF1ZGlvIGZyYW1lJyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoaWQzW1wiZGVmYXVsdFwiXS5pc0hlYWRlcihkYXRhLCBvZmZzZXQpKSB7XG4gICAgICAgIGlkM0RhdGEgPSBpZDNbXCJkZWZhdWx0XCJdLmdldElEM0RhdGEoZGF0YSwgb2Zmc2V0KTtcbiAgICAgICAgaWQzU2FtcGxlcy5wdXNoKHtcbiAgICAgICAgICBwdHM6IHN0YW1wLFxuICAgICAgICAgIGR0czogc3RhbXAsXG4gICAgICAgICAgZGF0YTogaWQzRGF0YVxuICAgICAgICB9KTtcbiAgICAgICAgb2Zmc2V0ICs9IGlkM0RhdGEubGVuZ3RoO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gbm90aGluZyBmb3VuZCwga2VlcCBsb29raW5nXG4gICAgICAgIG9mZnNldCsrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMucmVtdXhlci5yZW11eCh0cmFjaywge1xuICAgICAgc2FtcGxlczogW11cbiAgICB9LCB7XG4gICAgICBzYW1wbGVzOiBpZDNTYW1wbGVzLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwXG4gICAgfSwge1xuICAgICAgc2FtcGxlczogW11cbiAgICB9LCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuICB9O1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHt9O1xuXG4gIHJldHVybiBNUDNEZW11eGVyO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBtcDNkZW11eGVyID0gKG1wM2RlbXV4ZXJfTVAzRGVtdXhlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZW11eC9hYWMtaGVscGVyLmpzXG4vKipcbiAqICBBQUMgaGVscGVyXG4gKi9cbnZhciBBQUMgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBBQUMoKSB7fVxuXG4gIEFBQy5nZXRTaWxlbnRGcmFtZSA9IGZ1bmN0aW9uIGdldFNpbGVudEZyYW1lKGNvZGVjLCBjaGFubmVsQ291bnQpIHtcbiAgICBzd2l0Y2ggKGNvZGVjKSB7XG4gICAgICBjYXNlICdtcDRhLjQwLjInOlxuICAgICAgICBpZiAoY2hhbm5lbENvdW50ID09PSAxKSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDAwLCAweGM4LCAweDAwLCAweDgwLCAweDIzLCAweDgwXSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSAyKSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDIxLCAweDAwLCAweDQ5LCAweDkwLCAweDAyLCAweDE5LCAweDAwLCAweDIzLCAweDgwXSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSAzKSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDAwLCAweGM4LCAweDAwLCAweDgwLCAweDIwLCAweDg0LCAweDAxLCAweDI2LCAweDQwLCAweDA4LCAweDY0LCAweDAwLCAweDhlXSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSA0KSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDAwLCAweGM4LCAweDAwLCAweDgwLCAweDIwLCAweDg0LCAweDAxLCAweDI2LCAweDQwLCAweDA4LCAweDY0LCAweDAwLCAweDgwLCAweDJjLCAweDgwLCAweDA4LCAweDAyLCAweDM4XSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSA1KSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDAwLCAweGM4LCAweDAwLCAweDgwLCAweDIwLCAweDg0LCAweDAxLCAweDI2LCAweDQwLCAweDA4LCAweDY0LCAweDAwLCAweDgyLCAweDMwLCAweDA0LCAweDk5LCAweDAwLCAweDIxLCAweDkwLCAweDAyLCAweDM4XSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSA2KSB7XG4gICAgICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KFsweDAwLCAweGM4LCAweDAwLCAweDgwLCAweDIwLCAweDg0LCAweDAxLCAweDI2LCAweDQwLCAweDA4LCAweDY0LCAweDAwLCAweDgyLCAweDMwLCAweDA0LCAweDk5LCAweDAwLCAweDIxLCAweDkwLCAweDAyLCAweDAwLCAweGIyLCAweDAwLCAweDIwLCAweDA4LCAweGUwXSk7XG4gICAgICAgIH1cblxuICAgICAgICBicmVhaztcbiAgICAgIC8vIGhhbmRsZSBIRS1BQUMgYmVsb3cgKG1wNGEuNDAuNSAvIG1wNGEuNDAuMjkpXG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGlmIChjaGFubmVsQ291bnQgPT09IDEpIHtcbiAgICAgICAgICAvLyBmZm1wZWcgLXkgLWYgbGF2ZmkgLWkgXCJhZXZhbHNyYz0wOmQ9MC4wNVwiIC1jOmEgbGliZmRrX2FhYyAtcHJvZmlsZTphIGFhY19oZSAtYjphIDRrIG91dHB1dC5hYWMgJiYgaGV4ZHVtcCAtdiAtZSAnMTYvMSBcIjB4JXgsXCIgXCJcXG5cIicgLXYgb3V0cHV0LmFhY1xuICAgICAgICAgIHJldHVybiBuZXcgVWludDhBcnJheShbMHgxLCAweDQwLCAweDIyLCAweDgwLCAweGEzLCAweDRlLCAweGU2LCAweDgwLCAweGJhLCAweDgsIDB4MCwgMHgwLCAweDAsIDB4MWMsIDB4NiwgMHhmMSwgMHhjMSwgMHhhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVhLCAweDVlXSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2hhbm5lbENvdW50ID09PSAyKSB7XG4gICAgICAgICAgLy8gZmZtcGVnIC15IC1mIGxhdmZpIC1pIFwiYWV2YWxzcmM9MHwwOmQ9MC4wNVwiIC1jOmEgbGliZmRrX2FhYyAtcHJvZmlsZTphIGFhY19oZV92MiAtYjphIDRrIG91dHB1dC5hYWMgJiYgaGV4ZHVtcCAtdiAtZSAnMTYvMSBcIjB4JXgsXCIgXCJcXG5cIicgLXYgb3V0cHV0LmFhY1xuICAgICAgICAgIHJldHVybiBuZXcgVWludDhBcnJheShbMHgxLCAweDQwLCAweDIyLCAweDgwLCAweGEzLCAweDVlLCAweGU2LCAweDgwLCAweGJhLCAweDgsIDB4MCwgMHgwLCAweDAsIDB4MCwgMHg5NSwgMHgwLCAweDYsIDB4ZjEsIDB4YTEsIDB4YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1ZV0pO1xuICAgICAgICB9IGVsc2UgaWYgKGNoYW5uZWxDb3VudCA9PT0gMykge1xuICAgICAgICAgIC8vIGZmbXBlZyAteSAtZiBsYXZmaSAtaSBcImFldmFsc3JjPTB8MHwwOmQ9MC4wNVwiIC1jOmEgbGliZmRrX2FhYyAtcHJvZmlsZTphIGFhY19oZV92MiAtYjphIDRrIG91dHB1dC5hYWMgJiYgaGV4ZHVtcCAtdiAtZSAnMTYvMSBcIjB4JXgsXCIgXCJcXG5cIicgLXYgb3V0cHV0LmFhY1xuICAgICAgICAgIHJldHVybiBuZXcgVWludDhBcnJheShbMHgxLCAweDQwLCAweDIyLCAweDgwLCAweGEzLCAweDVlLCAweGU2LCAweDgwLCAweGJhLCAweDgsIDB4MCwgMHgwLCAweDAsIDB4MCwgMHg5NSwgMHgwLCAweDYsIDB4ZjEsIDB4YTEsIDB4YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1ZV0pO1xuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH07XG5cbiAgcmV0dXJuIEFBQztcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgYWFjX2hlbHBlciA9IChBQUMpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvcmVtdXgvbXA0LWdlbmVyYXRvci5qc1xuLyoqXG4gKiBHZW5lcmF0ZSBNUDQgQm94XG4qL1xudmFyIFVJTlQzMl9NQVggPSBNYXRoLnBvdygyLCAzMikgLSAxO1xuXG52YXIgTVA0ID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gTVA0KCkge31cblxuICBNUDQuaW5pdCA9IGZ1bmN0aW9uIGluaXQoKSB7XG4gICAgTVA0LnR5cGVzID0ge1xuICAgICAgYXZjMTogW10sXG4gICAgICAvLyBjb2RpbmduYW1lXG4gICAgICBhdmNDOiBbXSxcbiAgICAgIGJ0cnQ6IFtdLFxuICAgICAgZGluZjogW10sXG4gICAgICBkcmVmOiBbXSxcbiAgICAgIGVzZHM6IFtdLFxuICAgICAgZnR5cDogW10sXG4gICAgICBoZGxyOiBbXSxcbiAgICAgIG1kYXQ6IFtdLFxuICAgICAgbWRoZDogW10sXG4gICAgICBtZGlhOiBbXSxcbiAgICAgIG1maGQ6IFtdLFxuICAgICAgbWluZjogW10sXG4gICAgICBtb29mOiBbXSxcbiAgICAgIG1vb3Y6IFtdLFxuICAgICAgbXA0YTogW10sXG4gICAgICAnLm1wMyc6IFtdLFxuICAgICAgbXZleDogW10sXG4gICAgICBtdmhkOiBbXSxcbiAgICAgIHBhc3A6IFtdLFxuICAgICAgc2R0cDogW10sXG4gICAgICBzdGJsOiBbXSxcbiAgICAgIHN0Y286IFtdLFxuICAgICAgc3RzYzogW10sXG4gICAgICBzdHNkOiBbXSxcbiAgICAgIHN0c3o6IFtdLFxuICAgICAgc3R0czogW10sXG4gICAgICB0ZmR0OiBbXSxcbiAgICAgIHRmaGQ6IFtdLFxuICAgICAgdHJhZjogW10sXG4gICAgICB0cmFrOiBbXSxcbiAgICAgIHRydW46IFtdLFxuICAgICAgdHJleDogW10sXG4gICAgICB0a2hkOiBbXSxcbiAgICAgIHZtaGQ6IFtdLFxuICAgICAgc21oZDogW11cbiAgICB9O1xuICAgIHZhciBpO1xuXG4gICAgZm9yIChpIGluIE1QNC50eXBlcykge1xuICAgICAgaWYgKE1QNC50eXBlcy5oYXNPd25Qcm9wZXJ0eShpKSkge1xuICAgICAgICBNUDQudHlwZXNbaV0gPSBbaS5jaGFyQ29kZUF0KDApLCBpLmNoYXJDb2RlQXQoMSksIGkuY2hhckNvZGVBdCgyKSwgaS5jaGFyQ29kZUF0KDMpXTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgdmlkZW9IZGxyID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb24gMFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcHJlX2RlZmluZWRcbiAgICAweDc2LCAweDY5LCAweDY0LCAweDY1LCAvLyBoYW5kbGVyX3R5cGU6ICd2aWRlJ1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4NTYsIDB4NjksIDB4NjQsIDB4NjUsIDB4NmYsIDB4NDgsIDB4NjEsIDB4NmUsIDB4NjQsIDB4NmMsIDB4NjUsIDB4NzIsIDB4MDAgLy8gbmFtZTogJ1ZpZGVvSGFuZGxlcidcbiAgICBdKTtcbiAgICB2YXIgYXVkaW9IZGxyID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb24gMFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcHJlX2RlZmluZWRcbiAgICAweDczLCAweDZmLCAweDc1LCAweDZlLCAvLyBoYW5kbGVyX3R5cGU6ICdzb3VuJ1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4NTMsIDB4NmYsIDB4NzUsIDB4NmUsIDB4NjQsIDB4NDgsIDB4NjEsIDB4NmUsIDB4NjQsIDB4NmMsIDB4NjUsIDB4NzIsIDB4MDAgLy8gbmFtZTogJ1NvdW5kSGFuZGxlcidcbiAgICBdKTtcbiAgICBNUDQuSERMUl9UWVBFUyA9IHtcbiAgICAgICd2aWRlbyc6IHZpZGVvSGRscixcbiAgICAgICdhdWRpbyc6IGF1ZGlvSGRsclxuICAgIH07XG4gICAgdmFyIGRyZWYgPSBuZXcgVWludDhBcnJheShbMHgwMCwgLy8gdmVyc2lvbiAwXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAxLCAvLyBlbnRyeV9jb3VudFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MGMsIC8vIGVudHJ5X3NpemVcbiAgICAweDc1LCAweDcyLCAweDZjLCAweDIwLCAvLyAndXJsJyB0eXBlXG4gICAgMHgwMCwgLy8gdmVyc2lvbiAwXG4gICAgMHgwMCwgMHgwMCwgMHgwMSAvLyBlbnRyeV9mbGFnc1xuICAgIF0pO1xuICAgIHZhciBzdGNvID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb25cbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAgLy8gZW50cnlfY291bnRcbiAgICBdKTtcbiAgICBNUDQuU1RUUyA9IE1QNC5TVFNDID0gTVA0LlNUQ08gPSBzdGNvO1xuICAgIE1QNC5TVFNaID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb25cbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHNhbXBsZV9zaXplXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCAvLyBzYW1wbGVfY291bnRcbiAgICBdKTtcbiAgICBNUDQuVk1IRCA9IG5ldyBVaW50OEFycmF5KFsweDAwLCAvLyB2ZXJzaW9uXG4gICAgMHgwMCwgMHgwMCwgMHgwMSwgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAvLyBncmFwaGljc21vZGVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwIC8vIG9wY29sb3JcbiAgICBdKTtcbiAgICBNUDQuU01IRCA9IG5ldyBVaW50OEFycmF5KFsweDAwLCAvLyB2ZXJzaW9uXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAvLyBiYWxhbmNlXG4gICAgMHgwMCwgMHgwMCAvLyByZXNlcnZlZFxuICAgIF0pO1xuICAgIE1QNC5TVFNEID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb24gMFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMV0pOyAvLyBlbnRyeV9jb3VudFxuXG4gICAgdmFyIG1ham9yQnJhbmQgPSBuZXcgVWludDhBcnJheShbMTA1LCAxMTUsIDExMSwgMTA5XSk7IC8vIGlzb21cblxuICAgIHZhciBhdmMxQnJhbmQgPSBuZXcgVWludDhBcnJheShbOTcsIDExOCwgOTksIDQ5XSk7IC8vIGF2YzFcblxuICAgIHZhciBtaW5vclZlcnNpb24gPSBuZXcgVWludDhBcnJheShbMCwgMCwgMCwgMV0pO1xuICAgIE1QNC5GVFlQID0gTVA0LmJveChNUDQudHlwZXMuZnR5cCwgbWFqb3JCcmFuZCwgbWlub3JWZXJzaW9uLCBtYWpvckJyYW5kLCBhdmMxQnJhbmQpO1xuICAgIE1QNC5ESU5GID0gTVA0LmJveChNUDQudHlwZXMuZGluZiwgTVA0LmJveChNUDQudHlwZXMuZHJlZiwgZHJlZikpO1xuICB9O1xuXG4gIE1QNC5ib3ggPSBmdW5jdGlvbiBib3godHlwZSkge1xuICAgIHZhciBwYXlsb2FkID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKSxcbiAgICAgICAgc2l6ZSA9IDgsXG4gICAgICAgIGkgPSBwYXlsb2FkLmxlbmd0aCxcbiAgICAgICAgbGVuID0gaSxcbiAgICAgICAgcmVzdWx0OyAvLyBjYWxjdWxhdGUgdGhlIHRvdGFsIHNpemUgd2UgbmVlZCB0byBhbGxvY2F0ZVxuXG4gICAgd2hpbGUgKGktLSkge1xuICAgICAgc2l6ZSArPSBwYXlsb2FkW2ldLmJ5dGVMZW5ndGg7XG4gICAgfVxuXG4gICAgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkoc2l6ZSk7XG4gICAgcmVzdWx0WzBdID0gc2l6ZSA+PiAyNCAmIDB4ZmY7XG4gICAgcmVzdWx0WzFdID0gc2l6ZSA+PiAxNiAmIDB4ZmY7XG4gICAgcmVzdWx0WzJdID0gc2l6ZSA+PiA4ICYgMHhmZjtcbiAgICByZXN1bHRbM10gPSBzaXplICYgMHhmZjtcbiAgICByZXN1bHQuc2V0KHR5cGUsIDQpOyAvLyBjb3B5IHRoZSBwYXlsb2FkIGludG8gdGhlIHJlc3VsdFxuXG4gICAgZm9yIChpID0gMCwgc2l6ZSA9IDg7IGkgPCBsZW47IGkrKykge1xuICAgICAgLy8gY29weSBwYXlsb2FkW2ldIGFycmF5IEAgb2Zmc2V0IHNpemVcbiAgICAgIHJlc3VsdC5zZXQocGF5bG9hZFtpXSwgc2l6ZSk7XG4gICAgICBzaXplICs9IHBheWxvYWRbaV0uYnl0ZUxlbmd0aDtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9O1xuXG4gIE1QNC5oZGxyID0gZnVuY3Rpb24gaGRscih0eXBlKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLmhkbHIsIE1QNC5IRExSX1RZUEVTW3R5cGVdKTtcbiAgfTtcblxuICBNUDQubWRhdCA9IGZ1bmN0aW9uIG1kYXQoZGF0YSkge1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tZGF0LCBkYXRhKTtcbiAgfTtcblxuICBNUDQubWRoZCA9IGZ1bmN0aW9uIG1kaGQodGltZXNjYWxlLCBkdXJhdGlvbikge1xuICAgIGR1cmF0aW9uICo9IHRpbWVzY2FsZTtcbiAgICB2YXIgdXBwZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uIC8gKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgdmFyIGxvd2VyV29yZER1cmF0aW9uID0gTWF0aC5mbG9vcihkdXJhdGlvbiAlIChVSU5UMzJfTUFYICsgMSkpO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tZGhkLCBuZXcgVWludDhBcnJheShbMHgwMSwgLy8gdmVyc2lvbiAxXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAyLCAvLyBjcmVhdGlvbl90aW1lXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMywgLy8gbW9kaWZpY2F0aW9uX3RpbWVcbiAgICB0aW1lc2NhbGUgPj4gMjQgJiAweEZGLCB0aW1lc2NhbGUgPj4gMTYgJiAweEZGLCB0aW1lc2NhbGUgPj4gOCAmIDB4RkYsIHRpbWVzY2FsZSAmIDB4RkYsIC8vIHRpbWVzY2FsZVxuICAgIHVwcGVyV29yZER1cmF0aW9uID4+IDI0LCB1cHBlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4RkYsIHVwcGVyV29yZER1cmF0aW9uID4+IDggJiAweEZGLCB1cHBlcldvcmREdXJhdGlvbiAmIDB4RkYsIGxvd2VyV29yZER1cmF0aW9uID4+IDI0LCBsb3dlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4RkYsIGxvd2VyV29yZER1cmF0aW9uID4+IDggJiAweEZGLCBsb3dlcldvcmREdXJhdGlvbiAmIDB4RkYsIDB4NTUsIDB4YzQsIC8vICd1bmQnIGxhbmd1YWdlICh1bmRldGVybWluZWQpXG4gICAgMHgwMCwgMHgwMF0pKTtcbiAgfTtcblxuICBNUDQubWRpYSA9IGZ1bmN0aW9uIG1kaWEodHJhY2spIHtcbiAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMubWRpYSwgTVA0Lm1kaGQodHJhY2sudGltZXNjYWxlLCB0cmFjay5kdXJhdGlvbiksIE1QNC5oZGxyKHRyYWNrLnR5cGUpLCBNUDQubWluZih0cmFjaykpO1xuICB9O1xuXG4gIE1QNC5tZmhkID0gZnVuY3Rpb24gbWZoZChzZXF1ZW5jZU51bWJlcikge1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tZmhkLCBuZXcgVWludDhBcnJheShbMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gZmxhZ3NcbiAgICBzZXF1ZW5jZU51bWJlciA+PiAyNCwgc2VxdWVuY2VOdW1iZXIgPj4gMTYgJiAweEZGLCBzZXF1ZW5jZU51bWJlciA+PiA4ICYgMHhGRiwgc2VxdWVuY2VOdW1iZXIgJiAweEZGIC8vIHNlcXVlbmNlX251bWJlclxuICAgIF0pKTtcbiAgfTtcblxuICBNUDQubWluZiA9IGZ1bmN0aW9uIG1pbmYodHJhY2spIHtcbiAgICBpZiAodHJhY2sudHlwZSA9PT0gJ2F1ZGlvJykge1xuICAgICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1pbmYsIE1QNC5ib3goTVA0LnR5cGVzLnNtaGQsIE1QNC5TTUhEKSwgTVA0LkRJTkYsIE1QNC5zdGJsKHRyYWNrKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5taW5mLCBNUDQuYm94KE1QNC50eXBlcy52bWhkLCBNUDQuVk1IRCksIE1QNC5ESU5GLCBNUDQuc3RibCh0cmFjaykpO1xuICAgIH1cbiAgfTtcblxuICBNUDQubW9vZiA9IGZ1bmN0aW9uIG1vb2Yoc24sIGJhc2VNZWRpYURlY29kZVRpbWUsIHRyYWNrKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1vb2YsIE1QNC5tZmhkKHNuKSwgTVA0LnRyYWYodHJhY2ssIGJhc2VNZWRpYURlY29kZVRpbWUpKTtcbiAgfVxuICAvKipcbiAgKiBAcGFyYW0gdHJhY2tzLi4uIChvcHRpb25hbCkge2FycmF5fSB0aGUgdHJhY2tzIGFzc29jaWF0ZWQgd2l0aCB0aGlzIG1vdmllXG4gICovXG4gIDtcblxuICBNUDQubW9vdiA9IGZ1bmN0aW9uIG1vb3YodHJhY2tzKSB7XG4gICAgdmFyIGkgPSB0cmFja3MubGVuZ3RoLFxuICAgICAgICBib3hlcyA9IFtdO1xuXG4gICAgd2hpbGUgKGktLSkge1xuICAgICAgYm94ZXNbaV0gPSBNUDQudHJhayh0cmFja3NbaV0pO1xuICAgIH1cblxuICAgIHJldHVybiBNUDQuYm94LmFwcGx5KG51bGwsIFtNUDQudHlwZXMubW9vdiwgTVA0Lm12aGQodHJhY2tzWzBdLnRpbWVzY2FsZSwgdHJhY2tzWzBdLmR1cmF0aW9uKV0uY29uY2F0KGJveGVzKS5jb25jYXQoTVA0Lm12ZXgodHJhY2tzKSkpO1xuICB9O1xuXG4gIE1QNC5tdmV4ID0gZnVuY3Rpb24gbXZleCh0cmFja3MpIHtcbiAgICB2YXIgaSA9IHRyYWNrcy5sZW5ndGgsXG4gICAgICAgIGJveGVzID0gW107XG5cbiAgICB3aGlsZSAoaS0tKSB7XG4gICAgICBib3hlc1tpXSA9IE1QNC50cmV4KHRyYWNrc1tpXSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE1QNC5ib3guYXBwbHkobnVsbCwgW01QNC50eXBlcy5tdmV4XS5jb25jYXQoYm94ZXMpKTtcbiAgfTtcblxuICBNUDQubXZoZCA9IGZ1bmN0aW9uIG12aGQodGltZXNjYWxlLCBkdXJhdGlvbikge1xuICAgIGR1cmF0aW9uICo9IHRpbWVzY2FsZTtcbiAgICB2YXIgdXBwZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uIC8gKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgdmFyIGxvd2VyV29yZER1cmF0aW9uID0gTWF0aC5mbG9vcihkdXJhdGlvbiAlIChVSU5UMzJfTUFYICsgMSkpO1xuICAgIHZhciBieXRlcyA9IG5ldyBVaW50OEFycmF5KFsweDAxLCAvLyB2ZXJzaW9uIDFcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDIsIC8vIGNyZWF0aW9uX3RpbWVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAzLCAvLyBtb2RpZmljYXRpb25fdGltZVxuICAgIHRpbWVzY2FsZSA+PiAyNCAmIDB4RkYsIHRpbWVzY2FsZSA+PiAxNiAmIDB4RkYsIHRpbWVzY2FsZSA+PiA4ICYgMHhGRiwgdGltZXNjYWxlICYgMHhGRiwgLy8gdGltZXNjYWxlXG4gICAgdXBwZXJXb3JkRHVyYXRpb24gPj4gMjQsIHVwcGVyV29yZER1cmF0aW9uID4+IDE2ICYgMHhGRiwgdXBwZXJXb3JkRHVyYXRpb24gPj4gOCAmIDB4RkYsIHVwcGVyV29yZER1cmF0aW9uICYgMHhGRiwgbG93ZXJXb3JkRHVyYXRpb24gPj4gMjQsIGxvd2VyV29yZER1cmF0aW9uID4+IDE2ICYgMHhGRiwgbG93ZXJXb3JkRHVyYXRpb24gPj4gOCAmIDB4RkYsIGxvd2VyV29yZER1cmF0aW9uICYgMHhGRiwgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgLy8gMS4wIHJhdGVcbiAgICAweDAxLCAweDAwLCAvLyAxLjAgdm9sdW1lXG4gICAgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHg0MCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gdHJhbnNmb3JtYXRpb246IHVuaXR5IG1hdHJpeFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHByZV9kZWZpbmVkXG4gICAgMHhmZiwgMHhmZiwgMHhmZiwgMHhmZiAvLyBuZXh0X3RyYWNrX0lEXG4gICAgXSk7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm12aGQsIGJ5dGVzKTtcbiAgfTtcblxuICBNUDQuc2R0cCA9IGZ1bmN0aW9uIHNkdHAodHJhY2spIHtcbiAgICB2YXIgc2FtcGxlcyA9IHRyYWNrLnNhbXBsZXMgfHwgW10sXG4gICAgICAgIGJ5dGVzID0gbmV3IFVpbnQ4QXJyYXkoNCArIHNhbXBsZXMubGVuZ3RoKSxcbiAgICAgICAgZmxhZ3MsXG4gICAgICAgIGk7IC8vIGxlYXZlIHRoZSBmdWxsIGJveCBoZWFkZXIgKDQgYnl0ZXMpIGFsbCB6ZXJvXG4gICAgLy8gd3JpdGUgdGhlIHNhbXBsZSB0YWJsZVxuXG4gICAgZm9yIChpID0gMDsgaSA8IHNhbXBsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGZsYWdzID0gc2FtcGxlc1tpXS5mbGFncztcbiAgICAgIGJ5dGVzW2kgKyA0XSA9IGZsYWdzLmRlcGVuZHNPbiA8PCA0IHwgZmxhZ3MuaXNEZXBlbmRlZE9uIDw8IDIgfCBmbGFncy5oYXNSZWR1bmRhbmN5O1xuICAgIH1cblxuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5zZHRwLCBieXRlcyk7XG4gIH07XG5cbiAgTVA0LnN0YmwgPSBmdW5jdGlvbiBzdGJsKHRyYWNrKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnN0YmwsIE1QNC5zdHNkKHRyYWNrKSwgTVA0LmJveChNUDQudHlwZXMuc3R0cywgTVA0LlNUVFMpLCBNUDQuYm94KE1QNC50eXBlcy5zdHNjLCBNUDQuU1RTQyksIE1QNC5ib3goTVA0LnR5cGVzLnN0c3osIE1QNC5TVFNaKSwgTVA0LmJveChNUDQudHlwZXMuc3RjbywgTVA0LlNUQ08pKTtcbiAgfTtcblxuICBNUDQuYXZjMSA9IGZ1bmN0aW9uIGF2YzEodHJhY2spIHtcbiAgICB2YXIgc3BzID0gW10sXG4gICAgICAgIHBwcyA9IFtdLFxuICAgICAgICBpLFxuICAgICAgICBkYXRhLFxuICAgICAgICBsZW47IC8vIGFzc2VtYmxlIHRoZSBTUFNzXG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgdHJhY2suc3BzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBkYXRhID0gdHJhY2suc3BzW2ldO1xuICAgICAgbGVuID0gZGF0YS5ieXRlTGVuZ3RoO1xuICAgICAgc3BzLnB1c2gobGVuID4+PiA4ICYgMHhGRik7XG4gICAgICBzcHMucHVzaChsZW4gJiAweEZGKTsgLy8gU1BTXG5cbiAgICAgIHNwcyA9IHNwcy5jb25jYXQoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZGF0YSkpO1xuICAgIH0gLy8gYXNzZW1ibGUgdGhlIFBQU3NcblxuXG4gICAgZm9yIChpID0gMDsgaSA8IHRyYWNrLnBwcy5sZW5ndGg7IGkrKykge1xuICAgICAgZGF0YSA9IHRyYWNrLnBwc1tpXTtcbiAgICAgIGxlbiA9IGRhdGEuYnl0ZUxlbmd0aDtcbiAgICAgIHBwcy5wdXNoKGxlbiA+Pj4gOCAmIDB4RkYpO1xuICAgICAgcHBzLnB1c2gobGVuICYgMHhGRik7XG4gICAgICBwcHMgPSBwcHMuY29uY2F0KEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRhdGEpKTtcbiAgICB9XG5cbiAgICB2YXIgYXZjYyA9IE1QNC5ib3goTVA0LnR5cGVzLmF2Y0MsIG5ldyBVaW50OEFycmF5KFsweDAxLCAvLyB2ZXJzaW9uXG4gICAgc3BzWzNdLCAvLyBwcm9maWxlXG4gICAgc3BzWzRdLCAvLyBwcm9maWxlIGNvbXBhdFxuICAgIHNwc1s1XSwgLy8gbGV2ZWxcbiAgICAweGZjIHwgMywgLy8gbGVuZ3RoU2l6ZU1pbnVzT25lLCBoYXJkLWNvZGVkIHRvIDQgYnl0ZXNcbiAgICAweEUwIHwgdHJhY2suc3BzLmxlbmd0aCAvLyAzYml0IHJlc2VydmVkICgxMTEpICsgbnVtT2ZTZXF1ZW5jZVBhcmFtZXRlclNldHNcbiAgICBdLmNvbmNhdChzcHMpLmNvbmNhdChbdHJhY2sucHBzLmxlbmd0aCAvLyBudW1PZlBpY3R1cmVQYXJhbWV0ZXJTZXRzXG4gICAgXSkuY29uY2F0KHBwcykpKSxcbiAgICAgICAgLy8gXCJQUFNcIlxuICAgIHdpZHRoID0gdHJhY2sud2lkdGgsXG4gICAgICAgIGhlaWdodCA9IHRyYWNrLmhlaWdodCxcbiAgICAgICAgaFNwYWNpbmcgPSB0cmFjay5waXhlbFJhdGlvWzBdLFxuICAgICAgICB2U3BhY2luZyA9IHRyYWNrLnBpeGVsUmF0aW9bMV07XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLmF2YzEsIG5ldyBVaW50OEFycmF5KFsweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSwgLy8gZGF0YV9yZWZlcmVuY2VfaW5kZXhcbiAgICAweDAwLCAweDAwLCAvLyBwcmVfZGVmaW5lZFxuICAgIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcHJlX2RlZmluZWRcbiAgICB3aWR0aCA+PiA4ICYgMHhGRiwgd2lkdGggJiAweGZmLCAvLyB3aWR0aFxuICAgIGhlaWdodCA+PiA4ICYgMHhGRiwgaGVpZ2h0ICYgMHhmZiwgLy8gaGVpZ2h0XG4gICAgMHgwMCwgMHg0OCwgMHgwMCwgMHgwMCwgLy8gaG9yaXpyZXNvbHV0aW9uXG4gICAgMHgwMCwgMHg0OCwgMHgwMCwgMHgwMCwgLy8gdmVydHJlc29sdXRpb25cbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDEsIC8vIGZyYW1lX2NvdW50XG4gICAgMHgxMiwgMHg2NCwgMHg2MSwgMHg2OSwgMHg2QywgLy8gZGFpbHltb3Rpb24vaGxzLmpzXG4gICAgMHg3OSwgMHg2RCwgMHg2RiwgMHg3NCwgMHg2OSwgMHg2RiwgMHg2RSwgMHgyRiwgMHg2OCwgMHg2QywgMHg3MywgMHgyRSwgMHg2QSwgMHg3MywgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gY29tcHJlc3Nvcm5hbWVcbiAgICAweDAwLCAweDE4LCAvLyBkZXB0aCA9IDI0XG4gICAgMHgxMSwgMHgxMV0pLCAvLyBwcmVfZGVmaW5lZCA9IC0xXG4gICAgYXZjYywgTVA0LmJveChNUDQudHlwZXMuYnRydCwgbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4MWMsIDB4OWMsIDB4ODAsIC8vIGJ1ZmZlclNpemVEQlxuICAgIDB4MDAsIDB4MmQsIDB4YzYsIDB4YzAsIC8vIG1heEJpdHJhdGVcbiAgICAweDAwLCAweDJkLCAweGM2LCAweGMwXSkpLCAvLyBhdmdCaXRyYXRlXG4gICAgTVA0LmJveChNUDQudHlwZXMucGFzcCwgbmV3IFVpbnQ4QXJyYXkoW2hTcGFjaW5nID4+IDI0LCAvLyBoU3BhY2luZ1xuICAgIGhTcGFjaW5nID4+IDE2ICYgMHhGRiwgaFNwYWNpbmcgPj4gOCAmIDB4RkYsIGhTcGFjaW5nICYgMHhGRiwgdlNwYWNpbmcgPj4gMjQsIC8vIHZTcGFjaW5nXG4gICAgdlNwYWNpbmcgPj4gMTYgJiAweEZGLCB2U3BhY2luZyA+PiA4ICYgMHhGRiwgdlNwYWNpbmcgJiAweEZGXSkpKTtcbiAgfTtcblxuICBNUDQuZXNkcyA9IGZ1bmN0aW9uIGVzZHModHJhY2spIHtcbiAgICB2YXIgY29uZmlnbGVuID0gdHJhY2suY29uZmlnLmxlbmd0aDtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb24gMFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGZsYWdzXG4gICAgMHgwMywgLy8gZGVzY3JpcHRvcl90eXBlXG4gICAgMHgxNyArIGNvbmZpZ2xlbiwgLy8gbGVuZ3RoXG4gICAgMHgwMCwgMHgwMSwgLy8gZXNfaWRcbiAgICAweDAwLCAvLyBzdHJlYW1fcHJpb3JpdHlcbiAgICAweDA0LCAvLyBkZXNjcmlwdG9yX3R5cGVcbiAgICAweDBmICsgY29uZmlnbGVuLCAvLyBsZW5ndGhcbiAgICAweDQwLCAvLyBjb2RlYyA6IG1wZWc0X2F1ZGlvXG4gICAgMHgxNSwgLy8gc3RyZWFtX3R5cGVcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBidWZmZXJfc2l6ZVxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIG1heEJpdHJhdGVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAvLyBhdmdCaXRyYXRlXG4gICAgMHgwNSAvLyBkZXNjcmlwdG9yX3R5cGVcbiAgICBdLmNvbmNhdChbY29uZmlnbGVuXSkuY29uY2F0KHRyYWNrLmNvbmZpZykuY29uY2F0KFsweDA2LCAweDAxLCAweDAyXSkpOyAvLyBHQVNwZWNpZmljQ29uZmlnKSk7IC8vIGxlbmd0aCArIGF1ZGlvIGNvbmZpZyBkZXNjcmlwdG9yXG4gIH07XG5cbiAgTVA0Lm1wNGEgPSBmdW5jdGlvbiBtcDRhKHRyYWNrKSB7XG4gICAgdmFyIHNhbXBsZXJhdGUgPSB0cmFjay5zYW1wbGVyYXRlO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tcDRhLCBuZXcgVWludDhBcnJheShbMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDEsIC8vIGRhdGFfcmVmZXJlbmNlX2luZGV4XG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCB0cmFjay5jaGFubmVsQ291bnQsIC8vIGNoYW5uZWxjb3VudFxuICAgIDB4MDAsIDB4MTAsIC8vIHNhbXBsZVNpemU6MTZiaXRzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWQyXG4gICAgc2FtcGxlcmF0ZSA+PiA4ICYgMHhGRiwgc2FtcGxlcmF0ZSAmIDB4ZmYsIC8vXG4gICAgMHgwMCwgMHgwMF0pLCBNUDQuYm94KE1QNC50eXBlcy5lc2RzLCBNUDQuZXNkcyh0cmFjaykpKTtcbiAgfTtcblxuICBNUDQubXAzID0gZnVuY3Rpb24gbXAzKHRyYWNrKSB7XG4gICAgdmFyIHNhbXBsZXJhdGUgPSB0cmFjay5zYW1wbGVyYXRlO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlc1snLm1wMyddLCBuZXcgVWludDhBcnJheShbMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDEsIC8vIGRhdGFfcmVmZXJlbmNlX2luZGV4XG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCB0cmFjay5jaGFubmVsQ291bnQsIC8vIGNoYW5uZWxjb3VudFxuICAgIDB4MDAsIDB4MTAsIC8vIHNhbXBsZVNpemU6MTZiaXRzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWQyXG4gICAgc2FtcGxlcmF0ZSA+PiA4ICYgMHhGRiwgc2FtcGxlcmF0ZSAmIDB4ZmYsIC8vXG4gICAgMHgwMCwgMHgwMF0pKTtcbiAgfTtcblxuICBNUDQuc3RzZCA9IGZ1bmN0aW9uIHN0c2QodHJhY2spIHtcbiAgICBpZiAodHJhY2sudHlwZSA9PT0gJ2F1ZGlvJykge1xuICAgICAgaWYgKCF0cmFjay5pc0FBQyAmJiB0cmFjay5jb2RlYyA9PT0gJ21wMycpIHtcbiAgICAgICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnN0c2QsIE1QNC5TVFNELCBNUDQubXAzKHRyYWNrKSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5zdHNkLCBNUDQuU1RTRCwgTVA0Lm1wNGEodHJhY2spKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnN0c2QsIE1QNC5TVFNELCBNUDQuYXZjMSh0cmFjaykpO1xuICAgIH1cbiAgfTtcblxuICBNUDQudGtoZCA9IGZ1bmN0aW9uIHRraGQodHJhY2spIHtcbiAgICB2YXIgaWQgPSB0cmFjay5pZCxcbiAgICAgICAgZHVyYXRpb24gPSB0cmFjay5kdXJhdGlvbiAqIHRyYWNrLnRpbWVzY2FsZSxcbiAgICAgICAgd2lkdGggPSB0cmFjay53aWR0aCxcbiAgICAgICAgaGVpZ2h0ID0gdHJhY2suaGVpZ2h0LFxuICAgICAgICB1cHBlcldvcmREdXJhdGlvbiA9IE1hdGguZmxvb3IoZHVyYXRpb24gLyAoVUlOVDMyX01BWCArIDEpKSxcbiAgICAgICAgbG93ZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uICUgKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnRraGQsIG5ldyBVaW50OEFycmF5KFsweDAxLCAvLyB2ZXJzaW9uIDFcbiAgICAweDAwLCAweDAwLCAweDA3LCAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDIsIC8vIGNyZWF0aW9uX3RpbWVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAzLCAvLyBtb2RpZmljYXRpb25fdGltZVxuICAgIGlkID4+IDI0ICYgMHhGRiwgaWQgPj4gMTYgJiAweEZGLCBpZCA+PiA4ICYgMHhGRiwgaWQgJiAweEZGLCAvLyB0cmFja19JRFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHJlc2VydmVkXG4gICAgdXBwZXJXb3JkRHVyYXRpb24gPj4gMjQsIHVwcGVyV29yZER1cmF0aW9uID4+IDE2ICYgMHhGRiwgdXBwZXJXb3JkRHVyYXRpb24gPj4gOCAmIDB4RkYsIHVwcGVyV29yZER1cmF0aW9uICYgMHhGRiwgbG93ZXJXb3JkRHVyYXRpb24gPj4gMjQsIGxvd2VyV29yZER1cmF0aW9uID4+IDE2ICYgMHhGRiwgbG93ZXJXb3JkRHVyYXRpb24gPj4gOCAmIDB4RkYsIGxvd2VyV29yZER1cmF0aW9uICYgMHhGRiwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAvLyBsYXllclxuICAgIDB4MDAsIDB4MDAsIC8vIGFsdGVybmF0ZV9ncm91cFxuICAgIDB4MDAsIDB4MDAsIC8vIG5vbi1hdWRpbyB0cmFjayB2b2x1bWVcbiAgICAweDAwLCAweDAwLCAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDEsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDEsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4NDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIHRyYW5zZm9ybWF0aW9uOiB1bml0eSBtYXRyaXhcbiAgICB3aWR0aCA+PiA4ICYgMHhGRiwgd2lkdGggJiAweEZGLCAweDAwLCAweDAwLCAvLyB3aWR0aFxuICAgIGhlaWdodCA+PiA4ICYgMHhGRiwgaGVpZ2h0ICYgMHhGRiwgMHgwMCwgMHgwMCAvLyBoZWlnaHRcbiAgICBdKSk7XG4gIH07XG5cbiAgTVA0LnRyYWYgPSBmdW5jdGlvbiB0cmFmKHRyYWNrLCBiYXNlTWVkaWFEZWNvZGVUaW1lKSB7XG4gICAgdmFyIHNhbXBsZURlcGVuZGVuY3lUYWJsZSA9IE1QNC5zZHRwKHRyYWNrKSxcbiAgICAgICAgaWQgPSB0cmFjay5pZCxcbiAgICAgICAgdXBwZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA9IE1hdGguZmxvb3IoYmFzZU1lZGlhRGVjb2RlVGltZSAvIChVSU5UMzJfTUFYICsgMSkpLFxuICAgICAgICBsb3dlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lID0gTWF0aC5mbG9vcihiYXNlTWVkaWFEZWNvZGVUaW1lICUgKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnRyYWYsIE1QNC5ib3goTVA0LnR5cGVzLnRmaGQsIG5ldyBVaW50OEFycmF5KFsweDAwLCAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBmbGFnc1xuICAgIGlkID4+IDI0LCBpZCA+PiAxNiAmIDBYRkYsIGlkID4+IDggJiAwWEZGLCBpZCAmIDB4RkYgLy8gdHJhY2tfSURcbiAgICBdKSksIE1QNC5ib3goTVA0LnR5cGVzLnRmZHQsIG5ldyBVaW50OEFycmF5KFsweDAxLCAvLyB2ZXJzaW9uIDFcbiAgICAweDAwLCAweDAwLCAweDAwLCAvLyBmbGFnc1xuICAgIHVwcGVyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gMjQsIHVwcGVyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gMTYgJiAwWEZGLCB1cHBlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lID4+IDggJiAwWEZGLCB1cHBlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lICYgMHhGRiwgbG93ZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA+PiAyNCwgbG93ZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA+PiAxNiAmIDBYRkYsIGxvd2VyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gOCAmIDBYRkYsIGxvd2VyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgJiAweEZGXSkpLCBNUDQudHJ1bih0cmFjaywgc2FtcGxlRGVwZW5kZW5jeVRhYmxlLmxlbmd0aCArIDE2ICsgLy8gdGZoZFxuICAgIDIwICsgLy8gdGZkdFxuICAgIDggKyAvLyB0cmFmIGhlYWRlclxuICAgIDE2ICsgLy8gbWZoZFxuICAgIDggKyAvLyBtb29mIGhlYWRlclxuICAgIDgpLCAvLyBtZGF0IGhlYWRlclxuICAgIHNhbXBsZURlcGVuZGVuY3lUYWJsZSk7XG4gIH1cbiAgLyoqXG4gICAqIEdlbmVyYXRlIGEgdHJhY2sgYm94LlxuICAgKiBAcGFyYW0gdHJhY2sge29iamVjdH0gYSB0cmFjayBkZWZpbml0aW9uXG4gICAqIEByZXR1cm4ge1VpbnQ4QXJyYXl9IHRoZSB0cmFjayBib3hcbiAgICovXG4gIDtcblxuICBNUDQudHJhayA9IGZ1bmN0aW9uIHRyYWsodHJhY2spIHtcbiAgICB0cmFjay5kdXJhdGlvbiA9IHRyYWNrLmR1cmF0aW9uIHx8IDB4ZmZmZmZmZmY7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnRyYWssIE1QNC50a2hkKHRyYWNrKSwgTVA0Lm1kaWEodHJhY2spKTtcbiAgfTtcblxuICBNUDQudHJleCA9IGZ1bmN0aW9uIHRyZXgodHJhY2spIHtcbiAgICB2YXIgaWQgPSB0cmFjay5pZDtcbiAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMudHJleCwgbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIC8vIHZlcnNpb24gMFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGZsYWdzXG4gICAgaWQgPj4gMjQsIGlkID4+IDE2ICYgMFhGRiwgaWQgPj4gOCAmIDBYRkYsIGlkICYgMHhGRiwgLy8gdHJhY2tfSURcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAxLCAvLyBkZWZhdWx0X3NhbXBsZV9kZXNjcmlwdGlvbl9pbmRleFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIC8vIGRlZmF1bHRfc2FtcGxlX2R1cmF0aW9uXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgLy8gZGVmYXVsdF9zYW1wbGVfc2l6ZVxuICAgIDB4MDAsIDB4MDEsIDB4MDAsIDB4MDEgLy8gZGVmYXVsdF9zYW1wbGVfZmxhZ3NcbiAgICBdKSk7XG4gIH07XG5cbiAgTVA0LnRydW4gPSBmdW5jdGlvbiB0cnVuKHRyYWNrLCBvZmZzZXQpIHtcbiAgICB2YXIgc2FtcGxlcyA9IHRyYWNrLnNhbXBsZXMgfHwgW10sXG4gICAgICAgIGxlbiA9IHNhbXBsZXMubGVuZ3RoLFxuICAgICAgICBhcnJheWxlbiA9IDEyICsgMTYgKiBsZW4sXG4gICAgICAgIGFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXlsZW4pLFxuICAgICAgICBpLFxuICAgICAgICBzYW1wbGUsXG4gICAgICAgIGR1cmF0aW9uLFxuICAgICAgICBzaXplLFxuICAgICAgICBmbGFncyxcbiAgICAgICAgY3RzO1xuICAgIG9mZnNldCArPSA4ICsgYXJyYXlsZW47XG4gICAgYXJyYXkuc2V0KFsweDAwLCAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDBmLCAweDAxLCAvLyBmbGFnc1xuICAgIGxlbiA+Pj4gMjQgJiAweEZGLCBsZW4gPj4+IDE2ICYgMHhGRiwgbGVuID4+PiA4ICYgMHhGRiwgbGVuICYgMHhGRiwgLy8gc2FtcGxlX2NvdW50XG4gICAgb2Zmc2V0ID4+PiAyNCAmIDB4RkYsIG9mZnNldCA+Pj4gMTYgJiAweEZGLCBvZmZzZXQgPj4+IDggJiAweEZGLCBvZmZzZXQgJiAweEZGIC8vIGRhdGFfb2Zmc2V0XG4gICAgXSwgMCk7XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIHNhbXBsZSA9IHNhbXBsZXNbaV07XG4gICAgICBkdXJhdGlvbiA9IHNhbXBsZS5kdXJhdGlvbjtcbiAgICAgIHNpemUgPSBzYW1wbGUuc2l6ZTtcbiAgICAgIGZsYWdzID0gc2FtcGxlLmZsYWdzO1xuICAgICAgY3RzID0gc2FtcGxlLmN0cztcbiAgICAgIGFycmF5LnNldChbZHVyYXRpb24gPj4+IDI0ICYgMHhGRiwgZHVyYXRpb24gPj4+IDE2ICYgMHhGRiwgZHVyYXRpb24gPj4+IDggJiAweEZGLCBkdXJhdGlvbiAmIDB4RkYsIC8vIHNhbXBsZV9kdXJhdGlvblxuICAgICAgc2l6ZSA+Pj4gMjQgJiAweEZGLCBzaXplID4+PiAxNiAmIDB4RkYsIHNpemUgPj4+IDggJiAweEZGLCBzaXplICYgMHhGRiwgLy8gc2FtcGxlX3NpemVcbiAgICAgIGZsYWdzLmlzTGVhZGluZyA8PCAyIHwgZmxhZ3MuZGVwZW5kc09uLCBmbGFncy5pc0RlcGVuZGVkT24gPDwgNiB8IGZsYWdzLmhhc1JlZHVuZGFuY3kgPDwgNCB8IGZsYWdzLnBhZGRpbmdWYWx1ZSA8PCAxIHwgZmxhZ3MuaXNOb25TeW5jLCBmbGFncy5kZWdyYWRQcmlvICYgMHhGMCA8PCA4LCBmbGFncy5kZWdyYWRQcmlvICYgMHgwRiwgLy8gc2FtcGxlX2ZsYWdzXG4gICAgICBjdHMgPj4+IDI0ICYgMHhGRiwgY3RzID4+PiAxNiAmIDB4RkYsIGN0cyA+Pj4gOCAmIDB4RkYsIGN0cyAmIDB4RkYgLy8gc2FtcGxlX2NvbXBvc2l0aW9uX3RpbWVfb2Zmc2V0XG4gICAgICBdLCAxMiArIDE2ICogaSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnRydW4sIGFycmF5KTtcbiAgfTtcblxuICBNUDQuaW5pdFNlZ21lbnQgPSBmdW5jdGlvbiBpbml0U2VnbWVudCh0cmFja3MpIHtcbiAgICBpZiAoIU1QNC50eXBlcykge1xuICAgICAgTVA0LmluaXQoKTtcbiAgICB9XG5cbiAgICB2YXIgbW92aWUgPSBNUDQubW9vdih0cmFja3MpLFxuICAgICAgICByZXN1bHQ7XG4gICAgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkoTVA0LkZUWVAuYnl0ZUxlbmd0aCArIG1vdmllLmJ5dGVMZW5ndGgpO1xuICAgIHJlc3VsdC5zZXQoTVA0LkZUWVApO1xuICAgIHJlc3VsdC5zZXQobW92aWUsIE1QNC5GVFlQLmJ5dGVMZW5ndGgpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG5cbiAgcmV0dXJuIE1QNDtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgbXA0X2dlbmVyYXRvciA9IChNUDQpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvdGltZXNjYWxlLWNvbnZlcnNpb24udHNcbnZhciBNUEVHX1RTX0NMT0NLX0ZSRVFfSFogPSA5MDAwMDtcbmZ1bmN0aW9uIHRvVGltZXNjYWxlRnJvbVNjYWxlKHZhbHVlLCBkZXN0U2NhbGUsIHNyY1NjYWxlLCByb3VuZCkge1xuICBpZiAoc3JjU2NhbGUgPT09IHZvaWQgMCkge1xuICAgIHNyY1NjYWxlID0gMTtcbiAgfVxuXG4gIGlmIChyb3VuZCA9PT0gdm9pZCAwKSB7XG4gICAgcm91bmQgPSBmYWxzZTtcbiAgfVxuXG4gIHJldHVybiB0b1RpbWVzY2FsZUZyb21CYXNlKHZhbHVlLCBkZXN0U2NhbGUsIDEgLyBzcmNTY2FsZSk7XG59XG5mdW5jdGlvbiB0b1RpbWVzY2FsZUZyb21CYXNlKHZhbHVlLCBkZXN0U2NhbGUsIHNyY0Jhc2UsIHJvdW5kKSB7XG4gIGlmIChzcmNCYXNlID09PSB2b2lkIDApIHtcbiAgICBzcmNCYXNlID0gMTtcbiAgfVxuXG4gIGlmIChyb3VuZCA9PT0gdm9pZCAwKSB7XG4gICAgcm91bmQgPSBmYWxzZTtcbiAgfVxuXG4gIHZhciByZXN1bHQgPSB2YWx1ZSAqIGRlc3RTY2FsZSAqIHNyY0Jhc2U7IC8vIGVxdWl2YWxlbnQgdG8gYCh2YWx1ZSAqIHNjYWxlKSAvICgxIC8gYmFzZSlgXG5cbiAgcmV0dXJuIHJvdW5kID8gTWF0aC5yb3VuZChyZXN1bHQpIDogcmVzdWx0O1xufVxuZnVuY3Rpb24gdG9Nc0Zyb21NcGVnVHNDbG9jayh2YWx1ZSwgcm91bmQpIHtcbiAgaWYgKHJvdW5kID09PSB2b2lkIDApIHtcbiAgICByb3VuZCA9IGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIHRvVGltZXNjYWxlRnJvbUJhc2UodmFsdWUsIDEwMDAsIDEgLyBNUEVHX1RTX0NMT0NLX0ZSRVFfSFosIHJvdW5kKTtcbn1cbmZ1bmN0aW9uIHRvTXBlZ1RzQ2xvY2tGcm9tVGltZXNjYWxlKHZhbHVlLCBzcmNTY2FsZSkge1xuICBpZiAoc3JjU2NhbGUgPT09IHZvaWQgMCkge1xuICAgIHNyY1NjYWxlID0gMTtcbiAgfVxuXG4gIHJldHVybiB0b1RpbWVzY2FsZUZyb21CYXNlKHZhbHVlLCBNUEVHX1RTX0NMT0NLX0ZSRVFfSFosIDEgLyBzcmNTY2FsZSk7XG59XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9yZW11eC9tcDQtcmVtdXhlci5qc1xuLyoqXG4gKiBmTVA0IHJlbXV4ZXJcbiovXG5cblxuXG5cblxuXG52YXIgTUFYX1NJTEVOVF9GUkFNRV9EVVJBVElPTl85MEtIWiA9IHRvTXBlZ1RzQ2xvY2tGcm9tVGltZXNjYWxlKDEwKTtcbnZhciBQVFNfRFRTX1NISUZUX1RPTEVSQU5DRV85MEtIWiA9IHRvTXBlZ1RzQ2xvY2tGcm9tVGltZXNjYWxlKDAuMik7XG52YXIgY2hyb21lVmVyc2lvbiA9IG51bGw7XG5cbnZhciBtcDRfcmVtdXhlcl9NUDRSZW11eGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gTVA0UmVtdXhlcihvYnNlcnZlciwgY29uZmlnLCB0eXBlU3VwcG9ydGVkLCB2ZW5kb3IpIHtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy50eXBlU3VwcG9ydGVkID0gdHlwZVN1cHBvcnRlZDtcbiAgICB0aGlzLklTR2VuZXJhdGVkID0gZmFsc2U7XG5cbiAgICBpZiAoY2hyb21lVmVyc2lvbiA9PT0gbnVsbCkge1xuICAgICAgdmFyIHJlc3VsdCA9IG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0Nocm9tZVxcLyhcXGQrKS9pKTtcbiAgICAgIGNocm9tZVZlcnNpb24gPSByZXN1bHQgPyBwYXJzZUludChyZXN1bHRbMV0pIDogMDtcbiAgICB9XG4gIH1cblxuICB2YXIgX3Byb3RvID0gTVA0UmVtdXhlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge307XG5cbiAgX3Byb3RvLnJlc2V0VGltZVN0YW1wID0gZnVuY3Rpb24gcmVzZXRUaW1lU3RhbXAoZGVmYXVsdFRpbWVTdGFtcCkge1xuICAgIHRoaXMuX2luaXRQVFMgPSB0aGlzLl9pbml0RFRTID0gZGVmYXVsdFRpbWVTdGFtcDtcbiAgfTtcblxuICBfcHJvdG8ucmVzZXRJbml0U2VnbWVudCA9IGZ1bmN0aW9uIHJlc2V0SW5pdFNlZ21lbnQoKSB7XG4gICAgdGhpcy5JU0dlbmVyYXRlZCA9IGZhbHNlO1xuICB9O1xuXG4gIF9wcm90by5nZXRWaWRlb1N0YXJ0UHRzID0gZnVuY3Rpb24gZ2V0VmlkZW9TdGFydFB0cyh2aWRlb1NhbXBsZXMpIHtcbiAgICB2YXIgcm9sbG92ZXJEZXRlY3RlZCA9IGZhbHNlO1xuICAgIHZhciBzdGFydFBUUyA9IHZpZGVvU2FtcGxlcy5yZWR1Y2UoZnVuY3Rpb24gKG1pblBUUywgc2FtcGxlKSB7XG4gICAgICB2YXIgZGVsdGEgPSBzYW1wbGUucHRzIC0gbWluUFRTO1xuXG4gICAgICBpZiAoZGVsdGEgPCAtNDI5NDk2NzI5Nikge1xuICAgICAgICAvLyAyXjMyLCBzZWUgUFRTTm9ybWFsaXplIGZvciByZWFzb25pbmcsIGJ1dCB3ZSdyZSBoaXR0aW5nIGEgcm9sbG92ZXIgaGVyZSwgYW5kIHdlIGRvbid0IHdhbnQgdGhhdCB0byBpbXBhY3QgdGhlIHRpbWVPZmZzZXQgY2FsY3VsYXRpb25cbiAgICAgICAgcm9sbG92ZXJEZXRlY3RlZCA9IHRydWU7XG4gICAgICAgIHJldHVybiBQVFNOb3JtYWxpemUobWluUFRTLCBzYW1wbGUucHRzKTtcbiAgICAgIH0gZWxzZSBpZiAoZGVsdGEgPiAwKSB7XG4gICAgICAgIHJldHVybiBtaW5QVFM7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gc2FtcGxlLnB0cztcbiAgICAgIH1cbiAgICB9LCB2aWRlb1NhbXBsZXNbMF0ucHRzKTtcblxuICAgIGlmIChyb2xsb3ZlckRldGVjdGVkKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZGVidWcoJ1BUUyByb2xsb3ZlciBkZXRlY3RlZCcpO1xuICAgIH1cblxuICAgIHJldHVybiBzdGFydFBUUztcbiAgfTtcblxuICBfcHJvdG8ucmVtdXggPSBmdW5jdGlvbiByZW11eChhdWRpb1RyYWNrLCB2aWRlb1RyYWNrLCBpZDNUcmFjaywgdGV4dFRyYWNrLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICAvLyBnZW5lcmF0ZSBJbml0IFNlZ21lbnQgaWYgbmVlZGVkXG4gICAgaWYgKCF0aGlzLklTR2VuZXJhdGVkKSB7XG4gICAgICB0aGlzLmdlbmVyYXRlSVMoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgdGltZU9mZnNldCk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuSVNHZW5lcmF0ZWQpIHtcbiAgICAgIHZhciBuYkF1ZGlvU2FtcGxlcyA9IGF1ZGlvVHJhY2suc2FtcGxlcy5sZW5ndGg7XG4gICAgICB2YXIgbmJWaWRlb1NhbXBsZXMgPSB2aWRlb1RyYWNrLnNhbXBsZXMubGVuZ3RoO1xuICAgICAgdmFyIGF1ZGlvVGltZU9mZnNldCA9IHRpbWVPZmZzZXQ7XG4gICAgICB2YXIgdmlkZW9UaW1lT2Zmc2V0ID0gdGltZU9mZnNldDtcblxuICAgICAgaWYgKG5iQXVkaW9TYW1wbGVzICYmIG5iVmlkZW9TYW1wbGVzKSB7XG4gICAgICAgIC8vIHRpbWVPZmZzZXQgaXMgZXhwZWN0ZWQgdG8gYmUgdGhlIG9mZnNldCBvZiB0aGUgZmlyc3QgdGltZXN0YW1wIG9mIHRoaXMgZnJhZ21lbnQgKGZpcnN0IERUUylcbiAgICAgICAgLy8gaWYgZmlyc3QgYXVkaW8gRFRTIGlzIG5vdCBhbGlnbmVkIHdpdGggZmlyc3QgdmlkZW8gRFRTIHRoZW4gd2UgbmVlZCB0byB0YWtlIHRoYXQgaW50byBhY2NvdW50XG4gICAgICAgIC8vIHdoZW4gcHJvdmlkaW5nIHRpbWVPZmZzZXQgdG8gcmVtdXhBdWRpbyAvIHJlbXV4VmlkZW8uIGlmIHdlIGRvbid0IGRvIHRoYXQsIHRoZXJlIG1pZ2h0IGJlIGEgcGVybWFuZW50IC8gc21hbGxcbiAgICAgICAgLy8gZHJpZnQgYmV0d2VlbiBhdWRpbyBhbmQgdmlkZW8gc3RyZWFtc1xuICAgICAgICB2YXIgc3RhcnRQVFMgPSB0aGlzLmdldFZpZGVvU3RhcnRQdHModmlkZW9UcmFjay5zYW1wbGVzKTtcbiAgICAgICAgdmFyIHRzRGVsdGEgPSBQVFNOb3JtYWxpemUoYXVkaW9UcmFjay5zYW1wbGVzWzBdLnB0cywgc3RhcnRQVFMpIC0gc3RhcnRQVFM7XG4gICAgICAgIHZhciBhdWRpb3ZpZGVvVGltZXN0YW1wRGVsdGEgPSB0c0RlbHRhIC8gdmlkZW9UcmFjay5pbnB1dFRpbWVTY2FsZTtcbiAgICAgICAgYXVkaW9UaW1lT2Zmc2V0ICs9IE1hdGgubWF4KDAsIGF1ZGlvdmlkZW9UaW1lc3RhbXBEZWx0YSk7XG4gICAgICAgIHZpZGVvVGltZU9mZnNldCArPSBNYXRoLm1heCgwLCAtYXVkaW92aWRlb1RpbWVzdGFtcERlbHRhKTtcbiAgICAgIH0gLy8gUHVycG9zZWZ1bGx5IHJlbXV4aW5nIGF1ZGlvIGJlZm9yZSB2aWRlbywgc28gdGhhdCByZW11eFZpZGVvIGNhbiB1c2UgbmV4dEF1ZGlvUHRzLCB3aGljaCBpc1xuICAgICAgLy8gY2FsY3VsYXRlZCBpbiByZW11eEF1ZGlvLlxuICAgICAgLy8gbG9nZ2VyLmxvZygnbmIgQUFDIHNhbXBsZXM6JyArIGF1ZGlvVHJhY2suc2FtcGxlcy5sZW5ndGgpO1xuXG5cbiAgICAgIGlmIChuYkF1ZGlvU2FtcGxlcykge1xuICAgICAgICAvLyBpZiBpbml0U2VnbWVudCB3YXMgZ2VuZXJhdGVkIHdpdGhvdXQgdmlkZW8gc2FtcGxlcywgcmVnZW5lcmF0ZSBpdCBhZ2FpblxuICAgICAgICBpZiAoIWF1ZGlvVHJhY2sudGltZXNjYWxlKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ3JlZ2VuZXJhdGUgSW5pdFNlZ21lbnQgYXMgYXVkaW8gZGV0ZWN0ZWQnKTtcbiAgICAgICAgICB0aGlzLmdlbmVyYXRlSVMoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgdGltZU9mZnNldCk7XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgYXVkaW9EYXRhID0gdGhpcy5yZW11eEF1ZGlvKGF1ZGlvVHJhY2ssIGF1ZGlvVGltZU9mZnNldCwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0KTsgLy8gbG9nZ2VyLmxvZygnbmIgQVZDIHNhbXBsZXM6JyArIHZpZGVvVHJhY2suc2FtcGxlcy5sZW5ndGgpO1xuXG4gICAgICAgIGlmIChuYlZpZGVvU2FtcGxlcykge1xuICAgICAgICAgIHZhciBhdWRpb1RyYWNrTGVuZ3RoO1xuXG4gICAgICAgICAgaWYgKGF1ZGlvRGF0YSkge1xuICAgICAgICAgICAgYXVkaW9UcmFja0xlbmd0aCA9IGF1ZGlvRGF0YS5lbmRQVFMgLSBhdWRpb0RhdGEuc3RhcnRQVFM7XG4gICAgICAgICAgfSAvLyBpZiBpbml0U2VnbWVudCB3YXMgZ2VuZXJhdGVkIHdpdGhvdXQgdmlkZW8gc2FtcGxlcywgcmVnZW5lcmF0ZSBpdCBhZ2FpblxuXG5cbiAgICAgICAgICBpZiAoIXZpZGVvVHJhY2sudGltZXNjYWxlKSB7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybigncmVnZW5lcmF0ZSBJbml0U2VnbWVudCBhcyB2aWRlbyBkZXRlY3RlZCcpO1xuICAgICAgICAgICAgdGhpcy5nZW5lcmF0ZUlTKGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIHRpbWVPZmZzZXQpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMucmVtdXhWaWRlbyh2aWRlb1RyYWNrLCB2aWRlb1RpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGF1ZGlvVHJhY2tMZW5ndGgpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBsb2dnZXIubG9nKCduYiBBVkMgc2FtcGxlczonICsgdmlkZW9UcmFjay5zYW1wbGVzLmxlbmd0aCk7XG4gICAgICAgIGlmIChuYlZpZGVvU2FtcGxlcykge1xuICAgICAgICAgIHZhciB2aWRlb0RhdGEgPSB0aGlzLnJlbXV4VmlkZW8odmlkZW9UcmFjaywgdmlkZW9UaW1lT2Zmc2V0LCBjb250aWd1b3VzLCAwLCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuXG4gICAgICAgICAgaWYgKHZpZGVvRGF0YSAmJiBhdWRpb1RyYWNrLmNvZGVjKSB7XG4gICAgICAgICAgICB0aGlzLnJlbXV4RW1wdHlBdWRpbyhhdWRpb1RyYWNrLCBhdWRpb1RpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIHZpZGVvRGF0YSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSAvLyBsb2dnZXIubG9nKCduYiBJRDMgc2FtcGxlczonICsgYXVkaW9UcmFjay5zYW1wbGVzLmxlbmd0aCk7XG5cblxuICAgIGlmIChpZDNUcmFjay5zYW1wbGVzLmxlbmd0aCkge1xuICAgICAgdGhpcy5yZW11eElEMyhpZDNUcmFjaywgdGltZU9mZnNldCk7XG4gICAgfSAvLyBsb2dnZXIubG9nKCduYiBJRDMgc2FtcGxlczonICsgYXVkaW9UcmFjay5zYW1wbGVzLmxlbmd0aCk7XG5cblxuICAgIGlmICh0ZXh0VHJhY2suc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIHRoaXMucmVtdXhUZXh0KHRleHRUcmFjaywgdGltZU9mZnNldCk7XG4gICAgfSAvLyBub3RpZnkgZW5kIG9mIHBhcnNpbmdcblxuXG4gICAgdGhpcy5vYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTRUQpO1xuICB9O1xuXG4gIF9wcm90by5nZW5lcmF0ZUlTID0gZnVuY3Rpb24gZ2VuZXJhdGVJUyhhdWRpb1RyYWNrLCB2aWRlb1RyYWNrLCB0aW1lT2Zmc2V0KSB7XG4gICAgdmFyIG9ic2VydmVyID0gdGhpcy5vYnNlcnZlcixcbiAgICAgICAgYXVkaW9TYW1wbGVzID0gYXVkaW9UcmFjay5zYW1wbGVzLFxuICAgICAgICB2aWRlb1NhbXBsZXMgPSB2aWRlb1RyYWNrLnNhbXBsZXMsXG4gICAgICAgIHR5cGVTdXBwb3J0ZWQgPSB0aGlzLnR5cGVTdXBwb3J0ZWQsXG4gICAgICAgIGNvbnRhaW5lciA9ICdhdWRpby9tcDQnLFxuICAgICAgICB0cmFja3MgPSB7fSxcbiAgICAgICAgZGF0YSA9IHtcbiAgICAgIHRyYWNrczogdHJhY2tzXG4gICAgfSxcbiAgICAgICAgY29tcHV0ZVBUU0RUUyA9IHRoaXMuX2luaXRQVFMgPT09IHVuZGVmaW5lZCxcbiAgICAgICAgaW5pdFBUUyxcbiAgICAgICAgaW5pdERUUztcblxuICAgIGlmIChjb21wdXRlUFRTRFRTKSB7XG4gICAgICBpbml0UFRTID0gaW5pdERUUyA9IEluZmluaXR5O1xuICAgIH1cblxuICAgIGlmIChhdWRpb1RyYWNrLmNvbmZpZyAmJiBhdWRpb1NhbXBsZXMubGVuZ3RoKSB7XG4gICAgICAvLyBsZXQncyB1c2UgYXVkaW8gc2FtcGxpbmcgcmF0ZSBhcyBNUDQgdGltZSBzY2FsZS5cbiAgICAgIC8vIHJhdGlvbmFsZSBpcyB0aGF0IHRoZXJlIGlzIGEgaW50ZWdlciBuYiBvZiBhdWRpbyBmcmFtZXMgcGVyIGF1ZGlvIHNhbXBsZSAoMTAyNCBmb3IgQUFDKVxuICAgICAgLy8gdXNpbmcgYXVkaW8gc2FtcGxpbmcgcmF0ZSBoZXJlIGhlbHBzIGhhdmluZyBhbiBpbnRlZ2VyIE1QNCBmcmFtZSBkdXJhdGlvblxuICAgICAgLy8gdGhpcyBhdm9pZHMgcG90ZW50aWFsIHJvdW5kaW5nIGlzc3VlIGFuZCBBViBzeW5jIGlzc3VlXG4gICAgICBhdWRpb1RyYWNrLnRpbWVzY2FsZSA9IGF1ZGlvVHJhY2suc2FtcGxlcmF0ZTtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJhdWRpbyBzYW1wbGluZyByYXRlIDogXCIgKyBhdWRpb1RyYWNrLnNhbXBsZXJhdGUpO1xuXG4gICAgICBpZiAoIWF1ZGlvVHJhY2suaXNBQUMpIHtcbiAgICAgICAgaWYgKHR5cGVTdXBwb3J0ZWQubXBlZykge1xuICAgICAgICAgIC8vIENocm9tZSBhbmQgU2FmYXJpXG4gICAgICAgICAgY29udGFpbmVyID0gJ2F1ZGlvL21wZWcnO1xuICAgICAgICAgIGF1ZGlvVHJhY2suY29kZWMgPSAnJztcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlU3VwcG9ydGVkLm1wMykge1xuICAgICAgICAgIC8vIEZpcmVmb3hcbiAgICAgICAgICBhdWRpb1RyYWNrLmNvZGVjID0gJ21wMyc7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdHJhY2tzLmF1ZGlvID0ge1xuICAgICAgICBjb250YWluZXI6IGNvbnRhaW5lcixcbiAgICAgICAgY29kZWM6IGF1ZGlvVHJhY2suY29kZWMsXG4gICAgICAgIGluaXRTZWdtZW50OiAhYXVkaW9UcmFjay5pc0FBQyAmJiB0eXBlU3VwcG9ydGVkLm1wZWcgPyBuZXcgVWludDhBcnJheSgpIDogbXA0X2dlbmVyYXRvci5pbml0U2VnbWVudChbYXVkaW9UcmFja10pLFxuICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgIGNoYW5uZWxDb3VudDogYXVkaW9UcmFjay5jaGFubmVsQ291bnRcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgaWYgKGNvbXB1dGVQVFNEVFMpIHtcbiAgICAgICAgLy8gcmVtZW1iZXIgZmlyc3QgUFRTIG9mIHRoaXMgZGVtdXhpbmcgY29udGV4dC4gZm9yIGF1ZGlvLCBQVFMgPSBEVFNcbiAgICAgICAgaW5pdFBUUyA9IGluaXREVFMgPSBhdWRpb1NhbXBsZXNbMF0ucHRzIC0gTWF0aC5yb3VuZChhdWRpb1RyYWNrLmlucHV0VGltZVNjYWxlICogdGltZU9mZnNldCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHZpZGVvVHJhY2suc3BzICYmIHZpZGVvVHJhY2sucHBzICYmIHZpZGVvU2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIC8vIGxldCdzIHVzZSBpbnB1dCB0aW1lIHNjYWxlIGFzIE1QNCB2aWRlbyB0aW1lc2NhbGVcbiAgICAgIC8vIHdlIHVzZSBpbnB1dCB0aW1lIHNjYWxlIHN0cmFpZ2h0IGF3YXkgdG8gYXZvaWQgcm91bmRpbmcgaXNzdWVzIG9uIGZyYW1lIGR1cmF0aW9uIC8gY3RzIGNvbXB1dGF0aW9uXG4gICAgICB2YXIgaW5wdXRUaW1lU2NhbGUgPSB2aWRlb1RyYWNrLmlucHV0VGltZVNjYWxlO1xuICAgICAgdmlkZW9UcmFjay50aW1lc2NhbGUgPSBpbnB1dFRpbWVTY2FsZTtcbiAgICAgIHRyYWNrcy52aWRlbyA9IHtcbiAgICAgICAgY29udGFpbmVyOiAndmlkZW8vbXA0JyxcbiAgICAgICAgY29kZWM6IHZpZGVvVHJhY2suY29kZWMsXG4gICAgICAgIGluaXRTZWdtZW50OiBtcDRfZ2VuZXJhdG9yLmluaXRTZWdtZW50KFt2aWRlb1RyYWNrXSksXG4gICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgd2lkdGg6IHZpZGVvVHJhY2sud2lkdGgsXG4gICAgICAgICAgaGVpZ2h0OiB2aWRlb1RyYWNrLmhlaWdodFxuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBpZiAoY29tcHV0ZVBUU0RUUykge1xuICAgICAgICB2YXIgc3RhcnRQVFMgPSB0aGlzLmdldFZpZGVvU3RhcnRQdHModmlkZW9TYW1wbGVzKTtcbiAgICAgICAgdmFyIHN0YXJ0T2Zmc2V0ID0gTWF0aC5yb3VuZChpbnB1dFRpbWVTY2FsZSAqIHRpbWVPZmZzZXQpO1xuICAgICAgICBpbml0RFRTID0gTWF0aC5taW4oaW5pdERUUywgUFRTTm9ybWFsaXplKHZpZGVvU2FtcGxlc1swXS5kdHMsIHN0YXJ0UFRTKSAtIHN0YXJ0T2Zmc2V0KTtcbiAgICAgICAgaW5pdFBUUyA9IE1hdGgubWluKGluaXRQVFMsIHN0YXJ0UFRTIC0gc3RhcnRPZmZzZXQpO1xuICAgICAgICB0aGlzLm9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5JTklUX1BUU19GT1VORCwge1xuICAgICAgICAgIGluaXRQVFM6IGluaXRQVFNcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChjb21wdXRlUFRTRFRTICYmIHRyYWNrcy5hdWRpbykge1xuICAgICAgLy8gaW5pdFBUUyBmb3VuZCBmb3IgYXVkaW8tb25seSBzdHJlYW0gd2l0aCBtYWluIGFuZCBhbHQgYXVkaW9cbiAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLklOSVRfUFRTX0ZPVU5ELCB7XG4gICAgICAgIGluaXRQVFM6IGluaXRQVFNcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmIChPYmplY3Qua2V5cyh0cmFja3MpLmxlbmd0aCkge1xuICAgICAgb2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19JTklUX1NFR01FTlQsIGRhdGEpO1xuICAgICAgdGhpcy5JU0dlbmVyYXRlZCA9IHRydWU7XG5cbiAgICAgIGlmIChjb21wdXRlUFRTRFRTKSB7XG4gICAgICAgIHRoaXMuX2luaXRQVFMgPSBpbml0UFRTO1xuICAgICAgICB0aGlzLl9pbml0RFRTID0gaW5pdERUUztcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgb2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTUVESUFfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5GUkFHX1BBUlNJTkdfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgcmVhc29uOiAnbm8gYXVkaW8vdmlkZW8gc2FtcGxlcyBmb3VuZCdcbiAgICAgIH0pO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ucmVtdXhWaWRlbyA9IGZ1bmN0aW9uIHJlbXV4VmlkZW8odHJhY2ssIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGF1ZGlvVHJhY2tMZW5ndGgpIHtcbiAgICB2YXIgdGltZVNjYWxlID0gdHJhY2sudGltZXNjYWxlO1xuICAgIHZhciBpbnB1dFNhbXBsZXMgPSB0cmFjay5zYW1wbGVzO1xuICAgIHZhciBvdXRwdXRTYW1wbGVzID0gW107XG4gICAgdmFyIG5iU2FtcGxlcyA9IGlucHV0U2FtcGxlcy5sZW5ndGg7XG4gICAgdmFyIGluaXRQVFMgPSB0aGlzLl9pbml0UFRTO1xuICAgIHZhciBvZmZzZXQgPSA4O1xuICAgIHZhciBtcDRTYW1wbGVEdXJhdGlvbjtcbiAgICB2YXIgbWRhdDtcbiAgICB2YXIgbW9vZjtcbiAgICB2YXIgZmlyc3REVFM7XG4gICAgdmFyIGxhc3REVFM7XG4gICAgdmFyIG1pblBUUyA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgICB2YXIgbWF4UFRTID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIHZhciBwdHNEdHNTaGlmdCA9IDA7XG4gICAgdmFyIHNvcnRTYW1wbGVzID0gZmFsc2U7IC8vIGlmIHBhcnNlZCBmcmFnbWVudCBpcyBjb250aWd1b3VzIHdpdGggbGFzdCBvbmUsIGxldCdzIHVzZSBsYXN0IERUUyB2YWx1ZSBhcyByZWZlcmVuY2VcblxuICAgIHZhciBuZXh0QXZjRHRzID0gdGhpcy5uZXh0QXZjRHRzO1xuXG4gICAgaWYgKG5iU2FtcGxlcyA9PT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghY29udGlndW91cykge1xuICAgICAgdmFyIHB0cyA9IHRpbWVPZmZzZXQgKiB0aW1lU2NhbGU7XG4gICAgICB2YXIgY3RzID0gaW5wdXRTYW1wbGVzWzBdLnB0cyAtIFBUU05vcm1hbGl6ZShpbnB1dFNhbXBsZXNbMF0uZHRzLCBpbnB1dFNhbXBsZXNbMF0ucHRzKTsgLy8gaWYgbm90IGNvbnRpZ3VvdXMsIGxldCdzIHVzZSB0YXJnZXQgdGltZU9mZnNldFxuXG4gICAgICBuZXh0QXZjRHRzID0gcHRzIC0gY3RzO1xuICAgIH0gLy8gUFRTIGlzIGNvZGVkIG9uIDMzYml0cywgYW5kIGNhbiBsb29wIGZyb20gLTJeMzIgdG8gMl4zMlxuICAgIC8vIFBUU05vcm1hbGl6ZSB3aWxsIG1ha2UgUFRTL0RUUyB2YWx1ZSBtb25vdG9uaWMsIHdlIHVzZSBsYXN0IGtub3duIERUUyB2YWx1ZSBhcyByZWZlcmVuY2UgdmFsdWVcblxuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBuYlNhbXBsZXM7IGkrKykge1xuICAgICAgdmFyIHNhbXBsZSA9IGlucHV0U2FtcGxlc1tpXTtcbiAgICAgIHNhbXBsZS5wdHMgPSBQVFNOb3JtYWxpemUoc2FtcGxlLnB0cyAtIGluaXRQVFMsIG5leHRBdmNEdHMpO1xuICAgICAgc2FtcGxlLmR0cyA9IFBUU05vcm1hbGl6ZShzYW1wbGUuZHRzIC0gaW5pdFBUUywgbmV4dEF2Y0R0cyk7XG5cbiAgICAgIGlmIChzYW1wbGUuZHRzID4gc2FtcGxlLnB0cykge1xuICAgICAgICBwdHNEdHNTaGlmdCA9IE1hdGgubWF4KE1hdGgubWluKHB0c0R0c1NoaWZ0LCBzYW1wbGUucHRzIC0gc2FtcGxlLmR0cyksIC0xICogUFRTX0RUU19TSElGVF9UT0xFUkFOQ0VfOTBLSFopO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2FtcGxlLmR0cyA8IGlucHV0U2FtcGxlc1tpID4gMCA/IGkgLSAxIDogaV0uZHRzKSB7XG4gICAgICAgIHNvcnRTYW1wbGVzID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9IC8vIHNvcnQgdmlkZW8gc2FtcGxlcyBieSBEVFMgdGhlbiBQVFMgdGhlbiBkZW11eCBpZCBvcmRlclxuXG5cbiAgICBpZiAoc29ydFNhbXBsZXMpIHtcbiAgICAgIGlucHV0U2FtcGxlcy5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7XG4gICAgICAgIHZhciBkZWx0YWR0cyA9IGEuZHRzIC0gYi5kdHM7XG4gICAgICAgIHZhciBkZWx0YXB0cyA9IGEucHRzIC0gYi5wdHM7XG4gICAgICAgIHJldHVybiBkZWx0YWR0cyB8fCBkZWx0YXB0cyB8fCBhLmlkIC0gYi5pZDtcbiAgICAgIH0pO1xuICAgIH0gLy8gR2V0IGZpcnN0L2xhc3QgRFRTXG5cblxuICAgIGZpcnN0RFRTID0gaW5wdXRTYW1wbGVzWzBdLmR0cztcbiAgICBsYXN0RFRTID0gaW5wdXRTYW1wbGVzW25iU2FtcGxlcyAtIDFdLmR0czsgLy8gb24gU2FmYXJpIGxldCdzIHNpZ25hbCB0aGUgc2FtZSBzYW1wbGUgZHVyYXRpb24gZm9yIGFsbCBzYW1wbGVzXG4gICAgLy8gc2FtcGxlIGR1cmF0aW9uIChhcyBleHBlY3RlZCBieSB0cnVuIE1QNCBib3hlcyksIHNob3VsZCBiZSB0aGUgZGVsdGEgYmV0d2VlbiBzYW1wbGUgRFRTXG4gICAgLy8gc2V0IHRoaXMgY29uc3RhbnQgZHVyYXRpb24gYXMgYmVpbmcgdGhlIGF2ZyBkZWx0YSBiZXR3ZWVuIGNvbnNlY3V0aXZlIERUUy5cblxuICAgIHZhciBhdmVyYWdlU2FtcGxlRHVyYXRpb24gPSBNYXRoLnJvdW5kKChsYXN0RFRTIC0gZmlyc3REVFMpIC8gKG5iU2FtcGxlcyAtIDEpKTsgLy8gaGFuZGxlIGJyb2tlbiBzdHJlYW1zIHdpdGggUFRTIDwgRFRTLCB0b2xlcmFuY2UgdXAgMC4yIHNlY29uZHNcblxuICAgIGlmIChwdHNEdHNTaGlmdCA8IDApIHtcbiAgICAgIGlmIChwdHNEdHNTaGlmdCA8IGF2ZXJhZ2VTYW1wbGVEdXJhdGlvbiAqIC0yKSB7XG4gICAgICAgIC8vIEZpeCBmb3IgXCJDTk4gc3BlY2lhbCByZXBvcnQsIHdpdGggQ0NcIiBpbiB0ZXN0LXN0cmVhbXMgKGluY2x1ZGluZyBTYWZhcmkgYnJvd3NlcilcbiAgICAgICAgLy8gV2l0aCBsYXJnZSBQVFMgPCBEVFMgZXJyb3JzIHN1Y2ggYXMgdGhpcywgd2Ugd2FudCB0byBjb3JyZWN0IENUUyB3aGlsZSBtYWludGFpbmluZyBpbmNyZWFzaW5nIERUUyB2YWx1ZXNcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJQVFMgPCBEVFMgZGV0ZWN0ZWQgaW4gdmlkZW8gc2FtcGxlcywgb2Zmc2V0dGluZyBEVFMgZnJvbSBQVFMgYnkgXCIgKyB0b01zRnJvbU1wZWdUc0Nsb2NrKC1hdmVyYWdlU2FtcGxlRHVyYXRpb24sIHRydWUpICsgXCIgbXNcIik7XG4gICAgICAgIHZhciBsYXN0RHRzID0gcHRzRHRzU2hpZnQ7XG5cbiAgICAgICAgZm9yICh2YXIgX2kgPSAwOyBfaSA8IG5iU2FtcGxlczsgX2krKykge1xuICAgICAgICAgIGlucHV0U2FtcGxlc1tfaV0uZHRzID0gbGFzdER0cyA9IE1hdGgubWF4KGxhc3REdHMsIGlucHV0U2FtcGxlc1tfaV0ucHRzIC0gYXZlcmFnZVNhbXBsZUR1cmF0aW9uKTtcbiAgICAgICAgICBpbnB1dFNhbXBsZXNbX2ldLnB0cyA9IE1hdGgubWF4KGxhc3REdHMsIGlucHV0U2FtcGxlc1tfaV0ucHRzKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gRml4IGZvciBcIkN1c3RvbSBJViB3aXRoIGJhZCBQVFMgRFRTXCIgaW4gdGVzdC1zdHJlYW1zXG4gICAgICAgIC8vIFdpdGggc21hbGxlciBQVFMgPCBEVFMgZXJyb3JzIHdlIGNhbiBzaW1wbHkgbW92ZSBhbGwgRFRTIGJhY2suIFRoaXMgaW5jcmVhc2VzIENUUyB3aXRob3V0IGNhdXNpbmcgYnVmZmVyIGdhcHMgb3IgZGVjb2RlIGVycm9ycyBpbiBTYWZhcmlcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJQVFMgPCBEVFMgZGV0ZWN0ZWQgaW4gdmlkZW8gc2FtcGxlcywgc2hpZnRpbmcgRFRTIGJ5IFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhwdHNEdHNTaGlmdCwgdHJ1ZSkgKyBcIiBtcyB0byBvdmVyY29tZSB0aGlzIGlzc3VlXCIpO1xuXG4gICAgICAgIGZvciAodmFyIF9pMiA9IDA7IF9pMiA8IG5iU2FtcGxlczsgX2kyKyspIHtcbiAgICAgICAgICBpbnB1dFNhbXBsZXNbX2kyXS5kdHMgPSBpbnB1dFNhbXBsZXNbX2kyXS5kdHMgKyBwdHNEdHNTaGlmdDtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmaXJzdERUUyA9IGlucHV0U2FtcGxlc1swXS5kdHM7XG4gICAgICBsYXN0RFRTID0gaW5wdXRTYW1wbGVzW25iU2FtcGxlcyAtIDFdLmR0cztcbiAgICB9IC8vIGlmIGZyYWdtZW50IGFyZSBjb250aWd1b3VzLCBkZXRlY3QgaG9sZS9vdmVybGFwcGluZyBiZXR3ZWVuIGZyYWdtZW50c1xuXG5cbiAgICBpZiAoY29udGlndW91cykge1xuICAgICAgLy8gY2hlY2sgdGltZXN0YW1wIGNvbnRpbnVpdHkgYWNyb3NzIGNvbnNlY3V0aXZlIGZyYWdtZW50cyAodGhpcyBpcyB0byByZW1vdmUgaW50ZXItZnJhZ21lbnQgZ2FwL2hvbGUpXG4gICAgICB2YXIgZGVsdGEgPSBmaXJzdERUUyAtIG5leHRBdmNEdHM7XG4gICAgICB2YXIgZm91bmRIb2xlID0gZGVsdGEgPiBhdmVyYWdlU2FtcGxlRHVyYXRpb247XG4gICAgICB2YXIgZm91bmRPdmVybGFwID0gZGVsdGEgPCAtMTtcblxuICAgICAgaWYgKGZvdW5kSG9sZSB8fCBmb3VuZE92ZXJsYXApIHtcbiAgICAgICAgaWYgKGZvdW5kSG9sZSkge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiQVZDOiBcIiArIHRvTXNGcm9tTXBlZ1RzQ2xvY2soZGVsdGEsIHRydWUpICsgXCIgbXMgKFwiICsgZGVsdGEgKyBcImR0cykgaG9sZSBiZXR3ZWVuIGZyYWdtZW50cyBkZXRlY3RlZCwgZmlsbGluZyBpdFwiKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcIkFWQzogXCIgKyB0b01zRnJvbU1wZWdUc0Nsb2NrKC1kZWx0YSwgdHJ1ZSkgKyBcIiBtcyAoXCIgKyBkZWx0YSArIFwiZHRzKSBvdmVybGFwcGluZyBiZXR3ZWVuIGZyYWdtZW50cyBkZXRlY3RlZFwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZpcnN0RFRTID0gbmV4dEF2Y0R0cztcbiAgICAgICAgdmFyIGZpcnN0UFRTID0gaW5wdXRTYW1wbGVzWzBdLnB0cyAtIGRlbHRhO1xuICAgICAgICBpbnB1dFNhbXBsZXNbMF0uZHRzID0gZmlyc3REVFM7XG4gICAgICAgIGlucHV0U2FtcGxlc1swXS5wdHMgPSBmaXJzdFBUUztcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIlZpZGVvOiBGaXJzdCBQVFMvRFRTIGFkanVzdGVkOiBcIiArIHRvTXNGcm9tTXBlZ1RzQ2xvY2soZmlyc3RQVFMsIHRydWUpICsgXCIvXCIgKyB0b01zRnJvbU1wZWdUc0Nsb2NrKGZpcnN0RFRTLCB0cnVlKSArIFwiLCBkZWx0YTogXCIgKyB0b01zRnJvbU1wZWdUc0Nsb2NrKGRlbHRhLCB0cnVlKSArIFwiIG1zXCIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChjaHJvbWVWZXJzaW9uICYmIGNocm9tZVZlcnNpb24gPCA3NSkge1xuICAgICAgZmlyc3REVFMgPSBNYXRoLm1heCgwLCBmaXJzdERUUyk7XG4gICAgfVxuXG4gICAgdmFyIG5iTmFsdSA9IDA7XG4gICAgdmFyIG5hbHVMZW4gPSAwO1xuXG4gICAgZm9yICh2YXIgX2kzID0gMDsgX2kzIDwgbmJTYW1wbGVzOyBfaTMrKykge1xuICAgICAgLy8gY29tcHV0ZSB0b3RhbC9hdmMgc2FtcGxlIGxlbmd0aCBhbmQgbmIgb2YgTkFMIHVuaXRzXG4gICAgICB2YXIgX3NhbXBsZSA9IGlucHV0U2FtcGxlc1tfaTNdO1xuICAgICAgdmFyIHVuaXRzID0gX3NhbXBsZS51bml0cztcbiAgICAgIHZhciBuYlVuaXRzID0gdW5pdHMubGVuZ3RoO1xuICAgICAgdmFyIHNhbXBsZUxlbiA9IDA7XG5cbiAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgbmJVbml0czsgaisrKSB7XG4gICAgICAgIHNhbXBsZUxlbiArPSB1bml0c1tqXS5kYXRhLmxlbmd0aDtcbiAgICAgIH1cblxuICAgICAgbmFsdUxlbiArPSBzYW1wbGVMZW47XG4gICAgICBuYk5hbHUgKz0gbmJVbml0cztcbiAgICAgIF9zYW1wbGUubGVuZ3RoID0gc2FtcGxlTGVuOyAvLyBub3JtYWxpemUgUFRTL0RUU1xuICAgICAgLy8gZW5zdXJlIHNhbXBsZSBtb25vdG9uaWMgRFRTXG5cbiAgICAgIF9zYW1wbGUuZHRzID0gTWF0aC5tYXgoX3NhbXBsZS5kdHMsIGZpcnN0RFRTKTsgLy8gZW5zdXJlIHRoYXQgY29tcHV0ZWQgdmFsdWUgaXMgZ3JlYXRlciBvciBlcXVhbCB0aGFuIHNhbXBsZSBEVFNcblxuICAgICAgX3NhbXBsZS5wdHMgPSBNYXRoLm1heChfc2FtcGxlLnB0cywgX3NhbXBsZS5kdHMsIDApO1xuICAgICAgbWluUFRTID0gTWF0aC5taW4oX3NhbXBsZS5wdHMsIG1pblBUUyk7XG4gICAgICBtYXhQVFMgPSBNYXRoLm1heChfc2FtcGxlLnB0cywgbWF4UFRTKTtcbiAgICB9XG5cbiAgICBsYXN0RFRTID0gaW5wdXRTYW1wbGVzW25iU2FtcGxlcyAtIDFdLmR0cztcbiAgICAvKiBjb25jYXRlbmF0ZSB0aGUgdmlkZW8gZGF0YSBhbmQgY29uc3RydWN0IHRoZSBtZGF0IGluIHBsYWNlXG4gICAgICAobmVlZCA4IG1vcmUgYnl0ZXMgdG8gZmlsbCBsZW5ndGggYW5kIG1wZGF0IHR5cGUpICovXG5cbiAgICB2YXIgbWRhdFNpemUgPSBuYWx1TGVuICsgNCAqIG5iTmFsdSArIDg7XG5cbiAgICB0cnkge1xuICAgICAgbWRhdCA9IG5ldyBVaW50OEFycmF5KG1kYXRTaXplKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTVVYX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uUkVNVVhfQUxMT0NfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgYnl0ZXM6IG1kYXRTaXplLFxuICAgICAgICByZWFzb246IFwiZmFpbCBhbGxvY2F0aW5nIHZpZGVvIG1kYXQgXCIgKyBtZGF0U2l6ZVxuICAgICAgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHZpZXcgPSBuZXcgRGF0YVZpZXcobWRhdC5idWZmZXIpO1xuICAgIHZpZXcuc2V0VWludDMyKDAsIG1kYXRTaXplKTtcbiAgICBtZGF0LnNldChtcDRfZ2VuZXJhdG9yLnR5cGVzLm1kYXQsIDQpO1xuXG4gICAgZm9yICh2YXIgX2k0ID0gMDsgX2k0IDwgbmJTYW1wbGVzOyBfaTQrKykge1xuICAgICAgdmFyIGF2Y1NhbXBsZSA9IGlucHV0U2FtcGxlc1tfaTRdO1xuICAgICAgdmFyIGF2Y1NhbXBsZVVuaXRzID0gYXZjU2FtcGxlLnVuaXRzO1xuICAgICAgdmFyIG1wNFNhbXBsZUxlbmd0aCA9IDA7XG4gICAgICB2YXIgY29tcG9zaXRpb25UaW1lT2Zmc2V0ID0gdm9pZCAwOyAvLyBjb252ZXJ0IE5BTFUgYml0c3RyZWFtIHRvIE1QNCBmb3JtYXQgKHByZXBlbmQgTkFMVSB3aXRoIHNpemUgZmllbGQpXG5cbiAgICAgIGZvciAodmFyIF9qID0gMCwgX25iVW5pdHMgPSBhdmNTYW1wbGVVbml0cy5sZW5ndGg7IF9qIDwgX25iVW5pdHM7IF9qKyspIHtcbiAgICAgICAgdmFyIHVuaXQgPSBhdmNTYW1wbGVVbml0c1tfal07XG4gICAgICAgIHZhciB1bml0RGF0YSA9IHVuaXQuZGF0YTtcbiAgICAgICAgdmFyIHVuaXREYXRhTGVuID0gdW5pdC5kYXRhLmJ5dGVMZW5ndGg7XG4gICAgICAgIHZpZXcuc2V0VWludDMyKG9mZnNldCwgdW5pdERhdGFMZW4pO1xuICAgICAgICBvZmZzZXQgKz0gNDtcbiAgICAgICAgbWRhdC5zZXQodW5pdERhdGEsIG9mZnNldCk7XG4gICAgICAgIG9mZnNldCArPSB1bml0RGF0YUxlbjtcbiAgICAgICAgbXA0U2FtcGxlTGVuZ3RoICs9IDQgKyB1bml0RGF0YUxlbjtcbiAgICAgIH0gLy8gZXhwZWN0ZWQgc2FtcGxlIGR1cmF0aW9uIGlzIHRoZSBEZWNvZGluZyBUaW1lc3RhbXAgZGlmZiBvZiBjb25zZWN1dGl2ZSBzYW1wbGVzXG5cblxuICAgICAgaWYgKF9pNCA8IG5iU2FtcGxlcyAtIDEpIHtcbiAgICAgICAgbXA0U2FtcGxlRHVyYXRpb24gPSBpbnB1dFNhbXBsZXNbX2k0ICsgMV0uZHRzIC0gYXZjU2FtcGxlLmR0cztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBjb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICAgICAgdmFyIGxhc3RGcmFtZUR1cmF0aW9uID0gYXZjU2FtcGxlLmR0cyAtIGlucHV0U2FtcGxlc1tfaTQgPiAwID8gX2k0IC0gMSA6IF9pNF0uZHRzO1xuXG4gICAgICAgIGlmIChjb25maWcuc3RyZXRjaFNob3J0VmlkZW9UcmFjaykge1xuICAgICAgICAgIC8vIEluIHNvbWUgY2FzZXMsIGEgc2VnbWVudCdzIGF1ZGlvIHRyYWNrIGR1cmF0aW9uIG1heSBleGNlZWQgdGhlIHZpZGVvIHRyYWNrIGR1cmF0aW9uLlxuICAgICAgICAgIC8vIFNpbmNlIHdlJ3ZlIGFscmVhZHkgcmVtdXhlZCBhdWRpbywgYW5kIHdlIGtub3cgaG93IGxvbmcgdGhlIGF1ZGlvIHRyYWNrIGlzLCB3ZSBsb29rIHRvXG4gICAgICAgICAgLy8gc2VlIGlmIHRoZSBkZWx0YSB0byB0aGUgbmV4dCBzZWdtZW50IGlzIGxvbmdlciB0aGFuIG1heEJ1ZmZlckhvbGUuXG4gICAgICAgICAgLy8gSWYgc28sIHBsYXliYWNrIHdvdWxkIHBvdGVudGlhbGx5IGdldCBzdHVjaywgc28gd2UgYXJ0aWZpY2lhbGx5IGluZmxhdGVcbiAgICAgICAgICAvLyB0aGUgZHVyYXRpb24gb2YgdGhlIGxhc3QgZnJhbWUgdG8gbWluaW1pemUgYW55IHBvdGVudGlhbCBnYXAgYmV0d2VlbiBzZWdtZW50cy5cbiAgICAgICAgICB2YXIgbWF4QnVmZmVySG9sZSA9IGNvbmZpZy5tYXhCdWZmZXJIb2xlO1xuICAgICAgICAgIHZhciBnYXBUb2xlcmFuY2UgPSBNYXRoLmZsb29yKG1heEJ1ZmZlckhvbGUgKiB0aW1lU2NhbGUpO1xuICAgICAgICAgIHZhciBkZWx0YVRvRnJhbWVFbmQgPSAoYXVkaW9UcmFja0xlbmd0aCA/IG1pblBUUyArIGF1ZGlvVHJhY2tMZW5ndGggKiB0aW1lU2NhbGUgOiB0aGlzLm5leHRBdWRpb1B0cykgLSBhdmNTYW1wbGUucHRzO1xuXG4gICAgICAgICAgaWYgKGRlbHRhVG9GcmFtZUVuZCA+IGdhcFRvbGVyYW5jZSkge1xuICAgICAgICAgICAgLy8gV2Ugc3VidHJhY3QgbGFzdEZyYW1lRHVyYXRpb24gZnJvbSBkZWx0YVRvRnJhbWVFbmQgdG8gdHJ5IHRvIHByZXZlbnQgYW55IHZpZGVvXG4gICAgICAgICAgICAvLyBmcmFtZSBvdmVybGFwLiBtYXhCdWZmZXJIb2xlIHNob3VsZCBiZSA+PiBsYXN0RnJhbWVEdXJhdGlvbiBhbnl3YXkuXG4gICAgICAgICAgICBtcDRTYW1wbGVEdXJhdGlvbiA9IGRlbHRhVG9GcmFtZUVuZCAtIGxhc3RGcmFtZUR1cmF0aW9uO1xuXG4gICAgICAgICAgICBpZiAobXA0U2FtcGxlRHVyYXRpb24gPCAwKSB7XG4gICAgICAgICAgICAgIG1wNFNhbXBsZUR1cmF0aW9uID0gbGFzdEZyYW1lRHVyYXRpb247XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJJdCBpcyBhcHByb3hpbWF0ZWx5IFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhkZWx0YVRvRnJhbWVFbmQsIGZhbHNlKSArIFwiIG1zIHRvIHRoZSBuZXh0IHNlZ21lbnQ7IHVzaW5nIGR1cmF0aW9uIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhtcDRTYW1wbGVEdXJhdGlvbiwgZmFsc2UpICsgXCIgbXMgZm9yIHRoZSBsYXN0IHZpZGVvIGZyYW1lLlwiKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbXA0U2FtcGxlRHVyYXRpb24gPSBsYXN0RnJhbWVEdXJhdGlvbjtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbXA0U2FtcGxlRHVyYXRpb24gPSBsYXN0RnJhbWVEdXJhdGlvbjtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb21wb3NpdGlvblRpbWVPZmZzZXQgPSBNYXRoLnJvdW5kKGF2Y1NhbXBsZS5wdHMgLSBhdmNTYW1wbGUuZHRzKTsgLy8gY29uc29sZS5sb2coJ1BUUy9EVFMvaW5pdERUUy9ub3JtUFRTL25vcm1EVFMvcmVsYXRpdmUgUFRTIDogJHthdmNTYW1wbGUucHRzfS8ke2F2Y1NhbXBsZS5kdHN9LyR7aW5pdERUU30vJHtwdHNub3JtfS8ke2R0c25vcm19LyR7KGF2Y1NhbXBsZS5wdHMvNDI5NDk2NzI5NikudG9GaXhlZCgzKX0nKTtcblxuICAgICAgb3V0cHV0U2FtcGxlcy5wdXNoKHtcbiAgICAgICAgc2l6ZTogbXA0U2FtcGxlTGVuZ3RoLFxuICAgICAgICAvLyBjb25zdGFudCBkdXJhdGlvblxuICAgICAgICBkdXJhdGlvbjogbXA0U2FtcGxlRHVyYXRpb24sXG4gICAgICAgIGN0czogY29tcG9zaXRpb25UaW1lT2Zmc2V0LFxuICAgICAgICBmbGFnczoge1xuICAgICAgICAgIGlzTGVhZGluZzogMCxcbiAgICAgICAgICBpc0RlcGVuZGVkT246IDAsXG4gICAgICAgICAgaGFzUmVkdW5kYW5jeTogMCxcbiAgICAgICAgICBkZWdyYWRQcmlvOiAwLFxuICAgICAgICAgIGRlcGVuZHNPbjogYXZjU2FtcGxlLmtleSA/IDIgOiAxLFxuICAgICAgICAgIGlzTm9uU3luYzogYXZjU2FtcGxlLmtleSA/IDAgOiAxXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0gLy8gbmV4dCBBVkMgc2FtcGxlIERUUyBzaG91bGQgYmUgZXF1YWwgdG8gbGFzdCBzYW1wbGUgRFRTICsgbGFzdCBzYW1wbGUgZHVyYXRpb24gKGluIFBFUyB0aW1lc2NhbGUpXG5cblxuICAgIHRoaXMubmV4dEF2Y0R0cyA9IGxhc3REVFMgKyBtcDRTYW1wbGVEdXJhdGlvbjtcbiAgICB2YXIgZHJvcHBlZCA9IHRyYWNrLmRyb3BwZWQ7XG4gICAgdHJhY2submJOYWx1ID0gMDtcbiAgICB0cmFjay5kcm9wcGVkID0gMDtcblxuICAgIGlmIChvdXRwdXRTYW1wbGVzLmxlbmd0aCAmJiBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCkuaW5kZXhPZignY2hyb21lJykgPiAtMSkge1xuICAgICAgdmFyIGZsYWdzID0gb3V0cHV0U2FtcGxlc1swXS5mbGFnczsgLy8gY2hyb21lIHdvcmthcm91bmQsIG1hcmsgZmlyc3Qgc2FtcGxlIGFzIGJlaW5nIGEgUmFuZG9tIEFjY2VzcyBQb2ludCB0byBhdm9pZCBzb3VyY2VidWZmZXIgYXBwZW5kIGlzc3VlXG4gICAgICAvLyBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9MjI5NDEyXG5cbiAgICAgIGZsYWdzLmRlcGVuZHNPbiA9IDI7XG4gICAgICBmbGFncy5pc05vblN5bmMgPSAwO1xuICAgIH1cblxuICAgIHRyYWNrLnNhbXBsZXMgPSBvdXRwdXRTYW1wbGVzO1xuICAgIG1vb2YgPSBtcDRfZ2VuZXJhdG9yLm1vb2YodHJhY2suc2VxdWVuY2VOdW1iZXIrKywgZmlyc3REVFMsIHRyYWNrKTtcbiAgICB0cmFjay5zYW1wbGVzID0gW107XG4gICAgdmFyIGRhdGEgPSB7XG4gICAgICBkYXRhMTogbW9vZixcbiAgICAgIGRhdGEyOiBtZGF0LFxuICAgICAgc3RhcnRQVFM6IG1pblBUUyAvIHRpbWVTY2FsZSxcbiAgICAgIGVuZFBUUzogKG1heFBUUyArIG1wNFNhbXBsZUR1cmF0aW9uKSAvIHRpbWVTY2FsZSxcbiAgICAgIHN0YXJ0RFRTOiBmaXJzdERUUyAvIHRpbWVTY2FsZSxcbiAgICAgIGVuZERUUzogdGhpcy5uZXh0QXZjRHRzIC8gdGltZVNjYWxlLFxuICAgICAgdHlwZTogJ3ZpZGVvJyxcbiAgICAgIGhhc0F1ZGlvOiBmYWxzZSxcbiAgICAgIGhhc1ZpZGVvOiB0cnVlLFxuICAgICAgbmI6IG91dHB1dFNhbXBsZXMubGVuZ3RoLFxuICAgICAgZHJvcHBlZDogZHJvcHBlZFxuICAgIH07XG4gICAgdGhpcy5vYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX0RBVEEsIGRhdGEpO1xuICAgIHJldHVybiBkYXRhO1xuICB9O1xuXG4gIF9wcm90by5yZW11eEF1ZGlvID0gZnVuY3Rpb24gcmVtdXhBdWRpbyh0cmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0KSB7XG4gICAgdmFyIGlucHV0VGltZVNjYWxlID0gdHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgdmFyIG1wNHRpbWVTY2FsZSA9IHRyYWNrLnRpbWVzY2FsZTtcbiAgICB2YXIgc2NhbGVGYWN0b3IgPSBpbnB1dFRpbWVTY2FsZSAvIG1wNHRpbWVTY2FsZTtcbiAgICB2YXIgbXA0U2FtcGxlRHVyYXRpb24gPSB0cmFjay5pc0FBQyA/IDEwMjQgOiAxMTUyO1xuICAgIHZhciBpbnB1dFNhbXBsZUR1cmF0aW9uID0gbXA0U2FtcGxlRHVyYXRpb24gKiBzY2FsZUZhY3RvcjtcbiAgICB2YXIgaW5pdFBUUyA9IHRoaXMuX2luaXRQVFM7XG4gICAgdmFyIHJhd01QRUcgPSAhdHJhY2suaXNBQUMgJiYgdGhpcy50eXBlU3VwcG9ydGVkLm1wZWc7XG4gICAgdmFyIG1wNFNhbXBsZTtcbiAgICB2YXIgZmlsbEZyYW1lO1xuICAgIHZhciBtZGF0O1xuICAgIHZhciBtb29mO1xuICAgIHZhciBmaXJzdFBUUztcbiAgICB2YXIgbGFzdFBUUztcbiAgICB2YXIgb2Zmc2V0ID0gcmF3TVBFRyA/IDAgOiA4O1xuICAgIHZhciBpbnB1dFNhbXBsZXMgPSB0cmFjay5zYW1wbGVzO1xuICAgIHZhciBvdXRwdXRTYW1wbGVzID0gW107XG4gICAgdmFyIG5leHRBdWRpb1B0cyA9IHRoaXMubmV4dEF1ZGlvUHRzOyAvLyBmb3IgYXVkaW8gc2FtcGxlcywgYWxzbyBjb25zaWRlciBjb25zZWN1dGl2ZSBmcmFnbWVudHMgYXMgYmVpbmcgY29udGlndW91cyAoZXZlbiBpZiBhIGxldmVsIHN3aXRjaCBvY2N1cnMpLFxuICAgIC8vIGZvciBzYWtlIG9mIGNsYXJpdHk6XG4gICAgLy8gY29uc2VjdXRpdmUgZnJhZ21lbnRzIGFyZSBmcmFncyB3aXRoXG4gICAgLy8gIC0gbGVzcyB0aGFuIDEwMG1zIGdhcHMgYmV0d2VlbiBuZXcgdGltZSBvZmZzZXQgKGlmIGFjY3VyYXRlKSBhbmQgbmV4dCBleHBlY3RlZCBQVFMgT1JcbiAgICAvLyAgLSBsZXNzIHRoYW4gMjAgYXVkaW8gZnJhbWVzIGRpc3RhbmNlXG4gICAgLy8gY29udGlndW91cyBmcmFnbWVudHMgYXJlIGNvbnNlY3V0aXZlIGZyYWdtZW50cyBmcm9tIHNhbWUgcXVhbGl0eSBsZXZlbCAoc2FtZSBsZXZlbCwgbmV3IFNOID0gb2xkIFNOICsgMSlcbiAgICAvLyB0aGlzIGhlbHBzIGVuc3VyaW5nIGF1ZGlvIGNvbnRpbnVpdHlcbiAgICAvLyBhbmQgdGhpcyBhbHNvIGF2b2lkcyBhdWRpbyBnbGl0Y2hlcy9jdXQgd2hlbiBzd2l0Y2hpbmcgcXVhbGl0eSwgb3IgcmVwb3J0aW5nIHdyb25nIGR1cmF0aW9uIG9uIGZpcnN0IGF1ZGlvIGZyYW1lXG5cbiAgICBjb250aWd1b3VzIHw9IGlucHV0U2FtcGxlcy5sZW5ndGggJiYgbmV4dEF1ZGlvUHRzICYmIChhY2N1cmF0ZVRpbWVPZmZzZXQgJiYgTWF0aC5hYnModGltZU9mZnNldCAtIG5leHRBdWRpb1B0cyAvIGlucHV0VGltZVNjYWxlKSA8IDAuMSB8fCBNYXRoLmFicyhpbnB1dFNhbXBsZXNbMF0ucHRzIC0gbmV4dEF1ZGlvUHRzIC0gaW5pdFBUUykgPCAyMCAqIGlucHV0U2FtcGxlRHVyYXRpb24pOyAvLyBjb21wdXRlIG5vcm1hbGl6ZWQgUFRTXG5cbiAgICBpbnB1dFNhbXBsZXMuZm9yRWFjaChmdW5jdGlvbiAoc2FtcGxlKSB7XG4gICAgICBzYW1wbGUucHRzID0gc2FtcGxlLmR0cyA9IFBUU05vcm1hbGl6ZShzYW1wbGUucHRzIC0gaW5pdFBUUywgdGltZU9mZnNldCAqIGlucHV0VGltZVNjYWxlKTtcbiAgICB9KTsgLy8gZmlsdGVyIG91dCBzYW1wbGUgd2l0aCBuZWdhdGl2ZSBQVFMgdGhhdCBhcmUgbm90IHBsYXlhYmxlIGFueXdheVxuICAgIC8vIGlmIHdlIGRvbid0IHJlbW92ZSB0aGVzZSBuZWdhdGl2ZSBzYW1wbGVzLCB0aGV5IHdpbGwgc2hpZnQgYWxsIGF1ZGlvIHNhbXBsZXMgZm9yd2FyZC5cbiAgICAvLyBsZWFkaW5nIHRvIGF1ZGlvIG92ZXJsYXAgYmV0d2VlbiBjdXJyZW50IC8gbmV4dCBmcmFnbWVudFxuXG4gICAgaW5wdXRTYW1wbGVzID0gaW5wdXRTYW1wbGVzLmZpbHRlcihmdW5jdGlvbiAoc2FtcGxlKSB7XG4gICAgICByZXR1cm4gc2FtcGxlLnB0cyA+PSAwO1xuICAgIH0pOyAvLyBpbiBjYXNlIGFsbCBzYW1wbGVzIGhhdmUgbmVnYXRpdmUgUFRTLCBhbmQgaGF2ZSBiZWVuIGZpbHRlcmVkIG91dCwgcmV0dXJuIG5vd1xuXG4gICAgaWYgKGlucHV0U2FtcGxlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoIWNvbnRpZ3VvdXMpIHtcbiAgICAgIGlmICghYWNjdXJhdGVUaW1lT2Zmc2V0KSB7XG4gICAgICAgIC8vIGlmIGZyYWcgYXJlIG1vdCBjb250aWd1b3VzIGFuZCBpZiB3ZSBjYW50IHRydXN0IHRpbWUgb2Zmc2V0LCBsZXQncyB1c2UgZmlyc3Qgc2FtcGxlIFBUUyBhcyBuZXh0IGF1ZGlvIFBUU1xuICAgICAgICBuZXh0QXVkaW9QdHMgPSBpbnB1dFNhbXBsZXNbMF0ucHRzO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaWYgdGltZU9mZnNldCBpcyBhY2N1cmF0ZSwgbGV0J3MgdXNlIGl0IGFzIHByZWRpY3RlZCBuZXh0IGF1ZGlvIFBUU1xuICAgICAgICBuZXh0QXVkaW9QdHMgPSBNYXRoLm1heCgwLCB0aW1lT2Zmc2V0ICogaW5wdXRUaW1lU2NhbGUpO1xuICAgICAgfVxuICAgIH0gLy8gSWYgdGhlIGF1ZGlvIHRyYWNrIGlzIG1pc3Npbmcgc2FtcGxlcywgdGhlIGZyYW1lcyBzZWVtIHRvIGdldCBcImxlZnQtc2hpZnRlZFwiIHdpdGhpbiB0aGVcbiAgICAvLyByZXN1bHRpbmcgbXA0IHNlZ21lbnQsIGNhdXNpbmcgc3luYyBpc3N1ZXMgYW5kIGxlYXZpbmcgZ2FwcyBhdCB0aGUgZW5kIG9mIHRoZSBhdWRpbyBzZWdtZW50LlxuICAgIC8vIEluIGFuIGVmZm9ydCB0byBwcmV2ZW50IHRoaXMgZnJvbSBoYXBwZW5pbmcsIHdlIGluamVjdCBmcmFtZXMgaGVyZSB3aGVyZSB0aGVyZSBhcmUgZ2Fwcy5cbiAgICAvLyBXaGVuIHBvc3NpYmxlLCB3ZSBpbmplY3QgYSBzaWxlbnQgZnJhbWU7IHdoZW4gdGhhdCdzIG5vdCBwb3NzaWJsZSwgd2UgZHVwbGljYXRlIHRoZSBsYXN0XG4gICAgLy8gZnJhbWUuXG5cblxuICAgIGlmICh0cmFjay5pc0FBQykge1xuICAgICAgdmFyIG1heEF1ZGlvRnJhbWVzRHJpZnQgPSB0aGlzLmNvbmZpZy5tYXhBdWRpb0ZyYW1lc0RyaWZ0O1xuXG4gICAgICBmb3IgKHZhciBpID0gMCwgbmV4dFB0cyA9IG5leHRBdWRpb1B0czsgaSA8IGlucHV0U2FtcGxlcy5sZW5ndGg7KSB7XG4gICAgICAgIC8vIEZpcnN0LCBsZXQncyBzZWUgaG93IGZhciBvZmYgdGhpcyBmcmFtZSBpcyBmcm9tIHdoZXJlIHdlIGV4cGVjdCBpdCB0byBiZVxuICAgICAgICB2YXIgc2FtcGxlID0gaW5wdXRTYW1wbGVzW2ldO1xuICAgICAgICB2YXIgcHRzID0gc2FtcGxlLnB0cztcbiAgICAgICAgdmFyIGRlbHRhID0gcHRzIC0gbmV4dFB0czsgLy8gSWYgd2UncmUgb3ZlcmxhcHBpbmcgYnkgbW9yZSB0aGFuIGEgZHVyYXRpb24sIGRyb3AgdGhpcyBzYW1wbGVcblxuICAgICAgICBpZiAoZGVsdGEgPD0gLW1heEF1ZGlvRnJhbWVzRHJpZnQgKiBpbnB1dFNhbXBsZUR1cmF0aW9uKSB7XG4gICAgICAgICAgaWYgKGNvbnRpZ3VvdXMgfHwgaSA+IDApIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiRHJvcHBpbmcgMSBhdWRpbyBmcmFtZSBAIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhuZXh0UHRzLCB0cnVlKSAvIDEwMDAgKyBcInMgZHVlIHRvIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhkZWx0YSwgdHJ1ZSkgKyBcIiBtcyBvdmVybGFwLlwiKTtcbiAgICAgICAgICAgIGlucHV0U2FtcGxlcy5zcGxpY2UoaSwgMSk7IC8vIERvbid0IHRvdWNoIG5leHRQdHNOb3JtIG9yIGlcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gV2hlbiBjaGFuZ2luZyBxdWFsaXRpZXMgd2UgY2FuJ3QgdHJ1c3QgdGhhdCBhdWRpbyBoYXMgYmVlbiBhcHBlbmRlZCB1cCB0byBuZXh0QXVkaW9QdHNcbiAgICAgICAgICAgIC8vIFdhcm4gYWJvdXQgdGhlIG92ZXJsYXAgYnV0IGRvIG5vdCBkcm9wIHNhbXBsZXMgYXMgdGhhdCBjYW4gaW50cm9kdWNlIGJ1ZmZlciBnYXBzXG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcIkF1ZGlvIGZyYW1lIEAgXCIgKyB0b01zRnJvbU1wZWdUc0Nsb2NrKHB0cywgdHJ1ZSkgLyAxMDAwICsgXCJzIG92ZXJsYXBzIG5leHRBdWRpb1B0cyBieSBcIiArIHRvTXNGcm9tTXBlZ1RzQ2xvY2soZGVsdGEsIHRydWUpICsgXCIgbXMuXCIpO1xuICAgICAgICAgICAgbmV4dFB0cyA9IHB0cyArIGlucHV0U2FtcGxlRHVyYXRpb247XG4gICAgICAgICAgICBpKys7XG4gICAgICAgICAgfVxuICAgICAgICB9IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgYnJhY2Utc3R5bGVcbiAgICAgICAgLy8gSW5zZXJ0IG1pc3NpbmcgZnJhbWVzIGlmOlxuICAgICAgICAvLyAxOiBXZSdyZSBtb3JlIHRoYW4gbWF4QXVkaW9GcmFtZXNEcmlmdCBmcmFtZSBhd2F5XG4gICAgICAgIC8vIDI6IE5vdCBtb3JlIHRoYW4gTUFYX1NJTEVOVF9GUkFNRV9EVVJBVElPTiBhd2F5XG4gICAgICAgIC8vIDM6IGN1cnJlbnRUaW1lIChha2EgbmV4dFB0c05vcm0pIGlzIG5vdCAwXG4gICAgICAgIGVsc2UgaWYgKGRlbHRhID49IG1heEF1ZGlvRnJhbWVzRHJpZnQgKiBpbnB1dFNhbXBsZUR1cmF0aW9uICYmIGRlbHRhIDwgTUFYX1NJTEVOVF9GUkFNRV9EVVJBVElPTl85MEtIWiAmJiBuZXh0UHRzKSB7XG4gICAgICAgICAgICB2YXIgbWlzc2luZyA9IE1hdGgucm91bmQoZGVsdGEgLyBpbnB1dFNhbXBsZUR1cmF0aW9uKTtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiSW5qZWN0aW5nIFwiICsgbWlzc2luZyArIFwiIGF1ZGlvIGZyYW1lcyBAIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhuZXh0UHRzLCB0cnVlKSAvIDEwMDAgKyBcInMgZHVlIHRvIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhkZWx0YSwgdHJ1ZSkgKyBcIiBtcyBnYXAuXCIpO1xuXG4gICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IG1pc3Npbmc7IGorKykge1xuICAgICAgICAgICAgICB2YXIgbmV3U3RhbXAgPSBNYXRoLm1heChuZXh0UHRzLCAwKTtcbiAgICAgICAgICAgICAgZmlsbEZyYW1lID0gYWFjX2hlbHBlci5nZXRTaWxlbnRGcmFtZSh0cmFjay5tYW5pZmVzdENvZGVjIHx8IHRyYWNrLmNvZGVjLCB0cmFjay5jaGFubmVsQ291bnQpO1xuXG4gICAgICAgICAgICAgIGlmICghZmlsbEZyYW1lKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnVW5hYmxlIHRvIGdldCBzaWxlbnQgZnJhbWUgZm9yIGdpdmVuIGF1ZGlvIGNvZGVjOyBkdXBsaWNhdGluZyBsYXN0IGZyYW1lIGluc3RlYWQuJyk7XG4gICAgICAgICAgICAgICAgZmlsbEZyYW1lID0gc2FtcGxlLnVuaXQuc3ViYXJyYXkoKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGlucHV0U2FtcGxlcy5zcGxpY2UoaSwgMCwge1xuICAgICAgICAgICAgICAgIHVuaXQ6IGZpbGxGcmFtZSxcbiAgICAgICAgICAgICAgICBwdHM6IG5ld1N0YW1wLFxuICAgICAgICAgICAgICAgIGR0czogbmV3U3RhbXBcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIG5leHRQdHMgKz0gaW5wdXRTYW1wbGVEdXJhdGlvbjtcbiAgICAgICAgICAgICAgaSsrO1xuICAgICAgICAgICAgfSAvLyBBZGp1c3Qgc2FtcGxlIHRvIG5leHQgZXhwZWN0ZWQgcHRzXG5cblxuICAgICAgICAgICAgc2FtcGxlLnB0cyA9IHNhbXBsZS5kdHMgPSBuZXh0UHRzO1xuICAgICAgICAgICAgbmV4dFB0cyArPSBpbnB1dFNhbXBsZUR1cmF0aW9uO1xuICAgICAgICAgICAgaSsrO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBPdGhlcndpc2UsIGp1c3QgYWRqdXN0IHB0c1xuICAgICAgICAgICAgaWYgKE1hdGguYWJzKGRlbHRhKSA+IDAuMSAqIGlucHV0U2FtcGxlRHVyYXRpb24pIHsvLyBsb2dnZXIubG9nKGBJbnZhbGlkIGZyYW1lIGRlbHRhICR7TWF0aC5yb3VuZChkZWx0YSArIGlucHV0U2FtcGxlRHVyYXRpb24pfSBhdCBQVFMgJHtNYXRoLnJvdW5kKHB0cyAvIDkwKX0gKHNob3VsZCBiZSAke01hdGgucm91bmQoaW5wdXRTYW1wbGVEdXJhdGlvbil9KS5gKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2FtcGxlLnB0cyA9IHNhbXBsZS5kdHMgPSBuZXh0UHRzO1xuICAgICAgICAgICAgbmV4dFB0cyArPSBpbnB1dFNhbXBsZUR1cmF0aW9uO1xuICAgICAgICAgICAgaSsrO1xuICAgICAgICAgIH1cbiAgICAgIH1cbiAgICB9IC8vIGNvbXB1dGUgbWRhdCBzaXplLCBhcyB3ZSBldmVudHVhbGx5IGZpbHRlcmVkL2FkZGVkIHNvbWUgc2FtcGxlc1xuXG5cbiAgICB2YXIgbmJTYW1wbGVzID0gaW5wdXRTYW1wbGVzLmxlbmd0aDtcbiAgICB2YXIgbWRhdFNpemUgPSAwO1xuXG4gICAgd2hpbGUgKG5iU2FtcGxlcy0tKSB7XG4gICAgICBtZGF0U2l6ZSArPSBpbnB1dFNhbXBsZXNbbmJTYW1wbGVzXS51bml0LmJ5dGVMZW5ndGg7XG4gICAgfVxuXG4gICAgZm9yICh2YXIgX2oyID0gMCwgX25iU2FtcGxlcyA9IGlucHV0U2FtcGxlcy5sZW5ndGg7IF9qMiA8IF9uYlNhbXBsZXM7IF9qMisrKSB7XG4gICAgICB2YXIgYXVkaW9TYW1wbGUgPSBpbnB1dFNhbXBsZXNbX2oyXTtcbiAgICAgIHZhciB1bml0ID0gYXVkaW9TYW1wbGUudW5pdDtcbiAgICAgIHZhciBfcHRzID0gYXVkaW9TYW1wbGUucHRzOyAvLyBsb2dnZXIubG9nKGBBdWRpby9QVFM6JHt0b01zRnJvbU1wZWdUc0Nsb2NrKHB0cywgdHJ1ZSl9YCk7XG4gICAgICAvLyBpZiBub3QgZmlyc3Qgc2FtcGxlXG5cbiAgICAgIGlmIChsYXN0UFRTICE9PSB1bmRlZmluZWQgJiYgbXA0U2FtcGxlKSB7XG4gICAgICAgIG1wNFNhbXBsZS5kdXJhdGlvbiA9IE1hdGgucm91bmQoKF9wdHMgLSBsYXN0UFRTKSAvIHNjYWxlRmFjdG9yKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBfZGVsdGEgPSBfcHRzIC0gbmV4dEF1ZGlvUHRzO1xuXG4gICAgICAgIHZhciBudW1NaXNzaW5nRnJhbWVzID0gMDsgLy8gaWYgZnJhZ21lbnQgYXJlIGNvbnRpZ3VvdXMsIGRldGVjdCBob2xlL292ZXJsYXBwaW5nIGJldHdlZW4gZnJhZ21lbnRzXG4gICAgICAgIC8vIGNvbnRpZ3VvdXMgZnJhZ21lbnRzIGFyZSBjb25zZWN1dGl2ZSBmcmFnbWVudHMgZnJvbSBzYW1lIHF1YWxpdHkgbGV2ZWwgKHNhbWUgbGV2ZWwsIG5ldyBTTiA9IG9sZCBTTiArIDEpXG5cbiAgICAgICAgaWYgKGNvbnRpZ3VvdXMgJiYgdHJhY2suaXNBQUMpIHtcbiAgICAgICAgICAvLyBsb2cgZGVsdGFcbiAgICAgICAgICBpZiAoX2RlbHRhKSB7XG4gICAgICAgICAgICBpZiAoX2RlbHRhID4gMCAmJiBfZGVsdGEgPCBNQVhfU0lMRU5UX0ZSQU1FX0RVUkFUSU9OXzkwS0haKSB7XG4gICAgICAgICAgICAgIC8vIFE6IHdoeSBkbyB3ZSBoYXZlIHRvIHJvdW5kIGhlcmUsIHNob3VsZG4ndCB0aGlzIGFsd2F5cyByZXN1bHQgaW4gYW4gaW50ZWdlciBpZiB0aW1lc3RhbXBzIGFyZSBjb3JyZWN0LFxuICAgICAgICAgICAgICAvLyBhbmQgaWYgbm90LCBzaG91bGRuJ3Qgd2UgYWN0dWFsbHkgTWF0aC5jZWlsKCkgaW5zdGVhZD9cbiAgICAgICAgICAgICAgbnVtTWlzc2luZ0ZyYW1lcyA9IE1hdGgucm91bmQoKF9wdHMgLSBuZXh0QXVkaW9QdHMpIC8gaW5wdXRTYW1wbGVEdXJhdGlvbik7XG4gICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2codG9Nc0Zyb21NcGVnVHNDbG9jayhfZGVsdGEsIHRydWUpICsgXCIgbXMgaG9sZSBiZXR3ZWVuIEFBQyBzYW1wbGVzIGRldGVjdGVkLGZpbGxpbmcgaXRcIik7XG5cbiAgICAgICAgICAgICAgaWYgKG51bU1pc3NpbmdGcmFtZXMgPiAwKSB7XG4gICAgICAgICAgICAgICAgZmlsbEZyYW1lID0gYWFjX2hlbHBlci5nZXRTaWxlbnRGcmFtZSh0cmFjay5tYW5pZmVzdENvZGVjIHx8IHRyYWNrLmNvZGVjLCB0cmFjay5jaGFubmVsQ291bnQpO1xuXG4gICAgICAgICAgICAgICAgaWYgKCFmaWxsRnJhbWUpIHtcbiAgICAgICAgICAgICAgICAgIGZpbGxGcmFtZSA9IHVuaXQuc3ViYXJyYXkoKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBtZGF0U2l6ZSArPSBudW1NaXNzaW5nRnJhbWVzICogZmlsbEZyYW1lLmxlbmd0aDtcbiAgICAgICAgICAgICAgfSAvLyBpZiB3ZSBoYXZlIGZyYW1lIG92ZXJsYXAsIG92ZXJsYXBwaW5nIGZvciBtb3JlIHRoYW4gaGFsZiBhIGZyYW1lIGR1cmFpb25cblxuICAgICAgICAgICAgfSBlbHNlIGlmIChfZGVsdGEgPCAtMTIpIHtcbiAgICAgICAgICAgICAgLy8gZHJvcCBvdmVybGFwcGluZyBhdWRpbyBmcmFtZXMuLi4gYnJvd3NlciB3aWxsIGRlYWwgd2l0aCBpdFxuICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiZHJvcCBvdmVybGFwcGluZyBBQUMgc2FtcGxlLCBleHBlY3RlZC9wYXJzZWQvZGVsdGE6IFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jayhuZXh0QXVkaW9QdHMsIHRydWUpICsgXCIgbXMgLyBcIiArIHRvTXNGcm9tTXBlZ1RzQ2xvY2soX3B0cywgdHJ1ZSkgKyBcIiBtcyAvIFwiICsgdG9Nc0Zyb21NcGVnVHNDbG9jaygtX2RlbHRhLCB0cnVlKSArIFwiIG1zXCIpO1xuICAgICAgICAgICAgICBtZGF0U2l6ZSAtPSB1bml0LmJ5dGVMZW5ndGg7XG4gICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfSAvLyBzZXQgUFRTL0RUUyB0byBleHBlY3RlZCBQVFMvRFRTXG5cblxuICAgICAgICAgICAgX3B0cyA9IG5leHRBdWRpb1B0cztcbiAgICAgICAgICB9XG4gICAgICAgIH0gLy8gcmVtZW1iZXIgZmlyc3QgUFRTIG9mIG91ciBhdWRpb1NhbXBsZXNcblxuXG4gICAgICAgIGZpcnN0UFRTID0gX3B0cztcblxuICAgICAgICBpZiAobWRhdFNpemUgPiAwKSB7XG4gICAgICAgICAgbWRhdFNpemUgKz0gb2Zmc2V0O1xuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIG1kYXQgPSBuZXcgVWludDhBcnJheShtZGF0U2l6ZSk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICB0aGlzLm9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1VWF9FUlJPUixcbiAgICAgICAgICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLlJFTVVYX0FMTE9DX0VSUk9SLFxuICAgICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICAgIGJ5dGVzOiBtZGF0U2l6ZSxcbiAgICAgICAgICAgICAgcmVhc29uOiBcImZhaWwgYWxsb2NhdGluZyBhdWRpbyBtZGF0IFwiICsgbWRhdFNpemVcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghcmF3TVBFRykge1xuICAgICAgICAgICAgdmFyIHZpZXcgPSBuZXcgRGF0YVZpZXcobWRhdC5idWZmZXIpO1xuICAgICAgICAgICAgdmlldy5zZXRVaW50MzIoMCwgbWRhdFNpemUpO1xuICAgICAgICAgICAgbWRhdC5zZXQobXA0X2dlbmVyYXRvci50eXBlcy5tZGF0LCA0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gbm8gYXVkaW8gc2FtcGxlc1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAodmFyIF9pNSA9IDA7IF9pNSA8IG51bU1pc3NpbmdGcmFtZXM7IF9pNSsrKSB7XG4gICAgICAgICAgZmlsbEZyYW1lID0gYWFjX2hlbHBlci5nZXRTaWxlbnRGcmFtZSh0cmFjay5tYW5pZmVzdENvZGVjIHx8IHRyYWNrLmNvZGVjLCB0cmFjay5jaGFubmVsQ291bnQpO1xuXG4gICAgICAgICAgaWYgKCFmaWxsRnJhbWUpIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ1VuYWJsZSB0byBnZXQgc2lsZW50IGZyYW1lIGZvciBnaXZlbiBhdWRpbyBjb2RlYzsgZHVwbGljYXRpbmcgdGhpcyBmcmFtZSBpbnN0ZWFkLicpO1xuICAgICAgICAgICAgZmlsbEZyYW1lID0gdW5pdC5zdWJhcnJheSgpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIG1kYXQuc2V0KGZpbGxGcmFtZSwgb2Zmc2V0KTtcbiAgICAgICAgICBvZmZzZXQgKz0gZmlsbEZyYW1lLmJ5dGVMZW5ndGg7XG4gICAgICAgICAgbXA0U2FtcGxlID0ge1xuICAgICAgICAgICAgc2l6ZTogZmlsbEZyYW1lLmJ5dGVMZW5ndGgsXG4gICAgICAgICAgICBjdHM6IDAsXG4gICAgICAgICAgICBkdXJhdGlvbjogMTAyNCxcbiAgICAgICAgICAgIGZsYWdzOiB7XG4gICAgICAgICAgICAgIGlzTGVhZGluZzogMCxcbiAgICAgICAgICAgICAgaXNEZXBlbmRlZE9uOiAwLFxuICAgICAgICAgICAgICBoYXNSZWR1bmRhbmN5OiAwLFxuICAgICAgICAgICAgICBkZWdyYWRQcmlvOiAwLFxuICAgICAgICAgICAgICBkZXBlbmRzT246IDFcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9O1xuICAgICAgICAgIG91dHB1dFNhbXBsZXMucHVzaChtcDRTYW1wbGUpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIG1kYXQuc2V0KHVuaXQsIG9mZnNldCk7XG4gICAgICB2YXIgdW5pdExlbiA9IHVuaXQuYnl0ZUxlbmd0aDtcbiAgICAgIG9mZnNldCArPSB1bml0TGVuOyAvLyBjb25zb2xlLmxvZygnUFRTL0RUUy9pbml0RFRTL25vcm1QVFMvbm9ybURUUy9yZWxhdGl2ZSBQVFMgOiAke2F1ZGlvU2FtcGxlLnB0c30vJHthdWRpb1NhbXBsZS5kdHN9LyR7aW5pdERUU30vJHtwdHNub3JtfS8ke2R0c25vcm19LyR7KGF1ZGlvU2FtcGxlLnB0cy80Mjk0OTY3Mjk2KS50b0ZpeGVkKDMpfScpO1xuXG4gICAgICBtcDRTYW1wbGUgPSB7XG4gICAgICAgIHNpemU6IHVuaXRMZW4sXG4gICAgICAgIGN0czogMCxcbiAgICAgICAgZHVyYXRpb246IDAsXG4gICAgICAgIGZsYWdzOiB7XG4gICAgICAgICAgaXNMZWFkaW5nOiAwLFxuICAgICAgICAgIGlzRGVwZW5kZWRPbjogMCxcbiAgICAgICAgICBoYXNSZWR1bmRhbmN5OiAwLFxuICAgICAgICAgIGRlZ3JhZFByaW86IDAsXG4gICAgICAgICAgZGVwZW5kc09uOiAxXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBvdXRwdXRTYW1wbGVzLnB1c2gobXA0U2FtcGxlKTtcbiAgICAgIGxhc3RQVFMgPSBfcHRzO1xuICAgIH1cblxuICAgIHZhciBsYXN0U2FtcGxlRHVyYXRpb24gPSAwO1xuICAgIG5iU2FtcGxlcyA9IG91dHB1dFNhbXBsZXMubGVuZ3RoOyAvLyBzZXQgbGFzdCBzYW1wbGUgZHVyYXRpb24gYXMgYmVpbmcgaWRlbnRpY2FsIHRvIHByZXZpb3VzIHNhbXBsZVxuXG4gICAgaWYgKG5iU2FtcGxlcyA+PSAyKSB7XG4gICAgICBsYXN0U2FtcGxlRHVyYXRpb24gPSBvdXRwdXRTYW1wbGVzW25iU2FtcGxlcyAtIDJdLmR1cmF0aW9uO1xuICAgICAgbXA0U2FtcGxlLmR1cmF0aW9uID0gbGFzdFNhbXBsZUR1cmF0aW9uO1xuICAgIH1cblxuICAgIGlmIChuYlNhbXBsZXMpIHtcbiAgICAgIC8vIG5leHQgYXVkaW8gc2FtcGxlIFBUUyBzaG91bGQgYmUgZXF1YWwgdG8gbGFzdCBzYW1wbGUgUFRTICsgZHVyYXRpb25cbiAgICAgIHRoaXMubmV4dEF1ZGlvUHRzID0gbmV4dEF1ZGlvUHRzID0gbGFzdFBUUyArIHNjYWxlRmFjdG9yICogbGFzdFNhbXBsZUR1cmF0aW9uOyAvLyBsb2dnZXIubG9nKCdBdWRpby9QVFMvUFRTZW5kOicgKyBhdWRpb1NhbXBsZS5wdHMudG9GaXhlZCgwKSArICcvJyArIHRoaXMubmV4dEFhY0R0cy50b0ZpeGVkKDApKTtcblxuICAgICAgdHJhY2suc2FtcGxlcyA9IG91dHB1dFNhbXBsZXM7XG5cbiAgICAgIGlmIChyYXdNUEVHKSB7XG4gICAgICAgIG1vb2YgPSBuZXcgVWludDhBcnJheSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbW9vZiA9IG1wNF9nZW5lcmF0b3IubW9vZih0cmFjay5zZXF1ZW5jZU51bWJlcisrLCBmaXJzdFBUUyAvIHNjYWxlRmFjdG9yLCB0cmFjayk7XG4gICAgICB9XG5cbiAgICAgIHRyYWNrLnNhbXBsZXMgPSBbXTtcbiAgICAgIHZhciBzdGFydCA9IGZpcnN0UFRTIC8gaW5wdXRUaW1lU2NhbGU7XG4gICAgICB2YXIgZW5kID0gbmV4dEF1ZGlvUHRzIC8gaW5wdXRUaW1lU2NhbGU7XG4gICAgICB2YXIgYXVkaW9EYXRhID0ge1xuICAgICAgICBkYXRhMTogbW9vZixcbiAgICAgICAgZGF0YTI6IG1kYXQsXG4gICAgICAgIHN0YXJ0UFRTOiBzdGFydCxcbiAgICAgICAgZW5kUFRTOiBlbmQsXG4gICAgICAgIHN0YXJ0RFRTOiBzdGFydCxcbiAgICAgICAgZW5kRFRTOiBlbmQsXG4gICAgICAgIHR5cGU6ICdhdWRpbycsXG4gICAgICAgIGhhc0F1ZGlvOiB0cnVlLFxuICAgICAgICBoYXNWaWRlbzogZmFsc2UsXG4gICAgICAgIG5iOiBuYlNhbXBsZXNcbiAgICAgIH07XG4gICAgICB0aGlzLm9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNJTkdfREFUQSwgYXVkaW9EYXRhKTtcbiAgICAgIHJldHVybiBhdWRpb0RhdGE7XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH07XG5cbiAgX3Byb3RvLnJlbXV4RW1wdHlBdWRpbyA9IGZ1bmN0aW9uIHJlbXV4RW1wdHlBdWRpbyh0cmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgdmlkZW9EYXRhKSB7XG4gICAgdmFyIGlucHV0VGltZVNjYWxlID0gdHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgdmFyIG1wNHRpbWVTY2FsZSA9IHRyYWNrLnNhbXBsZXJhdGUgPyB0cmFjay5zYW1wbGVyYXRlIDogaW5wdXRUaW1lU2NhbGU7XG4gICAgdmFyIHNjYWxlRmFjdG9yID0gaW5wdXRUaW1lU2NhbGUgLyBtcDR0aW1lU2NhbGU7XG4gICAgdmFyIG5leHRBdWRpb1B0cyA9IHRoaXMubmV4dEF1ZGlvUHRzOyAvLyBzeW5jIHdpdGggdmlkZW8ncyB0aW1lc3RhbXBcblxuICAgIHZhciBzdGFydERUUyA9IChuZXh0QXVkaW9QdHMgIT09IHVuZGVmaW5lZCA/IG5leHRBdWRpb1B0cyA6IHZpZGVvRGF0YS5zdGFydERUUyAqIGlucHV0VGltZVNjYWxlKSArIHRoaXMuX2luaXREVFM7XG4gICAgdmFyIGVuZERUUyA9IHZpZGVvRGF0YS5lbmREVFMgKiBpbnB1dFRpbWVTY2FsZSArIHRoaXMuX2luaXREVFM7IC8vIG9uZSBzYW1wbGUncyBkdXJhdGlvbiB2YWx1ZVxuXG4gICAgdmFyIHNhbXBsZUR1cmF0aW9uID0gMTAyNDtcbiAgICB2YXIgZnJhbWVEdXJhdGlvbiA9IHNjYWxlRmFjdG9yICogc2FtcGxlRHVyYXRpb247IC8vIHNhbXBsZXMgY291bnQgb2YgdGhpcyBzZWdtZW50J3MgZHVyYXRpb25cblxuICAgIHZhciBuYlNhbXBsZXMgPSBNYXRoLmNlaWwoKGVuZERUUyAtIHN0YXJ0RFRTKSAvIGZyYW1lRHVyYXRpb24pOyAvLyBzaWxlbnQgZnJhbWVcblxuICAgIHZhciBzaWxlbnRGcmFtZSA9IGFhY19oZWxwZXIuZ2V0U2lsZW50RnJhbWUodHJhY2subWFuaWZlc3RDb2RlYyB8fCB0cmFjay5jb2RlYywgdHJhY2suY2hhbm5lbENvdW50KTtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybigncmVtdXggZW1wdHkgQXVkaW8nKTsgLy8gQ2FuJ3QgcmVtdXggaWYgd2UgY2FuJ3QgZ2VuZXJhdGUgYSBzaWxlbnQgZnJhbWUuLi5cblxuICAgIGlmICghc2lsZW50RnJhbWUpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS50cmFjZSgnVW5hYmxlIHRvIHJlbXV4RW1wdHlBdWRpbyBzaW5jZSB3ZSB3ZXJlIHVuYWJsZSB0byBnZXQgYSBzaWxlbnQgZnJhbWUgZm9yIGdpdmVuIGF1ZGlvIGNvZGVjIScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBzYW1wbGVzID0gW107XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IG5iU2FtcGxlczsgaSsrKSB7XG4gICAgICB2YXIgc3RhbXAgPSBzdGFydERUUyArIGkgKiBmcmFtZUR1cmF0aW9uO1xuICAgICAgc2FtcGxlcy5wdXNoKHtcbiAgICAgICAgdW5pdDogc2lsZW50RnJhbWUsXG4gICAgICAgIHB0czogc3RhbXAsXG4gICAgICAgIGR0czogc3RhbXBcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRyYWNrLnNhbXBsZXMgPSBzYW1wbGVzO1xuICAgIHRoaXMucmVtdXhBdWRpbyh0cmFjaywgdGltZU9mZnNldCwgY29udGlndW91cyk7XG4gIH07XG5cbiAgX3Byb3RvLnJlbXV4SUQzID0gZnVuY3Rpb24gcmVtdXhJRDModHJhY2ssIHRpbWVPZmZzZXQpIHtcbiAgICB2YXIgbGVuZ3RoID0gdHJhY2suc2FtcGxlcy5sZW5ndGg7XG5cbiAgICBpZiAoIWxlbmd0aCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBpbnB1dFRpbWVTY2FsZSA9IHRyYWNrLmlucHV0VGltZVNjYWxlO1xuICAgIHZhciBpbml0UFRTID0gdGhpcy5faW5pdFBUUztcbiAgICB2YXIgaW5pdERUUyA9IHRoaXMuX2luaXREVFM7IC8vIGNvbnN1bWUgc2FtcGxlc1xuXG4gICAgZm9yICh2YXIgaW5kZXggPSAwOyBpbmRleCA8IGxlbmd0aDsgaW5kZXgrKykge1xuICAgICAgdmFyIHNhbXBsZSA9IHRyYWNrLnNhbXBsZXNbaW5kZXhdOyAvLyBzZXR0aW5nIGlkMyBwdHMsIGR0cyB0byByZWxhdGl2ZSB0aW1lXG4gICAgICAvLyB1c2luZyB0aGlzLl9pbml0UFRTIGFuZCB0aGlzLl9pbml0RFRTIHRvIGNhbGN1bGF0ZSByZWxhdGl2ZSB0aW1lXG5cbiAgICAgIHNhbXBsZS5wdHMgPSBQVFNOb3JtYWxpemUoc2FtcGxlLnB0cyAtIGluaXRQVFMsIHRpbWVPZmZzZXQgKiBpbnB1dFRpbWVTY2FsZSkgLyBpbnB1dFRpbWVTY2FsZTtcbiAgICAgIHNhbXBsZS5kdHMgPSBQVFNOb3JtYWxpemUoc2FtcGxlLmR0cyAtIGluaXREVFMsIHRpbWVPZmZzZXQgKiBpbnB1dFRpbWVTY2FsZSkgLyBpbnB1dFRpbWVTY2FsZTtcbiAgICB9XG5cbiAgICB0aGlzLm9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNJTkdfTUVUQURBVEEsIHtcbiAgICAgIHNhbXBsZXM6IHRyYWNrLnNhbXBsZXNcbiAgICB9KTtcbiAgICB0cmFjay5zYW1wbGVzID0gW107XG4gIH07XG5cbiAgX3Byb3RvLnJlbXV4VGV4dCA9IGZ1bmN0aW9uIHJlbXV4VGV4dCh0cmFjaywgdGltZU9mZnNldCkge1xuICAgIHZhciBsZW5ndGggPSB0cmFjay5zYW1wbGVzLmxlbmd0aDtcbiAgICB2YXIgaW5wdXRUaW1lU2NhbGUgPSB0cmFjay5pbnB1dFRpbWVTY2FsZTtcbiAgICB2YXIgaW5pdFBUUyA9IHRoaXMuX2luaXRQVFM7IC8vIGNvbnN1bWUgc2FtcGxlc1xuXG4gICAgaWYgKGxlbmd0aCkge1xuICAgICAgZm9yICh2YXIgaW5kZXggPSAwOyBpbmRleCA8IGxlbmd0aDsgaW5kZXgrKykge1xuICAgICAgICB2YXIgc2FtcGxlID0gdHJhY2suc2FtcGxlc1tpbmRleF07IC8vIHNldHRpbmcgdGV4dCBwdHMsIGR0cyB0byByZWxhdGl2ZSB0aW1lXG4gICAgICAgIC8vIHVzaW5nIHRoaXMuX2luaXRQVFMgYW5kIHRoaXMuX2luaXREVFMgdG8gY2FsY3VsYXRlIHJlbGF0aXZlIHRpbWVcblxuICAgICAgICBzYW1wbGUucHRzID0gUFRTTm9ybWFsaXplKHNhbXBsZS5wdHMgLSBpbml0UFRTLCB0aW1lT2Zmc2V0ICogaW5wdXRUaW1lU2NhbGUpIC8gaW5wdXRUaW1lU2NhbGU7XG4gICAgICB9XG5cbiAgICAgIHRyYWNrLnNhbXBsZXMuc29ydChmdW5jdGlvbiAoYSwgYikge1xuICAgICAgICByZXR1cm4gYS5wdHMgLSBiLnB0cztcbiAgICAgIH0pO1xuICAgICAgdGhpcy5vYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX1VTRVJEQVRBLCB7XG4gICAgICAgIHNhbXBsZXM6IHRyYWNrLnNhbXBsZXNcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHRyYWNrLnNhbXBsZXMgPSBbXTtcbiAgfTtcblxuICByZXR1cm4gTVA0UmVtdXhlcjtcbn0oKTtcblxuZnVuY3Rpb24gUFRTTm9ybWFsaXplKHZhbHVlLCByZWZlcmVuY2UpIHtcbiAgdmFyIG9mZnNldDtcblxuICBpZiAocmVmZXJlbmNlID09PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICBpZiAocmVmZXJlbmNlIDwgdmFsdWUpIHtcbiAgICAvLyAtIDJeMzNcbiAgICBvZmZzZXQgPSAtODU4OTkzNDU5MjtcbiAgfSBlbHNlIHtcbiAgICAvLyArIDJeMzNcbiAgICBvZmZzZXQgPSA4NTg5OTM0NTkyO1xuICB9XG4gIC8qIFBUUyBpcyAzM2JpdCAoZnJvbSAwIHRvIDJeMzMgLTEpXG4gICAgaWYgZGlmZiBiZXR3ZWVuIHZhbHVlIGFuZCByZWZlcmVuY2UgaXMgYmlnZ2VyIHRoYW4gaGFsZiBvZiB0aGUgYW1wbGl0dWRlICgyXjMyKSB0aGVuIGl0IG1lYW5zIHRoYXRcbiAgICBQVFMgbG9vcGluZyBvY2N1cmVkLiBmaWxsIHRoZSBnYXAgKi9cblxuXG4gIHdoaWxlIChNYXRoLmFicyh2YWx1ZSAtIHJlZmVyZW5jZSkgPiA0Mjk0OTY3Mjk2KSB7XG4gICAgdmFsdWUgKz0gb2Zmc2V0O1xuICB9XG5cbiAgcmV0dXJuIHZhbHVlO1xufVxuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBtcDRfcmVtdXhlciA9IChtcDRfcmVtdXhlcl9NUDRSZW11eGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3JlbXV4L3Bhc3N0aHJvdWdoLXJlbXV4ZXIuanNcbi8qKlxuICogcGFzc3Rocm91Z2ggcmVtdXhlclxuKi9cblxuXG52YXIgcGFzc3Rocm91Z2hfcmVtdXhlcl9QYXNzVGhyb3VnaFJlbXV4ZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBQYXNzVGhyb3VnaFJlbXV4ZXIob2JzZXJ2ZXIpIHtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gUGFzc1Rocm91Z2hSZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7fTtcblxuICBfcHJvdG8ucmVzZXRUaW1lU3RhbXAgPSBmdW5jdGlvbiByZXNldFRpbWVTdGFtcCgpIHt9O1xuXG4gIF9wcm90by5yZXNldEluaXRTZWdtZW50ID0gZnVuY3Rpb24gcmVzZXRJbml0U2VnbWVudCgpIHt9O1xuXG4gIF9wcm90by5yZW11eCA9IGZ1bmN0aW9uIHJlbXV4KGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIGlkM1RyYWNrLCB0ZXh0VHJhY2ssIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCwgcmF3RGF0YSkge1xuICAgIHZhciBvYnNlcnZlciA9IHRoaXMub2JzZXJ2ZXI7XG4gICAgdmFyIHN0cmVhbVR5cGUgPSAnJztcblxuICAgIGlmIChhdWRpb1RyYWNrKSB7XG4gICAgICBzdHJlYW1UeXBlICs9ICdhdWRpbyc7XG4gICAgfVxuXG4gICAgaWYgKHZpZGVvVHJhY2spIHtcbiAgICAgIHN0cmVhbVR5cGUgKz0gJ3ZpZGVvJztcbiAgICB9XG5cbiAgICBvYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX0RBVEEsIHtcbiAgICAgIGRhdGExOiByYXdEYXRhLFxuICAgICAgc3RhcnRQVFM6IHRpbWVPZmZzZXQsXG4gICAgICBzdGFydERUUzogdGltZU9mZnNldCxcbiAgICAgIHR5cGU6IHN0cmVhbVR5cGUsXG4gICAgICBoYXNBdWRpbzogISFhdWRpb1RyYWNrLFxuICAgICAgaGFzVmlkZW86ICEhdmlkZW9UcmFjayxcbiAgICAgIG5iOiAxLFxuICAgICAgZHJvcHBlZDogMFxuICAgIH0pOyAvLyBub3RpZnkgZW5kIG9mIHBhcnNpbmdcblxuICAgIG9ic2VydmVyLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNFRCk7XG4gIH07XG5cbiAgcmV0dXJuIFBhc3NUaHJvdWdoUmVtdXhlcjtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgcGFzc3Rocm91Z2hfcmVtdXhlciA9IChwYXNzdGhyb3VnaF9yZW11eGVyX1Bhc3NUaHJvdWdoUmVtdXhlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9kZW11eC9kZW11eGVyLWlubGluZS5qc1xuLyoqXG4gKlxuICogaW5saW5lIGRlbXV4ZXI6IHByb2JlIGZyYWdtZW50cyBhbmQgaW5zdGFudGlhdGVcbiAqIGFwcHJvcHJpYXRlIGRlbXV4ZXIgZGVwZW5kaW5nIG9uIGNvbnRlbnQgdHlwZSAoVFNEZW11eGVyLCBBQUNEZW11eGVyLCAuLi4pXG4gKlxuICovXG5cblxuXG5cblxuXG5cblxuXG5cbiAvLyBzZWUgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzExMjM3MjU5LzU4OTQ5M1xuXG52YXIgZ2xvYmFsID0gT2JqZWN0KGdldF9zZWxmX3Njb3BlW1wiZ2V0U2VsZlNjb3BlXCJdKSgpOyAvLyBzYWZlZ3VhcmQgZm9yIGNvZGUgdGhhdCBtaWdodCBydW4gYm90aCBvbiB3b3JrZXIgYW5kIG1haW4gdGhyZWFkXG5cbnZhciBub3c7IC8vIHBlcmZvcm1hbmNlLm5vdygpIG5vdCBhdmFpbGFibGUgb24gV2ViV29ya2VyLCBhdCBsZWFzdCBvbiBTYWZhcmkgRGVza3RvcFxuXG50cnkge1xuICBub3cgPSBnbG9iYWwucGVyZm9ybWFuY2Uubm93LmJpbmQoZ2xvYmFsLnBlcmZvcm1hbmNlKTtcbn0gY2F0Y2ggKGVycikge1xuICBsb2dnZXJbXCJsb2dnZXJcIl0uZGVidWcoJ1VuYWJsZSB0byB1c2UgUGVyZm9ybWFuY2UgQVBJIG9uIHRoaXMgZW52aXJvbm1lbnQnKTtcbiAgbm93ID0gZ2xvYmFsLkRhdGUubm93O1xufVxuXG52YXIgZGVtdXhlcl9pbmxpbmVfRGVtdXhlcklubGluZSA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIERlbXV4ZXJJbmxpbmUob2JzZXJ2ZXIsIHR5cGVTdXBwb3J0ZWQsIGNvbmZpZywgdmVuZG9yKSB7XG4gICAgdGhpcy5vYnNlcnZlciA9IG9ic2VydmVyO1xuICAgIHRoaXMudHlwZVN1cHBvcnRlZCA9IHR5cGVTdXBwb3J0ZWQ7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy52ZW5kb3IgPSB2ZW5kb3I7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gRGVtdXhlcklubGluZS5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHZhciBkZW11eGVyID0gdGhpcy5kZW11eGVyO1xuXG4gICAgaWYgKGRlbXV4ZXIpIHtcbiAgICAgIGRlbXV4ZXIuZGVzdHJveSgpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ucHVzaCA9IGZ1bmN0aW9uIHB1c2goZGF0YSwgZGVjcnlwdGRhdGEsIGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0aW1lT2Zmc2V0LCBkaXNjb250aW51aXR5LCB0cmFja1N3aXRjaCwgY29udGlndW91cywgZHVyYXRpb24sIGFjY3VyYXRlVGltZU9mZnNldCwgZGVmYXVsdEluaXRQVFMpIHtcbiAgICB2YXIgX3RoaXMgPSB0aGlzO1xuXG4gICAgaWYgKGRhdGEuYnl0ZUxlbmd0aCA+IDAgJiYgZGVjcnlwdGRhdGEgIT0gbnVsbCAmJiBkZWNyeXB0ZGF0YS5rZXkgIT0gbnVsbCAmJiBkZWNyeXB0ZGF0YS5tZXRob2QgPT09ICdBRVMtMTI4Jykge1xuICAgICAgdmFyIGRlY3J5cHRlciA9IHRoaXMuZGVjcnlwdGVyO1xuXG4gICAgICBpZiAoZGVjcnlwdGVyID09IG51bGwpIHtcbiAgICAgICAgZGVjcnlwdGVyID0gdGhpcy5kZWNyeXB0ZXIgPSBuZXcgY3J5cHRfZGVjcnlwdGVyW1wiZGVmYXVsdFwiXSh0aGlzLm9ic2VydmVyLCB0aGlzLmNvbmZpZyk7XG4gICAgICB9XG5cbiAgICAgIHZhciBzdGFydFRpbWUgPSBub3coKTtcbiAgICAgIGRlY3J5cHRlci5kZWNyeXB0KGRhdGEsIGRlY3J5cHRkYXRhLmtleS5idWZmZXIsIGRlY3J5cHRkYXRhLml2LmJ1ZmZlciwgZnVuY3Rpb24gKGRlY3J5cHRlZERhdGEpIHtcbiAgICAgICAgdmFyIGVuZFRpbWUgPSBub3coKTtcblxuICAgICAgICBfdGhpcy5vYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19ERUNSWVBURUQsIHtcbiAgICAgICAgICBzdGF0czoge1xuICAgICAgICAgICAgdHN0YXJ0OiBzdGFydFRpbWUsXG4gICAgICAgICAgICB0ZGVjcnlwdDogZW5kVGltZVxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgX3RoaXMucHVzaERlY3J5cHRlZChuZXcgVWludDhBcnJheShkZWNyeXB0ZWREYXRhKSwgZGVjcnlwdGRhdGEsIG5ldyBVaW50OEFycmF5KGluaXRTZWdtZW50KSwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdGltZU9mZnNldCwgZGlzY29udGludWl0eSwgdHJhY2tTd2l0Y2gsIGNvbnRpZ3VvdXMsIGR1cmF0aW9uLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGRlZmF1bHRJbml0UFRTKTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnB1c2hEZWNyeXB0ZWQobmV3IFVpbnQ4QXJyYXkoZGF0YSksIGRlY3J5cHRkYXRhLCBuZXcgVWludDhBcnJheShpbml0U2VnbWVudCksIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIHRpbWVPZmZzZXQsIGRpc2NvbnRpbnVpdHksIHRyYWNrU3dpdGNoLCBjb250aWd1b3VzLCBkdXJhdGlvbiwgYWNjdXJhdGVUaW1lT2Zmc2V0LCBkZWZhdWx0SW5pdFBUUyk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5wdXNoRGVjcnlwdGVkID0gZnVuY3Rpb24gcHVzaERlY3J5cHRlZChkYXRhLCBkZWNyeXB0ZGF0YSwgaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIHRpbWVPZmZzZXQsIGRpc2NvbnRpbnVpdHksIHRyYWNrU3dpdGNoLCBjb250aWd1b3VzLCBkdXJhdGlvbiwgYWNjdXJhdGVUaW1lT2Zmc2V0LCBkZWZhdWx0SW5pdFBUUykge1xuICAgIHZhciBkZW11eGVyID0gdGhpcy5kZW11eGVyO1xuICAgIHZhciByZW11eGVyID0gdGhpcy5yZW11eGVyO1xuXG4gICAgaWYgKCFkZW11eGVyIHx8IC8vIGluIGNhc2Ugb2YgY29udGludWl0eSBjaGFuZ2UsIG9yIHRyYWNrIHN3aXRjaFxuICAgIC8vIHdlIG1pZ2h0IHN3aXRjaCBmcm9tIGNvbnRlbnQgdHlwZSAoQUFDIGNvbnRhaW5lciB0byBUUyBjb250YWluZXIsIG9yIFRTIHRvIGZtcDQgZm9yIGV4YW1wbGUpXG4gICAgZGlzY29udGludWl0eSB8fCB0cmFja1N3aXRjaCkge1xuICAgICAgdmFyIG9ic2VydmVyID0gdGhpcy5vYnNlcnZlcjtcbiAgICAgIHZhciB0eXBlU3VwcG9ydGVkID0gdGhpcy50eXBlU3VwcG9ydGVkO1xuICAgICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnOyAvLyBwcm9iaW5nIG9yZGVyIGlzIFRTL01QNC9BQUMvTVAzXG5cbiAgICAgIHZhciBtdXhDb25maWcgPSBbe1xuICAgICAgICBkZW11eDogdHNkZW11eGVyLFxuICAgICAgICByZW11eDogbXA0X3JlbXV4ZXJcbiAgICAgIH0sIHtcbiAgICAgICAgZGVtdXg6IG1wNGRlbXV4ZXJbXCJkZWZhdWx0XCJdLFxuICAgICAgICByZW11eDogcGFzc3Rocm91Z2hfcmVtdXhlclxuICAgICAgfSwge1xuICAgICAgICBkZW11eDogYWFjZGVtdXhlcixcbiAgICAgICAgcmVtdXg6IG1wNF9yZW11eGVyXG4gICAgICB9LCB7XG4gICAgICAgIGRlbXV4OiBtcDNkZW11eGVyLFxuICAgICAgICByZW11eDogbXA0X3JlbXV4ZXJcbiAgICAgIH1dOyAvLyBwcm9iZSBmb3IgY29udGVudCB0eXBlXG5cbiAgICAgIHZhciBtdXg7XG5cbiAgICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBtdXhDb25maWcubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgbXV4ID0gbXV4Q29uZmlnW2ldO1xuXG4gICAgICAgIGlmIChtdXguZGVtdXgucHJvYmUoZGF0YSkpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoIW11eCkge1xuICAgICAgICBvYnNlcnZlci50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5GUkFHX1BBUlNJTkdfRVJST1IsXG4gICAgICAgICAgZmF0YWw6IHRydWUsXG4gICAgICAgICAgcmVhc29uOiAnbm8gZGVtdXggbWF0Y2hpbmcgd2l0aCBjb250ZW50IGZvdW5kJ1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSAvLyBzbyBsZXQncyBjaGVjayB0aGF0IGN1cnJlbnQgcmVtdXhlciBhbmQgZGVtdXhlciBhcmUgc3RpbGwgdmFsaWRcblxuXG4gICAgICBpZiAoIXJlbXV4ZXIgfHwgIShyZW11eGVyIGluc3RhbmNlb2YgbXV4LnJlbXV4KSkge1xuICAgICAgICByZW11eGVyID0gbmV3IG11eC5yZW11eChvYnNlcnZlciwgY29uZmlnLCB0eXBlU3VwcG9ydGVkLCB0aGlzLnZlbmRvcik7XG4gICAgICB9XG5cbiAgICAgIGlmICghZGVtdXhlciB8fCAhKGRlbXV4ZXIgaW5zdGFuY2VvZiBtdXguZGVtdXgpKSB7XG4gICAgICAgIGRlbXV4ZXIgPSBuZXcgbXV4LmRlbXV4KG9ic2VydmVyLCByZW11eGVyLCBjb25maWcsIHR5cGVTdXBwb3J0ZWQpO1xuICAgICAgICB0aGlzLnByb2JlID0gbXV4LmRlbXV4LnByb2JlO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmRlbXV4ZXIgPSBkZW11eGVyO1xuICAgICAgdGhpcy5yZW11eGVyID0gcmVtdXhlcjtcbiAgICB9XG5cbiAgICBpZiAoZGlzY29udGludWl0eSB8fCB0cmFja1N3aXRjaCkge1xuICAgICAgZGVtdXhlci5yZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCBkdXJhdGlvbik7XG4gICAgICByZW11eGVyLnJlc2V0SW5pdFNlZ21lbnQoKTtcbiAgICB9XG5cbiAgICBpZiAoZGlzY29udGludWl0eSkge1xuICAgICAgZGVtdXhlci5yZXNldFRpbWVTdGFtcChkZWZhdWx0SW5pdFBUUyk7XG4gICAgICByZW11eGVyLnJlc2V0VGltZVN0YW1wKGRlZmF1bHRJbml0UFRTKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGRlbXV4ZXIuc2V0RGVjcnlwdERhdGEgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGRlbXV4ZXIuc2V0RGVjcnlwdERhdGEoZGVjcnlwdGRhdGEpO1xuICAgIH1cblxuICAgIGRlbXV4ZXIuYXBwZW5kKGRhdGEsIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCk7XG4gIH07XG5cbiAgcmV0dXJuIERlbXV4ZXJJbmxpbmU7XG59KCk7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGRlbXV4ZXJfaW5saW5lID0gX193ZWJwYWNrX2V4cG9ydHNfX1tcImRlZmF1bHRcIl0gPSAoZGVtdXhlcl9pbmxpbmVfRGVtdXhlcklubGluZSk7XG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzXCI6XG4vKiEqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqISpcXFxuICAhKioqIC4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyohIGV4cG9ydHMgcHJvdmlkZWQ6IGRlZmF1bHQgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IE1vZHVsZSBpcyByZWZlcmVuY2VkIGZyb20gdGhlc2UgbW9kdWxlcyB3aXRoIHVuc3VwcG9ydGVkIHN5bnRheDogLi9zcmMvZGVtdXgvZGVtdXhlci5qcyAocmVmZXJlbmNlZCB3aXRoIHJlcXVpcmUucmVzb2x2ZSkgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIF9fd2VicGFja19leHBvcnRzX18sIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG4vKiBoYXJtb255IGltcG9ydCAqLyB2YXIgX2RlbXV4X2RlbXV4ZXJfaW5saW5lX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8wX18gPSBfX3dlYnBhY2tfcmVxdWlyZV9fKC8qISAuLi9kZW11eC9kZW11eGVyLWlubGluZSAqLyBcIi4vc3JjL2RlbXV4L2RlbXV4ZXItaW5saW5lLmpzXCIpO1xuLyogaGFybW9ueSBpbXBvcnQgKi8gdmFyIF9ldmVudHNfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzFfXyA9IF9fd2VicGFja19yZXF1aXJlX18oLyohIC4uL2V2ZW50cyAqLyBcIi4vc3JjL2V2ZW50cy5qc1wiKTtcbi8qIGhhcm1vbnkgaW1wb3J0ICovIHZhciBfdXRpbHNfbG9nZ2VyX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8yX18gPSBfX3dlYnBhY2tfcmVxdWlyZV9fKC8qISAuLi91dGlscy9sb2dnZXIgKi8gXCIuL3NyYy91dGlscy9sb2dnZXIuanNcIik7XG4vKiBoYXJtb255IGltcG9ydCAqLyB2YXIgZXZlbnRlbWl0dGVyM19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfM19fID0gX193ZWJwYWNrX3JlcXVpcmVfXygvKiEgZXZlbnRlbWl0dGVyMyAqLyBcIi4vbm9kZV9tb2R1bGVzL2V2ZW50ZW1pdHRlcjMvaW5kZXguanNcIik7XG4vKiBoYXJtb255IGltcG9ydCAqLyB2YXIgZXZlbnRlbWl0dGVyM19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfM19fX2RlZmF1bHQgPSAvKiNfX1BVUkVfXyovX193ZWJwYWNrX3JlcXVpcmVfXy5uKGV2ZW50ZW1pdHRlcjNfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzNfXyk7XG4vKiBkZW11eGVyIHdlYiB3b3JrZXIuXG4gKiAgLSBsaXN0ZW4gdG8gd29ya2VyIG1lc3NhZ2UsIGFuZCB0cmlnZ2VyIERlbXV4ZXJJbmxpbmUgdXBvbiByZWNlcHRpb24gb2YgRnJhZ21lbnRzLlxuICogIC0gcHJvdmlkZXMgTVA0IEJveGVzIGJhY2sgdG8gbWFpbiB0aHJlYWQgdXNpbmcgW3RyYW5zZmVyYWJsZSBvYmplY3RzXShodHRwczovL2RldmVsb3BlcnMuZ29vZ2xlLmNvbS93ZWIvdXBkYXRlcy8yMDExLzEyL1RyYW5zZmVyYWJsZS1PYmplY3RzLUxpZ2h0bmluZy1GYXN0KSBpbiBvcmRlciB0byBtaW5pbWl6ZSBtZXNzYWdlIHBhc3Npbmcgb3ZlcmhlYWQuXG4gKi9cblxuXG5cblxuXG52YXIgRGVtdXhlcldvcmtlciA9IGZ1bmN0aW9uIERlbXV4ZXJXb3JrZXIoc2VsZikge1xuICAvLyBvYnNlcnZlciBzZXR1cFxuICB2YXIgb2JzZXJ2ZXIgPSBuZXcgZXZlbnRlbWl0dGVyM19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfM19fW1wiRXZlbnRFbWl0dGVyXCJdKCk7XG5cbiAgb2JzZXJ2ZXIudHJpZ2dlciA9IGZ1bmN0aW9uIHRyaWdnZXIoZXZlbnQpIHtcbiAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgZGF0YSA9IG5ldyBBcnJheShfbGVuID4gMSA/IF9sZW4gLSAxIDogMCksIF9rZXkgPSAxOyBfa2V5IDwgX2xlbjsgX2tleSsrKSB7XG4gICAgICBkYXRhW19rZXkgLSAxXSA9IGFyZ3VtZW50c1tfa2V5XTtcbiAgICB9XG5cbiAgICBvYnNlcnZlci5lbWl0LmFwcGx5KG9ic2VydmVyLCBbZXZlbnQsIGV2ZW50XS5jb25jYXQoZGF0YSkpO1xuICB9O1xuXG4gIG9ic2VydmVyLm9mZiA9IGZ1bmN0aW9uIG9mZihldmVudCkge1xuICAgIGZvciAodmFyIF9sZW4yID0gYXJndW1lbnRzLmxlbmd0aCwgZGF0YSA9IG5ldyBBcnJheShfbGVuMiA+IDEgPyBfbGVuMiAtIDEgOiAwKSwgX2tleTIgPSAxOyBfa2V5MiA8IF9sZW4yOyBfa2V5MisrKSB7XG4gICAgICBkYXRhW19rZXkyIC0gMV0gPSBhcmd1bWVudHNbX2tleTJdO1xuICAgIH1cblxuICAgIG9ic2VydmVyLnJlbW92ZUxpc3RlbmVyLmFwcGx5KG9ic2VydmVyLCBbZXZlbnRdLmNvbmNhdChkYXRhKSk7XG4gIH07XG5cbiAgdmFyIGZvcndhcmRNZXNzYWdlID0gZnVuY3Rpb24gZm9yd2FyZE1lc3NhZ2UoZXYsIGRhdGEpIHtcbiAgICBzZWxmLnBvc3RNZXNzYWdlKHtcbiAgICAgIGV2ZW50OiBldixcbiAgICAgIGRhdGE6IGRhdGFcbiAgICB9KTtcbiAgfTtcblxuICBzZWxmLmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCBmdW5jdGlvbiAoZXYpIHtcbiAgICB2YXIgZGF0YSA9IGV2LmRhdGE7IC8vIGNvbnNvbGUubG9nKCdkZW11eGVyIGNtZDonICsgZGF0YS5jbWQpO1xuXG4gICAgc3dpdGNoIChkYXRhLmNtZCkge1xuICAgICAgY2FzZSAnaW5pdCc6XG4gICAgICAgIHZhciBjb25maWcgPSBKU09OLnBhcnNlKGRhdGEuY29uZmlnKTtcbiAgICAgICAgc2VsZi5kZW11eGVyID0gbmV3IF9kZW11eF9kZW11eGVyX2lubGluZV9fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMF9fW1wiZGVmYXVsdFwiXShvYnNlcnZlciwgZGF0YS50eXBlU3VwcG9ydGVkLCBjb25maWcsIGRhdGEudmVuZG9yKTtcbiAgICAgICAgT2JqZWN0KF91dGlsc19sb2dnZXJfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzJfX1tcImVuYWJsZUxvZ3NcIl0pKGNvbmZpZy5kZWJ1Zyk7IC8vIHNpZ25hbCBlbmQgb2Ygd29ya2VyIGluaXRcblxuICAgICAgICBmb3J3YXJkTWVzc2FnZSgnaW5pdCcsIG51bGwpO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSAnZGVtdXgnOlxuICAgICAgICBzZWxmLmRlbXV4ZXIucHVzaChkYXRhLmRhdGEsIGRhdGEuZGVjcnlwdGRhdGEsIGRhdGEuaW5pdFNlZ21lbnQsIGRhdGEuYXVkaW9Db2RlYywgZGF0YS52aWRlb0NvZGVjLCBkYXRhLnRpbWVPZmZzZXQsIGRhdGEuZGlzY29udGludWl0eSwgZGF0YS50cmFja1N3aXRjaCwgZGF0YS5jb250aWd1b3VzLCBkYXRhLmR1cmF0aW9uLCBkYXRhLmFjY3VyYXRlVGltZU9mZnNldCwgZGF0YS5kZWZhdWx0SW5pdFBUUyk7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBicmVhaztcbiAgICB9XG4gIH0pOyAvLyBmb3J3YXJkIGV2ZW50cyB0byBtYWluIHRocmVhZFxuXG4gIG9ic2VydmVyLm9uKF9ldmVudHNfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzFfX1tcImRlZmF1bHRcIl0uRlJBR19ERUNSWVBURUQsIGZvcndhcmRNZXNzYWdlKTtcbiAgb2JzZXJ2ZXIub24oX2V2ZW50c19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMV9fW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNJTkdfSU5JVF9TRUdNRU5ULCBmb3J3YXJkTWVzc2FnZSk7XG4gIG9ic2VydmVyLm9uKF9ldmVudHNfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzFfX1tcImRlZmF1bHRcIl0uRlJBR19QQVJTRUQsIGZvcndhcmRNZXNzYWdlKTtcbiAgb2JzZXJ2ZXIub24oX2V2ZW50c19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMV9fW1wiZGVmYXVsdFwiXS5FUlJPUiwgZm9yd2FyZE1lc3NhZ2UpO1xuICBvYnNlcnZlci5vbihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19NRVRBREFUQSwgZm9yd2FyZE1lc3NhZ2UpO1xuICBvYnNlcnZlci5vbihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19VU0VSREFUQSwgZm9yd2FyZE1lc3NhZ2UpO1xuICBvYnNlcnZlci5vbihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLklOSVRfUFRTX0ZPVU5ELCBmb3J3YXJkTWVzc2FnZSk7IC8vIHNwZWNpYWwgY2FzZSBmb3IgRlJBR19QQVJTSU5HX0RBVEE6IHBhc3MgZGF0YTEvZGF0YTIgYXMgdHJhbnNmZXJhYmxlIG9iamVjdCAobm8gY29weSlcblxuICBvYnNlcnZlci5vbihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19EQVRBLCBmdW5jdGlvbiAoZXYsIGRhdGEpIHtcbiAgICB2YXIgdHJhbnNmZXJhYmxlID0gW107XG4gICAgdmFyIG1lc3NhZ2UgPSB7XG4gICAgICBldmVudDogZXYsXG4gICAgICBkYXRhOiBkYXRhXG4gICAgfTtcblxuICAgIGlmIChkYXRhLmRhdGExKSB7XG4gICAgICBtZXNzYWdlLmRhdGExID0gZGF0YS5kYXRhMS5idWZmZXI7XG4gICAgICB0cmFuc2ZlcmFibGUucHVzaChkYXRhLmRhdGExLmJ1ZmZlcik7XG4gICAgICBkZWxldGUgZGF0YS5kYXRhMTtcbiAgICB9XG5cbiAgICBpZiAoZGF0YS5kYXRhMikge1xuICAgICAgbWVzc2FnZS5kYXRhMiA9IGRhdGEuZGF0YTIuYnVmZmVyO1xuICAgICAgdHJhbnNmZXJhYmxlLnB1c2goZGF0YS5kYXRhMi5idWZmZXIpO1xuICAgICAgZGVsZXRlIGRhdGEuZGF0YTI7XG4gICAgfVxuXG4gICAgc2VsZi5wb3N0TWVzc2FnZShtZXNzYWdlLCB0cmFuc2ZlcmFibGUpO1xuICB9KTtcbn07XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gX193ZWJwYWNrX2V4cG9ydHNfX1tcImRlZmF1bHRcIl0gPSAoRGVtdXhlcldvcmtlcik7XG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2RlbXV4L2lkMy5qc1wiOlxuLyohKioqKioqKioqKioqKioqKioqKioqKioqKiohKlxcXG4gICEqKiogLi9zcmMvZGVtdXgvaWQzLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qISBleHBvcnRzIHByb3ZpZGVkOiBkZWZhdWx0LCB1dGY4QXJyYXlUb1N0ciAqL1xuLyoqKi8gKGZ1bmN0aW9uKG1vZHVsZSwgX193ZWJwYWNrX2V4cG9ydHNfXywgX193ZWJwYWNrX3JlcXVpcmVfXykge1xuXG5cInVzZSBzdHJpY3RcIjtcbl9fd2VicGFja19yZXF1aXJlX18ucihfX3dlYnBhY2tfZXhwb3J0c19fKTtcbi8qIGhhcm1vbnkgZXhwb3J0IChiaW5kaW5nKSAqLyBfX3dlYnBhY2tfcmVxdWlyZV9fLmQoX193ZWJwYWNrX2V4cG9ydHNfXywgXCJ1dGY4QXJyYXlUb1N0clwiLCBmdW5jdGlvbigpIHsgcmV0dXJuIHV0ZjhBcnJheVRvU3RyOyB9KTtcbi8qIGhhcm1vbnkgaW1wb3J0ICovIHZhciBfdXRpbHNfZ2V0X3NlbGZfc2NvcGVfX1dFQlBBQ0tfSU1QT1JURURfTU9EVUxFXzBfXyA9IF9fd2VicGFja19yZXF1aXJlX18oLyohIC4uL3V0aWxzL2dldC1zZWxmLXNjb3BlICovIFwiLi9zcmMvdXRpbHMvZ2V0LXNlbGYtc2NvcGUuanNcIik7XG5cbi8qKlxuICogSUQzIHBhcnNlclxuICovXG5cbnZhciBJRDMgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBJRDMoKSB7fVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRydWUgaWYgYW4gSUQzIGhlYWRlciBjYW4gYmUgZm91bmQgYXQgb2Zmc2V0IGluIGRhdGFcbiAgICogQHBhcmFtIHtVaW50OEFycmF5fSBkYXRhIC0gVGhlIGRhdGEgdG8gc2VhcmNoIGluXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBvZmZzZXQgLSBUaGUgb2Zmc2V0IGF0IHdoaWNoIHRvIHN0YXJ0IHNlYXJjaGluZ1xuICAgKiBAcmV0dXJuIHtib29sZWFufSAtIFRydWUgaWYgYW4gSUQzIGhlYWRlciBpcyBmb3VuZFxuICAgKi9cbiAgSUQzLmlzSGVhZGVyID0gZnVuY3Rpb24gaXNIZWFkZXIoZGF0YSwgb2Zmc2V0KSB7XG4gICAgLypcbiAgICAqIGh0dHA6Ly9pZDMub3JnL2lkM3YyLjMuMFxuICAgICogWzBdICAgICA9ICdJJ1xuICAgICogWzFdICAgICA9ICdEJ1xuICAgICogWzJdICAgICA9ICczJ1xuICAgICogWzMsNF0gICA9IHtWZXJzaW9ufVxuICAgICogWzVdICAgICA9IHtGbGFnc31cbiAgICAqIFs2LTldICAgPSB7SUQzIFNpemV9XG4gICAgKlxuICAgICogQW4gSUQzdjIgdGFnIGNhbiBiZSBkZXRlY3RlZCB3aXRoIHRoZSBmb2xsb3dpbmcgcGF0dGVybjpcbiAgICAqICAkNDkgNDQgMzMgeXkgeXkgeHggenogenogenogenpcbiAgICAqIFdoZXJlIHl5IGlzIGxlc3MgdGhhbiAkRkYsIHh4IGlzIHRoZSAnZmxhZ3MnIGJ5dGUgYW5kIHp6IGlzIGxlc3MgdGhhbiAkODBcbiAgICAqL1xuICAgIGlmIChvZmZzZXQgKyAxMCA8PSBkYXRhLmxlbmd0aCkge1xuICAgICAgLy8gbG9vayBmb3IgJ0lEMycgaWRlbnRpZmllclxuICAgICAgaWYgKGRhdGFbb2Zmc2V0XSA9PT0gMHg0OSAmJiBkYXRhW29mZnNldCArIDFdID09PSAweDQ0ICYmIGRhdGFbb2Zmc2V0ICsgMl0gPT09IDB4MzMpIHtcbiAgICAgICAgLy8gY2hlY2sgdmVyc2lvbiBpcyB3aXRoaW4gcmFuZ2VcbiAgICAgICAgaWYgKGRhdGFbb2Zmc2V0ICsgM10gPCAweEZGICYmIGRhdGFbb2Zmc2V0ICsgNF0gPCAweEZGKSB7XG4gICAgICAgICAgLy8gY2hlY2sgc2l6ZSBpcyB3aXRoaW4gcmFuZ2VcbiAgICAgICAgICBpZiAoZGF0YVtvZmZzZXQgKyA2XSA8IDB4ODAgJiYgZGF0YVtvZmZzZXQgKyA3XSA8IDB4ODAgJiYgZGF0YVtvZmZzZXQgKyA4XSA8IDB4ODAgJiYgZGF0YVtvZmZzZXQgKyA5XSA8IDB4ODApIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICAvKipcbiAgICogUmV0dXJucyB0cnVlIGlmIGFuIElEMyBmb290ZXIgY2FuIGJlIGZvdW5kIGF0IG9mZnNldCBpbiBkYXRhXG4gICAqIEBwYXJhbSB7VWludDhBcnJheX0gZGF0YSAtIFRoZSBkYXRhIHRvIHNlYXJjaCBpblxuICAgKiBAcGFyYW0ge251bWJlcn0gb2Zmc2V0IC0gVGhlIG9mZnNldCBhdCB3aGljaCB0byBzdGFydCBzZWFyY2hpbmdcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gLSBUcnVlIGlmIGFuIElEMyBmb290ZXIgaXMgZm91bmRcbiAgICovXG4gIDtcblxuICBJRDMuaXNGb290ZXIgPSBmdW5jdGlvbiBpc0Zvb3RlcihkYXRhLCBvZmZzZXQpIHtcbiAgICAvKlxuICAgICogVGhlIGZvb3RlciBpcyBhIGNvcHkgb2YgdGhlIGhlYWRlciwgYnV0IHdpdGggYSBkaWZmZXJlbnQgaWRlbnRpZmllclxuICAgICovXG4gICAgaWYgKG9mZnNldCArIDEwIDw9IGRhdGEubGVuZ3RoKSB7XG4gICAgICAvLyBsb29rIGZvciAnM0RJJyBpZGVudGlmaWVyXG4gICAgICBpZiAoZGF0YVtvZmZzZXRdID09PSAweDMzICYmIGRhdGFbb2Zmc2V0ICsgMV0gPT09IDB4NDQgJiYgZGF0YVtvZmZzZXQgKyAyXSA9PT0gMHg0OSkge1xuICAgICAgICAvLyBjaGVjayB2ZXJzaW9uIGlzIHdpdGhpbiByYW5nZVxuICAgICAgICBpZiAoZGF0YVtvZmZzZXQgKyAzXSA8IDB4RkYgJiYgZGF0YVtvZmZzZXQgKyA0XSA8IDB4RkYpIHtcbiAgICAgICAgICAvLyBjaGVjayBzaXplIGlzIHdpdGhpbiByYW5nZVxuICAgICAgICAgIGlmIChkYXRhW29mZnNldCArIDZdIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDddIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDhdIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDldIDwgMHg4MCkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIC8qKlxuICAgKiBSZXR1cm5zIGFueSBhZGphY2VudCBJRDMgdGFncyBmb3VuZCBpbiBkYXRhIHN0YXJ0aW5nIGF0IG9mZnNldCwgYXMgb25lIGJsb2NrIG9mIGRhdGFcbiAgICogQHBhcmFtIHtVaW50OEFycmF5fSBkYXRhIC0gVGhlIGRhdGEgdG8gc2VhcmNoIGluXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBvZmZzZXQgLSBUaGUgb2Zmc2V0IGF0IHdoaWNoIHRvIHN0YXJ0IHNlYXJjaGluZ1xuICAgKiBAcmV0dXJuIHtVaW50OEFycmF5fSAtIFRoZSBibG9jayBvZiBkYXRhIGNvbnRhaW5pbmcgYW55IElEMyB0YWdzIGZvdW5kXG4gICAqL1xuICA7XG5cbiAgSUQzLmdldElEM0RhdGEgPSBmdW5jdGlvbiBnZXRJRDNEYXRhKGRhdGEsIG9mZnNldCkge1xuICAgIHZhciBmcm9udCA9IG9mZnNldDtcbiAgICB2YXIgbGVuZ3RoID0gMDtcblxuICAgIHdoaWxlIChJRDMuaXNIZWFkZXIoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgLy8gSUQzIGhlYWRlciBpcyAxMCBieXRlc1xuICAgICAgbGVuZ3RoICs9IDEwO1xuXG4gICAgICB2YXIgc2l6ZSA9IElEMy5fcmVhZFNpemUoZGF0YSwgb2Zmc2V0ICsgNik7XG5cbiAgICAgIGxlbmd0aCArPSBzaXplO1xuXG4gICAgICBpZiAoSUQzLmlzRm9vdGVyKGRhdGEsIG9mZnNldCArIDEwKSkge1xuICAgICAgICAvLyBJRDMgZm9vdGVyIGlzIDEwIGJ5dGVzXG4gICAgICAgIGxlbmd0aCArPSAxMDtcbiAgICAgIH1cblxuICAgICAgb2Zmc2V0ICs9IGxlbmd0aDtcbiAgICB9XG5cbiAgICBpZiAobGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIGRhdGEuc3ViYXJyYXkoZnJvbnQsIGZyb250ICsgbGVuZ3RoKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9O1xuXG4gIElEMy5fcmVhZFNpemUgPSBmdW5jdGlvbiBfcmVhZFNpemUoZGF0YSwgb2Zmc2V0KSB7XG4gICAgdmFyIHNpemUgPSAwO1xuICAgIHNpemUgPSAoZGF0YVtvZmZzZXRdICYgMHg3ZikgPDwgMjE7XG4gICAgc2l6ZSB8PSAoZGF0YVtvZmZzZXQgKyAxXSAmIDB4N2YpIDw8IDE0O1xuICAgIHNpemUgfD0gKGRhdGFbb2Zmc2V0ICsgMl0gJiAweDdmKSA8PCA3O1xuICAgIHNpemUgfD0gZGF0YVtvZmZzZXQgKyAzXSAmIDB4N2Y7XG4gICAgcmV0dXJuIHNpemU7XG4gIH1cbiAgLyoqXG4gICAqIFNlYXJjaGVzIGZvciB0aGUgRWxlbWVudGFyeSBTdHJlYW0gdGltZXN0YW1wIGZvdW5kIGluIHRoZSBJRDMgZGF0YSBjaHVua1xuICAgKiBAcGFyYW0ge1VpbnQ4QXJyYXl9IGRhdGEgLSBCbG9jayBvZiBkYXRhIGNvbnRhaW5pbmcgb25lIG9yIG1vcmUgSUQzIHRhZ3NcbiAgICogQHJldHVybiB7bnVtYmVyfSAtIFRoZSB0aW1lc3RhbXBcbiAgICovXG4gIDtcblxuICBJRDMuZ2V0VGltZVN0YW1wID0gZnVuY3Rpb24gZ2V0VGltZVN0YW1wKGRhdGEpIHtcbiAgICB2YXIgZnJhbWVzID0gSUQzLmdldElEM0ZyYW1lcyhkYXRhKTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgZnJhbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgZnJhbWUgPSBmcmFtZXNbaV07XG5cbiAgICAgIGlmIChJRDMuaXNUaW1lU3RhbXBGcmFtZShmcmFtZSkpIHtcbiAgICAgICAgcmV0dXJuIElEMy5fcmVhZFRpbWVTdGFtcChmcmFtZSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICAvKipcbiAgICogUmV0dXJucyB0cnVlIGlmIHRoZSBJRDMgZnJhbWUgaXMgYW4gRWxlbWVudGFyeSBTdHJlYW0gdGltZXN0YW1wIGZyYW1lXG4gICAqIEBwYXJhbSB7SUQzIGZyYW1lfSBmcmFtZVxuICAgKi9cbiAgO1xuXG4gIElEMy5pc1RpbWVTdGFtcEZyYW1lID0gZnVuY3Rpb24gaXNUaW1lU3RhbXBGcmFtZShmcmFtZSkge1xuICAgIHJldHVybiBmcmFtZSAmJiBmcmFtZS5rZXkgPT09ICdQUklWJyAmJiBmcmFtZS5pbmZvID09PSAnY29tLmFwcGxlLnN0cmVhbWluZy50cmFuc3BvcnRTdHJlYW1UaW1lc3RhbXAnO1xuICB9O1xuXG4gIElEMy5fZ2V0RnJhbWVEYXRhID0gZnVuY3Rpb24gX2dldEZyYW1lRGF0YShkYXRhKSB7XG4gICAgLypcbiAgICBGcmFtZSBJRCAgICAgICAkeHggeHggeHggeHggKGZvdXIgY2hhcmFjdGVycylcbiAgICBTaXplICAgICAgICAgICAkeHggeHggeHggeHhcbiAgICBGbGFncyAgICAgICAgICAkeHggeHhcbiAgICAqL1xuICAgIHZhciB0eXBlID0gU3RyaW5nLmZyb21DaGFyQ29kZShkYXRhWzBdLCBkYXRhWzFdLCBkYXRhWzJdLCBkYXRhWzNdKTtcblxuICAgIHZhciBzaXplID0gSUQzLl9yZWFkU2l6ZShkYXRhLCA0KTsgLy8gc2tpcCBmcmFtZSBpZCwgc2l6ZSwgYW5kIGZsYWdzXG5cblxuICAgIHZhciBvZmZzZXQgPSAxMDtcbiAgICByZXR1cm4ge1xuICAgICAgdHlwZTogdHlwZSxcbiAgICAgIHNpemU6IHNpemUsXG4gICAgICBkYXRhOiBkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgc2l6ZSlcbiAgICB9O1xuICB9XG4gIC8qKlxuICAgKiBSZXR1cm5zIGFuIGFycmF5IG9mIElEMyBmcmFtZXMgZm91bmQgaW4gYWxsIHRoZSBJRDMgdGFncyBpbiB0aGUgaWQzRGF0YVxuICAgKiBAcGFyYW0ge1VpbnQ4QXJyYXl9IGlkM0RhdGEgLSBUaGUgSUQzIGRhdGEgY29udGFpbmluZyBvbmUgb3IgbW9yZSBJRDMgdGFnc1xuICAgKiBAcmV0dXJuIHtJRDMgZnJhbWVbXX0gLSBBcnJheSBvZiBJRDMgZnJhbWUgb2JqZWN0c1xuICAgKi9cbiAgO1xuXG4gIElEMy5nZXRJRDNGcmFtZXMgPSBmdW5jdGlvbiBnZXRJRDNGcmFtZXMoaWQzRGF0YSkge1xuICAgIHZhciBvZmZzZXQgPSAwO1xuICAgIHZhciBmcmFtZXMgPSBbXTtcblxuICAgIHdoaWxlIChJRDMuaXNIZWFkZXIoaWQzRGF0YSwgb2Zmc2V0KSkge1xuICAgICAgdmFyIHNpemUgPSBJRDMuX3JlYWRTaXplKGlkM0RhdGEsIG9mZnNldCArIDYpOyAvLyBza2lwIHBhc3QgSUQzIGhlYWRlclxuXG5cbiAgICAgIG9mZnNldCArPSAxMDtcbiAgICAgIHZhciBlbmQgPSBvZmZzZXQgKyBzaXplOyAvLyBsb29wIHRocm91Z2ggZnJhbWVzIGluIHRoZSBJRDMgdGFnXG5cbiAgICAgIHdoaWxlIChvZmZzZXQgKyA4IDwgZW5kKSB7XG4gICAgICAgIHZhciBmcmFtZURhdGEgPSBJRDMuX2dldEZyYW1lRGF0YShpZDNEYXRhLnN1YmFycmF5KG9mZnNldCkpO1xuXG4gICAgICAgIHZhciBmcmFtZSA9IElEMy5fZGVjb2RlRnJhbWUoZnJhbWVEYXRhKTtcblxuICAgICAgICBpZiAoZnJhbWUpIHtcbiAgICAgICAgICBmcmFtZXMucHVzaChmcmFtZSk7XG4gICAgICAgIH0gLy8gc2tpcCBmcmFtZSBoZWFkZXIgYW5kIGZyYW1lIGRhdGFcblxuXG4gICAgICAgIG9mZnNldCArPSBmcmFtZURhdGEuc2l6ZSArIDEwO1xuICAgICAgfVxuXG4gICAgICBpZiAoSUQzLmlzRm9vdGVyKGlkM0RhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgb2Zmc2V0ICs9IDEwO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBmcmFtZXM7XG4gIH07XG5cbiAgSUQzLl9kZWNvZGVGcmFtZSA9IGZ1bmN0aW9uIF9kZWNvZGVGcmFtZShmcmFtZSkge1xuICAgIGlmIChmcmFtZS50eXBlID09PSAnUFJJVicpIHtcbiAgICAgIHJldHVybiBJRDMuX2RlY29kZVByaXZGcmFtZShmcmFtZSk7XG4gICAgfSBlbHNlIGlmIChmcmFtZS50eXBlWzBdID09PSAnVycpIHtcbiAgICAgIHJldHVybiBJRDMuX2RlY29kZVVSTEZyYW1lKGZyYW1lKTtcbiAgICB9XG5cbiAgICByZXR1cm4gSUQzLl9kZWNvZGVUZXh0RnJhbWUoZnJhbWUpO1xuICB9O1xuXG4gIElEMy5fcmVhZFRpbWVTdGFtcCA9IGZ1bmN0aW9uIF9yZWFkVGltZVN0YW1wKHRpbWVTdGFtcEZyYW1lKSB7XG4gICAgaWYgKHRpbWVTdGFtcEZyYW1lLmRhdGEuYnl0ZUxlbmd0aCA9PT0gOCkge1xuICAgICAgdmFyIGRhdGEgPSBuZXcgVWludDhBcnJheSh0aW1lU3RhbXBGcmFtZS5kYXRhKTsgLy8gdGltZXN0YW1wIGlzIDMzIGJpdCBleHByZXNzZWQgYXMgYSBiaWctZW5kaWFuIGVpZ2h0LW9jdGV0IG51bWJlcixcbiAgICAgIC8vIHdpdGggdGhlIHVwcGVyIDMxIGJpdHMgc2V0IHRvIHplcm8uXG5cbiAgICAgIHZhciBwdHMzM0JpdCA9IGRhdGFbM10gJiAweDE7XG4gICAgICB2YXIgdGltZXN0YW1wID0gKGRhdGFbNF0gPDwgMjMpICsgKGRhdGFbNV0gPDwgMTUpICsgKGRhdGFbNl0gPDwgNykgKyBkYXRhWzddO1xuICAgICAgdGltZXN0YW1wIC89IDQ1O1xuXG4gICAgICBpZiAocHRzMzNCaXQpIHtcbiAgICAgICAgdGltZXN0YW1wICs9IDQ3NzIxODU4Ljg0O1xuICAgICAgfSAvLyAyXjMyIC8gOTBcblxuXG4gICAgICByZXR1cm4gTWF0aC5yb3VuZCh0aW1lc3RhbXApO1xuICAgIH1cblxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH07XG5cbiAgSUQzLl9kZWNvZGVQcml2RnJhbWUgPSBmdW5jdGlvbiBfZGVjb2RlUHJpdkZyYW1lKGZyYW1lKSB7XG4gICAgLypcbiAgICBGb3JtYXQ6IDx0ZXh0IHN0cmluZz5cXDA8YmluYXJ5IGRhdGE+XG4gICAgKi9cbiAgICBpZiAoZnJhbWUuc2l6ZSA8IDIpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgdmFyIG93bmVyID0gSUQzLl91dGY4QXJyYXlUb1N0cihmcmFtZS5kYXRhLCB0cnVlKTtcblxuICAgIHZhciBwcml2YXRlRGF0YSA9IG5ldyBVaW50OEFycmF5KGZyYW1lLmRhdGEuc3ViYXJyYXkob3duZXIubGVuZ3RoICsgMSkpO1xuICAgIHJldHVybiB7XG4gICAgICBrZXk6IGZyYW1lLnR5cGUsXG4gICAgICBpbmZvOiBvd25lcixcbiAgICAgIGRhdGE6IHByaXZhdGVEYXRhLmJ1ZmZlclxuICAgIH07XG4gIH07XG5cbiAgSUQzLl9kZWNvZGVUZXh0RnJhbWUgPSBmdW5jdGlvbiBfZGVjb2RlVGV4dEZyYW1lKGZyYW1lKSB7XG4gICAgaWYgKGZyYW1lLnNpemUgPCAyKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGlmIChmcmFtZS50eXBlID09PSAnVFhYWCcpIHtcbiAgICAgIC8qXG4gICAgICBGb3JtYXQ6XG4gICAgICBbMF0gICA9IHtUZXh0IEVuY29kaW5nfVxuICAgICAgWzEtP10gPSB7RGVzY3JpcHRpb259XFwwe1ZhbHVlfVxuICAgICAgKi9cbiAgICAgIHZhciBpbmRleCA9IDE7XG5cbiAgICAgIHZhciBkZXNjcmlwdGlvbiA9IElEMy5fdXRmOEFycmF5VG9TdHIoZnJhbWUuZGF0YS5zdWJhcnJheShpbmRleCksIHRydWUpO1xuXG4gICAgICBpbmRleCArPSBkZXNjcmlwdGlvbi5sZW5ndGggKyAxO1xuXG4gICAgICB2YXIgdmFsdWUgPSBJRDMuX3V0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoaW5kZXgpKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2V5OiBmcmFtZS50eXBlLFxuICAgICAgICBpbmZvOiBkZXNjcmlwdGlvbixcbiAgICAgICAgZGF0YTogdmFsdWVcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qXG4gICAgICBGb3JtYXQ6XG4gICAgICBbMF0gICA9IHtUZXh0IEVuY29kaW5nfVxuICAgICAgWzEtP10gPSB7VmFsdWV9XG4gICAgICAqL1xuICAgICAgdmFyIHRleHQgPSBJRDMuX3V0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoMSkpO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBrZXk6IGZyYW1lLnR5cGUsXG4gICAgICAgIGRhdGE6IHRleHRcbiAgICAgIH07XG4gICAgfVxuICB9O1xuXG4gIElEMy5fZGVjb2RlVVJMRnJhbWUgPSBmdW5jdGlvbiBfZGVjb2RlVVJMRnJhbWUoZnJhbWUpIHtcbiAgICBpZiAoZnJhbWUudHlwZSA9PT0gJ1dYWFgnKSB7XG4gICAgICAvKlxuICAgICAgRm9ybWF0OlxuICAgICAgWzBdICAgPSB7VGV4dCBFbmNvZGluZ31cbiAgICAgIFsxLT9dID0ge0Rlc2NyaXB0aW9ufVxcMHtVUkx9XG4gICAgICAqL1xuICAgICAgaWYgKGZyYW1lLnNpemUgPCAyKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIHZhciBpbmRleCA9IDE7XG5cbiAgICAgIHZhciBkZXNjcmlwdGlvbiA9IElEMy5fdXRmOEFycmF5VG9TdHIoZnJhbWUuZGF0YS5zdWJhcnJheShpbmRleCksIHRydWUpO1xuXG4gICAgICBpbmRleCArPSBkZXNjcmlwdGlvbi5sZW5ndGggKyAxO1xuXG4gICAgICB2YXIgdmFsdWUgPSBJRDMuX3V0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoaW5kZXgpKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2V5OiBmcmFtZS50eXBlLFxuICAgICAgICBpbmZvOiBkZXNjcmlwdGlvbixcbiAgICAgICAgZGF0YTogdmFsdWVcbiAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8qXG4gICAgICBGb3JtYXQ6XG4gICAgICBbMC0/XSA9IHtVUkx9XG4gICAgICAqL1xuICAgICAgdmFyIHVybCA9IElEMy5fdXRmOEFycmF5VG9TdHIoZnJhbWUuZGF0YSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGtleTogZnJhbWUudHlwZSxcbiAgICAgICAgZGF0YTogdXJsXG4gICAgICB9O1xuICAgIH1cbiAgfSAvLyBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzg5MzY5ODQvdWludDhhcnJheS10by1zdHJpbmctaW4tamF2YXNjcmlwdC8yMjM3MzE5N1xuICAvLyBodHRwOi8vd3d3Lm9uaWNvcy5jb20vc3RhZmYvaXovYW11c2UvamF2YXNjcmlwdC9leHBlcnQvdXRmLnR4dFxuXG4gIC8qIHV0Zi5qcyAtIFVURi04IDw9PiBVVEYtMTYgY29udmVydGlvblxuICAgKlxuICAgKiBDb3B5cmlnaHQgKEMpIDE5OTkgTWFzYW5hbyBJenVtbyA8aXpAb25pY29zLmNvLmpwPlxuICAgKiBWZXJzaW9uOiAxLjBcbiAgICogTGFzdE1vZGlmaWVkOiBEZWMgMjUgMTk5OVxuICAgKiBUaGlzIGxpYnJhcnkgaXMgZnJlZS4gIFlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkgaXQuXG4gICAqL1xuICA7XG5cbiAgSUQzLl91dGY4QXJyYXlUb1N0ciA9IGZ1bmN0aW9uIF91dGY4QXJyYXlUb1N0cihhcnJheSwgZXhpdE9uTnVsbCkge1xuICAgIGlmIChleGl0T25OdWxsID09PSB2b2lkIDApIHtcbiAgICAgIGV4aXRPbk51bGwgPSBmYWxzZTtcbiAgICB9XG5cbiAgICB2YXIgZGVjb2RlciA9IGdldFRleHREZWNvZGVyKCk7XG5cbiAgICBpZiAoZGVjb2Rlcikge1xuICAgICAgdmFyIGRlY29kZWQgPSBkZWNvZGVyLmRlY29kZShhcnJheSk7XG5cbiAgICAgIGlmIChleGl0T25OdWxsKSB7XG4gICAgICAgIC8vIGdyYWIgdXAgdG8gdGhlIGZpcnN0IG51bGxcbiAgICAgICAgdmFyIGlkeCA9IGRlY29kZWQuaW5kZXhPZignXFwwJyk7XG4gICAgICAgIHJldHVybiBpZHggIT09IC0xID8gZGVjb2RlZC5zdWJzdHJpbmcoMCwgaWR4KSA6IGRlY29kZWQ7XG4gICAgICB9IC8vIHJlbW92ZSBhbnkgbnVsbCBjaGFyYWN0ZXJzXG5cblxuICAgICAgcmV0dXJuIGRlY29kZWQucmVwbGFjZSgvXFwwL2csICcnKTtcbiAgICB9XG5cbiAgICB2YXIgbGVuID0gYXJyYXkubGVuZ3RoO1xuICAgIHZhciBjO1xuICAgIHZhciBjaGFyMjtcbiAgICB2YXIgY2hhcjM7XG4gICAgdmFyIG91dCA9ICcnO1xuICAgIHZhciBpID0gMDtcblxuICAgIHdoaWxlIChpIDwgbGVuKSB7XG4gICAgICBjID0gYXJyYXlbaSsrXTtcblxuICAgICAgaWYgKGMgPT09IDB4MDAgJiYgZXhpdE9uTnVsbCkge1xuICAgICAgICByZXR1cm4gb3V0O1xuICAgICAgfSBlbHNlIGlmIChjID09PSAweDAwIHx8IGMgPT09IDB4MDMpIHtcbiAgICAgICAgLy8gSWYgdGhlIGNoYXJhY3RlciBpcyAzIChFTkRfT0ZfVEVYVCkgb3IgMCAoTlVMTCkgdGhlbiBza2lwIGl0XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBzd2l0Y2ggKGMgPj4gNCkge1xuICAgICAgICBjYXNlIDA6XG4gICAgICAgIGNhc2UgMTpcbiAgICAgICAgY2FzZSAyOlxuICAgICAgICBjYXNlIDM6XG4gICAgICAgIGNhc2UgNDpcbiAgICAgICAgY2FzZSA1OlxuICAgICAgICBjYXNlIDY6XG4gICAgICAgIGNhc2UgNzpcbiAgICAgICAgICAvLyAweHh4eHh4eFxuICAgICAgICAgIG91dCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGMpO1xuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNhc2UgMTI6XG4gICAgICAgIGNhc2UgMTM6XG4gICAgICAgICAgLy8gMTEweCB4eHh4ICAgMTB4eCB4eHh4XG4gICAgICAgICAgY2hhcjIgPSBhcnJheVtpKytdO1xuICAgICAgICAgIG91dCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChjICYgMHgxRikgPDwgNiB8IGNoYXIyICYgMHgzRik7XG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgY2FzZSAxNDpcbiAgICAgICAgICAvLyAxMTEwIHh4eHggIDEweHggeHh4eCAgMTB4eCB4eHh4XG4gICAgICAgICAgY2hhcjIgPSBhcnJheVtpKytdO1xuICAgICAgICAgIGNoYXIzID0gYXJyYXlbaSsrXTtcbiAgICAgICAgICBvdXQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZSgoYyAmIDB4MEYpIDw8IDEyIHwgKGNoYXIyICYgMHgzRikgPDwgNiB8IChjaGFyMyAmIDB4M0YpIDw8IDApO1xuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dDtcbiAgfTtcblxuICByZXR1cm4gSUQzO1xufSgpO1xuXG52YXIgZGVjb2RlcjtcblxuZnVuY3Rpb24gZ2V0VGV4dERlY29kZXIoKSB7XG4gIHZhciBnbG9iYWwgPSBPYmplY3QoX3V0aWxzX2dldF9zZWxmX3Njb3BlX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8wX19bXCJnZXRTZWxmU2NvcGVcIl0pKCk7IC8vIHNhZmVndWFyZCBmb3IgY29kZSB0aGF0IG1pZ2h0IHJ1biBib3RoIG9uIHdvcmtlciBhbmQgbWFpbiB0aHJlYWRcblxuICBpZiAoIWRlY29kZXIgJiYgdHlwZW9mIGdsb2JhbC5UZXh0RGVjb2RlciAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBkZWNvZGVyID0gbmV3IGdsb2JhbC5UZXh0RGVjb2RlcigndXRmLTgnKTtcbiAgfVxuXG4gIHJldHVybiBkZWNvZGVyO1xufVxuXG52YXIgdXRmOEFycmF5VG9TdHIgPSBJRDMuX3V0ZjhBcnJheVRvU3RyO1xuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBfX3dlYnBhY2tfZXhwb3J0c19fW1wiZGVmYXVsdFwiXSA9IChJRDMpO1xuXG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2RlbXV4L21wNGRlbXV4ZXIuanNcIjpcbi8qISoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL3NyYy9kZW11eC9tcDRkZW11eGVyLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKiEgZXhwb3J0cyBwcm92aWRlZDogZGVmYXVsdCAqL1xuLyoqKi8gKGZ1bmN0aW9uKG1vZHVsZSwgX193ZWJwYWNrX2V4cG9ydHNfXywgX193ZWJwYWNrX3JlcXVpcmVfXykge1xuXG5cInVzZSBzdHJpY3RcIjtcbl9fd2VicGFja19yZXF1aXJlX18ucihfX3dlYnBhY2tfZXhwb3J0c19fKTtcbi8qIGhhcm1vbnkgaW1wb3J0ICovIHZhciBfdXRpbHNfbG9nZ2VyX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8wX18gPSBfX3dlYnBhY2tfcmVxdWlyZV9fKC8qISAuLi91dGlscy9sb2dnZXIgKi8gXCIuL3NyYy91dGlscy9sb2dnZXIuanNcIik7XG4vKiBoYXJtb255IGltcG9ydCAqLyB2YXIgX2V2ZW50c19fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMV9fID0gX193ZWJwYWNrX3JlcXVpcmVfXygvKiEgLi4vZXZlbnRzICovIFwiLi9zcmMvZXZlbnRzLmpzXCIpO1xuLyoqXG4gKiBNUDQgZGVtdXhlclxuICovXG5cblxudmFyIFVJTlQzMl9NQVggPSBNYXRoLnBvdygyLCAzMikgLSAxO1xuXG52YXIgTVA0RGVtdXhlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIE1QNERlbXV4ZXIob2JzZXJ2ZXIsIHJlbXV4ZXIpIHtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gICAgdGhpcy5yZW11eGVyID0gcmVtdXhlcjtcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBNUDREZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8ucmVzZXRUaW1lU3RhbXAgPSBmdW5jdGlvbiByZXNldFRpbWVTdGFtcChpbml0UFRTKSB7XG4gICAgdGhpcy5pbml0UFRTID0gaW5pdFBUUztcbiAgfTtcblxuICBfcHJvdG8ucmVzZXRJbml0U2VnbWVudCA9IGZ1bmN0aW9uIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGR1cmF0aW9uKSB7XG4gICAgLy8ganNoaW50IHVudXNlZDpmYWxzZVxuICAgIGlmIChpbml0U2VnbWVudCAmJiBpbml0U2VnbWVudC5ieXRlTGVuZ3RoKSB7XG4gICAgICB2YXIgaW5pdERhdGEgPSB0aGlzLmluaXREYXRhID0gTVA0RGVtdXhlci5wYXJzZUluaXRTZWdtZW50KGluaXRTZWdtZW50KTsgLy8gZGVmYXVsdCBhdWRpbyBjb2RlYyBpZiBub3RoaW5nIHNwZWNpZmllZFxuICAgICAgLy8gVE9ETyA6IGV4dHJhY3QgdGhhdCBmcm9tIGluaXRzZWdtZW50XG5cbiAgICAgIGlmIChhdWRpb0NvZGVjID09IG51bGwpIHtcbiAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjUnO1xuICAgICAgfVxuXG4gICAgICBpZiAodmlkZW9Db2RlYyA9PSBudWxsKSB7XG4gICAgICAgIHZpZGVvQ29kZWMgPSAnYXZjMS40MmUwMWUnO1xuICAgICAgfVxuXG4gICAgICB2YXIgdHJhY2tzID0ge307XG5cbiAgICAgIGlmIChpbml0RGF0YS5hdWRpbyAmJiBpbml0RGF0YS52aWRlbykge1xuICAgICAgICB0cmFja3MuYXVkaW92aWRlbyA9IHtcbiAgICAgICAgICBjb250YWluZXI6ICd2aWRlby9tcDQnLFxuICAgICAgICAgIGNvZGVjOiBhdWRpb0NvZGVjICsgJywnICsgdmlkZW9Db2RlYyxcbiAgICAgICAgICBpbml0U2VnbWVudDogZHVyYXRpb24gPyBpbml0U2VnbWVudCA6IG51bGxcbiAgICAgICAgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmIChpbml0RGF0YS5hdWRpbykge1xuICAgICAgICAgIHRyYWNrcy5hdWRpbyA9IHtcbiAgICAgICAgICAgIGNvbnRhaW5lcjogJ2F1ZGlvL21wNCcsXG4gICAgICAgICAgICBjb2RlYzogYXVkaW9Db2RlYyxcbiAgICAgICAgICAgIGluaXRTZWdtZW50OiBkdXJhdGlvbiA/IGluaXRTZWdtZW50IDogbnVsbFxuICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoaW5pdERhdGEudmlkZW8pIHtcbiAgICAgICAgICB0cmFja3MudmlkZW8gPSB7XG4gICAgICAgICAgICBjb250YWluZXI6ICd2aWRlby9tcDQnLFxuICAgICAgICAgICAgY29kZWM6IHZpZGVvQ29kZWMsXG4gICAgICAgICAgICBpbml0U2VnbWVudDogZHVyYXRpb24gPyBpbml0U2VnbWVudCA6IG51bGxcbiAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19JTklUX1NFR01FTlQsIHtcbiAgICAgICAgdHJhY2tzOiB0cmFja3NcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoYXVkaW9Db2RlYykge1xuICAgICAgICB0aGlzLmF1ZGlvQ29kZWMgPSBhdWRpb0NvZGVjO1xuICAgICAgfVxuXG4gICAgICBpZiAodmlkZW9Db2RlYykge1xuICAgICAgICB0aGlzLnZpZGVvQ29kZWMgPSB2aWRlb0NvZGVjO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBNUDREZW11eGVyLnByb2JlID0gZnVuY3Rpb24gcHJvYmUoZGF0YSkge1xuICAgIC8vIGVuc3VyZSB3ZSBmaW5kIGEgbW9vZiBib3ggaW4gdGhlIGZpcnN0IDE2IGtCXG4gICAgcmV0dXJuIE1QNERlbXV4ZXIuZmluZEJveCh7XG4gICAgICBkYXRhOiBkYXRhLFxuICAgICAgc3RhcnQ6IDAsXG4gICAgICBlbmQ6IE1hdGgubWluKGRhdGEubGVuZ3RoLCAxNjM4NClcbiAgICB9LCBbJ21vb2YnXSkubGVuZ3RoID4gMDtcbiAgfTtcblxuICBNUDREZW11eGVyLmJpbjJzdHIgPSBmdW5jdGlvbiBiaW4yc3RyKGJ1ZmZlcikge1xuICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGJ1ZmZlcik7XG4gIH07XG5cbiAgTVA0RGVtdXhlci5yZWFkVWludDE2ID0gZnVuY3Rpb24gcmVhZFVpbnQxNihidWZmZXIsIG9mZnNldCkge1xuICAgIGlmIChidWZmZXIuZGF0YSkge1xuICAgICAgb2Zmc2V0ICs9IGJ1ZmZlci5zdGFydDtcbiAgICAgIGJ1ZmZlciA9IGJ1ZmZlci5kYXRhO1xuICAgIH1cblxuICAgIHZhciB2YWwgPSBidWZmZXJbb2Zmc2V0XSA8PCA4IHwgYnVmZmVyW29mZnNldCArIDFdO1xuICAgIHJldHVybiB2YWwgPCAwID8gNjU1MzYgKyB2YWwgOiB2YWw7XG4gIH07XG5cbiAgTVA0RGVtdXhlci5yZWFkVWludDMyID0gZnVuY3Rpb24gcmVhZFVpbnQzMihidWZmZXIsIG9mZnNldCkge1xuICAgIGlmIChidWZmZXIuZGF0YSkge1xuICAgICAgb2Zmc2V0ICs9IGJ1ZmZlci5zdGFydDtcbiAgICAgIGJ1ZmZlciA9IGJ1ZmZlci5kYXRhO1xuICAgIH1cblxuICAgIHZhciB2YWwgPSBidWZmZXJbb2Zmc2V0XSA8PCAyNCB8IGJ1ZmZlcltvZmZzZXQgKyAxXSA8PCAxNiB8IGJ1ZmZlcltvZmZzZXQgKyAyXSA8PCA4IHwgYnVmZmVyW29mZnNldCArIDNdO1xuICAgIHJldHVybiB2YWwgPCAwID8gNDI5NDk2NzI5NiArIHZhbCA6IHZhbDtcbiAgfTtcblxuICBNUDREZW11eGVyLndyaXRlVWludDMyID0gZnVuY3Rpb24gd3JpdGVVaW50MzIoYnVmZmVyLCBvZmZzZXQsIHZhbHVlKSB7XG4gICAgaWYgKGJ1ZmZlci5kYXRhKSB7XG4gICAgICBvZmZzZXQgKz0gYnVmZmVyLnN0YXJ0O1xuICAgICAgYnVmZmVyID0gYnVmZmVyLmRhdGE7XG4gICAgfVxuXG4gICAgYnVmZmVyW29mZnNldF0gPSB2YWx1ZSA+PiAyNDtcbiAgICBidWZmZXJbb2Zmc2V0ICsgMV0gPSB2YWx1ZSA+PiAxNiAmIDB4ZmY7XG4gICAgYnVmZmVyW29mZnNldCArIDJdID0gdmFsdWUgPj4gOCAmIDB4ZmY7XG4gICAgYnVmZmVyW29mZnNldCArIDNdID0gdmFsdWUgJiAweGZmO1xuICB9IC8vIEZpbmQgdGhlIGRhdGEgZm9yIGEgYm94IHNwZWNpZmllZCBieSBpdHMgcGF0aFxuICA7XG5cbiAgTVA0RGVtdXhlci5maW5kQm94ID0gZnVuY3Rpb24gZmluZEJveChkYXRhLCBwYXRoKSB7XG4gICAgdmFyIHJlc3VsdHMgPSBbXSxcbiAgICAgICAgaSxcbiAgICAgICAgc2l6ZSxcbiAgICAgICAgdHlwZSxcbiAgICAgICAgZW5kLFxuICAgICAgICBzdWJyZXN1bHRzLFxuICAgICAgICBzdGFydCxcbiAgICAgICAgZW5kYm94O1xuXG4gICAgaWYgKGRhdGEuZGF0YSkge1xuICAgICAgc3RhcnQgPSBkYXRhLnN0YXJ0O1xuICAgICAgZW5kID0gZGF0YS5lbmQ7XG4gICAgICBkYXRhID0gZGF0YS5kYXRhO1xuICAgIH0gZWxzZSB7XG4gICAgICBzdGFydCA9IDA7XG4gICAgICBlbmQgPSBkYXRhLmJ5dGVMZW5ndGg7XG4gICAgfVxuXG4gICAgaWYgKCFwYXRoLmxlbmd0aCkge1xuICAgICAgLy8gc2hvcnQtY2lyY3VpdCB0aGUgc2VhcmNoIGZvciBlbXB0eSBwYXRoc1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgZm9yIChpID0gc3RhcnQ7IGkgPCBlbmQ7KSB7XG4gICAgICBzaXplID0gTVA0RGVtdXhlci5yZWFkVWludDMyKGRhdGEsIGkpO1xuICAgICAgdHlwZSA9IE1QNERlbXV4ZXIuYmluMnN0cihkYXRhLnN1YmFycmF5KGkgKyA0LCBpICsgOCkpO1xuICAgICAgZW5kYm94ID0gc2l6ZSA+IDEgPyBpICsgc2l6ZSA6IGVuZDtcblxuICAgICAgaWYgKHR5cGUgPT09IHBhdGhbMF0pIHtcbiAgICAgICAgaWYgKHBhdGgubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgLy8gdGhpcyBpcyB0aGUgZW5kIG9mIHRoZSBwYXRoIGFuZCB3ZSd2ZSBmb3VuZCB0aGUgYm94IHdlIHdlcmVcbiAgICAgICAgICAvLyBsb29raW5nIGZvclxuICAgICAgICAgIHJlc3VsdHMucHVzaCh7XG4gICAgICAgICAgICBkYXRhOiBkYXRhLFxuICAgICAgICAgICAgc3RhcnQ6IGkgKyA4LFxuICAgICAgICAgICAgZW5kOiBlbmRib3hcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyByZWN1cnNpdmVseSBzZWFyY2ggZm9yIHRoZSBuZXh0IGJveCBhbG9uZyB0aGUgcGF0aFxuICAgICAgICAgIHN1YnJlc3VsdHMgPSBNUDREZW11eGVyLmZpbmRCb3goe1xuICAgICAgICAgICAgZGF0YTogZGF0YSxcbiAgICAgICAgICAgIHN0YXJ0OiBpICsgOCxcbiAgICAgICAgICAgIGVuZDogZW5kYm94XG4gICAgICAgICAgfSwgcGF0aC5zbGljZSgxKSk7XG5cbiAgICAgICAgICBpZiAoc3VicmVzdWx0cy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHJlc3VsdHMgPSByZXN1bHRzLmNvbmNhdChzdWJyZXN1bHRzKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaSA9IGVuZGJveDtcbiAgICB9IC8vIHdlJ3ZlIGZpbmlzaGVkIHNlYXJjaGluZyBhbGwgb2YgZGF0YVxuXG5cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfTtcblxuICBNUDREZW11eGVyLnBhcnNlU2VnbWVudEluZGV4ID0gZnVuY3Rpb24gcGFyc2VTZWdtZW50SW5kZXgoaW5pdFNlZ21lbnQpIHtcbiAgICB2YXIgbW9vdiA9IE1QNERlbXV4ZXIuZmluZEJveChpbml0U2VnbWVudCwgWydtb292J10pWzBdO1xuICAgIHZhciBtb292RW5kT2Zmc2V0ID0gbW9vdiA/IG1vb3YuZW5kIDogbnVsbDsgLy8gd2UgbmVlZCB0aGlzIGluIGNhc2Ugd2UgbmVlZCB0byBjaG9wIG9mIGdhcmJhZ2Ugb2YgdGhlIGVuZCBvZiBjdXJyZW50IGRhdGFcblxuICAgIHZhciBpbmRleCA9IDA7XG4gICAgdmFyIHNpZHggPSBNUDREZW11eGVyLmZpbmRCb3goaW5pdFNlZ21lbnQsIFsnc2lkeCddKTtcbiAgICB2YXIgcmVmZXJlbmNlcztcblxuICAgIGlmICghc2lkeCB8fCAhc2lkeFswXSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgcmVmZXJlbmNlcyA9IFtdO1xuICAgIHNpZHggPSBzaWR4WzBdO1xuICAgIHZhciB2ZXJzaW9uID0gc2lkeC5kYXRhWzBdOyAvLyBzZXQgaW5pdGlhbCBvZmZzZXQsIHdlIHNraXAgdGhlIHJlZmVyZW5jZSBJRCAobm90IG5lZWRlZClcblxuICAgIGluZGV4ID0gdmVyc2lvbiA9PT0gMCA/IDggOiAxNjtcbiAgICB2YXIgdGltZXNjYWxlID0gTVA0RGVtdXhlci5yZWFkVWludDMyKHNpZHgsIGluZGV4KTtcbiAgICBpbmRleCArPSA0OyAvLyBUT0RPOiBwYXJzZSBlYXJsaWVzdFByZXNlbnRhdGlvblRpbWUgYW5kIGZpcnN0T2Zmc2V0XG4gICAgLy8gdXN1YWxseSB6ZXJvIGluIG91ciBjYXNlXG5cbiAgICB2YXIgZWFybGllc3RQcmVzZW50YXRpb25UaW1lID0gMDtcbiAgICB2YXIgZmlyc3RPZmZzZXQgPSAwO1xuXG4gICAgaWYgKHZlcnNpb24gPT09IDApIHtcbiAgICAgIGluZGV4ICs9IDg7XG4gICAgfSBlbHNlIHtcbiAgICAgIGluZGV4ICs9IDE2O1xuICAgIH0gLy8gc2tpcCByZXNlcnZlZFxuXG5cbiAgICBpbmRleCArPSAyO1xuICAgIHZhciBzdGFydEJ5dGUgPSBzaWR4LmVuZCArIGZpcnN0T2Zmc2V0O1xuICAgIHZhciByZWZlcmVuY2VzQ291bnQgPSBNUDREZW11eGVyLnJlYWRVaW50MTYoc2lkeCwgaW5kZXgpO1xuICAgIGluZGV4ICs9IDI7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHJlZmVyZW5jZXNDb3VudDsgaSsrKSB7XG4gICAgICB2YXIgcmVmZXJlbmNlSW5kZXggPSBpbmRleDtcbiAgICAgIHZhciByZWZlcmVuY2VJbmZvID0gTVA0RGVtdXhlci5yZWFkVWludDMyKHNpZHgsIHJlZmVyZW5jZUluZGV4KTtcbiAgICAgIHJlZmVyZW5jZUluZGV4ICs9IDQ7XG4gICAgICB2YXIgcmVmZXJlbmNlU2l6ZSA9IHJlZmVyZW5jZUluZm8gJiAweDdGRkZGRkZGO1xuICAgICAgdmFyIHJlZmVyZW5jZVR5cGUgPSAocmVmZXJlbmNlSW5mbyAmIDB4ODAwMDAwMDApID4+PiAzMTtcblxuICAgICAgaWYgKHJlZmVyZW5jZVR5cGUgPT09IDEpIHtcbiAgICAgICAgY29uc29sZS53YXJuKCdTSURYIGhhcyBoaWVyYXJjaGljYWwgcmVmZXJlbmNlcyAobm90IHN1cHBvcnRlZCknKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB2YXIgc3Vic2VnbWVudER1cmF0aW9uID0gTVA0RGVtdXhlci5yZWFkVWludDMyKHNpZHgsIHJlZmVyZW5jZUluZGV4KTtcbiAgICAgIHJlZmVyZW5jZUluZGV4ICs9IDQ7XG4gICAgICByZWZlcmVuY2VzLnB1c2goe1xuICAgICAgICByZWZlcmVuY2VTaXplOiByZWZlcmVuY2VTaXplLFxuICAgICAgICBzdWJzZWdtZW50RHVyYXRpb246IHN1YnNlZ21lbnREdXJhdGlvbixcbiAgICAgICAgLy8gdW5zY2FsZWRcbiAgICAgICAgaW5mbzoge1xuICAgICAgICAgIGR1cmF0aW9uOiBzdWJzZWdtZW50RHVyYXRpb24gLyB0aW1lc2NhbGUsXG4gICAgICAgICAgc3RhcnQ6IHN0YXJ0Qnl0ZSxcbiAgICAgICAgICBlbmQ6IHN0YXJ0Qnl0ZSArIHJlZmVyZW5jZVNpemUgLSAxXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgc3RhcnRCeXRlICs9IHJlZmVyZW5jZVNpemU7IC8vIFNraXBwaW5nIDEgYml0IGZvciB8c3RhcnRzV2l0aFNhcHwsIDMgYml0cyBmb3IgfHNhcFR5cGV8LCBhbmQgMjggYml0c1xuICAgICAgLy8gZm9yIHxzYXBEZWx0YXwuXG5cbiAgICAgIHJlZmVyZW5jZUluZGV4ICs9IDQ7IC8vIHNraXAgdG8gbmV4dCByZWZcblxuICAgICAgaW5kZXggPSByZWZlcmVuY2VJbmRleDtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgZWFybGllc3RQcmVzZW50YXRpb25UaW1lOiBlYXJsaWVzdFByZXNlbnRhdGlvblRpbWUsXG4gICAgICB0aW1lc2NhbGU6IHRpbWVzY2FsZSxcbiAgICAgIHZlcnNpb246IHZlcnNpb24sXG4gICAgICByZWZlcmVuY2VzQ291bnQ6IHJlZmVyZW5jZXNDb3VudCxcbiAgICAgIHJlZmVyZW5jZXM6IHJlZmVyZW5jZXMsXG4gICAgICBtb292RW5kT2Zmc2V0OiBtb292RW5kT2Zmc2V0XG4gICAgfTtcbiAgfVxuICAvKipcbiAgICogUGFyc2VzIGFuIE1QNCBpbml0aWFsaXphdGlvbiBzZWdtZW50IGFuZCBleHRyYWN0cyBzdHJlYW0gdHlwZSBhbmRcbiAgICogdGltZXNjYWxlIHZhbHVlcyBmb3IgYW55IGRlY2xhcmVkIHRyYWNrcy4gVGltZXNjYWxlIHZhbHVlcyBpbmRpY2F0ZSB0aGVcbiAgICogbnVtYmVyIG9mIGNsb2NrIHRpY2tzIHBlciBzZWNvbmQgdG8gYXNzdW1lIGZvciB0aW1lLWJhc2VkIHZhbHVlc1xuICAgKiBlbHNld2hlcmUgaW4gdGhlIE1QNC5cbiAgICpcbiAgICogVG8gZGV0ZXJtaW5lIHRoZSBzdGFydCB0aW1lIG9mIGFuIE1QNCwgeW91IG5lZWQgdHdvIHBpZWNlcyBvZlxuICAgKiBpbmZvcm1hdGlvbjogdGhlIHRpbWVzY2FsZSB1bml0IGFuZCB0aGUgZWFybGllc3QgYmFzZSBtZWRpYSBkZWNvZGVcbiAgICogdGltZS4gTXVsdGlwbGUgdGltZXNjYWxlcyBjYW4gYmUgc3BlY2lmaWVkIHdpdGhpbiBhbiBNUDQgYnV0IHRoZVxuICAgKiBiYXNlIG1lZGlhIGRlY29kZSB0aW1lIGlzIGFsd2F5cyBleHByZXNzZWQgaW4gdGhlIHRpbWVzY2FsZSBmcm9tXG4gICAqIHRoZSBtZWRpYSBoZWFkZXIgYm94IGZvciB0aGUgdHJhY2s6XG4gICAqIGBgYFxuICAgKiBtb292ID4gdHJhayA+IG1kaWEgPiBtZGhkLnRpbWVzY2FsZVxuICAgKiBtb292ID4gdHJhayA+IG1kaWEgPiBoZGxyXG4gICAqIGBgYFxuICAgKiBAcGFyYW0gaW5pdCB7VWludDhBcnJheX0gdGhlIGJ5dGVzIG9mIHRoZSBpbml0IHNlZ21lbnRcbiAgICogQHJldHVybiB7b2JqZWN0fSBhIGhhc2ggb2YgdHJhY2sgdHlwZSB0byB0aW1lc2NhbGUgdmFsdWVzIG9yIG51bGwgaWZcbiAgICogdGhlIGluaXQgc2VnbWVudCBpcyBtYWxmb3JtZWQuXG4gICAqL1xuICA7XG5cbiAgTVA0RGVtdXhlci5wYXJzZUluaXRTZWdtZW50ID0gZnVuY3Rpb24gcGFyc2VJbml0U2VnbWVudChpbml0U2VnbWVudCkge1xuICAgIHZhciByZXN1bHQgPSBbXTtcbiAgICB2YXIgdHJha3MgPSBNUDREZW11eGVyLmZpbmRCb3goaW5pdFNlZ21lbnQsIFsnbW9vdicsICd0cmFrJ10pO1xuICAgIHRyYWtzLmZvckVhY2goZnVuY3Rpb24gKHRyYWspIHtcbiAgICAgIHZhciB0a2hkID0gTVA0RGVtdXhlci5maW5kQm94KHRyYWssIFsndGtoZCddKVswXTtcblxuICAgICAgaWYgKHRraGQpIHtcbiAgICAgICAgdmFyIHZlcnNpb24gPSB0a2hkLmRhdGFbdGtoZC5zdGFydF07XG4gICAgICAgIHZhciBpbmRleCA9IHZlcnNpb24gPT09IDAgPyAxMiA6IDIwO1xuICAgICAgICB2YXIgdHJhY2tJZCA9IE1QNERlbXV4ZXIucmVhZFVpbnQzMih0a2hkLCBpbmRleCk7XG4gICAgICAgIHZhciBtZGhkID0gTVA0RGVtdXhlci5maW5kQm94KHRyYWssIFsnbWRpYScsICdtZGhkJ10pWzBdO1xuXG4gICAgICAgIGlmIChtZGhkKSB7XG4gICAgICAgICAgdmVyc2lvbiA9IG1kaGQuZGF0YVttZGhkLnN0YXJ0XTtcbiAgICAgICAgICBpbmRleCA9IHZlcnNpb24gPT09IDAgPyAxMiA6IDIwO1xuICAgICAgICAgIHZhciB0aW1lc2NhbGUgPSBNUDREZW11eGVyLnJlYWRVaW50MzIobWRoZCwgaW5kZXgpO1xuICAgICAgICAgIHZhciBoZGxyID0gTVA0RGVtdXhlci5maW5kQm94KHRyYWssIFsnbWRpYScsICdoZGxyJ10pWzBdO1xuXG4gICAgICAgICAgaWYgKGhkbHIpIHtcbiAgICAgICAgICAgIHZhciBoZGxyVHlwZSA9IE1QNERlbXV4ZXIuYmluMnN0cihoZGxyLmRhdGEuc3ViYXJyYXkoaGRsci5zdGFydCArIDgsIGhkbHIuc3RhcnQgKyAxMikpO1xuICAgICAgICAgICAgdmFyIHR5cGUgPSB7XG4gICAgICAgICAgICAgICdzb3VuJzogJ2F1ZGlvJyxcbiAgICAgICAgICAgICAgJ3ZpZGUnOiAndmlkZW8nXG4gICAgICAgICAgICB9W2hkbHJUeXBlXTtcblxuICAgICAgICAgICAgaWYgKHR5cGUpIHtcbiAgICAgICAgICAgICAgLy8gZXh0cmFjdCBjb2RlYyBpbmZvLiBUT0RPIDogcGFyc2UgY29kZWMgZGV0YWlscyB0byBiZSBhYmxlIHRvIGJ1aWxkIE1JTUUgdHlwZVxuICAgICAgICAgICAgICB2YXIgY29kZWNCb3ggPSBNUDREZW11eGVyLmZpbmRCb3godHJhaywgWydtZGlhJywgJ21pbmYnLCAnc3RibCcsICdzdHNkJ10pO1xuXG4gICAgICAgICAgICAgIGlmIChjb2RlY0JveC5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICBjb2RlY0JveCA9IGNvZGVjQm94WzBdO1xuICAgICAgICAgICAgICAgIHZhciBjb2RlY1R5cGUgPSBNUDREZW11eGVyLmJpbjJzdHIoY29kZWNCb3guZGF0YS5zdWJhcnJheShjb2RlY0JveC5zdGFydCArIDEyLCBjb2RlY0JveC5zdGFydCArIDE2KSk7XG4gICAgICAgICAgICAgICAgX3V0aWxzX2xvZ2dlcl9fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMF9fW1wibG9nZ2VyXCJdLmxvZyhcIk1QNERlbXV4ZXI6XCIgKyB0eXBlICsgXCI6XCIgKyBjb2RlY1R5cGUgKyBcIiBmb3VuZFwiKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHJlc3VsdFt0cmFja0lkXSA9IHtcbiAgICAgICAgICAgICAgICB0aW1lc2NhbGU6IHRpbWVzY2FsZSxcbiAgICAgICAgICAgICAgICB0eXBlOiB0eXBlXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIHJlc3VsdFt0eXBlXSA9IHtcbiAgICAgICAgICAgICAgICB0aW1lc2NhbGU6IHRpbWVzY2FsZSxcbiAgICAgICAgICAgICAgICBpZDogdHJhY2tJZFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgLyoqXG4gICogRGV0ZXJtaW5lIHRoZSBiYXNlIG1lZGlhIGRlY29kZSBzdGFydCB0aW1lLCBpbiBzZWNvbmRzLCBmb3IgYW4gTVA0XG4gICogZnJhZ21lbnQuIElmIG11bHRpcGxlIGZyYWdtZW50cyBhcmUgc3BlY2lmaWVkLCB0aGUgZWFybGllc3QgdGltZSBpc1xuICAqIHJldHVybmVkLlxuICAqXG4gICogVGhlIGJhc2UgbWVkaWEgZGVjb2RlIHRpbWUgY2FuIGJlIHBhcnNlZCBmcm9tIHRyYWNrIGZyYWdtZW50XG4gICogbWV0YWRhdGE6XG4gICogYGBgXG4gICogbW9vZiA+IHRyYWYgPiB0ZmR0LmJhc2VNZWRpYURlY29kZVRpbWVcbiAgKiBgYGBcbiAgKiBJdCByZXF1aXJlcyB0aGUgdGltZXNjYWxlIHZhbHVlIGZyb20gdGhlIG1kaGQgdG8gaW50ZXJwcmV0LlxuICAqXG4gICogQHBhcmFtIHRpbWVzY2FsZSB7b2JqZWN0fSBhIGhhc2ggb2YgdHJhY2sgaWRzIHRvIHRpbWVzY2FsZSB2YWx1ZXMuXG4gICogQHJldHVybiB7bnVtYmVyfSB0aGUgZWFybGllc3QgYmFzZSBtZWRpYSBkZWNvZGUgc3RhcnQgdGltZSBmb3IgdGhlXG4gICogZnJhZ21lbnQsIGluIHNlY29uZHNcbiAgKi9cbiAgO1xuXG4gIE1QNERlbXV4ZXIuZ2V0U3RhcnREVFMgPSBmdW5jdGlvbiBnZXRTdGFydERUUyhpbml0RGF0YSwgZnJhZ21lbnQpIHtcbiAgICB2YXIgdHJhZnMsIGJhc2VUaW1lcywgcmVzdWx0OyAvLyB3ZSBuZWVkIGluZm8gZnJvbSB0d28gY2hpbGRyZW5kIG9mIGVhY2ggdHJhY2sgZnJhZ21lbnQgYm94XG5cbiAgICB0cmFmcyA9IE1QNERlbXV4ZXIuZmluZEJveChmcmFnbWVudCwgWydtb29mJywgJ3RyYWYnXSk7IC8vIGRldGVybWluZSB0aGUgc3RhcnQgdGltZXMgZm9yIGVhY2ggdHJhY2tcblxuICAgIGJhc2VUaW1lcyA9IFtdLmNvbmNhdC5hcHBseShbXSwgdHJhZnMubWFwKGZ1bmN0aW9uICh0cmFmKSB7XG4gICAgICByZXR1cm4gTVA0RGVtdXhlci5maW5kQm94KHRyYWYsIFsndGZoZCddKS5tYXAoZnVuY3Rpb24gKHRmaGQpIHtcbiAgICAgICAgdmFyIGlkLCBzY2FsZSwgYmFzZVRpbWU7IC8vIGdldCB0aGUgdHJhY2sgaWQgZnJvbSB0aGUgdGZoZFxuXG4gICAgICAgIGlkID0gTVA0RGVtdXhlci5yZWFkVWludDMyKHRmaGQsIDQpOyAvLyBhc3N1bWUgYSA5MGtIeiBjbG9jayBpZiBubyB0aW1lc2NhbGUgd2FzIHNwZWNpZmllZFxuXG4gICAgICAgIHNjYWxlID0gaW5pdERhdGFbaWRdLnRpbWVzY2FsZSB8fCA5MGUzOyAvLyBnZXQgdGhlIGJhc2UgbWVkaWEgZGVjb2RlIHRpbWUgZnJvbSB0aGUgdGZkdFxuXG4gICAgICAgIGJhc2VUaW1lID0gTVA0RGVtdXhlci5maW5kQm94KHRyYWYsIFsndGZkdCddKS5tYXAoZnVuY3Rpb24gKHRmZHQpIHtcbiAgICAgICAgICB2YXIgdmVyc2lvbiwgcmVzdWx0O1xuICAgICAgICAgIHZlcnNpb24gPSB0ZmR0LmRhdGFbdGZkdC5zdGFydF07XG4gICAgICAgICAgcmVzdWx0ID0gTVA0RGVtdXhlci5yZWFkVWludDMyKHRmZHQsIDQpO1xuXG4gICAgICAgICAgaWYgKHZlcnNpb24gPT09IDEpIHtcbiAgICAgICAgICAgIHJlc3VsdCAqPSBNYXRoLnBvdygyLCAzMik7XG4gICAgICAgICAgICByZXN1bHQgKz0gTVA0RGVtdXhlci5yZWFkVWludDMyKHRmZHQsIDgpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH0pWzBdOyAvLyBjb252ZXJ0IGJhc2UgdGltZSB0byBzZWNvbmRzXG5cbiAgICAgICAgcmV0dXJuIGJhc2VUaW1lIC8gc2NhbGU7XG4gICAgICB9KTtcbiAgICB9KSk7IC8vIHJldHVybiB0aGUgbWluaW11bVxuXG4gICAgcmVzdWx0ID0gTWF0aC5taW4uYXBwbHkobnVsbCwgYmFzZVRpbWVzKTtcbiAgICByZXR1cm4gaXNGaW5pdGUocmVzdWx0KSA/IHJlc3VsdCA6IDA7XG4gIH07XG5cbiAgTVA0RGVtdXhlci5vZmZzZXRTdGFydERUUyA9IGZ1bmN0aW9uIG9mZnNldFN0YXJ0RFRTKGluaXREYXRhLCBmcmFnbWVudCwgdGltZU9mZnNldCkge1xuICAgIE1QNERlbXV4ZXIuZmluZEJveChmcmFnbWVudCwgWydtb29mJywgJ3RyYWYnXSkubWFwKGZ1bmN0aW9uICh0cmFmKSB7XG4gICAgICByZXR1cm4gTVA0RGVtdXhlci5maW5kQm94KHRyYWYsIFsndGZoZCddKS5tYXAoZnVuY3Rpb24gKHRmaGQpIHtcbiAgICAgICAgLy8gZ2V0IHRoZSB0cmFjayBpZCBmcm9tIHRoZSB0ZmhkXG4gICAgICAgIHZhciBpZCA9IE1QNERlbXV4ZXIucmVhZFVpbnQzMih0ZmhkLCA0KTsgLy8gYXNzdW1lIGEgOTBrSHogY2xvY2sgaWYgbm8gdGltZXNjYWxlIHdhcyBzcGVjaWZpZWRcblxuICAgICAgICB2YXIgdGltZXNjYWxlID0gaW5pdERhdGFbaWRdLnRpbWVzY2FsZSB8fCA5MGUzOyAvLyBnZXQgdGhlIGJhc2UgbWVkaWEgZGVjb2RlIHRpbWUgZnJvbSB0aGUgdGZkdFxuXG4gICAgICAgIE1QNERlbXV4ZXIuZmluZEJveCh0cmFmLCBbJ3RmZHQnXSkubWFwKGZ1bmN0aW9uICh0ZmR0KSB7XG4gICAgICAgICAgdmFyIHZlcnNpb24gPSB0ZmR0LmRhdGFbdGZkdC5zdGFydF07XG4gICAgICAgICAgdmFyIGJhc2VNZWRpYURlY29kZVRpbWUgPSBNUDREZW11eGVyLnJlYWRVaW50MzIodGZkdCwgNCk7XG5cbiAgICAgICAgICBpZiAodmVyc2lvbiA9PT0gMCkge1xuICAgICAgICAgICAgTVA0RGVtdXhlci53cml0ZVVpbnQzMih0ZmR0LCA0LCBiYXNlTWVkaWFEZWNvZGVUaW1lIC0gdGltZU9mZnNldCAqIHRpbWVzY2FsZSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGJhc2VNZWRpYURlY29kZVRpbWUgKj0gTWF0aC5wb3coMiwgMzIpO1xuICAgICAgICAgICAgYmFzZU1lZGlhRGVjb2RlVGltZSArPSBNUDREZW11eGVyLnJlYWRVaW50MzIodGZkdCwgOCk7XG4gICAgICAgICAgICBiYXNlTWVkaWFEZWNvZGVUaW1lIC09IHRpbWVPZmZzZXQgKiB0aW1lc2NhbGU7XG4gICAgICAgICAgICBiYXNlTWVkaWFEZWNvZGVUaW1lID0gTWF0aC5tYXgoYmFzZU1lZGlhRGVjb2RlVGltZSwgMCk7XG4gICAgICAgICAgICB2YXIgdXBwZXIgPSBNYXRoLmZsb29yKGJhc2VNZWRpYURlY29kZVRpbWUgLyAoVUlOVDMyX01BWCArIDEpKTtcbiAgICAgICAgICAgIHZhciBsb3dlciA9IE1hdGguZmxvb3IoYmFzZU1lZGlhRGVjb2RlVGltZSAlIChVSU5UMzJfTUFYICsgMSkpO1xuICAgICAgICAgICAgTVA0RGVtdXhlci53cml0ZVVpbnQzMih0ZmR0LCA0LCB1cHBlcik7XG4gICAgICAgICAgICBNUDREZW11eGVyLndyaXRlVWludDMyKHRmZHQsIDgsIGxvd2VyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH0gLy8gZmVlZCBpbmNvbWluZyBkYXRhIHRvIHRoZSBmcm9udCBvZiB0aGUgcGFyc2luZyBwaXBlbGluZVxuICA7XG5cbiAgX3Byb3RvLmFwcGVuZCA9IGZ1bmN0aW9uIGFwcGVuZChkYXRhLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICB2YXIgaW5pdERhdGEgPSB0aGlzLmluaXREYXRhO1xuXG4gICAgaWYgKCFpbml0RGF0YSkge1xuICAgICAgdGhpcy5yZXNldEluaXRTZWdtZW50KGRhdGEsIHRoaXMuYXVkaW9Db2RlYywgdGhpcy52aWRlb0NvZGVjLCBmYWxzZSk7XG4gICAgICBpbml0RGF0YSA9IHRoaXMuaW5pdERhdGE7XG4gICAgfVxuXG4gICAgdmFyIHN0YXJ0RFRTLFxuICAgICAgICBpbml0UFRTID0gdGhpcy5pbml0UFRTO1xuXG4gICAgaWYgKGluaXRQVFMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdmFyIF9zdGFydERUUyA9IE1QNERlbXV4ZXIuZ2V0U3RhcnREVFMoaW5pdERhdGEsIGRhdGEpO1xuXG4gICAgICB0aGlzLmluaXRQVFMgPSBpbml0UFRTID0gX3N0YXJ0RFRTIC0gdGltZU9mZnNldDtcbiAgICAgIHRoaXMub2JzZXJ2ZXIudHJpZ2dlcihfZXZlbnRzX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8xX19bXCJkZWZhdWx0XCJdLklOSVRfUFRTX0ZPVU5ELCB7XG4gICAgICAgIGluaXRQVFM6IGluaXRQVFNcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIE1QNERlbXV4ZXIub2Zmc2V0U3RhcnREVFMoaW5pdERhdGEsIGRhdGEsIGluaXRQVFMpO1xuICAgIHN0YXJ0RFRTID0gTVA0RGVtdXhlci5nZXRTdGFydERUUyhpbml0RGF0YSwgZGF0YSk7XG4gICAgdGhpcy5yZW11eGVyLnJlbXV4KGluaXREYXRhLmF1ZGlvLCBpbml0RGF0YS52aWRlbywgbnVsbCwgbnVsbCwgc3RhcnREVFMsIGNvbnRpZ3VvdXMsIGFjY3VyYXRlVGltZU9mZnNldCwgZGF0YSk7XG4gIH07XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge307XG5cbiAgcmV0dXJuIE1QNERlbXV4ZXI7XG59KCk7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gX193ZWJwYWNrX2V4cG9ydHNfX1tcImRlZmF1bHRcIl0gPSAoTVA0RGVtdXhlcik7XG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2Vycm9ycy50c1wiOlxuLyohKioqKioqKioqKioqKioqKioqKioqKiohKlxcXG4gICEqKiogLi9zcmMvZXJyb3JzLnRzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qISBleHBvcnRzIHByb3ZpZGVkOiBFcnJvclR5cGVzLCBFcnJvckRldGFpbHMgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIF9fd2VicGFja19leHBvcnRzX18sIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG4vKiBoYXJtb255IGV4cG9ydCAoYmluZGluZykgKi8gX193ZWJwYWNrX3JlcXVpcmVfXy5kKF9fd2VicGFja19leHBvcnRzX18sIFwiRXJyb3JUeXBlc1wiLCBmdW5jdGlvbigpIHsgcmV0dXJuIEVycm9yVHlwZXM7IH0pO1xuLyogaGFybW9ueSBleHBvcnQgKGJpbmRpbmcpICovIF9fd2VicGFja19yZXF1aXJlX18uZChfX3dlYnBhY2tfZXhwb3J0c19fLCBcIkVycm9yRGV0YWlsc1wiLCBmdW5jdGlvbigpIHsgcmV0dXJuIEVycm9yRGV0YWlsczsgfSk7XG52YXIgRXJyb3JUeXBlcztcbi8qKlxuICogQGVudW0ge0Vycm9yRGV0YWlsc31cbiAqIEB0eXBlZGVmIHtzdHJpbmd9IEVycm9yRGV0YWlsXG4gKi9cblxuKGZ1bmN0aW9uIChFcnJvclR5cGVzKSB7XG4gIEVycm9yVHlwZXNbXCJORVRXT1JLX0VSUk9SXCJdID0gXCJuZXR3b3JrRXJyb3JcIjtcbiAgRXJyb3JUeXBlc1tcIk1FRElBX0VSUk9SXCJdID0gXCJtZWRpYUVycm9yXCI7XG4gIEVycm9yVHlwZXNbXCJLRVlfU1lTVEVNX0VSUk9SXCJdID0gXCJrZXlTeXN0ZW1FcnJvclwiO1xuICBFcnJvclR5cGVzW1wiTVVYX0VSUk9SXCJdID0gXCJtdXhFcnJvclwiO1xuICBFcnJvclR5cGVzW1wiT1RIRVJfRVJST1JcIl0gPSBcIm90aGVyRXJyb3JcIjtcbn0pKEVycm9yVHlwZXMgfHwgKEVycm9yVHlwZXMgPSB7fSkpO1xuXG52YXIgRXJyb3JEZXRhaWxzO1xuXG4oZnVuY3Rpb24gKEVycm9yRGV0YWlscykge1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX05PX0tFWVNcIl0gPSBcImtleVN5c3RlbU5vS2V5c1wiO1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX05PX0FDQ0VTU1wiXSA9IFwia2V5U3lzdGVtTm9BY2Nlc3NcIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX1NZU1RFTV9OT19TRVNTSU9OXCJdID0gXCJrZXlTeXN0ZW1Ob1Nlc3Npb25cIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX1NZU1RFTV9MSUNFTlNFX1JFUVVFU1RfRkFJTEVEXCJdID0gXCJrZXlTeXN0ZW1MaWNlbnNlUmVxdWVzdEZhaWxlZFwiO1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX05PX0lOSVRfREFUQVwiXSA9IFwia2V5U3lzdGVtTm9Jbml0RGF0YVwiO1xuICBFcnJvckRldGFpbHNbXCJNQU5JRkVTVF9MT0FEX0VSUk9SXCJdID0gXCJtYW5pZmVzdExvYWRFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJNQU5JRkVTVF9MT0FEX1RJTUVPVVRcIl0gPSBcIm1hbmlmZXN0TG9hZFRpbWVPdXRcIjtcbiAgRXJyb3JEZXRhaWxzW1wiTUFOSUZFU1RfUEFSU0lOR19FUlJPUlwiXSA9IFwibWFuaWZlc3RQYXJzaW5nRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiTUFOSUZFU1RfSU5DT01QQVRJQkxFX0NPREVDU19FUlJPUlwiXSA9IFwibWFuaWZlc3RJbmNvbXBhdGlibGVDb2RlY3NFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJMRVZFTF9FTVBUWV9FUlJPUlwiXSA9IFwibGV2ZWxFbXB0eUVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkxFVkVMX0xPQURfRVJST1JcIl0gPSBcImxldmVsTG9hZEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkxFVkVMX0xPQURfVElNRU9VVFwiXSA9IFwibGV2ZWxMb2FkVGltZU91dFwiO1xuICBFcnJvckRldGFpbHNbXCJMRVZFTF9TV0lUQ0hfRVJST1JcIl0gPSBcImxldmVsU3dpdGNoRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQVVESU9fVFJBQ0tfTE9BRF9FUlJPUlwiXSA9IFwiYXVkaW9UcmFja0xvYWRFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJBVURJT19UUkFDS19MT0FEX1RJTUVPVVRcIl0gPSBcImF1ZGlvVHJhY2tMb2FkVGltZU91dFwiO1xuICBFcnJvckRldGFpbHNbXCJGUkFHX0xPQURfRVJST1JcIl0gPSBcImZyYWdMb2FkRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiRlJBR19MT0FEX1RJTUVPVVRcIl0gPSBcImZyYWdMb2FkVGltZU91dFwiO1xuICBFcnJvckRldGFpbHNbXCJGUkFHX0RFQ1JZUFRfRVJST1JcIl0gPSBcImZyYWdEZWNyeXB0RXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiRlJBR19QQVJTSU5HX0VSUk9SXCJdID0gXCJmcmFnUGFyc2luZ0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIlJFTVVYX0FMTE9DX0VSUk9SXCJdID0gXCJyZW11eEFsbG9jRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX0xPQURfRVJST1JcIl0gPSBcImtleUxvYWRFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJLRVlfTE9BRF9USU1FT1VUXCJdID0gXCJrZXlMb2FkVGltZU91dFwiO1xuICBFcnJvckRldGFpbHNbXCJCVUZGRVJfQUREX0NPREVDX0VSUk9SXCJdID0gXCJidWZmZXJBZGRDb2RlY0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkJVRkZFUl9BUFBFTkRfRVJST1JcIl0gPSBcImJ1ZmZlckFwcGVuZEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkJVRkZFUl9BUFBFTkRJTkdfRVJST1JcIl0gPSBcImJ1ZmZlckFwcGVuZGluZ0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkJVRkZFUl9TVEFMTEVEX0VSUk9SXCJdID0gXCJidWZmZXJTdGFsbGVkRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQlVGRkVSX0ZVTExfRVJST1JcIl0gPSBcImJ1ZmZlckZ1bGxFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJCVUZGRVJfU0VFS19PVkVSX0hPTEVcIl0gPSBcImJ1ZmZlclNlZWtPdmVySG9sZVwiO1xuICBFcnJvckRldGFpbHNbXCJCVUZGRVJfTlVER0VfT05fU1RBTExcIl0gPSBcImJ1ZmZlck51ZGdlT25TdGFsbFwiO1xuICBFcnJvckRldGFpbHNbXCJJTlRFUk5BTF9FWENFUFRJT05cIl0gPSBcImludGVybmFsRXhjZXB0aW9uXCI7XG59KShFcnJvckRldGFpbHMgfHwgKEVycm9yRGV0YWlscyA9IHt9KSk7XG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL2V2ZW50cy5qc1wiOlxuLyohKioqKioqKioqKioqKioqKioqKioqKiohKlxcXG4gICEqKiogLi9zcmMvZXZlbnRzLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qISBleHBvcnRzIHByb3ZpZGVkOiBkZWZhdWx0ICovXG4vKioqLyAoZnVuY3Rpb24obW9kdWxlLCBfX3dlYnBhY2tfZXhwb3J0c19fLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7XG5cblwidXNlIHN0cmljdFwiO1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yKF9fd2VicGFja19leHBvcnRzX18pO1xuLyoqXG4gKiBAcmVhZG9ubHlcbiAqIEBlbnVtIHtzdHJpbmd9XG4gKi9cbnZhciBIbHNFdmVudHMgPSB7XG4gIC8vIGZpcmVkIGJlZm9yZSBNZWRpYVNvdXJjZSBpcyBhdHRhY2hpbmcgdG8gbWVkaWEgZWxlbWVudCAtIGRhdGE6IHsgbWVkaWEgfVxuICBNRURJQV9BVFRBQ0hJTkc6ICdobHNNZWRpYUF0dGFjaGluZycsXG4gIC8vIGZpcmVkIHdoZW4gTWVkaWFTb3VyY2UgaGFzIGJlZW4gc3VjY2VzZnVsbHkgYXR0YWNoZWQgdG8gbWVkaWEgZWxlbWVudCAtIGRhdGE6IHsgfVxuICBNRURJQV9BVFRBQ0hFRDogJ2hsc01lZGlhQXR0YWNoZWQnLFxuICAvLyBmaXJlZCBiZWZvcmUgZGV0YWNoaW5nIE1lZGlhU291cmNlIGZyb20gbWVkaWEgZWxlbWVudCAtIGRhdGE6IHsgfVxuICBNRURJQV9ERVRBQ0hJTkc6ICdobHNNZWRpYURldGFjaGluZycsXG4gIC8vIGZpcmVkIHdoZW4gTWVkaWFTb3VyY2UgaGFzIGJlZW4gZGV0YWNoZWQgZnJvbSBtZWRpYSBlbGVtZW50IC0gZGF0YTogeyB9XG4gIE1FRElBX0RFVEFDSEVEOiAnaGxzTWVkaWFEZXRhY2hlZCcsXG4gIC8vIGZpcmVkIHdoZW4gd2UgYnVmZmVyIGlzIGdvaW5nIHRvIGJlIHJlc2V0IC0gZGF0YTogeyB9XG4gIEJVRkZFUl9SRVNFVDogJ2hsc0J1ZmZlclJlc2V0JyxcbiAgLy8gZmlyZWQgd2hlbiB3ZSBrbm93IGFib3V0IHRoZSBjb2RlY3MgdGhhdCB3ZSBuZWVkIGJ1ZmZlcnMgZm9yIHRvIHB1c2ggaW50byAtIGRhdGE6IHt0cmFja3MgOiB7IGNvbnRhaW5lciwgY29kZWMsIGxldmVsQ29kZWMsIGluaXRTZWdtZW50LCBtZXRhZGF0YSB9fVxuICBCVUZGRVJfQ09ERUNTOiAnaGxzQnVmZmVyQ29kZWNzJyxcbiAgLy8gZmlyZWQgd2hlbiBzb3VyY2VidWZmZXJzIGhhdmUgYmVlbiBjcmVhdGVkIC0gZGF0YTogeyB0cmFja3MgOiB0cmFja3MgfVxuICBCVUZGRVJfQ1JFQVRFRDogJ2hsc0J1ZmZlckNyZWF0ZWQnLFxuICAvLyBmaXJlZCB3aGVuIHdlIGFwcGVuZCBhIHNlZ21lbnQgdG8gdGhlIGJ1ZmZlciAtIGRhdGE6IHsgc2VnbWVudDogc2VnbWVudCBvYmplY3QgfVxuICBCVUZGRVJfQVBQRU5ESU5HOiAnaGxzQnVmZmVyQXBwZW5kaW5nJyxcbiAgLy8gZmlyZWQgd2hlbiB3ZSBhcmUgZG9uZSB3aXRoIGFwcGVuZGluZyBhIG1lZGlhIHNlZ21lbnQgdG8gdGhlIGJ1ZmZlciAtIGRhdGEgOiB7IHBhcmVudCA6IHNlZ21lbnQgcGFyZW50IHRoYXQgdHJpZ2dlcmVkIEJVRkZFUl9BUFBFTkRJTkcsIHBlbmRpbmcgOiBuYiBvZiBzZWdtZW50cyB3YWl0aW5nIGZvciBhcHBlbmRpbmcgZm9yIHRoaXMgc2VnbWVudCBwYXJlbnR9XG4gIEJVRkZFUl9BUFBFTkRFRDogJ2hsc0J1ZmZlckFwcGVuZGVkJyxcbiAgLy8gZmlyZWQgd2hlbiB0aGUgc3RyZWFtIGlzIGZpbmlzaGVkIGFuZCB3ZSB3YW50IHRvIG5vdGlmeSB0aGUgbWVkaWEgYnVmZmVyIHRoYXQgdGhlcmUgd2lsbCBiZSBubyBtb3JlIGRhdGEgLSBkYXRhOiB7IH1cbiAgQlVGRkVSX0VPUzogJ2hsc0J1ZmZlckVvcycsXG4gIC8vIGZpcmVkIHdoZW4gdGhlIG1lZGlhIGJ1ZmZlciBzaG91bGQgYmUgZmx1c2hlZCAtIGRhdGEgeyBzdGFydE9mZnNldCwgZW5kT2Zmc2V0IH1cbiAgQlVGRkVSX0ZMVVNISU5HOiAnaGxzQnVmZmVyRmx1c2hpbmcnLFxuICAvLyBmaXJlZCB3aGVuIHRoZSBtZWRpYSBidWZmZXIgaGFzIGJlZW4gZmx1c2hlZCAtIGRhdGE6IHsgfVxuICBCVUZGRVJfRkxVU0hFRDogJ2hsc0J1ZmZlckZsdXNoZWQnLFxuICAvLyBmaXJlZCB0byBzaWduYWwgdGhhdCBhIG1hbmlmZXN0IGxvYWRpbmcgc3RhcnRzIC0gZGF0YTogeyB1cmwgOiBtYW5pZmVzdFVSTH1cbiAgTUFOSUZFU1RfTE9BRElORzogJ2hsc01hbmlmZXN0TG9hZGluZycsXG4gIC8vIGZpcmVkIGFmdGVyIG1hbmlmZXN0IGhhcyBiZWVuIGxvYWRlZCAtIGRhdGE6IHsgbGV2ZWxzIDogW2F2YWlsYWJsZSBxdWFsaXR5IGxldmVsc10sIGF1ZGlvVHJhY2tzIDogWyBhdmFpbGFibGUgYXVkaW8gdHJhY2tzXSwgdXJsIDogbWFuaWZlc3RVUkwsIHN0YXRzIDogeyB0cmVxdWVzdCwgdGZpcnN0LCB0bG9hZCwgbXRpbWV9fVxuICBNQU5JRkVTVF9MT0FERUQ6ICdobHNNYW5pZmVzdExvYWRlZCcsXG4gIC8vIGZpcmVkIGFmdGVyIG1hbmlmZXN0IGhhcyBiZWVuIHBhcnNlZCAtIGRhdGE6IHsgbGV2ZWxzIDogW2F2YWlsYWJsZSBxdWFsaXR5IGxldmVsc10sIGZpcnN0TGV2ZWwgOiBpbmRleCBvZiBmaXJzdCBxdWFsaXR5IGxldmVsIGFwcGVhcmluZyBpbiBNYW5pZmVzdH1cbiAgTUFOSUZFU1RfUEFSU0VEOiAnaGxzTWFuaWZlc3RQYXJzZWQnLFxuICAvLyBmaXJlZCB3aGVuIGEgbGV2ZWwgc3dpdGNoIGlzIHJlcXVlc3RlZCAtIGRhdGE6IHsgbGV2ZWwgOiBpZCBvZiBuZXcgbGV2ZWwgfVxuICBMRVZFTF9TV0lUQ0hJTkc6ICdobHNMZXZlbFN3aXRjaGluZycsXG4gIC8vIGZpcmVkIHdoZW4gYSBsZXZlbCBzd2l0Y2ggaXMgZWZmZWN0aXZlIC0gZGF0YTogeyBsZXZlbCA6IGlkIG9mIG5ldyBsZXZlbCB9XG4gIExFVkVMX1NXSVRDSEVEOiAnaGxzTGV2ZWxTd2l0Y2hlZCcsXG4gIC8vIGZpcmVkIHdoZW4gYSBsZXZlbCBwbGF5bGlzdCBsb2FkaW5nIHN0YXJ0cyAtIGRhdGE6IHsgdXJsIDogbGV2ZWwgVVJMLCBsZXZlbCA6IGlkIG9mIGxldmVsIGJlaW5nIGxvYWRlZH1cbiAgTEVWRUxfTE9BRElORzogJ2hsc0xldmVsTG9hZGluZycsXG4gIC8vIGZpcmVkIHdoZW4gYSBsZXZlbCBwbGF5bGlzdCBsb2FkaW5nIGZpbmlzaGVzIC0gZGF0YTogeyBkZXRhaWxzIDogbGV2ZWxEZXRhaWxzIG9iamVjdCwgbGV2ZWwgOiBpZCBvZiBsb2FkZWQgbGV2ZWwsIHN0YXRzIDogeyB0cmVxdWVzdCwgdGZpcnN0LCB0bG9hZCwgbXRpbWV9IH1cbiAgTEVWRUxfTE9BREVEOiAnaGxzTGV2ZWxMb2FkZWQnLFxuICAvLyBmaXJlZCB3aGVuIGEgbGV2ZWwncyBkZXRhaWxzIGhhdmUgYmVlbiB1cGRhdGVkIGJhc2VkIG9uIHByZXZpb3VzIGRldGFpbHMsIGFmdGVyIGl0IGhhcyBiZWVuIGxvYWRlZCAtIGRhdGE6IHsgZGV0YWlscyA6IGxldmVsRGV0YWlscyBvYmplY3QsIGxldmVsIDogaWQgb2YgdXBkYXRlZCBsZXZlbCB9XG4gIExFVkVMX1VQREFURUQ6ICdobHNMZXZlbFVwZGF0ZWQnLFxuICAvLyBmaXJlZCB3aGVuIGEgbGV2ZWwncyBQVFMgaW5mb3JtYXRpb24gaGFzIGJlZW4gdXBkYXRlZCBhZnRlciBwYXJzaW5nIGEgZnJhZ21lbnQgLSBkYXRhOiB7IGRldGFpbHMgOiBsZXZlbERldGFpbHMgb2JqZWN0LCBsZXZlbCA6IGlkIG9mIHVwZGF0ZWQgbGV2ZWwsIGRyaWZ0OiBQVFMgZHJpZnQgb2JzZXJ2ZWQgd2hlbiBwYXJzaW5nIGxhc3QgZnJhZ21lbnQgfVxuICBMRVZFTF9QVFNfVVBEQVRFRDogJ2hsc0xldmVsUHRzVXBkYXRlZCcsXG4gIC8vIGZpcmVkIHRvIG5vdGlmeSB0aGF0IGxldmVscyBoYXZlIGNoYW5nZWQgYWZ0ZXIgcmVtb3ZpbmcgYSBsZXZlbCAtIGRhdGE6IHsgbGV2ZWxzIDogW2F2YWlsYWJsZSBxdWFsaXR5IGxldmVsc10gfVxuICBMRVZFTFNfVVBEQVRFRDogJ2hsc0xldmVsc1VwZGF0ZWQnLFxuICAvLyBmaXJlZCB0byBub3RpZnkgdGhhdCBhdWRpbyB0cmFjayBsaXN0cyBoYXMgYmVlbiB1cGRhdGVkIC0gZGF0YTogeyBhdWRpb1RyYWNrcyA6IGF1ZGlvVHJhY2tzIH1cbiAgQVVESU9fVFJBQ0tTX1VQREFURUQ6ICdobHNBdWRpb1RyYWNrc1VwZGF0ZWQnLFxuICAvLyBmaXJlZCB3aGVuIGFuIGF1ZGlvIHRyYWNrIHN3aXRjaGluZyBpcyByZXF1ZXN0ZWQgLSBkYXRhOiB7IGlkIDogYXVkaW8gdHJhY2sgaWQgfVxuICBBVURJT19UUkFDS19TV0lUQ0hJTkc6ICdobHNBdWRpb1RyYWNrU3dpdGNoaW5nJyxcbiAgLy8gZmlyZWQgd2hlbiBhbiBhdWRpbyB0cmFjayBzd2l0Y2ggYWN0dWFsbHkgb2NjdXJzIC0gZGF0YTogeyBpZCA6IGF1ZGlvIHRyYWNrIGlkIH1cbiAgQVVESU9fVFJBQ0tfU1dJVENIRUQ6ICdobHNBdWRpb1RyYWNrU3dpdGNoZWQnLFxuICAvLyBmaXJlZCB3aGVuIGFuIGF1ZGlvIHRyYWNrIGxvYWRpbmcgc3RhcnRzIC0gZGF0YTogeyB1cmwgOiBhdWRpbyB0cmFjayBVUkwsIGlkIDogYXVkaW8gdHJhY2sgaWQgfVxuICBBVURJT19UUkFDS19MT0FESU5HOiAnaGxzQXVkaW9UcmFja0xvYWRpbmcnLFxuICAvLyBmaXJlZCB3aGVuIGFuIGF1ZGlvIHRyYWNrIGxvYWRpbmcgZmluaXNoZXMgLSBkYXRhOiB7IGRldGFpbHMgOiBsZXZlbERldGFpbHMgb2JqZWN0LCBpZCA6IGF1ZGlvIHRyYWNrIGlkLCBzdGF0cyA6IHsgdHJlcXVlc3QsIHRmaXJzdCwgdGxvYWQsIG10aW1lIH0gfVxuICBBVURJT19UUkFDS19MT0FERUQ6ICdobHNBdWRpb1RyYWNrTG9hZGVkJyxcbiAgLy8gZmlyZWQgdG8gbm90aWZ5IHRoYXQgc3VidGl0bGUgdHJhY2sgbGlzdHMgaGFzIGJlZW4gdXBkYXRlZCAtIGRhdGE6IHsgc3VidGl0bGVUcmFja3MgOiBzdWJ0aXRsZVRyYWNrcyB9XG4gIFNVQlRJVExFX1RSQUNLU19VUERBVEVEOiAnaGxzU3VidGl0bGVUcmFja3NVcGRhdGVkJyxcbiAgLy8gZmlyZWQgd2hlbiBhbiBzdWJ0aXRsZSB0cmFjayBzd2l0Y2ggb2NjdXJzIC0gZGF0YTogeyBpZCA6IHN1YnRpdGxlIHRyYWNrIGlkIH1cbiAgU1VCVElUTEVfVFJBQ0tfU1dJVENIOiAnaGxzU3VidGl0bGVUcmFja1N3aXRjaCcsXG4gIC8vIGZpcmVkIHdoZW4gYSBzdWJ0aXRsZSB0cmFjayBsb2FkaW5nIHN0YXJ0cyAtIGRhdGE6IHsgdXJsIDogc3VidGl0bGUgdHJhY2sgVVJMLCBpZCA6IHN1YnRpdGxlIHRyYWNrIGlkIH1cbiAgU1VCVElUTEVfVFJBQ0tfTE9BRElORzogJ2hsc1N1YnRpdGxlVHJhY2tMb2FkaW5nJyxcbiAgLy8gZmlyZWQgd2hlbiBhIHN1YnRpdGxlIHRyYWNrIGxvYWRpbmcgZmluaXNoZXMgLSBkYXRhOiB7IGRldGFpbHMgOiBsZXZlbERldGFpbHMgb2JqZWN0LCBpZCA6IHN1YnRpdGxlIHRyYWNrIGlkLCBzdGF0cyA6IHsgdHJlcXVlc3QsIHRmaXJzdCwgdGxvYWQsIG10aW1lIH0gfVxuICBTVUJUSVRMRV9UUkFDS19MT0FERUQ6ICdobHNTdWJ0aXRsZVRyYWNrTG9hZGVkJyxcbiAgLy8gZmlyZWQgd2hlbiBhIHN1YnRpdGxlIGZyYWdtZW50IGhhcyBiZWVuIHByb2Nlc3NlZCAtIGRhdGE6IHsgc3VjY2VzcyA6IGJvb2xlYW4sIGZyYWcgOiB0aGUgcHJvY2Vzc2VkIGZyYWcgfVxuICBTVUJUSVRMRV9GUkFHX1BST0NFU1NFRDogJ2hsc1N1YnRpdGxlRnJhZ1Byb2Nlc3NlZCcsXG4gIC8vIGZpcmVkIHdoZW4gYSBzZXQgb2YgVlRUQ3VlcyB0byBiZSBtYW5hZ2VkIGV4dGVybmFsbHkgaGFzIGJlZW4gcGFyc2VkIC0gZGF0YTogeyB0eXBlOiBzdHJpbmcsIHRyYWNrOiBzdHJpbmcsIGN1ZXM6IFsgVlRUQ3VlIF0gfVxuICBDVUVTX1BBUlNFRDogJ2hsc0N1ZXNQYXJzZWQnLFxuICAvLyBmaXJlZCB3aGVuIGEgdGV4dCB0cmFjayB0byBiZSBtYW5hZ2VkIGV4dGVybmFsbHkgaXMgZm91bmQgLSBkYXRhOiB7IHRyYWNrczogWyB7IGxhYmVsOiBzdHJpbmcsIGtpbmQ6IHN0cmluZywgZGVmYXVsdDogYm9vbGVhbiB9IF0gfVxuICBOT05fTkFUSVZFX1RFWFRfVFJBQ0tTX0ZPVU5EOiAnaGxzTm9uTmF0aXZlVGV4dFRyYWNrc0ZvdW5kJyxcbiAgLy8gZmlyZWQgd2hlbiB0aGUgZmlyc3QgdGltZXN0YW1wIGlzIGZvdW5kIC0gZGF0YTogeyBpZCA6IGRlbXV4ZXIgaWQsIGluaXRQVFM6IGluaXRQVFMsIGZyYWcgOiBmcmFnbWVudCBvYmplY3QgfVxuICBJTklUX1BUU19GT1VORDogJ2hsc0luaXRQdHNGb3VuZCcsXG4gIC8vIGZpcmVkIHdoZW4gYSBmcmFnbWVudCBsb2FkaW5nIHN0YXJ0cyAtIGRhdGE6IHsgZnJhZyA6IGZyYWdtZW50IG9iamVjdCB9XG4gIEZSQUdfTE9BRElORzogJ2hsc0ZyYWdMb2FkaW5nJyxcbiAgLy8gZmlyZWQgd2hlbiBhIGZyYWdtZW50IGxvYWRpbmcgaXMgcHJvZ3Jlc3NpbmcgLSBkYXRhOiB7IGZyYWcgOiBmcmFnbWVudCBvYmplY3QsIHsgdHJlcXVlc3QsIHRmaXJzdCwgbG9hZGVkIH0gfVxuICBGUkFHX0xPQURfUFJPR1JFU1M6ICdobHNGcmFnTG9hZFByb2dyZXNzJyxcbiAgLy8gSWRlbnRpZmllciBmb3IgZnJhZ21lbnQgbG9hZCBhYm9ydGluZyBmb3IgZW1lcmdlbmN5IHN3aXRjaCBkb3duIC0gZGF0YTogeyBmcmFnIDogZnJhZ21lbnQgb2JqZWN0IH1cbiAgRlJBR19MT0FEX0VNRVJHRU5DWV9BQk9SVEVEOiAnaGxzRnJhZ0xvYWRFbWVyZ2VuY3lBYm9ydGVkJyxcbiAgLy8gZmlyZWQgd2hlbiBhIGZyYWdtZW50IGxvYWRpbmcgaXMgY29tcGxldGVkIC0gZGF0YTogeyBmcmFnIDogZnJhZ21lbnQgb2JqZWN0LCBwYXlsb2FkIDogZnJhZ21lbnQgcGF5bG9hZCwgc3RhdHMgOiB7IHRyZXF1ZXN0LCB0Zmlyc3QsIHRsb2FkLCBsZW5ndGggfSB9XG4gIEZSQUdfTE9BREVEOiAnaGxzRnJhZ0xvYWRlZCcsXG4gIC8vIGZpcmVkIHdoZW4gYSBmcmFnbWVudCBoYXMgZmluaXNoZWQgZGVjcnlwdGluZyAtIGRhdGE6IHsgaWQgOiBkZW11eGVyIGlkLCBmcmFnOiBmcmFnbWVudCBvYmplY3QsIHBheWxvYWQgOiBmcmFnbWVudCBwYXlsb2FkLCBzdGF0cyA6IHsgdHN0YXJ0LCB0ZGVjcnlwdCB9IH1cbiAgRlJBR19ERUNSWVBURUQ6ICdobHNGcmFnRGVjcnlwdGVkJyxcbiAgLy8gZmlyZWQgd2hlbiBJbml0IFNlZ21lbnQgaGFzIGJlZW4gZXh0cmFjdGVkIGZyb20gZnJhZ21lbnQgLSBkYXRhOiB7IGlkIDogZGVtdXhlciBpZCwgZnJhZzogZnJhZ21lbnQgb2JqZWN0LCBtb292IDogbW9vdiBNUDQgYm94LCBjb2RlY3MgOiBjb2RlY3MgZm91bmQgd2hpbGUgcGFyc2luZyBmcmFnbWVudCB9XG4gIEZSQUdfUEFSU0lOR19JTklUX1NFR01FTlQ6ICdobHNGcmFnUGFyc2luZ0luaXRTZWdtZW50JyxcbiAgLy8gZmlyZWQgd2hlbiBwYXJzaW5nIHNlaSB0ZXh0IGlzIGNvbXBsZXRlZCAtIGRhdGE6IHsgaWQgOiBkZW11eGVyIGlkLCBmcmFnOiBmcmFnbWVudCBvYmplY3QsIHNhbXBsZXMgOiBbIHNlaSBzYW1wbGVzIHBlcyBdIH1cbiAgRlJBR19QQVJTSU5HX1VTRVJEQVRBOiAnaGxzRnJhZ1BhcnNpbmdVc2VyZGF0YScsXG4gIC8vIGZpcmVkIHdoZW4gcGFyc2luZyBpZDMgaXMgY29tcGxldGVkIC0gZGF0YTogeyBpZCA6IGRlbXV4ZXIgaWQsIGZyYWc6IGZyYWdtZW50IG9iamVjdCwgc2FtcGxlcyA6IFsgaWQzIHNhbXBsZXMgcGVzIF0gfVxuICBGUkFHX1BBUlNJTkdfTUVUQURBVEE6ICdobHNGcmFnUGFyc2luZ01ldGFkYXRhJyxcbiAgLy8gZmlyZWQgd2hlbiBkYXRhIGhhdmUgYmVlbiBleHRyYWN0ZWQgZnJvbSBmcmFnbWVudCAtIGRhdGE6IHsgaWQgOiBkZW11eGVyIGlkLCBmcmFnOiBmcmFnbWVudCBvYmplY3QsIGRhdGExIDogbW9vZiBNUDQgYm94IG9yIFRTIGZyYWdtZW50cywgZGF0YTIgOiBtZGF0IE1QNCBib3ggb3IgbnVsbH1cbiAgRlJBR19QQVJTSU5HX0RBVEE6ICdobHNGcmFnUGFyc2luZ0RhdGEnLFxuICAvLyBmaXJlZCB3aGVuIGZyYWdtZW50IHBhcnNpbmcgaXMgY29tcGxldGVkIC0gZGF0YTogeyBpZCA6IGRlbXV4ZXIgaWQsIGZyYWc6IGZyYWdtZW50IG9iamVjdCB9XG4gIEZSQUdfUEFSU0VEOiAnaGxzRnJhZ1BhcnNlZCcsXG4gIC8vIGZpcmVkIHdoZW4gZnJhZ21lbnQgcmVtdXhlZCBNUDQgYm94ZXMgaGF2ZSBhbGwgYmVlbiBhcHBlbmRlZCBpbnRvIFNvdXJjZUJ1ZmZlciAtIGRhdGE6IHsgaWQgOiBkZW11eGVyIGlkLCBmcmFnIDogZnJhZ21lbnQgb2JqZWN0LCBzdGF0cyA6IHsgdHJlcXVlc3QsIHRmaXJzdCwgdGxvYWQsIHRwYXJzZWQsIHRidWZmZXJlZCwgbGVuZ3RoLCBid0VzdGltYXRlIH0gfVxuICBGUkFHX0JVRkZFUkVEOiAnaGxzRnJhZ0J1ZmZlcmVkJyxcbiAgLy8gZmlyZWQgd2hlbiBmcmFnbWVudCBtYXRjaGluZyB3aXRoIGN1cnJlbnQgbWVkaWEgcG9zaXRpb24gaXMgY2hhbmdpbmcgLSBkYXRhIDogeyBpZCA6IGRlbXV4ZXIgaWQsIGZyYWcgOiBmcmFnbWVudCBvYmplY3QgfVxuICBGUkFHX0NIQU5HRUQ6ICdobHNGcmFnQ2hhbmdlZCcsXG4gIC8vIElkZW50aWZpZXIgZm9yIGEgRlBTIGRyb3AgZXZlbnQgLSBkYXRhOiB7IGN1cmVudERyb3BwZWQsIGN1cnJlbnREZWNvZGVkLCB0b3RhbERyb3BwZWRGcmFtZXMgfVxuICBGUFNfRFJPUDogJ2hsc0Zwc0Ryb3AnLFxuICAvLyB0cmlnZ2VyZWQgd2hlbiBGUFMgZHJvcCB0cmlnZ2VycyBhdXRvIGxldmVsIGNhcHBpbmcgLSBkYXRhOiB7IGxldmVsLCBkcm9wcGVkbGV2ZWwgfVxuICBGUFNfRFJPUF9MRVZFTF9DQVBQSU5HOiAnaGxzRnBzRHJvcExldmVsQ2FwcGluZycsXG4gIC8vIElkZW50aWZpZXIgZm9yIGFuIGVycm9yIGV2ZW50IC0gZGF0YTogeyB0eXBlIDogZXJyb3IgdHlwZSwgZGV0YWlscyA6IGVycm9yIGRldGFpbHMsIGZhdGFsIDogaWYgdHJ1ZSwgaGxzLmpzIGNhbm5vdC93aWxsIG5vdCB0cnkgdG8gcmVjb3ZlciwgaWYgZmFsc2UsIGhscy5qcyB3aWxsIHRyeSB0byByZWNvdmVyLG90aGVyIGVycm9yIHNwZWNpZmljIGRhdGEgfVxuICBFUlJPUjogJ2hsc0Vycm9yJyxcbiAgLy8gZmlyZWQgd2hlbiBobHMuanMgaW5zdGFuY2Ugc3RhcnRzIGRlc3Ryb3lpbmcuIERpZmZlcmVudCBmcm9tIE1FRElBX0RFVEFDSEVEIGFzIG9uZSBjb3VsZCB3YW50IHRvIGRldGFjaCBhbmQgcmVhdHRhY2ggYSBtZWRpYSB0byB0aGUgaW5zdGFuY2Ugb2YgaGxzLmpzIHRvIGhhbmRsZSBtaWQtcm9sbHMgZm9yIGV4YW1wbGUgLSBkYXRhOiB7IH1cbiAgREVTVFJPWUlORzogJ2hsc0Rlc3Ryb3lpbmcnLFxuICAvLyBmaXJlZCB3aGVuIGEgZGVjcnlwdCBrZXkgbG9hZGluZyBzdGFydHMgLSBkYXRhOiB7IGZyYWcgOiBmcmFnbWVudCBvYmplY3QgfVxuICBLRVlfTE9BRElORzogJ2hsc0tleUxvYWRpbmcnLFxuICAvLyBmaXJlZCB3aGVuIGEgZGVjcnlwdCBrZXkgbG9hZGluZyBpcyBjb21wbGV0ZWQgLSBkYXRhOiB7IGZyYWcgOiBmcmFnbWVudCBvYmplY3QsIHBheWxvYWQgOiBrZXkgcGF5bG9hZCwgc3RhdHMgOiB7IHRyZXF1ZXN0LCB0Zmlyc3QsIHRsb2FkLCBsZW5ndGggfSB9XG4gIEtFWV9MT0FERUQ6ICdobHNLZXlMb2FkZWQnLFxuICAvLyBmaXJlZCB1cG9uIHN0cmVhbSBjb250cm9sbGVyIHN0YXRlIHRyYW5zaXRpb25zIC0gZGF0YTogeyBwcmV2aW91c1N0YXRlLCBuZXh0U3RhdGUgfVxuICBTVFJFQU1fU1RBVEVfVFJBTlNJVElPTjogJ2hsc1N0cmVhbVN0YXRlVHJhbnNpdGlvbicsXG4gIC8vIGZpcmVkIHdoZW4gdGhlIGxpdmUgYmFjayBidWZmZXIgaXMgcmVhY2hlZCBkZWZpbmVkIGJ5IHRoZSBsaXZlQmFja0J1ZmZlckxlbmd0aCBjb25maWcgb3B0aW9uIC0gZGF0YSA6IHsgYnVmZmVyRW5kOiBudW1iZXIgfVxuICBMSVZFX0JBQ0tfQlVGRkVSX1JFQUNIRUQ6ICdobHNMaXZlQmFja0J1ZmZlclJlYWNoZWQnXG59O1xuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyBfX3dlYnBhY2tfZXhwb3J0c19fW1wiZGVmYXVsdFwiXSA9IChIbHNFdmVudHMpO1xuXG4vKioqLyB9KSxcblxuLyoqKi8gXCIuL3NyYy9obHMudHNcIjpcbi8qISoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL3NyYy9obHMudHMgKyA1MCBtb2R1bGVzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKiEgZXhwb3J0cyBwcm92aWRlZDogZGVmYXVsdCAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vc3JjL2NyeXB0L2RlY3J5cHRlci5qcyBiZWNhdXNlIG9mIC4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZGVtdXgvZGVtdXhlci1pbmxpbmUuanMgYmVjYXVzZSBvZiAuL3NyYy9kZW11eC9kZW11eGVyLXdvcmtlci5qcyAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vc3JjL2RlbXV4L2lkMy5qcyBiZWNhdXNlIG9mIC4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZGVtdXgvbXA0ZGVtdXhlci5qcyBiZWNhdXNlIG9mIC4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9zcmMvZXJyb3JzLnRzIGJlY2F1c2Ugb2YgLi9zcmMvZGVtdXgvZGVtdXhlci13b3JrZXIuanMgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy9ldmVudHMuanMgYmVjYXVzZSBvZiAuL3NyYy9kZW11eC9kZW11eGVyLXdvcmtlci5qcyAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vc3JjL3BvbHlmaWxscy9udW1iZXIuanMgYmVjYXVzZSBvZiAuL3NyYy9kZW11eC9kZW11eGVyLXdvcmtlci5qcyAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vc3JjL3V0aWxzL2dldC1zZWxmLXNjb3BlLmpzIGJlY2F1c2Ugb2YgLi9zcmMvZGVtdXgvZGVtdXhlci13b3JrZXIuanMgKi9cbi8qISBNb2R1bGVDb25jYXRlbmF0aW9uIGJhaWxvdXQ6IENhbm5vdCBjb25jYXQgd2l0aCAuL3NyYy91dGlscy9sb2dnZXIuanMgYmVjYXVzZSBvZiAuL3NyYy9kZW11eC9kZW11eGVyLXdvcmtlci5qcyAqL1xuLyohIE1vZHVsZUNvbmNhdGVuYXRpb24gYmFpbG91dDogQ2Fubm90IGNvbmNhdCB3aXRoIC4vbm9kZV9tb2R1bGVzL2V2ZW50ZW1pdHRlcjMvaW5kZXguanMgKDwtIE1vZHVsZSBpcyBub3QgYW4gRUNNQVNjcmlwdCBtb2R1bGUpICovXG4vKiEgTW9kdWxlQ29uY2F0ZW5hdGlvbiBiYWlsb3V0OiBDYW5ub3QgY29uY2F0IHdpdGggLi9ub2RlX21vZHVsZXMvdXJsLXRvb2xraXQvc3JjL3VybC10b29sa2l0LmpzICg8LSBNb2R1bGUgaXMgbm90IGFuIEVDTUFTY3JpcHQgbW9kdWxlKSAqL1xuLyoqKi8gKGZ1bmN0aW9uKG1vZHVsZSwgX193ZWJwYWNrX2V4cG9ydHNfXywgX193ZWJwYWNrX3JlcXVpcmVfXykge1xuXG5cInVzZSBzdHJpY3RcIjtcbi8vIEVTTSBDT01QQVQgRkxBR1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yKF9fd2VicGFja19leHBvcnRzX18pO1xuXG4vLyBFWFBPUlRTXG5fX3dlYnBhY2tfcmVxdWlyZV9fLmQoX193ZWJwYWNrX2V4cG9ydHNfXywgXCJkZWZhdWx0XCIsIGZ1bmN0aW9uKCkgeyByZXR1cm4gLyogYmluZGluZyAqLyBobHNfSGxzOyB9KTtcblxuLy8gTkFNRVNQQUNFIE9CSkVDVDogLi9zcmMvdXRpbHMvY3Vlcy50c1xudmFyIGN1ZXNfbmFtZXNwYWNlT2JqZWN0ID0ge307XG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoY3Vlc19uYW1lc3BhY2VPYmplY3QpO1xuX193ZWJwYWNrX3JlcXVpcmVfXy5kKGN1ZXNfbmFtZXNwYWNlT2JqZWN0LCBcIm5ld0N1ZVwiLCBmdW5jdGlvbigpIHsgcmV0dXJuIG5ld0N1ZTsgfSk7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9ub2RlX21vZHVsZXMvdXJsLXRvb2xraXQvc3JjL3VybC10b29sa2l0LmpzXG52YXIgdXJsX3Rvb2xraXQgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9ub2RlX21vZHVsZXMvdXJsLXRvb2xraXQvc3JjL3VybC10b29sa2l0LmpzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2Vycm9ycy50c1xudmFyIGVycm9ycyA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9lcnJvcnMudHNcIik7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvcG9seWZpbGxzL251bWJlci5qc1xudmFyIG51bWJlciA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9wb2x5ZmlsbHMvbnVtYmVyLmpzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2V2ZW50cy5qc1xudmFyIGV2ZW50cyA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9ldmVudHMuanNcIik7XG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvdXRpbHMvbG9nZ2VyLmpzXG52YXIgbG9nZ2VyID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL3V0aWxzL2xvZ2dlci5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvZXZlbnQtaGFuZGxlci50c1xuLypcbipcbiogQWxsIG9iamVjdHMgaW4gdGhlIGV2ZW50IGhhbmRsaW5nIGNoYWluIHNob3VsZCBpbmhlcml0IGZyb20gdGhpcyBjbGFzc1xuKlxuKi9cblxuXG5cbnZhciBGT1JCSURERU5fRVZFTlRfTkFNRVMgPSB7XG4gICdobHNFdmVudEdlbmVyaWMnOiB0cnVlLFxuICAnaGxzSGFuZGxlckRlc3Ryb3lpbmcnOiB0cnVlLFxuICAnaGxzSGFuZGxlckRlc3Ryb3llZCc6IHRydWVcbn07XG5cbnZhciBldmVudF9oYW5kbGVyX0V2ZW50SGFuZGxlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIEV2ZW50SGFuZGxlcihobHMpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmhhbmRsZWRFdmVudHMgPSB2b2lkIDA7XG4gICAgdGhpcy51c2VHZW5lcmljSGFuZGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICB0aGlzLm9uRXZlbnQgPSB0aGlzLm9uRXZlbnQuYmluZCh0aGlzKTtcblxuICAgIGZvciAodmFyIF9sZW4gPSBhcmd1bWVudHMubGVuZ3RoLCBldmVudHMgPSBuZXcgQXJyYXkoX2xlbiA+IDEgPyBfbGVuIC0gMSA6IDApLCBfa2V5ID0gMTsgX2tleSA8IF9sZW47IF9rZXkrKykge1xuICAgICAgZXZlbnRzW19rZXkgLSAxXSA9IGFyZ3VtZW50c1tfa2V5XTtcbiAgICB9XG5cbiAgICB0aGlzLmhhbmRsZWRFdmVudHMgPSBldmVudHM7XG4gICAgdGhpcy51c2VHZW5lcmljSGFuZGxlciA9IHRydWU7XG4gICAgdGhpcy5yZWdpc3Rlckxpc3RlbmVycygpO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IEV2ZW50SGFuZGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHRoaXMub25IYW5kbGVyRGVzdHJveWluZygpO1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMub25IYW5kbGVyRGVzdHJveWVkKCk7XG4gIH07XG5cbiAgX3Byb3RvLm9uSGFuZGxlckRlc3Ryb3lpbmcgPSBmdW5jdGlvbiBvbkhhbmRsZXJEZXN0cm95aW5nKCkge307XG5cbiAgX3Byb3RvLm9uSGFuZGxlckRlc3Ryb3llZCA9IGZ1bmN0aW9uIG9uSGFuZGxlckRlc3Ryb3llZCgpIHt9O1xuXG4gIF9wcm90by5pc0V2ZW50SGFuZGxlciA9IGZ1bmN0aW9uIGlzRXZlbnRIYW5kbGVyKCkge1xuICAgIHJldHVybiB0eXBlb2YgdGhpcy5oYW5kbGVkRXZlbnRzID09PSAnb2JqZWN0JyAmJiB0aGlzLmhhbmRsZWRFdmVudHMubGVuZ3RoICYmIHR5cGVvZiB0aGlzLm9uRXZlbnQgPT09ICdmdW5jdGlvbic7XG4gIH07XG5cbiAgX3Byb3RvLnJlZ2lzdGVyTGlzdGVuZXJzID0gZnVuY3Rpb24gcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgaWYgKHRoaXMuaXNFdmVudEhhbmRsZXIoKSkge1xuICAgICAgdGhpcy5oYW5kbGVkRXZlbnRzLmZvckVhY2goZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgIGlmIChGT1JCSURERU5fRVZFTlRfTkFNRVNbZXZlbnRdKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdGb3JiaWRkZW4gZXZlbnQtbmFtZTogJyArIGV2ZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuaGxzLm9uKGV2ZW50LCB0aGlzLm9uRXZlbnQpO1xuICAgICAgfSwgdGhpcyk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by51bnJlZ2lzdGVyTGlzdGVuZXJzID0gZnVuY3Rpb24gdW5yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBpZiAodGhpcy5pc0V2ZW50SGFuZGxlcigpKSB7XG4gICAgICB0aGlzLmhhbmRsZWRFdmVudHMuZm9yRWFjaChmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgICAgdGhpcy5obHMub2ZmKGV2ZW50LCB0aGlzLm9uRXZlbnQpO1xuICAgICAgfSwgdGhpcyk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBhcmd1bWVudHM6IGV2ZW50IChzdHJpbmcpLCBkYXRhIChhbnkpXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLm9uRXZlbnQgPSBmdW5jdGlvbiBvbkV2ZW50KGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5vbkV2ZW50R2VuZXJpYyhldmVudCwgZGF0YSk7XG4gIH07XG5cbiAgX3Byb3RvLm9uRXZlbnRHZW5lcmljID0gZnVuY3Rpb24gb25FdmVudEdlbmVyaWMoZXZlbnQsIGRhdGEpIHtcbiAgICB2YXIgZXZlbnRUb0Z1bmN0aW9uID0gZnVuY3Rpb24gZXZlbnRUb0Z1bmN0aW9uKGV2ZW50LCBkYXRhKSB7XG4gICAgICB2YXIgZnVuY05hbWUgPSAnb24nICsgZXZlbnQucmVwbGFjZSgnaGxzJywgJycpO1xuXG4gICAgICBpZiAodHlwZW9mIHRoaXNbZnVuY05hbWVdICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkV2ZW50IFwiICsgZXZlbnQgKyBcIiBoYXMgbm8gZ2VuZXJpYyBoYW5kbGVyIGluIHRoaXMgXCIgKyB0aGlzLmNvbnN0cnVjdG9yLm5hbWUgKyBcIiBjbGFzcyAodHJpZWQgXCIgKyBmdW5jTmFtZSArIFwiKVwiKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRoaXNbZnVuY05hbWVdLmJpbmQodGhpcywgZGF0YSk7XG4gICAgfTtcblxuICAgIHRyeSB7XG4gICAgICBldmVudFRvRnVuY3Rpb24uY2FsbCh0aGlzLCBldmVudCwgZGF0YSkuY2FsbCgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKFwiQW4gaW50ZXJuYWwgZXJyb3IgaGFwcGVuZWQgd2hpbGUgaGFuZGxpbmcgZXZlbnQgXCIgKyBldmVudCArIFwiLiBFcnJvciBtZXNzYWdlOiBcXFwiXCIgKyBlcnIubWVzc2FnZSArIFwiXFxcIi4gSGVyZSBpcyBhIHN0YWNrdHJhY2U6XCIsIGVycik7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5PVEhFUl9FUlJPUixcbiAgICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLklOVEVSTkFMX0VYQ0VQVElPTixcbiAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICBldmVudDogZXZlbnQsXG4gICAgICAgIGVycjogZXJyXG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIEV2ZW50SGFuZGxlcjtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgZXZlbnRfaGFuZGxlciA9IChldmVudF9oYW5kbGVyX0V2ZW50SGFuZGxlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy90eXBlcy9sb2FkZXIudHNcbi8qKlxuICogYHR5cGVgIHByb3BlcnR5IHZhbHVlcyBmb3IgdGhpcyBsb2FkZXJzJyBjb250ZXh0IG9iamVjdFxuICogQGVudW1cbiAqXG4gKi9cbnZhciBQbGF5bGlzdENvbnRleHRUeXBlO1xuLyoqXG4gKiBAZW51bSB7c3RyaW5nfVxuICovXG5cbihmdW5jdGlvbiAoUGxheWxpc3RDb250ZXh0VHlwZSkge1xuICBQbGF5bGlzdENvbnRleHRUeXBlW1wiTUFOSUZFU1RcIl0gPSBcIm1hbmlmZXN0XCI7XG4gIFBsYXlsaXN0Q29udGV4dFR5cGVbXCJMRVZFTFwiXSA9IFwibGV2ZWxcIjtcbiAgUGxheWxpc3RDb250ZXh0VHlwZVtcIkFVRElPX1RSQUNLXCJdID0gXCJhdWRpb1RyYWNrXCI7XG4gIFBsYXlsaXN0Q29udGV4dFR5cGVbXCJTVUJUSVRMRV9UUkFDS1wiXSA9IFwic3VidGl0bGVUcmFja1wiO1xufSkoUGxheWxpc3RDb250ZXh0VHlwZSB8fCAoUGxheWxpc3RDb250ZXh0VHlwZSA9IHt9KSk7XG5cbnZhciBQbGF5bGlzdExldmVsVHlwZTtcblxuKGZ1bmN0aW9uIChQbGF5bGlzdExldmVsVHlwZSkge1xuICBQbGF5bGlzdExldmVsVHlwZVtcIk1BSU5cIl0gPSBcIm1haW5cIjtcbiAgUGxheWxpc3RMZXZlbFR5cGVbXCJBVURJT1wiXSA9IFwiYXVkaW9cIjtcbiAgUGxheWxpc3RMZXZlbFR5cGVbXCJTVUJUSVRMRVwiXSA9IFwic3VidGl0bGVcIjtcbn0pKFBsYXlsaXN0TGV2ZWxUeXBlIHx8IChQbGF5bGlzdExldmVsVHlwZSA9IHt9KSk7XG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2RlbXV4L21wNGRlbXV4ZXIuanNcbnZhciBtcDRkZW11eGVyID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL2RlbXV4L21wNGRlbXV4ZXIuanNcIik7XG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2xvYWRlci9sZXZlbC1rZXkudHNcbmZ1bmN0aW9uIF9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gX2NyZWF0ZUNsYXNzKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgX2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuXG5cbnZhciBsZXZlbF9rZXlfTGV2ZWxLZXkgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBMZXZlbEtleShiYXNlVVJJLCByZWxhdGl2ZVVSSSkge1xuICAgIHRoaXMuX3VyaSA9IG51bGw7XG4gICAgdGhpcy5iYXNldXJpID0gdm9pZCAwO1xuICAgIHRoaXMucmVsdXJpID0gdm9pZCAwO1xuICAgIHRoaXMubWV0aG9kID0gbnVsbDtcbiAgICB0aGlzLmtleSA9IG51bGw7XG4gICAgdGhpcy5pdiA9IG51bGw7XG4gICAgdGhpcy5iYXNldXJpID0gYmFzZVVSSTtcbiAgICB0aGlzLnJlbHVyaSA9IHJlbGF0aXZlVVJJO1xuICB9XG5cbiAgX2NyZWF0ZUNsYXNzKExldmVsS2V5LCBbe1xuICAgIGtleTogXCJ1cmlcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIGlmICghdGhpcy5fdXJpICYmIHRoaXMucmVsdXJpKSB7XG4gICAgICAgIHRoaXMuX3VyaSA9IE9iamVjdCh1cmxfdG9vbGtpdFtcImJ1aWxkQWJzb2x1dGVVUkxcIl0pKHRoaXMuYmFzZXVyaSwgdGhpcy5yZWx1cmksIHtcbiAgICAgICAgICBhbHdheXNOb3JtYWxpemU6IHRydWVcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0aGlzLl91cmk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIExldmVsS2V5O1xufSgpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2xvYWRlci9mcmFnbWVudC50c1xuXG5cblxuZnVuY3Rpb24gZnJhZ21lbnRfZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9XG5cbmZ1bmN0aW9uIGZyYWdtZW50X2NyZWF0ZUNsYXNzKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZnJhZ21lbnRfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGZyYWdtZW50X2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9XG5cblxuXG5cbnZhciBFbGVtZW50YXJ5U3RyZWFtVHlwZXM7XG5cbihmdW5jdGlvbiAoRWxlbWVudGFyeVN0cmVhbVR5cGVzKSB7XG4gIEVsZW1lbnRhcnlTdHJlYW1UeXBlc1tcIkFVRElPXCJdID0gXCJhdWRpb1wiO1xuICBFbGVtZW50YXJ5U3RyZWFtVHlwZXNbXCJWSURFT1wiXSA9IFwidmlkZW9cIjtcbn0pKEVsZW1lbnRhcnlTdHJlYW1UeXBlcyB8fCAoRWxlbWVudGFyeVN0cmVhbVR5cGVzID0ge30pKTtcblxudmFyIGZyYWdtZW50X0ZyYWdtZW50ID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRnJhZ21lbnQoKSB7XG4gICAgdmFyIF90aGlzJF9lbGVtZW50YXJ5U3RyZTtcblxuICAgIHRoaXMuX3VybCA9IG51bGw7XG4gICAgdGhpcy5fYnl0ZVJhbmdlID0gbnVsbDtcbiAgICB0aGlzLl9kZWNyeXB0ZGF0YSA9IG51bGw7XG4gICAgdGhpcy5fZWxlbWVudGFyeVN0cmVhbXMgPSAoX3RoaXMkX2VsZW1lbnRhcnlTdHJlID0ge30sIF90aGlzJF9lbGVtZW50YXJ5U3RyZVtFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU9dID0gZmFsc2UsIF90aGlzJF9lbGVtZW50YXJ5U3RyZVtFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU9dID0gZmFsc2UsIF90aGlzJF9lbGVtZW50YXJ5U3RyZSk7XG4gICAgdGhpcy5kZWx0YVBUUyA9IDA7XG4gICAgdGhpcy5yYXdQcm9ncmFtRGF0ZVRpbWUgPSBudWxsO1xuICAgIHRoaXMucHJvZ3JhbURhdGVUaW1lID0gbnVsbDtcbiAgICB0aGlzLnRpdGxlID0gbnVsbDtcbiAgICB0aGlzLnRhZ0xpc3QgPSBbXTtcbiAgICB0aGlzLmNjID0gdm9pZCAwO1xuICAgIHRoaXMudHlwZSA9IHZvaWQgMDtcbiAgICB0aGlzLnJlbHVybCA9IHZvaWQgMDtcbiAgICB0aGlzLmJhc2V1cmwgPSB2b2lkIDA7XG4gICAgdGhpcy5kdXJhdGlvbiA9IHZvaWQgMDtcbiAgICB0aGlzLnN0YXJ0ID0gdm9pZCAwO1xuICAgIHRoaXMuc24gPSAwO1xuICAgIHRoaXMudXJsSWQgPSAwO1xuICAgIHRoaXMubGV2ZWwgPSAwO1xuICAgIHRoaXMubGV2ZWxrZXkgPSB2b2lkIDA7XG4gICAgdGhpcy5sb2FkZXIgPSB2b2lkIDA7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gRnJhZ21lbnQucHJvdG90eXBlO1xuXG4gIC8vIHNldEJ5dGVSYW5nZSBjb252ZXJ0cyBhIEVYVC1YLUJZVEVSQU5HRSBhdHRyaWJ1dGUgaW50byBhIHR3byBlbGVtZW50IGFycmF5XG4gIF9wcm90by5zZXRCeXRlUmFuZ2UgPSBmdW5jdGlvbiBzZXRCeXRlUmFuZ2UodmFsdWUsIHByZXZpb3VzRnJhZykge1xuICAgIHZhciBwYXJhbXMgPSB2YWx1ZS5zcGxpdCgnQCcsIDIpO1xuICAgIHZhciBieXRlUmFuZ2UgPSBbXTtcblxuICAgIGlmIChwYXJhbXMubGVuZ3RoID09PSAxKSB7XG4gICAgICBieXRlUmFuZ2VbMF0gPSBwcmV2aW91c0ZyYWcgPyBwcmV2aW91c0ZyYWcuYnl0ZVJhbmdlRW5kT2Zmc2V0IDogMDtcbiAgICB9IGVsc2Uge1xuICAgICAgYnl0ZVJhbmdlWzBdID0gcGFyc2VJbnQocGFyYW1zWzFdKTtcbiAgICB9XG5cbiAgICBieXRlUmFuZ2VbMV0gPSBwYXJzZUludChwYXJhbXNbMF0pICsgYnl0ZVJhbmdlWzBdO1xuICAgIHRoaXMuX2J5dGVSYW5nZSA9IGJ5dGVSYW5nZTtcbiAgfTtcblxuICAvKipcbiAgICogQHBhcmFtIHtFbGVtZW50YXJ5U3RyZWFtVHlwZXN9IHR5cGVcbiAgICovXG4gIF9wcm90by5hZGRFbGVtZW50YXJ5U3RyZWFtID0gZnVuY3Rpb24gYWRkRWxlbWVudGFyeVN0cmVhbSh0eXBlKSB7XG4gICAgdGhpcy5fZWxlbWVudGFyeVN0cmVhbXNbdHlwZV0gPSB0cnVlO1xuICB9XG4gIC8qKlxuICAgKiBAcGFyYW0ge0VsZW1lbnRhcnlTdHJlYW1UeXBlc30gdHlwZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5oYXNFbGVtZW50YXJ5U3RyZWFtID0gZnVuY3Rpb24gaGFzRWxlbWVudGFyeVN0cmVhbSh0eXBlKSB7XG4gICAgcmV0dXJuIHRoaXMuX2VsZW1lbnRhcnlTdHJlYW1zW3R5cGVdID09PSB0cnVlO1xuICB9XG4gIC8qKlxuICAgKiBVdGlsaXR5IG1ldGhvZCBmb3IgcGFyc2VMZXZlbFBsYXlsaXN0IHRvIGNyZWF0ZSBhbiBpbml0aWFsaXphdGlvbiB2ZWN0b3IgZm9yIGEgZ2l2ZW4gc2VnbWVudFxuICAgKiBAcGFyYW0ge251bWJlcn0gc2VnbWVudE51bWJlciAtIHNlZ21lbnQgbnVtYmVyIHRvIGdlbmVyYXRlIElWIHdpdGhcbiAgICogQHJldHVybnMge1VpbnQ4QXJyYXl9XG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmNyZWF0ZUluaXRpYWxpemF0aW9uVmVjdG9yID0gZnVuY3Rpb24gY3JlYXRlSW5pdGlhbGl6YXRpb25WZWN0b3Ioc2VnbWVudE51bWJlcikge1xuICAgIHZhciB1aW50OFZpZXcgPSBuZXcgVWludDhBcnJheSgxNik7XG5cbiAgICBmb3IgKHZhciBpID0gMTI7IGkgPCAxNjsgaSsrKSB7XG4gICAgICB1aW50OFZpZXdbaV0gPSBzZWdtZW50TnVtYmVyID4+IDggKiAoMTUgLSBpKSAmIDB4ZmY7XG4gICAgfVxuXG4gICAgcmV0dXJuIHVpbnQ4VmlldztcbiAgfVxuICAvKipcbiAgICogVXRpbGl0eSBtZXRob2QgZm9yIHBhcnNlTGV2ZWxQbGF5bGlzdCB0byBnZXQgYSBmcmFnbWVudCdzIGRlY3J5cHRpb24gZGF0YSBmcm9tIHRoZSBjdXJyZW50bHkgcGFyc2VkIGVuY3J5cHRpb24ga2V5IGRhdGFcbiAgICogQHBhcmFtIGxldmVsa2V5IC0gYSBwbGF5bGlzdCdzIGVuY3J5cHRpb24gaW5mb1xuICAgKiBAcGFyYW0gc2VnbWVudE51bWJlciAtIHRoZSBmcmFnbWVudCdzIHNlZ21lbnQgbnVtYmVyXG4gICAqIEByZXR1cm5zIHtMZXZlbEtleX0gLSBhbiBvYmplY3QgdG8gYmUgYXBwbGllZCBhcyBhIGZyYWdtZW50J3MgZGVjcnlwdGRhdGFcbiAgICovXG4gIDtcblxuICBfcHJvdG8uc2V0RGVjcnlwdERhdGFGcm9tTGV2ZWxLZXkgPSBmdW5jdGlvbiBzZXREZWNyeXB0RGF0YUZyb21MZXZlbEtleShsZXZlbGtleSwgc2VnbWVudE51bWJlcikge1xuICAgIHZhciBkZWNyeXB0ZGF0YSA9IGxldmVsa2V5O1xuXG4gICAgaWYgKChsZXZlbGtleSA9PT0gbnVsbCB8fCBsZXZlbGtleSA9PT0gdm9pZCAwID8gdm9pZCAwIDogbGV2ZWxrZXkubWV0aG9kKSAmJiBsZXZlbGtleS51cmkgJiYgIWxldmVsa2V5Lml2KSB7XG4gICAgICBkZWNyeXB0ZGF0YSA9IG5ldyBsZXZlbF9rZXlfTGV2ZWxLZXkobGV2ZWxrZXkuYmFzZXVyaSwgbGV2ZWxrZXkucmVsdXJpKTtcbiAgICAgIGRlY3J5cHRkYXRhLm1ldGhvZCA9IGxldmVsa2V5Lm1ldGhvZDtcbiAgICAgIGRlY3J5cHRkYXRhLml2ID0gdGhpcy5jcmVhdGVJbml0aWFsaXphdGlvblZlY3RvcihzZWdtZW50TnVtYmVyKTtcbiAgICB9XG5cbiAgICByZXR1cm4gZGVjcnlwdGRhdGE7XG4gIH07XG5cbiAgZnJhZ21lbnRfY3JlYXRlQ2xhc3MoRnJhZ21lbnQsIFt7XG4gICAga2V5OiBcInVybFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgaWYgKCF0aGlzLl91cmwgJiYgdGhpcy5yZWx1cmwpIHtcbiAgICAgICAgdGhpcy5fdXJsID0gT2JqZWN0KHVybF90b29sa2l0W1wiYnVpbGRBYnNvbHV0ZVVSTFwiXSkodGhpcy5iYXNldXJsLCB0aGlzLnJlbHVybCwge1xuICAgICAgICAgIGFsd2F5c05vcm1hbGl6ZTogdHJ1ZVxuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRoaXMuX3VybDtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gc2V0KHZhbHVlKSB7XG4gICAgICB0aGlzLl91cmwgPSB2YWx1ZTtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiYnl0ZVJhbmdlXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICBpZiAoIXRoaXMuX2J5dGVSYW5nZSkge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0aGlzLl9ieXRlUmFuZ2U7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJieXRlUmFuZ2VTdGFydE9mZnNldFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuYnl0ZVJhbmdlWzBdO1xuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJieXRlUmFuZ2VFbmRPZmZzZXRcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLmJ5dGVSYW5nZVsxXTtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiZGVjcnlwdGRhdGFcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIGlmICghdGhpcy5sZXZlbGtleSAmJiAhdGhpcy5fZGVjcnlwdGRhdGEpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG5cbiAgICAgIGlmICghdGhpcy5fZGVjcnlwdGRhdGEgJiYgdGhpcy5sZXZlbGtleSkge1xuICAgICAgICB2YXIgc24gPSB0aGlzLnNuO1xuXG4gICAgICAgIGlmICh0eXBlb2Ygc24gIT09ICdudW1iZXInKSB7XG4gICAgICAgICAgLy8gV2UgYXJlIGZldGNoaW5nIGRlY3J5cHRpb24gZGF0YSBmb3IgYSBpbml0aWFsaXphdGlvbiBzZWdtZW50XG4gICAgICAgICAgLy8gSWYgdGhlIHNlZ21lbnQgd2FzIGVuY3J5cHRlZCB3aXRoIEFFUy0xMjhcbiAgICAgICAgICAvLyBJdCBtdXN0IGhhdmUgYW4gSVYgZGVmaW5lZC4gV2UgY2Fubm90IHN1YnN0aXR1dGUgdGhlIFNlZ21lbnQgTnVtYmVyIGluLlxuICAgICAgICAgIGlmICh0aGlzLmxldmVsa2V5ICYmIHRoaXMubGV2ZWxrZXkubWV0aG9kID09PSAnQUVTLTEyOCcgJiYgIXRoaXMubGV2ZWxrZXkuaXYpIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwibWlzc2luZyBJViBmb3IgaW5pdGlhbGl6YXRpb24gc2VnbWVudCB3aXRoIG1ldGhvZD1cXFwiXCIgKyB0aGlzLmxldmVsa2V5Lm1ldGhvZCArIFwiXFxcIiAtIGNvbXBsaWFuY2UgaXNzdWVcIik7XG4gICAgICAgICAgfVxuICAgICAgICAgIC8qXG4gICAgICAgICAgQmUgY29udmVydGVkIHRvIGEgTnVtYmVyLlxuICAgICAgICAgICdpbml0U2VnbWVudCcgd2lsbCBiZWNvbWUgTmFOLlxuICAgICAgICAgIE5hTiwgd2hpY2ggd2hlbiBjb252ZXJ0ZWQgdGhyb3VnaCBUb0ludDMyKCkgLT4gKzAuXG4gICAgICAgICAgLS0tXG4gICAgICAgICAgRXhwbGljaXRseSBzZXQgc24gdG8gcmVzdWx0aW5nIHZhbHVlIGZyb20gaW1wbGljaXQgY29udmVyc2lvbnMgJ2luaXRTZWdtZW50JyB2YWx1ZXMgZm9yIElWIGdlbmVyYXRpb24uXG4gICAgICAgICAgKi9cblxuXG4gICAgICAgICAgc24gPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fZGVjcnlwdGRhdGEgPSB0aGlzLnNldERlY3J5cHREYXRhRnJvbUxldmVsS2V5KHRoaXMubGV2ZWxrZXksIHNuKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRoaXMuX2RlY3J5cHRkYXRhO1xuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJlbmRQcm9ncmFtRGF0ZVRpbWVcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIGlmICh0aGlzLnByb2dyYW1EYXRlVGltZSA9PT0gbnVsbCkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHRoaXMucHJvZ3JhbURhdGVUaW1lKSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgdmFyIGR1cmF0aW9uID0gIU9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkodGhpcy5kdXJhdGlvbikgPyAwIDogdGhpcy5kdXJhdGlvbjtcbiAgICAgIHJldHVybiB0aGlzLnByb2dyYW1EYXRlVGltZSArIGR1cmF0aW9uICogMTAwMDtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiZW5jcnlwdGVkXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gISEodGhpcy5kZWNyeXB0ZGF0YSAmJiB0aGlzLmRlY3J5cHRkYXRhLnVyaSAhPT0gbnVsbCAmJiB0aGlzLmRlY3J5cHRkYXRhLmtleSA9PT0gbnVsbCk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIEZyYWdtZW50O1xufSgpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2xvYWRlci9sZXZlbC5qc1xuXG5cbmZ1bmN0aW9uIGxldmVsX2RlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfVxuXG5mdW5jdGlvbiBsZXZlbF9jcmVhdGVDbGFzcyhDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGxldmVsX2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBsZXZlbF9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfVxuXG52YXIgbGV2ZWxfTGV2ZWwgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBMZXZlbChiYXNlVXJsKSB7XG4gICAgLy8gUGxlYXNlIGtlZXAgcHJvcGVydGllcyBpbiBhbHBoYWJldGljYWwgb3JkZXJcbiAgICB0aGlzLmVuZENDID0gMDtcbiAgICB0aGlzLmVuZFNOID0gMDtcbiAgICB0aGlzLmZyYWdtZW50cyA9IFtdO1xuICAgIHRoaXMuaW5pdFNlZ21lbnQgPSBudWxsO1xuICAgIHRoaXMubGl2ZSA9IHRydWU7XG4gICAgdGhpcy5uZWVkU2lkeFJhbmdlcyA9IGZhbHNlO1xuICAgIHRoaXMuc3RhcnRDQyA9IDA7XG4gICAgdGhpcy5zdGFydFNOID0gMDtcbiAgICB0aGlzLnN0YXJ0VGltZU9mZnNldCA9IG51bGw7XG4gICAgdGhpcy50YXJnZXRkdXJhdGlvbiA9IDA7XG4gICAgdGhpcy50b3RhbGR1cmF0aW9uID0gMDtcbiAgICB0aGlzLnR5cGUgPSBudWxsO1xuICAgIHRoaXMudXJsID0gYmFzZVVybDtcbiAgICB0aGlzLnZlcnNpb24gPSBudWxsO1xuICB9XG5cbiAgbGV2ZWxfY3JlYXRlQ2xhc3MoTGV2ZWwsIFt7XG4gICAga2V5OiBcImhhc1Byb2dyYW1EYXRlVGltZVwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuICEhKHRoaXMuZnJhZ21lbnRzWzBdICYmIE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkodGhpcy5mcmFnbWVudHNbMF0ucHJvZ3JhbURhdGVUaW1lKSk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIExldmVsO1xufSgpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL2F0dHItbGlzdC5qc1xudmFyIERFQ0lNQUxfUkVTT0xVVElPTl9SRUdFWCA9IC9eKFxcZCspeChcXGQrKSQvOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVzZWxlc3MtZXNjYXBlXG5cbnZhciBBVFRSX0xJU1RfUkVHRVggPSAvXFxzKiguKz8pXFxzKj0oKD86XFxcIi4qP1xcXCIpfC4qPykoPzosfCQpL2c7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdXNlbGVzcy1lc2NhcGVcbi8vIGFkYXB0ZWQgZnJvbSBodHRwczovL2dpdGh1Yi5jb20va2Fub25naWwvbm9kZS1tM3U4cGFyc2UvYmxvYi9tYXN0ZXIvYXR0cmxpc3QuanNcblxudmFyIEF0dHJMaXN0ID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gQXR0ckxpc3QoYXR0cnMpIHtcbiAgICBpZiAodHlwZW9mIGF0dHJzID09PSAnc3RyaW5nJykge1xuICAgICAgYXR0cnMgPSBBdHRyTGlzdC5wYXJzZUF0dHJMaXN0KGF0dHJzKTtcbiAgICB9XG5cbiAgICBmb3IgKHZhciBhdHRyIGluIGF0dHJzKSB7XG4gICAgICBpZiAoYXR0cnMuaGFzT3duUHJvcGVydHkoYXR0cikpIHtcbiAgICAgICAgdGhpc1thdHRyXSA9IGF0dHJzW2F0dHJdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBBdHRyTGlzdC5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlY2ltYWxJbnRlZ2VyID0gZnVuY3Rpb24gZGVjaW1hbEludGVnZXIoYXR0ck5hbWUpIHtcbiAgICB2YXIgaW50VmFsdWUgPSBwYXJzZUludCh0aGlzW2F0dHJOYW1lXSwgMTApO1xuXG4gICAgaWYgKGludFZhbHVlID4gTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIpIHtcbiAgICAgIHJldHVybiBJbmZpbml0eTtcbiAgICB9XG5cbiAgICByZXR1cm4gaW50VmFsdWU7XG4gIH07XG5cbiAgX3Byb3RvLmhleGFkZWNpbWFsSW50ZWdlciA9IGZ1bmN0aW9uIGhleGFkZWNpbWFsSW50ZWdlcihhdHRyTmFtZSkge1xuICAgIGlmICh0aGlzW2F0dHJOYW1lXSkge1xuICAgICAgdmFyIHN0cmluZ1ZhbHVlID0gKHRoaXNbYXR0ck5hbWVdIHx8ICcweCcpLnNsaWNlKDIpO1xuICAgICAgc3RyaW5nVmFsdWUgPSAoc3RyaW5nVmFsdWUubGVuZ3RoICYgMSA/ICcwJyA6ICcnKSArIHN0cmluZ1ZhbHVlO1xuICAgICAgdmFyIHZhbHVlID0gbmV3IFVpbnQ4QXJyYXkoc3RyaW5nVmFsdWUubGVuZ3RoIC8gMik7XG5cbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgc3RyaW5nVmFsdWUubGVuZ3RoIC8gMjsgaSsrKSB7XG4gICAgICAgIHZhbHVlW2ldID0gcGFyc2VJbnQoc3RyaW5nVmFsdWUuc2xpY2UoaSAqIDIsIGkgKiAyICsgMiksIDE2KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmhleGFkZWNpbWFsSW50ZWdlckFzTnVtYmVyID0gZnVuY3Rpb24gaGV4YWRlY2ltYWxJbnRlZ2VyQXNOdW1iZXIoYXR0ck5hbWUpIHtcbiAgICB2YXIgaW50VmFsdWUgPSBwYXJzZUludCh0aGlzW2F0dHJOYW1lXSwgMTYpO1xuXG4gICAgaWYgKGludFZhbHVlID4gTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIpIHtcbiAgICAgIHJldHVybiBJbmZpbml0eTtcbiAgICB9XG5cbiAgICByZXR1cm4gaW50VmFsdWU7XG4gIH07XG5cbiAgX3Byb3RvLmRlY2ltYWxGbG9hdGluZ1BvaW50ID0gZnVuY3Rpb24gZGVjaW1hbEZsb2F0aW5nUG9pbnQoYXR0ck5hbWUpIHtcbiAgICByZXR1cm4gcGFyc2VGbG9hdCh0aGlzW2F0dHJOYW1lXSk7XG4gIH07XG5cbiAgX3Byb3RvLmVudW1lcmF0ZWRTdHJpbmcgPSBmdW5jdGlvbiBlbnVtZXJhdGVkU3RyaW5nKGF0dHJOYW1lKSB7XG4gICAgcmV0dXJuIHRoaXNbYXR0ck5hbWVdO1xuICB9O1xuXG4gIF9wcm90by5kZWNpbWFsUmVzb2x1dGlvbiA9IGZ1bmN0aW9uIGRlY2ltYWxSZXNvbHV0aW9uKGF0dHJOYW1lKSB7XG4gICAgdmFyIHJlcyA9IERFQ0lNQUxfUkVTT0xVVElPTl9SRUdFWC5leGVjKHRoaXNbYXR0ck5hbWVdKTtcblxuICAgIGlmIChyZXMgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIHdpZHRoOiBwYXJzZUludChyZXNbMV0sIDEwKSxcbiAgICAgIGhlaWdodDogcGFyc2VJbnQocmVzWzJdLCAxMClcbiAgICB9O1xuICB9O1xuXG4gIEF0dHJMaXN0LnBhcnNlQXR0ckxpc3QgPSBmdW5jdGlvbiBwYXJzZUF0dHJMaXN0KGlucHV0KSB7XG4gICAgdmFyIG1hdGNoLFxuICAgICAgICBhdHRycyA9IHt9O1xuICAgIEFUVFJfTElTVF9SRUdFWC5sYXN0SW5kZXggPSAwO1xuXG4gICAgd2hpbGUgKChtYXRjaCA9IEFUVFJfTElTVF9SRUdFWC5leGVjKGlucHV0KSkgIT09IG51bGwpIHtcbiAgICAgIHZhciB2YWx1ZSA9IG1hdGNoWzJdLFxuICAgICAgICAgIHF1b3RlID0gJ1wiJztcblxuICAgICAgaWYgKHZhbHVlLmluZGV4T2YocXVvdGUpID09PSAwICYmIHZhbHVlLmxhc3RJbmRleE9mKHF1b3RlKSA9PT0gdmFsdWUubGVuZ3RoIC0gMSkge1xuICAgICAgICB2YWx1ZSA9IHZhbHVlLnNsaWNlKDEsIC0xKTtcbiAgICAgIH1cblxuICAgICAgYXR0cnNbbWF0Y2hbMV1dID0gdmFsdWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIGF0dHJzO1xuICB9O1xuXG4gIHJldHVybiBBdHRyTGlzdDtcbn0oKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgYXR0cl9saXN0ID0gKEF0dHJMaXN0KTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL2NvZGVjcy50c1xuLy8gZnJvbSBodHRwOi8vbXA0cmEub3JnL2NvZGVjcy5odG1sXG52YXIgc2FtcGxlRW50cnlDb2Rlc0lTTyA9IHtcbiAgYXVkaW86IHtcbiAgICAnYTNkcyc6IHRydWUsXG4gICAgJ2FjLTMnOiB0cnVlLFxuICAgICdhYy00JzogdHJ1ZSxcbiAgICAnYWxhYyc6IHRydWUsXG4gICAgJ2FsYXcnOiB0cnVlLFxuICAgICdkcmExJzogdHJ1ZSxcbiAgICAnZHRzKyc6IHRydWUsXG4gICAgJ2R0cy0nOiB0cnVlLFxuICAgICdkdHNjJzogdHJ1ZSxcbiAgICAnZHRzZSc6IHRydWUsXG4gICAgJ2R0c2gnOiB0cnVlLFxuICAgICdlYy0zJzogdHJ1ZSxcbiAgICAnZW5jYSc6IHRydWUsXG4gICAgJ2c3MTknOiB0cnVlLFxuICAgICdnNzI2JzogdHJ1ZSxcbiAgICAnbTRhZSc6IHRydWUsXG4gICAgJ21oYTEnOiB0cnVlLFxuICAgICdtaGEyJzogdHJ1ZSxcbiAgICAnbWhtMSc6IHRydWUsXG4gICAgJ21obTInOiB0cnVlLFxuICAgICdtbHBhJzogdHJ1ZSxcbiAgICAnbXA0YSc6IHRydWUsXG4gICAgJ3JhdyAnOiB0cnVlLFxuICAgICdPcHVzJzogdHJ1ZSxcbiAgICAnc2Ftcic6IHRydWUsXG4gICAgJ3Nhd2InOiB0cnVlLFxuICAgICdzYXdwJzogdHJ1ZSxcbiAgICAnc2V2Yyc6IHRydWUsXG4gICAgJ3NxY3AnOiB0cnVlLFxuICAgICdzc212JzogdHJ1ZSxcbiAgICAndHdvcyc6IHRydWUsXG4gICAgJ3VsYXcnOiB0cnVlXG4gIH0sXG4gIHZpZGVvOiB7XG4gICAgJ2F2YzEnOiB0cnVlLFxuICAgICdhdmMyJzogdHJ1ZSxcbiAgICAnYXZjMyc6IHRydWUsXG4gICAgJ2F2YzQnOiB0cnVlLFxuICAgICdhdmNwJzogdHJ1ZSxcbiAgICAnZHJhYyc6IHRydWUsXG4gICAgJ2R2YXYnOiB0cnVlLFxuICAgICdkdmhlJzogdHJ1ZSxcbiAgICAnZW5jdic6IHRydWUsXG4gICAgJ2hldjEnOiB0cnVlLFxuICAgICdodmMxJzogdHJ1ZSxcbiAgICAnbWpwMic6IHRydWUsXG4gICAgJ21wNHYnOiB0cnVlLFxuICAgICdtdmMxJzogdHJ1ZSxcbiAgICAnbXZjMic6IHRydWUsXG4gICAgJ212YzMnOiB0cnVlLFxuICAgICdtdmM0JzogdHJ1ZSxcbiAgICAncmVzdic6IHRydWUsXG4gICAgJ3J2NjAnOiB0cnVlLFxuICAgICdzMjYzJzogdHJ1ZSxcbiAgICAnc3ZjMSc6IHRydWUsXG4gICAgJ3N2YzInOiB0cnVlLFxuICAgICd2Yy0xJzogdHJ1ZSxcbiAgICAndnAwOCc6IHRydWUsXG4gICAgJ3ZwMDknOiB0cnVlXG4gIH1cbn07XG5cbmZ1bmN0aW9uIGlzQ29kZWNUeXBlKGNvZGVjLCB0eXBlKSB7XG4gIHZhciB0eXBlQ29kZXMgPSBzYW1wbGVFbnRyeUNvZGVzSVNPW3R5cGVdO1xuICByZXR1cm4gISF0eXBlQ29kZXMgJiYgdHlwZUNvZGVzW2NvZGVjLnNsaWNlKDAsIDQpXSA9PT0gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gaXNDb2RlY1N1cHBvcnRlZEluTXA0KGNvZGVjLCB0eXBlKSB7XG4gIHJldHVybiBNZWRpYVNvdXJjZS5pc1R5cGVTdXBwb3J0ZWQoKHR5cGUgfHwgJ3ZpZGVvJykgKyBcIi9tcDQ7Y29kZWNzPVxcXCJcIiArIGNvZGVjICsgXCJcXFwiXCIpO1xufVxuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2xvYWRlci9tM3U4LXBhcnNlci50c1xuXG5cblxuXG5cblxuXG5cblxuXG5cbi8qKlxuICogTTNVOCBwYXJzZXJcbiAqIEBtb2R1bGVcbiAqL1xuLy8gaHR0cHM6Ly9yZWdleDEwMS5jb20gaXMgeW91ciBmcmllbmRcbnZhciBNQVNURVJfUExBWUxJU1RfUkVHRVggPSAvKD86I0VYVC1YLVNUUkVBTS1JTkY6KFteXFxuXFxyXSopW1xcclxcbl0rKFteXFxyXFxuXSspfCNFWFQtWC1TRVNTSU9OLURBVEE6KFteXFxuXFxyXSopW1xcclxcbl0rKS9nO1xudmFyIE1BU1RFUl9QTEFZTElTVF9NRURJQV9SRUdFWCA9IC8jRVhULVgtTUVESUE6KC4qKS9nO1xudmFyIExFVkVMX1BMQVlMSVNUX1JFR0VYX0ZBU1QgPSBuZXcgUmVnRXhwKFsvI0VYVElORjpcXHMqKFxcZCooPzpcXC5cXGQrKT8pKD86LCguKilcXHMrKT8vLnNvdXJjZSwgLy8gZHVyYXRpb24gKCNFWFRJTkY6PGR1cmF0aW9uPiw8dGl0bGU+KSwgZ3JvdXAgMSA9PiBkdXJhdGlvbiwgZ3JvdXAgMiA9PiB0aXRsZVxuL3woPyEjKShbXFxTKyA/XSspLy5zb3VyY2UsIC8vIHNlZ21lbnQgVVJJLCBncm91cCAzID0+IHRoZSBVUkkgKG5vdGUgbmV3bGluZSBpcyBub3QgZWF0ZW4pXG4vfCNFWFQtWC1CWVRFUkFOR0U6KiguKykvLnNvdXJjZSwgLy8gbmV4dCBzZWdtZW50J3MgYnl0ZXJhbmdlLCBncm91cCA0ID0+IHJhbmdlIHNwZWMgKHhAeSlcbi98I0VYVC1YLVBST0dSQU0tREFURS1USU1FOiguKykvLnNvdXJjZSwgLy8gbmV4dCBzZWdtZW50J3MgcHJvZ3JhbSBkYXRlL3RpbWUgZ3JvdXAgNSA9PiB0aGUgZGF0ZXRpbWUgc3BlY1xuL3wjLiovLnNvdXJjZSAvLyBBbGwgb3RoZXIgbm9uLXNlZ21lbnQgb3JpZW50ZWQgdGFncyB3aWxsIG1hdGNoIHdpdGggYWxsIGdyb3VwcyBlbXB0eVxuXS5qb2luKCcnKSwgJ2cnKTtcbnZhciBMRVZFTF9QTEFZTElTVF9SRUdFWF9TTE9XID0gLyg/Oig/OiMoRVhUTTNVKSl8KD86I0VYVC1YLShQTEFZTElTVC1UWVBFKTooLispKXwoPzojRVhULVgtKE1FRElBLVNFUVVFTkNFKTogKihcXGQrKSl8KD86I0VYVC1YLShUQVJHRVREVVJBVElPTik6ICooXFxkKykpfCg/OiNFWFQtWC0oS0VZKTooLispKXwoPzojRVhULVgtKFNUQVJUKTooLispKXwoPzojRVhULVgtKEVORExJU1QpKXwoPzojRVhULVgtKERJU0NPTlRJTlVJVFktU0VRKVVFTkNFOihcXGQrKSl8KD86I0VYVC1YLShESVMpQ09OVElOVUlUWSkpfCg/OiNFWFQtWC0oVkVSU0lPTik6KFxcZCspKXwoPzojRVhULVgtKE1BUCk6KC4rKSl8KD86KCMpKFteOl0qKTooLiopKXwoPzooIykoLiopKSg/Oi4qKVxccj9cXG4/LztcbnZhciBNUDRfUkVHRVhfU1VGRklYID0gL1xcLihtcDR8bTRzfG00dnxtNGEpJC9pO1xuXG52YXIgbTN1OF9wYXJzZXJfTTNVOFBhcnNlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIE0zVThQYXJzZXIoKSB7fVxuXG4gIE0zVThQYXJzZXIuZmluZEdyb3VwID0gZnVuY3Rpb24gZmluZEdyb3VwKGdyb3VwcywgbWVkaWFHcm91cElkKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBncm91cHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBncm91cCA9IGdyb3Vwc1tpXTtcblxuICAgICAgaWYgKGdyb3VwLmlkID09PSBtZWRpYUdyb3VwSWQpIHtcbiAgICAgICAgcmV0dXJuIGdyb3VwO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBNM1U4UGFyc2VyLmNvbnZlcnRBVkMxVG9BVkNPVEkgPSBmdW5jdGlvbiBjb252ZXJ0QVZDMVRvQVZDT1RJKGNvZGVjKSB7XG4gICAgdmFyIGF2Y2RhdGEgPSBjb2RlYy5zcGxpdCgnLicpO1xuICAgIHZhciByZXN1bHQ7XG5cbiAgICBpZiAoYXZjZGF0YS5sZW5ndGggPiAyKSB7XG4gICAgICByZXN1bHQgPSBhdmNkYXRhLnNoaWZ0KCkgKyAnLic7XG4gICAgICByZXN1bHQgKz0gcGFyc2VJbnQoYXZjZGF0YS5zaGlmdCgpKS50b1N0cmluZygxNik7XG4gICAgICByZXN1bHQgKz0gKCcwMDAnICsgcGFyc2VJbnQoYXZjZGF0YS5zaGlmdCgpKS50b1N0cmluZygxNikpLnN1YnN0cigtNCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdCA9IGNvZGVjO1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG5cbiAgTTNVOFBhcnNlci5yZXNvbHZlID0gZnVuY3Rpb24gcmVzb2x2ZSh1cmwsIGJhc2VVcmwpIHtcbiAgICByZXR1cm4gdXJsX3Rvb2xraXRbXCJidWlsZEFic29sdXRlVVJMXCJdKGJhc2VVcmwsIHVybCwge1xuICAgICAgYWx3YXlzTm9ybWFsaXplOiB0cnVlXG4gICAgfSk7XG4gIH07XG5cbiAgTTNVOFBhcnNlci5wYXJzZU1hc3RlclBsYXlsaXN0ID0gZnVuY3Rpb24gcGFyc2VNYXN0ZXJQbGF5bGlzdChzdHJpbmcsIGJhc2V1cmwpIHtcbiAgICAvLyBUT0RPKHR5cGVzY3JpcHQtbGV2ZWwpXG4gICAgdmFyIGxldmVscyA9IFtdO1xuICAgIHZhciBzZXNzaW9uRGF0YSA9IHt9O1xuICAgIHZhciBoYXNTZXNzaW9uRGF0YSA9IGZhbHNlO1xuICAgIE1BU1RFUl9QTEFZTElTVF9SRUdFWC5sYXN0SW5kZXggPSAwOyAvLyBUT0RPKHR5cGVzY3JpcHQtbGV2ZWwpXG5cbiAgICBmdW5jdGlvbiBzZXRDb2RlY3MoY29kZWNzLCBsZXZlbCkge1xuICAgICAgWyd2aWRlbycsICdhdWRpbyddLmZvckVhY2goZnVuY3Rpb24gKHR5cGUpIHtcbiAgICAgICAgdmFyIGZpbHRlcmVkID0gY29kZWNzLmZpbHRlcihmdW5jdGlvbiAoY29kZWMpIHtcbiAgICAgICAgICByZXR1cm4gaXNDb2RlY1R5cGUoY29kZWMsIHR5cGUpO1xuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoZmlsdGVyZWQubGVuZ3RoKSB7XG4gICAgICAgICAgdmFyIHByZWZlcnJlZCA9IGZpbHRlcmVkLmZpbHRlcihmdW5jdGlvbiAoY29kZWMpIHtcbiAgICAgICAgICAgIHJldHVybiBjb2RlYy5sYXN0SW5kZXhPZignYXZjMScsIDApID09PSAwIHx8IGNvZGVjLmxhc3RJbmRleE9mKCdtcDRhJywgMCkgPT09IDA7XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgbGV2ZWxbdHlwZSArIFwiQ29kZWNcIl0gPSBwcmVmZXJyZWQubGVuZ3RoID4gMCA/IHByZWZlcnJlZFswXSA6IGZpbHRlcmVkWzBdOyAvLyByZW1vdmUgZnJvbSBsaXN0XG5cbiAgICAgICAgICBjb2RlY3MgPSBjb2RlY3MuZmlsdGVyKGZ1bmN0aW9uIChjb2RlYykge1xuICAgICAgICAgICAgcmV0dXJuIGZpbHRlcmVkLmluZGV4T2YoY29kZWMpID09PSAtMTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICBsZXZlbC51bmtub3duQ29kZWNzID0gY29kZWNzO1xuICAgIH1cblxuICAgIHZhciByZXN1bHQ7XG5cbiAgICB3aGlsZSAoKHJlc3VsdCA9IE1BU1RFUl9QTEFZTElTVF9SRUdFWC5leGVjKHN0cmluZykpICE9IG51bGwpIHtcbiAgICAgIGlmIChyZXN1bHRbMV0pIHtcbiAgICAgICAgLy8gJyNFWFQtWC1TVFJFQU0tSU5GJyBpcyBmb3VuZCwgcGFyc2UgbGV2ZWwgdGFnICBpbiBncm91cCAxXG4gICAgICAgIC8vIFRPRE8odHlwZXNjcmlwdC1sZXZlbClcbiAgICAgICAgdmFyIGxldmVsID0ge307XG4gICAgICAgIHZhciBhdHRycyA9IGxldmVsLmF0dHJzID0gbmV3IGF0dHJfbGlzdChyZXN1bHRbMV0pO1xuICAgICAgICBsZXZlbC51cmwgPSBNM1U4UGFyc2VyLnJlc29sdmUocmVzdWx0WzJdLCBiYXNldXJsKTtcbiAgICAgICAgdmFyIHJlc29sdXRpb24gPSBhdHRycy5kZWNpbWFsUmVzb2x1dGlvbignUkVTT0xVVElPTicpO1xuXG4gICAgICAgIGlmIChyZXNvbHV0aW9uKSB7XG4gICAgICAgICAgbGV2ZWwud2lkdGggPSByZXNvbHV0aW9uLndpZHRoO1xuICAgICAgICAgIGxldmVsLmhlaWdodCA9IHJlc29sdXRpb24uaGVpZ2h0O1xuICAgICAgICB9XG5cbiAgICAgICAgbGV2ZWwuYml0cmF0ZSA9IGF0dHJzLmRlY2ltYWxJbnRlZ2VyKCdBVkVSQUdFLUJBTkRXSURUSCcpIHx8IGF0dHJzLmRlY2ltYWxJbnRlZ2VyKCdCQU5EV0lEVEgnKTtcbiAgICAgICAgbGV2ZWwubmFtZSA9IGF0dHJzLk5BTUU7XG4gICAgICAgIHNldENvZGVjcyhbXS5jb25jYXQoKGF0dHJzLkNPREVDUyB8fCAnJykuc3BsaXQoL1sgLF0rLykpLCBsZXZlbCk7XG5cbiAgICAgICAgaWYgKGxldmVsLnZpZGVvQ29kZWMgJiYgbGV2ZWwudmlkZW9Db2RlYy5pbmRleE9mKCdhdmMxJykgIT09IC0xKSB7XG4gICAgICAgICAgbGV2ZWwudmlkZW9Db2RlYyA9IE0zVThQYXJzZXIuY29udmVydEFWQzFUb0FWQ09USShsZXZlbC52aWRlb0NvZGVjKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldmVscy5wdXNoKGxldmVsKTtcbiAgICAgIH0gZWxzZSBpZiAocmVzdWx0WzNdKSB7XG4gICAgICAgIC8vICcjRVhULVgtU0VTU0lPTi1EQVRBJyBpcyBmb3VuZCwgcGFyc2Ugc2Vzc2lvbiBkYXRhIGluIGdyb3VwIDNcbiAgICAgICAgdmFyIHNlc3Npb25BdHRycyA9IG5ldyBhdHRyX2xpc3QocmVzdWx0WzNdKTtcblxuICAgICAgICBpZiAoc2Vzc2lvbkF0dHJzWydEQVRBLUlEJ10pIHtcbiAgICAgICAgICBoYXNTZXNzaW9uRGF0YSA9IHRydWU7XG4gICAgICAgICAgc2Vzc2lvbkRhdGFbc2Vzc2lvbkF0dHJzWydEQVRBLUlEJ11dID0gc2Vzc2lvbkF0dHJzO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxldmVsczogbGV2ZWxzLFxuICAgICAgc2Vzc2lvbkRhdGE6IGhhc1Nlc3Npb25EYXRhID8gc2Vzc2lvbkRhdGEgOiBudWxsXG4gICAgfTtcbiAgfTtcblxuICBNM1U4UGFyc2VyLnBhcnNlTWFzdGVyUGxheWxpc3RNZWRpYSA9IGZ1bmN0aW9uIHBhcnNlTWFzdGVyUGxheWxpc3RNZWRpYShzdHJpbmcsIGJhc2V1cmwsIHR5cGUsIGF1ZGlvR3JvdXBzKSB7XG4gICAgaWYgKGF1ZGlvR3JvdXBzID09PSB2b2lkIDApIHtcbiAgICAgIGF1ZGlvR3JvdXBzID0gW107XG4gICAgfVxuXG4gICAgdmFyIHJlc3VsdDtcbiAgICB2YXIgbWVkaWFzID0gW107XG4gICAgdmFyIGlkID0gMDtcbiAgICBNQVNURVJfUExBWUxJU1RfTUVESUFfUkVHRVgubGFzdEluZGV4ID0gMDtcblxuICAgIHdoaWxlICgocmVzdWx0ID0gTUFTVEVSX1BMQVlMSVNUX01FRElBX1JFR0VYLmV4ZWMoc3RyaW5nKSkgIT09IG51bGwpIHtcbiAgICAgIHZhciBhdHRycyA9IG5ldyBhdHRyX2xpc3QocmVzdWx0WzFdKTtcblxuICAgICAgaWYgKGF0dHJzLlRZUEUgPT09IHR5cGUpIHtcbiAgICAgICAgdmFyIG1lZGlhID0ge1xuICAgICAgICAgIGF0dHJzOiBhdHRycyxcbiAgICAgICAgICBpZDogaWQrKyxcbiAgICAgICAgICBncm91cElkOiBhdHRyc1snR1JPVVAtSUQnXSxcbiAgICAgICAgICBpbnN0cmVhbUlkOiBhdHRyc1snSU5TVFJFQU0tSUQnXSxcbiAgICAgICAgICBuYW1lOiBhdHRycy5OQU1FIHx8IGF0dHJzLkxBTkdVQUdFLFxuICAgICAgICAgIHR5cGU6IHR5cGUsXG4gICAgICAgICAgZGVmYXVsdDogYXR0cnMuREVGQVVMVCA9PT0gJ1lFUycsXG4gICAgICAgICAgYXV0b3NlbGVjdDogYXR0cnMuQVVUT1NFTEVDVCA9PT0gJ1lFUycsXG4gICAgICAgICAgZm9yY2VkOiBhdHRycy5GT1JDRUQgPT09ICdZRVMnLFxuICAgICAgICAgIGxhbmc6IGF0dHJzLkxBTkdVQUdFXG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKGF0dHJzLlVSSSkge1xuICAgICAgICAgIG1lZGlhLnVybCA9IE0zVThQYXJzZXIucmVzb2x2ZShhdHRycy5VUkksIGJhc2V1cmwpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGF1ZGlvR3JvdXBzLmxlbmd0aCkge1xuICAgICAgICAgIC8vIElmIHRoZXJlIGFyZSBhdWRpbyBncm91cHMgc2lnbmFsbGVkIGluIHRoZSBtYW5pZmVzdCwgbGV0J3MgbG9vayBmb3IgYSBtYXRjaGluZyBjb2RlYyBzdHJpbmcgZm9yIHRoaXMgdHJhY2tcbiAgICAgICAgICB2YXIgZ3JvdXBDb2RlYyA9IE0zVThQYXJzZXIuZmluZEdyb3VwKGF1ZGlvR3JvdXBzLCBtZWRpYS5ncm91cElkKTsgLy8gSWYgd2UgZG9uJ3QgZmluZCB0aGUgdHJhY2sgc2lnbmFsbGVkLCBsZXRzIHVzZSB0aGUgZmlyc3QgYXVkaW8gZ3JvdXBzIGNvZGVjIHdlIGhhdmVcbiAgICAgICAgICAvLyBBY3RpbmcgYXMgYSBiZXN0IGd1ZXNzXG5cbiAgICAgICAgICBtZWRpYS5hdWRpb0NvZGVjID0gZ3JvdXBDb2RlYyA/IGdyb3VwQ29kZWMuY29kZWMgOiBhdWRpb0dyb3Vwc1swXS5jb2RlYztcbiAgICAgICAgfVxuXG4gICAgICAgIG1lZGlhcy5wdXNoKG1lZGlhKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbWVkaWFzO1xuICB9O1xuXG4gIE0zVThQYXJzZXIucGFyc2VMZXZlbFBsYXlsaXN0ID0gZnVuY3Rpb24gcGFyc2VMZXZlbFBsYXlsaXN0KHN0cmluZywgYmFzZXVybCwgaWQsIHR5cGUsIGxldmVsVXJsSWQpIHtcbiAgICB2YXIgY3VycmVudFNOID0gMDtcbiAgICB2YXIgdG90YWxkdXJhdGlvbiA9IDA7XG4gICAgdmFyIGxldmVsID0gbmV3IGxldmVsX0xldmVsKGJhc2V1cmwpO1xuICAgIHZhciBkaXNjb250aW51aXR5Q291bnRlciA9IDA7XG4gICAgdmFyIHByZXZGcmFnID0gbnVsbDtcbiAgICB2YXIgZnJhZyA9IG5ldyBmcmFnbWVudF9GcmFnbWVudCgpO1xuICAgIHZhciByZXN1bHQ7XG4gICAgdmFyIGk7XG4gICAgdmFyIGxldmVsa2V5O1xuICAgIHZhciBmaXJzdFBkdEluZGV4ID0gbnVsbDtcbiAgICBMRVZFTF9QTEFZTElTVF9SRUdFWF9GQVNULmxhc3RJbmRleCA9IDA7XG5cbiAgICB3aGlsZSAoKHJlc3VsdCA9IExFVkVMX1BMQVlMSVNUX1JFR0VYX0ZBU1QuZXhlYyhzdHJpbmcpKSAhPT0gbnVsbCkge1xuICAgICAgdmFyIGR1cmF0aW9uID0gcmVzdWx0WzFdO1xuXG4gICAgICBpZiAoZHVyYXRpb24pIHtcbiAgICAgICAgLy8gSU5GXG4gICAgICAgIGZyYWcuZHVyYXRpb24gPSBwYXJzZUZsb2F0KGR1cmF0aW9uKTsgLy8gYXZvaWQgc2xpY2VkIHN0cmluZ3MgICAgaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvaXNzdWVzLzkzOVxuXG4gICAgICAgIHZhciB0aXRsZSA9ICgnICcgKyByZXN1bHRbMl0pLnNsaWNlKDEpO1xuICAgICAgICBmcmFnLnRpdGxlID0gdGl0bGUgfHwgbnVsbDtcbiAgICAgICAgZnJhZy50YWdMaXN0LnB1c2godGl0bGUgPyBbJ0lORicsIGR1cmF0aW9uLCB0aXRsZV0gOiBbJ0lORicsIGR1cmF0aW9uXSk7XG4gICAgICB9IGVsc2UgaWYgKHJlc3VsdFszXSkge1xuICAgICAgICAvLyB1cmxcbiAgICAgICAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoZnJhZy5kdXJhdGlvbikpIHtcbiAgICAgICAgICB2YXIgc24gPSBjdXJyZW50U04rKztcbiAgICAgICAgICBmcmFnLnR5cGUgPSB0eXBlO1xuICAgICAgICAgIGZyYWcuc3RhcnQgPSB0b3RhbGR1cmF0aW9uO1xuXG4gICAgICAgICAgaWYgKGxldmVsa2V5KSB7XG4gICAgICAgICAgICBmcmFnLmxldmVsa2V5ID0gbGV2ZWxrZXk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZnJhZy5zbiA9IHNuO1xuICAgICAgICAgIGZyYWcubGV2ZWwgPSBpZDtcbiAgICAgICAgICBmcmFnLmNjID0gZGlzY29udGludWl0eUNvdW50ZXI7XG4gICAgICAgICAgZnJhZy51cmxJZCA9IGxldmVsVXJsSWQ7XG4gICAgICAgICAgZnJhZy5iYXNldXJsID0gYmFzZXVybDsgLy8gYXZvaWQgc2xpY2VkIHN0cmluZ3MgICAgaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvaXNzdWVzLzkzOVxuXG4gICAgICAgICAgZnJhZy5yZWx1cmwgPSAoJyAnICsgcmVzdWx0WzNdKS5zbGljZSgxKTtcbiAgICAgICAgICBhc3NpZ25Qcm9ncmFtRGF0ZVRpbWUoZnJhZywgcHJldkZyYWcpO1xuICAgICAgICAgIGxldmVsLmZyYWdtZW50cy5wdXNoKGZyYWcpO1xuICAgICAgICAgIHByZXZGcmFnID0gZnJhZztcbiAgICAgICAgICB0b3RhbGR1cmF0aW9uICs9IGZyYWcuZHVyYXRpb247XG4gICAgICAgICAgZnJhZyA9IG5ldyBmcmFnbWVudF9GcmFnbWVudCgpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHJlc3VsdFs0XSkge1xuICAgICAgICAvLyBYLUJZVEVSQU5HRVxuICAgICAgICB2YXIgZGF0YSA9ICgnICcgKyByZXN1bHRbNF0pLnNsaWNlKDEpO1xuXG4gICAgICAgIGlmIChwcmV2RnJhZykge1xuICAgICAgICAgIGZyYWcuc2V0Qnl0ZVJhbmdlKGRhdGEsIHByZXZGcmFnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmcmFnLnNldEJ5dGVSYW5nZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyZXN1bHRbNV0pIHtcbiAgICAgICAgLy8gUFJPR1JBTS1EQVRFLVRJTUVcbiAgICAgICAgLy8gYXZvaWQgc2xpY2VkIHN0cmluZ3MgICAgaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvaXNzdWVzLzkzOVxuICAgICAgICBmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSA9ICgnICcgKyByZXN1bHRbNV0pLnNsaWNlKDEpO1xuICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaChbJ1BST0dSQU0tREFURS1USU1FJywgZnJhZy5yYXdQcm9ncmFtRGF0ZVRpbWVdKTtcblxuICAgICAgICBpZiAoZmlyc3RQZHRJbmRleCA9PT0gbnVsbCkge1xuICAgICAgICAgIGZpcnN0UGR0SW5kZXggPSBsZXZlbC5mcmFnbWVudHMubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSByZXN1bHRbMF0ubWF0Y2goTEVWRUxfUExBWUxJU1RfUkVHRVhfU0xPVyk7XG5cbiAgICAgICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignTm8gbWF0Y2hlcyBvbiBzbG93IHJlZ2V4IG1hdGNoIGZvciBsZXZlbCBwbGF5bGlzdCEnKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAoaSA9IDE7IGkgPCByZXN1bHQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICBpZiAodHlwZW9mIHJlc3VsdFtpXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfSAvLyBhdm9pZCBzbGljZWQgc3RyaW5ncyAgICBodHRwczovL2dpdGh1Yi5jb20vdmlkZW8tZGV2L2hscy5qcy9pc3N1ZXMvOTM5XG5cblxuICAgICAgICB2YXIgdmFsdWUxID0gKCcgJyArIHJlc3VsdFtpICsgMV0pLnNsaWNlKDEpO1xuICAgICAgICB2YXIgdmFsdWUyID0gKCcgJyArIHJlc3VsdFtpICsgMl0pLnNsaWNlKDEpO1xuXG4gICAgICAgIHN3aXRjaCAocmVzdWx0W2ldKSB7XG4gICAgICAgICAgY2FzZSAnIyc6XG4gICAgICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaCh2YWx1ZTIgPyBbdmFsdWUxLCB2YWx1ZTJdIDogW3ZhbHVlMV0pO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlICdQTEFZTElTVC1UWVBFJzpcbiAgICAgICAgICAgIGxldmVsLnR5cGUgPSB2YWx1ZTEudG9VcHBlckNhc2UoKTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAnTUVESUEtU0VRVUVOQ0UnOlxuICAgICAgICAgICAgY3VycmVudFNOID0gbGV2ZWwuc3RhcnRTTiA9IHBhcnNlSW50KHZhbHVlMSk7XG4gICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgIGNhc2UgJ1RBUkdFVERVUkFUSU9OJzpcbiAgICAgICAgICAgIGxldmVsLnRhcmdldGR1cmF0aW9uID0gcGFyc2VGbG9hdCh2YWx1ZTEpO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlICdWRVJTSU9OJzpcbiAgICAgICAgICAgIGxldmVsLnZlcnNpb24gPSBwYXJzZUludCh2YWx1ZTEpO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlICdFWFRNM1UnOlxuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlICdFTkRMSVNUJzpcbiAgICAgICAgICAgIGxldmVsLmxpdmUgPSBmYWxzZTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAnRElTJzpcbiAgICAgICAgICAgIGRpc2NvbnRpbnVpdHlDb3VudGVyKys7XG4gICAgICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaChbJ0RJUyddKTtcbiAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgY2FzZSAnRElTQ09OVElOVUlUWS1TRVEnOlxuICAgICAgICAgICAgZGlzY29udGludWl0eUNvdW50ZXIgPSBwYXJzZUludCh2YWx1ZTEpO1xuICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICBjYXNlICdLRVknOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAvLyBodHRwczovL3Rvb2xzLmlldGYub3JnL2h0bWwvcmZjODIxNiNzZWN0aW9uLTQuMy4yLjRcbiAgICAgICAgICAgICAgdmFyIGRlY3J5cHRwYXJhbXMgPSB2YWx1ZTE7XG4gICAgICAgICAgICAgIHZhciBrZXlBdHRycyA9IG5ldyBhdHRyX2xpc3QoZGVjcnlwdHBhcmFtcyk7XG4gICAgICAgICAgICAgIHZhciBkZWNyeXB0bWV0aG9kID0ga2V5QXR0cnMuZW51bWVyYXRlZFN0cmluZygnTUVUSE9EJyk7XG4gICAgICAgICAgICAgIHZhciBkZWNyeXB0dXJpID0ga2V5QXR0cnMuVVJJO1xuICAgICAgICAgICAgICB2YXIgZGVjcnlwdGl2ID0ga2V5QXR0cnMuaGV4YWRlY2ltYWxJbnRlZ2VyKCdJVicpOyAvLyBGcm9tIFJGQzogVGhpcyBhdHRyaWJ1dGUgaXMgT1BUSU9OQUw7IGl0cyBhYnNlbmNlIGluZGljYXRlcyBhbiBpbXBsaWNpdCB2YWx1ZSBvZiBcImlkZW50aXR5XCIuXG5cbiAgICAgICAgICAgICAgdmFyIGRlY3J5cHRrZXlmb3JtYXQgPSBrZXlBdHRycy5LRVlGT1JNQVQgfHwgJ2lkZW50aXR5JztcblxuICAgICAgICAgICAgICBpZiAoZGVjcnlwdGtleWZvcm1hdCA9PT0gJ2NvbS5hcHBsZS5zdHJlYW1pbmdrZXlkZWxpdmVyeScpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignS2V5Zm9ybWF0IGNvbS5hcHBsZS5zdHJlYW1pbmdrZXlkZWxpdmVyeSBpcyBub3Qgc3VwcG9ydGVkJyk7XG4gICAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBpZiAoZGVjcnlwdG1ldGhvZCkge1xuICAgICAgICAgICAgICAgIGxldmVsa2V5ID0gbmV3IGxldmVsX2tleV9MZXZlbEtleShiYXNldXJsLCBkZWNyeXB0dXJpKTtcblxuICAgICAgICAgICAgICAgIGlmIChkZWNyeXB0dXJpICYmIFsnQUVTLTEyOCcsICdTQU1QTEUtQUVTJywgJ1NBTVBMRS1BRVMtQ0VOQyddLmluZGV4T2YoZGVjcnlwdG1ldGhvZCkgPj0gMCkge1xuICAgICAgICAgICAgICAgICAgbGV2ZWxrZXkubWV0aG9kID0gZGVjcnlwdG1ldGhvZDtcbiAgICAgICAgICAgICAgICAgIGxldmVsa2V5LmtleSA9IG51bGw7IC8vIEluaXRpYWxpemF0aW9uIFZlY3RvciAoSVYpXG5cbiAgICAgICAgICAgICAgICAgIGxldmVsa2V5Lml2ID0gZGVjcnlwdGl2O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgY2FzZSAnU1RBUlQnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICB2YXIgc3RhcnRBdHRycyA9IG5ldyBhdHRyX2xpc3QodmFsdWUxKTtcbiAgICAgICAgICAgICAgdmFyIHN0YXJ0VGltZU9mZnNldCA9IHN0YXJ0QXR0cnMuZGVjaW1hbEZsb2F0aW5nUG9pbnQoJ1RJTUUtT0ZGU0VUJyk7IC8vIFRJTUUtT0ZGU0VUIGNhbiBiZSAwXG5cbiAgICAgICAgICAgICAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoc3RhcnRUaW1lT2Zmc2V0KSkge1xuICAgICAgICAgICAgICAgIGxldmVsLnN0YXJ0VGltZU9mZnNldCA9IHN0YXJ0VGltZU9mZnNldDtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgY2FzZSAnTUFQJzpcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgdmFyIG1hcEF0dHJzID0gbmV3IGF0dHJfbGlzdCh2YWx1ZTEpO1xuICAgICAgICAgICAgICBmcmFnLnJlbHVybCA9IG1hcEF0dHJzLlVSSTtcblxuICAgICAgICAgICAgICBpZiAobWFwQXR0cnMuQllURVJBTkdFKSB7XG4gICAgICAgICAgICAgICAgZnJhZy5zZXRCeXRlUmFuZ2UobWFwQXR0cnMuQllURVJBTkdFKTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGZyYWcuYmFzZXVybCA9IGJhc2V1cmw7XG4gICAgICAgICAgICAgIGZyYWcubGV2ZWwgPSBpZDtcbiAgICAgICAgICAgICAgZnJhZy50eXBlID0gdHlwZTtcbiAgICAgICAgICAgICAgZnJhZy5zbiA9ICdpbml0U2VnbWVudCc7XG4gICAgICAgICAgICAgIGxldmVsLmluaXRTZWdtZW50ID0gZnJhZztcbiAgICAgICAgICAgICAgZnJhZyA9IG5ldyBmcmFnbWVudF9GcmFnbWVudCgpO1xuICAgICAgICAgICAgICBmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSA9IGxldmVsLmluaXRTZWdtZW50LnJhd1Byb2dyYW1EYXRlVGltZTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJsaW5lIHBhcnNlZCBidXQgbm90IGhhbmRsZWQ6IFwiICsgcmVzdWx0KTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgZnJhZyA9IHByZXZGcmFnOyAvLyBsb2dnZXIubG9nKCdmb3VuZCAnICsgbGV2ZWwuZnJhZ21lbnRzLmxlbmd0aCArICcgZnJhZ21lbnRzJyk7XG5cbiAgICBpZiAoZnJhZyAmJiAhZnJhZy5yZWx1cmwpIHtcbiAgICAgIGxldmVsLmZyYWdtZW50cy5wb3AoKTtcbiAgICAgIHRvdGFsZHVyYXRpb24gLT0gZnJhZy5kdXJhdGlvbjtcbiAgICB9XG5cbiAgICBsZXZlbC50b3RhbGR1cmF0aW9uID0gdG90YWxkdXJhdGlvbjtcbiAgICBsZXZlbC5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gPSB0b3RhbGR1cmF0aW9uIC8gbGV2ZWwuZnJhZ21lbnRzLmxlbmd0aDtcbiAgICBsZXZlbC5lbmRTTiA9IGN1cnJlbnRTTiAtIDE7XG4gICAgbGV2ZWwuc3RhcnRDQyA9IGxldmVsLmZyYWdtZW50c1swXSA/IGxldmVsLmZyYWdtZW50c1swXS5jYyA6IDA7XG4gICAgbGV2ZWwuZW5kQ0MgPSBkaXNjb250aW51aXR5Q291bnRlcjtcblxuICAgIGlmICghbGV2ZWwuaW5pdFNlZ21lbnQgJiYgbGV2ZWwuZnJhZ21lbnRzLmxlbmd0aCkge1xuICAgICAgLy8gdGhpcyBpcyBhIGJpdCBsdXJreSBidXQgSExTIHJlYWxseSBoYXMgbm8gb3RoZXIgd2F5IHRvIHRlbGwgdXNcbiAgICAgIC8vIGlmIHRoZSBmcmFnbWVudHMgYXJlIFRTIG9yIE1QNCwgZXhjZXB0IGlmIHdlIGRvd25sb2FkIHRoZW0gOi9cbiAgICAgIC8vIGJ1dCB0aGlzIGlzIHRvIGJlIGFibGUgdG8gaGFuZGxlIFNJRFguXG4gICAgICBpZiAobGV2ZWwuZnJhZ21lbnRzLmV2ZXJ5KGZ1bmN0aW9uIChmcmFnKSB7XG4gICAgICAgIHJldHVybiBNUDRfUkVHRVhfU1VGRklYLnRlc3QoZnJhZy5yZWx1cmwpO1xuICAgICAgfSkpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ01QNCBmcmFnbWVudHMgZm91bmQgYnV0IG5vIGluaXQgc2VnbWVudCAocHJvYmFibHkgbm8gTUFQLCBpbmNvbXBsZXRlIE0zVTgpLCB0cnlpbmcgdG8gZmV0Y2ggU0lEWCcpO1xuICAgICAgICBmcmFnID0gbmV3IGZyYWdtZW50X0ZyYWdtZW50KCk7XG4gICAgICAgIGZyYWcucmVsdXJsID0gbGV2ZWwuZnJhZ21lbnRzWzBdLnJlbHVybDtcbiAgICAgICAgZnJhZy5iYXNldXJsID0gYmFzZXVybDtcbiAgICAgICAgZnJhZy5sZXZlbCA9IGlkO1xuICAgICAgICBmcmFnLnR5cGUgPSB0eXBlO1xuICAgICAgICBmcmFnLnNuID0gJ2luaXRTZWdtZW50JztcbiAgICAgICAgbGV2ZWwuaW5pdFNlZ21lbnQgPSBmcmFnO1xuICAgICAgICBsZXZlbC5uZWVkU2lkeFJhbmdlcyA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEJhY2tmaWxsIGFueSBtaXNzaW5nIFBEVCB2YWx1ZXNcbiAgICAgICBcIklmIHRoZSBmaXJzdCBFWFQtWC1QUk9HUkFNLURBVEUtVElNRSB0YWcgaW4gYSBQbGF5bGlzdCBhcHBlYXJzIGFmdGVyXG4gICAgICAgb25lIG9yIG1vcmUgTWVkaWEgU2VnbWVudCBVUklzLCB0aGUgY2xpZW50IFNIT1VMRCBleHRyYXBvbGF0ZVxuICAgICAgIGJhY2t3YXJkIGZyb20gdGhhdCB0YWcgKHVzaW5nIEVYVElORiBkdXJhdGlvbnMgYW5kL29yIG1lZGlhXG4gICAgICAgdGltZXN0YW1wcykgdG8gYXNzb2NpYXRlIGRhdGVzIHdpdGggdGhvc2Ugc2VnbWVudHMuXCJcbiAgICAgKiBXZSBoYXZlIGFscmVhZHkgZXh0cmFwb2xhdGVkIGZvcndhcmQsIGJ1dCBhbGwgZnJhZ21lbnRzIHVwIHRvIHRoZSBmaXJzdCBpbnN0YW5jZSBvZiBQRFQgZG8gbm90IGhhdmUgdGhlaXIgUERUc1xuICAgICAqIGNvbXB1dGVkLlxuICAgICAqL1xuXG5cbiAgICBpZiAoZmlyc3RQZHRJbmRleCkge1xuICAgICAgYmFja2ZpbGxQcm9ncmFtRGF0ZVRpbWVzKGxldmVsLmZyYWdtZW50cywgZmlyc3RQZHRJbmRleCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGxldmVsO1xuICB9O1xuXG4gIHJldHVybiBNM1U4UGFyc2VyO1xufSgpO1xuXG5cblxuZnVuY3Rpb24gYmFja2ZpbGxQcm9ncmFtRGF0ZVRpbWVzKGZyYWdtZW50cywgc3RhcnRJbmRleCkge1xuICB2YXIgZnJhZ1ByZXYgPSBmcmFnbWVudHNbc3RhcnRJbmRleF07XG5cbiAgZm9yICh2YXIgaSA9IHN0YXJ0SW5kZXggLSAxOyBpID49IDA7IGktLSkge1xuICAgIHZhciBmcmFnID0gZnJhZ21lbnRzW2ldO1xuICAgIGZyYWcucHJvZ3JhbURhdGVUaW1lID0gZnJhZ1ByZXYucHJvZ3JhbURhdGVUaW1lIC0gZnJhZy5kdXJhdGlvbiAqIDEwMDA7XG4gICAgZnJhZ1ByZXYgPSBmcmFnO1xuICB9XG59XG5cbmZ1bmN0aW9uIGFzc2lnblByb2dyYW1EYXRlVGltZShmcmFnLCBwcmV2RnJhZykge1xuICBpZiAoZnJhZy5yYXdQcm9ncmFtRGF0ZVRpbWUpIHtcbiAgICBmcmFnLnByb2dyYW1EYXRlVGltZSA9IERhdGUucGFyc2UoZnJhZy5yYXdQcm9ncmFtRGF0ZVRpbWUpO1xuICB9IGVsc2UgaWYgKHByZXZGcmFnID09PSBudWxsIHx8IHByZXZGcmFnID09PSB2b2lkIDAgPyB2b2lkIDAgOiBwcmV2RnJhZy5wcm9ncmFtRGF0ZVRpbWUpIHtcbiAgICBmcmFnLnByb2dyYW1EYXRlVGltZSA9IHByZXZGcmFnLmVuZFByb2dyYW1EYXRlVGltZTtcbiAgfVxuXG4gIGlmICghT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShmcmFnLnByb2dyYW1EYXRlVGltZSkpIHtcbiAgICBmcmFnLnByb2dyYW1EYXRlVGltZSA9IG51bGw7XG4gICAgZnJhZy5yYXdQcm9ncmFtRGF0ZVRpbWUgPSBudWxsO1xuICB9XG59XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9sb2FkZXIvcGxheWxpc3QtbG9hZGVyLnRzXG5cblxuXG5mdW5jdGlvbiBfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLyoqXG4gKiBQbGF5bGlzdExvYWRlciAtIGRlbGVnYXRlIGZvciBtZWRpYSBtYW5pZmVzdC9wbGF5bGlzdCBsb2FkaW5nIHRhc2tzLiBUYWtlcyBjYXJlIG9mIHBhcnNpbmcgbWVkaWEgdG8gaW50ZXJuYWwgZGF0YS1tb2RlbHMuXG4gKlxuICogT25jZSBsb2FkZWQsIGRpc3BhdGNoZXMgZXZlbnRzIHdpdGggcGFyc2VkIGRhdGEtbW9kZWxzIG9mIG1hbmlmZXN0L2xldmVscy9hdWRpby9zdWJ0aXRsZSB0cmFja3MuXG4gKlxuICogVXNlcyBsb2FkZXIocykgc2V0IGluIGNvbmZpZyB0byBkbyBhY3R1YWwgaW50ZXJuYWwgbG9hZGluZyBvZiByZXNvdXJjZSB0YXNrcy5cbiAqXG4gKiBAbW9kdWxlXG4gKlxuICovXG5cblxuXG5cblxuXG5cbnZhciBfd2luZG93ID0gd2luZG93LFxuICAgIHBlcmZvcm1hbmNlID0gX3dpbmRvdy5wZXJmb3JtYW5jZTtcbi8qKlxuICogQGNvbnN0cnVjdG9yXG4gKi9cblxudmFyIHBsYXlsaXN0X2xvYWRlcl9QbGF5bGlzdExvYWRlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBfaW5oZXJpdHNMb29zZShQbGF5bGlzdExvYWRlciwgX0V2ZW50SGFuZGxlcik7XG5cbiAgLyoqXG4gICAqIEBjb25zdHJ1Y3RzXG4gICAqIEBwYXJhbSB7SGxzfSBobHNcbiAgICovXG4gIGZ1bmN0aW9uIFBsYXlsaXN0TG9hZGVyKGhscykge1xuICAgIHZhciBfdGhpcztcblxuICAgIF90aGlzID0gX0V2ZW50SGFuZGxlci5jYWxsKHRoaXMsIGhscywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9MT0FESU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMX0xPQURJTkcsIGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfTE9BRElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5TVUJUSVRMRV9UUkFDS19MT0FESU5HKSB8fCB0aGlzO1xuICAgIF90aGlzLmxvYWRlcnMgPSB7fTtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cbiAgLyoqXG4gICAqIEBwYXJhbSB7UGxheWxpc3RDb250ZXh0VHlwZX0gdHlwZVxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG5cblxuICBQbGF5bGlzdExvYWRlci5jYW5IYXZlUXVhbGl0eUxldmVscyA9IGZ1bmN0aW9uIGNhbkhhdmVRdWFsaXR5TGV2ZWxzKHR5cGUpIHtcbiAgICByZXR1cm4gdHlwZSAhPT0gUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSyAmJiB0eXBlICE9PSBQbGF5bGlzdENvbnRleHRUeXBlLlNVQlRJVExFX1RSQUNLO1xuICB9XG4gIC8qKlxuICAgKiBNYXAgY29udGV4dC50eXBlIHRvIExldmVsVHlwZVxuICAgKiBAcGFyYW0ge1BsYXlsaXN0TG9hZGVyQ29udGV4dH0gY29udGV4dFxuICAgKiBAcmV0dXJucyB7TGV2ZWxUeXBlfVxuICAgKi9cbiAgO1xuXG4gIFBsYXlsaXN0TG9hZGVyLm1hcENvbnRleHRUb0xldmVsVHlwZSA9IGZ1bmN0aW9uIG1hcENvbnRleHRUb0xldmVsVHlwZShjb250ZXh0KSB7XG4gICAgdmFyIHR5cGUgPSBjb250ZXh0LnR5cGU7XG5cbiAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgIGNhc2UgUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSzpcbiAgICAgICAgcmV0dXJuIFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPO1xuXG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0s6XG4gICAgICAgIHJldHVybiBQbGF5bGlzdExldmVsVHlwZS5TVUJUSVRMRTtcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU47XG4gICAgfVxuICB9O1xuXG4gIFBsYXlsaXN0TG9hZGVyLmdldFJlc3BvbnNlVXJsID0gZnVuY3Rpb24gZ2V0UmVzcG9uc2VVcmwocmVzcG9uc2UsIGNvbnRleHQpIHtcbiAgICB2YXIgdXJsID0gcmVzcG9uc2UudXJsOyAvLyByZXNwb25zZVVSTCBub3Qgc3VwcG9ydGVkIG9uIHNvbWUgYnJvd3NlcnMgKGl0IGlzIHVzZWQgdG8gZGV0ZWN0IFVSTCByZWRpcmVjdGlvbilcbiAgICAvLyBkYXRhLXVyaSBtb2RlIGFsc28gbm90IHN1cHBvcnRlZCAoYnV0IG5vIG5lZWQgdG8gZGV0ZWN0IHJlZGlyZWN0aW9uKVxuXG4gICAgaWYgKHVybCA9PT0gdW5kZWZpbmVkIHx8IHVybC5pbmRleE9mKCdkYXRhOicpID09PSAwKSB7XG4gICAgICAvLyBmYWxsYmFjayB0byBpbml0aWFsIFVSTFxuICAgICAgdXJsID0gY29udGV4dC51cmw7XG4gICAgfVxuXG4gICAgcmV0dXJuIHVybDtcbiAgfVxuICAvKipcbiAgICogUmV0dXJucyBkZWZhdWx0cyBvciBjb25maWd1cmVkIGxvYWRlci10eXBlIG92ZXJsb2FkcyAocExvYWRlciBhbmQgbG9hZGVyIGNvbmZpZyBwYXJhbXMpXG4gICAqIERlZmF1bHQgbG9hZGVyIGlzIFhIUkxvYWRlciAoc2VlIHV0aWxzKVxuICAgKiBAcGFyYW0ge1BsYXlsaXN0TG9hZGVyQ29udGV4dH0gY29udGV4dFxuICAgKiBAcmV0dXJucyB7TG9hZGVyfSBvciBvdGhlciBjb21wYXRpYmxlIGNvbmZpZ3VyZWQgb3ZlcmxvYWRcbiAgICovXG4gIDtcblxuICB2YXIgX3Byb3RvID0gUGxheWxpc3RMb2FkZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5jcmVhdGVJbnRlcm5hbExvYWRlciA9IGZ1bmN0aW9uIGNyZWF0ZUludGVybmFsTG9hZGVyKGNvbnRleHQpIHtcbiAgICB2YXIgY29uZmlnID0gdGhpcy5obHMuY29uZmlnO1xuICAgIHZhciBQTG9hZGVyID0gY29uZmlnLnBMb2FkZXI7XG4gICAgdmFyIExvYWRlciA9IGNvbmZpZy5sb2FkZXI7IC8vIFRPRE8odHlwZXNjcmlwdC1jb25maWcpOiBWZXJpZnkgb25jZSBjb25maWcgaXMgdHlwZWQgdGhhdCBJbnRlcm5hbExvYWRlciBhbHdheXMgcmV0dXJucyBhIExvYWRlclxuXG4gICAgdmFyIEludGVybmFsTG9hZGVyID0gUExvYWRlciB8fCBMb2FkZXI7XG4gICAgdmFyIGxvYWRlciA9IG5ldyBJbnRlcm5hbExvYWRlcihjb25maWcpOyAvLyBUT0RPIC0gRG8gd2UgcmVhbGx5IG5lZWQgdG8gYXNzaWduIHRoZSBpbnN0YW5jZSBvciBpZiB0aGUgZGVwIGhhcyBiZWVuIGxvc3RcblxuICAgIGNvbnRleHQubG9hZGVyID0gbG9hZGVyO1xuICAgIHRoaXMubG9hZGVyc1tjb250ZXh0LnR5cGVdID0gbG9hZGVyO1xuICAgIHJldHVybiBsb2FkZXI7XG4gIH07XG5cbiAgX3Byb3RvLmdldEludGVybmFsTG9hZGVyID0gZnVuY3Rpb24gZ2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dCkge1xuICAgIHJldHVybiB0aGlzLmxvYWRlcnNbY29udGV4dC50eXBlXTtcbiAgfTtcblxuICBfcHJvdG8ucmVzZXRJbnRlcm5hbExvYWRlciA9IGZ1bmN0aW9uIHJlc2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dFR5cGUpIHtcbiAgICBpZiAodGhpcy5sb2FkZXJzW2NvbnRleHRUeXBlXSkge1xuICAgICAgZGVsZXRlIHRoaXMubG9hZGVyc1tjb250ZXh0VHlwZV07XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBDYWxsIGBkZXN0cm95YCBvbiBhbGwgaW50ZXJuYWwgbG9hZGVyIGluc3RhbmNlcyBtYXBwZWQgKG9uZSBwZXIgY29udGV4dCB0eXBlKVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5kZXN0cm95SW50ZXJuYWxMb2FkZXJzID0gZnVuY3Rpb24gZGVzdHJveUludGVybmFsTG9hZGVycygpIHtcbiAgICBmb3IgKHZhciBjb250ZXh0VHlwZSBpbiB0aGlzLmxvYWRlcnMpIHtcbiAgICAgIHZhciBsb2FkZXIgPSB0aGlzLmxvYWRlcnNbY29udGV4dFR5cGVdO1xuXG4gICAgICBpZiAobG9hZGVyKSB7XG4gICAgICAgIGxvYWRlci5kZXN0cm95KCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMucmVzZXRJbnRlcm5hbExvYWRlcihjb250ZXh0VHlwZSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHtcbiAgICB0aGlzLmRlc3Ryb3lJbnRlcm5hbExvYWRlcnMoKTtcblxuICAgIF9FdmVudEhhbmRsZXIucHJvdG90eXBlLmRlc3Ryb3kuY2FsbCh0aGlzKTtcbiAgfTtcblxuICBfcHJvdG8ub25NYW5pZmVzdExvYWRpbmcgPSBmdW5jdGlvbiBvbk1hbmlmZXN0TG9hZGluZyhkYXRhKSB7XG4gICAgdGhpcy5sb2FkKHtcbiAgICAgIHVybDogZGF0YS51cmwsXG4gICAgICB0eXBlOiBQbGF5bGlzdENvbnRleHRUeXBlLk1BTklGRVNULFxuICAgICAgbGV2ZWw6IDAsXG4gICAgICBpZDogbnVsbCxcbiAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnXG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLm9uTGV2ZWxMb2FkaW5nID0gZnVuY3Rpb24gb25MZXZlbExvYWRpbmcoZGF0YSkge1xuICAgIHRoaXMubG9hZCh7XG4gICAgICB1cmw6IGRhdGEudXJsLFxuICAgICAgdHlwZTogUGxheWxpc3RDb250ZXh0VHlwZS5MRVZFTCxcbiAgICAgIGxldmVsOiBkYXRhLmxldmVsLFxuICAgICAgaWQ6IGRhdGEuaWQsXG4gICAgICByZXNwb25zZVR5cGU6ICd0ZXh0J1xuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90by5vbkF1ZGlvVHJhY2tMb2FkaW5nID0gZnVuY3Rpb24gb25BdWRpb1RyYWNrTG9hZGluZyhkYXRhKSB7XG4gICAgdGhpcy5sb2FkKHtcbiAgICAgIHVybDogZGF0YS51cmwsXG4gICAgICB0eXBlOiBQbGF5bGlzdENvbnRleHRUeXBlLkFVRElPX1RSQUNLLFxuICAgICAgbGV2ZWw6IG51bGwsXG4gICAgICBpZDogZGF0YS5pZCxcbiAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnXG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLm9uU3VidGl0bGVUcmFja0xvYWRpbmcgPSBmdW5jdGlvbiBvblN1YnRpdGxlVHJhY2tMb2FkaW5nKGRhdGEpIHtcbiAgICB0aGlzLmxvYWQoe1xuICAgICAgdXJsOiBkYXRhLnVybCxcbiAgICAgIHR5cGU6IFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0ssXG4gICAgICBsZXZlbDogbnVsbCxcbiAgICAgIGlkOiBkYXRhLmlkLFxuICAgICAgcmVzcG9uc2VUeXBlOiAndGV4dCdcbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8ubG9hZCA9IGZ1bmN0aW9uIGxvYWQoY29udGV4dCkge1xuICAgIHZhciBjb25maWcgPSB0aGlzLmhscy5jb25maWc7XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmRlYnVnKFwiTG9hZGluZyBwbGF5bGlzdCBvZiB0eXBlIFwiICsgY29udGV4dC50eXBlICsgXCIsIGxldmVsOiBcIiArIGNvbnRleHQubGV2ZWwgKyBcIiwgaWQ6IFwiICsgY29udGV4dC5pZCk7IC8vIENoZWNrIGlmIGEgbG9hZGVyIGZvciB0aGlzIGNvbnRleHQgYWxyZWFkeSBleGlzdHNcblxuICAgIHZhciBsb2FkZXIgPSB0aGlzLmdldEludGVybmFsTG9hZGVyKGNvbnRleHQpO1xuXG4gICAgaWYgKGxvYWRlcikge1xuICAgICAgdmFyIGxvYWRlckNvbnRleHQgPSBsb2FkZXIuY29udGV4dDtcblxuICAgICAgaWYgKGxvYWRlckNvbnRleHQgJiYgbG9hZGVyQ29udGV4dC51cmwgPT09IGNvbnRleHQudXJsKSB7XG4gICAgICAgIC8vIHNhbWUgVVJMIGNhbid0IG92ZXJsYXBcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLnRyYWNlKCdwbGF5bGlzdCByZXF1ZXN0IG9uZ29pbmcnKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJhYm9ydGluZyBwcmV2aW91cyBsb2FkZXIgZm9yIHR5cGU6IFwiICsgY29udGV4dC50eXBlKTtcbiAgICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIG1heFJldHJ5O1xuICAgIHZhciB0aW1lb3V0O1xuICAgIHZhciByZXRyeURlbGF5O1xuICAgIHZhciBtYXhSZXRyeURlbGF5OyAvLyBhcHBseSBkaWZmZXJlbnQgY29uZmlncyBmb3IgcmV0cmllcyBkZXBlbmRpbmcgb25cbiAgICAvLyBjb250ZXh0IChtYW5pZmVzdCwgbGV2ZWwsIGF1ZGlvL3N1YnMgcGxheWxpc3QpXG5cbiAgICBzd2l0Y2ggKGNvbnRleHQudHlwZSkge1xuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLk1BTklGRVNUOlxuICAgICAgICBtYXhSZXRyeSA9IGNvbmZpZy5tYW5pZmVzdExvYWRpbmdNYXhSZXRyeTtcbiAgICAgICAgdGltZW91dCA9IGNvbmZpZy5tYW5pZmVzdExvYWRpbmdUaW1lT3V0O1xuICAgICAgICByZXRyeURlbGF5ID0gY29uZmlnLm1hbmlmZXN0TG9hZGluZ1JldHJ5RGVsYXk7XG4gICAgICAgIG1heFJldHJ5RGVsYXkgPSBjb25maWcubWFuaWZlc3RMb2FkaW5nTWF4UmV0cnlUaW1lb3V0O1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMOlxuICAgICAgICAvLyBEaXNhYmxlIGludGVybmFsIGxvYWRlciByZXRyeSBsb2dpYywgc2luY2Ugd2UgYXJlIG1hbmFnaW5nIHJldHJpZXMgaW4gTGV2ZWwgQ29udHJvbGxlclxuICAgICAgICBtYXhSZXRyeSA9IDA7XG4gICAgICAgIG1heFJldHJ5RGVsYXkgPSAwO1xuICAgICAgICByZXRyeURlbGF5ID0gMDtcbiAgICAgICAgdGltZW91dCA9IGNvbmZpZy5sZXZlbExvYWRpbmdUaW1lT3V0OyAvLyBUT0RPIEludHJvZHVjZSByZXRyeSBzZXR0aW5ncyBmb3IgYXVkaW8tdHJhY2sgYW5kIHN1YnRpdGxlLXRyYWNrLCBpdCBzaG91bGQgbm90IHVzZSBsZXZlbCByZXRyeSBjb25maWdcblxuICAgICAgICBicmVhaztcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgbWF4UmV0cnkgPSBjb25maWcubGV2ZWxMb2FkaW5nTWF4UmV0cnk7XG4gICAgICAgIHRpbWVvdXQgPSBjb25maWcubGV2ZWxMb2FkaW5nVGltZU91dDtcbiAgICAgICAgcmV0cnlEZWxheSA9IGNvbmZpZy5sZXZlbExvYWRpbmdSZXRyeURlbGF5O1xuICAgICAgICBtYXhSZXRyeURlbGF5ID0gY29uZmlnLmxldmVsTG9hZGluZ01heFJldHJ5VGltZW91dDtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgbG9hZGVyID0gdGhpcy5jcmVhdGVJbnRlcm5hbExvYWRlcihjb250ZXh0KTtcbiAgICB2YXIgbG9hZGVyQ29uZmlnID0ge1xuICAgICAgdGltZW91dDogdGltZW91dCxcbiAgICAgIG1heFJldHJ5OiBtYXhSZXRyeSxcbiAgICAgIHJldHJ5RGVsYXk6IHJldHJ5RGVsYXksXG4gICAgICBtYXhSZXRyeURlbGF5OiBtYXhSZXRyeURlbGF5XG4gICAgfTtcbiAgICB2YXIgbG9hZGVyQ2FsbGJhY2tzID0ge1xuICAgICAgb25TdWNjZXNzOiB0aGlzLmxvYWRzdWNjZXNzLmJpbmQodGhpcyksXG4gICAgICBvbkVycm9yOiB0aGlzLmxvYWRlcnJvci5iaW5kKHRoaXMpLFxuICAgICAgb25UaW1lb3V0OiB0aGlzLmxvYWR0aW1lb3V0LmJpbmQodGhpcylcbiAgICB9O1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5kZWJ1ZyhcIkNhbGxpbmcgaW50ZXJuYWwgbG9hZGVyIGRlbGVnYXRlIGZvciBVUkw6IFwiICsgY29udGV4dC51cmwpO1xuICAgIGxvYWRlci5sb2FkKGNvbnRleHQsIGxvYWRlckNvbmZpZywgbG9hZGVyQ2FsbGJhY2tzKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfTtcblxuICBfcHJvdG8ubG9hZHN1Y2Nlc3MgPSBmdW5jdGlvbiBsb2Fkc3VjY2VzcyhyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSB7XG4gICAgaWYgKG5ldHdvcmtEZXRhaWxzID09PSB2b2lkIDApIHtcbiAgICAgIG5ldHdvcmtEZXRhaWxzID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAoY29udGV4dC5pc1NpZHhSZXF1ZXN0KSB7XG4gICAgICB0aGlzLl9oYW5kbGVTaWR4UmVxdWVzdChyZXNwb25zZSwgY29udGV4dCk7XG5cbiAgICAgIHRoaXMuX2hhbmRsZVBsYXlsaXN0TG9hZGVkKHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5yZXNldEludGVybmFsTG9hZGVyKGNvbnRleHQudHlwZSk7XG5cbiAgICBpZiAodHlwZW9mIHJlc3BvbnNlLmRhdGEgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2V4cGVjdGVkIHJlc3BvbnNlVHlwZSBvZiBcInRleHRcIiBmb3IgUGxheWxpc3RMb2FkZXInKTtcbiAgICB9XG5cbiAgICB2YXIgc3RyaW5nID0gcmVzcG9uc2UuZGF0YTtcbiAgICBzdGF0cy50bG9hZCA9IHBlcmZvcm1hbmNlLm5vdygpOyAvLyBzdGF0cy5tdGltZSA9IG5ldyBEYXRlKHRhcmdldC5nZXRSZXNwb25zZUhlYWRlcignTGFzdC1Nb2RpZmllZCcpKTtcbiAgICAvLyBWYWxpZGF0ZSBpZiBpdCBpcyBhbiBNM1U4IGF0IGFsbFxuXG4gICAgaWYgKHN0cmluZy5pbmRleE9mKCcjRVhUTTNVJykgIT09IDApIHtcbiAgICAgIHRoaXMuX2hhbmRsZU1hbmlmZXN0UGFyc2luZ0Vycm9yKHJlc3BvbnNlLCBjb250ZXh0LCAnbm8gRVhUTTNVIGRlbGltaXRlcicsIG5ldHdvcmtEZXRhaWxzKTtcblxuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gQ2hlY2sgaWYgY2h1bmstbGlzdCBvciBtYXN0ZXIuIGhhbmRsZSBlbXB0eSBjaHVuayBsaXN0IGNhc2UgKGZpcnN0IEVYVElORiBub3Qgc2lnbmFsZWQsIGJ1dCBUQVJHRVREVVJBVElPTiBwcmVzZW50KVxuXG5cbiAgICBpZiAoc3RyaW5nLmluZGV4T2YoJyNFWFRJTkY6JykgPiAwIHx8IHN0cmluZy5pbmRleE9mKCcjRVhULVgtVEFSR0VURFVSQVRJT046JykgPiAwKSB7XG4gICAgICB0aGlzLl9oYW5kbGVUcmFja09yTGV2ZWxQbGF5bGlzdChyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5faGFuZGxlTWFzdGVyUGxheWxpc3QocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscyk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5sb2FkZXJyb3IgPSBmdW5jdGlvbiBsb2FkZXJyb3IocmVzcG9uc2UsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSB7XG4gICAgaWYgKG5ldHdvcmtEZXRhaWxzID09PSB2b2lkIDApIHtcbiAgICAgIG5ldHdvcmtEZXRhaWxzID0gbnVsbDtcbiAgICB9XG5cbiAgICB0aGlzLl9oYW5kbGVOZXR3b3JrRXJyb3IoY29udGV4dCwgbmV0d29ya0RldGFpbHMsIGZhbHNlLCByZXNwb25zZSk7XG4gIH07XG5cbiAgX3Byb3RvLmxvYWR0aW1lb3V0ID0gZnVuY3Rpb24gbG9hZHRpbWVvdXQoc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSB7XG4gICAgaWYgKG5ldHdvcmtEZXRhaWxzID09PSB2b2lkIDApIHtcbiAgICAgIG5ldHdvcmtEZXRhaWxzID0gbnVsbDtcbiAgICB9XG5cbiAgICB0aGlzLl9oYW5kbGVOZXR3b3JrRXJyb3IoY29udGV4dCwgbmV0d29ya0RldGFpbHMsIHRydWUpO1xuICB9IC8vIFRPRE8odHlwZXNjcmlwdC1jb25maWcpOiBuZXR3b3JrRGV0YWlscyBjYW4gY3VycmVudGx5IGJlIGEgWEhSIG9yIEZldGNoIGltcGwsXG4gIC8vIGJ1dCB3aXRoIGN1c3RvbSBsb2FkZXJzIGl0IGNvdWxkIGJlIGdlbmVyaWMgaW52ZXN0aWdhdGUgdGhpcyBmdXJ0aGVyIHdoZW4gY29uZmlnIGlzIHR5cGVkXG4gIDtcblxuICBfcHJvdG8uX2hhbmRsZU1hc3RlclBsYXlsaXN0ID0gZnVuY3Rpb24gX2hhbmRsZU1hc3RlclBsYXlsaXN0KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHM7XG4gICAgdmFyIHN0cmluZyA9IHJlc3BvbnNlLmRhdGE7XG4gICAgdmFyIHVybCA9IFBsYXlsaXN0TG9hZGVyLmdldFJlc3BvbnNlVXJsKHJlc3BvbnNlLCBjb250ZXh0KTtcblxuICAgIHZhciBfTTNVOFBhcnNlciRwYXJzZU1hc3QgPSBtM3U4X3BhcnNlcl9NM1U4UGFyc2VyLnBhcnNlTWFzdGVyUGxheWxpc3Qoc3RyaW5nLCB1cmwpLFxuICAgICAgICBsZXZlbHMgPSBfTTNVOFBhcnNlciRwYXJzZU1hc3QubGV2ZWxzLFxuICAgICAgICBzZXNzaW9uRGF0YSA9IF9NM1U4UGFyc2VyJHBhcnNlTWFzdC5zZXNzaW9uRGF0YTtcblxuICAgIGlmICghbGV2ZWxzLmxlbmd0aCkge1xuICAgICAgdGhpcy5faGFuZGxlTWFuaWZlc3RQYXJzaW5nRXJyb3IocmVzcG9uc2UsIGNvbnRleHQsICdubyBsZXZlbCBmb3VuZCBpbiBtYW5pZmVzdCcsIG5ldHdvcmtEZXRhaWxzKTtcblxuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gbXVsdGkgbGV2ZWwgcGxheWxpc3QsIHBhcnNlIGxldmVsIGluZm9cblxuXG4gICAgdmFyIGF1ZGlvR3JvdXBzID0gbGV2ZWxzLm1hcChmdW5jdGlvbiAobGV2ZWwpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGlkOiBsZXZlbC5hdHRycy5BVURJTyxcbiAgICAgICAgY29kZWM6IGxldmVsLmF1ZGlvQ29kZWNcbiAgICAgIH07XG4gICAgfSk7XG4gICAgdmFyIGF1ZGlvVHJhY2tzID0gbTN1OF9wYXJzZXJfTTNVOFBhcnNlci5wYXJzZU1hc3RlclBsYXlsaXN0TWVkaWEoc3RyaW5nLCB1cmwsICdBVURJTycsIGF1ZGlvR3JvdXBzKTtcbiAgICB2YXIgc3VidGl0bGVzID0gbTN1OF9wYXJzZXJfTTNVOFBhcnNlci5wYXJzZU1hc3RlclBsYXlsaXN0TWVkaWEoc3RyaW5nLCB1cmwsICdTVUJUSVRMRVMnKTtcbiAgICB2YXIgY2FwdGlvbnMgPSBtM3U4X3BhcnNlcl9NM1U4UGFyc2VyLnBhcnNlTWFzdGVyUGxheWxpc3RNZWRpYShzdHJpbmcsIHVybCwgJ0NMT1NFRC1DQVBUSU9OUycpO1xuXG4gICAgaWYgKGF1ZGlvVHJhY2tzLmxlbmd0aCkge1xuICAgICAgLy8gY2hlY2sgaWYgd2UgaGF2ZSBmb3VuZCBhbiBhdWRpbyB0cmFjayBlbWJlZGRlZCBpbiBtYWluIHBsYXlsaXN0IChhdWRpbyB0cmFjayB3aXRob3V0IFVSSSBhdHRyaWJ1dGUpXG4gICAgICB2YXIgZW1iZWRkZWRBdWRpb0ZvdW5kID0gZmFsc2U7XG4gICAgICBhdWRpb1RyYWNrcy5mb3JFYWNoKGZ1bmN0aW9uIChhdWRpb1RyYWNrKSB7XG4gICAgICAgIGlmICghYXVkaW9UcmFjay51cmwpIHtcbiAgICAgICAgICBlbWJlZGRlZEF1ZGlvRm91bmQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9KTsgLy8gaWYgbm8gZW1iZWRkZWQgYXVkaW8gdHJhY2sgZGVmaW5lZCwgYnV0IGF1ZGlvIGNvZGVjIHNpZ25hbGVkIGluIHF1YWxpdHkgbGV2ZWwsXG4gICAgICAvLyB3ZSBuZWVkIHRvIHNpZ25hbCB0aGlzIG1haW4gYXVkaW8gdHJhY2sgdGhpcyBjb3VsZCBoYXBwZW4gd2l0aCBwbGF5bGlzdHMgd2l0aFxuICAgICAgLy8gYWx0IGF1ZGlvIHJlbmRpdGlvbiBpbiB3aGljaCBxdWFsaXR5IGxldmVscyAobWFpbilcbiAgICAgIC8vIGNvbnRhaW5zIGJvdGggYXVkaW8rdmlkZW8uIGJ1dCB3aXRoIG1peGVkIGF1ZGlvIHRyYWNrIG5vdCBzaWduYWxlZFxuXG4gICAgICBpZiAoZW1iZWRkZWRBdWRpb0ZvdW5kID09PSBmYWxzZSAmJiBsZXZlbHNbMF0uYXVkaW9Db2RlYyAmJiAhbGV2ZWxzWzBdLmF0dHJzLkFVRElPKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ2F1ZGlvIGNvZGVjIHNpZ25hbGVkIGluIHF1YWxpdHkgbGV2ZWwsIGJ1dCBubyBlbWJlZGRlZCBhdWRpbyB0cmFjayBzaWduYWxlZCwgY3JlYXRlIG9uZScpO1xuICAgICAgICBhdWRpb1RyYWNrcy51bnNoaWZ0KHtcbiAgICAgICAgICB0eXBlOiAnbWFpbicsXG4gICAgICAgICAgbmFtZTogJ21haW4nLFxuICAgICAgICAgIGRlZmF1bHQ6IGZhbHNlLFxuICAgICAgICAgIGF1dG9zZWxlY3Q6IGZhbHNlLFxuICAgICAgICAgIGZvcmNlZDogZmFsc2UsXG4gICAgICAgICAgaWQ6IC0xLFxuICAgICAgICAgIGF0dHJzOiB7fSxcbiAgICAgICAgICB1cmw6ICcnXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uTUFOSUZFU1RfTE9BREVELCB7XG4gICAgICBsZXZlbHM6IGxldmVscyxcbiAgICAgIGF1ZGlvVHJhY2tzOiBhdWRpb1RyYWNrcyxcbiAgICAgIHN1YnRpdGxlczogc3VidGl0bGVzLFxuICAgICAgY2FwdGlvbnM6IGNhcHRpb25zLFxuICAgICAgdXJsOiB1cmwsXG4gICAgICBzdGF0czogc3RhdHMsXG4gICAgICBuZXR3b3JrRGV0YWlsczogbmV0d29ya0RldGFpbHMsXG4gICAgICBzZXNzaW9uRGF0YTogc2Vzc2lvbkRhdGFcbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8uX2hhbmRsZVRyYWNrT3JMZXZlbFBsYXlsaXN0ID0gZnVuY3Rpb24gX2hhbmRsZVRyYWNrT3JMZXZlbFBsYXlsaXN0KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHM7XG4gICAgdmFyIGlkID0gY29udGV4dC5pZCxcbiAgICAgICAgbGV2ZWwgPSBjb250ZXh0LmxldmVsLFxuICAgICAgICB0eXBlID0gY29udGV4dC50eXBlO1xuICAgIHZhciB1cmwgPSBQbGF5bGlzdExvYWRlci5nZXRSZXNwb25zZVVybChyZXNwb25zZSwgY29udGV4dCk7IC8vIGlmIHRoZSB2YWx1ZXMgYXJlIG51bGwsIHRoZXkgd2lsbCByZXN1bHQgaW4gdGhlIGVsc2UgY29uZGl0aW9uYWxcblxuICAgIHZhciBsZXZlbFVybElkID0gT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShpZCkgPyBpZCA6IDA7XG4gICAgdmFyIGxldmVsSWQgPSBPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGxldmVsKSA/IGxldmVsIDogbGV2ZWxVcmxJZDtcbiAgICB2YXIgbGV2ZWxUeXBlID0gUGxheWxpc3RMb2FkZXIubWFwQ29udGV4dFRvTGV2ZWxUeXBlKGNvbnRleHQpO1xuICAgIHZhciBsZXZlbERldGFpbHMgPSBtM3U4X3BhcnNlcl9NM1U4UGFyc2VyLnBhcnNlTGV2ZWxQbGF5bGlzdChyZXNwb25zZS5kYXRhLCB1cmwsIGxldmVsSWQsIGxldmVsVHlwZSwgbGV2ZWxVcmxJZCk7IC8vIHNldCBzdGF0cyBvbiBsZXZlbCBzdHJ1Y3R1cmVcbiAgICAvLyBUT0RPKGpzdGFja2hvdXNlKTogd2h5PyBtaXhpbmcgY29uY2VybnMsIGlzIGl0IGp1c3QgdHJlYXRlZCBhcyB2YWx1ZSBiYWc/XG5cbiAgICBsZXZlbERldGFpbHMudGxvYWQgPSBzdGF0cy50bG9hZDtcblxuICAgIGlmICghbGV2ZWxEZXRhaWxzLmZyYWdtZW50cy5sZW5ndGgpIHtcbiAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5ORVRXT1JLX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uTEVWRUxfRU1QVFlfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgdXJsOiB1cmwsXG4gICAgICAgIHJlYXNvbjogJ25vIGZyYWdtZW50cyBmb3VuZCBpbiBsZXZlbCcsXG4gICAgICAgIGxldmVsOiB0eXBlb2YgY29udGV4dC5sZXZlbCA9PT0gJ251bWJlcicgPyBjb250ZXh0LmxldmVsIDogdW5kZWZpbmVkXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIFdlIGhhdmUgZG9uZSBvdXIgZmlyc3QgcmVxdWVzdCAoTWFuaWZlc3QtdHlwZSkgYW5kIHJlY2VpdmVcbiAgICAvLyBub3QgYSBtYXN0ZXIgcGxheWxpc3QgYnV0IGEgY2h1bmstbGlzdCAodHJhY2svbGV2ZWwpXG4gICAgLy8gV2UgZmlyZSB0aGUgbWFuaWZlc3QtbG9hZGVkIGV2ZW50IGFueXdheSB3aXRoIHRoZSBwYXJzZWQgbGV2ZWwtZGV0YWlsc1xuICAgIC8vIGJ5IGNyZWF0aW5nIGEgc2luZ2xlLWxldmVsIHN0cnVjdHVyZSBmb3IgaXQuXG5cblxuICAgIGlmICh0eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLk1BTklGRVNUKSB7XG4gICAgICB2YXIgc2luZ2xlTGV2ZWwgPSB7XG4gICAgICAgIHVybDogdXJsLFxuICAgICAgICBkZXRhaWxzOiBsZXZlbERldGFpbHNcbiAgICAgIH07XG4gICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLk1BTklGRVNUX0xPQURFRCwge1xuICAgICAgICBsZXZlbHM6IFtzaW5nbGVMZXZlbF0sXG4gICAgICAgIGF1ZGlvVHJhY2tzOiBbXSxcbiAgICAgICAgdXJsOiB1cmwsXG4gICAgICAgIHN0YXRzOiBzdGF0cyxcbiAgICAgICAgbmV0d29ya0RldGFpbHM6IG5ldHdvcmtEZXRhaWxzLFxuICAgICAgICBzZXNzaW9uRGF0YTogbnVsbFxuICAgICAgfSk7XG4gICAgfSAvLyBzYXZlIHBhcnNpbmcgdGltZVxuXG5cbiAgICBzdGF0cy50cGFyc2VkID0gcGVyZm9ybWFuY2Uubm93KCk7IC8vIGluIGNhc2Ugd2UgbmVlZCBTSURYIHJhbmdlc1xuICAgIC8vIHJldHVybiBlYXJseSBhZnRlciBjYWxsaW5nIGxvYWQgZm9yXG4gICAgLy8gdGhlIFNJRFggYm94LlxuXG4gICAgaWYgKGxldmVsRGV0YWlscy5uZWVkU2lkeFJhbmdlcykge1xuICAgICAgdmFyIHNpZHhVcmwgPSBsZXZlbERldGFpbHMuaW5pdFNlZ21lbnQudXJsO1xuICAgICAgdGhpcy5sb2FkKHtcbiAgICAgICAgdXJsOiBzaWR4VXJsLFxuICAgICAgICBpc1NpZHhSZXF1ZXN0OiB0cnVlLFxuICAgICAgICB0eXBlOiB0eXBlLFxuICAgICAgICBsZXZlbDogbGV2ZWwsXG4gICAgICAgIGxldmVsRGV0YWlsczogbGV2ZWxEZXRhaWxzLFxuICAgICAgICBpZDogaWQsXG4gICAgICAgIHJhbmdlU3RhcnQ6IDAsXG4gICAgICAgIHJhbmdlRW5kOiAyMDQ4LFxuICAgICAgICByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcidcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gZXh0ZW5kIHRoZSBjb250ZXh0IHdpdGggdGhlIG5ldyBsZXZlbERldGFpbHMgcHJvcGVydHlcblxuXG4gICAgY29udGV4dC5sZXZlbERldGFpbHMgPSBsZXZlbERldGFpbHM7XG5cbiAgICB0aGlzLl9oYW5kbGVQbGF5bGlzdExvYWRlZChyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKTtcbiAgfTtcblxuICBfcHJvdG8uX2hhbmRsZVNpZHhSZXF1ZXN0ID0gZnVuY3Rpb24gX2hhbmRsZVNpZHhSZXF1ZXN0KHJlc3BvbnNlLCBjb250ZXh0KSB7XG4gICAgaWYgKHR5cGVvZiByZXNwb25zZS5kYXRhID09PSAnc3RyaW5nJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzaWR4IHJlcXVlc3QgbXVzdCBiZSBtYWRlIHdpdGggcmVzcG9uc2VUeXBlIG9mIGFycmF5IGJ1ZmZlcicpO1xuICAgIH1cblxuICAgIHZhciBzaWR4SW5mbyA9IG1wNGRlbXV4ZXJbXCJkZWZhdWx0XCJdLnBhcnNlU2VnbWVudEluZGV4KG5ldyBVaW50OEFycmF5KHJlc3BvbnNlLmRhdGEpKTsgLy8gaWYgcHJvdmlkZWQgZnJhZ21lbnQgZG9lcyBub3QgY29udGFpbiBzaWR4LCBlYXJseSByZXR1cm5cblxuICAgIGlmICghc2lkeEluZm8pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc2lkeFJlZmVyZW5jZXMgPSBzaWR4SW5mby5yZWZlcmVuY2VzO1xuICAgIHZhciBsZXZlbERldGFpbHMgPSBjb250ZXh0LmxldmVsRGV0YWlscztcbiAgICBzaWR4UmVmZXJlbmNlcy5mb3JFYWNoKGZ1bmN0aW9uIChzZWdtZW50UmVmLCBpbmRleCkge1xuICAgICAgdmFyIHNlZ1JlZkluZm8gPSBzZWdtZW50UmVmLmluZm87XG5cbiAgICAgIGlmICghbGV2ZWxEZXRhaWxzKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGZyYWcgPSBsZXZlbERldGFpbHMuZnJhZ21lbnRzW2luZGV4XTtcblxuICAgICAgaWYgKGZyYWcuYnl0ZVJhbmdlLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBmcmFnLnNldEJ5dGVSYW5nZShTdHJpbmcoMSArIHNlZ1JlZkluZm8uZW5kIC0gc2VnUmVmSW5mby5zdGFydCkgKyAnQCcgKyBTdHJpbmcoc2VnUmVmSW5mby5zdGFydCkpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgaWYgKGxldmVsRGV0YWlscykge1xuICAgICAgbGV2ZWxEZXRhaWxzLmluaXRTZWdtZW50LnNldEJ5dGVSYW5nZShTdHJpbmcoc2lkeEluZm8ubW9vdkVuZE9mZnNldCkgKyAnQDAnKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLl9oYW5kbGVNYW5pZmVzdFBhcnNpbmdFcnJvciA9IGZ1bmN0aW9uIF9oYW5kbGVNYW5pZmVzdFBhcnNpbmdFcnJvcihyZXNwb25zZSwgY29udGV4dCwgcmVhc29uLCBuZXR3b3JrRGV0YWlscykge1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5ORVRXT1JLX0VSUk9SLFxuICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLk1BTklGRVNUX1BBUlNJTkdfRVJST1IsXG4gICAgICBmYXRhbDogdHJ1ZSxcbiAgICAgIHVybDogcmVzcG9uc2UudXJsLFxuICAgICAgcmVhc29uOiByZWFzb24sXG4gICAgICBuZXR3b3JrRGV0YWlsczogbmV0d29ya0RldGFpbHNcbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8uX2hhbmRsZU5ldHdvcmtFcnJvciA9IGZ1bmN0aW9uIF9oYW5kbGVOZXR3b3JrRXJyb3IoY29udGV4dCwgbmV0d29ya0RldGFpbHMsIHRpbWVvdXQsIHJlc3BvbnNlKSB7XG4gICAgaWYgKHRpbWVvdXQgPT09IHZvaWQgMCkge1xuICAgICAgdGltZW91dCA9IGZhbHNlO1xuICAgIH1cblxuICAgIGlmIChyZXNwb25zZSA9PT0gdm9pZCAwKSB7XG4gICAgICByZXNwb25zZSA9IG51bGw7XG4gICAgfVxuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmluZm8oXCJBIG5ldHdvcmsgZXJyb3Igb2NjdXJlZCB3aGlsZSBsb2FkaW5nIGEgXCIgKyBjb250ZXh0LnR5cGUgKyBcIi10eXBlIHBsYXlsaXN0XCIpO1xuICAgIHZhciBkZXRhaWxzO1xuICAgIHZhciBmYXRhbDtcbiAgICB2YXIgbG9hZGVyID0gdGhpcy5nZXRJbnRlcm5hbExvYWRlcihjb250ZXh0KTtcblxuICAgIHN3aXRjaCAoY29udGV4dC50eXBlKSB7XG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuTUFOSUZFU1Q6XG4gICAgICAgIGRldGFpbHMgPSB0aW1lb3V0ID8gZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLk1BTklGRVNUX0xPQURfVElNRU9VVCA6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5NQU5JRkVTVF9MT0FEX0VSUk9SO1xuICAgICAgICBmYXRhbCA9IHRydWU7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuTEVWRUw6XG4gICAgICAgIGRldGFpbHMgPSB0aW1lb3V0ID8gZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkxFVkVMX0xPQURfVElNRU9VVCA6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5MRVZFTF9MT0FEX0VSUk9SO1xuICAgICAgICBmYXRhbCA9IGZhbHNlO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLkFVRElPX1RSQUNLOlxuICAgICAgICBkZXRhaWxzID0gdGltZW91dCA/IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5BVURJT19UUkFDS19MT0FEX1RJTUVPVVQgOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQVVESU9fVFJBQ0tfTE9BRF9FUlJPUjtcbiAgICAgICAgZmF0YWwgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIGRldGFpbHMgPSAuLi4/XG4gICAgICAgIGZhdGFsID0gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKGxvYWRlcikge1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgICB0aGlzLnJlc2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dC50eXBlKTtcbiAgICB9IC8vIFRPRE8odHlwZXNjcmlwdC1ldmVudHMpOiB3aGVuIGVycm9yIGV2ZW50cyBhcmUgaGFuZGxlZCwgdHlwZSB0aGlzXG5cblxuICAgIHZhciBlcnJvckRhdGEgPSB7XG4gICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk5FVFdPUktfRVJST1IsXG4gICAgICBkZXRhaWxzOiBkZXRhaWxzLFxuICAgICAgZmF0YWw6IGZhdGFsLFxuICAgICAgdXJsOiBjb250ZXh0LnVybCxcbiAgICAgIGxvYWRlcjogbG9hZGVyLFxuICAgICAgY29udGV4dDogY29udGV4dCxcbiAgICAgIG5ldHdvcmtEZXRhaWxzOiBuZXR3b3JrRGV0YWlsc1xuICAgIH07XG5cbiAgICBpZiAocmVzcG9uc2UpIHtcbiAgICAgIGVycm9yRGF0YS5yZXNwb25zZSA9IHJlc3BvbnNlO1xuICAgIH1cblxuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwgZXJyb3JEYXRhKTtcbiAgfTtcblxuICBfcHJvdG8uX2hhbmRsZVBsYXlsaXN0TG9hZGVkID0gZnVuY3Rpb24gX2hhbmRsZVBsYXlsaXN0TG9hZGVkKHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpIHtcbiAgICB2YXIgdHlwZSA9IGNvbnRleHQudHlwZSxcbiAgICAgICAgbGV2ZWwgPSBjb250ZXh0LmxldmVsLFxuICAgICAgICBpZCA9IGNvbnRleHQuaWQsXG4gICAgICAgIGxldmVsRGV0YWlscyA9IGNvbnRleHQubGV2ZWxEZXRhaWxzO1xuXG4gICAgaWYgKCFsZXZlbERldGFpbHMgfHwgIWxldmVsRGV0YWlscy50YXJnZXRkdXJhdGlvbikge1xuICAgICAgdGhpcy5faGFuZGxlTWFuaWZlc3RQYXJzaW5nRXJyb3IocmVzcG9uc2UsIGNvbnRleHQsICdpbnZhbGlkIHRhcmdldCBkdXJhdGlvbicsIG5ldHdvcmtEZXRhaWxzKTtcblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBjYW5IYXZlTGV2ZWxzID0gUGxheWxpc3RMb2FkZXIuY2FuSGF2ZVF1YWxpdHlMZXZlbHMoY29udGV4dC50eXBlKTtcblxuICAgIGlmIChjYW5IYXZlTGV2ZWxzKSB7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uTEVWRUxfTE9BREVELCB7XG4gICAgICAgIGRldGFpbHM6IGxldmVsRGV0YWlscyxcbiAgICAgICAgbGV2ZWw6IGxldmVsIHx8IDAsXG4gICAgICAgIGlkOiBpZCB8fCAwLFxuICAgICAgICBzdGF0czogc3RhdHMsXG4gICAgICAgIG5ldHdvcmtEZXRhaWxzOiBuZXR3b3JrRGV0YWlsc1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuQVVESU9fVFJBQ0s6XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkFVRElPX1RSQUNLX0xPQURFRCwge1xuICAgICAgICAgICAgZGV0YWlsczogbGV2ZWxEZXRhaWxzLFxuICAgICAgICAgICAgaWQ6IGlkLFxuICAgICAgICAgICAgc3RhdHM6IHN0YXRzLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHM6IG5ldHdvcmtEZXRhaWxzXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLlNVQlRJVExFX1RSQUNLOlxuICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5TVUJUSVRMRV9UUkFDS19MT0FERUQsIHtcbiAgICAgICAgICAgIGRldGFpbHM6IGxldmVsRGV0YWlscyxcbiAgICAgICAgICAgIGlkOiBpZCxcbiAgICAgICAgICAgIHN0YXRzOiBzdGF0cyxcbiAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzOiBuZXR3b3JrRGV0YWlsc1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICByZXR1cm4gUGxheWxpc3RMb2FkZXI7XG59KGV2ZW50X2hhbmRsZXIpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBwbGF5bGlzdF9sb2FkZXIgPSAocGxheWxpc3RfbG9hZGVyX1BsYXlsaXN0TG9hZGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2xvYWRlci9mcmFnbWVudC1sb2FkZXIuanNcblxuXG5cbmZ1bmN0aW9uIGZyYWdtZW50X2xvYWRlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG4vKlxuICogRnJhZ21lbnQgTG9hZGVyXG4qL1xuXG5cblxuXG5cbnZhciBmcmFnbWVudF9sb2FkZXJfRnJhZ21lbnRMb2FkZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9FdmVudEhhbmRsZXIpIHtcbiAgZnJhZ21lbnRfbG9hZGVyX2luaGVyaXRzTG9vc2UoRnJhZ21lbnRMb2FkZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIEZyYWdtZW50TG9hZGVyKGhscykge1xuICAgIHZhciBfdGhpcztcblxuICAgIF90aGlzID0gX0V2ZW50SGFuZGxlci5jYWxsKHRoaXMsIGhscywgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0xPQURJTkcpIHx8IHRoaXM7XG4gICAgX3RoaXMubG9hZGVycyA9IHt9O1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBGcmFnbWVudExvYWRlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHZhciBsb2FkZXJzID0gdGhpcy5sb2FkZXJzO1xuXG4gICAgZm9yICh2YXIgbG9hZGVyTmFtZSBpbiBsb2FkZXJzKSB7XG4gICAgICB2YXIgbG9hZGVyID0gbG9hZGVyc1tsb2FkZXJOYW1lXTtcblxuICAgICAgaWYgKGxvYWRlcikge1xuICAgICAgICBsb2FkZXIuZGVzdHJveSgpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMubG9hZGVycyA9IHt9O1xuXG4gICAgX0V2ZW50SGFuZGxlci5wcm90b3R5cGUuZGVzdHJveS5jYWxsKHRoaXMpO1xuICB9O1xuXG4gIF9wcm90by5vbkZyYWdMb2FkaW5nID0gZnVuY3Rpb24gb25GcmFnTG9hZGluZyhkYXRhKSB7XG4gICAgdmFyIGZyYWcgPSBkYXRhLmZyYWcsXG4gICAgICAgIHR5cGUgPSBmcmFnLnR5cGUsXG4gICAgICAgIGxvYWRlcnMgPSB0aGlzLmxvYWRlcnMsXG4gICAgICAgIGNvbmZpZyA9IHRoaXMuaGxzLmNvbmZpZyxcbiAgICAgICAgRnJhZ21lbnRJTG9hZGVyID0gY29uZmlnLmZMb2FkZXIsXG4gICAgICAgIERlZmF1bHRJTG9hZGVyID0gY29uZmlnLmxvYWRlcjsgLy8gcmVzZXQgZnJhZ21lbnQgc3RhdGVcblxuICAgIGZyYWcubG9hZGVkID0gMDtcbiAgICB2YXIgbG9hZGVyID0gbG9hZGVyc1t0eXBlXTtcblxuICAgIGlmIChsb2FkZXIpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiYWJvcnQgcHJldmlvdXMgZnJhZ21lbnQgbG9hZGVyIGZvciB0eXBlOiBcIiArIHR5cGUpO1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgbG9hZGVyID0gbG9hZGVyc1t0eXBlXSA9IGZyYWcubG9hZGVyID0gY29uZmlnLmZMb2FkZXIgPyBuZXcgRnJhZ21lbnRJTG9hZGVyKGNvbmZpZykgOiBuZXcgRGVmYXVsdElMb2FkZXIoY29uZmlnKTtcbiAgICB2YXIgbG9hZGVyQ29udGV4dCwgbG9hZGVyQ29uZmlnLCBsb2FkZXJDYWxsYmFja3M7XG4gICAgbG9hZGVyQ29udGV4dCA9IHtcbiAgICAgIHVybDogZnJhZy51cmwsXG4gICAgICBmcmFnOiBmcmFnLFxuICAgICAgcmVzcG9uc2VUeXBlOiAnYXJyYXlidWZmZXInLFxuICAgICAgcHJvZ3Jlc3NEYXRhOiBmYWxzZVxuICAgIH07XG4gICAgdmFyIHN0YXJ0ID0gZnJhZy5ieXRlUmFuZ2VTdGFydE9mZnNldCxcbiAgICAgICAgZW5kID0gZnJhZy5ieXRlUmFuZ2VFbmRPZmZzZXQ7XG5cbiAgICBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShzdGFydCkgJiYgT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShlbmQpKSB7XG4gICAgICBsb2FkZXJDb250ZXh0LnJhbmdlU3RhcnQgPSBzdGFydDtcbiAgICAgIGxvYWRlckNvbnRleHQucmFuZ2VFbmQgPSBlbmQ7XG4gICAgfVxuXG4gICAgbG9hZGVyQ29uZmlnID0ge1xuICAgICAgdGltZW91dDogY29uZmlnLmZyYWdMb2FkaW5nVGltZU91dCxcbiAgICAgIG1heFJldHJ5OiAwLFxuICAgICAgcmV0cnlEZWxheTogMCxcbiAgICAgIG1heFJldHJ5RGVsYXk6IGNvbmZpZy5mcmFnTG9hZGluZ01heFJldHJ5VGltZW91dFxuICAgIH07XG4gICAgbG9hZGVyQ2FsbGJhY2tzID0ge1xuICAgICAgb25TdWNjZXNzOiB0aGlzLmxvYWRzdWNjZXNzLmJpbmQodGhpcyksXG4gICAgICBvbkVycm9yOiB0aGlzLmxvYWRlcnJvci5iaW5kKHRoaXMpLFxuICAgICAgb25UaW1lb3V0OiB0aGlzLmxvYWR0aW1lb3V0LmJpbmQodGhpcyksXG4gICAgICBvblByb2dyZXNzOiB0aGlzLmxvYWRwcm9ncmVzcy5iaW5kKHRoaXMpXG4gICAgfTtcbiAgICBsb2FkZXIubG9hZChsb2FkZXJDb250ZXh0LCBsb2FkZXJDb25maWcsIGxvYWRlckNhbGxiYWNrcyk7XG4gIH07XG5cbiAgX3Byb3RvLmxvYWRzdWNjZXNzID0gZnVuY3Rpb24gbG9hZHN1Y2Nlc3MocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykge1xuICAgIGlmIChuZXR3b3JrRGV0YWlscyA9PT0gdm9pZCAwKSB7XG4gICAgICBuZXR3b3JrRGV0YWlscyA9IG51bGw7XG4gICAgfVxuXG4gICAgdmFyIHBheWxvYWQgPSByZXNwb25zZS5kYXRhLFxuICAgICAgICBmcmFnID0gY29udGV4dC5mcmFnOyAvLyBkZXRhY2ggZnJhZ21lbnQgbG9hZGVyIG9uIGxvYWQgc3VjY2Vzc1xuXG4gICAgZnJhZy5sb2FkZXIgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5sb2FkZXJzW2ZyYWcudHlwZV0gPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfTE9BREVELCB7XG4gICAgICBwYXlsb2FkOiBwYXlsb2FkLFxuICAgICAgZnJhZzogZnJhZyxcbiAgICAgIHN0YXRzOiBzdGF0cyxcbiAgICAgIG5ldHdvcmtEZXRhaWxzOiBuZXR3b3JrRGV0YWlsc1xuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90by5sb2FkZXJyb3IgPSBmdW5jdGlvbiBsb2FkZXJyb3IocmVzcG9uc2UsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSB7XG4gICAgaWYgKG5ldHdvcmtEZXRhaWxzID09PSB2b2lkIDApIHtcbiAgICAgIG5ldHdvcmtEZXRhaWxzID0gbnVsbDtcbiAgICB9XG5cbiAgICB2YXIgZnJhZyA9IGNvbnRleHQuZnJhZztcbiAgICB2YXIgbG9hZGVyID0gZnJhZy5sb2FkZXI7XG5cbiAgICBpZiAobG9hZGVyKSB7XG4gICAgICBsb2FkZXIuYWJvcnQoKTtcbiAgICB9XG5cbiAgICB0aGlzLmxvYWRlcnNbZnJhZy50eXBlXSA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTkVUV09SS19FUlJPUixcbiAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5GUkFHX0xPQURfRVJST1IsXG4gICAgICBmYXRhbDogZmFsc2UsXG4gICAgICBmcmFnOiBjb250ZXh0LmZyYWcsXG4gICAgICByZXNwb25zZTogcmVzcG9uc2UsXG4gICAgICBuZXR3b3JrRGV0YWlsczogbmV0d29ya0RldGFpbHNcbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8ubG9hZHRpbWVvdXQgPSBmdW5jdGlvbiBsb2FkdGltZW91dChzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpIHtcbiAgICBpZiAobmV0d29ya0RldGFpbHMgPT09IHZvaWQgMCkge1xuICAgICAgbmV0d29ya0RldGFpbHMgPSBudWxsO1xuICAgIH1cblxuICAgIHZhciBmcmFnID0gY29udGV4dC5mcmFnO1xuICAgIHZhciBsb2FkZXIgPSBmcmFnLmxvYWRlcjtcblxuICAgIGlmIChsb2FkZXIpIHtcbiAgICAgIGxvYWRlci5hYm9ydCgpO1xuICAgIH1cblxuICAgIHRoaXMubG9hZGVyc1tmcmFnLnR5cGVdID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5ORVRXT1JLX0VSUk9SLFxuICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkZSQUdfTE9BRF9USU1FT1VULFxuICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgZnJhZzogY29udGV4dC5mcmFnLFxuICAgICAgbmV0d29ya0RldGFpbHM6IG5ldHdvcmtEZXRhaWxzXG4gICAgfSk7XG4gIH0gLy8gZGF0YSB3aWxsIGJlIHVzZWQgZm9yIHByb2dyZXNzaXZlIHBhcnNpbmdcbiAgO1xuXG4gIF9wcm90by5sb2FkcHJvZ3Jlc3MgPSBmdW5jdGlvbiBsb2FkcHJvZ3Jlc3Moc3RhdHMsIGNvbnRleHQsIGRhdGEsIG5ldHdvcmtEZXRhaWxzKSB7XG4gICAgaWYgKG5ldHdvcmtEZXRhaWxzID09PSB2b2lkIDApIHtcbiAgICAgIG5ldHdvcmtEZXRhaWxzID0gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbiAgICB2YXIgZnJhZyA9IGNvbnRleHQuZnJhZztcbiAgICBmcmFnLmxvYWRlZCA9IHN0YXRzLmxvYWRlZDtcbiAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FEX1BST0dSRVNTLCB7XG4gICAgICBmcmFnOiBmcmFnLFxuICAgICAgc3RhdHM6IHN0YXRzLFxuICAgICAgbmV0d29ya0RldGFpbHM6IG5ldHdvcmtEZXRhaWxzXG4gICAgfSk7XG4gIH07XG5cbiAgcmV0dXJuIEZyYWdtZW50TG9hZGVyO1xufShldmVudF9oYW5kbGVyKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgZnJhZ21lbnRfbG9hZGVyID0gKGZyYWdtZW50X2xvYWRlcl9GcmFnbWVudExvYWRlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9sb2FkZXIva2V5LWxvYWRlci50c1xuZnVuY3Rpb24ga2V5X2xvYWRlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG4vKlxuICogRGVjcnlwdCBrZXkgTG9hZGVyXG4qL1xuXG5cblxuXG5cbnZhciBrZXlfbG9hZGVyX0tleUxvYWRlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBrZXlfbG9hZGVyX2luaGVyaXRzTG9vc2UoS2V5TG9hZGVyLCBfRXZlbnRIYW5kbGVyKTtcblxuICBmdW5jdGlvbiBLZXlMb2FkZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FESU5HKSB8fCB0aGlzO1xuICAgIF90aGlzLmxvYWRlcnMgPSB7fTtcbiAgICBfdGhpcy5kZWNyeXB0a2V5ID0gbnVsbDtcbiAgICBfdGhpcy5kZWNyeXB0dXJsID0gbnVsbDtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gS2V5TG9hZGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gICAgZm9yICh2YXIgbG9hZGVyTmFtZSBpbiB0aGlzLmxvYWRlcnMpIHtcbiAgICAgIHZhciBsb2FkZXIgPSB0aGlzLmxvYWRlcnNbbG9hZGVyTmFtZV07XG5cbiAgICAgIGlmIChsb2FkZXIpIHtcbiAgICAgICAgbG9hZGVyLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmxvYWRlcnMgPSB7fTtcblxuICAgIF9FdmVudEhhbmRsZXIucHJvdG90eXBlLmRlc3Ryb3kuY2FsbCh0aGlzKTtcbiAgfTtcblxuICBfcHJvdG8ub25LZXlMb2FkaW5nID0gZnVuY3Rpb24gb25LZXlMb2FkaW5nKGRhdGEpIHtcbiAgICB2YXIgZnJhZyA9IGRhdGEuZnJhZztcbiAgICB2YXIgdHlwZSA9IGZyYWcudHlwZTtcbiAgICB2YXIgbG9hZGVyID0gdGhpcy5sb2FkZXJzW3R5cGVdO1xuXG4gICAgaWYgKCFmcmFnLmRlY3J5cHRkYXRhKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignTWlzc2luZyBkZWNyeXB0aW9uIGRhdGEgb24gZnJhZ21lbnQgaW4gb25LZXlMb2FkaW5nJyk7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBMb2FkIHRoZSBrZXkgaWYgdGhlIHVyaSBpcyBkaWZmZXJlbnQgZnJvbSBwcmV2aW91cyBvbmUsIG9yIGlmIHRoZSBkZWNyeXB0IGtleSBoYXMgbm90IHlldCBiZWVuIHJldHJpZXZlZFxuXG5cbiAgICB2YXIgdXJpID0gZnJhZy5kZWNyeXB0ZGF0YS51cmk7XG5cbiAgICBpZiAodXJpICE9PSB0aGlzLmRlY3J5cHR1cmwgfHwgdGhpcy5kZWNyeXB0a2V5ID09PSBudWxsKSB7XG4gICAgICB2YXIgY29uZmlnID0gdGhpcy5obHMuY29uZmlnO1xuXG4gICAgICBpZiAobG9hZGVyKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiYWJvcnQgcHJldmlvdXMga2V5IGxvYWRlciBmb3IgdHlwZTpcIiArIHR5cGUpO1xuICAgICAgICBsb2FkZXIuYWJvcnQoKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCF1cmkpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ2tleSB1cmkgaXMgZmFsc3knKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBmcmFnLmxvYWRlciA9IHRoaXMubG9hZGVyc1t0eXBlXSA9IG5ldyBjb25maWcubG9hZGVyKGNvbmZpZyk7XG4gICAgICB0aGlzLmRlY3J5cHR1cmwgPSB1cmk7XG4gICAgICB0aGlzLmRlY3J5cHRrZXkgPSBudWxsO1xuICAgICAgdmFyIGxvYWRlckNvbnRleHQgPSB7XG4gICAgICAgIHVybDogdXJpLFxuICAgICAgICBmcmFnOiBmcmFnLFxuICAgICAgICByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcidcbiAgICAgIH07IC8vIG1heFJldHJ5IGlzIDAgc28gdGhhdCBpbnN0ZWFkIG9mIHJldHJ5aW5nIHRoZSBzYW1lIGtleSBvbiB0aGUgc2FtZSB2YXJpYW50IG11bHRpcGxlIHRpbWVzLFxuICAgICAgLy8ga2V5LWxvYWRlciB3aWxsIHRyaWdnZXIgYW4gZXJyb3IgYW5kIHJlbHkgb24gc3RyZWFtLWNvbnRyb2xsZXIgdG8gaGFuZGxlIHJldHJ5IGxvZ2ljLlxuICAgICAgLy8gdGhpcyB3aWxsIGFsc28gYWxpZ24gcmV0cnkgbG9naWMgd2l0aCBmcmFnbWVudC1sb2FkZXJcblxuICAgICAgdmFyIGxvYWRlckNvbmZpZyA9IHtcbiAgICAgICAgdGltZW91dDogY29uZmlnLmZyYWdMb2FkaW5nVGltZU91dCxcbiAgICAgICAgbWF4UmV0cnk6IDAsXG4gICAgICAgIHJldHJ5RGVsYXk6IGNvbmZpZy5mcmFnTG9hZGluZ1JldHJ5RGVsYXksXG4gICAgICAgIG1heFJldHJ5RGVsYXk6IGNvbmZpZy5mcmFnTG9hZGluZ01heFJldHJ5VGltZW91dFxuICAgICAgfTtcbiAgICAgIHZhciBsb2FkZXJDYWxsYmFja3MgPSB7XG4gICAgICAgIG9uU3VjY2VzczogdGhpcy5sb2Fkc3VjY2Vzcy5iaW5kKHRoaXMpLFxuICAgICAgICBvbkVycm9yOiB0aGlzLmxvYWRlcnJvci5iaW5kKHRoaXMpLFxuICAgICAgICBvblRpbWVvdXQ6IHRoaXMubG9hZHRpbWVvdXQuYmluZCh0aGlzKVxuICAgICAgfTtcbiAgICAgIGZyYWcubG9hZGVyLmxvYWQobG9hZGVyQ29udGV4dCwgbG9hZGVyQ29uZmlnLCBsb2FkZXJDYWxsYmFja3MpO1xuICAgIH0gZWxzZSBpZiAodGhpcy5kZWNyeXB0a2V5KSB7XG4gICAgICAvLyBSZXR1cm4gdGhlIGtleSBpZiBpdCdzIGFscmVhZHkgYmVlbiBsb2FkZWRcbiAgICAgIGZyYWcuZGVjcnlwdGRhdGEua2V5ID0gdGhpcy5kZWNyeXB0a2V5O1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FERUQsIHtcbiAgICAgICAgZnJhZzogZnJhZ1xuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5sb2Fkc3VjY2VzcyA9IGZ1bmN0aW9uIGxvYWRzdWNjZXNzKHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCkge1xuICAgIHZhciBmcmFnID0gY29udGV4dC5mcmFnO1xuXG4gICAgaWYgKCFmcmFnLmRlY3J5cHRkYXRhKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoJ2FmdGVyIGtleSBsb2FkLCBkZWNyeXB0ZGF0YSB1bnNldCcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuZGVjcnlwdGtleSA9IGZyYWcuZGVjcnlwdGRhdGEua2V5ID0gbmV3IFVpbnQ4QXJyYXkocmVzcG9uc2UuZGF0YSk7IC8vIGRldGFjaCBmcmFnbWVudCBsb2FkZXIgb24gbG9hZCBzdWNjZXNzXG5cbiAgICBmcmFnLmxvYWRlciA9IHVuZGVmaW5lZDtcbiAgICBkZWxldGUgdGhpcy5sb2FkZXJzW2ZyYWcudHlwZV07XG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FERUQsIHtcbiAgICAgIGZyYWc6IGZyYWdcbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8ubG9hZGVycm9yID0gZnVuY3Rpb24gbG9hZGVycm9yKHJlc3BvbnNlLCBjb250ZXh0KSB7XG4gICAgdmFyIGZyYWcgPSBjb250ZXh0LmZyYWc7XG4gICAgdmFyIGxvYWRlciA9IGZyYWcubG9hZGVyO1xuXG4gICAgaWYgKGxvYWRlcikge1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgZGVsZXRlIHRoaXMubG9hZGVyc1tmcmFnLnR5cGVdO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5ORVRXT1JLX0VSUk9SLFxuICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLktFWV9MT0FEX0VSUk9SLFxuICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgZnJhZzogZnJhZyxcbiAgICAgIHJlc3BvbnNlOiByZXNwb25zZVxuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90by5sb2FkdGltZW91dCA9IGZ1bmN0aW9uIGxvYWR0aW1lb3V0KHN0YXRzLCBjb250ZXh0KSB7XG4gICAgdmFyIGZyYWcgPSBjb250ZXh0LmZyYWc7XG4gICAgdmFyIGxvYWRlciA9IGZyYWcubG9hZGVyO1xuXG4gICAgaWYgKGxvYWRlcikge1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgZGVsZXRlIHRoaXMubG9hZGVyc1tmcmFnLnR5cGVdO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5ORVRXT1JLX0VSUk9SLFxuICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLktFWV9MT0FEX1RJTUVPVVQsXG4gICAgICBmYXRhbDogZmFsc2UsXG4gICAgICBmcmFnOiBmcmFnXG4gICAgfSk7XG4gIH07XG5cbiAgcmV0dXJuIEtleUxvYWRlcjtcbn0oZXZlbnRfaGFuZGxlcik7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGtleV9sb2FkZXIgPSAoa2V5X2xvYWRlcl9LZXlMb2FkZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9mcmFnbWVudC10cmFja2VyLmpzXG5cblxuZnVuY3Rpb24gZnJhZ21lbnRfdHJhY2tlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG5cblxudmFyIEZyYWdtZW50U3RhdGUgPSB7XG4gIE5PVF9MT0FERUQ6ICdOT1RfTE9BREVEJyxcbiAgQVBQRU5ESU5HOiAnQVBQRU5ESU5HJyxcbiAgUEFSVElBTDogJ1BBUlRJQUwnLFxuICBPSzogJ09LJ1xufTtcbnZhciBmcmFnbWVudF90cmFja2VyX0ZyYWdtZW50VHJhY2tlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBmcmFnbWVudF90cmFja2VyX2luaGVyaXRzTG9vc2UoRnJhZ21lbnRUcmFja2VyLCBfRXZlbnRIYW5kbGVyKTtcblxuICBmdW5jdGlvbiBGcmFnbWVudFRyYWNrZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0JVRkZFUkVELCBldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfTE9BREVEKSB8fCB0aGlzO1xuICAgIF90aGlzLmJ1ZmZlclBhZGRpbmcgPSAwLjI7XG4gICAgX3RoaXMuZnJhZ21lbnRzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICBfdGhpcy50aW1lUmFuZ2VzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICBfdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBGcmFnbWVudFRyYWNrZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHtcbiAgICB0aGlzLmZyYWdtZW50cyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gICAgdGhpcy50aW1lUmFuZ2VzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLmNvbmZpZyA9IG51bGw7XG4gICAgZXZlbnRfaGFuZGxlci5wcm90b3R5cGUuZGVzdHJveS5jYWxsKHRoaXMpO1xuXG4gICAgX0V2ZW50SGFuZGxlci5wcm90b3R5cGUuZGVzdHJveS5jYWxsKHRoaXMpO1xuICB9XG4gIC8qKlxuICAgKiBSZXR1cm4gYSBGcmFnbWVudCB0aGF0IG1hdGNoIHRoZSBwb3NpdGlvbiBhbmQgbGV2ZWxUeXBlLlxuICAgKiBJZiBub3QgZm91bmQgYW55IEZyYWdtZW50LCByZXR1cm4gbnVsbFxuICAgKiBAcGFyYW0ge251bWJlcn0gcG9zaXRpb25cbiAgICogQHBhcmFtIHtMZXZlbFR5cGV9IGxldmVsVHlwZVxuICAgKiBAcmV0dXJucyB7RnJhZ21lbnR8bnVsbH1cbiAgICovXG4gIDtcblxuICBfcHJvdG8uZ2V0QnVmZmVyZWRGcmFnID0gZnVuY3Rpb24gZ2V0QnVmZmVyZWRGcmFnKHBvc2l0aW9uLCBsZXZlbFR5cGUpIHtcbiAgICB2YXIgZnJhZ21lbnRzID0gdGhpcy5mcmFnbWVudHM7XG4gICAgdmFyIGJ1ZmZlcmVkRnJhZ3MgPSBPYmplY3Qua2V5cyhmcmFnbWVudHMpLmZpbHRlcihmdW5jdGlvbiAoa2V5KSB7XG4gICAgICB2YXIgZnJhZ21lbnRFbnRpdHkgPSBmcmFnbWVudHNba2V5XTtcblxuICAgICAgaWYgKGZyYWdtZW50RW50aXR5LmJvZHkudHlwZSAhPT0gbGV2ZWxUeXBlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFmcmFnbWVudEVudGl0eS5idWZmZXJlZCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIHZhciBmcmFnID0gZnJhZ21lbnRFbnRpdHkuYm9keTtcbiAgICAgIHJldHVybiBmcmFnLnN0YXJ0UFRTIDw9IHBvc2l0aW9uICYmIHBvc2l0aW9uIDw9IGZyYWcuZW5kUFRTO1xuICAgIH0pO1xuXG4gICAgaWYgKGJ1ZmZlcmVkRnJhZ3MubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvcHVsbC8xNTQ1I2Rpc2N1c3Npb25fcjE2NjIyOTU2NlxuICAgICAgdmFyIGJ1ZmZlcmVkRnJhZ0tleSA9IGJ1ZmZlcmVkRnJhZ3MucG9wKCk7XG4gICAgICByZXR1cm4gZnJhZ21lbnRzW2J1ZmZlcmVkRnJhZ0tleV0uYm9keTtcbiAgICB9XG4gIH1cbiAgLyoqXG4gICAqIFBhcnRpYWwgZnJhZ21lbnRzIGVmZmVjdGVkIGJ5IGNvZGVkIGZyYW1lIGV2aWN0aW9uIHdpbGwgYmUgcmVtb3ZlZFxuICAgKiBUaGUgYnJvd3NlciB3aWxsIHVubG9hZCBwYXJ0cyBvZiB0aGUgYnVmZmVyIHRvIGZyZWUgdXAgbWVtb3J5IGZvciBuZXcgYnVmZmVyIGRhdGFcbiAgICogRnJhZ21lbnRzIHdpbGwgbmVlZCB0byBiZSByZWxvYWRlZCB3aGVuIHRoZSBidWZmZXIgaXMgZnJlZWQgdXAsIHJlbW92aW5nIHBhcnRpYWwgZnJhZ21lbnRzIHdpbGwgYWxsb3cgdGhlbSB0byByZWxvYWQoc2luY2UgdGhlcmUgbWlnaHQgYmUgcGFydHMgdGhhdCBhcmUgc3RpbGwgcGxheWFibGUpXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBlbGVtZW50YXJ5U3RyZWFtIFRoZSBlbGVtZW50YXJ5U3RyZWFtIG9mIG1lZGlhIHRoaXMgaXMgKGVnLiB2aWRlby9hdWRpbylcbiAgICogQHBhcmFtIHtUaW1lUmFuZ2VzfSB0aW1lUmFuZ2UgVGltZVJhbmdlIG9iamVjdCBmcm9tIGEgc291cmNlQnVmZmVyXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmRldGVjdEV2aWN0ZWRGcmFnbWVudHMgPSBmdW5jdGlvbiBkZXRlY3RFdmljdGVkRnJhZ21lbnRzKGVsZW1lbnRhcnlTdHJlYW0sIHRpbWVSYW5nZSkge1xuICAgIHZhciBfdGhpczIgPSB0aGlzO1xuXG4gICAgLy8gQ2hlY2sgaWYgYW55IGZsYWdnZWQgZnJhZ21lbnRzIGhhdmUgYmVlbiB1bmxvYWRlZFxuICAgIE9iamVjdC5rZXlzKHRoaXMuZnJhZ21lbnRzKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgIHZhciBmcmFnbWVudEVudGl0eSA9IF90aGlzMi5mcmFnbWVudHNba2V5XTtcblxuICAgICAgaWYgKCFmcmFnbWVudEVudGl0eSB8fCAhZnJhZ21lbnRFbnRpdHkuYnVmZmVyZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICB2YXIgZXNEYXRhID0gZnJhZ21lbnRFbnRpdHkucmFuZ2VbZWxlbWVudGFyeVN0cmVhbV07XG5cbiAgICAgIGlmICghZXNEYXRhKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGZyYWdtZW50VGltZXMgPSBlc0RhdGEudGltZTtcblxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBmcmFnbWVudFRpbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciB0aW1lID0gZnJhZ21lbnRUaW1lc1tpXTtcblxuICAgICAgICBpZiAoIV90aGlzMi5pc1RpbWVCdWZmZXJlZCh0aW1lLnN0YXJ0UFRTLCB0aW1lLmVuZFBUUywgdGltZVJhbmdlKSkge1xuICAgICAgICAgIC8vIFVucmVnaXN0ZXIgcGFydGlhbCBmcmFnbWVudCBhcyBpdCBuZWVkcyB0byBsb2FkIGFnYWluIHRvIGJlIHJldXNlZFxuICAgICAgICAgIF90aGlzMi5yZW1vdmVGcmFnbWVudChmcmFnbWVudEVudGl0eS5ib2R5KTtcblxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrcyBpZiB0aGUgZnJhZ21lbnQgcGFzc2VkIGluIGlzIGxvYWRlZCBpbiB0aGUgYnVmZmVyIHByb3Blcmx5XG4gICAqIFBhcnRpYWxseSBsb2FkZWQgZnJhZ21lbnRzIHdpbGwgYmUgcmVnaXN0ZXJlZCBhcyBhIHBhcnRpYWwgZnJhZ21lbnRcbiAgICogQHBhcmFtIHtPYmplY3R9IGZyYWdtZW50IENoZWNrIHRoZSBmcmFnbWVudCBhZ2FpbnN0IGFsbCBzb3VyY2VCdWZmZXJzIGxvYWRlZFxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5kZXRlY3RQYXJ0aWFsRnJhZ21lbnRzID0gZnVuY3Rpb24gZGV0ZWN0UGFydGlhbEZyYWdtZW50cyhmcmFnbWVudCkge1xuICAgIHZhciBfdGhpczMgPSB0aGlzO1xuXG4gICAgdmFyIGZyYWdLZXkgPSB0aGlzLmdldEZyYWdtZW50S2V5KGZyYWdtZW50KTtcbiAgICB2YXIgZnJhZ21lbnRFbnRpdHkgPSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcblxuICAgIGlmIChmcmFnbWVudEVudGl0eSkge1xuICAgICAgZnJhZ21lbnRFbnRpdHkuYnVmZmVyZWQgPSB0cnVlO1xuICAgICAgT2JqZWN0LmtleXModGhpcy50aW1lUmFuZ2VzKS5mb3JFYWNoKGZ1bmN0aW9uIChlbGVtZW50YXJ5U3RyZWFtKSB7XG4gICAgICAgIGlmIChmcmFnbWVudC5oYXNFbGVtZW50YXJ5U3RyZWFtKGVsZW1lbnRhcnlTdHJlYW0pKSB7XG4gICAgICAgICAgdmFyIHRpbWVSYW5nZSA9IF90aGlzMy50aW1lUmFuZ2VzW2VsZW1lbnRhcnlTdHJlYW1dOyAvLyBDaGVjayBmb3IgbWFsZm9ybWVkIGZyYWdtZW50c1xuICAgICAgICAgIC8vIEdhcHMgbmVlZCB0byBiZSBjYWxjdWxhdGVkIGZvciBlYWNoIGVsZW1lbnRhcnlTdHJlYW1cblxuICAgICAgICAgIGZyYWdtZW50RW50aXR5LnJhbmdlW2VsZW1lbnRhcnlTdHJlYW1dID0gX3RoaXMzLmdldEJ1ZmZlcmVkVGltZXMoZnJhZ21lbnQuc3RhcnRQVFMsIGZyYWdtZW50LmVuZFBUUywgdGltZVJhbmdlKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5nZXRCdWZmZXJlZFRpbWVzID0gZnVuY3Rpb24gZ2V0QnVmZmVyZWRUaW1lcyhzdGFydFBUUywgZW5kUFRTLCB0aW1lUmFuZ2UpIHtcbiAgICB2YXIgZnJhZ21lbnRUaW1lcyA9IFtdO1xuICAgIHZhciBzdGFydFRpbWUsIGVuZFRpbWU7XG4gICAgdmFyIGZyYWdtZW50UGFydGlhbCA9IGZhbHNlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aW1lUmFuZ2UubGVuZ3RoOyBpKyspIHtcbiAgICAgIHN0YXJ0VGltZSA9IHRpbWVSYW5nZS5zdGFydChpKSAtIHRoaXMuYnVmZmVyUGFkZGluZztcbiAgICAgIGVuZFRpbWUgPSB0aW1lUmFuZ2UuZW5kKGkpICsgdGhpcy5idWZmZXJQYWRkaW5nO1xuXG4gICAgICBpZiAoc3RhcnRQVFMgPj0gc3RhcnRUaW1lICYmIGVuZFBUUyA8PSBlbmRUaW1lKSB7XG4gICAgICAgIC8vIEZyYWdtZW50IGlzIGVudGlyZWx5IGNvbnRhaW5lZCBpbiBidWZmZXJcbiAgICAgICAgLy8gTm8gbmVlZCB0byBjaGVjayB0aGUgb3RoZXIgdGltZVJhbmdlIHRpbWVzIHNpbmNlIGl0J3MgY29tcGxldGVseSBwbGF5YWJsZVxuICAgICAgICBmcmFnbWVudFRpbWVzLnB1c2goe1xuICAgICAgICAgIHN0YXJ0UFRTOiBNYXRoLm1heChzdGFydFBUUywgdGltZVJhbmdlLnN0YXJ0KGkpKSxcbiAgICAgICAgICBlbmRQVFM6IE1hdGgubWluKGVuZFBUUywgdGltZVJhbmdlLmVuZChpKSlcbiAgICAgICAgfSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfSBlbHNlIGlmIChzdGFydFBUUyA8IGVuZFRpbWUgJiYgZW5kUFRTID4gc3RhcnRUaW1lKSB7XG4gICAgICAgIC8vIENoZWNrIGZvciBpbnRlcnNlY3Rpb24gd2l0aCBidWZmZXJcbiAgICAgICAgLy8gR2V0IHBsYXlhYmxlIHNlY3Rpb25zIG9mIHRoZSBmcmFnbWVudFxuICAgICAgICBmcmFnbWVudFRpbWVzLnB1c2goe1xuICAgICAgICAgIHN0YXJ0UFRTOiBNYXRoLm1heChzdGFydFBUUywgdGltZVJhbmdlLnN0YXJ0KGkpKSxcbiAgICAgICAgICBlbmRQVFM6IE1hdGgubWluKGVuZFBUUywgdGltZVJhbmdlLmVuZChpKSlcbiAgICAgICAgfSk7XG4gICAgICAgIGZyYWdtZW50UGFydGlhbCA9IHRydWU7XG4gICAgICB9IGVsc2UgaWYgKGVuZFBUUyA8PSBzdGFydFRpbWUpIHtcbiAgICAgICAgLy8gTm8gbmVlZCB0byBjaGVjayB0aGUgcmVzdCBvZiB0aGUgdGltZVJhbmdlIGFzIGl0IGlzIGluIG9yZGVyXG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICB0aW1lOiBmcmFnbWVudFRpbWVzLFxuICAgICAgcGFydGlhbDogZnJhZ21lbnRQYXJ0aWFsXG4gICAgfTtcbiAgfTtcblxuICBfcHJvdG8uZ2V0RnJhZ21lbnRLZXkgPSBmdW5jdGlvbiBnZXRGcmFnbWVudEtleShmcmFnbWVudCkge1xuICAgIHJldHVybiBmcmFnbWVudC50eXBlICsgXCJfXCIgKyBmcmFnbWVudC5sZXZlbCArIFwiX1wiICsgZnJhZ21lbnQudXJsSWQgKyBcIl9cIiArIGZyYWdtZW50LnNuO1xuICB9XG4gIC8qKlxuICAgKiBHZXRzIHRoZSBwYXJ0aWFsIGZyYWdtZW50IGZvciBhIGNlcnRhaW4gdGltZVxuICAgKiBAcGFyYW0ge051bWJlcn0gdGltZVxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSBmcmFnbWVudCBSZXR1cm5zIGEgcGFydGlhbCBmcmFnbWVudCBhdCBhIHRpbWUgb3IgbnVsbCBpZiB0aGVyZSBpcyBubyBwYXJ0aWFsIGZyYWdtZW50XG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmdldFBhcnRpYWxGcmFnbWVudCA9IGZ1bmN0aW9uIGdldFBhcnRpYWxGcmFnbWVudCh0aW1lKSB7XG4gICAgdmFyIF90aGlzNCA9IHRoaXM7XG5cbiAgICB2YXIgdGltZVBhZGRpbmcsIHN0YXJ0VGltZSwgZW5kVGltZTtcbiAgICB2YXIgYmVzdEZyYWdtZW50ID0gbnVsbDtcbiAgICB2YXIgYmVzdE92ZXJsYXAgPSAwO1xuICAgIE9iamVjdC5rZXlzKHRoaXMuZnJhZ21lbnRzKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgIHZhciBmcmFnbWVudEVudGl0eSA9IF90aGlzNC5mcmFnbWVudHNba2V5XTtcblxuICAgICAgaWYgKF90aGlzNC5pc1BhcnRpYWwoZnJhZ21lbnRFbnRpdHkpKSB7XG4gICAgICAgIHN0YXJ0VGltZSA9IGZyYWdtZW50RW50aXR5LmJvZHkuc3RhcnRQVFMgLSBfdGhpczQuYnVmZmVyUGFkZGluZztcbiAgICAgICAgZW5kVGltZSA9IGZyYWdtZW50RW50aXR5LmJvZHkuZW5kUFRTICsgX3RoaXM0LmJ1ZmZlclBhZGRpbmc7XG5cbiAgICAgICAgaWYgKHRpbWUgPj0gc3RhcnRUaW1lICYmIHRpbWUgPD0gZW5kVGltZSkge1xuICAgICAgICAgIC8vIFVzZSB0aGUgZnJhZ21lbnQgdGhhdCBoYXMgdGhlIG1vc3QgcGFkZGluZyBmcm9tIHN0YXJ0IGFuZCBlbmQgdGltZVxuICAgICAgICAgIHRpbWVQYWRkaW5nID0gTWF0aC5taW4odGltZSAtIHN0YXJ0VGltZSwgZW5kVGltZSAtIHRpbWUpO1xuXG4gICAgICAgICAgaWYgKGJlc3RPdmVybGFwIDw9IHRpbWVQYWRkaW5nKSB7XG4gICAgICAgICAgICBiZXN0RnJhZ21lbnQgPSBmcmFnbWVudEVudGl0eS5ib2R5O1xuICAgICAgICAgICAgYmVzdE92ZXJsYXAgPSB0aW1lUGFkZGluZztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICByZXR1cm4gYmVzdEZyYWdtZW50O1xuICB9XG4gIC8qKlxuICAgKiBAcGFyYW0ge09iamVjdH0gZnJhZ21lbnQgVGhlIGZyYWdtZW50IHRvIGNoZWNrXG4gICAqIEByZXR1cm5zIHtTdHJpbmd9IFJldHVybnMgdGhlIGZyYWdtZW50IHN0YXRlIHdoZW4gYSBmcmFnbWVudCBuZXZlciBsb2FkZWQgb3IgaWYgaXQgcGFydGlhbGx5IGxvYWRlZFxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5nZXRTdGF0ZSA9IGZ1bmN0aW9uIGdldFN0YXRlKGZyYWdtZW50KSB7XG4gICAgdmFyIGZyYWdLZXkgPSB0aGlzLmdldEZyYWdtZW50S2V5KGZyYWdtZW50KTtcbiAgICB2YXIgZnJhZ21lbnRFbnRpdHkgPSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcbiAgICB2YXIgc3RhdGUgPSBGcmFnbWVudFN0YXRlLk5PVF9MT0FERUQ7XG5cbiAgICBpZiAoZnJhZ21lbnRFbnRpdHkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgaWYgKCFmcmFnbWVudEVudGl0eS5idWZmZXJlZCkge1xuICAgICAgICBzdGF0ZSA9IEZyYWdtZW50U3RhdGUuQVBQRU5ESU5HO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLmlzUGFydGlhbChmcmFnbWVudEVudGl0eSkgPT09IHRydWUpIHtcbiAgICAgICAgc3RhdGUgPSBGcmFnbWVudFN0YXRlLlBBUlRJQUw7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzdGF0ZSA9IEZyYWdtZW50U3RhdGUuT0s7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHN0YXRlO1xuICB9O1xuXG4gIF9wcm90by5pc1BhcnRpYWwgPSBmdW5jdGlvbiBpc1BhcnRpYWwoZnJhZ21lbnRFbnRpdHkpIHtcbiAgICByZXR1cm4gZnJhZ21lbnRFbnRpdHkuYnVmZmVyZWQgPT09IHRydWUgJiYgKGZyYWdtZW50RW50aXR5LnJhbmdlLnZpZGVvICE9PSB1bmRlZmluZWQgJiYgZnJhZ21lbnRFbnRpdHkucmFuZ2UudmlkZW8ucGFydGlhbCA9PT0gdHJ1ZSB8fCBmcmFnbWVudEVudGl0eS5yYW5nZS5hdWRpbyAhPT0gdW5kZWZpbmVkICYmIGZyYWdtZW50RW50aXR5LnJhbmdlLmF1ZGlvLnBhcnRpYWwgPT09IHRydWUpO1xuICB9O1xuXG4gIF9wcm90by5pc1RpbWVCdWZmZXJlZCA9IGZ1bmN0aW9uIGlzVGltZUJ1ZmZlcmVkKHN0YXJ0UFRTLCBlbmRQVFMsIHRpbWVSYW5nZSkge1xuICAgIHZhciBzdGFydFRpbWUsIGVuZFRpbWU7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRpbWVSYW5nZS5sZW5ndGg7IGkrKykge1xuICAgICAgc3RhcnRUaW1lID0gdGltZVJhbmdlLnN0YXJ0KGkpIC0gdGhpcy5idWZmZXJQYWRkaW5nO1xuICAgICAgZW5kVGltZSA9IHRpbWVSYW5nZS5lbmQoaSkgKyB0aGlzLmJ1ZmZlclBhZGRpbmc7XG5cbiAgICAgIGlmIChzdGFydFBUUyA+PSBzdGFydFRpbWUgJiYgZW5kUFRTIDw9IGVuZFRpbWUpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbmRQVFMgPD0gc3RhcnRUaW1lKSB7XG4gICAgICAgIC8vIE5vIG5lZWQgdG8gY2hlY2sgdGhlIHJlc3Qgb2YgdGhlIHRpbWVSYW5nZSBhcyBpdCBpcyBpbiBvcmRlclxuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIC8qKlxuICAgKiBGaXJlcyB3aGVuIGEgZnJhZ21lbnQgbG9hZGluZyBpcyBjb21wbGV0ZWRcbiAgICovXG4gIDtcblxuICBfcHJvdG8ub25GcmFnTG9hZGVkID0gZnVuY3Rpb24gb25GcmFnTG9hZGVkKGUpIHtcbiAgICB2YXIgZnJhZ21lbnQgPSBlLmZyYWc7IC8vIGRvbid0IHRyYWNrIGluaXRzZWdtZW50IChmb3Igd2hpY2ggc24gaXMgbm90IGEgbnVtYmVyKVxuICAgIC8vIGRvbid0IHRyYWNrIGZyYWdzIHVzZWQgZm9yIGJpdHJhdGVUZXN0LCB0aGV5J3JlIGlycmVsZXZhbnQuXG5cbiAgICBpZiAoIU9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoZnJhZ21lbnQuc24pIHx8IGZyYWdtZW50LmJpdHJhdGVUZXN0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5mcmFnbWVudHNbdGhpcy5nZXRGcmFnbWVudEtleShmcmFnbWVudCldID0ge1xuICAgICAgYm9keTogZnJhZ21lbnQsXG4gICAgICByYW5nZTogT2JqZWN0LmNyZWF0ZShudWxsKSxcbiAgICAgIGJ1ZmZlcmVkOiBmYWxzZVxuICAgIH07XG4gIH1cbiAgLyoqXG4gICAqIEZpcmVzIHdoZW4gdGhlIGJ1ZmZlciBpcyB1cGRhdGVkXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLm9uQnVmZmVyQXBwZW5kZWQgPSBmdW5jdGlvbiBvbkJ1ZmZlckFwcGVuZGVkKGUpIHtcbiAgICB2YXIgX3RoaXM1ID0gdGhpcztcblxuICAgIC8vIFN0b3JlIHRoZSBsYXRlc3QgdGltZVJhbmdlcyBsb2FkZWQgaW4gdGhlIGJ1ZmZlclxuICAgIHRoaXMudGltZVJhbmdlcyA9IGUudGltZVJhbmdlcztcbiAgICBPYmplY3Qua2V5cyh0aGlzLnRpbWVSYW5nZXMpLmZvckVhY2goZnVuY3Rpb24gKGVsZW1lbnRhcnlTdHJlYW0pIHtcbiAgICAgIHZhciB0aW1lUmFuZ2UgPSBfdGhpczUudGltZVJhbmdlc1tlbGVtZW50YXJ5U3RyZWFtXTtcblxuICAgICAgX3RoaXM1LmRldGVjdEV2aWN0ZWRGcmFnbWVudHMoZWxlbWVudGFyeVN0cmVhbSwgdGltZVJhbmdlKTtcbiAgICB9KTtcbiAgfVxuICAvKipcbiAgICogRmlyZXMgYWZ0ZXIgYSBmcmFnbWVudCBoYXMgYmVlbiBsb2FkZWQgaW50byB0aGUgc291cmNlIGJ1ZmZlclxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5vbkZyYWdCdWZmZXJlZCA9IGZ1bmN0aW9uIG9uRnJhZ0J1ZmZlcmVkKGUpIHtcbiAgICB0aGlzLmRldGVjdFBhcnRpYWxGcmFnbWVudHMoZS5mcmFnKTtcbiAgfVxuICAvKipcbiAgICogUmV0dXJuIHRydWUgaWYgZnJhZ21lbnQgdHJhY2tlciBoYXMgdGhlIGZyYWdtZW50LlxuICAgKiBAcGFyYW0ge09iamVjdH0gZnJhZ21lbnRcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmhhc0ZyYWdtZW50ID0gZnVuY3Rpb24gaGFzRnJhZ21lbnQoZnJhZ21lbnQpIHtcbiAgICB2YXIgZnJhZ0tleSA9IHRoaXMuZ2V0RnJhZ21lbnRLZXkoZnJhZ21lbnQpO1xuICAgIHJldHVybiB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XSAhPT0gdW5kZWZpbmVkO1xuICB9XG4gIC8qKlxuICAgKiBSZW1vdmUgYSBmcmFnbWVudCBmcm9tIGZyYWdtZW50IHRyYWNrZXIgdW50aWwgaXQgaXMgbG9hZGVkIGFnYWluXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBmcmFnbWVudCBUaGUgZnJhZ21lbnQgdG8gcmVtb3ZlXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnJlbW92ZUZyYWdtZW50ID0gZnVuY3Rpb24gcmVtb3ZlRnJhZ21lbnQoZnJhZ21lbnQpIHtcbiAgICB2YXIgZnJhZ0tleSA9IHRoaXMuZ2V0RnJhZ21lbnRLZXkoZnJhZ21lbnQpO1xuICAgIGRlbGV0ZSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcbiAgfVxuICAvKipcbiAgICogUmVtb3ZlIGFsbCBmcmFnbWVudHMgZnJvbSBmcmFnbWVudCB0cmFja2VyLlxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5yZW1vdmVBbGxGcmFnbWVudHMgPSBmdW5jdGlvbiByZW1vdmVBbGxGcmFnbWVudHMoKSB7XG4gICAgdGhpcy5mcmFnbWVudHMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuICB9O1xuXG4gIHJldHVybiBGcmFnbWVudFRyYWNrZXI7XG59KGV2ZW50X2hhbmRsZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvYmluYXJ5LXNlYXJjaC50c1xudmFyIEJpbmFyeVNlYXJjaCA9IHtcbiAgLyoqXG4gICAqIFNlYXJjaGVzIGZvciBhbiBpdGVtIGluIGFuIGFycmF5IHdoaWNoIG1hdGNoZXMgYSBjZXJ0YWluIGNvbmRpdGlvbi5cbiAgICogVGhpcyByZXF1aXJlcyB0aGUgY29uZGl0aW9uIHRvIG9ubHkgbWF0Y2ggb25lIGl0ZW0gaW4gdGhlIGFycmF5LFxuICAgKiBhbmQgZm9yIHRoZSBhcnJheSB0byBiZSBvcmRlcmVkLlxuICAgKlxuICAgKiBAcGFyYW0ge0FycmF5PFQ+fSBsaXN0IFRoZSBhcnJheSB0byBzZWFyY2guXG4gICAqIEBwYXJhbSB7QmluYXJ5U2VhcmNoQ29tcGFyaXNvbjxUPn0gY29tcGFyaXNvbkZuXG4gICAqICAgICAgQ2FsbGVkIGFuZCBwcm92aWRlZCBhIGNhbmRpZGF0ZSBpdGVtIGFzIHRoZSBmaXJzdCBhcmd1bWVudC5cbiAgICogICAgICBTaG91bGQgcmV0dXJuOlxuICAgKiAgICAgICAgICA+IC0xIGlmIHRoZSBpdGVtIHNob3VsZCBiZSBsb2NhdGVkIGF0IGEgbG93ZXIgaW5kZXggdGhhbiB0aGUgcHJvdmlkZWQgaXRlbS5cbiAgICogICAgICAgICAgPiAxIGlmIHRoZSBpdGVtIHNob3VsZCBiZSBsb2NhdGVkIGF0IGEgaGlnaGVyIGluZGV4IHRoYW4gdGhlIHByb3ZpZGVkIGl0ZW0uXG4gICAqICAgICAgICAgID4gMCBpZiB0aGUgaXRlbSBpcyB0aGUgaXRlbSB5b3UncmUgbG9va2luZyBmb3IuXG4gICAqXG4gICAqIEByZXR1cm4ge1QgfCBudWxsfSBUaGUgb2JqZWN0IGlmIGl0IGlzIGZvdW5kIG9yIG51bGwgb3RoZXJ3aXNlLlxuICAgKi9cbiAgc2VhcmNoOiBmdW5jdGlvbiBzZWFyY2gobGlzdCwgY29tcGFyaXNvbkZuKSB7XG4gICAgdmFyIG1pbkluZGV4ID0gMDtcbiAgICB2YXIgbWF4SW5kZXggPSBsaXN0Lmxlbmd0aCAtIDE7XG4gICAgdmFyIGN1cnJlbnRJbmRleCA9IG51bGw7XG4gICAgdmFyIGN1cnJlbnRFbGVtZW50ID0gbnVsbDtcblxuICAgIHdoaWxlIChtaW5JbmRleCA8PSBtYXhJbmRleCkge1xuICAgICAgY3VycmVudEluZGV4ID0gKG1pbkluZGV4ICsgbWF4SW5kZXgpIC8gMiB8IDA7XG4gICAgICBjdXJyZW50RWxlbWVudCA9IGxpc3RbY3VycmVudEluZGV4XTtcbiAgICAgIHZhciBjb21wYXJpc29uUmVzdWx0ID0gY29tcGFyaXNvbkZuKGN1cnJlbnRFbGVtZW50KTtcblxuICAgICAgaWYgKGNvbXBhcmlzb25SZXN1bHQgPiAwKSB7XG4gICAgICAgIG1pbkluZGV4ID0gY3VycmVudEluZGV4ICsgMTtcbiAgICAgIH0gZWxzZSBpZiAoY29tcGFyaXNvblJlc3VsdCA8IDApIHtcbiAgICAgICAgbWF4SW5kZXggPSBjdXJyZW50SW5kZXggLSAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGN1cnJlbnRFbGVtZW50O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9XG59O1xuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgYmluYXJ5X3NlYXJjaCA9IChCaW5hcnlTZWFyY2gpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvYnVmZmVyLWhlbHBlci50c1xuLyoqXG4gKiBAbW9kdWxlIEJ1ZmZlckhlbHBlclxuICpcbiAqIFByb3ZpZGluZyBtZXRob2RzIGRlYWxpbmcgd2l0aCBidWZmZXIgbGVuZ3RoIHJldHJpZXZhbCBmb3IgZXhhbXBsZS5cbiAqXG4gKiBJbiBnZW5lcmFsLCBhIGhlbHBlciBhcm91bmQgSFRNTDUgTWVkaWFFbGVtZW50IFRpbWVSYW5nZXMgZ2F0aGVyZWQgZnJvbSBgYnVmZmVyZWRgIHByb3BlcnR5LlxuICpcbiAqIEFsc28gQHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvSFRNTE1lZGlhRWxlbWVudC9idWZmZXJlZFxuKi9cbnZhciBCdWZmZXJIZWxwZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBCdWZmZXJIZWxwZXIoKSB7fVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdHJ1ZSBpZiBgbWVkaWFgJ3MgYnVmZmVyZWQgaW5jbHVkZSBgcG9zaXRpb25gXG4gICAqIEBwYXJhbSB7QnVmZmVyYWJsZX0gbWVkaWFcbiAgICogQHBhcmFtIHtudW1iZXJ9IHBvc2l0aW9uXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgQnVmZmVySGVscGVyLmlzQnVmZmVyZWQgPSBmdW5jdGlvbiBpc0J1ZmZlcmVkKG1lZGlhLCBwb3NpdGlvbikge1xuICAgIHRyeSB7XG4gICAgICBpZiAobWVkaWEpIHtcbiAgICAgICAgdmFyIGJ1ZmZlcmVkID0gbWVkaWEuYnVmZmVyZWQ7XG5cbiAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBidWZmZXJlZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGlmIChwb3NpdGlvbiA+PSBidWZmZXJlZC5zdGFydChpKSAmJiBwb3NpdGlvbiA8PSBidWZmZXJlZC5lbmQoaSkpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7Ly8gdGhpcyBpcyB0byBjYXRjaFxuICAgICAgLy8gSW52YWxpZFN0YXRlRXJyb3I6IEZhaWxlZCB0byByZWFkIHRoZSAnYnVmZmVyZWQnIHByb3BlcnR5IGZyb20gJ1NvdXJjZUJ1ZmZlcic6XG4gICAgICAvLyBUaGlzIFNvdXJjZUJ1ZmZlciBoYXMgYmVlbiByZW1vdmVkIGZyb20gdGhlIHBhcmVudCBtZWRpYSBzb3VyY2VcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH07XG5cbiAgQnVmZmVySGVscGVyLmJ1ZmZlckluZm8gPSBmdW5jdGlvbiBidWZmZXJJbmZvKG1lZGlhLCBwb3MsIG1heEhvbGVEdXJhdGlvbikge1xuICAgIHRyeSB7XG4gICAgICBpZiAobWVkaWEpIHtcbiAgICAgICAgdmFyIHZidWZmZXJlZCA9IG1lZGlhLmJ1ZmZlcmVkO1xuICAgICAgICB2YXIgYnVmZmVyZWQgPSBbXTtcbiAgICAgICAgdmFyIGk7XG5cbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHZidWZmZXJlZC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGJ1ZmZlcmVkLnB1c2goe1xuICAgICAgICAgICAgc3RhcnQ6IHZidWZmZXJlZC5zdGFydChpKSxcbiAgICAgICAgICAgIGVuZDogdmJ1ZmZlcmVkLmVuZChpKVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyZWRJbmZvKGJ1ZmZlcmVkLCBwb3MsIG1heEhvbGVEdXJhdGlvbik7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHsvLyB0aGlzIGlzIHRvIGNhdGNoXG4gICAgICAvLyBJbnZhbGlkU3RhdGVFcnJvcjogRmFpbGVkIHRvIHJlYWQgdGhlICdidWZmZXJlZCcgcHJvcGVydHkgZnJvbSAnU291cmNlQnVmZmVyJzpcbiAgICAgIC8vIFRoaXMgU291cmNlQnVmZmVyIGhhcyBiZWVuIHJlbW92ZWQgZnJvbSB0aGUgcGFyZW50IG1lZGlhIHNvdXJjZVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBsZW46IDAsXG4gICAgICBzdGFydDogcG9zLFxuICAgICAgZW5kOiBwb3MsXG4gICAgICBuZXh0U3RhcnQ6IHVuZGVmaW5lZFxuICAgIH07XG4gIH07XG5cbiAgQnVmZmVySGVscGVyLmJ1ZmZlcmVkSW5mbyA9IGZ1bmN0aW9uIGJ1ZmZlcmVkSW5mbyhidWZmZXJlZCwgcG9zLCBtYXhIb2xlRHVyYXRpb24pIHtcbiAgICAvLyBzb3J0IG9uIGJ1ZmZlci5zdGFydC9zbWFsbGVyIGVuZCAoSUUgZG9lcyBub3QgYWx3YXlzIHJldHVybiBzb3J0ZWQgYnVmZmVyZWQgcmFuZ2UpXG4gICAgYnVmZmVyZWQuc29ydChmdW5jdGlvbiAoYSwgYikge1xuICAgICAgdmFyIGRpZmYgPSBhLnN0YXJ0IC0gYi5zdGFydDtcblxuICAgICAgaWYgKGRpZmYpIHtcbiAgICAgICAgcmV0dXJuIGRpZmY7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gYi5lbmQgLSBhLmVuZDtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB2YXIgYnVmZmVyZWQyID0gW107XG5cbiAgICBpZiAobWF4SG9sZUR1cmF0aW9uKSB7XG4gICAgICAvLyB0aGVyZSBtaWdodCBiZSBzb21lIHNtYWxsIGhvbGVzIGJldHdlZW4gYnVmZmVyIHRpbWUgcmFuZ2VcbiAgICAgIC8vIGNvbnNpZGVyIHRoYXQgaG9sZXMgc21hbGxlciB0aGFuIG1heEhvbGVEdXJhdGlvbiBhcmUgaXJyZWxldmFudCBhbmQgYnVpbGQgYW5vdGhlclxuICAgICAgLy8gYnVmZmVyIHRpbWUgcmFuZ2UgcmVwcmVzZW50YXRpb25zIHRoYXQgZGlzY2FyZHMgdGhvc2UgaG9sZXNcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnVmZmVyZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIGJ1ZjJsZW4gPSBidWZmZXJlZDIubGVuZ3RoO1xuXG4gICAgICAgIGlmIChidWYybGVuKSB7XG4gICAgICAgICAgdmFyIGJ1ZjJlbmQgPSBidWZmZXJlZDJbYnVmMmxlbiAtIDFdLmVuZDsgLy8gaWYgc21hbGwgaG9sZSAodmFsdWUgYmV0d2VlbiAwIG9yIG1heEhvbGVEdXJhdGlvbiApIG9yIG92ZXJsYXBwaW5nIChuZWdhdGl2ZSlcblxuICAgICAgICAgIGlmIChidWZmZXJlZFtpXS5zdGFydCAtIGJ1ZjJlbmQgPCBtYXhIb2xlRHVyYXRpb24pIHtcbiAgICAgICAgICAgIC8vIG1lcmdlIG92ZXJsYXBwaW5nIHRpbWUgcmFuZ2VzXG4gICAgICAgICAgICAvLyB1cGRhdGUgbGFzdFJhbmdlLmVuZCBvbmx5IGlmIHNtYWxsZXIgdGhhbiBpdGVtLmVuZFxuICAgICAgICAgICAgLy8gZS5nLiAgWyAxLCAxNV0gd2l0aCAgWyAyLDhdID0+IFsgMSwxNV0gKG5vIG5lZWQgdG8gbW9kaWZ5IGxhc3RSYW5nZS5lbmQpXG4gICAgICAgICAgICAvLyB3aGVyZWFzIFsgMSwgOF0gd2l0aCAgWyAyLDE1XSA9PiBbIDEsMTVdICggbGFzdFJhbmdlIHNob3VsZCBzd2l0Y2ggZnJvbSBbMSw4XSB0byBbMSwxNV0pXG4gICAgICAgICAgICBpZiAoYnVmZmVyZWRbaV0uZW5kID4gYnVmMmVuZCkge1xuICAgICAgICAgICAgICBidWZmZXJlZDJbYnVmMmxlbiAtIDFdLmVuZCA9IGJ1ZmZlcmVkW2ldLmVuZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gYmlnIGhvbGVcbiAgICAgICAgICAgIGJ1ZmZlcmVkMi5wdXNoKGJ1ZmZlcmVkW2ldKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gZmlyc3QgdmFsdWVcbiAgICAgICAgICBidWZmZXJlZDIucHVzaChidWZmZXJlZFtpXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgYnVmZmVyZWQyID0gYnVmZmVyZWQ7XG4gICAgfVxuXG4gICAgdmFyIGJ1ZmZlckxlbiA9IDA7IC8vIGJ1ZmZlclN0YXJ0TmV4dCBjYW4gcG9zc2libHkgYmUgdW5kZWZpbmVkIGJhc2VkIG9uIHRoZSBjb25kaXRpb25hbCBsb2dpYyBiZWxvd1xuXG4gICAgdmFyIGJ1ZmZlclN0YXJ0TmV4dDsgLy8gYnVmZmVyU3RhcnQgYW5kIGJ1ZmZlckVuZCBhcmUgYnVmZmVyIGJvdW5kYXJpZXMgYXJvdW5kIGN1cnJlbnQgdmlkZW8gcG9zaXRpb25cblxuICAgIHZhciBidWZmZXJTdGFydCA9IHBvcztcbiAgICB2YXIgYnVmZmVyRW5kID0gcG9zO1xuXG4gICAgZm9yICh2YXIgX2kgPSAwOyBfaSA8IGJ1ZmZlcmVkMi5sZW5ndGg7IF9pKyspIHtcbiAgICAgIHZhciBzdGFydCA9IGJ1ZmZlcmVkMltfaV0uc3RhcnQsXG4gICAgICAgICAgZW5kID0gYnVmZmVyZWQyW19pXS5lbmQ7IC8vIGxvZ2dlci5sb2coJ2J1ZiBzdGFydC9lbmQ6JyArIGJ1ZmZlcmVkLnN0YXJ0KGkpICsgJy8nICsgYnVmZmVyZWQuZW5kKGkpKTtcblxuICAgICAgaWYgKHBvcyArIG1heEhvbGVEdXJhdGlvbiA+PSBzdGFydCAmJiBwb3MgPCBlbmQpIHtcbiAgICAgICAgLy8gcGxheSBwb3NpdGlvbiBpcyBpbnNpZGUgdGhpcyBidWZmZXIgVGltZVJhbmdlLCByZXRyaWV2ZSBlbmQgb2YgYnVmZmVyIHBvc2l0aW9uIGFuZCBidWZmZXIgbGVuZ3RoXG4gICAgICAgIGJ1ZmZlclN0YXJ0ID0gc3RhcnQ7XG4gICAgICAgIGJ1ZmZlckVuZCA9IGVuZDtcbiAgICAgICAgYnVmZmVyTGVuID0gYnVmZmVyRW5kIC0gcG9zO1xuICAgICAgfSBlbHNlIGlmIChwb3MgKyBtYXhIb2xlRHVyYXRpb24gPCBzdGFydCkge1xuICAgICAgICBidWZmZXJTdGFydE5leHQgPSBzdGFydDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxlbjogYnVmZmVyTGVuLFxuICAgICAgc3RhcnQ6IGJ1ZmZlclN0YXJ0LFxuICAgICAgZW5kOiBidWZmZXJFbmQsXG4gICAgICBuZXh0U3RhcnQ6IGJ1ZmZlclN0YXJ0TmV4dFxuICAgIH07XG4gIH07XG5cbiAgcmV0dXJuIEJ1ZmZlckhlbHBlcjtcbn0oKTtcbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9ub2RlX21vZHVsZXMvZXZlbnRlbWl0dGVyMy9pbmRleC5qc1xudmFyIGV2ZW50ZW1pdHRlcjMgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9ub2RlX21vZHVsZXMvZXZlbnRlbWl0dGVyMy9pbmRleC5qc1wiKTtcblxuLy8gRVhURVJOQUwgTU9EVUxFOiAuL25vZGVfbW9kdWxlcy93ZWJ3b3JraWZ5LXdlYnBhY2svaW5kZXguanNcbnZhciB3ZWJ3b3JraWZ5X3dlYnBhY2sgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9ub2RlX21vZHVsZXMvd2Vid29ya2lmeS13ZWJwYWNrL2luZGV4LmpzXCIpO1xuXG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2RlbXV4L2RlbXV4ZXItaW5saW5lLmpzICsgMTIgbW9kdWxlc1xudmFyIGRlbXV4ZXJfaW5saW5lID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL2RlbXV4L2RlbXV4ZXItaW5saW5lLmpzXCIpO1xuXG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy91dGlscy9tZWRpYXNvdXJjZS1oZWxwZXIudHNcbi8qKlxuICogTWVkaWFTb3VyY2UgaGVscGVyXG4gKi9cbmZ1bmN0aW9uIGdldE1lZGlhU291cmNlKCkge1xuICByZXR1cm4gd2luZG93Lk1lZGlhU291cmNlIHx8IHdpbmRvdy5XZWJLaXRNZWRpYVNvdXJjZTtcbn1cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvdXRpbHMvZ2V0LXNlbGYtc2NvcGUuanNcbnZhciBnZXRfc2VsZl9zY29wZSA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy91dGlscy9nZXQtc2VsZi1zY29wZS5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvb2JzZXJ2ZXIudHNcbmZ1bmN0aW9uIG9ic2VydmVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cblxuLyoqXG4gKiBTaW1wbGUgYWRhcHRlciBzdWItY2xhc3Mgb2YgTm9kZWpzLWxpa2UgRXZlbnRFbWl0dGVyLlxuICovXG5cbnZhciBPYnNlcnZlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50RW1pdHRlcikge1xuICBvYnNlcnZlcl9pbmhlcml0c0xvb3NlKE9ic2VydmVyLCBfRXZlbnRFbWl0dGVyKTtcblxuICBmdW5jdGlvbiBPYnNlcnZlcigpIHtcbiAgICByZXR1cm4gX0V2ZW50RW1pdHRlci5hcHBseSh0aGlzLCBhcmd1bWVudHMpIHx8IHRoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gT2JzZXJ2ZXIucHJvdG90eXBlO1xuXG4gIC8qKlxuICAgKiBXZSBzaW1wbHkgd2FudCB0byBwYXNzIGFsb25nIHRoZSBldmVudC1uYW1lIGl0c2VsZlxuICAgKiBpbiBldmVyeSBjYWxsIHRvIGEgaGFuZGxlciwgd2hpY2ggaXMgdGhlIHB1cnBvc2Ugb2Ygb3VyIGB0cmlnZ2VyYCBtZXRob2RcbiAgICogZXh0ZW5kaW5nIHRoZSBzdGFuZGFyZCBBUEkuXG4gICAqL1xuICBfcHJvdG8udHJpZ2dlciA9IGZ1bmN0aW9uIHRyaWdnZXIoZXZlbnQpIHtcbiAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgZGF0YSA9IG5ldyBBcnJheShfbGVuID4gMSA/IF9sZW4gLSAxIDogMCksIF9rZXkgPSAxOyBfa2V5IDwgX2xlbjsgX2tleSsrKSB7XG4gICAgICBkYXRhW19rZXkgLSAxXSA9IGFyZ3VtZW50c1tfa2V5XTtcbiAgICB9XG5cbiAgICB0aGlzLmVtaXQuYXBwbHkodGhpcywgW2V2ZW50LCBldmVudF0uY29uY2F0KGRhdGEpKTtcbiAgfTtcblxuICByZXR1cm4gT2JzZXJ2ZXI7XG59KGV2ZW50ZW1pdHRlcjNbXCJFdmVudEVtaXR0ZXJcIl0pO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvZGVtdXgvZGVtdXhlci5qc1xuXG5cblxuXG5cblxuXG5cblxuIC8vIHNlZSBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMTEyMzcyNTkvNTg5NDkzXG5cbnZhciBnbG9iYWwgPSBPYmplY3QoZ2V0X3NlbGZfc2NvcGVbXCJnZXRTZWxmU2NvcGVcIl0pKCk7IC8vIHNhZmVndWFyZCBmb3IgY29kZSB0aGF0IG1pZ2h0IHJ1biBib3RoIG9uIHdvcmtlciBhbmQgbWFpbiB0aHJlYWRcblxudmFyIGRlbXV4ZXJfTWVkaWFTb3VyY2UgPSBnZXRNZWRpYVNvdXJjZSgpIHx8IHtcbiAgaXNUeXBlU3VwcG9ydGVkOiBmdW5jdGlvbiBpc1R5cGVTdXBwb3J0ZWQoKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59O1xuXG52YXIgZGVtdXhlcl9EZW11eGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRGVtdXhlcihobHMsIGlkKSB7XG4gICAgdmFyIF90aGlzID0gdGhpcztcblxuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIHRoaXMuaWQgPSBpZDtcbiAgICB2YXIgb2JzZXJ2ZXIgPSB0aGlzLm9ic2VydmVyID0gbmV3IE9ic2VydmVyKCk7XG4gICAgdmFyIGNvbmZpZyA9IGhscy5jb25maWc7XG5cbiAgICB2YXIgZm9yd2FyZE1lc3NhZ2UgPSBmdW5jdGlvbiBmb3J3YXJkTWVzc2FnZShldiwgZGF0YSkge1xuICAgICAgZGF0YSA9IGRhdGEgfHwge307XG4gICAgICBkYXRhLmZyYWcgPSBfdGhpcy5mcmFnO1xuICAgICAgZGF0YS5pZCA9IF90aGlzLmlkO1xuICAgICAgaGxzLnRyaWdnZXIoZXYsIGRhdGEpO1xuICAgIH07IC8vIGZvcndhcmQgZXZlbnRzIHRvIG1haW4gdGhyZWFkXG5cblxuICAgIG9ic2VydmVyLm9uKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19ERUNSWVBURUQsIGZvcndhcmRNZXNzYWdlKTtcbiAgICBvYnNlcnZlci5vbihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19JTklUX1NFR01FTlQsIGZvcndhcmRNZXNzYWdlKTtcbiAgICBvYnNlcnZlci5vbihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19EQVRBLCBmb3J3YXJkTWVzc2FnZSk7XG4gICAgb2JzZXJ2ZXIub24oZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNFRCwgZm9yd2FyZE1lc3NhZ2UpO1xuICAgIG9ic2VydmVyLm9uKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIGZvcndhcmRNZXNzYWdlKTtcbiAgICBvYnNlcnZlci5vbihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19NRVRBREFUQSwgZm9yd2FyZE1lc3NhZ2UpO1xuICAgIG9ic2VydmVyLm9uKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX1VTRVJEQVRBLCBmb3J3YXJkTWVzc2FnZSk7XG4gICAgb2JzZXJ2ZXIub24oZXZlbnRzW1wiZGVmYXVsdFwiXS5JTklUX1BUU19GT1VORCwgZm9yd2FyZE1lc3NhZ2UpO1xuICAgIHZhciB0eXBlU3VwcG9ydGVkID0ge1xuICAgICAgbXA0OiBkZW11eGVyX01lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCgndmlkZW8vbXA0JyksXG4gICAgICBtcGVnOiBkZW11eGVyX01lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCgnYXVkaW8vbXBlZycpLFxuICAgICAgbXAzOiBkZW11eGVyX01lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCgnYXVkaW8vbXA0OyBjb2RlY3M9XCJtcDNcIicpXG4gICAgfTsgLy8gbmF2aWdhdG9yLnZlbmRvciBpcyBub3QgYWx3YXlzIGF2YWlsYWJsZSBpbiBXZWIgV29ya2VyXG4gICAgLy8gcmVmZXIgdG8gaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1dvcmtlckdsb2JhbFNjb3BlL25hdmlnYXRvclxuXG4gICAgdmFyIHZlbmRvciA9IG5hdmlnYXRvci52ZW5kb3I7XG5cbiAgICBpZiAoY29uZmlnLmVuYWJsZVdvcmtlciAmJiB0eXBlb2YgV29ya2VyICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnZGVtdXhpbmcgaW4gd2Vid29ya2VyJyk7XG4gICAgICB2YXIgdztcblxuICAgICAgdHJ5IHtcbiAgICAgICAgdyA9IHRoaXMudyA9IHdlYndvcmtpZnlfd2VicGFjaygvKnJlcXVpcmUucmVzb2x2ZSovKC8qISAuLi9kZW11eC9kZW11eGVyLXdvcmtlci5qcyAqLyBcIi4vc3JjL2RlbXV4L2RlbXV4ZXItd29ya2VyLmpzXCIpKTtcbiAgICAgICAgdGhpcy5vbndtc2cgPSB0aGlzLm9uV29ya2VyTWVzc2FnZS5iaW5kKHRoaXMpO1xuICAgICAgICB3LmFkZEV2ZW50TGlzdGVuZXIoJ21lc3NhZ2UnLCB0aGlzLm9ud21zZyk7XG5cbiAgICAgICAgdy5vbmVycm9yID0gZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5PVEhFUl9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5JTlRFUk5BTF9FWENFUFRJT04sXG4gICAgICAgICAgICBmYXRhbDogdHJ1ZSxcbiAgICAgICAgICAgIGV2ZW50OiAnZGVtdXhlcldvcmtlcicsXG4gICAgICAgICAgICBlcnI6IHtcbiAgICAgICAgICAgICAgbWVzc2FnZTogZXZlbnQubWVzc2FnZSArICcgKCcgKyBldmVudC5maWxlbmFtZSArICc6JyArIGV2ZW50LmxpbmVubyArICcpJ1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuXG4gICAgICAgIHcucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgIGNtZDogJ2luaXQnLFxuICAgICAgICAgIHR5cGVTdXBwb3J0ZWQ6IHR5cGVTdXBwb3J0ZWQsXG4gICAgICAgICAgdmVuZG9yOiB2ZW5kb3IsXG4gICAgICAgICAgaWQ6IGlkLFxuICAgICAgICAgIGNvbmZpZzogSlNPTi5zdHJpbmdpZnkoY29uZmlnKVxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignRXJyb3IgaW4gd29ya2VyOicsIGVycik7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignRXJyb3Igd2hpbGUgaW5pdGlhbGl6aW5nIERlbXV4ZXJXb3JrZXIsIGZhbGxiYWNrIG9uIERlbXV4ZXJJbmxpbmUnKTtcblxuICAgICAgICBpZiAodykge1xuICAgICAgICAgIC8vIHJldm9rZSB0aGUgT2JqZWN0IFVSTCB0aGF0IHdhcyB1c2VkIHRvIGNyZWF0ZSBkZW11eGVyIHdvcmtlciwgc28gYXMgbm90IHRvIGxlYWsgaXRcbiAgICAgICAgICBnbG9iYWwuVVJMLnJldm9rZU9iamVjdFVSTCh3Lm9iamVjdFVSTCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmRlbXV4ZXIgPSBuZXcgZGVtdXhlcl9pbmxpbmVbXCJkZWZhdWx0XCJdKG9ic2VydmVyLCB0eXBlU3VwcG9ydGVkLCBjb25maWcsIHZlbmRvcik7XG4gICAgICAgIHRoaXMudyA9IHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5kZW11eGVyID0gbmV3IGRlbXV4ZXJfaW5saW5lW1wiZGVmYXVsdFwiXShvYnNlcnZlciwgdHlwZVN1cHBvcnRlZCwgY29uZmlnLCB2ZW5kb3IpO1xuICAgIH1cbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBEZW11eGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gICAgdmFyIHcgPSB0aGlzLnc7XG5cbiAgICBpZiAodykge1xuICAgICAgdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgdGhpcy5vbndtc2cpO1xuICAgICAgdy50ZXJtaW5hdGUoKTtcbiAgICAgIHRoaXMudyA9IG51bGw7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBkZW11eGVyID0gdGhpcy5kZW11eGVyO1xuXG4gICAgICBpZiAoZGVtdXhlcikge1xuICAgICAgICBkZW11eGVyLmRlc3Ryb3koKTtcbiAgICAgICAgdGhpcy5kZW11eGVyID0gbnVsbDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgb2JzZXJ2ZXIgPSB0aGlzLm9ic2VydmVyO1xuXG4gICAgaWYgKG9ic2VydmVyKSB7XG4gICAgICBvYnNlcnZlci5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcbiAgICAgIHRoaXMub2JzZXJ2ZXIgPSBudWxsO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ucHVzaCA9IGZ1bmN0aW9uIHB1c2goZGF0YSwgaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGZyYWcsIGR1cmF0aW9uLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGRlZmF1bHRJbml0UFRTKSB7XG4gICAgdmFyIHcgPSB0aGlzLnc7XG4gICAgdmFyIHRpbWVPZmZzZXQgPSBPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGZyYWcuc3RhcnRQVFMpID8gZnJhZy5zdGFydFBUUyA6IGZyYWcuc3RhcnQ7XG4gICAgdmFyIGRlY3J5cHRkYXRhID0gZnJhZy5kZWNyeXB0ZGF0YTtcbiAgICB2YXIgbGFzdEZyYWcgPSB0aGlzLmZyYWc7XG4gICAgdmFyIGRpc2NvbnRpbnVpdHkgPSAhKGxhc3RGcmFnICYmIGZyYWcuY2MgPT09IGxhc3RGcmFnLmNjKTtcbiAgICB2YXIgdHJhY2tTd2l0Y2ggPSAhKGxhc3RGcmFnICYmIGZyYWcubGV2ZWwgPT09IGxhc3RGcmFnLmxldmVsKTtcbiAgICB2YXIgbmV4dFNOID0gbGFzdEZyYWcgJiYgZnJhZy5zbiA9PT0gbGFzdEZyYWcuc24gKyAxO1xuICAgIHZhciBjb250aWd1b3VzID0gIXRyYWNrU3dpdGNoICYmIG5leHRTTjtcblxuICAgIGlmIChkaXNjb250aW51aXR5KSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKHRoaXMuaWQgKyBcIjpkaXNjb250aW51aXR5IGRldGVjdGVkXCIpO1xuICAgIH1cblxuICAgIGlmICh0cmFja1N3aXRjaCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyh0aGlzLmlkICsgXCI6c3dpdGNoIGRldGVjdGVkXCIpO1xuICAgIH1cblxuICAgIHRoaXMuZnJhZyA9IGZyYWc7XG5cbiAgICBpZiAodykge1xuICAgICAgLy8gcG9zdCBmcmFnbWVudCBwYXlsb2FkIGFzIHRyYW5zZmVyYWJsZSBvYmplY3RzIGZvciBBcnJheUJ1ZmZlciAobm8gY29weSlcbiAgICAgIHcucG9zdE1lc3NhZ2Uoe1xuICAgICAgICBjbWQ6ICdkZW11eCcsXG4gICAgICAgIGRhdGE6IGRhdGEsXG4gICAgICAgIGRlY3J5cHRkYXRhOiBkZWNyeXB0ZGF0YSxcbiAgICAgICAgaW5pdFNlZ21lbnQ6IGluaXRTZWdtZW50LFxuICAgICAgICBhdWRpb0NvZGVjOiBhdWRpb0NvZGVjLFxuICAgICAgICB2aWRlb0NvZGVjOiB2aWRlb0NvZGVjLFxuICAgICAgICB0aW1lT2Zmc2V0OiB0aW1lT2Zmc2V0LFxuICAgICAgICBkaXNjb250aW51aXR5OiBkaXNjb250aW51aXR5LFxuICAgICAgICB0cmFja1N3aXRjaDogdHJhY2tTd2l0Y2gsXG4gICAgICAgIGNvbnRpZ3VvdXM6IGNvbnRpZ3VvdXMsXG4gICAgICAgIGR1cmF0aW9uOiBkdXJhdGlvbixcbiAgICAgICAgYWNjdXJhdGVUaW1lT2Zmc2V0OiBhY2N1cmF0ZVRpbWVPZmZzZXQsXG4gICAgICAgIGRlZmF1bHRJbml0UFRTOiBkZWZhdWx0SW5pdFBUU1xuICAgICAgfSwgZGF0YSBpbnN0YW5jZW9mIEFycmF5QnVmZmVyID8gW2RhdGFdIDogW10pO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgZGVtdXhlciA9IHRoaXMuZGVtdXhlcjtcblxuICAgICAgaWYgKGRlbXV4ZXIpIHtcbiAgICAgICAgZGVtdXhlci5wdXNoKGRhdGEsIGRlY3J5cHRkYXRhLCBpbml0U2VnbWVudCwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdGltZU9mZnNldCwgZGlzY29udGludWl0eSwgdHJhY2tTd2l0Y2gsIGNvbnRpZ3VvdXMsIGR1cmF0aW9uLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGRlZmF1bHRJbml0UFRTKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uV29ya2VyTWVzc2FnZSA9IGZ1bmN0aW9uIG9uV29ya2VyTWVzc2FnZShldikge1xuICAgIHZhciBkYXRhID0gZXYuZGF0YSxcbiAgICAgICAgaGxzID0gdGhpcy5obHM7XG5cbiAgICBzd2l0Y2ggKGRhdGEuZXZlbnQpIHtcbiAgICAgIGNhc2UgJ2luaXQnOlxuICAgICAgICAvLyByZXZva2UgdGhlIE9iamVjdCBVUkwgdGhhdCB3YXMgdXNlZCB0byBjcmVhdGUgZGVtdXhlciB3b3JrZXIsIHNvIGFzIG5vdCB0byBsZWFrIGl0XG4gICAgICAgIGdsb2JhbC5VUkwucmV2b2tlT2JqZWN0VVJMKHRoaXMudy5vYmplY3RVUkwpO1xuICAgICAgICBicmVhaztcbiAgICAgIC8vIHNwZWNpYWwgY2FzZSBmb3IgRlJBR19QQVJTSU5HX0RBVEE6IGRhdGExIGFuZCBkYXRhMiBhcmUgdHJhbnNmZXJhYmxlIG9iamVjdHNcblxuICAgICAgY2FzZSBldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19EQVRBOlxuICAgICAgICBkYXRhLmRhdGEuZGF0YTEgPSBuZXcgVWludDhBcnJheShkYXRhLmRhdGExKTtcblxuICAgICAgICBpZiAoZGF0YS5kYXRhMikge1xuICAgICAgICAgIGRhdGEuZGF0YS5kYXRhMiA9IG5ldyBVaW50OEFycmF5KGRhdGEuZGF0YTIpO1xuICAgICAgICB9XG5cbiAgICAgIC8qIGZhbGxzIHRocm91Z2ggKi9cblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgZGF0YS5kYXRhID0gZGF0YS5kYXRhIHx8IHt9O1xuICAgICAgICBkYXRhLmRhdGEuZnJhZyA9IHRoaXMuZnJhZztcbiAgICAgICAgZGF0YS5kYXRhLmlkID0gdGhpcy5pZDtcbiAgICAgICAgaGxzLnRyaWdnZXIoZGF0YS5ldmVudCwgZGF0YS5kYXRhKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9O1xuXG4gIHJldHVybiBEZW11eGVyO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBkZW11eF9kZW11eGVyID0gKGRlbXV4ZXJfRGVtdXhlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9jb250cm9sbGVyL2xldmVsLWhlbHBlci5qc1xuXG5cblxuXG5cbi8qKlxuICogQG1vZHVsZSBMZXZlbEhlbHBlclxuICpcbiAqIFByb3ZpZGluZyBtZXRob2RzIGRlYWxpbmcgd2l0aCBwbGF5bGlzdCBzbGlkaW5nIGFuZCBkcmlmdFxuICpcbiAqIFRPRE86IENyZWF0ZSBhbiBhY3R1YWwgYExldmVsYCBjbGFzcy9tb2RlbCB0aGF0IGRlYWxzIHdpdGggYWxsIHRoaXMgbG9naWMgaW4gYW4gb2JqZWN0LW9yaWVudGVkLW1hbm5lci5cbiAqXG4gKiAqL1xuXG5mdW5jdGlvbiBhZGRHcm91cElkKGxldmVsLCB0eXBlLCBpZCkge1xuICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlICdhdWRpbyc6XG4gICAgICBpZiAoIWxldmVsLmF1ZGlvR3JvdXBJZHMpIHtcbiAgICAgICAgbGV2ZWwuYXVkaW9Hcm91cElkcyA9IFtdO1xuICAgICAgfVxuXG4gICAgICBsZXZlbC5hdWRpb0dyb3VwSWRzLnB1c2goaWQpO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICd0ZXh0JzpcbiAgICAgIGlmICghbGV2ZWwudGV4dEdyb3VwSWRzKSB7XG4gICAgICAgIGxldmVsLnRleHRHcm91cElkcyA9IFtdO1xuICAgICAgfVxuXG4gICAgICBsZXZlbC50ZXh0R3JvdXBJZHMucHVzaChpZCk7XG4gICAgICBicmVhaztcbiAgfVxufVxuZnVuY3Rpb24gdXBkYXRlUFRTKGZyYWdtZW50cywgZnJvbUlkeCwgdG9JZHgpIHtcbiAgdmFyIGZyYWdGcm9tID0gZnJhZ21lbnRzW2Zyb21JZHhdLFxuICAgICAgZnJhZ1RvID0gZnJhZ21lbnRzW3RvSWR4XSxcbiAgICAgIGZyYWdUb1BUUyA9IGZyYWdUby5zdGFydFBUUzsgLy8gaWYgd2Uga25vdyBzdGFydFBUU1t0b0lkeF1cblxuICBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShmcmFnVG9QVFMpKSB7XG4gICAgLy8gdXBkYXRlIGZyYWdtZW50IGR1cmF0aW9uLlxuICAgIC8vIGl0IGhlbHBzIHRvIGZpeCBkcmlmdHMgYmV0d2VlbiBwbGF5bGlzdCByZXBvcnRlZCBkdXJhdGlvbiBhbmQgZnJhZ21lbnQgcmVhbCBkdXJhdGlvblxuICAgIGlmICh0b0lkeCA+IGZyb21JZHgpIHtcbiAgICAgIGZyYWdGcm9tLmR1cmF0aW9uID0gZnJhZ1RvUFRTIC0gZnJhZ0Zyb20uc3RhcnQ7XG5cbiAgICAgIGlmIChmcmFnRnJvbS5kdXJhdGlvbiA8IDApIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJuZWdhdGl2ZSBkdXJhdGlvbiBjb21wdXRlZCBmb3IgZnJhZyBcIiArIGZyYWdGcm9tLnNuICsgXCIsbGV2ZWwgXCIgKyBmcmFnRnJvbS5sZXZlbCArIFwiLCB0aGVyZSBzaG91bGQgYmUgc29tZSBkdXJhdGlvbiBkcmlmdCBiZXR3ZWVuIHBsYXlsaXN0IGFuZCBmcmFnbWVudCFcIik7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGZyYWdUby5kdXJhdGlvbiA9IGZyYWdGcm9tLnN0YXJ0IC0gZnJhZ1RvUFRTO1xuXG4gICAgICBpZiAoZnJhZ1RvLmR1cmF0aW9uIDwgMCkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcIm5lZ2F0aXZlIGR1cmF0aW9uIGNvbXB1dGVkIGZvciBmcmFnIFwiICsgZnJhZ1RvLnNuICsgXCIsbGV2ZWwgXCIgKyBmcmFnVG8ubGV2ZWwgKyBcIiwgdGhlcmUgc2hvdWxkIGJlIHNvbWUgZHVyYXRpb24gZHJpZnQgYmV0d2VlbiBwbGF5bGlzdCBhbmQgZnJhZ21lbnQhXCIpO1xuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyB3ZSBkb250IGtub3cgc3RhcnRQVFNbdG9JZHhdXG4gICAgaWYgKHRvSWR4ID4gZnJvbUlkeCkge1xuICAgICAgdmFyIGNvbnRpZ3VvdXMgPSBmcmFnRnJvbS5jYyA9PT0gZnJhZ1RvLmNjO1xuICAgICAgZnJhZ1RvLnN0YXJ0ID0gZnJhZ0Zyb20uc3RhcnQgKyAoY29udGlndW91cyAmJiBmcmFnRnJvbS5taW5FbmRQVFMgPyBmcmFnRnJvbS5taW5FbmRQVFMgLSBmcmFnRnJvbS5zdGFydCA6IGZyYWdGcm9tLmR1cmF0aW9uKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZnJhZ1RvLnN0YXJ0ID0gTWF0aC5tYXgoZnJhZ0Zyb20uc3RhcnQgLSBmcmFnVG8uZHVyYXRpb24sIDApO1xuICAgIH1cbiAgfVxufVxuZnVuY3Rpb24gdXBkYXRlRnJhZ1BUU0RUUyhkZXRhaWxzLCBmcmFnLCBzdGFydFBUUywgZW5kUFRTLCBzdGFydERUUywgZW5kRFRTKSB7XG4gIC8vIHVwZGF0ZSBmcmFnIFBUUy9EVFNcbiAgdmFyIG1heFN0YXJ0UFRTID0gc3RhcnRQVFM7XG4gIHZhciBtaW5FbmRQVFMgPSBlbmRQVFM7XG5cbiAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoZnJhZy5zdGFydFBUUykpIHtcbiAgICAvLyBkZWx0YSBQVFMgYmV0d2VlbiBhdWRpbyBhbmQgdmlkZW9cbiAgICB2YXIgZGVsdGFQVFMgPSBNYXRoLmFicyhmcmFnLnN0YXJ0UFRTIC0gc3RhcnRQVFMpO1xuXG4gICAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGZyYWcuZGVsdGFQVFMpKSB7XG4gICAgICBmcmFnLmRlbHRhUFRTID0gZGVsdGFQVFM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZyYWcuZGVsdGFQVFMgPSBNYXRoLm1heChkZWx0YVBUUywgZnJhZy5kZWx0YVBUUyk7XG4gICAgfVxuXG4gICAgbWF4U3RhcnRQVFMgPSBNYXRoLm1heChzdGFydFBUUywgZnJhZy5zdGFydFBUUyk7XG4gICAgc3RhcnRQVFMgPSBNYXRoLm1pbihzdGFydFBUUywgZnJhZy5zdGFydFBUUyk7XG4gICAgbWluRW5kUFRTID0gTWF0aC5taW4oZW5kUFRTLCBmcmFnLmVuZFBUUyk7XG4gICAgZW5kUFRTID0gTWF0aC5tYXgoZW5kUFRTLCBmcmFnLmVuZFBUUyk7XG4gICAgc3RhcnREVFMgPSBNYXRoLm1pbihzdGFydERUUywgZnJhZy5zdGFydERUUyk7XG4gICAgZW5kRFRTID0gTWF0aC5tYXgoZW5kRFRTLCBmcmFnLmVuZERUUyk7XG4gIH1cblxuICB2YXIgZHJpZnQgPSBzdGFydFBUUyAtIGZyYWcuc3RhcnQ7XG4gIGZyYWcuc3RhcnQgPSBmcmFnLnN0YXJ0UFRTID0gc3RhcnRQVFM7XG4gIGZyYWcubWF4U3RhcnRQVFMgPSBtYXhTdGFydFBUUztcbiAgZnJhZy5lbmRQVFMgPSBlbmRQVFM7XG4gIGZyYWcubWluRW5kUFRTID0gbWluRW5kUFRTO1xuICBmcmFnLnN0YXJ0RFRTID0gc3RhcnREVFM7XG4gIGZyYWcuZW5kRFRTID0gZW5kRFRTO1xuICBmcmFnLmR1cmF0aW9uID0gZW5kUFRTIC0gc3RhcnRQVFM7XG4gIHZhciBzbiA9IGZyYWcuc247IC8vIGV4aXQgaWYgc24gb3V0IG9mIHJhbmdlXG5cbiAgaWYgKCFkZXRhaWxzIHx8IHNuIDwgZGV0YWlscy5zdGFydFNOIHx8IHNuID4gZGV0YWlscy5lbmRTTikge1xuICAgIHJldHVybiAwO1xuICB9XG5cbiAgdmFyIGZyYWdJZHgsIGZyYWdtZW50cywgaTtcbiAgZnJhZ0lkeCA9IHNuIC0gZGV0YWlscy5zdGFydFNOO1xuICBmcmFnbWVudHMgPSBkZXRhaWxzLmZyYWdtZW50czsgLy8gdXBkYXRlIGZyYWcgcmVmZXJlbmNlIGluIGZyYWdtZW50cyBhcnJheVxuICAvLyByYXRpb25hbGUgaXMgdGhhdCBmcmFnbWVudHMgYXJyYXkgbWlnaHQgbm90IGNvbnRhaW4gdGhpcyBmcmFnIG9iamVjdC5cbiAgLy8gdGhpcyB3aWxsIGhhcHBlbiBpZiBwbGF5bGlzdCBoYXMgYmVlbiByZWZyZXNoZWQgYmV0d2VlbiBmcmFnIGxvYWRpbmcgYW5kIGNhbGwgdG8gdXBkYXRlRnJhZ1BUU0RUUygpXG4gIC8vIGlmIHdlIGRvbid0IHVwZGF0ZSBmcmFnLCB3ZSB3b24ndCBiZSBhYmxlIHRvIHByb3BhZ2F0ZSBQVFMgaW5mbyBvbiB0aGUgcGxheWxpc3RcbiAgLy8gcmVzdWx0aW5nIGluIGludmFsaWQgc2xpZGluZyBjb21wdXRhdGlvblxuXG4gIGZyYWdtZW50c1tmcmFnSWR4XSA9IGZyYWc7IC8vIGFkanVzdCBmcmFnbWVudCBQVFMvZHVyYXRpb24gZnJvbSBzZXFudW0tMSB0byBmcmFnIDBcblxuICBmb3IgKGkgPSBmcmFnSWR4OyBpID4gMDsgaS0tKSB7XG4gICAgdXBkYXRlUFRTKGZyYWdtZW50cywgaSwgaSAtIDEpO1xuICB9IC8vIGFkanVzdCBmcmFnbWVudCBQVFMvZHVyYXRpb24gZnJvbSBzZXFudW0gdG8gbGFzdCBmcmFnXG5cblxuICBmb3IgKGkgPSBmcmFnSWR4OyBpIDwgZnJhZ21lbnRzLmxlbmd0aCAtIDE7IGkrKykge1xuICAgIHVwZGF0ZVBUUyhmcmFnbWVudHMsIGksIGkgKyAxKTtcbiAgfVxuXG4gIGRldGFpbHMuUFRTS25vd24gPSB0cnVlO1xuICByZXR1cm4gZHJpZnQ7XG59XG5mdW5jdGlvbiBtZXJnZURldGFpbHMob2xkRGV0YWlscywgbmV3RGV0YWlscykge1xuICAvLyBwb3RlbnRpYWxseSByZXRyaWV2ZSBjYWNoZWQgaW5pdHNlZ21lbnRcbiAgaWYgKG5ld0RldGFpbHMuaW5pdFNlZ21lbnQgJiYgb2xkRGV0YWlscy5pbml0U2VnbWVudCkge1xuICAgIG5ld0RldGFpbHMuaW5pdFNlZ21lbnQgPSBvbGREZXRhaWxzLmluaXRTZWdtZW50O1xuICB9IC8vIGNoZWNrIGlmIG9sZC9uZXcgcGxheWxpc3RzIGhhdmUgZnJhZ21lbnRzIGluIGNvbW1vblxuICAvLyBsb29wIHRocm91Z2ggb3ZlcmxhcHBpbmcgU04gYW5kIHVwZGF0ZSBzdGFydFBUUyAsIGNjLCBhbmQgZHVyYXRpb24gaWYgYW55IGZvdW5kXG5cblxuICB2YXIgY2NPZmZzZXQgPSAwO1xuICB2YXIgUFRTRnJhZztcbiAgbWFwRnJhZ21lbnRJbnRlcnNlY3Rpb24ob2xkRGV0YWlscywgbmV3RGV0YWlscywgZnVuY3Rpb24gKG9sZEZyYWcsIG5ld0ZyYWcpIHtcbiAgICBjY09mZnNldCA9IG9sZEZyYWcuY2MgLSBuZXdGcmFnLmNjO1xuXG4gICAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkob2xkRnJhZy5zdGFydFBUUykpIHtcbiAgICAgIG5ld0ZyYWcuc3RhcnQgPSBuZXdGcmFnLnN0YXJ0UFRTID0gb2xkRnJhZy5zdGFydFBUUztcbiAgICAgIG5ld0ZyYWcuZW5kUFRTID0gb2xkRnJhZy5lbmRQVFM7XG4gICAgICBuZXdGcmFnLmR1cmF0aW9uID0gb2xkRnJhZy5kdXJhdGlvbjtcbiAgICAgIG5ld0ZyYWcuYmFja3RyYWNrZWQgPSBvbGRGcmFnLmJhY2t0cmFja2VkO1xuICAgICAgbmV3RnJhZy5kcm9wcGVkID0gb2xkRnJhZy5kcm9wcGVkO1xuICAgICAgUFRTRnJhZyA9IG5ld0ZyYWc7XG4gICAgfSAvLyBQVFMgaXMga25vd24gd2hlbiB0aGVyZSBhcmUgb3ZlcmxhcHBpbmcgc2VnbWVudHNcblxuXG4gICAgbmV3RGV0YWlscy5QVFNLbm93biA9IHRydWU7XG4gIH0pO1xuXG4gIGlmICghbmV3RGV0YWlscy5QVFNLbm93bikge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGlmIChjY09mZnNldCkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ2Rpc2NvbnRpbnVpdHkgc2xpZGluZyBmcm9tIHBsYXlsaXN0LCB0YWtlIGRyaWZ0IGludG8gYWNjb3VudCcpO1xuICAgIHZhciBuZXdGcmFnbWVudHMgPSBuZXdEZXRhaWxzLmZyYWdtZW50cztcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbmV3RnJhZ21lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBuZXdGcmFnbWVudHNbaV0uY2MgKz0gY2NPZmZzZXQ7XG4gICAgfVxuICB9IC8vIGlmIGF0IGxlYXN0IG9uZSBmcmFnbWVudCBjb250YWlucyBQVFMgaW5mbywgcmVjb21wdXRlIFBUUyBpbmZvcm1hdGlvbiBmb3IgYWxsIGZyYWdtZW50c1xuXG5cbiAgaWYgKFBUU0ZyYWcpIHtcbiAgICB1cGRhdGVGcmFnUFRTRFRTKG5ld0RldGFpbHMsIFBUU0ZyYWcsIFBUU0ZyYWcuc3RhcnRQVFMsIFBUU0ZyYWcuZW5kUFRTLCBQVFNGcmFnLnN0YXJ0RFRTLCBQVFNGcmFnLmVuZERUUyk7XG4gIH0gZWxzZSB7XG4gICAgLy8gZW5zdXJlIHRoYXQgZGVsdGEgaXMgd2l0aGluIG9sZEZyYWdtZW50cyByYW5nZVxuICAgIC8vIGFsc28gYWRqdXN0IHNsaWRpbmcgaW4gY2FzZSBkZWx0YSBpcyAwICh3ZSBjb3VsZCBoYXZlIG9sZD1bNTAtNjBdIGFuZCBuZXc9b2xkPVs1MC02MV0pXG4gICAgLy8gaW4gdGhhdCBjYXNlIHdlIGFsc28gbmVlZCB0byBhZGp1c3Qgc3RhcnQgb2Zmc2V0IG9mIGFsbCBmcmFnbWVudHNcbiAgICBhZGp1c3RTbGlkaW5nKG9sZERldGFpbHMsIG5ld0RldGFpbHMpO1xuICB9IC8vIGlmIHdlIGFyZSBoZXJlLCBpdCBtZWFucyB3ZSBoYXZlIGZyYWdtZW50cyBvdmVybGFwcGluZyBiZXR3ZWVuXG4gIC8vIG9sZCBhbmQgbmV3IGxldmVsLiByZWxpYWJsZSBQVFMgaW5mbyBpcyB0aHVzIHJlbHlpbmcgb24gb2xkIGxldmVsXG5cblxuICBuZXdEZXRhaWxzLlBUU0tub3duID0gb2xkRGV0YWlscy5QVFNLbm93bjtcbn1cbmZ1bmN0aW9uIG1lcmdlU3VidGl0bGVQbGF5bGlzdHMob2xkUGxheWxpc3QsIG5ld1BsYXlsaXN0LCByZWZlcmVuY2VTdGFydCkge1xuICBpZiAocmVmZXJlbmNlU3RhcnQgPT09IHZvaWQgMCkge1xuICAgIHJlZmVyZW5jZVN0YXJ0ID0gMDtcbiAgfVxuXG4gIHZhciBsYXN0SW5kZXggPSAtMTtcbiAgbWFwRnJhZ21lbnRJbnRlcnNlY3Rpb24ob2xkUGxheWxpc3QsIG5ld1BsYXlsaXN0LCBmdW5jdGlvbiAob2xkRnJhZywgbmV3RnJhZywgaW5kZXgpIHtcbiAgICBuZXdGcmFnLnN0YXJ0ID0gb2xkRnJhZy5zdGFydDtcbiAgICBsYXN0SW5kZXggPSBpbmRleDtcbiAgfSk7XG4gIHZhciBmcmFncyA9IG5ld1BsYXlsaXN0LmZyYWdtZW50cztcblxuICBpZiAobGFzdEluZGV4IDwgMCkge1xuICAgIGZyYWdzLmZvckVhY2goZnVuY3Rpb24gKGZyYWcpIHtcbiAgICAgIGZyYWcuc3RhcnQgKz0gcmVmZXJlbmNlU3RhcnQ7XG4gICAgfSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgZm9yICh2YXIgaSA9IGxhc3RJbmRleCArIDE7IGkgPCBmcmFncy5sZW5ndGg7IGkrKykge1xuICAgIGZyYWdzW2ldLnN0YXJ0ID0gZnJhZ3NbaSAtIDFdLnN0YXJ0ICsgZnJhZ3NbaSAtIDFdLmR1cmF0aW9uO1xuICB9XG59XG5mdW5jdGlvbiBtYXBGcmFnbWVudEludGVyc2VjdGlvbihvbGRQbGF5bGlzdCwgbmV3UGxheWxpc3QsIGludGVyc2VjdGlvbkZuKSB7XG4gIGlmICghb2xkUGxheWxpc3QgfHwgIW5ld1BsYXlsaXN0KSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdmFyIHN0YXJ0ID0gTWF0aC5tYXgob2xkUGxheWxpc3Quc3RhcnRTTiwgbmV3UGxheWxpc3Quc3RhcnRTTikgLSBuZXdQbGF5bGlzdC5zdGFydFNOO1xuICB2YXIgZW5kID0gTWF0aC5taW4ob2xkUGxheWxpc3QuZW5kU04sIG5ld1BsYXlsaXN0LmVuZFNOKSAtIG5ld1BsYXlsaXN0LnN0YXJ0U047XG4gIHZhciBkZWx0YSA9IG5ld1BsYXlsaXN0LnN0YXJ0U04gLSBvbGRQbGF5bGlzdC5zdGFydFNOO1xuXG4gIGZvciAodmFyIGkgPSBzdGFydDsgaSA8PSBlbmQ7IGkrKykge1xuICAgIHZhciBvbGRGcmFnID0gb2xkUGxheWxpc3QuZnJhZ21lbnRzW2RlbHRhICsgaV07XG4gICAgdmFyIG5ld0ZyYWcgPSBuZXdQbGF5bGlzdC5mcmFnbWVudHNbaV07XG5cbiAgICBpZiAoIW9sZEZyYWcgfHwgIW5ld0ZyYWcpIHtcbiAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGludGVyc2VjdGlvbkZuKG9sZEZyYWcsIG5ld0ZyYWcsIGkpO1xuICB9XG59XG5mdW5jdGlvbiBhZGp1c3RTbGlkaW5nKG9sZFBsYXlsaXN0LCBuZXdQbGF5bGlzdCkge1xuICB2YXIgZGVsdGEgPSBuZXdQbGF5bGlzdC5zdGFydFNOIC0gb2xkUGxheWxpc3Quc3RhcnRTTjtcbiAgdmFyIG9sZEZyYWdtZW50cyA9IG9sZFBsYXlsaXN0LmZyYWdtZW50cztcbiAgdmFyIG5ld0ZyYWdtZW50cyA9IG5ld1BsYXlsaXN0LmZyYWdtZW50cztcblxuICBpZiAoZGVsdGEgPCAwIHx8IGRlbHRhID4gb2xkRnJhZ21lbnRzLmxlbmd0aCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbmV3RnJhZ21lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgbmV3RnJhZ21lbnRzW2ldLnN0YXJ0ICs9IG9sZEZyYWdtZW50c1tkZWx0YV0uc3RhcnQ7XG4gIH1cbn1cbmZ1bmN0aW9uIGNvbXB1dGVSZWxvYWRJbnRlcnZhbChjdXJyZW50UGxheWxpc3QsIG5ld1BsYXlsaXN0LCBsYXN0UmVxdWVzdFRpbWUpIHtcbiAgdmFyIHJlbG9hZEludGVydmFsID0gMTAwMCAqIChuZXdQbGF5bGlzdC5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gPyBuZXdQbGF5bGlzdC5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gOiBuZXdQbGF5bGlzdC50YXJnZXRkdXJhdGlvbik7XG4gIHZhciBtaW5SZWxvYWRJbnRlcnZhbCA9IHJlbG9hZEludGVydmFsIC8gMjtcblxuICBpZiAoY3VycmVudFBsYXlsaXN0ICYmIG5ld1BsYXlsaXN0LmVuZFNOID09PSBjdXJyZW50UGxheWxpc3QuZW5kU04pIHtcbiAgICAvLyBmb2xsb3cgSExTIFNwZWMsIElmIHRoZSBjbGllbnQgcmVsb2FkcyBhIFBsYXlsaXN0IGZpbGUgYW5kIGZpbmRzIHRoYXQgaXQgaGFzIG5vdFxuICAgIC8vIGNoYW5nZWQgdGhlbiBpdCBNVVNUIHdhaXQgZm9yIGEgcGVyaW9kIG9mIG9uZS1oYWxmIHRoZSB0YXJnZXRcbiAgICAvLyBkdXJhdGlvbiBiZWZvcmUgcmV0cnlpbmcuXG4gICAgcmVsb2FkSW50ZXJ2YWwgPSBtaW5SZWxvYWRJbnRlcnZhbDtcbiAgfVxuXG4gIGlmIChsYXN0UmVxdWVzdFRpbWUpIHtcbiAgICByZWxvYWRJbnRlcnZhbCA9IE1hdGgubWF4KG1pblJlbG9hZEludGVydmFsLCByZWxvYWRJbnRlcnZhbCAtICh3aW5kb3cucGVyZm9ybWFuY2Uubm93KCkgLSBsYXN0UmVxdWVzdFRpbWUpKTtcbiAgfSAvLyBpbiBhbnkgY2FzZSwgZG9uJ3QgcmVsb2FkIG1vcmUgdGhhbiBoYWxmIG9mIHRhcmdldCBkdXJhdGlvblxuXG5cbiAgcmV0dXJuIE1hdGgucm91bmQocmVsb2FkSW50ZXJ2YWwpO1xufVxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvdGltZS1yYW5nZXMudHNcbi8qKlxuICogIFRpbWVSYW5nZXMgdG8gc3RyaW5nIGhlbHBlclxuICovXG52YXIgVGltZVJhbmdlcyA9IHtcbiAgdG9TdHJpbmc6IGZ1bmN0aW9uIHRvU3RyaW5nKHIpIHtcbiAgICB2YXIgbG9nID0gJyc7XG4gICAgdmFyIGxlbiA9IHIubGVuZ3RoO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgbG9nICs9ICdbJyArIHIuc3RhcnQoaSkudG9GaXhlZCgzKSArICcsJyArIHIuZW5kKGkpLnRvRml4ZWQoMykgKyAnXSc7XG4gICAgfVxuXG4gICAgcmV0dXJuIGxvZztcbiAgfVxufTtcbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIHRpbWVfcmFuZ2VzID0gKFRpbWVSYW5nZXMpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvZGlzY29udGludWl0aWVzLmpzXG5cblxuXG5mdW5jdGlvbiBmaW5kRmlyc3RGcmFnV2l0aENDKGZyYWdtZW50cywgY2MpIHtcbiAgdmFyIGZpcnN0RnJhZyA9IG51bGw7XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBmcmFnbWVudHMubGVuZ3RoOyBpICs9IDEpIHtcbiAgICB2YXIgY3VycmVudEZyYWcgPSBmcmFnbWVudHNbaV07XG5cbiAgICBpZiAoY3VycmVudEZyYWcgJiYgY3VycmVudEZyYWcuY2MgPT09IGNjKSB7XG4gICAgICBmaXJzdEZyYWcgPSBjdXJyZW50RnJhZztcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBmaXJzdEZyYWc7XG59XG5mdW5jdGlvbiBmaW5kRnJhZ1dpdGhDQyhmcmFnbWVudHMsIENDKSB7XG4gIHJldHVybiBiaW5hcnlfc2VhcmNoLnNlYXJjaChmcmFnbWVudHMsIGZ1bmN0aW9uIChjYW5kaWRhdGUpIHtcbiAgICBpZiAoY2FuZGlkYXRlLmNjIDwgQ0MpIHtcbiAgICAgIHJldHVybiAxO1xuICAgIH0gZWxzZSBpZiAoY2FuZGlkYXRlLmNjID4gQ0MpIHtcbiAgICAgIHJldHVybiAtMTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICB9KTtcbn1cbmZ1bmN0aW9uIHNob3VsZEFsaWduT25EaXNjb250aW51aXRpZXMobGFzdEZyYWcsIGxhc3RMZXZlbCwgZGV0YWlscykge1xuICB2YXIgc2hvdWxkQWxpZ24gPSBmYWxzZTtcblxuICBpZiAobGFzdExldmVsICYmIGxhc3RMZXZlbC5kZXRhaWxzICYmIGRldGFpbHMpIHtcbiAgICBpZiAoZGV0YWlscy5lbmRDQyA+IGRldGFpbHMuc3RhcnRDQyB8fCBsYXN0RnJhZyAmJiBsYXN0RnJhZy5jYyA8IGRldGFpbHMuc3RhcnRDQykge1xuICAgICAgc2hvdWxkQWxpZ24gPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzaG91bGRBbGlnbjtcbn0gLy8gRmluZCB0aGUgZmlyc3QgZnJhZyBpbiB0aGUgcHJldmlvdXMgbGV2ZWwgd2hpY2ggbWF0Y2hlcyB0aGUgQ0Mgb2YgdGhlIGZpcnN0IGZyYWcgb2YgdGhlIG5ldyBsZXZlbFxuXG5mdW5jdGlvbiBmaW5kRGlzY29udGludW91c1JlZmVyZW5jZUZyYWcocHJldkRldGFpbHMsIGN1ckRldGFpbHMpIHtcbiAgdmFyIHByZXZGcmFncyA9IHByZXZEZXRhaWxzLmZyYWdtZW50cztcbiAgdmFyIGN1ckZyYWdzID0gY3VyRGV0YWlscy5mcmFnbWVudHM7XG5cbiAgaWYgKCFjdXJGcmFncy5sZW5ndGggfHwgIXByZXZGcmFncy5sZW5ndGgpIHtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdObyBmcmFnbWVudHMgdG8gYWxpZ24nKTtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgcHJldlN0YXJ0RnJhZyA9IGZpbmRGaXJzdEZyYWdXaXRoQ0MocHJldkZyYWdzLCBjdXJGcmFnc1swXS5jYyk7XG5cbiAgaWYgKCFwcmV2U3RhcnRGcmFnIHx8IHByZXZTdGFydEZyYWcgJiYgIXByZXZTdGFydEZyYWcuc3RhcnRQVFMpIHtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdObyBmcmFnIGluIHByZXZpb3VzIGxldmVsIHRvIGFsaWduIG9uJyk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgcmV0dXJuIHByZXZTdGFydEZyYWc7XG59XG5mdW5jdGlvbiBhZGp1c3RQdHMoc2xpZGluZywgZGV0YWlscykge1xuICBkZXRhaWxzLmZyYWdtZW50cy5mb3JFYWNoKGZ1bmN0aW9uIChmcmFnKSB7XG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIHZhciBzdGFydCA9IGZyYWcuc3RhcnQgKyBzbGlkaW5nO1xuICAgICAgZnJhZy5zdGFydCA9IGZyYWcuc3RhcnRQVFMgPSBzdGFydDtcbiAgICAgIGZyYWcuZW5kUFRTID0gc3RhcnQgKyBmcmFnLmR1cmF0aW9uO1xuICAgIH1cbiAgfSk7XG4gIGRldGFpbHMuUFRTS25vd24gPSB0cnVlO1xufVxuLyoqXG4gKiBVc2luZyB0aGUgcGFyYW1ldGVycyBvZiB0aGUgbGFzdCBsZXZlbCwgdGhpcyBmdW5jdGlvbiBjb21wdXRlcyBQVFMnIG9mIHRoZSBuZXcgZnJhZ21lbnRzIHNvIHRoYXQgdGhleSBmb3JtIGFcbiAqIGNvbnRpZ3VvdXMgc3RyZWFtIHdpdGggdGhlIGxhc3QgZnJhZ21lbnRzLlxuICogVGhlIFBUUyBvZiBhIGZyYWdtZW50IGxldHMgSGxzLmpzIGtub3cgd2hlcmUgaXQgZml0cyBpbnRvIGEgc3RyZWFtIC0gYnkga25vd2luZyBldmVyeSBQVFMsIHdlIGtub3cgd2hpY2ggZnJhZ21lbnQgdG9cbiAqIGRvd25sb2FkIGF0IGFueSBnaXZlbiB0aW1lLiBQVFMgaXMgbm9ybWFsbHkgY29tcHV0ZWQgd2hlbiB0aGUgZnJhZ21lbnQgaXMgZGVtdXhlZCwgc28gdGFraW5nIHRoaXMgc3RlcCBzYXZlcyB1cyB0aW1lXG4gKiBhbmQgYW4gZXh0cmEgZG93bmxvYWQuXG4gKiBAcGFyYW0gbGFzdEZyYWdcbiAqIEBwYXJhbSBsYXN0TGV2ZWxcbiAqIEBwYXJhbSBkZXRhaWxzXG4gKi9cblxuZnVuY3Rpb24gYWxpZ25TdHJlYW0obGFzdEZyYWcsIGxhc3RMZXZlbCwgZGV0YWlscykge1xuICBhbGlnbkRpc2NvbnRpbnVpdGllcyhsYXN0RnJhZywgZGV0YWlscywgbGFzdExldmVsKTtcblxuICBpZiAoIWRldGFpbHMuUFRTS25vd24gJiYgbGFzdExldmVsKSB7XG4gICAgLy8gSWYgdGhlIFBUUyB3YXNuJ3QgZmlndXJlZCBvdXQgdmlhIGRpc2NvbnRpbnVpdHkgc2VxdWVuY2UgdGhhdCBtZWFucyB0aGVyZSB3YXMgbm8gQ0MgaW5jcmVhc2Ugd2l0aGluIHRoZSBsZXZlbC5cbiAgICAvLyBBbGlnbmluZyB2aWEgUHJvZ3JhbSBEYXRlIFRpbWUgc2hvdWxkIHRoZXJlZm9yZSBiZSByZWxpYWJsZSwgc2luY2UgUERUIHNob3VsZCBiZSB0aGUgc2FtZSB3aXRoaW4gdGhlIHNhbWVcbiAgICAvLyBkaXNjb250aW51aXR5IHNlcXVlbmNlLlxuICAgIGFsaWduUERUKGRldGFpbHMsIGxhc3RMZXZlbC5kZXRhaWxzKTtcbiAgfVxufVxuLyoqXG4gKiBDb21wdXRlcyB0aGUgUFRTIGlmIGEgbmV3IGxldmVsJ3MgZnJhZ21lbnRzIHVzaW5nIHRoZSBQVFMgb2YgYSBmcmFnbWVudCBpbiB0aGUgbGFzdCBsZXZlbCB3aGljaCBzaGFyZXMgdGhlIHNhbWVcbiAqIGRpc2NvbnRpbnVpdHkgc2VxdWVuY2UuXG4gKiBAcGFyYW0gbGFzdExldmVsIC0gVGhlIGRldGFpbHMgb2YgdGhlIGxhc3QgbG9hZGVkIGxldmVsXG4gKiBAcGFyYW0gZGV0YWlscyAtIFRoZSBkZXRhaWxzIG9mIHRoZSBuZXcgbGV2ZWxcbiAqL1xuXG5mdW5jdGlvbiBhbGlnbkRpc2NvbnRpbnVpdGllcyhsYXN0RnJhZywgZGV0YWlscywgbGFzdExldmVsKSB7XG4gIGlmIChzaG91bGRBbGlnbk9uRGlzY29udGludWl0aWVzKGxhc3RGcmFnLCBsYXN0TGV2ZWwsIGRldGFpbHMpKSB7XG4gICAgdmFyIHJlZmVyZW5jZUZyYWcgPSBmaW5kRGlzY29udGludW91c1JlZmVyZW5jZUZyYWcobGFzdExldmVsLmRldGFpbHMsIGRldGFpbHMpO1xuXG4gICAgaWYgKHJlZmVyZW5jZUZyYWcpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ0FkanVzdGluZyBQVFMgdXNpbmcgbGFzdCBsZXZlbCBkdWUgdG8gQ0MgaW5jcmVhc2Ugd2l0aGluIGN1cnJlbnQgbGV2ZWwnKTtcbiAgICAgIGFkanVzdFB0cyhyZWZlcmVuY2VGcmFnLnN0YXJ0LCBkZXRhaWxzKTtcbiAgICB9XG4gIH1cbn1cbi8qKlxuICogQ29tcHV0ZXMgdGhlIFBUUyBvZiBhIG5ldyBsZXZlbCdzIGZyYWdtZW50cyB1c2luZyB0aGUgZGlmZmVyZW5jZSBpbiBQcm9ncmFtIERhdGUgVGltZSBmcm9tIHRoZSBsYXN0IGxldmVsLlxuICogQHBhcmFtIGRldGFpbHMgLSBUaGUgZGV0YWlscyBvZiB0aGUgbmV3IGxldmVsXG4gKiBAcGFyYW0gbGFzdERldGFpbHMgLSBUaGUgZGV0YWlscyBvZiB0aGUgbGFzdCBsb2FkZWQgbGV2ZWxcbiAqL1xuXG5mdW5jdGlvbiBhbGlnblBEVChkZXRhaWxzLCBsYXN0RGV0YWlscykge1xuICBpZiAobGFzdERldGFpbHMgJiYgbGFzdERldGFpbHMuZnJhZ21lbnRzLmxlbmd0aCkge1xuICAgIGlmICghZGV0YWlscy5oYXNQcm9ncmFtRGF0ZVRpbWUgfHwgIWxhc3REZXRhaWxzLmhhc1Byb2dyYW1EYXRlVGltZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gaWYgbGFzdCBsZXZlbCBzbGlkaW5nIGlzIDEwMDAgYW5kIGl0cyBmaXJzdCBmcmFnIFBST0dSQU0tREFURS1USU1FIGlzIDIwMTctMDgtMjAgMToxMDowMCBBTVxuICAgIC8vIGFuZCBpZiBuZXcgZGV0YWlscyBmaXJzdCBmcmFnIFBST0dSQU0gREFURS1USU1FIGlzIDIwMTctMDgtMjAgMToxMDowOCBBTVxuICAgIC8vIHRoZW4gd2UgY2FuIGRlZHVjZSB0aGF0IHBsYXlsaXN0IEIgc2xpZGluZyBpcyAxMDAwKzggPSAxMDA4c1xuXG5cbiAgICB2YXIgbGFzdFBEVCA9IGxhc3REZXRhaWxzLmZyYWdtZW50c1swXS5wcm9ncmFtRGF0ZVRpbWU7XG4gICAgdmFyIG5ld1BEVCA9IGRldGFpbHMuZnJhZ21lbnRzWzBdLnByb2dyYW1EYXRlVGltZTsgLy8gZGF0ZSBkaWZmIGlzIGluIG1zLiBmcmFnLnN0YXJ0IGlzIGluIHNlY29uZHNcblxuICAgIHZhciBzbGlkaW5nID0gKG5ld1BEVCAtIGxhc3RQRFQpIC8gMTAwMCArIGxhc3REZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDtcblxuICAgIGlmIChPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHNsaWRpbmcpKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiYWRqdXN0aW5nIFBUUyB1c2luZyBwcm9ncmFtRGF0ZVRpbWUgZGVsdGEsIHNsaWRpbmc6XCIgKyBzbGlkaW5nLnRvRml4ZWQoMykpO1xuICAgICAgYWRqdXN0UHRzKHNsaWRpbmcsIGRldGFpbHMpO1xuICAgIH1cbiAgfVxufVxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9mcmFnbWVudC1maW5kZXJzLnRzXG5cblxuXG4vKipcbiAqIFJldHVybnMgZmlyc3QgZnJhZ21lbnQgd2hvc2UgZW5kUGR0IHZhbHVlIGV4Y2VlZHMgdGhlIGdpdmVuIFBEVC5cbiAqIEBwYXJhbSB7QXJyYXk8RnJhZ21lbnQ+fSBmcmFnbWVudHMgLSBUaGUgYXJyYXkgb2YgY2FuZGlkYXRlIGZyYWdtZW50c1xuICogQHBhcmFtIHtudW1iZXJ8bnVsbH0gW1BEVFZhbHVlID0gbnVsbF0gLSBUaGUgUERUIHZhbHVlIHdoaWNoIG11c3QgYmUgZXhjZWVkZWRcbiAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IDBdIC0gVGhlIGFtb3VudCBvZiB0aW1lIHRoYXQgYSBmcmFnbWVudCdzIHN0YXJ0L2VuZCBjYW4gYmUgd2l0aGluIGluIG9yZGVyIHRvIGJlIGNvbnNpZGVyZWQgY29udGlndW91c1xuICogQHJldHVybnMgeyp8bnVsbH0gZnJhZ21lbnQgLSBUaGUgYmVzdCBtYXRjaGluZyBmcmFnbWVudFxuICovXG5mdW5jdGlvbiBmaW5kRnJhZ21lbnRCeVBEVChmcmFnbWVudHMsIFBEVFZhbHVlLCBtYXhGcmFnTG9va1VwVG9sZXJhbmNlKSB7XG4gIGlmIChQRFRWYWx1ZSA9PT0gbnVsbCB8fCAhQXJyYXkuaXNBcnJheShmcmFnbWVudHMpIHx8ICFmcmFnbWVudHMubGVuZ3RoIHx8ICFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKFBEVFZhbHVlKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9IC8vIGlmIGxlc3MgdGhhbiBzdGFydFxuXG5cbiAgdmFyIHN0YXJ0UERUID0gZnJhZ21lbnRzWzBdLnByb2dyYW1EYXRlVGltZTtcblxuICBpZiAoUERUVmFsdWUgPCAoc3RhcnRQRFQgfHwgMCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHZhciBlbmRQRFQgPSBmcmFnbWVudHNbZnJhZ21lbnRzLmxlbmd0aCAtIDFdLmVuZFByb2dyYW1EYXRlVGltZTtcblxuICBpZiAoUERUVmFsdWUgPj0gKGVuZFBEVCB8fCAwKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IG1heEZyYWdMb29rVXBUb2xlcmFuY2UgfHwgMDtcblxuICBmb3IgKHZhciBzZWcgPSAwOyBzZWcgPCBmcmFnbWVudHMubGVuZ3RoOyArK3NlZykge1xuICAgIHZhciBmcmFnID0gZnJhZ21lbnRzW3NlZ107XG5cbiAgICBpZiAocGR0V2l0aGluVG9sZXJhbmNlVGVzdChQRFRWYWx1ZSwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgZnJhZykpIHtcbiAgICAgIHJldHVybiBmcmFnO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBudWxsO1xufVxuLyoqXG4gKiBGaW5kcyBhIGZyYWdtZW50IGJhc2VkIG9uIHRoZSBTTiBvZiB0aGUgcHJldmlvdXMgZnJhZ21lbnQ7IG9yIGJhc2VkIG9uIHRoZSBuZWVkcyBvZiB0aGUgY3VycmVudCBidWZmZXIuXG4gKiBUaGlzIG1ldGhvZCBjb21wZW5zYXRlcyBmb3Igc21hbGwgYnVmZmVyIGdhcHMgYnkgYXBwbHlpbmcgYSB0b2xlcmFuY2UgdG8gdGhlIHN0YXJ0IG9mIGFueSBjYW5kaWRhdGUgZnJhZ21lbnQsIHRodXNcbiAqIGJyZWFraW5nIGFueSB0cmFwcyB3aGljaCB3b3VsZCBjYXVzZSB0aGUgc2FtZSBmcmFnbWVudCB0byBiZSBjb250aW51b3VzbHkgc2VsZWN0ZWQgd2l0aGluIGEgc21hbGwgcmFuZ2UuXG4gKiBAcGFyYW0geyp9IGZyYWdQcmV2aW91cyAtIFRoZSBsYXN0IGZyYWcgc3VjY2Vzc2Z1bGx5IGFwcGVuZGVkXG4gKiBAcGFyYW0ge0FycmF5PEZyYWdtZW50Pn0gZnJhZ21lbnRzIC0gVGhlIGFycmF5IG9mIGNhbmRpZGF0ZSBmcmFnbWVudHNcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYnVmZmVyRW5kID0gMF0gLSBUaGUgZW5kIG9mIHRoZSBjb250aWd1b3VzIGJ1ZmZlcmVkIHJhbmdlIHRoZSBwbGF5aGVhZCBpcyBjdXJyZW50bHkgd2l0aGluXG4gKiBAcGFyYW0ge251bWJlcn0gbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSAtIFRoZSBhbW91bnQgb2YgdGltZSB0aGF0IGEgZnJhZ21lbnQncyBzdGFydC9lbmQgY2FuIGJlIHdpdGhpbiBpbiBvcmRlciB0byBiZSBjb25zaWRlcmVkIGNvbnRpZ3VvdXNcbiAqIEByZXR1cm5zIHsqfSBmb3VuZEZyYWcgLSBUaGUgYmVzdCBtYXRjaGluZyBmcmFnbWVudFxuICovXG5cbmZ1bmN0aW9uIGZpbmRGcmFnbWVudEJ5UFRTKGZyYWdQcmV2aW91cywgZnJhZ21lbnRzLCBidWZmZXJFbmQsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UpIHtcbiAgaWYgKGJ1ZmZlckVuZCA9PT0gdm9pZCAwKSB7XG4gICAgYnVmZmVyRW5kID0gMDtcbiAgfVxuXG4gIGlmIChtYXhGcmFnTG9va1VwVG9sZXJhbmNlID09PSB2b2lkIDApIHtcbiAgICBtYXhGcmFnTG9va1VwVG9sZXJhbmNlID0gMDtcbiAgfVxuXG4gIHZhciBmcmFnTmV4dCA9IG51bGw7XG5cbiAgaWYgKGZyYWdQcmV2aW91cykge1xuICAgIGZyYWdOZXh0ID0gZnJhZ21lbnRzW2ZyYWdQcmV2aW91cy5zbiAtIGZyYWdtZW50c1swXS5zbiArIDFdO1xuICB9IGVsc2UgaWYgKGJ1ZmZlckVuZCA9PT0gMCAmJiBmcmFnbWVudHNbMF0uc3RhcnQgPT09IDApIHtcbiAgICBmcmFnTmV4dCA9IGZyYWdtZW50c1swXTtcbiAgfSAvLyBQcmVmZXIgdGhlIG5leHQgZnJhZ21lbnQgaWYgaXQncyB3aXRoaW4gdG9sZXJhbmNlXG5cblxuICBpZiAoZnJhZ05leHQgJiYgZnJhZ21lbnRXaXRoaW5Ub2xlcmFuY2VUZXN0KGJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgZnJhZ05leHQpID09PSAwKSB7XG4gICAgcmV0dXJuIGZyYWdOZXh0O1xuICB9IC8vIFdlIG1pZ2h0IGJlIHNlZWtpbmcgcGFzdCB0aGUgdG9sZXJhbmNlIHNvIGZpbmQgdGhlIGJlc3QgbWF0Y2hcblxuXG4gIHZhciBmb3VuZEZyYWdtZW50ID0gYmluYXJ5X3NlYXJjaC5zZWFyY2goZnJhZ21lbnRzLCBmcmFnbWVudFdpdGhpblRvbGVyYW5jZVRlc3QuYmluZChudWxsLCBidWZmZXJFbmQsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UpKTtcblxuICBpZiAoZm91bmRGcmFnbWVudCkge1xuICAgIHJldHVybiBmb3VuZEZyYWdtZW50O1xuICB9IC8vIElmIG5vIG1hdGNoIHdhcyBmb3VuZCByZXR1cm4gdGhlIG5leHQgZnJhZ21lbnQgYWZ0ZXIgZnJhZ1ByZXZpb3VzLCBvciBudWxsXG5cblxuICByZXR1cm4gZnJhZ05leHQ7XG59XG4vKipcbiAqIFRoZSB0ZXN0IGZ1bmN0aW9uIHVzZWQgYnkgdGhlIGZpbmRGcmFnbWVudEJ5U24ncyBCaW5hcnlTZWFyY2ggdG8gbG9vayBmb3IgdGhlIGJlc3QgbWF0Y2ggdG8gdGhlIGN1cnJlbnQgYnVmZmVyIGNvbmRpdGlvbnMuXG4gKiBAcGFyYW0geyp9IGNhbmRpZGF0ZSAtIFRoZSBmcmFnbWVudCB0byB0ZXN0XG4gKiBAcGFyYW0ge251bWJlcn0gW2J1ZmZlckVuZCA9IDBdIC0gVGhlIGVuZCBvZiB0aGUgY3VycmVudCBidWZmZXJlZCByYW5nZSB0aGUgcGxheWhlYWQgaXMgY3VycmVudGx5IHdpdGhpblxuICogQHBhcmFtIHtudW1iZXJ9IFttYXhGcmFnTG9va1VwVG9sZXJhbmNlID0gMF0gLSBUaGUgYW1vdW50IG9mIHRpbWUgdGhhdCBhIGZyYWdtZW50J3Mgc3RhcnQgY2FuIGJlIHdpdGhpbiBpbiBvcmRlciB0byBiZSBjb25zaWRlcmVkIGNvbnRpZ3VvdXNcbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gMCBpZiBpdCBtYXRjaGVzLCAxIGlmIHRvbyBsb3csIC0xIGlmIHRvbyBoaWdoXG4gKi9cblxuZnVuY3Rpb24gZnJhZ21lbnRXaXRoaW5Ub2xlcmFuY2VUZXN0KGJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgY2FuZGlkYXRlKSB7XG4gIGlmIChidWZmZXJFbmQgPT09IHZvaWQgMCkge1xuICAgIGJ1ZmZlckVuZCA9IDA7XG4gIH1cblxuICBpZiAobWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9PT0gdm9pZCAwKSB7XG4gICAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IDA7XG4gIH1cblxuICAvLyBvZmZzZXQgc2hvdWxkIGJlIHdpdGhpbiBmcmFnbWVudCBib3VuZGFyeSAtIGNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlXG4gIC8vIHRoaXMgaXMgdG8gY29wZSB3aXRoIHNpdHVhdGlvbnMgbGlrZVxuICAvLyBidWZmZXJFbmQgPSA5Ljk5MVxuICAvLyBmcmFnW8OYXSA6IFswLDEwXVxuICAvLyBmcmFnWzFdIDogWzEwLDIwXVxuICAvLyBidWZmZXJFbmQgaXMgd2l0aGluIGZyYWdbMF0gcmFuZ2UgLi4uIGFsdGhvdWdoIHdoYXQgd2UgYXJlIGV4cGVjdGluZyBpcyB0byByZXR1cm4gZnJhZ1sxXSBoZXJlXG4gIC8vICAgICAgICAgICAgICBmcmFnIHN0YXJ0ICAgICAgICAgICAgICAgZnJhZyBzdGFydCtkdXJhdGlvblxuICAvLyAgICAgICAgICAgICAgICAgIHwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXxcbiAgLy8gICAgICAgICAgICAgIDwtLS0+ICAgICAgICAgICAgICAgICAgICAgICAgIDwtLS0+XG4gIC8vICAuLi4tLS0tLS0tLT48LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+PC0tLS0tLS0tLS4uLi5cbiAgLy8gcHJldmlvdXMgZnJhZyAgICAgICAgIG1hdGNoaW5nIGZyYWdtZW50ICAgICAgICAgbmV4dCBmcmFnXG4gIC8vICByZXR1cm4gLTEgICAgICAgICAgICAgcmV0dXJuIDAgICAgICAgICAgICAgICAgIHJldHVybiAxXG4gIC8vIGxvZ2dlci5sb2coYGxldmVsL3NuL3N0YXJ0L2VuZC9idWZFbmQ6JHtsZXZlbH0vJHtjYW5kaWRhdGUuc259LyR7Y2FuZGlkYXRlLnN0YXJ0fS8keyhjYW5kaWRhdGUuc3RhcnQrY2FuZGlkYXRlLmR1cmF0aW9uKX0vJHtidWZmZXJFbmR9YCk7XG4gIC8vIFNldCB0aGUgbG9va3VwIHRvbGVyYW5jZSB0byBiZSBzbWFsbCBlbm91Z2ggdG8gZGV0ZWN0IHRoZSBjdXJyZW50IHNlZ21lbnQgLSBlbnN1cmVzIHdlIGRvbid0IHNraXAgb3ZlciB2ZXJ5IHNtYWxsIHNlZ21lbnRzXG4gIHZhciBjYW5kaWRhdGVMb29rdXBUb2xlcmFuY2UgPSBNYXRoLm1pbihtYXhGcmFnTG9va1VwVG9sZXJhbmNlLCBjYW5kaWRhdGUuZHVyYXRpb24gKyAoY2FuZGlkYXRlLmRlbHRhUFRTID8gY2FuZGlkYXRlLmRlbHRhUFRTIDogMCkpO1xuXG4gIGlmIChjYW5kaWRhdGUuc3RhcnQgKyBjYW5kaWRhdGUuZHVyYXRpb24gLSBjYW5kaWRhdGVMb29rdXBUb2xlcmFuY2UgPD0gYnVmZmVyRW5kKSB7XG4gICAgcmV0dXJuIDE7XG4gIH0gZWxzZSBpZiAoY2FuZGlkYXRlLnN0YXJ0IC0gY2FuZGlkYXRlTG9va3VwVG9sZXJhbmNlID4gYnVmZmVyRW5kICYmIGNhbmRpZGF0ZS5zdGFydCkge1xuICAgIC8vIGlmIG1heEZyYWdMb29rVXBUb2xlcmFuY2Ugd2lsbCBoYXZlIG5lZ2F0aXZlIHZhbHVlIHRoZW4gZG9uJ3QgcmV0dXJuIC0xIGZvciBmaXJzdCBlbGVtZW50XG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgcmV0dXJuIDA7XG59XG4vKipcbiAqIFRoZSB0ZXN0IGZ1bmN0aW9uIHVzZWQgYnkgdGhlIGZpbmRGcmFnbWVudEJ5UGR0J3MgQmluYXJ5U2VhcmNoIHRvIGxvb2sgZm9yIHRoZSBiZXN0IG1hdGNoIHRvIHRoZSBjdXJyZW50IGJ1ZmZlciBjb25kaXRpb25zLlxuICogVGhpcyBmdW5jdGlvbiB0ZXN0cyB0aGUgY2FuZGlkYXRlJ3MgcHJvZ3JhbSBkYXRlIHRpbWUgdmFsdWVzLCBhcyByZXByZXNlbnRlZCBpbiBVbml4IHRpbWVcbiAqIEBwYXJhbSB7Kn0gY2FuZGlkYXRlIC0gVGhlIGZyYWdtZW50IHRvIHRlc3RcbiAqIEBwYXJhbSB7bnVtYmVyfSBbcGR0QnVmZmVyRW5kID0gMF0gLSBUaGUgVW5peCB0aW1lIHJlcHJlc2VudGluZyB0aGUgZW5kIG9mIHRoZSBjdXJyZW50IGJ1ZmZlcmVkIHJhbmdlXG4gKiBAcGFyYW0ge251bWJlcn0gW21heEZyYWdMb29rVXBUb2xlcmFuY2UgPSAwXSAtIFRoZSBhbW91bnQgb2YgdGltZSB0aGF0IGEgZnJhZ21lbnQncyBzdGFydCBjYW4gYmUgd2l0aGluIGluIG9yZGVyIHRvIGJlIGNvbnNpZGVyZWQgY29udGlndW91c1xuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgY29udGlndW91cywgZmFsc2Ugb3RoZXJ3aXNlXG4gKi9cblxuZnVuY3Rpb24gcGR0V2l0aGluVG9sZXJhbmNlVGVzdChwZHRCdWZmZXJFbmQsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UsIGNhbmRpZGF0ZSkge1xuICB2YXIgY2FuZGlkYXRlTG9va3VwVG9sZXJhbmNlID0gTWF0aC5taW4obWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgY2FuZGlkYXRlLmR1cmF0aW9uICsgKGNhbmRpZGF0ZS5kZWx0YVBUUyA/IGNhbmRpZGF0ZS5kZWx0YVBUUyA6IDApKSAqIDEwMDA7IC8vIGVuZFByb2dyYW1EYXRlVGltZSBjYW4gYmUgbnVsbCwgZGVmYXVsdCB0byB6ZXJvXG5cbiAgdmFyIGVuZFByb2dyYW1EYXRlVGltZSA9IGNhbmRpZGF0ZS5lbmRQcm9ncmFtRGF0ZVRpbWUgfHwgMDtcbiAgcmV0dXJuIGVuZFByb2dyYW1EYXRlVGltZSAtIGNhbmRpZGF0ZUxvb2t1cFRvbGVyYW5jZSA+IHBkdEJ1ZmZlckVuZDtcbn1cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvZ2FwLWNvbnRyb2xsZXIuanNcblxuXG5cblxudmFyIFNUQUxMX01JTklNVU1fRFVSQVRJT05fTVMgPSAyNTA7XG52YXIgTUFYX1NUQVJUX0dBUF9KVU1QID0gMi4wO1xudmFyIFNLSVBfQlVGRkVSX0hPTEVfU1RFUF9TRUNPTkRTID0gMC4xO1xudmFyIFNLSVBfQlVGRkVSX1JBTkdFX1NUQVJUID0gMC4wNTtcblxudmFyIGdhcF9jb250cm9sbGVyX0dhcENvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBHYXBDb250cm9sbGVyKGNvbmZpZywgbWVkaWEsIGZyYWdtZW50VHJhY2tlciwgaGxzKSB7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy5tZWRpYSA9IG1lZGlhO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyID0gZnJhZ21lbnRUcmFja2VyO1xuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIHRoaXMubnVkZ2VSZXRyeSA9IDA7XG4gICAgdGhpcy5zdGFsbFJlcG9ydGVkID0gZmFsc2U7XG4gICAgdGhpcy5zdGFsbGVkID0gbnVsbDtcbiAgICB0aGlzLm1vdmVkID0gZmFsc2U7XG4gICAgdGhpcy5zZWVraW5nID0gZmFsc2U7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrcyBpZiB0aGUgcGxheWhlYWQgaXMgc3R1Y2sgd2l0aGluIGEgZ2FwLCBhbmQgaWYgc28sIGF0dGVtcHRzIHRvIGZyZWUgaXQuXG4gICAqIEEgZ2FwIGlzIGFuIHVuYnVmZmVyZWQgcmFuZ2UgYmV0d2VlbiB0d28gYnVmZmVyZWQgcmFuZ2VzIChvciB0aGUgc3RhcnQgYW5kIHRoZSBmaXJzdCBidWZmZXJlZCByYW5nZSkuXG4gICAqXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBsYXN0Q3VycmVudFRpbWUgUHJldmlvdXNseSByZWFkIHBsYXloZWFkIHBvc2l0aW9uXG4gICAqL1xuXG5cbiAgdmFyIF9wcm90byA9IEdhcENvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5wb2xsID0gZnVuY3Rpb24gcG9sbChsYXN0Q3VycmVudFRpbWUpIHtcbiAgICB2YXIgY29uZmlnID0gdGhpcy5jb25maWcsXG4gICAgICAgIG1lZGlhID0gdGhpcy5tZWRpYSxcbiAgICAgICAgc3RhbGxlZCA9IHRoaXMuc3RhbGxlZDtcbiAgICB2YXIgY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZSxcbiAgICAgICAgc2Vla2luZyA9IG1lZGlhLnNlZWtpbmc7XG4gICAgdmFyIHNlZWtlZCA9IHRoaXMuc2Vla2luZyAmJiAhc2Vla2luZztcbiAgICB2YXIgYmVnaW5TZWVrID0gIXRoaXMuc2Vla2luZyAmJiBzZWVraW5nO1xuICAgIHRoaXMuc2Vla2luZyA9IHNlZWtpbmc7IC8vIFRoZSBwbGF5aGVhZCBpcyBtb3ZpbmcsIG5vLW9wXG5cbiAgICBpZiAoY3VycmVudFRpbWUgIT09IGxhc3RDdXJyZW50VGltZSkge1xuICAgICAgdGhpcy5tb3ZlZCA9IHRydWU7XG5cbiAgICAgIGlmIChzdGFsbGVkICE9PSBudWxsKSB7XG4gICAgICAgIC8vIFRoZSBwbGF5aGVhZCBpcyBub3cgbW92aW5nLCBidXQgd2FzIHByZXZpb3VzbHkgc3RhbGxlZFxuICAgICAgICBpZiAodGhpcy5zdGFsbFJlcG9ydGVkKSB7XG4gICAgICAgICAgdmFyIF9zdGFsbGVkRHVyYXRpb24gPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpIC0gc3RhbGxlZDtcblxuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwicGxheWJhY2sgbm90IHN0dWNrIGFueW1vcmUgQFwiICsgY3VycmVudFRpbWUgKyBcIiwgYWZ0ZXIgXCIgKyBNYXRoLnJvdW5kKF9zdGFsbGVkRHVyYXRpb24pICsgXCJtc1wiKTtcbiAgICAgICAgICB0aGlzLnN0YWxsUmVwb3J0ZWQgPSBmYWxzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc3RhbGxlZCA9IG51bGw7XG4gICAgICAgIHRoaXMubnVkZ2VSZXRyeSA9IDA7XG4gICAgICB9XG5cbiAgICAgIHJldHVybjtcbiAgICB9IC8vIENsZWFyIHN0YWxsZWQgc3RhdGUgd2hlbiBiZWdpbm5pbmcgb3IgZmluaXNoaW5nIHNlZWtpbmcgc28gdGhhdCB3ZSBkb24ndCByZXBvcnQgc3RhbGxzIGNvbWluZyBvdXQgb2YgYSBzZWVrXG5cblxuICAgIGlmIChiZWdpblNlZWsgfHwgc2Vla2VkKSB7XG4gICAgICB0aGlzLnN0YWxsZWQgPSBudWxsO1xuICAgIH0gLy8gVGhlIHBsYXloZWFkIHNob3VsZCBub3QgYmUgbW92aW5nXG5cblxuICAgIGlmIChtZWRpYS5wYXVzZWQgfHwgbWVkaWEuZW5kZWQgfHwgbWVkaWEucGxheWJhY2tSYXRlID09PSAwIHx8ICFtZWRpYS5idWZmZXJlZC5sZW5ndGgpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKG1lZGlhLCBjdXJyZW50VGltZSwgMCk7XG4gICAgdmFyIGlzQnVmZmVyZWQgPSBidWZmZXJJbmZvLmxlbiA+IDA7XG4gICAgdmFyIG5leHRTdGFydCA9IGJ1ZmZlckluZm8ubmV4dFN0YXJ0IHx8IDA7IC8vIFRoZXJlIGlzIG5vIHBsYXlhYmxlIGJ1ZmZlciAod2FpdGluZyBmb3IgYnVmZmVyIGFwcGVuZClcblxuICAgIGlmICghaXNCdWZmZXJlZCAmJiAhbmV4dFN0YXJ0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHNlZWtpbmcpIHtcbiAgICAgIC8vIFdhaXRpbmcgZm9yIHNlZWtpbmcgaW4gYSBidWZmZXJlZCByYW5nZSB0byBjb21wbGV0ZVxuICAgICAgdmFyIGhhc0Vub3VnaEJ1ZmZlciA9IGJ1ZmZlckluZm8ubGVuID4gTUFYX1NUQVJUX0dBUF9KVU1QOyAvLyBOZXh0IGJ1ZmZlcmVkIHJhbmdlIGlzIHRvbyBmYXIgYWhlYWQgdG8ganVtcCB0byB3aGlsZSBzdGlsbCBzZWVraW5nXG5cbiAgICAgIHZhciBub0J1ZmZlckdhcCA9ICFuZXh0U3RhcnQgfHwgbmV4dFN0YXJ0IC0gY3VycmVudFRpbWUgPiBNQVhfU1RBUlRfR0FQX0pVTVAgJiYgIXRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFBhcnRpYWxGcmFnbWVudChjdXJyZW50VGltZSk7XG5cbiAgICAgIGlmIChoYXNFbm91Z2hCdWZmZXIgfHwgbm9CdWZmZXJHYXApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSAvLyBSZXNldCBtb3ZlZCBzdGF0ZSB3aGVuIHNlZWtpbmcgdG8gYSBwb2ludCBpbiBvciBiZWZvcmUgYSBnYXBcblxuXG4gICAgICB0aGlzLm1vdmVkID0gZmFsc2U7XG4gICAgfSAvLyBTa2lwIHN0YXJ0IGdhcHMgaWYgd2UgaGF2ZW4ndCBwbGF5ZWQsIGJ1dCB0aGUgbGFzdCBwb2xsIGRldGVjdGVkIHRoZSBzdGFydCBvZiBhIHN0YWxsXG4gICAgLy8gVGhlIGFkZGl0aW9uIHBvbGwgZ2l2ZXMgdGhlIGJyb3dzZXIgYSBjaGFuY2UgdG8ganVtcCB0aGUgZ2FwIGZvciB1c1xuXG5cbiAgICBpZiAoIXRoaXMubW92ZWQgJiYgdGhpcy5zdGFsbGVkKSB7XG4gICAgICAvLyBKdW1wIHN0YXJ0IGdhcHMgd2l0aGluIGp1bXAgdGhyZXNob2xkXG4gICAgICB2YXIgc3RhcnRKdW1wID0gTWF0aC5tYXgobmV4dFN0YXJ0LCBidWZmZXJJbmZvLnN0YXJ0IHx8IDApIC0gY3VycmVudFRpbWU7XG5cbiAgICAgIGlmIChzdGFydEp1bXAgPiAwICYmIHN0YXJ0SnVtcCA8PSBNQVhfU1RBUlRfR0FQX0pVTVApIHtcbiAgICAgICAgdGhpcy5fdHJ5U2tpcEJ1ZmZlckhvbGUobnVsbCk7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH0gLy8gU3RhcnQgdHJhY2tpbmcgc3RhbGwgdGltZVxuXG5cbiAgICB2YXIgdG5vdyA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgICBpZiAoc3RhbGxlZCA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5zdGFsbGVkID0gdG5vdztcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc3RhbGxlZER1cmF0aW9uID0gdG5vdyAtIHN0YWxsZWQ7XG5cbiAgICBpZiAoIXNlZWtpbmcgJiYgc3RhbGxlZER1cmF0aW9uID49IFNUQUxMX01JTklNVU1fRFVSQVRJT05fTVMpIHtcbiAgICAgIC8vIFJlcG9ydCBzdGFsbGluZyBhZnRlciB0cnlpbmcgdG8gZml4XG4gICAgICB0aGlzLl9yZXBvcnRTdGFsbChidWZmZXJJbmZvLmxlbik7XG4gICAgfVxuXG4gICAgdmFyIGJ1ZmZlcmVkV2l0aEhvbGVzID0gQnVmZmVySGVscGVyLmJ1ZmZlckluZm8obWVkaWEsIGN1cnJlbnRUaW1lLCBjb25maWcubWF4QnVmZmVySG9sZSk7XG5cbiAgICB0aGlzLl90cnlGaXhCdWZmZXJTdGFsbChidWZmZXJlZFdpdGhIb2xlcywgc3RhbGxlZER1cmF0aW9uKTtcbiAgfVxuICAvKipcbiAgICogRGV0ZWN0cyBhbmQgYXR0ZW1wdHMgdG8gZml4IGtub3duIGJ1ZmZlciBzdGFsbGluZyBpc3N1ZXMuXG4gICAqIEBwYXJhbSBidWZmZXJJbmZvIC0gVGhlIHByb3BlcnRpZXMgb2YgdGhlIGN1cnJlbnQgYnVmZmVyLlxuICAgKiBAcGFyYW0gc3RhbGxlZER1cmF0aW9uTXMgLSBUaGUgYW1vdW50IG9mIHRpbWUgSGxzLmpzIGhhcyBiZWVuIHN0YWxsaW5nIGZvci5cbiAgICogQHByaXZhdGVcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX3RyeUZpeEJ1ZmZlclN0YWxsID0gZnVuY3Rpb24gX3RyeUZpeEJ1ZmZlclN0YWxsKGJ1ZmZlckluZm8sIHN0YWxsZWREdXJhdGlvbk1zKSB7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnLFxuICAgICAgICBmcmFnbWVudFRyYWNrZXIgPSB0aGlzLmZyYWdtZW50VHJhY2tlcixcbiAgICAgICAgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIHZhciBjdXJyZW50VGltZSA9IG1lZGlhLmN1cnJlbnRUaW1lO1xuICAgIHZhciBwYXJ0aWFsID0gZnJhZ21lbnRUcmFja2VyLmdldFBhcnRpYWxGcmFnbWVudChjdXJyZW50VGltZSk7XG5cbiAgICBpZiAocGFydGlhbCkge1xuICAgICAgLy8gVHJ5IHRvIHNraXAgb3ZlciB0aGUgYnVmZmVyIGhvbGUgY2F1c2VkIGJ5IGEgcGFydGlhbCBmcmFnbWVudFxuICAgICAgLy8gVGhpcyBtZXRob2QgaXNuJ3QgbGltaXRlZCBieSB0aGUgc2l6ZSBvZiB0aGUgZ2FwIGJldHdlZW4gYnVmZmVyZWQgcmFuZ2VzXG4gICAgICB2YXIgdGFyZ2V0VGltZSA9IHRoaXMuX3RyeVNraXBCdWZmZXJIb2xlKHBhcnRpYWwpOyAvLyB3ZSByZXR1cm4gaGVyZSBpbiB0aGlzIGNhc2UsIG1lYW5pbmdcbiAgICAgIC8vIHRoZSBicmFuY2ggYmVsb3cgb25seSBleGVjdXRlcyB3aGVuIHdlIGRvbid0IGhhbmRsZSBhIHBhcnRpYWwgZnJhZ21lbnRcblxuXG4gICAgICBpZiAodGFyZ2V0VGltZSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfSAvLyBpZiB3ZSBoYXZlbid0IGhhZCB0byBza2lwIG92ZXIgYSBidWZmZXIgaG9sZSBvZiBhIHBhcnRpYWwgZnJhZ21lbnRcbiAgICAvLyB3ZSBtYXkganVzdCBoYXZlIHRvIFwibnVkZ2VcIiB0aGUgcGxheWxpc3QgYXMgdGhlIGJyb3dzZXIgZGVjb2RpbmcvcmVuZGVyaW5nIGVuZ2luZVxuICAgIC8vIG5lZWRzIHRvIGNyb3NzIHNvbWUgc29ydCBvZiB0aHJlc2hvbGQgY292ZXJpbmcgYWxsIHNvdXJjZS1idWZmZXJzIGNvbnRlbnRcbiAgICAvLyB0byBzdGFydCBwbGF5aW5nIHByb3Blcmx5LlxuXG5cbiAgICBpZiAoYnVmZmVySW5mby5sZW4gPiBjb25maWcubWF4QnVmZmVySG9sZSAmJiBzdGFsbGVkRHVyYXRpb25NcyA+IGNvbmZpZy5oaWdoQnVmZmVyV2F0Y2hkb2dQZXJpb2QgKiAxMDAwKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignVHJ5aW5nIHRvIG51ZGdlIHBsYXloZWFkIG92ZXIgYnVmZmVyLWhvbGUnKTsgLy8gVHJ5IHRvIG51ZGdlIGN1cnJlbnRUaW1lIG92ZXIgYSBidWZmZXIgaG9sZSBpZiB3ZSd2ZSBiZWVuIHN0YWxsaW5nIGZvciB0aGUgY29uZmlndXJlZCBhbW91bnQgb2Ygc2Vjb25kc1xuICAgICAgLy8gV2Ugb25seSB0cnkgdG8ganVtcCB0aGUgaG9sZSBpZiBpdCdzIHVuZGVyIHRoZSBjb25maWd1cmVkIHNpemVcbiAgICAgIC8vIFJlc2V0IHN0YWxsZWQgc28gdG8gcmVhcm0gd2F0Y2hkb2cgdGltZXJcblxuICAgICAgdGhpcy5zdGFsbGVkID0gbnVsbDtcblxuICAgICAgdGhpcy5fdHJ5TnVkZ2VCdWZmZXIoKTtcbiAgICB9XG4gIH1cbiAgLyoqXG4gICAqIFRyaWdnZXJzIGEgQlVGRkVSX1NUQUxMRURfRVJST1IgZXZlbnQsIGJ1dCBvbmx5IG9uY2UgcGVyIHN0YWxsIHBlcmlvZC5cbiAgICogQHBhcmFtIGJ1ZmZlckxlbiAtIFRoZSBwbGF5aGVhZCBkaXN0YW5jZSBmcm9tIHRoZSBlbmQgb2YgdGhlIGN1cnJlbnQgYnVmZmVyIHNlZ21lbnQuXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl9yZXBvcnRTdGFsbCA9IGZ1bmN0aW9uIF9yZXBvcnRTdGFsbChidWZmZXJMZW4pIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHMsXG4gICAgICAgIG1lZGlhID0gdGhpcy5tZWRpYSxcbiAgICAgICAgc3RhbGxSZXBvcnRlZCA9IHRoaXMuc3RhbGxSZXBvcnRlZDtcblxuICAgIGlmICghc3RhbGxSZXBvcnRlZCkge1xuICAgICAgLy8gUmVwb3J0IHN0YWxsZWQgZXJyb3Igb25jZVxuICAgICAgdGhpcy5zdGFsbFJlcG9ydGVkID0gdHJ1ZTtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiUGxheWJhY2sgc3RhbGxpbmcgYXQgQFwiICsgbWVkaWEuY3VycmVudFRpbWUgKyBcIiBkdWUgdG8gbG93IGJ1ZmZlciAoYnVmZmVyPVwiICsgYnVmZmVyTGVuICsgXCIpXCIpO1xuICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQlVGRkVSX1NUQUxMRURfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgYnVmZmVyOiBidWZmZXJMZW5cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogQXR0ZW1wdHMgdG8gZml4IGJ1ZmZlciBzdGFsbHMgYnkganVtcGluZyBvdmVyIGtub3duIGdhcHMgY2F1c2VkIGJ5IHBhcnRpYWwgZnJhZ21lbnRzXG4gICAqIEBwYXJhbSBwYXJ0aWFsIC0gVGhlIHBhcnRpYWwgZnJhZ21lbnQgZm91bmQgYXQgdGhlIGN1cnJlbnQgdGltZSAod2hlcmUgcGxheWJhY2sgaXMgc3RhbGxpbmcpLlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fdHJ5U2tpcEJ1ZmZlckhvbGUgPSBmdW5jdGlvbiBfdHJ5U2tpcEJ1ZmZlckhvbGUocGFydGlhbCkge1xuICAgIHZhciBjb25maWcgPSB0aGlzLmNvbmZpZyxcbiAgICAgICAgaGxzID0gdGhpcy5obHMsXG4gICAgICAgIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICB2YXIgY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICB2YXIgbGFzdEVuZFRpbWUgPSAwOyAvLyBDaGVjayBpZiBjdXJyZW50VGltZSBpcyBiZXR3ZWVuIHVuYnVmZmVyZWQgcmVnaW9ucyBvZiBwYXJ0aWFsIGZyYWdtZW50c1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBtZWRpYS5idWZmZXJlZC5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIHN0YXJ0VGltZSA9IG1lZGlhLmJ1ZmZlcmVkLnN0YXJ0KGkpO1xuXG4gICAgICBpZiAoY3VycmVudFRpbWUgKyBjb25maWcubWF4QnVmZmVySG9sZSA+PSBsYXN0RW5kVGltZSAmJiBjdXJyZW50VGltZSA8IHN0YXJ0VGltZSkge1xuICAgICAgICB2YXIgdGFyZ2V0VGltZSA9IE1hdGgubWF4KHN0YXJ0VGltZSArIFNLSVBfQlVGRkVSX1JBTkdFX1NUQVJULCBtZWRpYS5jdXJyZW50VGltZSArIFNLSVBfQlVGRkVSX0hPTEVfU1RFUF9TRUNPTkRTKTtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJza2lwcGluZyBob2xlLCBhZGp1c3RpbmcgY3VycmVudFRpbWUgZnJvbSBcIiArIGN1cnJlbnRUaW1lICsgXCIgdG8gXCIgKyB0YXJnZXRUaW1lKTtcbiAgICAgICAgdGhpcy5tb3ZlZCA9IHRydWU7XG4gICAgICAgIHRoaXMuc3RhbGxlZCA9IG51bGw7XG4gICAgICAgIG1lZGlhLmN1cnJlbnRUaW1lID0gdGFyZ2V0VGltZTtcblxuICAgICAgICBpZiAocGFydGlhbCkge1xuICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTUVESUFfRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQlVGRkVSX1NFRUtfT1ZFUl9IT0xFLFxuICAgICAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICAgICAgcmVhc29uOiBcImZyYWdtZW50IGxvYWRlZCB3aXRoIGJ1ZmZlciBob2xlcywgc2Vla2luZyBmcm9tIFwiICsgY3VycmVudFRpbWUgKyBcIiB0byBcIiArIHRhcmdldFRpbWUsXG4gICAgICAgICAgICBmcmFnOiBwYXJ0aWFsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGFyZ2V0VGltZTtcbiAgICAgIH1cblxuICAgICAgbGFzdEVuZFRpbWUgPSBtZWRpYS5idWZmZXJlZC5lbmQoaSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIDA7XG4gIH1cbiAgLyoqXG4gICAqIEF0dGVtcHRzIHRvIGZpeCBidWZmZXIgc3RhbGxzIGJ5IGFkdmFuY2luZyB0aGUgbWVkaWFFbGVtZW50J3MgY3VycmVudCB0aW1lIGJ5IGEgc21hbGwgYW1vdW50LlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fdHJ5TnVkZ2VCdWZmZXIgPSBmdW5jdGlvbiBfdHJ5TnVkZ2VCdWZmZXIoKSB7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnLFxuICAgICAgICBobHMgPSB0aGlzLmhscyxcbiAgICAgICAgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIHZhciBjdXJyZW50VGltZSA9IG1lZGlhLmN1cnJlbnRUaW1lO1xuICAgIHZhciBudWRnZVJldHJ5ID0gKHRoaXMubnVkZ2VSZXRyeSB8fCAwKSArIDE7XG4gICAgdGhpcy5udWRnZVJldHJ5ID0gbnVkZ2VSZXRyeTtcblxuICAgIGlmIChudWRnZVJldHJ5IDwgY29uZmlnLm51ZGdlTWF4UmV0cnkpIHtcbiAgICAgIHZhciB0YXJnZXRUaW1lID0gY3VycmVudFRpbWUgKyBudWRnZVJldHJ5ICogY29uZmlnLm51ZGdlT2Zmc2V0OyAvLyBwbGF5YmFjayBzdGFsbGVkIGluIGJ1ZmZlcmVkIGFyZWEgLi4uIGxldCdzIG51ZGdlIGN1cnJlbnRUaW1lIHRvIHRyeSB0byBvdmVyY29tZSB0aGlzXG5cbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiTnVkZ2luZyAnY3VycmVudFRpbWUnIGZyb20gXCIgKyBjdXJyZW50VGltZSArIFwiIHRvIFwiICsgdGFyZ2V0VGltZSk7XG4gICAgICBtZWRpYS5jdXJyZW50VGltZSA9IHRhcmdldFRpbWU7XG4gICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTUVESUFfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5CVUZGRVJfTlVER0VfT05fU1RBTEwsXG4gICAgICAgIGZhdGFsOiBmYWxzZVxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcihcIlBsYXloZWFkIHN0aWxsIG5vdCBtb3Zpbmcgd2hpbGUgZW5vdWdoIGRhdGEgYnVmZmVyZWQgQFwiICsgY3VycmVudFRpbWUgKyBcIiBhZnRlciBcIiArIGNvbmZpZy5udWRnZU1heFJldHJ5ICsgXCIgbnVkZ2VzXCIpO1xuICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQlVGRkVSX1NUQUxMRURfRVJST1IsXG4gICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIEdhcENvbnRyb2xsZXI7XG59KCk7XG5cblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdGFzay1sb29wLnRzXG5mdW5jdGlvbiBfYXNzZXJ0VGhpc0luaXRpYWxpemVkKHNlbGYpIHsgaWYgKHNlbGYgPT09IHZvaWQgMCkgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIHNlbGY7IH1cblxuZnVuY3Rpb24gdGFza19sb29wX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cblxuXG4vKipcbiAqIFN1Yi1jbGFzcyBzcGVjaWFsaXphdGlvbiBvZiBFdmVudEhhbmRsZXIgYmFzZSBjbGFzcy5cbiAqXG4gKiBUYXNrTG9vcCBhbGxvd3MgdG8gc2NoZWR1bGUgYSB0YXNrIGZ1bmN0aW9uIGJlaW5nIGNhbGxlZCAob3B0aW9ubmFseSByZXBlYXRlZGx5KSBvbiB0aGUgbWFpbiBsb29wLFxuICogc2NoZWR1bGVkIGFzeW5jaHJvbmVvdXNseSwgYXZvaWRpbmcgcmVjdXJzaXZlIGNhbGxzIGluIHRoZSBzYW1lIHRpY2suXG4gKlxuICogVGhlIHRhc2sgaXRzZWxmIGlzIGltcGxlbWVudGVkIGluIGBkb1RpY2tgLiBJdCBjYW4gYmUgcmVxdWVzdGVkIGFuZCBjYWxsZWQgZm9yIHNpbmdsZSBleGVjdXRpb25cbiAqIHVzaW5nIHRoZSBgdGlja2AgbWV0aG9kLlxuICpcbiAqIEl0IHdpbGwgYmUgYXNzdXJlZCB0aGF0IHRoZSB0YXNrIGV4ZWN1dGlvbiBtZXRob2QgKGB0aWNrYCkgb25seSBnZXRzIGNhbGxlZCBvbmNlIHBlciBtYWluIGxvb3AgXCJ0aWNrXCIsXG4gKiBubyBtYXR0ZXIgaG93IG9mdGVuIGl0IGdldHMgcmVxdWVzdGVkIGZvciBleGVjdXRpb24uIEV4ZWN1dGlvbiBpbiBmdXJ0aGVyIHRpY2tzIHdpbGwgYmUgc2NoZWR1bGVkIGFjY29yZGluZ2x5LlxuICpcbiAqIElmIGZ1cnRoZXIgZXhlY3V0aW9uIHJlcXVlc3RzIGhhdmUgYWxyZWFkeSBiZWVuIHNjaGVkdWxlZCBvbiB0aGUgbmV4dCB0aWNrLCBpdCBjYW4gYmUgY2hlY2tlZCB3aXRoIGBoYXNOZXh0VGlja2AsXG4gKiBhbmQgY2FuY2VsbGVkIHdpdGggYGNsZWFyTmV4dFRpY2tgLlxuICpcbiAqIFRoZSB0YXNrIGNhbiBiZSBzY2hlZHVsZWQgYXMgYW4gaW50ZXJ2YWwgcmVwZWF0ZWRseSB3aXRoIGEgcGVyaW9kIGFzIHBhcmFtZXRlciAoc2VlIGBzZXRJbnRlcnZhbGAsIGBjbGVhckludGVydmFsYCkuXG4gKlxuICogU3ViLWNsYXNzZXMgbmVlZCB0byBpbXBsZW1lbnQgdGhlIGBkb1RpY2tgIG1ldGhvZCB3aGljaCB3aWxsIGVmZmVjdGl2ZWx5IGhhdmUgdGhlIHRhc2sgZXhlY3V0aW9uIHJvdXRpbmUuXG4gKlxuICogRnVydGhlciBleHBsYW5hdGlvbnM6XG4gKlxuICogVGhlIGJhc2VjbGFzcyBoYXMgYSBgdGlja2AgbWV0aG9kIHRoYXQgd2lsbCBzY2hlZHVsZSB0aGUgZG9UaWNrIGNhbGwuIEl0IG1heSBiZSBjYWxsZWQgc3luY2hyb25lb3VzbHlcbiAqIG9ubHkgZm9yIGEgc3RhY2stZGVwdGggb2Ygb25lLiBPbiByZS1lbnRyYW50IGNhbGxzLCBzdWItc2VxdWVudCBjYWxscyBhcmUgc2NoZWR1bGVkIGZvciBuZXh0IG1haW4gbG9vcCB0aWNrcy5cbiAqXG4gKiBXaGVuIHRoZSB0YXNrIGV4ZWN1dGlvbiAoYHRpY2tgIG1ldGhvZCkgaXMgY2FsbGVkIGluIHJlLWVudHJhbnQgd2F5IHRoaXMgaXMgZGV0ZWN0ZWQgYW5kXG4gKiB3ZSBhcmUgbGltaXRpbmcgdGhlIHRhc2sgZXhlY3V0aW9uIHBlciBjYWxsIHN0YWNrIHRvIGV4YWN0bHkgb25lLCBidXQgc2NoZWR1bGluZy9wb3N0LXBvbmluZyBmdXJ0aGVyXG4gKiB0YXNrIHByb2Nlc3Npbmcgb24gdGhlIG5leHQgbWFpbiBsb29wIGl0ZXJhdGlvbiAoYWxzbyBrbm93biBhcyBcIm5leHQgdGlja1wiIGluIHRoZSBOb2RlL0pTIHJ1bnRpbWUgbGluZ28pLlxuICovXG52YXIgVGFza0xvb3AgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9FdmVudEhhbmRsZXIpIHtcbiAgdGFza19sb29wX2luaGVyaXRzTG9vc2UoVGFza0xvb3AsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIFRhc2tMb29wKGhscykge1xuICAgIHZhciBfdGhpcztcblxuICAgIGZvciAodmFyIF9sZW4gPSBhcmd1bWVudHMubGVuZ3RoLCBldmVudHMgPSBuZXcgQXJyYXkoX2xlbiA+IDEgPyBfbGVuIC0gMSA6IDApLCBfa2V5ID0gMTsgX2tleSA8IF9sZW47IF9rZXkrKykge1xuICAgICAgZXZlbnRzW19rZXkgLSAxXSA9IGFyZ3VtZW50c1tfa2V5XTtcbiAgICB9XG5cbiAgICBfdGhpcyA9IF9FdmVudEhhbmRsZXIuY2FsbC5hcHBseShfRXZlbnRIYW5kbGVyLCBbdGhpcywgaGxzXS5jb25jYXQoZXZlbnRzKSkgfHwgdGhpcztcbiAgICBfdGhpcy5fYm91bmRUaWNrID0gdm9pZCAwO1xuICAgIF90aGlzLl90aWNrVGltZXIgPSBudWxsO1xuICAgIF90aGlzLl90aWNrSW50ZXJ2YWwgPSBudWxsO1xuICAgIF90aGlzLl90aWNrQ2FsbENvdW50ID0gMDtcbiAgICBfdGhpcy5fYm91bmRUaWNrID0gX3RoaXMudGljay5iaW5kKF9hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cbiAgLyoqXG4gICAqIEBvdmVycmlkZVxuICAgKi9cblxuXG4gIHZhciBfcHJvdG8gPSBUYXNrTG9vcC5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLm9uSGFuZGxlckRlc3Ryb3lpbmcgPSBmdW5jdGlvbiBvbkhhbmRsZXJEZXN0cm95aW5nKCkge1xuICAgIC8vIGNsZWFyIGFsbCB0aW1lcnMgYmVmb3JlIHVucmVnaXN0ZXJpbmcgZnJvbSBldmVudCBidXNcbiAgICB0aGlzLmNsZWFyTmV4dFRpY2soKTtcbiAgICB0aGlzLmNsZWFySW50ZXJ2YWwoKTtcbiAgfVxuICAvKipcbiAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmhhc0ludGVydmFsID0gZnVuY3Rpb24gaGFzSW50ZXJ2YWwoKSB7XG4gICAgcmV0dXJuICEhdGhpcy5fdGlja0ludGVydmFsO1xuICB9XG4gIC8qKlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIDtcblxuICBfcHJvdG8uaGFzTmV4dFRpY2sgPSBmdW5jdGlvbiBoYXNOZXh0VGljaygpIHtcbiAgICByZXR1cm4gISF0aGlzLl90aWNrVGltZXI7XG4gIH1cbiAgLyoqXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBtaWxsaXMgSW50ZXJ2YWwgdGltZSAobXMpXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIHdoZW4gaW50ZXJ2YWwgaGFzIGJlZW4gc2NoZWR1bGVkLCBmYWxzZSB3aGVuIGFscmVhZHkgc2NoZWR1bGVkIChubyBlZmZlY3QpXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnNldEludGVydmFsID0gZnVuY3Rpb24gc2V0SW50ZXJ2YWwobWlsbGlzKSB7XG4gICAgaWYgKCF0aGlzLl90aWNrSW50ZXJ2YWwpIHtcbiAgICAgIHRoaXMuX3RpY2tJbnRlcnZhbCA9IHNlbGYuc2V0SW50ZXJ2YWwodGhpcy5fYm91bmRUaWNrLCBtaWxsaXMpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIC8qKlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSB3aGVuIGludGVydmFsIHdhcyBjbGVhcmVkLCBmYWxzZSB3aGVuIG5vbmUgd2FzIHNldCAobm8gZWZmZWN0KVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5jbGVhckludGVydmFsID0gZnVuY3Rpb24gY2xlYXJJbnRlcnZhbCgpIHtcbiAgICBpZiAodGhpcy5fdGlja0ludGVydmFsKSB7XG4gICAgICBzZWxmLmNsZWFySW50ZXJ2YWwodGhpcy5fdGlja0ludGVydmFsKTtcbiAgICAgIHRoaXMuX3RpY2tJbnRlcnZhbCA9IG51bGw7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgLyoqXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIHdoZW4gdGltZW91dCB3YXMgY2xlYXJlZCwgZmFsc2Ugd2hlbiBub25lIHdhcyBzZXQgKG5vIGVmZmVjdClcbiAgICovXG4gIDtcblxuICBfcHJvdG8uY2xlYXJOZXh0VGljayA9IGZ1bmN0aW9uIGNsZWFyTmV4dFRpY2soKSB7XG4gICAgaWYgKHRoaXMuX3RpY2tUaW1lcikge1xuICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy5fdGlja1RpbWVyKTtcbiAgICAgIHRoaXMuX3RpY2tUaW1lciA9IG51bGw7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgLyoqXG4gICAqIFdpbGwgY2FsbCB0aGUgc3ViY2xhc3MgZG9UaWNrIGltcGxlbWVudGF0aW9uIGluIHRoaXMgbWFpbiBsb29wIHRpY2tcbiAgICogb3IgaW4gdGhlIG5leHQgb25lICh2aWEgc2V0VGltZW91dCgsMCkpIGluIGNhc2UgaXQgaGFzIGFscmVhZHkgYmVlbiBjYWxsZWRcbiAgICogaW4gdGhpcyB0aWNrIChpbiBjYXNlIHRoaXMgaXMgYSByZS1lbnRyYW50IGNhbGwpLlxuICAgKi9cbiAgO1xuXG4gIF9wcm90by50aWNrID0gZnVuY3Rpb24gdGljaygpIHtcbiAgICB0aGlzLl90aWNrQ2FsbENvdW50Kys7XG5cbiAgICBpZiAodGhpcy5fdGlja0NhbGxDb3VudCA9PT0gMSkge1xuICAgICAgdGhpcy5kb1RpY2soKTsgLy8gcmUtZW50cmFudCBjYWxsIHRvIHRpY2sgZnJvbSBwcmV2aW91cyBkb1RpY2sgY2FsbCBzdGFja1xuICAgICAgLy8gLT4gc2NoZWR1bGUgYSBjYWxsIG9uIHRoZSBuZXh0IG1haW4gbG9vcCBpdGVyYXRpb24gdG8gcHJvY2VzcyB0aGlzIHRhc2sgcHJvY2Vzc2luZyByZXF1ZXN0XG5cbiAgICAgIGlmICh0aGlzLl90aWNrQ2FsbENvdW50ID4gMSkge1xuICAgICAgICAvLyBtYWtlIHN1cmUgb25seSBvbmUgdGltZXIgZXhpc3RzIGF0IGFueSB0aW1lIGF0IG1heFxuICAgICAgICB0aGlzLmNsZWFyTmV4dFRpY2soKTtcbiAgICAgICAgdGhpcy5fdGlja1RpbWVyID0gc2VsZi5zZXRUaW1lb3V0KHRoaXMuX2JvdW5kVGljaywgMCk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3RpY2tDYWxsQ291bnQgPSAwO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogRm9yIHN1YmNsYXNzIHRvIGltcGxlbWVudCB0YXNrIGxvZ2ljXG4gICAqIEBhYnN0cmFjdFxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5kb1RpY2sgPSBmdW5jdGlvbiBkb1RpY2soKSB7fTtcblxuICByZXR1cm4gVGFza0xvb3A7XG59KGV2ZW50X2hhbmRsZXIpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvYmFzZS1zdHJlYW0tY29udHJvbGxlci5qc1xuXG5cbmZ1bmN0aW9uIGJhc2Vfc3RyZWFtX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuXG5cblxuXG52YXIgU3RhdGUgPSB7XG4gIFNUT1BQRUQ6ICdTVE9QUEVEJyxcbiAgU1RBUlRJTkc6ICdTVEFSVElORycsXG4gIElETEU6ICdJRExFJyxcbiAgUEFVU0VEOiAnUEFVU0VEJyxcbiAgS0VZX0xPQURJTkc6ICdLRVlfTE9BRElORycsXG4gIEZSQUdfTE9BRElORzogJ0ZSQUdfTE9BRElORycsXG4gIEZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZOiAnRlJBR19MT0FESU5HX1dBSVRJTkdfUkVUUlknLFxuICBXQUlUSU5HX1RSQUNLOiAnV0FJVElOR19UUkFDSycsXG4gIFBBUlNJTkc6ICdQQVJTSU5HJyxcbiAgUEFSU0VEOiAnUEFSU0VEJyxcbiAgQlVGRkVSX0ZMVVNISU5HOiAnQlVGRkVSX0ZMVVNISU5HJyxcbiAgRU5ERUQ6ICdFTkRFRCcsXG4gIEVSUk9SOiAnRVJST1InLFxuICBXQUlUSU5HX0lOSVRfUFRTOiAnV0FJVElOR19JTklUX1BUUycsXG4gIFdBSVRJTkdfTEVWRUw6ICdXQUlUSU5HX0xFVkVMJ1xufTtcblxudmFyIGJhc2Vfc3RyZWFtX2NvbnRyb2xsZXJfQmFzZVN0cmVhbUNvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9UYXNrTG9vcCkge1xuICBiYXNlX3N0cmVhbV9jb250cm9sbGVyX2luaGVyaXRzTG9vc2UoQmFzZVN0cmVhbUNvbnRyb2xsZXIsIF9UYXNrTG9vcCk7XG5cbiAgZnVuY3Rpb24gQmFzZVN0cmVhbUNvbnRyb2xsZXIoKSB7XG4gICAgcmV0dXJuIF9UYXNrTG9vcC5hcHBseSh0aGlzLCBhcmd1bWVudHMpIHx8IHRoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gQmFzZVN0cmVhbUNvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5kb1RpY2sgPSBmdW5jdGlvbiBkb1RpY2soKSB7fTtcblxuICBfcHJvdG8uc3RhcnRMb2FkID0gZnVuY3Rpb24gc3RhcnRMb2FkKCkge307XG5cbiAgX3Byb3RvLnN0b3BMb2FkID0gZnVuY3Rpb24gc3RvcExvYWQoKSB7XG4gICAgdmFyIGZyYWcgPSB0aGlzLmZyYWdDdXJyZW50O1xuXG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIGlmIChmcmFnLmxvYWRlcikge1xuICAgICAgICBmcmFnLmxvYWRlci5hYm9ydCgpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5kZW11eGVyKSB7XG4gICAgICB0aGlzLmRlbXV4ZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5kZW11eGVyID0gbnVsbDtcbiAgICB9XG5cbiAgICB0aGlzLmZyYWdDdXJyZW50ID0gbnVsbDtcbiAgICB0aGlzLmZyYWdQcmV2aW91cyA9IG51bGw7XG4gICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgdGhpcy5jbGVhck5leHRUaWNrKCk7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gIH07XG5cbiAgX3Byb3RvLl9zdHJlYW1FbmRlZCA9IGZ1bmN0aW9uIF9zdHJlYW1FbmRlZChidWZmZXJJbmZvLCBsZXZlbERldGFpbHMpIHtcbiAgICB2YXIgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50LFxuICAgICAgICBmcmFnbWVudFRyYWNrZXIgPSB0aGlzLmZyYWdtZW50VHJhY2tlcjsgLy8gd2UganVzdCBnb3QgZG9uZSBsb2FkaW5nIHRoZSBmaW5hbCBmcmFnbWVudCBhbmQgdGhlcmUgaXMgbm8gb3RoZXIgYnVmZmVyZWQgcmFuZ2UgYWZ0ZXIgLi4uXG4gICAgLy8gcmF0aW9uYWxlIGlzIHRoYXQgaW4gY2FzZSB0aGVyZSBhcmUgYW55IGJ1ZmZlcmVkIHJhbmdlcyBhZnRlciwgaXQgbWVhbnMgdGhhdCB0aGVyZSBhcmUgdW5idWZmZXJlZCBwb3J0aW9uIGluIGJldHdlZW5cbiAgICAvLyBzbyB3ZSBzaG91bGQgbm90IHN3aXRjaCB0byBFTkRFRCBpbiB0aGF0IGNhc2UsIHRvIGJlIGFibGUgdG8gYnVmZmVyIHRoZW1cbiAgICAvLyBkb250IHN3aXRjaCB0byBFTkRFRCBpZiB3ZSBuZWVkIHRvIGJhY2t0cmFjayBsYXN0IGZyYWdtZW50XG5cbiAgICBpZiAoIWxldmVsRGV0YWlscy5saXZlICYmIGZyYWdDdXJyZW50ICYmICFmcmFnQ3VycmVudC5iYWNrdHJhY2tlZCAmJiBmcmFnQ3VycmVudC5zbiA9PT0gbGV2ZWxEZXRhaWxzLmVuZFNOICYmICFidWZmZXJJbmZvLm5leHRTdGFydCkge1xuICAgICAgdmFyIGZyYWdTdGF0ZSA9IGZyYWdtZW50VHJhY2tlci5nZXRTdGF0ZShmcmFnQ3VycmVudCk7XG4gICAgICByZXR1cm4gZnJhZ1N0YXRlID09PSBGcmFnbWVudFN0YXRlLlBBUlRJQUwgfHwgZnJhZ1N0YXRlID09PSBGcmFnbWVudFN0YXRlLk9LO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYVNlZWtpbmcgPSBmdW5jdGlvbiBvbk1lZGlhU2Vla2luZygpIHtcbiAgICB2YXIgY29uZmlnID0gdGhpcy5jb25maWcsXG4gICAgICAgIG1lZGlhID0gdGhpcy5tZWRpYSxcbiAgICAgICAgbWVkaWFCdWZmZXIgPSB0aGlzLm1lZGlhQnVmZmVyLFxuICAgICAgICBzdGF0ZSA9IHRoaXMuc3RhdGU7XG4gICAgdmFyIGN1cnJlbnRUaW1lID0gbWVkaWEgPyBtZWRpYS5jdXJyZW50VGltZSA6IG51bGw7XG4gICAgdmFyIGJ1ZmZlckluZm8gPSBCdWZmZXJIZWxwZXIuYnVmZmVySW5mbyhtZWRpYUJ1ZmZlciB8fCBtZWRpYSwgY3VycmVudFRpbWUsIHRoaXMuY29uZmlnLm1heEJ1ZmZlckhvbGUpO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJtZWRpYSBzZWVraW5nIHRvIFwiICsgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoY3VycmVudFRpbWUpID8gY3VycmVudFRpbWUudG9GaXhlZCgzKSA6IGN1cnJlbnRUaW1lKSk7XG5cbiAgICBpZiAoc3RhdGUgPT09IFN0YXRlLkZSQUdfTE9BRElORykge1xuICAgICAgdmFyIGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudDsgLy8gY2hlY2sgaWYgd2UgYXJlIHNlZWtpbmcgdG8gYSB1bmJ1ZmZlcmVkIGFyZWEgQU5EIGlmIGZyYWcgbG9hZGluZyBpcyBpbiBwcm9ncmVzc1xuXG4gICAgICBpZiAoYnVmZmVySW5mby5sZW4gPT09IDAgJiYgZnJhZ0N1cnJlbnQpIHtcbiAgICAgICAgdmFyIHRvbGVyYW5jZSA9IGNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlO1xuICAgICAgICB2YXIgZnJhZ1N0YXJ0T2Zmc2V0ID0gZnJhZ0N1cnJlbnQuc3RhcnQgLSB0b2xlcmFuY2U7XG4gICAgICAgIHZhciBmcmFnRW5kT2Zmc2V0ID0gZnJhZ0N1cnJlbnQuc3RhcnQgKyBmcmFnQ3VycmVudC5kdXJhdGlvbiArIHRvbGVyYW5jZTsgLy8gY2hlY2sgaWYgd2Ugc2VlayBwb3NpdGlvbiB3aWxsIGJlIG91dCBvZiBjdXJyZW50bHkgbG9hZGVkIGZyYWcgcmFuZ2UgOiBpZiBvdXQgY2FuY2VsIGZyYWcgbG9hZCwgaWYgaW4sIGRvbid0IGRvIGFueXRoaW5nXG5cbiAgICAgICAgaWYgKGN1cnJlbnRUaW1lIDwgZnJhZ1N0YXJ0T2Zmc2V0IHx8IGN1cnJlbnRUaW1lID4gZnJhZ0VuZE9mZnNldCkge1xuICAgICAgICAgIGlmIChmcmFnQ3VycmVudC5sb2FkZXIpIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3NlZWtpbmcgb3V0c2lkZSBvZiBidWZmZXIgd2hpbGUgZnJhZ21lbnQgbG9hZCBpbiBwcm9ncmVzcywgY2FuY2VsIGZyYWdtZW50IGxvYWQnKTtcbiAgICAgICAgICAgIGZyYWdDdXJyZW50LmxvYWRlci5hYm9ydCgpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBudWxsO1xuICAgICAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gbnVsbDsgLy8gc3dpdGNoIHRvIElETEUgc3RhdGUgdG8gbG9hZCBuZXcgZnJhZ21lbnRcblxuICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3NlZWtpbmcgb3V0c2lkZSBvZiBidWZmZXIgYnV0IHdpdGhpbiBjdXJyZW50bHkgbG9hZGVkIGZyYWdtZW50IHJhbmdlJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHN0YXRlID09PSBTdGF0ZS5FTkRFRCkge1xuICAgICAgLy8gaWYgc2Vla2luZyB0byB1bmJ1ZmZlcmVkIGFyZWEsIGNsZWFuIHVwIGZyYWdQcmV2aW91c1xuICAgICAgaWYgKGJ1ZmZlckluZm8ubGVuID09PSAwKSB7XG4gICAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gbnVsbDtcbiAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7XG4gICAgICB9IC8vIHN3aXRjaCB0byBJRExFIHN0YXRlIHRvIGNoZWNrIGZvciBwb3RlbnRpYWwgbmV3IGZyYWdtZW50XG5cblxuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgfVxuXG4gICAgaWYgKG1lZGlhKSB7XG4gICAgICB0aGlzLmxhc3RDdXJyZW50VGltZSA9IGN1cnJlbnRUaW1lO1xuICAgIH0gLy8gaW4gY2FzZSBzZWVraW5nIG9jY3VycyBhbHRob3VnaCBubyBtZWRpYSBidWZmZXJlZCwgYWRqdXN0IHN0YXJ0UG9zaXRpb24gYW5kIG5leHRMb2FkUG9zaXRpb24gdG8gc2VlayB0YXJnZXRcblxuXG4gICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhKSB7XG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb24gPSBjdXJyZW50VGltZTtcbiAgICB9IC8vIHRpY2sgdG8gc3BlZWQgdXAgcHJvY2Vzc2luZ1xuXG5cbiAgICB0aGlzLnRpY2soKTtcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYUVuZGVkID0gZnVuY3Rpb24gb25NZWRpYUVuZGVkKCkge1xuICAgIC8vIHJlc2V0IHN0YXJ0UG9zaXRpb24gYW5kIGxhc3RDdXJyZW50VGltZSB0byByZXN0YXJ0IHBsYXliYWNrIEAgc3RyZWFtIGJlZ2lubmluZ1xuICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IHRoaXMubGFzdEN1cnJlbnRUaW1lID0gMDtcbiAgfTtcblxuICBfcHJvdG8ub25IYW5kbGVyRGVzdHJveWluZyA9IGZ1bmN0aW9uIG9uSGFuZGxlckRlc3Ryb3lpbmcoKSB7XG4gICAgdGhpcy5zdG9wTG9hZCgpO1xuXG4gICAgX1Rhc2tMb29wLnByb3RvdHlwZS5vbkhhbmRsZXJEZXN0cm95aW5nLmNhbGwodGhpcyk7XG4gIH07XG5cbiAgX3Byb3RvLm9uSGFuZGxlckRlc3Ryb3llZCA9IGZ1bmN0aW9uIG9uSGFuZGxlckRlc3Ryb3llZCgpIHtcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuU1RPUFBFRDtcbiAgICB0aGlzLmZyYWdtZW50VHJhY2tlciA9IG51bGw7XG4gIH07XG5cbiAgX3Byb3RvLmNvbXB1dGVMaXZlUG9zaXRpb24gPSBmdW5jdGlvbiBjb21wdXRlTGl2ZVBvc2l0aW9uKHNsaWRpbmcsIGxldmVsRGV0YWlscykge1xuICAgIHZhciB0YXJnZXRMYXRlbmN5ID0gdGhpcy5jb25maWcubGl2ZVN5bmNEdXJhdGlvbiAhPT0gdW5kZWZpbmVkID8gdGhpcy5jb25maWcubGl2ZVN5bmNEdXJhdGlvbiA6IHRoaXMuY29uZmlnLmxpdmVTeW5jRHVyYXRpb25Db3VudCAqIGxldmVsRGV0YWlscy50YXJnZXRkdXJhdGlvbjtcbiAgICByZXR1cm4gc2xpZGluZyArIE1hdGgubWF4KDAsIGxldmVsRGV0YWlscy50b3RhbGR1cmF0aW9uIC0gdGFyZ2V0TGF0ZW5jeSk7XG4gIH07XG5cbiAgcmV0dXJuIEJhc2VTdHJlYW1Db250cm9sbGVyO1xufShUYXNrTG9vcCk7XG5cblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9zdHJlYW0tY29udHJvbGxlci5qc1xuXG5cblxuXG5cblxuXG5mdW5jdGlvbiBzdHJlYW1fY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gc3RyZWFtX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBzdHJlYW1fY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgc3RyZWFtX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gc3RyZWFtX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLypcbiAqIFN0cmVhbSBDb250cm9sbGVyXG4qL1xuXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG5cblxudmFyIFRJQ0tfSU5URVJWQUwgPSAxMDA7IC8vIGhvdyBvZnRlbiB0byB0aWNrIGluIG1zXG5cbnZhciBzdHJlYW1fY29udHJvbGxlcl9TdHJlYW1Db250cm9sbGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChfQmFzZVN0cmVhbUNvbnRyb2xsZXIpIHtcbiAgc3RyZWFtX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShTdHJlYW1Db250cm9sbGVyLCBfQmFzZVN0cmVhbUNvbnRyb2xsZXIpO1xuXG4gIGZ1bmN0aW9uIFN0cmVhbUNvbnRyb2xsZXIoaGxzLCBmcmFnbWVudFRyYWNrZXIpIHtcbiAgICB2YXIgX3RoaXM7XG5cbiAgICBfdGhpcyA9IF9CYXNlU3RyZWFtQ29udHJvbGxlci5jYWxsKHRoaXMsIGhscywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NRURJQV9BVFRBQ0hFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5NRURJQV9ERVRBQ0hJTkcsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUFOSUZFU1RfTE9BRElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9QQVJTRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uTEVWRUxfTE9BREVELCBldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMU19VUERBVEVELCBldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FEX0VNRVJHRU5DWV9BQk9SVEVELCBldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19JTklUX1NFR01FTlQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX0RBVEEsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfU1dJVENISU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLkFVRElPX1RSQUNLX1NXSVRDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9DUkVBVEVELCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfRkxVU0hFRCkgfHwgdGhpcztcbiAgICBfdGhpcy5mcmFnbWVudFRyYWNrZXIgPSBmcmFnbWVudFRyYWNrZXI7XG4gICAgX3RoaXMuY29uZmlnID0gaGxzLmNvbmZpZztcbiAgICBfdGhpcy5hdWRpb0NvZGVjU3dhcCA9IGZhbHNlO1xuICAgIF90aGlzLl9zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gICAgX3RoaXMuc3RhbGxSZXBvcnRlZCA9IGZhbHNlO1xuICAgIF90aGlzLmdhcENvbnRyb2xsZXIgPSBudWxsO1xuICAgIF90aGlzLmFsdEF1ZGlvID0gZmFsc2U7XG4gICAgX3RoaXMuYXVkaW9Pbmx5ID0gZmFsc2U7XG4gICAgX3RoaXMuYml0cmF0ZVRlc3QgPSBmYWxzZTtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLnN0YXJ0TG9hZCA9IGZ1bmN0aW9uIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKSB7XG4gICAgaWYgKHRoaXMubGV2ZWxzKSB7XG4gICAgICB2YXIgbGFzdEN1cnJlbnRUaW1lID0gdGhpcy5sYXN0Q3VycmVudFRpbWUsXG4gICAgICAgICAgaGxzID0gdGhpcy5obHM7XG4gICAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgICB0aGlzLnNldEludGVydmFsKFRJQ0tfSU5URVJWQUwpO1xuICAgICAgdGhpcy5sZXZlbCA9IC0xO1xuICAgICAgdGhpcy5mcmFnTG9hZEVycm9yID0gMDtcblxuICAgICAgaWYgKCF0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCkge1xuICAgICAgICAvLyBkZXRlcm1pbmUgbG9hZCBsZXZlbFxuICAgICAgICB2YXIgc3RhcnRMZXZlbCA9IGhscy5zdGFydExldmVsO1xuXG4gICAgICAgIGlmIChzdGFydExldmVsID09PSAtMSkge1xuICAgICAgICAgIGlmIChobHMuY29uZmlnLnRlc3RCYW5kd2lkdGgpIHtcbiAgICAgICAgICAgIC8vIC0xIDogZ3Vlc3Mgc3RhcnQgTGV2ZWwgYnkgZG9pbmcgYSBiaXRyYXRlIHRlc3QgYnkgbG9hZGluZyBmaXJzdCBmcmFnbWVudCBvZiBsb3dlc3QgcXVhbGl0eSBsZXZlbFxuICAgICAgICAgICAgc3RhcnRMZXZlbCA9IDA7XG4gICAgICAgICAgICB0aGlzLmJpdHJhdGVUZXN0ID0gdHJ1ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc3RhcnRMZXZlbCA9IGhscy5uZXh0QXV0b0xldmVsO1xuICAgICAgICAgIH1cbiAgICAgICAgfSAvLyBzZXQgbmV3IGxldmVsIHRvIHBsYXlsaXN0IGxvYWRlciA6IHRoaXMgd2lsbCB0cmlnZ2VyIHN0YXJ0IGxldmVsIGxvYWRcbiAgICAgICAgLy8gaGxzLm5leHRMb2FkTGV2ZWwgcmVtYWlucyB1bnRpbCBpdCBpcyBzZXQgdG8gYSBuZXcgdmFsdWUgb3IgdW50aWwgYSBuZXcgZnJhZyBpcyBzdWNjZXNzZnVsbHkgbG9hZGVkXG5cblxuICAgICAgICB0aGlzLmxldmVsID0gaGxzLm5leHRMb2FkTGV2ZWwgPSBzdGFydExldmVsO1xuICAgICAgICB0aGlzLmxvYWRlZG1ldGFkYXRhID0gZmFsc2U7XG4gICAgICB9IC8vIGlmIHN0YXJ0UG9zaXRpb24gdW5kZWZpbmVkIGJ1dCBsYXN0Q3VycmVudFRpbWUgc2V0LCBzZXQgc3RhcnRQb3NpdGlvbiB0byBsYXN0IGN1cnJlbnRUaW1lXG5cblxuICAgICAgaWYgKGxhc3RDdXJyZW50VGltZSA+IDAgJiYgc3RhcnRQb3NpdGlvbiA9PT0gLTEpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIm92ZXJyaWRlIHN0YXJ0UG9zaXRpb24gd2l0aCBsYXN0Q3VycmVudFRpbWUgQFwiICsgbGFzdEN1cnJlbnRUaW1lLnRvRml4ZWQoMykpO1xuICAgICAgICBzdGFydFBvc2l0aW9uID0gbGFzdEN1cnJlbnRUaW1lO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IHRoaXMuc3RhcnRQb3NpdGlvbiA9IHRoaXMubGFzdEN1cnJlbnRUaW1lID0gc3RhcnRQb3NpdGlvbjtcbiAgICAgIHRoaXMudGljaygpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmZvcmNlU3RhcnRMb2FkID0gdHJ1ZTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5TVE9QUEVEO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uc3RvcExvYWQgPSBmdW5jdGlvbiBzdG9wTG9hZCgpIHtcbiAgICB0aGlzLmZvcmNlU3RhcnRMb2FkID0gZmFsc2U7XG5cbiAgICBfQmFzZVN0cmVhbUNvbnRyb2xsZXIucHJvdG90eXBlLnN0b3BMb2FkLmNhbGwodGhpcyk7XG4gIH07XG5cbiAgX3Byb3RvLmRvVGljayA9IGZ1bmN0aW9uIGRvVGljaygpIHtcbiAgICBzd2l0Y2ggKHRoaXMuc3RhdGUpIHtcbiAgICAgIGNhc2UgU3RhdGUuQlVGRkVSX0ZMVVNISU5HOlxuICAgICAgICAvLyBpbiBidWZmZXIgZmx1c2hpbmcgc3RhdGUsIHJlc2V0IGZyYWdMb2FkRXJyb3IgY291bnRlclxuICAgICAgICB0aGlzLmZyYWdMb2FkRXJyb3IgPSAwO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBTdGF0ZS5JRExFOlxuICAgICAgICB0aGlzLl9kb1RpY2tJZGxlKCk7XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgU3RhdGUuV0FJVElOR19MRVZFTDpcbiAgICAgICAgdmFyIGxldmVsID0gdGhpcy5sZXZlbHNbdGhpcy5sZXZlbF07IC8vIGNoZWNrIGlmIHBsYXlsaXN0IGlzIGFscmVhZHkgbG9hZGVkXG5cbiAgICAgICAgaWYgKGxldmVsICYmIGxldmVsLmRldGFpbHMpIHtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlIFN0YXRlLkZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZOlxuICAgICAgICB2YXIgbm93ID0gd2luZG93LnBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICB2YXIgcmV0cnlEYXRlID0gdGhpcy5yZXRyeURhdGU7IC8vIGlmIGN1cnJlbnQgdGltZSBpcyBndCB0aGFuIHJldHJ5RGF0ZSwgb3IgaWYgbWVkaWEgc2Vla2luZyBsZXQncyBzd2l0Y2ggdG8gSURMRSBzdGF0ZSB0byByZXRyeSBsb2FkaW5nXG5cbiAgICAgICAgaWYgKCFyZXRyeURhdGUgfHwgbm93ID49IHJldHJ5RGF0ZSB8fCB0aGlzLm1lZGlhICYmIHRoaXMubWVkaWEuc2Vla2luZykge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ21lZGlhQ29udHJvbGxlcjogcmV0cnlEYXRlIHJlYWNoZWQsIHN3aXRjaCBiYWNrIHRvIElETEUgc3RhdGUnKTtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlIFN0YXRlLkVSUk9SOlxuICAgICAgY2FzZSBTdGF0ZS5TVE9QUEVEOlxuICAgICAgY2FzZSBTdGF0ZS5GUkFHX0xPQURJTkc6XG4gICAgICBjYXNlIFN0YXRlLlBBUlNJTkc6XG4gICAgICBjYXNlIFN0YXRlLlBBUlNFRDpcbiAgICAgIGNhc2UgU3RhdGUuRU5ERUQ6XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBicmVhaztcbiAgICB9IC8vIGNoZWNrIGJ1ZmZlclxuXG5cbiAgICB0aGlzLl9jaGVja0J1ZmZlcigpOyAvLyBjaGVjay91cGRhdGUgY3VycmVudCBmcmFnbWVudFxuXG5cbiAgICB0aGlzLl9jaGVja0ZyYWdtZW50Q2hhbmdlZCgpO1xuICB9IC8vIElyb25pY2FsbHkgdGhlIFwiaWRsZVwiIHN0YXRlIGlzIHRoZSBvbiB3ZSBkbyB0aGUgbW9zdCBsb2dpYyBpbiBpdCBzZWVtcyAuLi4uXG4gIC8vIE5PVEU6IE1heWJlIHdlIGNvdWxkIHJhdGhlciBzY2hlZHVsZSBhIGNoZWNrIGZvciBidWZmZXIgbGVuZ3RoIGFmdGVyIGhhbGYgb2YgdGhlIGN1cnJlbnRseVxuICAvLyAgICAgICBwbGF5ZWQgc2VnbWVudCwgb3Igb24gcGF1c2UvcGxheS9zZWVrIGluc3RlYWQgb2YgbmFpdmVseSBjaGVja2luZyBldmVyeSAxMDBtcz9cbiAgO1xuXG4gIF9wcm90by5fZG9UaWNrSWRsZSA9IGZ1bmN0aW9uIF9kb1RpY2tJZGxlKCkge1xuICAgIHZhciBobHMgPSB0aGlzLmhscyxcbiAgICAgICAgY29uZmlnID0gaGxzLmNvbmZpZyxcbiAgICAgICAgbWVkaWEgPSB0aGlzLm1lZGlhOyAvLyBpZiBzdGFydCBsZXZlbCBub3QgcGFyc2VkIHlldCBPUlxuICAgIC8vIGlmIHZpZGVvIG5vdCBhdHRhY2hlZCBBTkQgc3RhcnQgZnJhZ21lbnQgYWxyZWFkeSByZXF1ZXN0ZWQgT1Igc3RhcnQgZnJhZyBwcmVmZXRjaCBkaXNhYmxlXG4gICAgLy8gZXhpdCBsb29wLCBhcyB3ZSBlaXRoZXIgbmVlZCBtb3JlIGluZm8gKGxldmVsIG5vdCBwYXJzZWQpIG9yIHdlIG5lZWQgbWVkaWEgdG8gYmUgYXR0YWNoZWQgdG8gbG9hZCBuZXcgZnJhZ21lbnRcblxuICAgIGlmICh0aGlzLmxldmVsTGFzdExvYWRlZCA9PT0gdW5kZWZpbmVkIHx8ICFtZWRpYSAmJiAodGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgfHwgIWNvbmZpZy5zdGFydEZyYWdQcmVmZXRjaCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIElmIHRoZSBcIm1haW5cIiBsZXZlbCBpcyBhdWRpby1vbmx5IGJ1dCB3ZSBhcmUgbG9hZGluZyBhbiBhbHRlcm5hdGUgdHJhY2sgaW4gdGhlIHNhbWUgZ3JvdXAsIGRvIG5vdCBsb2FkIGFueXRoaW5nXG5cblxuICAgIGlmICh0aGlzLmFsdEF1ZGlvICYmIHRoaXMuYXVkaW9Pbmx5KSB7XG4gICAgICAvLyBDbGVhciBhdWRpbyBkZW11eGVyIHN0YXRlIHNvIHdoZW4gc3dpdGNoaW5nIGJhY2sgdG8gbWFpbiBhdWRpbyB3ZSdyZSBub3Qgc3RpbGwgYXBwZW5kaW5nIHdoZXJlIHdlIGxlZnQgb2ZmXG4gICAgICB0aGlzLmRlbXV4ZXIuZnJhZyA9IG51bGw7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBpZiB3ZSBoYXZlIG5vdCB5ZXQgbG9hZGVkIGFueSBmcmFnbWVudCwgc3RhcnQgbG9hZGluZyBmcm9tIHN0YXJ0IHBvc2l0aW9uXG5cblxuICAgIHZhciBwb3M7XG5cbiAgICBpZiAodGhpcy5sb2FkZWRtZXRhZGF0YSkge1xuICAgICAgcG9zID0gbWVkaWEuY3VycmVudFRpbWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHBvcyA9IHRoaXMubmV4dExvYWRQb3NpdGlvbjtcbiAgICB9IC8vIGRldGVybWluZSBuZXh0IGxvYWQgbGV2ZWxcblxuXG4gICAgdmFyIGxldmVsID0gaGxzLm5leHRMb2FkTGV2ZWwsXG4gICAgICAgIGxldmVsSW5mbyA9IHRoaXMubGV2ZWxzW2xldmVsXTtcblxuICAgIGlmICghbGV2ZWxJbmZvKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGxldmVsQml0cmF0ZSA9IGxldmVsSW5mby5iaXRyYXRlLFxuICAgICAgICBtYXhCdWZMZW47IC8vIGNvbXB1dGUgbWF4IEJ1ZmZlciBMZW5ndGggdGhhdCB3ZSBjb3VsZCBnZXQgZnJvbSB0aGlzIGxvYWQgbGV2ZWwsIGJhc2VkIG9uIGxldmVsIGJpdHJhdGUuXG5cbiAgICBpZiAobGV2ZWxCaXRyYXRlKSB7XG4gICAgICBtYXhCdWZMZW4gPSBNYXRoLm1heCg4ICogY29uZmlnLm1heEJ1ZmZlclNpemUgLyBsZXZlbEJpdHJhdGUsIGNvbmZpZy5tYXhCdWZmZXJMZW5ndGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBtYXhCdWZMZW4gPSBjb25maWcubWF4QnVmZmVyTGVuZ3RoO1xuICAgIH1cblxuICAgIG1heEJ1ZkxlbiA9IE1hdGgubWluKG1heEJ1ZkxlbiwgY29uZmlnLm1heE1heEJ1ZmZlckxlbmd0aCk7IC8vIGRldGVybWluZSBuZXh0IGNhbmRpZGF0ZSBmcmFnbWVudCB0byBiZSBsb2FkZWQsIGJhc2VkIG9uIGN1cnJlbnQgcG9zaXRpb24gYW5kIGVuZCBvZiBidWZmZXIgcG9zaXRpb25cbiAgICAvLyBlbnN1cmUgdXAgdG8gYGNvbmZpZy5tYXhNYXhCdWZmZXJMZW5ndGhgIG9mIGJ1ZmZlciB1cGZyb250XG5cbiAgICB2YXIgbWF4QnVmZmVySG9sZSA9IHBvcyA8IGNvbmZpZy5tYXhCdWZmZXJIb2xlID8gTWF0aC5tYXgoTUFYX1NUQVJUX0dBUF9KVU1QLCBjb25maWcubWF4QnVmZmVySG9sZSkgOiBjb25maWcubWF4QnVmZmVySG9sZTtcbiAgICB2YXIgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKHRoaXMubWVkaWFCdWZmZXIgPyB0aGlzLm1lZGlhQnVmZmVyIDogbWVkaWEsIHBvcywgbWF4QnVmZmVySG9sZSk7XG4gICAgdmFyIGJ1ZmZlckxlbiA9IGJ1ZmZlckluZm8ubGVuOyAvLyBTdGF5IGlkbGUgaWYgd2UgYXJlIHN0aWxsIHdpdGggYnVmZmVyIG1hcmdpbnNcblxuICAgIGlmIChidWZmZXJMZW4gPj0gbWF4QnVmTGVuKSB7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBpZiBidWZmZXIgbGVuZ3RoIGlzIGxlc3MgdGhhbiBtYXhCdWZMZW4gdHJ5IHRvIGxvYWQgYSBuZXcgZnJhZ21lbnQgLi4uXG5cblxuICAgIGxvZ2dlcltcImxvZ2dlclwiXS50cmFjZShcImJ1ZmZlciBsZW5ndGggb2YgXCIgKyBidWZmZXJMZW4udG9GaXhlZCgzKSArIFwiIGlzIGJlbG93IG1heCBvZiBcIiArIG1heEJ1Zkxlbi50b0ZpeGVkKDMpICsgXCIuIGNoZWNraW5nIGZvciBtb3JlIHBheWxvYWQgLi4uXCIpOyAvLyBzZXQgbmV4dCBsb2FkIGxldmVsIDogdGhpcyB3aWxsIHRyaWdnZXIgYSBwbGF5bGlzdCBsb2FkIGlmIG5lZWRlZFxuXG4gICAgdGhpcy5sZXZlbCA9IGhscy5uZXh0TG9hZExldmVsID0gbGV2ZWw7XG4gICAgdmFyIGxldmVsRGV0YWlscyA9IGxldmVsSW5mby5kZXRhaWxzOyAvLyBpZiBsZXZlbCBpbmZvIG5vdCByZXRyaWV2ZWQgeWV0LCBzd2l0Y2ggc3RhdGUgYW5kIHdhaXQgZm9yIGxldmVsIHJldHJpZXZhbFxuICAgIC8vIGlmIGxpdmUgcGxheWxpc3QsIGVuc3VyZSB0aGF0IG5ldyBwbGF5bGlzdCBoYXMgYmVlbiByZWZyZXNoZWQgdG8gYXZvaWQgbG9hZGluZy90cnkgdG8gbG9hZFxuICAgIC8vIGEgdXNlbGVzcyBhbmQgb3V0ZGF0ZWQgZnJhZ21lbnQgKHRoYXQgbWlnaHQgZXZlbiBpbnRyb2R1Y2UgbG9hZCBlcnJvciBpZiBpdCBpcyBhbHJlYWR5IG91dCBvZiB0aGUgbGl2ZSBwbGF5bGlzdClcblxuICAgIGlmICghbGV2ZWxEZXRhaWxzIHx8IGxldmVsRGV0YWlscy5saXZlICYmIHRoaXMubGV2ZWxMYXN0TG9hZGVkICE9PSBsZXZlbCkge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfTEVWRUw7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX3N0cmVhbUVuZGVkKGJ1ZmZlckluZm8sIGxldmVsRGV0YWlscykpIHtcbiAgICAgIHZhciBkYXRhID0ge307XG5cbiAgICAgIGlmICh0aGlzLmFsdEF1ZGlvKSB7XG4gICAgICAgIGRhdGEudHlwZSA9ICd2aWRlbyc7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfRU9TLCBkYXRhKTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5FTkRFRDtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIGlmIHdlIGhhdmUgdGhlIGxldmVsRGV0YWlscyBmb3IgdGhlIHNlbGVjdGVkIHZhcmlhbnQsIGxldHMgY29udGludWUgZW5yaWNoZW4gb3VyIHN0cmVhbSAobG9hZCBrZXlzL2ZyYWdtZW50cyBvciB0cmlnZ2VyIEVPUywgZXRjLi4pXG5cblxuICAgIHRoaXMuX2ZldGNoUGF5bG9hZE9yRW9zKHBvcywgYnVmZmVySW5mbywgbGV2ZWxEZXRhaWxzKTtcbiAgfTtcblxuICBfcHJvdG8uX2ZldGNoUGF5bG9hZE9yRW9zID0gZnVuY3Rpb24gX2ZldGNoUGF5bG9hZE9yRW9zKHBvcywgYnVmZmVySW5mbywgbGV2ZWxEZXRhaWxzKSB7XG4gICAgdmFyIGZyYWdQcmV2aW91cyA9IHRoaXMuZnJhZ1ByZXZpb3VzLFxuICAgICAgICBsZXZlbCA9IHRoaXMubGV2ZWwsXG4gICAgICAgIGZyYWdtZW50cyA9IGxldmVsRGV0YWlscy5mcmFnbWVudHMsXG4gICAgICAgIGZyYWdMZW4gPSBmcmFnbWVudHMubGVuZ3RoOyAvLyBlbXB0eSBwbGF5bGlzdFxuXG4gICAgaWYgKGZyYWdMZW4gPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIGZpbmQgZnJhZ21lbnQgaW5kZXgsIGNvbnRpZ3VvdXMgd2l0aCBlbmQgb2YgYnVmZmVyIHBvc2l0aW9uXG5cblxuICAgIHZhciBzdGFydCA9IGZyYWdtZW50c1swXS5zdGFydCxcbiAgICAgICAgZW5kID0gZnJhZ21lbnRzW2ZyYWdMZW4gLSAxXS5zdGFydCArIGZyYWdtZW50c1tmcmFnTGVuIC0gMV0uZHVyYXRpb24sXG4gICAgICAgIGJ1ZmZlckVuZCA9IGJ1ZmZlckluZm8uZW5kLFxuICAgICAgICBmcmFnO1xuXG4gICAgaWYgKGxldmVsRGV0YWlscy5pbml0U2VnbWVudCAmJiAhbGV2ZWxEZXRhaWxzLmluaXRTZWdtZW50LmRhdGEpIHtcbiAgICAgIGZyYWcgPSBsZXZlbERldGFpbHMuaW5pdFNlZ21lbnQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGluIGNhc2Ugb2YgbGl2ZSBwbGF5bGlzdCB3ZSBuZWVkIHRvIGVuc3VyZSB0aGF0IHJlcXVlc3RlZCBwb3NpdGlvbiBpcyBub3QgbG9jYXRlZCBiZWZvcmUgcGxheWxpc3Qgc3RhcnRcbiAgICAgIGlmIChsZXZlbERldGFpbHMubGl2ZSkge1xuICAgICAgICB2YXIgaW5pdGlhbExpdmVNYW5pZmVzdFNpemUgPSB0aGlzLmNvbmZpZy5pbml0aWFsTGl2ZU1hbmlmZXN0U2l6ZTtcblxuICAgICAgICBpZiAoZnJhZ0xlbiA8IGluaXRpYWxMaXZlTWFuaWZlc3RTaXplKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJDYW4gbm90IHN0YXJ0IHBsYXliYWNrIG9mIGEgbGV2ZWwsIHJlYXNvbjogbm90IGVub3VnaCBmcmFnbWVudHMgXCIgKyBmcmFnTGVuICsgXCIgPCBcIiArIGluaXRpYWxMaXZlTWFuaWZlc3RTaXplKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBmcmFnID0gdGhpcy5fZW5zdXJlRnJhZ21lbnRBdExpdmVQb2ludChsZXZlbERldGFpbHMsIGJ1ZmZlckVuZCwgc3RhcnQsIGVuZCwgZnJhZ1ByZXZpb3VzLCBmcmFnbWVudHMpOyAvLyBpZiBpdCBleHBsaWNpdGVseSByZXR1cm5zIG51bGwgZG9uJ3QgbG9hZCBhbnkgZnJhZ21lbnQgYW5kIGV4aXQgZnVuY3Rpb24gbm93XG5cbiAgICAgICAgaWYgKGZyYWcgPT09IG51bGwpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFZvRCBwbGF5bGlzdDogaWYgYnVmZmVyRW5kIGJlZm9yZSBzdGFydCBvZiBwbGF5bGlzdCwgbG9hZCBmaXJzdCBmcmFnbWVudFxuICAgICAgICBpZiAoYnVmZmVyRW5kIDwgc3RhcnQpIHtcbiAgICAgICAgICBmcmFnID0gZnJhZ21lbnRzWzBdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFmcmFnKSB7XG4gICAgICBmcmFnID0gdGhpcy5fZmluZEZyYWdtZW50KHN0YXJ0LCBmcmFnUHJldmlvdXMsIGZyYWdMZW4sIGZyYWdtZW50cywgYnVmZmVyRW5kLCBlbmQsIGxldmVsRGV0YWlscyk7XG4gICAgfVxuXG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIGlmIChmcmFnLmVuY3J5cHRlZCkge1xuICAgICAgICB0aGlzLl9sb2FkS2V5KGZyYWcsIGxldmVsRGV0YWlscyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9sb2FkRnJhZ21lbnQoZnJhZywgbGV2ZWxEZXRhaWxzLCBwb3MsIGJ1ZmZlckVuZCk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5fZW5zdXJlRnJhZ21lbnRBdExpdmVQb2ludCA9IGZ1bmN0aW9uIF9lbnN1cmVGcmFnbWVudEF0TGl2ZVBvaW50KGxldmVsRGV0YWlscywgYnVmZmVyRW5kLCBzdGFydCwgZW5kLCBmcmFnUHJldmlvdXMsIGZyYWdtZW50cykge1xuICAgIHZhciBjb25maWcgPSB0aGlzLmhscy5jb25maWcsXG4gICAgICAgIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICB2YXIgZnJhZzsgLy8gY2hlY2sgaWYgcmVxdWVzdGVkIHBvc2l0aW9uIGlzIHdpdGhpbiBzZWVrYWJsZSBib3VuZGFyaWVzIDpcbiAgICAvLyBsb2dnZXIubG9nKGBzdGFydC9wb3MvYnVmRW5kL3NlZWtpbmc6JHtzdGFydC50b0ZpeGVkKDMpfS8ke3Bvcy50b0ZpeGVkKDMpfS8ke2J1ZmZlckVuZC50b0ZpeGVkKDMpfS8ke3RoaXMubWVkaWEuc2Vla2luZ31gKTtcblxuICAgIHZhciBtYXhMYXRlbmN5ID0gSW5maW5pdHk7XG5cbiAgICBpZiAoY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb24gIT09IHVuZGVmaW5lZCkge1xuICAgICAgbWF4TGF0ZW5jeSA9IGNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uO1xuICAgIH0gZWxzZSBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShjb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50KSkge1xuICAgICAgbWF4TGF0ZW5jeSA9IGNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnQgKiBsZXZlbERldGFpbHMudGFyZ2V0ZHVyYXRpb247XG4gICAgfVxuXG4gICAgaWYgKGJ1ZmZlckVuZCA8IE1hdGgubWF4KHN0YXJ0IC0gY29uZmlnLm1heEZyYWdMb29rVXBUb2xlcmFuY2UsIGVuZCAtIG1heExhdGVuY3kpKSB7XG4gICAgICB2YXIgbGl2ZVN5bmNQb3NpdGlvbiA9IHRoaXMubGl2ZVN5bmNQb3NpdGlvbiA9IHRoaXMuY29tcHV0ZUxpdmVQb3NpdGlvbihzdGFydCwgbGV2ZWxEZXRhaWxzKTtcbiAgICAgIGJ1ZmZlckVuZCA9IGxpdmVTeW5jUG9zaXRpb247XG5cbiAgICAgIGlmIChtZWRpYSAmJiAhbWVkaWEucGF1c2VkICYmIG1lZGlhLnJlYWR5U3RhdGUgJiYgbWVkaWEuZHVyYXRpb24gPiBsaXZlU3luY1Bvc2l0aW9uICYmIGxpdmVTeW5jUG9zaXRpb24gPiBtZWRpYS5jdXJyZW50VGltZSkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiYnVmZmVyIGVuZDogXCIgKyBidWZmZXJFbmQudG9GaXhlZCgzKSArIFwiIGlzIGxvY2F0ZWQgdG9vIGZhciBmcm9tIHRoZSBlbmQgb2YgbGl2ZSBzbGlkaW5nIHBsYXlsaXN0LCByZXNldCBjdXJyZW50VGltZSB0byA6IFwiICsgbGl2ZVN5bmNQb3NpdGlvbi50b0ZpeGVkKDMpKTtcbiAgICAgICAgbWVkaWEuY3VycmVudFRpbWUgPSBsaXZlU3luY1Bvc2l0aW9uO1xuICAgICAgfVxuXG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSBsaXZlU3luY1Bvc2l0aW9uO1xuICAgIH0gLy8gaWYgZW5kIG9mIGJ1ZmZlciBncmVhdGVyIHRoYW4gbGl2ZSBlZGdlLCBkb24ndCBsb2FkIGFueSBmcmFnbWVudFxuICAgIC8vIHRoaXMgY291bGQgaGFwcGVuIGlmIGxpdmUgcGxheWxpc3QgaW50ZXJtaXR0ZW50bHkgc2xpZGVzIGluIHRoZSBwYXN0LlxuICAgIC8vIGxldmVsIDEgbG9hZGVkIFsxODI1ODAxNjEsMTgyNTgwMTY3XVxuICAgIC8vIGxldmVsIDEgbG9hZGVkIFsxODI1ODAxNjIsMTgyNTgwMTY5XVxuICAgIC8vIExvYWRpbmcgMTgyNTgwMTY4IG9mIFsxODI1ODAxNjIgLDE4MjU4MDE2OV0sbGV2ZWwgMSAuLlxuICAgIC8vIExvYWRpbmcgMTgyNTgwMTY5IG9mIFsxODI1ODAxNjIgLDE4MjU4MDE2OV0sbGV2ZWwgMSAuLlxuICAgIC8vIGxldmVsIDEgbG9hZGVkIFsxODI1ODAxNjIsMTgyNTgwMTY4XSA8PT09PT09PT09PT09PSBoZXJlIHdlIHNob3VsZCBoYXZlIGJ1ZmZlckVuZCA+IGVuZC4gaW4gdGhhdCBjYXNlIGJyZWFrIHRvIGF2b2lkIHJlbG9hZGluZyAxODI1ODAxNjhcbiAgICAvLyBsZXZlbCAxIGxvYWRlZCBbMTgyNTgwMTY0LDE4MjU4MDE3MV1cbiAgICAvL1xuICAgIC8vIGRvbid0IHJldHVybiBudWxsIGluIGNhc2UgbWVkaWEgbm90IGxvYWRlZCB5ZXQgKHJlYWR5c3RhdGUgPT09IDApXG5cblxuICAgIGlmIChsZXZlbERldGFpbHMuUFRTS25vd24gJiYgYnVmZmVyRW5kID4gZW5kICYmIG1lZGlhICYmIG1lZGlhLnJlYWR5U3RhdGUpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCAmJiAhbGV2ZWxEZXRhaWxzLlBUU0tub3duKSB7XG4gICAgICAvKiB3ZSBhcmUgc3dpdGNoaW5nIGxldmVsIG9uIGxpdmUgcGxheWxpc3QsIGJ1dCB3ZSBkb24ndCBoYXZlIGFueSBQVFMgaW5mbyBmb3IgdGhhdCBxdWFsaXR5IGxldmVsIC4uLlxuICAgICAgICAgdHJ5IHRvIGxvYWQgZnJhZyBtYXRjaGluZyB3aXRoIG5leHQgU04uXG4gICAgICAgICBldmVuIGlmIFNOIGFyZSBub3Qgc3luY2hyb25pemVkIGJldHdlZW4gcGxheWxpc3RzLCBsb2FkaW5nIHRoaXMgZnJhZyB3aWxsIGhlbHAgdXNcbiAgICAgICAgIGNvbXB1dGUgcGxheWxpc3Qgc2xpZGluZyBhbmQgZmluZCB0aGUgcmlnaHQgb25lIGFmdGVyIGluIGNhc2UgaXQgd2FzIG5vdCB0aGUgcmlnaHQgY29uc2VjdXRpdmUgb25lICovXG4gICAgICBpZiAoZnJhZ1ByZXZpb3VzKSB7XG4gICAgICAgIGlmIChsZXZlbERldGFpbHMuaGFzUHJvZ3JhbURhdGVUaW1lKSB7XG4gICAgICAgICAgLy8gUmVsaWVzIG9uIFBEVCBpbiBvcmRlciB0byBzd2l0Y2ggYml0cmF0ZXMgKFN1cHBvcnQgRVhULVgtRElTQ09OVElOVUlUWSB3aXRob3V0IEVYVC1YLURJU0NPTlRJTlVJVFktU0VRVUVOQ0UpXG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImxpdmUgcGxheWxpc3QsIHN3aXRjaGluZyBwbGF5bGlzdCwgbG9hZCBmcmFnIHdpdGggc2FtZSBQRFQ6IFwiICsgZnJhZ1ByZXZpb3VzLnByb2dyYW1EYXRlVGltZSk7XG4gICAgICAgICAgZnJhZyA9IGZpbmRGcmFnbWVudEJ5UERUKGZyYWdtZW50cywgZnJhZ1ByZXZpb3VzLmVuZFByb2dyYW1EYXRlVGltZSwgY29uZmlnLm1heEZyYWdMb29rVXBUb2xlcmFuY2UpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIFVzZXMgYnVmZmVyIGFuZCBzZXF1ZW5jZSBudW1iZXIgdG8gY2FsY3VsYXRlIHN3aXRjaCBzZWdtZW50IChyZXF1aXJlZCBpZiB1c2luZyBFWFQtWC1ESVNDT05USU5VSVRZLVNFUVVFTkNFKVxuICAgICAgICAgIHZhciB0YXJnZXRTTiA9IGZyYWdQcmV2aW91cy5zbiArIDE7XG5cbiAgICAgICAgICBpZiAodGFyZ2V0U04gPj0gbGV2ZWxEZXRhaWxzLnN0YXJ0U04gJiYgdGFyZ2V0U04gPD0gbGV2ZWxEZXRhaWxzLmVuZFNOKSB7XG4gICAgICAgICAgICB2YXIgZnJhZ05leHQgPSBmcmFnbWVudHNbdGFyZ2V0U04gLSBsZXZlbERldGFpbHMuc3RhcnRTTl07XG5cbiAgICAgICAgICAgIGlmIChmcmFnUHJldmlvdXMuY2MgPT09IGZyYWdOZXh0LmNjKSB7XG4gICAgICAgICAgICAgIGZyYWcgPSBmcmFnTmV4dDtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImxpdmUgcGxheWxpc3QsIHN3aXRjaGluZyBwbGF5bGlzdCwgbG9hZCBmcmFnIHdpdGggbmV4dCBTTjogXCIgKyBmcmFnLnNuKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IC8vIG5leHQgZnJhZyBTTiBub3QgYXZhaWxhYmxlIChvciBub3Qgd2l0aCBzYW1lIGNvbnRpbnVpdHkgY291bnRlcilcbiAgICAgICAgICAvLyBsb29rIGZvciBhIGZyYWcgc2hhcmluZyB0aGUgc2FtZSBDQ1xuXG5cbiAgICAgICAgICBpZiAoIWZyYWcpIHtcbiAgICAgICAgICAgIGZyYWcgPSBiaW5hcnlfc2VhcmNoLnNlYXJjaChmcmFnbWVudHMsIGZ1bmN0aW9uIChmcmFnKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmcmFnUHJldmlvdXMuY2MgLSBmcmFnLmNjO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJsaXZlIHBsYXlsaXN0LCBzd2l0Y2hpbmcgcGxheWxpc3QsIGxvYWQgZnJhZyB3aXRoIHNhbWUgQ0M6IFwiICsgZnJhZy5zbik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGZyYWc7XG4gIH07XG5cbiAgX3Byb3RvLl9maW5kRnJhZ21lbnQgPSBmdW5jdGlvbiBfZmluZEZyYWdtZW50KHN0YXJ0LCBmcmFnUHJldmlvdXNMb2FkLCBmcmFnbWVudEluZGV4UmFuZ2UsIGZyYWdtZW50cywgYnVmZmVyRW5kLCBlbmQsIGxldmVsRGV0YWlscykge1xuICAgIHZhciBjb25maWcgPSB0aGlzLmhscy5jb25maWc7XG4gICAgdmFyIGZyYWdOZXh0TG9hZDtcblxuICAgIGlmIChidWZmZXJFbmQgPCBlbmQpIHtcbiAgICAgIHZhciBsb29rdXBUb2xlcmFuY2UgPSBidWZmZXJFbmQgPiBlbmQgLSBjb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA/IDAgOiBjb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTsgLy8gUmVtb3ZlIHRoZSB0b2xlcmFuY2UgaWYgaXQgd291bGQgcHV0IHRoZSBidWZmZXJFbmQgcGFzdCB0aGUgYWN0dWFsIGVuZCBvZiBzdHJlYW1cbiAgICAgIC8vIFVzZXMgYnVmZmVyIGFuZCBzZXF1ZW5jZSBudW1iZXIgdG8gY2FsY3VsYXRlIHN3aXRjaCBzZWdtZW50IChyZXF1aXJlZCBpZiB1c2luZyBFWFQtWC1ESVNDT05USU5VSVRZLVNFUVVFTkNFKVxuXG4gICAgICBmcmFnTmV4dExvYWQgPSBmaW5kRnJhZ21lbnRCeVBUUyhmcmFnUHJldmlvdXNMb2FkLCBmcmFnbWVudHMsIGJ1ZmZlckVuZCwgbG9va3VwVG9sZXJhbmNlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gcmVhY2ggZW5kIG9mIHBsYXlsaXN0XG4gICAgICBmcmFnTmV4dExvYWQgPSBmcmFnbWVudHNbZnJhZ21lbnRJbmRleFJhbmdlIC0gMV07XG4gICAgfVxuXG4gICAgaWYgKGZyYWdOZXh0TG9hZCkge1xuICAgICAgdmFyIGN1clNOSWR4ID0gZnJhZ05leHRMb2FkLnNuIC0gbGV2ZWxEZXRhaWxzLnN0YXJ0U047XG4gICAgICB2YXIgc2FtZUxldmVsID0gZnJhZ1ByZXZpb3VzTG9hZCAmJiBmcmFnTmV4dExvYWQubGV2ZWwgPT09IGZyYWdQcmV2aW91c0xvYWQubGV2ZWw7XG4gICAgICB2YXIgcHJldlNuRnJhZyA9IGZyYWdtZW50c1tjdXJTTklkeCAtIDFdO1xuICAgICAgdmFyIG5leHRTbkZyYWcgPSBmcmFnbWVudHNbY3VyU05JZHggKyAxXTsgLy8gbG9nZ2VyLmxvZygnZmluZCBTTiBtYXRjaGluZyB3aXRoIHBvczonICsgIGJ1ZmZlckVuZCArICc6JyArIGZyYWcuc24pO1xuXG4gICAgICBpZiAoZnJhZ1ByZXZpb3VzTG9hZCAmJiBmcmFnTmV4dExvYWQuc24gPT09IGZyYWdQcmV2aW91c0xvYWQuc24pIHtcbiAgICAgICAgaWYgKHNhbWVMZXZlbCAmJiAhZnJhZ05leHRMb2FkLmJhY2t0cmFja2VkKSB7XG4gICAgICAgICAgaWYgKGZyYWdOZXh0TG9hZC5zbiA8IGxldmVsRGV0YWlscy5lbmRTTikge1xuICAgICAgICAgICAgdmFyIGRlbHRhUFRTID0gZnJhZ1ByZXZpb3VzTG9hZC5kZWx0YVBUUzsgLy8gaWYgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkZWx0YSBiZXR3ZWVuIGF1ZGlvIGFuZCB2aWRlbywgbGFyZ2VyIHRoYW4gbWF4IGFsbG93ZWQgaG9sZSxcbiAgICAgICAgICAgIC8vIGFuZCBpZiBwcmV2aW91cyByZW11eGVkIGZyYWdtZW50IGRpZCBub3Qgc3RhcnQgd2l0aCBhIGtleWZyYW1lLiAoZnJhZ1ByZXZpb3VzLmRyb3BwZWQpXG4gICAgICAgICAgICAvLyBsZXQncyB0cnkgdG8gbG9hZCBwcmV2aW91cyBmcmFnbWVudCBhZ2FpbiB0byBnZXQgbGFzdCBrZXlmcmFtZVxuICAgICAgICAgICAgLy8gdGhlbiB3ZSB3aWxsIHJlbG9hZCBhZ2FpbiBjdXJyZW50IGZyYWdtZW50ICh0aGF0IHdheSB3ZSBzaG91bGQgYmUgYWJsZSB0byBmaWxsIHRoZSBidWZmZXIgaG9sZSAuLi4pXG5cbiAgICAgICAgICAgIGlmIChkZWx0YVBUUyAmJiBkZWx0YVBUUyA+IGNvbmZpZy5tYXhCdWZmZXJIb2xlICYmIGZyYWdQcmV2aW91c0xvYWQuZHJvcHBlZCAmJiBjdXJTTklkeCkge1xuICAgICAgICAgICAgICBmcmFnTmV4dExvYWQgPSBwcmV2U25GcmFnO1xuICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignUHJldmlvdXMgZnJhZ21lbnQgd2FzIGRyb3BwZWQgd2l0aCBsYXJnZSBQVFMgZ2FwIGJldHdlZW4gYXVkaW8gYW5kIHZpZGVvLiBNYXliZSBmcmFnbWVudCBpcyBub3Qgc3RhcnRpbmcgd2l0aCBhIGtleWZyYW1lPyBMb2FkaW5nIHByZXZpb3VzIG9uZSB0byB0cnkgdG8gb3ZlcmNvbWUgdGhpcycpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgZnJhZ05leHRMb2FkID0gbmV4dFNuRnJhZztcblxuICAgICAgICAgICAgICBpZiAodGhpcy5mcmFnbWVudFRyYWNrZXIuZ2V0U3RhdGUoZnJhZ05leHRMb2FkKSAhPT0gRnJhZ21lbnRTdGF0ZS5PSykge1xuICAgICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJSZS1sb2FkaW5nIGZyYWdtZW50IHdpdGggU046IFwiICsgZnJhZ05leHRMb2FkLnNuKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmcmFnTmV4dExvYWQgPSBudWxsO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChmcmFnTmV4dExvYWQuYmFja3RyYWNrZWQpIHtcbiAgICAgICAgICAvLyBPbmx5IGJhY2t0cmFjayBhIG1heCBvZiAxIGNvbnNlY3V0aXZlIGZyYWdtZW50IHRvIHByZXZlbnQgc2xpZGluZyBiYWNrIHRvbyBmYXIgd2hlbiBsaXR0bGUgb3Igbm8gZnJhZ3Mgc3RhcnQgd2l0aCBrZXlmcmFtZXNcbiAgICAgICAgICBpZiAobmV4dFNuRnJhZyAmJiBuZXh0U25GcmFnLmJhY2t0cmFja2VkKSB7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcIkFscmVhZHkgYmFja3RyYWNrZWQgZnJvbSBmcmFnbWVudCBcIiArIG5leHRTbkZyYWcuc24gKyBcIiwgd2lsbCBub3QgYmFja3RyYWNrIHRvIGZyYWdtZW50IFwiICsgZnJhZ05leHRMb2FkLnNuICsgXCIuIExvYWRpbmcgZnJhZ21lbnQgXCIgKyBuZXh0U25GcmFnLnNuKTtcbiAgICAgICAgICAgIGZyYWdOZXh0TG9hZCA9IG5leHRTbkZyYWc7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIElmIGEgZnJhZ21lbnQgaGFzIGRyb3BwZWQgZnJhbWVzIGFuZCBpdCdzIGluIGEgc2FtZSBsZXZlbC9zZXF1ZW5jZSwgbG9hZCB0aGUgcHJldmlvdXMgZnJhZ21lbnQgdG8gdHJ5IGFuZCBmaW5kIHRoZSBrZXlmcmFtZVxuICAgICAgICAgICAgLy8gUmVzZXQgdGhlIGRyb3BwZWQgY291bnQgbm93IHNpbmNlIGl0IHdvbid0IGJlIHJlc2V0IHVudGlsIHdlIHBhcnNlIHRoZSBmcmFnbWVudCBhZ2Fpbiwgd2hpY2ggcHJldmVudHMgaW5maW5pdGUgYmFja3RyYWNraW5nIG9uIHRoZSBzYW1lIHNlZ21lbnRcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdMb2FkZWQgZnJhZ21lbnQgd2l0aCBkcm9wcGVkIGZyYW1lcywgYmFja3RyYWNraW5nIDEgc2VnbWVudCB0byBmaW5kIGEga2V5ZnJhbWUnKTtcbiAgICAgICAgICAgIGZyYWdOZXh0TG9hZC5kcm9wcGVkID0gMDtcblxuICAgICAgICAgICAgaWYgKHByZXZTbkZyYWcpIHtcbiAgICAgICAgICAgICAgZnJhZ05leHRMb2FkID0gcHJldlNuRnJhZztcbiAgICAgICAgICAgICAgZnJhZ05leHRMb2FkLmJhY2t0cmFja2VkID0gdHJ1ZTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VyU05JZHgpIHtcbiAgICAgICAgICAgICAgLy8gY2FuJ3QgYmFja3RyYWNrIG9uIHZlcnkgZmlyc3QgZnJhZ21lbnRcbiAgICAgICAgICAgICAgZnJhZ05leHRMb2FkID0gbnVsbDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZnJhZ05leHRMb2FkO1xuICB9O1xuXG4gIF9wcm90by5fbG9hZEtleSA9IGZ1bmN0aW9uIF9sb2FkS2V5KGZyYWcsIGxldmVsRGV0YWlscykge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJMb2FkaW5nIGtleSBmb3IgXCIgKyBmcmFnLnNuICsgXCIgb2YgW1wiICsgbGV2ZWxEZXRhaWxzLnN0YXJ0U04gKyBcIi1cIiArIGxldmVsRGV0YWlscy5lbmRTTiArIFwiXSwgbGV2ZWwgXCIgKyB0aGlzLmxldmVsKTtcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuS0VZX0xPQURJTkc7XG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FESU5HLCB7XG4gICAgICBmcmFnOiBmcmFnXG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLl9sb2FkRnJhZ21lbnQgPSBmdW5jdGlvbiBfbG9hZEZyYWdtZW50KGZyYWcsIGxldmVsRGV0YWlscywgcG9zLCBidWZmZXJFbmQpIHtcbiAgICAvLyBDaGVjayBpZiBmcmFnbWVudCBpcyBub3QgbG9hZGVkXG4gICAgdmFyIGZyYWdTdGF0ZSA9IHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBmcmFnO1xuXG4gICAgaWYgKGZyYWcuc24gIT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gdHJ1ZTtcbiAgICB9IC8vIERvbid0IHVwZGF0ZSBuZXh0TG9hZFBvc2l0aW9uIGZvciBmcmFnbWVudHMgd2hpY2ggYXJlIG5vdCBidWZmZXJlZFxuXG5cbiAgICBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShmcmFnLnNuKSAmJiAhZnJhZy5iaXRyYXRlVGVzdCkge1xuICAgICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gZnJhZy5zdGFydCArIGZyYWcuZHVyYXRpb247XG4gICAgfSAvLyBBbGxvdyBiYWNrdHJhY2tlZCBmcmFnbWVudHMgdG8gbG9hZFxuXG5cbiAgICBpZiAoZnJhZy5iYWNrdHJhY2tlZCB8fCBmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuTk9UX0xPQURFRCB8fCBmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuUEFSVElBTCkge1xuICAgICAgZnJhZy5hdXRvTGV2ZWwgPSB0aGlzLmhscy5hdXRvTGV2ZWxFbmFibGVkO1xuICAgICAgZnJhZy5iaXRyYXRlVGVzdCA9IHRoaXMuYml0cmF0ZVRlc3Q7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiTG9hZGluZyBcIiArIGZyYWcuc24gKyBcIiBvZiBbXCIgKyBsZXZlbERldGFpbHMuc3RhcnRTTiArIFwiLVwiICsgbGV2ZWxEZXRhaWxzLmVuZFNOICsgXCJdLCBsZXZlbCBcIiArIHRoaXMubGV2ZWwgKyBcIiwgXCIgKyAodGhpcy5sb2FkZWRtZXRhZGF0YSA/ICdjdXJyZW50VGltZScgOiAnbmV4dExvYWRQb3NpdGlvbicpICsgXCI6IFwiICsgcGFyc2VGbG9hdChwb3MudG9GaXhlZCgzKSkgKyBcIiwgYnVmZmVyRW5kOiBcIiArIHBhcnNlRmxvYXQoYnVmZmVyRW5kLnRvRml4ZWQoMykpKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0xPQURJTkcsIHtcbiAgICAgICAgZnJhZzogZnJhZ1xuICAgICAgfSk7IC8vIGxhenkgZGVtdXhlciBpbml0LCBhcyB0aGlzIGNvdWxkIHRha2Ugc29tZSB0aW1lIC4uLiBkbyBpdCBkdXJpbmcgZnJhZyBsb2FkaW5nXG5cbiAgICAgIGlmICghdGhpcy5kZW11eGVyKSB7XG4gICAgICAgIHRoaXMuZGVtdXhlciA9IG5ldyBkZW11eF9kZW11eGVyKHRoaXMuaGxzLCAnbWFpbicpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRlJBR19MT0FESU5HO1xuICAgIH0gZWxzZSBpZiAoZnJhZ1N0YXRlID09PSBGcmFnbWVudFN0YXRlLkFQUEVORElORykge1xuICAgICAgLy8gTG93ZXIgdGhlIGJ1ZmZlciBzaXplIGFuZCB0cnkgYWdhaW5cbiAgICAgIGlmICh0aGlzLl9yZWR1Y2VNYXhCdWZmZXJMZW5ndGgoZnJhZy5kdXJhdGlvbikpIHtcbiAgICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5nZXRCdWZmZXJlZEZyYWcgPSBmdW5jdGlvbiBnZXRCdWZmZXJlZEZyYWcocG9zaXRpb24pIHtcbiAgICByZXR1cm4gdGhpcy5mcmFnbWVudFRyYWNrZXIuZ2V0QnVmZmVyZWRGcmFnKHBvc2l0aW9uLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOKTtcbiAgfTtcblxuICBfcHJvdG8uZm9sbG93aW5nQnVmZmVyZWRGcmFnID0gZnVuY3Rpb24gZm9sbG93aW5nQnVmZmVyZWRGcmFnKGZyYWcpIHtcbiAgICBpZiAoZnJhZykge1xuICAgICAgLy8gdHJ5IHRvIGdldCByYW5nZSBvZiBuZXh0IGZyYWdtZW50ICg1MDBtcyBhZnRlciB0aGlzIHJhbmdlKVxuICAgICAgcmV0dXJuIHRoaXMuZ2V0QnVmZmVyZWRGcmFnKGZyYWcuZW5kUFRTICsgMC41KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfTtcblxuICBfcHJvdG8uX2NoZWNrRnJhZ21lbnRDaGFuZ2VkID0gZnVuY3Rpb24gX2NoZWNrRnJhZ21lbnRDaGFuZ2VkKCkge1xuICAgIHZhciBmcmFnUGxheWluZ0N1cnJlbnQsXG4gICAgICAgIGN1cnJlbnRUaW1lLFxuICAgICAgICB2aWRlbyA9IHRoaXMubWVkaWE7XG5cbiAgICBpZiAodmlkZW8gJiYgdmlkZW8ucmVhZHlTdGF0ZSAmJiB2aWRlby5zZWVraW5nID09PSBmYWxzZSkge1xuICAgICAgY3VycmVudFRpbWUgPSB2aWRlby5jdXJyZW50VGltZTtcbiAgICAgIC8qIGlmIHZpZGVvIGVsZW1lbnQgaXMgaW4gc2Vla2VkIHN0YXRlLCBjdXJyZW50VGltZSBjYW4gb25seSBpbmNyZWFzZS5cbiAgICAgICAgKGFzc3VtaW5nIHRoYXQgcGxheWJhY2sgcmF0ZSBpcyBwb3NpdGl2ZSAuLi4pXG4gICAgICAgIEFzIHNvbWV0aW1lcyBjdXJyZW50VGltZSBqdW1wcyBiYWNrIHRvIHplcm8gYWZ0ZXIgYVxuICAgICAgICBtZWRpYSBkZWNvZGUgZXJyb3IsIGNoZWNrIHRoaXMsIHRvIGF2b2lkIHNlZWtpbmcgYmFjayB0b1xuICAgICAgICB3cm9uZyBwb3NpdGlvbiBhZnRlciBhIG1lZGlhIGRlY29kZSBlcnJvclxuICAgICAgKi9cblxuICAgICAgaWYgKGN1cnJlbnRUaW1lID4gdGhpcy5sYXN0Q3VycmVudFRpbWUpIHtcbiAgICAgICAgdGhpcy5sYXN0Q3VycmVudFRpbWUgPSBjdXJyZW50VGltZTtcbiAgICAgIH1cblxuICAgICAgaWYgKEJ1ZmZlckhlbHBlci5pc0J1ZmZlcmVkKHZpZGVvLCBjdXJyZW50VGltZSkpIHtcbiAgICAgICAgZnJhZ1BsYXlpbmdDdXJyZW50ID0gdGhpcy5nZXRCdWZmZXJlZEZyYWcoY3VycmVudFRpbWUpO1xuICAgICAgfSBlbHNlIGlmIChCdWZmZXJIZWxwZXIuaXNCdWZmZXJlZCh2aWRlbywgY3VycmVudFRpbWUgKyAwLjEpKSB7XG4gICAgICAgIC8qIGVuc3VyZSB0aGF0IEZSQUdfQ0hBTkdFRCBldmVudCBpcyB0cmlnZ2VyZWQgYXQgc3RhcnR1cCxcbiAgICAgICAgICB3aGVuIGZpcnN0IHZpZGVvIGZyYW1lIGlzIGRpc3BsYXllZCBhbmQgcGxheWJhY2sgaXMgcGF1c2VkLlxuICAgICAgICAgIGFkZCBhIHRvbGVyYW5jZSBvZiAxMDBtcywgaW4gY2FzZSBjdXJyZW50IHBvc2l0aW9uIGlzIG5vdCBidWZmZXJlZCxcbiAgICAgICAgICBjaGVjayBpZiBjdXJyZW50IHBvcysxMDBtcyBpcyBidWZmZXJlZCBhbmQgdXNlIHRoYXQgYnVmZmVyIHJhbmdlXG4gICAgICAgICAgZm9yIEZSQUdfQ0hBTkdFRCBldmVudCByZXBvcnRpbmcgKi9cbiAgICAgICAgZnJhZ1BsYXlpbmdDdXJyZW50ID0gdGhpcy5nZXRCdWZmZXJlZEZyYWcoY3VycmVudFRpbWUgKyAwLjEpO1xuICAgICAgfVxuXG4gICAgICBpZiAoZnJhZ1BsYXlpbmdDdXJyZW50KSB7XG4gICAgICAgIHZhciBmcmFnUGxheWluZyA9IGZyYWdQbGF5aW5nQ3VycmVudDtcblxuICAgICAgICBpZiAoZnJhZ1BsYXlpbmcgIT09IHRoaXMuZnJhZ1BsYXlpbmcpIHtcbiAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19DSEFOR0VELCB7XG4gICAgICAgICAgICBmcmFnOiBmcmFnUGxheWluZ1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHZhciBmcmFnUGxheWluZ0xldmVsID0gZnJhZ1BsYXlpbmcubGV2ZWw7XG5cbiAgICAgICAgICBpZiAoIXRoaXMuZnJhZ1BsYXlpbmcgfHwgdGhpcy5mcmFnUGxheWluZy5sZXZlbCAhPT0gZnJhZ1BsYXlpbmdMZXZlbCkge1xuICAgICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMX1NXSVRDSEVELCB7XG4gICAgICAgICAgICAgIGxldmVsOiBmcmFnUGxheWluZ0xldmVsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB0aGlzLmZyYWdQbGF5aW5nID0gZnJhZ1BsYXlpbmc7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgLypcbiAgICBvbiBpbW1lZGlhdGUgbGV2ZWwgc3dpdGNoIDpcbiAgICAgLSBwYXVzZSBwbGF5YmFjayBpZiBwbGF5aW5nXG4gICAgIC0gY2FuY2VsIGFueSBwZW5kaW5nIGxvYWQgcmVxdWVzdFxuICAgICAtIGFuZCB0cmlnZ2VyIGEgYnVmZmVyIGZsdXNoXG4gICovXG4gIDtcblxuICBfcHJvdG8uaW1tZWRpYXRlTGV2ZWxTd2l0Y2ggPSBmdW5jdGlvbiBpbW1lZGlhdGVMZXZlbFN3aXRjaCgpIHtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdpbW1lZGlhdGVMZXZlbFN3aXRjaCcpO1xuXG4gICAgaWYgKCF0aGlzLmltbWVkaWF0ZVN3aXRjaCkge1xuICAgICAgdGhpcy5pbW1lZGlhdGVTd2l0Y2ggPSB0cnVlO1xuICAgICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYSxcbiAgICAgICAgICBwcmV2aW91c2x5UGF1c2VkO1xuXG4gICAgICBpZiAobWVkaWEpIHtcbiAgICAgICAgcHJldmlvdXNseVBhdXNlZCA9IG1lZGlhLnBhdXNlZDtcblxuICAgICAgICBpZiAoIXByZXZpb3VzbHlQYXVzZWQpIHtcbiAgICAgICAgICBtZWRpYS5wYXVzZSgpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBkb24ndCByZXN0YXJ0IHBsYXliYWNrIGFmdGVyIGluc3RhbnQgbGV2ZWwgc3dpdGNoIGluIGNhc2UgbWVkaWEgbm90IGF0dGFjaGVkXG4gICAgICAgIHByZXZpb3VzbHlQYXVzZWQgPSB0cnVlO1xuICAgICAgfVxuXG4gICAgICB0aGlzLnByZXZpb3VzbHlQYXVzZWQgPSBwcmV2aW91c2x5UGF1c2VkO1xuICAgIH1cblxuICAgIHZhciBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG5cbiAgICBpZiAoZnJhZ0N1cnJlbnQgJiYgZnJhZ0N1cnJlbnQubG9hZGVyKSB7XG4gICAgICBmcmFnQ3VycmVudC5sb2FkZXIuYWJvcnQoKTtcbiAgICB9XG5cbiAgICB0aGlzLmZyYWdDdXJyZW50ID0gbnVsbDsgLy8gZmx1c2ggZXZlcnl0aGluZ1xuXG4gICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgfVxuICAvKipcbiAgICogb24gaW1tZWRpYXRlIGxldmVsIHN3aXRjaCBlbmQsIGFmdGVyIG5ldyBmcmFnbWVudCBoYXMgYmVlbiBidWZmZXJlZDpcbiAgICogLSBudWRnZSB2aWRlbyBkZWNvZGVyIGJ5IHNsaWdodGx5IGFkanVzdGluZyB2aWRlbyBjdXJyZW50VGltZSAoaWYgY3VycmVudFRpbWUgYnVmZmVyZWQpXG4gICAqIC0gcmVzdW1lIHRoZSBwbGF5YmFjayBpZiBuZWVkZWRcbiAgICovXG4gIDtcblxuICBfcHJvdG8uaW1tZWRpYXRlTGV2ZWxTd2l0Y2hFbmQgPSBmdW5jdGlvbiBpbW1lZGlhdGVMZXZlbFN3aXRjaEVuZCgpIHtcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhO1xuXG4gICAgaWYgKG1lZGlhICYmIG1lZGlhLmJ1ZmZlcmVkLmxlbmd0aCkge1xuICAgICAgdGhpcy5pbW1lZGlhdGVTd2l0Y2ggPSBmYWxzZTtcblxuICAgICAgaWYgKG1lZGlhLmN1cnJlbnRUaW1lID4gMCAmJiBCdWZmZXJIZWxwZXIuaXNCdWZmZXJlZChtZWRpYSwgbWVkaWEuY3VycmVudFRpbWUpKSB7XG4gICAgICAgIC8vIG9ubHkgbnVkZ2UgaWYgY3VycmVudFRpbWUgaXMgYnVmZmVyZWRcbiAgICAgICAgbWVkaWEuY3VycmVudFRpbWUgLT0gMC4wMDAxO1xuICAgICAgfVxuXG4gICAgICBpZiAoIXRoaXMucHJldmlvdXNseVBhdXNlZCkge1xuICAgICAgICBtZWRpYS5wbGF5KCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiB0cnkgdG8gc3dpdGNoIEFTQVAgd2l0aG91dCBicmVha2luZyB2aWRlbyBwbGF5YmFjazpcbiAgICogaW4gb3JkZXIgdG8gZW5zdXJlIHNtb290aCBidXQgcXVpY2sgbGV2ZWwgc3dpdGNoaW5nLFxuICAgKiB3ZSBuZWVkIHRvIGZpbmQgdGhlIG5leHQgZmx1c2hhYmxlIGJ1ZmZlciByYW5nZVxuICAgKiB3ZSBzaG91bGQgdGFrZSBpbnRvIGFjY291bnQgbmV3IHNlZ21lbnQgZmV0Y2ggdGltZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5uZXh0TGV2ZWxTd2l0Y2ggPSBmdW5jdGlvbiBuZXh0TGV2ZWxTd2l0Y2goKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTsgLy8gZW5zdXJlIHRoYXQgbWVkaWEgaXMgZGVmaW5lZCBhbmQgdGhhdCBtZXRhZGF0YSBhcmUgYXZhaWxhYmxlICh0byByZXRyaWV2ZSBjdXJyZW50VGltZSlcblxuICAgIGlmIChtZWRpYSAmJiBtZWRpYS5yZWFkeVN0YXRlKSB7XG4gICAgICB2YXIgZmV0Y2hkZWxheTtcbiAgICAgIHZhciBmcmFnUGxheWluZ0N1cnJlbnQgPSB0aGlzLmdldEJ1ZmZlcmVkRnJhZyhtZWRpYS5jdXJyZW50VGltZSk7XG5cbiAgICAgIGlmIChmcmFnUGxheWluZ0N1cnJlbnQgJiYgZnJhZ1BsYXlpbmdDdXJyZW50LnN0YXJ0UFRTID4gMSkge1xuICAgICAgICAvLyBmbHVzaCBidWZmZXIgcHJlY2VkaW5nIGN1cnJlbnQgZnJhZ21lbnQgKGZsdXNoIHVudGlsIGN1cnJlbnQgZnJhZ21lbnQgc3RhcnQgb2Zmc2V0KVxuICAgICAgICAvLyBtaW51cyAxcyB0byBhdm9pZCB2aWRlbyBmcmVlemluZywgdGhhdCBjb3VsZCBoYXBwZW4gaWYgd2UgZmx1c2gga2V5ZnJhbWUgb2YgY3VycmVudCB2aWRlbyAuLi5cbiAgICAgICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgZnJhZ1BsYXlpbmdDdXJyZW50LnN0YXJ0UFRTIC0gMSk7XG4gICAgICB9XG5cbiAgICAgIGlmICghbWVkaWEucGF1c2VkKSB7XG4gICAgICAgIC8vIGFkZCBhIHNhZmV0eSBkZWxheSBvZiAxc1xuICAgICAgICB2YXIgbmV4dExldmVsSWQgPSB0aGlzLmhscy5uZXh0TG9hZExldmVsLFxuICAgICAgICAgICAgbmV4dExldmVsID0gdGhpcy5sZXZlbHNbbmV4dExldmVsSWRdLFxuICAgICAgICAgICAgZnJhZ0xhc3RLYnBzID0gdGhpcy5mcmFnTGFzdEticHM7XG5cbiAgICAgICAgaWYgKGZyYWdMYXN0S2JwcyAmJiB0aGlzLmZyYWdDdXJyZW50KSB7XG4gICAgICAgICAgZmV0Y2hkZWxheSA9IHRoaXMuZnJhZ0N1cnJlbnQuZHVyYXRpb24gKiBuZXh0TGV2ZWwuYml0cmF0ZSAvICgxMDAwICogZnJhZ0xhc3RLYnBzKSArIDE7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZmV0Y2hkZWxheSA9IDA7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGZldGNoZGVsYXkgPSAwO1xuICAgICAgfSAvLyBsb2dnZXIubG9nKCdmZXRjaGRlbGF5OicrZmV0Y2hkZWxheSk7XG4gICAgICAvLyBmaW5kIGJ1ZmZlciByYW5nZSB0aGF0IHdpbGwgYmUgcmVhY2hlZCBvbmNlIG5ldyBmcmFnbWVudCB3aWxsIGJlIGZldGNoZWRcblxuXG4gICAgICB2YXIgYnVmZmVyZWRGcmFnID0gdGhpcy5nZXRCdWZmZXJlZEZyYWcobWVkaWEuY3VycmVudFRpbWUgKyBmZXRjaGRlbGF5KTtcblxuICAgICAgaWYgKGJ1ZmZlcmVkRnJhZykge1xuICAgICAgICAvLyB3ZSBjYW4gZmx1c2ggYnVmZmVyIHJhbmdlIGZvbGxvd2luZyB0aGlzIG9uZSB3aXRob3V0IHN0YWxsaW5nIHBsYXliYWNrXG4gICAgICAgIHZhciBuZXh0QnVmZmVyZWRGcmFnID0gdGhpcy5mb2xsb3dpbmdCdWZmZXJlZEZyYWcoYnVmZmVyZWRGcmFnKTtcblxuICAgICAgICBpZiAobmV4dEJ1ZmZlcmVkRnJhZykge1xuICAgICAgICAgIC8vIGlmIHdlIGFyZSBoZXJlLCB3ZSBjYW4gYWxzbyBjYW5jZWwgYW55IGxvYWRpbmcvZGVtdXhpbmcgaW4gcHJvZ3Jlc3MsIGFzIHRoZXkgYXJlIHVzZWxlc3NcbiAgICAgICAgICB2YXIgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50O1xuXG4gICAgICAgICAgaWYgKGZyYWdDdXJyZW50ICYmIGZyYWdDdXJyZW50LmxvYWRlcikge1xuICAgICAgICAgICAgZnJhZ0N1cnJlbnQubG9hZGVyLmFib3J0KCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7IC8vIHN0YXJ0IGZsdXNoIHBvc2l0aW9uIGlzIHRoZSBzdGFydCBQVFMgb2YgbmV4dCBidWZmZXJlZCBmcmFnLlxuICAgICAgICAgIC8vIHdlIHVzZSBmcmFnLm5heFN0YXJ0UFRTIHdoaWNoIGlzIG1heChhdWRpbyBzdGFydFBUUywgdmlkZW8gc3RhcnRQVFMpLlxuICAgICAgICAgIC8vIGluIGNhc2UgdGhlcmUgaXMgYSBzbWFsbCBQVFMgRGVsdGEgYmV0d2VlbiBhdWRpbyBhbmQgdmlkZW8sIHVzaW5nIG1heFN0YXJ0UFRTIGF2b2lkcyBmbHVzaGluZyBsYXN0IHNhbXBsZXMgZnJvbSBjdXJyZW50IGZyYWdtZW50XG5cbiAgICAgICAgICB2YXIgc3RhcnRQdHMgPSBNYXRoLm1heChidWZmZXJlZEZyYWcuZW5kUFRTLCBuZXh0QnVmZmVyZWRGcmFnLm1heFN0YXJ0UFRTICsgTWF0aC5taW4odGhpcy5jb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgbmV4dEJ1ZmZlcmVkRnJhZy5kdXJhdGlvbikpO1xuICAgICAgICAgIHRoaXMuZmx1c2hNYWluQnVmZmVyKHN0YXJ0UHRzLCBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5mbHVzaE1haW5CdWZmZXIgPSBmdW5jdGlvbiBmbHVzaE1haW5CdWZmZXIoc3RhcnRPZmZzZXQsIGVuZE9mZnNldCkge1xuICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5CVUZGRVJfRkxVU0hJTkc7XG4gICAgdmFyIGZsdXNoU2NvcGUgPSB7XG4gICAgICBzdGFydE9mZnNldDogc3RhcnRPZmZzZXQsXG4gICAgICBlbmRPZmZzZXQ6IGVuZE9mZnNldFxuICAgIH07IC8vIGlmIGFsdGVybmF0ZSBhdWRpbyB0cmFja3MgYXJlIHVzZWQsIG9ubHkgZmx1c2ggdmlkZW8sIG90aGVyd2lzZSBmbHVzaCBldmVyeXRoaW5nXG5cbiAgICBpZiAodGhpcy5hbHRBdWRpbykge1xuICAgICAgZmx1c2hTY29wZS50eXBlID0gJ3ZpZGVvJztcbiAgICB9XG5cbiAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0ZMVVNISU5HLCBmbHVzaFNjb3BlKTtcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYUF0dGFjaGVkID0gZnVuY3Rpb24gb25NZWRpYUF0dGFjaGVkKGRhdGEpIHtcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlciA9IGRhdGEubWVkaWE7XG4gICAgdGhpcy5vbnZzZWVraW5nID0gdGhpcy5vbk1lZGlhU2Vla2luZy5iaW5kKHRoaXMpO1xuICAgIHRoaXMub252c2Vla2VkID0gdGhpcy5vbk1lZGlhU2Vla2VkLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbnZlbmRlZCA9IHRoaXMub25NZWRpYUVuZGVkLmJpbmQodGhpcyk7XG4gICAgbWVkaWEuYWRkRXZlbnRMaXN0ZW5lcignc2Vla2luZycsIHRoaXMub252c2Vla2luZyk7XG4gICAgbWVkaWEuYWRkRXZlbnRMaXN0ZW5lcignc2Vla2VkJywgdGhpcy5vbnZzZWVrZWQpO1xuICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ2VuZGVkJywgdGhpcy5vbnZlbmRlZCk7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuXG4gICAgaWYgKHRoaXMubGV2ZWxzICYmIGNvbmZpZy5hdXRvU3RhcnRMb2FkKSB7XG4gICAgICB0aGlzLmhscy5zdGFydExvYWQoY29uZmlnLnN0YXJ0UG9zaXRpb24pO1xuICAgIH1cblxuICAgIHRoaXMuZ2FwQ29udHJvbGxlciA9IG5ldyBnYXBfY29udHJvbGxlcl9HYXBDb250cm9sbGVyKGNvbmZpZywgbWVkaWEsIHRoaXMuZnJhZ21lbnRUcmFja2VyLCB0aGlzLmhscyk7XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFEZXRhY2hpbmcgPSBmdW5jdGlvbiBvbk1lZGlhRGV0YWNoaW5nKCkge1xuICAgIHZhciBtZWRpYSA9IHRoaXMubWVkaWE7XG5cbiAgICBpZiAobWVkaWEgJiYgbWVkaWEuZW5kZWQpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ01TRSBkZXRhY2hpbmcgYW5kIHZpZGVvIGVuZGVkLCByZXNldCBzdGFydFBvc2l0aW9uJyk7XG4gICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSB0aGlzLmxhc3RDdXJyZW50VGltZSA9IDA7XG4gICAgfSAvLyByZXNldCBmcmFnbWVudCBiYWNrdHJhY2tlZCBmbGFnXG5cblxuICAgIHZhciBsZXZlbHMgPSB0aGlzLmxldmVscztcblxuICAgIGlmIChsZXZlbHMpIHtcbiAgICAgIGxldmVscy5mb3JFYWNoKGZ1bmN0aW9uIChsZXZlbCkge1xuICAgICAgICBpZiAobGV2ZWwuZGV0YWlscykge1xuICAgICAgICAgIGxldmVsLmRldGFpbHMuZnJhZ21lbnRzLmZvckVhY2goZnVuY3Rpb24gKGZyYWdtZW50KSB7XG4gICAgICAgICAgICBmcmFnbWVudC5iYWNrdHJhY2tlZCA9IHVuZGVmaW5lZDtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSAvLyByZW1vdmUgdmlkZW8gbGlzdGVuZXJzXG5cblxuICAgIGlmIChtZWRpYSkge1xuICAgICAgbWVkaWEucmVtb3ZlRXZlbnRMaXN0ZW5lcignc2Vla2luZycsIHRoaXMub252c2Vla2luZyk7XG4gICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdzZWVrZWQnLCB0aGlzLm9udnNlZWtlZCk7XG4gICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdlbmRlZCcsIHRoaXMub252ZW5kZWQpO1xuICAgICAgdGhpcy5vbnZzZWVraW5nID0gdGhpcy5vbnZzZWVrZWQgPSB0aGlzLm9udmVuZGVkID0gbnVsbDtcbiAgICB9XG5cbiAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVBbGxGcmFnbWVudHMoKTtcbiAgICB0aGlzLm1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlciA9IG51bGw7XG4gICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IGZhbHNlO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYVNlZWtlZCA9IGZ1bmN0aW9uIG9uTWVkaWFTZWVrZWQoKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICB2YXIgY3VycmVudFRpbWUgPSBtZWRpYSA/IG1lZGlhLmN1cnJlbnRUaW1lIDogdW5kZWZpbmVkO1xuXG4gICAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoY3VycmVudFRpbWUpKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibWVkaWEgc2Vla2VkIHRvIFwiICsgY3VycmVudFRpbWUudG9GaXhlZCgzKSk7XG4gICAgfSAvLyB0aWNrIHRvIHNwZWVkIHVwIEZSQUdNRU5UX1BMQVlJTkcgdHJpZ2dlcmluZ1xuXG5cbiAgICB0aGlzLnRpY2soKTtcbiAgfTtcblxuICBfcHJvdG8ub25NYW5pZmVzdExvYWRpbmcgPSBmdW5jdGlvbiBvbk1hbmlmZXN0TG9hZGluZygpIHtcbiAgICAvLyByZXNldCBidWZmZXIgb24gbWFuaWZlc3QgbG9hZGluZ1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3RyaWdnZXIgQlVGRkVSX1JFU0VUJyk7XG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9SRVNFVCk7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlQWxsRnJhZ21lbnRzKCk7XG4gICAgdGhpcy5zdGFsbGVkID0gZmFsc2U7XG4gICAgdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5sYXN0Q3VycmVudFRpbWUgPSAwO1xuICB9O1xuXG4gIF9wcm90by5vbk1hbmlmZXN0UGFyc2VkID0gZnVuY3Rpb24gb25NYW5pZmVzdFBhcnNlZChkYXRhKSB7XG4gICAgdmFyIGFhYyA9IGZhbHNlLFxuICAgICAgICBoZWFhYyA9IGZhbHNlLFxuICAgICAgICBjb2RlYztcbiAgICBkYXRhLmxldmVscy5mb3JFYWNoKGZ1bmN0aW9uIChsZXZlbCkge1xuICAgICAgLy8gZGV0ZWN0IGlmIHdlIGhhdmUgZGlmZmVyZW50IGtpbmQgb2YgYXVkaW8gY29kZWNzIHVzZWQgYW1vbmdzdCBwbGF5bGlzdHNcbiAgICAgIGNvZGVjID0gbGV2ZWwuYXVkaW9Db2RlYztcblxuICAgICAgaWYgKGNvZGVjKSB7XG4gICAgICAgIGlmIChjb2RlYy5pbmRleE9mKCdtcDRhLjQwLjInKSAhPT0gLTEpIHtcbiAgICAgICAgICBhYWMgPSB0cnVlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNvZGVjLmluZGV4T2YoJ21wNGEuNDAuNScpICE9PSAtMSkge1xuICAgICAgICAgIGhlYWFjID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuYXVkaW9Db2RlY1N3aXRjaCA9IGFhYyAmJiBoZWFhYztcblxuICAgIGlmICh0aGlzLmF1ZGlvQ29kZWNTd2l0Y2gpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ2JvdGggQUFDL0hFLUFBQyBhdWRpbyBmb3VuZCBpbiBsZXZlbHM7IGRlY2xhcmluZyBsZXZlbCBjb2RlYyBhcyBIRS1BQUMnKTtcbiAgICB9XG5cbiAgICB0aGlzLmFsdEF1ZGlvID0gZGF0YS5hbHRBdWRpbztcbiAgICB0aGlzLmxldmVscyA9IGRhdGEubGV2ZWxzO1xuICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gZmFsc2U7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuXG4gICAgaWYgKGNvbmZpZy5hdXRvU3RhcnRMb2FkIHx8IHRoaXMuZm9yY2VTdGFydExvYWQpIHtcbiAgICAgIHRoaXMuaGxzLnN0YXJ0TG9hZChjb25maWcuc3RhcnRQb3NpdGlvbik7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkxldmVsTG9hZGVkID0gZnVuY3Rpb24gb25MZXZlbExvYWRlZChkYXRhKSB7XG4gICAgdmFyIG5ld0RldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgdmFyIG5ld0xldmVsSWQgPSBkYXRhLmxldmVsO1xuICAgIHZhciBsYXN0TGV2ZWwgPSB0aGlzLmxldmVsc1t0aGlzLmxldmVsTGFzdExvYWRlZF07XG4gICAgdmFyIGN1ckxldmVsID0gdGhpcy5sZXZlbHNbbmV3TGV2ZWxJZF07XG4gICAgdmFyIGR1cmF0aW9uID0gbmV3RGV0YWlscy50b3RhbGR1cmF0aW9uO1xuICAgIHZhciBzbGlkaW5nID0gMDtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibGV2ZWwgXCIgKyBuZXdMZXZlbElkICsgXCIgbG9hZGVkIFtcIiArIG5ld0RldGFpbHMuc3RhcnRTTiArIFwiLFwiICsgbmV3RGV0YWlscy5lbmRTTiArIFwiXSxkdXJhdGlvbjpcIiArIGR1cmF0aW9uKTtcblxuICAgIGlmIChuZXdEZXRhaWxzLmxpdmUgfHwgY3VyTGV2ZWwuZGV0YWlscyAmJiBjdXJMZXZlbC5kZXRhaWxzLmxpdmUpIHtcbiAgICAgIHZhciBjdXJEZXRhaWxzID0gY3VyTGV2ZWwuZGV0YWlscztcblxuICAgICAgaWYgKGN1ckRldGFpbHMgJiYgbmV3RGV0YWlscy5mcmFnbWVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyB3ZSBhbHJlYWR5IGhhdmUgZGV0YWlscyBmb3IgdGhhdCBsZXZlbCwgbWVyZ2UgdGhlbVxuICAgICAgICBtZXJnZURldGFpbHMoY3VyRGV0YWlscywgbmV3RGV0YWlscyk7XG4gICAgICAgIHNsaWRpbmcgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDtcbiAgICAgICAgdGhpcy5saXZlU3luY1Bvc2l0aW9uID0gdGhpcy5jb21wdXRlTGl2ZVBvc2l0aW9uKHNsaWRpbmcsIGN1ckRldGFpbHMpO1xuXG4gICAgICAgIGlmIChuZXdEZXRhaWxzLlBUU0tub3duICYmIE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoc2xpZGluZykpIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibGl2ZSBwbGF5bGlzdCBzbGlkaW5nOlwiICsgc2xpZGluZy50b0ZpeGVkKDMpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdsaXZlIHBsYXlsaXN0IC0gb3V0ZGF0ZWQgUFRTLCB1bmtub3duIHNsaWRpbmcnKTtcbiAgICAgICAgICBhbGlnblN0cmVhbSh0aGlzLmZyYWdQcmV2aW91cywgbGFzdExldmVsLCBuZXdEZXRhaWxzKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnbGl2ZSBwbGF5bGlzdCAtIGZpcnN0IGxvYWQsIHVua25vd24gc2xpZGluZycpO1xuICAgICAgICBuZXdEZXRhaWxzLlBUU0tub3duID0gZmFsc2U7XG4gICAgICAgIGFsaWduU3RyZWFtKHRoaXMuZnJhZ1ByZXZpb3VzLCBsYXN0TGV2ZWwsIG5ld0RldGFpbHMpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBuZXdEZXRhaWxzLlBUU0tub3duID0gZmFsc2U7XG4gICAgfSAvLyBvdmVycmlkZSBsZXZlbCBpbmZvXG5cblxuICAgIGN1ckxldmVsLmRldGFpbHMgPSBuZXdEZXRhaWxzO1xuICAgIHRoaXMubGV2ZWxMYXN0TG9hZGVkID0gbmV3TGV2ZWxJZDtcbiAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uTEVWRUxfVVBEQVRFRCwge1xuICAgICAgZGV0YWlsczogbmV3RGV0YWlscyxcbiAgICAgIGxldmVsOiBuZXdMZXZlbElkXG4gICAgfSk7XG5cbiAgICBpZiAodGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgPT09IGZhbHNlKSB7XG4gICAgICAvLyBjb21wdXRlIHN0YXJ0IHBvc2l0aW9uIGlmIHNldCB0byAtMS4gdXNlIGl0IHN0cmFpZ2h0IGF3YXkgaWYgdmFsdWUgaXMgZGVmaW5lZFxuICAgICAgaWYgKHRoaXMuc3RhcnRQb3NpdGlvbiA9PT0gLTEgfHwgdGhpcy5sYXN0Q3VycmVudFRpbWUgPT09IC0xKSB7XG4gICAgICAgIC8vIGZpcnN0LCBjaGVjayBpZiBzdGFydCB0aW1lIG9mZnNldCBoYXMgYmVlbiBzZXQgaW4gcGxheWxpc3QsIGlmIHllcywgdXNlIHRoaXMgdmFsdWVcbiAgICAgICAgdmFyIHN0YXJ0VGltZU9mZnNldCA9IG5ld0RldGFpbHMuc3RhcnRUaW1lT2Zmc2V0O1xuXG4gICAgICAgIGlmIChPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHN0YXJ0VGltZU9mZnNldCkpIHtcbiAgICAgICAgICBpZiAoc3RhcnRUaW1lT2Zmc2V0IDwgMCkge1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIm5lZ2F0aXZlIHN0YXJ0IHRpbWUgb2Zmc2V0IFwiICsgc3RhcnRUaW1lT2Zmc2V0ICsgXCIsIGNvdW50IGZyb20gZW5kIG9mIGxhc3QgZnJhZ21lbnRcIik7XG4gICAgICAgICAgICBzdGFydFRpbWVPZmZzZXQgPSBzbGlkaW5nICsgZHVyYXRpb24gKyBzdGFydFRpbWVPZmZzZXQ7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInN0YXJ0IHRpbWUgb2Zmc2V0IGZvdW5kIGluIHBsYXlsaXN0LCBhZGp1c3Qgc3RhcnRQb3NpdGlvbiB0byBcIiArIHN0YXJ0VGltZU9mZnNldCk7XG4gICAgICAgICAgdGhpcy5zdGFydFBvc2l0aW9uID0gc3RhcnRUaW1lT2Zmc2V0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGlmIGxpdmUgcGxheWxpc3QsIHNldCBzdGFydCBwb3NpdGlvbiB0byBiZSBmcmFnbWVudCBOLXRoaXMuY29uZmlnLmxpdmVTeW5jRHVyYXRpb25Db3VudCAodXN1YWxseSAzKVxuICAgICAgICAgIGlmIChuZXdEZXRhaWxzLmxpdmUpIHtcbiAgICAgICAgICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IHRoaXMuY29tcHV0ZUxpdmVQb3NpdGlvbihzbGlkaW5nLCBuZXdEZXRhaWxzKTtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJjb25maWd1cmUgc3RhcnRQb3NpdGlvbiB0byBcIiArIHRoaXMuc3RhcnRQb3NpdGlvbik7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IDA7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5sYXN0Q3VycmVudFRpbWUgPSB0aGlzLnN0YXJ0UG9zaXRpb247XG4gICAgICB9XG5cbiAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IHRoaXMuc3RhcnRQb3NpdGlvbjtcbiAgICB9IC8vIG9ubHkgc3dpdGNoIGJhdGNrIHRvIElETEUgc3RhdGUgaWYgd2Ugd2VyZSB3YWl0aW5nIGZvciBsZXZlbCB0byBzdGFydCBkb3dubG9hZGluZyBhIG5ldyBmcmFnbWVudFxuXG5cbiAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuV0FJVElOR19MRVZFTCkge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgfSAvLyB0cmlnZ2VyIGhhbmRsZXIgcmlnaHQgbm93XG5cblxuICAgIHRoaXMudGljaygpO1xuICB9O1xuXG4gIF9wcm90by5vbktleUxvYWRlZCA9IGZ1bmN0aW9uIG9uS2V5TG9hZGVkKCkge1xuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5LRVlfTE9BRElORykge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICB0aGlzLnRpY2soKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uRnJhZ0xvYWRlZCA9IGZ1bmN0aW9uIG9uRnJhZ0xvYWRlZChkYXRhKSB7XG4gICAgdmFyIGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudCxcbiAgICAgICAgaGxzID0gdGhpcy5obHMsXG4gICAgICAgIGxldmVscyA9IHRoaXMubGV2ZWxzLFxuICAgICAgICBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgdmFyIGZyYWdMb2FkZWQgPSBkYXRhLmZyYWc7XG5cbiAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuRlJBR19MT0FESU5HICYmIGZyYWdDdXJyZW50ICYmIGZyYWdMb2FkZWQudHlwZSA9PT0gJ21haW4nICYmIGZyYWdMb2FkZWQubGV2ZWwgPT09IGZyYWdDdXJyZW50LmxldmVsICYmIGZyYWdMb2FkZWQuc24gPT09IGZyYWdDdXJyZW50LnNuKSB7XG4gICAgICB2YXIgc3RhdHMgPSBkYXRhLnN0YXRzO1xuICAgICAgdmFyIGN1cnJlbnRMZXZlbCA9IGxldmVsc1tmcmFnQ3VycmVudC5sZXZlbF07XG4gICAgICB2YXIgZGV0YWlscyA9IGN1cnJlbnRMZXZlbC5kZXRhaWxzOyAvLyByZXNldCBmcmFnIGJpdHJhdGUgdGVzdCBpbiBhbnkgY2FzZSBhZnRlciBmcmFnIGxvYWRlZCBldmVudFxuICAgICAgLy8gaWYgdGhpcyBmcmFnIHdhcyBsb2FkZWQgdG8gcGVyZm9ybSBhIGJpdHJhdGUgdGVzdCBBTkQgaWYgaGxzLm5leHRMb2FkTGV2ZWwgaXMgZ3JlYXRlciB0aGFuIDBcbiAgICAgIC8vIHRoZW4gdGhpcyBtZWFucyB0aGF0IHdlIHNob3VsZCBiZSBhYmxlIHRvIGxvYWQgYSBmcmFnbWVudCBhdCBhIGhpZ2hlciBxdWFsaXR5IGxldmVsXG5cbiAgICAgIHRoaXMuYml0cmF0ZVRlc3QgPSBmYWxzZTtcbiAgICAgIHRoaXMuc3RhdHMgPSBzdGF0cztcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJMb2FkZWQgXCIgKyBmcmFnQ3VycmVudC5zbiArIFwiIG9mIFtcIiArIGRldGFpbHMuc3RhcnRTTiArIFwiICxcIiArIGRldGFpbHMuZW5kU04gKyBcIl0sbGV2ZWwgXCIgKyBmcmFnQ3VycmVudC5sZXZlbCk7XG5cbiAgICAgIGlmIChmcmFnTG9hZGVkLmJpdHJhdGVUZXN0ICYmIGhscy5uZXh0TG9hZExldmVsKSB7XG4gICAgICAgIC8vIHN3aXRjaCBiYWNrIHRvIElETEUgc3RhdGUgLi4uIHdlIGp1c3QgbG9hZGVkIGEgZnJhZ21lbnQgdG8gZGV0ZXJtaW5lIGFkZXF1YXRlIHN0YXJ0IGJpdHJhdGUgYW5kIGluaXRpYWxpemUgYXV0b3N3aXRjaCBhbGdvXG4gICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IGZhbHNlO1xuICAgICAgICBzdGF0cy50cGFyc2VkID0gc3RhdHMudGJ1ZmZlcmVkID0gd2luZG93LnBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfQlVGRkVSRUQsIHtcbiAgICAgICAgICBzdGF0czogc3RhdHMsXG4gICAgICAgICAgZnJhZzogZnJhZ0N1cnJlbnQsXG4gICAgICAgICAgaWQ6ICdtYWluJ1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy50aWNrKCk7XG4gICAgICB9IGVsc2UgaWYgKGZyYWdMb2FkZWQuc24gPT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgIHN0YXRzLnRwYXJzZWQgPSBzdGF0cy50YnVmZmVyZWQgPSB3aW5kb3cucGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgIGRldGFpbHMuaW5pdFNlZ21lbnQuZGF0YSA9IGRhdGEucGF5bG9hZDtcbiAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0JVRkZFUkVELCB7XG4gICAgICAgICAgc3RhdHM6IHN0YXRzLFxuICAgICAgICAgIGZyYWc6IGZyYWdDdXJyZW50LFxuICAgICAgICAgIGlkOiAnbWFpbidcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMudGljaygpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIlBhcnNpbmcgXCIgKyBmcmFnQ3VycmVudC5zbiArIFwiIG9mIFtcIiArIGRldGFpbHMuc3RhcnRTTiArIFwiICxcIiArIGRldGFpbHMuZW5kU04gKyBcIl0sbGV2ZWwgXCIgKyBmcmFnQ3VycmVudC5sZXZlbCArIFwiLCBjYyBcIiArIGZyYWdDdXJyZW50LmNjKTtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlBBUlNJTkc7XG4gICAgICAgIHRoaXMucGVuZGluZ0J1ZmZlcmluZyA9IHRydWU7XG4gICAgICAgIHRoaXMuYXBwZW5kZWQgPSBmYWxzZTsgLy8gQml0cmF0ZSB0ZXN0IGZyYWdzIGFyZSBub3QgdXN1YWxseSBidWZmZXJlZCBzbyB0aGUgZnJhZ21lbnQgdHJhY2tlciBpZ25vcmVzIHRoZW0uIElmIEhscy5qcyBkZWNpZGVzIHRvIGJ1ZmZlclxuICAgICAgICAvLyBpdCAoYW5kIHRoZXJlZm9yZSBlbmRzIHVwIGF0IHRoaXMgbGluZSksIHRoZW4gdGhlIGZyYWdtZW50IHRyYWNrZXIgbmVlZHMgdG8gYmUgbWFudWFsbHkgaW5mb3JtZWQuXG5cbiAgICAgICAgaWYgKGZyYWdMb2FkZWQuYml0cmF0ZVRlc3QpIHtcbiAgICAgICAgICBmcmFnTG9hZGVkLmJpdHJhdGVUZXN0ID0gZmFsc2U7XG4gICAgICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIub25GcmFnTG9hZGVkKHtcbiAgICAgICAgICAgIGZyYWc6IGZyYWdMb2FkZWRcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSAvLyB0aW1lIE9mZnNldCBpcyBhY2N1cmF0ZSBpZiBsZXZlbCBQVFMgaXMga25vd24sIG9yIGlmIHBsYXlsaXN0IGlzIG5vdCBzbGlkaW5nIChub3QgbGl2ZSkgYW5kIGlmIG1lZGlhIGlzIG5vdCBzZWVraW5nICh0aGlzIGlzIHRvIG92ZXJjb21lIHBvdGVudGlhbCB0aW1lc3RhbXAgZHJpZnRzIGJldHdlZW4gcGxheWxpc3RzIGFuZCBmcmFnbWVudHMpXG5cblxuICAgICAgICB2YXIgYWNjdXJhdGVUaW1lT2Zmc2V0ID0gIShtZWRpYSAmJiBtZWRpYS5zZWVraW5nKSAmJiAoZGV0YWlscy5QVFNLbm93biB8fCAhZGV0YWlscy5saXZlKTtcbiAgICAgICAgdmFyIGluaXRTZWdtZW50RGF0YSA9IGRldGFpbHMuaW5pdFNlZ21lbnQgPyBkZXRhaWxzLmluaXRTZWdtZW50LmRhdGEgOiBbXTtcblxuICAgICAgICB2YXIgYXVkaW9Db2RlYyA9IHRoaXMuX2dldEF1ZGlvQ29kZWMoY3VycmVudExldmVsKTsgLy8gdHJhbnNtdXggdGhlIE1QRUctVFMgZGF0YSB0byBJU08tQk1GRiBzZWdtZW50c1xuXG5cbiAgICAgICAgdmFyIGRlbXV4ZXIgPSB0aGlzLmRlbXV4ZXIgPSB0aGlzLmRlbXV4ZXIgfHwgbmV3IGRlbXV4X2RlbXV4ZXIodGhpcy5obHMsICdtYWluJyk7XG4gICAgICAgIGRlbXV4ZXIucHVzaChkYXRhLnBheWxvYWQsIGluaXRTZWdtZW50RGF0YSwgYXVkaW9Db2RlYywgY3VycmVudExldmVsLnZpZGVvQ29kZWMsIGZyYWdDdXJyZW50LCBkZXRhaWxzLnRvdGFsZHVyYXRpb24sIGFjY3VyYXRlVGltZU9mZnNldCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5mcmFnTG9hZEVycm9yID0gMDtcbiAgfTtcblxuICBfcHJvdG8ub25GcmFnUGFyc2luZ0luaXRTZWdtZW50ID0gZnVuY3Rpb24gb25GcmFnUGFyc2luZ0luaXRTZWdtZW50KGRhdGEpIHtcbiAgICB2YXIgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50O1xuICAgIHZhciBmcmFnTmV3ID0gZGF0YS5mcmFnO1xuXG4gICAgaWYgKGZyYWdDdXJyZW50ICYmIGRhdGEuaWQgPT09ICdtYWluJyAmJiBmcmFnTmV3LnNuID09PSBmcmFnQ3VycmVudC5zbiAmJiBmcmFnTmV3LmxldmVsID09PSBmcmFnQ3VycmVudC5sZXZlbCAmJiB0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICB2YXIgdHJhY2tzID0gZGF0YS50cmFja3MsXG4gICAgICAgICAgdHJhY2tOYW1lLFxuICAgICAgICAgIHRyYWNrO1xuICAgICAgdGhpcy5hdWRpb09ubHkgPSB0cmFja3MuYXVkaW8gJiYgIXRyYWNrcy52aWRlbzsgLy8gaWYgYXVkaW8gdHJhY2sgaXMgZXhwZWN0ZWQgdG8gY29tZSBmcm9tIGF1ZGlvIHN0cmVhbSBjb250cm9sbGVyLCBkaXNjYXJkIGFueSBjb21pbmcgZnJvbSBtYWluXG5cbiAgICAgIGlmICh0aGlzLmFsdEF1ZGlvICYmICF0aGlzLmF1ZGlvT25seSkge1xuICAgICAgICBkZWxldGUgdHJhY2tzLmF1ZGlvO1xuICAgICAgfSAvLyBpbmNsdWRlIGxldmVsQ29kZWMgaW4gYXVkaW8gYW5kIHZpZGVvIHRyYWNrc1xuXG5cbiAgICAgIHRyYWNrID0gdHJhY2tzLmF1ZGlvO1xuXG4gICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgdmFyIGF1ZGlvQ29kZWMgPSB0aGlzLmxldmVsc1t0aGlzLmxldmVsXS5hdWRpb0NvZGVjLFxuICAgICAgICAgICAgdWEgPSBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCk7XG5cbiAgICAgICAgaWYgKGF1ZGlvQ29kZWMgJiYgdGhpcy5hdWRpb0NvZGVjU3dhcCkge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3N3YXBwaW5nIHBsYXlsaXN0IGF1ZGlvIGNvZGVjJyk7XG5cbiAgICAgICAgICBpZiAoYXVkaW9Db2RlYy5pbmRleE9mKCdtcDRhLjQwLjUnKSAhPT0gLTEpIHtcbiAgICAgICAgICAgIGF1ZGlvQ29kZWMgPSAnbXA0YS40MC4yJztcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjUnO1xuICAgICAgICAgIH1cbiAgICAgICAgfSAvLyBpbiBjYXNlIEFBQyBhbmQgSEUtQUFDIGF1ZGlvIGNvZGVjcyBhcmUgc2lnbmFsbGVkIGluIG1hbmlmZXN0XG4gICAgICAgIC8vIGZvcmNlIEhFLUFBQyAsIGFzIGl0IHNlZW1zIHRoYXQgbW9zdCBicm93c2VycyBwcmVmZXJzIHRoYXQgd2F5LFxuICAgICAgICAvLyBleGNlcHQgZm9yIG1vbm8gc3RyZWFtcyBPUiBvbiBGRlxuICAgICAgICAvLyB0aGVzZSBjb25kaXRpb25zIG1pZ2h0IG5lZWQgdG8gYmUgcmV2aWV3ZWQgLi4uXG5cblxuICAgICAgICBpZiAodGhpcy5hdWRpb0NvZGVjU3dpdGNoKSB7XG4gICAgICAgICAgLy8gZG9uJ3QgZm9yY2UgSEUtQUFDIGlmIG1vbm8gc3RyZWFtXG4gICAgICAgICAgaWYgKHRyYWNrLm1ldGFkYXRhLmNoYW5uZWxDb3VudCAhPT0gMSAmJiAvLyBkb24ndCBmb3JjZSBIRS1BQUMgaWYgZmlyZWZveFxuICAgICAgICAgIHVhLmluZGV4T2YoJ2ZpcmVmb3gnKSA9PT0gLTEpIHtcbiAgICAgICAgICAgIGF1ZGlvQ29kZWMgPSAnbXA0YS40MC41JztcbiAgICAgICAgICB9XG4gICAgICAgIH0gLy8gSEUtQUFDIGlzIGJyb2tlbiBvbiBBbmRyb2lkLCBhbHdheXMgc2lnbmFsIGF1ZGlvIGNvZGVjIGFzIEFBQyBldmVuIGlmIHZhcmlhbnQgbWFuaWZlc3Qgc3RhdGVzIG90aGVyd2lzZVxuXG5cbiAgICAgICAgaWYgKHVhLmluZGV4T2YoJ2FuZHJvaWQnKSAhPT0gLTEgJiYgdHJhY2suY29udGFpbmVyICE9PSAnYXVkaW8vbXBlZycpIHtcbiAgICAgICAgICAvLyBFeGNsdWRlIG1wZWcgYXVkaW9cbiAgICAgICAgICBhdWRpb0NvZGVjID0gJ21wNGEuNDAuMic7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIkFuZHJvaWQ6IGZvcmNlIGF1ZGlvIGNvZGVjIHRvIFwiICsgYXVkaW9Db2RlYyk7XG4gICAgICAgIH1cblxuICAgICAgICB0cmFjay5sZXZlbENvZGVjID0gYXVkaW9Db2RlYztcbiAgICAgICAgdHJhY2suaWQgPSBkYXRhLmlkO1xuICAgICAgfVxuXG4gICAgICB0cmFjayA9IHRyYWNrcy52aWRlbztcblxuICAgICAgaWYgKHRyYWNrKSB7XG4gICAgICAgIHRyYWNrLmxldmVsQ29kZWMgPSB0aGlzLmxldmVsc1t0aGlzLmxldmVsXS52aWRlb0NvZGVjO1xuICAgICAgICB0cmFjay5pZCA9IGRhdGEuaWQ7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfQ09ERUNTLCB0cmFja3MpOyAvLyBsb29wIHRocm91Z2ggdHJhY2tzIHRoYXQgYXJlIGdvaW5nIHRvIGJlIHByb3ZpZGVkIHRvIGJ1ZmZlckNvbnRyb2xsZXJcblxuICAgICAgZm9yICh0cmFja05hbWUgaW4gdHJhY2tzKSB7XG4gICAgICAgIHRyYWNrID0gdHJhY2tzW3RyYWNrTmFtZV07XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJtYWluIHRyYWNrOlwiICsgdHJhY2tOYW1lICsgXCIsY29udGFpbmVyOlwiICsgdHJhY2suY29udGFpbmVyICsgXCIsY29kZWNzW2xldmVsL3BhcnNlZF09W1wiICsgdHJhY2subGV2ZWxDb2RlYyArIFwiL1wiICsgdHJhY2suY29kZWMgKyBcIl1cIik7XG4gICAgICAgIHZhciBpbml0U2VnbWVudCA9IHRyYWNrLmluaXRTZWdtZW50O1xuXG4gICAgICAgIGlmIChpbml0U2VnbWVudCkge1xuICAgICAgICAgIHRoaXMuYXBwZW5kZWQgPSB0cnVlOyAvLyBhcm0gcGVuZGluZyBCdWZmZXJpbmcgZmxhZyBiZWZvcmUgYXBwZW5kaW5nIGEgc2VnbWVudFxuXG4gICAgICAgICAgdGhpcy5wZW5kaW5nQnVmZmVyaW5nID0gdHJ1ZTtcbiAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0FQUEVORElORywge1xuICAgICAgICAgICAgdHlwZTogdHJhY2tOYW1lLFxuICAgICAgICAgICAgZGF0YTogaW5pdFNlZ21lbnQsXG4gICAgICAgICAgICBwYXJlbnQ6ICdtYWluJyxcbiAgICAgICAgICAgIGNvbnRlbnQ6ICdpbml0U2VnbWVudCdcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSAvLyB0cmlnZ2VyIGhhbmRsZXIgcmlnaHQgbm93XG5cblxuICAgICAgdGhpcy50aWNrKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkZyYWdQYXJzaW5nRGF0YSA9IGZ1bmN0aW9uIG9uRnJhZ1BhcnNpbmdEYXRhKGRhdGEpIHtcbiAgICB2YXIgX3RoaXMyID0gdGhpcztcblxuICAgIHZhciBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG4gICAgdmFyIGZyYWdOZXcgPSBkYXRhLmZyYWc7XG5cbiAgICBpZiAoZnJhZ0N1cnJlbnQgJiYgZGF0YS5pZCA9PT0gJ21haW4nICYmIGZyYWdOZXcuc24gPT09IGZyYWdDdXJyZW50LnNuICYmIGZyYWdOZXcubGV2ZWwgPT09IGZyYWdDdXJyZW50LmxldmVsICYmICEoZGF0YS50eXBlID09PSAnYXVkaW8nICYmIHRoaXMuYWx0QXVkaW8pICYmIC8vIGZpbHRlciBvdXQgbWFpbiBhdWRpbyBpZiBhdWRpbyB0cmFjayBpcyBsb2FkZWQgdGhyb3VnaCBhdWRpbyBzdHJlYW0gY29udHJvbGxlclxuICAgIHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcpIHtcbiAgICAgIHZhciBsZXZlbCA9IHRoaXMubGV2ZWxzW3RoaXMubGV2ZWxdLFxuICAgICAgICAgIGZyYWcgPSBmcmFnQ3VycmVudDtcblxuICAgICAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGRhdGEuZW5kUFRTKSkge1xuICAgICAgICBkYXRhLmVuZFBUUyA9IGRhdGEuc3RhcnRQVFMgKyBmcmFnQ3VycmVudC5kdXJhdGlvbjtcbiAgICAgICAgZGF0YS5lbmREVFMgPSBkYXRhLnN0YXJ0RFRTICsgZnJhZ0N1cnJlbnQuZHVyYXRpb247XG4gICAgICB9XG5cbiAgICAgIGlmIChkYXRhLmhhc0F1ZGlvID09PSB0cnVlKSB7XG4gICAgICAgIGZyYWcuYWRkRWxlbWVudGFyeVN0cmVhbShFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU8pO1xuICAgICAgfVxuXG4gICAgICBpZiAoZGF0YS5oYXNWaWRlbyA9PT0gdHJ1ZSkge1xuICAgICAgICBmcmFnLmFkZEVsZW1lbnRhcnlTdHJlYW0oRWxlbWVudGFyeVN0cmVhbVR5cGVzLlZJREVPKTtcbiAgICAgIH1cblxuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIlBhcnNlZCBcIiArIGRhdGEudHlwZSArIFwiLFBUUzpbXCIgKyBkYXRhLnN0YXJ0UFRTLnRvRml4ZWQoMykgKyBcIixcIiArIGRhdGEuZW5kUFRTLnRvRml4ZWQoMykgKyBcIl0sRFRTOltcIiArIGRhdGEuc3RhcnREVFMudG9GaXhlZCgzKSArIFwiL1wiICsgZGF0YS5lbmREVFMudG9GaXhlZCgzKSArIFwiXSxuYjpcIiArIGRhdGEubmIgKyBcIixkcm9wcGVkOlwiICsgKGRhdGEuZHJvcHBlZCB8fCAwKSk7IC8vIERldGVjdCBnYXBzIGluIGEgZnJhZ21lbnQgIGFuZCB0cnkgdG8gZml4IGl0IGJ5IGZpbmRpbmcgYSBrZXlmcmFtZSBpbiB0aGUgcHJldmlvdXMgZnJhZ21lbnQgKHNlZSBfZmluZEZyYWdtZW50cylcblxuICAgICAgaWYgKGRhdGEudHlwZSA9PT0gJ3ZpZGVvJykge1xuICAgICAgICBmcmFnLmRyb3BwZWQgPSBkYXRhLmRyb3BwZWQ7XG5cbiAgICAgICAgaWYgKGZyYWcuZHJvcHBlZCkge1xuICAgICAgICAgIGlmICghZnJhZy5iYWNrdHJhY2tlZCkge1xuICAgICAgICAgICAgdmFyIGxldmVsRGV0YWlscyA9IGxldmVsLmRldGFpbHM7XG5cbiAgICAgICAgICAgIGlmIChsZXZlbERldGFpbHMgJiYgZnJhZy5zbiA9PT0gbGV2ZWxEZXRhaWxzLnN0YXJ0U04pIHtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ21pc3NpbmcgdmlkZW8gZnJhbWUocykgb24gZmlyc3QgZnJhZywgYXBwZW5kaW5nIHdpdGggZ2FwJywgZnJhZy5zbik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignbWlzc2luZyB2aWRlbyBmcmFtZShzKSwgYmFja3RyYWNraW5nIGZyYWdtZW50JywgZnJhZy5zbik7IC8vIFJldHVybiBiYWNrIHRvIHRoZSBJRExFIHN0YXRlIHdpdGhvdXQgYXBwZW5kaW5nIHRvIGJ1ZmZlclxuICAgICAgICAgICAgICAvLyBDYXVzZXMgZmluZEZyYWdtZW50cyB0byBiYWNrdHJhY2sgYSBzZWdtZW50IGFuZCBmaW5kIHRoZSBrZXlmcmFtZVxuICAgICAgICAgICAgICAvLyBBdWRpbyBmcmFnbWVudHMgYXJyaXZpbmcgYmVmb3JlIHZpZGVvIHNldHMgdGhlIG5leHRMb2FkUG9zaXRpb24sIGNhdXNpbmcgX2ZpbmRGcmFnbWVudHMgdG8gc2tpcCB0aGUgYmFja3RyYWNrZWQgZnJhZ21lbnRcblxuICAgICAgICAgICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICAgICAgICAgICAgZnJhZy5iYWNrdHJhY2tlZCA9IHRydWU7XG4gICAgICAgICAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IGRhdGEuc3RhcnRQVFM7XG4gICAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICAgICAgICB0aGlzLmZyYWdQcmV2aW91cyA9IGZyYWc7XG5cbiAgICAgICAgICAgICAgaWYgKHRoaXMuZGVtdXhlcikge1xuICAgICAgICAgICAgICAgIHRoaXMuZGVtdXhlci5kZXN0cm95KCk7XG4gICAgICAgICAgICAgICAgdGhpcy5kZW11eGVyID0gbnVsbDtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHRoaXMudGljaygpO1xuICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdBbHJlYWR5IGJhY2t0cmFja2VkIG9uIHRoaXMgZnJhZ21lbnQsIGFwcGVuZGluZyB3aXRoIHRoZSBnYXAnLCBmcmFnLnNuKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gT25seSByZXNldCB0aGUgYmFja3RyYWNrZWQgZmxhZyBpZiB3ZSd2ZSBsb2FkZWQgdGhlIGZyYWcgd2l0aG91dCBhbnkgZHJvcHBlZCBmcmFtZXNcbiAgICAgICAgICBmcmFnLmJhY2t0cmFja2VkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIGRyaWZ0ID0gdXBkYXRlRnJhZ1BUU0RUUyhsZXZlbC5kZXRhaWxzLCBmcmFnLCBkYXRhLnN0YXJ0UFRTLCBkYXRhLmVuZFBUUywgZGF0YS5zdGFydERUUywgZGF0YS5lbmREVFMpLFxuICAgICAgICAgIGhscyA9IHRoaXMuaGxzO1xuICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9QVFNfVVBEQVRFRCwge1xuICAgICAgICBkZXRhaWxzOiBsZXZlbC5kZXRhaWxzLFxuICAgICAgICBsZXZlbDogdGhpcy5sZXZlbCxcbiAgICAgICAgZHJpZnQ6IGRyaWZ0LFxuICAgICAgICB0eXBlOiBkYXRhLnR5cGUsXG4gICAgICAgIHN0YXJ0OiBkYXRhLnN0YXJ0UFRTLFxuICAgICAgICBlbmQ6IGRhdGEuZW5kUFRTXG4gICAgICB9KTsgLy8gaGFzIHJlbXV4ZXIgZHJvcHBlZCB2aWRlbyBmcmFtZXMgbG9jYXRlZCBiZWZvcmUgZmlyc3Qga2V5ZnJhbWUgP1xuXG4gICAgICBbZGF0YS5kYXRhMSwgZGF0YS5kYXRhMl0uZm9yRWFjaChmdW5jdGlvbiAoYnVmZmVyKSB7XG4gICAgICAgIC8vIG9ubHkgYXBwZW5kIGluIFBBUlNJTkcgc3RhdGUgKHJhdGlvbmFsZSBpcyB0aGF0IGFuIGFwcGVuZGluZyBlcnJvciBjb3VsZCBoYXBwZW4gc3luY2hyb25vdXNseSBvbiBmaXJzdCBzZWdtZW50IGFwcGVuZGluZylcbiAgICAgICAgLy8gaW4gdGhhdCBjYXNlIGl0IGlzIHVzZWxlc3MgdG8gYXBwZW5kIGZvbGxvd2luZyBzZWdtZW50c1xuICAgICAgICBpZiAoYnVmZmVyICYmIGJ1ZmZlci5sZW5ndGggJiYgX3RoaXMyLnN0YXRlID09PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICAgICAgX3RoaXMyLmFwcGVuZGVkID0gdHJ1ZTsgLy8gYXJtIHBlbmRpbmcgQnVmZmVyaW5nIGZsYWcgYmVmb3JlIGFwcGVuZGluZyBhIHNlZ21lbnRcblxuICAgICAgICAgIF90aGlzMi5wZW5kaW5nQnVmZmVyaW5nID0gdHJ1ZTtcbiAgICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRJTkcsIHtcbiAgICAgICAgICAgIHR5cGU6IGRhdGEudHlwZSxcbiAgICAgICAgICAgIGRhdGE6IGJ1ZmZlcixcbiAgICAgICAgICAgIHBhcmVudDogJ21haW4nLFxuICAgICAgICAgICAgY29udGVudDogJ2RhdGEnXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pOyAvLyB0cmlnZ2VyIGhhbmRsZXIgcmlnaHQgbm93XG5cbiAgICAgIHRoaXMudGljaygpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25GcmFnUGFyc2VkID0gZnVuY3Rpb24gb25GcmFnUGFyc2VkKGRhdGEpIHtcbiAgICB2YXIgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50O1xuICAgIHZhciBmcmFnTmV3ID0gZGF0YS5mcmFnO1xuXG4gICAgaWYgKGZyYWdDdXJyZW50ICYmIGRhdGEuaWQgPT09ICdtYWluJyAmJiBmcmFnTmV3LnNuID09PSBmcmFnQ3VycmVudC5zbiAmJiBmcmFnTmV3LmxldmVsID09PSBmcmFnQ3VycmVudC5sZXZlbCAmJiB0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICB0aGlzLnN0YXRzLnRwYXJzZWQgPSB3aW5kb3cucGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuUEFSU0VEO1xuXG4gICAgICB0aGlzLl9jaGVja0FwcGVuZGVkUGFyc2VkKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkF1ZGlvVHJhY2tTd2l0Y2hpbmcgPSBmdW5jdGlvbiBvbkF1ZGlvVHJhY2tTd2l0Y2hpbmcoZGF0YSkge1xuICAgIC8vIGlmIGFueSBVUkwgZm91bmQgb24gbmV3IGF1ZGlvIHRyYWNrLCBpdCBpcyBhbiBhbHRlcm5hdGUgYXVkaW8gdHJhY2tcbiAgICB2YXIgZnJvbUFsdEF1ZGlvID0gdGhpcy5hbHRBdWRpbztcbiAgICB2YXIgYWx0QXVkaW8gPSAhIWRhdGEudXJsO1xuICAgIHZhciB0cmFja0lkID0gZGF0YS5pZDsgLy8gaWYgd2Ugc3dpdGNoIG9uIG1haW4gYXVkaW8sIGVuc3VyZSB0aGF0IG1haW4gZnJhZ21lbnQgc2NoZWR1bGluZyBpcyBzeW5jZWQgd2l0aCBtZWRpYS5idWZmZXJlZFxuICAgIC8vIGRvbid0IGRvIGFueXRoaW5nIGlmIHdlIHN3aXRjaCB0byBhbHQgYXVkaW86IGF1ZGlvIHN0cmVhbSBjb250cm9sbGVyIGlzIGhhbmRsaW5nIGl0LlxuICAgIC8vIHdlIHdpbGwganVzdCBoYXZlIHRvIGNoYW5nZSBidWZmZXIgc2NoZWR1bGluZyBvbiBhdWRpb1RyYWNrU3dpdGNoZWRcblxuICAgIGlmICghYWx0QXVkaW8pIHtcbiAgICAgIGlmICh0aGlzLm1lZGlhQnVmZmVyICE9PSB0aGlzLm1lZGlhKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3N3aXRjaGluZyBvbiBtYWluIGF1ZGlvLCB1c2UgbWVkaWEuYnVmZmVyZWQgdG8gc2NoZWR1bGUgbWFpbiBmcmFnbWVudCBsb2FkaW5nJyk7XG4gICAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSB0aGlzLm1lZGlhO1xuICAgICAgICB2YXIgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50OyAvLyB3ZSBuZWVkIHRvIHJlZmlsbCBhdWRpbyBidWZmZXIgZnJvbSBtYWluOiBjYW5jZWwgYW55IGZyYWcgbG9hZGluZyB0byBzcGVlZCB1cCBhdWRpbyBzd2l0Y2hcblxuICAgICAgICBpZiAoZnJhZ0N1cnJlbnQubG9hZGVyKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnc3dpdGNoaW5nIHRvIG1haW4gYXVkaW8gdHJhY2ssIGNhbmNlbCBtYWluIGZyYWdtZW50IGxvYWQnKTtcbiAgICAgICAgICBmcmFnQ3VycmVudC5sb2FkZXIuYWJvcnQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBudWxsO1xuICAgICAgICB0aGlzLmZyYWdQcmV2aW91cyA9IG51bGw7IC8vIGRlc3Ryb3kgZGVtdXhlciB0byBmb3JjZSBpbml0IHNlZ21lbnQgZ2VuZXJhdGlvbiAoZm9sbG93aW5nIGF1ZGlvIHN3aXRjaClcblxuICAgICAgICBpZiAodGhpcy5kZW11eGVyKSB7XG4gICAgICAgICAgdGhpcy5kZW11eGVyLmRlc3Ryb3koKTtcbiAgICAgICAgICB0aGlzLmRlbXV4ZXIgPSBudWxsO1xuICAgICAgICB9IC8vIHN3aXRjaCB0byBJRExFIHN0YXRlIHRvIGxvYWQgbmV3IGZyYWdtZW50XG5cblxuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIH1cblxuICAgICAgdmFyIGhscyA9IHRoaXMuaGxzOyAvLyBJZiBzd2l0Y2hpbmcgZnJvbSBhbHQgdG8gbWFpbiBhdWRpbywgZmx1c2ggYWxsIGF1ZGlvIGFuZCB0cmlnZ2VyIHRyYWNrIHN3aXRjaGVkXG5cbiAgICAgIGlmIChmcm9tQWx0QXVkaW8pIHtcbiAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfRkxVU0hJTkcsIHtcbiAgICAgICAgICBzdGFydE9mZnNldDogMCxcbiAgICAgICAgICBlbmRPZmZzZXQ6IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSxcbiAgICAgICAgICB0eXBlOiAnYXVkaW8nXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkFVRElPX1RSQUNLX1NXSVRDSEVELCB7XG4gICAgICAgIGlkOiB0cmFja0lkXG4gICAgICB9KTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uQXVkaW9UcmFja1N3aXRjaGVkID0gZnVuY3Rpb24gb25BdWRpb1RyYWNrU3dpdGNoZWQoZGF0YSkge1xuICAgIHZhciB0cmFja0lkID0gZGF0YS5pZCxcbiAgICAgICAgYWx0QXVkaW8gPSAhIXRoaXMuaGxzLmF1ZGlvVHJhY2tzW3RyYWNrSWRdLnVybDtcblxuICAgIGlmIChhbHRBdWRpbykge1xuICAgICAgdmFyIHZpZGVvQnVmZmVyID0gdGhpcy52aWRlb0J1ZmZlcjsgLy8gaWYgd2Ugc3dpdGNoZWQgb24gYWx0ZXJuYXRlIGF1ZGlvLCBlbnN1cmUgdGhhdCBtYWluIGZyYWdtZW50IHNjaGVkdWxpbmcgaXMgc3luY2VkIHdpdGggdmlkZW8gc291cmNlYnVmZmVyIGJ1ZmZlcmVkXG5cbiAgICAgIGlmICh2aWRlb0J1ZmZlciAmJiB0aGlzLm1lZGlhQnVmZmVyICE9PSB2aWRlb0J1ZmZlcikge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdzd2l0Y2hpbmcgb24gYWx0ZXJuYXRlIGF1ZGlvLCB1c2UgdmlkZW8uYnVmZmVyZWQgdG8gc2NoZWR1bGUgbWFpbiBmcmFnbWVudCBsb2FkaW5nJyk7XG4gICAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSB2aWRlb0J1ZmZlcjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmFsdEF1ZGlvID0gYWx0QXVkaW87XG4gICAgdGhpcy50aWNrKCk7XG4gIH07XG5cbiAgX3Byb3RvLm9uQnVmZmVyQ3JlYXRlZCA9IGZ1bmN0aW9uIG9uQnVmZmVyQ3JlYXRlZChkYXRhKSB7XG4gICAgdmFyIHRyYWNrcyA9IGRhdGEudHJhY2tzLFxuICAgICAgICBtZWRpYVRyYWNrLFxuICAgICAgICBuYW1lLFxuICAgICAgICBhbHRlcm5hdGUgPSBmYWxzZTtcblxuICAgIGZvciAodmFyIHR5cGUgaW4gdHJhY2tzKSB7XG4gICAgICB2YXIgdHJhY2sgPSB0cmFja3NbdHlwZV07XG5cbiAgICAgIGlmICh0cmFjay5pZCA9PT0gJ21haW4nKSB7XG4gICAgICAgIG5hbWUgPSB0eXBlO1xuICAgICAgICBtZWRpYVRyYWNrID0gdHJhY2s7IC8vIGtlZXAgdmlkZW8gc291cmNlIGJ1ZmZlciByZWZlcmVuY2VcblxuICAgICAgICBpZiAodHlwZSA9PT0gJ3ZpZGVvJykge1xuICAgICAgICAgIHRoaXMudmlkZW9CdWZmZXIgPSB0cmFja3NbdHlwZV0uYnVmZmVyO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhbHRlcm5hdGUgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChhbHRlcm5hdGUgJiYgbWVkaWFUcmFjaykge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImFsdGVybmF0ZSB0cmFjayBmb3VuZCwgdXNlIFwiICsgbmFtZSArIFwiLmJ1ZmZlcmVkIHRvIHNjaGVkdWxlIG1haW4gZnJhZ21lbnQgbG9hZGluZ1wiKTtcbiAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSBtZWRpYVRyYWNrLmJ1ZmZlcjtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5tZWRpYUJ1ZmZlciA9IHRoaXMubWVkaWE7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkJ1ZmZlckFwcGVuZGVkID0gZnVuY3Rpb24gb25CdWZmZXJBcHBlbmRlZChkYXRhKSB7XG4gICAgaWYgKGRhdGEucGFyZW50ID09PSAnbWFpbicpIHtcbiAgICAgIHZhciBzdGF0ZSA9IHRoaXMuc3RhdGU7XG5cbiAgICAgIGlmIChzdGF0ZSA9PT0gU3RhdGUuUEFSU0lORyB8fCBzdGF0ZSA9PT0gU3RhdGUuUEFSU0VEKSB7XG4gICAgICAgIC8vIGNoZWNrIGlmIGFsbCBidWZmZXJzIGhhdmUgYmVlbiBhcHBlbmRlZFxuICAgICAgICB0aGlzLnBlbmRpbmdCdWZmZXJpbmcgPSBkYXRhLnBlbmRpbmcgPiAwO1xuXG4gICAgICAgIHRoaXMuX2NoZWNrQXBwZW5kZWRQYXJzZWQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLl9jaGVja0FwcGVuZGVkUGFyc2VkID0gZnVuY3Rpb24gX2NoZWNrQXBwZW5kZWRQYXJzZWQoKSB7XG4gICAgLy8gdHJpZ2dlciBoYW5kbGVyIHJpZ2h0IG5vd1xuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTRUQgJiYgKCF0aGlzLmFwcGVuZGVkIHx8ICF0aGlzLnBlbmRpbmdCdWZmZXJpbmcpKSB7XG4gICAgICB2YXIgZnJhZyA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG5cbiAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgIHZhciBtZWRpYSA9IHRoaXMubWVkaWFCdWZmZXIgPyB0aGlzLm1lZGlhQnVmZmVyIDogdGhpcy5tZWRpYTtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIm1haW4gYnVmZmVyZWQgOiBcIiArIHRpbWVfcmFuZ2VzLnRvU3RyaW5nKG1lZGlhLmJ1ZmZlcmVkKSk7XG4gICAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gZnJhZztcbiAgICAgICAgdmFyIHN0YXRzID0gdGhpcy5zdGF0cztcbiAgICAgICAgc3RhdHMudGJ1ZmZlcmVkID0gd2luZG93LnBlcmZvcm1hbmNlLm5vdygpOyAvLyB3ZSBzaG91bGQgZ2V0IHJpZCBvZiB0aGlzLmZyYWdMYXN0S2Jwc1xuXG4gICAgICAgIHRoaXMuZnJhZ0xhc3RLYnBzID0gTWF0aC5yb3VuZCg4ICogc3RhdHMudG90YWwgLyAoc3RhdHMudGJ1ZmZlcmVkIC0gc3RhdHMudGZpcnN0KSk7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0JVRkZFUkVELCB7XG4gICAgICAgICAgc3RhdHM6IHN0YXRzLFxuICAgICAgICAgIGZyYWc6IGZyYWcsXG4gICAgICAgICAgaWQ6ICdtYWluJ1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICB9IC8vIERvIG5vdCB0aWNrIHdoZW4gX3NlZWtUb1N0YXJ0UG9zIG5lZWRzIHRvIGJlIGNhbGxlZCBhcyBzZWVraW5nIHRvIHRoZSBzdGFydCBjYW4gZmFpbCBvbiBsaXZlIHN0cmVhbXMgYXQgdGhpcyBwb2ludFxuXG5cbiAgICAgIGlmICh0aGlzLmxvYWRlZG1ldGFkYXRhIHx8IHRoaXMuc3RhcnRQb3NpdGlvbiA8PSAwKSB7XG4gICAgICAgIHRoaXMudGljaygpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25FcnJvciA9IGZ1bmN0aW9uIG9uRXJyb3IoZGF0YSkge1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnIHx8IHRoaXMuZnJhZ0N1cnJlbnQ7IC8vIGRvbid0IGhhbmRsZSBmcmFnIGVycm9yIG5vdCByZWxhdGVkIHRvIG1haW4gZnJhZ21lbnRcblxuICAgIGlmIChmcmFnICYmIGZyYWcudHlwZSAhPT0gJ21haW4nKSB7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyAwLjUgOiB0b2xlcmFuY2UgbmVlZGVkIGFzIHNvbWUgYnJvd3NlcnMgc3RhbGxzIHBsYXliYWNrIGJlZm9yZSByZWFjaGluZyBidWZmZXJlZCBlbmRcblxuXG4gICAgdmFyIG1lZGlhQnVmZmVyZWQgPSAhIXRoaXMubWVkaWEgJiYgQnVmZmVySGVscGVyLmlzQnVmZmVyZWQodGhpcy5tZWRpYSwgdGhpcy5tZWRpYS5jdXJyZW50VGltZSkgJiYgQnVmZmVySGVscGVyLmlzQnVmZmVyZWQodGhpcy5tZWRpYSwgdGhpcy5tZWRpYS5jdXJyZW50VGltZSArIDAuNSk7XG5cbiAgICBzd2l0Y2ggKGRhdGEuZGV0YWlscykge1xuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLktFWV9MT0FEX1RJTUVPVVQ6XG4gICAgICAgIGlmICghZGF0YS5mYXRhbCkge1xuICAgICAgICAgIC8vIGtlZXAgcmV0cnlpbmcgdW50aWwgdGhlIGxpbWl0IHdpbGwgYmUgcmVhY2hlZFxuICAgICAgICAgIGlmICh0aGlzLmZyYWdMb2FkRXJyb3IgKyAxIDw9IHRoaXMuY29uZmlnLmZyYWdMb2FkaW5nTWF4UmV0cnkpIHtcbiAgICAgICAgICAgIC8vIGV4cG9uZW50aWFsIGJhY2tvZmYgY2FwcGVkIHRvIGNvbmZpZy5mcmFnTG9hZGluZ01heFJldHJ5VGltZW91dFxuICAgICAgICAgICAgdmFyIGRlbGF5ID0gTWF0aC5taW4oTWF0aC5wb3coMiwgdGhpcy5mcmFnTG9hZEVycm9yKSAqIHRoaXMuY29uZmlnLmZyYWdMb2FkaW5nUmV0cnlEZWxheSwgdGhpcy5jb25maWcuZnJhZ0xvYWRpbmdNYXhSZXRyeVRpbWVvdXQpO1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJtZWRpYUNvbnRyb2xsZXI6IGZyYWcgbG9hZGluZyBmYWlsZWQsIHJldHJ5IGluIFwiICsgZGVsYXkgKyBcIiBtc1wiKTtcbiAgICAgICAgICAgIHRoaXMucmV0cnlEYXRlID0gd2luZG93LnBlcmZvcm1hbmNlLm5vdygpICsgZGVsYXk7IC8vIHJldHJ5IGxvYWRpbmcgc3RhdGVcbiAgICAgICAgICAgIC8vIGlmIGxvYWRlZG1ldGFkYXRhIGlzIG5vdCBzZXQsIGl0IG1lYW5zIHRoYXQgd2UgYXJlIGVtZXJnZW5jeSBzd2l0Y2ggZG93biBvbiBmaXJzdCBmcmFnXG4gICAgICAgICAgICAvLyBpbiB0aGF0IGNhc2UsIHJlc2V0IHN0YXJ0RnJhZ1JlcXVlc3RlZCBmbGFnXG5cbiAgICAgICAgICAgIGlmICghdGhpcy5sb2FkZWRtZXRhZGF0YSkge1xuICAgICAgICAgICAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb247XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRoaXMuZnJhZ0xvYWRFcnJvcisrO1xuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoXCJtZWRpYUNvbnRyb2xsZXI6IFwiICsgZGF0YS5kZXRhaWxzICsgXCIgcmVhY2hlcyBtYXggcmV0cnksIHJlZGlzcGF0Y2ggYXMgZmF0YWwgLi4uXCIpOyAvLyBzd2l0Y2ggZXJyb3IgdG8gZmF0YWxcblxuICAgICAgICAgICAgZGF0YS5mYXRhbCA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRVJST1I7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkxFVkVMX0xPQURfRVJST1I6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5MRVZFTF9MT0FEX1RJTUVPVVQ6XG4gICAgICAgIGlmICh0aGlzLnN0YXRlICE9PSBTdGF0ZS5FUlJPUikge1xuICAgICAgICAgIGlmIChkYXRhLmZhdGFsKSB7XG4gICAgICAgICAgICAvLyBpZiBmYXRhbCBlcnJvciwgc3RvcCBwcm9jZXNzaW5nXG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRVJST1I7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcInN0cmVhbUNvbnRyb2xsZXI6IFwiICsgZGF0YS5kZXRhaWxzICsgXCIsc3dpdGNoIHRvIFwiICsgdGhpcy5zdGF0ZSArIFwiIHN0YXRlIC4uLlwiKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gaW4gY2FzZSBvZiBub24gZmF0YWwgZXJyb3Igd2hpbGUgbG9hZGluZyBsZXZlbCwgaWYgbGV2ZWwgY29udHJvbGxlciBpcyBub3QgcmV0cnlpbmcgdG8gbG9hZCBsZXZlbCAsIHN3aXRjaCBiYWNrIHRvIElETEVcbiAgICAgICAgICAgIGlmICghZGF0YS5sZXZlbFJldHJ5ICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLldBSVRJTkdfTEVWRUwpIHtcbiAgICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkJVRkZFUl9GVUxMX0VSUk9SOlxuICAgICAgICAvLyBpZiBpbiBhcHBlbmRpbmcgc3RhdGVcbiAgICAgICAgaWYgKGRhdGEucGFyZW50ID09PSAnbWFpbicgJiYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcgfHwgdGhpcy5zdGF0ZSA9PT0gU3RhdGUuUEFSU0VEKSkge1xuICAgICAgICAgIC8vIHJlZHVjZSBtYXggYnVmIGxlbiBpZiBjdXJyZW50IHBvc2l0aW9uIGlzIGJ1ZmZlcmVkXG4gICAgICAgICAgaWYgKG1lZGlhQnVmZmVyZWQpIHtcbiAgICAgICAgICAgIHRoaXMuX3JlZHVjZU1heEJ1ZmZlckxlbmd0aCh0aGlzLmNvbmZpZy5tYXhCdWZmZXJMZW5ndGgpO1xuXG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gY3VycmVudCBwb3NpdGlvbiBpcyBub3QgYnVmZmVyZWQsIGJ1dCBicm93c2VyIGlzIHN0aWxsIGNvbXBsYWluaW5nIGFib3V0IGJ1ZmZlciBmdWxsIGVycm9yXG4gICAgICAgICAgICAvLyB0aGlzIGhhcHBlbnMgb24gSUUvRWRnZSwgcmVmZXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvcHVsbC83MDhcbiAgICAgICAgICAgIC8vIGluIHRoYXQgY2FzZSBmbHVzaCB0aGUgd2hvbGUgYnVmZmVyIHRvIHJlY292ZXJcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdidWZmZXIgZnVsbCBlcnJvciBhbHNvIG1lZGlhLmN1cnJlbnRUaW1lIGlzIG5vdCBidWZmZXJlZCwgZmx1c2ggZXZlcnl0aGluZycpO1xuICAgICAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7IC8vIGZsdXNoIGV2ZXJ5dGhpbmdcblxuICAgICAgICAgICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBicmVhaztcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5fcmVkdWNlTWF4QnVmZmVyTGVuZ3RoID0gZnVuY3Rpb24gX3JlZHVjZU1heEJ1ZmZlckxlbmd0aChtaW5MZW5ndGgpIHtcbiAgICB2YXIgY29uZmlnID0gdGhpcy5jb25maWc7XG5cbiAgICBpZiAoY29uZmlnLm1heE1heEJ1ZmZlckxlbmd0aCA+PSBtaW5MZW5ndGgpIHtcbiAgICAgIC8vIHJlZHVjZSBtYXggYnVmZmVyIGxlbmd0aCBhcyBpdCBtaWdodCBiZSB0b28gaGlnaC4gd2UgZG8gdGhpcyB0byBhdm9pZCBsb29wIGZsdXNoaW5nIC4uLlxuICAgICAgY29uZmlnLm1heE1heEJ1ZmZlckxlbmd0aCAvPSAyO1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJtYWluOnJlZHVjZSBtYXggYnVmZmVyIGxlbmd0aCB0byBcIiArIGNvbmZpZy5tYXhNYXhCdWZmZXJMZW5ndGggKyBcInNcIik7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrcyB0aGUgaGVhbHRoIG9mIHRoZSBidWZmZXIgYW5kIGF0dGVtcHRzIHRvIHJlc29sdmUgcGxheWJhY2sgc3RhbGxzLlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fY2hlY2tCdWZmZXIgPSBmdW5jdGlvbiBfY2hlY2tCdWZmZXIoKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcblxuICAgIGlmICghbWVkaWEgfHwgbWVkaWEucmVhZHlTdGF0ZSA9PT0gMCkge1xuICAgICAgLy8gRXhpdCBlYXJseSBpZiB3ZSBkb24ndCBoYXZlIG1lZGlhIG9yIGlmIHRoZSBtZWRpYSBoYXNuJ3QgYnVmZmVyZCBhbnl0aGluZyB5ZXQgKHJlYWR5U3RhdGUgMClcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgbWVkaWFCdWZmZXIgPSB0aGlzLm1lZGlhQnVmZmVyID8gdGhpcy5tZWRpYUJ1ZmZlciA6IG1lZGlhO1xuICAgIHZhciBidWZmZXJlZCA9IG1lZGlhQnVmZmVyLmJ1ZmZlcmVkO1xuXG4gICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhICYmIGJ1ZmZlcmVkLmxlbmd0aCkge1xuICAgICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IHRydWU7XG5cbiAgICAgIHRoaXMuX3NlZWtUb1N0YXJ0UG9zKCk7XG4gICAgfSBlbHNlIGlmICh0aGlzLmltbWVkaWF0ZVN3aXRjaCkge1xuICAgICAgdGhpcy5pbW1lZGlhdGVMZXZlbFN3aXRjaEVuZCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmdhcENvbnRyb2xsZXIucG9sbCh0aGlzLmxhc3RDdXJyZW50VGltZSwgYnVmZmVyZWQpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25GcmFnTG9hZEVtZXJnZW5jeUFib3J0ZWQgPSBmdW5jdGlvbiBvbkZyYWdMb2FkRW1lcmdlbmN5QWJvcnRlZCgpIHtcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTsgLy8gaWYgbG9hZGVkbWV0YWRhdGEgaXMgbm90IHNldCwgaXQgbWVhbnMgdGhhdCB3ZSBhcmUgZW1lcmdlbmN5IHN3aXRjaCBkb3duIG9uIGZpcnN0IGZyYWdcbiAgICAvLyBpbiB0aGF0IGNhc2UsIHJlc2V0IHN0YXJ0RnJhZ1JlcXVlc3RlZCBmbGFnXG5cbiAgICBpZiAoIXRoaXMubG9hZGVkbWV0YWRhdGEpIHtcbiAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gZmFsc2U7XG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb247XG4gICAgfVxuXG4gICAgdGhpcy50aWNrKCk7XG4gIH07XG5cbiAgX3Byb3RvLm9uQnVmZmVyRmx1c2hlZCA9IGZ1bmN0aW9uIG9uQnVmZmVyRmx1c2hlZCgpIHtcbiAgICAvKiBhZnRlciBzdWNjZXNzZnVsIGJ1ZmZlciBmbHVzaGluZywgZmlsdGVyIGZsdXNoZWQgZnJhZ21lbnRzIGZyb20gYnVmZmVyZWRGcmFnc1xuICAgICAgdXNlIG1lZGlhQnVmZmVyZWQgaW5zdGVhZCBvZiBtZWRpYSAoc28gdGhhdCB3ZSB3aWxsIGNoZWNrIGFnYWluc3QgdmlkZW8uYnVmZmVyZWQgcmFuZ2VzIGluIGNhc2Ugb2YgYWx0IGF1ZGlvIHRyYWNrKVxuICAgICovXG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlciA/IHRoaXMubWVkaWFCdWZmZXIgOiB0aGlzLm1lZGlhO1xuXG4gICAgaWYgKG1lZGlhKSB7XG4gICAgICAvLyBmaWx0ZXIgZnJhZ21lbnRzIHBvdGVudGlhbGx5IGV2aWN0ZWQgZnJvbSBidWZmZXIuIHRoaXMgaXMgdG8gYXZvaWQgbWVtbGVhayBvbiBsaXZlIHN0cmVhbXNcbiAgICAgIHZhciBlbGVtZW50YXJ5U3RyZWFtVHlwZSA9IHRoaXMuYXVkaW9Pbmx5ID8gRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPIDogRWxlbWVudGFyeVN0cmVhbVR5cGVzLlZJREVPO1xuICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIuZGV0ZWN0RXZpY3RlZEZyYWdtZW50cyhlbGVtZW50YXJ5U3RyZWFtVHlwZSwgbWVkaWEuYnVmZmVyZWQpO1xuICAgIH0gLy8gbW92ZSB0byBJRExFIG9uY2UgZmx1c2ggY29tcGxldGUuIHRoaXMgc2hvdWxkIHRyaWdnZXIgbmV3IGZyYWdtZW50IGxvYWRpbmdcblxuXG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7IC8vIHJlc2V0IHJlZmVyZW5jZSB0byBmcmFnXG5cbiAgICB0aGlzLmZyYWdQcmV2aW91cyA9IG51bGw7XG4gIH07XG5cbiAgX3Byb3RvLm9uTGV2ZWxzVXBkYXRlZCA9IGZ1bmN0aW9uIG9uTGV2ZWxzVXBkYXRlZChkYXRhKSB7XG4gICAgdGhpcy5sZXZlbHMgPSBkYXRhLmxldmVscztcbiAgfTtcblxuICBfcHJvdG8uc3dhcEF1ZGlvQ29kZWMgPSBmdW5jdGlvbiBzd2FwQXVkaW9Db2RlYygpIHtcbiAgICB0aGlzLmF1ZGlvQ29kZWNTd2FwID0gIXRoaXMuYXVkaW9Db2RlY1N3YXA7XG4gIH1cbiAgLyoqXG4gICAqIFNlZWtzIHRvIHRoZSBzZXQgc3RhcnRQb3NpdGlvbiBpZiBub3QgZXF1YWwgdG8gdGhlIG1lZGlhRWxlbWVudCdzIGN1cnJlbnQgdGltZS5cbiAgICogQHByaXZhdGVcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX3NlZWtUb1N0YXJ0UG9zID0gZnVuY3Rpb24gX3NlZWtUb1N0YXJ0UG9zKCkge1xuICAgIHZhciBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgdmFyIGN1cnJlbnRUaW1lID0gbWVkaWEuY3VycmVudFRpbWU7XG4gICAgdmFyIHN0YXJ0UG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb247IC8vIG9ubHkgYWRqdXN0IGN1cnJlbnRUaW1lIGlmIGRpZmZlcmVudCBmcm9tIHN0YXJ0UG9zaXRpb24gb3IgaWYgc3RhcnRQb3NpdGlvbiBub3QgYnVmZmVyZWRcbiAgICAvLyBhdCB0aGF0IHN0YWdlLCB0aGVyZSBzaG91bGQgYmUgb25seSBvbmUgYnVmZmVyZWQgcmFuZ2UsIGFzIHdlIHJlYWNoIHRoYXQgY29kZSBhZnRlciBmaXJzdCBmcmFnbWVudCBoYXMgYmVlbiBidWZmZXJlZFxuXG4gICAgaWYgKGN1cnJlbnRUaW1lICE9PSBzdGFydFBvc2l0aW9uICYmIHN0YXJ0UG9zaXRpb24gPj0gMCkge1xuICAgICAgaWYgKG1lZGlhLnNlZWtpbmcpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImNvdWxkIG5vdCBzZWVrIHRvIFwiICsgc3RhcnRQb3NpdGlvbiArIFwiLCBhbHJlYWR5IHNlZWtpbmcgYXQgXCIgKyBjdXJyZW50VGltZSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGJ1ZmZlclN0YXJ0ID0gbWVkaWEuYnVmZmVyZWQubGVuZ3RoID8gbWVkaWEuYnVmZmVyZWQuc3RhcnQoMCkgOiAwO1xuICAgICAgdmFyIGRlbHRhID0gYnVmZmVyU3RhcnQgLSBzdGFydFBvc2l0aW9uO1xuXG4gICAgICBpZiAoZGVsdGEgPiAwICYmIGRlbHRhIDwgdGhpcy5jb25maWcubWF4QnVmZmVySG9sZSkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiYWRqdXN0aW5nIHN0YXJ0IHBvc2l0aW9uIGJ5IFwiICsgZGVsdGEgKyBcIiB0byBtYXRjaCBidWZmZXIgc3RhcnRcIik7XG4gICAgICAgIHN0YXJ0UG9zaXRpb24gKz0gZGVsdGE7XG4gICAgICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IHN0YXJ0UG9zaXRpb247XG4gICAgICB9XG5cbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJzZWVrIHRvIHRhcmdldCBzdGFydCBwb3NpdGlvbiBcIiArIHN0YXJ0UG9zaXRpb24gKyBcIiBmcm9tIGN1cnJlbnQgdGltZSBcIiArIGN1cnJlbnRUaW1lICsgXCIuIHJlYWR5IHN0YXRlIFwiICsgbWVkaWEucmVhZHlTdGF0ZSk7XG4gICAgICBtZWRpYS5jdXJyZW50VGltZSA9IHN0YXJ0UG9zaXRpb247XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5fZ2V0QXVkaW9Db2RlYyA9IGZ1bmN0aW9uIF9nZXRBdWRpb0NvZGVjKGN1cnJlbnRMZXZlbCkge1xuICAgIHZhciBhdWRpb0NvZGVjID0gdGhpcy5jb25maWcuZGVmYXVsdEF1ZGlvQ29kZWMgfHwgY3VycmVudExldmVsLmF1ZGlvQ29kZWM7XG5cbiAgICBpZiAodGhpcy5hdWRpb0NvZGVjU3dhcCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnc3dhcHBpbmcgcGxheWxpc3QgYXVkaW8gY29kZWMnKTtcblxuICAgICAgaWYgKGF1ZGlvQ29kZWMpIHtcbiAgICAgICAgaWYgKGF1ZGlvQ29kZWMuaW5kZXhPZignbXA0YS40MC41JykgIT09IC0xKSB7XG4gICAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjInO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGF1ZGlvQ29kZWMgPSAnbXA0YS40MC41JztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBhdWRpb0NvZGVjO1xuICB9O1xuXG4gIHN0cmVhbV9jb250cm9sbGVyX2NyZWF0ZUNsYXNzKFN0cmVhbUNvbnRyb2xsZXIsIFt7XG4gICAga2V5OiBcInN0YXRlXCIsXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV4dFN0YXRlKSB7XG4gICAgICBpZiAodGhpcy5zdGF0ZSAhPT0gbmV4dFN0YXRlKSB7XG4gICAgICAgIHZhciBwcmV2aW91c1N0YXRlID0gdGhpcy5zdGF0ZTtcbiAgICAgICAgdGhpcy5fc3RhdGUgPSBuZXh0U3RhdGU7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJtYWluIHN0cmVhbS1jb250cm9sbGVyOiBcIiArIHByZXZpb3VzU3RhdGUgKyBcIi0+XCIgKyBuZXh0U3RhdGUpO1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uU1RSRUFNX1NUQVRFX1RSQU5TSVRJT04sIHtcbiAgICAgICAgICBwcmV2aW91c1N0YXRlOiBwcmV2aW91c1N0YXRlLFxuICAgICAgICAgIG5leHRTdGF0ZTogbmV4dFN0YXRlXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5fc3RhdGU7XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcImN1cnJlbnRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcblxuICAgICAgaWYgKG1lZGlhKSB7XG4gICAgICAgIHZhciBmcmFnID0gdGhpcy5nZXRCdWZmZXJlZEZyYWcobWVkaWEuY3VycmVudFRpbWUpO1xuXG4gICAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgICAgcmV0dXJuIGZyYWcubGV2ZWw7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJuZXh0QnVmZmVyZWRGcmFnXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhO1xuXG4gICAgICBpZiAobWVkaWEpIHtcbiAgICAgICAgLy8gZmlyc3QgZ2V0IGVuZCByYW5nZSBvZiBjdXJyZW50IGZyYWdtZW50XG4gICAgICAgIHJldHVybiB0aGlzLmZvbGxvd2luZ0J1ZmZlcmVkRnJhZyh0aGlzLmdldEJ1ZmZlcmVkRnJhZyhtZWRpYS5jdXJyZW50VGltZSkpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcIm5leHRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIGZyYWcgPSB0aGlzLm5leHRCdWZmZXJlZEZyYWc7XG5cbiAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgIHJldHVybiBmcmFnLmxldmVsO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgfVxuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJsaXZlU3luY1Bvc2l0aW9uXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5fbGl2ZVN5bmNQb3NpdGlvbjtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gc2V0KHZhbHVlKSB7XG4gICAgICB0aGlzLl9saXZlU3luY1Bvc2l0aW9uID0gdmFsdWU7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIFN0cmVhbUNvbnRyb2xsZXI7XG59KGJhc2Vfc3RyZWFtX2NvbnRyb2xsZXJfQmFzZVN0cmVhbUNvbnRyb2xsZXIpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBzdHJlYW1fY29udHJvbGxlciA9IChzdHJlYW1fY29udHJvbGxlcl9TdHJlYW1Db250cm9sbGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvbGV2ZWwtY29udHJvbGxlci5qc1xuZnVuY3Rpb24gbGV2ZWxfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gbGV2ZWxfY29udHJvbGxlcl9jcmVhdGVDbGFzcyhDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGxldmVsX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGxldmVsX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gbGV2ZWxfY29udHJvbGxlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG4vKlxuICogTGV2ZWwgQ29udHJvbGxlclxuKi9cblxuXG5cblxuXG5cbnZhciBjaHJvbWVPckZpcmVmb3g7XG5cbnZhciBsZXZlbF9jb250cm9sbGVyX0xldmVsQ29udHJvbGxlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBsZXZlbF9jb250cm9sbGVyX2luaGVyaXRzTG9vc2UoTGV2ZWxDb250cm9sbGVyLCBfRXZlbnRIYW5kbGVyKTtcblxuICBmdW5jdGlvbiBMZXZlbENvbnRyb2xsZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLk1BTklGRVNUX0xPQURFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfU1dJVENIRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IpIHx8IHRoaXM7XG4gICAgX3RoaXMuY2FubG9hZCA9IGZhbHNlO1xuICAgIF90aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gbnVsbDtcbiAgICBfdGhpcy5tYW51YWxMZXZlbEluZGV4ID0gLTE7XG4gICAgX3RoaXMudGltZXIgPSBudWxsO1xuICAgIGNocm9tZU9yRmlyZWZveCA9IC9jaHJvbWV8ZmlyZWZveC8udGVzdChuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCkpO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBMZXZlbENvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5vbkhhbmRsZXJEZXN0cm95aW5nID0gZnVuY3Rpb24gb25IYW5kbGVyRGVzdHJveWluZygpIHtcbiAgICB0aGlzLmNsZWFyVGltZXIoKTtcbiAgICB0aGlzLm1hbnVhbExldmVsSW5kZXggPSAtMTtcbiAgfTtcblxuICBfcHJvdG8uY2xlYXJUaW1lciA9IGZ1bmN0aW9uIGNsZWFyVGltZXIoKSB7XG4gICAgaWYgKHRoaXMudGltZXIgIT09IG51bGwpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLnRpbWVyKTtcbiAgICAgIHRoaXMudGltZXIgPSBudWxsO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uc3RhcnRMb2FkID0gZnVuY3Rpb24gc3RhcnRMb2FkKCkge1xuICAgIHZhciBsZXZlbHMgPSB0aGlzLl9sZXZlbHM7XG4gICAgdGhpcy5jYW5sb2FkID0gdHJ1ZTtcbiAgICB0aGlzLmxldmVsUmV0cnlDb3VudCA9IDA7IC8vIGNsZWFuIHVwIGxpdmUgbGV2ZWwgZGV0YWlscyB0byBmb3JjZSByZWxvYWQgdGhlbSwgYW5kIHJlc2V0IGxvYWQgZXJyb3JzXG5cbiAgICBpZiAobGV2ZWxzKSB7XG4gICAgICBsZXZlbHMuZm9yRWFjaChmdW5jdGlvbiAobGV2ZWwpIHtcbiAgICAgICAgbGV2ZWwubG9hZEVycm9yID0gMDtcbiAgICAgICAgdmFyIGxldmVsRGV0YWlscyA9IGxldmVsLmRldGFpbHM7XG5cbiAgICAgICAgaWYgKGxldmVsRGV0YWlscyAmJiBsZXZlbERldGFpbHMubGl2ZSkge1xuICAgICAgICAgIGxldmVsLmRldGFpbHMgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0gLy8gc3BlZWQgdXAgbGl2ZSBwbGF5bGlzdCByZWZyZXNoIGlmIHRpbWVyIGV4aXN0c1xuXG5cbiAgICBpZiAodGhpcy50aW1lciAhPT0gbnVsbCkge1xuICAgICAgdGhpcy5sb2FkTGV2ZWwoKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLnN0b3BMb2FkID0gZnVuY3Rpb24gc3RvcExvYWQoKSB7XG4gICAgdGhpcy5jYW5sb2FkID0gZmFsc2U7XG4gIH07XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RMb2FkZWQgPSBmdW5jdGlvbiBvbk1hbmlmZXN0TG9hZGVkKGRhdGEpIHtcbiAgICB2YXIgbGV2ZWxzID0gW107XG4gICAgdmFyIGF1ZGlvVHJhY2tzID0gW107XG4gICAgdmFyIGJpdHJhdGVTdGFydDtcbiAgICB2YXIgbGV2ZWxTZXQgPSB7fTtcbiAgICB2YXIgbGV2ZWxGcm9tU2V0ID0gbnVsbDtcbiAgICB2YXIgdmlkZW9Db2RlY0ZvdW5kID0gZmFsc2U7XG4gICAgdmFyIGF1ZGlvQ29kZWNGb3VuZCA9IGZhbHNlOyAvLyByZWdyb3VwIHJlZHVuZGFudCBsZXZlbHMgdG9nZXRoZXJcblxuICAgIGRhdGEubGV2ZWxzLmZvckVhY2goZnVuY3Rpb24gKGxldmVsKSB7XG4gICAgICB2YXIgYXR0cmlidXRlcyA9IGxldmVsLmF0dHJzO1xuICAgICAgbGV2ZWwubG9hZEVycm9yID0gMDtcbiAgICAgIGxldmVsLmZyYWdtZW50RXJyb3IgPSBmYWxzZTtcbiAgICAgIHZpZGVvQ29kZWNGb3VuZCA9IHZpZGVvQ29kZWNGb3VuZCB8fCAhIWxldmVsLnZpZGVvQ29kZWM7XG4gICAgICBhdWRpb0NvZGVjRm91bmQgPSBhdWRpb0NvZGVjRm91bmQgfHwgISFsZXZlbC5hdWRpb0NvZGVjOyAvLyBlcmFzZSBhdWRpbyBjb2RlYyBpbmZvIGlmIGJyb3dzZXIgZG9lcyBub3Qgc3VwcG9ydCBtcDRhLjQwLjM0LlxuICAgICAgLy8gZGVtdXhlciB3aWxsIGF1dG9kZXRlY3QgY29kZWMgYW5kIGZhbGxiYWNrIHRvIG1wZWcvYXVkaW9cblxuICAgICAgaWYgKGNocm9tZU9yRmlyZWZveCAmJiBsZXZlbC5hdWRpb0NvZGVjICYmIGxldmVsLmF1ZGlvQ29kZWMuaW5kZXhPZignbXA0YS40MC4zNCcpICE9PSAtMSkge1xuICAgICAgICBsZXZlbC5hdWRpb0NvZGVjID0gdW5kZWZpbmVkO1xuICAgICAgfVxuXG4gICAgICBsZXZlbEZyb21TZXQgPSBsZXZlbFNldFtsZXZlbC5iaXRyYXRlXTsgLy8gRklYTUU6IHdlIHdvdWxkIGFsc28gaGF2ZSB0byBtYXRjaCB0aGUgcmVzb2x1dGlvbiBoZXJlXG5cbiAgICAgIGlmICghbGV2ZWxGcm9tU2V0KSB7XG4gICAgICAgIGxldmVsLnVybCA9IFtsZXZlbC51cmxdO1xuICAgICAgICBsZXZlbC51cmxJZCA9IDA7XG4gICAgICAgIGxldmVsU2V0W2xldmVsLmJpdHJhdGVdID0gbGV2ZWw7XG4gICAgICAgIGxldmVscy5wdXNoKGxldmVsKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldmVsRnJvbVNldC51cmwucHVzaChsZXZlbC51cmwpO1xuICAgICAgfVxuXG4gICAgICBpZiAoYXR0cmlidXRlcykge1xuICAgICAgICBpZiAoYXR0cmlidXRlcy5BVURJTykge1xuICAgICAgICAgIGFkZEdyb3VwSWQobGV2ZWxGcm9tU2V0IHx8IGxldmVsLCAnYXVkaW8nLCBhdHRyaWJ1dGVzLkFVRElPKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChhdHRyaWJ1dGVzLlNVQlRJVExFUykge1xuICAgICAgICAgIGFkZEdyb3VwSWQobGV2ZWxGcm9tU2V0IHx8IGxldmVsLCAndGV4dCcsIGF0dHJpYnV0ZXMuU1VCVElUTEVTKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pOyAvLyByZW1vdmUgYXVkaW8tb25seSBsZXZlbCBpZiB3ZSBhbHNvIGhhdmUgbGV2ZWxzIHdpdGggYXVkaW8rdmlkZW8gY29kZWNzIHNpZ25hbGxlZFxuXG4gICAgaWYgKHZpZGVvQ29kZWNGb3VuZCAmJiBhdWRpb0NvZGVjRm91bmQpIHtcbiAgICAgIGxldmVscyA9IGxldmVscy5maWx0ZXIoZnVuY3Rpb24gKF9yZWYpIHtcbiAgICAgICAgdmFyIHZpZGVvQ29kZWMgPSBfcmVmLnZpZGVvQ29kZWM7XG4gICAgICAgIHJldHVybiAhIXZpZGVvQ29kZWM7XG4gICAgICB9KTtcbiAgICB9IC8vIG9ubHkga2VlcCBsZXZlbHMgd2l0aCBzdXBwb3J0ZWQgYXVkaW8vdmlkZW8gY29kZWNzXG5cblxuICAgIGxldmVscyA9IGxldmVscy5maWx0ZXIoZnVuY3Rpb24gKF9yZWYyKSB7XG4gICAgICB2YXIgYXVkaW9Db2RlYyA9IF9yZWYyLmF1ZGlvQ29kZWMsXG4gICAgICAgICAgdmlkZW9Db2RlYyA9IF9yZWYyLnZpZGVvQ29kZWM7XG4gICAgICByZXR1cm4gKCFhdWRpb0NvZGVjIHx8IGlzQ29kZWNTdXBwb3J0ZWRJbk1wNChhdWRpb0NvZGVjLCAnYXVkaW8nKSkgJiYgKCF2aWRlb0NvZGVjIHx8IGlzQ29kZWNTdXBwb3J0ZWRJbk1wNCh2aWRlb0NvZGVjLCAndmlkZW8nKSk7XG4gICAgfSk7XG5cbiAgICBpZiAoZGF0YS5hdWRpb1RyYWNrcykge1xuICAgICAgYXVkaW9UcmFja3MgPSBkYXRhLmF1ZGlvVHJhY2tzLmZpbHRlcihmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgcmV0dXJuICF0cmFjay5hdWRpb0NvZGVjIHx8IGlzQ29kZWNTdXBwb3J0ZWRJbk1wNCh0cmFjay5hdWRpb0NvZGVjLCAnYXVkaW8nKTtcbiAgICAgIH0pOyAvLyBSZWFzc2lnbiBpZCdzIGFmdGVyIGZpbHRlcmluZyBzaW5jZSB0aGV5J3JlIHVzZWQgYXMgYXJyYXkgaW5kaWNlc1xuXG4gICAgICBhdWRpb1RyYWNrcy5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaywgaW5kZXgpIHtcbiAgICAgICAgdHJhY2suaWQgPSBpbmRleDtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmIChsZXZlbHMubGVuZ3RoID4gMCkge1xuICAgICAgLy8gc3RhcnQgYml0cmF0ZSBpcyB0aGUgZmlyc3QgYml0cmF0ZSBvZiB0aGUgbWFuaWZlc3RcbiAgICAgIGJpdHJhdGVTdGFydCA9IGxldmVsc1swXS5iaXRyYXRlOyAvLyBzb3J0IGxldmVsIG9uIGJpdHJhdGVcblxuICAgICAgbGV2ZWxzLnNvcnQoZnVuY3Rpb24gKGEsIGIpIHtcbiAgICAgICAgcmV0dXJuIGEuYml0cmF0ZSAtIGIuYml0cmF0ZTtcbiAgICAgIH0pO1xuICAgICAgdGhpcy5fbGV2ZWxzID0gbGV2ZWxzOyAvLyBmaW5kIGluZGV4IG9mIGZpcnN0IGxldmVsIGluIHNvcnRlZCBsZXZlbHNcblxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZXZlbHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGxldmVsc1tpXS5iaXRyYXRlID09PSBiaXRyYXRlU3RhcnQpIHtcbiAgICAgICAgICB0aGlzLl9maXJzdExldmVsID0gaTtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibWFuaWZlc3QgbG9hZGVkLFwiICsgbGV2ZWxzLmxlbmd0aCArIFwiIGxldmVsKHMpIGZvdW5kLCBmaXJzdCBiaXRyYXRlOlwiICsgYml0cmF0ZVN0YXJ0KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfSAvLyBBdWRpbyBpcyBvbmx5IGFsdGVybmF0ZSBpZiBtYW5pZmVzdCBpbmNsdWRlIGEgVVJJIGFsb25nIHdpdGggdGhlIGF1ZGlvIGdyb3VwIHRhZyxcbiAgICAgIC8vIGFuZCB0aGlzIGlzIG5vdCBhbiBhdWRpby1vbmx5IHN0cmVhbSB3aGVyZSBsZXZlbHMgY29udGFpbiBhdWRpby1vbmx5XG5cblxuICAgICAgdmFyIGF1ZGlvT25seSA9IGF1ZGlvQ29kZWNGb3VuZCAmJiAhdmlkZW9Db2RlY0ZvdW5kO1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLk1BTklGRVNUX1BBUlNFRCwge1xuICAgICAgICBsZXZlbHM6IGxldmVscyxcbiAgICAgICAgYXVkaW9UcmFja3M6IGF1ZGlvVHJhY2tzLFxuICAgICAgICBmaXJzdExldmVsOiB0aGlzLl9maXJzdExldmVsLFxuICAgICAgICBzdGF0czogZGF0YS5zdGF0cyxcbiAgICAgICAgYXVkaW86IGF1ZGlvQ29kZWNGb3VuZCxcbiAgICAgICAgdmlkZW86IHZpZGVvQ29kZWNGb3VuZCxcbiAgICAgICAgYWx0QXVkaW86ICFhdWRpb09ubHkgJiYgYXVkaW9UcmFja3Muc29tZShmdW5jdGlvbiAodCkge1xuICAgICAgICAgIHJldHVybiAhIXQudXJsO1xuICAgICAgICB9KVxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uTUFOSUZFU1RfSU5DT01QQVRJQkxFX0NPREVDU19FUlJPUixcbiAgICAgICAgZmF0YWw6IHRydWUsXG4gICAgICAgIHVybDogdGhpcy5obHMudXJsLFxuICAgICAgICByZWFzb246ICdubyBsZXZlbCB3aXRoIGNvbXBhdGlibGUgY29kZWNzIGZvdW5kIGluIG1hbmlmZXN0J1xuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5zZXRMZXZlbEludGVybmFsID0gZnVuY3Rpb24gc2V0TGV2ZWxJbnRlcm5hbChuZXdMZXZlbCkge1xuICAgIHZhciBsZXZlbHMgPSB0aGlzLl9sZXZlbHM7XG4gICAgdmFyIGhscyA9IHRoaXMuaGxzOyAvLyBjaGVjayBpZiBsZXZlbCBpZHggaXMgdmFsaWRcblxuICAgIGlmIChuZXdMZXZlbCA+PSAwICYmIG5ld0xldmVsIDwgbGV2ZWxzLmxlbmd0aCkge1xuICAgICAgLy8gc3RvcHBpbmcgbGl2ZSByZWxvYWRpbmcgdGltZXIgaWYgYW55XG4gICAgICB0aGlzLmNsZWFyVGltZXIoKTtcblxuICAgICAgaWYgKHRoaXMuY3VycmVudExldmVsSW5kZXggIT09IG5ld0xldmVsKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJzd2l0Y2hpbmcgdG8gbGV2ZWwgXCIgKyBuZXdMZXZlbCk7XG4gICAgICAgIHRoaXMuY3VycmVudExldmVsSW5kZXggPSBuZXdMZXZlbDtcbiAgICAgICAgdmFyIGxldmVsUHJvcGVydGllcyA9IGxldmVsc1tuZXdMZXZlbF07XG4gICAgICAgIGxldmVsUHJvcGVydGllcy5sZXZlbCA9IG5ld0xldmVsO1xuICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMX1NXSVRDSElORywgbGV2ZWxQcm9wZXJ0aWVzKTtcbiAgICAgIH1cblxuICAgICAgdmFyIGxldmVsID0gbGV2ZWxzW25ld0xldmVsXTtcbiAgICAgIHZhciBsZXZlbERldGFpbHMgPSBsZXZlbC5kZXRhaWxzOyAvLyBjaGVjayBpZiB3ZSBuZWVkIHRvIGxvYWQgcGxheWxpc3QgZm9yIHRoaXMgbGV2ZWxcblxuICAgICAgaWYgKCFsZXZlbERldGFpbHMgfHwgbGV2ZWxEZXRhaWxzLmxpdmUpIHtcbiAgICAgICAgLy8gbGV2ZWwgbm90IHJldHJpZXZlZCB5ZXQsIG9yIGxpdmUgcGxheWxpc3Qgd2UgbmVlZCB0byAocmUpbG9hZCBpdFxuICAgICAgICB2YXIgdXJsSWQgPSBsZXZlbC51cmxJZDtcbiAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9MT0FESU5HLCB7XG4gICAgICAgICAgdXJsOiBsZXZlbC51cmxbdXJsSWRdLFxuICAgICAgICAgIGxldmVsOiBuZXdMZXZlbCxcbiAgICAgICAgICBpZDogdXJsSWRcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGludmFsaWQgbGV2ZWwgaWQgZ2l2ZW4sIHRyaWdnZXIgZXJyb3JcbiAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5PVEhFUl9FUlJPUixcbiAgICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkxFVkVMX1NXSVRDSF9FUlJPUixcbiAgICAgICAgbGV2ZWw6IG5ld0xldmVsLFxuICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgIHJlYXNvbjogJ2ludmFsaWQgbGV2ZWwgaWR4J1xuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkVycm9yID0gZnVuY3Rpb24gb25FcnJvcihkYXRhKSB7XG4gICAgaWYgKGRhdGEuZmF0YWwpIHtcbiAgICAgIGlmIChkYXRhLnR5cGUgPT09IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTkVUV09SS19FUlJPUikge1xuICAgICAgICB0aGlzLmNsZWFyVGltZXIoKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBsZXZlbEVycm9yID0gZmFsc2UsXG4gICAgICAgIGZyYWdtZW50RXJyb3IgPSBmYWxzZTtcbiAgICB2YXIgbGV2ZWxJbmRleDsgLy8gdHJ5IHRvIHJlY292ZXIgbm90IGZhdGFsIGVycm9yc1xuXG4gICAgc3dpdGNoIChkYXRhLmRldGFpbHMpIHtcbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkZSQUdfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkZSQUdfTE9BRF9USU1FT1VUOlxuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX0xPQURfRVJST1I6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfTE9BRF9USU1FT1VUOlxuICAgICAgICBsZXZlbEluZGV4ID0gZGF0YS5mcmFnLmxldmVsO1xuICAgICAgICBmcmFnbWVudEVycm9yID0gdHJ1ZTtcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkxFVkVMX0xPQURfRVJST1I6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5MRVZFTF9MT0FEX1RJTUVPVVQ6XG4gICAgICAgIGxldmVsSW5kZXggPSBkYXRhLmNvbnRleHQubGV2ZWw7XG4gICAgICAgIGxldmVsRXJyb3IgPSB0cnVlO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uUkVNVVhfQUxMT0NfRVJST1I6XG4gICAgICAgIGxldmVsSW5kZXggPSBkYXRhLmxldmVsO1xuICAgICAgICBsZXZlbEVycm9yID0gdHJ1ZTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgaWYgKGxldmVsSW5kZXggIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5yZWNvdmVyTGV2ZWwoZGF0YSwgbGV2ZWxJbmRleCwgbGV2ZWxFcnJvciwgZnJhZ21lbnRFcnJvcik7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBTd2l0Y2ggdG8gYSByZWR1bmRhbnQgc3RyZWFtIGlmIGFueSBhdmFpbGFibGUuXG4gICAqIElmIHJlZHVuZGFudCBzdHJlYW0gaXMgbm90IGF2YWlsYWJsZSwgZW1lcmdlbmN5IHN3aXRjaCBkb3duIGlmIEFCUiBtb2RlIGlzIGVuYWJsZWQuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBlcnJvckV2ZW50XG4gICAqIEBwYXJhbSB7TnVtYmVyfSBsZXZlbEluZGV4IGN1cnJlbnQgbGV2ZWwgaW5kZXhcbiAgICogQHBhcmFtIHtCb29sZWFufSBsZXZlbEVycm9yXG4gICAqIEBwYXJhbSB7Qm9vbGVhbn0gZnJhZ21lbnRFcnJvclxuICAgKi9cbiAgLy8gRklYTUUgRmluZCBhIGJldHRlciBhYnN0cmFjdGlvbiB3aGVyZSBmcmFnbWVudC9sZXZlbCByZXRyeSBtYW5hZ2VtZW50IGlzIHdlbGwgZGVjb3VwbGVkXG4gIDtcblxuICBfcHJvdG8ucmVjb3ZlckxldmVsID0gZnVuY3Rpb24gcmVjb3ZlckxldmVsKGVycm9yRXZlbnQsIGxldmVsSW5kZXgsIGxldmVsRXJyb3IsIGZyYWdtZW50RXJyb3IpIHtcbiAgICB2YXIgX3RoaXMyID0gdGhpcztcblxuICAgIHZhciBjb25maWcgPSB0aGlzLmhscy5jb25maWc7XG4gICAgdmFyIGVycm9yRGV0YWlscyA9IGVycm9yRXZlbnQuZGV0YWlscztcbiAgICB2YXIgbGV2ZWwgPSB0aGlzLl9sZXZlbHNbbGV2ZWxJbmRleF07XG4gICAgdmFyIHJlZHVuZGFudExldmVscywgZGVsYXksIG5leHRMZXZlbDtcbiAgICBsZXZlbC5sb2FkRXJyb3IrKztcbiAgICBsZXZlbC5mcmFnbWVudEVycm9yID0gZnJhZ21lbnRFcnJvcjtcblxuICAgIGlmIChsZXZlbEVycm9yKSB7XG4gICAgICBpZiAodGhpcy5sZXZlbFJldHJ5Q291bnQgKyAxIDw9IGNvbmZpZy5sZXZlbExvYWRpbmdNYXhSZXRyeSkge1xuICAgICAgICAvLyBleHBvbmVudGlhbCBiYWNrb2ZmIGNhcHBlZCB0byBtYXggcmV0cnkgdGltZW91dFxuICAgICAgICBkZWxheSA9IE1hdGgubWluKE1hdGgucG93KDIsIHRoaXMubGV2ZWxSZXRyeUNvdW50KSAqIGNvbmZpZy5sZXZlbExvYWRpbmdSZXRyeURlbGF5LCBjb25maWcubGV2ZWxMb2FkaW5nTWF4UmV0cnlUaW1lb3V0KTsgLy8gU2NoZWR1bGUgbGV2ZWwgcmVsb2FkXG5cbiAgICAgICAgdGhpcy50aW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIHJldHVybiBfdGhpczIubG9hZExldmVsKCk7XG4gICAgICAgIH0sIGRlbGF5KTsgLy8gYm9vbGVhbiB1c2VkIHRvIGluZm9ybSBzdHJlYW0gY29udHJvbGxlciBub3QgdG8gc3dpdGNoIGJhY2sgdG8gSURMRSBvbiBub24gZmF0YWwgZXJyb3JcblxuICAgICAgICBlcnJvckV2ZW50LmxldmVsUmV0cnkgPSB0cnVlO1xuICAgICAgICB0aGlzLmxldmVsUmV0cnlDb3VudCsrO1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcImxldmVsIGNvbnRyb2xsZXIsIFwiICsgZXJyb3JEZXRhaWxzICsgXCIsIHJldHJ5IGluIFwiICsgZGVsYXkgKyBcIiBtcywgY3VycmVudCByZXRyeSBjb3VudCBpcyBcIiArIHRoaXMubGV2ZWxSZXRyeUNvdW50KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcihcImxldmVsIGNvbnRyb2xsZXIsIGNhbm5vdCByZWNvdmVyIGZyb20gXCIgKyBlcnJvckRldGFpbHMgKyBcIiBlcnJvclwiKTtcbiAgICAgICAgdGhpcy5jdXJyZW50TGV2ZWxJbmRleCA9IG51bGw7IC8vIHN0b3BwaW5nIGxpdmUgcmVsb2FkaW5nIHRpbWVyIGlmIGFueVxuXG4gICAgICAgIHRoaXMuY2xlYXJUaW1lcigpOyAvLyBzd2l0Y2ggZXJyb3IgdG8gZmF0YWxcblxuICAgICAgICBlcnJvckV2ZW50LmZhdGFsID0gdHJ1ZTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH0gLy8gVHJ5IGFueSByZWR1bmRhbnQgc3RyZWFtcyBpZiBhdmFpbGFibGUgZm9yIGJvdGggZXJyb3JzOiBsZXZlbCBhbmQgZnJhZ21lbnRcbiAgICAvLyBJZiBsZXZlbC5sb2FkRXJyb3IgcmVhY2hlcyByZWR1bmRhbnRMZXZlbHMgaXQgbWVhbnMgdGhhdCB3ZSB0cmllZCB0aGVtIGFsbCwgbm8gaG9wZSAgPT4gbGV0J3Mgc3dpdGNoIGRvd25cblxuXG4gICAgaWYgKGxldmVsRXJyb3IgfHwgZnJhZ21lbnRFcnJvcikge1xuICAgICAgcmVkdW5kYW50TGV2ZWxzID0gbGV2ZWwudXJsLmxlbmd0aDtcblxuICAgICAgaWYgKHJlZHVuZGFudExldmVscyA+IDEgJiYgbGV2ZWwubG9hZEVycm9yIDwgcmVkdW5kYW50TGV2ZWxzKSB7XG4gICAgICAgIGxldmVsLnVybElkID0gKGxldmVsLnVybElkICsgMSkgJSByZWR1bmRhbnRMZXZlbHM7XG4gICAgICAgIGxldmVsLmRldGFpbHMgPSB1bmRlZmluZWQ7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwibGV2ZWwgY29udHJvbGxlciwgXCIgKyBlcnJvckRldGFpbHMgKyBcIiBmb3IgbGV2ZWwgXCIgKyBsZXZlbEluZGV4ICsgXCI6IHN3aXRjaGluZyB0byByZWR1bmRhbnQgVVJMLWlkIFwiICsgbGV2ZWwudXJsSWQpOyAvLyBjb25zb2xlLmxvZygnQ3VycmVudCBhdWRpbyB0cmFjayBncm91cCBJRDonLCB0aGlzLmhscy5hdWRpb1RyYWNrc1t0aGlzLmhscy5hdWRpb1RyYWNrXS5ncm91cElkKTtcbiAgICAgICAgLy8gY29uc29sZS5sb2coJ05ldyB2aWRlbyBxdWFsaXR5IGxldmVsIGF1ZGlvIGdyb3VwIGlkOicsIGxldmVsLmF0dHJzLkFVRElPKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFNlYXJjaCBmb3IgYXZhaWxhYmxlIGxldmVsXG4gICAgICAgIGlmICh0aGlzLm1hbnVhbExldmVsSW5kZXggPT09IC0xKSB7XG4gICAgICAgICAgLy8gV2hlbiBsb3dlc3QgbGV2ZWwgaGFzIGJlZW4gcmVhY2hlZCwgbGV0J3Mgc3RhcnQgaHVudCBmcm9tIHRoZSB0b3BcbiAgICAgICAgICBuZXh0TGV2ZWwgPSBsZXZlbEluZGV4ID09PSAwID8gdGhpcy5fbGV2ZWxzLmxlbmd0aCAtIDEgOiBsZXZlbEluZGV4IC0gMTtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcImxldmVsIGNvbnRyb2xsZXIsIFwiICsgZXJyb3JEZXRhaWxzICsgXCI6IHN3aXRjaCB0byBcIiArIG5leHRMZXZlbCk7XG4gICAgICAgICAgdGhpcy5obHMubmV4dEF1dG9MZXZlbCA9IHRoaXMuY3VycmVudExldmVsSW5kZXggPSBuZXh0TGV2ZWw7XG4gICAgICAgIH0gZWxzZSBpZiAoZnJhZ21lbnRFcnJvcikge1xuICAgICAgICAgIC8vIEFsbG93IGZyYWdtZW50IHJldHJ5IGFzIGxvbmcgYXMgY29uZmlndXJhdGlvbiBhbGxvd3MuXG4gICAgICAgICAgLy8gcmVzZXQgdGhpcy5fbGV2ZWwgc28gdGhhdCBhbm90aGVyIGNhbGwgdG8gc2V0IGxldmVsKCkgd2lsbCB0cmlnZ2VyIGFnYWluIGEgZnJhZyBsb2FkXG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJsZXZlbCBjb250cm9sbGVyLCBcIiArIGVycm9yRGV0YWlscyArIFwiOiByZWxvYWQgYSBmcmFnbWVudFwiKTtcbiAgICAgICAgICB0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSAvLyByZXNldCBlcnJvcnMgb24gdGhlIHN1Y2Nlc3NmdWwgbG9hZCBvZiBhIGZyYWdtZW50XG4gIDtcblxuICBfcHJvdG8ub25GcmFnTG9hZGVkID0gZnVuY3Rpb24gb25GcmFnTG9hZGVkKF9yZWYzKSB7XG4gICAgdmFyIGZyYWcgPSBfcmVmMy5mcmFnO1xuXG4gICAgaWYgKGZyYWcgIT09IHVuZGVmaW5lZCAmJiBmcmFnLnR5cGUgPT09ICdtYWluJykge1xuICAgICAgdmFyIGxldmVsID0gdGhpcy5fbGV2ZWxzW2ZyYWcubGV2ZWxdO1xuXG4gICAgICBpZiAobGV2ZWwgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBsZXZlbC5mcmFnbWVudEVycm9yID0gZmFsc2U7XG4gICAgICAgIGxldmVsLmxvYWRFcnJvciA9IDA7XG4gICAgICAgIHRoaXMubGV2ZWxSZXRyeUNvdW50ID0gMDtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTGV2ZWxMb2FkZWQgPSBmdW5jdGlvbiBvbkxldmVsTG9hZGVkKGRhdGEpIHtcbiAgICB2YXIgX3RoaXMzID0gdGhpcztcblxuICAgIHZhciBsZXZlbCA9IGRhdGEubGV2ZWwsXG4gICAgICAgIGRldGFpbHMgPSBkYXRhLmRldGFpbHM7IC8vIG9ubHkgcHJvY2VzcyBsZXZlbCBsb2FkZWQgZXZlbnRzIG1hdGNoaW5nIHdpdGggZXhwZWN0ZWQgbGV2ZWxcblxuICAgIGlmIChsZXZlbCAhPT0gdGhpcy5jdXJyZW50TGV2ZWxJbmRleCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBjdXJMZXZlbCA9IHRoaXMuX2xldmVsc1tsZXZlbF07IC8vIHJlc2V0IGxldmVsIGxvYWQgZXJyb3IgY291bnRlciBvbiBzdWNjZXNzZnVsIGxldmVsIGxvYWRlZCBvbmx5IGlmIHRoZXJlIGlzIG5vIGlzc3VlcyB3aXRoIGZyYWdtZW50c1xuXG4gICAgaWYgKCFjdXJMZXZlbC5mcmFnbWVudEVycm9yKSB7XG4gICAgICBjdXJMZXZlbC5sb2FkRXJyb3IgPSAwO1xuICAgICAgdGhpcy5sZXZlbFJldHJ5Q291bnQgPSAwO1xuICAgIH0gLy8gaWYgY3VycmVudCBwbGF5bGlzdCBpcyBhIGxpdmUgcGxheWxpc3QsIGFybSBhIHRpbWVyIHRvIHJlbG9hZCBpdFxuXG5cbiAgICBpZiAoZGV0YWlscy5saXZlKSB7XG4gICAgICB2YXIgcmVsb2FkSW50ZXJ2YWwgPSBjb21wdXRlUmVsb2FkSW50ZXJ2YWwoY3VyTGV2ZWwuZGV0YWlscywgZGV0YWlscywgZGF0YS5zdGF0cy50cmVxdWVzdCk7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibGl2ZSBwbGF5bGlzdCwgcmVsb2FkIGluIFwiICsgTWF0aC5yb3VuZChyZWxvYWRJbnRlcnZhbCkgKyBcIiBtc1wiKTtcbiAgICAgIHRoaXMudGltZXIgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIF90aGlzMy5sb2FkTGV2ZWwoKTtcbiAgICAgIH0sIHJlbG9hZEludGVydmFsKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkF1ZGlvVHJhY2tTd2l0Y2hlZCA9IGZ1bmN0aW9uIG9uQXVkaW9UcmFja1N3aXRjaGVkKGRhdGEpIHtcbiAgICB2YXIgYXVkaW9Hcm91cElkID0gdGhpcy5obHMuYXVkaW9UcmFja3NbZGF0YS5pZF0uZ3JvdXBJZDtcbiAgICB2YXIgY3VycmVudExldmVsID0gdGhpcy5obHMubGV2ZWxzW3RoaXMuY3VycmVudExldmVsSW5kZXhdO1xuXG4gICAgaWYgKCFjdXJyZW50TGV2ZWwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoY3VycmVudExldmVsLmF1ZGlvR3JvdXBJZHMpIHtcbiAgICAgIHZhciB1cmxJZCA9IC0xO1xuXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGN1cnJlbnRMZXZlbC5hdWRpb0dyb3VwSWRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGlmIChjdXJyZW50TGV2ZWwuYXVkaW9Hcm91cElkc1tpXSA9PT0gYXVkaW9Hcm91cElkKSB7XG4gICAgICAgICAgdXJsSWQgPSBpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICh1cmxJZCAhPT0gY3VycmVudExldmVsLnVybElkKSB7XG4gICAgICAgIGN1cnJlbnRMZXZlbC51cmxJZCA9IHVybElkO1xuICAgICAgICB0aGlzLnN0YXJ0TG9hZCgpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ubG9hZExldmVsID0gZnVuY3Rpb24gbG9hZExldmVsKCkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5kZWJ1ZygnY2FsbCB0byBsb2FkTGV2ZWwnKTtcblxuICAgIGlmICh0aGlzLmN1cnJlbnRMZXZlbEluZGV4ICE9PSBudWxsICYmIHRoaXMuY2FubG9hZCkge1xuICAgICAgdmFyIGxldmVsT2JqZWN0ID0gdGhpcy5fbGV2ZWxzW3RoaXMuY3VycmVudExldmVsSW5kZXhdO1xuXG4gICAgICBpZiAodHlwZW9mIGxldmVsT2JqZWN0ID09PSAnb2JqZWN0JyAmJiBsZXZlbE9iamVjdC51cmwubGVuZ3RoID4gMCkge1xuICAgICAgICB2YXIgbGV2ZWwgPSB0aGlzLmN1cnJlbnRMZXZlbEluZGV4O1xuICAgICAgICB2YXIgaWQgPSBsZXZlbE9iamVjdC51cmxJZDtcbiAgICAgICAgdmFyIHVybCA9IGxldmVsT2JqZWN0LnVybFtpZF07XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJBdHRlbXB0IGxvYWRpbmcgbGV2ZWwgaW5kZXggXCIgKyBsZXZlbCArIFwiIHdpdGggVVJMLWlkIFwiICsgaWQpOyAvLyBjb25zb2xlLmxvZygnQ3VycmVudCBhdWRpbyB0cmFjayBncm91cCBJRDonLCB0aGlzLmhscy5hdWRpb1RyYWNrc1t0aGlzLmhscy5hdWRpb1RyYWNrXS5ncm91cElkKTtcbiAgICAgICAgLy8gY29uc29sZS5sb2coJ05ldyB2aWRlbyBxdWFsaXR5IGxldmVsIGF1ZGlvIGdyb3VwIGlkOicsIGxldmVsT2JqZWN0LmF0dHJzLkFVRElPLCBsZXZlbCk7XG5cbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMX0xPQURJTkcsIHtcbiAgICAgICAgICB1cmw6IHVybCxcbiAgICAgICAgICBsZXZlbDogbGV2ZWwsXG4gICAgICAgICAgaWQ6IGlkXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ucmVtb3ZlTGV2ZWwgPSBmdW5jdGlvbiByZW1vdmVMZXZlbChsZXZlbEluZGV4LCB1cmxJZCkge1xuICAgIHZhciBsZXZlbHMgPSB0aGlzLmxldmVscy5maWx0ZXIoZnVuY3Rpb24gKGxldmVsLCBpbmRleCkge1xuICAgICAgaWYgKGluZGV4ICE9PSBsZXZlbEluZGV4KSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuXG4gICAgICBpZiAobGV2ZWwudXJsLmxlbmd0aCA+IDEgJiYgdXJsSWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBsZXZlbC51cmwgPSBsZXZlbC51cmwuZmlsdGVyKGZ1bmN0aW9uICh1cmwsIGlkKSB7XG4gICAgICAgICAgcmV0dXJuIGlkICE9PSB1cmxJZDtcbiAgICAgICAgfSk7XG4gICAgICAgIGxldmVsLnVybElkID0gMDtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9KS5tYXAoZnVuY3Rpb24gKGxldmVsLCBpbmRleCkge1xuICAgICAgdmFyIGRldGFpbHMgPSBsZXZlbC5kZXRhaWxzO1xuXG4gICAgICBpZiAoZGV0YWlscyAmJiBkZXRhaWxzLmZyYWdtZW50cykge1xuICAgICAgICBkZXRhaWxzLmZyYWdtZW50cy5mb3JFYWNoKGZ1bmN0aW9uIChmcmFnbWVudCkge1xuICAgICAgICAgIGZyYWdtZW50LmxldmVsID0gaW5kZXg7XG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbGV2ZWw7XG4gICAgfSk7XG4gICAgdGhpcy5fbGV2ZWxzID0gbGV2ZWxzO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTFNfVVBEQVRFRCwge1xuICAgICAgbGV2ZWxzOiBsZXZlbHNcbiAgICB9KTtcbiAgfTtcblxuICBsZXZlbF9jb250cm9sbGVyX2NyZWF0ZUNsYXNzKExldmVsQ29udHJvbGxlciwgW3tcbiAgICBrZXk6IFwibGV2ZWxzXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5fbGV2ZWxzO1xuICAgIH1cbiAgfSwge1xuICAgIGtleTogXCJsZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuY3VycmVudExldmVsSW5kZXg7XG4gICAgfSxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChuZXdMZXZlbCkge1xuICAgICAgdmFyIGxldmVscyA9IHRoaXMuX2xldmVscztcblxuICAgICAgaWYgKGxldmVscykge1xuICAgICAgICBuZXdMZXZlbCA9IE1hdGgubWluKG5ld0xldmVsLCBsZXZlbHMubGVuZ3RoIC0gMSk7XG5cbiAgICAgICAgaWYgKHRoaXMuY3VycmVudExldmVsSW5kZXggIT09IG5ld0xldmVsIHx8ICFsZXZlbHNbbmV3TGV2ZWxdLmRldGFpbHMpIHtcbiAgICAgICAgICB0aGlzLnNldExldmVsSW50ZXJuYWwobmV3TGV2ZWwpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcIm1hbnVhbExldmVsXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5tYW51YWxMZXZlbEluZGV4O1xuICAgIH0sXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV3TGV2ZWwpIHtcbiAgICAgIHRoaXMubWFudWFsTGV2ZWxJbmRleCA9IG5ld0xldmVsO1xuXG4gICAgICBpZiAodGhpcy5fc3RhcnRMZXZlbCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRoaXMuX3N0YXJ0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgICAgIH1cblxuICAgICAgaWYgKG5ld0xldmVsICE9PSAtMSkge1xuICAgICAgICB0aGlzLmxldmVsID0gbmV3TGV2ZWw7XG4gICAgICB9XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcImZpcnN0TGV2ZWxcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLl9maXJzdExldmVsO1xuICAgIH0sXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV3TGV2ZWwpIHtcbiAgICAgIHRoaXMuX2ZpcnN0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwic3RhcnRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgLy8gaGxzLnN0YXJ0TGV2ZWwgdGFrZXMgcHJlY2VkZW5jZSBvdmVyIGNvbmZpZy5zdGFydExldmVsXG4gICAgICAvLyBpZiBub25lIG9mIHRoZXNlIHZhbHVlcyBhcmUgZGVmaW5lZCwgZmFsbGJhY2sgb24gdGhpcy5fZmlyc3RMZXZlbCAoZmlyc3QgcXVhbGl0eSBsZXZlbCBhcHBlYXJpbmcgaW4gdmFyaWFudCBtYW5pZmVzdClcbiAgICAgIGlmICh0aGlzLl9zdGFydExldmVsID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdmFyIGNvbmZpZ1N0YXJ0TGV2ZWwgPSB0aGlzLmhscy5jb25maWcuc3RhcnRMZXZlbDtcblxuICAgICAgICBpZiAoY29uZmlnU3RhcnRMZXZlbCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuIGNvbmZpZ1N0YXJ0TGV2ZWw7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuX2ZpcnN0TGV2ZWw7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9zdGFydExldmVsO1xuICAgICAgfVxuICAgIH0sXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV3TGV2ZWwpIHtcbiAgICAgIHRoaXMuX3N0YXJ0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwibmV4dExvYWRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgaWYgKHRoaXMubWFudWFsTGV2ZWxJbmRleCAhPT0gLTEpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWFudWFsTGV2ZWxJbmRleDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aGlzLmhscy5uZXh0QXV0b0xldmVsO1xuICAgICAgfVxuICAgIH0sXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV4dExldmVsKSB7XG4gICAgICB0aGlzLmxldmVsID0gbmV4dExldmVsO1xuXG4gICAgICBpZiAodGhpcy5tYW51YWxMZXZlbEluZGV4ID09PSAtMSkge1xuICAgICAgICB0aGlzLmhscy5uZXh0QXV0b0xldmVsID0gbmV4dExldmVsO1xuICAgICAgfVxuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBMZXZlbENvbnRyb2xsZXI7XG59KGV2ZW50X2hhbmRsZXIpO1xuXG5cbi8vIEVYVEVSTkFMIE1PRFVMRTogLi9zcmMvZGVtdXgvaWQzLmpzXG52YXIgaWQzID0gX193ZWJwYWNrX3JlcXVpcmVfXyhcIi4vc3JjL2RlbXV4L2lkMy5qc1wiKTtcblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvdGV4dHRyYWNrLXV0aWxzLnRzXG5mdW5jdGlvbiBzZW5kQWRkVHJhY2tFdmVudCh0cmFjaywgdmlkZW9FbCkge1xuICB2YXIgZXZlbnQ7XG5cbiAgdHJ5IHtcbiAgICBldmVudCA9IG5ldyBFdmVudCgnYWRkdHJhY2snKTtcbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgLy8gZm9yIElFMTFcbiAgICBldmVudCA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KCdFdmVudCcpO1xuICAgIGV2ZW50LmluaXRFdmVudCgnYWRkdHJhY2snLCBmYWxzZSwgZmFsc2UpO1xuICB9XG5cbiAgZXZlbnQudHJhY2sgPSB0cmFjaztcbiAgdmlkZW9FbC5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbn1cbmZ1bmN0aW9uIGNsZWFyQ3VycmVudEN1ZXModHJhY2spIHtcbiAgaWYgKHRyYWNrID09PSBudWxsIHx8IHRyYWNrID09PSB2b2lkIDAgPyB2b2lkIDAgOiB0cmFjay5jdWVzKSB7XG4gICAgd2hpbGUgKHRyYWNrLmN1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgdHJhY2sucmVtb3ZlQ3VlKHRyYWNrLmN1ZXNbMF0pO1xuICAgIH1cbiAgfVxufVxuLyoqXG4gKiAgR2l2ZW4gYSBsaXN0IG9mIEN1ZXMsIGZpbmRzIHRoZSBjbG9zZXN0IGN1ZSBtYXRjaGluZyB0aGUgZ2l2ZW4gdGltZS5cbiAqICBNb2RpZmllZCB2ZXJpc29uIG9mIGJpbmFyeSBzZWFyY2ggTyhsb2cobikpLlxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7KFRleHRUcmFja0N1ZUxpc3QgfCBUZXh0VHJhY2tDdWVbXSl9IGN1ZXMgLSBMaXN0IG9mIGN1ZXMuXG4gKiBAcGFyYW0ge251bWJlcn0gdGltZSAtIFRhcmdldCB0aW1lLCB0byBmaW5kIGNsb3Nlc3QgY3VlIHRvLlxuICogQHJldHVybnMge1RleHRUcmFja0N1ZX1cbiAqL1xuXG5mdW5jdGlvbiBnZXRDbG9zZXN0Q3VlKGN1ZXMsIHRpbWUpIHtcbiAgLy8gSWYgdGhlIG9mZnNldCBpcyBsZXNzIHRoYW4gdGhlIGZpcnN0IGVsZW1lbnQsIHRoZSBmaXJzdCBlbGVtZW50IGlzIHRoZSBjbG9zZXN0LlxuICBpZiAodGltZSA8IGN1ZXNbMF0uZW5kVGltZSkge1xuICAgIHJldHVybiBjdWVzWzBdO1xuICB9IC8vIElmIHRoZSBvZmZzZXQgaXMgZ3JlYXRlciB0aGFuIHRoZSBsYXN0IGN1ZSwgdGhlIGxhc3QgaXMgdGhlIGNsb3Nlc3QuXG5cblxuICBpZiAodGltZSA+IGN1ZXNbY3Vlcy5sZW5ndGggLSAxXS5lbmRUaW1lKSB7XG4gICAgcmV0dXJuIGN1ZXNbY3Vlcy5sZW5ndGggLSAxXTtcbiAgfVxuXG4gIHZhciBsZWZ0ID0gMDtcbiAgdmFyIHJpZ2h0ID0gY3Vlcy5sZW5ndGggLSAxO1xuXG4gIHdoaWxlIChsZWZ0IDw9IHJpZ2h0KSB7XG4gICAgdmFyIG1pZCA9IE1hdGguZmxvb3IoKHJpZ2h0ICsgbGVmdCkgLyAyKTtcblxuICAgIGlmICh0aW1lIDwgY3Vlc1ttaWRdLmVuZFRpbWUpIHtcbiAgICAgIHJpZ2h0ID0gbWlkIC0gMTtcbiAgICB9IGVsc2UgaWYgKHRpbWUgPiBjdWVzW21pZF0uZW5kVGltZSkge1xuICAgICAgbGVmdCA9IG1pZCArIDE7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIElmIGl0J3Mgbm90IGxvd2VyIG9yIGhpZ2hlciwgaXQgbXVzdCBiZSBlcXVhbC5cbiAgICAgIHJldHVybiBjdWVzW21pZF07XG4gICAgfVxuICB9IC8vIEF0IHRoaXMgcG9pbnQsIGxlZnQgYW5kIHJpZ2h0IGhhdmUgc3dhcHBlZC5cbiAgLy8gTm8gZGlyZWN0IG1hdGNoIHdhcyBmb3VuZCwgbGVmdCBvciByaWdodCBlbGVtZW50IG11c3QgYmUgdGhlIGNsb3Nlc3QuIENoZWNrIHdoaWNoIG9uZSBoYXMgdGhlIHNtYWxsZXN0IGRpZmYuXG5cblxuICByZXR1cm4gY3Vlc1tsZWZ0XS5lbmRUaW1lIC0gdGltZSA8IHRpbWUgLSBjdWVzW3JpZ2h0XS5lbmRUaW1lID8gY3Vlc1tsZWZ0XSA6IGN1ZXNbcmlnaHRdO1xufVxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9pZDMtdHJhY2stY29udHJvbGxlci5qc1xuZnVuY3Rpb24gaWQzX3RyYWNrX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLypcbiAqIGlkMyBtZXRhZGF0YSB0cmFjayBjb250cm9sbGVyXG4qL1xuXG5cblxuXG52YXIgTUlOX0NVRV9EVVJBVElPTiA9IDAuMjU7XG5cbnZhciBpZDNfdHJhY2tfY29udHJvbGxlcl9JRDNUcmFja0NvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9FdmVudEhhbmRsZXIpIHtcbiAgaWQzX3RyYWNrX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShJRDNUcmFja0NvbnRyb2xsZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIElEM1RyYWNrQ29udHJvbGxlcihobHMpIHtcbiAgICB2YXIgX3RoaXM7XG5cbiAgICBfdGhpcyA9IF9FdmVudEhhbmRsZXIuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfQVRUQUNIRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfREVUQUNISU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfUEFSU0lOR19NRVRBREFUQSwgZXZlbnRzW1wiZGVmYXVsdFwiXS5MSVZFX0JBQ0tfQlVGRkVSX1JFQUNIRUQpIHx8IHRoaXM7XG4gICAgX3RoaXMuaWQzVHJhY2sgPSB1bmRlZmluZWQ7XG4gICAgX3RoaXMubWVkaWEgPSB1bmRlZmluZWQ7XG4gICAgcmV0dXJuIF90aGlzO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IElEM1RyYWNrQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIGV2ZW50X2hhbmRsZXIucHJvdG90eXBlLmRlc3Ryb3kuY2FsbCh0aGlzKTtcbiAgfSAvLyBBZGQgSUQzIG1ldGF0YWRhdGEgdGV4dCB0cmFjay5cbiAgO1xuXG4gIF9wcm90by5vbk1lZGlhQXR0YWNoZWQgPSBmdW5jdGlvbiBvbk1lZGlhQXR0YWNoZWQoZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuXG4gICAgaWYgKCF0aGlzLm1lZGlhKSB7fVxuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhRGV0YWNoaW5nID0gZnVuY3Rpb24gb25NZWRpYURldGFjaGluZygpIHtcbiAgICBjbGVhckN1cnJlbnRDdWVzKHRoaXMuaWQzVHJhY2spO1xuICAgIHRoaXMuaWQzVHJhY2sgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5tZWRpYSA9IHVuZGVmaW5lZDtcbiAgfTtcblxuICBfcHJvdG8uZ2V0SUQzVHJhY2sgPSBmdW5jdGlvbiBnZXRJRDNUcmFjayh0ZXh0VHJhY2tzKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0ZXh0VHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgdGV4dFRyYWNrID0gdGV4dFRyYWNrc1tpXTtcblxuICAgICAgaWYgKHRleHRUcmFjay5raW5kID09PSAnbWV0YWRhdGEnICYmIHRleHRUcmFjay5sYWJlbCA9PT0gJ2lkMycpIHtcbiAgICAgICAgLy8gc2VuZCAnYWRkdHJhY2snIHdoZW4gcmV1c2luZyB0aGUgdGV4dFRyYWNrIGZvciBtZXRhZGF0YSxcbiAgICAgICAgLy8gc2FtZSBhcyB3aGF0IHdlIGRvIGZvciBjYXB0aW9uc1xuICAgICAgICBzZW5kQWRkVHJhY2tFdmVudCh0ZXh0VHJhY2ssIHRoaXMubWVkaWEpO1xuICAgICAgICByZXR1cm4gdGV4dFRyYWNrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLm1lZGlhLmFkZFRleHRUcmFjaygnbWV0YWRhdGEnLCAnaWQzJyk7XG4gIH07XG5cbiAgX3Byb3RvLm9uRnJhZ1BhcnNpbmdNZXRhZGF0YSA9IGZ1bmN0aW9uIG9uRnJhZ1BhcnNpbmdNZXRhZGF0YShkYXRhKSB7XG4gICAgdmFyIGZyYWdtZW50ID0gZGF0YS5mcmFnO1xuICAgIHZhciBzYW1wbGVzID0gZGF0YS5zYW1wbGVzOyAvLyBjcmVhdGUgdHJhY2sgZHluYW1pY2FsbHlcblxuICAgIGlmICghdGhpcy5pZDNUcmFjaykge1xuICAgICAgdGhpcy5pZDNUcmFjayA9IHRoaXMuZ2V0SUQzVHJhY2sodGhpcy5tZWRpYS50ZXh0VHJhY2tzKTtcbiAgICAgIHRoaXMuaWQzVHJhY2subW9kZSA9ICdoaWRkZW4nO1xuICAgIH0gLy8gQXR0ZW1wdCB0byByZWNyZWF0ZSBTYWZhcmkgZnVuY3Rpb25hbGl0eSBieSBjcmVhdGluZ1xuICAgIC8vIFdlYktpdERhdGFDdWUgb2JqZWN0cyB3aGVuIGF2YWlsYWJsZSBhbmQgc3RvcmUgdGhlIGRlY29kZWRcbiAgICAvLyBJRDMgZGF0YSBpbiB0aGUgdmFsdWUgcHJvcGVydHkgb2YgdGhlIGN1ZVxuXG5cbiAgICB2YXIgQ3VlID0gd2luZG93LldlYktpdERhdGFDdWUgfHwgd2luZG93LlZUVEN1ZSB8fCB3aW5kb3cuVGV4dFRyYWNrQ3VlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzYW1wbGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgZnJhbWVzID0gaWQzW1wiZGVmYXVsdFwiXS5nZXRJRDNGcmFtZXMoc2FtcGxlc1tpXS5kYXRhKTtcblxuICAgICAgaWYgKGZyYW1lcykge1xuICAgICAgICAvLyBFbnN1cmUgdGhlIHB0cyBpcyBwb3NpdGl2ZSAtIHNvbWV0aW1lcyBpdCdzIHJlcG9ydGVkIGFzIGEgc21hbGwgbmVnYXRpdmUgbnVtYmVyXG4gICAgICAgIHZhciBzdGFydFRpbWUgPSBNYXRoLm1heChzYW1wbGVzW2ldLnB0cywgMCk7XG4gICAgICAgIHZhciBlbmRUaW1lID0gaSA8IHNhbXBsZXMubGVuZ3RoIC0gMSA/IHNhbXBsZXNbaSArIDFdLnB0cyA6IGZyYWdtZW50LmVuZFBUUztcblxuICAgICAgICBpZiAoIWVuZFRpbWUpIHtcbiAgICAgICAgICBlbmRUaW1lID0gZnJhZ21lbnQuc3RhcnQgKyBmcmFnbWVudC5kdXJhdGlvbjtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciB0aW1lRGlmZiA9IGVuZFRpbWUgLSBzdGFydFRpbWU7XG5cbiAgICAgICAgaWYgKHRpbWVEaWZmIDw9IDApIHtcbiAgICAgICAgICBlbmRUaW1lID0gc3RhcnRUaW1lICsgTUlOX0NVRV9EVVJBVElPTjtcbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgZnJhbWVzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgdmFyIGZyYW1lID0gZnJhbWVzW2pdOyAvLyBTYWZhcmkgZG9lc24ndCBwdXQgdGhlIHRpbWVzdGFtcCBmcmFtZSBpbiB0aGUgVGV4dFRyYWNrXG5cbiAgICAgICAgICBpZiAoIWlkM1tcImRlZmF1bHRcIl0uaXNUaW1lU3RhbXBGcmFtZShmcmFtZSkpIHtcbiAgICAgICAgICAgIHZhciBjdWUgPSBuZXcgQ3VlKHN0YXJ0VGltZSwgZW5kVGltZSwgJycpO1xuICAgICAgICAgICAgY3VlLnZhbHVlID0gZnJhbWU7XG4gICAgICAgICAgICB0aGlzLmlkM1RyYWNrLmFkZEN1ZShjdWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25MaXZlQmFja0J1ZmZlclJlYWNoZWQgPSBmdW5jdGlvbiBvbkxpdmVCYWNrQnVmZmVyUmVhY2hlZChfcmVmKSB7XG4gICAgdmFyIGJ1ZmZlckVuZCA9IF9yZWYuYnVmZmVyRW5kO1xuICAgIHZhciBpZDNUcmFjayA9IHRoaXMuaWQzVHJhY2s7XG5cbiAgICBpZiAoIWlkM1RyYWNrIHx8ICFpZDNUcmFjay5jdWVzIHx8ICFpZDNUcmFjay5jdWVzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBmb3VuZEN1ZSA9IGdldENsb3Nlc3RDdWUoaWQzVHJhY2suY3VlcywgYnVmZmVyRW5kKTtcblxuICAgIGlmICghZm91bmRDdWUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB3aGlsZSAoaWQzVHJhY2suY3Vlc1swXSAhPT0gZm91bmRDdWUpIHtcbiAgICAgIGlkM1RyYWNrLnJlbW92ZUN1ZShpZDNUcmFjay5jdWVzWzBdKTtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIElEM1RyYWNrQ29udHJvbGxlcjtcbn0oZXZlbnRfaGFuZGxlcik7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGlkM190cmFja19jb250cm9sbGVyID0gKGlkM190cmFja19jb250cm9sbGVyX0lEM1RyYWNrQ29udHJvbGxlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9pcy1zdXBwb3J0ZWQudHNcblxuZnVuY3Rpb24gaXNfc3VwcG9ydGVkX2lzU3VwcG9ydGVkKCkge1xuICB2YXIgbWVkaWFTb3VyY2UgPSBnZXRNZWRpYVNvdXJjZSgpO1xuXG4gIGlmICghbWVkaWFTb3VyY2UpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICB2YXIgc291cmNlQnVmZmVyID0gc2VsZi5Tb3VyY2VCdWZmZXIgfHwgc2VsZi5XZWJLaXRTb3VyY2VCdWZmZXI7XG4gIHZhciBpc1R5cGVTdXBwb3J0ZWQgPSBtZWRpYVNvdXJjZSAmJiB0eXBlb2YgbWVkaWFTb3VyY2UuaXNUeXBlU3VwcG9ydGVkID09PSAnZnVuY3Rpb24nICYmIG1lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCgndmlkZW8vbXA0OyBjb2RlY3M9XCJhdmMxLjQyRTAxRSxtcDRhLjQwLjJcIicpOyAvLyBpZiBTb3VyY2VCdWZmZXIgaXMgZXhwb3NlZCBlbnN1cmUgaXRzIEFQSSBpcyB2YWxpZFxuICAvLyBzYWZhcmkgYW5kIG9sZCB2ZXJzaW9uIG9mIENocm9tZSBkb2Ugbm90IGV4cG9zZSBTb3VyY2VCdWZmZXIgZ2xvYmFsbHkgc28gY2hlY2tpbmcgU291cmNlQnVmZmVyLnByb3RvdHlwZSBpcyBpbXBvc3NpYmxlXG5cbiAgdmFyIHNvdXJjZUJ1ZmZlclZhbGlkQVBJID0gIXNvdXJjZUJ1ZmZlciB8fCBzb3VyY2VCdWZmZXIucHJvdG90eXBlICYmIHR5cGVvZiBzb3VyY2VCdWZmZXIucHJvdG90eXBlLmFwcGVuZEJ1ZmZlciA9PT0gJ2Z1bmN0aW9uJyAmJiB0eXBlb2Ygc291cmNlQnVmZmVyLnByb3RvdHlwZS5yZW1vdmUgPT09ICdmdW5jdGlvbic7XG4gIHJldHVybiAhIWlzVHlwZVN1cHBvcnRlZCAmJiAhIXNvdXJjZUJ1ZmZlclZhbGlkQVBJO1xufVxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvZXdtYS50c1xuLypcbiAqIGNvbXB1dGUgYW4gRXhwb25lbnRpYWwgV2VpZ2h0ZWQgbW92aW5nIGF2ZXJhZ2VcbiAqIC0gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTW92aW5nX2F2ZXJhZ2UjRXhwb25lbnRpYWxfbW92aW5nX2F2ZXJhZ2VcbiAqICAtIGhlYXZpbHkgaW5zcGlyZWQgZnJvbSBzaGFrYS1wbGF5ZXJcbiAqL1xudmFyIEVXTUEgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICAvLyAgQWJvdXQgaGFsZiBvZiB0aGUgZXN0aW1hdGVkIHZhbHVlIHdpbGwgYmUgZnJvbSB0aGUgbGFzdCB8aGFsZkxpZmV8IHNhbXBsZXMgYnkgd2VpZ2h0LlxuICBmdW5jdGlvbiBFV01BKGhhbGZMaWZlKSB7XG4gICAgdGhpcy5hbHBoYV8gPSB2b2lkIDA7XG4gICAgdGhpcy5lc3RpbWF0ZV8gPSB2b2lkIDA7XG4gICAgdGhpcy50b3RhbFdlaWdodF8gPSB2b2lkIDA7XG4gICAgLy8gTGFyZ2VyIHZhbHVlcyBvZiBhbHBoYSBleHBpcmUgaGlzdG9yaWNhbCBkYXRhIG1vcmUgc2xvd2x5LlxuICAgIHRoaXMuYWxwaGFfID0gaGFsZkxpZmUgPyBNYXRoLmV4cChNYXRoLmxvZygwLjUpIC8gaGFsZkxpZmUpIDogMDtcbiAgICB0aGlzLmVzdGltYXRlXyA9IDA7XG4gICAgdGhpcy50b3RhbFdlaWdodF8gPSAwO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IEVXTUEucHJvdG90eXBlO1xuXG4gIF9wcm90by5zYW1wbGUgPSBmdW5jdGlvbiBzYW1wbGUod2VpZ2h0LCB2YWx1ZSkge1xuICAgIHZhciBhZGpBbHBoYSA9IE1hdGgucG93KHRoaXMuYWxwaGFfLCB3ZWlnaHQpO1xuICAgIHRoaXMuZXN0aW1hdGVfID0gdmFsdWUgKiAoMSAtIGFkakFscGhhKSArIGFkakFscGhhICogdGhpcy5lc3RpbWF0ZV87XG4gICAgdGhpcy50b3RhbFdlaWdodF8gKz0gd2VpZ2h0O1xuICB9O1xuXG4gIF9wcm90by5nZXRUb3RhbFdlaWdodCA9IGZ1bmN0aW9uIGdldFRvdGFsV2VpZ2h0KCkge1xuICAgIHJldHVybiB0aGlzLnRvdGFsV2VpZ2h0XztcbiAgfTtcblxuICBfcHJvdG8uZ2V0RXN0aW1hdGUgPSBmdW5jdGlvbiBnZXRFc3RpbWF0ZSgpIHtcbiAgICBpZiAodGhpcy5hbHBoYV8pIHtcbiAgICAgIHZhciB6ZXJvRmFjdG9yID0gMSAtIE1hdGgucG93KHRoaXMuYWxwaGFfLCB0aGlzLnRvdGFsV2VpZ2h0Xyk7XG4gICAgICByZXR1cm4gdGhpcy5lc3RpbWF0ZV8gLyB6ZXJvRmFjdG9yO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5lc3RpbWF0ZV87XG4gICAgfVxuICB9O1xuXG4gIHJldHVybiBFV01BO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBld21hID0gKEVXTUEpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvZXdtYS1iYW5kd2lkdGgtZXN0aW1hdG9yLnRzXG4vKlxuICogRVdNQSBCYW5kd2lkdGggRXN0aW1hdG9yXG4gKiAgLSBoZWF2aWx5IGluc3BpcmVkIGZyb20gc2hha2EtcGxheWVyXG4gKiBUcmFja3MgYmFuZHdpZHRoIHNhbXBsZXMgYW5kIGVzdGltYXRlcyBhdmFpbGFibGUgYmFuZHdpZHRoLlxuICogQmFzZWQgb24gdGhlIG1pbmltdW0gb2YgdHdvIGV4cG9uZW50aWFsbHktd2VpZ2h0ZWQgbW92aW5nIGF2ZXJhZ2VzIHdpdGhcbiAqIGRpZmZlcmVudCBoYWxmLWxpdmVzLlxuICovXG5cblxudmFyIGV3bWFfYmFuZHdpZHRoX2VzdGltYXRvcl9Fd21hQmFuZFdpZHRoRXN0aW1hdG9yID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgLy8gVE9ETyh0eXBlc2NyaXB0LWhscylcbiAgZnVuY3Rpb24gRXdtYUJhbmRXaWR0aEVzdGltYXRvcihobHMsIHNsb3csIGZhc3QsIGRlZmF1bHRFc3RpbWF0ZSkge1xuICAgIHRoaXMuaGxzID0gdm9pZCAwO1xuICAgIHRoaXMuZGVmYXVsdEVzdGltYXRlXyA9IHZvaWQgMDtcbiAgICB0aGlzLm1pbldlaWdodF8gPSB2b2lkIDA7XG4gICAgdGhpcy5taW5EZWxheU1zXyA9IHZvaWQgMDtcbiAgICB0aGlzLnNsb3dfID0gdm9pZCAwO1xuICAgIHRoaXMuZmFzdF8gPSB2b2lkIDA7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5kZWZhdWx0RXN0aW1hdGVfID0gZGVmYXVsdEVzdGltYXRlO1xuICAgIHRoaXMubWluV2VpZ2h0XyA9IDAuMDAxO1xuICAgIHRoaXMubWluRGVsYXlNc18gPSA1MDtcbiAgICB0aGlzLnNsb3dfID0gbmV3IGV3bWEoc2xvdyk7XG4gICAgdGhpcy5mYXN0XyA9IG5ldyBld21hKGZhc3QpO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IEV3bWFCYW5kV2lkdGhFc3RpbWF0b3IucHJvdG90eXBlO1xuXG4gIF9wcm90by5zYW1wbGUgPSBmdW5jdGlvbiBzYW1wbGUoZHVyYXRpb25NcywgbnVtQnl0ZXMpIHtcbiAgICBkdXJhdGlvbk1zID0gTWF0aC5tYXgoZHVyYXRpb25NcywgdGhpcy5taW5EZWxheU1zXyk7XG4gICAgdmFyIG51bUJpdHMgPSA4ICogbnVtQnl0ZXMsXG4gICAgICAgIC8vIHdlaWdodCBpcyBkdXJhdGlvbiBpbiBzZWNvbmRzXG4gICAgZHVyYXRpb25TID0gZHVyYXRpb25NcyAvIDEwMDAsXG4gICAgICAgIC8vIHZhbHVlIGlzIGJhbmR3aWR0aCBpbiBiaXRzL3NcbiAgICBiYW5kd2lkdGhJbkJwcyA9IG51bUJpdHMgLyBkdXJhdGlvblM7XG4gICAgdGhpcy5mYXN0Xy5zYW1wbGUoZHVyYXRpb25TLCBiYW5kd2lkdGhJbkJwcyk7XG4gICAgdGhpcy5zbG93Xy5zYW1wbGUoZHVyYXRpb25TLCBiYW5kd2lkdGhJbkJwcyk7XG4gIH07XG5cbiAgX3Byb3RvLmNhbkVzdGltYXRlID0gZnVuY3Rpb24gY2FuRXN0aW1hdGUoKSB7XG4gICAgdmFyIGZhc3QgPSB0aGlzLmZhc3RfO1xuICAgIHJldHVybiBmYXN0ICYmIGZhc3QuZ2V0VG90YWxXZWlnaHQoKSA+PSB0aGlzLm1pbldlaWdodF87XG4gIH07XG5cbiAgX3Byb3RvLmdldEVzdGltYXRlID0gZnVuY3Rpb24gZ2V0RXN0aW1hdGUoKSB7XG4gICAgaWYgKHRoaXMuY2FuRXN0aW1hdGUoKSkge1xuICAgICAgLy8gY29uc29sZS5sb2coJ3Nsb3cgZXN0aW1hdGU6JysgTWF0aC5yb3VuZCh0aGlzLnNsb3dfLmdldEVzdGltYXRlKCkpKTtcbiAgICAgIC8vIGNvbnNvbGUubG9nKCdmYXN0IGVzdGltYXRlOicrIE1hdGgucm91bmQodGhpcy5mYXN0Xy5nZXRFc3RpbWF0ZSgpKSk7XG4gICAgICAvLyBUYWtlIHRoZSBtaW5pbXVtIG9mIHRoZXNlIHR3byBlc3RpbWF0ZXMuICBUaGlzIHNob3VsZCBoYXZlIHRoZSBlZmZlY3Qgb2ZcbiAgICAgIC8vIGFkYXB0aW5nIGRvd24gcXVpY2tseSwgYnV0IHVwIG1vcmUgc2xvd2x5LlxuICAgICAgcmV0dXJuIE1hdGgubWluKHRoaXMuZmFzdF8uZ2V0RXN0aW1hdGUoKSwgdGhpcy5zbG93Xy5nZXRFc3RpbWF0ZSgpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRoaXMuZGVmYXVsdEVzdGltYXRlXztcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge307XG5cbiAgcmV0dXJuIEV3bWFCYW5kV2lkdGhFc3RpbWF0b3I7XG59KCk7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGV3bWFfYmFuZHdpZHRoX2VzdGltYXRvciA9IChld21hX2JhbmR3aWR0aF9lc3RpbWF0b3JfRXdtYUJhbmRXaWR0aEVzdGltYXRvcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9jb250cm9sbGVyL2Fici1jb250cm9sbGVyLmpzXG5cblxuXG5mdW5jdGlvbiBhYnJfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gYWJyX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBhYnJfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgYWJyX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gYWJyX2NvbnRyb2xsZXJfYXNzZXJ0VGhpc0luaXRpYWxpemVkKHNlbGYpIHsgaWYgKHNlbGYgPT09IHZvaWQgMCkgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIHNlbGY7IH1cblxuZnVuY3Rpb24gYWJyX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLypcbiAqIHNpbXBsZSBBQlIgQ29udHJvbGxlclxuICogIC0gY29tcHV0ZSBuZXh0IGxldmVsIGJhc2VkIG9uIGxhc3QgZnJhZ21lbnQgYncgaGV1cmlzdGljc1xuICogIC0gaW1wbGVtZW50IGFuIGFiYW5kb24gcnVsZXMgdHJpZ2dlcmVkIGlmIHdlIGhhdmUgbGVzcyB0aGFuIDIgZnJhZyBidWZmZXJlZCBhbmQgaWYgY29tcHV0ZWQgYncgc2hvd3MgdGhhdCB3ZSByaXNrIGJ1ZmZlciBzdGFsbGluZ1xuICovXG5cblxuXG5cblxuXG52YXIgYWJyX2NvbnRyb2xsZXJfd2luZG93ID0gd2luZG93LFxuICAgIGFicl9jb250cm9sbGVyX3BlcmZvcm1hbmNlID0gYWJyX2NvbnRyb2xsZXJfd2luZG93LnBlcmZvcm1hbmNlO1xuXG52YXIgYWJyX2NvbnRyb2xsZXJfQWJyQ29udHJvbGxlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBhYnJfY29udHJvbGxlcl9pbmhlcml0c0xvb3NlKEFickNvbnRyb2xsZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIEFickNvbnRyb2xsZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfTE9BRElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0xPQURFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0JVRkZFUkVELCBldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SKSB8fCB0aGlzO1xuICAgIF90aGlzLmxhc3RMb2FkZWRGcmFnTGV2ZWwgPSAwO1xuICAgIF90aGlzLl9uZXh0QXV0b0xldmVsID0gLTE7XG4gICAgX3RoaXMuaGxzID0gaGxzO1xuICAgIF90aGlzLnRpbWVyID0gbnVsbDtcbiAgICBfdGhpcy5fYndFc3RpbWF0b3IgPSBudWxsO1xuICAgIF90aGlzLm9uQ2hlY2sgPSBfdGhpcy5fYWJhbmRvblJ1bGVzQ2hlY2suYmluZChhYnJfY29udHJvbGxlcl9hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gQWJyQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgIGV2ZW50X2hhbmRsZXIucHJvdG90eXBlLmRlc3Ryb3kuY2FsbCh0aGlzKTtcbiAgfTtcblxuICBfcHJvdG8ub25GcmFnTG9hZGluZyA9IGZ1bmN0aW9uIG9uRnJhZ0xvYWRpbmcoZGF0YSkge1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnO1xuXG4gICAgaWYgKGZyYWcudHlwZSA9PT0gJ21haW4nKSB7XG4gICAgICBpZiAoIXRoaXMudGltZXIpIHtcbiAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IGZyYWc7XG4gICAgICAgIHRoaXMudGltZXIgPSBzZXRJbnRlcnZhbCh0aGlzLm9uQ2hlY2ssIDEwMCk7XG4gICAgICB9IC8vIGxhenkgaW5pdCBvZiBCd0VzdGltYXRvciwgcmF0aW9uYWxlIGlzIHRoYXQgd2UgdXNlIGRpZmZlcmVudCBwYXJhbXMgZm9yIExpdmUvVm9EXG4gICAgICAvLyBzbyB3ZSBuZWVkIHRvIHdhaXQgZm9yIHN0cmVhbSBtYW5pZmVzdCAvIHBsYXlsaXN0IHR5cGUgdG8gaW5zdGFudGlhdGUgaXQuXG5cblxuICAgICAgaWYgKCF0aGlzLl9id0VzdGltYXRvcikge1xuICAgICAgICB2YXIgaGxzID0gdGhpcy5obHM7XG4gICAgICAgIHZhciBjb25maWcgPSBobHMuY29uZmlnO1xuICAgICAgICB2YXIgbGV2ZWwgPSBmcmFnLmxldmVsO1xuICAgICAgICB2YXIgaXNMaXZlID0gaGxzLmxldmVsc1tsZXZlbF0uZGV0YWlscy5saXZlO1xuICAgICAgICB2YXIgZXdtYUZhc3Q7XG4gICAgICAgIHZhciBld21hU2xvdztcblxuICAgICAgICBpZiAoaXNMaXZlKSB7XG4gICAgICAgICAgZXdtYUZhc3QgPSBjb25maWcuYWJyRXdtYUZhc3RMaXZlO1xuICAgICAgICAgIGV3bWFTbG93ID0gY29uZmlnLmFickV3bWFTbG93TGl2ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBld21hRmFzdCA9IGNvbmZpZy5hYnJFd21hRmFzdFZvRDtcbiAgICAgICAgICBld21hU2xvdyA9IGNvbmZpZy5hYnJFd21hU2xvd1ZvRDtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX2J3RXN0aW1hdG9yID0gbmV3IGV3bWFfYmFuZHdpZHRoX2VzdGltYXRvcihobHMsIGV3bWFTbG93LCBld21hRmFzdCwgY29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGUpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uX2FiYW5kb25SdWxlc0NoZWNrID0gZnVuY3Rpb24gX2FiYW5kb25SdWxlc0NoZWNrKCkge1xuICAgIC8qXG4gICAgICBtb25pdG9yIGZyYWdtZW50IHJldHJpZXZhbCB0aW1lLi4uXG4gICAgICB3ZSBjb21wdXRlIGV4cGVjdGVkIHRpbWUgb2YgYXJyaXZhbCBvZiB0aGUgY29tcGxldGUgZnJhZ21lbnQuXG4gICAgICB3ZSBjb21wYXJlIGl0IHRvIGV4cGVjdGVkIHRpbWUgb2YgYnVmZmVyIHN0YXJ2YXRpb25cbiAgICAqL1xuICAgIHZhciBobHMgPSB0aGlzLmhscztcbiAgICB2YXIgdmlkZW8gPSBobHMubWVkaWE7XG4gICAgdmFyIGZyYWcgPSB0aGlzLmZyYWdDdXJyZW50O1xuXG4gICAgaWYgKCFmcmFnKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGxvYWRlciA9IGZyYWcubG9hZGVyOyAvLyBpZiBsb2FkZXIgaGFzIGJlZW4gZGVzdHJveWVkIG9yIGxvYWRpbmcgaGFzIGJlZW4gYWJvcnRlZCwgc3RvcCB0aW1lciBhbmQgcmV0dXJuXG5cbiAgICBpZiAoIWxvYWRlciB8fCBsb2FkZXIuc3RhdHMgJiYgbG9hZGVyLnN0YXRzLmFib3J0ZWQpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdmcmFnIGxvYWRlciBkZXN0cm95IG9yIGFib3J0ZWQsIGRpc2FybSBhYmFuZG9uUnVsZXMnKTtcbiAgICAgIHRoaXMuY2xlYXJUaW1lcigpOyAvLyByZXNldCBmb3JjZWQgYXV0byBsZXZlbCB2YWx1ZSBzbyB0aGF0IG5leHQgbGV2ZWwgd2lsbCBiZSBzZWxlY3RlZFxuXG4gICAgICB0aGlzLl9uZXh0QXV0b0xldmVsID0gLTE7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHN0YXRzID0gbG9hZGVyLnN0YXRzO1xuICAgIC8qIG9ubHkgbW9uaXRvciBmcmFnIHJldHJpZXZhbCB0aW1lIGlmXG4gICAgKHZpZGVvIG5vdCBwYXVzZWQgT1IgZmlyc3QgZnJhZ21lbnQgYmVpbmcgbG9hZGVkKHJlYWR5IHN0YXRlID09PSBIQVZFX05PVEhJTkcgPSAwKSkgQU5EIGF1dG9zd2l0Y2hpbmcgZW5hYmxlZCBBTkQgbm90IGxvd2VzdCBsZXZlbCAoPT4gbWVhbnMgdGhhdCB3ZSBoYXZlIHNldmVyYWwgbGV2ZWxzKSAqL1xuXG4gICAgaWYgKHZpZGVvICYmIHN0YXRzICYmICghdmlkZW8ucGF1c2VkICYmIHZpZGVvLnBsYXliYWNrUmF0ZSAhPT0gMCB8fCAhdmlkZW8ucmVhZHlTdGF0ZSkgJiYgZnJhZy5hdXRvTGV2ZWwgJiYgZnJhZy5sZXZlbCkge1xuICAgICAgdmFyIHJlcXVlc3REZWxheSA9IGFicl9jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpIC0gc3RhdHMudHJlcXVlc3Q7XG4gICAgICB2YXIgcGxheWJhY2tSYXRlID0gTWF0aC5hYnModmlkZW8ucGxheWJhY2tSYXRlKTsgLy8gbW9uaXRvciBmcmFnbWVudCBsb2FkIHByb2dyZXNzIGFmdGVyIGhhbGYgb2YgZXhwZWN0ZWQgZnJhZ21lbnQgZHVyYXRpb24sdG8gc3RhYmlsaXplIGJpdHJhdGVcblxuICAgICAgaWYgKHJlcXVlc3REZWxheSA+IDUwMCAqIGZyYWcuZHVyYXRpb24gLyBwbGF5YmFja1JhdGUpIHtcbiAgICAgICAgdmFyIGxldmVscyA9IGhscy5sZXZlbHM7XG4gICAgICAgIHZhciBsb2FkUmF0ZSA9IE1hdGgubWF4KDEsIHN0YXRzLmJ3ID8gc3RhdHMuYncgLyA4IDogc3RhdHMubG9hZGVkICogMTAwMCAvIHJlcXVlc3REZWxheSk7IC8vIGJ5dGUvczsgYXQgbGVhc3QgMSBieXRlL3MgdG8gYXZvaWQgZGl2aXNpb24gYnkgemVyb1xuICAgICAgICAvLyBjb21wdXRlIGV4cGVjdGVkIGZyYWdtZW50IGxlbmd0aCB1c2luZyBmcmFnIGR1cmF0aW9uIGFuZCBsZXZlbCBiaXRyYXRlLiBhbHNvIGVuc3VyZSB0aGF0IGV4cGVjdGVkIGxlbiBpcyBndGUgdGhhbiBhbHJlYWR5IGxvYWRlZCBzaXplXG5cbiAgICAgICAgdmFyIGxldmVsID0gbGV2ZWxzW2ZyYWcubGV2ZWxdO1xuXG4gICAgICAgIGlmICghbGV2ZWwpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgbGV2ZWxCaXRyYXRlID0gbGV2ZWwucmVhbEJpdHJhdGUgPyBNYXRoLm1heChsZXZlbC5yZWFsQml0cmF0ZSwgbGV2ZWwuYml0cmF0ZSkgOiBsZXZlbC5iaXRyYXRlO1xuICAgICAgICB2YXIgZXhwZWN0ZWRMZW4gPSBzdGF0cy50b3RhbCA/IHN0YXRzLnRvdGFsIDogTWF0aC5tYXgoc3RhdHMubG9hZGVkLCBNYXRoLnJvdW5kKGZyYWcuZHVyYXRpb24gKiBsZXZlbEJpdHJhdGUgLyA4KSk7XG4gICAgICAgIHZhciBwb3MgPSB2aWRlby5jdXJyZW50VGltZTtcbiAgICAgICAgdmFyIGZyYWdMb2FkZWREZWxheSA9IChleHBlY3RlZExlbiAtIHN0YXRzLmxvYWRlZCkgLyBsb2FkUmF0ZTtcbiAgICAgICAgdmFyIGJ1ZmZlclN0YXJ2YXRpb25EZWxheSA9IChCdWZmZXJIZWxwZXIuYnVmZmVySW5mbyh2aWRlbywgcG9zLCBobHMuY29uZmlnLm1heEJ1ZmZlckhvbGUpLmVuZCAtIHBvcykgLyBwbGF5YmFja1JhdGU7IC8vIGNvbnNpZGVyIGVtZXJnZW5jeSBzd2l0Y2ggZG93biBvbmx5IGlmIHdlIGhhdmUgbGVzcyB0aGFuIDIgZnJhZyBidWZmZXJlZCBBTkRcbiAgICAgICAgLy8gdGltZSB0byBmaW5pc2ggbG9hZGluZyBjdXJyZW50IGZyYWdtZW50IGlzIGJpZ2dlciB0aGFuIGJ1ZmZlciBzdGFydmF0aW9uIGRlbGF5XG4gICAgICAgIC8vIGllIGlmIHdlIHJpc2sgYnVmZmVyIHN0YXJ2YXRpb24gaWYgYncgZG9lcyBub3QgaW5jcmVhc2UgcXVpY2tseVxuXG4gICAgICAgIGlmIChidWZmZXJTdGFydmF0aW9uRGVsYXkgPCAyICogZnJhZy5kdXJhdGlvbiAvIHBsYXliYWNrUmF0ZSAmJiBmcmFnTG9hZGVkRGVsYXkgPiBidWZmZXJTdGFydmF0aW9uRGVsYXkpIHtcbiAgICAgICAgICB2YXIgbWluQXV0b0xldmVsID0gaGxzLm1pbkF1dG9MZXZlbDtcbiAgICAgICAgICB2YXIgZnJhZ0xldmVsTmV4dExvYWRlZERlbGF5O1xuICAgICAgICAgIHZhciBuZXh0TG9hZExldmVsOyAvLyBsZXRzIGl0ZXJhdGUgdGhyb3VnaCBsb3dlciBsZXZlbCBhbmQgdHJ5IHRvIGZpbmQgdGhlIGJpZ2dlc3Qgb25lIHRoYXQgY291bGQgYXZvaWQgcmVidWZmZXJpbmdcbiAgICAgICAgICAvLyB3ZSBzdGFydCBmcm9tIGN1cnJlbnQgbGV2ZWwgLSAxIGFuZCB3ZSBzdGVwIGRvd24gLCB1bnRpbCB3ZSBmaW5kIGEgbWF0Y2hpbmcgbGV2ZWxcblxuICAgICAgICAgIGZvciAobmV4dExvYWRMZXZlbCA9IGZyYWcubGV2ZWwgLSAxOyBuZXh0TG9hZExldmVsID4gbWluQXV0b0xldmVsOyBuZXh0TG9hZExldmVsLS0pIHtcbiAgICAgICAgICAgIC8vIGNvbXB1dGUgdGltZSB0byBsb2FkIG5leHQgZnJhZ21lbnQgYXQgbG93ZXIgbGV2ZWxcbiAgICAgICAgICAgIC8vIDAuOCA6IGNvbnNpZGVyIG9ubHkgODAlIG9mIGN1cnJlbnQgYncgdG8gYmUgY29uc2VydmF0aXZlXG4gICAgICAgICAgICAvLyA4ID0gYml0cyBwZXIgYnl0ZSAoYnBzL0JwcylcbiAgICAgICAgICAgIHZhciBsZXZlbE5leHRCaXRyYXRlID0gbGV2ZWxzW25leHRMb2FkTGV2ZWxdLnJlYWxCaXRyYXRlID8gTWF0aC5tYXgobGV2ZWxzW25leHRMb2FkTGV2ZWxdLnJlYWxCaXRyYXRlLCBsZXZlbHNbbmV4dExvYWRMZXZlbF0uYml0cmF0ZSkgOiBsZXZlbHNbbmV4dExvYWRMZXZlbF0uYml0cmF0ZTtcblxuICAgICAgICAgICAgdmFyIF9mcmFnTGV2ZWxOZXh0TG9hZGVkRGVsYXkgPSBmcmFnLmR1cmF0aW9uICogbGV2ZWxOZXh0Qml0cmF0ZSAvICg4ICogMC44ICogbG9hZFJhdGUpO1xuXG4gICAgICAgICAgICBpZiAoX2ZyYWdMZXZlbE5leHRMb2FkZWREZWxheSA8IGJ1ZmZlclN0YXJ2YXRpb25EZWxheSkge1xuICAgICAgICAgICAgICAvLyB3ZSBmb3VuZCBhIGxvd2VyIGxldmVsIHRoYXQgYmUgcmVidWZmZXJpbmcgZnJlZSB3aXRoIGN1cnJlbnQgZXN0aW1hdGVkIGJ3ICFcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSAvLyBvbmx5IGVtZXJnZW5jeSBzd2l0Y2ggZG93biBpZiBpdCB0YWtlcyBsZXNzIHRpbWUgdG8gbG9hZCBuZXcgZnJhZ21lbnQgYXQgbG93ZXN0IGxldmVsIGluc3RlYWRcbiAgICAgICAgICAvLyBvZiBmaW5pc2hpbmcgbG9hZGluZyBjdXJyZW50IG9uZSAuLi5cblxuXG4gICAgICAgICAgaWYgKGZyYWdMZXZlbE5leHRMb2FkZWREZWxheSA8IGZyYWdMb2FkZWREZWxheSkge1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJsb2FkaW5nIHRvbyBzbG93LCBhYm9ydCBmcmFnbWVudCBsb2FkaW5nIGFuZCBzd2l0Y2ggdG8gbGV2ZWwgXCIgKyBuZXh0TG9hZExldmVsICsgXCI6ZnJhZ0xvYWRlZERlbGF5W1wiICsgbmV4dExvYWRMZXZlbCArIFwiXTxmcmFnTG9hZGVkRGVsYXlbXCIgKyAoZnJhZy5sZXZlbCAtIDEpICsgXCJdO2J1ZmZlclN0YXJ2YXRpb25EZWxheTpcIiArIGZyYWdMZXZlbE5leHRMb2FkZWREZWxheS50b0ZpeGVkKDEpICsgXCI8XCIgKyBmcmFnTG9hZGVkRGVsYXkudG9GaXhlZCgxKSArIFwiOlwiICsgYnVmZmVyU3RhcnZhdGlvbkRlbGF5LnRvRml4ZWQoMSkpOyAvLyBmb3JjZSBuZXh0IGxvYWQgbGV2ZWwgaW4gYXV0byBtb2RlXG5cbiAgICAgICAgICAgIGhscy5uZXh0TG9hZExldmVsID0gbmV4dExvYWRMZXZlbDsgLy8gdXBkYXRlIGJ3IGVzdGltYXRlIGZvciB0aGlzIGZyYWdtZW50IGJlZm9yZSBjYW5jZWxsaW5nIGxvYWQgKHRoaXMgd2lsbCBoZWxwIHJlZHVjaW5nIHRoZSBidylcblxuICAgICAgICAgICAgdGhpcy5fYndFc3RpbWF0b3Iuc2FtcGxlKHJlcXVlc3REZWxheSwgc3RhdHMubG9hZGVkKTsgLy8gYWJvcnQgZnJhZ21lbnQgbG9hZGluZ1xuXG5cbiAgICAgICAgICAgIGxvYWRlci5hYm9ydCgpOyAvLyBzdG9wIGFiYW5kb24gcnVsZXMgdGltZXJcblxuICAgICAgICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkZSQUdfTE9BRF9FTUVSR0VOQ1lfQUJPUlRFRCwge1xuICAgICAgICAgICAgICBmcmFnOiBmcmFnLFxuICAgICAgICAgICAgICBzdGF0czogc3RhdHNcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25GcmFnTG9hZGVkID0gZnVuY3Rpb24gb25GcmFnTG9hZGVkKGRhdGEpIHtcbiAgICB2YXIgZnJhZyA9IGRhdGEuZnJhZztcblxuICAgIGlmIChmcmFnLnR5cGUgPT09ICdtYWluJyAmJiBPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGZyYWcuc24pKSB7XG4gICAgICAvLyBzdG9wIG1vbml0b3JpbmcgYncgb25jZSBmcmFnIGxvYWRlZFxuICAgICAgdGhpcy5jbGVhclRpbWVyKCk7IC8vIHN0b3JlIGxldmVsIGlkIGFmdGVyIHN1Y2Nlc3NmdWwgZnJhZ21lbnQgbG9hZFxuXG4gICAgICB0aGlzLmxhc3RMb2FkZWRGcmFnTGV2ZWwgPSBmcmFnLmxldmVsOyAvLyByZXNldCBmb3JjZWQgYXV0byBsZXZlbCB2YWx1ZSBzbyB0aGF0IG5leHQgbGV2ZWwgd2lsbCBiZSBzZWxlY3RlZFxuXG4gICAgICB0aGlzLl9uZXh0QXV0b0xldmVsID0gLTE7IC8vIGNvbXB1dGUgbGV2ZWwgYXZlcmFnZSBiaXRyYXRlXG5cbiAgICAgIGlmICh0aGlzLmhscy5jb25maWcuYWJyTWF4V2l0aFJlYWxCaXRyYXRlKSB7XG4gICAgICAgIHZhciBsZXZlbCA9IHRoaXMuaGxzLmxldmVsc1tmcmFnLmxldmVsXTtcbiAgICAgICAgdmFyIGxvYWRlZEJ5dGVzID0gKGxldmVsLmxvYWRlZCA/IGxldmVsLmxvYWRlZC5ieXRlcyA6IDApICsgZGF0YS5zdGF0cy5sb2FkZWQ7XG4gICAgICAgIHZhciBsb2FkZWREdXJhdGlvbiA9IChsZXZlbC5sb2FkZWQgPyBsZXZlbC5sb2FkZWQuZHVyYXRpb24gOiAwKSArIGRhdGEuZnJhZy5kdXJhdGlvbjtcbiAgICAgICAgbGV2ZWwubG9hZGVkID0ge1xuICAgICAgICAgIGJ5dGVzOiBsb2FkZWRCeXRlcyxcbiAgICAgICAgICBkdXJhdGlvbjogbG9hZGVkRHVyYXRpb25cbiAgICAgICAgfTtcbiAgICAgICAgbGV2ZWwucmVhbEJpdHJhdGUgPSBNYXRoLnJvdW5kKDggKiBsb2FkZWRCeXRlcyAvIGxvYWRlZER1cmF0aW9uKTtcbiAgICAgIH0gLy8gaWYgZnJhZ21lbnQgaGFzIGJlZW4gbG9hZGVkIHRvIHBlcmZvcm0gYSBiaXRyYXRlIHRlc3QsXG5cblxuICAgICAgaWYgKGRhdGEuZnJhZy5iaXRyYXRlVGVzdCkge1xuICAgICAgICB2YXIgc3RhdHMgPSBkYXRhLnN0YXRzO1xuICAgICAgICBzdGF0cy50cGFyc2VkID0gc3RhdHMudGJ1ZmZlcmVkID0gc3RhdHMudGxvYWQ7XG4gICAgICAgIHRoaXMub25GcmFnQnVmZmVyZWQoZGF0YSk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkZyYWdCdWZmZXJlZCA9IGZ1bmN0aW9uIG9uRnJhZ0J1ZmZlcmVkKGRhdGEpIHtcbiAgICB2YXIgc3RhdHMgPSBkYXRhLnN0YXRzO1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnOyAvLyBvbmx5IHVwZGF0ZSBzdGF0cyBvbiBmaXJzdCBmcmFnIGJ1ZmZlcmluZ1xuICAgIC8vIGlmIHNhbWUgZnJhZyBpcyBsb2FkZWQgbXVsdGlwbGUgdGltZXMsIGl0IG1pZ2h0IGJlIGluIGJyb3dzZXIgY2FjaGUsIGFuZCBsb2FkZWQgcXVpY2tseVxuICAgIC8vIGFuZCBsZWFkaW5nIHRvIHdyb25nIGJ3IGVzdGltYXRpb25cbiAgICAvLyBvbiBiaXRyYXRlIHRlc3QsIGFsc28gb25seSB1cGRhdGUgc3RhdHMgb25jZSAoaWYgdGxvYWQgPSB0YnVmZmVyZWQgPT0gb24gRlJBR19MT0FERUQpXG5cbiAgICBpZiAoc3RhdHMuYWJvcnRlZCAhPT0gdHJ1ZSAmJiBmcmFnLnR5cGUgPT09ICdtYWluJyAmJiBPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGZyYWcuc24pICYmICghZnJhZy5iaXRyYXRlVGVzdCB8fCBzdGF0cy50bG9hZCA9PT0gc3RhdHMudGJ1ZmZlcmVkKSkge1xuICAgICAgLy8gdXNlIHRwYXJzZWQtdHJlcXVlc3QgaW5zdGVhZCBvZiB0YnVmZmVyZWQtdHJlcXVlc3QgdG8gY29tcHV0ZSBmcmFnTG9hZGluZ1Byb2Nlc3Npbmc7IHJhdGlvbmFsZSBpcyB0aGF0ICBidWZmZXIgYXBwZW5kaW5nIG9ubHkgaGFwcGVucyBvbmNlIG1lZGlhIGlzIGF0dGFjaGVkXG4gICAgICAvLyBpbiBjYXNlIHdlIHVzZSBjb25maWcuc3RhcnRGcmFnUHJlZmV0Y2ggd2hpbGUgbWVkaWEgaXMgbm90IGF0dGFjaGVkIHlldCwgZnJhZ21lbnQgbWlnaHQgYmUgcGFyc2VkIHdoaWxlIG1lZGlhIG5vdCBhdHRhY2hlZCB5ZXQsIGJ1dCBpdCB3aWxsIG9ubHkgYmUgYnVmZmVyZWQgb24gbWVkaWEgYXR0YWNoZWRcbiAgICAgIC8vIGFzIGEgY29uc2VxdWVuY2UgaXQgY291bGQgaGFwcGVuIHJlYWxseSBsYXRlIGluIHRoZSBwcm9jZXNzLiBtZWFuaW5nIHRoYXQgYXBwZW5kaW5nIGR1cmF0aW9uIG1pZ2h0IGFwcGVhcnMgaHVnZSAuLi4gbGVhZGluZyB0byB1bmRlcmVzdGltYXRlZCB0aHJvdWdocHV0IGVzdGltYXRpb25cbiAgICAgIHZhciBmcmFnTG9hZGluZ1Byb2Nlc3NpbmdNcyA9IHN0YXRzLnRwYXJzZWQgLSBzdGF0cy50cmVxdWVzdDtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJsYXRlbmN5L2xvYWRpbmcvcGFyc2luZy9hcHBlbmQva2JwczpcIiArIE1hdGgucm91bmQoc3RhdHMudGZpcnN0IC0gc3RhdHMudHJlcXVlc3QpICsgXCIvXCIgKyBNYXRoLnJvdW5kKHN0YXRzLnRsb2FkIC0gc3RhdHMudGZpcnN0KSArIFwiL1wiICsgTWF0aC5yb3VuZChzdGF0cy50cGFyc2VkIC0gc3RhdHMudGxvYWQpICsgXCIvXCIgKyBNYXRoLnJvdW5kKHN0YXRzLnRidWZmZXJlZCAtIHN0YXRzLnRwYXJzZWQpICsgXCIvXCIgKyBNYXRoLnJvdW5kKDggKiBzdGF0cy5sb2FkZWQgLyAoc3RhdHMudGJ1ZmZlcmVkIC0gc3RhdHMudHJlcXVlc3QpKSk7XG5cbiAgICAgIHRoaXMuX2J3RXN0aW1hdG9yLnNhbXBsZShmcmFnTG9hZGluZ1Byb2Nlc3NpbmdNcywgc3RhdHMubG9hZGVkKTtcblxuICAgICAgc3RhdHMuYndFc3RpbWF0ZSA9IHRoaXMuX2J3RXN0aW1hdG9yLmdldEVzdGltYXRlKCk7IC8vIGlmIGZyYWdtZW50IGhhcyBiZWVuIGxvYWRlZCB0byBwZXJmb3JtIGEgYml0cmF0ZSB0ZXN0LCAoaGxzLnN0YXJ0TGV2ZWwgPSAtMSksIHN0b3JlIGJpdHJhdGUgdGVzdCBkZWxheSBkdXJhdGlvblxuXG4gICAgICBpZiAoZnJhZy5iaXRyYXRlVGVzdCkge1xuICAgICAgICB0aGlzLmJpdHJhdGVUZXN0RGVsYXkgPSBmcmFnTG9hZGluZ1Byb2Nlc3NpbmdNcyAvIDEwMDA7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmJpdHJhdGVUZXN0RGVsYXkgPSAwO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25FcnJvciA9IGZ1bmN0aW9uIG9uRXJyb3IoZGF0YSkge1xuICAgIC8vIHN0b3AgdGltZXIgaW4gY2FzZSBvZiBmcmFnIGxvYWRpbmcgZXJyb3JcbiAgICBzd2l0Y2ggKGRhdGEuZGV0YWlscykge1xuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX1RJTUVPVVQ6XG4gICAgICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgICAgICBicmVhaztcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5jbGVhclRpbWVyID0gZnVuY3Rpb24gY2xlYXJUaW1lcigpIHtcbiAgICBjbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgIHRoaXMudGltZXIgPSBudWxsO1xuICB9IC8vIHJldHVybiBuZXh0IGF1dG8gbGV2ZWxcbiAgO1xuXG4gIF9wcm90by5fZmluZEJlc3RMZXZlbCA9IGZ1bmN0aW9uIF9maW5kQmVzdExldmVsKGN1cnJlbnRMZXZlbCwgY3VycmVudEZyYWdEdXJhdGlvbiwgY3VycmVudEJ3LCBtaW5BdXRvTGV2ZWwsIG1heEF1dG9MZXZlbCwgbWF4RmV0Y2hEdXJhdGlvbiwgYndGYWN0b3IsIGJ3VXBGYWN0b3IsIGxldmVscykge1xuICAgIGZvciAodmFyIGkgPSBtYXhBdXRvTGV2ZWw7IGkgPj0gbWluQXV0b0xldmVsOyBpLS0pIHtcbiAgICAgIHZhciBsZXZlbEluZm8gPSBsZXZlbHNbaV07XG5cbiAgICAgIGlmICghbGV2ZWxJbmZvKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICB2YXIgbGV2ZWxEZXRhaWxzID0gbGV2ZWxJbmZvLmRldGFpbHM7XG4gICAgICB2YXIgYXZnRHVyYXRpb24gPSBsZXZlbERldGFpbHMgPyBsZXZlbERldGFpbHMudG90YWxkdXJhdGlvbiAvIGxldmVsRGV0YWlscy5mcmFnbWVudHMubGVuZ3RoIDogY3VycmVudEZyYWdEdXJhdGlvbjtcbiAgICAgIHZhciBsaXZlID0gbGV2ZWxEZXRhaWxzID8gbGV2ZWxEZXRhaWxzLmxpdmUgOiBmYWxzZTtcbiAgICAgIHZhciBhZGp1c3RlZGJ3ID0gdm9pZCAwOyAvLyBmb2xsb3cgYWxnb3JpdGhtIGNhcHR1cmVkIGZyb20gc3RhZ2VmcmlnaHQgOlxuICAgICAgLy8gaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZXNvdXJjZS5jb20vcGxhdGZvcm0vZnJhbWV3b3Jrcy9hdi8rL21hc3Rlci9tZWRpYS9saWJzdGFnZWZyaWdodC9odHRwbGl2ZS9MaXZlU2Vzc2lvbi5jcHBcbiAgICAgIC8vIFBpY2sgdGhlIGhpZ2hlc3QgYmFuZHdpZHRoIHN0cmVhbSBiZWxvdyBvciBlcXVhbCB0byBlc3RpbWF0ZWQgYmFuZHdpZHRoLlxuICAgICAgLy8gY29uc2lkZXIgb25seSA4MCUgb2YgdGhlIGF2YWlsYWJsZSBiYW5kd2lkdGgsIGJ1dCBpZiB3ZSBhcmUgc3dpdGNoaW5nIHVwLFxuICAgICAgLy8gYmUgZXZlbiBtb3JlIGNvbnNlcnZhdGl2ZSAoNzAlKSB0byBhdm9pZCBvdmVyZXN0aW1hdGluZyBhbmQgaW1tZWRpYXRlbHlcbiAgICAgIC8vIHN3aXRjaGluZyBiYWNrLlxuXG4gICAgICBpZiAoaSA8PSBjdXJyZW50TGV2ZWwpIHtcbiAgICAgICAgYWRqdXN0ZWRidyA9IGJ3RmFjdG9yICogY3VycmVudEJ3O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYWRqdXN0ZWRidyA9IGJ3VXBGYWN0b3IgKiBjdXJyZW50Qnc7XG4gICAgICB9XG5cbiAgICAgIHZhciBiaXRyYXRlID0gbGV2ZWxzW2ldLnJlYWxCaXRyYXRlID8gTWF0aC5tYXgobGV2ZWxzW2ldLnJlYWxCaXRyYXRlLCBsZXZlbHNbaV0uYml0cmF0ZSkgOiBsZXZlbHNbaV0uYml0cmF0ZTtcbiAgICAgIHZhciBmZXRjaER1cmF0aW9uID0gYml0cmF0ZSAqIGF2Z0R1cmF0aW9uIC8gYWRqdXN0ZWRidztcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS50cmFjZShcImxldmVsL2FkanVzdGVkYncvYml0cmF0ZS9hdmdEdXJhdGlvbi9tYXhGZXRjaER1cmF0aW9uL2ZldGNoRHVyYXRpb246IFwiICsgaSArIFwiL1wiICsgTWF0aC5yb3VuZChhZGp1c3RlZGJ3KSArIFwiL1wiICsgYml0cmF0ZSArIFwiL1wiICsgYXZnRHVyYXRpb24gKyBcIi9cIiArIG1heEZldGNoRHVyYXRpb24gKyBcIi9cIiArIGZldGNoRHVyYXRpb24pOyAvLyBpZiBhZGp1c3RlZCBidyBpcyBncmVhdGVyIHRoYW4gbGV2ZWwgYml0cmF0ZSBBTkRcblxuICAgICAgaWYgKGFkanVzdGVkYncgPiBiaXRyYXRlICYmICggLy8gZnJhZ21lbnQgZmV0Y2hEdXJhdGlvbiB1bmtub3duIE9SIGxpdmUgc3RyZWFtIE9SIGZyYWdtZW50IGZldGNoRHVyYXRpb24gbGVzcyB0aGFuIG1heCBhbGxvd2VkIGZldGNoIGR1cmF0aW9uLCB0aGVuIHRoaXMgbGV2ZWwgbWF0Y2hlc1xuICAgICAgLy8gd2UgZG9uJ3QgYWNjb3VudCBmb3IgbWF4IEZldGNoIER1cmF0aW9uIGZvciBsaXZlIHN0cmVhbXMsIHRoaXMgaXMgdG8gYXZvaWQgc3dpdGNoaW5nIGRvd24gd2hlbiBuZWFyIHRoZSBlZGdlIG9mIGxpdmUgc2xpZGluZyB3aW5kb3cgLi4uXG4gICAgICAvLyBzcGVjaWFsIGNhc2UgdG8gc3VwcG9ydCBzdGFydExldmVsID0gLTEgKGJpdHJhdGVUZXN0KSBvbiBsaXZlIHN0cmVhbXMgOiBpbiB0aGF0IGNhc2Ugd2Ugc2hvdWxkIG5vdCBleGl0IGxvb3Agc28gdGhhdCBfZmluZEJlc3RMZXZlbCB3aWxsIHJldHVybiAtMVxuICAgICAgIWZldGNoRHVyYXRpb24gfHwgbGl2ZSAmJiAhdGhpcy5iaXRyYXRlVGVzdERlbGF5IHx8IGZldGNoRHVyYXRpb24gPCBtYXhGZXRjaER1cmF0aW9uKSkge1xuICAgICAgICAvLyBhcyB3ZSBhcmUgbG9vcGluZyBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0LCB0aGlzIHdpbGwgcmV0dXJuIHRoZSBiZXN0IGFjaGlldmFibGUgcXVhbGl0eSBsZXZlbFxuICAgICAgICByZXR1cm4gaTtcbiAgICAgIH1cbiAgICB9IC8vIG5vdCBlbm91Z2ggdGltZSBidWRnZXQgZXZlbiB3aXRoIHF1YWxpdHkgbGV2ZWwgMCAuLi4gcmVidWZmZXJpbmcgbWlnaHQgaGFwcGVuXG5cblxuICAgIHJldHVybiAtMTtcbiAgfTtcblxuICBhYnJfY29udHJvbGxlcl9jcmVhdGVDbGFzcyhBYnJDb250cm9sbGVyLCBbe1xuICAgIGtleTogXCJuZXh0QXV0b0xldmVsXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICB2YXIgZm9yY2VkQXV0b0xldmVsID0gdGhpcy5fbmV4dEF1dG9MZXZlbDtcbiAgICAgIHZhciBid0VzdGltYXRvciA9IHRoaXMuX2J3RXN0aW1hdG9yOyAvLyBpbiBjYXNlIG5leHQgYXV0byBsZXZlbCBoYXMgYmVlbiBmb3JjZWQsIGFuZCBidyBub3QgYXZhaWxhYmxlIG9yIG5vdCByZWxpYWJsZSwgcmV0dXJuIGZvcmNlZCB2YWx1ZVxuXG4gICAgICBpZiAoZm9yY2VkQXV0b0xldmVsICE9PSAtMSAmJiAoIWJ3RXN0aW1hdG9yIHx8ICFid0VzdGltYXRvci5jYW5Fc3RpbWF0ZSgpKSkge1xuICAgICAgICByZXR1cm4gZm9yY2VkQXV0b0xldmVsO1xuICAgICAgfSAvLyBjb21wdXRlIG5leHQgbGV2ZWwgdXNpbmcgQUJSIGxvZ2ljXG5cblxuICAgICAgdmFyIG5leHRBQlJBdXRvTGV2ZWwgPSB0aGlzLl9uZXh0QUJSQXV0b0xldmVsOyAvLyBpZiBmb3JjZWQgYXV0byBsZXZlbCBoYXMgYmVlbiBkZWZpbmVkLCB1c2UgaXQgdG8gY2FwIEFCUiBjb21wdXRlZCBxdWFsaXR5IGxldmVsXG5cbiAgICAgIGlmIChmb3JjZWRBdXRvTGV2ZWwgIT09IC0xKSB7XG4gICAgICAgIG5leHRBQlJBdXRvTGV2ZWwgPSBNYXRoLm1pbihmb3JjZWRBdXRvTGV2ZWwsIG5leHRBQlJBdXRvTGV2ZWwpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbmV4dEFCUkF1dG9MZXZlbDtcbiAgICB9LFxuICAgIHNldDogZnVuY3Rpb24gc2V0KG5leHRMZXZlbCkge1xuICAgICAgdGhpcy5fbmV4dEF1dG9MZXZlbCA9IG5leHRMZXZlbDtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwiX25leHRBQlJBdXRvTGV2ZWxcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHZhciBobHMgPSB0aGlzLmhscztcbiAgICAgIHZhciBtYXhBdXRvTGV2ZWwgPSBobHMubWF4QXV0b0xldmVsLFxuICAgICAgICAgIGxldmVscyA9IGhscy5sZXZlbHMsXG4gICAgICAgICAgY29uZmlnID0gaGxzLmNvbmZpZyxcbiAgICAgICAgICBtaW5BdXRvTGV2ZWwgPSBobHMubWluQXV0b0xldmVsO1xuICAgICAgdmFyIHZpZGVvID0gaGxzLm1lZGlhO1xuICAgICAgdmFyIGN1cnJlbnRMZXZlbCA9IHRoaXMubGFzdExvYWRlZEZyYWdMZXZlbDtcbiAgICAgIHZhciBjdXJyZW50RnJhZ0R1cmF0aW9uID0gdGhpcy5mcmFnQ3VycmVudCA/IHRoaXMuZnJhZ0N1cnJlbnQuZHVyYXRpb24gOiAwO1xuICAgICAgdmFyIHBvcyA9IHZpZGVvID8gdmlkZW8uY3VycmVudFRpbWUgOiAwOyAvLyBwbGF5YmFja1JhdGUgaXMgdGhlIGFic29sdXRlIHZhbHVlIG9mIHRoZSBwbGF5YmFjayByYXRlOyBpZiB2aWRlby5wbGF5YmFja1JhdGUgaXMgMCwgd2UgdXNlIDEgdG8gbG9hZCBhc1xuICAgICAgLy8gaWYgd2UncmUgcGxheWluZyBiYWNrIGF0IHRoZSBub3JtYWwgcmF0ZS5cblxuICAgICAgdmFyIHBsYXliYWNrUmF0ZSA9IHZpZGVvICYmIHZpZGVvLnBsYXliYWNrUmF0ZSAhPT0gMCA/IE1hdGguYWJzKHZpZGVvLnBsYXliYWNrUmF0ZSkgOiAxLjA7XG4gICAgICB2YXIgYXZnYncgPSB0aGlzLl9id0VzdGltYXRvciA/IHRoaXMuX2J3RXN0aW1hdG9yLmdldEVzdGltYXRlKCkgOiBjb25maWcuYWJyRXdtYURlZmF1bHRFc3RpbWF0ZTsgLy8gYnVmZmVyU3RhcnZhdGlvbkRlbGF5IGlzIHRoZSB3YWxsLWNsb2NrIHRpbWUgbGVmdCB1bnRpbCB0aGUgcGxheWJhY2sgYnVmZmVyIGlzIGV4aGF1c3RlZC5cblxuICAgICAgdmFyIGJ1ZmZlclN0YXJ2YXRpb25EZWxheSA9IChCdWZmZXJIZWxwZXIuYnVmZmVySW5mbyh2aWRlbywgcG9zLCBjb25maWcubWF4QnVmZmVySG9sZSkuZW5kIC0gcG9zKSAvIHBsYXliYWNrUmF0ZTsgLy8gRmlyc3QsIGxvb2sgdG8gc2VlIGlmIHdlIGNhbiBmaW5kIGEgbGV2ZWwgbWF0Y2hpbmcgd2l0aCBvdXIgYXZnIGJhbmR3aWR0aCBBTkQgdGhhdCBjb3VsZCBhbHNvIGd1YXJhbnRlZSBubyByZWJ1ZmZlcmluZyBhdCBhbGxcblxuICAgICAgdmFyIGJlc3RMZXZlbCA9IHRoaXMuX2ZpbmRCZXN0TGV2ZWwoY3VycmVudExldmVsLCBjdXJyZW50RnJhZ0R1cmF0aW9uLCBhdmdidywgbWluQXV0b0xldmVsLCBtYXhBdXRvTGV2ZWwsIGJ1ZmZlclN0YXJ2YXRpb25EZWxheSwgY29uZmlnLmFickJhbmRXaWR0aEZhY3RvciwgY29uZmlnLmFickJhbmRXaWR0aFVwRmFjdG9yLCBsZXZlbHMpO1xuXG4gICAgICBpZiAoYmVzdExldmVsID49IDApIHtcbiAgICAgICAgcmV0dXJuIGJlc3RMZXZlbDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS50cmFjZSgncmVidWZmZXJpbmcgZXhwZWN0ZWQgdG8gaGFwcGVuLCBsZXRzIHRyeSB0byBmaW5kIGEgcXVhbGl0eSBsZXZlbCBtaW5pbWl6aW5nIHRoZSByZWJ1ZmZlcmluZycpOyAvLyBub3QgcG9zc2libGUgdG8gZ2V0IHJpZCBvZiByZWJ1ZmZlcmluZyAuLi4gbGV0J3MgdHJ5IHRvIGZpbmQgbGV2ZWwgdGhhdCB3aWxsIGd1YXJhbnRlZSBsZXNzIHRoYW4gbWF4U3RhcnZhdGlvbkRlbGF5IG9mIHJlYnVmZmVyaW5nXG4gICAgICAgIC8vIGlmIG5vIG1hdGNoaW5nIGxldmVsIGZvdW5kLCBsb2dpYyB3aWxsIHJldHVybiAwXG5cbiAgICAgICAgdmFyIG1heFN0YXJ2YXRpb25EZWxheSA9IGN1cnJlbnRGcmFnRHVyYXRpb24gPyBNYXRoLm1pbihjdXJyZW50RnJhZ0R1cmF0aW9uLCBjb25maWcubWF4U3RhcnZhdGlvbkRlbGF5KSA6IGNvbmZpZy5tYXhTdGFydmF0aW9uRGVsYXk7XG4gICAgICAgIHZhciBid0ZhY3RvciA9IGNvbmZpZy5hYnJCYW5kV2lkdGhGYWN0b3I7XG4gICAgICAgIHZhciBid1VwRmFjdG9yID0gY29uZmlnLmFickJhbmRXaWR0aFVwRmFjdG9yO1xuXG4gICAgICAgIGlmIChidWZmZXJTdGFydmF0aW9uRGVsYXkgPT09IDApIHtcbiAgICAgICAgICAvLyBpbiBjYXNlIGJ1ZmZlciBpcyBlbXB0eSwgbGV0J3MgY2hlY2sgaWYgcHJldmlvdXMgZnJhZ21lbnQgd2FzIGxvYWRlZCB0byBwZXJmb3JtIGEgYml0cmF0ZSB0ZXN0XG4gICAgICAgICAgdmFyIGJpdHJhdGVUZXN0RGVsYXkgPSB0aGlzLmJpdHJhdGVUZXN0RGVsYXk7XG5cbiAgICAgICAgICBpZiAoYml0cmF0ZVRlc3REZWxheSkge1xuICAgICAgICAgICAgLy8gaWYgaXQgaXMgdGhlIGNhc2UsIHRoZW4gd2UgbmVlZCB0byBhZGp1c3Qgb3VyIG1heCBzdGFydmF0aW9uIGRlbGF5IHVzaW5nIG1heExvYWRpbmdEZWxheSBjb25maWcgdmFsdWVcbiAgICAgICAgICAgIC8vIG1heCB2aWRlbyBsb2FkaW5nIGRlbGF5IHVzZWQgaW4gIGF1dG9tYXRpYyBzdGFydCBsZXZlbCBzZWxlY3Rpb24gOlxuICAgICAgICAgICAgLy8gaW4gdGhhdCBtb2RlIEFCUiBjb250cm9sbGVyIHdpbGwgZW5zdXJlIHRoYXQgdmlkZW8gbG9hZGluZyB0aW1lIChpZSB0aGUgdGltZSB0byBmZXRjaCB0aGUgZmlyc3QgZnJhZ21lbnQgYXQgbG93ZXN0IHF1YWxpdHkgbGV2ZWwgK1xuICAgICAgICAgICAgLy8gdGhlIHRpbWUgdG8gZmV0Y2ggdGhlIGZyYWdtZW50IGF0IHRoZSBhcHByb3ByaWF0ZSBxdWFsaXR5IGxldmVsIGlzIGxlc3MgdGhhbiBgYGBtYXhMb2FkaW5nRGVsYXlgYGAgKVxuICAgICAgICAgICAgLy8gY2FwIG1heExvYWRpbmdEZWxheSBhbmQgZW5zdXJlIGl0IGlzIG5vdCBiaWdnZXIgJ3RoYW4gYml0cmF0ZSB0ZXN0JyBmcmFnIGR1cmF0aW9uXG4gICAgICAgICAgICB2YXIgbWF4TG9hZGluZ0RlbGF5ID0gY3VycmVudEZyYWdEdXJhdGlvbiA/IE1hdGgubWluKGN1cnJlbnRGcmFnRHVyYXRpb24sIGNvbmZpZy5tYXhMb2FkaW5nRGVsYXkpIDogY29uZmlnLm1heExvYWRpbmdEZWxheTtcbiAgICAgICAgICAgIG1heFN0YXJ2YXRpb25EZWxheSA9IG1heExvYWRpbmdEZWxheSAtIGJpdHJhdGVUZXN0RGVsYXk7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0udHJhY2UoXCJiaXRyYXRlIHRlc3QgdG9vayBcIiArIE1hdGgucm91bmQoMTAwMCAqIGJpdHJhdGVUZXN0RGVsYXkpICsgXCJtcywgc2V0IGZpcnN0IGZyYWdtZW50IG1heCBmZXRjaER1cmF0aW9uIHRvIFwiICsgTWF0aC5yb3VuZCgxMDAwICogbWF4U3RhcnZhdGlvbkRlbGF5KSArIFwiIG1zXCIpOyAvLyBkb24ndCB1c2UgY29uc2VydmF0aXZlIGZhY3RvciBvbiBiaXRyYXRlIHRlc3RcblxuICAgICAgICAgICAgYndGYWN0b3IgPSBid1VwRmFjdG9yID0gMTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBiZXN0TGV2ZWwgPSB0aGlzLl9maW5kQmVzdExldmVsKGN1cnJlbnRMZXZlbCwgY3VycmVudEZyYWdEdXJhdGlvbiwgYXZnYncsIG1pbkF1dG9MZXZlbCwgbWF4QXV0b0xldmVsLCBidWZmZXJTdGFydmF0aW9uRGVsYXkgKyBtYXhTdGFydmF0aW9uRGVsYXksIGJ3RmFjdG9yLCBid1VwRmFjdG9yLCBsZXZlbHMpO1xuICAgICAgICByZXR1cm4gTWF0aC5tYXgoYmVzdExldmVsLCAwKTtcbiAgICAgIH1cbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gQWJyQ29udHJvbGxlcjtcbn0oZXZlbnRfaGFuZGxlcik7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGFicl9jb250cm9sbGVyID0gKGFicl9jb250cm9sbGVyX0FickNvbnRyb2xsZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9idWZmZXItY29udHJvbGxlci50c1xuXG5cbmZ1bmN0aW9uIGJ1ZmZlcl9jb250cm9sbGVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qXG4gKiBCdWZmZXIgQ29udHJvbGxlclxuICovXG5cblxuXG5cblxudmFyIGJ1ZmZlcl9jb250cm9sbGVyX01lZGlhU291cmNlID0gZ2V0TWVkaWFTb3VyY2UoKTtcblxudmFyIGJ1ZmZlcl9jb250cm9sbGVyX0J1ZmZlckNvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9FdmVudEhhbmRsZXIpIHtcbiAgYnVmZmVyX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShCdWZmZXJDb250cm9sbGVyLCBfRXZlbnRIYW5kbGVyKTtcblxuICAvLyB0aGUgdmFsdWUgdGhhdCB3ZSBoYXZlIHNldCBtZWRpYXNvdXJjZS5kdXJhdGlvbiB0b1xuICAvLyAodGhlIGFjdHVhbCBkdXJhdGlvbiBtYXkgYmUgdHdlYWtlZCBzbGlnaGx5IGJ5IHRoZSBicm93c2VyKVxuICAvLyB0aGUgdmFsdWUgdGhhdCB3ZSB3YW50IHRvIHNldCBtZWRpYVNvdXJjZS5kdXJhdGlvbiB0b1xuICAvLyB0aGUgdGFyZ2V0IGR1cmF0aW9uIG9mIHRoZSBjdXJyZW50IG1lZGlhIHBsYXlsaXN0XG4gIC8vIGN1cnJlbnQgc3RyZWFtIHN0YXRlOiB0cnVlIC0gZm9yIGxpdmUgYnJvYWRjYXN0LCBmYWxzZSAtIGZvciBWb0QgY29udGVudFxuICAvLyBjYWNoZSB0aGUgc2VsZiBnZW5lcmF0ZWQgb2JqZWN0IHVybCB0byBkZXRlY3QgaGlqYWNrIG9mIHZpZGVvIHRhZ1xuICAvLyBzaWduYWxzIHRoYXQgdGhlIHNvdXJjZUJ1ZmZlcnMgbmVlZCB0byBiZSBmbHVzaGVkXG4gIC8vIHNpZ25hbHMgdGhhdCBtZWRpYVNvdXJjZSBzaG91bGQgaGF2ZSBlbmRPZlN0cmVhbSBjYWxsZWRcbiAgLy8gdGhpcyBpcyBvcHRpb25hbCBiZWNhdXNlIHRoaXMgcHJvcGVydHkgaXMgcmVtb3ZlZCBmcm9tIHRoZSBjbGFzcyBzb21ldGltZXNcbiAgLy8gVGhlIG51bWJlciBvZiBCVUZGRVJfQ09ERUMgZXZlbnRzIHJlY2VpdmVkIGJlZm9yZSBhbnkgc291cmNlQnVmZmVycyBhcmUgY3JlYXRlZFxuICAvLyBUaGUgdG90YWwgbnVtYmVyIG9mIEJVRkZFUl9DT0RFQyBldmVudHMgcmVjZWl2ZWRcbiAgLy8gQSByZWZlcmVuY2UgdG8gdGhlIGF0dGFjaGVkIG1lZGlhIGVsZW1lbnRcbiAgLy8gQSByZWZlcmVuY2UgdG8gdGhlIGFjdGl2ZSBtZWRpYSBzb3VyY2VcbiAgLy8gTGlzdCBvZiBwZW5kaW5nIHNlZ21lbnRzIHRvIGJlIGFwcGVuZGVkIHRvIHNvdXJjZSBidWZmZXJcbiAgLy8gQSBndWFyZCB0byBzZWUgaWYgd2UgYXJlIGN1cnJlbnRseSBhcHBlbmRpbmcgdG8gdGhlIHNvdXJjZSBidWZmZXJcbiAgLy8gY291bnRlcnNcbiAgZnVuY3Rpb24gQnVmZmVyQ29udHJvbGxlcihobHMpIHtcbiAgICB2YXIgX3RoaXM7XG5cbiAgICBfdGhpcyA9IF9FdmVudEhhbmRsZXIuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfQVRUQUNISU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9QQVJTRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX1JFU0VULCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRJTkcsIGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0NPREVDUywgZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfRU9TLCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9GTFVTSElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9QVFNfVVBEQVRFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9VUERBVEVEKSB8fCB0aGlzO1xuICAgIF90aGlzLl9tc0R1cmF0aW9uID0gbnVsbDtcbiAgICBfdGhpcy5fbGV2ZWxEdXJhdGlvbiA9IG51bGw7XG4gICAgX3RoaXMuX2xldmVsVGFyZ2V0RHVyYXRpb24gPSAxMDtcbiAgICBfdGhpcy5fbGl2ZSA9IG51bGw7XG4gICAgX3RoaXMuX29iamVjdFVybCA9IG51bGw7XG4gICAgX3RoaXMuX25lZWRzRmx1c2ggPSBmYWxzZTtcbiAgICBfdGhpcy5fbmVlZHNFb3MgPSBmYWxzZTtcbiAgICBfdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgX3RoaXMuYXVkaW9UaW1lc3RhbXBPZmZzZXQgPSB2b2lkIDA7XG4gICAgX3RoaXMuYnVmZmVyQ29kZWNFdmVudHNFeHBlY3RlZCA9IDA7XG4gICAgX3RoaXMuX2J1ZmZlckNvZGVjRXZlbnRzVG90YWwgPSAwO1xuICAgIF90aGlzLm1lZGlhID0gbnVsbDtcbiAgICBfdGhpcy5tZWRpYVNvdXJjZSA9IG51bGw7XG4gICAgX3RoaXMuc2VnbWVudHMgPSBbXTtcbiAgICBfdGhpcy5wYXJlbnQgPSB2b2lkIDA7XG4gICAgX3RoaXMuYXBwZW5kaW5nID0gZmFsc2U7XG4gICAgX3RoaXMuYXBwZW5kZWQgPSAwO1xuICAgIF90aGlzLmFwcGVuZEVycm9yID0gMDtcbiAgICBfdGhpcy5mbHVzaEJ1ZmZlckNvdW50ZXIgPSAwO1xuICAgIF90aGlzLnRyYWNrcyA9IHt9O1xuICAgIF90aGlzLnBlbmRpbmdUcmFja3MgPSB7fTtcbiAgICBfdGhpcy5zb3VyY2VCdWZmZXIgPSB7fTtcbiAgICBfdGhpcy5mbHVzaFJhbmdlID0gW107XG5cbiAgICBfdGhpcy5fb25NZWRpYVNvdXJjZU9wZW4gPSBmdW5jdGlvbiAoKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdtZWRpYSBzb3VyY2Ugb3BlbmVkJyk7XG5cbiAgICAgIF90aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfQVRUQUNIRUQsIHtcbiAgICAgICAgbWVkaWE6IF90aGlzLm1lZGlhXG4gICAgICB9KTtcblxuICAgICAgdmFyIG1lZGlhU291cmNlID0gX3RoaXMubWVkaWFTb3VyY2U7XG5cbiAgICAgIGlmIChtZWRpYVNvdXJjZSkge1xuICAgICAgICAvLyBvbmNlIHJlY2VpdmVkLCBkb24ndCBsaXN0ZW4gYW55bW9yZSB0byBzb3VyY2VvcGVuIGV2ZW50XG4gICAgICAgIG1lZGlhU291cmNlLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NvdXJjZW9wZW4nLCBfdGhpcy5fb25NZWRpYVNvdXJjZU9wZW4pO1xuICAgICAgfVxuXG4gICAgICBfdGhpcy5jaGVja1BlbmRpbmdUcmFja3MoKTtcbiAgICB9O1xuXG4gICAgX3RoaXMuX29uTWVkaWFTb3VyY2VDbG9zZSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ21lZGlhIHNvdXJjZSBjbG9zZWQnKTtcbiAgICB9O1xuXG4gICAgX3RoaXMuX29uTWVkaWFTb3VyY2VFbmRlZCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ21lZGlhIHNvdXJjZSBlbmRlZCcpO1xuICAgIH07XG5cbiAgICBfdGhpcy5fb25TQlVwZGF0ZUVuZCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgIC8vIHVwZGF0ZSB0aW1lc3RhbXBPZmZzZXRcbiAgICAgIGlmIChfdGhpcy5hdWRpb1RpbWVzdGFtcE9mZnNldCAmJiBfdGhpcy5zb3VyY2VCdWZmZXIuYXVkaW8pIHtcbiAgICAgICAgdmFyIGF1ZGlvQnVmZmVyID0gX3RoaXMuc291cmNlQnVmZmVyLmF1ZGlvO1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcImNoYW5nZSBtcGVnIGF1ZGlvIHRpbWVzdGFtcCBvZmZzZXQgZnJvbSBcIiArIGF1ZGlvQnVmZmVyLnRpbWVzdGFtcE9mZnNldCArIFwiIHRvIFwiICsgX3RoaXMuYXVkaW9UaW1lc3RhbXBPZmZzZXQpO1xuICAgICAgICBhdWRpb0J1ZmZlci50aW1lc3RhbXBPZmZzZXQgPSBfdGhpcy5hdWRpb1RpbWVzdGFtcE9mZnNldDtcbiAgICAgICAgZGVsZXRlIF90aGlzLmF1ZGlvVGltZXN0YW1wT2Zmc2V0O1xuICAgICAgfVxuXG4gICAgICBpZiAoX3RoaXMuX25lZWRzRmx1c2gpIHtcbiAgICAgICAgX3RoaXMuZG9GbHVzaCgpO1xuICAgICAgfVxuXG4gICAgICBpZiAoX3RoaXMuX25lZWRzRW9zKSB7XG4gICAgICAgIF90aGlzLmNoZWNrRW9zKCk7XG4gICAgICB9XG5cbiAgICAgIF90aGlzLmFwcGVuZGluZyA9IGZhbHNlO1xuICAgICAgdmFyIHBhcmVudCA9IF90aGlzLnBhcmVudDsgLy8gY291bnQgbmIgb2YgcGVuZGluZyBzZWdtZW50cyB3YWl0aW5nIGZvciBhcHBlbmRpbmcgb24gdGhpcyBzb3VyY2VidWZmZXJcblxuICAgICAgdmFyIHBlbmRpbmcgPSBfdGhpcy5zZWdtZW50cy5yZWR1Y2UoZnVuY3Rpb24gKGNvdW50ZXIsIHNlZ21lbnQpIHtcbiAgICAgICAgcmV0dXJuIHNlZ21lbnQucGFyZW50ID09PSBwYXJlbnQgPyBjb3VudGVyICsgMSA6IGNvdW50ZXI7XG4gICAgICB9LCAwKTsgLy8gdGhpcy5zb3VyY2VCdWZmZXIgaXMgYmV0dGVyIHRvIHVzZSB0aGFuIG1lZGlhLmJ1ZmZlcmVkIGFzIGl0IGlzIGNsb3NlciB0byB0aGUgUFRTIGRhdGEgZnJvbSB0aGUgZnJhZ21lbnRzXG5cblxuICAgICAgdmFyIHRpbWVSYW5nZXMgPSB7fTtcbiAgICAgIHZhciBzYlNldCA9IF90aGlzLnNvdXJjZUJ1ZmZlcjtcblxuICAgICAgZm9yICh2YXIgc3RyZWFtVHlwZSBpbiBzYlNldCkge1xuICAgICAgICB2YXIgc2IgPSBzYlNldFtzdHJlYW1UeXBlXTtcblxuICAgICAgICBpZiAoIXNiKSB7XG4gICAgICAgICAgdGhyb3cgRXJyb3IoXCJoYW5kbGluZyBzb3VyY2UgYnVmZmVyIHVwZGF0ZSBlbmQgZXJyb3I6IHNvdXJjZSBidWZmZXIgZm9yIFwiICsgc3RyZWFtVHlwZSArIFwiIHVuaW5pdGlsaXplZCBhbmQgdW5hYmxlIHRvIHVwZGF0ZSBidWZmZXJlZCBUaW1lUmFuZ2VzLlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRpbWVSYW5nZXNbc3RyZWFtVHlwZV0gPSBzYi5idWZmZXJlZDtcbiAgICAgIH1cblxuICAgICAgX3RoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfQVBQRU5ERUQsIHtcbiAgICAgICAgcGFyZW50OiBwYXJlbnQsXG4gICAgICAgIHBlbmRpbmc6IHBlbmRpbmcsXG4gICAgICAgIHRpbWVSYW5nZXM6IHRpbWVSYW5nZXNcbiAgICAgIH0pOyAvLyBkb24ndCBhcHBlbmQgaW4gZmx1c2hpbmcgbW9kZVxuXG5cbiAgICAgIGlmICghX3RoaXMuX25lZWRzRmx1c2gpIHtcbiAgICAgICAgX3RoaXMuZG9BcHBlbmRpbmcoKTtcbiAgICAgIH1cblxuICAgICAgX3RoaXMudXBkYXRlTWVkaWFFbGVtZW50RHVyYXRpb24oKTsgLy8gYXBwZW5kaW5nIGdvZXMgZmlyc3RcblxuXG4gICAgICBpZiAocGVuZGluZyA9PT0gMCkge1xuICAgICAgICBfdGhpcy5mbHVzaExpdmVCYWNrQnVmZmVyKCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIF90aGlzLl9vblNCVXBkYXRlRXJyb3IgPSBmdW5jdGlvbiAoZXZlbnQpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignc291cmNlQnVmZmVyIGVycm9yOicsIGV2ZW50KTsgLy8gYWNjb3JkaW5nIHRvIGh0dHA6Ly93d3cudzMub3JnL1RSL21lZGlhLXNvdXJjZS8jc291cmNlYnVmZmVyLWFwcGVuZC1lcnJvclxuICAgICAgLy8gdGhpcyBlcnJvciBtaWdodCBub3QgYWx3YXlzIGJlIGZhdGFsIChpdCBpcyBmYXRhbCBpZiBkZWNvZGUgZXJyb3IgaXMgc2V0LCBpbiB0aGF0IGNhc2VcbiAgICAgIC8vIGl0IHdpbGwgYmUgZm9sbG93ZWQgYnkgYSBtZWRpYUVsZW1lbnQgZXJyb3IgLi4uKVxuXG4gICAgICBfdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uTUVESUFfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5CVUZGRVJfQVBQRU5ESU5HX0VSUk9SLFxuICAgICAgICBmYXRhbDogZmFsc2VcbiAgICAgIH0pOyAvLyB3ZSBkb24ndCBuZWVkIHRvIGRvIG1vcmUgdGhhbiB0aGF0LCBhcyBhY2NvcmRpbiB0byB0aGUgc3BlYywgdXBkYXRlZW5kIHdpbGwgYmUgZmlyZWQganVzdCBhZnRlclxuXG4gICAgfTtcblxuICAgIF90aGlzLmNvbmZpZyA9IGhscy5jb25maWc7XG4gICAgcmV0dXJuIF90aGlzO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IEJ1ZmZlckNvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHtcbiAgICBldmVudF9oYW5kbGVyLnByb3RvdHlwZS5kZXN0cm95LmNhbGwodGhpcyk7XG4gIH07XG5cbiAgX3Byb3RvLm9uTGV2ZWxQdHNVcGRhdGVkID0gZnVuY3Rpb24gb25MZXZlbFB0c1VwZGF0ZWQoZGF0YSkge1xuICAgIHZhciB0eXBlID0gZGF0YS50eXBlO1xuICAgIHZhciBhdWRpb1RyYWNrID0gdGhpcy50cmFja3MuYXVkaW87IC8vIEFkanVzdGluZyBgU291cmNlQnVmZmVyLnRpbWVzdGFtcE9mZnNldGAgKGRlc2lyZWQgcG9pbnQgaW4gdGhlIHRpbWVsaW5lIHdoZXJlIHRoZSBuZXh0IGZyYW1lcyBzaG91bGQgYmUgYXBwZW5kZWQpXG4gICAgLy8gaW4gQ2hyb21lIGJyb3dzZXIgd2hlbiB3ZSBkZXRlY3QgTVBFRyBhdWRpbyBjb250YWluZXIgYW5kIHRpbWUgZGVsdGEgYmV0d2VlbiBsZXZlbCBQVFMgYW5kIGBTb3VyY2VCdWZmZXIudGltZXN0YW1wT2Zmc2V0YFxuICAgIC8vIGlzIGdyZWF0ZXIgdGhhbiAxMDBtcyAodGhpcyBpcyBlbm91Z2ggdG8gaGFuZGxlIHNlZWsgZm9yIFZPRCBvciBsZXZlbCBjaGFuZ2UgZm9yIExJVkUgdmlkZW9zKS4gQXQgdGhlIHRpbWUgb2YgY2hhbmdlIHdlIGlzc3VlXG4gICAgLy8gYFNvdXJjZUJ1ZmZlci5hYm9ydCgpYCBhbmQgYWRqdXN0aW5nIGBTb3VyY2VCdWZmZXIudGltZXN0YW1wT2Zmc2V0YCBpZiBgU291cmNlQnVmZmVyLnVwZGF0aW5nYCBpcyBmYWxzZSBvciBhd2FpdGluZyBgdXBkYXRlZW5kYFxuICAgIC8vIGV2ZW50IGlmIFNCIGlzIGluIHVwZGF0aW5nIHN0YXRlLlxuICAgIC8vIE1vcmUgaW5mbyBoZXJlOiBodHRwczovL2dpdGh1Yi5jb20vdmlkZW8tZGV2L2hscy5qcy9pc3N1ZXMvMzMyI2lzc3VlY29tbWVudC0yNTc5ODY0ODZcblxuICAgIGlmICh0eXBlID09PSAnYXVkaW8nICYmIGF1ZGlvVHJhY2sgJiYgYXVkaW9UcmFjay5jb250YWluZXIgPT09ICdhdWRpby9tcGVnJykge1xuICAgICAgLy8gQ2hyb21lIGF1ZGlvIG1wMyB0cmFja1xuICAgICAgdmFyIGF1ZGlvQnVmZmVyID0gdGhpcy5zb3VyY2VCdWZmZXIuYXVkaW87XG5cbiAgICAgIGlmICghYXVkaW9CdWZmZXIpIHtcbiAgICAgICAgdGhyb3cgRXJyb3IoJ0xldmVsIFBUUyBVcGRhdGVkIGFuZCBzb3VyY2UgYnVmZmVyIGZvciBhdWRpbyB1bmluaXRhbGl6ZWQnKTtcbiAgICAgIH1cblxuICAgICAgdmFyIGRlbHRhID0gTWF0aC5hYnMoYXVkaW9CdWZmZXIudGltZXN0YW1wT2Zmc2V0IC0gZGF0YS5zdGFydCk7IC8vIGFkanVzdCB0aW1lc3RhbXAgb2Zmc2V0IGlmIHRpbWUgZGVsdGEgaXMgZ3JlYXRlciB0aGFuIDEwMG1zXG5cbiAgICAgIGlmIChkZWx0YSA+IDAuMSkge1xuICAgICAgICB2YXIgdXBkYXRpbmcgPSBhdWRpb0J1ZmZlci51cGRhdGluZztcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGF1ZGlvQnVmZmVyLmFib3J0KCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdjYW4gbm90IGFib3J0IGF1ZGlvIGJ1ZmZlcjogJyArIGVycik7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXVwZGF0aW5nKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ2NoYW5nZSBtcGVnIGF1ZGlvIHRpbWVzdGFtcCBvZmZzZXQgZnJvbSAnICsgYXVkaW9CdWZmZXIudGltZXN0YW1wT2Zmc2V0ICsgJyB0byAnICsgZGF0YS5zdGFydCk7XG4gICAgICAgICAgYXVkaW9CdWZmZXIudGltZXN0YW1wT2Zmc2V0ID0gZGF0YS5zdGFydDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmF1ZGlvVGltZXN0YW1wT2Zmc2V0ID0gZGF0YS5zdGFydDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25NYW5pZmVzdFBhcnNlZCA9IGZ1bmN0aW9uIG9uTWFuaWZlc3RQYXJzZWQoZGF0YSkge1xuICAgIC8vIGluIGNhc2Ugb2YgYWx0IGF1ZGlvICh3aGVyZSBhbGwgdHJhY2tzIGhhdmUgdXJscykgMiBCVUZGRVJfQ09ERUNTIGV2ZW50cyB3aWxsIGJlIHRyaWdnZXJlZCwgb25lIHBlciBzdHJlYW0gY29udHJvbGxlclxuICAgIC8vIHNvdXJjZWJ1ZmZlcnMgd2lsbCBiZSBjcmVhdGVkIGFsbCBhdCBvbmNlIHdoZW4gdGhlIGV4cGVjdGVkIG5iIG9mIHRyYWNrcyB3aWxsIGJlIHJlYWNoZWRcbiAgICAvLyBpbiBjYXNlIGFsdCBhdWRpbyBpcyBub3QgdXNlZCwgb25seSBvbmUgQlVGRkVSX0NPREVDIGV2ZW50IHdpbGwgYmUgZmlyZWQgZnJvbSBtYWluIHN0cmVhbSBjb250cm9sbGVyXG4gICAgLy8gaXQgd2lsbCBjb250YWluIHRoZSBleHBlY3RlZCBuYiBvZiBzb3VyY2UgYnVmZmVycywgbm8gbmVlZCB0byBjb21wdXRlIGl0XG4gICAgdmFyIGNvZGVjRXZlbnRzID0gMjtcblxuICAgIGlmIChkYXRhLmF1ZGlvICYmICFkYXRhLnZpZGVvIHx8ICFkYXRhLmFsdEF1ZGlvKSB7XG4gICAgICBjb2RlY0V2ZW50cyA9IDE7XG4gICAgfVxuXG4gICAgdGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkID0gdGhpcy5fYnVmZmVyQ29kZWNFdmVudHNUb3RhbCA9IGNvZGVjRXZlbnRzO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2codGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkICsgXCIgYnVmZmVyQ29kZWMgZXZlbnQocykgZXhwZWN0ZWRcIik7XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFBdHRhY2hpbmcgPSBmdW5jdGlvbiBvbk1lZGlhQXR0YWNoaW5nKGRhdGEpIHtcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhID0gZGF0YS5tZWRpYTtcblxuICAgIGlmIChtZWRpYSAmJiBidWZmZXJfY29udHJvbGxlcl9NZWRpYVNvdXJjZSkge1xuICAgICAgLy8gc2V0dXAgdGhlIG1lZGlhIHNvdXJjZVxuICAgICAgdmFyIG1zID0gdGhpcy5tZWRpYVNvdXJjZSA9IG5ldyBidWZmZXJfY29udHJvbGxlcl9NZWRpYVNvdXJjZSgpOyAvLyBNZWRpYSBTb3VyY2UgbGlzdGVuZXJzXG5cbiAgICAgIG1zLmFkZEV2ZW50TGlzdGVuZXIoJ3NvdXJjZW9wZW4nLCB0aGlzLl9vbk1lZGlhU291cmNlT3Blbik7XG4gICAgICBtcy5hZGRFdmVudExpc3RlbmVyKCdzb3VyY2VlbmRlZCcsIHRoaXMuX29uTWVkaWFTb3VyY2VFbmRlZCk7XG4gICAgICBtcy5hZGRFdmVudExpc3RlbmVyKCdzb3VyY2VjbG9zZScsIHRoaXMuX29uTWVkaWFTb3VyY2VDbG9zZSk7IC8vIGxpbmsgdmlkZW8gYW5kIG1lZGlhIFNvdXJjZVxuXG4gICAgICBtZWRpYS5zcmMgPSB3aW5kb3cuVVJMLmNyZWF0ZU9iamVjdFVSTChtcyk7IC8vIGNhY2hlIHRoZSBsb2NhbGx5IGdlbmVyYXRlZCBvYmplY3QgdXJsXG5cbiAgICAgIHRoaXMuX29iamVjdFVybCA9IG1lZGlhLnNyYztcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFEZXRhY2hpbmcgPSBmdW5jdGlvbiBvbk1lZGlhRGV0YWNoaW5nKCkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ21lZGlhIHNvdXJjZSBkZXRhY2hpbmcnKTtcbiAgICB2YXIgbXMgPSB0aGlzLm1lZGlhU291cmNlO1xuXG4gICAgaWYgKG1zKSB7XG4gICAgICBpZiAobXMucmVhZHlTdGF0ZSA9PT0gJ29wZW4nKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gZW5kT2ZTdHJlYW0gY291bGQgdHJpZ2dlciBleGNlcHRpb24gaWYgYW55IHNvdXJjZWJ1ZmZlciBpcyBpbiB1cGRhdGluZyBzdGF0ZVxuICAgICAgICAgIC8vIHdlIGRvbid0IHJlYWxseSBjYXJlIGFib3V0IGNoZWNraW5nIHNvdXJjZWJ1ZmZlciBzdGF0ZSBoZXJlLFxuICAgICAgICAgIC8vIGFzIHdlIGFyZSBhbnl3YXkgZGV0YWNoaW5nIHRoZSBNZWRpYVNvdXJjZVxuICAgICAgICAgIC8vIGxldCdzIGp1c3QgYXZvaWQgdGhpcyBleGNlcHRpb24gdG8gcHJvcGFnYXRlXG4gICAgICAgICAgbXMuZW5kT2ZTdHJlYW0oKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJvbk1lZGlhRGV0YWNoaW5nOlwiICsgZXJyLm1lc3NhZ2UgKyBcIiB3aGlsZSBjYWxsaW5nIGVuZE9mU3RyZWFtXCIpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIG1zLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NvdXJjZW9wZW4nLCB0aGlzLl9vbk1lZGlhU291cmNlT3Blbik7XG4gICAgICBtcy5yZW1vdmVFdmVudExpc3RlbmVyKCdzb3VyY2VlbmRlZCcsIHRoaXMuX29uTWVkaWFTb3VyY2VFbmRlZCk7XG4gICAgICBtcy5yZW1vdmVFdmVudExpc3RlbmVyKCdzb3VyY2VjbG9zZScsIHRoaXMuX29uTWVkaWFTb3VyY2VDbG9zZSk7IC8vIERldGFjaCBwcm9wZXJseSB0aGUgTWVkaWFTb3VyY2UgZnJvbSB0aGUgSFRNTE1lZGlhRWxlbWVudCBhc1xuICAgICAgLy8gc3VnZ2VzdGVkIGluIGh0dHBzOi8vZ2l0aHViLmNvbS93M2MvbWVkaWEtc291cmNlL2lzc3Vlcy81My5cblxuICAgICAgaWYgKHRoaXMubWVkaWEpIHtcbiAgICAgICAgaWYgKHRoaXMuX29iamVjdFVybCkge1xuICAgICAgICAgIHdpbmRvdy5VUkwucmV2b2tlT2JqZWN0VVJMKHRoaXMuX29iamVjdFVybCk7XG4gICAgICAgIH0gLy8gY2xlYW4gdXAgdmlkZW8gdGFnIHNyYyBvbmx5IGlmIGl0J3Mgb3VyIG93biB1cmwuIHNvbWUgZXh0ZXJuYWwgbGlicmFyaWVzIG1pZ2h0XG4gICAgICAgIC8vIGhpamFjayB0aGUgdmlkZW8gdGFnIGFuZCBjaGFuZ2UgaXRzICdzcmMnIHdpdGhvdXQgZGVzdHJveWluZyB0aGUgSGxzIGluc3RhbmNlIGZpcnN0XG5cblxuICAgICAgICBpZiAodGhpcy5tZWRpYS5zcmMgPT09IHRoaXMuX29iamVjdFVybCkge1xuICAgICAgICAgIHRoaXMubWVkaWEucmVtb3ZlQXR0cmlidXRlKCdzcmMnKTtcbiAgICAgICAgICB0aGlzLm1lZGlhLmxvYWQoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignbWVkaWEuc3JjIHdhcyBjaGFuZ2VkIGJ5IGEgdGhpcmQgcGFydHkgLSBza2lwIGNsZWFudXAnKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0aGlzLm1lZGlhU291cmNlID0gbnVsbDtcbiAgICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgICAgdGhpcy5fb2JqZWN0VXJsID0gbnVsbDtcbiAgICAgIHRoaXMuYnVmZmVyQ29kZWNFdmVudHNFeHBlY3RlZCA9IHRoaXMuX2J1ZmZlckNvZGVjRXZlbnRzVG90YWw7XG4gICAgICB0aGlzLnBlbmRpbmdUcmFja3MgPSB7fTtcbiAgICAgIHRoaXMudHJhY2tzID0ge307XG4gICAgICB0aGlzLnNvdXJjZUJ1ZmZlciA9IHt9O1xuICAgICAgdGhpcy5mbHVzaFJhbmdlID0gW107XG4gICAgICB0aGlzLnNlZ21lbnRzID0gW107XG4gICAgICB0aGlzLmFwcGVuZGVkID0gMDtcbiAgICB9XG5cbiAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfREVUQUNIRUQpO1xuICB9O1xuXG4gIF9wcm90by5jaGVja1BlbmRpbmdUcmFja3MgPSBmdW5jdGlvbiBjaGVja1BlbmRpbmdUcmFja3MoKSB7XG4gICAgdmFyIGJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgPSB0aGlzLmJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQsXG4gICAgICAgIHBlbmRpbmdUcmFja3MgPSB0aGlzLnBlbmRpbmdUcmFja3M7IC8vIENoZWNrIGlmIHdlJ3ZlIHJlY2VpdmVkIGFsbCBvZiB0aGUgZXhwZWN0ZWQgYnVmZmVyQ29kZWMgZXZlbnRzLiBXaGVuIG5vbmUgcmVtYWluLCBjcmVhdGUgYWxsIHRoZSBzb3VyY2VCdWZmZXJzIGF0IG9uY2UuXG4gICAgLy8gVGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSB0aGUgTVNFIHNwZWMgYWxsb3dzIGltcGxlbWVudGF0aW9ucyB0byB0aHJvdyBRdW90YUV4Y2VlZGVkRXJyb3JzIGlmIGNyZWF0aW5nIG5ldyBzb3VyY2VCdWZmZXJzIGFmdGVyXG4gICAgLy8gZGF0YSBoYXMgYmVlbiBhcHBlbmRlZCB0byBleGlzdGluZyBvbmVzLlxuICAgIC8vIDIgdHJhY2tzIGlzIHRoZSBtYXggKG9uZSBmb3IgYXVkaW8sIG9uZSBmb3IgdmlkZW8pLiBJZiB3ZSd2ZSByZWFjaCB0aGlzIG1heCBnbyBhaGVhZCBhbmQgY3JlYXRlIHRoZSBidWZmZXJzLlxuXG4gICAgdmFyIHBlbmRpbmdUcmFja3NDb3VudCA9IE9iamVjdC5rZXlzKHBlbmRpbmdUcmFja3MpLmxlbmd0aDtcblxuICAgIGlmIChwZW5kaW5nVHJhY2tzQ291bnQgJiYgIWJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgfHwgcGVuZGluZ1RyYWNrc0NvdW50ID09PSAyKSB7XG4gICAgICAvLyBvaywgbGV0J3MgY3JlYXRlIHRoZW0gbm93ICFcbiAgICAgIHRoaXMuY3JlYXRlU291cmNlQnVmZmVycyhwZW5kaW5nVHJhY2tzKTtcbiAgICAgIHRoaXMucGVuZGluZ1RyYWNrcyA9IHt9OyAvLyBhcHBlbmQgYW55IHBlbmRpbmcgc2VnbWVudHMgbm93ICFcblxuICAgICAgdGhpcy5kb0FwcGVuZGluZygpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25CdWZmZXJSZXNldCA9IGZ1bmN0aW9uIG9uQnVmZmVyUmVzZXQoKSB7XG4gICAgdmFyIHNvdXJjZUJ1ZmZlciA9IHRoaXMuc291cmNlQnVmZmVyO1xuXG4gICAgZm9yICh2YXIgdHlwZSBpbiBzb3VyY2VCdWZmZXIpIHtcbiAgICAgIHZhciBzYiA9IHNvdXJjZUJ1ZmZlclt0eXBlXTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKHNiKSB7XG4gICAgICAgICAgaWYgKHRoaXMubWVkaWFTb3VyY2UpIHtcbiAgICAgICAgICAgIHRoaXMubWVkaWFTb3VyY2UucmVtb3ZlU291cmNlQnVmZmVyKHNiKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzYi5yZW1vdmVFdmVudExpc3RlbmVyKCd1cGRhdGVlbmQnLCB0aGlzLl9vblNCVXBkYXRlRW5kKTtcbiAgICAgICAgICBzYi5yZW1vdmVFdmVudExpc3RlbmVyKCdlcnJvcicsIHRoaXMuX29uU0JVcGRhdGVFcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycikge31cbiAgICB9XG5cbiAgICB0aGlzLnNvdXJjZUJ1ZmZlciA9IHt9O1xuICAgIHRoaXMuZmx1c2hSYW5nZSA9IFtdO1xuICAgIHRoaXMuc2VnbWVudHMgPSBbXTtcbiAgICB0aGlzLmFwcGVuZGVkID0gMDtcbiAgfTtcblxuICBfcHJvdG8ub25CdWZmZXJDb2RlY3MgPSBmdW5jdGlvbiBvbkJ1ZmZlckNvZGVjcyh0cmFja3MpIHtcbiAgICB2YXIgX3RoaXMyID0gdGhpcztcblxuICAgIC8vIGlmIHNvdXJjZSBidWZmZXIocykgbm90IGNyZWF0ZWQgeWV0LCBhcHBlbmRlZCBidWZmZXIgdHJhY2tzIGluIHRoaXMucGVuZGluZ1RyYWNrc1xuICAgIC8vIGlmIHNvdXJjZWJ1ZmZlcnMgYWxyZWFkeSBjcmVhdGVkLCBkbyBub3RoaW5nIC4uLlxuICAgIGlmIChPYmplY3Qua2V5cyh0aGlzLnNvdXJjZUJ1ZmZlcikubGVuZ3RoKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgT2JqZWN0LmtleXModHJhY2tzKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFja05hbWUpIHtcbiAgICAgIF90aGlzMi5wZW5kaW5nVHJhY2tzW3RyYWNrTmFtZV0gPSB0cmFja3NbdHJhY2tOYW1lXTtcbiAgICB9KTtcbiAgICB0aGlzLmJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgPSBNYXRoLm1heCh0aGlzLmJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgLSAxLCAwKTtcblxuICAgIGlmICh0aGlzLm1lZGlhU291cmNlICYmIHRoaXMubWVkaWFTb3VyY2UucmVhZHlTdGF0ZSA9PT0gJ29wZW4nKSB7XG4gICAgICB0aGlzLmNoZWNrUGVuZGluZ1RyYWNrcygpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uY3JlYXRlU291cmNlQnVmZmVycyA9IGZ1bmN0aW9uIGNyZWF0ZVNvdXJjZUJ1ZmZlcnModHJhY2tzKSB7XG4gICAgdmFyIHNvdXJjZUJ1ZmZlciA9IHRoaXMuc291cmNlQnVmZmVyLFxuICAgICAgICBtZWRpYVNvdXJjZSA9IHRoaXMubWVkaWFTb3VyY2U7XG5cbiAgICBpZiAoIW1lZGlhU291cmNlKSB7XG4gICAgICB0aHJvdyBFcnJvcignY3JlYXRlU291cmNlQnVmZmVycyBjYWxsZWQgd2hlbiBtZWRpYVNvdXJjZSB3YXMgbnVsbCcpO1xuICAgIH1cblxuICAgIGZvciAodmFyIHRyYWNrTmFtZSBpbiB0cmFja3MpIHtcbiAgICAgIGlmICghc291cmNlQnVmZmVyW3RyYWNrTmFtZV0pIHtcbiAgICAgICAgdmFyIHRyYWNrID0gdHJhY2tzW3RyYWNrTmFtZV07XG5cbiAgICAgICAgaWYgKCF0cmFjaykge1xuICAgICAgICAgIHRocm93IEVycm9yKFwic291cmNlIGJ1ZmZlciBleGlzdHMgZm9yIHRyYWNrIFwiICsgdHJhY2tOYW1lICsgXCIsIGhvd2V2ZXIgdHJhY2sgZG9lcyBub3RcIik7XG4gICAgICAgIH0gLy8gdXNlIGxldmVsQ29kZWMgYXMgZmlyc3QgcHJpb3JpdHlcblxuXG4gICAgICAgIHZhciBjb2RlYyA9IHRyYWNrLmxldmVsQ29kZWMgfHwgdHJhY2suY29kZWM7XG4gICAgICAgIHZhciBtaW1lVHlwZSA9IHRyYWNrLmNvbnRhaW5lciArIFwiO2NvZGVjcz1cIiArIGNvZGVjO1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiY3JlYXRpbmcgc291cmNlQnVmZmVyKFwiICsgbWltZVR5cGUgKyBcIilcIik7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB2YXIgc2IgPSBzb3VyY2VCdWZmZXJbdHJhY2tOYW1lXSA9IG1lZGlhU291cmNlLmFkZFNvdXJjZUJ1ZmZlcihtaW1lVHlwZSk7XG4gICAgICAgICAgc2IuYWRkRXZlbnRMaXN0ZW5lcigndXBkYXRlZW5kJywgdGhpcy5fb25TQlVwZGF0ZUVuZCk7XG4gICAgICAgICAgc2IuYWRkRXZlbnRMaXN0ZW5lcignZXJyb3InLCB0aGlzLl9vblNCVXBkYXRlRXJyb3IpO1xuICAgICAgICAgIHRoaXMudHJhY2tzW3RyYWNrTmFtZV0gPSB7XG4gICAgICAgICAgICBidWZmZXI6IHNiLFxuICAgICAgICAgICAgY29kZWM6IGNvZGVjLFxuICAgICAgICAgICAgaWQ6IHRyYWNrLmlkLFxuICAgICAgICAgICAgY29udGFpbmVyOiB0cmFjay5jb250YWluZXIsXG4gICAgICAgICAgICBsZXZlbENvZGVjOiB0cmFjay5sZXZlbENvZGVjXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKFwiZXJyb3Igd2hpbGUgdHJ5aW5nIHRvIGFkZCBzb3VyY2VCdWZmZXI6XCIgKyBlcnIubWVzc2FnZSk7XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICAgICAgZGV0YWlsczogZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkJVRkZFUl9BRERfQ09ERUNfRVJST1IsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICBlcnI6IGVycixcbiAgICAgICAgICAgIG1pbWVUeXBlOiBtaW1lVHlwZVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9DUkVBVEVELCB7XG4gICAgICB0cmFja3M6IHRoaXMudHJhY2tzXG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLm9uQnVmZmVyQXBwZW5kaW5nID0gZnVuY3Rpb24gb25CdWZmZXJBcHBlbmRpbmcoZGF0YSkge1xuICAgIGlmICghdGhpcy5fbmVlZHNGbHVzaCkge1xuICAgICAgaWYgKCF0aGlzLnNlZ21lbnRzKSB7XG4gICAgICAgIHRoaXMuc2VnbWVudHMgPSBbZGF0YV07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNlZ21lbnRzLnB1c2goZGF0YSk7XG4gICAgICB9XG5cbiAgICAgIHRoaXMuZG9BcHBlbmRpbmcoKTtcbiAgICB9XG4gIH0gLy8gb24gQlVGRkVSX0VPUyBtYXJrIG1hdGNoaW5nIHNvdXJjZWJ1ZmZlcihzKSBhcyBlbmRlZCBhbmQgdHJpZ2dlciBjaGVja0VvcygpXG4gIC8vIGFuIHVuZGVmaW5lZCBkYXRhLnR5cGUgd2lsbCBtYXJrIGFsbCBidWZmZXJzIGFzIEVPUy5cbiAgO1xuXG4gIF9wcm90by5vbkJ1ZmZlckVvcyA9IGZ1bmN0aW9uIG9uQnVmZmVyRW9zKGRhdGEpIHtcbiAgICBmb3IgKHZhciB0eXBlIGluIHRoaXMuc291cmNlQnVmZmVyKSB7XG4gICAgICBpZiAoIWRhdGEudHlwZSB8fCBkYXRhLnR5cGUgPT09IHR5cGUpIHtcbiAgICAgICAgdmFyIHNiID0gdGhpcy5zb3VyY2VCdWZmZXJbdHlwZV07XG5cbiAgICAgICAgaWYgKHNiICYmICFzYi5lbmRlZCkge1xuICAgICAgICAgIHNiLmVuZGVkID0gdHJ1ZTtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKHR5cGUgKyBcIiBzb3VyY2VCdWZmZXIgbm93IEVPU1wiKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuY2hlY2tFb3MoKTtcbiAgfSAvLyBpZiBhbGwgc291cmNlIGJ1ZmZlcnMgYXJlIG1hcmtlZCBhcyBlbmRlZCwgc2lnbmFsIGVuZE9mU3RyZWFtKCkgdG8gTWVkaWFTb3VyY2UuXG4gIDtcblxuICBfcHJvdG8uY2hlY2tFb3MgPSBmdW5jdGlvbiBjaGVja0VvcygpIHtcbiAgICB2YXIgc291cmNlQnVmZmVyID0gdGhpcy5zb3VyY2VCdWZmZXIsXG4gICAgICAgIG1lZGlhU291cmNlID0gdGhpcy5tZWRpYVNvdXJjZTtcblxuICAgIGlmICghbWVkaWFTb3VyY2UgfHwgbWVkaWFTb3VyY2UucmVhZHlTdGF0ZSAhPT0gJ29wZW4nKSB7XG4gICAgICB0aGlzLl9uZWVkc0VvcyA9IGZhbHNlO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGZvciAodmFyIHR5cGUgaW4gc291cmNlQnVmZmVyKSB7XG4gICAgICB2YXIgc2IgPSBzb3VyY2VCdWZmZXJbdHlwZV07XG4gICAgICBpZiAoIXNiKSBjb250aW51ZTtcblxuICAgICAgaWYgKCFzYi5lbmRlZCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChzYi51cGRhdGluZykge1xuICAgICAgICB0aGlzLl9uZWVkc0VvcyA9IHRydWU7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdhbGwgbWVkaWEgZGF0YSBhcmUgYXZhaWxhYmxlLCBzaWduYWwgZW5kT2ZTdHJlYW0oKSB0byBNZWRpYVNvdXJjZSBhbmQgc3RvcCBsb2FkaW5nIGZyYWdtZW50Jyk7IC8vIE5vdGlmeSB0aGUgbWVkaWEgZWxlbWVudCB0aGF0IGl0IG5vdyBoYXMgYWxsIG9mIHRoZSBtZWRpYSBkYXRhXG5cbiAgICB0cnkge1xuICAgICAgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignZXhjZXB0aW9uIHdoaWxlIGNhbGxpbmcgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKScpO1xuICAgIH1cblxuICAgIHRoaXMuX25lZWRzRW9zID0gZmFsc2U7XG4gIH07XG5cbiAgX3Byb3RvLm9uQnVmZmVyRmx1c2hpbmcgPSBmdW5jdGlvbiBvbkJ1ZmZlckZsdXNoaW5nKGRhdGEpIHtcbiAgICBpZiAoZGF0YS50eXBlKSB7XG4gICAgICB0aGlzLmZsdXNoUmFuZ2UucHVzaCh7XG4gICAgICAgIHN0YXJ0OiBkYXRhLnN0YXJ0T2Zmc2V0LFxuICAgICAgICBlbmQ6IGRhdGEuZW5kT2Zmc2V0LFxuICAgICAgICB0eXBlOiBkYXRhLnR5cGVcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmZsdXNoUmFuZ2UucHVzaCh7XG4gICAgICAgIHN0YXJ0OiBkYXRhLnN0YXJ0T2Zmc2V0LFxuICAgICAgICBlbmQ6IGRhdGEuZW5kT2Zmc2V0LFxuICAgICAgICB0eXBlOiAndmlkZW8nXG4gICAgICB9KTtcbiAgICAgIHRoaXMuZmx1c2hSYW5nZS5wdXNoKHtcbiAgICAgICAgc3RhcnQ6IGRhdGEuc3RhcnRPZmZzZXQsXG4gICAgICAgIGVuZDogZGF0YS5lbmRPZmZzZXQsXG4gICAgICAgIHR5cGU6ICdhdWRpbydcbiAgICAgIH0pO1xuICAgIH0gLy8gYXR0ZW1wdCBmbHVzaCBpbW1lZGlhdGVseVxuXG5cbiAgICB0aGlzLmZsdXNoQnVmZmVyQ291bnRlciA9IDA7XG4gICAgdGhpcy5kb0ZsdXNoKCk7XG4gIH07XG5cbiAgX3Byb3RvLmZsdXNoTGl2ZUJhY2tCdWZmZXIgPSBmdW5jdGlvbiBmbHVzaExpdmVCYWNrQnVmZmVyKCkge1xuICAgIC8vIGNsZWFyIGJhY2sgYnVmZmVyIGZvciBsaXZlIG9ubHlcbiAgICBpZiAoIXRoaXMuX2xpdmUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgbGl2ZUJhY2tCdWZmZXJMZW5ndGggPSB0aGlzLmNvbmZpZy5saXZlQmFja0J1ZmZlckxlbmd0aDtcblxuICAgIGlmICghaXNGaW5pdGUobGl2ZUJhY2tCdWZmZXJMZW5ndGgpIHx8IGxpdmVCYWNrQnVmZmVyTGVuZ3RoIDwgMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5tZWRpYSkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKCdmbHVzaExpdmVCYWNrQnVmZmVyIGNhbGxlZCB3aXRob3V0IGF0dGFjaGluZyBtZWRpYScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBjdXJyZW50VGltZSA9IHRoaXMubWVkaWEuY3VycmVudFRpbWU7XG4gICAgdmFyIHNvdXJjZUJ1ZmZlciA9IHRoaXMuc291cmNlQnVmZmVyO1xuICAgIHZhciBidWZmZXJUeXBlcyA9IE9iamVjdC5rZXlzKHNvdXJjZUJ1ZmZlcik7XG4gICAgdmFyIHRhcmdldEJhY2tCdWZmZXJQb3NpdGlvbiA9IGN1cnJlbnRUaW1lIC0gTWF0aC5tYXgobGl2ZUJhY2tCdWZmZXJMZW5ndGgsIHRoaXMuX2xldmVsVGFyZ2V0RHVyYXRpb24pO1xuXG4gICAgZm9yICh2YXIgaW5kZXggPSBidWZmZXJUeXBlcy5sZW5ndGggLSAxOyBpbmRleCA+PSAwOyBpbmRleC0tKSB7XG4gICAgICB2YXIgYnVmZmVyVHlwZSA9IGJ1ZmZlclR5cGVzW2luZGV4XTtcbiAgICAgIHZhciBzYiA9IHNvdXJjZUJ1ZmZlcltidWZmZXJUeXBlXTtcblxuICAgICAgaWYgKHNiKSB7XG4gICAgICAgIHZhciBidWZmZXJlZCA9IHNiLmJ1ZmZlcmVkOyAvLyB3aGVuIHRhcmdldCBidWZmZXIgc3RhcnQgZXhjZWVkcyBhY3R1YWwgYnVmZmVyIHN0YXJ0XG5cbiAgICAgICAgaWYgKGJ1ZmZlcmVkLmxlbmd0aCA+IDAgJiYgdGFyZ2V0QmFja0J1ZmZlclBvc2l0aW9uID4gYnVmZmVyZWQuc3RhcnQoMCkpIHtcbiAgICAgICAgICAvLyByZW1vdmUgYnVmZmVyIHVwIHVudGlsIGN1cnJlbnQgdGltZSBtaW51cyBtaW5pbXVtIGJhY2sgYnVmZmVyIGxlbmd0aCAocmVtb3ZpbmcgYnVmZmVyIHRvbyBjbG9zZSB0byBjdXJyZW50XG4gICAgICAgICAgLy8gdGltZSB3aWxsIGxlYWQgdG8gcGxheWJhY2sgZnJlZXppbmcpXG4gICAgICAgICAgLy8gY3JlZGl0cyBmb3IgbGV2ZWwgdGFyZ2V0IGR1cmF0aW9uIC0gaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvanMvaHR0cC1zdHJlYW1pbmcvYmxvYi8zMTMyOTMzYjZhYTk5ZGRlZmFiMjljMTA0NDc2MjRlZmQ2ZmQ2ZTUyL3NyYy9zZWdtZW50LWxvYWRlci5qcyNMOTFcbiAgICAgICAgICBpZiAodGhpcy5yZW1vdmVCdWZmZXJSYW5nZShidWZmZXJUeXBlLCBzYiwgMCwgdGFyZ2V0QmFja0J1ZmZlclBvc2l0aW9uKSkge1xuICAgICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkxJVkVfQkFDS19CVUZGRVJfUkVBQ0hFRCwge1xuICAgICAgICAgICAgICBidWZmZXJFbmQ6IHRhcmdldEJhY2tCdWZmZXJQb3NpdGlvblxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkxldmVsVXBkYXRlZCA9IGZ1bmN0aW9uIG9uTGV2ZWxVcGRhdGVkKF9yZWYpIHtcbiAgICB2YXIgZGV0YWlscyA9IF9yZWYuZGV0YWlscztcblxuICAgIGlmIChkZXRhaWxzLmZyYWdtZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLl9sZXZlbER1cmF0aW9uID0gZGV0YWlscy50b3RhbGR1cmF0aW9uICsgZGV0YWlscy5mcmFnbWVudHNbMF0uc3RhcnQ7XG4gICAgICB0aGlzLl9sZXZlbFRhcmdldER1cmF0aW9uID0gZGV0YWlscy5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gfHwgZGV0YWlscy50YXJnZXRkdXJhdGlvbiB8fCAxMDtcbiAgICAgIHRoaXMuX2xpdmUgPSBkZXRhaWxzLmxpdmU7XG4gICAgICB0aGlzLnVwZGF0ZU1lZGlhRWxlbWVudER1cmF0aW9uKCk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBVcGRhdGUgTWVkaWEgU291cmNlIGR1cmF0aW9uIHRvIGN1cnJlbnQgbGV2ZWwgZHVyYXRpb24gb3Igb3ZlcnJpZGUgdG8gSW5maW5pdHkgaWYgY29uZmlndXJhdGlvbiBwYXJhbWV0ZXJcbiAgICogJ2xpdmVEdXJhdGlvbkluZmluaXR5YCBpcyBzZXQgdG8gYHRydWVgXG4gICAqIE1vcmUgZGV0YWlsczogaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvaXNzdWVzLzM1NVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by51cGRhdGVNZWRpYUVsZW1lbnREdXJhdGlvbiA9IGZ1bmN0aW9uIHVwZGF0ZU1lZGlhRWxlbWVudER1cmF0aW9uKCkge1xuICAgIHZhciBjb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICB2YXIgZHVyYXRpb247XG5cbiAgICBpZiAodGhpcy5fbGV2ZWxEdXJhdGlvbiA9PT0gbnVsbCB8fCAhdGhpcy5tZWRpYSB8fCAhdGhpcy5tZWRpYVNvdXJjZSB8fCAhdGhpcy5zb3VyY2VCdWZmZXIgfHwgdGhpcy5tZWRpYS5yZWFkeVN0YXRlID09PSAwIHx8IHRoaXMubWVkaWFTb3VyY2UucmVhZHlTdGF0ZSAhPT0gJ29wZW4nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgZm9yICh2YXIgdHlwZSBpbiB0aGlzLnNvdXJjZUJ1ZmZlcikge1xuICAgICAgdmFyIHNiID0gdGhpcy5zb3VyY2VCdWZmZXJbdHlwZV07XG5cbiAgICAgIGlmIChzYiAmJiBzYi51cGRhdGluZyA9PT0gdHJ1ZSkge1xuICAgICAgICAvLyBjYW4ndCBzZXQgZHVyYXRpb24gd2hpbHN0IGEgYnVmZmVyIGlzIHVwZGF0aW5nXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBkdXJhdGlvbiA9IHRoaXMubWVkaWEuZHVyYXRpb247IC8vIGluaXRpYWxpc2UgdG8gdGhlIHZhbHVlIHRoYXQgdGhlIG1lZGlhIHNvdXJjZSBpcyByZXBvcnRpbmdcblxuICAgIGlmICh0aGlzLl9tc0R1cmF0aW9uID09PSBudWxsKSB7XG4gICAgICB0aGlzLl9tc0R1cmF0aW9uID0gdGhpcy5tZWRpYVNvdXJjZS5kdXJhdGlvbjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5fbGl2ZSA9PT0gdHJ1ZSAmJiBjb25maWcubGl2ZUR1cmF0aW9uSW5maW5pdHkgPT09IHRydWUpIHtcbiAgICAgIC8vIE92ZXJyaWRlIGR1cmF0aW9uIHRvIEluZmluaXR5XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdNZWRpYSBTb3VyY2UgZHVyYXRpb24gaXMgc2V0IHRvIEluZmluaXR5Jyk7XG4gICAgICB0aGlzLl9tc0R1cmF0aW9uID0gdGhpcy5tZWRpYVNvdXJjZS5kdXJhdGlvbiA9IEluZmluaXR5O1xuICAgIH0gZWxzZSBpZiAodGhpcy5fbGV2ZWxEdXJhdGlvbiA+IHRoaXMuX21zRHVyYXRpb24gJiYgdGhpcy5fbGV2ZWxEdXJhdGlvbiA+IGR1cmF0aW9uIHx8ICFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKGR1cmF0aW9uKSkge1xuICAgICAgLy8gbGV2ZWxEdXJhdGlvbiB3YXMgdGhlIGxhc3QgdmFsdWUgd2Ugc2V0LlxuICAgICAgLy8gbm90IHVzaW5nIG1lZGlhU291cmNlLmR1cmF0aW9uIGFzIHRoZSBicm93c2VyIG1heSB0d2VhayB0aGlzIHZhbHVlXG4gICAgICAvLyBvbmx5IHVwZGF0ZSBNZWRpYSBTb3VyY2UgZHVyYXRpb24gaWYgaXRzIHZhbHVlIGluY3JlYXNlLCB0aGlzIGlzIHRvIGF2b2lkXG4gICAgICAvLyBmbHVzaGluZyBhbHJlYWR5IGJ1ZmZlcmVkIHBvcnRpb24gd2hlbiBzd2l0Y2hpbmcgYmV0d2VlbiBxdWFsaXR5IGxldmVsXG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiVXBkYXRpbmcgTWVkaWEgU291cmNlIGR1cmF0aW9uIHRvIFwiICsgdGhpcy5fbGV2ZWxEdXJhdGlvbi50b0ZpeGVkKDMpKTtcbiAgICAgIHRoaXMuX21zRHVyYXRpb24gPSB0aGlzLm1lZGlhU291cmNlLmR1cmF0aW9uID0gdGhpcy5fbGV2ZWxEdXJhdGlvbjtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmRvRmx1c2ggPSBmdW5jdGlvbiBkb0ZsdXNoKCkge1xuICAgIC8vIGxvb3AgdGhyb3VnaCBhbGwgYnVmZmVyIHJhbmdlcyB0byBmbHVzaFxuICAgIHdoaWxlICh0aGlzLmZsdXNoUmFuZ2UubGVuZ3RoKSB7XG4gICAgICB2YXIgcmFuZ2UgPSB0aGlzLmZsdXNoUmFuZ2VbMF07IC8vIGZsdXNoQnVmZmVyIHdpbGwgYWJvcnQgYW55IGJ1ZmZlciBhcHBlbmQgaW4gcHJvZ3Jlc3MgYW5kIGZsdXNoIEF1ZGlvL1ZpZGVvIEJ1ZmZlclxuXG4gICAgICBpZiAodGhpcy5mbHVzaEJ1ZmZlcihyYW5nZS5zdGFydCwgcmFuZ2UuZW5kLCByYW5nZS50eXBlKSkge1xuICAgICAgICAvLyByYW5nZSBmbHVzaGVkLCByZW1vdmUgZnJvbSBmbHVzaCBhcnJheVxuICAgICAgICB0aGlzLmZsdXNoUmFuZ2Uuc2hpZnQoKTtcbiAgICAgICAgdGhpcy5mbHVzaEJ1ZmZlckNvdW50ZXIgPSAwO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5fbmVlZHNGbHVzaCA9IHRydWU7IC8vIGF2b2lkIGxvb3BpbmcsIHdhaXQgZm9yIFNCIHVwZGF0ZSBlbmQgdG8gcmV0cmlnZ2VyIGEgZmx1c2hcblxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZmx1c2hSYW5nZS5sZW5ndGggPT09IDApIHtcbiAgICAgIC8vIGV2ZXJ5dGhpbmcgZmx1c2hlZFxuICAgICAgdGhpcy5fbmVlZHNGbHVzaCA9IGZhbHNlOyAvLyBsZXQncyByZWNvbXB1dGUgdGhpcy5hcHBlbmRlZCwgd2hpY2ggaXMgdXNlZCB0byBhdm9pZCBmbHVzaCBsb29waW5nXG5cbiAgICAgIHZhciBhcHBlbmRlZCA9IDA7XG4gICAgICB2YXIgc291cmNlQnVmZmVyID0gdGhpcy5zb3VyY2VCdWZmZXI7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIGZvciAodmFyIHR5cGUgaW4gc291cmNlQnVmZmVyKSB7XG4gICAgICAgICAgdmFyIHNiID0gc291cmNlQnVmZmVyW3R5cGVdO1xuXG4gICAgICAgICAgaWYgKHNiKSB7XG4gICAgICAgICAgICBhcHBlbmRlZCArPSBzYi5idWZmZXJlZC5sZW5ndGg7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAvLyBlcnJvciBjb3VsZCBiZSB0aHJvd24gd2hpbGUgYWNjZXNzaW5nIGJ1ZmZlcmVkLCBpbiBjYXNlIHNvdXJjZWJ1ZmZlciBoYXMgYWxyZWFkeSBiZWVuIHJlbW92ZWQgZnJvbSBNZWRpYVNvdXJjZVxuICAgICAgICAvLyB0aGlzIGlzIGhhcm1lc3MgYXQgdGhpcyBzdGFnZSwgY2F0Y2ggdGhpcyB0byBhdm9pZCByZXBvcnRpbmcgYW4gaW50ZXJuYWwgZXhjZXB0aW9uXG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignZXJyb3Igd2hpbGUgYWNjZXNzaW5nIHNvdXJjZUJ1ZmZlci5idWZmZXJlZCcpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmFwcGVuZGVkID0gYXBwZW5kZWQ7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0ZMVVNIRUQpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uZG9BcHBlbmRpbmcgPSBmdW5jdGlvbiBkb0FwcGVuZGluZygpIHtcbiAgICB2YXIgY29uZmlnID0gdGhpcy5jb25maWcsXG4gICAgICAgIGhscyA9IHRoaXMuaGxzLFxuICAgICAgICBzZWdtZW50cyA9IHRoaXMuc2VnbWVudHMsXG4gICAgICAgIHNvdXJjZUJ1ZmZlciA9IHRoaXMuc291cmNlQnVmZmVyO1xuXG4gICAgaWYgKCFPYmplY3Qua2V5cyhzb3VyY2VCdWZmZXIpLmxlbmd0aCkge1xuICAgICAgLy8gZWFybHkgZXhpdCBpZiBubyBzb3VyY2UgYnVmZmVycyBoYXZlIGJlZW4gaW5pdGlhbGl6ZWQgeWV0XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLm1lZGlhIHx8IHRoaXMubWVkaWEuZXJyb3IpIHtcbiAgICAgIHRoaXMuc2VnbWVudHMgPSBbXTtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcigndHJ5aW5nIHRvIGFwcGVuZCBhbHRob3VnaCBhIG1lZGlhIGVycm9yIG9jY3VyZWQsIGZsdXNoIHNlZ21lbnQgYW5kIGFib3J0Jyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuYXBwZW5kaW5nKSB7XG4gICAgICAvLyBsb2dnZXIubG9nKGBzYiBhcHBlbmRpbmcgaW4gcHJvZ3Jlc3NgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgc2VnbWVudCA9IHNlZ21lbnRzLnNoaWZ0KCk7XG5cbiAgICBpZiAoIXNlZ21lbnQpIHtcbiAgICAgIC8vIGhhbmRsZSB1bmRlZmluZWQgc2hpZnRcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgdmFyIHNiID0gc291cmNlQnVmZmVyW3NlZ21lbnQudHlwZV07XG5cbiAgICAgIGlmICghc2IpIHtcbiAgICAgICAgLy8gaW4gY2FzZSB3ZSBkb24ndCBoYXZlIGFueSBzb3VyY2UgYnVmZmVyIG1hdGNoaW5nIHdpdGggdGhpcyBzZWdtZW50IHR5cGUsXG4gICAgICAgIC8vIGl0IG1lYW5zIHRoYXQgTWVkaWFzb3VyY2UgZmFpbHMgdG8gY3JlYXRlIHNvdXJjZWJ1ZmZlclxuICAgICAgICAvLyBkaXNjYXJkIHRoaXMgc2VnbWVudCwgYW5kIHRyaWdnZXIgdXBkYXRlIGVuZFxuICAgICAgICB0aGlzLl9vblNCVXBkYXRlRW5kKCk7XG5cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2IudXBkYXRpbmcpIHtcbiAgICAgICAgLy8gaWYgd2UgYXJlIHN0aWxsIHVwZGF0aW5nIHRoZSBzb3VyY2UgYnVmZmVyIGZyb20gdGhlIGxhc3Qgc2VnbWVudCwgcGxhY2UgdGhpcyBiYWNrIGF0IHRoZSBmcm9udCBvZiB0aGUgcXVldWVcbiAgICAgICAgc2VnbWVudHMudW5zaGlmdChzZWdtZW50KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSAvLyByZXNldCBzb3VyY2VCdWZmZXIgZW5kZWQgZmxhZyBiZWZvcmUgYXBwZW5kaW5nIHNlZ21lbnRcblxuXG4gICAgICBzYi5lbmRlZCA9IGZhbHNlOyAvLyBsb2dnZXIubG9nKGBhcHBlbmRpbmcgJHtzZWdtZW50LmNvbnRlbnR9ICR7dHlwZX0gU0IsIHNpemU6JHtzZWdtZW50LmRhdGEubGVuZ3RofSwgJHtzZWdtZW50LnBhcmVudH1gKTtcblxuICAgICAgdGhpcy5wYXJlbnQgPSBzZWdtZW50LnBhcmVudDtcbiAgICAgIHNiLmFwcGVuZEJ1ZmZlcihzZWdtZW50LmRhdGEpO1xuICAgICAgdGhpcy5hcHBlbmRFcnJvciA9IDA7XG4gICAgICB0aGlzLmFwcGVuZGVkKys7XG4gICAgICB0aGlzLmFwcGVuZGluZyA9IHRydWU7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBpbiBjYXNlIGFueSBlcnJvciBvY2N1cmVkIHdoaWxlIGFwcGVuZGluZywgcHV0IGJhY2sgc2VnbWVudCBpbiBzZWdtZW50cyB0YWJsZVxuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKFwiZXJyb3Igd2hpbGUgdHJ5aW5nIHRvIGFwcGVuZCBidWZmZXI6XCIgKyBlcnIubWVzc2FnZSk7XG4gICAgICBzZWdtZW50cy51bnNoaWZ0KHNlZ21lbnQpO1xuICAgICAgdmFyIGV2ZW50ID0ge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBwYXJlbnQ6IHNlZ21lbnQucGFyZW50LFxuICAgICAgICBkZXRhaWxzOiAnJyxcbiAgICAgICAgZmF0YWw6IGZhbHNlXG4gICAgICB9O1xuXG4gICAgICBpZiAoZXJyLmNvZGUgPT09IDIyKSB7XG4gICAgICAgIC8vIFF1b3RhRXhjZWVkZWRFcnJvcjogaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDUvaW5mcmFzdHJ1Y3R1cmUuaHRtbCNxdW90YWV4Y2VlZGVkZXJyb3JcbiAgICAgICAgLy8gbGV0J3Mgc3RvcCBhcHBlbmRpbmcgYW55IHNlZ21lbnRzLCBhbmQgcmVwb3J0IEJVRkZFUl9GVUxMX0VSUk9SIGVycm9yXG4gICAgICAgIHRoaXMuc2VnbWVudHMgPSBbXTtcbiAgICAgICAgZXZlbnQuZGV0YWlscyA9IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5CVUZGRVJfRlVMTF9FUlJPUjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuYXBwZW5kRXJyb3IrKztcbiAgICAgICAgZXZlbnQuZGV0YWlscyA9IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5CVUZGRVJfQVBQRU5EX0VSUk9SO1xuICAgICAgICAvKiB3aXRoIFVIRCBjb250ZW50LCB3ZSBjb3VsZCBnZXQgbG9vcCBvZiBxdW90YSBleGNlZWRlZCBlcnJvciB1bnRpbFxuICAgICAgICAgIGJyb3dzZXIgaXMgYWJsZSB0byBldmljdCBzb21lIGRhdGEgZnJvbSBzb3VyY2VidWZmZXIuIHJldHJ5aW5nIGhlbHAgcmVjb3ZlcmluZyB0aGlzXG4gICAgICAgICovXG5cbiAgICAgICAgaWYgKHRoaXMuYXBwZW5kRXJyb3IgPiBjb25maWcuYXBwZW5kRXJyb3JNYXhSZXRyeSkge1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJmYWlsIFwiICsgY29uZmlnLmFwcGVuZEVycm9yTWF4UmV0cnkgKyBcIiB0aW1lcyB0byBhcHBlbmQgc2VnbWVudCBpbiBzb3VyY2VCdWZmZXJcIik7XG4gICAgICAgICAgdGhpcy5zZWdtZW50cyA9IFtdO1xuICAgICAgICAgIGV2ZW50LmZhdGFsID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCBldmVudCk7XG4gICAgfVxuICB9XG4gIC8qXG4gICAgZmx1c2ggc3BlY2lmaWVkIGJ1ZmZlcmVkIHJhbmdlLFxuICAgIHJldHVybiB0cnVlIG9uY2UgcmFuZ2UgaGFzIGJlZW4gZmx1c2hlZC5cbiAgICBhcyBzb3VyY2VCdWZmZXIucmVtb3ZlKCkgaXMgYXN5bmNocm9ub3VzLCBmbHVzaEJ1ZmZlciB3aWxsIGJlIHJldHJpZ2dlcmVkIG9uIHNvdXJjZUJ1ZmZlciB1cGRhdGUgZW5kXG4gICovXG4gIDtcblxuICBfcHJvdG8uZmx1c2hCdWZmZXIgPSBmdW5jdGlvbiBmbHVzaEJ1ZmZlcihzdGFydE9mZnNldCwgZW5kT2Zmc2V0LCBzYlR5cGUpIHtcbiAgICB2YXIgc291cmNlQnVmZmVyID0gdGhpcy5zb3VyY2VCdWZmZXI7IC8vIGV4aXQgaWYgbm8gc291cmNlQnVmZmVycyBhcmUgaW5pdGlhbGl6ZWRcblxuICAgIGlmICghT2JqZWN0LmtleXMoc291cmNlQnVmZmVyKS5sZW5ndGgpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHZhciBjdXJyZW50VGltZSA9ICdudWxsJztcblxuICAgIGlmICh0aGlzLm1lZGlhKSB7XG4gICAgICBjdXJyZW50VGltZSA9IHRoaXMubWVkaWEuY3VycmVudFRpbWUudG9GaXhlZCgzKTtcbiAgICB9XG5cbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiZmx1c2hCdWZmZXIscG9zL3N0YXJ0L2VuZDogXCIgKyBjdXJyZW50VGltZSArIFwiL1wiICsgc3RhcnRPZmZzZXQgKyBcIi9cIiArIGVuZE9mZnNldCk7IC8vIHNhZmVndWFyZCB0byBhdm9pZCBpbmZpbml0ZSBsb29waW5nIDogZG9uJ3QgdHJ5IHRvIGZsdXNoIG1vcmUgdGhhbiB0aGUgbmIgb2YgYXBwZW5kZWQgc2VnbWVudHNcblxuICAgIGlmICh0aGlzLmZsdXNoQnVmZmVyQ291bnRlciA+PSB0aGlzLmFwcGVuZGVkKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignYWJvcnQgZmx1c2hpbmcgdG9vIG1hbnkgcmV0cmllcycpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgdmFyIHNiID0gc291cmNlQnVmZmVyW3NiVHlwZV07IC8vIHdlIGFyZSBnb2luZyB0byBmbHVzaCBidWZmZXIsIG1hcmsgc291cmNlIGJ1ZmZlciBhcyAnbm90IGVuZGVkJ1xuXG4gICAgaWYgKHNiKSB7XG4gICAgICBzYi5lbmRlZCA9IGZhbHNlO1xuXG4gICAgICBpZiAoIXNiLnVwZGF0aW5nKSB7XG4gICAgICAgIGlmICh0aGlzLnJlbW92ZUJ1ZmZlclJhbmdlKHNiVHlwZSwgc2IsIHN0YXJ0T2Zmc2V0LCBlbmRPZmZzZXQpKSB7XG4gICAgICAgICAgdGhpcy5mbHVzaEJ1ZmZlckNvdW50ZXIrKztcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdjYW5ub3QgZmx1c2gsIHNiIHVwZGF0aW5nIGluIHByb2dyZXNzJyk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdidWZmZXIgZmx1c2hlZCcpOyAvLyBldmVyeXRoaW5nIGZsdXNoZWQgIVxuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgLyoqXG4gICAqIFJlbW92ZXMgZmlyc3QgYnVmZmVyZWQgcmFuZ2UgZnJvbSBwcm92aWRlZCBzb3VyY2UgYnVmZmVyIHRoYXQgbGllcyB3aXRoaW4gZ2l2ZW4gc3RhcnQgYW5kIGVuZCBvZmZzZXRzLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSBUeXBlIG9mIHRoZSBzb3VyY2UgYnVmZmVyLCBsb2dnaW5nIHB1cnBvc2VzIG9ubHkuXG4gICAqIEBwYXJhbSB7U291cmNlQnVmZmVyfSBzYiBUYXJnZXQgU291cmNlQnVmZmVyIGluc3RhbmNlLlxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhcnRPZmZzZXRcbiAgICogQHBhcmFtIHtudW1iZXJ9IGVuZE9mZnNldFxuICAgKlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSB3aGVuIHNvdXJjZSBidWZmZXIgcmVtb3ZlIHJlcXVlc3RlZC5cbiAgICovXG4gIDtcblxuICBfcHJvdG8ucmVtb3ZlQnVmZmVyUmFuZ2UgPSBmdW5jdGlvbiByZW1vdmVCdWZmZXJSYW5nZSh0eXBlLCBzYiwgc3RhcnRPZmZzZXQsIGVuZE9mZnNldCkge1xuICAgIHRyeSB7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHNiLmJ1ZmZlcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBidWZTdGFydCA9IHNiLmJ1ZmZlcmVkLnN0YXJ0KGkpO1xuICAgICAgICB2YXIgYnVmRW5kID0gc2IuYnVmZmVyZWQuZW5kKGkpO1xuICAgICAgICB2YXIgcmVtb3ZlU3RhcnQgPSBNYXRoLm1heChidWZTdGFydCwgc3RhcnRPZmZzZXQpO1xuICAgICAgICB2YXIgcmVtb3ZlRW5kID0gTWF0aC5taW4oYnVmRW5kLCBlbmRPZmZzZXQpO1xuICAgICAgICAvKiBzb21ldGltZXMgc291cmNlYnVmZmVyLnJlbW92ZSgpIGRvZXMgbm90IGZsdXNoXG4gICAgICAgICAgdGhlIGV4YWN0IGV4cGVjdGVkIHRpbWUgcmFuZ2UuXG4gICAgICAgICAgdG8gYXZvaWQgcm91bmRpbmcgaXNzdWVzL2luZmluaXRlIGxvb3AsXG4gICAgICAgICAgb25seSBmbHVzaCBidWZmZXIgcmFuZ2Ugb2YgbGVuZ3RoIGdyZWF0ZXIgdGhhbiA1MDBtcy5cbiAgICAgICAgKi9cblxuICAgICAgICBpZiAoTWF0aC5taW4ocmVtb3ZlRW5kLCBidWZFbmQpIC0gcmVtb3ZlU3RhcnQgPiAwLjUpIHtcbiAgICAgICAgICB2YXIgY3VycmVudFRpbWUgPSAnbnVsbCc7XG5cbiAgICAgICAgICBpZiAodGhpcy5tZWRpYSkge1xuICAgICAgICAgICAgY3VycmVudFRpbWUgPSB0aGlzLm1lZGlhLmN1cnJlbnRUaW1lLnRvU3RyaW5nKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInNiIHJlbW92ZSBcIiArIHR5cGUgKyBcIiBbXCIgKyByZW1vdmVTdGFydCArIFwiLFwiICsgcmVtb3ZlRW5kICsgXCJdLCBvZiBbXCIgKyBidWZTdGFydCArIFwiLFwiICsgYnVmRW5kICsgXCJdLCBwb3M6XCIgKyBjdXJyZW50VGltZSk7XG4gICAgICAgICAgc2IucmVtb3ZlKHJlbW92ZVN0YXJ0LCByZW1vdmVFbmQpO1xuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdyZW1vdmVCdWZmZXJSYW5nZSBmYWlsZWQnLCBlcnJvcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9O1xuXG4gIHJldHVybiBCdWZmZXJDb250cm9sbGVyO1xufShldmVudF9oYW5kbGVyKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgYnVmZmVyX2NvbnRyb2xsZXIgPSAoYnVmZmVyX2NvbnRyb2xsZXJfQnVmZmVyQ29udHJvbGxlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9jb250cm9sbGVyL2NhcC1sZXZlbC1jb250cm9sbGVyLmpzXG5mdW5jdGlvbiBjYXBfbGV2ZWxfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gY2FwX2xldmVsX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBjYXBfbGV2ZWxfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgY2FwX2xldmVsX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gY2FwX2xldmVsX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLypcbiAqIGNhcCBzdHJlYW0gbGV2ZWwgdG8gbWVkaWEgc2l6ZSBkaW1lbnNpb24gY29udHJvbGxlclxuKi9cblxuXG5cbnZhciBjYXBfbGV2ZWxfY29udHJvbGxlcl9DYXBMZXZlbENvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9FdmVudEhhbmRsZXIpIHtcbiAgY2FwX2xldmVsX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShDYXBMZXZlbENvbnRyb2xsZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIENhcExldmVsQ29udHJvbGxlcihobHMpIHtcbiAgICB2YXIgX3RoaXM7XG5cbiAgICBfdGhpcyA9IF9FdmVudEhhbmRsZXIuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlBTX0RST1BfTEVWRUxfQ0FQUElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NRURJQV9BVFRBQ0hJTkcsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUFOSUZFU1RfUEFSU0VELCBldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMU19VUERBVEVELCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9DT0RFQ1MsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfREVUQUNISU5HKSB8fCB0aGlzO1xuICAgIF90aGlzLmF1dG9MZXZlbENhcHBpbmcgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgX3RoaXMuZmlyc3RMZXZlbCA9IG51bGw7XG4gICAgX3RoaXMubGV2ZWxzID0gW107XG4gICAgX3RoaXMubWVkaWEgPSBudWxsO1xuICAgIF90aGlzLnJlc3RyaWN0ZWRMZXZlbHMgPSBbXTtcbiAgICBfdGhpcy50aW1lciA9IG51bGw7XG4gICAgX3RoaXMuY2xpZW50UmVjdCA9IG51bGw7XG4gICAgcmV0dXJuIF90aGlzO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IENhcExldmVsQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIGlmICh0aGlzLmhscy5jb25maWcuY2FwTGV2ZWxUb1BsYXllclNpemUpIHtcbiAgICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgICAgdGhpcy5jbGllbnRSZWN0ID0gbnVsbDtcbiAgICAgIHRoaXMuc3RvcENhcHBpbmcoKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uRnBzRHJvcExldmVsQ2FwcGluZyA9IGZ1bmN0aW9uIG9uRnBzRHJvcExldmVsQ2FwcGluZyhkYXRhKSB7XG4gICAgLy8gRG9uJ3QgYWRkIGEgcmVzdHJpY3RlZCBsZXZlbCBtb3JlIHRoYW4gb25jZVxuICAgIGlmIChDYXBMZXZlbENvbnRyb2xsZXIuaXNMZXZlbEFsbG93ZWQoZGF0YS5kcm9wcGVkTGV2ZWwsIHRoaXMucmVzdHJpY3RlZExldmVscykpIHtcbiAgICAgIHRoaXMucmVzdHJpY3RlZExldmVscy5wdXNoKGRhdGEuZHJvcHBlZExldmVsKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFBdHRhY2hpbmcgPSBmdW5jdGlvbiBvbk1lZGlhQXR0YWNoaW5nKGRhdGEpIHtcbiAgICB0aGlzLm1lZGlhID0gZGF0YS5tZWRpYSBpbnN0YW5jZW9mIHdpbmRvdy5IVE1MVmlkZW9FbGVtZW50ID8gZGF0YS5tZWRpYSA6IG51bGw7XG4gIH07XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RQYXJzZWQgPSBmdW5jdGlvbiBvbk1hbmlmZXN0UGFyc2VkKGRhdGEpIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHM7XG4gICAgdGhpcy5yZXN0cmljdGVkTGV2ZWxzID0gW107XG4gICAgdGhpcy5sZXZlbHMgPSBkYXRhLmxldmVscztcbiAgICB0aGlzLmZpcnN0TGV2ZWwgPSBkYXRhLmZpcnN0TGV2ZWw7XG5cbiAgICBpZiAoaGxzLmNvbmZpZy5jYXBMZXZlbFRvUGxheWVyU2l6ZSAmJiBkYXRhLnZpZGVvKSB7XG4gICAgICAvLyBTdGFydCBjYXBwaW5nIGltbWVkaWF0ZWx5IGlmIHRoZSBtYW5pZmVzdCBoYXMgc2lnbmFsZWQgdmlkZW8gY29kZWNzXG4gICAgICB0aGlzLnN0YXJ0Q2FwcGluZygpO1xuICAgIH1cbiAgfSAvLyBPbmx5IGFjdGl2YXRlIGNhcHBpbmcgd2hlbiBwbGF5aW5nIGEgdmlkZW8gc3RyZWFtOyBvdGhlcndpc2UsIG11bHRpLWJpdHJhdGUgYXVkaW8tb25seSBzdHJlYW1zIHdpbGwgYmUgcmVzdHJpY3RlZFxuICAvLyB0byB0aGUgZmlyc3QgbGV2ZWxcbiAgO1xuXG4gIF9wcm90by5vbkJ1ZmZlckNvZGVjcyA9IGZ1bmN0aW9uIG9uQnVmZmVyQ29kZWNzKGRhdGEpIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHM7XG5cbiAgICBpZiAoaGxzLmNvbmZpZy5jYXBMZXZlbFRvUGxheWVyU2l6ZSAmJiBkYXRhLnZpZGVvKSB7XG4gICAgICAvLyBJZiB0aGUgbWFuaWZlc3QgZGlkIG5vdCBzaWduYWwgYSB2aWRlbyBjb2RlYyBjYXBwaW5nIGhhcyBiZWVuIGRlZmVycmVkIHVudGlsIHdlJ3JlIGNlcnRhaW4gdmlkZW8gaXMgcHJlc2VudFxuICAgICAgdGhpcy5zdGFydENhcHBpbmcoKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTGV2ZWxzVXBkYXRlZCA9IGZ1bmN0aW9uIG9uTGV2ZWxzVXBkYXRlZChkYXRhKSB7XG4gICAgdGhpcy5sZXZlbHMgPSBkYXRhLmxldmVscztcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYURldGFjaGluZyA9IGZ1bmN0aW9uIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgdGhpcy5zdG9wQ2FwcGluZygpO1xuICB9O1xuXG4gIF9wcm90by5kZXRlY3RQbGF5ZXJTaXplID0gZnVuY3Rpb24gZGV0ZWN0UGxheWVyU2l6ZSgpIHtcbiAgICBpZiAodGhpcy5tZWRpYSkge1xuICAgICAgdmFyIGxldmVsc0xlbmd0aCA9IHRoaXMubGV2ZWxzID8gdGhpcy5sZXZlbHMubGVuZ3RoIDogMDtcblxuICAgICAgaWYgKGxldmVsc0xlbmd0aCkge1xuICAgICAgICB2YXIgaGxzID0gdGhpcy5obHM7XG4gICAgICAgIGhscy5hdXRvTGV2ZWxDYXBwaW5nID0gdGhpcy5nZXRNYXhMZXZlbChsZXZlbHNMZW5ndGggLSAxKTtcblxuICAgICAgICBpZiAoaGxzLmF1dG9MZXZlbENhcHBpbmcgPiB0aGlzLmF1dG9MZXZlbENhcHBpbmcpIHtcbiAgICAgICAgICAvLyBpZiBhdXRvIGxldmVsIGNhcHBpbmcgaGFzIGEgaGlnaGVyIHZhbHVlIGZvciB0aGUgcHJldmlvdXMgb25lLCBmbHVzaCB0aGUgYnVmZmVyIHVzaW5nIG5leHRMZXZlbFN3aXRjaFxuICAgICAgICAgIC8vIHVzdWFsbHkgaGFwcGVuIHdoZW4gdGhlIHVzZXIgZ28gdG8gdGhlIGZ1bGxzY3JlZW4gbW9kZS5cbiAgICAgICAgICBobHMuc3RyZWFtQ29udHJvbGxlci5uZXh0TGV2ZWxTd2l0Y2goKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYXV0b0xldmVsQ2FwcGluZyA9IGhscy5hdXRvTGV2ZWxDYXBwaW5nO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICAvKlxuICAqIHJldHVybnMgbGV2ZWwgc2hvdWxkIGJlIHRoZSBvbmUgd2l0aCB0aGUgZGltZW5zaW9ucyBlcXVhbCBvciBncmVhdGVyIHRoYW4gdGhlIG1lZGlhIChwbGF5ZXIpIGRpbWVuc2lvbnMgKHNvIHRoZSB2aWRlbyB3aWxsIGJlIGRvd25zY2FsZWQpXG4gICovXG4gIDtcblxuICBfcHJvdG8uZ2V0TWF4TGV2ZWwgPSBmdW5jdGlvbiBnZXRNYXhMZXZlbChjYXBMZXZlbEluZGV4KSB7XG4gICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICBpZiAoIXRoaXMubGV2ZWxzKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuXG4gICAgdmFyIHZhbGlkTGV2ZWxzID0gdGhpcy5sZXZlbHMuZmlsdGVyKGZ1bmN0aW9uIChsZXZlbCwgaW5kZXgpIHtcbiAgICAgIHJldHVybiBDYXBMZXZlbENvbnRyb2xsZXIuaXNMZXZlbEFsbG93ZWQoaW5kZXgsIF90aGlzMi5yZXN0cmljdGVkTGV2ZWxzKSAmJiBpbmRleCA8PSBjYXBMZXZlbEluZGV4O1xuICAgIH0pO1xuICAgIHRoaXMuY2xpZW50UmVjdCA9IG51bGw7XG4gICAgcmV0dXJuIENhcExldmVsQ29udHJvbGxlci5nZXRNYXhMZXZlbEJ5TWVkaWFTaXplKHZhbGlkTGV2ZWxzLCB0aGlzLm1lZGlhV2lkdGgsIHRoaXMubWVkaWFIZWlnaHQpO1xuICB9O1xuXG4gIF9wcm90by5zdGFydENhcHBpbmcgPSBmdW5jdGlvbiBzdGFydENhcHBpbmcoKSB7XG4gICAgaWYgKHRoaXMudGltZXIpIHtcbiAgICAgIC8vIERvbid0IHJlc2V0IGNhcHBpbmcgaWYgc3RhcnRlZCB0d2ljZTsgdGhpcyBjYW4gaGFwcGVuIGlmIHRoZSBtYW5pZmVzdCBzaWduYWxzIGEgdmlkZW8gY29kZWNcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLmF1dG9MZXZlbENhcHBpbmcgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgdGhpcy5obHMuZmlyc3RMZXZlbCA9IHRoaXMuZ2V0TWF4TGV2ZWwodGhpcy5maXJzdExldmVsKTtcbiAgICBjbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgIHRoaXMudGltZXIgPSBzZXRJbnRlcnZhbCh0aGlzLmRldGVjdFBsYXllclNpemUuYmluZCh0aGlzKSwgMTAwMCk7XG4gICAgdGhpcy5kZXRlY3RQbGF5ZXJTaXplKCk7XG4gIH07XG5cbiAgX3Byb3RvLnN0b3BDYXBwaW5nID0gZnVuY3Rpb24gc3RvcENhcHBpbmcoKSB7XG4gICAgdGhpcy5yZXN0cmljdGVkTGV2ZWxzID0gW107XG4gICAgdGhpcy5maXJzdExldmVsID0gbnVsbDtcbiAgICB0aGlzLmF1dG9MZXZlbENhcHBpbmcgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG5cbiAgICBpZiAodGhpcy50aW1lcikge1xuICAgICAgdGhpcy50aW1lciA9IGNsZWFySW50ZXJ2YWwodGhpcy50aW1lcik7XG4gICAgICB0aGlzLnRpbWVyID0gbnVsbDtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmdldERpbWVuc2lvbnMgPSBmdW5jdGlvbiBnZXREaW1lbnNpb25zKCkge1xuICAgIGlmICh0aGlzLmNsaWVudFJlY3QpIHtcbiAgICAgIHJldHVybiB0aGlzLmNsaWVudFJlY3Q7XG4gICAgfVxuXG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICB2YXIgYm91bmRzUmVjdCA9IHtcbiAgICAgIHdpZHRoOiAwLFxuICAgICAgaGVpZ2h0OiAwXG4gICAgfTtcblxuICAgIGlmIChtZWRpYSkge1xuICAgICAgdmFyIGNsaWVudFJlY3QgPSBtZWRpYS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgIGJvdW5kc1JlY3Qud2lkdGggPSBjbGllbnRSZWN0LndpZHRoO1xuICAgICAgYm91bmRzUmVjdC5oZWlnaHQgPSBjbGllbnRSZWN0LmhlaWdodDtcblxuICAgICAgaWYgKCFib3VuZHNSZWN0LndpZHRoICYmICFib3VuZHNSZWN0LmhlaWdodCkge1xuICAgICAgICAvLyBXaGVuIHRoZSBtZWRpYSBlbGVtZW50IGhhcyBubyB3aWR0aCBvciBoZWlnaHQgKGVxdWl2YWxlbnQgdG8gbm90IGJlaW5nIGluIHRoZSBET00pLFxuICAgICAgICAvLyB0aGVuIHVzZSBpdHMgd2lkdGggYW5kIGhlaWdodCBhdHRyaWJ1dGVzIChtZWRpYS53aWR0aCwgbWVkaWEuaGVpZ2h0KVxuICAgICAgICBib3VuZHNSZWN0LndpZHRoID0gY2xpZW50UmVjdC5yaWdodCAtIGNsaWVudFJlY3QubGVmdCB8fCBtZWRpYS53aWR0aCB8fCAwO1xuICAgICAgICBib3VuZHNSZWN0LmhlaWdodCA9IGNsaWVudFJlY3QuYm90dG9tIC0gY2xpZW50UmVjdC50b3AgfHwgbWVkaWEuaGVpZ2h0IHx8IDA7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5jbGllbnRSZWN0ID0gYm91bmRzUmVjdDtcbiAgICByZXR1cm4gYm91bmRzUmVjdDtcbiAgfTtcblxuICBDYXBMZXZlbENvbnRyb2xsZXIuaXNMZXZlbEFsbG93ZWQgPSBmdW5jdGlvbiBpc0xldmVsQWxsb3dlZChsZXZlbCwgcmVzdHJpY3RlZExldmVscykge1xuICAgIGlmIChyZXN0cmljdGVkTGV2ZWxzID09PSB2b2lkIDApIHtcbiAgICAgIHJlc3RyaWN0ZWRMZXZlbHMgPSBbXTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdHJpY3RlZExldmVscy5pbmRleE9mKGxldmVsKSA9PT0gLTE7XG4gIH07XG5cbiAgQ2FwTGV2ZWxDb250cm9sbGVyLmdldE1heExldmVsQnlNZWRpYVNpemUgPSBmdW5jdGlvbiBnZXRNYXhMZXZlbEJ5TWVkaWFTaXplKGxldmVscywgd2lkdGgsIGhlaWdodCkge1xuICAgIGlmICghbGV2ZWxzIHx8IGxldmVscyAmJiAhbGV2ZWxzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH0gLy8gTGV2ZWxzIGNhbiBoYXZlIHRoZSBzYW1lIGRpbWVuc2lvbnMgYnV0IGRpZmZlcmluZyBiYW5kd2lkdGhzIC0gc2luY2UgbGV2ZWxzIGFyZSBvcmRlcmVkLCB3ZSBjYW4gbG9vayB0byB0aGUgbmV4dFxuICAgIC8vIHRvIGRldGVybWluZSB3aGV0aGVyIHdlJ3ZlIGNob3NlbiB0aGUgZ3JlYXRlc3QgYmFuZHdpZHRoIGZvciB0aGUgbWVkaWEncyBkaW1lbnNpb25zXG5cblxuICAgIHZhciBhdEdyZWF0ZXN0QmFuZGl3ZHRoID0gZnVuY3Rpb24gYXRHcmVhdGVzdEJhbmRpd2R0aChjdXJMZXZlbCwgbmV4dExldmVsKSB7XG4gICAgICBpZiAoIW5leHRMZXZlbCkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGN1ckxldmVsLndpZHRoICE9PSBuZXh0TGV2ZWwud2lkdGggfHwgY3VyTGV2ZWwuaGVpZ2h0ICE9PSBuZXh0TGV2ZWwuaGVpZ2h0O1xuICAgIH07IC8vIElmIHdlIHJ1biB0aHJvdWdoIHRoZSBsb29wIHdpdGhvdXQgYnJlYWtpbmcsIHRoZSBtZWRpYSdzIGRpbWVuc2lvbnMgYXJlIGdyZWF0ZXIgdGhhbiBldmVyeSBsZXZlbCwgc28gZGVmYXVsdCB0b1xuICAgIC8vIHRoZSBtYXggbGV2ZWxcblxuXG4gICAgdmFyIG1heExldmVsSW5kZXggPSBsZXZlbHMubGVuZ3RoIC0gMTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGV2ZWxzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICB2YXIgbGV2ZWwgPSBsZXZlbHNbaV07XG5cbiAgICAgIGlmICgobGV2ZWwud2lkdGggPj0gd2lkdGggfHwgbGV2ZWwuaGVpZ2h0ID49IGhlaWdodCkgJiYgYXRHcmVhdGVzdEJhbmRpd2R0aChsZXZlbCwgbGV2ZWxzW2kgKyAxXSkpIHtcbiAgICAgICAgbWF4TGV2ZWxJbmRleCA9IGk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBtYXhMZXZlbEluZGV4O1xuICB9O1xuXG4gIGNhcF9sZXZlbF9jb250cm9sbGVyX2NyZWF0ZUNsYXNzKENhcExldmVsQ29udHJvbGxlciwgW3tcbiAgICBrZXk6IFwibWVkaWFXaWR0aFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuZ2V0RGltZW5zaW9ucygpLndpZHRoICogQ2FwTGV2ZWxDb250cm9sbGVyLmNvbnRlbnRTY2FsZUZhY3RvcjtcbiAgICB9XG4gIH0sIHtcbiAgICBrZXk6IFwibWVkaWFIZWlnaHRcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLmdldERpbWVuc2lvbnMoKS5oZWlnaHQgKiBDYXBMZXZlbENvbnRyb2xsZXIuY29udGVudFNjYWxlRmFjdG9yO1xuICAgIH1cbiAgfV0sIFt7XG4gICAga2V5OiBcImNvbnRlbnRTY2FsZUZhY3RvclwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIHBpeGVsUmF0aW8gPSAxO1xuXG4gICAgICB0cnkge1xuICAgICAgICBwaXhlbFJhdGlvID0gd2luZG93LmRldmljZVBpeGVsUmF0aW87XG4gICAgICB9IGNhdGNoIChlKSB7fVxuXG4gICAgICByZXR1cm4gcGl4ZWxSYXRpbztcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gQ2FwTGV2ZWxDb250cm9sbGVyO1xufShldmVudF9oYW5kbGVyKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgY2FwX2xldmVsX2NvbnRyb2xsZXIgPSAoY2FwX2xldmVsX2NvbnRyb2xsZXJfQ2FwTGV2ZWxDb250cm9sbGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvZnBzLWNvbnRyb2xsZXIuanNcbmZ1bmN0aW9uIGZwc19jb250cm9sbGVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qXG4gKiBGUFMgQ29udHJvbGxlclxuKi9cblxuXG5cbnZhciBmcHNfY29udHJvbGxlcl93aW5kb3cgPSB3aW5kb3csXG4gICAgZnBzX2NvbnRyb2xsZXJfcGVyZm9ybWFuY2UgPSBmcHNfY29udHJvbGxlcl93aW5kb3cucGVyZm9ybWFuY2U7XG5cbnZhciBmcHNfY29udHJvbGxlcl9GUFNDb250cm9sbGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChfRXZlbnRIYW5kbGVyKSB7XG4gIGZwc19jb250cm9sbGVyX2luaGVyaXRzTG9vc2UoRlBTQ29udHJvbGxlciwgX0V2ZW50SGFuZGxlcik7XG5cbiAgZnVuY3Rpb24gRlBTQ29udHJvbGxlcihobHMpIHtcbiAgICByZXR1cm4gX0V2ZW50SGFuZGxlci5jYWxsKHRoaXMsIGhscywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NRURJQV9BVFRBQ0hJTkcpIHx8IHRoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gRlBTQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmRlc3Ryb3kgPSBmdW5jdGlvbiBkZXN0cm95KCkge1xuICAgIGlmICh0aGlzLnRpbWVyKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgIH1cblxuICAgIHRoaXMuaXNWaWRlb1BsYXliYWNrUXVhbGl0eUF2YWlsYWJsZSA9IGZhbHNlO1xuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhQXR0YWNoaW5nID0gZnVuY3Rpb24gb25NZWRpYUF0dGFjaGluZyhkYXRhKSB7XG4gICAgdmFyIGNvbmZpZyA9IHRoaXMuaGxzLmNvbmZpZztcblxuICAgIGlmIChjb25maWcuY2FwTGV2ZWxPbkZQU0Ryb3ApIHtcbiAgICAgIHZhciB2aWRlbyA9IHRoaXMudmlkZW8gPSBkYXRhLm1lZGlhIGluc3RhbmNlb2Ygd2luZG93LkhUTUxWaWRlb0VsZW1lbnQgPyBkYXRhLm1lZGlhIDogbnVsbDtcblxuICAgICAgaWYgKHR5cGVvZiB2aWRlby5nZXRWaWRlb1BsYXliYWNrUXVhbGl0eSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB0aGlzLmlzVmlkZW9QbGF5YmFja1F1YWxpdHlBdmFpbGFibGUgPSB0cnVlO1xuICAgICAgfVxuXG4gICAgICBjbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgICAgdGhpcy50aW1lciA9IHNldEludGVydmFsKHRoaXMuY2hlY2tGUFNJbnRlcnZhbC5iaW5kKHRoaXMpLCBjb25maWcuZnBzRHJvcHBlZE1vbml0b3JpbmdQZXJpb2QpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uY2hlY2tGUFMgPSBmdW5jdGlvbiBjaGVja0ZQUyh2aWRlbywgZGVjb2RlZEZyYW1lcywgZHJvcHBlZEZyYW1lcykge1xuICAgIHZhciBjdXJyZW50VGltZSA9IGZwc19jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgaWYgKGRlY29kZWRGcmFtZXMpIHtcbiAgICAgIGlmICh0aGlzLmxhc3RUaW1lKSB7XG4gICAgICAgIHZhciBjdXJyZW50UGVyaW9kID0gY3VycmVudFRpbWUgLSB0aGlzLmxhc3RUaW1lLFxuICAgICAgICAgICAgY3VycmVudERyb3BwZWQgPSBkcm9wcGVkRnJhbWVzIC0gdGhpcy5sYXN0RHJvcHBlZEZyYW1lcyxcbiAgICAgICAgICAgIGN1cnJlbnREZWNvZGVkID0gZGVjb2RlZEZyYW1lcyAtIHRoaXMubGFzdERlY29kZWRGcmFtZXMsXG4gICAgICAgICAgICBkcm9wcGVkRlBTID0gMTAwMCAqIGN1cnJlbnREcm9wcGVkIC8gY3VycmVudFBlcmlvZCxcbiAgICAgICAgICAgIGhscyA9IHRoaXMuaGxzO1xuICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkZQU19EUk9QLCB7XG4gICAgICAgICAgY3VycmVudERyb3BwZWQ6IGN1cnJlbnREcm9wcGVkLFxuICAgICAgICAgIGN1cnJlbnREZWNvZGVkOiBjdXJyZW50RGVjb2RlZCxcbiAgICAgICAgICB0b3RhbERyb3BwZWRGcmFtZXM6IGRyb3BwZWRGcmFtZXNcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKGRyb3BwZWRGUFMgPiAwKSB7XG4gICAgICAgICAgLy8gbG9nZ2VyLmxvZygnY2hlY2tGUFMgOiBkcm9wcGVkRlBTL2RlY29kZWRGUFM6JyArIGRyb3BwZWRGUFMvKDEwMDAgKiBjdXJyZW50RGVjb2RlZCAvIGN1cnJlbnRQZXJpb2QpKTtcbiAgICAgICAgICBpZiAoY3VycmVudERyb3BwZWQgPiBobHMuY29uZmlnLmZwc0Ryb3BwZWRNb25pdG9yaW5nVGhyZXNob2xkICogY3VycmVudERlY29kZWQpIHtcbiAgICAgICAgICAgIHZhciBjdXJyZW50TGV2ZWwgPSBobHMuY3VycmVudExldmVsO1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ2Ryb3AgRlBTIHJhdGlvIGdyZWF0ZXIgdGhhbiBtYXggYWxsb3dlZCB2YWx1ZSBmb3IgY3VycmVudExldmVsOiAnICsgY3VycmVudExldmVsKTtcblxuICAgICAgICAgICAgaWYgKGN1cnJlbnRMZXZlbCA+IDAgJiYgKGhscy5hdXRvTGV2ZWxDYXBwaW5nID09PSAtMSB8fCBobHMuYXV0b0xldmVsQ2FwcGluZyA+PSBjdXJyZW50TGV2ZWwpKSB7XG4gICAgICAgICAgICAgIGN1cnJlbnRMZXZlbCA9IGN1cnJlbnRMZXZlbCAtIDE7XG4gICAgICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlBTX0RST1BfTEVWRUxfQ0FQUElORywge1xuICAgICAgICAgICAgICAgIGxldmVsOiBjdXJyZW50TGV2ZWwsXG4gICAgICAgICAgICAgICAgZHJvcHBlZExldmVsOiBobHMuY3VycmVudExldmVsXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICBobHMuYXV0b0xldmVsQ2FwcGluZyA9IGN1cnJlbnRMZXZlbDtcbiAgICAgICAgICAgICAgaGxzLnN0cmVhbUNvbnRyb2xsZXIubmV4dExldmVsU3dpdGNoKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHRoaXMubGFzdFRpbWUgPSBjdXJyZW50VGltZTtcbiAgICAgIHRoaXMubGFzdERyb3BwZWRGcmFtZXMgPSBkcm9wcGVkRnJhbWVzO1xuICAgICAgdGhpcy5sYXN0RGVjb2RlZEZyYW1lcyA9IGRlY29kZWRGcmFtZXM7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5jaGVja0ZQU0ludGVydmFsID0gZnVuY3Rpb24gY2hlY2tGUFNJbnRlcnZhbCgpIHtcbiAgICB2YXIgdmlkZW8gPSB0aGlzLnZpZGVvO1xuXG4gICAgaWYgKHZpZGVvKSB7XG4gICAgICBpZiAodGhpcy5pc1ZpZGVvUGxheWJhY2tRdWFsaXR5QXZhaWxhYmxlKSB7XG4gICAgICAgIHZhciB2aWRlb1BsYXliYWNrUXVhbGl0eSA9IHZpZGVvLmdldFZpZGVvUGxheWJhY2tRdWFsaXR5KCk7XG4gICAgICAgIHRoaXMuY2hlY2tGUFModmlkZW8sIHZpZGVvUGxheWJhY2tRdWFsaXR5LnRvdGFsVmlkZW9GcmFtZXMsIHZpZGVvUGxheWJhY2tRdWFsaXR5LmRyb3BwZWRWaWRlb0ZyYW1lcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmNoZWNrRlBTKHZpZGVvLCB2aWRlby53ZWJraXREZWNvZGVkRnJhbWVDb3VudCwgdmlkZW8ud2Via2l0RHJvcHBlZEZyYW1lQ291bnQpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICByZXR1cm4gRlBTQ29udHJvbGxlcjtcbn0oZXZlbnRfaGFuZGxlcik7XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIGZwc19jb250cm9sbGVyID0gKGZwc19jb250cm9sbGVyX0ZQU0NvbnRyb2xsZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMveGhyLWxvYWRlci5qc1xuLyoqXG4gKiBYSFIgYmFzZWQgbG9nZ2VyXG4qL1xuXG5cbnZhciB4aHJfbG9hZGVyX1hockxvYWRlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIFhockxvYWRlcihjb25maWcpIHtcbiAgICBpZiAoY29uZmlnICYmIGNvbmZpZy54aHJTZXR1cCkge1xuICAgICAgdGhpcy54aHJTZXR1cCA9IGNvbmZpZy54aHJTZXR1cDtcbiAgICB9XG4gIH1cblxuICB2YXIgX3Byb3RvID0gWGhyTG9hZGVyLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gICAgdGhpcy5hYm9ydCgpO1xuICAgIHRoaXMubG9hZGVyID0gbnVsbDtcbiAgfTtcblxuICBfcHJvdG8uYWJvcnQgPSBmdW5jdGlvbiBhYm9ydCgpIHtcbiAgICB2YXIgbG9hZGVyID0gdGhpcy5sb2FkZXI7XG5cbiAgICBpZiAobG9hZGVyICYmIGxvYWRlci5yZWFkeVN0YXRlICE9PSA0KSB7XG4gICAgICB0aGlzLnN0YXRzLmFib3J0ZWQgPSB0cnVlO1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgd2luZG93LmNsZWFyVGltZW91dCh0aGlzLnJlcXVlc3RUaW1lb3V0KTtcbiAgICB0aGlzLnJlcXVlc3RUaW1lb3V0ID0gbnVsbDtcbiAgICB3aW5kb3cuY2xlYXJUaW1lb3V0KHRoaXMucmV0cnlUaW1lb3V0KTtcbiAgICB0aGlzLnJldHJ5VGltZW91dCA9IG51bGw7XG4gIH07XG5cbiAgX3Byb3RvLmxvYWQgPSBmdW5jdGlvbiBsb2FkKGNvbnRleHQsIGNvbmZpZywgY2FsbGJhY2tzKSB7XG4gICAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLmNhbGxiYWNrcyA9IGNhbGxiYWNrcztcbiAgICB0aGlzLnN0YXRzID0ge1xuICAgICAgdHJlcXVlc3Q6IHdpbmRvdy5wZXJmb3JtYW5jZS5ub3coKSxcbiAgICAgIHJldHJ5OiAwXG4gICAgfTtcbiAgICB0aGlzLnJldHJ5RGVsYXkgPSBjb25maWcucmV0cnlEZWxheTtcbiAgICB0aGlzLmxvYWRJbnRlcm5hbCgpO1xuICB9O1xuXG4gIF9wcm90by5sb2FkSW50ZXJuYWwgPSBmdW5jdGlvbiBsb2FkSW50ZXJuYWwoKSB7XG4gICAgdmFyIHhocixcbiAgICAgICAgY29udGV4dCA9IHRoaXMuY29udGV4dDtcbiAgICB4aHIgPSB0aGlzLmxvYWRlciA9IG5ldyB3aW5kb3cuWE1MSHR0cFJlcXVlc3QoKTtcbiAgICB2YXIgc3RhdHMgPSB0aGlzLnN0YXRzO1xuICAgIHN0YXRzLnRmaXJzdCA9IDA7XG4gICAgc3RhdHMubG9hZGVkID0gMDtcbiAgICB2YXIgeGhyU2V0dXAgPSB0aGlzLnhoclNldHVwO1xuXG4gICAgdHJ5IHtcbiAgICAgIGlmICh4aHJTZXR1cCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHhoclNldHVwKHhociwgY29udGV4dC51cmwpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgLy8gZml4IHhoclNldHVwOiAoeGhyLCB1cmwpID0+IHt4aHIuc2V0UmVxdWVzdEhlYWRlcihcIkNvbnRlbnQtTGFuZ3VhZ2VcIiwgXCJ0ZXN0XCIpO31cbiAgICAgICAgICAvLyBub3Qgd29ya2luZywgYXMgeGhyLnNldFJlcXVlc3RIZWFkZXIgZXhwZWN0cyB4aHIucmVhZHlTdGF0ZSA9PT0gT1BFTlxuICAgICAgICAgIHhoci5vcGVuKCdHRVQnLCBjb250ZXh0LnVybCwgdHJ1ZSk7XG4gICAgICAgICAgeGhyU2V0dXAoeGhyLCBjb250ZXh0LnVybCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKCF4aHIucmVhZHlTdGF0ZSkge1xuICAgICAgICB4aHIub3BlbignR0VUJywgY29udGV4dC51cmwsIHRydWUpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIC8vIElFMTEgdGhyb3dzIGFuIGV4Y2VwdGlvbiBvbiB4aHIub3BlbiBpZiBhdHRlbXB0aW5nIHRvIGFjY2VzcyBhbiBIVFRQIHJlc291cmNlIG92ZXIgSFRUUFNcbiAgICAgIHRoaXMuY2FsbGJhY2tzLm9uRXJyb3Ioe1xuICAgICAgICBjb2RlOiB4aHIuc3RhdHVzLFxuICAgICAgICB0ZXh0OiBlLm1lc3NhZ2VcbiAgICAgIH0sIGNvbnRleHQsIHhocik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGNvbnRleHQucmFuZ2VFbmQpIHtcbiAgICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKCdSYW5nZScsICdieXRlcz0nICsgY29udGV4dC5yYW5nZVN0YXJ0ICsgJy0nICsgKGNvbnRleHQucmFuZ2VFbmQgLSAxKSk7XG4gICAgfVxuXG4gICAgeGhyLm9ucmVhZHlzdGF0ZWNoYW5nZSA9IHRoaXMucmVhZHlzdGF0ZWNoYW5nZS5iaW5kKHRoaXMpO1xuICAgIHhoci5vbnByb2dyZXNzID0gdGhpcy5sb2FkcHJvZ3Jlc3MuYmluZCh0aGlzKTtcbiAgICB4aHIucmVzcG9uc2VUeXBlID0gY29udGV4dC5yZXNwb25zZVR5cGU7IC8vIHNldHVwIHRpbWVvdXQgYmVmb3JlIHdlIHBlcmZvcm0gcmVxdWVzdFxuXG4gICAgdGhpcy5yZXF1ZXN0VGltZW91dCA9IHdpbmRvdy5zZXRUaW1lb3V0KHRoaXMubG9hZHRpbWVvdXQuYmluZCh0aGlzKSwgdGhpcy5jb25maWcudGltZW91dCk7XG4gICAgeGhyLnNlbmQoKTtcbiAgfTtcblxuICBfcHJvdG8ucmVhZHlzdGF0ZWNoYW5nZSA9IGZ1bmN0aW9uIHJlYWR5c3RhdGVjaGFuZ2UoZXZlbnQpIHtcbiAgICB2YXIgeGhyID0gZXZlbnQuY3VycmVudFRhcmdldCxcbiAgICAgICAgcmVhZHlTdGF0ZSA9IHhoci5yZWFkeVN0YXRlLFxuICAgICAgICBzdGF0cyA9IHRoaXMuc3RhdHMsXG4gICAgICAgIGNvbnRleHQgPSB0aGlzLmNvbnRleHQsXG4gICAgICAgIGNvbmZpZyA9IHRoaXMuY29uZmlnOyAvLyBkb24ndCBwcm9jZWVkIGlmIHhociBoYXMgYmVlbiBhYm9ydGVkXG5cbiAgICBpZiAoc3RhdHMuYWJvcnRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gPj0gSEVBREVSU19SRUNFSVZFRFxuXG5cbiAgICBpZiAocmVhZHlTdGF0ZSA+PSAyKSB7XG4gICAgICAvLyBjbGVhciB4aHIgdGltZW91dCBhbmQgcmVhcm0gaXQgaWYgcmVhZHlTdGF0ZSBsZXNzIHRoYW4gNFxuICAgICAgd2luZG93LmNsZWFyVGltZW91dCh0aGlzLnJlcXVlc3RUaW1lb3V0KTtcblxuICAgICAgaWYgKHN0YXRzLnRmaXJzdCA9PT0gMCkge1xuICAgICAgICBzdGF0cy50Zmlyc3QgPSBNYXRoLm1heCh3aW5kb3cucGVyZm9ybWFuY2Uubm93KCksIHN0YXRzLnRyZXF1ZXN0KTtcbiAgICAgIH1cblxuICAgICAgaWYgKHJlYWR5U3RhdGUgPT09IDQpIHtcbiAgICAgICAgdmFyIHN0YXR1cyA9IHhoci5zdGF0dXM7IC8vIGh0dHAgc3RhdHVzIGJldHdlZW4gMjAwIHRvIDI5OSBhcmUgYWxsIHN1Y2Nlc3NmdWxcblxuICAgICAgICBpZiAoc3RhdHVzID49IDIwMCAmJiBzdGF0dXMgPCAzMDApIHtcbiAgICAgICAgICBzdGF0cy50bG9hZCA9IE1hdGgubWF4KHN0YXRzLnRmaXJzdCwgd2luZG93LnBlcmZvcm1hbmNlLm5vdygpKTtcbiAgICAgICAgICB2YXIgZGF0YSwgbGVuO1xuXG4gICAgICAgICAgaWYgKGNvbnRleHQucmVzcG9uc2VUeXBlID09PSAnYXJyYXlidWZmZXInKSB7XG4gICAgICAgICAgICBkYXRhID0geGhyLnJlc3BvbnNlO1xuICAgICAgICAgICAgbGVuID0gZGF0YS5ieXRlTGVuZ3RoO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkYXRhID0geGhyLnJlc3BvbnNlVGV4dDtcbiAgICAgICAgICAgIGxlbiA9IGRhdGEubGVuZ3RoO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHN0YXRzLmxvYWRlZCA9IHN0YXRzLnRvdGFsID0gbGVuO1xuICAgICAgICAgIHZhciByZXNwb25zZSA9IHtcbiAgICAgICAgICAgIHVybDogeGhyLnJlc3BvbnNlVVJMLFxuICAgICAgICAgICAgZGF0YTogZGF0YVxuICAgICAgICAgIH07XG4gICAgICAgICAgdGhpcy5jYWxsYmFja3Mub25TdWNjZXNzKHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgeGhyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBpZiBtYXggbmIgb2YgcmV0cmllcyByZWFjaGVkIG9yIGlmIGh0dHAgc3RhdHVzIGJldHdlZW4gNDAwIGFuZCA0OTkgKHN1Y2ggZXJyb3IgY2Fubm90IGJlIHJlY292ZXJlZCwgcmV0cnlpbmcgaXMgdXNlbGVzcyksIHJldHVybiBlcnJvclxuICAgICAgICAgIGlmIChzdGF0cy5yZXRyeSA+PSBjb25maWcubWF4UmV0cnkgfHwgc3RhdHVzID49IDQwMCAmJiBzdGF0dXMgPCA0OTkpIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcihzdGF0dXMgKyBcIiB3aGlsZSBsb2FkaW5nIFwiICsgY29udGV4dC51cmwpO1xuICAgICAgICAgICAgdGhpcy5jYWxsYmFja3Mub25FcnJvcih7XG4gICAgICAgICAgICAgIGNvZGU6IHN0YXR1cyxcbiAgICAgICAgICAgICAgdGV4dDogeGhyLnN0YXR1c1RleHRcbiAgICAgICAgICAgIH0sIGNvbnRleHQsIHhocik7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIHJldHJ5XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihzdGF0dXMgKyBcIiB3aGlsZSBsb2FkaW5nIFwiICsgY29udGV4dC51cmwgKyBcIiwgcmV0cnlpbmcgaW4gXCIgKyB0aGlzLnJldHJ5RGVsYXkgKyBcIi4uLlwiKTsgLy8gYWJvcnRzIGFuZCByZXNldHMgaW50ZXJuYWwgc3RhdGVcblxuICAgICAgICAgICAgdGhpcy5kZXN0cm95KCk7IC8vIHNjaGVkdWxlIHJldHJ5XG5cbiAgICAgICAgICAgIHRoaXMucmV0cnlUaW1lb3V0ID0gd2luZG93LnNldFRpbWVvdXQodGhpcy5sb2FkSW50ZXJuYWwuYmluZCh0aGlzKSwgdGhpcy5yZXRyeURlbGF5KTsgLy8gc2V0IGV4cG9uZW50aWFsIGJhY2tvZmZcblxuICAgICAgICAgICAgdGhpcy5yZXRyeURlbGF5ID0gTWF0aC5taW4oMiAqIHRoaXMucmV0cnlEZWxheSwgY29uZmlnLm1heFJldHJ5RGVsYXkpO1xuICAgICAgICAgICAgc3RhdHMucmV0cnkrKztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIHJlYWR5U3RhdGUgPj0gMiBBTkQgcmVhZHlTdGF0ZSAhPT00IChyZWFkeVN0YXRlID0gSEVBREVSU19SRUNFSVZFRCB8fCBMT0FESU5HKSByZWFybSB0aW1lb3V0IGFzIHhociBub3QgZmluaXNoZWQgeWV0XG4gICAgICAgIHRoaXMucmVxdWVzdFRpbWVvdXQgPSB3aW5kb3cuc2V0VGltZW91dCh0aGlzLmxvYWR0aW1lb3V0LmJpbmQodGhpcyksIGNvbmZpZy50aW1lb3V0KTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmxvYWR0aW1lb3V0ID0gZnVuY3Rpb24gbG9hZHRpbWVvdXQoKSB7XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJ0aW1lb3V0IHdoaWxlIGxvYWRpbmcgXCIgKyB0aGlzLmNvbnRleHQudXJsKTtcbiAgICB0aGlzLmNhbGxiYWNrcy5vblRpbWVvdXQodGhpcy5zdGF0cywgdGhpcy5jb250ZXh0LCBudWxsKTtcbiAgfTtcblxuICBfcHJvdG8ubG9hZHByb2dyZXNzID0gZnVuY3Rpb24gbG9hZHByb2dyZXNzKGV2ZW50KSB7XG4gICAgdmFyIHhociA9IGV2ZW50LmN1cnJlbnRUYXJnZXQsXG4gICAgICAgIHN0YXRzID0gdGhpcy5zdGF0cztcbiAgICBzdGF0cy5sb2FkZWQgPSBldmVudC5sb2FkZWQ7XG5cbiAgICBpZiAoZXZlbnQubGVuZ3RoQ29tcHV0YWJsZSkge1xuICAgICAgc3RhdHMudG90YWwgPSBldmVudC50b3RhbDtcbiAgICB9XG5cbiAgICB2YXIgb25Qcm9ncmVzcyA9IHRoaXMuY2FsbGJhY2tzLm9uUHJvZ3Jlc3M7XG5cbiAgICBpZiAob25Qcm9ncmVzcykge1xuICAgICAgLy8gdGhpcmQgYXJnIGlzIHRvIHByb3ZpZGUgb24gcHJvZ3Jlc3MgZGF0YVxuICAgICAgb25Qcm9ncmVzcyhzdGF0cywgdGhpcy5jb250ZXh0LCBudWxsLCB4aHIpO1xuICAgIH1cbiAgfTtcblxuICByZXR1cm4gWGhyTG9hZGVyO1xufSgpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciB4aHJfbG9hZGVyID0gKHhocl9sb2FkZXJfWGhyTG9hZGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvYXVkaW8tdHJhY2stY29udHJvbGxlci5qc1xuZnVuY3Rpb24gYXVkaW9fdHJhY2tfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gYXVkaW9fdHJhY2tfY29udHJvbGxlcl9jcmVhdGVDbGFzcyhDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGF1ZGlvX3RyYWNrX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGF1ZGlvX3RyYWNrX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gYXVkaW9fdHJhY2tfY29udHJvbGxlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG5cblxuXG5cbi8qKlxuICogQGNsYXNzIEF1ZGlvVHJhY2tDb250cm9sbGVyXG4gKiBAaW1wbGVtZW50cyB7RXZlbnRIYW5kbGVyfVxuICpcbiAqIEhhbmRsZXMgbWFpbiBtYW5pZmVzdCBhbmQgYXVkaW8tdHJhY2sgbWV0YWRhdGEgbG9hZGVkLFxuICogb3ducyBhbmQgZXhwb3NlcyB0aGUgc2VsZWN0YWJsZSBhdWRpby10cmFja3MgZGF0YS1tb2RlbHMuXG4gKlxuICogRXhwb3NlcyBpbnRlcm5hbCBpbnRlcmZhY2UgdG8gc2VsZWN0IGF2YWlsYWJsZSBhdWRpby10cmFja3MuXG4gKlxuICogSGFuZGxlcyBlcnJvcnMgb24gbG9hZGluZyBhdWRpby10cmFjayBwbGF5bGlzdHMuIE1hbmFnZXMgZmFsbGJhY2sgbWVjaGFuaXNtXG4gKiB3aXRoIHJlZHVuZGFudHMgdHJhY2tzIChncm91cC1JRHMpLlxuICpcbiAqIEhhbmRsZXMgbGV2ZWwtbG9hZGluZyBhbmQgZ3JvdXAtSUQgc3dpdGNoZXMgZm9yIHZpZGVvIChmYWxsYmFjayBvbiB2aWRlbyBsZXZlbHMpLFxuICogYW5kIGV2ZW50dWFsbHkgYWRhcHRzIHRoZSBhdWRpby10cmFjayBncm91cC1JRCB0byBtYXRjaC5cbiAqXG4gKiBAZmlyZXMgQVVESU9fVFJBQ0tfTE9BRElOR1xuICogQGZpcmVzIEFVRElPX1RSQUNLX1NXSVRDSElOR1xuICogQGZpcmVzIEFVRElPX1RSQUNLU19VUERBVEVEXG4gKiBAZmlyZXMgRVJST1JcbiAqXG4gKi9cblxudmFyIGF1ZGlvX3RyYWNrX2NvbnRyb2xsZXJfQXVkaW9UcmFja0NvbnRyb2xsZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9UYXNrTG9vcCkge1xuICBhdWRpb190cmFja19jb250cm9sbGVyX2luaGVyaXRzTG9vc2UoQXVkaW9UcmFja0NvbnRyb2xsZXIsIF9UYXNrTG9vcCk7XG5cbiAgZnVuY3Rpb24gQXVkaW9UcmFja0NvbnRyb2xsZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfVGFza0xvb3AuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUFOSUZFU1RfTE9BRElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9QQVJTRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfTE9BREVELCBldmVudHNbXCJkZWZhdWx0XCJdLkFVRElPX1RSQUNLX1NXSVRDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLkxFVkVMX0xPQURFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUikgfHwgdGhpcztcbiAgICAvKipcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEN1cnJlbnRseSBzZWxlY3RlZCBpbmRleCBpbiBgdHJhY2tzYFxuICAgICAqIEBtZW1iZXIge251bWJlcn0gdHJhY2tJZFxuICAgICAqL1xuXG4gICAgX3RoaXMuX3RyYWNrSWQgPSAtMTtcbiAgICAvKipcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIElmIHNob3VsZCBzZWxlY3QgdHJhY2tzIGFjY29yZGluZyB0byBkZWZhdWx0IHRyYWNrIGF0dHJpYnV0ZVxuICAgICAqIEBtZW1iZXIge2Jvb2xlYW59IF9zZWxlY3REZWZhdWx0VHJhY2tcbiAgICAgKi9cblxuICAgIF90aGlzLl9zZWxlY3REZWZhdWx0VHJhY2sgPSB0cnVlO1xuICAgIC8qKlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBBbGwgdHJhY2tzIGF2YWlsYWJsZVxuICAgICAqIEBtZW1iZXIge0F1ZGlvVHJhY2tbXX1cbiAgICAgKi9cblxuICAgIF90aGlzLnRyYWNrcyA9IFtdO1xuICAgIC8qKlxuICAgICAqIEBwdWJsaWNcbiAgICAgKiBMaXN0IG9mIGJsYWNrbGlzdGVkIGF1ZGlvIHRyYWNrIElEcyAodGhhdCBoYXZlIGNhdXNlZCBmYWlsdXJlKVxuICAgICAqIEBtZW1iZXIge251bWJlcltdfVxuICAgICAqL1xuXG4gICAgX3RoaXMudHJhY2tJZEJsYWNrbGlzdCA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gICAgLyoqXG4gICAgICogQHB1YmxpY1xuICAgICAqIFRoZSBjdXJyZW50bHkgcnVubmluZyBncm91cCBJRCBmb3IgYXVkaW9cbiAgICAgKiAod2UgZ3JhYiB0aGlzIG9uIG1hbmlmZXN0LXBhcnNlZCBhbmQgbmV3IGxldmVsLWxvYWRlZClcbiAgICAgKiBAbWVtYmVyIHtzdHJpbmd9XG4gICAgICovXG5cbiAgICBfdGhpcy5hdWRpb0dyb3VwSWQgPSBudWxsO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuICAvKipcbiAgICogUmVzZXQgYXVkaW8gdHJhY2tzIG9uIG5ldyBtYW5pZmVzdCBsb2FkaW5nLlxuICAgKi9cblxuXG4gIHZhciBfcHJvdG8gPSBBdWRpb1RyYWNrQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RMb2FkaW5nID0gZnVuY3Rpb24gb25NYW5pZmVzdExvYWRpbmcoKSB7XG4gICAgdGhpcy50cmFja3MgPSBbXTtcbiAgICB0aGlzLl90cmFja0lkID0gLTE7XG4gICAgdGhpcy5fc2VsZWN0RGVmYXVsdFRyYWNrID0gdHJ1ZTtcbiAgfVxuICAvKipcbiAgICogU3RvcmUgdHJhY2tzIGRhdGEgZnJvbSBtYW5pZmVzdCBwYXJzZWQgZGF0YS5cbiAgICpcbiAgICogVHJpZ2dlciBBVURJT19UUkFDS1NfVVBEQVRFRCBldmVudC5cbiAgICpcbiAgICogQHBhcmFtIHsqfSBkYXRhXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RQYXJzZWQgPSBmdW5jdGlvbiBvbk1hbmlmZXN0UGFyc2VkKGRhdGEpIHtcbiAgICB2YXIgdHJhY2tzID0gdGhpcy50cmFja3MgPSBkYXRhLmF1ZGlvVHJhY2tzIHx8IFtdO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5BVURJT19UUkFDS1NfVVBEQVRFRCwge1xuICAgICAgYXVkaW9UcmFja3M6IHRyYWNrc1xuICAgIH0pO1xuXG4gICAgdGhpcy5fc2VsZWN0QXVkaW9Hcm91cCh0aGlzLmhscy5uZXh0TG9hZExldmVsKTtcbiAgfVxuICAvKipcbiAgICogU3RvcmUgdHJhY2sgZGV0YWlscyBvZiBsb2FkZWQgdHJhY2sgaW4gb3VyIGRhdGEtbW9kZWwuXG4gICAqXG4gICAqIFNldC11cCBtZXRhZGF0YSB1cGRhdGUgaW50ZXJ2YWwgdGFzayBmb3IgbGl2ZS1tb2RlIHN0cmVhbXMuXG4gICAqXG4gICAqIEBwYXJhbSB7Kn0gZGF0YVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5vbkF1ZGlvVHJhY2tMb2FkZWQgPSBmdW5jdGlvbiBvbkF1ZGlvVHJhY2tMb2FkZWQoZGF0YSkge1xuICAgIGlmIChkYXRhLmlkID49IHRoaXMudHJhY2tzLmxlbmd0aCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ0ludmFsaWQgYXVkaW8gdHJhY2sgaWQ6JywgZGF0YS5pZCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImF1ZGlvVHJhY2sgXCIgKyBkYXRhLmlkICsgXCIgbG9hZGVkXCIpO1xuICAgIHRoaXMudHJhY2tzW2RhdGEuaWRdLmRldGFpbHMgPSBkYXRhLmRldGFpbHM7IC8vIGNoZWNrIGlmIGN1cnJlbnQgcGxheWxpc3QgaXMgYSBsaXZlIHBsYXlsaXN0XG4gICAgLy8gYW5kIGlmIHdlIGhhdmUgYWxyZWFkeSBvdXIgcmVsb2FkIGludGVydmFsIHNldHVwXG5cbiAgICBpZiAoZGF0YS5kZXRhaWxzLmxpdmUgJiYgIXRoaXMuaGFzSW50ZXJ2YWwoKSkge1xuICAgICAgLy8gaWYgbGl2ZSBwbGF5bGlzdCB3ZSB3aWxsIGhhdmUgdG8gcmVsb2FkIGl0IHBlcmlvZGljYWxseVxuICAgICAgLy8gc2V0IHJlbG9hZCBwZXJpb2QgdG8gcGxheWxpc3QgdGFyZ2V0IGR1cmF0aW9uXG4gICAgICB2YXIgdXBkYXRlUGVyaW9kTXMgPSBkYXRhLmRldGFpbHMudGFyZ2V0ZHVyYXRpb24gKiAxMDAwO1xuICAgICAgdGhpcy5zZXRJbnRlcnZhbCh1cGRhdGVQZXJpb2RNcyk7XG4gICAgfVxuXG4gICAgaWYgKCFkYXRhLmRldGFpbHMubGl2ZSAmJiB0aGlzLmhhc0ludGVydmFsKCkpIHtcbiAgICAgIC8vIHBsYXlsaXN0IGlzIG5vdCBsaXZlIGFuZCB0aW1lciBpcyBzY2hlZHVsZWQ6IGNhbmNlbCBpdFxuICAgICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIGludGVybmFsIGdyb3VwIElEIHRvIGFueSBhdWRpby10cmFjayB3ZSBtYXkgaGF2ZSBzZXQgbWFudWFsbHlcbiAgICogb3IgYmVjYXVzZSBvZiBhIGZhaWx1cmUtaGFuZGxpbmcgZmFsbGJhY2suXG4gICAqXG4gICAqIFF1YWxpdHktbGV2ZWxzIHNob3VsZCB1cGRhdGUgdG8gdGhhdCBncm91cCBJRCBpbiB0aGlzIGNhc2UuXG4gICAqXG4gICAqIEBwYXJhbSB7Kn0gZGF0YVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5vbkF1ZGlvVHJhY2tTd2l0Y2hlZCA9IGZ1bmN0aW9uIG9uQXVkaW9UcmFja1N3aXRjaGVkKGRhdGEpIHtcbiAgICB2YXIgYXVkaW9Hcm91cElkID0gdGhpcy50cmFja3NbZGF0YS5pZF0uZ3JvdXBJZDtcblxuICAgIGlmIChhdWRpb0dyb3VwSWQgJiYgdGhpcy5hdWRpb0dyb3VwSWQgIT09IGF1ZGlvR3JvdXBJZCkge1xuICAgICAgdGhpcy5hdWRpb0dyb3VwSWQgPSBhdWRpb0dyb3VwSWQ7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBXaGVuIGEgbGV2ZWwgZ2V0cyBsb2FkZWQsIGlmIGl0IGhhcyByZWR1bmRhbnQgYXVkaW9Hcm91cElkcyAoaW4gdGhlIHNhbWUgb3JkaW5hbGl0eSBhcyBpdCdzIHJlZHVuZGFudCBVUkxzKVxuICAgKiB3ZSBhcmUgc2V0dGluZyBvdXIgYXVkaW8tZ3JvdXAgSUQgaW50ZXJuYWxseSB0byB0aGUgb25lIHNldCwgaWYgaXQgaXMgZGlmZmVyZW50IGZyb20gdGhlIGdyb3VwIElEIGN1cnJlbnRseSBzZXQuXG4gICAqXG4gICAqIElmIGdyb3VwLUlEIGdvdCB1cGRhdGUsIHdlIHJlLXNlbGVjdCB0aGUgYXBwcm9wcmlhdGUgYXVkaW8tdHJhY2sgd2l0aCB0aGlzIGdyb3VwLUlEIG1hdGNoaW5nIHRoZSBjdXJyZW50bHlcbiAgICogc2VsZWN0ZWQgb25lIChiYXNlZCBvbiBOQU1FIHByb3BlcnR5KS5cbiAgICpcbiAgICogQHBhcmFtIHsqfSBkYXRhXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLm9uTGV2ZWxMb2FkZWQgPSBmdW5jdGlvbiBvbkxldmVsTG9hZGVkKGRhdGEpIHtcbiAgICB0aGlzLl9zZWxlY3RBdWRpb0dyb3VwKGRhdGEubGV2ZWwpO1xuICB9XG4gIC8qKlxuICAgKiBIYW5kbGUgbmV0d29yayBlcnJvcnMgbG9hZGluZyBhdWRpbyB0cmFjayBtYW5pZmVzdHNcbiAgICogYW5kIGFsc28gcGF1c2luZyBvbiBhbnkgbmV0d29rIGVycm9ycy5cbiAgICpcbiAgICogQHBhcmFtIHtFcnJvckV2ZW50RGF0YX0gZGF0YVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5vbkVycm9yID0gZnVuY3Rpb24gb25FcnJvcihkYXRhKSB7XG4gICAgLy8gT25seSBoYW5kbGUgbmV0d29yayBlcnJvcnNcbiAgICBpZiAoZGF0YS50eXBlICE9PSBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk5FVFdPUktfRVJST1IpIHtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIElmIGZhdGFsIG5ldHdvcmsgZXJyb3IsIGNhbmNlbCB1cGRhdGUgdGFza1xuXG5cbiAgICBpZiAoZGF0YS5mYXRhbCkge1xuICAgICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgfSAvLyBJZiBub3QgYW4gYXVkaW8tdHJhY2sgbG9hZGluZyBlcnJvciBkb24ndCBoYW5kbGUgZnVydGhlclxuXG5cbiAgICBpZiAoZGF0YS5kZXRhaWxzICE9PSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQVVESU9fVFJBQ0tfTE9BRF9FUlJPUikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdOZXR3b3JrIGZhaWx1cmUgb24gYXVkaW8tdHJhY2sgaWQ6JywgZGF0YS5jb250ZXh0LmlkKTtcblxuICAgIHRoaXMuX2hhbmRsZUxvYWRFcnJvcigpO1xuICB9XG4gIC8qKlxuICAgKiBAdHlwZSB7QXVkaW9UcmFja1tdfSBBdWRpby10cmFjayBsaXN0IHdlIG93blxuICAgKi9cbiAgO1xuXG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge251bWJlcn0gbmV3SWRcbiAgICovXG4gIF9wcm90by5fc2V0QXVkaW9UcmFjayA9IGZ1bmN0aW9uIF9zZXRBdWRpb1RyYWNrKG5ld0lkKSB7XG4gICAgLy8gbm9vcCBvbiBzYW1lIGF1ZGlvIHRyYWNrIGlkIGFzIGFscmVhZHkgc2V0XG4gICAgaWYgKHRoaXMuX3RyYWNrSWQgPT09IG5ld0lkICYmIHRoaXMudHJhY2tzW3RoaXMuX3RyYWNrSWRdLmRldGFpbHMpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5kZWJ1ZygnU2FtZSBpZCBhcyBjdXJyZW50IGF1ZGlvLXRyYWNrIHBhc3NlZCwgYW5kIHRyYWNrIGRldGFpbHMgYXZhaWxhYmxlIC0+IG5vLW9wJyk7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBjaGVjayBpZiBsZXZlbCBpZHggaXMgdmFsaWRcblxuXG4gICAgaWYgKG5ld0lkIDwgMCB8fCBuZXdJZCA+PSB0aGlzLnRyYWNrcy5sZW5ndGgpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdJbnZhbGlkIGlkIHBhc3NlZCB0byBhdWRpby10cmFjayBjb250cm9sbGVyJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGF1ZGlvVHJhY2sgPSB0aGlzLnRyYWNrc1tuZXdJZF07XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIk5vdyBzd2l0Y2hpbmcgdG8gYXVkaW8tdHJhY2sgaW5kZXggXCIgKyBuZXdJZCk7IC8vIHN0b3BwaW5nIGxpdmUgcmVsb2FkaW5nIHRpbWVyIGlmIGFueVxuXG4gICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgdGhpcy5fdHJhY2tJZCA9IG5ld0lkO1xuICAgIHZhciB1cmwgPSBhdWRpb1RyYWNrLnVybCxcbiAgICAgICAgdHlwZSA9IGF1ZGlvVHJhY2sudHlwZSxcbiAgICAgICAgaWQgPSBhdWRpb1RyYWNrLmlkO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5BVURJT19UUkFDS19TV0lUQ0hJTkcsIHtcbiAgICAgIGlkOiBpZCxcbiAgICAgIHR5cGU6IHR5cGUsXG4gICAgICB1cmw6IHVybFxuICAgIH0pO1xuXG4gICAgdGhpcy5fbG9hZFRyYWNrRGV0YWlsc0lmTmVlZGVkKGF1ZGlvVHJhY2spO1xuICB9XG4gIC8qKlxuICAgKiBAb3ZlcnJpZGVcbiAgICovXG4gIDtcblxuICBfcHJvdG8uZG9UaWNrID0gZnVuY3Rpb24gZG9UaWNrKCkge1xuICAgIHRoaXMuX3VwZGF0ZVRyYWNrKHRoaXMuX3RyYWNrSWQpO1xuICB9XG4gIC8qKlxuICAgKiBAcGFyYW0gbGV2ZWxJZFxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fc2VsZWN0QXVkaW9Hcm91cCA9IGZ1bmN0aW9uIF9zZWxlY3RBdWRpb0dyb3VwKGxldmVsSWQpIHtcbiAgICB2YXIgbGV2ZWxJbmZvID0gdGhpcy5obHMubGV2ZWxzW2xldmVsSWRdO1xuXG4gICAgaWYgKCFsZXZlbEluZm8gfHwgIWxldmVsSW5mby5hdWRpb0dyb3VwSWRzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGF1ZGlvR3JvdXBJZCA9IGxldmVsSW5mby5hdWRpb0dyb3VwSWRzW2xldmVsSW5mby51cmxJZF07XG5cbiAgICBpZiAodGhpcy5hdWRpb0dyb3VwSWQgIT09IGF1ZGlvR3JvdXBJZCkge1xuICAgICAgdGhpcy5hdWRpb0dyb3VwSWQgPSBhdWRpb0dyb3VwSWQ7XG5cbiAgICAgIHRoaXMuX3NlbGVjdEluaXRpYWxBdWRpb1RyYWNrKCk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBTZWxlY3QgaW5pdGlhbCB0cmFja1xuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fc2VsZWN0SW5pdGlhbEF1ZGlvVHJhY2sgPSBmdW5jdGlvbiBfc2VsZWN0SW5pdGlhbEF1ZGlvVHJhY2soKSB7XG4gICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICB2YXIgdHJhY2tzID0gdGhpcy50cmFja3M7XG5cbiAgICBpZiAoIXRyYWNrcy5sZW5ndGgpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgY3VycmVudEF1ZGlvVHJhY2sgPSB0aGlzLnRyYWNrc1t0aGlzLl90cmFja0lkXTtcbiAgICB2YXIgbmFtZSA9IG51bGw7XG5cbiAgICBpZiAoY3VycmVudEF1ZGlvVHJhY2spIHtcbiAgICAgIG5hbWUgPSBjdXJyZW50QXVkaW9UcmFjay5uYW1lO1xuICAgIH0gLy8gUHJlLXNlbGVjdCBkZWZhdWx0IHRyYWNrcyBpZiB0aGVyZSBhcmUgYW55XG5cblxuICAgIGlmICh0aGlzLl9zZWxlY3REZWZhdWx0VHJhY2spIHtcbiAgICAgIHZhciBkZWZhdWx0VHJhY2tzID0gdHJhY2tzLmZpbHRlcihmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgcmV0dXJuIHRyYWNrLmRlZmF1bHQ7XG4gICAgICB9KTtcblxuICAgICAgaWYgKGRlZmF1bHRUcmFja3MubGVuZ3RoKSB7XG4gICAgICAgIHRyYWNrcyA9IGRlZmF1bHRUcmFja3M7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignTm8gZGVmYXVsdCBhdWRpbyB0cmFja3MgZGVmaW5lZCcpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHZhciB0cmFja0ZvdW5kID0gZmFsc2U7XG5cbiAgICB2YXIgdHJhdmVyc2VUcmFja3MgPSBmdW5jdGlvbiB0cmF2ZXJzZVRyYWNrcygpIHtcbiAgICAgIC8vIFNlbGVjdCB0cmFjayB3aXRoIHJpZ2h0IGdyb3VwIElEXG4gICAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgaWYgKHRyYWNrRm91bmQpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gLy8gV2UgbmVlZCB0byBtYXRjaCB0aGUgKHByZS0pc2VsZWN0ZWQgZ3JvdXAgSURcbiAgICAgICAgLy8gYW5kIHRoZSBOQU1FIG9mIHRoZSBjdXJyZW50IHRyYWNrLlxuXG5cbiAgICAgICAgaWYgKCghX3RoaXMyLmF1ZGlvR3JvdXBJZCB8fCB0cmFjay5ncm91cElkID09PSBfdGhpczIuYXVkaW9Hcm91cElkKSAmJiAoIW5hbWUgfHwgbmFtZSA9PT0gdHJhY2submFtZSkpIHtcbiAgICAgICAgICAvLyBJZiB0aGVyZSB3YXMgYSBwcmV2aW91cyB0cmFjayB0cnkgdG8gc3RheSB3aXRoIHRoZSBzYW1lIGBOQU1FYC5cbiAgICAgICAgICAvLyBJdCBzaG91bGQgYmUgdW5pcXVlIGFjcm9zcyB0cmFja3Mgb2Ygc2FtZSBncm91cCwgYW5kIGNvbnNpc3RlbnQgdGhyb3VnaCByZWR1bmRhbnQgdHJhY2sgZ3JvdXBzLlxuICAgICAgICAgIF90aGlzMi5fc2V0QXVkaW9UcmFjayh0cmFjay5pZCk7XG5cbiAgICAgICAgICB0cmFja0ZvdW5kID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfTtcblxuICAgIHRyYXZlcnNlVHJhY2tzKCk7XG5cbiAgICBpZiAoIXRyYWNrRm91bmQpIHtcbiAgICAgIG5hbWUgPSBudWxsO1xuICAgICAgdHJhdmVyc2VUcmFja3MoKTtcbiAgICB9XG5cbiAgICBpZiAoIXRyYWNrRm91bmQpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcihcIk5vIHRyYWNrIGZvdW5kIGZvciBydW5uaW5nIGF1ZGlvIGdyb3VwLUlEOiBcIiArIHRoaXMuYXVkaW9Hcm91cElkKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uQVVESU9fVFJBQ0tfTE9BRF9FUlJPUixcbiAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtBdWRpb1RyYWNrfSBhdWRpb1RyYWNrXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5fbmVlZHNUcmFja0xvYWRpbmcgPSBmdW5jdGlvbiBfbmVlZHNUcmFja0xvYWRpbmcoYXVkaW9UcmFjaykge1xuICAgIHZhciBkZXRhaWxzID0gYXVkaW9UcmFjay5kZXRhaWxzLFxuICAgICAgICB1cmwgPSBhdWRpb1RyYWNrLnVybDtcblxuICAgIGlmICghZGV0YWlscyB8fCBkZXRhaWxzLmxpdmUpIHtcbiAgICAgIC8vIGNoZWNrIGlmIHdlIGZhY2UgYW4gYXVkaW8gdHJhY2sgZW1iZWRkZWQgaW4gbWFpbiBwbGF5bGlzdCAoYXVkaW8gdHJhY2sgd2l0aG91dCBVUkkgYXR0cmlidXRlKVxuICAgICAgcmV0dXJuICEhdXJsO1xuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtBdWRpb1RyYWNrfSBhdWRpb1RyYWNrXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl9sb2FkVHJhY2tEZXRhaWxzSWZOZWVkZWQgPSBmdW5jdGlvbiBfbG9hZFRyYWNrRGV0YWlsc0lmTmVlZGVkKGF1ZGlvVHJhY2spIHtcbiAgICBpZiAodGhpcy5fbmVlZHNUcmFja0xvYWRpbmcoYXVkaW9UcmFjaykpIHtcbiAgICAgIHZhciB1cmwgPSBhdWRpb1RyYWNrLnVybCxcbiAgICAgICAgICBpZCA9IGF1ZGlvVHJhY2suaWQ7IC8vIHRyYWNrIG5vdCByZXRyaWV2ZWQgeWV0LCBvciBsaXZlIHBsYXlsaXN0IHdlIG5lZWQgdG8gKHJlKWxvYWQgaXRcblxuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImxvYWRpbmcgYXVkaW8tdHJhY2sgcGxheWxpc3QgZm9yIGlkOiBcIiArIGlkKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5BVURJT19UUkFDS19MT0FESU5HLCB7XG4gICAgICAgIHVybDogdXJsLFxuICAgICAgICBpZDogaWRcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtudW1iZXJ9IG5ld0lkXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl91cGRhdGVUcmFjayA9IGZ1bmN0aW9uIF91cGRhdGVUcmFjayhuZXdJZCkge1xuICAgIC8vIGNoZWNrIGlmIGxldmVsIGlkeCBpcyB2YWxpZFxuICAgIGlmIChuZXdJZCA8IDAgfHwgbmV3SWQgPj0gdGhpcy50cmFja3MubGVuZ3RoKSB7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBzdG9wcGluZyBsaXZlIHJlbG9hZGluZyB0aW1lciBpZiBhbnlcblxuXG4gICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgdGhpcy5fdHJhY2tJZCA9IG5ld0lkO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJ0cnlpbmcgdG8gdXBkYXRlIGF1ZGlvLXRyYWNrIFwiICsgbmV3SWQpO1xuICAgIHZhciBhdWRpb1RyYWNrID0gdGhpcy50cmFja3NbbmV3SWRdO1xuXG4gICAgdGhpcy5fbG9hZFRyYWNrRGV0YWlsc0lmTmVlZGVkKGF1ZGlvVHJhY2spO1xuICB9XG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5faGFuZGxlTG9hZEVycm9yID0gZnVuY3Rpb24gX2hhbmRsZUxvYWRFcnJvcigpIHtcbiAgICAvLyBGaXJzdCwgbGV0J3MgYmxhY2sgbGlzdCBjdXJyZW50IHRyYWNrIGlkXG4gICAgdGhpcy50cmFja0lkQmxhY2tsaXN0W3RoaXMuX3RyYWNrSWRdID0gdHJ1ZTsgLy8gTGV0J3MgdHJ5IHRvIGZhbGwgYmFjayBvbiBhIGZ1bmN0aW9uYWwgYXVkaW8tdHJhY2sgd2l0aCB0aGUgc2FtZSBncm91cCBJRFxuXG4gICAgdmFyIHByZXZpb3VzSWQgPSB0aGlzLl90cmFja0lkO1xuICAgIHZhciBfdGhpcyR0cmFja3MkcHJldmlvdXMgPSB0aGlzLnRyYWNrc1twcmV2aW91c0lkXSxcbiAgICAgICAgbmFtZSA9IF90aGlzJHRyYWNrcyRwcmV2aW91cy5uYW1lLFxuICAgICAgICBsYW5ndWFnZSA9IF90aGlzJHRyYWNrcyRwcmV2aW91cy5sYW5ndWFnZSxcbiAgICAgICAgZ3JvdXBJZCA9IF90aGlzJHRyYWNrcyRwcmV2aW91cy5ncm91cElkO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiTG9hZGluZyBmYWlsZWQgb24gYXVkaW8gdHJhY2sgaWQ6IFwiICsgcHJldmlvdXNJZCArIFwiLCBncm91cC1pZDogXCIgKyBncm91cElkICsgXCIsIG5hbWUvbGFuZ3VhZ2U6IFxcXCJcIiArIG5hbWUgKyBcIlxcXCIgLyBcXFwiXCIgKyBsYW5ndWFnZSArIFwiXFxcIlwiKTsgLy8gRmluZCBhIG5vbi1ibGFja2xpc3RlZCB0cmFjayBJRCB3aXRoIHRoZSBzYW1lIE5BTUVcbiAgICAvLyBBdCBsZWFzdCBhIHRyYWNrIHRoYXQgaXMgbm90IGJsYWNrbGlzdGVkLCB0aHVzIG9uIGFub3RoZXIgZ3JvdXAtSUQuXG5cbiAgICB2YXIgbmV3SWQgPSBwcmV2aW91c0lkO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLnRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKHRoaXMudHJhY2tJZEJsYWNrbGlzdFtpXSkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgdmFyIG5ld1RyYWNrID0gdGhpcy50cmFja3NbaV07XG5cbiAgICAgIGlmIChuZXdUcmFjay5uYW1lID09PSBuYW1lKSB7XG4gICAgICAgIG5ld0lkID0gaTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKG5ld0lkID09PSBwcmV2aW91c0lkKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybihcIk5vIGZhbGxiYWNrIGF1ZGlvLXRyYWNrIGZvdW5kIGZvciBuYW1lL2xhbmd1YWdlOiBcXFwiXCIgKyBuYW1lICsgXCJcXFwiIC8gXFxcIlwiICsgbGFuZ3VhZ2UgKyBcIlxcXCJcIik7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnQXR0ZW1wdGluZyBhdWRpby10cmFjayBmYWxsYmFjayBpZDonLCBuZXdJZCwgJ2dyb3VwLWlkOicsIHRoaXMudHJhY2tzW25ld0lkXS5ncm91cElkKTtcblxuICAgIHRoaXMuX3NldEF1ZGlvVHJhY2sobmV3SWQpO1xuICB9O1xuXG4gIGF1ZGlvX3RyYWNrX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQXVkaW9UcmFja0NvbnRyb2xsZXIsIFt7XG4gICAga2V5OiBcImF1ZGlvVHJhY2tzXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy50cmFja3M7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtudW1iZXJ9IEluZGV4IGludG8gYXVkaW8tdHJhY2tzIGxpc3Qgb2YgY3VycmVudGx5IHNlbGVjdGVkIHRyYWNrLlxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwiYXVkaW9UcmFja1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuX3RyYWNrSWQ7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNlbGVjdCBjdXJyZW50IHRyYWNrIGJ5IGluZGV4XG4gICAgICovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KG5ld0lkKSB7XG4gICAgICB0aGlzLl9zZXRBdWRpb1RyYWNrKG5ld0lkKTsgLy8gSWYgYXVkaW8gdHJhY2sgaXMgc2VsZWN0ZWQgZnJvbSBBUEkgdGhlbiBkb24ndCBjaG9vc2UgZnJvbSB0aGUgbWFuaWZlc3QgZGVmYXVsdCB0cmFja1xuXG5cbiAgICAgIHRoaXMuX3NlbGVjdERlZmF1bHRUcmFjayA9IGZhbHNlO1xuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBBdWRpb1RyYWNrQ29udHJvbGxlcjtcbn0oVGFza0xvb3ApO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBhdWRpb190cmFja19jb250cm9sbGVyID0gKGF1ZGlvX3RyYWNrX2NvbnRyb2xsZXJfQXVkaW9UcmFja0NvbnRyb2xsZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9hdWRpby1zdHJlYW0tY29udHJvbGxlci5qc1xuXG5cblxuXG5mdW5jdGlvbiBhdWRpb19zdHJlYW1fY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH1cblxuZnVuY3Rpb24gYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBhdWRpb19zdHJlYW1fY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH1cblxuZnVuY3Rpb24gYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLypcbiAqIEF1ZGlvIFN0cmVhbSBDb250cm9sbGVyXG4qL1xuXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG5cbnZhciBhdWRpb19zdHJlYW1fY29udHJvbGxlcl93aW5kb3cgPSB3aW5kb3csXG4gICAgYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfcGVyZm9ybWFuY2UgPSBhdWRpb19zdHJlYW1fY29udHJvbGxlcl93aW5kb3cucGVyZm9ybWFuY2U7XG52YXIgYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfVElDS19JTlRFUlZBTCA9IDEwMDsgLy8gaG93IG9mdGVuIHRvIHRpY2sgaW4gbXNcblxudmFyIGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX0F1ZGlvU3RyZWFtQ29udHJvbGxlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0Jhc2VTdHJlYW1Db250cm9sbGVyKSB7XG4gIGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX2luaGVyaXRzTG9vc2UoQXVkaW9TdHJlYW1Db250cm9sbGVyLCBfQmFzZVN0cmVhbUNvbnRyb2xsZXIpO1xuXG4gIGZ1bmN0aW9uIEF1ZGlvU3RyZWFtQ29udHJvbGxlcihobHMsIGZyYWdtZW50VHJhY2tlcikge1xuICAgIHZhciBfdGhpcztcblxuICAgIF90aGlzID0gX0Jhc2VTdHJlYW1Db250cm9sbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0FUVEFDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5BVURJT19UUkFDS1NfVVBEQVRFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5BVURJT19UUkFDS19TV0lUQ0hJTkcsIGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfTE9BREVELCBldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19QQVJTSU5HX0lOSVRfU0VHTUVOVCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNJTkdfREFUQSwgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwgZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfUkVTRVQsIGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0NSRUFURUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0FQUEVOREVELCBldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9GTFVTSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLklOSVRfUFRTX0ZPVU5EKSB8fCB0aGlzO1xuICAgIF90aGlzLmZyYWdtZW50VHJhY2tlciA9IGZyYWdtZW50VHJhY2tlcjtcbiAgICBfdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIF90aGlzLmF1ZGlvQ29kZWNTd2FwID0gZmFsc2U7XG4gICAgX3RoaXMuX3N0YXRlID0gU3RhdGUuU1RPUFBFRDtcbiAgICBfdGhpcy5pbml0UFRTID0gW107XG4gICAgX3RoaXMud2FpdGluZ0ZyYWdtZW50ID0gbnVsbDtcbiAgICBfdGhpcy52aWRlb1RyYWNrQ0MgPSBudWxsO1xuICAgIF90aGlzLndhaXRpbmdWaWRlb0NDID0gbnVsbDtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH0gLy8gU2lnbmFsIHRoYXQgdmlkZW8gUFRTIHdhcyBmb3VuZFxuXG5cbiAgdmFyIF9wcm90byA9IEF1ZGlvU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLm9uSW5pdFB0c0ZvdW5kID0gZnVuY3Rpb24gb25Jbml0UHRzRm91bmQoZGF0YSkge1xuICAgIHZhciBkZW11eGVySWQgPSBkYXRhLmlkLFxuICAgICAgICBjYyA9IGRhdGEuZnJhZy5jYyxcbiAgICAgICAgaW5pdFBUUyA9IGRhdGEuaW5pdFBUUztcblxuICAgIGlmIChkZW11eGVySWQgPT09ICdtYWluJykge1xuICAgICAgLy8gQWx3YXlzIHVwZGF0ZSB0aGUgbmV3IElOSVQgUFRTXG4gICAgICAvLyBDYW4gY2hhbmdlIGR1ZSBsZXZlbCBzd2l0Y2hcbiAgICAgIHRoaXMuaW5pdFBUU1tjY10gPSBpbml0UFRTO1xuICAgICAgdGhpcy52aWRlb1RyYWNrQ0MgPSBjYztcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJJbml0UFRTIGZvciBjYzogXCIgKyBjYyArIFwiIGZvdW5kIGZyb20gbWFpbjogXCIgKyBpbml0UFRTKTsgLy8gSWYgd2UgYXJlIHdhaXRpbmcgd2UgbmVlZCB0byBkZW11eC9yZW11eCB0aGUgd2FpdGluZyBmcmFnXG4gICAgICAvLyBXaXRoIHRoZSBuZXcgaW5pdFBUU1xuXG4gICAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuV0FJVElOR19JTklUX1BUUykge1xuICAgICAgICB0aGlzLnRpY2soKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLnN0YXJ0TG9hZCA9IGZ1bmN0aW9uIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKSB7XG4gICAgaWYgKHRoaXMudHJhY2tzKSB7XG4gICAgICB2YXIgbGFzdEN1cnJlbnRUaW1lID0gdGhpcy5sYXN0Q3VycmVudFRpbWU7XG4gICAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgICB0aGlzLnNldEludGVydmFsKGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX1RJQ0tfSU5URVJWQUwpO1xuICAgICAgdGhpcy5mcmFnTG9hZEVycm9yID0gMDtcblxuICAgICAgaWYgKGxhc3RDdXJyZW50VGltZSA+IDAgJiYgc3RhcnRQb3NpdGlvbiA9PT0gLTEpIHtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImF1ZGlvOm92ZXJyaWRlIHN0YXJ0UG9zaXRpb24gd2l0aCBsYXN0Q3VycmVudFRpbWUgQFwiICsgbGFzdEN1cnJlbnRUaW1lLnRvRml4ZWQoMykpO1xuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMubGFzdEN1cnJlbnRUaW1lID0gdGhpcy5zdGFydFBvc2l0aW9uID8gdGhpcy5zdGFydFBvc2l0aW9uIDogc3RhcnRQb3NpdGlvbjtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUQVJUSU5HO1xuICAgICAgfVxuXG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb24gPSB0aGlzLmxhc3RDdXJyZW50VGltZTtcbiAgICAgIHRoaXMudGljaygpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSBzdGFydFBvc2l0aW9uO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5kb1RpY2sgPSBmdW5jdGlvbiBkb1RpY2soKSB7XG4gICAgdmFyIHBvcyxcbiAgICAgICAgdHJhY2ssXG4gICAgICAgIHRyYWNrRGV0YWlscyxcbiAgICAgICAgaGxzID0gdGhpcy5obHMsXG4gICAgICAgIGNvbmZpZyA9IGhscy5jb25maWc7IC8vIGxvZ2dlci5sb2coJ2F1ZGlvU3RyZWFtOicgKyB0aGlzLnN0YXRlKTtcblxuICAgIHN3aXRjaCAodGhpcy5zdGF0ZSkge1xuICAgICAgY2FzZSBTdGF0ZS5FUlJPUjogLy8gZG9uJ3QgZG8gYW55dGhpbmcgaW4gZXJyb3Igc3RhdGUgdG8gYXZvaWQgYnJlYWtpbmcgZnVydGhlciAuLi5cblxuICAgICAgY2FzZSBTdGF0ZS5QQVVTRUQ6IC8vIGRvbid0IGRvIGFueXRoaW5nIGluIHBhdXNlZCBzdGF0ZSBlaXRoZXIgLi4uXG5cbiAgICAgIGNhc2UgU3RhdGUuQlVGRkVSX0ZMVVNISU5HOlxuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBTdGF0ZS5TVEFSVElORzpcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfVFJBQ0s7XG4gICAgICAgIHRoaXMubG9hZGVkbWV0YWRhdGEgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgU3RhdGUuSURMRTpcbiAgICAgICAgdmFyIHRyYWNrcyA9IHRoaXMudHJhY2tzOyAvLyBhdWRpbyB0cmFja3Mgbm90IHJlY2VpdmVkID0+IGV4aXQgbG9vcFxuXG4gICAgICAgIGlmICghdHJhY2tzKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gLy8gaWYgdmlkZW8gbm90IGF0dGFjaGVkIEFORFxuICAgICAgICAvLyBzdGFydCBmcmFnbWVudCBhbHJlYWR5IHJlcXVlc3RlZCBPUiBzdGFydCBmcmFnIHByZWZldGNoIGRpc2FibGVcbiAgICAgICAgLy8gZXhpdCBsb29wXG4gICAgICAgIC8vID0+IGlmIG1lZGlhIG5vdCBhdHRhY2hlZCBidXQgc3RhcnQgZnJhZyBwcmVmZXRjaCBpcyBlbmFibGVkIGFuZCBzdGFydCBmcmFnIG5vdCByZXF1ZXN0ZWQgeWV0LCB3ZSB3aWxsIG5vdCBleGl0IGxvb3BcblxuXG4gICAgICAgIGlmICghdGhpcy5tZWRpYSAmJiAodGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgfHwgIWNvbmZpZy5zdGFydEZyYWdQcmVmZXRjaCkpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfSAvLyBkZXRlcm1pbmUgbmV4dCBjYW5kaWRhdGUgZnJhZ21lbnQgdG8gYmUgbG9hZGVkLCBiYXNlZCBvbiBjdXJyZW50IHBvc2l0aW9uIGFuZFxuICAgICAgICAvLyAgZW5kIG9mIGJ1ZmZlciBwb3NpdGlvblxuICAgICAgICAvLyBpZiB3ZSBoYXZlIG5vdCB5ZXQgbG9hZGVkIGFueSBmcmFnbWVudCwgc3RhcnQgbG9hZGluZyBmcm9tIHN0YXJ0IHBvc2l0aW9uXG5cblxuICAgICAgICBpZiAodGhpcy5sb2FkZWRtZXRhZGF0YSkge1xuICAgICAgICAgIHBvcyA9IHRoaXMubWVkaWEuY3VycmVudFRpbWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcG9zID0gdGhpcy5uZXh0TG9hZFBvc2l0aW9uO1xuXG4gICAgICAgICAgaWYgKHBvcyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhQnVmZmVyID8gdGhpcy5tZWRpYUJ1ZmZlciA6IHRoaXMubWVkaWE7XG4gICAgICAgIHZhciB2aWRlb0J1ZmZlciA9IHRoaXMudmlkZW9CdWZmZXIgPyB0aGlzLnZpZGVvQnVmZmVyIDogdGhpcy5tZWRpYTtcbiAgICAgICAgdmFyIG1heEJ1ZmZlckhvbGUgPSBwb3MgPCBjb25maWcubWF4QnVmZmVySG9sZSA/IE1hdGgubWF4KE1BWF9TVEFSVF9HQVBfSlVNUCwgY29uZmlnLm1heEJ1ZmZlckhvbGUpIDogY29uZmlnLm1heEJ1ZmZlckhvbGU7XG4gICAgICAgIHZhciBidWZmZXJJbmZvID0gQnVmZmVySGVscGVyLmJ1ZmZlckluZm8obWVkaWEsIHBvcywgbWF4QnVmZmVySG9sZSk7XG4gICAgICAgIHZhciBtYWluQnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKHZpZGVvQnVmZmVyLCBwb3MsIG1heEJ1ZmZlckhvbGUpO1xuICAgICAgICB2YXIgYnVmZmVyTGVuID0gYnVmZmVySW5mby5sZW47XG4gICAgICAgIHZhciBidWZmZXJFbmQgPSBidWZmZXJJbmZvLmVuZDtcbiAgICAgICAgdmFyIGZyYWdQcmV2aW91cyA9IHRoaXMuZnJhZ1ByZXZpb3VzOyAvLyBlbnN1cmUgd2UgYnVmZmVyIGF0IGxlYXN0IGNvbmZpZy5tYXhCdWZmZXJMZW5ndGggKGRlZmF1bHQgMzBzKSBvciBjb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoIChkZWZhdWx0OiA2MDBzKVxuICAgICAgICAvLyB3aGljaGV2ZXIgaXMgc21hbGxlci5cbiAgICAgICAgLy8gb25jZSB3ZSByZWFjaCB0aGF0IHRocmVzaG9sZCwgZG9uJ3QgYnVmZmVyIG1vcmUgdGhhbiB2aWRlbyAobWFpbkJ1ZmZlckluZm8ubGVuKVxuXG4gICAgICAgIHZhciBtYXhDb25maWdCdWZmZXIgPSBNYXRoLm1pbihjb25maWcubWF4QnVmZmVyTGVuZ3RoLCBjb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoKTtcbiAgICAgICAgdmFyIG1heEJ1ZkxlbiA9IE1hdGgubWF4KG1heENvbmZpZ0J1ZmZlciwgbWFpbkJ1ZmZlckluZm8ubGVuKTtcbiAgICAgICAgdmFyIGF1ZGlvU3dpdGNoID0gdGhpcy5hdWRpb1N3aXRjaDtcbiAgICAgICAgdmFyIHRyYWNrSWQgPSB0aGlzLnRyYWNrSWQ7IC8vIGlmIGJ1ZmZlciBsZW5ndGggaXMgbGVzcyB0aGFuIG1heEJ1ZkxlbiB0cnkgdG8gbG9hZCBhIG5ldyBmcmFnbWVudFxuXG4gICAgICAgIGlmICgoYnVmZmVyTGVuIDwgbWF4QnVmTGVuIHx8IGF1ZGlvU3dpdGNoKSAmJiB0cmFja0lkIDwgdHJhY2tzLmxlbmd0aCkge1xuICAgICAgICAgIHRyYWNrRGV0YWlscyA9IHRyYWNrc1t0cmFja0lkXS5kZXRhaWxzOyAvLyBpZiB0cmFjayBpbmZvIG5vdCByZXRyaWV2ZWQgeWV0LCBzd2l0Y2ggc3RhdGUgYW5kIHdhaXQgZm9yIHRyYWNrIHJldHJpZXZhbFxuXG4gICAgICAgICAgaWYgKHR5cGVvZiB0cmFja0RldGFpbHMgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuV0FJVElOR19UUkFDSztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghYXVkaW9Td2l0Y2ggJiYgdGhpcy5fc3RyZWFtRW5kZWQoYnVmZmVySW5mbywgdHJhY2tEZXRhaWxzKSkge1xuICAgICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9FT1MsIHtcbiAgICAgICAgICAgICAgdHlwZTogJ2F1ZGlvJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRU5ERUQ7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfSAvLyBmaW5kIGZyYWdtZW50IGluZGV4LCBjb250aWd1b3VzIHdpdGggZW5kIG9mIGJ1ZmZlciBwb3NpdGlvblxuXG5cbiAgICAgICAgICB2YXIgZnJhZ21lbnRzID0gdHJhY2tEZXRhaWxzLmZyYWdtZW50cyxcbiAgICAgICAgICAgICAgZnJhZ0xlbiA9IGZyYWdtZW50cy5sZW5ndGgsXG4gICAgICAgICAgICAgIHN0YXJ0ID0gZnJhZ21lbnRzWzBdLnN0YXJ0LFxuICAgICAgICAgICAgICBlbmQgPSBmcmFnbWVudHNbZnJhZ0xlbiAtIDFdLnN0YXJ0ICsgZnJhZ21lbnRzW2ZyYWdMZW4gLSAxXS5kdXJhdGlvbixcbiAgICAgICAgICAgICAgZnJhZzsgLy8gV2hlbiBzd2l0Y2hpbmcgYXVkaW8gdHJhY2ssIHJlbG9hZCBhdWRpbyBhcyBjbG9zZSBhcyBwb3NzaWJsZSB0byBjdXJyZW50VGltZVxuXG4gICAgICAgICAgaWYgKGF1ZGlvU3dpdGNoKSB7XG4gICAgICAgICAgICBpZiAodHJhY2tEZXRhaWxzLmxpdmUgJiYgIXRyYWNrRGV0YWlscy5QVFNLbm93bikge1xuICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdzd2l0Y2hpbmcgYXVkaW90cmFjaywgbGl2ZSBzdHJlYW0sIHVua25vd24gUFRTLGxvYWQgZmlyc3QgZnJhZ21lbnQnKTtcbiAgICAgICAgICAgICAgYnVmZmVyRW5kID0gMDtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGJ1ZmZlckVuZCA9IHBvczsgLy8gaWYgY3VycmVudFRpbWUgKHBvcykgaXMgbGVzcyB0aGFuIGFsdCBhdWRpbyBwbGF5bGlzdCBzdGFydCB0aW1lLCBpdCBtZWFucyB0aGF0IGFsdCBhdWRpbyBpcyBhaGVhZCBvZiBjdXJyZW50VGltZVxuXG4gICAgICAgICAgICAgIGlmICh0cmFja0RldGFpbHMuUFRTS25vd24gJiYgcG9zIDwgc3RhcnQpIHtcbiAgICAgICAgICAgICAgICAvLyBpZiBldmVyeXRoaW5nIGlzIGJ1ZmZlcmVkIGZyb20gcG9zIHRvIHN0YXJ0IG9yIGlmIGF1ZGlvIGJ1ZmZlciB1cGZyb250LCBsZXQncyBzZWVrIHRvIHN0YXJ0XG4gICAgICAgICAgICAgICAgaWYgKGJ1ZmZlckluZm8uZW5kID4gc3RhcnQgfHwgYnVmZmVySW5mby5uZXh0U3RhcnQpIHtcbiAgICAgICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ2FsdCBhdWRpbyB0cmFjayBhaGVhZCBvZiBtYWluIHRyYWNrLCBzZWVrIHRvIHN0YXJ0IG9mIGFsdCBhdWRpbyB0cmFjaycpO1xuICAgICAgICAgICAgICAgICAgdGhpcy5tZWRpYS5jdXJyZW50VGltZSA9IHN0YXJ0ICsgMC4wNTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICh0cmFja0RldGFpbHMuaW5pdFNlZ21lbnQgJiYgIXRyYWNrRGV0YWlscy5pbml0U2VnbWVudC5kYXRhKSB7XG4gICAgICAgICAgICBmcmFnID0gdHJhY2tEZXRhaWxzLmluaXRTZWdtZW50O1xuICAgICAgICAgIH0gLy8gZXNsaW50LWRpc2FibGUtbGluZSBicmFjZS1zdHlsZVxuICAgICAgICAgIC8vIGlmIGJ1ZmZlckVuZCBiZWZvcmUgc3RhcnQgb2YgcGxheWxpc3QsIGxvYWQgZmlyc3QgZnJhZ21lbnRcbiAgICAgICAgICBlbHNlIGlmIChidWZmZXJFbmQgPD0gc3RhcnQpIHtcbiAgICAgICAgICAgICAgZnJhZyA9IGZyYWdtZW50c1swXTtcblxuICAgICAgICAgICAgICBpZiAodGhpcy52aWRlb1RyYWNrQ0MgIT09IG51bGwgJiYgZnJhZy5jYyAhPT0gdGhpcy52aWRlb1RyYWNrQ0MpIHtcbiAgICAgICAgICAgICAgICAvLyBFbnN1cmUgd2UgZmluZCBhIGZyYWdtZW50IHdoaWNoIG1hdGNoZXMgdGhlIGNvbnRpbnVpdHkgb2YgdGhlIHZpZGVvIHRyYWNrXG4gICAgICAgICAgICAgICAgZnJhZyA9IGZpbmRGcmFnV2l0aENDKGZyYWdtZW50cywgdGhpcy52aWRlb1RyYWNrQ0MpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgaWYgKHRyYWNrRGV0YWlscy5saXZlICYmIGZyYWcubG9hZElkeCAmJiBmcmFnLmxvYWRJZHggPT09IHRoaXMuZnJhZ0xvYWRJZHgpIHtcbiAgICAgICAgICAgICAgICAvLyB3ZSBqdXN0IGxvYWRlZCB0aGlzIGZpcnN0IGZyYWdtZW50LCBhbmQgd2UgYXJlIHN0aWxsIGxhZ2dpbmcgYmVoaW5kIHRoZSBzdGFydCBvZiB0aGUgbGl2ZSBwbGF5bGlzdFxuICAgICAgICAgICAgICAgIC8vIGxldCdzIGZvcmNlIHNlZWsgdG8gc3RhcnRcbiAgICAgICAgICAgICAgICB2YXIgbmV4dEJ1ZmZlcmVkID0gYnVmZmVySW5mby5uZXh0U3RhcnQgPyBidWZmZXJJbmZvLm5leHRTdGFydCA6IHN0YXJ0O1xuICAgICAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJubyBhbHQgYXVkaW8gYXZhaWxhYmxlIEBjdXJyZW50VGltZTpcIiArIHRoaXMubWVkaWEuY3VycmVudFRpbWUgKyBcIiwgc2Vla2luZyBAXCIgKyAobmV4dEJ1ZmZlcmVkICsgMC4wNSkpO1xuICAgICAgICAgICAgICAgIHRoaXMubWVkaWEuY3VycmVudFRpbWUgPSBuZXh0QnVmZmVyZWQgKyAwLjA1O1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdmFyIGZvdW5kRnJhZztcbiAgICAgICAgICAgICAgdmFyIG1heEZyYWdMb29rVXBUb2xlcmFuY2UgPSBjb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTtcbiAgICAgICAgICAgICAgdmFyIGZyYWdOZXh0ID0gZnJhZ1ByZXZpb3VzID8gZnJhZ21lbnRzW2ZyYWdQcmV2aW91cy5zbiAtIGZyYWdtZW50c1swXS5zbiArIDFdIDogdW5kZWZpbmVkO1xuXG4gICAgICAgICAgICAgIGlmIChidWZmZXJFbmQgPCBlbmQpIHtcbiAgICAgICAgICAgICAgICBpZiAoYnVmZmVyRW5kID4gZW5kIC0gbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSkge1xuICAgICAgICAgICAgICAgICAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IDA7XG4gICAgICAgICAgICAgICAgfSAvLyBQcmVmZXIgdGhlIG5leHQgZnJhZ21lbnQgaWYgaXQncyB3aXRoaW4gdG9sZXJhbmNlXG5cblxuICAgICAgICAgICAgICAgIGlmIChmcmFnTmV4dCAmJiAhZnJhZ21lbnRXaXRoaW5Ub2xlcmFuY2VUZXN0KGJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgZnJhZ05leHQpKSB7XG4gICAgICAgICAgICAgICAgICBmb3VuZEZyYWcgPSBmcmFnTmV4dDtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgZm91bmRGcmFnID0gYmluYXJ5X3NlYXJjaC5zZWFyY2goZnJhZ21lbnRzLCBmdW5jdGlvbiAoZnJhZykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gZnJhZ21lbnRXaXRoaW5Ub2xlcmFuY2VUZXN0KGJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgZnJhZyk7XG4gICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gcmVhY2ggZW5kIG9mIHBsYXlsaXN0XG4gICAgICAgICAgICAgICAgZm91bmRGcmFnID0gZnJhZ21lbnRzW2ZyYWdMZW4gLSAxXTtcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGlmIChmb3VuZEZyYWcpIHtcbiAgICAgICAgICAgICAgICBmcmFnID0gZm91bmRGcmFnO1xuICAgICAgICAgICAgICAgIHN0YXJ0ID0gZm91bmRGcmFnLnN0YXJ0OyAvLyBsb2dnZXIubG9nKCdmaW5kIFNOIG1hdGNoaW5nIHdpdGggcG9zOicgKyAgYnVmZmVyRW5kICsgJzonICsgZnJhZy5zbik7XG5cbiAgICAgICAgICAgICAgICBpZiAoZnJhZ1ByZXZpb3VzICYmIGZyYWcubGV2ZWwgPT09IGZyYWdQcmV2aW91cy5sZXZlbCAmJiBmcmFnLnNuID09PSBmcmFnUHJldmlvdXMuc24pIHtcbiAgICAgICAgICAgICAgICAgIGlmIChmcmFnLnNuIDwgdHJhY2tEZXRhaWxzLmVuZFNOKSB7XG4gICAgICAgICAgICAgICAgICAgIGZyYWcgPSBmcmFnbWVudHNbZnJhZy5zbiArIDEgLSB0cmFja0RldGFpbHMuc3RhcnRTTl07XG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpICE9PSBGcmFnbWVudFN0YXRlLk9LKSB7XG4gICAgICAgICAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIlNOIGp1c3QgbG9hZGVkLCBsb2FkIG5leHQgb25lOiBcIiArIGZyYWcuc24pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBmcmFnID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgICAgICAvLyBsb2dnZXIubG9nKCcgICAgICBsb2FkaW5nIGZyYWcgJyArIGkgKycscG9zL2J1ZkVuZDonICsgcG9zLnRvRml4ZWQoMykgKyAnLycgKyBidWZmZXJFbmQudG9GaXhlZCgzKSk7XG4gICAgICAgICAgICBpZiAoZnJhZy5lbmNyeXB0ZWQpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIkxvYWRpbmcga2V5IGZvciBcIiArIGZyYWcuc24gKyBcIiBvZiBbXCIgKyB0cmFja0RldGFpbHMuc3RhcnRTTiArIFwiICxcIiArIHRyYWNrRGV0YWlscy5lbmRTTiArIFwiXSx0cmFjayBcIiArIHRyYWNrSWQpO1xuICAgICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuS0VZX0xPQURJTkc7XG4gICAgICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uS0VZX0xPQURJTkcsIHtcbiAgICAgICAgICAgICAgICBmcmFnOiBmcmFnXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgLy8gb25seSBsb2FkIGlmIGZyYWdtZW50IGlzIG5vdCBsb2FkZWQgb3IgaWYgaW4gYXVkaW8gc3dpdGNoXG4gICAgICAgICAgICAgIC8vIHdlIGZvcmNlIGEgZnJhZyBsb2FkaW5nIGluIGF1ZGlvIHN3aXRjaCBhcyBmcmFnbWVudCB0cmFja2VyIG1pZ2h0IG5vdCBoYXZlIGV2aWN0ZWQgcHJldmlvdXMgZnJhZ3MgaW4gY2FzZSBvZiBxdWljayBhdWRpbyBzd2l0Y2hcbiAgICAgICAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IGZyYWc7XG5cbiAgICAgICAgICAgICAgaWYgKGF1ZGlvU3dpdGNoIHx8IHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpID09PSBGcmFnbWVudFN0YXRlLk5PVF9MT0FERUQpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiTG9hZGluZyBcIiArIGZyYWcuc24gKyBcIiwgY2M6IFwiICsgZnJhZy5jYyArIFwiIG9mIFtcIiArIHRyYWNrRGV0YWlscy5zdGFydFNOICsgXCIgLFwiICsgdHJhY2tEZXRhaWxzLmVuZFNOICsgXCJdLHRyYWNrIFwiICsgdHJhY2tJZCArIFwiLCBcIiArICh0aGlzLmxvYWRlZG1ldGFkYXRhID8gJ2N1cnJlbnRUaW1lJyA6ICduZXh0TG9hZFBvc2l0aW9uJykgKyBcIjogXCIgKyBwb3MgKyBcIiwgYnVmZmVyRW5kOiBcIiArIGJ1ZmZlckVuZC50b0ZpeGVkKDMpKTtcblxuICAgICAgICAgICAgICAgIGlmIChmcmFnLnNuICE9PSAnaW5pdFNlZ21lbnQnKSB7XG4gICAgICAgICAgICAgICAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKE9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoZnJhZy5zbikpIHtcbiAgICAgICAgICAgICAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IGZyYWcuc3RhcnQgKyBmcmFnLmR1cmF0aW9uO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FESU5HLCB7XG4gICAgICAgICAgICAgICAgICBmcmFnOiBmcmFnXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkZSQUdfTE9BRElORztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlIFN0YXRlLldBSVRJTkdfVFJBQ0s6XG4gICAgICAgIHRyYWNrID0gdGhpcy50cmFja3NbdGhpcy50cmFja0lkXTsgLy8gY2hlY2sgaWYgcGxheWxpc3QgaXMgYWxyZWFkeSBsb2FkZWRcblxuICAgICAgICBpZiAodHJhY2sgJiYgdHJhY2suZGV0YWlscykge1xuICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgU3RhdGUuRlJBR19MT0FESU5HX1dBSVRJTkdfUkVUUlk6XG4gICAgICAgIHZhciBub3cgPSBhdWRpb19zdHJlYW1fY29udHJvbGxlcl9wZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgICAgdmFyIHJldHJ5RGF0ZSA9IHRoaXMucmV0cnlEYXRlO1xuICAgICAgICBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgICAgIHZhciBpc1NlZWtpbmcgPSBtZWRpYSAmJiBtZWRpYS5zZWVraW5nOyAvLyBpZiBjdXJyZW50IHRpbWUgaXMgZ3QgdGhhbiByZXRyeURhdGUsIG9yIGlmIG1lZGlhIHNlZWtpbmcgbGV0J3Mgc3dpdGNoIHRvIElETEUgc3RhdGUgdG8gcmV0cnkgbG9hZGluZ1xuXG4gICAgICAgIGlmICghcmV0cnlEYXRlIHx8IG5vdyA+PSByZXRyeURhdGUgfHwgaXNTZWVraW5nKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnYXVkaW9TdHJlYW1Db250cm9sbGVyOiByZXRyeURhdGUgcmVhY2hlZCwgc3dpdGNoIGJhY2sgdG8gSURMRSBzdGF0ZScpO1xuICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgU3RhdGUuV0FJVElOR19JTklUX1BUUzpcbiAgICAgICAgLy8gRW5zdXJlIHdlIGRvbid0IGdldCBzdHVjayBpbiB0aGUgV0FJVElOR19JTklUX1BUUyBzdGF0ZSBpZiB0aGUgd2FpdGluZyBmcmFnIENDIGRvZXNuJ3QgbWF0Y2ggYW55IGluaXRQVFNcbiAgICAgICAgdmFyIHdhaXRpbmdGcmFnID0gdGhpcy53YWl0aW5nRnJhZ21lbnQ7XG5cbiAgICAgICAgaWYgKHdhaXRpbmdGcmFnKSB7XG4gICAgICAgICAgdmFyIHdhaXRpbmdGcmFnQ0MgPSB3YWl0aW5nRnJhZy5mcmFnLmNjO1xuXG4gICAgICAgICAgaWYgKHRoaXMuaW5pdFBUU1t3YWl0aW5nRnJhZ0NDXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICB0aGlzLndhaXRpbmdGcmFnbWVudCA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRlJBR19MT0FESU5HO1xuICAgICAgICAgICAgdGhpcy5vbkZyYWdMb2FkZWQod2FpdGluZ0ZyYWcpO1xuICAgICAgICAgIH0gZWxzZSBpZiAodGhpcy52aWRlb1RyYWNrQ0MgIT09IHRoaXMud2FpdGluZ1ZpZGVvQ0MpIHtcbiAgICAgICAgICAgIC8vIERyb3Agd2FpdGluZyBmcmFnbWVudCBpZiB2aWRlb1RyYWNrQ0MgaGFzIGNoYW5nZWQgc2luY2Ugd2FpdGluZ0ZyYWdtZW50IHdhcyBzZXQgYW5kIGluaXRQVFMgd2FzIG5vdCBmb3VuZFxuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIldhaXRpbmcgZnJhZ21lbnQgY2MgKFwiICsgd2FpdGluZ0ZyYWdDQyArIFwiKSBjYW5jZWxsZWQgYmVjYXVzZSB2aWRlbyBpcyBhdCBjYyBcIiArIHRoaXMudmlkZW9UcmFja0NDKTtcbiAgICAgICAgICAgIHRoaXMuY2xlYXJXYWl0aW5nRnJhZ21lbnQoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRHJvcCB3YWl0aW5nIGZyYWdtZW50IGlmIGFuIGVhcmxpZXIgZnJhZ21lbnQgaXMgbmVlZGVkXG4gICAgICAgICAgICB2YXIgX2J1ZmZlckluZm8gPSBCdWZmZXJIZWxwZXIuYnVmZmVySW5mbyh0aGlzLm1lZGlhQnVmZmVyLCB0aGlzLm1lZGlhLmN1cnJlbnRUaW1lLCBjb25maWcubWF4QnVmZmVySG9sZSk7XG5cbiAgICAgICAgICAgIHZhciB3YWl0aW5nRnJhZ21lbnRBdFBvc2l0aW9uID0gZnJhZ21lbnRXaXRoaW5Ub2xlcmFuY2VUZXN0KF9idWZmZXJJbmZvLmVuZCwgY29uZmlnLm1heEZyYWdMb29rVXBUb2xlcmFuY2UsIHdhaXRpbmdGcmFnLmZyYWcpO1xuXG4gICAgICAgICAgICBpZiAod2FpdGluZ0ZyYWdtZW50QXRQb3NpdGlvbiA8IDApIHtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIldhaXRpbmcgZnJhZ21lbnQgY2MgKFwiICsgd2FpdGluZ0ZyYWdDQyArIFwiKSBAIFwiICsgd2FpdGluZ0ZyYWcuZnJhZy5zdGFydCArIFwiIGNhbmNlbGxlZCBiZWNhdXNlIGFub3RoZXIgZnJhZ21lbnQgYXQgXCIgKyBfYnVmZmVySW5mby5lbmQgKyBcIiBpcyBuZWVkZWRcIik7XG4gICAgICAgICAgICAgIHRoaXMuY2xlYXJXYWl0aW5nRnJhZ21lbnQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgIH1cblxuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSBTdGF0ZS5TVE9QUEVEOlxuICAgICAgY2FzZSBTdGF0ZS5GUkFHX0xPQURJTkc6XG4gICAgICBjYXNlIFN0YXRlLlBBUlNJTkc6XG4gICAgICBjYXNlIFN0YXRlLlBBUlNFRDpcbiAgICAgIGNhc2UgU3RhdGUuRU5ERUQ6XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBicmVhaztcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmNsZWFyV2FpdGluZ0ZyYWdtZW50ID0gZnVuY3Rpb24gY2xlYXJXYWl0aW5nRnJhZ21lbnQoKSB7XG4gICAgdmFyIHdhaXRpbmdGcmFnID0gdGhpcy53YWl0aW5nRnJhZ21lbnQ7XG5cbiAgICBpZiAod2FpdGluZ0ZyYWcpIHtcbiAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50KHdhaXRpbmdGcmFnLmZyYWcpO1xuICAgICAgdGhpcy53YWl0aW5nRnJhZ21lbnQgPSBudWxsO1xuICAgICAgdGhpcy53YWl0aW5nVmlkZW9DQyA9IG51bGw7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFBdHRhY2hlZCA9IGZ1bmN0aW9uIG9uTWVkaWFBdHRhY2hlZChkYXRhKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYSA9IHRoaXMubWVkaWFCdWZmZXIgPSBkYXRhLm1lZGlhO1xuICAgIHRoaXMub252c2Vla2luZyA9IHRoaXMub25NZWRpYVNlZWtpbmcuYmluZCh0aGlzKTtcbiAgICB0aGlzLm9udmVuZGVkID0gdGhpcy5vbk1lZGlhRW5kZWQuYmluZCh0aGlzKTtcbiAgICBtZWRpYS5hZGRFdmVudExpc3RlbmVyKCdzZWVraW5nJywgdGhpcy5vbnZzZWVraW5nKTtcbiAgICBtZWRpYS5hZGRFdmVudExpc3RlbmVyKCdlbmRlZCcsIHRoaXMub252ZW5kZWQpO1xuICAgIHZhciBjb25maWcgPSB0aGlzLmNvbmZpZztcblxuICAgIGlmICh0aGlzLnRyYWNrcyAmJiBjb25maWcuYXV0b1N0YXJ0TG9hZCkge1xuICAgICAgdGhpcy5zdGFydExvYWQoY29uZmlnLnN0YXJ0UG9zaXRpb24pO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYURldGFjaGluZyA9IGZ1bmN0aW9uIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcblxuICAgIGlmIChtZWRpYSAmJiBtZWRpYS5lbmRlZCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnTVNFIGRldGFjaGluZyBhbmQgdmlkZW8gZW5kZWQsIHJlc2V0IHN0YXJ0UG9zaXRpb24nKTtcbiAgICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IHRoaXMubGFzdEN1cnJlbnRUaW1lID0gMDtcbiAgICB9IC8vIHJlbW92ZSB2aWRlbyBsaXN0ZW5lcnNcblxuXG4gICAgaWYgKG1lZGlhKSB7XG4gICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdzZWVraW5nJywgdGhpcy5vbnZzZWVraW5nKTtcbiAgICAgIG1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2VuZGVkJywgdGhpcy5vbnZlbmRlZCk7XG4gICAgICB0aGlzLm9udnNlZWtpbmcgPSB0aGlzLm9udnNlZWtlZCA9IHRoaXMub252ZW5kZWQgPSBudWxsO1xuICAgIH1cblxuICAgIHRoaXMubWVkaWEgPSB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy52aWRlb0J1ZmZlciA9IG51bGw7XG4gICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IGZhbHNlO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUFsbEZyYWdtZW50cygpO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgfTtcblxuICBfcHJvdG8ub25BdWRpb1RyYWNrc1VwZGF0ZWQgPSBmdW5jdGlvbiBvbkF1ZGlvVHJhY2tzVXBkYXRlZChkYXRhKSB7XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnYXVkaW8gdHJhY2tzIHVwZGF0ZWQnKTtcbiAgICB0aGlzLnRyYWNrcyA9IGRhdGEuYXVkaW9UcmFja3M7XG4gIH07XG5cbiAgX3Byb3RvLm9uQXVkaW9UcmFja1N3aXRjaGluZyA9IGZ1bmN0aW9uIG9uQXVkaW9UcmFja1N3aXRjaGluZyhkYXRhKSB7XG4gICAgLy8gaWYgYW55IFVSTCBmb3VuZCBvbiBuZXcgYXVkaW8gdHJhY2ssIGl0IGlzIGFuIGFsdGVybmF0ZSBhdWRpbyB0cmFja1xuICAgIHZhciBhbHRBdWRpbyA9ICEhZGF0YS51cmw7XG4gICAgdGhpcy50cmFja0lkID0gZGF0YS5pZDtcbiAgICB0aGlzLmZyYWdDdXJyZW50ID0gbnVsbDtcbiAgICB0aGlzLmNsZWFyV2FpdGluZ0ZyYWdtZW50KCk7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlBBVVNFRDsgLy8gZGVzdHJveSB1c2VsZXNzIGRlbXV4ZXIgd2hlbiBzd2l0Y2hpbmcgYXVkaW8gdG8gbWFpblxuXG4gICAgaWYgKCFhbHRBdWRpbykge1xuICAgICAgaWYgKHRoaXMuZGVtdXhlcikge1xuICAgICAgICB0aGlzLmRlbXV4ZXIuZGVzdHJveSgpO1xuICAgICAgICB0aGlzLmRlbXV4ZXIgPSBudWxsO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBzd2l0Y2hpbmcgdG8gYXVkaW8gdHJhY2ssIHN0YXJ0IHRpbWVyIGlmIG5vdCBhbHJlYWR5IHN0YXJ0ZWRcbiAgICAgIHRoaXMuc2V0SW50ZXJ2YWwoYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfVElDS19JTlRFUlZBTCk7XG4gICAgfSAvLyBzaG91bGQgd2Ugc3dpdGNoIHRyYWNrcyA/XG5cblxuICAgIGlmIChhbHRBdWRpbykge1xuICAgICAgdGhpcy5hdWRpb1N3aXRjaCA9IHRydWU7IC8vIG1haW4gYXVkaW8gdHJhY2sgYXJlIGhhbmRsZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXIsIGp1c3QgZG8gc29tZXRoaW5nIGlmIHN3aXRjaGluZyB0byBhbHQgYXVkaW8gdHJhY2tcblxuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgfVxuXG4gICAgdGhpcy50aWNrKCk7XG4gIH07XG5cbiAgX3Byb3RvLm9uQXVkaW9UcmFja0xvYWRlZCA9IGZ1bmN0aW9uIG9uQXVkaW9UcmFja0xvYWRlZChkYXRhKSB7XG4gICAgdmFyIG5ld0RldGFpbHMgPSBkYXRhLmRldGFpbHMsXG4gICAgICAgIHRyYWNrSWQgPSBkYXRhLmlkLFxuICAgICAgICB0cmFjayA9IHRoaXMudHJhY2tzW3RyYWNrSWRdLFxuICAgICAgICBjdXJEZXRhaWxzID0gdHJhY2suZGV0YWlscyxcbiAgICAgICAgZHVyYXRpb24gPSBuZXdEZXRhaWxzLnRvdGFsZHVyYXRpb24sXG4gICAgICAgIHNsaWRpbmcgPSAwO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJ0cmFjayBcIiArIHRyYWNrSWQgKyBcIiBsb2FkZWQgW1wiICsgbmV3RGV0YWlscy5zdGFydFNOICsgXCIsXCIgKyBuZXdEZXRhaWxzLmVuZFNOICsgXCJdLGR1cmF0aW9uOlwiICsgZHVyYXRpb24pO1xuXG4gICAgaWYgKG5ld0RldGFpbHMubGl2ZSB8fCBjdXJEZXRhaWxzICYmIGN1ckRldGFpbHMubGl2ZSkge1xuICAgICAgaWYgKGN1ckRldGFpbHMgJiYgbmV3RGV0YWlscy5mcmFnbWVudHMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyB3ZSBhbHJlYWR5IGhhdmUgZGV0YWlscyBmb3IgdGhhdCBsZXZlbCwgbWVyZ2UgdGhlbVxuICAgICAgICBtZXJnZURldGFpbHMoY3VyRGV0YWlscywgbmV3RGV0YWlscyk7XG4gICAgICAgIHNsaWRpbmcgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDsgLy8gVE9ET1xuICAgICAgICAvLyB0aGlzLmxpdmVTeW5jUG9zaXRpb24gPSB0aGlzLmNvbXB1dGVMaXZlUG9zaXRpb24oc2xpZGluZywgY3VyRGV0YWlscyk7XG5cbiAgICAgICAgaWYgKG5ld0RldGFpbHMuUFRTS25vd24pIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibGl2ZSBhdWRpbyBwbGF5bGlzdCBzbGlkaW5nOlwiICsgc2xpZGluZy50b0ZpeGVkKDMpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdsaXZlIGF1ZGlvIHBsYXlsaXN0IC0gb3V0ZGF0ZWQgUFRTLCB1bmtub3duIHNsaWRpbmcnKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbmV3RGV0YWlscy5QVFNLbm93biA9IGZhbHNlO1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdsaXZlIGF1ZGlvIHBsYXlsaXN0IC0gZmlyc3QgbG9hZCwgdW5rbm93biBzbGlkaW5nJyk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIG5ld0RldGFpbHMuUFRTS25vd24gPSBmYWxzZTtcbiAgICB9XG5cbiAgICB0cmFjay5kZXRhaWxzID0gbmV3RGV0YWlsczsgLy8gY29tcHV0ZSBzdGFydCBwb3NpdGlvblxuXG4gICAgaWYgKCF0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCkge1xuICAgICAgLy8gY29tcHV0ZSBzdGFydCBwb3NpdGlvbiBpZiBzZXQgdG8gLTEuIHVzZSBpdCBzdHJhaWdodCBhd2F5IGlmIHZhbHVlIGlzIGRlZmluZWRcbiAgICAgIGlmICh0aGlzLnN0YXJ0UG9zaXRpb24gPT09IC0xKSB7XG4gICAgICAgIC8vIGZpcnN0LCBjaGVjayBpZiBzdGFydCB0aW1lIG9mZnNldCBoYXMgYmVlbiBzZXQgaW4gcGxheWxpc3QsIGlmIHllcywgdXNlIHRoaXMgdmFsdWVcbiAgICAgICAgdmFyIHN0YXJ0VGltZU9mZnNldCA9IG5ld0RldGFpbHMuc3RhcnRUaW1lT2Zmc2V0O1xuXG4gICAgICAgIGlmIChPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHN0YXJ0VGltZU9mZnNldCkpIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwic3RhcnQgdGltZSBvZmZzZXQgZm91bmQgaW4gcGxheWxpc3QsIGFkanVzdCBzdGFydFBvc2l0aW9uIHRvIFwiICsgc3RhcnRUaW1lT2Zmc2V0KTtcbiAgICAgICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSBzdGFydFRpbWVPZmZzZXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaWYgKG5ld0RldGFpbHMubGl2ZSkge1xuICAgICAgICAgICAgdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5jb21wdXRlTGl2ZVBvc2l0aW9uKHNsaWRpbmcsIG5ld0RldGFpbHMpO1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImNvbXB1dGUgc3RhcnRQb3NpdGlvbiBmb3IgYXVkaW8tdHJhY2sgdG8gXCIgKyB0aGlzLnN0YXJ0UG9zaXRpb24pO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSAwO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb247XG4gICAgfSAvLyBvbmx5IHN3aXRjaCBiYXRjayB0byBJRExFIHN0YXRlIGlmIHdlIHdlcmUgd2FpdGluZyBmb3IgdHJhY2sgdG8gc3RhcnQgZG93bmxvYWRpbmcgYSBuZXcgZnJhZ21lbnRcblxuXG4gICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLldBSVRJTkdfVFJBQ0spIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIH0gLy8gdHJpZ2dlciBoYW5kbGVyIHJpZ2h0IG5vd1xuXG5cbiAgICB0aGlzLnRpY2soKTtcbiAgfTtcblxuICBfcHJvdG8ub25LZXlMb2FkZWQgPSBmdW5jdGlvbiBvbktleUxvYWRlZCgpIHtcbiAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuS0VZX0xPQURJTkcpIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgdGhpcy50aWNrKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkZyYWdMb2FkZWQgPSBmdW5jdGlvbiBvbkZyYWdMb2FkZWQoZGF0YSkge1xuICAgIHZhciBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQsXG4gICAgICAgIGZyYWdMb2FkZWQgPSBkYXRhLmZyYWc7XG5cbiAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuRlJBR19MT0FESU5HICYmIGZyYWdDdXJyZW50ICYmIGZyYWdMb2FkZWQudHlwZSA9PT0gJ2F1ZGlvJyAmJiBmcmFnTG9hZGVkLmxldmVsID09PSBmcmFnQ3VycmVudC5sZXZlbCAmJiBmcmFnTG9hZGVkLnNuID09PSBmcmFnQ3VycmVudC5zbikge1xuICAgICAgdmFyIHRyYWNrID0gdGhpcy50cmFja3NbdGhpcy50cmFja0lkXSxcbiAgICAgICAgICBkZXRhaWxzID0gdHJhY2suZGV0YWlscyxcbiAgICAgICAgICBkdXJhdGlvbiA9IGRldGFpbHMudG90YWxkdXJhdGlvbixcbiAgICAgICAgICB0cmFja0lkID0gZnJhZ0N1cnJlbnQubGV2ZWwsXG4gICAgICAgICAgc24gPSBmcmFnQ3VycmVudC5zbixcbiAgICAgICAgICBjYyA9IGZyYWdDdXJyZW50LmNjLFxuICAgICAgICAgIGF1ZGlvQ29kZWMgPSB0aGlzLmNvbmZpZy5kZWZhdWx0QXVkaW9Db2RlYyB8fCB0cmFjay5hdWRpb0NvZGVjIHx8ICdtcDRhLjQwLjInLFxuICAgICAgICAgIHN0YXRzID0gdGhpcy5zdGF0cyA9IGRhdGEuc3RhdHM7XG5cbiAgICAgIGlmIChzbiA9PT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgc3RhdHMudHBhcnNlZCA9IHN0YXRzLnRidWZmZXJlZCA9IGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICBkZXRhaWxzLmluaXRTZWdtZW50LmRhdGEgPSBkYXRhLnBheWxvYWQ7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0JVRkZFUkVELCB7XG4gICAgICAgICAgc3RhdHM6IHN0YXRzLFxuICAgICAgICAgIGZyYWc6IGZyYWdDdXJyZW50LFxuICAgICAgICAgIGlkOiAnYXVkaW8nXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLnRpY2soKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5QQVJTSU5HOyAvLyB0cmFuc211eCB0aGUgTVBFRy1UUyBkYXRhIHRvIElTTy1CTUZGIHNlZ21lbnRzXG5cbiAgICAgICAgdGhpcy5hcHBlbmRlZCA9IGZhbHNlO1xuXG4gICAgICAgIGlmICghdGhpcy5kZW11eGVyKSB7XG4gICAgICAgICAgdGhpcy5kZW11eGVyID0gbmV3IGRlbXV4X2RlbXV4ZXIodGhpcy5obHMsICdhdWRpbycpO1xuICAgICAgICB9IC8vIENoZWNrIGlmIHdlIGhhdmUgdmlkZW8gaW5pdFBUU1xuICAgICAgICAvLyBJZiBub3Qgd2UgbmVlZCB0byB3YWl0IGZvciBpdFxuXG5cbiAgICAgICAgdmFyIGluaXRQVFMgPSB0aGlzLmluaXRQVFNbY2NdO1xuICAgICAgICB2YXIgaW5pdFNlZ21lbnREYXRhID0gZGV0YWlscy5pbml0U2VnbWVudCA/IGRldGFpbHMuaW5pdFNlZ21lbnQuZGF0YSA6IFtdO1xuXG4gICAgICAgIGlmIChpbml0UFRTICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICB0aGlzLnBlbmRpbmdCdWZmZXJpbmcgPSB0cnVlO1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJEZW11eGluZyBcIiArIHNuICsgXCIgb2YgW1wiICsgZGV0YWlscy5zdGFydFNOICsgXCIgLFwiICsgZGV0YWlscy5lbmRTTiArIFwiXSx0cmFjayBcIiArIHRyYWNrSWQpOyAvLyB0aW1lIE9mZnNldCBpcyBhY2N1cmF0ZSBpZiBsZXZlbCBQVFMgaXMga25vd24sIG9yIGlmIHBsYXlsaXN0IGlzIG5vdCBzbGlkaW5nIChub3QgbGl2ZSlcblxuICAgICAgICAgIHZhciBhY2N1cmF0ZVRpbWVPZmZzZXQgPSBmYWxzZTsgLy8gZGV0YWlscy5QVFNLbm93biB8fCAhZGV0YWlscy5saXZlO1xuXG4gICAgICAgICAgdGhpcy5kZW11eGVyLnB1c2goZGF0YS5wYXlsb2FkLCBpbml0U2VnbWVudERhdGEsIGF1ZGlvQ29kZWMsIG51bGwsIGZyYWdDdXJyZW50LCBkdXJhdGlvbiwgYWNjdXJhdGVUaW1lT2Zmc2V0LCBpbml0UFRTKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiVW5rbm93biB2aWRlbyBQVFMgZm9yIGNjIFwiICsgY2MgKyBcIiwgd2FpdGluZyBmb3IgdmlkZW8gUFRTIGJlZm9yZSBkZW11eGluZyBhdWRpbyBmcmFnIFwiICsgc24gKyBcIiBvZiBbXCIgKyBkZXRhaWxzLnN0YXJ0U04gKyBcIiAsXCIgKyBkZXRhaWxzLmVuZFNOICsgXCJdLHRyYWNrIFwiICsgdHJhY2tJZCk7XG4gICAgICAgICAgdGhpcy53YWl0aW5nRnJhZ21lbnQgPSBkYXRhO1xuICAgICAgICAgIHRoaXMud2FpdGluZ1ZpZGVvQ0MgPSB0aGlzLnZpZGVvVHJhY2tDQztcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuV0FJVElOR19JTklUX1BUUztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuZnJhZ0xvYWRFcnJvciA9IDA7XG4gIH07XG5cbiAgX3Byb3RvLm9uRnJhZ1BhcnNpbmdJbml0U2VnbWVudCA9IGZ1bmN0aW9uIG9uRnJhZ1BhcnNpbmdJbml0U2VnbWVudChkYXRhKSB7XG4gICAgdmFyIGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudDtcbiAgICB2YXIgZnJhZ05ldyA9IGRhdGEuZnJhZztcblxuICAgIGlmIChmcmFnQ3VycmVudCAmJiBkYXRhLmlkID09PSAnYXVkaW8nICYmIGZyYWdOZXcuc24gPT09IGZyYWdDdXJyZW50LnNuICYmIGZyYWdOZXcubGV2ZWwgPT09IGZyYWdDdXJyZW50LmxldmVsICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcpIHtcbiAgICAgIHZhciB0cmFja3MgPSBkYXRhLnRyYWNrcyxcbiAgICAgICAgICB0cmFjazsgLy8gZGVsZXRlIGFueSB2aWRlbyB0cmFjayBmb3VuZCBvbiBhdWRpbyBkZW11eGVyXG5cbiAgICAgIGlmICh0cmFja3MudmlkZW8pIHtcbiAgICAgICAgZGVsZXRlIHRyYWNrcy52aWRlbztcbiAgICAgIH0gLy8gaW5jbHVkZSBsZXZlbENvZGVjIGluIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3NcblxuXG4gICAgICB0cmFjayA9IHRyYWNrcy5hdWRpbztcblxuICAgICAgaWYgKHRyYWNrKSB7XG4gICAgICAgIHRyYWNrLmxldmVsQ29kZWMgPSB0cmFjay5jb2RlYztcbiAgICAgICAgdHJhY2suaWQgPSBkYXRhLmlkO1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0NPREVDUywgdHJhY2tzKTtcbiAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImF1ZGlvIHRyYWNrOmF1ZGlvLGNvbnRhaW5lcjpcIiArIHRyYWNrLmNvbnRhaW5lciArIFwiLGNvZGVjc1tsZXZlbC9wYXJzZWRdPVtcIiArIHRyYWNrLmxldmVsQ29kZWMgKyBcIi9cIiArIHRyYWNrLmNvZGVjICsgXCJdXCIpO1xuICAgICAgICB2YXIgaW5pdFNlZ21lbnQgPSB0cmFjay5pbml0U2VnbWVudDtcblxuICAgICAgICBpZiAoaW5pdFNlZ21lbnQpIHtcbiAgICAgICAgICB2YXIgYXBwZW5kT2JqID0ge1xuICAgICAgICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgICAgICAgIGRhdGE6IGluaXRTZWdtZW50LFxuICAgICAgICAgICAgcGFyZW50OiAnYXVkaW8nLFxuICAgICAgICAgICAgY29udGVudDogJ2luaXRTZWdtZW50J1xuICAgICAgICAgIH07XG5cbiAgICAgICAgICBpZiAodGhpcy5hdWRpb1N3aXRjaCkge1xuICAgICAgICAgICAgdGhpcy5wZW5kaW5nRGF0YSA9IFthcHBlbmRPYmpdO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmFwcGVuZGVkID0gdHJ1ZTsgLy8gYXJtIHBlbmRpbmcgQnVmZmVyaW5nIGZsYWcgYmVmb3JlIGFwcGVuZGluZyBhIHNlZ21lbnRcblxuICAgICAgICAgICAgdGhpcy5wZW5kaW5nQnVmZmVyaW5nID0gdHJ1ZTtcbiAgICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfQVBQRU5ESU5HLCBhcHBlbmRPYmopO1xuICAgICAgICAgIH1cbiAgICAgICAgfSAvLyB0cmlnZ2VyIGhhbmRsZXIgcmlnaHQgbm93XG5cblxuICAgICAgICB0aGlzLnRpY2soKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uRnJhZ1BhcnNpbmdEYXRhID0gZnVuY3Rpb24gb25GcmFnUGFyc2luZ0RhdGEoZGF0YSkge1xuICAgIHZhciBfdGhpczIgPSB0aGlzO1xuXG4gICAgdmFyIGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudDtcbiAgICB2YXIgZnJhZ05ldyA9IGRhdGEuZnJhZztcblxuICAgIGlmIChmcmFnQ3VycmVudCAmJiBkYXRhLmlkID09PSAnYXVkaW8nICYmIGRhdGEudHlwZSA9PT0gJ2F1ZGlvJyAmJiBmcmFnTmV3LnNuID09PSBmcmFnQ3VycmVudC5zbiAmJiBmcmFnTmV3LmxldmVsID09PSBmcmFnQ3VycmVudC5sZXZlbCAmJiB0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICB2YXIgdHJhY2tJZCA9IHRoaXMudHJhY2tJZCxcbiAgICAgICAgICB0cmFjayA9IHRoaXMudHJhY2tzW3RyYWNrSWRdLFxuICAgICAgICAgIGhscyA9IHRoaXMuaGxzO1xuXG4gICAgICBpZiAoIU9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoZGF0YS5lbmRQVFMpKSB7XG4gICAgICAgIGRhdGEuZW5kUFRTID0gZGF0YS5zdGFydFBUUyArIGZyYWdDdXJyZW50LmR1cmF0aW9uO1xuICAgICAgICBkYXRhLmVuZERUUyA9IGRhdGEuc3RhcnREVFMgKyBmcmFnQ3VycmVudC5kdXJhdGlvbjtcbiAgICAgIH1cblxuICAgICAgZnJhZ0N1cnJlbnQuYWRkRWxlbWVudGFyeVN0cmVhbShFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU8pO1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInBhcnNlZCBcIiArIGRhdGEudHlwZSArIFwiLFBUUzpbXCIgKyBkYXRhLnN0YXJ0UFRTLnRvRml4ZWQoMykgKyBcIixcIiArIGRhdGEuZW5kUFRTLnRvRml4ZWQoMykgKyBcIl0sRFRTOltcIiArIGRhdGEuc3RhcnREVFMudG9GaXhlZCgzKSArIFwiL1wiICsgZGF0YS5lbmREVFMudG9GaXhlZCgzKSArIFwiXSxuYjpcIiArIGRhdGEubmIpO1xuICAgICAgdXBkYXRlRnJhZ1BUU0RUUyh0cmFjay5kZXRhaWxzLCBmcmFnQ3VycmVudCwgZGF0YS5zdGFydFBUUywgZGF0YS5lbmRQVFMpO1xuICAgICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICAgIHZhciBhcHBlbmRPbkJ1ZmZlckZsdXNoID0gZmFsc2U7IC8vIE9ubHkgZmx1c2ggYXVkaW8gZnJvbSBvbGQgYXVkaW8gdHJhY2tzIHdoZW4gUFRTIGlzIGtub3duIG9uIG5ldyBhdWRpbyB0cmFja1xuXG4gICAgICBpZiAodGhpcy5hdWRpb1N3aXRjaCkge1xuICAgICAgICBpZiAobWVkaWEgJiYgbWVkaWEucmVhZHlTdGF0ZSkge1xuICAgICAgICAgIHZhciBjdXJyZW50VGltZSA9IG1lZGlhLmN1cnJlbnRUaW1lO1xuICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3N3aXRjaGluZyBhdWRpbyB0cmFjayA6IGN1cnJlbnRUaW1lOicgKyBjdXJyZW50VGltZSk7XG5cbiAgICAgICAgICBpZiAoY3VycmVudFRpbWUgPj0gZGF0YS5zdGFydFBUUykge1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnc3dpdGNoaW5nIGF1ZGlvIHRyYWNrIDogZmx1c2hpbmcgYWxsIGF1ZGlvJyk7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuQlVGRkVSX0ZMVVNISU5HO1xuICAgICAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5CVUZGRVJfRkxVU0hJTkcsIHtcbiAgICAgICAgICAgICAgc3RhcnRPZmZzZXQ6IDAsXG4gICAgICAgICAgICAgIGVuZE9mZnNldDogTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZLFxuICAgICAgICAgICAgICB0eXBlOiAnYXVkaW8nXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGFwcGVuZE9uQnVmZmVyRmx1c2ggPSB0cnVlOyAvLyBMZXRzIGFubm91bmNlIHRoYXQgdGhlIGluaXRpYWwgYXVkaW8gdHJhY2sgc3dpdGNoIGZsdXNoIG9jY3VyXG5cbiAgICAgICAgICAgIHRoaXMuYXVkaW9Td2l0Y2ggPSBmYWxzZTtcbiAgICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfU1dJVENIRUQsIHtcbiAgICAgICAgICAgICAgaWQ6IHRyYWNrSWRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBMZXRzIGFubm91bmNlIHRoYXQgdGhlIGluaXRpYWwgYXVkaW8gdHJhY2sgc3dpdGNoIGZsdXNoIG9jY3VyXG4gICAgICAgICAgdGhpcy5hdWRpb1N3aXRjaCA9IGZhbHNlO1xuICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQVVESU9fVFJBQ0tfU1dJVENIRUQsIHtcbiAgICAgICAgICAgIGlkOiB0cmFja0lkXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdmFyIHBlbmRpbmdEYXRhID0gdGhpcy5wZW5kaW5nRGF0YTtcblxuICAgICAgaWYgKCFwZW5kaW5nRGF0YSkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ud2FybignQXBwYXJlbnRseSBhdHRlbXB0IHRvIGVucXVldWUgbWVkaWEgcGF5bG9hZCB3aXRob3V0IGNvZGVjIGluaXRpYWxpemF0aW9uIGRhdGEgdXBmcm9udCcpO1xuICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5NRURJQV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBudWxsLFxuICAgICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmICghdGhpcy5hdWRpb1N3aXRjaCkge1xuICAgICAgICBbZGF0YS5kYXRhMSwgZGF0YS5kYXRhMl0uZm9yRWFjaChmdW5jdGlvbiAoYnVmZmVyKSB7XG4gICAgICAgICAgaWYgKGJ1ZmZlciAmJiBidWZmZXIubGVuZ3RoKSB7XG4gICAgICAgICAgICBwZW5kaW5nRGF0YS5wdXNoKHtcbiAgICAgICAgICAgICAgdHlwZTogZGF0YS50eXBlLFxuICAgICAgICAgICAgICBkYXRhOiBidWZmZXIsXG4gICAgICAgICAgICAgIHBhcmVudDogJ2F1ZGlvJyxcbiAgICAgICAgICAgICAgY29udGVudDogJ2RhdGEnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGlmICghYXBwZW5kT25CdWZmZXJGbHVzaCAmJiBwZW5kaW5nRGF0YS5sZW5ndGgpIHtcbiAgICAgICAgICBwZW5kaW5nRGF0YS5mb3JFYWNoKGZ1bmN0aW9uIChhcHBlbmRPYmopIHtcbiAgICAgICAgICAgIC8vIG9ubHkgYXBwZW5kIGluIFBBUlNJTkcgc3RhdGUgKHJhdGlvbmFsZSBpcyB0aGF0IGFuIGFwcGVuZGluZyBlcnJvciBjb3VsZCBoYXBwZW4gc3luY2hyb25vdXNseSBvbiBmaXJzdCBzZWdtZW50IGFwcGVuZGluZylcbiAgICAgICAgICAgIC8vIGluIHRoYXQgY2FzZSBpdCBpcyB1c2VsZXNzIHRvIGFwcGVuZCBmb2xsb3dpbmcgc2VnbWVudHNcbiAgICAgICAgICAgIGlmIChfdGhpczIuc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcpIHtcbiAgICAgICAgICAgICAgLy8gYXJtIHBlbmRpbmcgQnVmZmVyaW5nIGZsYWcgYmVmb3JlIGFwcGVuZGluZyBhIHNlZ21lbnRcbiAgICAgICAgICAgICAgX3RoaXMyLnBlbmRpbmdCdWZmZXJpbmcgPSB0cnVlO1xuXG4gICAgICAgICAgICAgIF90aGlzMi5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRJTkcsIGFwcGVuZE9iaik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgdGhpcy5wZW5kaW5nRGF0YSA9IFtdO1xuICAgICAgICAgIHRoaXMuYXBwZW5kZWQgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9IC8vIHRyaWdnZXIgaGFuZGxlciByaWdodCBub3dcblxuXG4gICAgICB0aGlzLnRpY2soKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uRnJhZ1BhcnNlZCA9IGZ1bmN0aW9uIG9uRnJhZ1BhcnNlZChkYXRhKSB7XG4gICAgdmFyIGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudDtcbiAgICB2YXIgZnJhZ05ldyA9IGRhdGEuZnJhZztcblxuICAgIGlmIChmcmFnQ3VycmVudCAmJiBkYXRhLmlkID09PSAnYXVkaW8nICYmIGZyYWdOZXcuc24gPT09IGZyYWdDdXJyZW50LnNuICYmIGZyYWdOZXcubGV2ZWwgPT09IGZyYWdDdXJyZW50LmxldmVsICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcpIHtcbiAgICAgIHRoaXMuc3RhdHMudHBhcnNlZCA9IGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlBBUlNFRDtcblxuICAgICAgdGhpcy5fY2hlY2tBcHBlbmRlZFBhcnNlZCgpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25CdWZmZXJSZXNldCA9IGZ1bmN0aW9uIG9uQnVmZmVyUmVzZXQoKSB7XG4gICAgLy8gcmVzZXQgcmVmZXJlbmNlIHRvIHNvdXJjZWJ1ZmZlcnNcbiAgICB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy52aWRlb0J1ZmZlciA9IG51bGw7XG4gICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IGZhbHNlO1xuICB9O1xuXG4gIF9wcm90by5vbkJ1ZmZlckNyZWF0ZWQgPSBmdW5jdGlvbiBvbkJ1ZmZlckNyZWF0ZWQoZGF0YSkge1xuICAgIHZhciBhdWRpb1RyYWNrID0gZGF0YS50cmFja3MuYXVkaW87XG5cbiAgICBpZiAoYXVkaW9UcmFjaykge1xuICAgICAgdGhpcy5tZWRpYUJ1ZmZlciA9IGF1ZGlvVHJhY2suYnVmZmVyO1xuICAgICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IHRydWU7XG4gICAgfVxuXG4gICAgaWYgKGRhdGEudHJhY2tzLnZpZGVvKSB7XG4gICAgICB0aGlzLnZpZGVvQnVmZmVyID0gZGF0YS50cmFja3MudmlkZW8uYnVmZmVyO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25CdWZmZXJBcHBlbmRlZCA9IGZ1bmN0aW9uIG9uQnVmZmVyQXBwZW5kZWQoZGF0YSkge1xuICAgIGlmIChkYXRhLnBhcmVudCA9PT0gJ2F1ZGlvJykge1xuICAgICAgdmFyIHN0YXRlID0gdGhpcy5zdGF0ZTtcblxuICAgICAgaWYgKHN0YXRlID09PSBTdGF0ZS5QQVJTSU5HIHx8IHN0YXRlID09PSBTdGF0ZS5QQVJTRUQpIHtcbiAgICAgICAgLy8gY2hlY2sgaWYgYWxsIGJ1ZmZlcnMgaGF2ZSBiZWVuIGFwcGVuZGVkXG4gICAgICAgIHRoaXMucGVuZGluZ0J1ZmZlcmluZyA9IGRhdGEucGVuZGluZyA+IDA7XG5cbiAgICAgICAgdGhpcy5fY2hlY2tBcHBlbmRlZFBhcnNlZCgpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uX2NoZWNrQXBwZW5kZWRQYXJzZWQgPSBmdW5jdGlvbiBfY2hlY2tBcHBlbmRlZFBhcnNlZCgpIHtcbiAgICAvLyB0cmlnZ2VyIGhhbmRsZXIgcmlnaHQgbm93XG4gICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNFRCAmJiAoIXRoaXMuYXBwZW5kZWQgfHwgIXRoaXMucGVuZGluZ0J1ZmZlcmluZykpIHtcbiAgICAgIHZhciBmcmFnID0gdGhpcy5mcmFnQ3VycmVudCxcbiAgICAgICAgICBzdGF0cyA9IHRoaXMuc3RhdHMsXG4gICAgICAgICAgaGxzID0gdGhpcy5obHM7XG5cbiAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gZnJhZztcbiAgICAgICAgc3RhdHMudGJ1ZmZlcmVkID0gYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19CVUZGRVJFRCwge1xuICAgICAgICAgIHN0YXRzOiBzdGF0cyxcbiAgICAgICAgICBmcmFnOiBmcmFnLFxuICAgICAgICAgIGlkOiAnYXVkaW8nXG4gICAgICAgIH0pO1xuICAgICAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhQnVmZmVyID8gdGhpcy5tZWRpYUJ1ZmZlciA6IHRoaXMubWVkaWE7XG5cbiAgICAgICAgaWYgKG1lZGlhKSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcImF1ZGlvIGJ1ZmZlcmVkIDogXCIgKyB0aW1lX3Jhbmdlcy50b1N0cmluZyhtZWRpYS5idWZmZXJlZCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuYXVkaW9Td2l0Y2ggJiYgdGhpcy5hcHBlbmRlZCkge1xuICAgICAgICAgIHRoaXMuYXVkaW9Td2l0Y2ggPSBmYWxzZTtcbiAgICAgICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkFVRElPX1RSQUNLX1NXSVRDSEVELCB7XG4gICAgICAgICAgICBpZDogdGhpcy50cmFja0lkXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIH1cblxuICAgICAgdGhpcy50aWNrKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkVycm9yID0gZnVuY3Rpb24gb25FcnJvcihkYXRhKSB7XG4gICAgdmFyIGZyYWcgPSBkYXRhLmZyYWc7IC8vIGRvbid0IGhhbmRsZSBmcmFnIGVycm9yIG5vdCByZWxhdGVkIHRvIGF1ZGlvIGZyYWdtZW50XG5cbiAgICBpZiAoZnJhZyAmJiBmcmFnLnR5cGUgIT09ICdhdWRpbycpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBzd2l0Y2ggKGRhdGEuZGV0YWlscykge1xuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uRlJBR19MT0FEX1RJTUVPVVQ6XG4gICAgICAgIHZhciBfZnJhZyA9IGRhdGEuZnJhZzsgLy8gZG9uJ3QgaGFuZGxlIGZyYWcgZXJyb3Igbm90IHJlbGF0ZWQgdG8gYXVkaW8gZnJhZ21lbnRcblxuICAgICAgICBpZiAoX2ZyYWcgJiYgX2ZyYWcudHlwZSAhPT0gJ2F1ZGlvJykge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFkYXRhLmZhdGFsKSB7XG4gICAgICAgICAgdmFyIGxvYWRFcnJvciA9IHRoaXMuZnJhZ0xvYWRFcnJvcjtcblxuICAgICAgICAgIGlmIChsb2FkRXJyb3IpIHtcbiAgICAgICAgICAgIGxvYWRFcnJvcisrO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsb2FkRXJyb3IgPSAxO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHZhciBjb25maWcgPSB0aGlzLmNvbmZpZztcblxuICAgICAgICAgIGlmIChsb2FkRXJyb3IgPD0gY29uZmlnLmZyYWdMb2FkaW5nTWF4UmV0cnkpIHtcbiAgICAgICAgICAgIHRoaXMuZnJhZ0xvYWRFcnJvciA9IGxvYWRFcnJvcjsgLy8gZXhwb25lbnRpYWwgYmFja29mZiBjYXBwZWQgdG8gY29uZmlnLmZyYWdMb2FkaW5nTWF4UmV0cnlUaW1lb3V0XG5cbiAgICAgICAgICAgIHZhciBkZWxheSA9IE1hdGgubWluKE1hdGgucG93KDIsIGxvYWRFcnJvciAtIDEpICogY29uZmlnLmZyYWdMb2FkaW5nUmV0cnlEZWxheSwgY29uZmlnLmZyYWdMb2FkaW5nTWF4UmV0cnlUaW1lb3V0KTtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKFwiQXVkaW9TdHJlYW1Db250cm9sbGVyOiBmcmFnIGxvYWRpbmcgZmFpbGVkLCByZXRyeSBpbiBcIiArIGRlbGF5ICsgXCIgbXNcIik7XG4gICAgICAgICAgICB0aGlzLnJldHJ5RGF0ZSA9IGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpICsgZGVsYXk7IC8vIHJldHJ5IGxvYWRpbmcgc3RhdGVcblxuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoXCJBdWRpb1N0cmVhbUNvbnRyb2xsZXI6IFwiICsgZGF0YS5kZXRhaWxzICsgXCIgcmVhY2hlcyBtYXggcmV0cnksIHJlZGlzcGF0Y2ggYXMgZmF0YWwgLi4uXCIpOyAvLyBzd2l0Y2ggZXJyb3IgdG8gZmF0YWxcblxuICAgICAgICAgICAgZGF0YS5mYXRhbCA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRVJST1I7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLkFVRElPX1RSQUNLX0xPQURfRVJST1I6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5BVURJT19UUkFDS19MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgZXJyb3JzW1wiRXJyb3JEZXRhaWxzXCJdLktFWV9MT0FEX1RJTUVPVVQ6XG4gICAgICAgIC8vICB3aGVuIGluIEVSUk9SIHN0YXRlLCBkb24ndCBzd2l0Y2ggYmFjayB0byBJRExFIHN0YXRlIGluIGNhc2UgYSBub24tZmF0YWwgZXJyb3IgaXMgcmVjZWl2ZWRcbiAgICAgICAgaWYgKHRoaXMuc3RhdGUgIT09IFN0YXRlLkVSUk9SKSB7XG4gICAgICAgICAgLy8gaWYgZmF0YWwgZXJyb3IsIHN0b3AgcHJvY2Vzc2luZywgb3RoZXJ3aXNlIG1vdmUgdG8gSURMRSB0byByZXRyeSBsb2FkaW5nXG4gICAgICAgICAgdGhpcy5zdGF0ZSA9IGRhdGEuZmF0YWwgPyBTdGF0ZS5FUlJPUiA6IFN0YXRlLklETEU7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJBdWRpb1N0cmVhbUNvbnRyb2xsZXI6IFwiICsgZGF0YS5kZXRhaWxzICsgXCIgd2hpbGUgbG9hZGluZyBmcmFnLCBub3cgc3dpdGNoaW5nIHRvIFwiICsgdGhpcy5zdGF0ZSArIFwiIHN0YXRlIC4uLlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlIGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5CVUZGRVJfRlVMTF9FUlJPUjpcbiAgICAgICAgLy8gaWYgaW4gYXBwZW5kaW5nIHN0YXRlXG4gICAgICAgIGlmIChkYXRhLnBhcmVudCA9PT0gJ2F1ZGlvJyAmJiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuUEFSU0lORyB8fCB0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTRUQpKSB7XG4gICAgICAgICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlcixcbiAgICAgICAgICAgICAgY3VycmVudFRpbWUgPSB0aGlzLm1lZGlhLmN1cnJlbnRUaW1lLFxuICAgICAgICAgICAgICBtZWRpYUJ1ZmZlcmVkID0gbWVkaWEgJiYgQnVmZmVySGVscGVyLmlzQnVmZmVyZWQobWVkaWEsIGN1cnJlbnRUaW1lKSAmJiBCdWZmZXJIZWxwZXIuaXNCdWZmZXJlZChtZWRpYSwgY3VycmVudFRpbWUgKyAwLjUpOyAvLyByZWR1Y2UgbWF4IGJ1ZiBsZW4gaWYgY3VycmVudCBwb3NpdGlvbiBpcyBidWZmZXJlZFxuXG4gICAgICAgICAgaWYgKG1lZGlhQnVmZmVyZWQpIHtcbiAgICAgICAgICAgIHZhciBfY29uZmlnID0gdGhpcy5jb25maWc7XG5cbiAgICAgICAgICAgIGlmIChfY29uZmlnLm1heE1heEJ1ZmZlckxlbmd0aCA+PSBfY29uZmlnLm1heEJ1ZmZlckxlbmd0aCkge1xuICAgICAgICAgICAgICAvLyByZWR1Y2UgbWF4IGJ1ZmZlciBsZW5ndGggYXMgaXQgbWlnaHQgYmUgdG9vIGhpZ2guIHdlIGRvIHRoaXMgdG8gYXZvaWQgbG9vcCBmbHVzaGluZyAuLi5cbiAgICAgICAgICAgICAgX2NvbmZpZy5tYXhNYXhCdWZmZXJMZW5ndGggLz0gMjtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJBdWRpb1N0cmVhbUNvbnRyb2xsZXI6IHJlZHVjZSBtYXggYnVmZmVyIGxlbmd0aCB0byBcIiArIF9jb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoICsgXCJzXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gY3VycmVudCBwb3NpdGlvbiBpcyBub3QgYnVmZmVyZWQsIGJ1dCBicm93c2VyIGlzIHN0aWxsIGNvbXBsYWluaW5nIGFib3V0IGJ1ZmZlciBmdWxsIGVycm9yXG4gICAgICAgICAgICAvLyB0aGlzIGhhcHBlbnMgb24gSUUvRWRnZSwgcmVmZXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvcHVsbC83MDhcbiAgICAgICAgICAgIC8vIGluIHRoYXQgY2FzZSBmbHVzaCB0aGUgd2hvbGUgYXVkaW8gYnVmZmVyIHRvIHJlY292ZXJcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdBdWRpb1N0cmVhbUNvbnRyb2xsZXI6IGJ1ZmZlciBmdWxsIGVycm9yIGFsc28gbWVkaWEuY3VycmVudFRpbWUgaXMgbm90IGJ1ZmZlcmVkLCBmbHVzaCBhdWRpbyBidWZmZXInKTtcbiAgICAgICAgICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBudWxsOyAvLyBmbHVzaCBldmVyeXRoaW5nXG5cbiAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5CVUZGRVJfRkxVU0hJTkc7XG4gICAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQlVGRkVSX0ZMVVNISU5HLCB7XG4gICAgICAgICAgICAgIHN0YXJ0T2Zmc2V0OiAwLFxuICAgICAgICAgICAgICBlbmRPZmZzZXQ6IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSxcbiAgICAgICAgICAgICAgdHlwZTogJ2F1ZGlvJ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25CdWZmZXJGbHVzaGVkID0gZnVuY3Rpb24gb25CdWZmZXJGbHVzaGVkKCkge1xuICAgIHZhciBfdGhpczMgPSB0aGlzO1xuXG4gICAgdmFyIHBlbmRpbmdEYXRhID0gdGhpcy5wZW5kaW5nRGF0YTtcblxuICAgIGlmIChwZW5kaW5nRGF0YSAmJiBwZW5kaW5nRGF0YS5sZW5ndGgpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ0F1ZGlvU3RyZWFtQ29udHJvbGxlcjogYXBwZW5kaW5nIHBlbmRpbmcgYXVkaW8gZGF0YSBhZnRlciBidWZmZXIgZmx1c2hlZCcpO1xuICAgICAgcGVuZGluZ0RhdGEuZm9yRWFjaChmdW5jdGlvbiAoYXBwZW5kT2JqKSB7XG4gICAgICAgIF90aGlzMy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkJVRkZFUl9BUFBFTkRJTkcsIGFwcGVuZE9iaik7XG4gICAgICB9KTtcbiAgICAgIHRoaXMuYXBwZW5kZWQgPSB0cnVlO1xuICAgICAgdGhpcy5wZW5kaW5nRGF0YSA9IFtdO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlBBUlNFRDtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gbW92ZSB0byBJRExFIG9uY2UgZmx1c2ggY29tcGxldGUuIHRoaXMgc2hvdWxkIHRyaWdnZXIgbmV3IGZyYWdtZW50IGxvYWRpbmdcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFOyAvLyByZXNldCByZWZlcmVuY2UgdG8gZnJhZ1xuXG4gICAgICB0aGlzLmZyYWdQcmV2aW91cyA9IG51bGw7XG4gICAgICB0aGlzLnRpY2soKTtcbiAgICB9XG4gIH07XG5cbiAgYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoQXVkaW9TdHJlYW1Db250cm9sbGVyLCBbe1xuICAgIGtleTogXCJzdGF0ZVwiLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KG5leHRTdGF0ZSkge1xuICAgICAgaWYgKHRoaXMuc3RhdGUgIT09IG5leHRTdGF0ZSkge1xuICAgICAgICB2YXIgcHJldmlvdXNTdGF0ZSA9IHRoaXMuc3RhdGU7XG4gICAgICAgIHRoaXMuX3N0YXRlID0gbmV4dFN0YXRlO1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiYXVkaW8gc3RyZWFtOlwiICsgcHJldmlvdXNTdGF0ZSArIFwiLT5cIiArIG5leHRTdGF0ZSk7XG4gICAgICB9XG4gICAgfSxcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLl9zdGF0ZTtcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gQXVkaW9TdHJlYW1Db250cm9sbGVyO1xufShiYXNlX3N0cmVhbV9jb250cm9sbGVyX0Jhc2VTdHJlYW1Db250cm9sbGVyKTtcblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXIgPSAoYXVkaW9fc3RyZWFtX2NvbnRyb2xsZXJfQXVkaW9TdHJlYW1Db250cm9sbGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL3Z0dGN1ZS5qc1xuLyoqXG4gKiBDb3B5cmlnaHQgMjAxMyB2dHQuanMgQ29udHJpYnV0b3JzXG4gKlxuICogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbiAqIHlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbiAqIFlvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuICpcbiAqICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgdnR0Y3VlID0gKChmdW5jdGlvbiAoKSB7XG4gIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB3aW5kb3cuVlRUQ3VlKSB7XG4gICAgcmV0dXJuIHdpbmRvdy5WVFRDdWU7XG4gIH1cblxuICB2YXIgYXV0b0tleXdvcmQgPSAnYXV0byc7XG4gIHZhciBkaXJlY3Rpb25TZXR0aW5nID0ge1xuICAgICcnOiB0cnVlLFxuICAgIGxyOiB0cnVlLFxuICAgIHJsOiB0cnVlXG4gIH07XG4gIHZhciBhbGlnblNldHRpbmcgPSB7XG4gICAgc3RhcnQ6IHRydWUsXG4gICAgbWlkZGxlOiB0cnVlLFxuICAgIGVuZDogdHJ1ZSxcbiAgICBsZWZ0OiB0cnVlLFxuICAgIHJpZ2h0OiB0cnVlXG4gIH07XG5cbiAgZnVuY3Rpb24gZmluZERpcmVjdGlvblNldHRpbmcodmFsdWUpIHtcbiAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnc3RyaW5nJykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHZhciBkaXIgPSBkaXJlY3Rpb25TZXR0aW5nW3ZhbHVlLnRvTG93ZXJDYXNlKCldO1xuICAgIHJldHVybiBkaXIgPyB2YWx1ZS50b0xvd2VyQ2FzZSgpIDogZmFsc2U7XG4gIH1cblxuICBmdW5jdGlvbiBmaW5kQWxpZ25TZXR0aW5nKHZhbHVlKSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB2YXIgYWxpZ24gPSBhbGlnblNldHRpbmdbdmFsdWUudG9Mb3dlckNhc2UoKV07XG4gICAgcmV0dXJuIGFsaWduID8gdmFsdWUudG9Mb3dlckNhc2UoKSA6IGZhbHNlO1xuICB9XG5cbiAgZnVuY3Rpb24gZXh0ZW5kKG9iaikge1xuICAgIHZhciBpID0gMTtcblxuICAgIGZvciAoOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgY29iaiA9IGFyZ3VtZW50c1tpXTtcblxuICAgICAgZm9yICh2YXIgcCBpbiBjb2JqKSB7XG4gICAgICAgIG9ialtwXSA9IGNvYmpbcF07XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG9iajtcbiAgfVxuXG4gIGZ1bmN0aW9uIFZUVEN1ZShzdGFydFRpbWUsIGVuZFRpbWUsIHRleHQpIHtcbiAgICB2YXIgY3VlID0gdGhpcztcbiAgICB2YXIgYmFzZU9iaiA9IHt9O1xuICAgIGJhc2VPYmouZW51bWVyYWJsZSA9IHRydWU7XG4gICAgLyoqXG4gICAgICogU2hpbSBpbXBsZW1lbnRhdGlvbiBzcGVjaWZpYyBwcm9wZXJ0aWVzLiBUaGVzZSBwcm9wZXJ0aWVzIGFyZSBub3QgaW5cbiAgICAgKiB0aGUgc3BlYy5cbiAgICAgKi9cbiAgICAvLyBMZXRzIHVzIGtub3cgd2hlbiB0aGUgVlRUQ3VlJ3MgZGF0YSBoYXMgY2hhbmdlZCBpbiBzdWNoIGEgd2F5IHRoYXQgd2UgbmVlZFxuICAgIC8vIHRvIHJlY29tcHV0ZSBpdHMgZGlzcGxheSBzdGF0ZS4gVGhpcyBsZXRzIHVzIGNvbXB1dGUgaXRzIGRpc3BsYXkgc3RhdGVcbiAgICAvLyBsYXppbHkuXG5cbiAgICBjdWUuaGFzQmVlblJlc2V0ID0gZmFsc2U7XG4gICAgLyoqXG4gICAgICogVlRUQ3VlIGFuZCBUZXh0VHJhY2tDdWUgcHJvcGVydGllc1xuICAgICAqIGh0dHA6Ly9kZXYudzMub3JnL2h0bWw1L3dlYnZ0dC8jdnR0Y3VlLWludGVyZmFjZVxuICAgICAqL1xuXG4gICAgdmFyIF9pZCA9ICcnO1xuICAgIHZhciBfcGF1c2VPbkV4aXQgPSBmYWxzZTtcbiAgICB2YXIgX3N0YXJ0VGltZSA9IHN0YXJ0VGltZTtcbiAgICB2YXIgX2VuZFRpbWUgPSBlbmRUaW1lO1xuICAgIHZhciBfdGV4dCA9IHRleHQ7XG4gICAgdmFyIF9yZWdpb24gPSBudWxsO1xuICAgIHZhciBfdmVydGljYWwgPSAnJztcbiAgICB2YXIgX3NuYXBUb0xpbmVzID0gdHJ1ZTtcbiAgICB2YXIgX2xpbmUgPSAnYXV0byc7XG4gICAgdmFyIF9saW5lQWxpZ24gPSAnc3RhcnQnO1xuICAgIHZhciBfcG9zaXRpb24gPSA1MDtcbiAgICB2YXIgX3Bvc2l0aW9uQWxpZ24gPSAnbWlkZGxlJztcbiAgICB2YXIgX3NpemUgPSA1MDtcbiAgICB2YXIgX2FsaWduID0gJ21pZGRsZSc7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ2lkJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIF9pZDtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBfaWQgPSAnJyArIHZhbHVlO1xuICAgICAgfVxuICAgIH0pKTtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAncGF1c2VPbkV4aXQnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX3BhdXNlT25FeGl0O1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gc2V0KHZhbHVlKSB7XG4gICAgICAgIF9wYXVzZU9uRXhpdCA9ICEhdmFsdWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdzdGFydFRpbWUnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX3N0YXJ0VGltZTtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnbnVtYmVyJykge1xuICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1N0YXJ0IHRpbWUgbXVzdCBiZSBzZXQgdG8gYSBudW1iZXIuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBfc3RhcnRUaW1lID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ2VuZFRpbWUnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX2VuZFRpbWU7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiBzZXQodmFsdWUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ251bWJlcicpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdFbmQgdGltZSBtdXN0IGJlIHNldCB0byBhIG51bWJlci4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIF9lbmRUaW1lID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3RleHQnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX3RleHQ7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiBzZXQodmFsdWUpIHtcbiAgICAgICAgX3RleHQgPSAnJyArIHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdyZWdpb24nLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX3JlZ2lvbjtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBfcmVnaW9uID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3ZlcnRpY2FsJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIF92ZXJ0aWNhbDtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICB2YXIgc2V0dGluZyA9IGZpbmREaXJlY3Rpb25TZXR0aW5nKHZhbHVlKTsgLy8gSGF2ZSB0byBjaGVjayBmb3IgZmFsc2UgYmVjYXVzZSB0aGUgc2V0dGluZyBhbiBiZSBhbiBlbXB0eSBzdHJpbmcuXG5cbiAgICAgICAgaWYgKHNldHRpbmcgPT09IGZhbHNlKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFN5bnRheEVycm9yKCdBbiBpbnZhbGlkIG9yIGlsbGVnYWwgc3RyaW5nIHdhcyBzcGVjaWZpZWQuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBfdmVydGljYWwgPSBzZXR0aW5nO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdzbmFwVG9MaW5lcycsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgIHJldHVybiBfc25hcFRvTGluZXM7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiBzZXQodmFsdWUpIHtcbiAgICAgICAgX3NuYXBUb0xpbmVzID0gISF2YWx1ZTtcbiAgICAgICAgdGhpcy5oYXNCZWVuUmVzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKTtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAnbGluZScsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgIHJldHVybiBfbGluZTtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnbnVtYmVyJyAmJiB2YWx1ZSAhPT0gYXV0b0tleXdvcmQpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU3ludGF4RXJyb3IoJ0FuIGludmFsaWQgbnVtYmVyIG9yIGlsbGVnYWwgc3RyaW5nIHdhcyBzcGVjaWZpZWQuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBfbGluZSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdsaW5lQWxpZ24nLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX2xpbmVBbGlnbjtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICB2YXIgc2V0dGluZyA9IGZpbmRBbGlnblNldHRpbmcodmFsdWUpO1xuXG4gICAgICAgIGlmICghc2V0dGluZykge1xuICAgICAgICAgIHRocm93IG5ldyBTeW50YXhFcnJvcignQW4gaW52YWxpZCBvciBpbGxlZ2FsIHN0cmluZyB3YXMgc3BlY2lmaWVkLicpO1xuICAgICAgICB9XG5cbiAgICAgICAgX2xpbmVBbGlnbiA9IHNldHRpbmc7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3Bvc2l0aW9uJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIF9wb3NpdGlvbjtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBpZiAodmFsdWUgPCAwIHx8IHZhbHVlID4gMTAwKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQb3NpdGlvbiBtdXN0IGJlIGJldHdlZW4gMCBhbmQgMTAwLicpO1xuICAgICAgICB9XG5cbiAgICAgICAgX3Bvc2l0aW9uID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3Bvc2l0aW9uQWxpZ24nLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgICByZXR1cm4gX3Bvc2l0aW9uQWxpZ247XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiBzZXQodmFsdWUpIHtcbiAgICAgICAgdmFyIHNldHRpbmcgPSBmaW5kQWxpZ25TZXR0aW5nKHZhbHVlKTtcblxuICAgICAgICBpZiAoIXNldHRpbmcpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU3ludGF4RXJyb3IoJ0FuIGludmFsaWQgb3IgaWxsZWdhbCBzdHJpbmcgd2FzIHNwZWNpZmllZC4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIF9wb3NpdGlvbkFsaWduID0gc2V0dGluZztcbiAgICAgICAgdGhpcy5oYXNCZWVuUmVzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKTtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAnc2l6ZScsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgIHJldHVybiBfc2l6ZTtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgICBpZiAodmFsdWUgPCAwIHx8IHZhbHVlID4gMTAwKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdTaXplIG11c3QgYmUgYmV0d2VlbiAwIGFuZCAxMDAuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBfc2l6ZSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdhbGlnbicsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICAgIHJldHVybiBfYWxpZ247XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiBzZXQodmFsdWUpIHtcbiAgICAgICAgdmFyIHNldHRpbmcgPSBmaW5kQWxpZ25TZXR0aW5nKHZhbHVlKTtcblxuICAgICAgICBpZiAoIXNldHRpbmcpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU3ludGF4RXJyb3IoJ0FuIGludmFsaWQgb3IgaWxsZWdhbCBzdHJpbmcgd2FzIHNwZWNpZmllZC4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIF9hbGlnbiA9IHNldHRpbmc7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgLyoqXG4gICAgICogT3RoZXIgPHRyYWNrPiBzcGVjIGRlZmluZWQgcHJvcGVydGllc1xuICAgICAqL1xuICAgIC8vIGh0dHA6Ly93d3cud2hhdHdnLm9yZy9zcGVjcy93ZWItYXBwcy9jdXJyZW50LXdvcmsvbXVsdGlwYWdlL3RoZS12aWRlby1lbGVtZW50Lmh0bWwjdGV4dC10cmFjay1jdWUtZGlzcGxheS1zdGF0ZVxuXG4gICAgY3VlLmRpc3BsYXlTdGF0ZSA9IHZvaWQgMDtcbiAgfVxuICAvKipcbiAgICogVlRUQ3VlIG1ldGhvZHNcbiAgICovXG5cblxuICBWVFRDdWUucHJvdG90eXBlLmdldEN1ZUFzSFRNTCA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBBc3N1bWUgV2ViVlRULmNvbnZlcnRDdWVUb0RPTVRyZWUgaXMgb24gdGhlIGdsb2JhbC5cbiAgICB2YXIgV2ViVlRUID0gd2luZG93LldlYlZUVDtcbiAgICByZXR1cm4gV2ViVlRULmNvbnZlcnRDdWVUb0RPTVRyZWUod2luZG93LCB0aGlzLnRleHQpO1xuICB9O1xuXG4gIHJldHVybiBWVFRDdWU7XG59KSgpKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL3Z0dHBhcnNlci5qc1xuLypcbiAqIFNvdXJjZTogaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvdnR0LmpzL2Jsb2IvbWFzdGVyL2Rpc3QvdnR0LmpzI0wxNzE2XG4gKi9cblxuXG52YXIgU3RyaW5nRGVjb2RlciA9IGZ1bmN0aW9uIFN0cmluZ0RlY29kZXIoKSB7XG4gIHJldHVybiB7XG4gICAgZGVjb2RlOiBmdW5jdGlvbiBkZWNvZGUoZGF0YSkge1xuICAgICAgaWYgKCFkYXRhKSB7XG4gICAgICAgIHJldHVybiAnJztcbiAgICAgIH1cblxuICAgICAgaWYgKHR5cGVvZiBkYXRhICE9PSAnc3RyaW5nJykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Vycm9yIC0gZXhwZWN0ZWQgc3RyaW5nIGRhdGEuJyk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQoZW5jb2RlVVJJQ29tcG9uZW50KGRhdGEpKTtcbiAgICB9XG4gIH07XG59O1xuXG5mdW5jdGlvbiBWVFRQYXJzZXIoKSB7XG4gIHRoaXMud2luZG93ID0gd2luZG93O1xuICB0aGlzLnN0YXRlID0gJ0lOSVRJQUwnO1xuICB0aGlzLmJ1ZmZlciA9ICcnO1xuICB0aGlzLmRlY29kZXIgPSBuZXcgU3RyaW5nRGVjb2RlcigpO1xuICB0aGlzLnJlZ2lvbkxpc3QgPSBbXTtcbn0gLy8gVHJ5IHRvIHBhcnNlIGlucHV0IGFzIGEgdGltZSBzdGFtcC5cblxuXG5mdW5jdGlvbiBwYXJzZVRpbWVTdGFtcChpbnB1dCkge1xuICBmdW5jdGlvbiBjb21wdXRlU2Vjb25kcyhoLCBtLCBzLCBmKSB7XG4gICAgcmV0dXJuIChoIHwgMCkgKiAzNjAwICsgKG0gfCAwKSAqIDYwICsgKHMgfCAwKSArIChmIHwgMCkgLyAxMDAwO1xuICB9XG5cbiAgdmFyIG0gPSBpbnB1dC5tYXRjaCgvXihcXGQrKTooXFxkezJ9KSg6XFxkezJ9KT9cXC4oXFxkezN9KS8pO1xuXG4gIGlmICghbSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKG1bM10pIHtcbiAgICAvLyBUaW1lc3RhbXAgdGFrZXMgdGhlIGZvcm0gb2YgW2hvdXJzXTpbbWludXRlc106W3NlY29uZHNdLlttaWxsaXNlY29uZHNdXG4gICAgcmV0dXJuIGNvbXB1dGVTZWNvbmRzKG1bMV0sIG1bMl0sIG1bM10ucmVwbGFjZSgnOicsICcnKSwgbVs0XSk7XG4gIH0gZWxzZSBpZiAobVsxXSA+IDU5KSB7XG4gICAgLy8gVGltZXN0YW1wIHRha2VzIHRoZSBmb3JtIG9mIFtob3Vyc106W21pbnV0ZXNdLlttaWxsaXNlY29uZHNdXG4gICAgLy8gRmlyc3QgcG9zaXRpb24gaXMgaG91cnMgYXMgaXQncyBvdmVyIDU5LlxuICAgIHJldHVybiBjb21wdXRlU2Vjb25kcyhtWzFdLCBtWzJdLCAwLCBtWzRdKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBUaW1lc3RhbXAgdGFrZXMgdGhlIGZvcm0gb2YgW21pbnV0ZXNdOltzZWNvbmRzXS5bbWlsbGlzZWNvbmRzXVxuICAgIHJldHVybiBjb21wdXRlU2Vjb25kcygwLCBtWzFdLCBtWzJdLCBtWzRdKTtcbiAgfVxufSAvLyBBIHNldHRpbmdzIG9iamVjdCBob2xkcyBrZXkvdmFsdWUgcGFpcnMgYW5kIHdpbGwgaWdub3JlIGFueXRoaW5nIGJ1dCB0aGUgZmlyc3Rcbi8vIGFzc2lnbm1lbnQgdG8gYSBzcGVjaWZpYyBrZXkuXG5cblxuZnVuY3Rpb24gU2V0dGluZ3MoKSB7XG4gIHRoaXMudmFsdWVzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbn1cblxuU2V0dGluZ3MucHJvdG90eXBlID0ge1xuICAvLyBPbmx5IGFjY2VwdCB0aGUgZmlyc3QgYXNzaWdubWVudCB0byBhbnkga2V5LlxuICBzZXQ6IGZ1bmN0aW9uIHNldChrLCB2KSB7XG4gICAgaWYgKCF0aGlzLmdldChrKSAmJiB2ICE9PSAnJykge1xuICAgICAgdGhpcy52YWx1ZXNba10gPSB2O1xuICAgIH1cbiAgfSxcbiAgLy8gUmV0dXJuIHRoZSB2YWx1ZSBmb3IgYSBrZXksIG9yIGEgZGVmYXVsdCB2YWx1ZS5cbiAgLy8gSWYgJ2RlZmF1bHRLZXknIGlzIHBhc3NlZCB0aGVuICdkZmx0JyBpcyBhc3N1bWVkIHRvIGJlIGFuIG9iamVjdCB3aXRoXG4gIC8vIGEgbnVtYmVyIG9mIHBvc3NpYmxlIGRlZmF1bHQgdmFsdWVzIGFzIHByb3BlcnRpZXMgd2hlcmUgJ2RlZmF1bHRLZXknIGlzXG4gIC8vIHRoZSBrZXkgb2YgdGhlIHByb3BlcnR5IHRoYXQgd2lsbCBiZSBjaG9zZW47IG90aGVyd2lzZSBpdCdzIGFzc3VtZWQgdG8gYmVcbiAgLy8gYSBzaW5nbGUgdmFsdWUuXG4gIGdldDogZnVuY3Rpb24gZ2V0KGssIGRmbHQsIGRlZmF1bHRLZXkpIHtcbiAgICBpZiAoZGVmYXVsdEtleSkge1xuICAgICAgcmV0dXJuIHRoaXMuaGFzKGspID8gdGhpcy52YWx1ZXNba10gOiBkZmx0W2RlZmF1bHRLZXldO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLmhhcyhrKSA/IHRoaXMudmFsdWVzW2tdIDogZGZsdDtcbiAgfSxcbiAgLy8gQ2hlY2sgd2hldGhlciB3ZSBoYXZlIGEgdmFsdWUgZm9yIGEga2V5LlxuICBoYXM6IGZ1bmN0aW9uIGhhcyhrKSB7XG4gICAgcmV0dXJuIGsgaW4gdGhpcy52YWx1ZXM7XG4gIH0sXG4gIC8vIEFjY2VwdCBhIHNldHRpbmcgaWYgaXRzIG9uZSBvZiB0aGUgZ2l2ZW4gYWx0ZXJuYXRpdmVzLlxuICBhbHQ6IGZ1bmN0aW9uIGFsdChrLCB2LCBhKSB7XG4gICAgZm9yICh2YXIgbiA9IDA7IG4gPCBhLmxlbmd0aDsgKytuKSB7XG4gICAgICBpZiAodiA9PT0gYVtuXSkge1xuICAgICAgICB0aGlzLnNldChrLCB2KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9LFxuICAvLyBBY2NlcHQgYSBzZXR0aW5nIGlmIGl0cyBhIHZhbGlkIChzaWduZWQpIGludGVnZXIuXG4gIGludGVnZXI6IGZ1bmN0aW9uIGludGVnZXIoaywgdikge1xuICAgIGlmICgvXi0/XFxkKyQvLnRlc3QodikpIHtcbiAgICAgIC8vIGludGVnZXJcbiAgICAgIHRoaXMuc2V0KGssIHBhcnNlSW50KHYsIDEwKSk7XG4gICAgfVxuICB9LFxuICAvLyBBY2NlcHQgYSBzZXR0aW5nIGlmIGl0cyBhIHZhbGlkIHBlcmNlbnRhZ2UuXG4gIHBlcmNlbnQ6IGZ1bmN0aW9uIHBlcmNlbnQoaywgdikge1xuICAgIHZhciBtO1xuXG4gICAgaWYgKG0gPSB2Lm1hdGNoKC9eKFtcXGRdezEsM30pKFxcLltcXGRdKik/JSQvKSkge1xuICAgICAgdiA9IHBhcnNlRmxvYXQodik7XG5cbiAgICAgIGlmICh2ID49IDAgJiYgdiA8PSAxMDApIHtcbiAgICAgICAgdGhpcy5zZXQoaywgdik7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufTsgLy8gSGVscGVyIGZ1bmN0aW9uIHRvIHBhcnNlIGlucHV0IGludG8gZ3JvdXBzIHNlcGFyYXRlZCBieSAnZ3JvdXBEZWxpbScsIGFuZFxuLy8gaW50ZXJwcmV0ZSBlYWNoIGdyb3VwIGFzIGEga2V5L3ZhbHVlIHBhaXIgc2VwYXJhdGVkIGJ5ICdrZXlWYWx1ZURlbGltJy5cblxuZnVuY3Rpb24gcGFyc2VPcHRpb25zKGlucHV0LCBjYWxsYmFjaywga2V5VmFsdWVEZWxpbSwgZ3JvdXBEZWxpbSkge1xuICB2YXIgZ3JvdXBzID0gZ3JvdXBEZWxpbSA/IGlucHV0LnNwbGl0KGdyb3VwRGVsaW0pIDogW2lucHV0XTtcblxuICBmb3IgKHZhciBpIGluIGdyb3Vwcykge1xuICAgIGlmICh0eXBlb2YgZ3JvdXBzW2ldICE9PSAnc3RyaW5nJykge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgdmFyIGt2ID0gZ3JvdXBzW2ldLnNwbGl0KGtleVZhbHVlRGVsaW0pO1xuXG4gICAgaWYgKGt2Lmxlbmd0aCAhPT0gMikge1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgdmFyIGsgPSBrdlswXTtcbiAgICB2YXIgdiA9IGt2WzFdO1xuICAgIGNhbGxiYWNrKGssIHYpO1xuICB9XG59XG5cbnZhciBkZWZhdWx0cyA9IG5ldyB2dHRjdWUoMCwgMCwgMCk7IC8vICdtaWRkbGUnIHdhcyBjaGFuZ2VkIHRvICdjZW50ZXInIGluIHRoZSBzcGVjOiBodHRwczovL2dpdGh1Yi5jb20vdzNjL3dlYnZ0dC9wdWxsLzI0NFxuLy8gIFNhZmFyaSBkb2Vzbid0IHlldCBzdXBwb3J0IHRoaXMgY2hhbmdlLCBidXQgRkYgYW5kIENocm9tZSBkby5cblxudmFyIGNlbnRlciA9IGRlZmF1bHRzLmFsaWduID09PSAnbWlkZGxlJyA/ICdtaWRkbGUnIDogJ2NlbnRlcic7XG5cbmZ1bmN0aW9uIHBhcnNlQ3VlKGlucHV0LCBjdWUsIHJlZ2lvbkxpc3QpIHtcbiAgLy8gUmVtZW1iZXIgdGhlIG9yaWdpbmFsIGlucHV0IGlmIHdlIG5lZWQgdG8gdGhyb3cgYW4gZXJyb3IuXG4gIHZhciBvSW5wdXQgPSBpbnB1dDsgLy8gNC4xIFdlYlZUVCB0aW1lc3RhbXBcblxuICBmdW5jdGlvbiBjb25zdW1lVGltZVN0YW1wKCkge1xuICAgIHZhciB0cyA9IHBhcnNlVGltZVN0YW1wKGlucHV0KTtcblxuICAgIGlmICh0cyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNYWxmb3JtZWQgdGltZXN0YW1wOiAnICsgb0lucHV0KTtcbiAgICB9IC8vIFJlbW92ZSB0aW1lIHN0YW1wIGZyb20gaW5wdXQuXG5cblxuICAgIGlucHV0ID0gaW5wdXQucmVwbGFjZSgvXlteXFxzYS16QS1aLV0rLywgJycpO1xuICAgIHJldHVybiB0cztcbiAgfSAvLyA0LjQuMiBXZWJWVFQgY3VlIHNldHRpbmdzXG5cblxuICBmdW5jdGlvbiBjb25zdW1lQ3VlU2V0dGluZ3MoaW5wdXQsIGN1ZSkge1xuICAgIHZhciBzZXR0aW5ncyA9IG5ldyBTZXR0aW5ncygpO1xuICAgIHBhcnNlT3B0aW9ucyhpbnB1dCwgZnVuY3Rpb24gKGssIHYpIHtcbiAgICAgIHN3aXRjaCAoaykge1xuICAgICAgICBjYXNlICdyZWdpb24nOlxuICAgICAgICAgIC8vIEZpbmQgdGhlIGxhc3QgcmVnaW9uIHdlIHBhcnNlZCB3aXRoIHRoZSBzYW1lIHJlZ2lvbiBpZC5cbiAgICAgICAgICBmb3IgKHZhciBpID0gcmVnaW9uTGlzdC5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgICAgICAgICAgaWYgKHJlZ2lvbkxpc3RbaV0uaWQgPT09IHYpIHtcbiAgICAgICAgICAgICAgc2V0dGluZ3Muc2V0KGssIHJlZ2lvbkxpc3RbaV0ucmVnaW9uKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgY2FzZSAndmVydGljYWwnOlxuICAgICAgICAgIHNldHRpbmdzLmFsdChrLCB2LCBbJ3JsJywgJ2xyJ10pO1xuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNhc2UgJ2xpbmUnOlxuICAgICAgICAgIHZhciB2YWxzID0gdi5zcGxpdCgnLCcpLFxuICAgICAgICAgICAgICB2YWxzMCA9IHZhbHNbMF07XG4gICAgICAgICAgc2V0dGluZ3MuaW50ZWdlcihrLCB2YWxzMCk7XG5cbiAgICAgICAgICBpZiAoc2V0dGluZ3MucGVyY2VudChrLCB2YWxzMCkpIHtcbiAgICAgICAgICAgIHNldHRpbmdzLnNldCgnc25hcFRvTGluZXMnLCBmYWxzZSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgc2V0dGluZ3MuYWx0KGssIHZhbHMwLCBbJ2F1dG8nXSk7XG5cbiAgICAgICAgICBpZiAodmFscy5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgICAgIHNldHRpbmdzLmFsdCgnbGluZUFsaWduJywgdmFsc1sxXSwgWydzdGFydCcsIGNlbnRlciwgJ2VuZCddKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlICdwb3NpdGlvbic6XG4gICAgICAgICAgdmFscyA9IHYuc3BsaXQoJywnKTtcbiAgICAgICAgICBzZXR0aW5ncy5wZXJjZW50KGssIHZhbHNbMF0pO1xuXG4gICAgICAgICAgaWYgKHZhbHMubGVuZ3RoID09PSAyKSB7XG4gICAgICAgICAgICBzZXR0aW5ncy5hbHQoJ3Bvc2l0aW9uQWxpZ24nLCB2YWxzWzFdLCBbJ3N0YXJ0JywgY2VudGVyLCAnZW5kJywgJ2xpbmUtbGVmdCcsICdsaW5lLXJpZ2h0JywgJ2F1dG8nXSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgY2FzZSAnc2l6ZSc6XG4gICAgICAgICAgc2V0dGluZ3MucGVyY2VudChrLCB2KTtcbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlICdhbGlnbic6XG4gICAgICAgICAgc2V0dGluZ3MuYWx0KGssIHYsIFsnc3RhcnQnLCBjZW50ZXIsICdlbmQnLCAnbGVmdCcsICdyaWdodCddKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9LCAvOi8sIC9cXHMvKTsgLy8gQXBwbHkgZGVmYXVsdCB2YWx1ZXMgZm9yIGFueSBtaXNzaW5nIGZpZWxkcy5cblxuICAgIGN1ZS5yZWdpb24gPSBzZXR0aW5ncy5nZXQoJ3JlZ2lvbicsIG51bGwpO1xuICAgIGN1ZS52ZXJ0aWNhbCA9IHNldHRpbmdzLmdldCgndmVydGljYWwnLCAnJyk7XG4gICAgdmFyIGxpbmUgPSBzZXR0aW5ncy5nZXQoJ2xpbmUnLCAnYXV0bycpO1xuXG4gICAgaWYgKGxpbmUgPT09ICdhdXRvJyAmJiBkZWZhdWx0cy5saW5lID09PSAtMSkge1xuICAgICAgLy8gc2V0IG51bWVyaWMgbGluZSBudW1iZXIgZm9yIFNhZmFyaVxuICAgICAgbGluZSA9IC0xO1xuICAgIH1cblxuICAgIGN1ZS5saW5lID0gbGluZTtcbiAgICBjdWUubGluZUFsaWduID0gc2V0dGluZ3MuZ2V0KCdsaW5lQWxpZ24nLCAnc3RhcnQnKTtcbiAgICBjdWUuc25hcFRvTGluZXMgPSBzZXR0aW5ncy5nZXQoJ3NuYXBUb0xpbmVzJywgdHJ1ZSk7XG4gICAgY3VlLnNpemUgPSBzZXR0aW5ncy5nZXQoJ3NpemUnLCAxMDApO1xuICAgIGN1ZS5hbGlnbiA9IHNldHRpbmdzLmdldCgnYWxpZ24nLCBjZW50ZXIpO1xuICAgIHZhciBwb3NpdGlvbiA9IHNldHRpbmdzLmdldCgncG9zaXRpb24nLCAnYXV0bycpO1xuXG4gICAgaWYgKHBvc2l0aW9uID09PSAnYXV0bycgJiYgZGVmYXVsdHMucG9zaXRpb24gPT09IDUwKSB7XG4gICAgICAvLyBzZXQgbnVtZXJpYyBwb3NpdGlvbiBmb3IgU2FmYXJpXG4gICAgICBwb3NpdGlvbiA9IGN1ZS5hbGlnbiA9PT0gJ3N0YXJ0JyB8fCBjdWUuYWxpZ24gPT09ICdsZWZ0JyA/IDAgOiBjdWUuYWxpZ24gPT09ICdlbmQnIHx8IGN1ZS5hbGlnbiA9PT0gJ3JpZ2h0JyA/IDEwMCA6IDUwO1xuICAgIH1cblxuICAgIGN1ZS5wb3NpdGlvbiA9IHBvc2l0aW9uO1xuICB9XG5cbiAgZnVuY3Rpb24gc2tpcFdoaXRlc3BhY2UoKSB7XG4gICAgaW5wdXQgPSBpbnB1dC5yZXBsYWNlKC9eXFxzKy8sICcnKTtcbiAgfSAvLyA0LjEgV2ViVlRUIGN1ZSB0aW1pbmdzLlxuXG5cbiAgc2tpcFdoaXRlc3BhY2UoKTtcbiAgY3VlLnN0YXJ0VGltZSA9IGNvbnN1bWVUaW1lU3RhbXAoKTsgLy8gKDEpIGNvbGxlY3QgY3VlIHN0YXJ0IHRpbWVcblxuICBza2lwV2hpdGVzcGFjZSgpO1xuXG4gIGlmIChpbnB1dC5zdWJzdHIoMCwgMykgIT09ICctLT4nKSB7XG4gICAgLy8gKDMpIG5leHQgY2hhcmFjdGVycyBtdXN0IG1hdGNoICctLT4nXG4gICAgdGhyb3cgbmV3IEVycm9yKCdNYWxmb3JtZWQgdGltZSBzdGFtcCAodGltZSBzdGFtcHMgbXVzdCBiZSBzZXBhcmF0ZWQgYnkgXFwnLS0+XFwnKTogJyArIG9JbnB1dCk7XG4gIH1cblxuICBpbnB1dCA9IGlucHV0LnN1YnN0cigzKTtcbiAgc2tpcFdoaXRlc3BhY2UoKTtcbiAgY3VlLmVuZFRpbWUgPSBjb25zdW1lVGltZVN0YW1wKCk7IC8vICg1KSBjb2xsZWN0IGN1ZSBlbmQgdGltZVxuICAvLyA0LjEgV2ViVlRUIGN1ZSBzZXR0aW5ncyBsaXN0LlxuXG4gIHNraXBXaGl0ZXNwYWNlKCk7XG4gIGNvbnN1bWVDdWVTZXR0aW5ncyhpbnB1dCwgY3VlKTtcbn1cblxuZnVuY3Rpb24gZml4TGluZUJyZWFrcyhpbnB1dCkge1xuICByZXR1cm4gaW5wdXQucmVwbGFjZSgvPGJyKD86IFxcLyk/Pi9naSwgJ1xcbicpO1xufVxuXG5WVFRQYXJzZXIucHJvdG90eXBlID0ge1xuICBwYXJzZTogZnVuY3Rpb24gcGFyc2UoZGF0YSkge1xuICAgIHZhciBzZWxmID0gdGhpczsgLy8gSWYgdGhlcmUgaXMgbm8gZGF0YSB0aGVuIHdlIHdvbid0IGRlY29kZSBpdCwgYnV0IHdpbGwganVzdCB0cnkgdG8gcGFyc2VcbiAgICAvLyB3aGF0ZXZlciBpcyBpbiBidWZmZXIgYWxyZWFkeS4gVGhpcyBtYXkgb2NjdXIgaW4gY2lyY3Vtc3RhbmNlcywgZm9yXG4gICAgLy8gZXhhbXBsZSB3aGVuIGZsdXNoKCkgaXMgY2FsbGVkLlxuXG4gICAgaWYgKGRhdGEpIHtcbiAgICAgIC8vIFRyeSB0byBkZWNvZGUgdGhlIGRhdGEgdGhhdCB3ZSByZWNlaXZlZC5cbiAgICAgIHNlbGYuYnVmZmVyICs9IHNlbGYuZGVjb2Rlci5kZWNvZGUoZGF0YSwge1xuICAgICAgICBzdHJlYW06IHRydWVcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGZ1bmN0aW9uIGNvbGxlY3ROZXh0TGluZSgpIHtcbiAgICAgIHZhciBidWZmZXIgPSBzZWxmLmJ1ZmZlcjtcbiAgICAgIHZhciBwb3MgPSAwO1xuICAgICAgYnVmZmVyID0gZml4TGluZUJyZWFrcyhidWZmZXIpO1xuXG4gICAgICB3aGlsZSAocG9zIDwgYnVmZmVyLmxlbmd0aCAmJiBidWZmZXJbcG9zXSAhPT0gJ1xccicgJiYgYnVmZmVyW3Bvc10gIT09ICdcXG4nKSB7XG4gICAgICAgICsrcG9zO1xuICAgICAgfVxuXG4gICAgICB2YXIgbGluZSA9IGJ1ZmZlci5zdWJzdHIoMCwgcG9zKTsgLy8gQWR2YW5jZSB0aGUgYnVmZmVyIGVhcmx5IGluIGNhc2Ugd2UgZmFpbCBiZWxvdy5cblxuICAgICAgaWYgKGJ1ZmZlcltwb3NdID09PSAnXFxyJykge1xuICAgICAgICArK3BvcztcbiAgICAgIH1cblxuICAgICAgaWYgKGJ1ZmZlcltwb3NdID09PSAnXFxuJykge1xuICAgICAgICArK3BvcztcbiAgICAgIH1cblxuICAgICAgc2VsZi5idWZmZXIgPSBidWZmZXIuc3Vic3RyKHBvcyk7XG4gICAgICByZXR1cm4gbGluZTtcbiAgICB9IC8vIDMuMiBXZWJWVFQgbWV0YWRhdGEgaGVhZGVyIHN5bnRheFxuXG5cbiAgICBmdW5jdGlvbiBwYXJzZUhlYWRlcihpbnB1dCkge1xuICAgICAgcGFyc2VPcHRpb25zKGlucHV0LCBmdW5jdGlvbiAoaywgdikge1xuICAgICAgICBzd2l0Y2ggKGspIHtcbiAgICAgICAgICBjYXNlICdSZWdpb24nOlxuICAgICAgICAgICAgLy8gMy4zIFdlYlZUVCByZWdpb24gbWV0YWRhdGEgaGVhZGVyIHN5bnRheFxuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coJ3BhcnNlIHJlZ2lvbicsIHYpO1xuICAgICAgICAgICAgLy8gcGFyc2VSZWdpb24odik7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfSwgLzovKTtcbiAgICB9IC8vIDUuMSBXZWJWVFQgZmlsZSBwYXJzaW5nLlxuXG5cbiAgICB0cnkge1xuICAgICAgdmFyIGxpbmU7XG5cbiAgICAgIGlmIChzZWxmLnN0YXRlID09PSAnSU5JVElBTCcpIHtcbiAgICAgICAgLy8gV2UgY2FuJ3Qgc3RhcnQgcGFyc2luZyB1bnRpbCB3ZSBoYXZlIHRoZSBmaXJzdCBsaW5lLlxuICAgICAgICBpZiAoIS9cXHJcXG58XFxuLy50ZXN0KHNlbGYuYnVmZmVyKSkge1xuICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG5cbiAgICAgICAgbGluZSA9IGNvbGxlY3ROZXh0TGluZSgpOyAvLyBzdHJpcCBvZiBVVEYtOCBCT00gaWYgYW55XG4gICAgICAgIC8vIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0J5dGVfb3JkZXJfbWFyayNVVEYtOFxuXG4gICAgICAgIHZhciBtID0gbGluZS5tYXRjaCgvXijDr8K7wr8pP1dFQlZUVChbIFxcdF0uKik/JC8pO1xuXG4gICAgICAgIGlmICghbSB8fCAhbVswXSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTWFsZm9ybWVkIFdlYlZUVCBzaWduYXR1cmUuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBzZWxmLnN0YXRlID0gJ0hFQURFUic7XG4gICAgICB9XG5cbiAgICAgIHZhciBhbHJlYWR5Q29sbGVjdGVkTGluZSA9IGZhbHNlO1xuXG4gICAgICB3aGlsZSAoc2VsZi5idWZmZXIpIHtcbiAgICAgICAgLy8gV2UgY2FuJ3QgcGFyc2UgYSBsaW5lIHVudGlsIHdlIGhhdmUgdGhlIGZ1bGwgbGluZS5cbiAgICAgICAgaWYgKCEvXFxyXFxufFxcbi8udGVzdChzZWxmLmJ1ZmZlcikpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghYWxyZWFkeUNvbGxlY3RlZExpbmUpIHtcbiAgICAgICAgICBsaW5lID0gY29sbGVjdE5leHRMaW5lKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYWxyZWFkeUNvbGxlY3RlZExpbmUgPSBmYWxzZTtcbiAgICAgICAgfVxuXG4gICAgICAgIHN3aXRjaCAoc2VsZi5zdGF0ZSkge1xuICAgICAgICAgIGNhc2UgJ0hFQURFUic6XG4gICAgICAgICAgICAvLyAxMy0xOCAtIEFsbG93IGEgaGVhZGVyIChtZXRhZGF0YSkgdW5kZXIgdGhlIFdFQlZUVCBsaW5lLlxuICAgICAgICAgICAgaWYgKC86Ly50ZXN0KGxpbmUpKSB7XG4gICAgICAgICAgICAgIHBhcnNlSGVhZGVyKGxpbmUpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICghbGluZSkge1xuICAgICAgICAgICAgICAvLyBBbiBlbXB0eSBsaW5lIHRlcm1pbmF0ZXMgdGhlIGhlYWRlciBhbmQgc3RhcnRzIHRoZSBib2R5IChjdWVzKS5cbiAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdJRCc7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgY2FzZSAnTk9URSc6XG4gICAgICAgICAgICAvLyBJZ25vcmUgTk9URSBibG9ja3MuXG4gICAgICAgICAgICBpZiAoIWxpbmUpIHtcbiAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdJRCc7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgY2FzZSAnSUQnOlxuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIHRoZSBzdGFydCBvZiBOT1RFIGJsb2Nrcy5cbiAgICAgICAgICAgIGlmICgvXk5PVEUoJHxbIFxcdF0pLy50ZXN0KGxpbmUpKSB7XG4gICAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnTk9URSc7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfSAvLyAxOS0yOSAtIEFsbG93IGFueSBudW1iZXIgb2YgbGluZSB0ZXJtaW5hdG9ycywgdGhlbiBpbml0aWFsaXplIG5ldyBjdWUgdmFsdWVzLlxuXG5cbiAgICAgICAgICAgIGlmICghbGluZSkge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VsZi5jdWUgPSBuZXcgdnR0Y3VlKDAsIDAsICcnKTtcbiAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnQ1VFJzsgLy8gMzAtMzkgLSBDaGVjayBpZiBzZWxmIGxpbmUgY29udGFpbnMgYW4gb3B0aW9uYWwgaWRlbnRpZmllciBvciB0aW1pbmcgZGF0YS5cblxuICAgICAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignLS0+JykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIHNlbGYuY3VlLmlkID0gbGluZTtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBQcm9jZXNzIGxpbmUgYXMgc3RhcnQgb2YgYSBjdWUuXG5cbiAgICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG5cbiAgICAgICAgICBjYXNlICdDVUUnOlxuICAgICAgICAgICAgLy8gNDAgLSBDb2xsZWN0IGN1ZSB0aW1pbmdzIGFuZCBzZXR0aW5ncy5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIHBhcnNlQ3VlKGxpbmUsIHNlbGYuY3VlLCBzZWxmLnJlZ2lvbkxpc3QpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yIGlnbm9yZSByZXN0IG9mIHRoZSBjdWUuXG4gICAgICAgICAgICAgIHNlbGYuY3VlID0gbnVsbDtcbiAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdCQURDVUUnO1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdDVUVURVhUJztcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuXG4gICAgICAgICAgY2FzZSAnQ1VFVEVYVCc6XG4gICAgICAgICAgICB2YXIgaGFzU3Vic3RyaW5nID0gbGluZS5pbmRleE9mKCctLT4nKSAhPT0gLTE7IC8vIDM0IC0gSWYgd2UgaGF2ZSBhbiBlbXB0eSBsaW5lIHRoZW4gcmVwb3J0IHRoZSBjdWUuXG4gICAgICAgICAgICAvLyAzNSAtIElmIHdlIGhhdmUgdGhlIHNwZWNpYWwgc3Vic3RyaW5nICctLT4nIHRoZW4gcmVwb3J0IHRoZSBjdWUsXG4gICAgICAgICAgICAvLyBidXQgZG8gbm90IGNvbGxlY3QgdGhlIGxpbmUgYXMgd2UgbmVlZCB0byBwcm9jZXNzIHRoZSBjdXJyZW50XG4gICAgICAgICAgICAvLyBvbmUgYXMgYSBuZXcgY3VlLlxuXG4gICAgICAgICAgICBpZiAoIWxpbmUgfHwgaGFzU3Vic3RyaW5nICYmIChhbHJlYWR5Q29sbGVjdGVkTGluZSA9IHRydWUpKSB7XG4gICAgICAgICAgICAgIC8vIFdlIGFyZSBkb25lIHBhcnNpbmcgc2VsZiBjdWUuXG4gICAgICAgICAgICAgIGlmIChzZWxmLm9uY3VlKSB7XG4gICAgICAgICAgICAgICAgc2VsZi5vbmN1ZShzZWxmLmN1ZSk7XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBzZWxmLmN1ZSA9IG51bGw7XG4gICAgICAgICAgICAgIHNlbGYuc3RhdGUgPSAnSUQnO1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHNlbGYuY3VlLnRleHQpIHtcbiAgICAgICAgICAgICAgc2VsZi5jdWUudGV4dCArPSAnXFxuJztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VsZi5jdWUudGV4dCArPSBsaW5lO1xuICAgICAgICAgICAgY29udGludWU7XG5cbiAgICAgICAgICBjYXNlICdCQURDVUUnOlxuICAgICAgICAgICAgLy8gQkFEQ1VFXG4gICAgICAgICAgICAvLyA1NC02MiAtIENvbGxlY3QgYW5kIGRpc2NhcmQgdGhlIHJlbWFpbmluZyBjdWUuXG4gICAgICAgICAgICBpZiAoIWxpbmUpIHtcbiAgICAgICAgICAgICAgc2VsZi5zdGF0ZSA9ICdJRCc7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gSWYgd2UgYXJlIGN1cnJlbnRseSBwYXJzaW5nIGEgY3VlLCByZXBvcnQgd2hhdCB3ZSBoYXZlLlxuICAgICAgaWYgKHNlbGYuc3RhdGUgPT09ICdDVUVURVhUJyAmJiBzZWxmLmN1ZSAmJiBzZWxmLm9uY3VlKSB7XG4gICAgICAgIHNlbGYub25jdWUoc2VsZi5jdWUpO1xuICAgICAgfVxuXG4gICAgICBzZWxmLmN1ZSA9IG51bGw7IC8vIEVudGVyIEJBRFdFQlZUVCBzdGF0ZSBpZiBoZWFkZXIgd2FzIG5vdCBwYXJzZWQgY29ycmVjdGx5IG90aGVyd2lzZVxuICAgICAgLy8gYW5vdGhlciBleGNlcHRpb24gb2NjdXJyZWQgc28gZW50ZXIgQkFEQ1VFIHN0YXRlLlxuXG4gICAgICBzZWxmLnN0YXRlID0gc2VsZi5zdGF0ZSA9PT0gJ0lOSVRJQUwnID8gJ0JBRFdFQlZUVCcgOiAnQkFEQ1VFJztcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfSxcbiAgZmx1c2g6IGZ1bmN0aW9uIGZsdXNoKCkge1xuICAgIHZhciBzZWxmID0gdGhpcztcblxuICAgIHRyeSB7XG4gICAgICAvLyBGaW5pc2ggZGVjb2RpbmcgdGhlIHN0cmVhbS5cbiAgICAgIHNlbGYuYnVmZmVyICs9IHNlbGYuZGVjb2Rlci5kZWNvZGUoKTsgLy8gU3ludGhlc2l6ZSB0aGUgZW5kIG9mIHRoZSBjdXJyZW50IGN1ZSBvciByZWdpb24uXG5cbiAgICAgIGlmIChzZWxmLmN1ZSB8fCBzZWxmLnN0YXRlID09PSAnSEVBREVSJykge1xuICAgICAgICBzZWxmLmJ1ZmZlciArPSAnXFxuXFxuJztcbiAgICAgICAgc2VsZi5wYXJzZSgpO1xuICAgICAgfSAvLyBJZiB3ZSd2ZSBmbHVzaGVkLCBwYXJzZWQsIGFuZCB3ZSdyZSBzdGlsbCBvbiB0aGUgSU5JVElBTCBzdGF0ZSB0aGVuXG4gICAgICAvLyB0aGF0IG1lYW5zIHdlIGRvbid0IGhhdmUgZW5vdWdoIG9mIHRoZSBzdHJlYW0gdG8gcGFyc2UgdGhlIGZpcnN0XG4gICAgICAvLyBsaW5lLlxuXG5cbiAgICAgIGlmIChzZWxmLnN0YXRlID09PSAnSU5JVElBTCcpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdNYWxmb3JtZWQgV2ViVlRUIHNpZ25hdHVyZS4nKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICB0aHJvdyBlO1xuICAgIH1cblxuICAgIGlmIChzZWxmLm9uZmx1c2gpIHtcbiAgICAgIHNlbGYub25mbHVzaCgpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzO1xuICB9XG59O1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciB2dHRwYXJzZXIgPSAoVlRUUGFyc2VyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL2N1ZXMudHNcblxuZnVuY3Rpb24gbmV3Q3VlKHRyYWNrLCBzdGFydFRpbWUsIGVuZFRpbWUsIGNhcHRpb25TY3JlZW4pIHtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICB2YXIgcm93OyAvLyB0aGUgdHlwZSBkYXRhIHN0YXRlcyB0aGlzIGlzIFZUVEN1ZSwgYnV0IGl0IGNhbiBwb3RlbnRpYWxseSBiZSBhIFRleHRUcmFja0N1ZSBvbiBvbGQgYnJvd3NlcnNcblxuICB2YXIgY3VlO1xuICB2YXIgaW5kZW50aW5nO1xuICB2YXIgaW5kZW50O1xuICB2YXIgdGV4dDtcbiAgdmFyIFZUVEN1ZSA9IHdpbmRvdy5WVFRDdWUgfHwgVGV4dFRyYWNrQ3VlO1xuXG4gIGZvciAodmFyIHIgPSAwOyByIDwgY2FwdGlvblNjcmVlbi5yb3dzLmxlbmd0aDsgcisrKSB7XG4gICAgcm93ID0gY2FwdGlvblNjcmVlbi5yb3dzW3JdO1xuICAgIGluZGVudGluZyA9IHRydWU7XG4gICAgaW5kZW50ID0gMDtcbiAgICB0ZXh0ID0gJyc7XG5cbiAgICBpZiAoIXJvdy5pc0VtcHR5KCkpIHtcbiAgICAgIGZvciAodmFyIGMgPSAwOyBjIDwgcm93LmNoYXJzLmxlbmd0aDsgYysrKSB7XG4gICAgICAgIGlmIChyb3cuY2hhcnNbY10udWNoYXIubWF0Y2goL1xccy8pICYmIGluZGVudGluZykge1xuICAgICAgICAgIGluZGVudCsrO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRleHQgKz0gcm93LmNoYXJzW2NdLnVjaGFyO1xuICAgICAgICAgIGluZGVudGluZyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9IC8vIFRvIGJlIHVzZWQgZm9yIGNsZWFuaW5nLXVwIG9ycGhhbmVkIHJvbGwtdXAgY2FwdGlvbnNcblxuXG4gICAgICByb3cuY3VlU3RhcnRUaW1lID0gc3RhcnRUaW1lOyAvLyBHaXZlIGEgc2xpZ2h0IGJ1bXAgdG8gdGhlIGVuZFRpbWUgaWYgaXQncyBlcXVhbCB0byBzdGFydFRpbWUgdG8gYXZvaWQgYSBTeW50YXhFcnJvciBpbiBJRVxuXG4gICAgICBpZiAoc3RhcnRUaW1lID09PSBlbmRUaW1lKSB7XG4gICAgICAgIGVuZFRpbWUgKz0gMC4wMDAxO1xuICAgICAgfVxuXG4gICAgICBjdWUgPSBuZXcgVlRUQ3VlKHN0YXJ0VGltZSwgZW5kVGltZSwgZml4TGluZUJyZWFrcyh0ZXh0LnRyaW0oKSkpO1xuXG4gICAgICBpZiAoaW5kZW50ID49IDE2KSB7XG4gICAgICAgIGluZGVudC0tO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaW5kZW50Kys7XG4gICAgICB9IC8vIFZUVEN1ZS5saW5lIGdldCdzIGZsYWtleSB3aGVuIHVzaW5nIGNvbnRyb2xzLCBzbyBsZXQncyBub3cgaW5jbHVkZSBsaW5lIDEzJjE0XG4gICAgICAvLyBhbHNvLCBkcm9wIGxpbmUgMSBzaW5jZSBpdCdzIHRvIGNsb3NlIHRvIHRoZSB0b3BcblxuXG4gICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvRmlyZWZveFxcLy8pKSB7XG4gICAgICAgIGN1ZS5saW5lID0gciArIDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjdWUubGluZSA9IHIgPiA3ID8gciAtIDIgOiByICsgMTtcbiAgICAgIH1cblxuICAgICAgY3VlLmFsaWduID0gJ2xlZnQnOyAvLyBDbGFtcCB0aGUgcG9zaXRpb24gYmV0d2VlbiAwIGFuZCAxMDAgLSBpZiBvdXQgb2YgdGhlc2UgYm91bmRzLCBGaXJlZm94IHRocm93cyBhbiBleGNlcHRpb24gYW5kIGNhcHRpb25zIGJyZWFrXG5cbiAgICAgIGN1ZS5wb3NpdGlvbiA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEwMCwgMTAwICogKGluZGVudCAvIDMyKSkpO1xuICAgICAgcmVzdWx0LnB1c2goY3VlKTtcblxuICAgICAgaWYgKHRyYWNrKSB7XG4gICAgICAgIHRyYWNrLmFkZEN1ZShjdWUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy91dGlscy9jZWEtNjA4LXBhcnNlci50c1xuXG4vKipcbiAqXG4gKiBUaGlzIGNvZGUgd2FzIHBvcnRlZCBmcm9tIHRoZSBkYXNoLmpzIHByb2plY3QgYXQ6XG4gKiAgIGh0dHBzOi8vZ2l0aHViLmNvbS9EYXNoLUluZHVzdHJ5LUZvcnVtL2Rhc2guanMvYmxvYi9kZXZlbG9wbWVudC9leHRlcm5hbHMvY2VhNjA4LXBhcnNlci5qc1xuICogICBodHRwczovL2dpdGh1Yi5jb20vRGFzaC1JbmR1c3RyeS1Gb3J1bS9kYXNoLmpzL2NvbW1pdC84MjY5YjI2YTc2MWUwODUzYmIyMWQ3ODc4MGVkOTQ1MTQ0ZWNkZDRkI2RpZmYtNzFiYzI5NWEyZDZiNmI3MDkzYTFkMzI5MGQ1M2E0YjJcbiAqXG4gKiBUaGUgb3JpZ2luYWwgY29weXJpZ2h0IGFwcGVhcnMgYmVsb3c6XG4gKlxuICogVGhlIGNvcHlyaWdodCBpbiB0aGlzIHNvZnR3YXJlIGlzIGJlaW5nIG1hZGUgYXZhaWxhYmxlIHVuZGVyIHRoZSBCU0QgTGljZW5zZSxcbiAqIGluY2x1ZGVkIGJlbG93LiBUaGlzIHNvZnR3YXJlIG1heSBiZSBzdWJqZWN0IHRvIG90aGVyIHRoaXJkIHBhcnR5IGFuZCBjb250cmlidXRvclxuICogcmlnaHRzLCBpbmNsdWRpbmcgcGF0ZW50IHJpZ2h0cywgYW5kIG5vIHN1Y2ggcmlnaHRzIGFyZSBncmFudGVkIHVuZGVyIHRoaXMgbGljZW5zZS5cbiAqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTUtMjAxNiwgREFTSCBJbmR1c3RyeSBGb3J1bS5cbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKlxuICogUmVkaXN0cmlidXRpb24gYW5kIHVzZSBpbiBzb3VyY2UgYW5kIGJpbmFyeSBmb3Jtcywgd2l0aCBvciB3aXRob3V0IG1vZGlmaWNhdGlvbixcbiAqIGFyZSBwZXJtaXR0ZWQgcHJvdmlkZWQgdGhhdCB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnMgYXJlIG1ldDpcbiAqICAxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsIHRoaXNcbiAqICBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lci5cbiAqICAqIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSxcbiAqICB0aGlzIGxpc3Qgb2YgY29uZGl0aW9ucyBhbmQgdGhlIGZvbGxvd2luZyBkaXNjbGFpbWVyIGluIHRoZSBkb2N1bWVudGF0aW9uIGFuZC9vclxuICogIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24uXG4gKiAgMi4gTmVpdGhlciB0aGUgbmFtZSBvZiBEYXNoIEluZHVzdHJ5IEZvcnVtIG5vciB0aGUgbmFtZXMgb2YgaXRzXG4gKiAgY29udHJpYnV0b3JzIG1heSBiZSB1c2VkIHRvIGVuZG9yc2Ugb3IgcHJvbW90ZSBwcm9kdWN0cyBkZXJpdmVkIGZyb20gdGhpcyBzb2Z0d2FyZVxuICogIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLlxuICpcbiAqICBUSElTIFNPRlRXQVJFIElTIFBST1ZJREVEIEJZIFRIRSBDT1BZUklHSFQgSE9MREVSUyBBTkQgQ09OVFJJQlVUT1JTIEFTIElTIEFORCBBTllcbiAqICBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFOVElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSBJTVBMSUVEXG4gKiAgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgQU5EIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFSRSBESVNDTEFJTUVELlxuICogIElOIE5PIEVWRU5UIFNIQUxMIFRIRSBDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsXG4gKiAgSU5ESVJFQ1QsIElOQ0lERU5UQUwsIFNQRUNJQUwsIEVYRU1QTEFSWSwgT1IgQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJTkNMVURJTkcsIEJVVFxuICogIE5PVCBMSU1JVEVEIFRPLCBQUk9DVVJFTUVOVCBPRiBTVUJTVElUVVRFIEdPT0RTIE9SIFNFUlZJQ0VTOyBMT1NTIE9GIFVTRSwgREFUQSwgT1JcbiAqICBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSxcbiAqICBXSEVUSEVSIElOIENPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JUIChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpXG4gKiAgQVJJU0lORyBJTiBBTlkgV0FZIE9VVCBPRiBUSEUgVVNFIE9GIFRISVMgU09GVFdBUkUsIEVWRU4gSUYgQURWSVNFRCBPRiBUSEVcbiAqICBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS5cbiAqL1xuXG4vKipcbiAqICBFeGNlcHRpb25zIGZyb20gcmVndWxhciBBU0NJSS4gQ29kZVBvaW50cyBhcmUgbWFwcGVkIHRvIFVURi0xNiBjb2Rlc1xuICovXG5cbnZhciBzcGVjaWFsQ2VhNjA4Q2hhcnNDb2RlcyA9IHtcbiAgMHgyYTogMHhlMSxcbiAgLy8gbG93ZXJjYXNlIGEsIGFjdXRlIGFjY2VudFxuICAweDVjOiAweGU5LFxuICAvLyBsb3dlcmNhc2UgZSwgYWN1dGUgYWNjZW50XG4gIDB4NWU6IDB4ZWQsXG4gIC8vIGxvd2VyY2FzZSBpLCBhY3V0ZSBhY2NlbnRcbiAgMHg1ZjogMHhmMyxcbiAgLy8gbG93ZXJjYXNlIG8sIGFjdXRlIGFjY2VudFxuICAweDYwOiAweGZhLFxuICAvLyBsb3dlcmNhc2UgdSwgYWN1dGUgYWNjZW50XG4gIDB4N2I6IDB4ZTcsXG4gIC8vIGxvd2VyY2FzZSBjIHdpdGggY2VkaWxsYVxuICAweDdjOiAweGY3LFxuICAvLyBkaXZpc2lvbiBzeW1ib2xcbiAgMHg3ZDogMHhkMSxcbiAgLy8gdXBwZXJjYXNlIE4gdGlsZGVcbiAgMHg3ZTogMHhmMSxcbiAgLy8gbG93ZXJjYXNlIG4gdGlsZGVcbiAgMHg3ZjogMHgyNTg4LFxuICAvLyBGdWxsIGJsb2NrXG4gIC8vIFRISVMgQkxPQ0sgSU5DTFVERVMgVEhFIDE2IEVYVEVOREVEIChUV08tQllURSkgTElORSAyMSBDSEFSQUNURVJTXG4gIC8vIFRIQVQgQ09NRSBGUk9NIEhJIEJZVEU9MHgxMSBBTkQgTE9XIEJFVFdFRU4gMHgzMCBBTkQgMHgzRlxuICAvLyBUSElTIE1FQU5TIFRIQVQgXFx4NTAgTVVTVCBCRSBBRERFRCBUTyBUSEUgVkFMVUVTXG4gIDB4ODA6IDB4YWUsXG4gIC8vIFJlZ2lzdGVyZWQgc3ltYm9sIChSKVxuICAweDgxOiAweGIwLFxuICAvLyBkZWdyZWUgc2lnblxuICAweDgyOiAweGJkLFxuICAvLyAxLzIgc3ltYm9sXG4gIDB4ODM6IDB4YmYsXG4gIC8vIEludmVydGVkIChvcGVuKSBxdWVzdGlvbiBtYXJrXG4gIDB4ODQ6IDB4MjEyMixcbiAgLy8gVHJhZGVtYXJrIHN5bWJvbCAoVE0pXG4gIDB4ODU6IDB4YTIsXG4gIC8vIENlbnRzIHN5bWJvbFxuICAweDg2OiAweGEzLFxuICAvLyBQb3VuZHMgc3RlcmxpbmdcbiAgMHg4NzogMHgyNjZhLFxuICAvLyBNdXNpYyA4J3RoIG5vdGVcbiAgMHg4ODogMHhlMCxcbiAgLy8gbG93ZXJjYXNlIGEsIGdyYXZlIGFjY2VudFxuICAweDg5OiAweDIwLFxuICAvLyB0cmFuc3BhcmVudCBzcGFjZSAocmVndWxhcilcbiAgMHg4YTogMHhlOCxcbiAgLy8gbG93ZXJjYXNlIGUsIGdyYXZlIGFjY2VudFxuICAweDhiOiAweGUyLFxuICAvLyBsb3dlcmNhc2UgYSwgY2lyY3VtZmxleCBhY2NlbnRcbiAgMHg4YzogMHhlYSxcbiAgLy8gbG93ZXJjYXNlIGUsIGNpcmN1bWZsZXggYWNjZW50XG4gIDB4OGQ6IDB4ZWUsXG4gIC8vIGxvd2VyY2FzZSBpLCBjaXJjdW1mbGV4IGFjY2VudFxuICAweDhlOiAweGY0LFxuICAvLyBsb3dlcmNhc2UgbywgY2lyY3VtZmxleCBhY2NlbnRcbiAgMHg4ZjogMHhmYixcbiAgLy8gbG93ZXJjYXNlIHUsIGNpcmN1bWZsZXggYWNjZW50XG4gIC8vIFRISVMgQkxPQ0sgSU5DTFVERVMgVEhFIDMyIEVYVEVOREVEIChUV08tQllURSkgTElORSAyMSBDSEFSQUNURVJTXG4gIC8vIFRIQVQgQ09NRSBGUk9NIEhJIEJZVEU9MHgxMiBBTkQgTE9XIEJFVFdFRU4gMHgyMCBBTkQgMHgzRlxuICAweDkwOiAweGMxLFxuICAvLyBjYXBpdGFsIGxldHRlciBBIHdpdGggYWN1dGVcbiAgMHg5MTogMHhjOSxcbiAgLy8gY2FwaXRhbCBsZXR0ZXIgRSB3aXRoIGFjdXRlXG4gIDB4OTI6IDB4ZDMsXG4gIC8vIGNhcGl0YWwgbGV0dGVyIE8gd2l0aCBhY3V0ZVxuICAweDkzOiAweGRhLFxuICAvLyBjYXBpdGFsIGxldHRlciBVIHdpdGggYWN1dGVcbiAgMHg5NDogMHhkYyxcbiAgLy8gY2FwaXRhbCBsZXR0ZXIgVSB3aXRoIGRpYXJlc2lzXG4gIDB4OTU6IDB4ZmMsXG4gIC8vIGxvd2VyY2FzZSBsZXR0ZXIgVSB3aXRoIGRpYWVyZXNpc1xuICAweDk2OiAweDIwMTgsXG4gIC8vIG9wZW5pbmcgc2luZ2xlIHF1b3RlXG4gIDB4OTc6IDB4YTEsXG4gIC8vIGludmVydGVkIGV4Y2xhbWF0aW9uIG1hcmtcbiAgMHg5ODogMHgyYSxcbiAgLy8gYXN0ZXJpc2tcbiAgMHg5OTogMHgyMDE5LFxuICAvLyBjbG9zaW5nIHNpbmdsZSBxdW90ZVxuICAweDlhOiAweDI1MDEsXG4gIC8vIGJveCBkcmF3aW5ncyBoZWF2eSBob3Jpem9udGFsXG4gIDB4OWI6IDB4YTksXG4gIC8vIGNvcHlyaWdodCBzaWduXG4gIDB4OWM6IDB4MjEyMCxcbiAgLy8gU2VydmljZSBtYXJrXG4gIDB4OWQ6IDB4MjAyMixcbiAgLy8gKHJvdW5kKSBidWxsZXRcbiAgMHg5ZTogMHgyMDFjLFxuICAvLyBMZWZ0IGRvdWJsZSBxdW90YXRpb24gbWFya1xuICAweDlmOiAweDIwMWQsXG4gIC8vIFJpZ2h0IGRvdWJsZSBxdW90YXRpb24gbWFya1xuICAweGEwOiAweGMwLFxuICAvLyB1cHBlcmNhc2UgQSwgZ3JhdmUgYWNjZW50XG4gIDB4YTE6IDB4YzIsXG4gIC8vIHVwcGVyY2FzZSBBLCBjaXJjdW1mbGV4XG4gIDB4YTI6IDB4YzcsXG4gIC8vIHVwcGVyY2FzZSBDIHdpdGggY2VkaWxsYVxuICAweGEzOiAweGM4LFxuICAvLyB1cHBlcmNhc2UgRSwgZ3JhdmUgYWNjZW50XG4gIDB4YTQ6IDB4Y2EsXG4gIC8vIHVwcGVyY2FzZSBFLCBjaXJjdW1mbGV4XG4gIDB4YTU6IDB4Y2IsXG4gIC8vIGNhcGl0YWwgbGV0dGVyIEUgd2l0aCBkaWFyZXNpc1xuICAweGE2OiAweGViLFxuICAvLyBsb3dlcmNhc2UgbGV0dGVyIGUgd2l0aCBkaWFyZXNpc1xuICAweGE3OiAweGNlLFxuICAvLyB1cHBlcmNhc2UgSSwgY2lyY3VtZmxleFxuICAweGE4OiAweGNmLFxuICAvLyB1cHBlcmNhc2UgSSwgd2l0aCBkaWFyZXNpc1xuICAweGE5OiAweGVmLFxuICAvLyBsb3dlcmNhc2UgaSwgd2l0aCBkaWFyZXNpc1xuICAweGFhOiAweGQ0LFxuICAvLyB1cHBlcmNhc2UgTywgY2lyY3VtZmxleFxuICAweGFiOiAweGQ5LFxuICAvLyB1cHBlcmNhc2UgVSwgZ3JhdmUgYWNjZW50XG4gIDB4YWM6IDB4ZjksXG4gIC8vIGxvd2VyY2FzZSB1LCBncmF2ZSBhY2NlbnRcbiAgMHhhZDogMHhkYixcbiAgLy8gdXBwZXJjYXNlIFUsIGNpcmN1bWZsZXhcbiAgMHhhZTogMHhhYixcbiAgLy8gbGVmdC1wb2ludGluZyBkb3VibGUgYW5nbGUgcXVvdGF0aW9uIG1hcmtcbiAgMHhhZjogMHhiYixcbiAgLy8gcmlnaHQtcG9pbnRpbmcgZG91YmxlIGFuZ2xlIHF1b3RhdGlvbiBtYXJrXG4gIC8vIFRISVMgQkxPQ0sgSU5DTFVERVMgVEhFIDMyIEVYVEVOREVEIChUV08tQllURSkgTElORSAyMSBDSEFSQUNURVJTXG4gIC8vIFRIQVQgQ09NRSBGUk9NIEhJIEJZVEU9MHgxMyBBTkQgTE9XIEJFVFdFRU4gMHgyMCBBTkQgMHgzRlxuICAweGIwOiAweGMzLFxuICAvLyBVcHBlcmNhc2UgQSwgdGlsZGVcbiAgMHhiMTogMHhlMyxcbiAgLy8gTG93ZXJjYXNlIGEsIHRpbGRlXG4gIDB4YjI6IDB4Y2QsXG4gIC8vIFVwcGVyY2FzZSBJLCBhY3V0ZSBhY2NlbnRcbiAgMHhiMzogMHhjYyxcbiAgLy8gVXBwZXJjYXNlIEksIGdyYXZlIGFjY2VudFxuICAweGI0OiAweGVjLFxuICAvLyBMb3dlcmNhc2UgaSwgZ3JhdmUgYWNjZW50XG4gIDB4YjU6IDB4ZDIsXG4gIC8vIFVwcGVyY2FzZSBPLCBncmF2ZSBhY2NlbnRcbiAgMHhiNjogMHhmMixcbiAgLy8gTG93ZXJjYXNlIG8sIGdyYXZlIGFjY2VudFxuICAweGI3OiAweGQ1LFxuICAvLyBVcHBlcmNhc2UgTywgdGlsZGVcbiAgMHhiODogMHhmNSxcbiAgLy8gTG93ZXJjYXNlIG8sIHRpbGRlXG4gIDB4Yjk6IDB4N2IsXG4gIC8vIE9wZW4gY3VybHkgYnJhY2VcbiAgMHhiYTogMHg3ZCxcbiAgLy8gQ2xvc2luZyBjdXJseSBicmFjZVxuICAweGJiOiAweDVjLFxuICAvLyBCYWNrc2xhc2hcbiAgMHhiYzogMHg1ZSxcbiAgLy8gQ2FyZXRcbiAgMHhiZDogMHg1ZixcbiAgLy8gVW5kZXJzY29yZVxuICAweGJlOiAweDdjLFxuICAvLyBQaXBlICh2ZXJ0aWNhbCBsaW5lKVxuICAweGJmOiAweDIyM2MsXG4gIC8vIFRpbGRlIG9wZXJhdG9yXG4gIDB4YzA6IDB4YzQsXG4gIC8vIFVwcGVyY2FzZSBBLCB1bWxhdXRcbiAgMHhjMTogMHhlNCxcbiAgLy8gTG93ZXJjYXNlIEEsIHVtbGF1dFxuICAweGMyOiAweGQ2LFxuICAvLyBVcHBlcmNhc2UgTywgdW1sYXV0XG4gIDB4YzM6IDB4ZjYsXG4gIC8vIExvd2VyY2FzZSBvLCB1bWxhdXRcbiAgMHhjNDogMHhkZixcbiAgLy8gRXNzemV0dCAoc2hhcnAgUylcbiAgMHhjNTogMHhhNSxcbiAgLy8gWWVuIHN5bWJvbFxuICAweGM2OiAweGE0LFxuICAvLyBHZW5lcmljIGN1cnJlbmN5IHNpZ25cbiAgMHhjNzogMHgyNTAzLFxuICAvLyBCb3ggZHJhd2luZ3MgaGVhdnkgdmVydGljYWxcbiAgMHhjODogMHhjNSxcbiAgLy8gVXBwZXJjYXNlIEEsIHJpbmdcbiAgMHhjOTogMHhlNSxcbiAgLy8gTG93ZXJjYXNlIEEsIHJpbmdcbiAgMHhjYTogMHhkOCxcbiAgLy8gVXBwZXJjYXNlIE8sIHN0cm9rZVxuICAweGNiOiAweGY4LFxuICAvLyBMb3dlcmNhc2Ugbywgc3Ryb2tcbiAgMHhjYzogMHgyNTBmLFxuICAvLyBCb3ggZHJhd2luZ3MgaGVhdnkgZG93biBhbmQgcmlnaHRcbiAgMHhjZDogMHgyNTEzLFxuICAvLyBCb3ggZHJhd2luZ3MgaGVhdnkgZG93biBhbmQgbGVmdFxuICAweGNlOiAweDI1MTcsXG4gIC8vIEJveCBkcmF3aW5ncyBoZWF2eSB1cCBhbmQgcmlnaHRcbiAgMHhjZjogMHgyNTFiIC8vIEJveCBkcmF3aW5ncyBoZWF2eSB1cCBhbmQgbGVmdFxuXG59O1xuLyoqXG4gKiBVdGlsc1xuICovXG5cbnZhciBnZXRDaGFyRm9yQnl0ZSA9IGZ1bmN0aW9uIGdldENoYXJGb3JCeXRlKF9ieXRlKSB7XG4gIHZhciBjaGFyQ29kZSA9IF9ieXRlO1xuXG4gIGlmIChzcGVjaWFsQ2VhNjA4Q2hhcnNDb2Rlcy5oYXNPd25Qcm9wZXJ0eShfYnl0ZSkpIHtcbiAgICBjaGFyQ29kZSA9IHNwZWNpYWxDZWE2MDhDaGFyc0NvZGVzW19ieXRlXTtcbiAgfVxuXG4gIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKGNoYXJDb2RlKTtcbn07XG5cbnZhciBOUl9ST1dTID0gMTU7XG52YXIgTlJfQ09MUyA9IDEwMDsgLy8gVGFibGVzIHRvIGxvb2sgdXAgcm93IGZyb20gUEFDIGRhdGFcblxudmFyIHJvd3NMb3dDaDEgPSB7XG4gIDB4MTE6IDEsXG4gIDB4MTI6IDMsXG4gIDB4MTU6IDUsXG4gIDB4MTY6IDcsXG4gIDB4MTc6IDksXG4gIDB4MTA6IDExLFxuICAweDEzOiAxMixcbiAgMHgxNDogMTRcbn07XG52YXIgcm93c0hpZ2hDaDEgPSB7XG4gIDB4MTE6IDIsXG4gIDB4MTI6IDQsXG4gIDB4MTU6IDYsXG4gIDB4MTY6IDgsXG4gIDB4MTc6IDEwLFxuICAweDEzOiAxMyxcbiAgMHgxNDogMTVcbn07XG52YXIgcm93c0xvd0NoMiA9IHtcbiAgMHgxOTogMSxcbiAgMHgxQTogMyxcbiAgMHgxRDogNSxcbiAgMHgxRTogNyxcbiAgMHgxRjogOSxcbiAgMHgxODogMTEsXG4gIDB4MUI6IDEyLFxuICAweDFDOiAxNFxufTtcbnZhciByb3dzSGlnaENoMiA9IHtcbiAgMHgxOTogMixcbiAgMHgxQTogNCxcbiAgMHgxRDogNixcbiAgMHgxRTogOCxcbiAgMHgxRjogMTAsXG4gIDB4MUI6IDEzLFxuICAweDFDOiAxNVxufTtcbnZhciBiYWNrZ3JvdW5kQ29sb3JzID0gWyd3aGl0ZScsICdncmVlbicsICdibHVlJywgJ2N5YW4nLCAncmVkJywgJ3llbGxvdycsICdtYWdlbnRhJywgJ2JsYWNrJywgJ3RyYW5zcGFyZW50J107XG52YXIgVmVyYm9zZUxldmVsO1xuXG4oZnVuY3Rpb24gKFZlcmJvc2VMZXZlbCkge1xuICBWZXJib3NlTGV2ZWxbVmVyYm9zZUxldmVsW1wiRVJST1JcIl0gPSAwXSA9IFwiRVJST1JcIjtcbiAgVmVyYm9zZUxldmVsW1ZlcmJvc2VMZXZlbFtcIlRFWFRcIl0gPSAxXSA9IFwiVEVYVFwiO1xuICBWZXJib3NlTGV2ZWxbVmVyYm9zZUxldmVsW1wiV0FSTklOR1wiXSA9IDJdID0gXCJXQVJOSU5HXCI7XG4gIFZlcmJvc2VMZXZlbFtWZXJib3NlTGV2ZWxbXCJJTkZPXCJdID0gMl0gPSBcIklORk9cIjtcbiAgVmVyYm9zZUxldmVsW1ZlcmJvc2VMZXZlbFtcIkRFQlVHXCJdID0gM10gPSBcIkRFQlVHXCI7XG4gIFZlcmJvc2VMZXZlbFtWZXJib3NlTGV2ZWxbXCJEQVRBXCJdID0gM10gPSBcIkRBVEFcIjtcbn0pKFZlcmJvc2VMZXZlbCB8fCAoVmVyYm9zZUxldmVsID0ge30pKTtcblxudmFyIGNlYV82MDhfcGFyc2VyX0NhcHRpb25zTG9nZ2VyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gQ2FwdGlvbnNMb2dnZXIoKSB7XG4gICAgdGhpcy50aW1lID0gbnVsbDtcbiAgICB0aGlzLnZlcmJvc2VMZXZlbCA9IFZlcmJvc2VMZXZlbC5FUlJPUjtcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBDYXB0aW9uc0xvZ2dlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmxvZyA9IGZ1bmN0aW9uIGxvZyhzZXZlcml0eSwgbXNnKSB7XG4gICAgaWYgKHRoaXMudmVyYm9zZUxldmVsID49IHNldmVyaXR5KSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKHRoaXMudGltZSArIFwiIFtcIiArIHNldmVyaXR5ICsgXCJdIFwiICsgbXNnKTtcbiAgICB9XG4gIH07XG5cbiAgcmV0dXJuIENhcHRpb25zTG9nZ2VyO1xufSgpO1xuXG52YXIgbnVtQXJyYXlUb0hleEFycmF5ID0gZnVuY3Rpb24gbnVtQXJyYXlUb0hleEFycmF5KG51bUFycmF5KSB7XG4gIHZhciBoZXhBcnJheSA9IFtdO1xuXG4gIGZvciAodmFyIGogPSAwOyBqIDwgbnVtQXJyYXkubGVuZ3RoOyBqKyspIHtcbiAgICBoZXhBcnJheS5wdXNoKG51bUFycmF5W2pdLnRvU3RyaW5nKDE2KSk7XG4gIH1cblxuICByZXR1cm4gaGV4QXJyYXk7XG59O1xuXG52YXIgUGVuU3RhdGUgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBQZW5TdGF0ZShmb3JlZ3JvdW5kLCB1bmRlcmxpbmUsIGl0YWxpY3MsIGJhY2tncm91bmQsIGZsYXNoKSB7XG4gICAgdGhpcy5mb3JlZ3JvdW5kID0gdm9pZCAwO1xuICAgIHRoaXMudW5kZXJsaW5lID0gdm9pZCAwO1xuICAgIHRoaXMuaXRhbGljcyA9IHZvaWQgMDtcbiAgICB0aGlzLmJhY2tncm91bmQgPSB2b2lkIDA7XG4gICAgdGhpcy5mbGFzaCA9IHZvaWQgMDtcbiAgICB0aGlzLmZvcmVncm91bmQgPSBmb3JlZ3JvdW5kIHx8ICd3aGl0ZSc7XG4gICAgdGhpcy51bmRlcmxpbmUgPSB1bmRlcmxpbmUgfHwgZmFsc2U7XG4gICAgdGhpcy5pdGFsaWNzID0gaXRhbGljcyB8fCBmYWxzZTtcbiAgICB0aGlzLmJhY2tncm91bmQgPSBiYWNrZ3JvdW5kIHx8ICdibGFjayc7XG4gICAgdGhpcy5mbGFzaCA9IGZsYXNoIHx8IGZhbHNlO1xuICB9XG5cbiAgdmFyIF9wcm90bzIgPSBQZW5TdGF0ZS5wcm90b3R5cGU7XG5cbiAgX3Byb3RvMi5yZXNldCA9IGZ1bmN0aW9uIHJlc2V0KCkge1xuICAgIHRoaXMuZm9yZWdyb3VuZCA9ICd3aGl0ZSc7XG4gICAgdGhpcy51bmRlcmxpbmUgPSBmYWxzZTtcbiAgICB0aGlzLml0YWxpY3MgPSBmYWxzZTtcbiAgICB0aGlzLmJhY2tncm91bmQgPSAnYmxhY2snO1xuICAgIHRoaXMuZmxhc2ggPSBmYWxzZTtcbiAgfTtcblxuICBfcHJvdG8yLnNldFN0eWxlcyA9IGZ1bmN0aW9uIHNldFN0eWxlcyhzdHlsZXMpIHtcbiAgICB2YXIgYXR0cmlicyA9IFsnZm9yZWdyb3VuZCcsICd1bmRlcmxpbmUnLCAnaXRhbGljcycsICdiYWNrZ3JvdW5kJywgJ2ZsYXNoJ107XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGF0dHJpYnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBzdHlsZSA9IGF0dHJpYnNbaV07XG5cbiAgICAgIGlmIChzdHlsZXMuaGFzT3duUHJvcGVydHkoc3R5bGUpKSB7XG4gICAgICAgIHRoaXNbc3R5bGVdID0gc3R5bGVzW3N0eWxlXTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvMi5pc0RlZmF1bHQgPSBmdW5jdGlvbiBpc0RlZmF1bHQoKSB7XG4gICAgcmV0dXJuIHRoaXMuZm9yZWdyb3VuZCA9PT0gJ3doaXRlJyAmJiAhdGhpcy51bmRlcmxpbmUgJiYgIXRoaXMuaXRhbGljcyAmJiB0aGlzLmJhY2tncm91bmQgPT09ICdibGFjaycgJiYgIXRoaXMuZmxhc2g7XG4gIH07XG5cbiAgX3Byb3RvMi5lcXVhbHMgPSBmdW5jdGlvbiBlcXVhbHMob3RoZXIpIHtcbiAgICByZXR1cm4gdGhpcy5mb3JlZ3JvdW5kID09PSBvdGhlci5mb3JlZ3JvdW5kICYmIHRoaXMudW5kZXJsaW5lID09PSBvdGhlci51bmRlcmxpbmUgJiYgdGhpcy5pdGFsaWNzID09PSBvdGhlci5pdGFsaWNzICYmIHRoaXMuYmFja2dyb3VuZCA9PT0gb3RoZXIuYmFja2dyb3VuZCAmJiB0aGlzLmZsYXNoID09PSBvdGhlci5mbGFzaDtcbiAgfTtcblxuICBfcHJvdG8yLmNvcHkgPSBmdW5jdGlvbiBjb3B5KG5ld1BlblN0YXRlKSB7XG4gICAgdGhpcy5mb3JlZ3JvdW5kID0gbmV3UGVuU3RhdGUuZm9yZWdyb3VuZDtcbiAgICB0aGlzLnVuZGVybGluZSA9IG5ld1BlblN0YXRlLnVuZGVybGluZTtcbiAgICB0aGlzLml0YWxpY3MgPSBuZXdQZW5TdGF0ZS5pdGFsaWNzO1xuICAgIHRoaXMuYmFja2dyb3VuZCA9IG5ld1BlblN0YXRlLmJhY2tncm91bmQ7XG4gICAgdGhpcy5mbGFzaCA9IG5ld1BlblN0YXRlLmZsYXNoO1xuICB9O1xuXG4gIF9wcm90bzIudG9TdHJpbmcgPSBmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgICByZXR1cm4gJ2NvbG9yPScgKyB0aGlzLmZvcmVncm91bmQgKyAnLCB1bmRlcmxpbmU9JyArIHRoaXMudW5kZXJsaW5lICsgJywgaXRhbGljcz0nICsgdGhpcy5pdGFsaWNzICsgJywgYmFja2dyb3VuZD0nICsgdGhpcy5iYWNrZ3JvdW5kICsgJywgZmxhc2g9JyArIHRoaXMuZmxhc2g7XG4gIH07XG5cbiAgcmV0dXJuIFBlblN0YXRlO1xufSgpO1xuLyoqXG4gKiBVbmljb2RlIGNoYXJhY3RlciB3aXRoIHN0eWxpbmcgYW5kIGJhY2tncm91bmQuXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuXG5cbnZhciBTdHlsZWRVbmljb2RlQ2hhciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIFN0eWxlZFVuaWNvZGVDaGFyKHVjaGFyLCBmb3JlZ3JvdW5kLCB1bmRlcmxpbmUsIGl0YWxpY3MsIGJhY2tncm91bmQsIGZsYXNoKSB7XG4gICAgdGhpcy51Y2hhciA9IHZvaWQgMDtcbiAgICB0aGlzLnBlblN0YXRlID0gdm9pZCAwO1xuICAgIHRoaXMudWNoYXIgPSB1Y2hhciB8fCAnICc7IC8vIHVuaWNvZGUgY2hhcmFjdGVyXG5cbiAgICB0aGlzLnBlblN0YXRlID0gbmV3IFBlblN0YXRlKGZvcmVncm91bmQsIHVuZGVybGluZSwgaXRhbGljcywgYmFja2dyb3VuZCwgZmxhc2gpO1xuICB9XG5cbiAgdmFyIF9wcm90bzMgPSBTdHlsZWRVbmljb2RlQ2hhci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvMy5yZXNldCA9IGZ1bmN0aW9uIHJlc2V0KCkge1xuICAgIHRoaXMudWNoYXIgPSAnICc7XG4gICAgdGhpcy5wZW5TdGF0ZS5yZXNldCgpO1xuICB9O1xuXG4gIF9wcm90bzMuc2V0Q2hhciA9IGZ1bmN0aW9uIHNldENoYXIodWNoYXIsIG5ld1BlblN0YXRlKSB7XG4gICAgdGhpcy51Y2hhciA9IHVjaGFyO1xuICAgIHRoaXMucGVuU3RhdGUuY29weShuZXdQZW5TdGF0ZSk7XG4gIH07XG5cbiAgX3Byb3RvMy5zZXRQZW5TdGF0ZSA9IGZ1bmN0aW9uIHNldFBlblN0YXRlKG5ld1BlblN0YXRlKSB7XG4gICAgdGhpcy5wZW5TdGF0ZS5jb3B5KG5ld1BlblN0YXRlKTtcbiAgfTtcblxuICBfcHJvdG8zLmVxdWFscyA9IGZ1bmN0aW9uIGVxdWFscyhvdGhlcikge1xuICAgIHJldHVybiB0aGlzLnVjaGFyID09PSBvdGhlci51Y2hhciAmJiB0aGlzLnBlblN0YXRlLmVxdWFscyhvdGhlci5wZW5TdGF0ZSk7XG4gIH07XG5cbiAgX3Byb3RvMy5jb3B5ID0gZnVuY3Rpb24gY29weShuZXdDaGFyKSB7XG4gICAgdGhpcy51Y2hhciA9IG5ld0NoYXIudWNoYXI7XG4gICAgdGhpcy5wZW5TdGF0ZS5jb3B5KG5ld0NoYXIucGVuU3RhdGUpO1xuICB9O1xuXG4gIF9wcm90bzMuaXNFbXB0eSA9IGZ1bmN0aW9uIGlzRW1wdHkoKSB7XG4gICAgcmV0dXJuIHRoaXMudWNoYXIgPT09ICcgJyAmJiB0aGlzLnBlblN0YXRlLmlzRGVmYXVsdCgpO1xuICB9O1xuXG4gIHJldHVybiBTdHlsZWRVbmljb2RlQ2hhcjtcbn0oKTtcbi8qKlxuICogQ0VBLTYwOCByb3cgY29uc2lzdGluZyBvZiBOUl9DT0xTIGluc3RhbmNlcyBvZiBTdHlsZWRVbmljb2RlQ2hhci5cbiAqIEBjb25zdHJ1Y3RvclxuICovXG5cblxudmFyIFJvdyA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoKSB7XG4gIGZ1bmN0aW9uIFJvdyhsb2dnZXIpIHtcbiAgICB0aGlzLmNoYXJzID0gdm9pZCAwO1xuICAgIHRoaXMucG9zID0gdm9pZCAwO1xuICAgIHRoaXMuY3VyclBlblN0YXRlID0gdm9pZCAwO1xuICAgIHRoaXMuY3VlU3RhcnRUaW1lID0gdm9pZCAwO1xuICAgIHRoaXMubG9nZ2VyID0gdm9pZCAwO1xuICAgIHRoaXMuY2hhcnMgPSBbXTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgTlJfQ09MUzsgaSsrKSB7XG4gICAgICB0aGlzLmNoYXJzLnB1c2gobmV3IFN0eWxlZFVuaWNvZGVDaGFyKCkpO1xuICAgIH1cblxuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICAgIHRoaXMucG9zID0gMDtcbiAgICB0aGlzLmN1cnJQZW5TdGF0ZSA9IG5ldyBQZW5TdGF0ZSgpO1xuICB9XG5cbiAgdmFyIF9wcm90bzQgPSBSb3cucHJvdG90eXBlO1xuXG4gIF9wcm90bzQuZXF1YWxzID0gZnVuY3Rpb24gZXF1YWxzKG90aGVyKSB7XG4gICAgdmFyIGVxdWFsID0gdHJ1ZTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgTlJfQ09MUzsgaSsrKSB7XG4gICAgICBpZiAoIXRoaXMuY2hhcnNbaV0uZXF1YWxzKG90aGVyLmNoYXJzW2ldKSkge1xuICAgICAgICBlcXVhbCA9IGZhbHNlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZXF1YWw7XG4gIH07XG5cbiAgX3Byb3RvNC5jb3B5ID0gZnVuY3Rpb24gY29weShvdGhlcikge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgTlJfQ09MUzsgaSsrKSB7XG4gICAgICB0aGlzLmNoYXJzW2ldLmNvcHkob3RoZXIuY2hhcnNbaV0pO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG80LmlzRW1wdHkgPSBmdW5jdGlvbiBpc0VtcHR5KCkge1xuICAgIHZhciBlbXB0eSA9IHRydWU7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IE5SX0NPTFM7IGkrKykge1xuICAgICAgaWYgKCF0aGlzLmNoYXJzW2ldLmlzRW1wdHkoKSkge1xuICAgICAgICBlbXB0eSA9IGZhbHNlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZW1wdHk7XG4gIH1cbiAgLyoqXG4gICAqICBTZXQgdGhlIGN1cnNvciB0byBhIHZhbGlkIGNvbHVtbi5cbiAgICovXG4gIDtcblxuICBfcHJvdG80LnNldEN1cnNvciA9IGZ1bmN0aW9uIHNldEN1cnNvcihhYnNQb3MpIHtcbiAgICBpZiAodGhpcy5wb3MgIT09IGFic1Bvcykge1xuICAgICAgdGhpcy5wb3MgPSBhYnNQb3M7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMucG9zIDwgMCkge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5ERUJVRywgJ05lZ2F0aXZlIGN1cnNvciBwb3NpdGlvbiAnICsgdGhpcy5wb3MpO1xuICAgICAgdGhpcy5wb3MgPSAwO1xuICAgIH0gZWxzZSBpZiAodGhpcy5wb3MgPiBOUl9DT0xTKSB7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLkRFQlVHLCAnVG9vIGxhcmdlIGN1cnNvciBwb3NpdGlvbiAnICsgdGhpcy5wb3MpO1xuICAgICAgdGhpcy5wb3MgPSBOUl9DT0xTO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogTW92ZSB0aGUgY3Vyc29yIHJlbGF0aXZlIHRvIGN1cnJlbnQgcG9zaXRpb24uXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvNC5tb3ZlQ3Vyc29yID0gZnVuY3Rpb24gbW92ZUN1cnNvcihyZWxQb3MpIHtcbiAgICB2YXIgbmV3UG9zID0gdGhpcy5wb3MgKyByZWxQb3M7XG5cbiAgICBpZiAocmVsUG9zID4gMSkge1xuICAgICAgZm9yICh2YXIgaSA9IHRoaXMucG9zICsgMTsgaSA8IG5ld1BvcyArIDE7IGkrKykge1xuICAgICAgICB0aGlzLmNoYXJzW2ldLnNldFBlblN0YXRlKHRoaXMuY3VyclBlblN0YXRlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnNldEN1cnNvcihuZXdQb3MpO1xuICB9XG4gIC8qKlxuICAgKiBCYWNrc3BhY2UsIG1vdmUgb25lIHN0ZXAgYmFjayBhbmQgY2xlYXIgY2hhcmFjdGVyLlxuICAgKi9cbiAgO1xuXG4gIF9wcm90bzQuYmFja1NwYWNlID0gZnVuY3Rpb24gYmFja1NwYWNlKCkge1xuICAgIHRoaXMubW92ZUN1cnNvcigtMSk7XG4gICAgdGhpcy5jaGFyc1t0aGlzLnBvc10uc2V0Q2hhcignICcsIHRoaXMuY3VyclBlblN0YXRlKTtcbiAgfTtcblxuICBfcHJvdG80Lmluc2VydENoYXIgPSBmdW5jdGlvbiBpbnNlcnRDaGFyKF9ieXRlMikge1xuICAgIGlmIChfYnl0ZTIgPj0gMHg5MCkge1xuICAgICAgLy8gRXh0ZW5kZWQgY2hhclxuICAgICAgdGhpcy5iYWNrU3BhY2UoKTtcbiAgICB9XG5cbiAgICB2YXIgX2NoYXIgPSBnZXRDaGFyRm9yQnl0ZShfYnl0ZTIpO1xuXG4gICAgaWYgKHRoaXMucG9zID49IE5SX0NPTFMpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuRVJST1IsICdDYW5ub3QgaW5zZXJ0ICcgKyBfYnl0ZTIudG9TdHJpbmcoMTYpICsgJyAoJyArIF9jaGFyICsgJykgYXQgcG9zaXRpb24gJyArIHRoaXMucG9zICsgJy4gU2tpcHBpbmcgaXQhJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5jaGFyc1t0aGlzLnBvc10uc2V0Q2hhcihfY2hhciwgdGhpcy5jdXJyUGVuU3RhdGUpO1xuICAgIHRoaXMubW92ZUN1cnNvcigxKTtcbiAgfTtcblxuICBfcHJvdG80LmNsZWFyRnJvbVBvcyA9IGZ1bmN0aW9uIGNsZWFyRnJvbVBvcyhzdGFydFBvcykge1xuICAgIHZhciBpO1xuXG4gICAgZm9yIChpID0gc3RhcnRQb3M7IGkgPCBOUl9DT0xTOyBpKyspIHtcbiAgICAgIHRoaXMuY2hhcnNbaV0ucmVzZXQoKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvNC5jbGVhciA9IGZ1bmN0aW9uIGNsZWFyKCkge1xuICAgIHRoaXMuY2xlYXJGcm9tUG9zKDApO1xuICAgIHRoaXMucG9zID0gMDtcbiAgICB0aGlzLmN1cnJQZW5TdGF0ZS5yZXNldCgpO1xuICB9O1xuXG4gIF9wcm90bzQuY2xlYXJUb0VuZE9mUm93ID0gZnVuY3Rpb24gY2xlYXJUb0VuZE9mUm93KCkge1xuICAgIHRoaXMuY2xlYXJGcm9tUG9zKHRoaXMucG9zKTtcbiAgfTtcblxuICBfcHJvdG80LmdldFRleHRTdHJpbmcgPSBmdW5jdGlvbiBnZXRUZXh0U3RyaW5nKCkge1xuICAgIHZhciBjaGFycyA9IFtdO1xuICAgIHZhciBlbXB0eSA9IHRydWU7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IE5SX0NPTFM7IGkrKykge1xuICAgICAgdmFyIF9jaGFyMiA9IHRoaXMuY2hhcnNbaV0udWNoYXI7XG5cbiAgICAgIGlmIChfY2hhcjIgIT09ICcgJykge1xuICAgICAgICBlbXB0eSA9IGZhbHNlO1xuICAgICAgfVxuXG4gICAgICBjaGFycy5wdXNoKF9jaGFyMik7XG4gICAgfVxuXG4gICAgaWYgKGVtcHR5KSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBjaGFycy5qb2luKCcnKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvNC5zZXRQZW5TdHlsZXMgPSBmdW5jdGlvbiBzZXRQZW5TdHlsZXMoc3R5bGVzKSB7XG4gICAgdGhpcy5jdXJyUGVuU3RhdGUuc2V0U3R5bGVzKHN0eWxlcyk7XG4gICAgdmFyIGN1cnJDaGFyID0gdGhpcy5jaGFyc1t0aGlzLnBvc107XG4gICAgY3VyckNoYXIuc2V0UGVuU3RhdGUodGhpcy5jdXJyUGVuU3RhdGUpO1xuICB9O1xuXG4gIHJldHVybiBSb3c7XG59KCk7XG4vKipcbiAqIEtlZXAgYSBDRUEtNjA4IHNjcmVlbiBvZiAzMngxNSBzdHlsZWQgY2hhcmFjdGVyc1xuICogQGNvbnN0cnVjdG9yXG4gKi9cblxudmFyIENhcHRpb25TY3JlZW4gPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBDYXB0aW9uU2NyZWVuKGxvZ2dlcikge1xuICAgIHRoaXMucm93cyA9IHZvaWQgMDtcbiAgICB0aGlzLmN1cnJSb3cgPSB2b2lkIDA7XG4gICAgdGhpcy5uclJvbGxVcFJvd3MgPSB2b2lkIDA7XG4gICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuID0gdm9pZCAwO1xuICAgIHRoaXMubG9nZ2VyID0gdm9pZCAwO1xuICAgIHRoaXMucm93cyA9IFtdO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIHRoaXMucm93cy5wdXNoKG5ldyBSb3cobG9nZ2VyKSk7XG4gICAgfSAvLyBOb3RlIHRoYXQgd2UgdXNlIHplcm8tYmFzZWQgbnVtYmVyaW5nICgwLTE0KVxuXG5cbiAgICB0aGlzLmxvZ2dlciA9IGxvZ2dlcjtcbiAgICB0aGlzLmN1cnJSb3cgPSBOUl9ST1dTIC0gMTtcbiAgICB0aGlzLm5yUm9sbFVwUm93cyA9IG51bGw7XG4gICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuID0gbnVsbDtcbiAgICB0aGlzLnJlc2V0KCk7XG4gIH1cblxuICB2YXIgX3Byb3RvNSA9IENhcHRpb25TY3JlZW4ucHJvdG90eXBlO1xuXG4gIF9wcm90bzUucmVzZXQgPSBmdW5jdGlvbiByZXNldCgpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IE5SX1JPV1M7IGkrKykge1xuICAgICAgdGhpcy5yb3dzW2ldLmNsZWFyKCk7XG4gICAgfVxuXG4gICAgdGhpcy5jdXJyUm93ID0gTlJfUk9XUyAtIDE7XG4gIH07XG5cbiAgX3Byb3RvNS5lcXVhbHMgPSBmdW5jdGlvbiBlcXVhbHMob3RoZXIpIHtcbiAgICB2YXIgZXF1YWwgPSB0cnVlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIGlmICghdGhpcy5yb3dzW2ldLmVxdWFscyhvdGhlci5yb3dzW2ldKSkge1xuICAgICAgICBlcXVhbCA9IGZhbHNlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZXF1YWw7XG4gIH07XG5cbiAgX3Byb3RvNS5jb3B5ID0gZnVuY3Rpb24gY29weShvdGhlcikge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICB0aGlzLnJvd3NbaV0uY29weShvdGhlci5yb3dzW2ldKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvNS5pc0VtcHR5ID0gZnVuY3Rpb24gaXNFbXB0eSgpIHtcbiAgICB2YXIgZW1wdHkgPSB0cnVlO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIGlmICghdGhpcy5yb3dzW2ldLmlzRW1wdHkoKSkge1xuICAgICAgICBlbXB0eSA9IGZhbHNlO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZW1wdHk7XG4gIH07XG5cbiAgX3Byb3RvNS5iYWNrU3BhY2UgPSBmdW5jdGlvbiBiYWNrU3BhY2UoKSB7XG4gICAgdmFyIHJvdyA9IHRoaXMucm93c1t0aGlzLmN1cnJSb3ddO1xuICAgIHJvdy5iYWNrU3BhY2UoKTtcbiAgfTtcblxuICBfcHJvdG81LmNsZWFyVG9FbmRPZlJvdyA9IGZ1bmN0aW9uIGNsZWFyVG9FbmRPZlJvdygpIHtcbiAgICB2YXIgcm93ID0gdGhpcy5yb3dzW3RoaXMuY3VyclJvd107XG4gICAgcm93LmNsZWFyVG9FbmRPZlJvdygpO1xuICB9XG4gIC8qKlxuICAgKiBJbnNlcnQgYSBjaGFyYWN0ZXIgKHdpdGhvdXQgc3R5bGluZykgaW4gdGhlIGN1cnJlbnQgcm93LlxuICAgKi9cbiAgO1xuXG4gIF9wcm90bzUuaW5zZXJ0Q2hhciA9IGZ1bmN0aW9uIGluc2VydENoYXIoX2NoYXIzKSB7XG4gICAgdmFyIHJvdyA9IHRoaXMucm93c1t0aGlzLmN1cnJSb3ddO1xuICAgIHJvdy5pbnNlcnRDaGFyKF9jaGFyMyk7XG4gIH07XG5cbiAgX3Byb3RvNS5zZXRQZW4gPSBmdW5jdGlvbiBzZXRQZW4oc3R5bGVzKSB7XG4gICAgdmFyIHJvdyA9IHRoaXMucm93c1t0aGlzLmN1cnJSb3ddO1xuICAgIHJvdy5zZXRQZW5TdHlsZXMoc3R5bGVzKTtcbiAgfTtcblxuICBfcHJvdG81Lm1vdmVDdXJzb3IgPSBmdW5jdGlvbiBtb3ZlQ3Vyc29yKHJlbFBvcykge1xuICAgIHZhciByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcbiAgICByb3cubW92ZUN1cnNvcihyZWxQb3MpO1xuICB9O1xuXG4gIF9wcm90bzUuc2V0Q3Vyc29yID0gZnVuY3Rpb24gc2V0Q3Vyc29yKGFic1Bvcykge1xuICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuSU5GTywgJ3NldEN1cnNvcjogJyArIGFic1Bvcyk7XG4gICAgdmFyIHJvdyA9IHRoaXMucm93c1t0aGlzLmN1cnJSb3ddO1xuICAgIHJvdy5zZXRDdXJzb3IoYWJzUG9zKTtcbiAgfTtcblxuICBfcHJvdG81LnNldFBBQyA9IGZ1bmN0aW9uIHNldFBBQyhwYWNEYXRhKSB7XG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAncGFjRGF0YSA9ICcgKyBKU09OLnN0cmluZ2lmeShwYWNEYXRhKSk7XG4gICAgdmFyIG5ld1JvdyA9IHBhY0RhdGEucm93IC0gMTtcblxuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyAmJiBuZXdSb3cgPCB0aGlzLm5yUm9sbFVwUm93cyAtIDEpIHtcbiAgICAgIG5ld1JvdyA9IHRoaXMubnJSb2xsVXBSb3dzIC0gMTtcbiAgICB9IC8vIE1ha2Ugc3VyZSB0aGlzIG9ubHkgYWZmZWN0cyBSb2xsLXVwIENhcHRpb25zIGJ5IGNoZWNraW5nIHRoaXMubnJSb2xsVXBSb3dzXG5cblxuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyAmJiB0aGlzLmN1cnJSb3cgIT09IG5ld1Jvdykge1xuICAgICAgLy8gY2xlYXIgYWxsIHJvd3MgZmlyc3RcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICAgIHRoaXMucm93c1tpXS5jbGVhcigpO1xuICAgICAgfSAvLyBDb3B5IHRoaXMubnJSb2xsVXBSb3dzIHJvd3MgZnJvbSBsYXN0T3V0cHV0U2NyZWVuIGFuZCBwbGFjZSBpdCBpbiB0aGUgbmV3Um93IGxvY2F0aW9uXG4gICAgICAvLyB0b3BSb3dJbmRleCAtIHRoZSBzdGFydCBvZiByb3dzIHRvIGNvcHkgKGluY2x1c2l2ZSBpbmRleClcblxuXG4gICAgICB2YXIgdG9wUm93SW5kZXggPSB0aGlzLmN1cnJSb3cgKyAxIC0gdGhpcy5uclJvbGxVcFJvd3M7IC8vIFdlIG9ubHkgY29weSBpZiB0aGUgbGFzdCBwb3NpdGlvbiB3YXMgYWxyZWFkeSBzaG93bi5cbiAgICAgIC8vIFdlIHVzZSB0aGUgY3VlU3RhcnRUaW1lIHZhbHVlIHRvIGNoZWNrIHRoaXMuXG5cbiAgICAgIHZhciBsYXN0T3V0cHV0U2NyZWVuID0gdGhpcy5sYXN0T3V0cHV0U2NyZWVuO1xuXG4gICAgICBpZiAobGFzdE91dHB1dFNjcmVlbikge1xuICAgICAgICB2YXIgcHJldkxpbmVUaW1lID0gbGFzdE91dHB1dFNjcmVlbi5yb3dzW3RvcFJvd0luZGV4XS5jdWVTdGFydFRpbWU7XG4gICAgICAgIHZhciB0aW1lID0gdGhpcy5sb2dnZXIudGltZTtcblxuICAgICAgICBpZiAocHJldkxpbmVUaW1lICYmIHRpbWUgIT09IG51bGwgJiYgcHJldkxpbmVUaW1lIDwgdGltZSkge1xuICAgICAgICAgIGZvciAodmFyIF9pID0gMDsgX2kgPCB0aGlzLm5yUm9sbFVwUm93czsgX2krKykge1xuICAgICAgICAgICAgdGhpcy5yb3dzW25ld1JvdyAtIHRoaXMubnJSb2xsVXBSb3dzICsgX2kgKyAxXS5jb3B5KGxhc3RPdXRwdXRTY3JlZW4ucm93c1t0b3BSb3dJbmRleCArIF9pXSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5jdXJyUm93ID0gbmV3Um93O1xuICAgIHZhciByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcblxuICAgIGlmIChwYWNEYXRhLmluZGVudCAhPT0gbnVsbCkge1xuICAgICAgdmFyIGluZGVudCA9IHBhY0RhdGEuaW5kZW50O1xuICAgICAgdmFyIHByZXZQb3MgPSBNYXRoLm1heChpbmRlbnQgLSAxLCAwKTtcbiAgICAgIHJvdy5zZXRDdXJzb3IocGFjRGF0YS5pbmRlbnQpO1xuICAgICAgcGFjRGF0YS5jb2xvciA9IHJvdy5jaGFyc1twcmV2UG9zXS5wZW5TdGF0ZS5mb3JlZ3JvdW5kO1xuICAgIH1cblxuICAgIHZhciBzdHlsZXMgPSB7XG4gICAgICBmb3JlZ3JvdW5kOiBwYWNEYXRhLmNvbG9yLFxuICAgICAgdW5kZXJsaW5lOiBwYWNEYXRhLnVuZGVybGluZSxcbiAgICAgIGl0YWxpY3M6IHBhY0RhdGEuaXRhbGljcyxcbiAgICAgIGJhY2tncm91bmQ6ICdibGFjaycsXG4gICAgICBmbGFzaDogZmFsc2VcbiAgICB9O1xuICAgIHRoaXMuc2V0UGVuKHN0eWxlcyk7XG4gIH1cbiAgLyoqXG4gICAqIFNldCBiYWNrZ3JvdW5kL2V4dHJhIGZvcmVncm91bmQsIGJ1dCBmaXJzdCBkbyBiYWNrX3NwYWNlLCBhbmQgdGhlbiBpbnNlcnQgc3BhY2UgKGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5KS5cbiAgICovXG4gIDtcblxuICBfcHJvdG81LnNldEJrZ0RhdGEgPSBmdW5jdGlvbiBzZXRCa2dEYXRhKGJrZ0RhdGEpIHtcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdia2dEYXRhID0gJyArIEpTT04uc3RyaW5naWZ5KGJrZ0RhdGEpKTtcbiAgICB0aGlzLmJhY2tTcGFjZSgpO1xuICAgIHRoaXMuc2V0UGVuKGJrZ0RhdGEpO1xuICAgIHRoaXMuaW5zZXJ0Q2hhcigweDIwKTsgLy8gU3BhY2VcbiAgfTtcblxuICBfcHJvdG81LnNldFJvbGxVcFJvd3MgPSBmdW5jdGlvbiBzZXRSb2xsVXBSb3dzKG5yUm93cykge1xuICAgIHRoaXMubnJSb2xsVXBSb3dzID0gbnJSb3dzO1xuICB9O1xuXG4gIF9wcm90bzUucm9sbFVwID0gZnVuY3Rpb24gcm9sbFVwKCkge1xuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5ERUJVRywgJ3JvbGxfdXAgYnV0IG5yUm9sbFVwUm93cyBub3Qgc2V0IHlldCcpO1xuICAgICAgcmV0dXJuOyAvLyBOb3QgcHJvcGVybHkgc2V0dXBcbiAgICB9XG5cbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLlRFWFQsIHRoaXMuZ2V0RGlzcGxheVRleHQoKSk7XG4gICAgdmFyIHRvcFJvd0luZGV4ID0gdGhpcy5jdXJyUm93ICsgMSAtIHRoaXMubnJSb2xsVXBSb3dzO1xuICAgIHZhciB0b3BSb3cgPSB0aGlzLnJvd3Muc3BsaWNlKHRvcFJvd0luZGV4LCAxKVswXTtcbiAgICB0b3BSb3cuY2xlYXIoKTtcbiAgICB0aGlzLnJvd3Muc3BsaWNlKHRoaXMuY3VyclJvdywgMCwgdG9wUm93KTtcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdSb2xsaW5nIHVwJyk7IC8vIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuVEVYVCwgdGhpcy5nZXRfZGlzcGxheV90ZXh0KCkpXG4gIH1cbiAgLyoqXG4gICAqIEdldCBhbGwgbm9uLWVtcHR5IHJvd3Mgd2l0aCBhcyB1bmljb2RlIHRleHQuXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvNS5nZXREaXNwbGF5VGV4dCA9IGZ1bmN0aW9uIGdldERpc3BsYXlUZXh0KGFzT25lUm93KSB7XG4gICAgYXNPbmVSb3cgPSBhc09uZVJvdyB8fCBmYWxzZTtcbiAgICB2YXIgZGlzcGxheVRleHQgPSBbXTtcbiAgICB2YXIgdGV4dCA9ICcnO1xuICAgIHZhciByb3dOciA9IC0xO1xuXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIHZhciByb3dUZXh0ID0gdGhpcy5yb3dzW2ldLmdldFRleHRTdHJpbmcoKTtcblxuICAgICAgaWYgKHJvd1RleHQpIHtcbiAgICAgICAgcm93TnIgPSBpICsgMTtcblxuICAgICAgICBpZiAoYXNPbmVSb3cpIHtcbiAgICAgICAgICBkaXNwbGF5VGV4dC5wdXNoKCdSb3cgJyArIHJvd05yICsgJzogXFwnJyArIHJvd1RleHQgKyAnXFwnJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZGlzcGxheVRleHQucHVzaChyb3dUZXh0LnRyaW0oKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZGlzcGxheVRleHQubGVuZ3RoID4gMCkge1xuICAgICAgaWYgKGFzT25lUm93KSB7XG4gICAgICAgIHRleHQgPSAnWycgKyBkaXNwbGF5VGV4dC5qb2luKCcgfCAnKSArICddJztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRleHQgPSBkaXNwbGF5VGV4dC5qb2luKCdcXG4nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdGV4dDtcbiAgfTtcblxuICBfcHJvdG81LmdldFRleHRBbmRGb3JtYXQgPSBmdW5jdGlvbiBnZXRUZXh0QW5kRm9ybWF0KCkge1xuICAgIHJldHVybiB0aGlzLnJvd3M7XG4gIH07XG5cbiAgcmV0dXJuIENhcHRpb25TY3JlZW47XG59KCk7IC8vIHZhciBtb2RlcyA9IFsnTU9ERV9ST0xMLVVQJywgJ01PREVfUE9QLU9OJywgJ01PREVfUEFJTlQtT04nLCAnTU9ERV9URVhUJ107XG5cbnZhciBDZWE2MDhDaGFubmVsID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gQ2VhNjA4Q2hhbm5lbChjaGFubmVsTnVtYmVyLCBvdXRwdXRGaWx0ZXIsIGxvZ2dlcikge1xuICAgIHRoaXMuY2hOciA9IHZvaWQgMDtcbiAgICB0aGlzLm91dHB1dEZpbHRlciA9IHZvaWQgMDtcbiAgICB0aGlzLm1vZGUgPSB2b2lkIDA7XG4gICAgdGhpcy52ZXJib3NlID0gdm9pZCAwO1xuICAgIHRoaXMuZGlzcGxheWVkTWVtb3J5ID0gdm9pZCAwO1xuICAgIHRoaXMubm9uRGlzcGxheWVkTWVtb3J5ID0gdm9pZCAwO1xuICAgIHRoaXMubGFzdE91dHB1dFNjcmVlbiA9IHZvaWQgMDtcbiAgICB0aGlzLmN1cnJSb2xsVXBSb3cgPSB2b2lkIDA7XG4gICAgdGhpcy53cml0ZVNjcmVlbiA9IHZvaWQgMDtcbiAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IHZvaWQgMDtcbiAgICB0aGlzLmxvZ2dlciA9IHZvaWQgMDtcbiAgICB0aGlzLmNoTnIgPSBjaGFubmVsTnVtYmVyO1xuICAgIHRoaXMub3V0cHV0RmlsdGVyID0gb3V0cHV0RmlsdGVyO1xuICAgIHRoaXMubW9kZSA9IG51bGw7XG4gICAgdGhpcy52ZXJib3NlID0gMDtcbiAgICB0aGlzLmRpc3BsYXllZE1lbW9yeSA9IG5ldyBDYXB0aW9uU2NyZWVuKGxvZ2dlcik7XG4gICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkgPSBuZXcgQ2FwdGlvblNjcmVlbihsb2dnZXIpO1xuICAgIHRoaXMubGFzdE91dHB1dFNjcmVlbiA9IG5ldyBDYXB0aW9uU2NyZWVuKGxvZ2dlcik7XG4gICAgdGhpcy5jdXJyUm9sbFVwUm93ID0gdGhpcy5kaXNwbGF5ZWRNZW1vcnkucm93c1tOUl9ST1dTIC0gMV07XG4gICAgdGhpcy53cml0ZVNjcmVlbiA9IHRoaXMuZGlzcGxheWVkTWVtb3J5O1xuICAgIHRoaXMubW9kZSA9IG51bGw7XG4gICAgdGhpcy5jdWVTdGFydFRpbWUgPSBudWxsOyAvLyBLZWVwcyB0cmFjayBvZiB3aGVyZSBhIGN1ZSBzdGFydGVkLlxuXG4gICAgdGhpcy5sb2dnZXIgPSBsb2dnZXI7XG4gIH1cblxuICB2YXIgX3Byb3RvNiA9IENlYTYwOENoYW5uZWwucHJvdG90eXBlO1xuXG4gIF9wcm90bzYucmVzZXQgPSBmdW5jdGlvbiByZXNldCgpIHtcbiAgICB0aGlzLm1vZGUgPSBudWxsO1xuICAgIHRoaXMuZGlzcGxheWVkTWVtb3J5LnJlc2V0KCk7XG4gICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkucmVzZXQoKTtcbiAgICB0aGlzLmxhc3RPdXRwdXRTY3JlZW4ucmVzZXQoKTtcbiAgICB0aGlzLm91dHB1dEZpbHRlci5yZXNldCgpO1xuICAgIHRoaXMuY3VyclJvbGxVcFJvdyA9IHRoaXMuZGlzcGxheWVkTWVtb3J5LnJvd3NbTlJfUk9XUyAtIDFdO1xuICAgIHRoaXMud3JpdGVTY3JlZW4gPSB0aGlzLmRpc3BsYXllZE1lbW9yeTtcbiAgICB0aGlzLm1vZGUgPSBudWxsO1xuICAgIHRoaXMuY3VlU3RhcnRUaW1lID0gbnVsbDtcbiAgfTtcblxuICBfcHJvdG82LmdldEhhbmRsZXIgPSBmdW5jdGlvbiBnZXRIYW5kbGVyKCkge1xuICAgIHJldHVybiB0aGlzLm91dHB1dEZpbHRlcjtcbiAgfTtcblxuICBfcHJvdG82LnNldEhhbmRsZXIgPSBmdW5jdGlvbiBzZXRIYW5kbGVyKG5ld0hhbmRsZXIpIHtcbiAgICB0aGlzLm91dHB1dEZpbHRlciA9IG5ld0hhbmRsZXI7XG4gIH07XG5cbiAgX3Byb3RvNi5zZXRQQUMgPSBmdW5jdGlvbiBzZXRQQUMocGFjRGF0YSkge1xuICAgIHRoaXMud3JpdGVTY3JlZW4uc2V0UEFDKHBhY0RhdGEpO1xuICB9O1xuXG4gIF9wcm90bzYuc2V0QmtnRGF0YSA9IGZ1bmN0aW9uIHNldEJrZ0RhdGEoYmtnRGF0YSkge1xuICAgIHRoaXMud3JpdGVTY3JlZW4uc2V0QmtnRGF0YShia2dEYXRhKTtcbiAgfTtcblxuICBfcHJvdG82LnNldE1vZGUgPSBmdW5jdGlvbiBzZXRNb2RlKG5ld01vZGUpIHtcbiAgICBpZiAobmV3TW9kZSA9PT0gdGhpcy5tb2RlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5tb2RlID0gbmV3TW9kZTtcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdNT0RFPScgKyBuZXdNb2RlKTtcblxuICAgIGlmICh0aGlzLm1vZGUgPT09ICdNT0RFX1BPUC1PTicpIHtcbiAgICAgIHRoaXMud3JpdGVTY3JlZW4gPSB0aGlzLm5vbkRpc3BsYXllZE1lbW9yeTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy53cml0ZVNjcmVlbiA9IHRoaXMuZGlzcGxheWVkTWVtb3J5O1xuICAgICAgdGhpcy53cml0ZVNjcmVlbi5yZXNldCgpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1vZGUgIT09ICdNT0RFX1JPTEwtVVAnKSB7XG4gICAgICB0aGlzLmRpc3BsYXllZE1lbW9yeS5uclJvbGxVcFJvd3MgPSBudWxsO1xuICAgICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkubnJSb2xsVXBSb3dzID0gbnVsbDtcbiAgICB9XG5cbiAgICB0aGlzLm1vZGUgPSBuZXdNb2RlO1xuICB9O1xuXG4gIF9wcm90bzYuaW5zZXJ0Q2hhcnMgPSBmdW5jdGlvbiBpbnNlcnRDaGFycyhjaGFycykge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgY2hhcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIHRoaXMud3JpdGVTY3JlZW4uaW5zZXJ0Q2hhcihjaGFyc1tpXSk7XG4gICAgfVxuXG4gICAgdmFyIHNjcmVlbiA9IHRoaXMud3JpdGVTY3JlZW4gPT09IHRoaXMuZGlzcGxheWVkTWVtb3J5ID8gJ0RJU1AnIDogJ05PTl9ESVNQJztcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sIHNjcmVlbiArICc6ICcgKyB0aGlzLndyaXRlU2NyZWVuLmdldERpc3BsYXlUZXh0KHRydWUpKTtcblxuICAgIGlmICh0aGlzLm1vZGUgPT09ICdNT0RFX1BBSU5ULU9OJyB8fCB0aGlzLm1vZGUgPT09ICdNT0RFX1JPTEwtVVAnKSB7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLlRFWFQsICdESVNQTEFZRUQ6ICcgKyB0aGlzLmRpc3BsYXllZE1lbW9yeS5nZXREaXNwbGF5VGV4dCh0cnVlKSk7XG4gICAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUoKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvNi5jY1JDTCA9IGZ1bmN0aW9uIGNjUkNMKCkge1xuICAgIC8vIFJlc3VtZSBDYXB0aW9uIExvYWRpbmcgKHN3aXRjaCBtb2RlIHRvIFBvcCBPbilcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdSQ0wgLSBSZXN1bWUgQ2FwdGlvbiBMb2FkaW5nJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1BPUC1PTicpO1xuICB9O1xuXG4gIF9wcm90bzYuY2NCUyA9IGZ1bmN0aW9uIGNjQlMoKSB7XG4gICAgLy8gQmFja1NwYWNlXG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnQlMgLSBCYWNrU3BhY2UnKTtcblxuICAgIGlmICh0aGlzLm1vZGUgPT09ICdNT0RFX1RFWFQnKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy53cml0ZVNjcmVlbi5iYWNrU3BhY2UoKTtcblxuICAgIGlmICh0aGlzLndyaXRlU2NyZWVuID09PSB0aGlzLmRpc3BsYXllZE1lbW9yeSkge1xuICAgICAgdGhpcy5vdXRwdXREYXRhVXBkYXRlKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90bzYuY2NBT0YgPSBmdW5jdGlvbiBjY0FPRigpIHsvLyBSZXNlcnZlZCAoZm9ybWVybHkgQWxhcm0gT2ZmKVxuICB9O1xuXG4gIF9wcm90bzYuY2NBT04gPSBmdW5jdGlvbiBjY0FPTigpIHsvLyBSZXNlcnZlZCAoZm9ybWVybHkgQWxhcm0gT24pXG4gIH07XG5cbiAgX3Byb3RvNi5jY0RFUiA9IGZ1bmN0aW9uIGNjREVSKCkge1xuICAgIC8vIERlbGV0ZSB0byBFbmQgb2YgUm93XG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnREVSLSBEZWxldGUgdG8gRW5kIG9mIFJvdycpO1xuICAgIHRoaXMud3JpdGVTY3JlZW4uY2xlYXJUb0VuZE9mUm93KCk7XG4gICAgdGhpcy5vdXRwdXREYXRhVXBkYXRlKCk7XG4gIH07XG5cbiAgX3Byb3RvNi5jY1JVID0gZnVuY3Rpb24gY2NSVShuclJvd3MpIHtcbiAgICAvLyBSb2xsLVVwIENhcHRpb25zLTIsMyxvciA0IFJvd3NcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdSVSgnICsgbnJSb3dzICsgJykgLSBSb2xsIFVwJyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbiA9IHRoaXMuZGlzcGxheWVkTWVtb3J5O1xuICAgIHRoaXMuc2V0TW9kZSgnTU9ERV9ST0xMLVVQJyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5zZXRSb2xsVXBSb3dzKG5yUm93cyk7XG4gIH07XG5cbiAgX3Byb3RvNi5jY0ZPTiA9IGZ1bmN0aW9uIGNjRk9OKCkge1xuICAgIC8vIEZsYXNoIE9uXG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnRk9OIC0gRmxhc2ggT24nKTtcbiAgICB0aGlzLndyaXRlU2NyZWVuLnNldFBlbih7XG4gICAgICBmbGFzaDogdHJ1ZVxuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90bzYuY2NSREMgPSBmdW5jdGlvbiBjY1JEQygpIHtcbiAgICAvLyBSZXN1bWUgRGlyZWN0IENhcHRpb25pbmcgKHN3aXRjaCBtb2RlIHRvIFBhaW50T24pXG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnUkRDIC0gUmVzdW1lIERpcmVjdCBDYXB0aW9uaW5nJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1BBSU5ULU9OJyk7XG4gIH07XG5cbiAgX3Byb3RvNi5jY1RSID0gZnVuY3Rpb24gY2NUUigpIHtcbiAgICAvLyBUZXh0IFJlc3RhcnQgaW4gdGV4dCBtb2RlIChub3Qgc3VwcG9ydGVkLCBob3dldmVyKVxuICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuSU5GTywgJ1RSJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1RFWFQnKTtcbiAgfTtcblxuICBfcHJvdG82LmNjUlREID0gZnVuY3Rpb24gY2NSVEQoKSB7XG4gICAgLy8gUmVzdW1lIFRleHQgRGlzcGxheSBpbiBUZXh0IG1vZGUgKG5vdCBzdXBwb3J0ZWQsIGhvd2V2ZXIpXG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnUlREJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1RFWFQnKTtcbiAgfTtcblxuICBfcHJvdG82LmNjRURNID0gZnVuY3Rpb24gY2NFRE0oKSB7XG4gICAgLy8gRXJhc2UgRGlzcGxheWVkIE1lbW9yeVxuICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuSU5GTywgJ0VETSAtIEVyYXNlIERpc3BsYXllZCBNZW1vcnknKTtcbiAgICB0aGlzLmRpc3BsYXllZE1lbW9yeS5yZXNldCgpO1xuICAgIHRoaXMub3V0cHV0RGF0YVVwZGF0ZSh0cnVlKTtcbiAgfTtcblxuICBfcHJvdG82LmNjQ1IgPSBmdW5jdGlvbiBjY0NSKCkge1xuICAgIC8vIENhcnJpYWdlIFJldHVyblxuICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuSU5GTywgJ0NSIC0gQ2FycmlhZ2UgUmV0dXJuJyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5yb2xsVXAoKTtcbiAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUodHJ1ZSk7XG4gIH07XG5cbiAgX3Byb3RvNi5jY0VOTSA9IGZ1bmN0aW9uIGNjRU5NKCkge1xuICAgIC8vIEVyYXNlIE5vbi1EaXNwbGF5ZWQgTWVtb3J5XG4gICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnRU5NIC0gRXJhc2UgTm9uLWRpc3BsYXllZCBNZW1vcnknKTtcbiAgICB0aGlzLm5vbkRpc3BsYXllZE1lbW9yeS5yZXNldCgpO1xuICB9O1xuXG4gIF9wcm90bzYuY2NFT0MgPSBmdW5jdGlvbiBjY0VPQygpIHtcbiAgICAvLyBFbmQgb2YgQ2FwdGlvbiAoRmxpcCBNZW1vcmllcylcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdFT0MgLSBFbmQgT2YgQ2FwdGlvbicpO1xuXG4gICAgaWYgKHRoaXMubW9kZSA9PT0gJ01PREVfUE9QLU9OJykge1xuICAgICAgdmFyIHRtcCA9IHRoaXMuZGlzcGxheWVkTWVtb3J5O1xuICAgICAgdGhpcy5kaXNwbGF5ZWRNZW1vcnkgPSB0aGlzLm5vbkRpc3BsYXllZE1lbW9yeTtcbiAgICAgIHRoaXMubm9uRGlzcGxheWVkTWVtb3J5ID0gdG1wO1xuICAgICAgdGhpcy53cml0ZVNjcmVlbiA9IHRoaXMubm9uRGlzcGxheWVkTWVtb3J5O1xuICAgICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5URVhULCAnRElTUDogJyArIHRoaXMuZGlzcGxheWVkTWVtb3J5LmdldERpc3BsYXlUZXh0KCkpO1xuICAgIH1cblxuICAgIHRoaXMub3V0cHV0RGF0YVVwZGF0ZSh0cnVlKTtcbiAgfTtcblxuICBfcHJvdG82LmNjVE8gPSBmdW5jdGlvbiBjY1RPKG5yQ29scykge1xuICAgIC8vIFRhYiBPZmZzZXQgMSwyLCBvciAzIGNvbHVtbnNcbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdUTygnICsgbnJDb2xzICsgJykgLSBUYWIgT2Zmc2V0Jyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5tb3ZlQ3Vyc29yKG5yQ29scyk7XG4gIH07XG5cbiAgX3Byb3RvNi5jY01JRFJPVyA9IGZ1bmN0aW9uIGNjTUlEUk9XKHNlY29uZEJ5dGUpIHtcbiAgICAvLyBQYXJzZSBNSURST1cgY29tbWFuZFxuICAgIHZhciBzdHlsZXMgPSB7XG4gICAgICBmbGFzaDogZmFsc2VcbiAgICB9O1xuICAgIHN0eWxlcy51bmRlcmxpbmUgPSBzZWNvbmRCeXRlICUgMiA9PT0gMTtcbiAgICBzdHlsZXMuaXRhbGljcyA9IHNlY29uZEJ5dGUgPj0gMHgyZTtcblxuICAgIGlmICghc3R5bGVzLml0YWxpY3MpIHtcbiAgICAgIHZhciBjb2xvckluZGV4ID0gTWF0aC5mbG9vcihzZWNvbmRCeXRlIC8gMikgLSAweDEwO1xuICAgICAgdmFyIGNvbG9ycyA9IFsnd2hpdGUnLCAnZ3JlZW4nLCAnYmx1ZScsICdjeWFuJywgJ3JlZCcsICd5ZWxsb3cnLCAnbWFnZW50YSddO1xuICAgICAgc3R5bGVzLmZvcmVncm91bmQgPSBjb2xvcnNbY29sb3JJbmRleF07XG4gICAgfSBlbHNlIHtcbiAgICAgIHN0eWxlcy5mb3JlZ3JvdW5kID0gJ3doaXRlJztcbiAgICB9XG5cbiAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLklORk8sICdNSURST1c6ICcgKyBKU09OLnN0cmluZ2lmeShzdHlsZXMpKTtcbiAgICB0aGlzLndyaXRlU2NyZWVuLnNldFBlbihzdHlsZXMpO1xuICB9O1xuXG4gIF9wcm90bzYub3V0cHV0RGF0YVVwZGF0ZSA9IGZ1bmN0aW9uIG91dHB1dERhdGFVcGRhdGUoZGlzcGF0Y2gpIHtcbiAgICBpZiAoZGlzcGF0Y2ggPT09IHZvaWQgMCkge1xuICAgICAgZGlzcGF0Y2ggPSBmYWxzZTtcbiAgICB9XG5cbiAgICB2YXIgdGltZSA9IHRoaXMubG9nZ2VyLnRpbWU7XG5cbiAgICBpZiAodGltZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm91dHB1dEZpbHRlcikge1xuICAgICAgaWYgKHRoaXMuY3VlU3RhcnRUaW1lID09PSBudWxsICYmICF0aGlzLmRpc3BsYXllZE1lbW9yeS5pc0VtcHR5KCkpIHtcbiAgICAgICAgLy8gU3RhcnQgb2YgYSBuZXcgY3VlXG4gICAgICAgIHRoaXMuY3VlU3RhcnRUaW1lID0gdGltZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICghdGhpcy5kaXNwbGF5ZWRNZW1vcnkuZXF1YWxzKHRoaXMubGFzdE91dHB1dFNjcmVlbikpIHtcbiAgICAgICAgICB0aGlzLm91dHB1dEZpbHRlci5uZXdDdWUodGhpcy5jdWVTdGFydFRpbWUsIHRpbWUsIHRoaXMubGFzdE91dHB1dFNjcmVlbik7XG5cbiAgICAgICAgICBpZiAoZGlzcGF0Y2ggJiYgdGhpcy5vdXRwdXRGaWx0ZXIuZGlzcGF0Y2hDdWUpIHtcbiAgICAgICAgICAgIHRoaXMub3V0cHV0RmlsdGVyLmRpc3BhdGNoQ3VlKCk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5jdWVTdGFydFRpbWUgPSB0aGlzLmRpc3BsYXllZE1lbW9yeS5pc0VtcHR5KCkgPyBudWxsIDogdGltZTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0aGlzLmxhc3RPdXRwdXRTY3JlZW4uY29weSh0aGlzLmRpc3BsYXllZE1lbW9yeSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90bzYuY3VlU3BsaXRBdFRpbWUgPSBmdW5jdGlvbiBjdWVTcGxpdEF0VGltZSh0KSB7XG4gICAgaWYgKHRoaXMub3V0cHV0RmlsdGVyKSB7XG4gICAgICBpZiAoIXRoaXMuZGlzcGxheWVkTWVtb3J5LmlzRW1wdHkoKSkge1xuICAgICAgICBpZiAodGhpcy5vdXRwdXRGaWx0ZXIubmV3Q3VlKSB7XG4gICAgICAgICAgdGhpcy5vdXRwdXRGaWx0ZXIubmV3Q3VlKHRoaXMuY3VlU3RhcnRUaW1lLCB0LCB0aGlzLmRpc3BsYXllZE1lbW9yeSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IHQ7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIHJldHVybiBDZWE2MDhDaGFubmVsO1xufSgpO1xuXG52YXIgQ2VhNjA4UGFyc2VyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gQ2VhNjA4UGFyc2VyKGZpZWxkLCBvdXQxLCBvdXQyKSB7XG4gICAgdGhpcy5jaGFubmVscyA9IHZvaWQgMDtcbiAgICB0aGlzLmN1cnJlbnRDaGFubmVsID0gMDtcbiAgICB0aGlzLmNtZEhpc3RvcnkgPSB2b2lkIDA7XG4gICAgdGhpcy5sb2dnZXIgPSB2b2lkIDA7XG4gICAgdmFyIGxvZ2dlciA9IG5ldyBjZWFfNjA4X3BhcnNlcl9DYXB0aW9uc0xvZ2dlcigpO1xuICAgIHRoaXMuY2hhbm5lbHMgPSBbbnVsbCwgbmV3IENlYTYwOENoYW5uZWwoZmllbGQsIG91dDEsIGxvZ2dlciksIG5ldyBDZWE2MDhDaGFubmVsKGZpZWxkICsgMSwgb3V0MiwgbG9nZ2VyKV07XG4gICAgdGhpcy5jbWRIaXN0b3J5ID0gY3JlYXRlQ21kSGlzdG9yeSgpO1xuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICB9XG5cbiAgdmFyIF9wcm90bzcgPSBDZWE2MDhQYXJzZXIucHJvdG90eXBlO1xuXG4gIF9wcm90bzcuZ2V0SGFuZGxlciA9IGZ1bmN0aW9uIGdldEhhbmRsZXIoY2hhbm5lbCkge1xuICAgIHJldHVybiB0aGlzLmNoYW5uZWxzW2NoYW5uZWxdLmdldEhhbmRsZXIoKTtcbiAgfTtcblxuICBfcHJvdG83LnNldEhhbmRsZXIgPSBmdW5jdGlvbiBzZXRIYW5kbGVyKGNoYW5uZWwsIG5ld0hhbmRsZXIpIHtcbiAgICB0aGlzLmNoYW5uZWxzW2NoYW5uZWxdLnNldEhhbmRsZXIobmV3SGFuZGxlcik7XG4gIH1cbiAgLyoqXG4gICAqIEFkZCBkYXRhIGZvciB0aW1lIHQgaW4gZm9ybXMgb2YgbGlzdCBvZiBieXRlcyAodW5zaWduZWQgaW50cykuIFRoZSBieXRlcyBhcmUgdHJlYXRlZCBhcyBwYWlycy5cbiAgICovXG4gIDtcblxuICBfcHJvdG83LmFkZERhdGEgPSBmdW5jdGlvbiBhZGREYXRhKHRpbWUsIGJ5dGVMaXN0KSB7XG4gICAgdmFyIGNtZEZvdW5kO1xuICAgIHZhciBhO1xuICAgIHZhciBiO1xuICAgIHZhciBjaGFyc0ZvdW5kID0gZmFsc2U7XG4gICAgdGhpcy5sb2dnZXIudGltZSA9IHRpbWU7XG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ5dGVMaXN0Lmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICBhID0gYnl0ZUxpc3RbaV0gJiAweDdmO1xuICAgICAgYiA9IGJ5dGVMaXN0W2kgKyAxXSAmIDB4N2Y7XG5cbiAgICAgIGlmIChhID09PSAwICYmIGIgPT09IDApIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLkRBVEEsICdbJyArIG51bUFycmF5VG9IZXhBcnJheShbYnl0ZUxpc3RbaV0sIGJ5dGVMaXN0W2kgKyAxXV0pICsgJ10gLT4gKCcgKyBudW1BcnJheVRvSGV4QXJyYXkoW2EsIGJdKSArICcpJyk7XG4gICAgICB9XG5cbiAgICAgIGNtZEZvdW5kID0gdGhpcy5wYXJzZUNtZChhLCBiKTtcblxuICAgICAgaWYgKCFjbWRGb3VuZCkge1xuICAgICAgICBjbWRGb3VuZCA9IHRoaXMucGFyc2VNaWRyb3coYSwgYik7XG4gICAgICB9XG5cbiAgICAgIGlmICghY21kRm91bmQpIHtcbiAgICAgICAgY21kRm91bmQgPSB0aGlzLnBhcnNlUEFDKGEsIGIpO1xuICAgICAgfVxuXG4gICAgICBpZiAoIWNtZEZvdW5kKSB7XG4gICAgICAgIGNtZEZvdW5kID0gdGhpcy5wYXJzZUJhY2tncm91bmRBdHRyaWJ1dGVzKGEsIGIpO1xuICAgICAgfVxuXG4gICAgICBpZiAoIWNtZEZvdW5kKSB7XG4gICAgICAgIGNoYXJzRm91bmQgPSB0aGlzLnBhcnNlQ2hhcnMoYSwgYik7XG5cbiAgICAgICAgaWYgKGNoYXJzRm91bmQpIHtcbiAgICAgICAgICB2YXIgY3VyckNoTnIgPSB0aGlzLmN1cnJlbnRDaGFubmVsO1xuXG4gICAgICAgICAgaWYgKGN1cnJDaE5yICYmIGN1cnJDaE5yID4gMCkge1xuICAgICAgICAgICAgdmFyIGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzW2N1cnJDaE5yXTtcbiAgICAgICAgICAgIGNoYW5uZWwuaW5zZXJ0Q2hhcnMoY2hhcnNGb3VuZCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuV0FSTklORywgJ05vIGNoYW5uZWwgZm91bmQgeWV0LiBURVhULU1PREU/Jyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICghY21kRm91bmQgJiYgIWNoYXJzRm91bmQpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5XQVJOSU5HLCAnQ291bGRuXFwndCBwYXJzZSBjbGVhbmVkIGRhdGEgJyArIG51bUFycmF5VG9IZXhBcnJheShbYSwgYl0pICsgJyBvcmlnOiAnICsgbnVtQXJyYXlUb0hleEFycmF5KFtieXRlTGlzdFtpXSwgYnl0ZUxpc3RbaSArIDFdXSkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICAvKipcbiAgICogUGFyc2UgQ29tbWFuZC5cbiAgICogQHJldHVybnMge0Jvb2xlYW59IFRlbGxzIGlmIGEgY29tbWFuZCB3YXMgZm91bmRcbiAgICovXG4gIDtcblxuICBfcHJvdG83LnBhcnNlQ21kID0gZnVuY3Rpb24gcGFyc2VDbWQoYSwgYikge1xuICAgIHZhciBjbWRIaXN0b3J5ID0gdGhpcy5jbWRIaXN0b3J5O1xuICAgIHZhciBjb25kMSA9IChhID09PSAweDE0IHx8IGEgPT09IDB4MUMgfHwgYSA9PT0gMHgxNSB8fCBhID09PSAweDFEKSAmJiBiID49IDB4MjAgJiYgYiA8PSAweDJGO1xuICAgIHZhciBjb25kMiA9IChhID09PSAweDE3IHx8IGEgPT09IDB4MUYpICYmIGIgPj0gMHgyMSAmJiBiIDw9IDB4MjM7XG5cbiAgICBpZiAoIShjb25kMSB8fCBjb25kMikpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBpZiAoaGFzQ21kUmVwZWF0ZWQoYSwgYiwgY21kSGlzdG9yeSkpIHtcbiAgICAgIHNldExhc3RDbWQobnVsbCwgbnVsbCwgY21kSGlzdG9yeSk7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLkRFQlVHLCAnUmVwZWF0ZWQgY29tbWFuZCAoJyArIG51bUFycmF5VG9IZXhBcnJheShbYSwgYl0pICsgJykgaXMgZHJvcHBlZCcpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgdmFyIGNoTnIgPSBhID09PSAweDE0IHx8IGEgPT09IDB4MTUgfHwgYSA9PT0gMHgxNyA/IDEgOiAyO1xuICAgIHZhciBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tjaE5yXTtcblxuICAgIGlmIChhID09PSAweDE0IHx8IGEgPT09IDB4MTUgfHwgYSA9PT0gMHgxQyB8fCBhID09PSAweDFEKSB7XG4gICAgICBpZiAoYiA9PT0gMHgyMCkge1xuICAgICAgICBjaGFubmVsLmNjUkNMKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjEpIHtcbiAgICAgICAgY2hhbm5lbC5jY0JTKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjIpIHtcbiAgICAgICAgY2hhbm5lbC5jY0FPRigpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDIzKSB7XG4gICAgICAgIGNoYW5uZWwuY2NBT04oKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyNCkge1xuICAgICAgICBjaGFubmVsLmNjREVSKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjUpIHtcbiAgICAgICAgY2hhbm5lbC5jY1JVKDIpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDI2KSB7XG4gICAgICAgIGNoYW5uZWwuY2NSVSgzKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyNykge1xuICAgICAgICBjaGFubmVsLmNjUlUoNCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjgpIHtcbiAgICAgICAgY2hhbm5lbC5jY0ZPTigpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDI5KSB7XG4gICAgICAgIGNoYW5uZWwuY2NSREMoKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyQSkge1xuICAgICAgICBjaGFubmVsLmNjVFIoKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyQikge1xuICAgICAgICBjaGFubmVsLmNjUlREKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MkMpIHtcbiAgICAgICAgY2hhbm5lbC5jY0VETSgpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDJEKSB7XG4gICAgICAgIGNoYW5uZWwuY2NDUigpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDJFKSB7XG4gICAgICAgIGNoYW5uZWwuY2NFTk0oKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyRikge1xuICAgICAgICBjaGFubmVsLmNjRU9DKCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGEgPT0gMHgxNyB8fCBhID09IDB4MUZcbiAgICAgIGNoYW5uZWwuY2NUTyhiIC0gMHgyMCk7XG4gICAgfVxuXG4gICAgc2V0TGFzdENtZChhLCBiLCBjbWRIaXN0b3J5KTtcbiAgICB0aGlzLmN1cnJlbnRDaGFubmVsID0gY2hOcjtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICAvKipcbiAgICogUGFyc2UgbWlkcm93IHN0eWxpbmcgY29tbWFuZFxuICAgKiBAcmV0dXJucyB7Qm9vbGVhbn1cbiAgICovXG4gIDtcblxuICBfcHJvdG83LnBhcnNlTWlkcm93ID0gZnVuY3Rpb24gcGFyc2VNaWRyb3coYSwgYikge1xuICAgIHZhciBjaE5yID0gMDtcblxuICAgIGlmICgoYSA9PT0gMHgxMSB8fCBhID09PSAweDE5KSAmJiBiID49IDB4MjAgJiYgYiA8PSAweDJmKSB7XG4gICAgICBpZiAoYSA9PT0gMHgxMSkge1xuICAgICAgICBjaE5yID0gMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNoTnIgPSAyO1xuICAgICAgfVxuXG4gICAgICBpZiAoY2hOciAhPT0gdGhpcy5jdXJyZW50Q2hhbm5lbCkge1xuICAgICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLkVSUk9SLCAnTWlzbWF0Y2ggY2hhbm5lbCBpbiBtaWRyb3cgcGFyc2luZycpO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIHZhciBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tjaE5yXTtcblxuICAgICAgaWYgKCFjaGFubmVsKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cblxuICAgICAgY2hhbm5lbC5jY01JRFJPVyhiKTtcbiAgICAgIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuREVCVUcsICdNSURST1cgKCcgKyBudW1BcnJheVRvSGV4QXJyYXkoW2EsIGJdKSArICcpJyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgLyoqXG4gICAqIFBhcnNlIFByZWFibGUgQWNjZXNzIENvZGVzIChUYWJsZSA1MykuXG4gICAqIEByZXR1cm5zIHtCb29sZWFufSBUZWxscyBpZiBQQUMgZm91bmRcbiAgICovXG4gIDtcblxuICBfcHJvdG83LnBhcnNlUEFDID0gZnVuY3Rpb24gcGFyc2VQQUMoYSwgYikge1xuICAgIHZhciByb3c7XG4gICAgdmFyIGNtZEhpc3RvcnkgPSB0aGlzLmNtZEhpc3Rvcnk7XG4gICAgdmFyIGNhc2UxID0gKGEgPj0gMHgxMSAmJiBhIDw9IDB4MTcgfHwgYSA+PSAweDE5ICYmIGEgPD0gMHgxRikgJiYgYiA+PSAweDQwICYmIGIgPD0gMHg3RjtcbiAgICB2YXIgY2FzZTIgPSAoYSA9PT0gMHgxMCB8fCBhID09PSAweDE4KSAmJiBiID49IDB4NDAgJiYgYiA8PSAweDVGO1xuXG4gICAgaWYgKCEoY2FzZTEgfHwgY2FzZTIpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKGhhc0NtZFJlcGVhdGVkKGEsIGIsIGNtZEhpc3RvcnkpKSB7XG4gICAgICBzZXRMYXN0Q21kKG51bGwsIG51bGwsIGNtZEhpc3RvcnkpO1xuICAgICAgcmV0dXJuIHRydWU7IC8vIFJlcGVhdGVkIGNvbW1hbmRzIGFyZSBkcm9wcGVkIChvbmNlKVxuICAgIH1cblxuICAgIHZhciBjaE5yID0gYSA8PSAweDE3ID8gMSA6IDI7XG5cbiAgICBpZiAoYiA+PSAweDQwICYmIGIgPD0gMHg1Rikge1xuICAgICAgcm93ID0gY2hOciA9PT0gMSA/IHJvd3NMb3dDaDFbYV0gOiByb3dzTG93Q2gyW2FdO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyAweDYwIDw9IGIgPD0gMHg3RlxuICAgICAgcm93ID0gY2hOciA9PT0gMSA/IHJvd3NIaWdoQ2gxW2FdIDogcm93c0hpZ2hDaDJbYV07XG4gICAgfVxuXG4gICAgdmFyIGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzW2NoTnJdO1xuXG4gICAgaWYgKCFjaGFubmVsKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgY2hhbm5lbC5zZXRQQUModGhpcy5pbnRlcnByZXRQQUMocm93LCBiKSk7XG4gICAgc2V0TGFzdENtZChhLCBiLCBjbWRIaXN0b3J5KTtcbiAgICB0aGlzLmN1cnJlbnRDaGFubmVsID0gY2hOcjtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICAvKipcbiAgICogSW50ZXJwcmV0IHRoZSBzZWNvbmQgYnl0ZSBvZiB0aGUgcGFjLCBhbmQgcmV0dXJuIHRoZSBpbmZvcm1hdGlvbi5cbiAgICogQHJldHVybnMge09iamVjdH0gcGFjRGF0YSB3aXRoIHN0eWxlIHBhcmFtZXRlcnMuXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvNy5pbnRlcnByZXRQQUMgPSBmdW5jdGlvbiBpbnRlcnByZXRQQUMocm93LCBfYnl0ZTMpIHtcbiAgICB2YXIgcGFjSW5kZXggPSBfYnl0ZTM7XG4gICAgdmFyIHBhY0RhdGEgPSB7XG4gICAgICBjb2xvcjogbnVsbCxcbiAgICAgIGl0YWxpY3M6IGZhbHNlLFxuICAgICAgaW5kZW50OiBudWxsLFxuICAgICAgdW5kZXJsaW5lOiBmYWxzZSxcbiAgICAgIHJvdzogcm93XG4gICAgfTtcblxuICAgIGlmIChfYnl0ZTMgPiAweDVGKSB7XG4gICAgICBwYWNJbmRleCA9IF9ieXRlMyAtIDB4NjA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhY0luZGV4ID0gX2J5dGUzIC0gMHg0MDtcbiAgICB9XG5cbiAgICBwYWNEYXRhLnVuZGVybGluZSA9IChwYWNJbmRleCAmIDEpID09PSAxO1xuXG4gICAgaWYgKHBhY0luZGV4IDw9IDB4ZCkge1xuICAgICAgcGFjRGF0YS5jb2xvciA9IFsnd2hpdGUnLCAnZ3JlZW4nLCAnYmx1ZScsICdjeWFuJywgJ3JlZCcsICd5ZWxsb3cnLCAnbWFnZW50YScsICd3aGl0ZSddW01hdGguZmxvb3IocGFjSW5kZXggLyAyKV07XG4gICAgfSBlbHNlIGlmIChwYWNJbmRleCA8PSAweGYpIHtcbiAgICAgIHBhY0RhdGEuaXRhbGljcyA9IHRydWU7XG4gICAgICBwYWNEYXRhLmNvbG9yID0gJ3doaXRlJztcbiAgICB9IGVsc2Uge1xuICAgICAgcGFjRGF0YS5pbmRlbnQgPSBNYXRoLmZsb29yKChwYWNJbmRleCAtIDB4MTApIC8gMikgKiA0O1xuICAgIH1cblxuICAgIHJldHVybiBwYWNEYXRhOyAvLyBOb3RlIHRoYXQgcm93IGhhcyB6ZXJvIG9mZnNldC4gVGhlIHNwZWMgdXNlcyAxLlxuICB9XG4gIC8qKlxuICAgKiBQYXJzZSBjaGFyYWN0ZXJzLlxuICAgKiBAcmV0dXJucyBBbiBhcnJheSB3aXRoIDEgdG8gMiBjb2RlcyBjb3JyZXNwb25kaW5nIHRvIGNoYXJzLCBpZiBmb3VuZC4gbnVsbCBvdGhlcndpc2UuXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvNy5wYXJzZUNoYXJzID0gZnVuY3Rpb24gcGFyc2VDaGFycyhhLCBiKSB7XG4gICAgdmFyIGNoYW5uZWxOcjtcbiAgICB2YXIgY2hhckNvZGVzID0gbnVsbDtcbiAgICB2YXIgY2hhckNvZGUxID0gbnVsbDtcblxuICAgIGlmIChhID49IDB4MTkpIHtcbiAgICAgIGNoYW5uZWxOciA9IDI7XG4gICAgICBjaGFyQ29kZTEgPSBhIC0gODtcbiAgICB9IGVsc2Uge1xuICAgICAgY2hhbm5lbE5yID0gMTtcbiAgICAgIGNoYXJDb2RlMSA9IGE7XG4gICAgfVxuXG4gICAgaWYgKGNoYXJDb2RlMSA+PSAweDExICYmIGNoYXJDb2RlMSA8PSAweDEzKSB7XG4gICAgICAvLyBTcGVjaWFsIGNoYXJhY3RlclxuICAgICAgdmFyIG9uZUNvZGUgPSBiO1xuXG4gICAgICBpZiAoY2hhckNvZGUxID09PSAweDExKSB7XG4gICAgICAgIG9uZUNvZGUgPSBiICsgMHg1MDtcbiAgICAgIH0gZWxzZSBpZiAoY2hhckNvZGUxID09PSAweDEyKSB7XG4gICAgICAgIG9uZUNvZGUgPSBiICsgMHg3MDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG9uZUNvZGUgPSBiICsgMHg5MDtcbiAgICAgIH1cblxuICAgICAgdGhpcy5sb2dnZXIubG9nKFZlcmJvc2VMZXZlbC5JTkZPLCAnU3BlY2lhbCBjaGFyIFxcJycgKyBnZXRDaGFyRm9yQnl0ZShvbmVDb2RlKSArICdcXCcgaW4gY2hhbm5lbCAnICsgY2hhbm5lbE5yKTtcbiAgICAgIGNoYXJDb2RlcyA9IFtvbmVDb2RlXTtcbiAgICB9IGVsc2UgaWYgKGEgPj0gMHgyMCAmJiBhIDw9IDB4N2YpIHtcbiAgICAgIGNoYXJDb2RlcyA9IGIgPT09IDAgPyBbYV0gOiBbYSwgYl07XG4gICAgfVxuXG4gICAgaWYgKGNoYXJDb2Rlcykge1xuICAgICAgdmFyIGhleENvZGVzID0gbnVtQXJyYXlUb0hleEFycmF5KGNoYXJDb2Rlcyk7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coVmVyYm9zZUxldmVsLkRFQlVHLCAnQ2hhciBjb2RlcyA9ICAnICsgaGV4Q29kZXMuam9pbignLCcpKTtcbiAgICAgIHNldExhc3RDbWQoYSwgYiwgdGhpcy5jbWRIaXN0b3J5KTtcbiAgICB9XG5cbiAgICByZXR1cm4gY2hhckNvZGVzO1xuICB9XG4gIC8qKlxuICAgKiBQYXJzZSBleHRlbmRlZCBiYWNrZ3JvdW5kIGF0dHJpYnV0ZXMgYXMgd2VsbCBhcyBuZXcgZm9yZWdyb3VuZCBjb2xvciBibGFjay5cbiAgICogQHJldHVybnMge0Jvb2xlYW59IFRlbGxzIGlmIGJhY2tncm91bmQgYXR0cmlidXRlcyBhcmUgZm91bmRcbiAgICovXG4gIDtcblxuICBfcHJvdG83LnBhcnNlQmFja2dyb3VuZEF0dHJpYnV0ZXMgPSBmdW5jdGlvbiBwYXJzZUJhY2tncm91bmRBdHRyaWJ1dGVzKGEsIGIpIHtcbiAgICB2YXIgY2FzZTEgPSAoYSA9PT0gMHgxMCB8fCBhID09PSAweDE4KSAmJiBiID49IDB4MjAgJiYgYiA8PSAweDJmO1xuICAgIHZhciBjYXNlMiA9IChhID09PSAweDE3IHx8IGEgPT09IDB4MWYpICYmIGIgPj0gMHgyZCAmJiBiIDw9IDB4MmY7XG5cbiAgICBpZiAoIShjYXNlMSB8fCBjYXNlMikpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB2YXIgaW5kZXg7XG4gICAgdmFyIGJrZ0RhdGEgPSB7fTtcblxuICAgIGlmIChhID09PSAweDEwIHx8IGEgPT09IDB4MTgpIHtcbiAgICAgIGluZGV4ID0gTWF0aC5mbG9vcigoYiAtIDB4MjApIC8gMik7XG4gICAgICBia2dEYXRhLmJhY2tncm91bmQgPSBiYWNrZ3JvdW5kQ29sb3JzW2luZGV4XTtcblxuICAgICAgaWYgKGIgJSAyID09PSAxKSB7XG4gICAgICAgIGJrZ0RhdGEuYmFja2dyb3VuZCA9IGJrZ0RhdGEuYmFja2dyb3VuZCArICdfc2VtaSc7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChiID09PSAweDJkKSB7XG4gICAgICBia2dEYXRhLmJhY2tncm91bmQgPSAndHJhbnNwYXJlbnQnO1xuICAgIH0gZWxzZSB7XG4gICAgICBia2dEYXRhLmZvcmVncm91bmQgPSAnYmxhY2snO1xuXG4gICAgICBpZiAoYiA9PT0gMHgyZikge1xuICAgICAgICBia2dEYXRhLnVuZGVybGluZSA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIGNoTnIgPSBhIDw9IDB4MTcgPyAxIDogMjtcbiAgICB2YXIgY2hhbm5lbCA9IHRoaXMuY2hhbm5lbHNbY2hOcl07XG4gICAgY2hhbm5lbC5zZXRCa2dEYXRhKGJrZ0RhdGEpO1xuICAgIHNldExhc3RDbWQoYSwgYiwgdGhpcy5jbWRIaXN0b3J5KTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICAvKipcbiAgICogUmVzZXQgc3RhdGUgb2YgcGFyc2VyIGFuZCBpdHMgY2hhbm5lbHMuXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvNy5yZXNldCA9IGZ1bmN0aW9uIHJlc2V0KCkge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgT2JqZWN0LmtleXModGhpcy5jaGFubmVscykubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tpXTtcblxuICAgICAgaWYgKGNoYW5uZWwpIHtcbiAgICAgICAgY2hhbm5lbC5yZXNldCgpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuY21kSGlzdG9yeSA9IGNyZWF0ZUNtZEhpc3RvcnkoKTtcbiAgfVxuICAvKipcbiAgICogVHJpZ2dlciB0aGUgZ2VuZXJhdGlvbiBvZiBhIGN1ZSwgYW5kIHRoZSBzdGFydCBvZiBhIG5ldyBvbmUgaWYgZGlzcGxheVNjcmVlbnMgYXJlIG5vdCBlbXB0eS5cbiAgICovXG4gIDtcblxuICBfcHJvdG83LmN1ZVNwbGl0QXRUaW1lID0gZnVuY3Rpb24gY3VlU3BsaXRBdFRpbWUodCkge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5jaGFubmVscy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzW2ldO1xuXG4gICAgICBpZiAoY2hhbm5lbCkge1xuICAgICAgICBjaGFubmVsLmN1ZVNwbGl0QXRUaW1lKHQpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcblxuICByZXR1cm4gQ2VhNjA4UGFyc2VyO1xufSgpO1xuXG5mdW5jdGlvbiBzZXRMYXN0Q21kKGEsIGIsIGNtZEhpc3RvcnkpIHtcbiAgY21kSGlzdG9yeS5hID0gYTtcbiAgY21kSGlzdG9yeS5iID0gYjtcbn1cblxuZnVuY3Rpb24gaGFzQ21kUmVwZWF0ZWQoYSwgYiwgY21kSGlzdG9yeSkge1xuICByZXR1cm4gY21kSGlzdG9yeS5hID09PSBhICYmIGNtZEhpc3RvcnkuYiA9PT0gYjtcbn1cblxuZnVuY3Rpb24gY3JlYXRlQ21kSGlzdG9yeSgpIHtcbiAgcmV0dXJuIHtcbiAgICBhOiBudWxsLFxuICAgIGI6IG51bGxcbiAgfTtcbn1cblxuLyogaGFybW9ueSBkZWZhdWx0IGV4cG9ydCAqLyB2YXIgY2VhXzYwOF9wYXJzZXIgPSAoQ2VhNjA4UGFyc2VyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL291dHB1dC1maWx0ZXIudHNcbnZhciBPdXRwdXRGaWx0ZXIgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBPdXRwdXRGaWx0ZXIodGltZWxpbmVDb250cm9sbGVyLCB0cmFja05hbWUpIHtcbiAgICB0aGlzLnRpbWVsaW5lQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmN1ZVJhbmdlcyA9IFtdO1xuICAgIHRoaXMudHJhY2tOYW1lID0gdm9pZCAwO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gbnVsbDtcbiAgICB0aGlzLmVuZFRpbWUgPSBudWxsO1xuICAgIHRoaXMuc2NyZWVuID0gbnVsbDtcbiAgICB0aGlzLnRpbWVsaW5lQ29udHJvbGxlciA9IHRpbWVsaW5lQ29udHJvbGxlcjtcbiAgICB0aGlzLnRyYWNrTmFtZSA9IHRyYWNrTmFtZTtcbiAgfVxuXG4gIHZhciBfcHJvdG8gPSBPdXRwdXRGaWx0ZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5kaXNwYXRjaEN1ZSA9IGZ1bmN0aW9uIGRpc3BhdGNoQ3VlKCkge1xuICAgIGlmICh0aGlzLnN0YXJ0VGltZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMudGltZWxpbmVDb250cm9sbGVyLmFkZEN1ZXModGhpcy50cmFja05hbWUsIHRoaXMuc3RhcnRUaW1lLCB0aGlzLmVuZFRpbWUsIHRoaXMuc2NyZWVuLCB0aGlzLmN1ZVJhbmdlcyk7XG4gICAgdGhpcy5zdGFydFRpbWUgPSBudWxsO1xuICB9O1xuXG4gIF9wcm90by5uZXdDdWUgPSBmdW5jdGlvbiBuZXdDdWUoc3RhcnRUaW1lLCBlbmRUaW1lLCBzY3JlZW4pIHtcbiAgICBpZiAodGhpcy5zdGFydFRpbWUgPT09IG51bGwgfHwgdGhpcy5zdGFydFRpbWUgPiBzdGFydFRpbWUpIHtcbiAgICAgIHRoaXMuc3RhcnRUaW1lID0gc3RhcnRUaW1lO1xuICAgIH1cblxuICAgIHRoaXMuZW5kVGltZSA9IGVuZFRpbWU7XG4gICAgdGhpcy5zY3JlZW4gPSBzY3JlZW47XG4gICAgdGhpcy50aW1lbGluZUNvbnRyb2xsZXIuY3JlYXRlQ2FwdGlvbnNUcmFjayh0aGlzLnRyYWNrTmFtZSk7XG4gIH07XG5cbiAgX3Byb3RvLnJlc2V0ID0gZnVuY3Rpb24gcmVzZXQoKSB7XG4gICAgdGhpcy5jdWVSYW5nZXMgPSBbXTtcbiAgfTtcblxuICByZXR1cm4gT3V0cHV0RmlsdGVyO1xufSgpO1xuXG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL3V0aWxzL3dlYnZ0dC1wYXJzZXIuanNcblxuXG5cblxuXG4gLy8gU3RyaW5nLnByb3RvdHlwZS5zdGFydHNXaXRoIGlzIG5vdCBzdXBwb3J0ZWQgaW4gSUUxMVxuXG52YXIgc3RhcnRzV2l0aCA9IGZ1bmN0aW9uIHN0YXJ0c1dpdGgoaW5wdXRTdHJpbmcsIHNlYXJjaFN0cmluZywgcG9zaXRpb24pIHtcbiAgcmV0dXJuIGlucHV0U3RyaW5nLnN1YnN0cihwb3NpdGlvbiB8fCAwLCBzZWFyY2hTdHJpbmcubGVuZ3RoKSA9PT0gc2VhcmNoU3RyaW5nO1xufTtcblxudmFyIHdlYnZ0dF9wYXJzZXJfY3VlU3RyaW5nMm1pbGxpcyA9IGZ1bmN0aW9uIGN1ZVN0cmluZzJtaWxsaXModGltZVN0cmluZykge1xuICB2YXIgdHMgPSBwYXJzZUludCh0aW1lU3RyaW5nLnN1YnN0cigtMykpO1xuICB2YXIgc2VjcyA9IHBhcnNlSW50KHRpbWVTdHJpbmcuc3Vic3RyKC02LCAyKSk7XG4gIHZhciBtaW5zID0gcGFyc2VJbnQodGltZVN0cmluZy5zdWJzdHIoLTksIDIpKTtcbiAgdmFyIGhvdXJzID0gdGltZVN0cmluZy5sZW5ndGggPiA5ID8gcGFyc2VJbnQodGltZVN0cmluZy5zdWJzdHIoMCwgdGltZVN0cmluZy5pbmRleE9mKCc6JykpKSA6IDA7XG5cbiAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHRzKSB8fCAhT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShzZWNzKSB8fCAhT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShtaW5zKSB8fCAhT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKShob3VycykpIHtcbiAgICB0aHJvdyBFcnJvcihcIk1hbGZvcm1lZCBYLVRJTUVTVEFNUC1NQVA6IExvY2FsOlwiICsgdGltZVN0cmluZyk7XG4gIH1cblxuICB0cyArPSAxMDAwICogc2VjcztcbiAgdHMgKz0gNjAgKiAxMDAwICogbWlucztcbiAgdHMgKz0gNjAgKiA2MCAqIDEwMDAgKiBob3VycztcbiAgcmV0dXJuIHRzO1xufTsgLy8gRnJvbSBodHRwczovL2dpdGh1Yi5jb20vZGFya3NreWFwcC9zdHJpbmctaGFzaFxuXG5cbnZhciBoYXNoID0gZnVuY3Rpb24gaGFzaCh0ZXh0KSB7XG4gIHZhciBoYXNoID0gNTM4MTtcbiAgdmFyIGkgPSB0ZXh0Lmxlbmd0aDtcblxuICB3aGlsZSAoaSkge1xuICAgIGhhc2ggPSBoYXNoICogMzMgXiB0ZXh0LmNoYXJDb2RlQXQoLS1pKTtcbiAgfVxuXG4gIHJldHVybiAoaGFzaCA+Pj4gMCkudG9TdHJpbmcoKTtcbn07XG5cbnZhciBjYWxjdWxhdGVPZmZzZXQgPSBmdW5jdGlvbiBjYWxjdWxhdGVPZmZzZXQodnR0Q0NzLCBjYywgcHJlc2VudGF0aW9uVGltZSkge1xuICB2YXIgY3VyckNDID0gdnR0Q0NzW2NjXTtcbiAgdmFyIHByZXZDQyA9IHZ0dENDc1tjdXJyQ0MucHJldkNDXTsgLy8gVGhpcyBpcyB0aGUgZmlyc3QgZGlzY29udGludWl0eSBvciBjdWVzIGhhdmUgYmVlbiBwcm9jZXNzZWQgc2luY2UgdGhlIGxhc3QgZGlzY29udGludWl0eVxuICAvLyBPZmZzZXQgPSBjdXJyZW50IGRpc2NvbnRpbnVpdHkgdGltZVxuXG4gIGlmICghcHJldkNDIHx8ICFwcmV2Q0MubmV3ICYmIGN1cnJDQy5uZXcpIHtcbiAgICB2dHRDQ3MuY2NPZmZzZXQgPSB2dHRDQ3MucHJlc2VudGF0aW9uT2Zmc2V0ID0gY3VyckNDLnN0YXJ0O1xuICAgIGN1cnJDQy5uZXcgPSBmYWxzZTtcbiAgICByZXR1cm47XG4gIH0gLy8gVGhlcmUgaGF2ZSBiZWVuIGRpc2NvbnRpbnVpdGllcyBzaW5jZSBjdWVzIHdlcmUgbGFzdCBwYXJzZWQuXG4gIC8vIE9mZnNldCA9IHRpbWUgZWxhcHNlZFxuXG5cbiAgd2hpbGUgKHByZXZDQyAmJiBwcmV2Q0MubmV3KSB7XG4gICAgdnR0Q0NzLmNjT2Zmc2V0ICs9IGN1cnJDQy5zdGFydCAtIHByZXZDQy5zdGFydDtcbiAgICBjdXJyQ0MubmV3ID0gZmFsc2U7XG4gICAgY3VyckNDID0gcHJldkNDO1xuICAgIHByZXZDQyA9IHZ0dENDc1tjdXJyQ0MucHJldkNDXTtcbiAgfVxuXG4gIHZ0dENDcy5wcmVzZW50YXRpb25PZmZzZXQgPSBwcmVzZW50YXRpb25UaW1lO1xufTtcblxudmFyIFdlYlZUVFBhcnNlciA9IHtcbiAgcGFyc2U6IGZ1bmN0aW9uIHBhcnNlKHZ0dEJ5dGVBcnJheSwgc3luY1BUUywgdnR0Q0NzLCBjYywgY2FsbEJhY2ssIGVycm9yQ2FsbEJhY2spIHtcbiAgICAvLyBDb252ZXJ0IGJ5dGVBcnJheSBpbnRvIHN0cmluZywgcmVwbGFjaW5nIGFueSBzb21ld2hhdCBleG90aWMgbGluZWZlZWRzIHdpdGggXCJcXG5cIiwgdGhlbiBzcGxpdCBvbiB0aGF0IGNoYXJhY3Rlci5cbiAgICB2YXIgcmUgPSAvXFxyXFxufFxcblxccnxcXG58XFxyL2c7IC8vIFVpbnQ4QXJyYXkucHJvdG90eXBlLnJlZHVjZSBpcyBub3QgaW1wbGVtZW50ZWQgaW4gSUUxMVxuXG4gICAgdmFyIHZ0dExpbmVzID0gT2JqZWN0KGlkM1tcInV0ZjhBcnJheVRvU3RyXCJdKShuZXcgVWludDhBcnJheSh2dHRCeXRlQXJyYXkpKS50cmltKCkucmVwbGFjZShyZSwgJ1xcbicpLnNwbGl0KCdcXG4nKTtcbiAgICB2YXIgY3VlVGltZSA9ICcwMDowMC4wMDAnO1xuICAgIHZhciBtcGVnVHMgPSAwO1xuICAgIHZhciBsb2NhbFRpbWUgPSAwO1xuICAgIHZhciBwcmVzZW50YXRpb25UaW1lID0gMDtcbiAgICB2YXIgY3VlcyA9IFtdO1xuICAgIHZhciBwYXJzaW5nRXJyb3I7XG4gICAgdmFyIGluSGVhZGVyID0gdHJ1ZTtcbiAgICB2YXIgdGltZXN0YW1wTWFwID0gZmFsc2U7IC8vIGxldCBWVFRDdWUgPSBWVFRDdWUgfHwgd2luZG93LlRleHRUcmFja0N1ZTtcbiAgICAvLyBDcmVhdGUgcGFyc2VyIG9iamVjdCB1c2luZyBWVFRDdWUgd2l0aCBUZXh0VHJhY2tDdWUgZmFsbGJhY2sgb24gY2VydGFpbiBicm93c2Vycy5cblxuICAgIHZhciBwYXJzZXIgPSBuZXcgdnR0cGFyc2VyKCk7XG5cbiAgICBwYXJzZXIub25jdWUgPSBmdW5jdGlvbiAoY3VlKSB7XG4gICAgICAvLyBBZGp1c3QgY3VlIHRpbWluZzsgY2xhbXAgY3VlcyB0byBzdGFydCBubyBlYXJsaWVyIHRoYW4gLSBhbmQgZHJvcCBjdWVzIHRoYXQgZG9uJ3QgZW5kIGFmdGVyIC0gMCBvbiB0aW1lbGluZS5cbiAgICAgIHZhciBjdXJyQ0MgPSB2dHRDQ3NbY2NdO1xuICAgICAgdmFyIGN1ZU9mZnNldCA9IHZ0dENDcy5jY09mZnNldDsgLy8gVXBkYXRlIG9mZnNldHMgZm9yIG5ldyBkaXNjb250aW51aXRpZXNcblxuICAgICAgaWYgKGN1cnJDQyAmJiBjdXJyQ0MubmV3KSB7XG4gICAgICAgIGlmIChsb2NhbFRpbWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIC8vIFdoZW4gbG9jYWwgdGltZSBpcyBwcm92aWRlZCwgb2Zmc2V0ID0gZGlzY29udGludWl0eSBzdGFydCB0aW1lIC0gbG9jYWwgdGltZVxuICAgICAgICAgIGN1ZU9mZnNldCA9IHZ0dENDcy5jY09mZnNldCA9IGN1cnJDQy5zdGFydDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjYWxjdWxhdGVPZmZzZXQodnR0Q0NzLCBjYywgcHJlc2VudGF0aW9uVGltZSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHByZXNlbnRhdGlvblRpbWUpIHtcbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBNUEVHVFMsIG9mZnNldCA9IHByZXNlbnRhdGlvbiB0aW1lICsgZGlzY29udGludWl0eSBvZmZzZXRcbiAgICAgICAgY3VlT2Zmc2V0ID0gcHJlc2VudGF0aW9uVGltZSAtIHZ0dENDcy5wcmVzZW50YXRpb25PZmZzZXQ7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aW1lc3RhbXBNYXApIHtcbiAgICAgICAgY3VlLnN0YXJ0VGltZSArPSBjdWVPZmZzZXQgLSBsb2NhbFRpbWU7XG4gICAgICAgIGN1ZS5lbmRUaW1lICs9IGN1ZU9mZnNldCAtIGxvY2FsVGltZTtcbiAgICAgIH0gLy8gQ3JlYXRlIGEgdW5pcXVlIGhhc2ggaWQgZm9yIGEgY3VlIGJhc2VkIG9uIHN0YXJ0L2VuZCB0aW1lcyBhbmQgdGV4dC5cbiAgICAgIC8vIFRoaXMgaGVscHMgdGltZWxpbmUtY29udHJvbGxlciB0byBhdm9pZCBzaG93aW5nIHJlcGVhdGVkIGNhcHRpb25zLlxuXG5cbiAgICAgIGN1ZS5pZCA9IGhhc2goY3VlLnN0YXJ0VGltZS50b1N0cmluZygpKSArIGhhc2goY3VlLmVuZFRpbWUudG9TdHJpbmcoKSkgKyBoYXNoKGN1ZS50ZXh0KTsgLy8gRml4IGVuY29kaW5nIG9mIHNwZWNpYWwgY2hhcmFjdGVycy4gVE9ETzogVGVzdCB3aXRoIGFsbCBzb3J0cyBvZiB3ZWlyZCBjaGFyYWN0ZXJzLlxuXG4gICAgICBjdWUudGV4dCA9IGRlY29kZVVSSUNvbXBvbmVudChlbmNvZGVVUklDb21wb25lbnQoY3VlLnRleHQpKTtcblxuICAgICAgaWYgKGN1ZS5lbmRUaW1lID4gMCkge1xuICAgICAgICBjdWVzLnB1c2goY3VlKTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgcGFyc2VyLm9ucGFyc2luZ2Vycm9yID0gZnVuY3Rpb24gKGUpIHtcbiAgICAgIHBhcnNpbmdFcnJvciA9IGU7XG4gICAgfTtcblxuICAgIHBhcnNlci5vbmZsdXNoID0gZnVuY3Rpb24gKCkge1xuICAgICAgaWYgKHBhcnNpbmdFcnJvciAmJiBlcnJvckNhbGxCYWNrKSB7XG4gICAgICAgIGVycm9yQ2FsbEJhY2socGFyc2luZ0Vycm9yKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjYWxsQmFjayhjdWVzKTtcbiAgICB9OyAvLyBHbyB0aHJvdWdoIGNvbnRlbnRzIGxpbmUgYnkgbGluZS5cblxuXG4gICAgdnR0TGluZXMuZm9yRWFjaChmdW5jdGlvbiAobGluZSkge1xuICAgICAgaWYgKGluSGVhZGVyKSB7XG4gICAgICAgIC8vIExvb2sgZm9yIFgtVElNRVNUQU1QLU1BUCBpbiBoZWFkZXIuXG4gICAgICAgIGlmIChzdGFydHNXaXRoKGxpbmUsICdYLVRJTUVTVEFNUC1NQVA9JykpIHtcbiAgICAgICAgICAvLyBPbmNlIGZvdW5kLCBubyBtb3JlIGFyZSBhbGxvd2VkIGFueXdheSwgc28gc3RvcCBzZWFyY2hpbmcuXG4gICAgICAgICAgaW5IZWFkZXIgPSBmYWxzZTtcbiAgICAgICAgICB0aW1lc3RhbXBNYXAgPSB0cnVlOyAvLyBFeHRyYWN0IExPQ0FMIGFuZCBNUEVHVFMuXG5cbiAgICAgICAgICBsaW5lLnN1YnN0cigxNikuc3BsaXQoJywnKS5mb3JFYWNoKGZ1bmN0aW9uICh0aW1lc3RhbXApIHtcbiAgICAgICAgICAgIGlmIChzdGFydHNXaXRoKHRpbWVzdGFtcCwgJ0xPQ0FMOicpKSB7XG4gICAgICAgICAgICAgIGN1ZVRpbWUgPSB0aW1lc3RhbXAuc3Vic3RyKDYpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChzdGFydHNXaXRoKHRpbWVzdGFtcCwgJ01QRUdUUzonKSkge1xuICAgICAgICAgICAgICBtcGVnVHMgPSBwYXJzZUludCh0aW1lc3RhbXAuc3Vic3RyKDcpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAvLyBDYWxjdWxhdGUgc3VidGl0bGUgb2Zmc2V0IGluIG1pbGxpc2Vjb25kcy5cbiAgICAgICAgICAgIGlmIChzeW5jUFRTICsgKHZ0dENDc1tjY10uc3RhcnQgKiA5MDAwMCB8fCAwKSA8IDApIHtcbiAgICAgICAgICAgICAgc3luY1BUUyArPSA4NTg5OTM0NTkyO1xuICAgICAgICAgICAgfSAvLyBBZGp1c3QgTVBFR1RTIGJ5IHN5bmMgUFRTLlxuXG5cbiAgICAgICAgICAgIG1wZWdUcyAtPSBzeW5jUFRTOyAvLyBDb252ZXJ0IGN1ZSB0aW1lIHRvIHNlY29uZHNcblxuICAgICAgICAgICAgbG9jYWxUaW1lID0gd2VidnR0X3BhcnNlcl9jdWVTdHJpbmcybWlsbGlzKGN1ZVRpbWUpIC8gMTAwMDsgLy8gQ29udmVydCBNUEVHVFMgdG8gc2Vjb25kcyBmcm9tIDkwa0h6LlxuXG4gICAgICAgICAgICBwcmVzZW50YXRpb25UaW1lID0gbXBlZ1RzIC8gOTAwMDA7XG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgdGltZXN0YW1wTWFwID0gZmFsc2U7XG4gICAgICAgICAgICBwYXJzaW5nRXJyb3IgPSBlO1xuICAgICAgICAgIH0gLy8gUmV0dXJuIHdpdGhvdXQgcGFyc2luZyBYLVRJTUVTVEFNUC1NQVAgbGluZS5cblxuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9IGVsc2UgaWYgKGxpbmUgPT09ICcnKSB7XG4gICAgICAgICAgaW5IZWFkZXIgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfSAvLyBQYXJzZSBsaW5lIGJ5IGRlZmF1bHQuXG5cblxuICAgICAgcGFyc2VyLnBhcnNlKGxpbmUgKyAnXFxuJyk7XG4gICAgfSk7XG4gICAgcGFyc2VyLmZsdXNoKCk7XG4gIH1cbn07XG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciB3ZWJ2dHRfcGFyc2VyID0gKFdlYlZUVFBhcnNlcik7XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9jb250cm9sbGVyL3RpbWVsaW5lLWNvbnRyb2xsZXIudHNcblxuXG5cbmZ1bmN0aW9uIHRpbWVsaW5lX2NvbnRyb2xsZXJfYXNzZXJ0VGhpc0luaXRpYWxpemVkKHNlbGYpIHsgaWYgKHNlbGYgPT09IHZvaWQgMCkgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIHNlbGY7IH1cblxuZnVuY3Rpb24gdGltZWxpbmVfY29udHJvbGxlcl9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpOyBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzczsgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG5cblxuXG5cblxuXG5cblxudmFyIHRpbWVsaW5lX2NvbnRyb2xsZXJfVGltZWxpbmVDb250cm9sbGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChfRXZlbnRIYW5kbGVyKSB7XG4gIHRpbWVsaW5lX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShUaW1lbGluZUNvbnRyb2xsZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIGZ1bmN0aW9uIFRpbWVsaW5lQ29udHJvbGxlcihobHMpIHtcbiAgICB2YXIgX3RoaXM7XG5cbiAgICBfdGhpcyA9IF9FdmVudEhhbmRsZXIuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfQVRUQUNISU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX1BBUlNJTkdfVVNFUkRBVEEsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19ERUNSWVBURUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUFOSUZFU1RfTE9BRElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uSU5JVF9QVFNfRk9VTkQpIHx8IHRoaXM7XG4gICAgX3RoaXMubWVkaWEgPSBudWxsO1xuICAgIF90aGlzLmNvbmZpZyA9IHZvaWQgMDtcbiAgICBfdGhpcy5lbmFibGVkID0gdHJ1ZTtcbiAgICBfdGhpcy5DdWVzID0gdm9pZCAwO1xuICAgIF90aGlzLnRleHRUcmFja3MgPSBbXTtcbiAgICBfdGhpcy50cmFja3MgPSBbXTtcbiAgICBfdGhpcy5pbml0UFRTID0gW107XG4gICAgX3RoaXMudW5wYXJzZWRWdHRGcmFncyA9IFtdO1xuICAgIF90aGlzLmNhcHRpb25zVHJhY2tzID0ge307XG4gICAgX3RoaXMubm9uTmF0aXZlQ2FwdGlvbnNUcmFja3MgPSB7fTtcbiAgICBfdGhpcy5jYXB0aW9uc1Byb3BlcnRpZXMgPSB2b2lkIDA7XG4gICAgX3RoaXMuY2VhNjA4UGFyc2VyMSA9IHZvaWQgMDtcbiAgICBfdGhpcy5jZWE2MDhQYXJzZXIyID0gdm9pZCAwO1xuICAgIF90aGlzLmxhc3RTbiA9IC0xO1xuICAgIF90aGlzLnByZXZDQyA9IC0xO1xuICAgIF90aGlzLnZ0dENDcyA9IG5ld1ZUVENDcygpO1xuICAgIF90aGlzLmhscyA9IGhscztcbiAgICBfdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIF90aGlzLkN1ZXMgPSBobHMuY29uZmlnLmN1ZUhhbmRsZXI7XG4gICAgX3RoaXMuY2FwdGlvbnNQcm9wZXJ0aWVzID0ge1xuICAgICAgdGV4dFRyYWNrMToge1xuICAgICAgICBsYWJlbDogX3RoaXMuY29uZmlnLmNhcHRpb25zVGV4dFRyYWNrMUxhYmVsLFxuICAgICAgICBsYW5ndWFnZUNvZGU6IF90aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazFMYW5ndWFnZUNvZGVcbiAgICAgIH0sXG4gICAgICB0ZXh0VHJhY2syOiB7XG4gICAgICAgIGxhYmVsOiBfdGhpcy5jb25maWcuY2FwdGlvbnNUZXh0VHJhY2syTGFiZWwsXG4gICAgICAgIGxhbmd1YWdlQ29kZTogX3RoaXMuY29uZmlnLmNhcHRpb25zVGV4dFRyYWNrMkxhbmd1YWdlQ29kZVxuICAgICAgfSxcbiAgICAgIHRleHRUcmFjazM6IHtcbiAgICAgICAgbGFiZWw6IF90aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazNMYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlOiBfdGhpcy5jb25maWcuY2FwdGlvbnNUZXh0VHJhY2szTGFuZ3VhZ2VDb2RlXG4gICAgICB9LFxuICAgICAgdGV4dFRyYWNrNDoge1xuICAgICAgICBsYWJlbDogX3RoaXMuY29uZmlnLmNhcHRpb25zVGV4dFRyYWNrNExhYmVsLFxuICAgICAgICBsYW5ndWFnZUNvZGU6IF90aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazRMYW5ndWFnZUNvZGVcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgaWYgKF90aGlzLmNvbmZpZy5lbmFibGVDRUE3MDhDYXB0aW9ucykge1xuICAgICAgdmFyIGNoYW5uZWwxID0gbmV3IE91dHB1dEZpbHRlcih0aW1lbGluZV9jb250cm9sbGVyX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcyksICd0ZXh0VHJhY2sxJyk7XG4gICAgICB2YXIgY2hhbm5lbDIgPSBuZXcgT3V0cHV0RmlsdGVyKHRpbWVsaW5lX2NvbnRyb2xsZXJfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSwgJ3RleHRUcmFjazInKTtcbiAgICAgIHZhciBjaGFubmVsMyA9IG5ldyBPdXRwdXRGaWx0ZXIodGltZWxpbmVfY29udHJvbGxlcl9hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpLCAndGV4dFRyYWNrMycpO1xuICAgICAgdmFyIGNoYW5uZWw0ID0gbmV3IE91dHB1dEZpbHRlcih0aW1lbGluZV9jb250cm9sbGVyX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcyksICd0ZXh0VHJhY2s0Jyk7XG4gICAgICBfdGhpcy5jZWE2MDhQYXJzZXIxID0gbmV3IGNlYV82MDhfcGFyc2VyKDEsIGNoYW5uZWwxLCBjaGFubmVsMik7XG4gICAgICBfdGhpcy5jZWE2MDhQYXJzZXIyID0gbmV3IGNlYV82MDhfcGFyc2VyKDMsIGNoYW5uZWwzLCBjaGFubmVsNCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIF90aGlzO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IFRpbWVsaW5lQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLmFkZEN1ZXMgPSBmdW5jdGlvbiBhZGRDdWVzKHRyYWNrTmFtZSwgc3RhcnRUaW1lLCBlbmRUaW1lLCBzY3JlZW4sIGN1ZVJhbmdlcykge1xuICAgIC8vIHNraXAgY3VlcyB3aGljaCBvdmVybGFwIG1vcmUgdGhhbiA1MCUgd2l0aCBwcmV2aW91c2x5IHBhcnNlZCB0aW1lIHJhbmdlc1xuICAgIHZhciBtZXJnZWQgPSBmYWxzZTtcblxuICAgIGZvciAodmFyIGkgPSBjdWVSYW5nZXMubGVuZ3RoOyBpLS07KSB7XG4gICAgICB2YXIgY3VlUmFuZ2UgPSBjdWVSYW5nZXNbaV07XG4gICAgICB2YXIgb3ZlcmxhcCA9IGludGVyc2VjdGlvbihjdWVSYW5nZVswXSwgY3VlUmFuZ2VbMV0sIHN0YXJ0VGltZSwgZW5kVGltZSk7XG5cbiAgICAgIGlmIChvdmVybGFwID49IDApIHtcbiAgICAgICAgY3VlUmFuZ2VbMF0gPSBNYXRoLm1pbihjdWVSYW5nZVswXSwgc3RhcnRUaW1lKTtcbiAgICAgICAgY3VlUmFuZ2VbMV0gPSBNYXRoLm1heChjdWVSYW5nZVsxXSwgZW5kVGltZSk7XG4gICAgICAgIG1lcmdlZCA9IHRydWU7XG5cbiAgICAgICAgaWYgKG92ZXJsYXAgLyAoZW5kVGltZSAtIHN0YXJ0VGltZSkgPiAwLjUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIW1lcmdlZCkge1xuICAgICAgY3VlUmFuZ2VzLnB1c2goW3N0YXJ0VGltZSwgZW5kVGltZV0pO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmNvbmZpZy5yZW5kZXJUZXh0VHJhY2tzTmF0aXZlbHkpIHtcbiAgICAgIHRoaXMuQ3Vlcy5uZXdDdWUodGhpcy5jYXB0aW9uc1RyYWNrc1t0cmFja05hbWVdLCBzdGFydFRpbWUsIGVuZFRpbWUsIHNjcmVlbik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciBjdWVzID0gdGhpcy5DdWVzLm5ld0N1ZShudWxsLCBzdGFydFRpbWUsIGVuZFRpbWUsIHNjcmVlbik7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQ1VFU19QQVJTRUQsIHtcbiAgICAgICAgdHlwZTogJ2NhcHRpb25zJyxcbiAgICAgICAgY3VlczogY3VlcyxcbiAgICAgICAgdHJhY2s6IHRyYWNrTmFtZVxuICAgICAgfSk7XG4gICAgfVxuICB9IC8vIFRyaWdnZXJlZCB3aGVuIGFuIGluaXRpYWwgUFRTIGlzIGZvdW5kOyB1c2VkIGZvciBzeW5jaHJvbmlzYXRpb24gb2YgV2ViVlRULlxuICA7XG5cbiAgX3Byb3RvLm9uSW5pdFB0c0ZvdW5kID0gZnVuY3Rpb24gb25Jbml0UHRzRm91bmQoZGF0YSkge1xuICAgIHZhciBfdGhpczIgPSB0aGlzO1xuXG4gICAgdmFyIGZyYWcgPSBkYXRhLmZyYWcsXG4gICAgICAgIGlkID0gZGF0YS5pZCxcbiAgICAgICAgaW5pdFBUUyA9IGRhdGEuaW5pdFBUUztcbiAgICB2YXIgdW5wYXJzZWRWdHRGcmFncyA9IHRoaXMudW5wYXJzZWRWdHRGcmFncztcblxuICAgIGlmIChpZCA9PT0gJ21haW4nKSB7XG4gICAgICB0aGlzLmluaXRQVFNbZnJhZy5jY10gPSBpbml0UFRTO1xuICAgIH0gLy8gRHVlIHRvIGFzeW5jaHJvbm91cyBwcm9jZXNzaW5nLCBpbml0aWFsIFBUUyBtYXkgYXJyaXZlIGxhdGVyIHRoYW4gdGhlIGZpcnN0IFZUVCBmcmFnbWVudHMgYXJlIGxvYWRlZC5cbiAgICAvLyBQYXJzZSBhbnkgdW5wYXJzZWQgZnJhZ21lbnRzIHVwb24gcmVjZWl2aW5nIHRoZSBpbml0aWFsIFBUUy5cblxuXG4gICAgaWYgKHVucGFyc2VkVnR0RnJhZ3MubGVuZ3RoKSB7XG4gICAgICB0aGlzLnVucGFyc2VkVnR0RnJhZ3MgPSBbXTtcbiAgICAgIHVucGFyc2VkVnR0RnJhZ3MuZm9yRWFjaChmdW5jdGlvbiAoZnJhZykge1xuICAgICAgICBfdGhpczIub25GcmFnTG9hZGVkKGZyYWcpO1xuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5nZXRFeGlzdGluZ1RyYWNrID0gZnVuY3Rpb24gZ2V0RXhpc3RpbmdUcmFjayh0cmFja05hbWUpIHtcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhO1xuXG4gICAgaWYgKG1lZGlhKSB7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IG1lZGlhLnRleHRUcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIHRleHRUcmFjayA9IG1lZGlhLnRleHRUcmFja3NbaV07XG5cbiAgICAgICAgaWYgKHRleHRUcmFja1t0cmFja05hbWVdKSB7XG4gICAgICAgICAgcmV0dXJuIHRleHRUcmFjaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBudWxsO1xuICB9O1xuXG4gIF9wcm90by5jcmVhdGVDYXB0aW9uc1RyYWNrID0gZnVuY3Rpb24gY3JlYXRlQ2FwdGlvbnNUcmFjayh0cmFja05hbWUpIHtcbiAgICBpZiAodGhpcy5jb25maWcucmVuZGVyVGV4dFRyYWNrc05hdGl2ZWx5KSB7XG4gICAgICB0aGlzLmNyZWF0ZU5hdGl2ZVRyYWNrKHRyYWNrTmFtZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY3JlYXRlTm9uTmF0aXZlVHJhY2sodHJhY2tOYW1lKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLmNyZWF0ZU5hdGl2ZVRyYWNrID0gZnVuY3Rpb24gY3JlYXRlTmF0aXZlVHJhY2sodHJhY2tOYW1lKSB7XG4gICAgaWYgKHRoaXMuY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBjYXB0aW9uc1Byb3BlcnRpZXMgPSB0aGlzLmNhcHRpb25zUHJvcGVydGllcyxcbiAgICAgICAgY2FwdGlvbnNUcmFja3MgPSB0aGlzLmNhcHRpb25zVHJhY2tzLFxuICAgICAgICBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgdmFyIF9jYXB0aW9uc1Byb3BlcnRpZXMkdCA9IGNhcHRpb25zUHJvcGVydGllc1t0cmFja05hbWVdLFxuICAgICAgICBsYWJlbCA9IF9jYXB0aW9uc1Byb3BlcnRpZXMkdC5sYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlID0gX2NhcHRpb25zUHJvcGVydGllcyR0Lmxhbmd1YWdlQ29kZTsgLy8gRW5hYmxlIHJldXNlIG9mIGV4aXN0aW5nIHRleHQgdHJhY2suXG5cbiAgICB2YXIgZXhpc3RpbmdUcmFjayA9IHRoaXMuZ2V0RXhpc3RpbmdUcmFjayh0cmFja05hbWUpO1xuXG4gICAgaWYgKCFleGlzdGluZ1RyYWNrKSB7XG4gICAgICB2YXIgdGV4dFRyYWNrID0gdGhpcy5jcmVhdGVUZXh0VHJhY2soJ2NhcHRpb25zJywgbGFiZWwsIGxhbmd1YWdlQ29kZSk7XG5cbiAgICAgIGlmICh0ZXh0VHJhY2spIHtcbiAgICAgICAgLy8gU2V0IGEgc3BlY2lhbCBwcm9wZXJ0eSBvbiB0aGUgdHJhY2sgc28gd2Uga25vdyBpdCdzIG1hbmFnZWQgYnkgSGxzLmpzXG4gICAgICAgIHRleHRUcmFja1t0cmFja05hbWVdID0gdHJ1ZTtcbiAgICAgICAgY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSA9IHRleHRUcmFjaztcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSA9IGV4aXN0aW5nVHJhY2s7XG4gICAgICBjbGVhckN1cnJlbnRDdWVzKGNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV0pO1xuICAgICAgc2VuZEFkZFRyYWNrRXZlbnQoY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSwgbWVkaWEpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uY3JlYXRlTm9uTmF0aXZlVHJhY2sgPSBmdW5jdGlvbiBjcmVhdGVOb25OYXRpdmVUcmFjayh0cmFja05hbWUpIHtcbiAgICBpZiAodGhpcy5ub25OYXRpdmVDYXB0aW9uc1RyYWNrc1t0cmFja05hbWVdKSB7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBDcmVhdGUgYSBsaXN0IG9mIGEgc2luZ2xlIHRyYWNrIGZvciB0aGUgcHJvdmlkZXIgdG8gY29uc3VtZVxuXG5cbiAgICB2YXIgdHJhY2tQcm9wZXJ0aWVzID0gdGhpcy5jYXB0aW9uc1Byb3BlcnRpZXNbdHJhY2tOYW1lXTtcblxuICAgIGlmICghdHJhY2tQcm9wZXJ0aWVzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGxhYmVsID0gdHJhY2tQcm9wZXJ0aWVzLmxhYmVsO1xuICAgIHZhciB0cmFjayA9IHtcbiAgICAgIF9pZDogdHJhY2tOYW1lLFxuICAgICAgbGFiZWw6IGxhYmVsLFxuICAgICAga2luZDogJ2NhcHRpb25zJyxcbiAgICAgIGRlZmF1bHQ6IHRyYWNrUHJvcGVydGllcy5tZWRpYSA/ICEhdHJhY2tQcm9wZXJ0aWVzLm1lZGlhLmRlZmF1bHQgOiBmYWxzZSxcbiAgICAgIGNsb3NlZENhcHRpb25zOiB0cmFja1Byb3BlcnRpZXMubWVkaWFcbiAgICB9O1xuICAgIHRoaXMubm9uTmF0aXZlQ2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSA9IHRyYWNrO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5OT05fTkFUSVZFX1RFWFRfVFJBQ0tTX0ZPVU5ELCB7XG4gICAgICB0cmFja3M6IFt0cmFja11cbiAgICB9KTtcbiAgfTtcblxuICBfcHJvdG8uY3JlYXRlVGV4dFRyYWNrID0gZnVuY3Rpb24gY3JlYXRlVGV4dFRyYWNrKGtpbmQsIGxhYmVsLCBsYW5nKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcblxuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICByZXR1cm4gbWVkaWEuYWRkVGV4dFRyYWNrKGtpbmQsIGxhYmVsLCBsYW5nKTtcbiAgfTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gICAgX0V2ZW50SGFuZGxlci5wcm90b3R5cGUuZGVzdHJveS5jYWxsKHRoaXMpO1xuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhQXR0YWNoaW5nID0gZnVuY3Rpb24gb25NZWRpYUF0dGFjaGluZyhkYXRhKSB7XG4gICAgdGhpcy5tZWRpYSA9IGRhdGEubWVkaWE7XG5cbiAgICB0aGlzLl9jbGVhblRyYWNrcygpO1xuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhRGV0YWNoaW5nID0gZnVuY3Rpb24gb25NZWRpYURldGFjaGluZygpIHtcbiAgICB2YXIgY2FwdGlvbnNUcmFja3MgPSB0aGlzLmNhcHRpb25zVHJhY2tzO1xuICAgIE9iamVjdC5rZXlzKGNhcHRpb25zVHJhY2tzKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFja05hbWUpIHtcbiAgICAgIGNsZWFyQ3VycmVudEN1ZXMoY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSk7XG4gICAgICBkZWxldGUgY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXTtcbiAgICB9KTtcbiAgICB0aGlzLm5vbk5hdGl2ZUNhcHRpb25zVHJhY2tzID0ge307XG4gIH07XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RMb2FkaW5nID0gZnVuY3Rpb24gb25NYW5pZmVzdExvYWRpbmcoKSB7XG4gICAgdGhpcy5sYXN0U24gPSAtMTsgLy8gRGV0ZWN0IGRpc2NvbnRpZ3VpdHkgaW4gZnJhZ21lbnQgcGFyc2luZ1xuXG4gICAgdGhpcy5wcmV2Q0MgPSAtMTtcbiAgICB0aGlzLnZ0dENDcyA9IG5ld1ZUVENDcygpOyAvLyBEZXRlY3QgZGlzY29udGludWl0eSBpbiBzdWJ0aXRsZSBtYW5pZmVzdHNcblxuICAgIHRoaXMuX2NsZWFuVHJhY2tzKCk7XG5cbiAgICB0aGlzLnRyYWNrcyA9IFtdO1xuICAgIHRoaXMuY2FwdGlvbnNUcmFja3MgPSB7fTtcbiAgICB0aGlzLm5vbk5hdGl2ZUNhcHRpb25zVHJhY2tzID0ge307XG4gIH07XG5cbiAgX3Byb3RvLl9jbGVhblRyYWNrcyA9IGZ1bmN0aW9uIF9jbGVhblRyYWNrcygpIHtcbiAgICAvLyBjbGVhciBvdXRkYXRlZCBzdWJ0aXRsZXNcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhO1xuXG4gICAgaWYgKCFtZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciB0ZXh0VHJhY2tzID0gbWVkaWEudGV4dFRyYWNrcztcblxuICAgIGlmICh0ZXh0VHJhY2tzKSB7XG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHRleHRUcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY2xlYXJDdXJyZW50Q3Vlcyh0ZXh0VHJhY2tzW2ldKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTWFuaWZlc3RMb2FkZWQgPSBmdW5jdGlvbiBvbk1hbmlmZXN0TG9hZGVkKGRhdGEpIHtcbiAgICB2YXIgX3RoaXMzID0gdGhpcztcblxuICAgIHRoaXMudGV4dFRyYWNrcyA9IFtdO1xuICAgIHRoaXMudW5wYXJzZWRWdHRGcmFncyA9IHRoaXMudW5wYXJzZWRWdHRGcmFncyB8fCBbXTtcbiAgICB0aGlzLmluaXRQVFMgPSBbXTtcblxuICAgIGlmICh0aGlzLmNlYTYwOFBhcnNlcjEgJiYgdGhpcy5jZWE2MDhQYXJzZXIyKSB7XG4gICAgICB0aGlzLmNlYTYwOFBhcnNlcjEucmVzZXQoKTtcbiAgICAgIHRoaXMuY2VhNjA4UGFyc2VyMi5yZXNldCgpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmNvbmZpZy5lbmFibGVXZWJWVFQpIHtcbiAgICAgIHZhciB0cmFja3MgPSBkYXRhLnN1YnRpdGxlcyB8fCBbXTtcbiAgICAgIHZhciBzYW1lVHJhY2tzID0gdGhpcy50cmFja3MgJiYgdHJhY2tzICYmIHRoaXMudHJhY2tzLmxlbmd0aCA9PT0gdHJhY2tzLmxlbmd0aDtcbiAgICAgIHRoaXMudHJhY2tzID0gZGF0YS5zdWJ0aXRsZXMgfHwgW107XG5cbiAgICAgIGlmICh0aGlzLmNvbmZpZy5yZW5kZXJUZXh0VHJhY2tzTmF0aXZlbHkpIHtcbiAgICAgICAgdmFyIGluVXNlVHJhY2tzID0gdGhpcy5tZWRpYSA/IHRoaXMubWVkaWEudGV4dFRyYWNrcyA6IFtdO1xuICAgICAgICB0aGlzLnRyYWNrcy5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaywgaW5kZXgpIHtcbiAgICAgICAgICB2YXIgdGV4dFRyYWNrO1xuXG4gICAgICAgICAgaWYgKGluZGV4IDwgaW5Vc2VUcmFja3MubGVuZ3RoKSB7XG4gICAgICAgICAgICB2YXIgaW5Vc2VUcmFjayA9IG51bGw7XG5cbiAgICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgaW5Vc2VUcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgaWYgKGNhblJldXNlVnR0VGV4dFRyYWNrKGluVXNlVHJhY2tzW2ldLCB0cmFjaykpIHtcbiAgICAgICAgICAgICAgICBpblVzZVRyYWNrID0gaW5Vc2VUcmFja3NbaV07XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gLy8gUmV1c2UgdHJhY2tzIHdpdGggdGhlIHNhbWUgbGFiZWwsIGJ1dCBkbyBub3QgcmV1c2UgNjA4LzcwOCB0cmFja3NcblxuXG4gICAgICAgICAgICBpZiAoaW5Vc2VUcmFjaykge1xuICAgICAgICAgICAgICB0ZXh0VHJhY2sgPSBpblVzZVRyYWNrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmICghdGV4dFRyYWNrKSB7XG4gICAgICAgICAgICB0ZXh0VHJhY2sgPSBfdGhpczMuY3JlYXRlVGV4dFRyYWNrKCdzdWJ0aXRsZXMnLCB0cmFjay5uYW1lLCB0cmFjay5sYW5nKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAodHJhY2suZGVmYXVsdCkge1xuICAgICAgICAgICAgdGV4dFRyYWNrLm1vZGUgPSBfdGhpczMuaGxzLnN1YnRpdGxlRGlzcGxheSA/ICdzaG93aW5nJyA6ICdoaWRkZW4nO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0ZXh0VHJhY2subW9kZSA9ICdkaXNhYmxlZCc7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgX3RoaXMzLnRleHRUcmFja3MucHVzaCh0ZXh0VHJhY2spO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAoIXNhbWVUcmFja3MgJiYgdGhpcy50cmFja3MgJiYgdGhpcy50cmFja3MubGVuZ3RoKSB7XG4gICAgICAgIC8vIENyZWF0ZSBhIGxpc3Qgb2YgdHJhY2tzIGZvciB0aGUgcHJvdmlkZXIgdG8gY29uc3VtZVxuICAgICAgICB2YXIgdHJhY2tzTGlzdCA9IHRoaXMudHJhY2tzLm1hcChmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgbGFiZWw6IHRyYWNrLm5hbWUsXG4gICAgICAgICAgICBraW5kOiB0cmFjay50eXBlLnRvTG93ZXJDYXNlKCksXG4gICAgICAgICAgICBkZWZhdWx0OiB0cmFjay5kZWZhdWx0LFxuICAgICAgICAgICAgc3VidGl0bGVUcmFjazogdHJhY2tcbiAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLk5PTl9OQVRJVkVfVEVYVF9UUkFDS1NfRk9VTkQsIHtcbiAgICAgICAgICB0cmFja3M6IHRyYWNrc0xpc3RcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuY29uZmlnLmVuYWJsZUNFQTcwOENhcHRpb25zICYmIGRhdGEuY2FwdGlvbnMpIHtcbiAgICAgIGRhdGEuY2FwdGlvbnMuZm9yRWFjaChmdW5jdGlvbiAoY2FwdGlvbnNUcmFjaykge1xuICAgICAgICB2YXIgaW5zdHJlYW1JZE1hdGNoID0gLyg/OkNDfFNFUlZJQ0UpKFsxLTRdKS8uZXhlYyhjYXB0aW9uc1RyYWNrLmluc3RyZWFtSWQpO1xuXG4gICAgICAgIGlmICghaW5zdHJlYW1JZE1hdGNoKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHRyYWNrTmFtZSA9IFwidGV4dFRyYWNrXCIgKyBpbnN0cmVhbUlkTWF0Y2hbMV07XG4gICAgICAgIHZhciB0cmFja1Byb3BlcnRpZXMgPSBfdGhpczMuY2FwdGlvbnNQcm9wZXJ0aWVzW3RyYWNrTmFtZV07XG5cbiAgICAgICAgaWYgKCF0cmFja1Byb3BlcnRpZXMpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0cmFja1Byb3BlcnRpZXMubGFiZWwgPSBjYXB0aW9uc1RyYWNrLm5hbWU7XG5cbiAgICAgICAgaWYgKGNhcHRpb25zVHJhY2subGFuZykge1xuICAgICAgICAgIC8vIG9wdGlvbmFsIGF0dHJpYnV0ZVxuICAgICAgICAgIHRyYWNrUHJvcGVydGllcy5sYW5ndWFnZUNvZGUgPSBjYXB0aW9uc1RyYWNrLmxhbmc7XG4gICAgICAgIH1cblxuICAgICAgICB0cmFja1Byb3BlcnRpZXMubWVkaWEgPSBjYXB0aW9uc1RyYWNrO1xuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkZyYWdMb2FkZWQgPSBmdW5jdGlvbiBvbkZyYWdMb2FkZWQoZGF0YSkge1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnLFxuICAgICAgICBwYXlsb2FkID0gZGF0YS5wYXlsb2FkO1xuICAgIHZhciBjZWE2MDhQYXJzZXIxID0gdGhpcy5jZWE2MDhQYXJzZXIxLFxuICAgICAgICBjZWE2MDhQYXJzZXIyID0gdGhpcy5jZWE2MDhQYXJzZXIyLFxuICAgICAgICBpbml0UFRTID0gdGhpcy5pbml0UFRTLFxuICAgICAgICBsYXN0U24gPSB0aGlzLmxhc3RTbixcbiAgICAgICAgdW5wYXJzZWRWdHRGcmFncyA9IHRoaXMudW5wYXJzZWRWdHRGcmFncztcblxuICAgIGlmIChmcmFnLnR5cGUgPT09ICdtYWluJykge1xuICAgICAgdmFyIHNuID0gZnJhZy5zbjsgLy8gaWYgdGhpcyBmcmFnIGlzbid0IGNvbnRpZ3VvdXMsIGNsZWFyIHRoZSBwYXJzZXIgc28gY3VlcyB3aXRoIGJhZCBzdGFydC9lbmQgdGltZXMgYXJlbid0IGFkZGVkIHRvIHRoZSB0ZXh0VHJhY2tcblxuICAgICAgaWYgKGZyYWcuc24gIT09IGxhc3RTbiArIDEpIHtcbiAgICAgICAgaWYgKGNlYTYwOFBhcnNlcjEgJiYgY2VhNjA4UGFyc2VyMikge1xuICAgICAgICAgIGNlYTYwOFBhcnNlcjEucmVzZXQoKTtcbiAgICAgICAgICBjZWE2MDhQYXJzZXIyLnJlc2V0KCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdGhpcy5sYXN0U24gPSBzbjtcbiAgICB9IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgYnJhY2Utc3R5bGVcbiAgICAvLyBJZiBmcmFnbWVudCBpcyBzdWJ0aXRsZSB0eXBlLCBwYXJzZSBhcyBXZWJWVFQuXG4gICAgZWxzZSBpZiAoZnJhZy50eXBlID09PSAnc3VidGl0bGUnKSB7XG4gICAgICAgIGlmIChwYXlsb2FkLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgICAvLyBXZSBuZWVkIGFuIGluaXRpYWwgc3luY2hyb25pc2F0aW9uIFBUUy4gU3RvcmUgZnJhZ21lbnRzIGFzIGxvbmcgYXMgbm9uZSBoYXMgYXJyaXZlZC5cbiAgICAgICAgICBpZiAoIU9iamVjdChudW1iZXJbXCJpc0Zpbml0ZU51bWJlclwiXSkoaW5pdFBUU1tmcmFnLmNjXSkpIHtcbiAgICAgICAgICAgIHVucGFyc2VkVnR0RnJhZ3MucHVzaChkYXRhKTtcblxuICAgICAgICAgICAgaWYgKGluaXRQVFMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgIC8vIGZpbmlzaCB1bnN1Y2Nlc3NmdWxseSwgb3RoZXJ3aXNlIHRoZSBzdWJ0aXRsZS1zdHJlYW0tY29udHJvbGxlciBjb3VsZCBiZSBibG9ja2VkIGZyb20gbG9hZGluZyBuZXcgZnJhZ3MuXG4gICAgICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5TVUJUSVRMRV9GUkFHX1BST0NFU1NFRCwge1xuICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIGZyYWc6IGZyYWdcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB2YXIgZGVjcnlwdERhdGEgPSBmcmFnLmRlY3J5cHRkYXRhOyAvLyBJZiB0aGUgc3VidGl0bGVzIGFyZSBub3QgZW5jcnlwdGVkLCBwYXJzZSBWVFRzIG5vdy4gT3RoZXJ3aXNlLCB3ZSBuZWVkIHRvIHdhaXQuXG5cbiAgICAgICAgICBpZiAoZGVjcnlwdERhdGEgPT0gbnVsbCB8fCBkZWNyeXB0RGF0YS5rZXkgPT0gbnVsbCB8fCBkZWNyeXB0RGF0YS5tZXRob2QgIT09ICdBRVMtMTI4Jykge1xuICAgICAgICAgICAgdGhpcy5fcGFyc2VWVFRzKGZyYWcsIHBheWxvYWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBJbiBjYXNlIHRoZXJlIGlzIG5vIHBheWxvYWQsIGZpbmlzaCB1bnN1Y2Nlc3NmdWxseS5cbiAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZnJhZzogZnJhZ1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gIH07XG5cbiAgX3Byb3RvLl9wYXJzZVZUVHMgPSBmdW5jdGlvbiBfcGFyc2VWVFRzKGZyYWcsIHBheWxvYWQpIHtcbiAgICB2YXIgX3RoaXM0ID0gdGhpcztcblxuICAgIHZhciBobHMgPSB0aGlzLmhscyxcbiAgICAgICAgcHJldkNDID0gdGhpcy5wcmV2Q0MsXG4gICAgICAgIHRleHRUcmFja3MgPSB0aGlzLnRleHRUcmFja3MsXG4gICAgICAgIHZ0dENDcyA9IHRoaXMudnR0Q0NzO1xuXG4gICAgaWYgKCF2dHRDQ3NbZnJhZy5jY10pIHtcbiAgICAgIHZ0dENDc1tmcmFnLmNjXSA9IHtcbiAgICAgICAgc3RhcnQ6IGZyYWcuc3RhcnQsXG4gICAgICAgIHByZXZDQzogcHJldkNDLFxuICAgICAgICBuZXc6IHRydWVcbiAgICAgIH07XG4gICAgICB0aGlzLnByZXZDQyA9IGZyYWcuY2M7XG4gICAgfSAvLyBQYXJzZSB0aGUgV2ViVlRUIGZpbGUgY29udGVudHMuXG5cblxuICAgIHdlYnZ0dF9wYXJzZXIucGFyc2UocGF5bG9hZCwgdGhpcy5pbml0UFRTW2ZyYWcuY2NdLCB2dHRDQ3MsIGZyYWcuY2MsIGZ1bmN0aW9uIChjdWVzKSB7XG4gICAgICBpZiAoX3RoaXM0LmNvbmZpZy5yZW5kZXJUZXh0VHJhY2tzTmF0aXZlbHkpIHtcbiAgICAgICAgdmFyIGN1cnJlbnRUcmFjayA9IHRleHRUcmFja3NbZnJhZy5sZXZlbF07IC8vIFdlYlZUVFBhcnNlci5wYXJzZSBpcyBhbiBhc3luYyBtZXRob2QgYW5kIGlmIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgdGV4dCB0cmFjayBtb2RlIGlzIHNldCB0byBcImRpc2FibGVkXCJcbiAgICAgICAgLy8gYmVmb3JlIHBhcnNpbmcgaXMgZG9uZSB0aGVuIGRvbid0IHRyeSB0byBhY2Nlc3MgY3VycmVudFRyYWNrLmN1ZXMuZ2V0Q3VlQnlJZCBhcyBjdWVzIHdpbGwgYmUgbnVsbFxuICAgICAgICAvLyBhbmQgdHJ5aW5nIHRvIGFjY2VzcyBnZXRDdWVCeUlkIG1ldGhvZCBvZiBjdWVzIHdpbGwgdGhyb3cgYW4gZXhjZXB0aW9uXG4gICAgICAgIC8vIEJlY2F1c2Ugd2UgY2hlY2sgaWYgdGhlIG1vZGUgaXMgZGlhYmxlZCwgd2UgY2FuIGZvcmNlIGNoZWNrIGBjdWVzYCBiZWxvdy4gVGhleSBjYW4ndCBiZSBudWxsLlxuXG4gICAgICAgIGlmIChjdXJyZW50VHJhY2subW9kZSA9PT0gJ2Rpc2FibGVkJykge1xuICAgICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgZnJhZzogZnJhZ1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSAvLyBBZGQgY3VlcyBhbmQgdHJpZ2dlciBldmVudCB3aXRoIHN1Y2Nlc3MgdHJ1ZS5cblxuXG4gICAgICAgIGN1ZXMuZm9yRWFjaChmdW5jdGlvbiAoY3VlKSB7XG4gICAgICAgICAgLy8gU29tZXRpbWVzIHRoZXJlIGFyZSBjdWUgb3ZlcmxhcHMgb24gc2VnbWVudGVkIHZ0dHMgc28gdGhlIHNhbWVcbiAgICAgICAgICAvLyBjdWUgY2FuIGFwcGVhciBtb3JlIHRoYW4gb25jZSBpbiBkaWZmZXJlbnQgdnR0IGZpbGVzLlxuICAgICAgICAgIC8vIFRoaXMgYXZvaWQgc2hvd2luZyBkdXBsaWNhdGVkIGN1ZXMgd2l0aCBzYW1lIHRpbWVjb2RlIGFuZCB0ZXh0LlxuICAgICAgICAgIGlmICghY3VycmVudFRyYWNrLmN1ZXMuZ2V0Q3VlQnlJZChjdWUuaWQpKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBjdXJyZW50VHJhY2suYWRkQ3VlKGN1ZSk7XG5cbiAgICAgICAgICAgICAgaWYgKCFjdXJyZW50VHJhY2suY3Vlcy5nZXRDdWVCeUlkKGN1ZS5pZCkpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJhZGRDdWUgaXMgZmFpbGVkIGZvcjogXCIgKyBjdWUpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmRlYnVnKFwiRmFpbGVkIG9jY3VycmVkIG9uIGFkZGluZyBjdWVzOiBcIiArIGVycik7XG4gICAgICAgICAgICAgIHZhciB0ZXh0VHJhY2tDdWUgPSBuZXcgd2luZG93LlRleHRUcmFja0N1ZShjdWUuc3RhcnRUaW1lLCBjdWUuZW5kVGltZSwgY3VlLnRleHQpO1xuICAgICAgICAgICAgICB0ZXh0VHJhY2tDdWUuaWQgPSBjdWUuaWQ7XG4gICAgICAgICAgICAgIGN1cnJlbnRUcmFjay5hZGRDdWUodGV4dFRyYWNrQ3VlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIHRyYWNrSWQgPSBfdGhpczQudHJhY2tzW2ZyYWcubGV2ZWxdLmRlZmF1bHQgPyAnZGVmYXVsdCcgOiAnc3VidGl0bGVzJyArIGZyYWcubGV2ZWw7XG4gICAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uQ1VFU19QQVJTRUQsIHtcbiAgICAgICAgICB0eXBlOiAnc3VidGl0bGVzJyxcbiAgICAgICAgICBjdWVzOiBjdWVzLFxuICAgICAgICAgIHRyYWNrOiB0cmFja0lkXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBobHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLlNVQlRJVExFX0ZSQUdfUFJPQ0VTU0VELCB7XG4gICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgIGZyYWc6IGZyYWdcbiAgICAgIH0pO1xuICAgIH0sIGZ1bmN0aW9uIChlKSB7XG4gICAgICAvLyBTb21ldGhpbmcgd2VudCB3cm9uZyB3aGlsZSBwYXJzaW5nLiBUcmlnZ2VyIGV2ZW50IHdpdGggc3VjY2VzcyBmYWxzZS5cbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJGYWlsZWQgdG8gcGFyc2UgVlRUIGN1ZTogXCIgKyBlKTtcbiAgICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIGZyYWc6IGZyYWdcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90by5vbkZyYWdEZWNyeXB0ZWQgPSBmdW5jdGlvbiBvbkZyYWdEZWNyeXB0ZWQoZGF0YSkge1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnLFxuICAgICAgICBwYXlsb2FkID0gZGF0YS5wYXlsb2FkO1xuXG4gICAgaWYgKGZyYWcudHlwZSA9PT0gJ3N1YnRpdGxlJykge1xuICAgICAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKHRoaXMuaW5pdFBUU1tmcmFnLmNjXSkpIHtcbiAgICAgICAgdGhpcy51bnBhcnNlZFZ0dEZyYWdzLnB1c2goZGF0YSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5fcGFyc2VWVFRzKGZyYWcsIHBheWxvYWQpO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8ub25GcmFnUGFyc2luZ1VzZXJkYXRhID0gZnVuY3Rpb24gb25GcmFnUGFyc2luZ1VzZXJkYXRhKGRhdGEpIHtcbiAgICB2YXIgY2VhNjA4UGFyc2VyMSA9IHRoaXMuY2VhNjA4UGFyc2VyMSxcbiAgICAgICAgY2VhNjA4UGFyc2VyMiA9IHRoaXMuY2VhNjA4UGFyc2VyMjtcblxuICAgIGlmICghdGhpcy5lbmFibGVkIHx8ICEoY2VhNjA4UGFyc2VyMSAmJiBjZWE2MDhQYXJzZXIyKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gSWYgdGhlIGV2ZW50IGNvbnRhaW5zIGNhcHRpb25zIChmb3VuZCBpbiB0aGUgYnl0ZXMgcHJvcGVydHkpLCBwdXNoIGFsbCBieXRlcyBpbnRvIHRoZSBwYXJzZXIgaW1tZWRpYXRlbHlcbiAgICAvLyBJdCB3aWxsIGNyZWF0ZSB0aGUgcHJvcGVyIHRpbWVzdGFtcHMgYmFzZWQgb24gdGhlIFBUUyB2YWx1ZVxuXG5cbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGRhdGEuc2FtcGxlcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGNjQnl0ZXMgPSBkYXRhLnNhbXBsZXNbaV0uYnl0ZXM7XG5cbiAgICAgIGlmIChjY0J5dGVzKSB7XG4gICAgICAgIHZhciBjY2RhdGFzID0gdGhpcy5leHRyYWN0Q2VhNjA4RGF0YShjY0J5dGVzKTtcbiAgICAgICAgY2VhNjA4UGFyc2VyMS5hZGREYXRhKGRhdGEuc2FtcGxlc1tpXS5wdHMsIGNjZGF0YXNbMF0pO1xuICAgICAgICBjZWE2MDhQYXJzZXIyLmFkZERhdGEoZGF0YS5zYW1wbGVzW2ldLnB0cywgY2NkYXRhc1sxXSk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5leHRyYWN0Q2VhNjA4RGF0YSA9IGZ1bmN0aW9uIGV4dHJhY3RDZWE2MDhEYXRhKGJ5dGVBcnJheSkge1xuICAgIHZhciBjb3VudCA9IGJ5dGVBcnJheVswXSAmIDMxO1xuICAgIHZhciBwb3NpdGlvbiA9IDI7XG4gICAgdmFyIGFjdHVhbENDQnl0ZXMgPSBbW10sIFtdXTtcblxuICAgIGZvciAodmFyIGogPSAwOyBqIDwgY291bnQ7IGorKykge1xuICAgICAgdmFyIHRtcEJ5dGUgPSBieXRlQXJyYXlbcG9zaXRpb24rK107XG4gICAgICB2YXIgY2NieXRlMSA9IDB4N0YgJiBieXRlQXJyYXlbcG9zaXRpb24rK107XG4gICAgICB2YXIgY2NieXRlMiA9IDB4N0YgJiBieXRlQXJyYXlbcG9zaXRpb24rK107XG4gICAgICB2YXIgY2NWYWxpZCA9ICg0ICYgdG1wQnl0ZSkgIT09IDA7XG4gICAgICB2YXIgY2NUeXBlID0gMyAmIHRtcEJ5dGU7XG5cbiAgICAgIGlmIChjY2J5dGUxID09PSAwICYmIGNjYnl0ZTIgPT09IDApIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChjY1ZhbGlkKSB7XG4gICAgICAgIGlmIChjY1R5cGUgPT09IDAgfHwgY2NUeXBlID09PSAxKSB7XG4gICAgICAgICAgYWN0dWFsQ0NCeXRlc1tjY1R5cGVdLnB1c2goY2NieXRlMSk7XG4gICAgICAgICAgYWN0dWFsQ0NCeXRlc1tjY1R5cGVdLnB1c2goY2NieXRlMik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gYWN0dWFsQ0NCeXRlcztcbiAgfTtcblxuICByZXR1cm4gVGltZWxpbmVDb250cm9sbGVyO1xufShldmVudF9oYW5kbGVyKTtcblxuZnVuY3Rpb24gY2FuUmV1c2VWdHRUZXh0VHJhY2soaW5Vc2VUcmFjaywgbWFuaWZlc3RUcmFjaykge1xuICByZXR1cm4gaW5Vc2VUcmFjayAmJiBpblVzZVRyYWNrLmxhYmVsID09PSBtYW5pZmVzdFRyYWNrLm5hbWUgJiYgIShpblVzZVRyYWNrLnRleHRUcmFjazEgfHwgaW5Vc2VUcmFjay50ZXh0VHJhY2syKTtcbn1cblxuZnVuY3Rpb24gaW50ZXJzZWN0aW9uKHgxLCB4MiwgeTEsIHkyKSB7XG4gIHJldHVybiBNYXRoLm1pbih4MiwgeTIpIC0gTWF0aC5tYXgoeDEsIHkxKTtcbn1cblxuZnVuY3Rpb24gbmV3VlRUQ0NzKCkge1xuICByZXR1cm4ge1xuICAgIGNjT2Zmc2V0OiAwLFxuICAgIHByZXNlbnRhdGlvbk9mZnNldDogMCxcbiAgICAwOiB7XG4gICAgICBzdGFydDogMCxcbiAgICAgIHByZXZDQzogLTEsXG4gICAgICBuZXc6IGZhbHNlXG4gICAgfVxuICB9O1xufVxuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciB0aW1lbGluZV9jb250cm9sbGVyID0gKHRpbWVsaW5lX2NvbnRyb2xsZXJfVGltZWxpbmVDb250cm9sbGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvc3VidGl0bGUtdHJhY2stY29udHJvbGxlci5qc1xuXG5cblxuXG5mdW5jdGlvbiBzdWJ0aXRsZV90cmFja19jb250cm9sbGVyX2RlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfVxuXG5mdW5jdGlvbiBzdWJ0aXRsZV90cmFja19jb250cm9sbGVyX2NyZWF0ZUNsYXNzKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgc3VidGl0bGVfdHJhY2tfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgc3VidGl0bGVfdHJhY2tfY29udHJvbGxlcl9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfVxuXG5mdW5jdGlvbiBzdWJ0aXRsZV90cmFja19jb250cm9sbGVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cblxuXG5cblxuXG5cbnZhciBzdWJ0aXRsZV90cmFja19jb250cm9sbGVyX1N1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChfRXZlbnRIYW5kbGVyKSB7XG4gIHN1YnRpdGxlX3RyYWNrX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShTdWJ0aXRsZVRyYWNrQ29udHJvbGxlciwgX0V2ZW50SGFuZGxlcik7XG5cbiAgZnVuY3Rpb24gU3VidGl0bGVUcmFja0NvbnRyb2xsZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0FUVEFDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSElORywgZXZlbnRzW1wiZGVmYXVsdFwiXS5NQU5JRkVTVF9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfVFJBQ0tfTE9BREVEKSB8fCB0aGlzO1xuICAgIF90aGlzLnRyYWNrcyA9IFtdO1xuICAgIF90aGlzLnRyYWNrSWQgPSAtMTtcbiAgICBfdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgX3RoaXMuc3RvcHBlZCA9IHRydWU7XG4gICAgLyoqXG4gICAgICogQG1lbWJlciB7Ym9vbGVhbn0gc3VidGl0bGVEaXNwbGF5IEVuYWJsZS9kaXNhYmxlIHN1YnRpdGxlIGRpc3BsYXkgcmVuZGVyaW5nXG4gICAgICovXG5cbiAgICBfdGhpcy5zdWJ0aXRsZURpc3BsYXkgPSB0cnVlO1xuICAgIC8qKlxuICAgICAqIEtlZXBzIHJlZmVyZW5jZSB0byBhIGRlZmF1bHQgdHJhY2sgaWQgd2hlbiBtZWRpYSBoYXMgbm90IGJlZW4gYXR0YWNoZWQgeWV0XG4gICAgICogQG1lbWJlciB7bnVtYmVyfVxuICAgICAqL1xuXG4gICAgX3RoaXMucXVldWVkRGVmYXVsdFRyYWNrID0gbnVsbDtcbiAgICByZXR1cm4gX3RoaXM7XG4gIH1cblxuICB2YXIgX3Byb3RvID0gU3VidGl0bGVUcmFja0NvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5kZXN0cm95ID0gZnVuY3Rpb24gZGVzdHJveSgpIHtcbiAgICBldmVudF9oYW5kbGVyLnByb3RvdHlwZS5kZXN0cm95LmNhbGwodGhpcyk7XG4gIH0gLy8gTGlzdGVuIGZvciBzdWJ0aXRsZSB0cmFjayBjaGFuZ2UsIHRoZW4gZXh0cmFjdCB0aGUgY3VycmVudCB0cmFjayBJRC5cbiAgO1xuXG4gIF9wcm90by5vbk1lZGlhQXR0YWNoZWQgPSBmdW5jdGlvbiBvbk1lZGlhQXR0YWNoZWQoZGF0YSkge1xuICAgIHZhciBfdGhpczIgPSB0aGlzO1xuXG4gICAgdGhpcy5tZWRpYSA9IGRhdGEubWVkaWE7XG5cbiAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKSh0aGlzLnF1ZXVlZERlZmF1bHRUcmFjaykpIHtcbiAgICAgIHRoaXMuc3VidGl0bGVUcmFjayA9IHRoaXMucXVldWVkRGVmYXVsdFRyYWNrO1xuICAgICAgdGhpcy5xdWV1ZWREZWZhdWx0VHJhY2sgPSBudWxsO1xuICAgIH1cblxuICAgIHRoaXMudHJhY2tDaGFuZ2VMaXN0ZW5lciA9IHRoaXMuX29uVGV4dFRyYWNrc0NoYW5nZWQuYmluZCh0aGlzKTtcbiAgICB0aGlzLnVzZVRleHRUcmFja1BvbGxpbmcgPSAhKHRoaXMubWVkaWEudGV4dFRyYWNrcyAmJiAnb25jaGFuZ2UnIGluIHRoaXMubWVkaWEudGV4dFRyYWNrcyk7XG5cbiAgICBpZiAodGhpcy51c2VUZXh0VHJhY2tQb2xsaW5nKSB7XG4gICAgICB0aGlzLnN1YnRpdGxlUG9sbGluZ0ludGVydmFsID0gc2V0SW50ZXJ2YWwoZnVuY3Rpb24gKCkge1xuICAgICAgICBfdGhpczIudHJhY2tDaGFuZ2VMaXN0ZW5lcigpO1xuICAgICAgfSwgNTAwKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5tZWRpYS50ZXh0VHJhY2tzLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHRoaXMudHJhY2tDaGFuZ2VMaXN0ZW5lcik7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhRGV0YWNoaW5nID0gZnVuY3Rpb24gb25NZWRpYURldGFjaGluZygpIHtcbiAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy51c2VUZXh0VHJhY2tQb2xsaW5nKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuc3VidGl0bGVQb2xsaW5nSW50ZXJ2YWwpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLm1lZGlhLnRleHRUcmFja3MucmVtb3ZlRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgdGhpcy50cmFja0NoYW5nZUxpc3RlbmVyKTtcbiAgICB9XG5cbiAgICBpZiAoT2JqZWN0KG51bWJlcltcImlzRmluaXRlTnVtYmVyXCJdKSh0aGlzLnN1YnRpdGxlVHJhY2spKSB7XG4gICAgICB0aGlzLnF1ZXVlZERlZmF1bHRUcmFjayA9IHRoaXMuc3VidGl0bGVUcmFjaztcbiAgICB9XG5cbiAgICB2YXIgdGV4dFRyYWNrcyA9IGZpbHRlclN1YnRpdGxlVHJhY2tzKHRoaXMubWVkaWEudGV4dFRyYWNrcyk7IC8vIENsZWFyIGxvYWRlZCBjdWVzIG9uIG1lZGlhIGRldGFjaG1lbnQgZnJvbSB0cmFja3NcblxuICAgIHRleHRUcmFja3MuZm9yRWFjaChmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgIGNsZWFyQ3VycmVudEN1ZXModHJhY2spO1xuICAgIH0pOyAvLyBEaXNhYmxlIGFsbCBzdWJ0aXRsZSB0cmFja3MgYmVmb3JlIGRldGFjaG1lbnQgc28gd2hlbiByZWF0dGFjaGVkIG9ubHkgdHJhY2tzIGluIHRoYXQgY29udGVudCBhcmUgZW5hYmxlZC5cblxuICAgIHRoaXMuc3VidGl0bGVUcmFjayA9IC0xO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICB9IC8vIEZpcmVkIHdoZW5ldmVyIGEgbmV3IG1hbmlmZXN0IGlzIGxvYWRlZC5cbiAgO1xuXG4gIF9wcm90by5vbk1hbmlmZXN0TG9hZGVkID0gZnVuY3Rpb24gb25NYW5pZmVzdExvYWRlZChkYXRhKSB7XG4gICAgdmFyIF90aGlzMyA9IHRoaXM7XG5cbiAgICB2YXIgdHJhY2tzID0gZGF0YS5zdWJ0aXRsZXMgfHwgW107XG4gICAgdGhpcy50cmFja3MgPSB0cmFja3M7XG4gICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLlNVQlRJVExFX1RSQUNLU19VUERBVEVELCB7XG4gICAgICBzdWJ0aXRsZVRyYWNrczogdHJhY2tzXG4gICAgfSk7IC8vIGxvb3AgdGhyb3VnaCBhdmFpbGFibGUgc3VidGl0bGUgdHJhY2tzIGFuZCBhdXRvc2VsZWN0IGRlZmF1bHQgaWYgbmVlZGVkXG4gICAgLy8gVE9ETzogaW1wcm92ZSBzZWxlY3Rpb24gbG9naWMgdG8gaGFuZGxlIGZvcmNlZCwgZXRjXG5cbiAgICB0cmFja3MuZm9yRWFjaChmdW5jdGlvbiAodHJhY2spIHtcbiAgICAgIGlmICh0cmFjay5kZWZhdWx0KSB7XG4gICAgICAgIC8vIHNldHRpbmcgdGhpcy5zdWJ0aXRsZVRyYWNrIHdpbGwgdHJpZ2dlciBpbnRlcm5hbCBsb2dpY1xuICAgICAgICAvLyBpZiBtZWRpYSBoYXMgbm90IGJlZW4gYXR0YWNoZWQgeWV0LCBpdCB3aWxsIGZhaWxcbiAgICAgICAgLy8gd2Uga2VlcCBhIHJlZmVyZW5jZSB0byB0aGUgZGVmYXVsdCB0cmFjayBpZFxuICAgICAgICAvLyBhbmQgd2UnbGwgc2V0IHN1YnRpdGxlVHJhY2sgd2hlbiBvbk1lZGlhQXR0YWNoZWQgaXMgdHJpZ2dlcmVkXG4gICAgICAgIGlmIChfdGhpczMubWVkaWEpIHtcbiAgICAgICAgICBfdGhpczMuc3VidGl0bGVUcmFjayA9IHRyYWNrLmlkO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIF90aGlzMy5xdWV1ZWREZWZhdWx0VHJhY2sgPSB0cmFjay5pZDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9O1xuXG4gIF9wcm90by5vblN1YnRpdGxlVHJhY2tMb2FkZWQgPSBmdW5jdGlvbiBvblN1YnRpdGxlVHJhY2tMb2FkZWQoZGF0YSkge1xuICAgIHZhciBfdGhpczQgPSB0aGlzO1xuXG4gICAgdmFyIGlkID0gZGF0YS5pZCxcbiAgICAgICAgZGV0YWlscyA9IGRhdGEuZGV0YWlscztcbiAgICB2YXIgdHJhY2tJZCA9IHRoaXMudHJhY2tJZCxcbiAgICAgICAgdHJhY2tzID0gdGhpcy50cmFja3M7XG4gICAgdmFyIGN1cnJlbnRUcmFjayA9IHRyYWNrc1t0cmFja0lkXTtcblxuICAgIGlmIChpZCA+PSB0cmFja3MubGVuZ3RoIHx8IGlkICE9PSB0cmFja0lkIHx8ICFjdXJyZW50VHJhY2sgfHwgdGhpcy5zdG9wcGVkKSB7XG4gICAgICB0aGlzLl9jbGVhclJlbG9hZFRpbWVyKCk7XG5cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwic3VidGl0bGUgdHJhY2sgXCIgKyBpZCArIFwiIGxvYWRlZFwiKTtcblxuICAgIGlmIChkZXRhaWxzLmxpdmUpIHtcbiAgICAgIHZhciByZWxvYWRJbnRlcnZhbCA9IGNvbXB1dGVSZWxvYWRJbnRlcnZhbChjdXJyZW50VHJhY2suZGV0YWlscywgZGV0YWlscywgZGF0YS5zdGF0cy50cmVxdWVzdCk7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiUmVsb2FkaW5nIGxpdmUgc3VidGl0bGUgcGxheWxpc3QgaW4gXCIgKyByZWxvYWRJbnRlcnZhbCArIFwibXNcIik7XG4gICAgICB0aGlzLnRpbWVyID0gc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG4gICAgICAgIF90aGlzNC5fbG9hZEN1cnJlbnRUcmFjaygpO1xuICAgICAgfSwgcmVsb2FkSW50ZXJ2YWwpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9jbGVhclJlbG9hZFRpbWVyKCk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5zdGFydExvYWQgPSBmdW5jdGlvbiBzdGFydExvYWQoKSB7XG4gICAgdGhpcy5zdG9wcGVkID0gZmFsc2U7XG5cbiAgICB0aGlzLl9sb2FkQ3VycmVudFRyYWNrKCk7XG4gIH07XG5cbiAgX3Byb3RvLnN0b3BMb2FkID0gZnVuY3Rpb24gc3RvcExvYWQoKSB7XG4gICAgdGhpcy5zdG9wcGVkID0gdHJ1ZTtcblxuICAgIHRoaXMuX2NsZWFyUmVsb2FkVGltZXIoKTtcbiAgfVxuICAvKiogZ2V0IGFsdGVybmF0ZSBzdWJ0aXRsZSB0cmFja3MgbGlzdCBmcm9tIHBsYXlsaXN0ICoqL1xuICA7XG5cbiAgX3Byb3RvLl9jbGVhclJlbG9hZFRpbWVyID0gZnVuY3Rpb24gX2NsZWFyUmVsb2FkVGltZXIoKSB7XG4gICAgaWYgKHRoaXMudGltZXIpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aGlzLnRpbWVyKTtcbiAgICAgIHRoaXMudGltZXIgPSBudWxsO1xuICAgIH1cbiAgfTtcblxuICBfcHJvdG8uX2xvYWRDdXJyZW50VHJhY2sgPSBmdW5jdGlvbiBfbG9hZEN1cnJlbnRUcmFjaygpIHtcbiAgICB2YXIgdHJhY2tJZCA9IHRoaXMudHJhY2tJZCxcbiAgICAgICAgdHJhY2tzID0gdGhpcy50cmFja3MsXG4gICAgICAgIGhscyA9IHRoaXMuaGxzO1xuICAgIHZhciBjdXJyZW50VHJhY2sgPSB0cmFja3NbdHJhY2tJZF07XG5cbiAgICBpZiAodHJhY2tJZCA8IDAgfHwgIWN1cnJlbnRUcmFjayB8fCBjdXJyZW50VHJhY2suZGV0YWlscyAmJiAhY3VycmVudFRyYWNrLmRldGFpbHMubGl2ZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJMb2FkaW5nIHN1YnRpdGxlIHRyYWNrIFwiICsgdHJhY2tJZCk7XG4gICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5TVUJUSVRMRV9UUkFDS19MT0FESU5HLCB7XG4gICAgICB1cmw6IGN1cnJlbnRUcmFjay51cmwsXG4gICAgICBpZDogdHJhY2tJZFxuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBEaXNhYmxlcyB0aGUgb2xkIHN1YnRpdGxlVHJhY2sgYW5kIHNldHMgY3VycmVudCBtb2RlIG9uIHRoZSBuZXh0IHN1YnRpdGxlVHJhY2suXG4gICAqIFRoaXMgb3BlcmF0ZXMgb24gdGhlIERPTSB0ZXh0VHJhY2tzLlxuICAgKiBBIHZhbHVlIG9mIC0xIHdpbGwgZGlzYWJsZSBhbGwgc3VidGl0bGUgdHJhY2tzLlxuICAgKiBAcGFyYW0gbmV3SWQgLSBUaGUgaWQgb2YgdGhlIG5leHQgdHJhY2sgdG8gZW5hYmxlXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl90b2dnbGVUcmFja01vZGVzID0gZnVuY3Rpb24gX3RvZ2dsZVRyYWNrTW9kZXMobmV3SWQpIHtcbiAgICB2YXIgbWVkaWEgPSB0aGlzLm1lZGlhLFxuICAgICAgICBzdWJ0aXRsZURpc3BsYXkgPSB0aGlzLnN1YnRpdGxlRGlzcGxheSxcbiAgICAgICAgdHJhY2tJZCA9IHRoaXMudHJhY2tJZDtcblxuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgdGV4dFRyYWNrcyA9IGZpbHRlclN1YnRpdGxlVHJhY2tzKG1lZGlhLnRleHRUcmFja3MpO1xuXG4gICAgaWYgKG5ld0lkID09PSAtMSkge1xuICAgICAgW10uc2xpY2UuY2FsbCh0ZXh0VHJhY2tzKS5mb3JFYWNoKGZ1bmN0aW9uICh0cmFjaykge1xuICAgICAgICB0cmFjay5tb2RlID0gJ2Rpc2FibGVkJztcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgb2xkVHJhY2sgPSB0ZXh0VHJhY2tzW3RyYWNrSWRdO1xuXG4gICAgICBpZiAob2xkVHJhY2spIHtcbiAgICAgICAgb2xkVHJhY2subW9kZSA9ICdkaXNhYmxlZCc7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIG5leHRUcmFjayA9IHRleHRUcmFja3NbbmV3SWRdO1xuXG4gICAgaWYgKG5leHRUcmFjaykge1xuICAgICAgbmV4dFRyYWNrLm1vZGUgPSBzdWJ0aXRsZURpc3BsYXkgPyAnc2hvd2luZycgOiAnaGlkZGVuJztcbiAgICB9XG4gIH1cbiAgLyoqXG4gICAgICogVGhpcyBtZXRob2QgaXMgcmVzcG9uc2libGUgZm9yIHZhbGlkYXRpbmcgdGhlIHN1YnRpdGxlIGluZGV4IGFuZCBwZXJpb2RpY2FsbHkgcmVsb2FkaW5nIGlmIGxpdmUuXG4gICAgICogRGlzcGF0Y2hlcyB0aGUgU1VCVElUTEVfVFJBQ0tfU1dJVENIIGV2ZW50LCB3aGljaCBpbnN0cnVjdHMgdGhlIHN1YnRpdGxlLXN0cmVhbS1jb250cm9sbGVyIHRvIGxvYWQgdGhlIHNlbGVjdGVkIHRyYWNrLlxuICAgICAqIEBwYXJhbSBuZXdJZCAtIFRoZSBpZCBvZiB0aGUgc3VidGl0bGUgdHJhY2sgdG8gYWN0aXZhdGUuXG4gICAgICovXG4gIDtcblxuICBfcHJvdG8uX3NldFN1YnRpdGxlVHJhY2tJbnRlcm5hbCA9IGZ1bmN0aW9uIF9zZXRTdWJ0aXRsZVRyYWNrSW50ZXJuYWwobmV3SWQpIHtcbiAgICB2YXIgaGxzID0gdGhpcy5obHMsXG4gICAgICAgIHRyYWNrcyA9IHRoaXMudHJhY2tzO1xuXG4gICAgaWYgKCFPYmplY3QobnVtYmVyW1wiaXNGaW5pdGVOdW1iZXJcIl0pKG5ld0lkKSB8fCBuZXdJZCA8IC0xIHx8IG5ld0lkID49IHRyYWNrcy5sZW5ndGgpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLnRyYWNrSWQgPSBuZXdJZDtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiU3dpdGNoaW5nIHRvIHN1YnRpdGxlIHRyYWNrIFwiICsgbmV3SWQpO1xuICAgIGhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfVFJBQ0tfU1dJVENILCB7XG4gICAgICBpZDogbmV3SWRcbiAgICB9KTtcblxuICAgIHRoaXMuX2xvYWRDdXJyZW50VHJhY2soKTtcbiAgfTtcblxuICBfcHJvdG8uX29uVGV4dFRyYWNrc0NoYW5nZWQgPSBmdW5jdGlvbiBfb25UZXh0VHJhY2tzQ2hhbmdlZCgpIHtcbiAgICAvLyBNZWRpYSBpcyB1bmRlZmluZWQgd2hlbiBzd2l0Y2hpbmcgc3RyZWFtcyB2aWEgbG9hZFNvdXJjZSgpXG4gICAgaWYgKCF0aGlzLm1lZGlhIHx8ICF0aGlzLmhscy5jb25maWcucmVuZGVyVGV4dFRyYWNrc05hdGl2ZWx5KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIHRyYWNrSWQgPSAtMTtcbiAgICB2YXIgdHJhY2tzID0gZmlsdGVyU3VidGl0bGVUcmFja3ModGhpcy5tZWRpYS50ZXh0VHJhY2tzKTtcblxuICAgIGZvciAodmFyIGlkID0gMDsgaWQgPCB0cmFja3MubGVuZ3RoOyBpZCsrKSB7XG4gICAgICBpZiAodHJhY2tzW2lkXS5tb2RlID09PSAnaGlkZGVuJykge1xuICAgICAgICAvLyBEbyBub3QgYnJlYWsgaW4gY2FzZSB0aGVyZSBpcyBhIGZvbGxvd2luZyB0cmFjayB3aXRoIHNob3dpbmcuXG4gICAgICAgIHRyYWNrSWQgPSBpZDtcbiAgICAgIH0gZWxzZSBpZiAodHJhY2tzW2lkXS5tb2RlID09PSAnc2hvd2luZycpIHtcbiAgICAgICAgdHJhY2tJZCA9IGlkO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9IC8vIFNldHRpbmcgY3VycmVudCBzdWJ0aXRsZVRyYWNrIHdpbGwgaW52b2tlIGNvZGUuXG5cblxuICAgIHRoaXMuc3VidGl0bGVUcmFjayA9IHRyYWNrSWQ7XG4gIH07XG5cbiAgc3VidGl0bGVfdHJhY2tfY29udHJvbGxlcl9jcmVhdGVDbGFzcyhTdWJ0aXRsZVRyYWNrQ29udHJvbGxlciwgW3tcbiAgICBrZXk6IFwic3VidGl0bGVUcmFja3NcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYWNrcztcbiAgICB9XG4gICAgLyoqIGdldCBpbmRleCBvZiB0aGUgc2VsZWN0ZWQgc3VidGl0bGUgdHJhY2sgKGluZGV4IGluIHN1YnRpdGxlIHRyYWNrIGxpc3RzKSAqKi9cblxuICB9LCB7XG4gICAga2V5OiBcInN1YnRpdGxlVHJhY2tcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLnRyYWNrSWQ7XG4gICAgfVxuICAgIC8qKiBzZWxlY3QgYSBzdWJ0aXRsZSB0cmFjaywgYmFzZWQgb24gaXRzIGluZGV4IGluIHN1YnRpdGxlIHRyYWNrIGxpc3RzKiovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KHN1YnRpdGxlVHJhY2tJZCkge1xuICAgICAgaWYgKHRoaXMudHJhY2tJZCAhPT0gc3VidGl0bGVUcmFja0lkKSB7XG4gICAgICAgIHRoaXMuX3RvZ2dsZVRyYWNrTW9kZXMoc3VidGl0bGVUcmFja0lkKTtcblxuICAgICAgICB0aGlzLl9zZXRTdWJ0aXRsZVRyYWNrSW50ZXJuYWwoc3VidGl0bGVUcmFja0lkKTtcbiAgICAgIH1cbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gU3VidGl0bGVUcmFja0NvbnRyb2xsZXI7XG59KGV2ZW50X2hhbmRsZXIpO1xuXG5mdW5jdGlvbiBmaWx0ZXJTdWJ0aXRsZVRyYWNrcyh0ZXh0VHJhY2tMaXN0KSB7XG4gIHZhciB0cmFja3MgPSBbXTtcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IHRleHRUcmFja0xpc3QubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgdHJhY2sgPSB0ZXh0VHJhY2tMaXN0W2ldOyAvLyBFZGdlIGFkZHMgYSB0cmFjayB3aXRob3V0IGEgbGFiZWw7IHdlIGRvbid0IHdhbnQgdG8gdXNlIGl0XG5cbiAgICBpZiAodHJhY2sua2luZCA9PT0gJ3N1YnRpdGxlcycgJiYgdHJhY2subGFiZWwpIHtcbiAgICAgIHRyYWNrcy5wdXNoKHRleHRUcmFja0xpc3RbaV0pO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0cmFja3M7XG59XG5cbi8qIGhhcm1vbnkgZGVmYXVsdCBleHBvcnQgKi8gdmFyIHN1YnRpdGxlX3RyYWNrX2NvbnRyb2xsZXIgPSAoc3VidGl0bGVfdHJhY2tfY29udHJvbGxlcl9TdWJ0aXRsZVRyYWNrQ29udHJvbGxlcik7XG4vLyBFWFRFUk5BTCBNT0RVTEU6IC4vc3JjL2NyeXB0L2RlY3J5cHRlci5qcyArIDMgbW9kdWxlc1xudmFyIGRlY3J5cHRlciA9IF9fd2VicGFja19yZXF1aXJlX18oXCIuL3NyYy9jcnlwdC9kZWNyeXB0ZXIuanNcIik7XG5cbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbnRyb2xsZXIvc3VidGl0bGUtc3RyZWFtLWNvbnRyb2xsZXIuanNcbmZ1bmN0aW9uIHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX2Fzc2VydFRoaXNJbml0aWFsaXplZChzZWxmKSB7IGlmIChzZWxmID09PSB2b2lkIDApIHsgdGhyb3cgbmV3IFJlZmVyZW5jZUVycm9yKFwidGhpcyBoYXNuJ3QgYmVlbiBpbml0aWFsaXNlZCAtIHN1cGVyKCkgaGFzbid0IGJlZW4gY2FsbGVkXCIpOyB9IHJldHVybiBzZWxmOyB9XG5cbmZ1bmN0aW9uIHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qKlxuICogQGNsYXNzIFN1YnRpdGxlU3RyZWFtQ29udHJvbGxlclxuICovXG5cblxuXG5cblxuXG5cblxudmFyIHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX3dpbmRvdyA9IHdpbmRvdyxcbiAgICBzdWJ0aXRsZV9zdHJlYW1fY29udHJvbGxlcl9wZXJmb3JtYW5jZSA9IHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX3dpbmRvdy5wZXJmb3JtYW5jZTtcbnZhciBzdWJ0aXRsZV9zdHJlYW1fY29udHJvbGxlcl9USUNLX0lOVEVSVkFMID0gNTAwOyAvLyBob3cgb2Z0ZW4gdG8gdGljayBpbiBtc1xuXG52YXIgc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfU3VidGl0bGVTdHJlYW1Db250cm9sbGVyID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChfQmFzZVN0cmVhbUNvbnRyb2xsZXIpIHtcbiAgc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfaW5oZXJpdHNMb29zZShTdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXIsIF9CYXNlU3RyZWFtQ29udHJvbGxlcik7XG5cbiAgZnVuY3Rpb24gU3VidGl0bGVTdHJlYW1Db250cm9sbGVyKGhscywgZnJhZ21lbnRUcmFja2VyKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfQmFzZVN0cmVhbUNvbnRyb2xsZXIuY2FsbCh0aGlzLCBobHMsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfQVRUQUNIRUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uTUVESUFfREVUQUNISU5HLCBldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCBldmVudHNbXCJkZWZhdWx0XCJdLktFWV9MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FERUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfVFJBQ0tTX1VQREFURUQsIGV2ZW50c1tcImRlZmF1bHRcIl0uU1VCVElUTEVfVFJBQ0tfU1dJVENILCBldmVudHNbXCJkZWZhdWx0XCJdLlNVQlRJVExFX1RSQUNLX0xPQURFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5TVUJUSVRMRV9GUkFHX1BST0NFU1NFRCwgZXZlbnRzW1wiZGVmYXVsdFwiXS5MRVZFTF9VUERBVEVEKSB8fCB0aGlzO1xuICAgIF90aGlzLmZyYWdtZW50VHJhY2tlciA9IGZyYWdtZW50VHJhY2tlcjtcbiAgICBfdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIF90aGlzLnN0YXRlID0gU3RhdGUuU1RPUFBFRDtcbiAgICBfdGhpcy50cmFja3MgPSBbXTtcbiAgICBfdGhpcy50cmFja3NCdWZmZXJlZCA9IFtdO1xuICAgIF90aGlzLmN1cnJlbnRUcmFja0lkID0gLTE7XG4gICAgX3RoaXMuZGVjcnlwdGVyID0gbmV3IGRlY3J5cHRlcltcImRlZmF1bHRcIl0oaGxzLCBobHMuY29uZmlnKTsgLy8gbGFzdEFWU3RhcnQgc3RvcmVzIHRoZSB0aW1lIGluIHNlY29uZHMgZm9yIHRoZSBzdGFydCB0aW1lIG9mIGEgbGV2ZWwgbG9hZFxuXG4gICAgX3RoaXMubGFzdEFWU3RhcnQgPSAwO1xuICAgIF90aGlzLl9vbk1lZGlhU2Vla2luZyA9IF90aGlzLm9uTWVkaWFTZWVraW5nLmJpbmQoc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSk7XG4gICAgcmV0dXJuIF90aGlzO1xuICB9XG5cbiAgdmFyIF9wcm90byA9IFN1YnRpdGxlU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGU7XG5cbiAgX3Byb3RvLnN0YXJ0TG9hZCA9IGZ1bmN0aW9uIHN0YXJ0TG9hZCgpIHtcbiAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7IC8vIENoZWNrIGlmIHdlIGFscmVhZHkgaGF2ZSBhIHRyYWNrIHdpdGggbmVjZXNzYXJ5IGRldGFpbHMgdG8gbG9hZCBmcmFnbWVudHNcblxuICAgIHZhciBjdXJyZW50VHJhY2sgPSB0aGlzLnRyYWNrc1t0aGlzLmN1cnJlbnRUcmFja0lkXTtcblxuICAgIGlmIChjdXJyZW50VHJhY2sgJiYgY3VycmVudFRyYWNrLmRldGFpbHMpIHtcbiAgICAgIHRoaXMuc2V0SW50ZXJ2YWwoc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfVElDS19JTlRFUlZBTCk7XG4gICAgICB0aGlzLnRpY2soKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uU3VidGl0bGVGcmFnUHJvY2Vzc2VkID0gZnVuY3Rpb24gb25TdWJ0aXRsZUZyYWdQcm9jZXNzZWQoZGF0YSkge1xuICAgIHZhciBmcmFnID0gZGF0YS5mcmFnLFxuICAgICAgICBzdWNjZXNzID0gZGF0YS5zdWNjZXNzO1xuICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gZnJhZztcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcblxuICAgIGlmICghc3VjY2Vzcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBidWZmZXJlZCA9IHRoaXMudHJhY2tzQnVmZmVyZWRbdGhpcy5jdXJyZW50VHJhY2tJZF07XG5cbiAgICBpZiAoIWJ1ZmZlcmVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfSAvLyBDcmVhdGUvdXBkYXRlIGEgYnVmZmVyZWQgYXJyYXkgbWF0Y2hpbmcgdGhlIGludGVyZmFjZSB1c2VkIGJ5IEJ1ZmZlckhlbHBlci5idWZmZXJlZEluZm9cbiAgICAvLyBzbyB3ZSBjYW4gcmUtdXNlIHRoZSBsb2dpYyB1c2VkIHRvIGRldGVjdCBob3cgbXVjaCBoYXZlIGJlZW4gYnVmZmVyZWRcblxuXG4gICAgdmFyIHRpbWVSYW5nZTtcbiAgICB2YXIgZnJhZ1N0YXJ0ID0gZnJhZy5zdGFydDtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnVmZmVyZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChmcmFnU3RhcnQgPj0gYnVmZmVyZWRbaV0uc3RhcnQgJiYgZnJhZ1N0YXJ0IDw9IGJ1ZmZlcmVkW2ldLmVuZCkge1xuICAgICAgICB0aW1lUmFuZ2UgPSBidWZmZXJlZFtpXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdmFyIGZyYWdFbmQgPSBmcmFnLnN0YXJ0ICsgZnJhZy5kdXJhdGlvbjtcblxuICAgIGlmICh0aW1lUmFuZ2UpIHtcbiAgICAgIHRpbWVSYW5nZS5lbmQgPSBmcmFnRW5kO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aW1lUmFuZ2UgPSB7XG4gICAgICAgIHN0YXJ0OiBmcmFnU3RhcnQsXG4gICAgICAgIGVuZDogZnJhZ0VuZFxuICAgICAgfTtcbiAgICAgIGJ1ZmZlcmVkLnB1c2godGltZVJhbmdlKTtcbiAgICB9XG4gIH07XG5cbiAgX3Byb3RvLm9uTWVkaWFBdHRhY2hlZCA9IGZ1bmN0aW9uIG9uTWVkaWFBdHRhY2hlZChfcmVmKSB7XG4gICAgdmFyIG1lZGlhID0gX3JlZi5tZWRpYTtcbiAgICB0aGlzLm1lZGlhID0gbWVkaWE7XG4gICAgbWVkaWEuYWRkRXZlbnRMaXN0ZW5lcignc2Vla2luZycsIHRoaXMuX29uTWVkaWFTZWVraW5nKTtcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgfTtcblxuICBfcHJvdG8ub25NZWRpYURldGFjaGluZyA9IGZ1bmN0aW9uIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0aGlzLm1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NlZWtpbmcnLCB0aGlzLl9vbk1lZGlhU2Vla2luZyk7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlQWxsRnJhZ21lbnRzKCk7XG4gICAgdGhpcy5jdXJyZW50VHJhY2tJZCA9IC0xO1xuICAgIHRoaXMudHJhY2tzLmZvckVhY2goZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICBfdGhpczIudHJhY2tzQnVmZmVyZWRbdHJhY2suaWRdID0gW107XG4gICAgfSk7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gIH0gLy8gSWYgc29tZXRoaW5nIGdvZXMgd3JvbmcsIHByb2NlZWQgdG8gbmV4dCBmcmFnLCBpZiB3ZSB3ZXJlIHByb2Nlc3Npbmcgb25lLlxuICA7XG5cbiAgX3Byb3RvLm9uRXJyb3IgPSBmdW5jdGlvbiBvbkVycm9yKGRhdGEpIHtcbiAgICB2YXIgZnJhZyA9IGRhdGEuZnJhZzsgLy8gZG9uJ3QgaGFuZGxlIGVycm9yIG5vdCByZWxhdGVkIHRvIHN1YnRpdGxlIGZyYWdtZW50XG5cbiAgICBpZiAoIWZyYWcgfHwgZnJhZy50eXBlICE9PSAnc3VidGl0bGUnKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZnJhZ0N1cnJlbnQgJiYgdGhpcy5mcmFnQ3VycmVudC5sb2FkZXIpIHtcbiAgICAgIHRoaXMuZnJhZ0N1cnJlbnQubG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gIH0gLy8gR290IGFsbCBuZXcgc3VidGl0bGUgdHJhY2tzLlxuICA7XG5cbiAgX3Byb3RvLm9uU3VidGl0bGVUcmFja3NVcGRhdGVkID0gZnVuY3Rpb24gb25TdWJ0aXRsZVRyYWNrc1VwZGF0ZWQoZGF0YSkge1xuICAgIHZhciBfdGhpczMgPSB0aGlzO1xuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnc3VidGl0bGUgdHJhY2tzIHVwZGF0ZWQnKTtcbiAgICB0aGlzLnRyYWNrc0J1ZmZlcmVkID0gW107XG4gICAgdGhpcy50cmFja3MgPSBkYXRhLnN1YnRpdGxlVHJhY2tzO1xuICAgIHRoaXMudHJhY2tzLmZvckVhY2goZnVuY3Rpb24gKHRyYWNrKSB7XG4gICAgICBfdGhpczMudHJhY2tzQnVmZmVyZWRbdHJhY2suaWRdID0gW107XG4gICAgfSk7XG4gIH07XG5cbiAgX3Byb3RvLm9uU3VidGl0bGVUcmFja1N3aXRjaCA9IGZ1bmN0aW9uIG9uU3VidGl0bGVUcmFja1N3aXRjaChkYXRhKSB7XG4gICAgdGhpcy5jdXJyZW50VHJhY2tJZCA9IGRhdGEuaWQ7XG5cbiAgICBpZiAoIXRoaXMudHJhY2tzIHx8ICF0aGlzLnRyYWNrcy5sZW5ndGggfHwgdGhpcy5jdXJyZW50VHJhY2tJZCA9PT0gLTEpIHtcbiAgICAgIHRoaXMuY2xlYXJJbnRlcnZhbCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH0gLy8gQ2hlY2sgaWYgdHJhY2sgaGFzIHRoZSBuZWNlc3NhcnkgZGV0YWlscyB0byBsb2FkIGZyYWdtZW50c1xuXG5cbiAgICB2YXIgY3VycmVudFRyYWNrID0gdGhpcy50cmFja3NbdGhpcy5jdXJyZW50VHJhY2tJZF07XG5cbiAgICBpZiAoY3VycmVudFRyYWNrICYmIGN1cnJlbnRUcmFjay5kZXRhaWxzKSB7XG4gICAgICB0aGlzLnNldEludGVydmFsKHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX1RJQ0tfSU5URVJWQUwpO1xuICAgIH1cbiAgfSAvLyBHb3QgYSBuZXcgc2V0IG9mIHN1YnRpdGxlIGZyYWdtZW50cy5cbiAgO1xuXG4gIF9wcm90by5vblN1YnRpdGxlVHJhY2tMb2FkZWQgPSBmdW5jdGlvbiBvblN1YnRpdGxlVHJhY2tMb2FkZWQoZGF0YSkge1xuICAgIHZhciBpZCA9IGRhdGEuaWQsXG4gICAgICAgIGRldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgdmFyIGN1cnJlbnRUcmFja0lkID0gdGhpcy5jdXJyZW50VHJhY2tJZCxcbiAgICAgICAgdHJhY2tzID0gdGhpcy50cmFja3M7XG4gICAgdmFyIGN1cnJlbnRUcmFjayA9IHRyYWNrc1tjdXJyZW50VHJhY2tJZF07XG5cbiAgICBpZiAoaWQgPj0gdHJhY2tzLmxlbmd0aCB8fCBpZCAhPT0gY3VycmVudFRyYWNrSWQgfHwgIWN1cnJlbnRUcmFjaykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChkZXRhaWxzLmxpdmUpIHtcbiAgICAgIG1lcmdlU3VidGl0bGVQbGF5bGlzdHMoY3VycmVudFRyYWNrLmRldGFpbHMsIGRldGFpbHMsIHRoaXMubGFzdEFWU3RhcnQpO1xuICAgIH1cblxuICAgIGN1cnJlbnRUcmFjay5kZXRhaWxzID0gZGV0YWlscztcbiAgICB0aGlzLnNldEludGVydmFsKHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX1RJQ0tfSU5URVJWQUwpO1xuICB9O1xuXG4gIF9wcm90by5vbktleUxvYWRlZCA9IGZ1bmN0aW9uIG9uS2V5TG9hZGVkKCkge1xuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5LRVlfTE9BRElORykge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkZyYWdMb2FkZWQgPSBmdW5jdGlvbiBvbkZyYWdMb2FkZWQoZGF0YSkge1xuICAgIHZhciBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG4gICAgdmFyIGRlY3J5cHREYXRhID0gZGF0YS5mcmFnLmRlY3J5cHRkYXRhO1xuICAgIHZhciBmcmFnTG9hZGVkID0gZGF0YS5mcmFnO1xuICAgIHZhciBobHMgPSB0aGlzLmhscztcblxuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5GUkFHX0xPQURJTkcgJiYgZnJhZ0N1cnJlbnQgJiYgZGF0YS5mcmFnLnR5cGUgPT09ICdzdWJ0aXRsZScgJiYgZnJhZ0N1cnJlbnQuc24gPT09IGRhdGEuZnJhZy5zbikge1xuICAgICAgLy8gY2hlY2sgdG8gc2VlIGlmIHRoZSBwYXlsb2FkIG5lZWRzIHRvIGJlIGRlY3J5cHRlZFxuICAgICAgaWYgKGRhdGEucGF5bG9hZC5ieXRlTGVuZ3RoID4gMCAmJiBkZWNyeXB0RGF0YSAmJiBkZWNyeXB0RGF0YS5rZXkgJiYgZGVjcnlwdERhdGEubWV0aG9kID09PSAnQUVTLTEyOCcpIHtcbiAgICAgICAgdmFyIHN0YXJ0VGltZSA9IHN1YnRpdGxlX3N0cmVhbV9jb250cm9sbGVyX3BlcmZvcm1hbmNlLm5vdygpOyAvLyBkZWNyeXB0IHRoZSBzdWJ0aXRsZXNcblxuICAgICAgICB0aGlzLmRlY3J5cHRlci5kZWNyeXB0KGRhdGEucGF5bG9hZCwgZGVjcnlwdERhdGEua2V5LmJ1ZmZlciwgZGVjcnlwdERhdGEuaXYuYnVmZmVyLCBmdW5jdGlvbiAoZGVjcnlwdGVkRGF0YSkge1xuICAgICAgICAgIHZhciBlbmRUaW1lID0gc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgICAgaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5GUkFHX0RFQ1JZUFRFRCwge1xuICAgICAgICAgICAgZnJhZzogZnJhZ0xvYWRlZCxcbiAgICAgICAgICAgIHBheWxvYWQ6IGRlY3J5cHRlZERhdGEsXG4gICAgICAgICAgICBzdGF0czoge1xuICAgICAgICAgICAgICB0c3RhcnQ6IHN0YXJ0VGltZSxcbiAgICAgICAgICAgICAgdGRlY3J5cHQ6IGVuZFRpbWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbkxldmVsVXBkYXRlZCA9IGZ1bmN0aW9uIG9uTGV2ZWxVcGRhdGVkKF9yZWYyKSB7XG4gICAgdmFyIGRldGFpbHMgPSBfcmVmMi5kZXRhaWxzO1xuICAgIHZhciBmcmFncyA9IGRldGFpbHMuZnJhZ21lbnRzO1xuICAgIHRoaXMubGFzdEFWU3RhcnQgPSBmcmFncy5sZW5ndGggPyBmcmFnc1swXS5zdGFydCA6IDA7XG4gIH07XG5cbiAgX3Byb3RvLmRvVGljayA9IGZ1bmN0aW9uIGRvVGljaygpIHtcbiAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHN3aXRjaCAodGhpcy5zdGF0ZSkge1xuICAgICAgY2FzZSBTdGF0ZS5JRExFOlxuICAgICAgICB7XG4gICAgICAgICAgdmFyIGNvbmZpZyA9IHRoaXMuY29uZmlnLFxuICAgICAgICAgICAgICBjdXJyZW50VHJhY2tJZCA9IHRoaXMuY3VycmVudFRyYWNrSWQsXG4gICAgICAgICAgICAgIGZyYWdtZW50VHJhY2tlciA9IHRoaXMuZnJhZ21lbnRUcmFja2VyLFxuICAgICAgICAgICAgICBtZWRpYSA9IHRoaXMubWVkaWEsXG4gICAgICAgICAgICAgIHRyYWNrcyA9IHRoaXMudHJhY2tzO1xuXG4gICAgICAgICAgaWYgKCF0cmFja3MgfHwgIXRyYWNrc1tjdXJyZW50VHJhY2tJZF0gfHwgIXRyYWNrc1tjdXJyZW50VHJhY2tJZF0uZGV0YWlscykge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdmFyIG1heEJ1ZmZlckhvbGUgPSBjb25maWcubWF4QnVmZmVySG9sZSxcbiAgICAgICAgICAgICAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IGNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlO1xuICAgICAgICAgIHZhciBtYXhDb25maWdCdWZmZXIgPSBNYXRoLm1pbihjb25maWcubWF4QnVmZmVyTGVuZ3RoLCBjb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoKTtcbiAgICAgICAgICB2YXIgYnVmZmVyZWRJbmZvID0gQnVmZmVySGVscGVyLmJ1ZmZlcmVkSW5mbyh0aGlzLl9nZXRCdWZmZXJlZCgpLCBtZWRpYS5jdXJyZW50VGltZSwgbWF4QnVmZmVySG9sZSk7XG4gICAgICAgICAgdmFyIGJ1ZmZlckVuZCA9IGJ1ZmZlcmVkSW5mby5lbmQsXG4gICAgICAgICAgICAgIGJ1ZmZlckxlbiA9IGJ1ZmZlcmVkSW5mby5sZW47XG4gICAgICAgICAgdmFyIHRyYWNrRGV0YWlscyA9IHRyYWNrc1tjdXJyZW50VHJhY2tJZF0uZGV0YWlscztcbiAgICAgICAgICB2YXIgZnJhZ21lbnRzID0gdHJhY2tEZXRhaWxzLmZyYWdtZW50cztcbiAgICAgICAgICB2YXIgZnJhZ0xlbiA9IGZyYWdtZW50cy5sZW5ndGg7XG4gICAgICAgICAgdmFyIGVuZCA9IGZyYWdtZW50c1tmcmFnTGVuIC0gMV0uc3RhcnQgKyBmcmFnbWVudHNbZnJhZ0xlbiAtIDFdLmR1cmF0aW9uO1xuXG4gICAgICAgICAgaWYgKGJ1ZmZlckxlbiA+IG1heENvbmZpZ0J1ZmZlcikge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHZhciBmb3VuZEZyYWc7XG4gICAgICAgICAgdmFyIGZyYWdQcmV2aW91cyA9IHRoaXMuZnJhZ1ByZXZpb3VzO1xuXG4gICAgICAgICAgaWYgKGJ1ZmZlckVuZCA8IGVuZCkge1xuICAgICAgICAgICAgaWYgKGZyYWdQcmV2aW91cyAmJiB0cmFja0RldGFpbHMuaGFzUHJvZ3JhbURhdGVUaW1lKSB7XG4gICAgICAgICAgICAgIGZvdW5kRnJhZyA9IGZpbmRGcmFnbWVudEJ5UERUKGZyYWdtZW50cywgZnJhZ1ByZXZpb3VzLmVuZFByb2dyYW1EYXRlVGltZSwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmICghZm91bmRGcmFnKSB7XG4gICAgICAgICAgICAgIGZvdW5kRnJhZyA9IGZpbmRGcmFnbWVudEJ5UFRTKGZyYWdQcmV2aW91cywgZnJhZ21lbnRzLCBidWZmZXJFbmQsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBmb3VuZEZyYWcgPSBmcmFnbWVudHNbZnJhZ0xlbiAtIDFdO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChmb3VuZEZyYWcgJiYgZm91bmRGcmFnLmVuY3J5cHRlZCkge1xuICAgICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIkxvYWRpbmcga2V5IGZvciBcIiArIGZvdW5kRnJhZy5zbik7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuS0VZX0xPQURJTkc7XG4gICAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uS0VZX0xPQURJTkcsIHtcbiAgICAgICAgICAgICAgZnJhZzogZm91bmRGcmFnXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGVsc2UgaWYgKGZvdW5kRnJhZyAmJiBmcmFnbWVudFRyYWNrZXIuZ2V0U3RhdGUoZm91bmRGcmFnKSA9PT0gRnJhZ21lbnRTdGF0ZS5OT1RfTE9BREVEKSB7XG4gICAgICAgICAgICAvLyBvbmx5IGxvYWQgaWYgZnJhZ21lbnQgaXMgbm90IGxvYWRlZFxuICAgICAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IGZvdW5kRnJhZztcbiAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5GUkFHX0xPQURJTkc7XG4gICAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRlJBR19MT0FESU5HLCB7XG4gICAgICAgICAgICAgIGZyYWc6IGZvdW5kRnJhZ1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5zdG9wTG9hZCA9IGZ1bmN0aW9uIHN0b3BMb2FkKCkge1xuICAgIHRoaXMubGFzdEFWU3RhcnQgPSAwO1xuICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gbnVsbDtcblxuICAgIF9CYXNlU3RyZWFtQ29udHJvbGxlci5wcm90b3R5cGUuc3RvcExvYWQuY2FsbCh0aGlzKTtcbiAgfTtcblxuICBfcHJvdG8uX2dldEJ1ZmZlcmVkID0gZnVuY3Rpb24gX2dldEJ1ZmZlcmVkKCkge1xuICAgIHJldHVybiB0aGlzLnRyYWNrc0J1ZmZlcmVkW3RoaXMuY3VycmVudFRyYWNrSWRdIHx8IFtdO1xuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhU2Vla2luZyA9IGZ1bmN0aW9uIG9uTWVkaWFTZWVraW5nKCkge1xuICAgIGlmICh0aGlzLmZyYWdDdXJyZW50KSB7XG4gICAgICB2YXIgY3VycmVudFRpbWUgPSB0aGlzLm1lZGlhID8gdGhpcy5tZWRpYS5jdXJyZW50VGltZSA6IDA7XG4gICAgICB2YXIgdG9sZXJhbmNlID0gdGhpcy5jb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTtcbiAgICAgIHZhciBmcmFnU3RhcnRPZmZzZXQgPSB0aGlzLmZyYWdDdXJyZW50LnN0YXJ0IC0gdG9sZXJhbmNlO1xuICAgICAgdmFyIGZyYWdFbmRPZmZzZXQgPSB0aGlzLmZyYWdDdXJyZW50LnN0YXJ0ICsgdGhpcy5mcmFnQ3VycmVudC5kdXJhdGlvbiArIHRvbGVyYW5jZTsgLy8gY2hlY2sgaWYgcG9zaXRpb24gd2lsbCBiZSBvdXQgb2YgY3VycmVudGx5IGxvYWRlZCBmcmFnIHJhbmdlIGFmdGVyIHNlZWtpbmcgOiBpZiBvdXQsIGNhbmNlbCBmcmFnIGxvYWQsIGlmIGluLCBkb24ndCBkbyBhbnl0aGluZ1xuXG4gICAgICBpZiAoY3VycmVudFRpbWUgPCBmcmFnU3RhcnRPZmZzZXQgfHwgY3VycmVudFRpbWUgPiBmcmFnRW5kT2Zmc2V0KSB7XG4gICAgICAgIGlmICh0aGlzLmZyYWdDdXJyZW50LmxvYWRlcikge1xuICAgICAgICAgIHRoaXMuZnJhZ0N1cnJlbnQubG9hZGVyLmFib3J0KCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudCh0aGlzLmZyYWdDdXJyZW50KTtcbiAgICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7XG4gICAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gbnVsbDsgLy8gc3dpdGNoIHRvIElETEUgc3RhdGUgdG8gbG9hZCBuZXcgZnJhZ21lbnRcblxuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTsgLy8gc3BlZWQgdXAgdGhpbmdzXG5cbiAgICAgICAgdGhpcy50aWNrKCk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIHJldHVybiBTdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXI7XG59KGJhc2Vfc3RyZWFtX2NvbnRyb2xsZXJfQmFzZVN0cmVhbUNvbnRyb2xsZXIpO1xuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvdXRpbHMvbWVkaWFrZXlzLWhlbHBlci50c1xuLyoqXG4gKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9OYXZpZ2F0b3IvcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzXG4gKi9cbnZhciBLZXlTeXN0ZW1zO1xuXG4oZnVuY3Rpb24gKEtleVN5c3RlbXMpIHtcbiAgS2V5U3lzdGVtc1tcIldJREVWSU5FXCJdID0gXCJjb20ud2lkZXZpbmUuYWxwaGFcIjtcbiAgS2V5U3lzdGVtc1tcIlBMQVlSRUFEWVwiXSA9IFwiY29tLm1pY3Jvc29mdC5wbGF5cmVhZHlcIjtcbn0pKEtleVN5c3RlbXMgfHwgKEtleVN5c3RlbXMgPSB7fSkpO1xuXG52YXIgcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzID0gZnVuY3Rpb24gKCkge1xuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93Lm5hdmlnYXRvciAmJiB3aW5kb3cubmF2aWdhdG9yLnJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzcykge1xuICAgIHJldHVybiB3aW5kb3cubmF2aWdhdG9yLnJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzcy5iaW5kKHdpbmRvdy5uYXZpZ2F0b3IpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59KCk7XG5cblxuLy8gQ09OQ0FURU5BVEVEIE1PRFVMRTogLi9zcmMvY29udHJvbGxlci9lbWUtY29udHJvbGxlci50c1xuZnVuY3Rpb24gZW1lX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9XG5cbmZ1bmN0aW9uIGVtZV9jb250cm9sbGVyX2NyZWF0ZUNsYXNzKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZW1lX2NvbnRyb2xsZXJfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGVtZV9jb250cm9sbGVyX2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9XG5cbmZ1bmN0aW9uIGVtZV9jb250cm9sbGVyX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7IHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzOyBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qKlxuICogQGF1dGhvciBTdGVwaGFuIEhlc3NlIDxkaXNwYXJhdEBnbWFpbC5jb20+IHwgPHRjaGFrYWJhbUBnbWFpbC5jb20+XG4gKlxuICogRFJNIHN1cHBvcnQgZm9yIEhscy5qc1xuICovXG5cblxuXG5cblxudmFyIE1BWF9MSUNFTlNFX1JFUVVFU1RfRkFJTFVSRVMgPSAzO1xuLyoqXG4gKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9NZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25cbiAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPn0gYXVkaW9Db2RlY3MgTGlzdCBvZiByZXF1aXJlZCBhdWRpbyBjb2RlY3MgdG8gc3VwcG9ydFxuICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSB2aWRlb0NvZGVjcyBMaXN0IG9mIHJlcXVpcmVkIHZpZGVvIGNvZGVjcyB0byBzdXBwb3J0XG4gKiBAcGFyYW0ge29iamVjdH0gZHJtU3lzdGVtT3B0aW9ucyBPcHRpb25hbCBwYXJhbWV0ZXJzL3JlcXVpcmVtZW50cyBmb3IgdGhlIGtleS1zeXN0ZW1cbiAqIEByZXR1cm5zIHtBcnJheTxNZWRpYVN5c3RlbUNvbmZpZ3VyYXRpb24+fSBBbiBhcnJheSBvZiBzdXBwb3J0ZWQgY29uZmlndXJhdGlvbnNcbiAqL1xuXG52YXIgY3JlYXRlV2lkZXZpbmVNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25zID0gZnVuY3Rpb24gY3JlYXRlV2lkZXZpbmVNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25zKGF1ZGlvQ29kZWNzLCB2aWRlb0NvZGVjcywgZHJtU3lzdGVtT3B0aW9ucykge1xuICAvKiBqc2hpbnQgaWdub3JlOmxpbmUgKi9cbiAgdmFyIGJhc2VDb25maWcgPSB7XG4gICAgLy8gaW5pdERhdGFUeXBlczogWydrZXlpZHMnLCAnbXA0J10sXG4gICAgLy8gbGFiZWw6IFwiXCIsXG4gICAgLy8gcGVyc2lzdGVudFN0YXRlOiBcIm5vdC1hbGxvd2VkXCIsIC8vIG9yIFwicmVxdWlyZWRcIiA/XG4gICAgLy8gZGlzdGluY3RpdmVJZGVudGlmaWVyOiBcIm5vdC1hbGxvd2VkXCIsIC8vIG9yIFwicmVxdWlyZWRcIiA/XG4gICAgLy8gc2Vzc2lvblR5cGVzOiBbJ3RlbXBvcmFyeSddLFxuICAgIGF1ZGlvQ2FwYWJpbGl0aWVzOiBbXSxcbiAgICAvLyB7IGNvbnRlbnRUeXBlOiAnYXVkaW8vbXA0OyBjb2RlY3M9XCJtcDRhLjQwLjJcIicgfVxuICAgIHZpZGVvQ2FwYWJpbGl0aWVzOiBbXSAvLyB7IGNvbnRlbnRUeXBlOiAndmlkZW8vbXA0OyBjb2RlY3M9XCJhdmMxLjQyRTAxRVwiJyB9XG5cbiAgfTtcbiAgYXVkaW9Db2RlY3MuZm9yRWFjaChmdW5jdGlvbiAoY29kZWMpIHtcbiAgICBiYXNlQ29uZmlnLmF1ZGlvQ2FwYWJpbGl0aWVzLnB1c2goe1xuICAgICAgY29udGVudFR5cGU6IFwiYXVkaW8vbXA0OyBjb2RlY3M9XFxcIlwiICsgY29kZWMgKyBcIlxcXCJcIixcbiAgICAgIHJvYnVzdG5lc3M6IGRybVN5c3RlbU9wdGlvbnMuYXVkaW9Sb2J1c3RuZXNzIHx8ICcnXG4gICAgfSk7XG4gIH0pO1xuICB2aWRlb0NvZGVjcy5mb3JFYWNoKGZ1bmN0aW9uIChjb2RlYykge1xuICAgIGJhc2VDb25maWcudmlkZW9DYXBhYmlsaXRpZXMucHVzaCh7XG4gICAgICBjb250ZW50VHlwZTogXCJ2aWRlby9tcDQ7IGNvZGVjcz1cXFwiXCIgKyBjb2RlYyArIFwiXFxcIlwiLFxuICAgICAgcm9idXN0bmVzczogZHJtU3lzdGVtT3B0aW9ucy52aWRlb1JvYnVzdG5lc3MgfHwgJydcbiAgICB9KTtcbiAgfSk7XG4gIHJldHVybiBbYmFzZUNvbmZpZ107XG59O1xuLyoqXG4gKiBUaGUgaWRlYSBoZXJlIGlzIHRvIGhhbmRsZSBrZXktc3lzdGVtIChhbmQgdGhlaXIgcmVzcGVjdGl2ZSBwbGF0Zm9ybXMpIHNwZWNpZmljIGNvbmZpZ3VyYXRpb24gZGlmZmVyZW5jZXNcbiAqIGluIG9yZGVyIHRvIHdvcmsgd2l0aCB0aGUgbG9jYWwgcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzIG1ldGhvZC5cbiAqXG4gKiBXZSBjYW4gYWxzbyBydWxlLW91dCBwbGF0Zm9ybS1yZWxhdGVkIGtleS1zeXN0ZW0gc3VwcG9ydCBhdCB0aGlzIHBvaW50IGJ5IHRocm93aW5nIGFuIGVycm9yLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBrZXlTeXN0ZW0gSWRlbnRpZmllciBmb3IgdGhlIGtleS1zeXN0ZW0sIHNlZSBgS2V5U3lzdGVtc2AgZW51bVxuICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSBhdWRpb0NvZGVjcyBMaXN0IG9mIHJlcXVpcmVkIGF1ZGlvIGNvZGVjcyB0byBzdXBwb3J0XG4gKiBAcGFyYW0ge0FycmF5PHN0cmluZz59IHZpZGVvQ29kZWNzIExpc3Qgb2YgcmVxdWlyZWQgdmlkZW8gY29kZWNzIHRvIHN1cHBvcnRcbiAqIEB0aHJvd3Mgd2lsbCB0aHJvdyBhbiBlcnJvciBpZiBhIHVua25vd24ga2V5IHN5c3RlbSBpcyBwYXNzZWRcbiAqIEByZXR1cm5zIHtBcnJheTxNZWRpYVN5c3RlbUNvbmZpZ3VyYXRpb24+fSBBIG5vbi1lbXB0eSBBcnJheSBvZiBNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb24gb2JqZWN0c1xuICovXG5cblxudmFyIGVtZV9jb250cm9sbGVyX2dldFN1cHBvcnRlZE1lZGlhS2V5U3lzdGVtQ29uZmlndXJhdGlvbnMgPSBmdW5jdGlvbiBnZXRTdXBwb3J0ZWRNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25zKGtleVN5c3RlbSwgYXVkaW9Db2RlY3MsIHZpZGVvQ29kZWNzLCBkcm1TeXN0ZW1PcHRpb25zKSB7XG4gIHN3aXRjaCAoa2V5U3lzdGVtKSB7XG4gICAgY2FzZSBLZXlTeXN0ZW1zLldJREVWSU5FOlxuICAgICAgcmV0dXJuIGNyZWF0ZVdpZGV2aW5lTWVkaWFLZXlTeXN0ZW1Db25maWd1cmF0aW9ucyhhdWRpb0NvZGVjcywgdmlkZW9Db2RlY3MsIGRybVN5c3RlbU9wdGlvbnMpO1xuXG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlVua25vd24ga2V5LXN5c3RlbTogXCIgKyBrZXlTeXN0ZW0pO1xuICB9XG59O1xuXG4vKipcbiAqIENvbnRyb2xsZXIgdG8gZGVhbCB3aXRoIGVuY3J5cHRlZCBtZWRpYSBleHRlbnNpb25zIChFTUUpXG4gKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9FbmNyeXB0ZWRfTWVkaWFfRXh0ZW5zaW9uc19BUElcbiAqXG4gKiBAY2xhc3NcbiAqIEBjb25zdHJ1Y3RvclxuICovXG52YXIgZW1lX2NvbnRyb2xsZXJfRU1FQ29udHJvbGxlciA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoX0V2ZW50SGFuZGxlcikge1xuICBlbWVfY29udHJvbGxlcl9pbmhlcml0c0xvb3NlKEVNRUNvbnRyb2xsZXIsIF9FdmVudEhhbmRsZXIpO1xuXG4gIC8qKlxuICAgICAqIEBjb25zdHJ1Y3RzXG4gICAgICogQHBhcmFtIHtIbHN9IGhscyBPdXIgSGxzLmpzIGluc3RhbmNlXG4gICAgICovXG4gIGZ1bmN0aW9uIEVNRUNvbnRyb2xsZXIoaGxzKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgX3RoaXMgPSBfRXZlbnRIYW5kbGVyLmNhbGwodGhpcywgaGxzLCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0FUVEFDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSEVELCBldmVudHNbXCJkZWZhdWx0XCJdLk1BTklGRVNUX1BBUlNFRCkgfHwgdGhpcztcbiAgICBfdGhpcy5fd2lkZXZpbmVMaWNlbnNlVXJsID0gdm9pZCAwO1xuICAgIF90aGlzLl9saWNlbnNlWGhyU2V0dXAgPSB2b2lkIDA7XG4gICAgX3RoaXMuX2VtZUVuYWJsZWQgPSB2b2lkIDA7XG4gICAgX3RoaXMuX3JlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2VzcyA9IHZvaWQgMDtcbiAgICBfdGhpcy5fZHJtU3lzdGVtT3B0aW9ucyA9IHZvaWQgMDtcbiAgICBfdGhpcy5fY29uZmlnID0gdm9pZCAwO1xuICAgIF90aGlzLl9tZWRpYUtleXNMaXN0ID0gW107XG4gICAgX3RoaXMuX21lZGlhID0gbnVsbDtcbiAgICBfdGhpcy5faGFzU2V0TWVkaWFLZXlzID0gZmFsc2U7XG4gICAgX3RoaXMuX3JlcXVlc3RMaWNlbnNlRmFpbHVyZUNvdW50ID0gMDtcbiAgICBfdGhpcy5tZWRpYUtleXNQcm9taXNlID0gbnVsbDtcblxuICAgIF90aGlzLl9vbk1lZGlhRW5jcnlwdGVkID0gZnVuY3Rpb24gKGUpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJNZWRpYSBpcyBlbmNyeXB0ZWQgdXNpbmcgXFxcIlwiICsgZS5pbml0RGF0YVR5cGUgKyBcIlxcXCIgaW5pdCBkYXRhIHR5cGVcIik7XG5cbiAgICAgIGlmICghX3RoaXMubWVkaWFLZXlzUHJvbWlzZSkge1xuICAgICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoJ0ZhdGFsOiBNZWRpYSBpcyBlbmNyeXB0ZWQgYnV0IG5vIENETSBhY2Nlc3Mgb3Igbm8ga2V5cyBoYXZlIGJlZW4gcmVxdWVzdGVkJyk7XG5cbiAgICAgICAgX3RoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICAgIHR5cGU6IGVycm9yc1tcIkVycm9yVHlwZXNcIl0uS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9OT19LRVlTLFxuICAgICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGZpbmFsbHlTZXRLZXlBbmRTdGFydFNlc3Npb24gPSBmdW5jdGlvbiBmaW5hbGx5U2V0S2V5QW5kU3RhcnRTZXNzaW9uKG1lZGlhS2V5cykge1xuICAgICAgICBpZiAoIV90aGlzLl9tZWRpYSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIF90aGlzLl9hdHRlbXB0U2V0TWVkaWFLZXlzKG1lZGlhS2V5cyk7XG5cbiAgICAgICAgX3RoaXMuX2dlbmVyYXRlUmVxdWVzdFdpdGhQcmVmZXJyZWRLZXlTZXNzaW9uKGUuaW5pdERhdGFUeXBlLCBlLmluaXREYXRhKTtcbiAgICAgIH07IC8vIENvdWxkIHVzZSBgUHJvbWlzZS5maW5hbGx5YCBidXQgc29tZSBQcm9taXNlIHBvbHlmaWxscyBhcmUgbWlzc2luZyBpdFxuXG5cbiAgICAgIF90aGlzLm1lZGlhS2V5c1Byb21pc2UudGhlbihmaW5hbGx5U2V0S2V5QW5kU3RhcnRTZXNzaW9uKS5jYXRjaChmaW5hbGx5U2V0S2V5QW5kU3RhcnRTZXNzaW9uKTtcbiAgICB9O1xuXG4gICAgX3RoaXMuX2NvbmZpZyA9IGhscy5jb25maWc7XG4gICAgX3RoaXMuX3dpZGV2aW5lTGljZW5zZVVybCA9IF90aGlzLl9jb25maWcud2lkZXZpbmVMaWNlbnNlVXJsO1xuICAgIF90aGlzLl9saWNlbnNlWGhyU2V0dXAgPSBfdGhpcy5fY29uZmlnLmxpY2Vuc2VYaHJTZXR1cDtcbiAgICBfdGhpcy5fZW1lRW5hYmxlZCA9IF90aGlzLl9jb25maWcuZW1lRW5hYmxlZDtcbiAgICBfdGhpcy5fcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzID0gX3RoaXMuX2NvbmZpZy5yZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3NGdW5jO1xuICAgIF90aGlzLl9kcm1TeXN0ZW1PcHRpb25zID0gaGxzLmNvbmZpZy5kcm1TeXN0ZW1PcHRpb25zO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuICAvKipcbiAgICogQHBhcmFtIHtzdHJpbmd9IGtleVN5c3RlbSBJZGVudGlmaWVyIGZvciB0aGUga2V5LXN5c3RlbSwgc2VlIGBLZXlTeXN0ZW1zYCBlbnVtXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IExpY2Vuc2Ugc2VydmVyIFVSTCBmb3Iga2V5LXN5c3RlbSAoaWYgYW55IGNvbmZpZ3VyZWQsIG90aGVyd2lzZSBjYXVzZXMgZXJyb3IpXG4gICAqIEB0aHJvd3MgaWYgYSB1bnN1cHBvcnRlZCBrZXlzeXN0ZW0gaXMgcGFzc2VkXG4gICAqL1xuXG5cbiAgdmFyIF9wcm90byA9IEVNRUNvbnRyb2xsZXIucHJvdG90eXBlO1xuXG4gIF9wcm90by5nZXRMaWNlbnNlU2VydmVyVXJsID0gZnVuY3Rpb24gZ2V0TGljZW5zZVNlcnZlclVybChrZXlTeXN0ZW0pIHtcbiAgICBzd2l0Y2ggKGtleVN5c3RlbSkge1xuICAgICAgY2FzZSBLZXlTeXN0ZW1zLldJREVWSU5FOlxuICAgICAgICBpZiAoIXRoaXMuX3dpZGV2aW5lTGljZW5zZVVybCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuX3dpZGV2aW5lTGljZW5zZVVybDtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJubyBsaWNlbnNlIHNlcnZlciBVUkwgY29uZmlndXJlZCBmb3Iga2V5LXN5c3RlbSBcXFwiXCIgKyBrZXlTeXN0ZW0gKyBcIlxcXCJcIik7XG4gIH1cbiAgLyoqXG4gICAgICogUmVxdWVzdHMgYWNjZXNzIG9iamVjdCBhbmQgYWRkcyBpdCB0byBvdXIgbGlzdCB1cG9uIHN1Y2Nlc3NcbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBrZXlTeXN0ZW0gU3lzdGVtIElEIChzZWUgYEtleVN5c3RlbXNgKVxuICAgICAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPn0gYXVkaW9Db2RlY3MgTGlzdCBvZiByZXF1aXJlZCBhdWRpbyBjb2RlY3MgdG8gc3VwcG9ydFxuICAgICAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPn0gdmlkZW9Db2RlY3MgTGlzdCBvZiByZXF1aXJlZCB2aWRlbyBjb2RlY3MgdG8gc3VwcG9ydFxuICAgICAqIEB0aHJvd3MgV2hlbiBhIHVuc3VwcG9ydGVkIEtleVN5c3RlbSBpcyBwYXNzZWRcbiAgICAgKi9cbiAgO1xuXG4gIF9wcm90by5fYXR0ZW1wdEtleVN5c3RlbUFjY2VzcyA9IGZ1bmN0aW9uIF9hdHRlbXB0S2V5U3lzdGVtQWNjZXNzKGtleVN5c3RlbSwgYXVkaW9Db2RlY3MsIHZpZGVvQ29kZWNzKSB7XG4gICAgdmFyIF90aGlzMiA9IHRoaXM7XG5cbiAgICAvLyBUaGlzIGNhbiB0aHJvdywgYnV0IGlzIGNhdWdodCBpbiBldmVudCBoYW5kbGVyIGNhbGxwYXRoXG4gICAgdmFyIG1lZGlhS2V5U3lzdGVtQ29uZmlncyA9IGVtZV9jb250cm9sbGVyX2dldFN1cHBvcnRlZE1lZGlhS2V5U3lzdGVtQ29uZmlndXJhdGlvbnMoa2V5U3lzdGVtLCBhdWRpb0NvZGVjcywgdmlkZW9Db2RlY3MsIHRoaXMuX2RybVN5c3RlbU9wdGlvbnMpO1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ1JlcXVlc3RpbmcgZW5jcnlwdGVkIG1lZGlhIGtleS1zeXN0ZW0gYWNjZXNzJyk7IC8vIGV4cGVjdGluZyBpbnRlcmZhY2UgbGlrZSB3aW5kb3cubmF2aWdhdG9yLnJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzc1xuXG4gICAgdmFyIGtleVN5c3RlbUFjY2Vzc1Byb21pc2UgPSB0aGlzLnJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2VzcyhrZXlTeXN0ZW0sIG1lZGlhS2V5U3lzdGVtQ29uZmlncyk7XG4gICAgdGhpcy5tZWRpYUtleXNQcm9taXNlID0ga2V5U3lzdGVtQWNjZXNzUHJvbWlzZS50aGVuKGZ1bmN0aW9uIChtZWRpYUtleVN5c3RlbUFjY2Vzcykge1xuICAgICAgcmV0dXJuIF90aGlzMi5fb25NZWRpYUtleVN5c3RlbUFjY2Vzc09idGFpbmVkKGtleVN5c3RlbSwgbWVkaWFLZXlTeXN0ZW1BY2Nlc3MpO1xuICAgIH0pO1xuICAgIGtleVN5c3RlbUFjY2Vzc1Byb21pc2UuY2F0Y2goZnVuY3Rpb24gKGVycikge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKFwiRmFpbGVkIHRvIG9idGFpbiBrZXktc3lzdGVtIFxcXCJcIiArIGtleVN5c3RlbSArIFwiXFxcIiBhY2Nlc3M6XCIsIGVycik7XG4gICAgfSk7XG4gIH07XG5cbiAgLyoqXG4gICAgICogSGFuZGxlcyBvYnRhaW5pbmcgYWNjZXNzIHRvIGEga2V5LXN5c3RlbVxuICAgICAqIEBwcml2YXRlXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGtleVN5c3RlbVxuICAgICAqIEBwYXJhbSB7TWVkaWFLZXlTeXN0ZW1BY2Nlc3N9IG1lZGlhS2V5U3lzdGVtQWNjZXNzIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9NZWRpYUtleVN5c3RlbUFjY2Vzc1xuICAgICAqL1xuICBfcHJvdG8uX29uTWVkaWFLZXlTeXN0ZW1BY2Nlc3NPYnRhaW5lZCA9IGZ1bmN0aW9uIF9vbk1lZGlhS2V5U3lzdGVtQWNjZXNzT2J0YWluZWQoa2V5U3lzdGVtLCBtZWRpYUtleVN5c3RlbUFjY2Vzcykge1xuICAgIHZhciBfdGhpczMgPSB0aGlzO1xuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIkFjY2VzcyBmb3Iga2V5LXN5c3RlbSBcXFwiXCIgKyBrZXlTeXN0ZW0gKyBcIlxcXCIgb2J0YWluZWRcIik7XG4gICAgdmFyIG1lZGlhS2V5c0xpc3RJdGVtID0ge1xuICAgICAgbWVkaWFLZXlzU2Vzc2lvbkluaXRpYWxpemVkOiBmYWxzZSxcbiAgICAgIG1lZGlhS2V5U3lzdGVtQWNjZXNzOiBtZWRpYUtleVN5c3RlbUFjY2VzcyxcbiAgICAgIG1lZGlhS2V5U3lzdGVtRG9tYWluOiBrZXlTeXN0ZW1cbiAgICB9O1xuXG4gICAgdGhpcy5fbWVkaWFLZXlzTGlzdC5wdXNoKG1lZGlhS2V5c0xpc3RJdGVtKTtcblxuICAgIHZhciBtZWRpYUtleXNQcm9taXNlID0gUHJvbWlzZS5yZXNvbHZlKCkudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICByZXR1cm4gbWVkaWFLZXlTeXN0ZW1BY2Nlc3MuY3JlYXRlTWVkaWFLZXlzKCk7XG4gICAgfSkudGhlbihmdW5jdGlvbiAobWVkaWFLZXlzKSB7XG4gICAgICBtZWRpYUtleXNMaXN0SXRlbS5tZWRpYUtleXMgPSBtZWRpYUtleXM7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiTWVkaWEta2V5cyBjcmVhdGVkIGZvciBrZXktc3lzdGVtIFxcXCJcIiArIGtleVN5c3RlbSArIFwiXFxcIlwiKTtcblxuICAgICAgX3RoaXMzLl9vbk1lZGlhS2V5c0NyZWF0ZWQoKTtcblxuICAgICAgcmV0dXJuIG1lZGlhS2V5cztcbiAgICB9KTtcbiAgICBtZWRpYUtleXNQcm9taXNlLmNhdGNoKGZ1bmN0aW9uIChlcnIpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignRmFpbGVkIHRvIGNyZWF0ZSBtZWRpYS1rZXlzOicsIGVycik7XG4gICAgfSk7XG4gICAgcmV0dXJuIG1lZGlhS2V5c1Byb21pc2U7XG4gIH1cbiAgLyoqXG4gICAqIEhhbmRsZXMga2V5LWNyZWF0aW9uIChyZXByZXNlbnRzIGFjY2VzcyB0byBDRE0pLiBXZSBhcmUgZ29pbmcgdG8gY3JlYXRlIGtleS1zZXNzaW9ucyB1cG9uIHRoaXNcbiAgICogZm9yIGFsbCBleGlzdGluZyBrZXlzIHdoZXJlIG5vIHNlc3Npb24gZXhpc3RzIHlldC5cbiAgICpcbiAgICogQHByaXZhdGVcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX29uTWVkaWFLZXlzQ3JlYXRlZCA9IGZ1bmN0aW9uIF9vbk1lZGlhS2V5c0NyZWF0ZWQoKSB7XG4gICAgdmFyIF90aGlzNCA9IHRoaXM7XG5cbiAgICAvLyBjaGVjayBmb3IgYWxsIGtleS1saXN0IGl0ZW1zIGlmIGEgc2Vzc2lvbiBleGlzdHMsIG90aGVyd2lzZSwgY3JlYXRlIG9uZVxuICAgIHRoaXMuX21lZGlhS2V5c0xpc3QuZm9yRWFjaChmdW5jdGlvbiAobWVkaWFLZXlzTGlzdEl0ZW0pIHtcbiAgICAgIGlmICghbWVkaWFLZXlzTGlzdEl0ZW0ubWVkaWFLZXlzU2Vzc2lvbikge1xuICAgICAgICAvLyBtZWRpYUtleXMgaXMgZGVmaW5pdGVseSBpbml0aWFsaXplZCBoZXJlXG4gICAgICAgIG1lZGlhS2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb24gPSBtZWRpYUtleXNMaXN0SXRlbS5tZWRpYUtleXMuY3JlYXRlU2Vzc2lvbigpO1xuXG4gICAgICAgIF90aGlzNC5fb25OZXdNZWRpYUtleVNlc3Npb24obWVkaWFLZXlzTGlzdEl0ZW0ubWVkaWFLZXlzU2Vzc2lvbik7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgLyoqXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcGFyYW0geyp9IGtleVNlc3Npb25cbiAgICAgKi9cbiAgO1xuXG4gIF9wcm90by5fb25OZXdNZWRpYUtleVNlc3Npb24gPSBmdW5jdGlvbiBfb25OZXdNZWRpYUtleVNlc3Npb24oa2V5U2Vzc2lvbikge1xuICAgIHZhciBfdGhpczUgPSB0aGlzO1xuXG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIk5ldyBrZXktc3lzdGVtIHNlc3Npb24gXCIgKyBrZXlTZXNzaW9uLnNlc3Npb25JZCk7XG4gICAga2V5U2Vzc2lvbi5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZnVuY3Rpb24gKGV2ZW50KSB7XG4gICAgICBfdGhpczUuX29uS2V5U2Vzc2lvbk1lc3NhZ2Uoa2V5U2Vzc2lvbiwgZXZlbnQubWVzc2FnZSk7XG4gICAgfSwgZmFsc2UpO1xuICB9XG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge01lZGlhS2V5U2Vzc2lvbn0ga2V5U2Vzc2lvblxuICAgKiBAcGFyYW0ge0FycmF5QnVmZmVyfSBtZXNzYWdlXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl9vbktleVNlc3Npb25NZXNzYWdlID0gZnVuY3Rpb24gX29uS2V5U2Vzc2lvbk1lc3NhZ2Uoa2V5U2Vzc2lvbiwgbWVzc2FnZSkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ0dvdCBFTUUgbWVzc2FnZSBldmVudCwgY3JlYXRpbmcgbGljZW5zZSByZXF1ZXN0Jyk7XG5cbiAgICB0aGlzLl9yZXF1ZXN0TGljZW5zZShtZXNzYWdlLCBmdW5jdGlvbiAoZGF0YSkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcIlJlY2VpdmVkIGxpY2Vuc2UgZGF0YSAobGVuZ3RoOiBcIiArIChkYXRhID8gZGF0YS5ieXRlTGVuZ3RoIDogZGF0YSkgKyBcIiksIHVwZGF0aW5nIGtleS1zZXNzaW9uXCIpO1xuICAgICAga2V5U2Vzc2lvbi51cGRhdGUoZGF0YSk7XG4gICAgfSk7XG4gIH1cbiAgLyoqXG4gICAqIEBwcml2YXRlXG4gICAqIEBwYXJhbSBlIHtNZWRpYUVuY3J5cHRlZEV2ZW50fVxuICAgKi9cbiAgO1xuXG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgX3Byb3RvLl9hdHRlbXB0U2V0TWVkaWFLZXlzID0gZnVuY3Rpb24gX2F0dGVtcHRTZXRNZWRpYUtleXMobWVkaWFLZXlzKSB7XG4gICAgaWYgKCF0aGlzLl9tZWRpYSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgdG8gc2V0IG1lZGlhS2V5cyB3aXRob3V0IGZpcnN0IGF0dGFjaGluZyBhIG1lZGlhIGVsZW1lbnQnKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuX2hhc1NldE1lZGlhS2V5cykge1xuICAgICAgLy8gRklYTUU6IHNlZSBpZiB3ZSBjYW4vd2FudC9uZWVkLXRvIHJlYWxseSB0byBkZWFsIHdpdGggc2V2ZXJhbCBwb3RlbnRpYWwga2V5LXNlc3Npb25zP1xuICAgICAgdmFyIGtleXNMaXN0SXRlbSA9IHRoaXMuX21lZGlhS2V5c0xpc3RbMF07XG5cbiAgICAgIGlmICgha2V5c0xpc3RJdGVtIHx8ICFrZXlzTGlzdEl0ZW0ubWVkaWFLZXlzKSB7XG4gICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignRmF0YWw6IE1lZGlhIGlzIGVuY3J5cHRlZCBidXQgbm8gQ0RNIGFjY2VzcyBvciBubyBrZXlzIGhhdmUgYmVlbiBvYnRhaW5lZCB5ZXQnKTtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkVSUk9SLCB7XG4gICAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfU1lTVEVNX05PX0tFWVMsXG4gICAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnU2V0dGluZyBrZXlzIGZvciBlbmNyeXB0ZWQgbWVkaWEnKTtcblxuICAgICAgdGhpcy5fbWVkaWEuc2V0TWVkaWFLZXlzKGtleXNMaXN0SXRlbS5tZWRpYUtleXMpO1xuXG4gICAgICB0aGlzLl9oYXNTZXRNZWRpYUtleXMgPSB0cnVlO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX2dlbmVyYXRlUmVxdWVzdFdpdGhQcmVmZXJyZWRLZXlTZXNzaW9uID0gZnVuY3Rpb24gX2dlbmVyYXRlUmVxdWVzdFdpdGhQcmVmZXJyZWRLZXlTZXNzaW9uKGluaXREYXRhVHlwZSwgaW5pdERhdGEpIHtcbiAgICB2YXIgX3RoaXM2ID0gdGhpcztcblxuICAgIC8vIEZJWE1FOiBzZWUgaWYgd2UgY2FuL3dhbnQvbmVlZC10byByZWFsbHkgdG8gZGVhbCB3aXRoIHNldmVyYWwgcG90ZW50aWFsIGtleS1zZXNzaW9ucz9cbiAgICB2YXIga2V5c0xpc3RJdGVtID0gdGhpcy5fbWVkaWFLZXlzTGlzdFswXTtcblxuICAgIGlmICgha2V5c0xpc3RJdGVtKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoJ0ZhdGFsOiBNZWRpYSBpcyBlbmNyeXB0ZWQgYnV0IG5vdCBhbnkga2V5LXN5c3RlbSBhY2Nlc3MgaGFzIGJlZW4gb2J0YWluZWQgeWV0Jyk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9OT19BQ0NFU1MsXG4gICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAoa2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb25Jbml0aWFsaXplZCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oJ0tleS1TZXNzaW9uIGFscmVhZHkgaW5pdGlhbGl6ZWQgYnV0IHJlcXVlc3RlZCBhZ2FpbicpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBrZXlTZXNzaW9uID0ga2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb247XG5cbiAgICBpZiAoIWtleVNlc3Npb24pIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5lcnJvcignRmF0YWw6IE1lZGlhIGlzIGVuY3J5cHRlZCBidXQgbm8ga2V5LXNlc3Npb24gZXhpc3RpbmcnKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLktFWV9TWVNURU1fRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfU1lTVEVNX05PX1NFU1NJT04sXG4gICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9IC8vIGluaXREYXRhIGlzIG51bGwgaWYgdGhlIG1lZGlhIGlzIG5vdCBDT1JTLXNhbWUtb3JpZ2luXG5cblxuICAgIGlmICghaW5pdERhdGEpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCdGYXRhbDogaW5pdERhdGEgcmVxdWlyZWQgZm9yIGdlbmVyYXRpbmcgYSBrZXkgc2Vzc2lvbiBpcyBudWxsJyk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9OT19JTklUX0RBVEEsXG4gICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiR2VuZXJhdGluZyBrZXktc2Vzc2lvbiByZXF1ZXN0IGZvciBcXFwiXCIgKyBpbml0RGF0YVR5cGUgKyBcIlxcXCIgaW5pdCBkYXRhIHR5cGVcIik7XG4gICAga2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb25Jbml0aWFsaXplZCA9IHRydWU7XG4gICAga2V5U2Vzc2lvbi5nZW5lcmF0ZVJlcXVlc3QoaW5pdERhdGFUeXBlLCBpbml0RGF0YSkudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZGVidWcoJ0tleS1zZXNzaW9uIGdlbmVyYXRpb24gc3VjY2VlZGVkJyk7XG4gICAgfSkuY2F0Y2goZnVuY3Rpb24gKGVycikge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKCdFcnJvciBnZW5lcmF0aW5nIGtleS1zZXNzaW9uIHJlcXVlc3Q6JywgZXJyKTtcblxuICAgICAgX3RoaXM2Lmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9OT19TRVNTSU9OLFxuICAgICAgICBmYXRhbDogZmFsc2VcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsIExpY2Vuc2Ugc2VydmVyIFVSTFxuICAgKiBAcGFyYW0ge0FycmF5QnVmZmVyfSBrZXlNZXNzYWdlIE1lc3NhZ2UgZGF0YSBpc3N1ZWQgYnkga2V5LXN5c3RlbVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBjYWxsYmFjayBDYWxsZWQgd2hlbiBYSFIgaGFzIHN1Y2NlZWRlZFxuICAgKiBAcmV0dXJucyB7WE1MSHR0cFJlcXVlc3R9IFVuc2VudCAoYnV0IG9wZW5lZCBzdGF0ZSkgWEhSIG9iamVjdFxuICAgKiBAdGhyb3dzIGlmIFhNTEh0dHBSZXF1ZXN0IGNvbnN0cnVjdGlvbiBmYWlsZWRcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX2NyZWF0ZUxpY2Vuc2VYaHIgPSBmdW5jdGlvbiBfY3JlYXRlTGljZW5zZVhocih1cmwsIGtleU1lc3NhZ2UsIGNhbGxiYWNrKSB7XG4gICAgdmFyIHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgIHZhciBsaWNlbnNlWGhyU2V0dXAgPSB0aGlzLl9saWNlbnNlWGhyU2V0dXA7XG5cbiAgICB0cnkge1xuICAgICAgaWYgKGxpY2Vuc2VYaHJTZXR1cCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGxpY2Vuc2VYaHJTZXR1cCh4aHIsIHVybCk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAvLyBsZXQncyB0cnkgdG8gb3BlbiBiZWZvcmUgcnVubmluZyBzZXR1cFxuICAgICAgICAgIHhoci5vcGVuKCdQT1NUJywgdXJsLCB0cnVlKTtcbiAgICAgICAgICBsaWNlbnNlWGhyU2V0dXAoeGhyLCB1cmwpO1xuICAgICAgICB9XG4gICAgICB9IC8vIGlmIGxpY2Vuc2VYaHJTZXR1cCBkaWQgbm90IHlldCBjYWxsIG9wZW4sIGxldCdzIGRvIGl0IG5vd1xuXG5cbiAgICAgIGlmICgheGhyLnJlYWR5U3RhdGUpIHtcbiAgICAgICAgeGhyLm9wZW4oJ1BPU1QnLCB1cmwsIHRydWUpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIC8vIElFMTEgdGhyb3dzIGFuIGV4Y2VwdGlvbiBvbiB4aHIub3BlbiBpZiBhdHRlbXB0aW5nIHRvIGFjY2VzcyBhbiBIVFRQIHJlc291cmNlIG92ZXIgSFRUUFNcbiAgICAgIHRocm93IG5ldyBFcnJvcihcImlzc3VlIHNldHRpbmcgdXAgS2V5U3lzdGVtIGxpY2Vuc2UgWEhSIFwiICsgZSk7XG4gICAgfSAvLyBCZWNhdXNlIHdlIHNldCByZXNwb25zZVR5cGUgdG8gQXJyYXlCdWZmZXIgaGVyZSwgY2FsbGJhY2sgaXMgdHlwZWQgYXMgaGFuZGxpbmcgb25seSBhcnJheSBidWZmZXJzXG5cblxuICAgIHhoci5yZXNwb25zZVR5cGUgPSAnYXJyYXlidWZmZXInO1xuICAgIHhoci5vbnJlYWR5c3RhdGVjaGFuZ2UgPSB0aGlzLl9vbkxpY2Vuc2VSZXF1ZXN0UmVhZHlTdGFnZUNoYW5nZS5iaW5kKHRoaXMsIHhociwgdXJsLCBrZXlNZXNzYWdlLCBjYWxsYmFjayk7XG4gICAgcmV0dXJuIHhocjtcbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtYTUxIdHRwUmVxdWVzdH0geGhyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgTGljZW5zZSBzZXJ2ZXIgVVJMXG4gICAqIEBwYXJhbSB7QXJyYXlCdWZmZXJ9IGtleU1lc3NhZ2UgTWVzc2FnZSBkYXRhIGlzc3VlZCBieSBrZXktc3lzdGVtXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrIENhbGxlZCB3aGVuIFhIUiBoYXMgc3VjY2VlZGVkXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl9vbkxpY2Vuc2VSZXF1ZXN0UmVhZHlTdGFnZUNoYW5nZSA9IGZ1bmN0aW9uIF9vbkxpY2Vuc2VSZXF1ZXN0UmVhZHlTdGFnZUNoYW5nZSh4aHIsIHVybCwga2V5TWVzc2FnZSwgY2FsbGJhY2spIHtcbiAgICBzd2l0Y2ggKHhoci5yZWFkeVN0YXRlKSB7XG4gICAgICBjYXNlIDQ6XG4gICAgICAgIGlmICh4aHIuc3RhdHVzID09PSAyMDApIHtcbiAgICAgICAgICB0aGlzLl9yZXF1ZXN0TGljZW5zZUZhaWx1cmVDb3VudCA9IDA7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnTGljZW5zZSByZXF1ZXN0IHN1Y2NlZWRlZCcpO1xuXG4gICAgICAgICAgaWYgKHhoci5yZXNwb25zZVR5cGUgIT09ICdhcnJheWJ1ZmZlcicpIHtcbiAgICAgICAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS53YXJuKCd4aHIgcmVzcG9uc2UgdHlwZSB3YXMgbm90IHNldCB0byB0aGUgZXhwZWN0ZWQgYXJyYXlidWZmZXIgZm9yIGxpY2Vuc2UgcmVxdWVzdCcpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNhbGxiYWNrKHhoci5yZXNwb25zZSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKFwiTGljZW5zZSBSZXF1ZXN0IFhIUiBmYWlsZWQgKFwiICsgdXJsICsgXCIpLiBTdGF0dXM6IFwiICsgeGhyLnN0YXR1cyArIFwiIChcIiArIHhoci5zdGF0dXNUZXh0ICsgXCIpXCIpO1xuICAgICAgICAgIHRoaXMuX3JlcXVlc3RMaWNlbnNlRmFpbHVyZUNvdW50Kys7XG5cbiAgICAgICAgICBpZiAodGhpcy5fcmVxdWVzdExpY2Vuc2VGYWlsdXJlQ291bnQgPiBNQVhfTElDRU5TRV9SRVFVRVNUX0ZBSUxVUkVTKSB7XG4gICAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9MSUNFTlNFX1JFUVVFU1RfRkFJTEVELFxuICAgICAgICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdmFyIGF0dGVtcHRzTGVmdCA9IE1BWF9MSUNFTlNFX1JFUVVFU1RfRkFJTFVSRVMgLSB0aGlzLl9yZXF1ZXN0TGljZW5zZUZhaWx1cmVDb3VudCArIDE7XG4gICAgICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLndhcm4oXCJSZXRyeWluZyBsaWNlbnNlIHJlcXVlc3QsIFwiICsgYXR0ZW1wdHNMZWZ0ICsgXCIgYXR0ZW1wdHMgbGVmdFwiKTtcblxuICAgICAgICAgIHRoaXMuX3JlcXVlc3RMaWNlbnNlKGtleU1lc3NhZ2UsIGNhbGxiYWNrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIHtNZWRpYUtleXNMaXN0SXRlbX0ga2V5c0xpc3RJdGVtXG4gICAqIEBwYXJhbSB7QXJyYXlCdWZmZXJ9IGtleU1lc3NhZ2VcbiAgICogQHJldHVybnMge0FycmF5QnVmZmVyfSBDaGFsbGVuZ2UgZGF0YSBwb3N0ZWQgdG8gbGljZW5zZSBzZXJ2ZXJcbiAgICogQHRocm93cyBpZiBLZXlTeXN0ZW0gaXMgdW5zdXBwb3J0ZWRcbiAgICovXG4gIDtcblxuICBfcHJvdG8uX2dlbmVyYXRlTGljZW5zZVJlcXVlc3RDaGFsbGVuZ2UgPSBmdW5jdGlvbiBfZ2VuZXJhdGVMaWNlbnNlUmVxdWVzdENoYWxsZW5nZShrZXlzTGlzdEl0ZW0sIGtleU1lc3NhZ2UpIHtcbiAgICBzd2l0Y2ggKGtleXNMaXN0SXRlbS5tZWRpYUtleVN5c3RlbURvbWFpbikge1xuICAgICAgLy8gY2FzZSBLZXlTeXN0ZW1zLlBMQVlSRUFEWTpcbiAgICAgIC8vIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL01pY3Jvc29mdEVkZ2UvRGVtb3MvYmxvYi9tYXN0ZXIvZW1lL3NjcmlwdHMvZGVtby5qc1xuXG4gICAgICAvKlxuICAgICAgICBpZiAodGhpcy5saWNlbnNlVHlwZSAhPT0gdGhpcy5MSUNFTlNFX1RZUEVfV0lERVZJTkUpIHtcbiAgICAgICAgICAvLyBGb3IgUGxheVJlYWR5IENETXMsIHdlIG5lZWQgdG8gZGlnIHRoZSBDaGFsbGVuZ2Ugb3V0IG9mIHRoZSBYTUwuXG4gICAgICAgICAgdmFyIGtleU1lc3NhZ2VYbWwgPSBuZXcgRE9NUGFyc2VyKCkucGFyc2VGcm9tU3RyaW5nKFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCwgbmV3IFVpbnQxNkFycmF5KGtleU1lc3NhZ2UpKSwgJ2FwcGxpY2F0aW9uL3htbCcpO1xuICAgICAgICAgIGlmIChrZXlNZXNzYWdlWG1sLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdDaGFsbGVuZ2UnKVswXSkge1xuICAgICAgICAgICAgICBjaGFsbGVuZ2UgPSBhdG9iKGtleU1lc3NhZ2VYbWwuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ0NoYWxsZW5nZScpWzBdLmNoaWxkTm9kZXNbMF0ubm9kZVZhbHVlKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB0aHJvdyAnQ2Fubm90IGZpbmQgPENoYWxsZW5nZT4gaW4ga2V5IG1lc3NhZ2UnO1xuICAgICAgICAgIH1cbiAgICAgICAgICB2YXIgaGVhZGVyTmFtZXMgPSBrZXlNZXNzYWdlWG1sLmdldEVsZW1lbnRzQnlUYWdOYW1lKCduYW1lJyk7XG4gICAgICAgICAgdmFyIGhlYWRlclZhbHVlcyA9IGtleU1lc3NhZ2VYbWwuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3ZhbHVlJyk7XG4gICAgICAgICAgaWYgKGhlYWRlck5hbWVzLmxlbmd0aCAhPT0gaGVhZGVyVmFsdWVzLmxlbmd0aCkge1xuICAgICAgICAgICAgICB0aHJvdyAnTWlzbWF0Y2hlZCBoZWFkZXIgPG5hbWU+Lzx2YWx1ZT4gcGFpciBpbiBrZXkgbWVzc2FnZSc7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgaGVhZGVyTmFtZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgeGhyLnNldFJlcXVlc3RIZWFkZXIoaGVhZGVyTmFtZXNbaV0uY2hpbGROb2Rlc1swXS5ub2RlVmFsdWUsIGhlYWRlclZhbHVlc1tpXS5jaGlsZE5vZGVzWzBdLm5vZGVWYWx1ZSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgKi9cbiAgICAgIGNhc2UgS2V5U3lzdGVtcy5XSURFVklORTpcbiAgICAgICAgLy8gRm9yIFdpZGV2aW5lIENETXMsIHRoZSBjaGFsbGVuZ2UgaXMgdGhlIGtleU1lc3NhZ2UuXG4gICAgICAgIHJldHVybiBrZXlNZXNzYWdlO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBFcnJvcihcInVuc3VwcG9ydGVkIGtleS1zeXN0ZW06IFwiICsga2V5c0xpc3RJdGVtLm1lZGlhS2V5U3lzdGVtRG9tYWluKTtcbiAgfVxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIGtleU1lc3NhZ2VcbiAgICogQHBhcmFtIGNhbGxiYWNrXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLl9yZXF1ZXN0TGljZW5zZSA9IGZ1bmN0aW9uIF9yZXF1ZXN0TGljZW5zZShrZXlNZXNzYWdlLCBjYWxsYmFjaykge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ1JlcXVlc3RpbmcgY29udGVudCBsaWNlbnNlIGZvciBrZXktc3lzdGVtJyk7XG4gICAgdmFyIGtleXNMaXN0SXRlbSA9IHRoaXMuX21lZGlhS2V5c0xpc3RbMF07XG5cbiAgICBpZiAoIWtleXNMaXN0SXRlbSkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmVycm9yKCdGYXRhbCBlcnJvcjogTWVkaWEgaXMgZW5jcnlwdGVkIGJ1dCBubyBrZXktc3lzdGVtIGFjY2VzcyBoYXMgYmVlbiBvYnRhaW5lZCB5ZXQnKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5FUlJPUiwge1xuICAgICAgICB0eXBlOiBlcnJvcnNbXCJFcnJvclR5cGVzXCJdLktFWV9TWVNURU1fRVJST1IsXG4gICAgICAgIGRldGFpbHM6IGVycm9yc1tcIkVycm9yRGV0YWlsc1wiXS5LRVlfU1lTVEVNX05PX0FDQ0VTUyxcbiAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICB2YXIgX3VybCA9IHRoaXMuZ2V0TGljZW5zZVNlcnZlclVybChrZXlzTGlzdEl0ZW0ubWVkaWFLZXlTeXN0ZW1Eb21haW4pO1xuXG4gICAgICB2YXIgX3hociA9IHRoaXMuX2NyZWF0ZUxpY2Vuc2VYaHIoX3VybCwga2V5TWVzc2FnZSwgY2FsbGJhY2spO1xuXG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwiU2VuZGluZyBsaWNlbnNlIHJlcXVlc3QgdG8gVVJMOiBcIiArIF91cmwpO1xuXG4gICAgICB2YXIgY2hhbGxlbmdlID0gdGhpcy5fZ2VuZXJhdGVMaWNlbnNlUmVxdWVzdENoYWxsZW5nZShrZXlzTGlzdEl0ZW0sIGtleU1lc3NhZ2UpO1xuXG4gICAgICBfeGhyLnNlbmQoY2hhbGxlbmdlKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0uZXJyb3IoXCJGYWlsdXJlIHJlcXVlc3RpbmcgRFJNIGxpY2Vuc2U6IFwiICsgZSk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKGV2ZW50c1tcImRlZmF1bHRcIl0uRVJST1IsIHtcbiAgICAgICAgdHlwZTogZXJyb3JzW1wiRXJyb3JUeXBlc1wiXS5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl0uS0VZX1NZU1RFTV9MSUNFTlNFX1JFUVVFU1RfRkFJTEVELFxuICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhQXR0YWNoZWQgPSBmdW5jdGlvbiBvbk1lZGlhQXR0YWNoZWQoZGF0YSkge1xuICAgIGlmICghdGhpcy5fZW1lRW5hYmxlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHZhciBtZWRpYSA9IGRhdGEubWVkaWE7IC8vIGtlZXAgcmVmZXJlbmNlIG9mIG1lZGlhXG5cbiAgICB0aGlzLl9tZWRpYSA9IG1lZGlhO1xuICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ2VuY3J5cHRlZCcsIHRoaXMuX29uTWVkaWFFbmNyeXB0ZWQpO1xuICB9O1xuXG4gIF9wcm90by5vbk1lZGlhRGV0YWNoZWQgPSBmdW5jdGlvbiBvbk1lZGlhRGV0YWNoZWQoKSB7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5fbWVkaWE7XG4gICAgdmFyIG1lZGlhS2V5c0xpc3QgPSB0aGlzLl9tZWRpYUtleXNMaXN0O1xuXG4gICAgaWYgKCFtZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIG1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2VuY3J5cHRlZCcsIHRoaXMuX29uTWVkaWFFbmNyeXB0ZWQpO1xuICAgIHRoaXMuX21lZGlhID0gbnVsbDtcbiAgICB0aGlzLl9tZWRpYUtleXNMaXN0ID0gW107IC8vIENsb3NlIGFsbCBzZXNzaW9ucyBhbmQgcmVtb3ZlIG1lZGlhIGtleXMgZnJvbSB0aGUgdmlkZW8gZWxlbWVudC5cblxuICAgIFByb21pc2UuYWxsKG1lZGlhS2V5c0xpc3QubWFwKGZ1bmN0aW9uIChtZWRpYUtleXNMaXN0SXRlbSkge1xuICAgICAgaWYgKG1lZGlhS2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb24pIHtcbiAgICAgICAgcmV0dXJuIG1lZGlhS2V5c0xpc3RJdGVtLm1lZGlhS2V5c1Nlc3Npb24uY2xvc2UoKS5jYXRjaChmdW5jdGlvbiAoKSB7Ly8gSWdub3JlIGVycm9ycyB3aGVuIGNsb3NpbmcgdGhlIHNlc3Npb25zLiBDbG9zaW5nIGEgc2Vzc2lvbiB0aGF0XG4gICAgICAgICAgLy8gZ2VuZXJhdGVkIG5vIGtleSByZXF1ZXN0cyB3aWxsIHRocm93IGFuIGVycm9yLlxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KSkudGhlbihmdW5jdGlvbiAoKSB7XG4gICAgICByZXR1cm4gbWVkaWEuc2V0TWVkaWFLZXlzKG51bGwpO1xuICAgIH0pLmNhdGNoKGZ1bmN0aW9uICgpIHsvLyBJZ25vcmUgYW55IGZhaWx1cmVzIHdoaWxlIHJlbW92aW5nIG1lZGlhIGtleXMgZnJvbSB0aGUgdmlkZW8gZWxlbWVudC5cbiAgICB9KTtcbiAgfSAvLyBUT0RPOiBVc2UgbWFuaWZlc3QgdHlwZXMgaGVyZSB3aGVuIHRoZXkgYXJlIGRlZmluZWRcbiAgO1xuXG4gIF9wcm90by5vbk1hbmlmZXN0UGFyc2VkID0gZnVuY3Rpb24gb25NYW5pZmVzdFBhcnNlZChkYXRhKSB7XG4gICAgaWYgKCF0aGlzLl9lbWVFbmFibGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdmFyIGF1ZGlvQ29kZWNzID0gZGF0YS5sZXZlbHMubWFwKGZ1bmN0aW9uIChsZXZlbCkge1xuICAgICAgcmV0dXJuIGxldmVsLmF1ZGlvQ29kZWM7XG4gICAgfSk7XG4gICAgdmFyIHZpZGVvQ29kZWNzID0gZGF0YS5sZXZlbHMubWFwKGZ1bmN0aW9uIChsZXZlbCkge1xuICAgICAgcmV0dXJuIGxldmVsLnZpZGVvQ29kZWM7XG4gICAgfSk7XG5cbiAgICB0aGlzLl9hdHRlbXB0S2V5U3lzdGVtQWNjZXNzKEtleVN5c3RlbXMuV0lERVZJTkUsIGF1ZGlvQ29kZWNzLCB2aWRlb0NvZGVjcyk7XG4gIH07XG5cbiAgZW1lX2NvbnRyb2xsZXJfY3JlYXRlQ2xhc3MoRU1FQ29udHJvbGxlciwgW3tcbiAgICBrZXk6IFwicmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICBpZiAoIXRoaXMuX3JlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzcykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIHJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2VzcyBmdW5jdGlvbiBjb25maWd1cmVkJyk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0aGlzLl9yZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3M7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIEVNRUNvbnRyb2xsZXI7XG59KGV2ZW50X2hhbmRsZXIpO1xuXG4vKiBoYXJtb255IGRlZmF1bHQgZXhwb3J0ICovIHZhciBlbWVfY29udHJvbGxlciA9IChlbWVfY29udHJvbGxlcl9FTUVDb250cm9sbGVyKTtcbi8vIENPTkNBVEVOQVRFRCBNT0RVTEU6IC4vc3JjL2NvbmZpZy50c1xuZnVuY3Rpb24gb3duS2V5cyhvYmplY3QsIGVudW1lcmFibGVPbmx5KSB7IHZhciBrZXlzID0gT2JqZWN0LmtleXMob2JqZWN0KTsgaWYgKE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMpIHsgdmFyIHN5bWJvbHMgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKG9iamVjdCk7IGlmIChlbnVtZXJhYmxlT25seSkgc3ltYm9scyA9IHN5bWJvbHMuZmlsdGVyKGZ1bmN0aW9uIChzeW0pIHsgcmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBzeW0pLmVudW1lcmFibGU7IH0pOyBrZXlzLnB1c2guYXBwbHkoa2V5cywgc3ltYm9scyk7IH0gcmV0dXJuIGtleXM7IH1cblxuZnVuY3Rpb24gX29iamVjdFNwcmVhZCh0YXJnZXQpIHsgZm9yICh2YXIgaSA9IDE7IGkgPCBhcmd1bWVudHMubGVuZ3RoOyBpKyspIHsgdmFyIHNvdXJjZSA9IGFyZ3VtZW50c1tpXSAhPSBudWxsID8gYXJndW1lbnRzW2ldIDoge307IGlmIChpICUgMikgeyBvd25LZXlzKE9iamVjdChzb3VyY2UpLCB0cnVlKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHsgX2RlZmluZVByb3BlcnR5KHRhcmdldCwga2V5LCBzb3VyY2Vba2V5XSk7IH0pOyB9IGVsc2UgaWYgKE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKSB7IE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcnMoc291cmNlKSk7IH0gZWxzZSB7IG93bktleXMoT2JqZWN0KHNvdXJjZSkpLmZvckVhY2goZnVuY3Rpb24gKGtleSkgeyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBrZXksIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Ioc291cmNlLCBrZXkpKTsgfSk7IH0gfSByZXR1cm4gdGFyZ2V0OyB9XG5cbmZ1bmN0aW9uIF9kZWZpbmVQcm9wZXJ0eShvYmosIGtleSwgdmFsdWUpIHsgaWYgKGtleSBpbiBvYmopIHsgT2JqZWN0LmRlZmluZVByb3BlcnR5KG9iaiwga2V5LCB7IHZhbHVlOiB2YWx1ZSwgZW51bWVyYWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlLCB3cml0YWJsZTogdHJ1ZSB9KTsgfSBlbHNlIHsgb2JqW2tleV0gPSB2YWx1ZTsgfSByZXR1cm4gb2JqOyB9XG5cbi8qKlxuICogSExTIGNvbmZpZ1xuICovXG5cblxuXG5cbiAvLyBpbXBvcnQgRmV0Y2hMb2FkZXIgZnJvbSAnLi91dGlscy9mZXRjaC1sb2FkZXInO1xuXG5cblxuXG5cblxuXG5cblxuLy8gSWYgcG9zc2libGUsIGtlZXAgaGxzRGVmYXVsdENvbmZpZyBzaGFsbG93XG4vLyBJdCBpcyBjbG9uZWQgd2hlbmV2ZXIgYSBuZXcgSGxzIGluc3RhbmNlIGlzIGNyZWF0ZWQsIGJ5IGtlZXBpbmcgdGhlIGNvbmZpZ1xuLy8gc2hhbGxvdyB0aGUgcHJvcGVydGllcyBhcmUgY2xvbmVkLCBhbmQgd2UgZG9uJ3QgZW5kIHVwIG1hbmlwdWxhdGluZyB0aGUgZGVmYXVsdFxudmFyIGhsc0RlZmF1bHRDb25maWcgPSBfb2JqZWN0U3ByZWFkKF9vYmplY3RTcHJlYWQoe1xuICBhdXRvU3RhcnRMb2FkOiB0cnVlLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIHN0YXJ0UG9zaXRpb246IC0xLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGRlZmF1bHRBdWRpb0NvZGVjOiB2b2lkIDAsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgZGVidWc6IGZhbHNlLFxuICAvLyB1c2VkIGJ5IGxvZ2dlclxuICBjYXBMZXZlbE9uRlBTRHJvcDogZmFsc2UsXG4gIC8vIHVzZWQgYnkgZnBzLWNvbnRyb2xsZXJcbiAgY2FwTGV2ZWxUb1BsYXllclNpemU6IGZhbHNlLFxuICAvLyB1c2VkIGJ5IGNhcC1sZXZlbC1jb250cm9sbGVyXG4gIGluaXRpYWxMaXZlTWFuaWZlc3RTaXplOiAxLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIG1heEJ1ZmZlckxlbmd0aDogMzAsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbWF4QnVmZmVyU2l6ZTogNjAgKiAxMDAwICogMTAwMCxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBtYXhCdWZmZXJIb2xlOiAwLjUsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbG93QnVmZmVyV2F0Y2hkb2dQZXJpb2Q6IDAuNSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBoaWdoQnVmZmVyV2F0Y2hkb2dQZXJpb2Q6IDMsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbnVkZ2VPZmZzZXQ6IDAuMSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBudWRnZU1heFJldHJ5OiAzLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIG1heEZyYWdMb29rVXBUb2xlcmFuY2U6IDAuMjUsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbGl2ZVN5bmNEdXJhdGlvbkNvdW50OiAzLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGxpdmVNYXhMYXRlbmN5RHVyYXRpb25Db3VudDogSW5maW5pdHksXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbGl2ZVN5bmNEdXJhdGlvbjogdm9pZCAwLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGxpdmVNYXhMYXRlbmN5RHVyYXRpb246IHZvaWQgMCxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBsaXZlRHVyYXRpb25JbmZpbml0eTogZmFsc2UsXG4gIC8vIHVzZWQgYnkgYnVmZmVyLWNvbnRyb2xsZXJcbiAgbGl2ZUJhY2tCdWZmZXJMZW5ndGg6IEluZmluaXR5LFxuICAvLyB1c2VkIGJ5IGJ1ZmZlci1jb250cm9sbGVyXG4gIG1heE1heEJ1ZmZlckxlbmd0aDogNjAwLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGVuYWJsZVdvcmtlcjogdHJ1ZSxcbiAgLy8gdXNlZCBieSBkZW11eGVyXG4gIGVuYWJsZVNvZnR3YXJlQUVTOiB0cnVlLFxuICAvLyB1c2VkIGJ5IGRlY3J5cHRlclxuICBtYW5pZmVzdExvYWRpbmdUaW1lT3V0OiAxMDAwMCxcbiAgLy8gdXNlZCBieSBwbGF5bGlzdC1sb2FkZXJcbiAgbWFuaWZlc3RMb2FkaW5nTWF4UmV0cnk6IDEsXG4gIC8vIHVzZWQgYnkgcGxheWxpc3QtbG9hZGVyXG4gIG1hbmlmZXN0TG9hZGluZ1JldHJ5RGVsYXk6IDEwMDAsXG4gIC8vIHVzZWQgYnkgcGxheWxpc3QtbG9hZGVyXG4gIG1hbmlmZXN0TG9hZGluZ01heFJldHJ5VGltZW91dDogNjQwMDAsXG4gIC8vIHVzZWQgYnkgcGxheWxpc3QtbG9hZGVyXG4gIHN0YXJ0TGV2ZWw6IHZvaWQgMCxcbiAgLy8gdXNlZCBieSBsZXZlbC1jb250cm9sbGVyXG4gIGxldmVsTG9hZGluZ1RpbWVPdXQ6IDEwMDAwLFxuICAvLyB1c2VkIGJ5IHBsYXlsaXN0LWxvYWRlclxuICBsZXZlbExvYWRpbmdNYXhSZXRyeTogNCxcbiAgLy8gdXNlZCBieSBwbGF5bGlzdC1sb2FkZXJcbiAgbGV2ZWxMb2FkaW5nUmV0cnlEZWxheTogMTAwMCxcbiAgLy8gdXNlZCBieSBwbGF5bGlzdC1sb2FkZXJcbiAgbGV2ZWxMb2FkaW5nTWF4UmV0cnlUaW1lb3V0OiA2NDAwMCxcbiAgLy8gdXNlZCBieSBwbGF5bGlzdC1sb2FkZXJcbiAgZnJhZ0xvYWRpbmdUaW1lT3V0OiAyMDAwMCxcbiAgLy8gdXNlZCBieSBmcmFnbWVudC1sb2FkZXJcbiAgZnJhZ0xvYWRpbmdNYXhSZXRyeTogNixcbiAgLy8gdXNlZCBieSBmcmFnbWVudC1sb2FkZXJcbiAgZnJhZ0xvYWRpbmdSZXRyeURlbGF5OiAxMDAwLFxuICAvLyB1c2VkIGJ5IGZyYWdtZW50LWxvYWRlclxuICBmcmFnTG9hZGluZ01heFJldHJ5VGltZW91dDogNjQwMDAsXG4gIC8vIHVzZWQgYnkgZnJhZ21lbnQtbG9hZGVyXG4gIHN0YXJ0RnJhZ1ByZWZldGNoOiBmYWxzZSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBmcHNEcm9wcGVkTW9uaXRvcmluZ1BlcmlvZDogNTAwMCxcbiAgLy8gdXNlZCBieSBmcHMtY29udHJvbGxlclxuICBmcHNEcm9wcGVkTW9uaXRvcmluZ1RocmVzaG9sZDogMC4yLFxuICAvLyB1c2VkIGJ5IGZwcy1jb250cm9sbGVyXG4gIGFwcGVuZEVycm9yTWF4UmV0cnk6IDMsXG4gIC8vIHVzZWQgYnkgYnVmZmVyLWNvbnRyb2xsZXJcbiAgbG9hZGVyOiB4aHJfbG9hZGVyLFxuICAvLyBsb2FkZXI6IEZldGNoTG9hZGVyLFxuICBmTG9hZGVyOiB2b2lkIDAsXG4gIC8vIHVzZWQgYnkgZnJhZ21lbnQtbG9hZGVyXG4gIHBMb2FkZXI6IHZvaWQgMCxcbiAgLy8gdXNlZCBieSBwbGF5bGlzdC1sb2FkZXJcbiAgeGhyU2V0dXA6IHZvaWQgMCxcbiAgLy8gdXNlZCBieSB4aHItbG9hZGVyXG4gIGxpY2Vuc2VYaHJTZXR1cDogdm9pZCAwLFxuICAvLyB1c2VkIGJ5IGVtZS1jb250cm9sbGVyXG4gIC8vIGZldGNoU2V0dXA6IHZvaWQgMCxcbiAgYWJyQ29udHJvbGxlcjogYWJyX2NvbnRyb2xsZXIsXG4gIGJ1ZmZlckNvbnRyb2xsZXI6IGJ1ZmZlcl9jb250cm9sbGVyLFxuICBjYXBMZXZlbENvbnRyb2xsZXI6IGNhcF9sZXZlbF9jb250cm9sbGVyLFxuICBmcHNDb250cm9sbGVyOiBmcHNfY29udHJvbGxlcixcbiAgc3RyZXRjaFNob3J0VmlkZW9UcmFjazogZmFsc2UsXG4gIC8vIHVzZWQgYnkgbXA0LXJlbXV4ZXJcbiAgbWF4QXVkaW9GcmFtZXNEcmlmdDogMSxcbiAgLy8gdXNlZCBieSBtcDQtcmVtdXhlclxuICBmb3JjZUtleUZyYW1lT25EaXNjb250aW51aXR5OiB0cnVlLFxuICAvLyB1c2VkIGJ5IHRzLWRlbXV4ZXJcbiAgYWJyRXdtYUZhc3RMaXZlOiAzLFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFickV3bWFTbG93TGl2ZTogOSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hRmFzdFZvRDogMyxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hU2xvd1ZvRDogOSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hRGVmYXVsdEVzdGltYXRlOiA1ZTUsXG4gIC8vIDUwMCBrYnBzICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFickJhbmRXaWR0aEZhY3RvcjogMC45NSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJCYW5kV2lkdGhVcEZhY3RvcjogMC43LFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFick1heFdpdGhSZWFsQml0cmF0ZTogZmFsc2UsXG4gIC8vIHVzZWQgYnkgYWJyLWNvbnRyb2xsZXJcbiAgbWF4U3RhcnZhdGlvbkRlbGF5OiA0LFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIG1heExvYWRpbmdEZWxheTogNCxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBtaW5BdXRvQml0cmF0ZTogMCxcbiAgLy8gdXNlZCBieSBobHNcbiAgZW1lRW5hYmxlZDogZmFsc2UsXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgd2lkZXZpbmVMaWNlbnNlVXJsOiB2b2lkIDAsXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgZHJtU3lzdGVtT3B0aW9uczoge30sXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzRnVuYzogcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzLFxuICAvLyB1c2VkIGJ5IGVtZS1jb250cm9sbGVyXG4gIHRlc3RCYW5kd2lkdGg6IHRydWVcbn0sIHRpbWVsaW5lQ29uZmlnKCkpLCB7fSwge1xuICBzdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXI6ICB0cnVlID8gc3VidGl0bGVfc3RyZWFtX2NvbnRyb2xsZXJfU3VidGl0bGVTdHJlYW1Db250cm9sbGVyIDogdW5kZWZpbmVkLFxuICBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjogIHRydWUgPyBzdWJ0aXRsZV90cmFja19jb250cm9sbGVyIDogdW5kZWZpbmVkLFxuICB0aW1lbGluZUNvbnRyb2xsZXI6ICB0cnVlID8gdGltZWxpbmVfY29udHJvbGxlciA6IHVuZGVmaW5lZCxcbiAgYXVkaW9TdHJlYW1Db250cm9sbGVyOiAgdHJ1ZSA/IGF1ZGlvX3N0cmVhbV9jb250cm9sbGVyIDogdW5kZWZpbmVkLFxuICBhdWRpb1RyYWNrQ29udHJvbGxlcjogIHRydWUgPyBhdWRpb190cmFja19jb250cm9sbGVyIDogdW5kZWZpbmVkLFxuICBlbWVDb250cm9sbGVyOiAgdHJ1ZSA/IGVtZV9jb250cm9sbGVyIDogdW5kZWZpbmVkXG59KTtcblxuZnVuY3Rpb24gdGltZWxpbmVDb25maWcoKSB7XG4gIHJldHVybiB7XG4gICAgY3VlSGFuZGxlcjogY3Vlc19uYW1lc3BhY2VPYmplY3QsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgZW5hYmxlQ0VBNzA4Q2FwdGlvbnM6IHRydWUsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgZW5hYmxlV2ViVlRUOiB0cnVlLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrMUxhYmVsOiAnRW5nbGlzaCcsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgY2FwdGlvbnNUZXh0VHJhY2sxTGFuZ3VhZ2VDb2RlOiAnZW4nLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrMkxhYmVsOiAnU3BhbmlzaCcsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgY2FwdGlvbnNUZXh0VHJhY2syTGFuZ3VhZ2VDb2RlOiAnZXMnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrM0xhYmVsOiAnVW5rbm93biBDQycsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgY2FwdGlvbnNUZXh0VHJhY2szTGFuZ3VhZ2VDb2RlOiAnJyxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBjYXB0aW9uc1RleHRUcmFjazRMYWJlbDogJ1Vua25vd24gQ0MnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrNExhbmd1YWdlQ29kZTogJycsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgcmVuZGVyVGV4dFRyYWNrc05hdGl2ZWx5OiB0cnVlXG4gIH07XG59XG4vLyBDT05DQVRFTkFURUQgTU9EVUxFOiAuL3NyYy9obHMudHNcbmZ1bmN0aW9uIGhsc19vd25LZXlzKG9iamVjdCwgZW51bWVyYWJsZU9ubHkpIHsgdmFyIGtleXMgPSBPYmplY3Qua2V5cyhvYmplY3QpOyBpZiAoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scykgeyB2YXIgc3ltYm9scyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMob2JqZWN0KTsgaWYgKGVudW1lcmFibGVPbmx5KSBzeW1ib2xzID0gc3ltYm9scy5maWx0ZXIoZnVuY3Rpb24gKHN5bSkgeyByZXR1cm4gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmplY3QsIHN5bSkuZW51bWVyYWJsZTsgfSk7IGtleXMucHVzaC5hcHBseShrZXlzLCBzeW1ib2xzKTsgfSByZXR1cm4ga2V5czsgfVxuXG5mdW5jdGlvbiBobHNfb2JqZWN0U3ByZWFkKHRhcmdldCkgeyBmb3IgKHZhciBpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGg7IGkrKykgeyB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldICE9IG51bGwgPyBhcmd1bWVudHNbaV0gOiB7fTsgaWYgKGkgJSAyKSB7IGhsc19vd25LZXlzKE9iamVjdChzb3VyY2UpLCB0cnVlKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHsgaGxzX2RlZmluZVByb3BlcnR5KHRhcmdldCwga2V5LCBzb3VyY2Vba2V5XSk7IH0pOyB9IGVsc2UgaWYgKE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKSB7IE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcnMoc291cmNlKSk7IH0gZWxzZSB7IGhsc19vd25LZXlzKE9iamVjdChzb3VyY2UpKS5mb3JFYWNoKGZ1bmN0aW9uIChrZXkpIHsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwga2V5LCBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHNvdXJjZSwga2V5KSk7IH0pOyB9IH0gcmV0dXJuIHRhcmdldDsgfVxuXG5mdW5jdGlvbiBobHNfZGVmaW5lUHJvcGVydHkob2JqLCBrZXksIHZhbHVlKSB7IGlmIChrZXkgaW4gb2JqKSB7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYmosIGtleSwgeyB2YWx1ZTogdmFsdWUsIGVudW1lcmFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSwgd3JpdGFibGU6IHRydWUgfSk7IH0gZWxzZSB7IG9ialtrZXldID0gdmFsdWU7IH0gcmV0dXJuIG9iajsgfVxuXG5mdW5jdGlvbiBobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKHNlbGYpIHsgaWYgKHNlbGYgPT09IHZvaWQgMCkgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIHNlbGY7IH1cblxuZnVuY3Rpb24gaGxzX2RlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfVxuXG5mdW5jdGlvbiBobHNfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBobHNfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGhsc19kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfVxuXG5mdW5jdGlvbiBobHNfaW5oZXJpdHNMb29zZShzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MucHJvdG90eXBlKTsgc3ViQ2xhc3MucHJvdG90eXBlLmNvbnN0cnVjdG9yID0gc3ViQ2xhc3M7IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG5cbi8qKlxuICogQG1vZHVsZSBIbHNcbiAqIEBjbGFzc1xuICogQGNvbnN0cnVjdG9yXG4gKi9cblxudmFyIGhsc19IbHMgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKF9PYnNlcnZlcikge1xuICBobHNfaW5oZXJpdHNMb29zZShIbHMsIF9PYnNlcnZlcik7XG5cbiAgLyoqXG4gICAqIEB0eXBlIHtib29sZWFufVxuICAgKi9cbiAgSGxzLmlzU3VwcG9ydGVkID0gZnVuY3Rpb24gaXNTdXBwb3J0ZWQoKSB7XG4gICAgcmV0dXJuIGlzX3N1cHBvcnRlZF9pc1N1cHBvcnRlZCgpO1xuICB9XG4gIC8qKlxuICAgKiBAdHlwZSB7SGxzRXZlbnRzfVxuICAgKi9cbiAgO1xuXG4gIGhsc19jcmVhdGVDbGFzcyhIbHMsIG51bGwsIFt7XG4gICAga2V5OiBcInZlcnNpb25cIixcblxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtzdHJpbmd9XG4gICAgICovXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gXCIwLjE0LjE3XCI7XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcIkV2ZW50c1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIGV2ZW50c1tcImRlZmF1bHRcIl07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtIbHNFcnJvclR5cGVzfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwiRXJyb3JUeXBlc1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIGVycm9yc1tcIkVycm9yVHlwZXNcIl07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtIbHNFcnJvckRldGFpbHN9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJFcnJvckRldGFpbHNcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiBlcnJvcnNbXCJFcnJvckRldGFpbHNcIl07XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtIbHNDb25maWd9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJEZWZhdWx0Q29uZmlnXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICBpZiAoIUhscy5kZWZhdWx0Q29uZmlnKSB7XG4gICAgICAgIHJldHVybiBobHNEZWZhdWx0Q29uZmlnO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gSGxzLmRlZmF1bHRDb25maWc7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtIbHNDb25maWd9XG4gICAgICovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KGRlZmF1bHRDb25maWcpIHtcbiAgICAgIEhscy5kZWZhdWx0Q29uZmlnID0gZGVmYXVsdENvbmZpZztcbiAgICB9XG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiBhbiBITFMgY2xpZW50IHRoYXQgY2FuIGF0dGFjaCB0byBleGFjdGx5IG9uZSBgSFRNTE1lZGlhRWxlbWVudGAuXG4gICAgICpcbiAgICAgKiBAY29uc3RydWN0cyBIbHNcbiAgICAgKiBAcGFyYW0ge0hsc0NvbmZpZ30gY29uZmlnXG4gICAgICovXG5cbiAgfV0pO1xuXG4gIGZ1bmN0aW9uIEhscyh1c2VyQ29uZmlnKSB7XG4gICAgdmFyIF90aGlzO1xuXG4gICAgaWYgKHVzZXJDb25maWcgPT09IHZvaWQgMCkge1xuICAgICAgdXNlckNvbmZpZyA9IHt9O1xuICAgIH1cblxuICAgIF90aGlzID0gX09ic2VydmVyLmNhbGwodGhpcykgfHwgdGhpcztcbiAgICBfdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgX3RoaXMuX2F1dG9MZXZlbENhcHBpbmcgPSB2b2lkIDA7XG4gICAgX3RoaXMuYWJyQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICBfdGhpcy5jYXBMZXZlbENvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgX3RoaXMubGV2ZWxDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIF90aGlzLnN0cmVhbUNvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgX3RoaXMubmV0d29ya0NvbnRyb2xsZXJzID0gdm9pZCAwO1xuICAgIF90aGlzLmF1ZGlvVHJhY2tDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIF90aGlzLnN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIF90aGlzLmVtZUNvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgX3RoaXMuY29yZUNvbXBvbmVudHMgPSB2b2lkIDA7XG4gICAgX3RoaXMubWVkaWEgPSBudWxsO1xuICAgIF90aGlzLnVybCA9IG51bGw7XG4gICAgdmFyIGRlZmF1bHRDb25maWcgPSBIbHMuRGVmYXVsdENvbmZpZztcblxuICAgIGlmICgodXNlckNvbmZpZy5saXZlU3luY0R1cmF0aW9uQ291bnQgfHwgdXNlckNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnQpICYmICh1c2VyQ29uZmlnLmxpdmVTeW5jRHVyYXRpb24gfHwgdXNlckNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbGxlZ2FsIGhscy5qcyBjb25maWc6IGRvblxcJ3QgbWl4IHVwIGxpdmVTeW5jRHVyYXRpb25Db3VudC9saXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnQgYW5kIGxpdmVTeW5jRHVyYXRpb24vbGl2ZU1heExhdGVuY3lEdXJhdGlvbicpO1xuICAgIH0gLy8gU2hhbGxvdyBjbG9uZVxuXG5cbiAgICBfdGhpcy5jb25maWcgPSBobHNfb2JqZWN0U3ByZWFkKGhsc19vYmplY3RTcHJlYWQoe30sIGRlZmF1bHRDb25maWcpLCB1c2VyQ29uZmlnKTtcblxuICAgIHZhciBfYXNzZXJ0VGhpc0luaXRpYWxpemUgPSBobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSxcbiAgICAgICAgY29uZmlnID0gX2Fzc2VydFRoaXNJbml0aWFsaXplLmNvbmZpZztcblxuICAgIGlmIChjb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50ICE9PSB2b2lkIDAgJiYgY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb25Db3VudCA8PSBjb25maWcubGl2ZVN5bmNEdXJhdGlvbkNvdW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0lsbGVnYWwgaGxzLmpzIGNvbmZpZzogXCJsaXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnRcIiBtdXN0IGJlIGd0IFwibGl2ZVN5bmNEdXJhdGlvbkNvdW50XCInKTtcbiAgICB9XG5cbiAgICBpZiAoY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb24gIT09IHZvaWQgMCAmJiAoY29uZmlnLmxpdmVTeW5jRHVyYXRpb24gPT09IHZvaWQgMCB8fCBjb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbiA8PSBjb25maWcubGl2ZVN5bmNEdXJhdGlvbikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSWxsZWdhbCBobHMuanMgY29uZmlnOiBcImxpdmVNYXhMYXRlbmN5RHVyYXRpb25cIiBtdXN0IGJlIGd0IFwibGl2ZVN5bmNEdXJhdGlvblwiJyk7XG4gICAgfVxuXG4gICAgT2JqZWN0KGxvZ2dlcltcImVuYWJsZUxvZ3NcIl0pKGNvbmZpZy5kZWJ1Zyk7XG4gICAgX3RoaXMuX2F1dG9MZXZlbENhcHBpbmcgPSAtMTsgLy8gY29yZSBjb250cm9sbGVycyBhbmQgbmV0d29yayBsb2FkZXJzXG5cbiAgICAvKipcbiAgICAgKiBAbWVtYmVyIHtBYnJDb250cm9sbGVyfSBhYnJDb250cm9sbGVyXG4gICAgICovXG5cbiAgICB2YXIgYWJyQ29udHJvbGxlciA9IF90aGlzLmFickNvbnRyb2xsZXIgPSBuZXcgY29uZmlnLmFickNvbnRyb2xsZXIoaGxzX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcykpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5ldy1jYXBcblxuICAgIHZhciBidWZmZXJDb250cm9sbGVyID0gbmV3IGNvbmZpZy5idWZmZXJDb250cm9sbGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuZXctY2FwXG5cbiAgICB2YXIgY2FwTGV2ZWxDb250cm9sbGVyID0gX3RoaXMuY2FwTGV2ZWxDb250cm9sbGVyID0gbmV3IGNvbmZpZy5jYXBMZXZlbENvbnRyb2xsZXIoaGxzX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcykpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5ldy1jYXBcblxuICAgIHZhciBmcHNDb250cm9sbGVyID0gbmV3IGNvbmZpZy5mcHNDb250cm9sbGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuZXctY2FwXG5cbiAgICB2YXIgcGxheUxpc3RMb2FkZXIgPSBuZXcgcGxheWxpc3RfbG9hZGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTtcbiAgICB2YXIgZnJhZ21lbnRMb2FkZXIgPSBuZXcgZnJhZ21lbnRfbG9hZGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTtcbiAgICB2YXIga2V5TG9hZGVyID0gbmV3IGtleV9sb2FkZXIoaGxzX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcykpO1xuICAgIHZhciBpZDNUcmFja0NvbnRyb2xsZXIgPSBuZXcgaWQzX3RyYWNrX2NvbnRyb2xsZXIoaGxzX2Fzc2VydFRoaXNJbml0aWFsaXplZChfdGhpcykpOyAvLyBuZXR3b3JrIGNvbnRyb2xsZXJzXG5cbiAgICAvKipcbiAgICAgKiBAbWVtYmVyIHtMZXZlbENvbnRyb2xsZXJ9IGxldmVsQ29udHJvbGxlclxuICAgICAqL1xuXG4gICAgdmFyIGxldmVsQ29udHJvbGxlciA9IF90aGlzLmxldmVsQ29udHJvbGxlciA9IG5ldyBsZXZlbF9jb250cm9sbGVyX0xldmVsQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSk7IC8vIEZJWE1FOiBGcmFnbWVudFRyYWNrZXIgbXVzdCBiZSBkZWZpbmVkIGJlZm9yZSBTdHJlYW1Db250cm9sbGVyIGJlY2F1c2UgdGhlIG9yZGVyIG9mIGV2ZW50IGhhbmRsaW5nIGlzIGltcG9ydGFudFxuXG4gICAgdmFyIGZyYWdtZW50VHJhY2tlciA9IG5ldyBmcmFnbWVudF90cmFja2VyX0ZyYWdtZW50VHJhY2tlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSk7XG4gICAgLyoqXG4gICAgICogQG1lbWJlciB7U3RyZWFtQ29udHJvbGxlcn0gc3RyZWFtQ29udHJvbGxlclxuICAgICAqL1xuXG4gICAgdmFyIHN0cmVhbUNvbnRyb2xsZXIgPSBfdGhpcy5zdHJlYW1Db250cm9sbGVyID0gbmV3IHN0cmVhbV9jb250cm9sbGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpLCBmcmFnbWVudFRyYWNrZXIpO1xuICAgIHZhciBuZXR3b3JrQ29udHJvbGxlcnMgPSBbbGV2ZWxDb250cm9sbGVyLCBzdHJlYW1Db250cm9sbGVyXTsgLy8gb3B0aW9uYWwgYXVkaW8gc3RyZWFtIGNvbnRyb2xsZXJcblxuICAgIC8qKlxuICAgICAqIEB2YXIge0lDb3JlQ29tcG9uZW50IHwgQ29udHJvbGxlcn1cbiAgICAgKi9cblxuICAgIHZhciBDb250cm9sbGVyID0gY29uZmlnLmF1ZGlvU3RyZWFtQ29udHJvbGxlcjtcblxuICAgIGlmIChDb250cm9sbGVyKSB7XG4gICAgICBuZXR3b3JrQ29udHJvbGxlcnMucHVzaChuZXcgQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSwgZnJhZ21lbnRUcmFja2VyKSk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEBtZW1iZXIge0lOZXR3b3JrQ29udHJvbGxlcltdfSBuZXR3b3JrQ29udHJvbGxlcnNcbiAgICAgKi9cblxuXG4gICAgX3RoaXMubmV0d29ya0NvbnRyb2xsZXJzID0gbmV0d29ya0NvbnRyb2xsZXJzO1xuICAgIC8qKlxuICAgICAqIEB2YXIge0lDb3JlQ29tcG9uZW50W119XG4gICAgICovXG5cbiAgICB2YXIgY29yZUNvbXBvbmVudHMgPSBbcGxheUxpc3RMb2FkZXIsIGZyYWdtZW50TG9hZGVyLCBrZXlMb2FkZXIsIGFickNvbnRyb2xsZXIsIGJ1ZmZlckNvbnRyb2xsZXIsIGNhcExldmVsQ29udHJvbGxlciwgZnBzQ29udHJvbGxlciwgaWQzVHJhY2tDb250cm9sbGVyLCBmcmFnbWVudFRyYWNrZXJdOyAvLyBvcHRpb25hbCBhdWRpbyB0cmFjayBhbmQgc3VidGl0bGUgY29udHJvbGxlclxuXG4gICAgQ29udHJvbGxlciA9IGNvbmZpZy5hdWRpb1RyYWNrQ29udHJvbGxlcjtcblxuICAgIGlmIChDb250cm9sbGVyKSB7XG4gICAgICB2YXIgYXVkaW9UcmFja0NvbnRyb2xsZXIgPSBuZXcgQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSk7XG4gICAgICAvKipcbiAgICAgICAqIEBtZW1iZXIge0F1ZGlvVHJhY2tDb250cm9sbGVyfSBhdWRpb1RyYWNrQ29udHJvbGxlclxuICAgICAgICovXG5cbiAgICAgIF90aGlzLmF1ZGlvVHJhY2tDb250cm9sbGVyID0gYXVkaW9UcmFja0NvbnRyb2xsZXI7XG4gICAgICBjb3JlQ29tcG9uZW50cy5wdXNoKGF1ZGlvVHJhY2tDb250cm9sbGVyKTtcbiAgICB9XG5cbiAgICBDb250cm9sbGVyID0gY29uZmlnLnN1YnRpdGxlVHJhY2tDb250cm9sbGVyO1xuXG4gICAgaWYgKENvbnRyb2xsZXIpIHtcbiAgICAgIHZhciBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA9IG5ldyBDb250cm9sbGVyKGhsc19hc3NlcnRUaGlzSW5pdGlhbGl6ZWQoX3RoaXMpKTtcbiAgICAgIC8qKlxuICAgICAgICogQG1lbWJlciB7U3VidGl0bGVUcmFja0NvbnRyb2xsZXJ9IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyXG4gICAgICAgKi9cblxuICAgICAgX3RoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPSBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICAgIG5ldHdvcmtDb250cm9sbGVycy5wdXNoKHN1YnRpdGxlVHJhY2tDb250cm9sbGVyKTtcbiAgICB9XG5cbiAgICBDb250cm9sbGVyID0gY29uZmlnLmVtZUNvbnRyb2xsZXI7XG5cbiAgICBpZiAoQ29udHJvbGxlcikge1xuICAgICAgdmFyIGVtZUNvbnRyb2xsZXIgPSBuZXcgQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSk7XG4gICAgICAvKipcbiAgICAgICAqIEBtZW1iZXIge0VNRUNvbnRyb2xsZXJ9IGVtZUNvbnRyb2xsZXJcbiAgICAgICAqL1xuXG4gICAgICBfdGhpcy5lbWVDb250cm9sbGVyID0gZW1lQ29udHJvbGxlcjtcbiAgICAgIGNvcmVDb21wb25lbnRzLnB1c2goZW1lQ29udHJvbGxlcik7XG4gICAgfSAvLyBvcHRpb25hbCBzdWJ0aXRsZSBjb250cm9sbGVyc1xuXG5cbiAgICBDb250cm9sbGVyID0gY29uZmlnLnN1YnRpdGxlU3RyZWFtQ29udHJvbGxlcjtcblxuICAgIGlmIChDb250cm9sbGVyKSB7XG4gICAgICBuZXR3b3JrQ29udHJvbGxlcnMucHVzaChuZXcgQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSwgZnJhZ21lbnRUcmFja2VyKSk7XG4gICAgfVxuXG4gICAgQ29udHJvbGxlciA9IGNvbmZpZy50aW1lbGluZUNvbnRyb2xsZXI7XG5cbiAgICBpZiAoQ29udHJvbGxlcikge1xuICAgICAgY29yZUNvbXBvbmVudHMucHVzaChuZXcgQ29udHJvbGxlcihobHNfYXNzZXJ0VGhpc0luaXRpYWxpemVkKF90aGlzKSkpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBAbWVtYmVyIHtJQ29yZUNvbXBvbmVudFtdfVxuICAgICAqL1xuXG5cbiAgICBfdGhpcy5jb3JlQ29tcG9uZW50cyA9IGNvcmVDb21wb25lbnRzO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuICAvKipcbiAgICogRGlzcG9zZSBvZiB0aGUgaW5zdGFuY2VcbiAgICovXG5cblxuICB2YXIgX3Byb3RvID0gSGxzLnByb3RvdHlwZTtcblxuICBfcHJvdG8uZGVzdHJveSA9IGZ1bmN0aW9uIGRlc3Ryb3koKSB7XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnZGVzdHJveScpO1xuICAgIHRoaXMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLkRFU1RST1lJTkcpO1xuICAgIHRoaXMuZGV0YWNoTWVkaWEoKTtcbiAgICB0aGlzLmNvcmVDb21wb25lbnRzLmNvbmNhdCh0aGlzLm5ldHdvcmtDb250cm9sbGVycykuZm9yRWFjaChmdW5jdGlvbiAoY29tcG9uZW50KSB7XG4gICAgICBjb21wb25lbnQuZGVzdHJveSgpO1xuICAgIH0pO1xuICAgIHRoaXMudXJsID0gbnVsbDtcbiAgICB0aGlzLnJlbW92ZUFsbExpc3RlbmVycygpO1xuICAgIHRoaXMuX2F1dG9MZXZlbENhcHBpbmcgPSAtMTtcbiAgfVxuICAvKipcbiAgICogQXR0YWNoIGEgbWVkaWEgZWxlbWVudFxuICAgKiBAcGFyYW0ge0hUTUxNZWRpYUVsZW1lbnR9IG1lZGlhXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmF0dGFjaE1lZGlhID0gZnVuY3Rpb24gYXR0YWNoTWVkaWEobWVkaWEpIHtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdhdHRhY2hNZWRpYScpO1xuICAgIHRoaXMubWVkaWEgPSBtZWRpYTtcbiAgICB0aGlzLnRyaWdnZXIoZXZlbnRzW1wiZGVmYXVsdFwiXS5NRURJQV9BVFRBQ0hJTkcsIHtcbiAgICAgIG1lZGlhOiBtZWRpYVxuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBEZXRhY2ggZnJvbSB0aGUgbWVkaWFcbiAgICovXG4gIDtcblxuICBfcHJvdG8uZGV0YWNoTWVkaWEgPSBmdW5jdGlvbiBkZXRhY2hNZWRpYSgpIHtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKCdkZXRhY2hNZWRpYScpO1xuICAgIHRoaXMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLk1FRElBX0RFVEFDSElORyk7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gIH1cbiAgLyoqXG4gICAqIFNldCB0aGUgc291cmNlIFVSTC4gQ2FuIGJlIHJlbGF0aXZlIG9yIGFic29sdXRlLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gdXJsXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLmxvYWRTb3VyY2UgPSBmdW5jdGlvbiBsb2FkU291cmNlKHVybCkge1xuICAgIHVybCA9IHVybF90b29sa2l0W1wiYnVpbGRBYnNvbHV0ZVVSTFwiXSh3aW5kb3cubG9jYXRpb24uaHJlZiwgdXJsLCB7XG4gICAgICBhbHdheXNOb3JtYWxpemU6IHRydWVcbiAgICB9KTtcbiAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwibG9hZFNvdXJjZTpcIiArIHVybCk7XG4gICAgdGhpcy51cmwgPSB1cmw7IC8vIHdoZW4gYXR0YWNoaW5nIHRvIGEgc291cmNlIFVSTCwgdHJpZ2dlciBhIHBsYXlsaXN0IGxvYWRcblxuICAgIHRoaXMudHJpZ2dlcihldmVudHNbXCJkZWZhdWx0XCJdLk1BTklGRVNUX0xPQURJTkcsIHtcbiAgICAgIHVybDogdXJsXG4gICAgfSk7XG4gIH1cbiAgLyoqXG4gICAqIFN0YXJ0IGxvYWRpbmcgZGF0YSBmcm9tIHRoZSBzdHJlYW0gc291cmNlLlxuICAgKiBEZXBlbmRpbmcgb24gZGVmYXVsdCBjb25maWcsIGNsaWVudCBzdGFydHMgbG9hZGluZyBhdXRvbWF0aWNhbGx5IHdoZW4gYSBzb3VyY2UgaXMgc2V0LlxuICAgKlxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhcnRQb3NpdGlvbiBTZXQgdGhlIHN0YXJ0IHBvc2l0aW9uIHRvIHN0cmVhbSBmcm9tXG4gICAqIEBkZWZhdWx0IC0xIE5vbmUgKGZyb20gZWFybGllc3QgcG9pbnQpXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnN0YXJ0TG9hZCA9IGZ1bmN0aW9uIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKSB7XG4gICAgaWYgKHN0YXJ0UG9zaXRpb24gPT09IHZvaWQgMCkge1xuICAgICAgc3RhcnRQb3NpdGlvbiA9IC0xO1xuICAgIH1cblxuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJzdGFydExvYWQoXCIgKyBzdGFydFBvc2l0aW9uICsgXCIpXCIpO1xuICAgIHRoaXMubmV0d29ya0NvbnRyb2xsZXJzLmZvckVhY2goZnVuY3Rpb24gKGNvbnRyb2xsZXIpIHtcbiAgICAgIGNvbnRyb2xsZXIuc3RhcnRMb2FkKHN0YXJ0UG9zaXRpb24pO1xuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBTdG9wIGxvYWRpbmcgb2YgYW55IHN0cmVhbSBkYXRhLlxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5zdG9wTG9hZCA9IGZ1bmN0aW9uIHN0b3BMb2FkKCkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3N0b3BMb2FkJyk7XG4gICAgdGhpcy5uZXR3b3JrQ29udHJvbGxlcnMuZm9yRWFjaChmdW5jdGlvbiAoY29udHJvbGxlcikge1xuICAgICAgY29udHJvbGxlci5zdG9wTG9hZCgpO1xuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBTd2FwIHRocm91Z2ggcG9zc2libGUgYXVkaW8gY29kZWNzIGluIHRoZSBzdHJlYW0gKGZvciBleGFtcGxlIHRvIHN3aXRjaCBmcm9tIHN0ZXJlbyB0byA1LjEpXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnN3YXBBdWRpb0NvZGVjID0gZnVuY3Rpb24gc3dhcEF1ZGlvQ29kZWMoKSB7XG4gICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZygnc3dhcEF1ZGlvQ29kZWMnKTtcbiAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIuc3dhcEF1ZGlvQ29kZWMoKTtcbiAgfVxuICAvKipcbiAgICogV2hlbiB0aGUgbWVkaWEtZWxlbWVudCBmYWlscywgdGhpcyBhbGxvd3MgdG8gZGV0YWNoIGFuZCB0aGVuIHJlLWF0dGFjaCBpdFxuICAgKiBhcyBvbmUgY2FsbCAoY29udmVuaWVuY2UgbWV0aG9kKS5cbiAgICpcbiAgICogQXV0b21hdGljIHJlY292ZXJ5IG9mIG1lZGlhLWVycm9ycyBieSB0aGlzIHByb2Nlc3MgaXMgY29uZmlndXJhYmxlLlxuICAgKi9cbiAgO1xuXG4gIF9wcm90by5yZWNvdmVyTWVkaWFFcnJvciA9IGZ1bmN0aW9uIHJlY292ZXJNZWRpYUVycm9yKCkge1xuICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coJ3JlY292ZXJNZWRpYUVycm9yJyk7XG4gICAgdmFyIG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICB0aGlzLmRldGFjaE1lZGlhKCk7XG5cbiAgICBpZiAobWVkaWEpIHtcbiAgICAgIHRoaXMuYXR0YWNoTWVkaWEobWVkaWEpO1xuICAgIH1cbiAgfVxuICAvKipcbiAgICogUmVtb3ZlIGEgbG9hZGVkIGxldmVsIGZyb20gdGhlIGxpc3Qgb2YgbGV2ZWxzLCBvciBhIGxldmVsIHVybCBpbiBmcm9tIGEgbGlzdCBvZiByZWR1bmRhbnQgbGV2ZWwgdXJscy5cbiAgICogVGhpcyBjYW4gYmUgdXNlZCB0byByZW1vdmUgYSByZW5kaXRpb24gb3IgcGxheWxpc3QgdXJsIHRoYXQgZXJyb3JzIGZyZXF1ZW50bHkgZnJvbSB0aGUgbGlzdCBvZiBsZXZlbHMgdGhhdCBhIHVzZXJcbiAgICogb3IgaGxzLmpzIGNhbiBjaG9vc2UgZnJvbS5cbiAgICpcbiAgICogQHBhcmFtIGxldmVsSW5kZXgge251bWJlcn0gVGhlIHF1YWxpdHkgbGV2ZWwgaW5kZXggdG8gb2YgdGhlIGxldmVsIHRvIHJlbW92ZVxuICAgKiBAcGFyYW0gdXJsSWQge251bWJlcn0gVGhlIHF1YWxpdHkgbGV2ZWwgdXJsIGluZGV4IGluIHRoZSBjYXNlIHRoYXQgZmFsbGJhY2sgbGV2ZWxzIGFyZSBhdmFpbGFibGUuIERlZmF1bHRzIHRvIDAuXG4gICAqL1xuICA7XG5cbiAgX3Byb3RvLnJlbW92ZUxldmVsID0gZnVuY3Rpb24gcmVtb3ZlTGV2ZWwobGV2ZWxJbmRleCwgdXJsSWQpIHtcbiAgICBpZiAodXJsSWQgPT09IHZvaWQgMCkge1xuICAgICAgdXJsSWQgPSAwO1xuICAgIH1cblxuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLnJlbW92ZUxldmVsKGxldmVsSW5kZXgsIHVybElkKTtcbiAgfVxuICAvKipcbiAgICogQHR5cGUge1F1YWxpdHlMZXZlbFtdfVxuICAgKi9cbiAgLy8gdG9kbyh0eXBlc2NyaXB0LWxldmVsQ29udHJvbGxlcilcbiAgO1xuXG4gIGhsc19jcmVhdGVDbGFzcyhIbHMsIFt7XG4gICAga2V5OiBcImxldmVsc1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMubGV2ZWxDb250cm9sbGVyLmxldmVscztcbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5kZXggb2YgcXVhbGl0eSBsZXZlbCBjdXJyZW50bHkgcGxheWVkXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiBcImN1cnJlbnRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtQ29udHJvbGxlci5jdXJyZW50TGV2ZWw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCBxdWFsaXR5IGxldmVsIGluZGV4IGltbWVkaWF0ZWx5IC5cbiAgICAgKiBUaGlzIHdpbGwgZmx1c2ggdGhlIGN1cnJlbnQgYnVmZmVyIHRvIHJlcGxhY2UgdGhlIHF1YWxpdHkgYXNhcC5cbiAgICAgKiBUaGF0IG1lYW5zIHBsYXliYWNrIHdpbGwgaW50ZXJydXB0IGF0IGxlYXN0IHNob3J0bHkgdG8gcmUtYnVmZmVyIGFuZCByZS1zeW5jIGV2ZW50dWFsbHkuXG4gICAgICogQHBhcmFtIG5ld0xldmVsIHtudW1iZXJ9IC0xIGZvciBhdXRvbWF0aWMgbGV2ZWwgc2VsZWN0aW9uXG4gICAgICovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KG5ld0xldmVsKSB7XG4gICAgICBsb2dnZXJbXCJsb2dnZXJcIl0ubG9nKFwic2V0IGN1cnJlbnRMZXZlbDpcIiArIG5ld0xldmVsKTtcbiAgICAgIHRoaXMubG9hZExldmVsID0gbmV3TGV2ZWw7XG4gICAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIuaW1tZWRpYXRlTGV2ZWxTd2l0Y2goKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogSW5kZXggb2YgbmV4dCBxdWFsaXR5IGxldmVsIGxvYWRlZCBhcyBzY2hlZHVsZWQgYnkgc3RyZWFtIGNvbnRyb2xsZXIuXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiBcIm5leHRMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtQ29udHJvbGxlci5uZXh0TGV2ZWw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCBxdWFsaXR5IGxldmVsIGluZGV4IGZvciBuZXh0IGxvYWRlZCBkYXRhLlxuICAgICAqIFRoaXMgd2lsbCBzd2l0Y2ggdGhlIHZpZGVvIHF1YWxpdHkgYXNhcCwgd2l0aG91dCBpbnRlcnJ1cHRpbmcgcGxheWJhY2suXG4gICAgICogTWF5IGFib3J0IGN1cnJlbnQgbG9hZGluZyBvZiBkYXRhLCBhbmQgZmx1c2ggcGFydHMgb2YgYnVmZmVyIChvdXRzaWRlIGN1cnJlbnRseSBwbGF5ZWQgZnJhZ21lbnQgcmVnaW9uKS5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfSAtMSBmb3IgYXV0b21hdGljIGxldmVsIHNlbGVjdGlvblxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChuZXdMZXZlbCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInNldCBuZXh0TGV2ZWw6XCIgKyBuZXdMZXZlbCk7XG4gICAgICB0aGlzLmxldmVsQ29udHJvbGxlci5tYW51YWxMZXZlbCA9IG5ld0xldmVsO1xuICAgICAgdGhpcy5zdHJlYW1Db250cm9sbGVyLm5leHRMZXZlbFN3aXRjaCgpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm4gdGhlIHF1YWxpdHkgbGV2ZWwgb2YgdGhlIGN1cnJlbnRseSBvciBsYXN0IChvZiBub25lIGlzIGxvYWRlZCBjdXJyZW50bHkpIHNlZ21lbnRcbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwibG9hZExldmVsXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5sZXZlbENvbnRyb2xsZXIubGV2ZWw7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFNldCBxdWFsaXR5IGxldmVsIGluZGV4IGZvciBuZXh0IGxvYWRlZCBkYXRhIGluIGEgY29uc2VydmF0aXZlIHdheS5cbiAgICAgKiBUaGlzIHdpbGwgc3dpdGNoIHRoZSBxdWFsaXR5IHdpdGhvdXQgZmx1c2hpbmcsIGJ1dCBpbnRlcnJ1cHQgY3VycmVudCBsb2FkaW5nLlxuICAgICAqIFRodXMgdGhlIG1vbWVudCB3aGVuIHRoZSBxdWFsaXR5IHN3aXRjaCB3aWxsIGFwcGVhciBpbiBlZmZlY3Qgd2lsbCBvbmx5IGJlIGFmdGVyIHRoZSBhbHJlYWR5IGV4aXN0aW5nIGJ1ZmZlci5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfSBuZXdMZXZlbCAtMSBmb3IgYXV0b21hdGljIGxldmVsIHNlbGVjdGlvblxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChuZXdMZXZlbCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInNldCBsb2FkTGV2ZWw6XCIgKyBuZXdMZXZlbCk7XG4gICAgICB0aGlzLmxldmVsQ29udHJvbGxlci5tYW51YWxMZXZlbCA9IG5ld0xldmVsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBnZXQgbmV4dCBxdWFsaXR5IGxldmVsIGxvYWRlZFxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJuZXh0TG9hZExldmVsXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5sZXZlbENvbnRyb2xsZXIubmV4dExvYWRMZXZlbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0IHF1YWxpdHkgbGV2ZWwgb2YgbmV4dCBsb2FkZWQgc2VnbWVudCBpbiBhIGZ1bGx5IFwibm9uLWRlc3RydWN0aXZlXCIgd2F5LlxuICAgICAqIFNhbWUgYXMgYGxvYWRMZXZlbGAgYnV0IHdpbGwgd2FpdCBmb3IgbmV4dCBzd2l0Y2ggKHVudGlsIGN1cnJlbnQgbG9hZGluZyBpcyBkb25lKS5cbiAgICAgKiBAdHlwZSB7bnVtYmVyfSBsZXZlbFxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChsZXZlbCkge1xuICAgICAgdGhpcy5sZXZlbENvbnRyb2xsZXIubmV4dExvYWRMZXZlbCA9IGxldmVsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBSZXR1cm4gXCJmaXJzdCBsZXZlbFwiOiBsaWtlIGEgZGVmYXVsdCBsZXZlbCwgaWYgbm90IHNldCxcbiAgICAgKiBmYWxscyBiYWNrIHRvIGluZGV4IG9mIGZpcnN0IGxldmVsIHJlZmVyZW5jZWQgaW4gbWFuaWZlc3RcbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwiZmlyc3RMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIE1hdGgubWF4KHRoaXMubGV2ZWxDb250cm9sbGVyLmZpcnN0TGV2ZWwsIHRoaXMubWluQXV0b0xldmVsKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogU2V0cyBcImZpcnN0LWxldmVsXCIsIHNlZSBnZXR0ZXIuXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cbiAgICAsXG4gICAgc2V0OiBmdW5jdGlvbiBzZXQobmV3TGV2ZWwpIHtcbiAgICAgIGxvZ2dlcltcImxvZ2dlclwiXS5sb2coXCJzZXQgZmlyc3RMZXZlbDpcIiArIG5ld0xldmVsKTtcbiAgICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLmZpcnN0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogUmV0dXJuIHN0YXJ0IGxldmVsIChsZXZlbCBvZiBmaXJzdCBmcmFnbWVudCB0aGF0IHdpbGwgYmUgcGxheWVkIGJhY2spXG4gICAgICogaWYgbm90IG92ZXJyaWRlZCBieSB1c2VyLCBmaXJzdCBsZXZlbCBhcHBlYXJpbmcgaW4gbWFuaWZlc3Qgd2lsbCBiZSB1c2VkIGFzIHN0YXJ0IGxldmVsXG4gICAgICogaWYgLTEgOiBhdXRvbWF0aWMgc3RhcnQgbGV2ZWwgc2VsZWN0aW9uLCBwbGF5YmFjayB3aWxsIHN0YXJ0IGZyb20gbGV2ZWwgbWF0Y2hpbmcgZG93bmxvYWQgYmFuZHdpZHRoXG4gICAgICogKGRldGVybWluZWQgZnJvbSBkb3dubG9hZCBvZiBmaXJzdCBzZWdtZW50KVxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJzdGFydExldmVsXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpcy5sZXZlbENvbnRyb2xsZXIuc3RhcnRMZXZlbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogc2V0ICBzdGFydCBsZXZlbCAobGV2ZWwgb2YgZmlyc3QgZnJhZ21lbnQgdGhhdCB3aWxsIGJlIHBsYXllZCBiYWNrKVxuICAgICAqIGlmIG5vdCBvdmVycmlkZWQgYnkgdXNlciwgZmlyc3QgbGV2ZWwgYXBwZWFyaW5nIGluIG1hbmlmZXN0IHdpbGwgYmUgdXNlZCBhcyBzdGFydCBsZXZlbFxuICAgICAqIGlmIC0xIDogYXV0b21hdGljIHN0YXJ0IGxldmVsIHNlbGVjdGlvbiwgcGxheWJhY2sgd2lsbCBzdGFydCBmcm9tIGxldmVsIG1hdGNoaW5nIGRvd25sb2FkIGJhbmR3aWR0aFxuICAgICAqIChkZXRlcm1pbmVkIGZyb20gZG93bmxvYWQgb2YgZmlyc3Qgc2VnbWVudClcbiAgICAgKiBAdHlwZSB7bnVtYmVyfSBuZXdMZXZlbFxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChuZXdMZXZlbCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInNldCBzdGFydExldmVsOlwiICsgbmV3TGV2ZWwpOyAvLyBpZiBub3QgaW4gYXV0b21hdGljIHN0YXJ0IGxldmVsIGRldGVjdGlvbiwgZW5zdXJlIHN0YXJ0TGV2ZWwgaXMgZ3JlYXRlciB0aGFuIG1pbkF1dG9MZXZlbFxuXG4gICAgICBpZiAobmV3TGV2ZWwgIT09IC0xKSB7XG4gICAgICAgIG5ld0xldmVsID0gTWF0aC5tYXgobmV3TGV2ZWwsIHRoaXMubWluQXV0b0xldmVsKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5sZXZlbENvbnRyb2xsZXIuc3RhcnRMZXZlbCA9IG5ld0xldmVsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBzZXQgIGR5bmFtaWNhbGx5IHNldCBjYXBMZXZlbFRvUGxheWVyU2l6ZSBhZ2FpbnN0IChgQ2FwTGV2ZWxDb250cm9sbGVyYClcbiAgICAgKlxuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwiY2FwTGV2ZWxUb1BsYXllclNpemVcIixcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChzaG91bGRTdGFydENhcHBpbmcpIHtcbiAgICAgIHZhciBuZXdDYXBMZXZlbFRvUGxheWVyU2l6ZSA9ICEhc2hvdWxkU3RhcnRDYXBwaW5nO1xuXG4gICAgICBpZiAobmV3Q2FwTGV2ZWxUb1BsYXllclNpemUgIT09IHRoaXMuY29uZmlnLmNhcExldmVsVG9QbGF5ZXJTaXplKSB7XG4gICAgICAgIGlmIChuZXdDYXBMZXZlbFRvUGxheWVyU2l6ZSkge1xuICAgICAgICAgIHRoaXMuY2FwTGV2ZWxDb250cm9sbGVyLnN0YXJ0Q2FwcGluZygpOyAvLyBJZiBjYXBwaW5nIG9jY3VycywgbmV4dExldmVsU3dpdGNoIHdpbGwgaGFwcGVuIGJhc2VkIG9uIHNpemUuXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5jYXBMZXZlbENvbnRyb2xsZXIuc3RvcENhcHBpbmcoKTtcbiAgICAgICAgICB0aGlzLmF1dG9MZXZlbENhcHBpbmcgPSAtMTtcbiAgICAgICAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIubmV4dExldmVsU3dpdGNoKCk7IC8vIE5vdyB3ZSdyZSB1bmNhcHBlZCwgZ2V0IHRoZSBuZXh0IGxldmVsIGFzYXAuXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNvbmZpZy5jYXBMZXZlbFRvUGxheWVyU2l6ZSA9IG5ld0NhcExldmVsVG9QbGF5ZXJTaXplO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBDYXBwaW5nL21heCBsZXZlbCB2YWx1ZSB0aGF0IHNob3VsZCBiZSB1c2VkIGJ5IGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb24gYWxnb3JpdGhtIChgQUJSQ29udHJvbGxlcmApXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiBcImF1dG9MZXZlbENhcHBpbmdcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLl9hdXRvTGV2ZWxDYXBwaW5nO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBnZXQgYmFuZHdpZHRoIGVzdGltYXRlXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cbiAgICAsXG5cbiAgICAvKipcbiAgICAgKiBDYXBwaW5nL21heCBsZXZlbCB2YWx1ZSB0aGF0IHNob3VsZCBiZSB1c2VkIGJ5IGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb24gYWxnb3JpdGhtIChgQUJSQ29udHJvbGxlcmApXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChuZXdMZXZlbCkge1xuICAgICAgbG9nZ2VyW1wibG9nZ2VyXCJdLmxvZyhcInNldCBhdXRvTGV2ZWxDYXBwaW5nOlwiICsgbmV3TGV2ZWwpO1xuICAgICAgdGhpcy5fYXV0b0xldmVsQ2FwcGluZyA9IG5ld0xldmVsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBUcnVlIHdoZW4gYXV0b21hdGljIGxldmVsIHNlbGVjdGlvbiBlbmFibGVkXG4gICAgICogQHR5cGUge2Jvb2xlYW59XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJiYW5kd2lkdGhFc3RpbWF0ZVwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIGJ3RXN0aW1hdG9yID0gdGhpcy5hYnJDb250cm9sbGVyLl9id0VzdGltYXRvcjtcbiAgICAgIHJldHVybiBid0VzdGltYXRvciA/IGJ3RXN0aW1hdG9yLmdldEVzdGltYXRlKCkgOiBOYU47XG4gICAgfVxuICB9LCB7XG4gICAga2V5OiBcImF1dG9MZXZlbEVuYWJsZWRcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHJldHVybiB0aGlzLmxldmVsQ29udHJvbGxlci5tYW51YWxMZXZlbCA9PT0gLTE7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIExldmVsIHNldCBtYW51YWxseSAoaWYgYW55KVxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJtYW51YWxMZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMubGV2ZWxDb250cm9sbGVyLm1hbnVhbExldmVsO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBtaW4gbGV2ZWwgc2VsZWN0YWJsZSBpbiBhdXRvIG1vZGUgYWNjb3JkaW5nIHRvIGNvbmZpZy5taW5BdXRvQml0cmF0ZVxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJtaW5BdXRvTGV2ZWxcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHZhciBsZXZlbHMgPSB0aGlzLmxldmVscyxcbiAgICAgICAgICBtaW5BdXRvQml0cmF0ZSA9IHRoaXMuY29uZmlnLm1pbkF1dG9CaXRyYXRlO1xuICAgICAgdmFyIGxlbiA9IGxldmVscyA/IGxldmVscy5sZW5ndGggOiAwO1xuXG4gICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgIHZhciBsZXZlbE5leHRCaXRyYXRlID0gbGV2ZWxzW2ldLnJlYWxCaXRyYXRlID8gTWF0aC5tYXgobGV2ZWxzW2ldLnJlYWxCaXRyYXRlLCBsZXZlbHNbaV0uYml0cmF0ZSkgOiBsZXZlbHNbaV0uYml0cmF0ZTtcblxuICAgICAgICBpZiAobGV2ZWxOZXh0Qml0cmF0ZSA+IG1pbkF1dG9CaXRyYXRlKSB7XG4gICAgICAgICAgcmV0dXJuIGk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIG1heCBsZXZlbCBzZWxlY3RhYmxlIGluIGF1dG8gbW9kZSBhY2NvcmRpbmcgdG8gYXV0b0xldmVsQ2FwcGluZ1xuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogXCJtYXhBdXRvTGV2ZWxcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHZhciBsZXZlbHMgPSB0aGlzLmxldmVscyxcbiAgICAgICAgICBhdXRvTGV2ZWxDYXBwaW5nID0gdGhpcy5hdXRvTGV2ZWxDYXBwaW5nO1xuICAgICAgdmFyIG1heEF1dG9MZXZlbDtcblxuICAgICAgaWYgKGF1dG9MZXZlbENhcHBpbmcgPT09IC0xICYmIGxldmVscyAmJiBsZXZlbHMubGVuZ3RoKSB7XG4gICAgICAgIG1heEF1dG9MZXZlbCA9IGxldmVscy5sZW5ndGggLSAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbWF4QXV0b0xldmVsID0gYXV0b0xldmVsQ2FwcGluZztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG1heEF1dG9MZXZlbDtcbiAgICB9XG4gICAgLyoqXG4gICAgICogbmV4dCBhdXRvbWF0aWNhbGx5IHNlbGVjdGVkIHF1YWxpdHkgbGV2ZWxcbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwibmV4dEF1dG9MZXZlbFwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgLy8gZW5zdXJlIG5leHQgYXV0byBsZXZlbCBpcyBiZXR3ZWVuICBtaW4gYW5kIG1heCBhdXRvIGxldmVsXG4gICAgICByZXR1cm4gTWF0aC5taW4oTWF0aC5tYXgodGhpcy5hYnJDb250cm9sbGVyLm5leHRBdXRvTGV2ZWwsIHRoaXMubWluQXV0b0xldmVsKSwgdGhpcy5tYXhBdXRvTGV2ZWwpO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiB0aGlzIHNldHRlciBpcyB1c2VkIHRvIGZvcmNlIG5leHQgYXV0byBsZXZlbC5cbiAgICAgKiB0aGlzIGlzIHVzZWZ1bCB0byBmb3JjZSBhIHN3aXRjaCBkb3duIGluIGF1dG8gbW9kZTpcbiAgICAgKiBpbiBjYXNlIG9mIGxvYWQgZXJyb3Igb24gbGV2ZWwgTiwgaGxzLmpzIGNhbiBzZXQgbmV4dEF1dG9MZXZlbCB0byBOLTEgZm9yIGV4YW1wbGUpXG4gICAgICogZm9yY2VkIHZhbHVlIGlzIHZhbGlkIGZvciBvbmUgZnJhZ21lbnQuIHVwb24gc3VjY2VzZnVsIGZyYWcgbG9hZGluZyBhdCBmb3JjZWQgbGV2ZWwsXG4gICAgICogdGhpcyB2YWx1ZSB3aWxsIGJlIHJlc2V0dGVkIHRvIC0xIGJ5IEFCUiBjb250cm9sbGVyLlxuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KG5leHRMZXZlbCkge1xuICAgICAgdGhpcy5hYnJDb250cm9sbGVyLm5leHRBdXRvTGV2ZWwgPSBNYXRoLm1heCh0aGlzLm1pbkF1dG9MZXZlbCwgbmV4dExldmVsKTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogQHR5cGUge0F1ZGlvVHJhY2tbXX1cbiAgICAgKi9cbiAgICAvLyB0b2RvKHR5cGVzY3JpcHQtYXVkaW9UcmFja0NvbnRyb2xsZXIpXG5cbiAgfSwge1xuICAgIGtleTogXCJhdWRpb1RyYWNrc1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIGF1ZGlvVHJhY2tDb250cm9sbGVyID0gdGhpcy5hdWRpb1RyYWNrQ29udHJvbGxlcjtcbiAgICAgIHJldHVybiBhdWRpb1RyYWNrQ29udHJvbGxlciA/IGF1ZGlvVHJhY2tDb250cm9sbGVyLmF1ZGlvVHJhY2tzIDogW107XG4gICAgfVxuICAgIC8qKlxuICAgICAqIGluZGV4IG9mIHRoZSBzZWxlY3RlZCBhdWRpbyB0cmFjayAoaW5kZXggaW4gYXVkaW8gdHJhY2sgbGlzdHMpXG4gICAgICogQHR5cGUge251bWJlcn1cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiBcImF1ZGlvVHJhY2tcIixcbiAgICBnZXQ6IGZ1bmN0aW9uIGdldCgpIHtcbiAgICAgIHZhciBhdWRpb1RyYWNrQ29udHJvbGxlciA9IHRoaXMuYXVkaW9UcmFja0NvbnRyb2xsZXI7XG4gICAgICByZXR1cm4gYXVkaW9UcmFja0NvbnRyb2xsZXIgPyBhdWRpb1RyYWNrQ29udHJvbGxlci5hdWRpb1RyYWNrIDogLTE7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIHNlbGVjdHMgYW4gYXVkaW8gdHJhY2ssIGJhc2VkIG9uIGl0cyBpbmRleCBpbiBhdWRpbyB0cmFjayBsaXN0c1xuICAgICAqIEB0eXBlIHtudW1iZXJ9XG4gICAgICovXG4gICAgLFxuICAgIHNldDogZnVuY3Rpb24gc2V0KGF1ZGlvVHJhY2tJZCkge1xuICAgICAgdmFyIGF1ZGlvVHJhY2tDb250cm9sbGVyID0gdGhpcy5hdWRpb1RyYWNrQ29udHJvbGxlcjtcblxuICAgICAgaWYgKGF1ZGlvVHJhY2tDb250cm9sbGVyKSB7XG4gICAgICAgIGF1ZGlvVHJhY2tDb250cm9sbGVyLmF1ZGlvVHJhY2sgPSBhdWRpb1RyYWNrSWQ7XG4gICAgICB9XG4gICAgfVxuICAgIC8qKlxuICAgICAqIEB0eXBlIHtTZWNvbmRzfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwibGl2ZVN5bmNQb3NpdGlvblwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuc3RyZWFtQ29udHJvbGxlci5saXZlU3luY1Bvc2l0aW9uO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBnZXQgYWx0ZXJuYXRlIHN1YnRpdGxlIHRyYWNrcyBsaXN0IGZyb20gcGxheWxpc3RcbiAgICAgKiBAdHlwZSB7U3VidGl0bGVUcmFja1tdfVxuICAgICAqL1xuICAgIC8vIHRvZG8odHlwZXNjcmlwdC1zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcilcblxuICB9LCB7XG4gICAga2V5OiBcInN1YnRpdGxlVHJhY2tzXCIsXG4gICAgZ2V0OiBmdW5jdGlvbiBnZXQoKSB7XG4gICAgICB2YXIgc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPSB0aGlzLnN1YnRpdGxlVHJhY2tDb250cm9sbGVyO1xuICAgICAgcmV0dXJuIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID8gc3VidGl0bGVUcmFja0NvbnRyb2xsZXIuc3VidGl0bGVUcmFja3MgOiBbXTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogaW5kZXggb2YgdGhlIHNlbGVjdGVkIHN1YnRpdGxlIHRyYWNrIChpbmRleCBpbiBzdWJ0aXRsZSB0cmFjayBsaXN0cylcbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6IFwic3VidGl0bGVUcmFja1wiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICAgIHJldHVybiBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA/IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyLnN1YnRpdGxlVHJhY2sgOiAtMTtcbiAgICB9XG4gICAgLyoqXG4gICAgICogc2VsZWN0IGFuIHN1YnRpdGxlIHRyYWNrLCBiYXNlZCBvbiBpdHMgaW5kZXggaW4gc3VidGl0bGUgdHJhY2sgbGlzdHNcbiAgICAgKiBAdHlwZSB7bnVtYmVyfVxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldChzdWJ0aXRsZVRyYWNrSWQpIHtcbiAgICAgIHZhciBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA9IHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXI7XG5cbiAgICAgIGlmIChzdWJ0aXRsZVRyYWNrQ29udHJvbGxlcikge1xuICAgICAgICBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlci5zdWJ0aXRsZVRyYWNrID0gc3VidGl0bGVUcmFja0lkO1xuICAgICAgfVxuICAgIH1cbiAgICAvKipcbiAgICAgKiBAdHlwZSB7Ym9vbGVhbn1cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiBcInN1YnRpdGxlRGlzcGxheVwiLFxuICAgIGdldDogZnVuY3Rpb24gZ2V0KCkge1xuICAgICAgdmFyIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICAgIHJldHVybiBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA/IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyLnN1YnRpdGxlRGlzcGxheSA6IGZhbHNlO1xuICAgIH1cbiAgICAvKipcbiAgICAgKiBFbmFibGUvZGlzYWJsZSBzdWJ0aXRsZSBkaXNwbGF5IHJlbmRlcmluZ1xuICAgICAqIEB0eXBlIHtib29sZWFufVxuICAgICAqL1xuICAgICxcbiAgICBzZXQ6IGZ1bmN0aW9uIHNldCh2YWx1ZSkge1xuICAgICAgdmFyIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcblxuICAgICAgaWYgKHN1YnRpdGxlVHJhY2tDb250cm9sbGVyKSB7XG4gICAgICAgIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyLnN1YnRpdGxlRGlzcGxheSA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgfV0pO1xuXG4gIHJldHVybiBIbHM7XG59KE9ic2VydmVyKTtcblxuaGxzX0hscy5kZWZhdWx0Q29uZmlnID0gdm9pZCAwO1xuXG5cbi8qKiovIH0pLFxuXG4vKioqLyBcIi4vc3JjL3BvbHlmaWxscy9udW1iZXIuanNcIjpcbi8qISoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL3NyYy9wb2x5ZmlsbHMvbnVtYmVyLmpzICoqKiFcbiAgXFwqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKiEgZXhwb3J0cyBwcm92aWRlZDogaXNGaW5pdGVOdW1iZXIsIE1BWF9TQUZFX0lOVEVHRVIgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIF9fd2VicGFja19leHBvcnRzX18sIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG4vKiBoYXJtb255IGV4cG9ydCAoYmluZGluZykgKi8gX193ZWJwYWNrX3JlcXVpcmVfXy5kKF9fd2VicGFja19leHBvcnRzX18sIFwiaXNGaW5pdGVOdW1iZXJcIiwgZnVuY3Rpb24oKSB7IHJldHVybiBpc0Zpbml0ZU51bWJlcjsgfSk7XG4vKiBoYXJtb255IGV4cG9ydCAoYmluZGluZykgKi8gX193ZWJwYWNrX3JlcXVpcmVfXy5kKF9fd2VicGFja19leHBvcnRzX18sIFwiTUFYX1NBRkVfSU5URUdFUlwiLCBmdW5jdGlvbigpIHsgcmV0dXJuIE1BWF9TQUZFX0lOVEVHRVI7IH0pO1xudmFyIGlzRmluaXRlTnVtYmVyID0gTnVtYmVyLmlzRmluaXRlIHx8IGZ1bmN0aW9uICh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyAmJiBpc0Zpbml0ZSh2YWx1ZSk7XG59O1xudmFyIE1BWF9TQUZFX0lOVEVHRVIgPSBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUiB8fCA5MDA3MTk5MjU0NzQwOTkxO1xuXG4vKioqLyB9KSxcblxuLyoqKi8gXCIuL3NyYy91dGlscy9nZXQtc2VsZi1zY29wZS5qc1wiOlxuLyohKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiEqXFxcbiAgISoqKiAuL3NyYy91dGlscy9nZXQtc2VsZi1zY29wZS5qcyAqKiohXG4gIFxcKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qISBleHBvcnRzIHByb3ZpZGVkOiBnZXRTZWxmU2NvcGUgKi9cbi8qKiovIChmdW5jdGlvbihtb2R1bGUsIF9fd2VicGFja19leHBvcnRzX18sIF9fd2VicGFja19yZXF1aXJlX18pIHtcblxuXCJ1c2Ugc3RyaWN0XCI7XG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIoX193ZWJwYWNrX2V4cG9ydHNfXyk7XG4vKiBoYXJtb255IGV4cG9ydCAoYmluZGluZykgKi8gX193ZWJwYWNrX3JlcXVpcmVfXy5kKF9fd2VicGFja19leHBvcnRzX18sIFwiZ2V0U2VsZlNjb3BlXCIsIGZ1bmN0aW9uKCkgeyByZXR1cm4gZ2V0U2VsZlNjb3BlOyB9KTtcbmZ1bmN0aW9uIGdldFNlbGZTY29wZSgpIHtcbiAgLy8gc2VlIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xMTIzNzI1OS81ODk0OTNcbiAgaWYgKHR5cGVvZiB3aW5kb3cgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgLyogZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmICovXG4gICAgcmV0dXJuIHNlbGY7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHdpbmRvdztcbiAgfVxufVxuXG4vKioqLyB9KSxcblxuLyoqKi8gXCIuL3NyYy91dGlscy9sb2dnZXIuanNcIjpcbi8qISoqKioqKioqKioqKioqKioqKioqKioqKioqKioqISpcXFxuICAhKioqIC4vc3JjL3V0aWxzL2xvZ2dlci5qcyAqKiohXG4gIFxcKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKiEgZXhwb3J0cyBwcm92aWRlZDogZW5hYmxlTG9ncywgbG9nZ2VyICovXG4vKioqLyAoZnVuY3Rpb24obW9kdWxlLCBfX3dlYnBhY2tfZXhwb3J0c19fLCBfX3dlYnBhY2tfcmVxdWlyZV9fKSB7XG5cblwidXNlIHN0cmljdFwiO1xuX193ZWJwYWNrX3JlcXVpcmVfXy5yKF9fd2VicGFja19leHBvcnRzX18pO1xuLyogaGFybW9ueSBleHBvcnQgKGJpbmRpbmcpICovIF9fd2VicGFja19yZXF1aXJlX18uZChfX3dlYnBhY2tfZXhwb3J0c19fLCBcImVuYWJsZUxvZ3NcIiwgZnVuY3Rpb24oKSB7IHJldHVybiBlbmFibGVMb2dzOyB9KTtcbi8qIGhhcm1vbnkgZXhwb3J0IChiaW5kaW5nKSAqLyBfX3dlYnBhY2tfcmVxdWlyZV9fLmQoX193ZWJwYWNrX2V4cG9ydHNfXywgXCJsb2dnZXJcIiwgZnVuY3Rpb24oKSB7IHJldHVybiBsb2dnZXI7IH0pO1xuLyogaGFybW9ueSBpbXBvcnQgKi8gdmFyIF9nZXRfc2VsZl9zY29wZV9fV0VCUEFDS19JTVBPUlRFRF9NT0RVTEVfMF9fID0gX193ZWJwYWNrX3JlcXVpcmVfXygvKiEgLi9nZXQtc2VsZi1zY29wZSAqLyBcIi4vc3JjL3V0aWxzL2dldC1zZWxmLXNjb3BlLmpzXCIpO1xuXG5cbmZ1bmN0aW9uIG5vb3AoKSB7fVxuXG52YXIgZmFrZUxvZ2dlciA9IHtcbiAgdHJhY2U6IG5vb3AsXG4gIGRlYnVnOiBub29wLFxuICBsb2c6IG5vb3AsXG4gIHdhcm46IG5vb3AsXG4gIGluZm86IG5vb3AsXG4gIGVycm9yOiBub29wXG59O1xudmFyIGV4cG9ydGVkTG9nZ2VyID0gZmFrZUxvZ2dlcjsgLy8gbGV0IGxhc3RDYWxsVGltZTtcbi8vIGZ1bmN0aW9uIGZvcm1hdE1zZ1dpdGhUaW1lSW5mbyh0eXBlLCBtc2cpIHtcbi8vICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbi8vICAgY29uc3QgZGlmZiA9IGxhc3RDYWxsVGltZSA/ICcrJyArIChub3cgLSBsYXN0Q2FsbFRpbWUpIDogJzAnO1xuLy8gICBsYXN0Q2FsbFRpbWUgPSBub3c7XG4vLyAgIG1zZyA9IChuZXcgRGF0ZShub3cpKS50b0lTT1N0cmluZygpICsgJyB8IFsnICsgIHR5cGUgKyAnXSA+ICcgKyBtc2cgKyAnICggJyArIGRpZmYgKyAnIG1zICknO1xuLy8gICByZXR1cm4gbXNnO1xuLy8gfVxuXG5mdW5jdGlvbiBmb3JtYXRNc2codHlwZSwgbXNnKSB7XG4gIG1zZyA9ICdbJyArIHR5cGUgKyAnXSA+ICcgKyBtc2c7XG4gIHJldHVybiBtc2c7XG59XG5cbnZhciBnbG9iYWwgPSBPYmplY3QoX2dldF9zZWxmX3Njb3BlX19XRUJQQUNLX0lNUE9SVEVEX01PRFVMRV8wX19bXCJnZXRTZWxmU2NvcGVcIl0pKCk7XG5cbmZ1bmN0aW9uIGNvbnNvbGVQcmludEZuKHR5cGUpIHtcbiAgdmFyIGZ1bmMgPSBnbG9iYWwuY29uc29sZVt0eXBlXTtcblxuICBpZiAoZnVuYykge1xuICAgIHJldHVybiBmdW5jdGlvbiAoKSB7XG4gICAgICBmb3IgKHZhciBfbGVuID0gYXJndW1lbnRzLmxlbmd0aCwgYXJncyA9IG5ldyBBcnJheShfbGVuKSwgX2tleSA9IDA7IF9rZXkgPCBfbGVuOyBfa2V5KyspIHtcbiAgICAgICAgYXJnc1tfa2V5XSA9IGFyZ3VtZW50c1tfa2V5XTtcbiAgICAgIH1cblxuICAgICAgaWYgKGFyZ3NbMF0pIHtcbiAgICAgICAgYXJnc1swXSA9IGZvcm1hdE1zZyh0eXBlLCBhcmdzWzBdKTtcbiAgICAgIH1cblxuICAgICAgZnVuYy5hcHBseShnbG9iYWwuY29uc29sZSwgYXJncyk7XG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiBub29wO1xufVxuXG5mdW5jdGlvbiBleHBvcnRMb2dnZXJGdW5jdGlvbnMoZGVidWdDb25maWcpIHtcbiAgZm9yICh2YXIgX2xlbjIgPSBhcmd1bWVudHMubGVuZ3RoLCBmdW5jdGlvbnMgPSBuZXcgQXJyYXkoX2xlbjIgPiAxID8gX2xlbjIgLSAxIDogMCksIF9rZXkyID0gMTsgX2tleTIgPCBfbGVuMjsgX2tleTIrKykge1xuICAgIGZ1bmN0aW9uc1tfa2V5MiAtIDFdID0gYXJndW1lbnRzW19rZXkyXTtcbiAgfVxuXG4gIGZ1bmN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgZXhwb3J0ZWRMb2dnZXJbdHlwZV0gPSBkZWJ1Z0NvbmZpZ1t0eXBlXSA/IGRlYnVnQ29uZmlnW3R5cGVdLmJpbmQoZGVidWdDb25maWcpIDogY29uc29sZVByaW50Rm4odHlwZSk7XG4gIH0pO1xufVxuXG52YXIgZW5hYmxlTG9ncyA9IGZ1bmN0aW9uIGVuYWJsZUxvZ3MoZGVidWdDb25maWcpIHtcbiAgLy8gY2hlY2sgdGhhdCBjb25zb2xlIGlzIGF2YWlsYWJsZVxuICBpZiAoZ2xvYmFsLmNvbnNvbGUgJiYgZGVidWdDb25maWcgPT09IHRydWUgfHwgdHlwZW9mIGRlYnVnQ29uZmlnID09PSAnb2JqZWN0Jykge1xuICAgIGV4cG9ydExvZ2dlckZ1bmN0aW9ucyhkZWJ1Z0NvbmZpZywgLy8gUmVtb3ZlIG91dCBmcm9tIGxpc3QgaGVyZSB0byBoYXJkLWRpc2FibGUgYSBsb2ctbGV2ZWxcbiAgICAvLyAndHJhY2UnLFxuICAgICdkZWJ1ZycsICdsb2cnLCAnaW5mbycsICd3YXJuJywgJ2Vycm9yJyk7IC8vIFNvbWUgYnJvd3NlcnMgZG9uJ3QgYWxsb3cgdG8gdXNlIGJpbmQgb24gY29uc29sZSBvYmplY3QgYW55d2F5XG4gICAgLy8gZmFsbGJhY2sgdG8gZGVmYXVsdCBpZiBuZWVkZWRcblxuICAgIHRyeSB7XG4gICAgICBleHBvcnRlZExvZ2dlci5sb2coKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBleHBvcnRlZExvZ2dlciA9IGZha2VMb2dnZXI7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGV4cG9ydGVkTG9nZ2VyID0gZmFrZUxvZ2dlcjtcbiAgfVxufTtcbnZhciBsb2dnZXIgPSBleHBvcnRlZExvZ2dlcjtcblxuLyoqKi8gfSlcblxuLyoqKioqKi8gfSlbXCJkZWZhdWx0XCJdO1xufSk7XG4vLyMgc291cmNlTWFwcGluZ1VSTD1obHMuanMubWFwIiwiKGZ1bmN0aW9uKHdpbmRvdywgZmFjdG9yeSkge1xuXHR2YXIgbGF6eVNpemVzID0gZmFjdG9yeSh3aW5kb3csIHdpbmRvdy5kb2N1bWVudCwgRGF0ZSk7XG5cdHdpbmRvdy5sYXp5U2l6ZXMgPSBsYXp5U2l6ZXM7XG5cdGlmKHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMpe1xuXHRcdG1vZHVsZS5leHBvcnRzID0gbGF6eVNpemVzO1xuXHR9XG59KHR5cGVvZiB3aW5kb3cgIT0gJ3VuZGVmaW5lZCcgP1xuICAgICAgd2luZG93IDoge30sIGZ1bmN0aW9uIGwod2luZG93LCBkb2N1bWVudCwgRGF0ZSkgeyAvLyBQYXNzIGluIHRoZSB3aW5kb2UgRGF0ZSBmdW5jdGlvbiBhbHNvIGZvciBTU1IgYmVjYXVzZSB0aGUgRGF0ZSBjbGFzcyBjYW4gYmUgbG9zdFxuXHQndXNlIHN0cmljdCc7XG5cdC8qanNoaW50IGVxbnVsbDp0cnVlICovXG5cblx0dmFyIGxhenlzaXplcywgbGF6eVNpemVzQ2ZnO1xuXG5cdChmdW5jdGlvbigpe1xuXHRcdHZhciBwcm9wO1xuXG5cdFx0dmFyIGxhenlTaXplc0RlZmF1bHRzID0ge1xuXHRcdFx0bGF6eUNsYXNzOiAnbGF6eWxvYWQnLFxuXHRcdFx0bG9hZGVkQ2xhc3M6ICdsYXp5bG9hZGVkJyxcblx0XHRcdGxvYWRpbmdDbGFzczogJ2xhenlsb2FkaW5nJyxcblx0XHRcdHByZWxvYWRDbGFzczogJ2xhenlwcmVsb2FkJyxcblx0XHRcdGVycm9yQ2xhc3M6ICdsYXp5ZXJyb3InLFxuXHRcdFx0Ly9zdHJpY3RDbGFzczogJ2xhenlzdHJpY3QnLFxuXHRcdFx0YXV0b3NpemVzQ2xhc3M6ICdsYXp5YXV0b3NpemVzJyxcblx0XHRcdHNyY0F0dHI6ICdkYXRhLXNyYycsXG5cdFx0XHRzcmNzZXRBdHRyOiAnZGF0YS1zcmNzZXQnLFxuXHRcdFx0c2l6ZXNBdHRyOiAnZGF0YS1zaXplcycsXG5cdFx0XHQvL3ByZWxvYWRBZnRlckxvYWQ6IGZhbHNlLFxuXHRcdFx0bWluU2l6ZTogNDAsXG5cdFx0XHRjdXN0b21NZWRpYToge30sXG5cdFx0XHRpbml0OiB0cnVlLFxuXHRcdFx0ZXhwRmFjdG9yOiAxLjUsXG5cdFx0XHRoRmFjOiAwLjgsXG5cdFx0XHRsb2FkTW9kZTogMixcblx0XHRcdGxvYWRIaWRkZW46IHRydWUsXG5cdFx0XHRyaWNUaW1lb3V0OiAwLFxuXHRcdFx0dGhyb3R0bGVEZWxheTogMTI1LFxuXHRcdH07XG5cblx0XHRsYXp5U2l6ZXNDZmcgPSB3aW5kb3cubGF6eVNpemVzQ29uZmlnIHx8IHdpbmRvdy5sYXp5c2l6ZXNDb25maWcgfHwge307XG5cblx0XHRmb3IocHJvcCBpbiBsYXp5U2l6ZXNEZWZhdWx0cyl7XG5cdFx0XHRpZighKHByb3AgaW4gbGF6eVNpemVzQ2ZnKSl7XG5cdFx0XHRcdGxhenlTaXplc0NmZ1twcm9wXSA9IGxhenlTaXplc0RlZmF1bHRzW3Byb3BdO1xuXHRcdFx0fVxuXHRcdH1cblx0fSkoKTtcblxuXHRpZiAoIWRvY3VtZW50IHx8ICFkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKSB7XG5cdFx0cmV0dXJuIHtcblx0XHRcdGluaXQ6IGZ1bmN0aW9uICgpIHt9LFxuXHRcdFx0Y2ZnOiBsYXp5U2l6ZXNDZmcsXG5cdFx0XHRub1N1cHBvcnQ6IHRydWUsXG5cdFx0fTtcblx0fVxuXG5cdHZhciBkb2NFbGVtID0gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50O1xuXG5cdHZhciBzdXBwb3J0UGljdHVyZSA9IHdpbmRvdy5IVE1MUGljdHVyZUVsZW1lbnQ7XG5cblx0dmFyIF9hZGRFdmVudExpc3RlbmVyID0gJ2FkZEV2ZW50TGlzdGVuZXInO1xuXG5cdHZhciBfZ2V0QXR0cmlidXRlID0gJ2dldEF0dHJpYnV0ZSc7XG5cblx0LyoqXG5cdCAqIFVwZGF0ZSB0byBiaW5kIHRvIHdpbmRvdyBiZWNhdXNlICd0aGlzJyBiZWNvbWVzIG51bGwgZHVyaW5nIFNTUlxuXHQgKiBidWlsZHMuXG5cdCAqL1xuXHR2YXIgYWRkRXZlbnRMaXN0ZW5lciA9IHdpbmRvd1tfYWRkRXZlbnRMaXN0ZW5lcl0uYmluZCh3aW5kb3cpO1xuXG5cdHZhciBzZXRUaW1lb3V0ID0gd2luZG93LnNldFRpbWVvdXQ7XG5cblx0dmFyIHJlcXVlc3RBbmltYXRpb25GcmFtZSA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgc2V0VGltZW91dDtcblxuXHR2YXIgcmVxdWVzdElkbGVDYWxsYmFjayA9IHdpbmRvdy5yZXF1ZXN0SWRsZUNhbGxiYWNrO1xuXG5cdHZhciByZWdQaWN0dXJlID0gL15waWN0dXJlJC9pO1xuXG5cdHZhciBsb2FkRXZlbnRzID0gWydsb2FkJywgJ2Vycm9yJywgJ2xhenlpbmNsdWRlZCcsICdfbGF6eWxvYWRlZCddO1xuXG5cdHZhciByZWdDbGFzc0NhY2hlID0ge307XG5cblx0dmFyIGZvckVhY2ggPSBBcnJheS5wcm90b3R5cGUuZm9yRWFjaDtcblxuXHR2YXIgaGFzQ2xhc3MgPSBmdW5jdGlvbihlbGUsIGNscykge1xuXHRcdGlmKCFyZWdDbGFzc0NhY2hlW2Nsc10pe1xuXHRcdFx0cmVnQ2xhc3NDYWNoZVtjbHNdID0gbmV3IFJlZ0V4cCgnKFxcXFxzfF4pJytjbHMrJyhcXFxcc3wkKScpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVnQ2xhc3NDYWNoZVtjbHNdLnRlc3QoZWxlW19nZXRBdHRyaWJ1dGVdKCdjbGFzcycpIHx8ICcnKSAmJiByZWdDbGFzc0NhY2hlW2Nsc107XG5cdH07XG5cblx0dmFyIGFkZENsYXNzID0gZnVuY3Rpb24oZWxlLCBjbHMpIHtcblx0XHRpZiAoIWhhc0NsYXNzKGVsZSwgY2xzKSl7XG5cdFx0XHRlbGUuc2V0QXR0cmlidXRlKCdjbGFzcycsIChlbGVbX2dldEF0dHJpYnV0ZV0oJ2NsYXNzJykgfHwgJycpLnRyaW0oKSArICcgJyArIGNscyk7XG5cdFx0fVxuXHR9O1xuXG5cdHZhciByZW1vdmVDbGFzcyA9IGZ1bmN0aW9uKGVsZSwgY2xzKSB7XG5cdFx0dmFyIHJlZztcblx0XHRpZiAoKHJlZyA9IGhhc0NsYXNzKGVsZSxjbHMpKSkge1xuXHRcdFx0ZWxlLnNldEF0dHJpYnV0ZSgnY2xhc3MnLCAoZWxlW19nZXRBdHRyaWJ1dGVdKCdjbGFzcycpIHx8ICcnKS5yZXBsYWNlKHJlZywgJyAnKSk7XG5cdFx0fVxuXHR9O1xuXG5cdHZhciBhZGRSZW1vdmVMb2FkRXZlbnRzID0gZnVuY3Rpb24oZG9tLCBmbiwgYWRkKXtcblx0XHR2YXIgYWN0aW9uID0gYWRkID8gX2FkZEV2ZW50TGlzdGVuZXIgOiAncmVtb3ZlRXZlbnRMaXN0ZW5lcic7XG5cdFx0aWYoYWRkKXtcblx0XHRcdGFkZFJlbW92ZUxvYWRFdmVudHMoZG9tLCBmbik7XG5cdFx0fVxuXHRcdGxvYWRFdmVudHMuZm9yRWFjaChmdW5jdGlvbihldnQpe1xuXHRcdFx0ZG9tW2FjdGlvbl0oZXZ0LCBmbik7XG5cdFx0fSk7XG5cdH07XG5cblx0dmFyIHRyaWdnZXJFdmVudCA9IGZ1bmN0aW9uKGVsZW0sIG5hbWUsIGRldGFpbCwgbm9CdWJibGVzLCBub0NhbmNlbGFibGUpe1xuXHRcdHZhciBldmVudCA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KCdFdmVudCcpO1xuXG5cdFx0aWYoIWRldGFpbCl7XG5cdFx0XHRkZXRhaWwgPSB7fTtcblx0XHR9XG5cblx0XHRkZXRhaWwuaW5zdGFuY2UgPSBsYXp5c2l6ZXM7XG5cblx0XHRldmVudC5pbml0RXZlbnQobmFtZSwgIW5vQnViYmxlcywgIW5vQ2FuY2VsYWJsZSk7XG5cblx0XHRldmVudC5kZXRhaWwgPSBkZXRhaWw7XG5cblx0XHRlbGVtLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xuXHRcdHJldHVybiBldmVudDtcblx0fTtcblxuXHR2YXIgdXBkYXRlUG9seWZpbGwgPSBmdW5jdGlvbiAoZWwsIGZ1bGwpe1xuXHRcdHZhciBwb2x5ZmlsbDtcblx0XHRpZiggIXN1cHBvcnRQaWN0dXJlICYmICggcG9seWZpbGwgPSAod2luZG93LnBpY3R1cmVmaWxsIHx8IGxhenlTaXplc0NmZy5wZikgKSApe1xuXHRcdFx0aWYoZnVsbCAmJiBmdWxsLnNyYyAmJiAhZWxbX2dldEF0dHJpYnV0ZV0oJ3NyY3NldCcpKXtcblx0XHRcdFx0ZWwuc2V0QXR0cmlidXRlKCdzcmNzZXQnLCBmdWxsLnNyYyk7XG5cdFx0XHR9XG5cdFx0XHRwb2x5ZmlsbCh7cmVldmFsdWF0ZTogdHJ1ZSwgZWxlbWVudHM6IFtlbF19KTtcblx0XHR9IGVsc2UgaWYoZnVsbCAmJiBmdWxsLnNyYyl7XG5cdFx0XHRlbC5zcmMgPSBmdWxsLnNyYztcblx0XHR9XG5cdH07XG5cblx0dmFyIGdldENTUyA9IGZ1bmN0aW9uIChlbGVtLCBzdHlsZSl7XG5cdFx0cmV0dXJuIChnZXRDb21wdXRlZFN0eWxlKGVsZW0sIG51bGwpIHx8IHt9KVtzdHlsZV07XG5cdH07XG5cblx0dmFyIGdldFdpZHRoID0gZnVuY3Rpb24oZWxlbSwgcGFyZW50LCB3aWR0aCl7XG5cdFx0d2lkdGggPSB3aWR0aCB8fCBlbGVtLm9mZnNldFdpZHRoO1xuXG5cdFx0d2hpbGUod2lkdGggPCBsYXp5U2l6ZXNDZmcubWluU2l6ZSAmJiBwYXJlbnQgJiYgIWVsZW0uX2xhenlzaXplc1dpZHRoKXtcblx0XHRcdHdpZHRoID0gIHBhcmVudC5vZmZzZXRXaWR0aDtcblx0XHRcdHBhcmVudCA9IHBhcmVudC5wYXJlbnROb2RlO1xuXHRcdH1cblxuXHRcdHJldHVybiB3aWR0aDtcblx0fTtcblxuXHR2YXIgckFGID0gKGZ1bmN0aW9uKCl7XG5cdFx0dmFyIHJ1bm5pbmcsIHdhaXRpbmc7XG5cdFx0dmFyIGZpcnN0Rm5zID0gW107XG5cdFx0dmFyIHNlY29uZEZucyA9IFtdO1xuXHRcdHZhciBmbnMgPSBmaXJzdEZucztcblxuXHRcdHZhciBydW4gPSBmdW5jdGlvbigpe1xuXHRcdFx0dmFyIHJ1bkZucyA9IGZucztcblxuXHRcdFx0Zm5zID0gZmlyc3RGbnMubGVuZ3RoID8gc2Vjb25kRm5zIDogZmlyc3RGbnM7XG5cblx0XHRcdHJ1bm5pbmcgPSB0cnVlO1xuXHRcdFx0d2FpdGluZyA9IGZhbHNlO1xuXG5cdFx0XHR3aGlsZShydW5GbnMubGVuZ3RoKXtcblx0XHRcdFx0cnVuRm5zLnNoaWZ0KCkoKTtcblx0XHRcdH1cblxuXHRcdFx0cnVubmluZyA9IGZhbHNlO1xuXHRcdH07XG5cblx0XHR2YXIgcmFmQmF0Y2ggPSBmdW5jdGlvbihmbiwgcXVldWUpe1xuXHRcdFx0aWYocnVubmluZyAmJiAhcXVldWUpe1xuXHRcdFx0XHRmbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Zm5zLnB1c2goZm4pO1xuXG5cdFx0XHRcdGlmKCF3YWl0aW5nKXtcblx0XHRcdFx0XHR3YWl0aW5nID0gdHJ1ZTtcblx0XHRcdFx0XHQoZG9jdW1lbnQuaGlkZGVuID8gc2V0VGltZW91dCA6IHJlcXVlc3RBbmltYXRpb25GcmFtZSkocnVuKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH07XG5cblx0XHRyYWZCYXRjaC5fbHNGbHVzaCA9IHJ1bjtcblxuXHRcdHJldHVybiByYWZCYXRjaDtcblx0fSkoKTtcblxuXHR2YXIgckFGSXQgPSBmdW5jdGlvbihmbiwgc2ltcGxlKXtcblx0XHRyZXR1cm4gc2ltcGxlID9cblx0XHRcdGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyQUYoZm4pO1xuXHRcdFx0fSA6XG5cdFx0XHRmdW5jdGlvbigpe1xuXHRcdFx0XHR2YXIgdGhhdCA9IHRoaXM7XG5cdFx0XHRcdHZhciBhcmdzID0gYXJndW1lbnRzO1xuXHRcdFx0XHRyQUYoZnVuY3Rpb24oKXtcblx0XHRcdFx0XHRmbi5hcHBseSh0aGF0LCBhcmdzKTtcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cdFx0O1xuXHR9O1xuXG5cdHZhciB0aHJvdHRsZSA9IGZ1bmN0aW9uKGZuKXtcblx0XHR2YXIgcnVubmluZztcblx0XHR2YXIgbGFzdFRpbWUgPSAwO1xuXHRcdHZhciBnRGVsYXkgPSBsYXp5U2l6ZXNDZmcudGhyb3R0bGVEZWxheTtcblx0XHR2YXIgcklDVGltZW91dCA9IGxhenlTaXplc0NmZy5yaWNUaW1lb3V0O1xuXHRcdHZhciBydW4gPSBmdW5jdGlvbigpe1xuXHRcdFx0cnVubmluZyA9IGZhbHNlO1xuXHRcdFx0bGFzdFRpbWUgPSBEYXRlLm5vdygpO1xuXHRcdFx0Zm4oKTtcblx0XHR9O1xuXHRcdHZhciBpZGxlQ2FsbGJhY2sgPSByZXF1ZXN0SWRsZUNhbGxiYWNrICYmIHJJQ1RpbWVvdXQgPiA0OSA/XG5cdFx0XHRmdW5jdGlvbigpe1xuXHRcdFx0XHRyZXF1ZXN0SWRsZUNhbGxiYWNrKHJ1biwge3RpbWVvdXQ6IHJJQ1RpbWVvdXR9KTtcblxuXHRcdFx0XHRpZihySUNUaW1lb3V0ICE9PSBsYXp5U2l6ZXNDZmcucmljVGltZW91dCl7XG5cdFx0XHRcdFx0cklDVGltZW91dCA9IGxhenlTaXplc0NmZy5yaWNUaW1lb3V0O1xuXHRcdFx0XHR9XG5cdFx0XHR9IDpcblx0XHRcdHJBRkl0KGZ1bmN0aW9uKCl7XG5cdFx0XHRcdHNldFRpbWVvdXQocnVuKTtcblx0XHRcdH0sIHRydWUpXG5cdFx0O1xuXG5cdFx0cmV0dXJuIGZ1bmN0aW9uKGlzUHJpb3JpdHkpe1xuXHRcdFx0dmFyIGRlbGF5O1xuXG5cdFx0XHRpZigoaXNQcmlvcml0eSA9IGlzUHJpb3JpdHkgPT09IHRydWUpKXtcblx0XHRcdFx0cklDVGltZW91dCA9IDMzO1xuXHRcdFx0fVxuXG5cdFx0XHRpZihydW5uaW5nKXtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHRydW5uaW5nID0gIHRydWU7XG5cblx0XHRcdGRlbGF5ID0gZ0RlbGF5IC0gKERhdGUubm93KCkgLSBsYXN0VGltZSk7XG5cblx0XHRcdGlmKGRlbGF5IDwgMCl7XG5cdFx0XHRcdGRlbGF5ID0gMDtcblx0XHRcdH1cblxuXHRcdFx0aWYoaXNQcmlvcml0eSB8fCBkZWxheSA8IDkpe1xuXHRcdFx0XHRpZGxlQ2FsbGJhY2soKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHNldFRpbWVvdXQoaWRsZUNhbGxiYWNrLCBkZWxheSk7XG5cdFx0XHR9XG5cdFx0fTtcblx0fTtcblxuXHQvL2Jhc2VkIG9uIGh0dHA6Ly9tb2Rlcm5qYXZhc2NyaXB0LmJsb2dzcG90LmRlLzIwMTMvMDgvYnVpbGRpbmctYmV0dGVyLWRlYm91bmNlLmh0bWxcblx0dmFyIGRlYm91bmNlID0gZnVuY3Rpb24oZnVuYykge1xuXHRcdHZhciB0aW1lb3V0LCB0aW1lc3RhbXA7XG5cdFx0dmFyIHdhaXQgPSA5OTtcblx0XHR2YXIgcnVuID0gZnVuY3Rpb24oKXtcblx0XHRcdHRpbWVvdXQgPSBudWxsO1xuXHRcdFx0ZnVuYygpO1xuXHRcdH07XG5cdFx0dmFyIGxhdGVyID0gZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgbGFzdCA9IERhdGUubm93KCkgLSB0aW1lc3RhbXA7XG5cblx0XHRcdGlmIChsYXN0IDwgd2FpdCkge1xuXHRcdFx0XHRzZXRUaW1lb3V0KGxhdGVyLCB3YWl0IC0gbGFzdCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQocmVxdWVzdElkbGVDYWxsYmFjayB8fCBydW4pKHJ1bik7XG5cdFx0XHR9XG5cdFx0fTtcblxuXHRcdHJldHVybiBmdW5jdGlvbigpIHtcblx0XHRcdHRpbWVzdGFtcCA9IERhdGUubm93KCk7XG5cblx0XHRcdGlmICghdGltZW91dCkge1xuXHRcdFx0XHR0aW1lb3V0ID0gc2V0VGltZW91dChsYXRlciwgd2FpdCk7XG5cdFx0XHR9XG5cdFx0fTtcblx0fTtcblxuXHR2YXIgbG9hZGVyID0gKGZ1bmN0aW9uKCl7XG5cdFx0dmFyIHByZWxvYWRFbGVtcywgaXNDb21wbGV0ZWQsIHJlc2V0UHJlbG9hZGluZ1RpbWVyLCBsb2FkTW9kZSwgc3RhcnRlZDtcblxuXHRcdHZhciBlTHZXLCBlbHZILCBlTHRvcCwgZUxsZWZ0LCBlTHJpZ2h0LCBlTGJvdHRvbSwgaXNCb2R5SGlkZGVuO1xuXG5cdFx0dmFyIHJlZ0ltZyA9IC9eaW1nJC9pO1xuXHRcdHZhciByZWdJZnJhbWUgPSAvXmlmcmFtZSQvaTtcblxuXHRcdHZhciBzdXBwb3J0U2Nyb2xsID0gKCdvbnNjcm9sbCcgaW4gd2luZG93KSAmJiAhKC8oZ2xlfGluZylib3QvLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCkpO1xuXG5cdFx0dmFyIHNocmlua0V4cGFuZCA9IDA7XG5cdFx0dmFyIGN1cnJlbnRFeHBhbmQgPSAwO1xuXG5cdFx0dmFyIGlzTG9hZGluZyA9IDA7XG5cdFx0dmFyIGxvd1J1bnMgPSAtMTtcblxuXHRcdHZhciByZXNldFByZWxvYWRpbmcgPSBmdW5jdGlvbihlKXtcblx0XHRcdGlzTG9hZGluZy0tO1xuXHRcdFx0aWYoIWUgfHwgaXNMb2FkaW5nIDwgMCB8fCAhZS50YXJnZXQpe1xuXHRcdFx0XHRpc0xvYWRpbmcgPSAwO1xuXHRcdFx0fVxuXHRcdH07XG5cblx0XHR2YXIgaXNWaXNpYmxlID0gZnVuY3Rpb24gKGVsZW0pIHtcblx0XHRcdGlmIChpc0JvZHlIaWRkZW4gPT0gbnVsbCkge1xuXHRcdFx0XHRpc0JvZHlIaWRkZW4gPSBnZXRDU1MoZG9jdW1lbnQuYm9keSwgJ3Zpc2liaWxpdHknKSA9PSAnaGlkZGVuJztcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGlzQm9keUhpZGRlbiB8fCAhKGdldENTUyhlbGVtLnBhcmVudE5vZGUsICd2aXNpYmlsaXR5JykgPT0gJ2hpZGRlbicgJiYgZ2V0Q1NTKGVsZW0sICd2aXNpYmlsaXR5JykgPT0gJ2hpZGRlbicpO1xuXHRcdH07XG5cblx0XHR2YXIgaXNOZXN0ZWRWaXNpYmxlID0gZnVuY3Rpb24oZWxlbSwgZWxlbUV4cGFuZCl7XG5cdFx0XHR2YXIgb3V0ZXJSZWN0O1xuXHRcdFx0dmFyIHBhcmVudCA9IGVsZW07XG5cdFx0XHR2YXIgdmlzaWJsZSA9IGlzVmlzaWJsZShlbGVtKTtcblxuXHRcdFx0ZUx0b3AgLT0gZWxlbUV4cGFuZDtcblx0XHRcdGVMYm90dG9tICs9IGVsZW1FeHBhbmQ7XG5cdFx0XHRlTGxlZnQgLT0gZWxlbUV4cGFuZDtcblx0XHRcdGVMcmlnaHQgKz0gZWxlbUV4cGFuZDtcblxuXHRcdFx0d2hpbGUodmlzaWJsZSAmJiAocGFyZW50ID0gcGFyZW50Lm9mZnNldFBhcmVudCkgJiYgcGFyZW50ICE9IGRvY3VtZW50LmJvZHkgJiYgcGFyZW50ICE9IGRvY0VsZW0pe1xuXHRcdFx0XHR2aXNpYmxlID0gKChnZXRDU1MocGFyZW50LCAnb3BhY2l0eScpIHx8IDEpID4gMCk7XG5cblx0XHRcdFx0aWYodmlzaWJsZSAmJiBnZXRDU1MocGFyZW50LCAnb3ZlcmZsb3cnKSAhPSAndmlzaWJsZScpe1xuXHRcdFx0XHRcdG91dGVyUmVjdCA9IHBhcmVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRcdFx0XHR2aXNpYmxlID0gZUxyaWdodCA+IG91dGVyUmVjdC5sZWZ0ICYmXG5cdFx0XHRcdFx0XHRlTGxlZnQgPCBvdXRlclJlY3QucmlnaHQgJiZcblx0XHRcdFx0XHRcdGVMYm90dG9tID4gb3V0ZXJSZWN0LnRvcCAtIDEgJiZcblx0XHRcdFx0XHRcdGVMdG9wIDwgb3V0ZXJSZWN0LmJvdHRvbSArIDFcblx0XHRcdFx0XHQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHZpc2libGU7XG5cdFx0fTtcblxuXHRcdHZhciBjaGVja0VsZW1lbnRzID0gZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgZUxsZW4sIGksIHJlY3QsIGF1dG9Mb2FkRWxlbSwgbG9hZGVkU29tZXRoaW5nLCBlbGVtRXhwYW5kLCBlbGVtTmVnYXRpdmVFeHBhbmQsIGVsZW1FeHBhbmRWYWwsXG5cdFx0XHRcdGJlZm9yZUV4cGFuZFZhbCwgZGVmYXVsdEV4cGFuZCwgcHJlbG9hZEV4cGFuZCwgaEZhYztcblx0XHRcdHZhciBsYXp5bG9hZEVsZW1zID0gbGF6eXNpemVzLmVsZW1lbnRzO1xuXG5cdFx0XHRpZigobG9hZE1vZGUgPSBsYXp5U2l6ZXNDZmcubG9hZE1vZGUpICYmIGlzTG9hZGluZyA8IDggJiYgKGVMbGVuID0gbGF6eWxvYWRFbGVtcy5sZW5ndGgpKXtcblxuXHRcdFx0XHRpID0gMDtcblxuXHRcdFx0XHRsb3dSdW5zKys7XG5cblx0XHRcdFx0Zm9yKDsgaSA8IGVMbGVuOyBpKyspe1xuXG5cdFx0XHRcdFx0aWYoIWxhenlsb2FkRWxlbXNbaV0gfHwgbGF6eWxvYWRFbGVtc1tpXS5fbGF6eVJhY2Upe2NvbnRpbnVlO31cblxuXHRcdFx0XHRcdGlmKCFzdXBwb3J0U2Nyb2xsIHx8IChsYXp5c2l6ZXMucHJlbWF0dXJlVW52ZWlsICYmIGxhenlzaXplcy5wcmVtYXR1cmVVbnZlaWwobGF6eWxvYWRFbGVtc1tpXSkpKXt1bnZlaWxFbGVtZW50KGxhenlsb2FkRWxlbXNbaV0pO2NvbnRpbnVlO31cblxuXHRcdFx0XHRcdGlmKCEoZWxlbUV4cGFuZFZhbCA9IGxhenlsb2FkRWxlbXNbaV1bX2dldEF0dHJpYnV0ZV0oJ2RhdGEtZXhwYW5kJykpIHx8ICEoZWxlbUV4cGFuZCA9IGVsZW1FeHBhbmRWYWwgKiAxKSl7XG5cdFx0XHRcdFx0XHRlbGVtRXhwYW5kID0gY3VycmVudEV4cGFuZDtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAoIWRlZmF1bHRFeHBhbmQpIHtcblx0XHRcdFx0XHRcdGRlZmF1bHRFeHBhbmQgPSAoIWxhenlTaXplc0NmZy5leHBhbmQgfHwgbGF6eVNpemVzQ2ZnLmV4cGFuZCA8IDEpID9cblx0XHRcdFx0XHRcdFx0ZG9jRWxlbS5jbGllbnRIZWlnaHQgPiA1MDAgJiYgZG9jRWxlbS5jbGllbnRXaWR0aCA+IDUwMCA/IDUwMCA6IDM3MCA6XG5cdFx0XHRcdFx0XHRcdGxhenlTaXplc0NmZy5leHBhbmQ7XG5cblx0XHRcdFx0XHRcdGxhenlzaXplcy5fZGVmRXggPSBkZWZhdWx0RXhwYW5kO1xuXG5cdFx0XHRcdFx0XHRwcmVsb2FkRXhwYW5kID0gZGVmYXVsdEV4cGFuZCAqIGxhenlTaXplc0NmZy5leHBGYWN0b3I7XG5cdFx0XHRcdFx0XHRoRmFjID0gbGF6eVNpemVzQ2ZnLmhGYWM7XG5cdFx0XHRcdFx0XHRpc0JvZHlIaWRkZW4gPSBudWxsO1xuXG5cdFx0XHRcdFx0XHRpZihjdXJyZW50RXhwYW5kIDwgcHJlbG9hZEV4cGFuZCAmJiBpc0xvYWRpbmcgPCAxICYmIGxvd1J1bnMgPiAyICYmIGxvYWRNb2RlID4gMiAmJiAhZG9jdW1lbnQuaGlkZGVuKXtcblx0XHRcdFx0XHRcdFx0Y3VycmVudEV4cGFuZCA9IHByZWxvYWRFeHBhbmQ7XG5cdFx0XHRcdFx0XHRcdGxvd1J1bnMgPSAwO1xuXHRcdFx0XHRcdFx0fSBlbHNlIGlmKGxvYWRNb2RlID4gMSAmJiBsb3dSdW5zID4gMSAmJiBpc0xvYWRpbmcgPCA2KXtcblx0XHRcdFx0XHRcdFx0Y3VycmVudEV4cGFuZCA9IGRlZmF1bHRFeHBhbmQ7XG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHRjdXJyZW50RXhwYW5kID0gc2hyaW5rRXhwYW5kO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGlmKGJlZm9yZUV4cGFuZFZhbCAhPT0gZWxlbUV4cGFuZCl7XG5cdFx0XHRcdFx0XHRlTHZXID0gaW5uZXJXaWR0aCArIChlbGVtRXhwYW5kICogaEZhYyk7XG5cdFx0XHRcdFx0XHRlbHZIID0gaW5uZXJIZWlnaHQgKyBlbGVtRXhwYW5kO1xuXHRcdFx0XHRcdFx0ZWxlbU5lZ2F0aXZlRXhwYW5kID0gZWxlbUV4cGFuZCAqIC0xO1xuXHRcdFx0XHRcdFx0YmVmb3JlRXhwYW5kVmFsID0gZWxlbUV4cGFuZDtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRyZWN0ID0gbGF6eWxvYWRFbGVtc1tpXS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblxuXHRcdFx0XHRcdGlmICgoZUxib3R0b20gPSByZWN0LmJvdHRvbSkgPj0gZWxlbU5lZ2F0aXZlRXhwYW5kICYmXG5cdFx0XHRcdFx0XHQoZUx0b3AgPSByZWN0LnRvcCkgPD0gZWx2SCAmJlxuXHRcdFx0XHRcdFx0KGVMcmlnaHQgPSByZWN0LnJpZ2h0KSA+PSBlbGVtTmVnYXRpdmVFeHBhbmQgKiBoRmFjICYmXG5cdFx0XHRcdFx0XHQoZUxsZWZ0ID0gcmVjdC5sZWZ0KSA8PSBlTHZXICYmXG5cdFx0XHRcdFx0XHQoZUxib3R0b20gfHwgZUxyaWdodCB8fCBlTGxlZnQgfHwgZUx0b3ApICYmXG5cdFx0XHRcdFx0XHQobGF6eVNpemVzQ2ZnLmxvYWRIaWRkZW4gfHwgaXNWaXNpYmxlKGxhenlsb2FkRWxlbXNbaV0pKSAmJlxuXHRcdFx0XHRcdFx0KChpc0NvbXBsZXRlZCAmJiBpc0xvYWRpbmcgPCAzICYmICFlbGVtRXhwYW5kVmFsICYmIChsb2FkTW9kZSA8IDMgfHwgbG93UnVucyA8IDQpKSB8fCBpc05lc3RlZFZpc2libGUobGF6eWxvYWRFbGVtc1tpXSwgZWxlbUV4cGFuZCkpKXtcblx0XHRcdFx0XHRcdHVudmVpbEVsZW1lbnQobGF6eWxvYWRFbGVtc1tpXSk7XG5cdFx0XHRcdFx0XHRsb2FkZWRTb21ldGhpbmcgPSB0cnVlO1xuXHRcdFx0XHRcdFx0aWYoaXNMb2FkaW5nID4gOSl7YnJlYWs7fVxuXHRcdFx0XHRcdH0gZWxzZSBpZighbG9hZGVkU29tZXRoaW5nICYmIGlzQ29tcGxldGVkICYmICFhdXRvTG9hZEVsZW0gJiZcblx0XHRcdFx0XHRcdGlzTG9hZGluZyA8IDQgJiYgbG93UnVucyA8IDQgJiYgbG9hZE1vZGUgPiAyICYmXG5cdFx0XHRcdFx0XHQocHJlbG9hZEVsZW1zWzBdIHx8IGxhenlTaXplc0NmZy5wcmVsb2FkQWZ0ZXJMb2FkKSAmJlxuXHRcdFx0XHRcdFx0KHByZWxvYWRFbGVtc1swXSB8fCAoIWVsZW1FeHBhbmRWYWwgJiYgKChlTGJvdHRvbSB8fCBlTHJpZ2h0IHx8IGVMbGVmdCB8fCBlTHRvcCkgfHwgbGF6eWxvYWRFbGVtc1tpXVtfZ2V0QXR0cmlidXRlXShsYXp5U2l6ZXNDZmcuc2l6ZXNBdHRyKSAhPSAnYXV0bycpKSkpe1xuXHRcdFx0XHRcdFx0YXV0b0xvYWRFbGVtID0gcHJlbG9hZEVsZW1zWzBdIHx8IGxhenlsb2FkRWxlbXNbaV07XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYoYXV0b0xvYWRFbGVtICYmICFsb2FkZWRTb21ldGhpbmcpe1xuXHRcdFx0XHRcdHVudmVpbEVsZW1lbnQoYXV0b0xvYWRFbGVtKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH07XG5cblx0XHR2YXIgdGhyb3R0bGVkQ2hlY2tFbGVtZW50cyA9IHRocm90dGxlKGNoZWNrRWxlbWVudHMpO1xuXG5cdFx0dmFyIHN3aXRjaExvYWRpbmdDbGFzcyA9IGZ1bmN0aW9uKGUpe1xuXHRcdFx0dmFyIGVsZW0gPSBlLnRhcmdldDtcblxuXHRcdFx0aWYgKGVsZW0uX2xhenlDYWNoZSkge1xuXHRcdFx0XHRkZWxldGUgZWxlbS5fbGF6eUNhY2hlO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdHJlc2V0UHJlbG9hZGluZyhlKTtcblx0XHRcdGFkZENsYXNzKGVsZW0sIGxhenlTaXplc0NmZy5sb2FkZWRDbGFzcyk7XG5cdFx0XHRyZW1vdmVDbGFzcyhlbGVtLCBsYXp5U2l6ZXNDZmcubG9hZGluZ0NsYXNzKTtcblx0XHRcdGFkZFJlbW92ZUxvYWRFdmVudHMoZWxlbSwgcmFmU3dpdGNoTG9hZGluZ0NsYXNzKTtcblx0XHRcdHRyaWdnZXJFdmVudChlbGVtLCAnbGF6eWxvYWRlZCcpO1xuXHRcdH07XG5cdFx0dmFyIHJhZmVkU3dpdGNoTG9hZGluZ0NsYXNzID0gckFGSXQoc3dpdGNoTG9hZGluZ0NsYXNzKTtcblx0XHR2YXIgcmFmU3dpdGNoTG9hZGluZ0NsYXNzID0gZnVuY3Rpb24oZSl7XG5cdFx0XHRyYWZlZFN3aXRjaExvYWRpbmdDbGFzcyh7dGFyZ2V0OiBlLnRhcmdldH0pO1xuXHRcdH07XG5cblx0XHR2YXIgY2hhbmdlSWZyYW1lU3JjID0gZnVuY3Rpb24oZWxlbSwgc3JjKXtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGVsZW0uY29udGVudFdpbmRvdy5sb2NhdGlvbi5yZXBsYWNlKHNyYyk7XG5cdFx0XHR9IGNhdGNoKGUpe1xuXHRcdFx0XHRlbGVtLnNyYyA9IHNyYztcblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0dmFyIGhhbmRsZVNvdXJjZXMgPSBmdW5jdGlvbihzb3VyY2Upe1xuXHRcdFx0dmFyIGN1c3RvbU1lZGlhO1xuXG5cdFx0XHR2YXIgc291cmNlU3Jjc2V0ID0gc291cmNlW19nZXRBdHRyaWJ1dGVdKGxhenlTaXplc0NmZy5zcmNzZXRBdHRyKTtcblxuXHRcdFx0aWYoIChjdXN0b21NZWRpYSA9IGxhenlTaXplc0NmZy5jdXN0b21NZWRpYVtzb3VyY2VbX2dldEF0dHJpYnV0ZV0oJ2RhdGEtbWVkaWEnKSB8fCBzb3VyY2VbX2dldEF0dHJpYnV0ZV0oJ21lZGlhJyldKSApe1xuXHRcdFx0XHRzb3VyY2Uuc2V0QXR0cmlidXRlKCdtZWRpYScsIGN1c3RvbU1lZGlhKTtcblx0XHRcdH1cblxuXHRcdFx0aWYoc291cmNlU3Jjc2V0KXtcblx0XHRcdFx0c291cmNlLnNldEF0dHJpYnV0ZSgnc3Jjc2V0Jywgc291cmNlU3Jjc2V0KTtcblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0dmFyIGxhenlVbnZlaWwgPSByQUZJdChmdW5jdGlvbiAoZWxlbSwgZGV0YWlsLCBpc0F1dG8sIHNpemVzLCBpc0ltZyl7XG5cdFx0XHR2YXIgc3JjLCBzcmNzZXQsIHBhcmVudCwgaXNQaWN0dXJlLCBldmVudCwgZmlyZXNMb2FkO1xuXG5cdFx0XHRpZighKGV2ZW50ID0gdHJpZ2dlckV2ZW50KGVsZW0sICdsYXp5YmVmb3JldW52ZWlsJywgZGV0YWlsKSkuZGVmYXVsdFByZXZlbnRlZCl7XG5cblx0XHRcdFx0aWYoc2l6ZXMpe1xuXHRcdFx0XHRcdGlmKGlzQXV0byl7XG5cdFx0XHRcdFx0XHRhZGRDbGFzcyhlbGVtLCBsYXp5U2l6ZXNDZmcuYXV0b3NpemVzQ2xhc3MpO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRlbGVtLnNldEF0dHJpYnV0ZSgnc2l6ZXMnLCBzaXplcyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0c3Jjc2V0ID0gZWxlbVtfZ2V0QXR0cmlidXRlXShsYXp5U2l6ZXNDZmcuc3Jjc2V0QXR0cik7XG5cdFx0XHRcdHNyYyA9IGVsZW1bX2dldEF0dHJpYnV0ZV0obGF6eVNpemVzQ2ZnLnNyY0F0dHIpO1xuXG5cdFx0XHRcdGlmKGlzSW1nKSB7XG5cdFx0XHRcdFx0cGFyZW50ID0gZWxlbS5wYXJlbnROb2RlO1xuXHRcdFx0XHRcdGlzUGljdHVyZSA9IHBhcmVudCAmJiByZWdQaWN0dXJlLnRlc3QocGFyZW50Lm5vZGVOYW1lIHx8ICcnKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGZpcmVzTG9hZCA9IGRldGFpbC5maXJlc0xvYWQgfHwgKCgnc3JjJyBpbiBlbGVtKSAmJiAoc3Jjc2V0IHx8IHNyYyB8fCBpc1BpY3R1cmUpKTtcblxuXHRcdFx0XHRldmVudCA9IHt0YXJnZXQ6IGVsZW19O1xuXG5cdFx0XHRcdGFkZENsYXNzKGVsZW0sIGxhenlTaXplc0NmZy5sb2FkaW5nQ2xhc3MpO1xuXG5cdFx0XHRcdGlmKGZpcmVzTG9hZCl7XG5cdFx0XHRcdFx0Y2xlYXJUaW1lb3V0KHJlc2V0UHJlbG9hZGluZ1RpbWVyKTtcblx0XHRcdFx0XHRyZXNldFByZWxvYWRpbmdUaW1lciA9IHNldFRpbWVvdXQocmVzZXRQcmVsb2FkaW5nLCAyNTAwKTtcblx0XHRcdFx0XHRhZGRSZW1vdmVMb2FkRXZlbnRzKGVsZW0sIHJhZlN3aXRjaExvYWRpbmdDbGFzcywgdHJ1ZSk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZihpc1BpY3R1cmUpe1xuXHRcdFx0XHRcdGZvckVhY2guY2FsbChwYXJlbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NvdXJjZScpLCBoYW5kbGVTb3VyY2VzKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKHNyY3NldCl7XG5cdFx0XHRcdFx0ZWxlbS5zZXRBdHRyaWJ1dGUoJ3NyY3NldCcsIHNyY3NldCk7XG5cdFx0XHRcdH0gZWxzZSBpZihzcmMgJiYgIWlzUGljdHVyZSl7XG5cdFx0XHRcdFx0aWYocmVnSWZyYW1lLnRlc3QoZWxlbS5ub2RlTmFtZSkpe1xuXHRcdFx0XHRcdFx0Y2hhbmdlSWZyYW1lU3JjKGVsZW0sIHNyYyk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdGVsZW0uc3JjID0gc3JjO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKGlzSW1nICYmIChzcmNzZXQgfHwgaXNQaWN0dXJlKSl7XG5cdFx0XHRcdFx0dXBkYXRlUG9seWZpbGwoZWxlbSwge3NyYzogc3JjfSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0aWYoZWxlbS5fbGF6eVJhY2Upe1xuXHRcdFx0XHRkZWxldGUgZWxlbS5fbGF6eVJhY2U7XG5cdFx0XHR9XG5cdFx0XHRyZW1vdmVDbGFzcyhlbGVtLCBsYXp5U2l6ZXNDZmcubGF6eUNsYXNzKTtcblxuXHRcdFx0ckFGKGZ1bmN0aW9uKCl7XG5cdFx0XHRcdC8vIFBhcnQgb2YgdGhpcyBjYW4gYmUgcmVtb3ZlZCBhcyBzb29uIGFzIHRoaXMgZml4IGlzIG9sZGVyOiBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD03NzMxICgyMDE1KVxuXHRcdFx0XHR2YXIgaXNMb2FkZWQgPSBlbGVtLmNvbXBsZXRlICYmIGVsZW0ubmF0dXJhbFdpZHRoID4gMTtcblxuXHRcdFx0XHRpZiggIWZpcmVzTG9hZCB8fCBpc0xvYWRlZCl7XG5cdFx0XHRcdFx0aWYgKGlzTG9hZGVkKSB7XG5cdFx0XHRcdFx0XHRhZGRDbGFzcyhlbGVtLCAnbHMtaXMtY2FjaGVkJyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHN3aXRjaExvYWRpbmdDbGFzcyhldmVudCk7XG5cdFx0XHRcdFx0ZWxlbS5fbGF6eUNhY2hlID0gdHJ1ZTtcblx0XHRcdFx0XHRzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7XG5cdFx0XHRcdFx0XHRpZiAoJ19sYXp5Q2FjaGUnIGluIGVsZW0pIHtcblx0XHRcdFx0XHRcdFx0ZGVsZXRlIGVsZW0uX2xhenlDYWNoZTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9LCA5KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoZWxlbS5sb2FkaW5nID09ICdsYXp5Jykge1xuXHRcdFx0XHRcdGlzTG9hZGluZy0tO1xuXHRcdFx0XHR9XG5cdFx0XHR9LCB0cnVlKTtcblx0XHR9KTtcblxuXHRcdHZhciB1bnZlaWxFbGVtZW50ID0gZnVuY3Rpb24gKGVsZW0pe1xuXHRcdFx0aWYgKGVsZW0uX2xhenlSYWNlKSB7cmV0dXJuO31cblx0XHRcdHZhciBkZXRhaWw7XG5cblx0XHRcdHZhciBpc0ltZyA9IHJlZ0ltZy50ZXN0KGVsZW0ubm9kZU5hbWUpO1xuXG5cdFx0XHQvL2FsbG93IHVzaW5nIHNpemVzPVwiYXV0b1wiLCBidXQgZG9uJ3QgdXNlLiBpdCdzIGludmFsaWQuIFVzZSBkYXRhLXNpemVzPVwiYXV0b1wiIG9yIGEgdmFsaWQgdmFsdWUgZm9yIHNpemVzIGluc3RlYWQgKGkuZS46IHNpemVzPVwiODB2d1wiKVxuXHRcdFx0dmFyIHNpemVzID0gaXNJbWcgJiYgKGVsZW1bX2dldEF0dHJpYnV0ZV0obGF6eVNpemVzQ2ZnLnNpemVzQXR0cikgfHwgZWxlbVtfZ2V0QXR0cmlidXRlXSgnc2l6ZXMnKSk7XG5cdFx0XHR2YXIgaXNBdXRvID0gc2l6ZXMgPT0gJ2F1dG8nO1xuXG5cdFx0XHRpZiggKGlzQXV0byB8fCAhaXNDb21wbGV0ZWQpICYmIGlzSW1nICYmIChlbGVtW19nZXRBdHRyaWJ1dGVdKCdzcmMnKSB8fCBlbGVtLnNyY3NldCkgJiYgIWVsZW0uY29tcGxldGUgJiYgIWhhc0NsYXNzKGVsZW0sIGxhenlTaXplc0NmZy5lcnJvckNsYXNzKSAmJiBoYXNDbGFzcyhlbGVtLCBsYXp5U2l6ZXNDZmcubGF6eUNsYXNzKSl7cmV0dXJuO31cblxuXHRcdFx0ZGV0YWlsID0gdHJpZ2dlckV2ZW50KGVsZW0sICdsYXp5dW52ZWlscmVhZCcpLmRldGFpbDtcblxuXHRcdFx0aWYoaXNBdXRvKXtcblx0XHRcdFx0IGF1dG9TaXplci51cGRhdGVFbGVtKGVsZW0sIHRydWUsIGVsZW0ub2Zmc2V0V2lkdGgpO1xuXHRcdFx0fVxuXG5cdFx0XHRlbGVtLl9sYXp5UmFjZSA9IHRydWU7XG5cdFx0XHRpc0xvYWRpbmcrKztcblxuXHRcdFx0bGF6eVVudmVpbChlbGVtLCBkZXRhaWwsIGlzQXV0bywgc2l6ZXMsIGlzSW1nKTtcblx0XHR9O1xuXG5cdFx0dmFyIGFmdGVyU2Nyb2xsID0gZGVib3VuY2UoZnVuY3Rpb24oKXtcblx0XHRcdGxhenlTaXplc0NmZy5sb2FkTW9kZSA9IDM7XG5cdFx0XHR0aHJvdHRsZWRDaGVja0VsZW1lbnRzKCk7XG5cdFx0fSk7XG5cblx0XHR2YXIgYWx0TG9hZG1vZGVTY3JvbGxMaXN0bmVyID0gZnVuY3Rpb24oKXtcblx0XHRcdGlmKGxhenlTaXplc0NmZy5sb2FkTW9kZSA9PSAzKXtcblx0XHRcdFx0bGF6eVNpemVzQ2ZnLmxvYWRNb2RlID0gMjtcblx0XHRcdH1cblx0XHRcdGFmdGVyU2Nyb2xsKCk7XG5cdFx0fTtcblxuXHRcdHZhciBvbmxvYWQgPSBmdW5jdGlvbigpe1xuXHRcdFx0aWYoaXNDb21wbGV0ZWQpe3JldHVybjt9XG5cdFx0XHRpZihEYXRlLm5vdygpIC0gc3RhcnRlZCA8IDk5OSl7XG5cdFx0XHRcdHNldFRpbWVvdXQob25sb2FkLCA5OTkpO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblxuXHRcdFx0aXNDb21wbGV0ZWQgPSB0cnVlO1xuXG5cdFx0XHRsYXp5U2l6ZXNDZmcubG9hZE1vZGUgPSAzO1xuXG5cdFx0XHR0aHJvdHRsZWRDaGVja0VsZW1lbnRzKCk7XG5cblx0XHRcdGFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIGFsdExvYWRtb2RlU2Nyb2xsTGlzdG5lciwgdHJ1ZSk7XG5cdFx0fTtcblxuXHRcdHJldHVybiB7XG5cdFx0XHRfOiBmdW5jdGlvbigpe1xuXHRcdFx0XHRzdGFydGVkID0gRGF0ZS5ub3coKTtcblxuXHRcdFx0XHRsYXp5c2l6ZXMuZWxlbWVudHMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKGxhenlTaXplc0NmZy5sYXp5Q2xhc3MpO1xuXHRcdFx0XHRwcmVsb2FkRWxlbXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKGxhenlTaXplc0NmZy5sYXp5Q2xhc3MgKyAnICcgKyBsYXp5U2l6ZXNDZmcucHJlbG9hZENsYXNzKTtcblxuXHRcdFx0XHRhZGRFdmVudExpc3RlbmVyKCdzY3JvbGwnLCB0aHJvdHRsZWRDaGVja0VsZW1lbnRzLCB0cnVlKTtcblxuXHRcdFx0XHRhZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCB0aHJvdHRsZWRDaGVja0VsZW1lbnRzLCB0cnVlKTtcblxuXHRcdFx0XHRhZGRFdmVudExpc3RlbmVyKCdwYWdlc2hvdycsIGZ1bmN0aW9uIChlKSB7XG5cdFx0XHRcdFx0aWYgKGUucGVyc2lzdGVkKSB7XG5cdFx0XHRcdFx0XHR2YXIgbG9hZGluZ0VsZW1lbnRzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLicgKyBsYXp5U2l6ZXNDZmcubG9hZGluZ0NsYXNzKTtcblxuXHRcdFx0XHRcdFx0aWYgKGxvYWRpbmdFbGVtZW50cy5sZW5ndGggJiYgbG9hZGluZ0VsZW1lbnRzLmZvckVhY2gpIHtcblx0XHRcdFx0XHRcdFx0cmVxdWVzdEFuaW1hdGlvbkZyYW1lKGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0XHRcdFx0XHRsb2FkaW5nRWxlbWVudHMuZm9yRWFjaCggZnVuY3Rpb24gKGltZykge1xuXHRcdFx0XHRcdFx0XHRcdFx0aWYgKGltZy5jb21wbGV0ZSkge1xuXHRcdFx0XHRcdFx0XHRcdFx0XHR1bnZlaWxFbGVtZW50KGltZyk7XG5cdFx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0fSk7XG5cdFx0XHRcdFx0XHRcdH0pO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSk7XG5cblx0XHRcdFx0aWYod2luZG93Lk11dGF0aW9uT2JzZXJ2ZXIpe1xuXHRcdFx0XHRcdG5ldyBNdXRhdGlvbk9ic2VydmVyKCB0aHJvdHRsZWRDaGVja0VsZW1lbnRzICkub2JzZXJ2ZSggZG9jRWxlbSwge2NoaWxkTGlzdDogdHJ1ZSwgc3VidHJlZTogdHJ1ZSwgYXR0cmlidXRlczogdHJ1ZX0gKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRkb2NFbGVtW19hZGRFdmVudExpc3RlbmVyXSgnRE9NTm9kZUluc2VydGVkJywgdGhyb3R0bGVkQ2hlY2tFbGVtZW50cywgdHJ1ZSk7XG5cdFx0XHRcdFx0ZG9jRWxlbVtfYWRkRXZlbnRMaXN0ZW5lcl0oJ0RPTUF0dHJNb2RpZmllZCcsIHRocm90dGxlZENoZWNrRWxlbWVudHMsIHRydWUpO1xuXHRcdFx0XHRcdHNldEludGVydmFsKHRocm90dGxlZENoZWNrRWxlbWVudHMsIDk5OSk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRhZGRFdmVudExpc3RlbmVyKCdoYXNoY2hhbmdlJywgdGhyb3R0bGVkQ2hlY2tFbGVtZW50cywgdHJ1ZSk7XG5cblx0XHRcdFx0Ly8sICdmdWxsc2NyZWVuY2hhbmdlJ1xuXHRcdFx0XHRbJ2ZvY3VzJywgJ21vdXNlb3ZlcicsICdjbGljaycsICdsb2FkJywgJ3RyYW5zaXRpb25lbmQnLCAnYW5pbWF0aW9uZW5kJ10uZm9yRWFjaChmdW5jdGlvbihuYW1lKXtcblx0XHRcdFx0XHRkb2N1bWVudFtfYWRkRXZlbnRMaXN0ZW5lcl0obmFtZSwgdGhyb3R0bGVkQ2hlY2tFbGVtZW50cywgdHJ1ZSk7XG5cdFx0XHRcdH0pO1xuXG5cdFx0XHRcdGlmKCgvZCR8XmMvLnRlc3QoZG9jdW1lbnQucmVhZHlTdGF0ZSkpKXtcblx0XHRcdFx0XHRvbmxvYWQoKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRhZGRFdmVudExpc3RlbmVyKCdsb2FkJywgb25sb2FkKTtcblx0XHRcdFx0XHRkb2N1bWVudFtfYWRkRXZlbnRMaXN0ZW5lcl0oJ0RPTUNvbnRlbnRMb2FkZWQnLCB0aHJvdHRsZWRDaGVja0VsZW1lbnRzKTtcblx0XHRcdFx0XHRzZXRUaW1lb3V0KG9ubG9hZCwgMjAwMDApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYobGF6eXNpemVzLmVsZW1lbnRzLmxlbmd0aCl7XG5cdFx0XHRcdFx0Y2hlY2tFbGVtZW50cygpO1xuXHRcdFx0XHRcdHJBRi5fbHNGbHVzaCgpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRocm90dGxlZENoZWNrRWxlbWVudHMoKTtcblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdGNoZWNrRWxlbXM6IHRocm90dGxlZENoZWNrRWxlbWVudHMsXG5cdFx0XHR1bnZlaWw6IHVudmVpbEVsZW1lbnQsXG5cdFx0XHRfYUxTTDogYWx0TG9hZG1vZGVTY3JvbGxMaXN0bmVyLFxuXHRcdH07XG5cdH0pKCk7XG5cblxuXHR2YXIgYXV0b1NpemVyID0gKGZ1bmN0aW9uKCl7XG5cdFx0dmFyIGF1dG9zaXplc0VsZW1zO1xuXG5cdFx0dmFyIHNpemVFbGVtZW50ID0gckFGSXQoZnVuY3Rpb24oZWxlbSwgcGFyZW50LCBldmVudCwgd2lkdGgpe1xuXHRcdFx0dmFyIHNvdXJjZXMsIGksIGxlbjtcblx0XHRcdGVsZW0uX2xhenlzaXplc1dpZHRoID0gd2lkdGg7XG5cdFx0XHR3aWR0aCArPSAncHgnO1xuXG5cdFx0XHRlbGVtLnNldEF0dHJpYnV0ZSgnc2l6ZXMnLCB3aWR0aCk7XG5cblx0XHRcdGlmKHJlZ1BpY3R1cmUudGVzdChwYXJlbnQubm9kZU5hbWUgfHwgJycpKXtcblx0XHRcdFx0c291cmNlcyA9IHBhcmVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc291cmNlJyk7XG5cdFx0XHRcdGZvcihpID0gMCwgbGVuID0gc291cmNlcy5sZW5ndGg7IGkgPCBsZW47IGkrKyl7XG5cdFx0XHRcdFx0c291cmNlc1tpXS5zZXRBdHRyaWJ1dGUoJ3NpemVzJywgd2lkdGgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGlmKCFldmVudC5kZXRhaWwuZGF0YUF0dHIpe1xuXHRcdFx0XHR1cGRhdGVQb2x5ZmlsbChlbGVtLCBldmVudC5kZXRhaWwpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdHZhciBnZXRTaXplRWxlbWVudCA9IGZ1bmN0aW9uIChlbGVtLCBkYXRhQXR0ciwgd2lkdGgpe1xuXHRcdFx0dmFyIGV2ZW50O1xuXHRcdFx0dmFyIHBhcmVudCA9IGVsZW0ucGFyZW50Tm9kZTtcblxuXHRcdFx0aWYocGFyZW50KXtcblx0XHRcdFx0d2lkdGggPSBnZXRXaWR0aChlbGVtLCBwYXJlbnQsIHdpZHRoKTtcblx0XHRcdFx0ZXZlbnQgPSB0cmlnZ2VyRXZlbnQoZWxlbSwgJ2xhenliZWZvcmVzaXplcycsIHt3aWR0aDogd2lkdGgsIGRhdGFBdHRyOiAhIWRhdGFBdHRyfSk7XG5cblx0XHRcdFx0aWYoIWV2ZW50LmRlZmF1bHRQcmV2ZW50ZWQpe1xuXHRcdFx0XHRcdHdpZHRoID0gZXZlbnQuZGV0YWlsLndpZHRoO1xuXG5cdFx0XHRcdFx0aWYod2lkdGggJiYgd2lkdGggIT09IGVsZW0uX2xhenlzaXplc1dpZHRoKXtcblx0XHRcdFx0XHRcdHNpemVFbGVtZW50KGVsZW0sIHBhcmVudCwgZXZlbnQsIHdpZHRoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0dmFyIHVwZGF0ZUVsZW1lbnRzU2l6ZXMgPSBmdW5jdGlvbigpe1xuXHRcdFx0dmFyIGk7XG5cdFx0XHR2YXIgbGVuID0gYXV0b3NpemVzRWxlbXMubGVuZ3RoO1xuXHRcdFx0aWYobGVuKXtcblx0XHRcdFx0aSA9IDA7XG5cblx0XHRcdFx0Zm9yKDsgaSA8IGxlbjsgaSsrKXtcblx0XHRcdFx0XHRnZXRTaXplRWxlbWVudChhdXRvc2l6ZXNFbGVtc1tpXSk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9O1xuXG5cdFx0dmFyIGRlYm91bmNlZFVwZGF0ZUVsZW1lbnRzU2l6ZXMgPSBkZWJvdW5jZSh1cGRhdGVFbGVtZW50c1NpemVzKTtcblxuXHRcdHJldHVybiB7XG5cdFx0XHRfOiBmdW5jdGlvbigpe1xuXHRcdFx0XHRhdXRvc2l6ZXNFbGVtcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUobGF6eVNpemVzQ2ZnLmF1dG9zaXplc0NsYXNzKTtcblx0XHRcdFx0YWRkRXZlbnRMaXN0ZW5lcigncmVzaXplJywgZGVib3VuY2VkVXBkYXRlRWxlbWVudHNTaXplcyk7XG5cdFx0XHR9LFxuXHRcdFx0Y2hlY2tFbGVtczogZGVib3VuY2VkVXBkYXRlRWxlbWVudHNTaXplcyxcblx0XHRcdHVwZGF0ZUVsZW06IGdldFNpemVFbGVtZW50XG5cdFx0fTtcblx0fSkoKTtcblxuXHR2YXIgaW5pdCA9IGZ1bmN0aW9uKCl7XG5cdFx0aWYoIWluaXQuaSAmJiBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKXtcblx0XHRcdGluaXQuaSA9IHRydWU7XG5cdFx0XHRhdXRvU2l6ZXIuXygpO1xuXHRcdFx0bG9hZGVyLl8oKTtcblx0XHR9XG5cdH07XG5cblx0c2V0VGltZW91dChmdW5jdGlvbigpe1xuXHRcdGlmKGxhenlTaXplc0NmZy5pbml0KXtcblx0XHRcdGluaXQoKTtcblx0XHR9XG5cdH0pO1xuXG5cdGxhenlzaXplcyA9IHtcblx0XHRjZmc6IGxhenlTaXplc0NmZyxcblx0XHRhdXRvU2l6ZXI6IGF1dG9TaXplcixcblx0XHRsb2FkZXI6IGxvYWRlcixcblx0XHRpbml0OiBpbml0LFxuXHRcdHVQOiB1cGRhdGVQb2x5ZmlsbCxcblx0XHRhQzogYWRkQ2xhc3MsXG5cdFx0ckM6IHJlbW92ZUNsYXNzLFxuXHRcdGhDOiBoYXNDbGFzcyxcblx0XHRmaXJlOiB0cmlnZ2VyRXZlbnQsXG5cdFx0Z1c6IGdldFdpZHRoLFxuXHRcdHJBRjogckFGLFxuXHR9O1xuXG5cdHJldHVybiBsYXp5c2l6ZXM7XG59XG4pKTtcbiIsIlwib2JqZWN0XCI9PXR5cGVvZiBuYXZpZ2F0b3ImJmZ1bmN0aW9uKGUsdCl7XCJvYmplY3RcIj09dHlwZW9mIGV4cG9ydHMmJlwidW5kZWZpbmVkXCIhPXR5cGVvZiBtb2R1bGU/bW9kdWxlLmV4cG9ydHM9dCgpOlwiZnVuY3Rpb25cIj09dHlwZW9mIGRlZmluZSYmZGVmaW5lLmFtZD9kZWZpbmUoXCJQbHlyXCIsdCk6KGU9XCJ1bmRlZmluZWRcIiE9dHlwZW9mIGdsb2JhbFRoaXM/Z2xvYmFsVGhpczplfHxzZWxmKS5QbHlyPXQoKX0odGhpcywoZnVuY3Rpb24oKXtcInVzZSBzdHJpY3RcIjtmdW5jdGlvbiBlKGUsdCl7aWYoIShlIGluc3RhbmNlb2YgdCkpdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvblwiKX1mdW5jdGlvbiB0KGUsdCl7Zm9yKHZhciBpPTA7aTx0Lmxlbmd0aDtpKyspe3ZhciBuPXRbaV07bi5lbnVtZXJhYmxlPW4uZW51bWVyYWJsZXx8ITEsbi5jb25maWd1cmFibGU9ITAsXCJ2YWx1ZVwiaW4gbiYmKG4ud3JpdGFibGU9ITApLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLG4ua2V5LG4pfX1mdW5jdGlvbiBpKGUsaSxuKXtyZXR1cm4gaSYmdChlLnByb3RvdHlwZSxpKSxuJiZ0KGUsbiksZX1mdW5jdGlvbiBuKGUsdCxpKXtyZXR1cm4gdCBpbiBlP09iamVjdC5kZWZpbmVQcm9wZXJ0eShlLHQse3ZhbHVlOmksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTplW3RdPWksZX1mdW5jdGlvbiBhKGUsdCl7dmFyIGk9T2JqZWN0LmtleXMoZSk7aWYoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyl7dmFyIG49T2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhlKTt0JiYobj1uLmZpbHRlcigoZnVuY3Rpb24odCl7cmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoZSx0KS5lbnVtZXJhYmxlfSkpKSxpLnB1c2guYXBwbHkoaSxuKX1yZXR1cm4gaX1mdW5jdGlvbiBzKGUpe2Zvcih2YXIgdD0xO3Q8YXJndW1lbnRzLmxlbmd0aDt0Kyspe3ZhciBpPW51bGwhPWFyZ3VtZW50c1t0XT9hcmd1bWVudHNbdF06e307dCUyP2EoT2JqZWN0KGkpLCEwKS5mb3JFYWNoKChmdW5jdGlvbih0KXtuKGUsdCxpW3RdKX0pKTpPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycz9PYmplY3QuZGVmaW5lUHJvcGVydGllcyhlLE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKGkpKTphKE9iamVjdChpKSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGUsdCxPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGksdCkpfSkpfXJldHVybiBlfWZ1bmN0aW9uIHIoZSx0KXtpZihudWxsPT1lKXJldHVybnt9O3ZhciBpLG4sYT1mdW5jdGlvbihlLHQpe2lmKG51bGw9PWUpcmV0dXJue307dmFyIGksbixhPXt9LHM9T2JqZWN0LmtleXMoZSk7Zm9yKG49MDtuPHMubGVuZ3RoO24rKylpPXNbbl0sdC5pbmRleE9mKGkpPj0wfHwoYVtpXT1lW2ldKTtyZXR1cm4gYX0oZSx0KTtpZihPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKXt2YXIgcz1PYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKGUpO2ZvcihuPTA7bjxzLmxlbmd0aDtuKyspaT1zW25dLHQuaW5kZXhPZihpKT49MHx8T2JqZWN0LnByb3RvdHlwZS5wcm9wZXJ0eUlzRW51bWVyYWJsZS5jYWxsKGUsaSkmJihhW2ldPWVbaV0pfXJldHVybiBhfWZ1bmN0aW9uIG8oZSx0KXtyZXR1cm4gZnVuY3Rpb24oZSl7aWYoQXJyYXkuaXNBcnJheShlKSlyZXR1cm4gZX0oZSl8fGZ1bmN0aW9uKGUsdCl7aWYoXCJ1bmRlZmluZWRcIj09dHlwZW9mIFN5bWJvbHx8IShTeW1ib2wuaXRlcmF0b3IgaW4gT2JqZWN0KGUpKSlyZXR1cm47dmFyIGk9W10sbj0hMCxhPSExLHM9dm9pZCAwO3RyeXtmb3IodmFyIHIsbz1lW1N5bWJvbC5pdGVyYXRvcl0oKTshKG49KHI9by5uZXh0KCkpLmRvbmUpJiYoaS5wdXNoKHIudmFsdWUpLCF0fHxpLmxlbmd0aCE9PXQpO249ITApO31jYXRjaChlKXthPSEwLHM9ZX1maW5hbGx5e3RyeXtufHxudWxsPT1vLnJldHVybnx8by5yZXR1cm4oKX1maW5hbGx5e2lmKGEpdGhyb3cgc319cmV0dXJuIGl9KGUsdCl8fGMoZSx0KXx8ZnVuY3Rpb24oKXt0aHJvdyBuZXcgVHlwZUVycm9yKFwiSW52YWxpZCBhdHRlbXB0IHRvIGRlc3RydWN0dXJlIG5vbi1pdGVyYWJsZSBpbnN0YW5jZS5cXG5JbiBvcmRlciB0byBiZSBpdGVyYWJsZSwgbm9uLWFycmF5IG9iamVjdHMgbXVzdCBoYXZlIGEgW1N5bWJvbC5pdGVyYXRvcl0oKSBtZXRob2QuXCIpfSgpfWZ1bmN0aW9uIGwoZSl7cmV0dXJuIGZ1bmN0aW9uKGUpe2lmKEFycmF5LmlzQXJyYXkoZSkpcmV0dXJuIHUoZSl9KGUpfHxmdW5jdGlvbihlKXtpZihcInVuZGVmaW5lZFwiIT10eXBlb2YgU3ltYm9sJiZTeW1ib2wuaXRlcmF0b3IgaW4gT2JqZWN0KGUpKXJldHVybiBBcnJheS5mcm9tKGUpfShlKXx8YyhlKXx8ZnVuY3Rpb24oKXt0aHJvdyBuZXcgVHlwZUVycm9yKFwiSW52YWxpZCBhdHRlbXB0IHRvIHNwcmVhZCBub24taXRlcmFibGUgaW5zdGFuY2UuXFxuSW4gb3JkZXIgdG8gYmUgaXRlcmFibGUsIG5vbi1hcnJheSBvYmplY3RzIG11c3QgaGF2ZSBhIFtTeW1ib2wuaXRlcmF0b3JdKCkgbWV0aG9kLlwiKX0oKX1mdW5jdGlvbiBjKGUsdCl7aWYoZSl7aWYoXCJzdHJpbmdcIj09dHlwZW9mIGUpcmV0dXJuIHUoZSx0KTt2YXIgaT1PYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoZSkuc2xpY2UoOCwtMSk7cmV0dXJuXCJPYmplY3RcIj09PWkmJmUuY29uc3RydWN0b3ImJihpPWUuY29uc3RydWN0b3IubmFtZSksXCJNYXBcIj09PWl8fFwiU2V0XCI9PT1pP0FycmF5LmZyb20oZSk6XCJBcmd1bWVudHNcIj09PWl8fC9eKD86VWl8SSludCg/Ojh8MTZ8MzIpKD86Q2xhbXBlZCk/QXJyYXkkLy50ZXN0KGkpP3UoZSx0KTp2b2lkIDB9fWZ1bmN0aW9uIHUoZSx0KXsobnVsbD09dHx8dD5lLmxlbmd0aCkmJih0PWUubGVuZ3RoKTtmb3IodmFyIGk9MCxuPW5ldyBBcnJheSh0KTtpPHQ7aSsrKW5baV09ZVtpXTtyZXR1cm4gbn1mdW5jdGlvbiBkKGUsdCl7Zm9yKHZhciBpPTA7aTx0Lmxlbmd0aDtpKyspe3ZhciBuPXRbaV07bi5lbnVtZXJhYmxlPW4uZW51bWVyYWJsZXx8ITEsbi5jb25maWd1cmFibGU9ITAsXCJ2YWx1ZVwiaW4gbiYmKG4ud3JpdGFibGU9ITApLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLG4ua2V5LG4pfX1mdW5jdGlvbiBoKGUsdCxpKXtyZXR1cm4gdCBpbiBlP09iamVjdC5kZWZpbmVQcm9wZXJ0eShlLHQse3ZhbHVlOmksZW51bWVyYWJsZTohMCxjb25maWd1cmFibGU6ITAsd3JpdGFibGU6ITB9KTplW3RdPWksZX1mdW5jdGlvbiBtKGUsdCl7dmFyIGk9T2JqZWN0LmtleXMoZSk7aWYoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyl7dmFyIG49T2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhlKTt0JiYobj1uLmZpbHRlcigoZnVuY3Rpb24odCl7cmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoZSx0KS5lbnVtZXJhYmxlfSkpKSxpLnB1c2guYXBwbHkoaSxuKX1yZXR1cm4gaX1mdW5jdGlvbiBwKGUpe2Zvcih2YXIgdD0xO3Q8YXJndW1lbnRzLmxlbmd0aDt0Kyspe3ZhciBpPW51bGwhPWFyZ3VtZW50c1t0XT9hcmd1bWVudHNbdF06e307dCUyP20oT2JqZWN0KGkpLCEwKS5mb3JFYWNoKChmdW5jdGlvbih0KXtoKGUsdCxpW3RdKX0pKTpPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycz9PYmplY3QuZGVmaW5lUHJvcGVydGllcyhlLE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKGkpKTptKE9iamVjdChpKSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGUsdCxPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGksdCkpfSkpfXJldHVybiBlfXZhciBmPXthZGRDU1M6ITAsdGh1bWJXaWR0aDoxNSx3YXRjaDohMH07ZnVuY3Rpb24gZyhlLHQpe3JldHVybiBmdW5jdGlvbigpe3JldHVybiBBcnJheS5mcm9tKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwodCkpLmluY2x1ZGVzKHRoaXMpfS5jYWxsKGUsdCl9dmFyIHk9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGwhPWU/ZS5jb25zdHJ1Y3RvcjpudWxsfSx2PWZ1bmN0aW9uKGUsdCl7cmV0dXJuISEoZSYmdCYmZSBpbnN0YW5jZW9mIHQpfSxiPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lfSx3PWZ1bmN0aW9uKGUpe3JldHVybiB5KGUpPT09T2JqZWN0fSxrPWZ1bmN0aW9uKGUpe3JldHVybiB5KGUpPT09U3RyaW5nfSxUPWZ1bmN0aW9uKGUpe3JldHVybiBBcnJheS5pc0FycmF5KGUpfSxDPWZ1bmN0aW9uKGUpe3JldHVybiB2KGUsTm9kZUxpc3QpfSxBPWssUz1ULFA9QyxFPWZ1bmN0aW9uKGUpe3JldHVybiB2KGUsRWxlbWVudCl9LE49ZnVuY3Rpb24oZSl7cmV0dXJuIHYoZSxFdmVudCl9LE09ZnVuY3Rpb24oZSl7cmV0dXJuIGIoZSl8fChrKGUpfHxUKGUpfHxDKGUpKSYmIWUubGVuZ3RofHx3KGUpJiYhT2JqZWN0LmtleXMoZSkubGVuZ3RofTtmdW5jdGlvbiB4KGUsdCl7aWYoMT50KXt2YXIgaT1mdW5jdGlvbihlKXt2YXIgdD1cIlwiLmNvbmNhdChlKS5tYXRjaCgvKD86XFwuKFxcZCspKT8oPzpbZUVdKFsrLV0/XFxkKykpPyQvKTtyZXR1cm4gdD9NYXRoLm1heCgwLCh0WzFdP3RbMV0ubGVuZ3RoOjApLSh0WzJdPyt0WzJdOjApKTowfSh0KTtyZXR1cm4gcGFyc2VGbG9hdChlLnRvRml4ZWQoaSkpfXJldHVybiBNYXRoLnJvdW5kKGUvdCkqdH12YXIgSSxMLE8sXz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUodCxpKXsoZnVuY3Rpb24oZSx0KXtpZighKGUgaW5zdGFuY2VvZiB0KSl0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uXCIpfSkodGhpcyxlKSxFKHQpP3RoaXMuZWxlbWVudD10OkEodCkmJih0aGlzLmVsZW1lbnQ9ZG9jdW1lbnQucXVlcnlTZWxlY3Rvcih0KSksRSh0aGlzLmVsZW1lbnQpJiZNKHRoaXMuZWxlbWVudC5yYW5nZVRvdWNoKSYmKHRoaXMuY29uZmlnPXAoe30sZix7fSxpKSx0aGlzLmluaXQoKSl9cmV0dXJuIGZ1bmN0aW9uKGUsdCxpKXt0JiZkKGUucHJvdG90eXBlLHQpLGkmJmQoZSxpKX0oZSxbe2tleTpcImluaXRcIix2YWx1ZTpmdW5jdGlvbigpe2UuZW5hYmxlZCYmKHRoaXMuY29uZmlnLmFkZENTUyYmKHRoaXMuZWxlbWVudC5zdHlsZS51c2VyU2VsZWN0PVwibm9uZVwiLHRoaXMuZWxlbWVudC5zdHlsZS53ZWJLaXRVc2VyU2VsZWN0PVwibm9uZVwiLHRoaXMuZWxlbWVudC5zdHlsZS50b3VjaEFjdGlvbj1cIm1hbmlwdWxhdGlvblwiKSx0aGlzLmxpc3RlbmVycyghMCksdGhpcy5lbGVtZW50LnJhbmdlVG91Y2g9dGhpcyl9fSx7a2V5OlwiZGVzdHJveVwiLHZhbHVlOmZ1bmN0aW9uKCl7ZS5lbmFibGVkJiYodGhpcy5jb25maWcuYWRkQ1NTJiYodGhpcy5lbGVtZW50LnN0eWxlLnVzZXJTZWxlY3Q9XCJcIix0aGlzLmVsZW1lbnQuc3R5bGUud2ViS2l0VXNlclNlbGVjdD1cIlwiLHRoaXMuZWxlbWVudC5zdHlsZS50b3VjaEFjdGlvbj1cIlwiKSx0aGlzLmxpc3RlbmVycyghMSksdGhpcy5lbGVtZW50LnJhbmdlVG91Y2g9bnVsbCl9fSx7a2V5OlwibGlzdGVuZXJzXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPWU/XCJhZGRFdmVudExpc3RlbmVyXCI6XCJyZW1vdmVFdmVudExpc3RlbmVyXCI7W1widG91Y2hzdGFydFwiLFwidG91Y2htb3ZlXCIsXCJ0b3VjaGVuZFwiXS5mb3JFYWNoKChmdW5jdGlvbihlKXt0LmVsZW1lbnRbaV0oZSwoZnVuY3Rpb24oZSl7cmV0dXJuIHQuc2V0KGUpfSksITEpfSkpfX0se2tleTpcImdldFwiLHZhbHVlOmZ1bmN0aW9uKHQpe2lmKCFlLmVuYWJsZWR8fCFOKHQpKXJldHVybiBudWxsO3ZhciBpLG49dC50YXJnZXQsYT10LmNoYW5nZWRUb3VjaGVzWzBdLHM9cGFyc2VGbG9hdChuLmdldEF0dHJpYnV0ZShcIm1pblwiKSl8fDAscj1wYXJzZUZsb2F0KG4uZ2V0QXR0cmlidXRlKFwibWF4XCIpKXx8MTAwLG89cGFyc2VGbG9hdChuLmdldEF0dHJpYnV0ZShcInN0ZXBcIikpfHwxLGw9bi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxjPTEwMC9sLndpZHRoKih0aGlzLmNvbmZpZy50aHVtYldpZHRoLzIpLzEwMDtyZXR1cm4gMD4oaT0xMDAvbC53aWR0aCooYS5jbGllbnRYLWwubGVmdCkpP2k9MDoxMDA8aSYmKGk9MTAwKSw1MD5pP2ktPSgxMDAtMippKSpjOjUwPGkmJihpKz0yKihpLTUwKSpjKSxzK3goaS8xMDAqKHItcyksbyl9fSx7a2V5Olwic2V0XCIsdmFsdWU6ZnVuY3Rpb24odCl7ZS5lbmFibGVkJiZOKHQpJiYhdC50YXJnZXQuZGlzYWJsZWQmJih0LnByZXZlbnREZWZhdWx0KCksdC50YXJnZXQudmFsdWU9dGhpcy5nZXQodCksZnVuY3Rpb24oZSx0KXtpZihlJiZ0KXt2YXIgaT1uZXcgRXZlbnQodCx7YnViYmxlczohMH0pO2UuZGlzcGF0Y2hFdmVudChpKX19KHQudGFyZ2V0LFwidG91Y2hlbmRcIj09PXQudHlwZT9cImNoYW5nZVwiOlwiaW5wdXRcIikpfX1dLFt7a2V5Olwic2V0dXBcIix2YWx1ZTpmdW5jdGlvbih0KXt2YXIgaT0xPGFyZ3VtZW50cy5sZW5ndGgmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXT9hcmd1bWVudHNbMV06e30sbj1udWxsO2lmKE0odCl8fEEodCk/bj1BcnJheS5mcm9tKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoQSh0KT90OidpbnB1dFt0eXBlPVwicmFuZ2VcIl0nKSk6RSh0KT9uPVt0XTpQKHQpP249QXJyYXkuZnJvbSh0KTpTKHQpJiYobj10LmZpbHRlcihFKSksTShuKSlyZXR1cm4gbnVsbDt2YXIgYT1wKHt9LGYse30saSk7aWYoQSh0KSYmYS53YXRjaCl7dmFyIHM9bmV3IE11dGF0aW9uT2JzZXJ2ZXIoKGZ1bmN0aW9uKGkpe0FycmF5LmZyb20oaSkuZm9yRWFjaCgoZnVuY3Rpb24oaSl7QXJyYXkuZnJvbShpLmFkZGVkTm9kZXMpLmZvckVhY2goKGZ1bmN0aW9uKGkpe0UoaSkmJmcoaSx0KSYmbmV3IGUoaSxhKX0pKX0pKX0pKTtzLm9ic2VydmUoZG9jdW1lbnQuYm9keSx7Y2hpbGRMaXN0OiEwLHN1YnRyZWU6ITB9KX1yZXR1cm4gbi5tYXAoKGZ1bmN0aW9uKHQpe3JldHVybiBuZXcgZSh0LGkpfSkpfX0se2tleTpcImVuYWJsZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm5cIm9udG91Y2hzdGFydFwiaW4gZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50fX1dKSxlfSgpLGo9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGwhPWU/ZS5jb25zdHJ1Y3RvcjpudWxsfSxEPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIEJvb2xlYW4oZSYmdCYmZSBpbnN0YW5jZW9mIHQpfSxxPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lfSxIPWZ1bmN0aW9uKGUpe3JldHVybiBqKGUpPT09T2JqZWN0fSxGPWZ1bmN0aW9uKGUpe3JldHVybiBqKGUpPT09U3RyaW5nfSxSPWZ1bmN0aW9uKGUpe3JldHVybiBqKGUpPT09RnVuY3Rpb259LFY9ZnVuY3Rpb24oZSl7cmV0dXJuIEFycmF5LmlzQXJyYXkoZSl9LEI9ZnVuY3Rpb24oZSl7cmV0dXJuIEQoZSxOb2RlTGlzdCl9LFU9ZnVuY3Rpb24oZSl7cmV0dXJuIHEoZSl8fChGKGUpfHxWKGUpfHxCKGUpKSYmIWUubGVuZ3RofHxIKGUpJiYhT2JqZWN0LmtleXMoZSkubGVuZ3RofSxXPXEsej1ILEs9ZnVuY3Rpb24oZSl7cmV0dXJuIGooZSk9PT1OdW1iZXImJiFOdW1iZXIuaXNOYU4oZSl9LFk9RixRPWZ1bmN0aW9uKGUpe3JldHVybiBqKGUpPT09Qm9vbGVhbn0sWD1SLCQ9VixKPUIsRz1mdW5jdGlvbihlKXtyZXR1cm4gRChlLEVsZW1lbnQpfSxaPWZ1bmN0aW9uKGUpe3JldHVybiBEKGUsRXZlbnQpfSxlZT1mdW5jdGlvbihlKXtyZXR1cm4gRChlLEtleWJvYXJkRXZlbnQpfSx0ZT1mdW5jdGlvbihlKXtyZXR1cm4gRChlLFRleHRUcmFjayl8fCFxKGUpJiZGKGUua2luZCl9LGllPWZ1bmN0aW9uKGUpe3JldHVybiBEKGUsUHJvbWlzZSkmJlIoZS50aGVuKX0sbmU9ZnVuY3Rpb24oZSl7aWYoRChlLHdpbmRvdy5VUkwpKXJldHVybiEwO2lmKCFGKGUpKXJldHVybiExO3ZhciB0PWU7ZS5zdGFydHNXaXRoKFwiaHR0cDovL1wiKSYmZS5zdGFydHNXaXRoKFwiaHR0cHM6Ly9cIil8fCh0PVwiaHR0cDovL1wiLmNvbmNhdChlKSk7dHJ5e3JldHVybiFVKG5ldyBVUkwodCkuaG9zdG5hbWUpfWNhdGNoKGUpe3JldHVybiExfX0sYWU9VSxzZT0oST1kb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3BhblwiKSxMPXtXZWJraXRUcmFuc2l0aW9uOlwid2Via2l0VHJhbnNpdGlvbkVuZFwiLE1velRyYW5zaXRpb246XCJ0cmFuc2l0aW9uZW5kXCIsT1RyYW5zaXRpb246XCJvVHJhbnNpdGlvbkVuZCBvdHJhbnNpdGlvbmVuZFwiLHRyYW5zaXRpb246XCJ0cmFuc2l0aW9uZW5kXCJ9LE89T2JqZWN0LmtleXMoTCkuZmluZCgoZnVuY3Rpb24oZSl7cmV0dXJuIHZvaWQgMCE9PUkuc3R5bGVbZV19KSksISFZKE8pJiZMW09dKTtmdW5jdGlvbiByZShlLHQpe3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dHJ5e2UuaGlkZGVuPSEwLGUub2Zmc2V0SGVpZ2h0LGUuaGlkZGVuPSExfWNhdGNoKGUpe319KSx0KX12YXIgb2U9e2lzSUU6XG4vKiBAY2Nfb24hQCAqL1xuISFkb2N1bWVudC5kb2N1bWVudE1vZGUsaXNFZGdlOndpbmRvdy5uYXZpZ2F0b3IudXNlckFnZW50LmluY2x1ZGVzKFwiRWRnZVwiKSxpc1dlYmtpdDpcIldlYmtpdEFwcGVhcmFuY2VcImluIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudC5zdHlsZSYmIS9FZGdlLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpLGlzSVBob25lOi8oaVBob25lfGlQb2QpL2dpLnRlc3QobmF2aWdhdG9yLnBsYXRmb3JtKSxpc0lvczovKGlQYWR8aVBob25lfGlQb2QpL2dpLnRlc3QobmF2aWdhdG9yLnBsYXRmb3JtKX07ZnVuY3Rpb24gbGUoZSx0KXtyZXR1cm4gdC5zcGxpdChcIi5cIikucmVkdWNlKChmdW5jdGlvbihlLHQpe3JldHVybiBlJiZlW3RdfSksZSl9ZnVuY3Rpb24gY2UoKXtmb3IodmFyIGU9YXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0/YXJndW1lbnRzWzBdOnt9LHQ9YXJndW1lbnRzLmxlbmd0aCxpPW5ldyBBcnJheSh0PjE/dC0xOjApLGE9MTthPHQ7YSsrKWlbYS0xXT1hcmd1bWVudHNbYV07aWYoIWkubGVuZ3RoKXJldHVybiBlO3ZhciBzPWkuc2hpZnQoKTtyZXR1cm4geihzKT8oT2JqZWN0LmtleXMocykuZm9yRWFjaCgoZnVuY3Rpb24odCl7eihzW3RdKT8oT2JqZWN0LmtleXMoZSkuaW5jbHVkZXModCl8fE9iamVjdC5hc3NpZ24oZSxuKHt9LHQse30pKSxjZShlW3RdLHNbdF0pKTpPYmplY3QuYXNzaWduKGUsbih7fSx0LHNbdF0pKX0pKSxjZS5hcHBseSh2b2lkIDAsW2VdLmNvbmNhdChpKSkpOmV9ZnVuY3Rpb24gdWUoZSx0KXt2YXIgaT1lLmxlbmd0aD9lOltlXTtBcnJheS5mcm9tKGkpLnJldmVyc2UoKS5mb3JFYWNoKChmdW5jdGlvbihlLGkpe3ZhciBuPWk+MD90LmNsb25lTm9kZSghMCk6dCxhPWUucGFyZW50Tm9kZSxzPWUubmV4dFNpYmxpbmc7bi5hcHBlbmRDaGlsZChlKSxzP2EuaW5zZXJ0QmVmb3JlKG4scyk6YS5hcHBlbmRDaGlsZChuKX0pKX1mdW5jdGlvbiBkZShlLHQpe0coZSkmJiFhZSh0KSYmT2JqZWN0LmVudHJpZXModCkuZmlsdGVyKChmdW5jdGlvbihlKXt2YXIgdD1vKGUsMilbMV07cmV0dXJuIVcodCl9KSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7dmFyIGk9byh0LDIpLG49aVswXSxhPWlbMV07cmV0dXJuIGUuc2V0QXR0cmlidXRlKG4sYSl9KSl9ZnVuY3Rpb24gaGUoZSx0LGkpe3ZhciBuPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoZSk7cmV0dXJuIHoodCkmJmRlKG4sdCksWShpKSYmKG4uaW5uZXJUZXh0PWkpLG59ZnVuY3Rpb24gbWUoZSx0LGksbil7Ryh0KSYmdC5hcHBlbmRDaGlsZChoZShlLGksbikpfWZ1bmN0aW9uIHBlKGUpe0ooZSl8fCQoZSk/QXJyYXkuZnJvbShlKS5mb3JFYWNoKHBlKTpHKGUpJiZHKGUucGFyZW50Tm9kZSkmJmUucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChlKX1mdW5jdGlvbiBmZShlKXtpZihHKGUpKWZvcih2YXIgdD1lLmNoaWxkTm9kZXMubGVuZ3RoO3Q+MDspZS5yZW1vdmVDaGlsZChlLmxhc3RDaGlsZCksdC09MX1mdW5jdGlvbiBnZShlLHQpe3JldHVybiBHKHQpJiZHKHQucGFyZW50Tm9kZSkmJkcoZSk/KHQucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoZSx0KSxlKTpudWxsfWZ1bmN0aW9uIHllKGUsdCl7aWYoIVkoZSl8fGFlKGUpKXJldHVybnt9O3ZhciBpPXt9LG49Y2Uoe30sdCk7cmV0dXJuIGUuc3BsaXQoXCIsXCIpLmZvckVhY2goKGZ1bmN0aW9uKGUpe3ZhciB0PWUudHJpbSgpLGE9dC5yZXBsYWNlKFwiLlwiLFwiXCIpLHM9dC5yZXBsYWNlKC9bW1xcXV0vZyxcIlwiKS5zcGxpdChcIj1cIikscj1vKHMsMSlbMF0sbD1zLmxlbmd0aD4xP3NbMV0ucmVwbGFjZSgvW1wiJ10vZyxcIlwiKTpcIlwiO3N3aXRjaCh0LmNoYXJBdCgwKSl7Y2FzZVwiLlwiOlkobi5jbGFzcyk/aS5jbGFzcz1cIlwiLmNvbmNhdChuLmNsYXNzLFwiIFwiKS5jb25jYXQoYSk6aS5jbGFzcz1hO2JyZWFrO2Nhc2VcIiNcIjppLmlkPXQucmVwbGFjZShcIiNcIixcIlwiKTticmVhaztjYXNlXCJbXCI6aVtyXT1sfX0pKSxjZShuLGkpfWZ1bmN0aW9uIHZlKGUsdCl7aWYoRyhlKSl7dmFyIGk9dDtRKGkpfHwoaT0hZS5oaWRkZW4pLGUuaGlkZGVuPWl9fWZ1bmN0aW9uIGJlKGUsdCxpKXtpZihKKGUpKXJldHVybiBBcnJheS5mcm9tKGUpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIGJlKGUsdCxpKX0pKTtpZihHKGUpKXt2YXIgbj1cInRvZ2dsZVwiO3JldHVybiB2b2lkIDAhPT1pJiYobj1pP1wiYWRkXCI6XCJyZW1vdmVcIiksZS5jbGFzc0xpc3Rbbl0odCksZS5jbGFzc0xpc3QuY29udGFpbnModCl9cmV0dXJuITF9ZnVuY3Rpb24gd2UoZSx0KXtyZXR1cm4gRyhlKSYmZS5jbGFzc0xpc3QuY29udGFpbnModCl9ZnVuY3Rpb24ga2UoZSx0KXt2YXIgaT1FbGVtZW50LnByb3RvdHlwZTtyZXR1cm4oaS5tYXRjaGVzfHxpLndlYmtpdE1hdGNoZXNTZWxlY3Rvcnx8aS5tb3pNYXRjaGVzU2VsZWN0b3J8fGkubXNNYXRjaGVzU2VsZWN0b3J8fGZ1bmN0aW9uKCl7cmV0dXJuIEFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCh0KSkuaW5jbHVkZXModGhpcyl9KS5jYWxsKGUsdCl9ZnVuY3Rpb24gVGUoZSl7cmV0dXJuIHRoaXMuZWxlbWVudHMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoZSl9ZnVuY3Rpb24gQ2UoZSl7cmV0dXJuIHRoaXMuZWxlbWVudHMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoZSl9ZnVuY3Rpb24gQWUoKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXT9hcmd1bWVudHNbMF06bnVsbCx0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdJiZhcmd1bWVudHNbMV07RyhlKSYmKGUuZm9jdXMoe3ByZXZlbnRTY3JvbGw6ITB9KSx0JiZiZShlLHRoaXMuY29uZmlnLmNsYXNzTmFtZXMudGFiRm9jdXMpKX12YXIgU2UsUGU9e1wiYXVkaW8vb2dnXCI6XCJ2b3JiaXNcIixcImF1ZGlvL3dhdlwiOlwiMVwiLFwidmlkZW8vd2VibVwiOlwidnA4LCB2b3JiaXNcIixcInZpZGVvL21wNFwiOlwiYXZjMS40MkUwMUUsIG1wNGEuNDAuMlwiLFwidmlkZW8vb2dnXCI6XCJ0aGVvcmFcIn0sRWU9e2F1ZGlvOlwiY2FuUGxheVR5cGVcImluIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJhdWRpb1wiKSx2aWRlbzpcImNhblBsYXlUeXBlXCJpbiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwidmlkZW9cIiksY2hlY2s6ZnVuY3Rpb24oZSx0LGkpe3ZhciBuPW9lLmlzSVBob25lJiZpJiZFZS5wbGF5c2lubGluZSxhPUVlW2VdfHxcImh0bWw1XCIhPT10O3JldHVybnthcGk6YSx1aTphJiZFZS5yYW5nZUlucHV0JiYoXCJ2aWRlb1wiIT09ZXx8IW9lLmlzSVBob25lfHxuKX19LHBpcDohKG9lLmlzSVBob25lfHwhWChoZShcInZpZGVvXCIpLndlYmtpdFNldFByZXNlbnRhdGlvbk1vZGUpJiYoIWRvY3VtZW50LnBpY3R1cmVJblBpY3R1cmVFbmFibGVkfHxoZShcInZpZGVvXCIpLmRpc2FibGVQaWN0dXJlSW5QaWN0dXJlKSksYWlycGxheTpYKHdpbmRvdy5XZWJLaXRQbGF5YmFja1RhcmdldEF2YWlsYWJpbGl0eUV2ZW50KSxwbGF5c2lubGluZTpcInBsYXlzSW5saW5lXCJpbiBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwidmlkZW9cIiksbWltZTpmdW5jdGlvbihlKXtpZihhZShlKSlyZXR1cm4hMTt2YXIgdD1vKGUuc3BsaXQoXCIvXCIpLDEpWzBdLGk9ZTtpZighdGhpcy5pc0hUTUw1fHx0IT09dGhpcy50eXBlKXJldHVybiExO09iamVjdC5rZXlzKFBlKS5pbmNsdWRlcyhpKSYmKGkrPSc7IGNvZGVjcz1cIicuY29uY2F0KFBlW2VdLCdcIicpKTt0cnl7cmV0dXJuIEJvb2xlYW4oaSYmdGhpcy5tZWRpYS5jYW5QbGF5VHlwZShpKS5yZXBsYWNlKC9uby8sXCJcIikpfWNhdGNoKGUpe3JldHVybiExfX0sdGV4dFRyYWNrczpcInRleHRUcmFja3NcImluIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJ2aWRlb1wiKSxyYW5nZUlucHV0OihTZT1kb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiaW5wdXRcIiksU2UudHlwZT1cInJhbmdlXCIsXCJyYW5nZVwiPT09U2UudHlwZSksdG91Y2g6XCJvbnRvdWNoc3RhcnRcImluIGRvY3VtZW50LmRvY3VtZW50RWxlbWVudCx0cmFuc2l0aW9uczohMSE9PXNlLHJlZHVjZWRNb3Rpb246XCJtYXRjaE1lZGlhXCJpbiB3aW5kb3cmJndpbmRvdy5tYXRjaE1lZGlhKFwiKHByZWZlcnMtcmVkdWNlZC1tb3Rpb24pXCIpLm1hdGNoZXN9LE5lPWZ1bmN0aW9uKCl7dmFyIGU9ITE7dHJ5e3ZhciB0PU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh7fSxcInBhc3NpdmVcIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIGU9ITAsbnVsbH19KTt3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcInRlc3RcIixudWxsLHQpLHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKFwidGVzdFwiLG51bGwsdCl9Y2F0Y2goZSl7fXJldHVybiBlfSgpO2Z1bmN0aW9uIE1lKGUsdCxpKXt2YXIgbj10aGlzLGE9YXJndW1lbnRzLmxlbmd0aD4zJiZ2b2lkIDAhPT1hcmd1bWVudHNbM10mJmFyZ3VtZW50c1szXSxzPSEoYXJndW1lbnRzLmxlbmd0aD40JiZ2b2lkIDAhPT1hcmd1bWVudHNbNF0pfHxhcmd1bWVudHNbNF0scj1hcmd1bWVudHMubGVuZ3RoPjUmJnZvaWQgMCE9PWFyZ3VtZW50c1s1XSYmYXJndW1lbnRzWzVdO2lmKGUmJlwiYWRkRXZlbnRMaXN0ZW5lclwiaW4gZSYmIWFlKHQpJiZYKGkpKXt2YXIgbz10LnNwbGl0KFwiIFwiKSxsPXI7TmUmJihsPXtwYXNzaXZlOnMsY2FwdHVyZTpyfSksby5mb3JFYWNoKChmdW5jdGlvbih0KXtuJiZuLmV2ZW50TGlzdGVuZXJzJiZhJiZuLmV2ZW50TGlzdGVuZXJzLnB1c2goe2VsZW1lbnQ6ZSx0eXBlOnQsY2FsbGJhY2s6aSxvcHRpb25zOmx9KSxlW2E/XCJhZGRFdmVudExpc3RlbmVyXCI6XCJyZW1vdmVFdmVudExpc3RlbmVyXCJdKHQsaSxsKX0pKX19ZnVuY3Rpb24geGUoZSl7dmFyIHQ9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0/YXJndW1lbnRzWzFdOlwiXCIsaT1hcmd1bWVudHMubGVuZ3RoPjI/YXJndW1lbnRzWzJdOnZvaWQgMCxuPSEoYXJndW1lbnRzLmxlbmd0aD4zJiZ2b2lkIDAhPT1hcmd1bWVudHNbM10pfHxhcmd1bWVudHNbM10sYT1hcmd1bWVudHMubGVuZ3RoPjQmJnZvaWQgMCE9PWFyZ3VtZW50c1s0XSYmYXJndW1lbnRzWzRdO01lLmNhbGwodGhpcyxlLHQsaSwhMCxuLGEpfWZ1bmN0aW9uIEllKGUpe3ZhciB0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdP2FyZ3VtZW50c1sxXTpcIlwiLGk9YXJndW1lbnRzLmxlbmd0aD4yP2FyZ3VtZW50c1syXTp2b2lkIDAsbj0hKGFyZ3VtZW50cy5sZW5ndGg+MyYmdm9pZCAwIT09YXJndW1lbnRzWzNdKXx8YXJndW1lbnRzWzNdLGE9YXJndW1lbnRzLmxlbmd0aD40JiZ2b2lkIDAhPT1hcmd1bWVudHNbNF0mJmFyZ3VtZW50c1s0XTtNZS5jYWxsKHRoaXMsZSx0LGksITEsbixhKX1mdW5jdGlvbiBMZShlKXt2YXIgdD10aGlzLGk9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0/YXJndW1lbnRzWzFdOlwiXCIsbj1hcmd1bWVudHMubGVuZ3RoPjI/YXJndW1lbnRzWzJdOnZvaWQgMCxhPSEoYXJndW1lbnRzLmxlbmd0aD4zJiZ2b2lkIDAhPT1hcmd1bWVudHNbM10pfHxhcmd1bWVudHNbM10scz1hcmd1bWVudHMubGVuZ3RoPjQmJnZvaWQgMCE9PWFyZ3VtZW50c1s0XSYmYXJndW1lbnRzWzRdLHI9ZnVuY3Rpb24gcigpe0llKGUsaSxyLGEscyk7Zm9yKHZhciBvPWFyZ3VtZW50cy5sZW5ndGgsbD1uZXcgQXJyYXkobyksYz0wO2M8bztjKyspbFtjXT1hcmd1bWVudHNbY107bi5hcHBseSh0LGwpfTtNZS5jYWxsKHRoaXMsZSxpLHIsITAsYSxzKX1mdW5jdGlvbiBPZShlKXt2YXIgdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXT9hcmd1bWVudHNbMV06XCJcIixpPWFyZ3VtZW50cy5sZW5ndGg+MiYmdm9pZCAwIT09YXJndW1lbnRzWzJdJiZhcmd1bWVudHNbMl0sbj1hcmd1bWVudHMubGVuZ3RoPjMmJnZvaWQgMCE9PWFyZ3VtZW50c1szXT9hcmd1bWVudHNbM106e307aWYoRyhlKSYmIWFlKHQpKXt2YXIgYT1uZXcgQ3VzdG9tRXZlbnQodCx7YnViYmxlczppLGRldGFpbDpzKHMoe30sbikse30se3BseXI6dGhpc30pfSk7ZS5kaXNwYXRjaEV2ZW50KGEpfX1mdW5jdGlvbiBfZSgpe3RoaXMmJnRoaXMuZXZlbnRMaXN0ZW5lcnMmJih0aGlzLmV2ZW50TGlzdGVuZXJzLmZvckVhY2goKGZ1bmN0aW9uKGUpe3ZhciB0PWUuZWxlbWVudCxpPWUudHlwZSxuPWUuY2FsbGJhY2ssYT1lLm9wdGlvbnM7dC5yZW1vdmVFdmVudExpc3RlbmVyKGksbixhKX0pKSx0aGlzLmV2ZW50TGlzdGVuZXJzPVtdKX1mdW5jdGlvbiBqZSgpe3ZhciBlPXRoaXM7cmV0dXJuIG5ldyBQcm9taXNlKChmdW5jdGlvbih0KXtyZXR1cm4gZS5yZWFkeT9zZXRUaW1lb3V0KHQsMCk6eGUuY2FsbChlLGUuZWxlbWVudHMuY29udGFpbmVyLFwicmVhZHlcIix0KX0pKS50aGVuKChmdW5jdGlvbigpe30pKX1mdW5jdGlvbiBEZShlKXtpZShlKSYmZS50aGVuKG51bGwsKGZ1bmN0aW9uKCl7fSkpfWZ1bmN0aW9uIHFlKGUpe3JldHVybiEhKCQoZSl8fFkoZSkmJmUuaW5jbHVkZXMoXCI6XCIpKSYmKCQoZSk/ZTplLnNwbGl0KFwiOlwiKSkubWFwKE51bWJlcikuZXZlcnkoSyl9ZnVuY3Rpb24gSGUoZSl7aWYoISQoZSl8fCFlLmV2ZXJ5KEspKXJldHVybiBudWxsO3ZhciB0PW8oZSwyKSxpPXRbMF0sbj10WzFdLGE9ZnVuY3Rpb24gZSh0LGkpe3JldHVybiAwPT09aT90OmUoaSx0JWkpfShpLG4pO3JldHVybltpL2Esbi9hXX1mdW5jdGlvbiBGZShlKXt2YXIgdD1mdW5jdGlvbihlKXtyZXR1cm4gcWUoZSk/ZS5zcGxpdChcIjpcIikubWFwKE51bWJlcik6bnVsbH0saT10KGUpO2lmKG51bGw9PT1pJiYoaT10KHRoaXMuY29uZmlnLnJhdGlvKSksbnVsbD09PWkmJiFhZSh0aGlzLmVtYmVkKSYmJCh0aGlzLmVtYmVkLnJhdGlvKSYmKGk9dGhpcy5lbWJlZC5yYXRpbyksbnVsbD09PWkmJnRoaXMuaXNIVE1MNSl7dmFyIG49dGhpcy5tZWRpYTtpPUhlKFtuLnZpZGVvV2lkdGgsbi52aWRlb0hlaWdodF0pfXJldHVybiBpfWZ1bmN0aW9uIFJlKGUpe2lmKCF0aGlzLmlzVmlkZW8pcmV0dXJue307dmFyIHQ9dGhpcy5lbGVtZW50cy53cmFwcGVyLGk9RmUuY2FsbCh0aGlzLGUpLG49bygkKGkpP2k6WzAsMF0sMiksYT0xMDAvblswXSpuWzFdO2lmKHQuc3R5bGUucGFkZGluZ0JvdHRvbT1cIlwiLmNvbmNhdChhLFwiJVwiKSx0aGlzLmlzVmltZW8mJiF0aGlzLmNvbmZpZy52aW1lby5wcmVtaXVtJiZ0aGlzLnN1cHBvcnRlZC51aSl7dmFyIHM9MTAwL3RoaXMubWVkaWEub2Zmc2V0V2lkdGgqcGFyc2VJbnQod2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy5tZWRpYSkucGFkZGluZ0JvdHRvbSwxMCkscj0ocy1hKS8ocy81MCk7dGhpcy5mdWxsc2NyZWVuLmFjdGl2ZT90LnN0eWxlLnBhZGRpbmdCb3R0b209bnVsbDp0aGlzLm1lZGlhLnN0eWxlLnRyYW5zZm9ybT1cInRyYW5zbGF0ZVkoLVwiLmNvbmNhdChyLFwiJSlcIil9ZWxzZSB0aGlzLmlzSFRNTDUmJnQuY2xhc3NMaXN0LnRvZ2dsZSh0aGlzLmNvbmZpZy5jbGFzc05hbWVzLnZpZGVvRml4ZWRSYXRpbyxudWxsIT09aSk7cmV0dXJue3BhZGRpbmc6YSxyYXRpbzppfX12YXIgVmU9e2dldFNvdXJjZXM6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3JldHVybiB0aGlzLmlzSFRNTDU/QXJyYXkuZnJvbSh0aGlzLm1lZGlhLnF1ZXJ5U2VsZWN0b3JBbGwoXCJzb3VyY2VcIikpLmZpbHRlcigoZnVuY3Rpb24odCl7dmFyIGk9dC5nZXRBdHRyaWJ1dGUoXCJ0eXBlXCIpO3JldHVybiEhYWUoaSl8fEVlLm1pbWUuY2FsbChlLGkpfSkpOltdfSxnZXRRdWFsaXR5T3B0aW9uczpmdW5jdGlvbigpe3JldHVybiB0aGlzLmNvbmZpZy5xdWFsaXR5LmZvcmNlZD90aGlzLmNvbmZpZy5xdWFsaXR5Lm9wdGlvbnM6VmUuZ2V0U291cmNlcy5jYWxsKHRoaXMpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIE51bWJlcihlLmdldEF0dHJpYnV0ZShcInNpemVcIikpfSkpLmZpbHRlcihCb29sZWFuKX0sc2V0dXA6ZnVuY3Rpb24oKXtpZih0aGlzLmlzSFRNTDUpe3ZhciBlPXRoaXM7ZS5vcHRpb25zLnNwZWVkPWUuY29uZmlnLnNwZWVkLm9wdGlvbnMsYWUodGhpcy5jb25maWcucmF0aW8pfHxSZS5jYWxsKGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLm1lZGlhLFwicXVhbGl0eVwiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgdD1WZS5nZXRTb3VyY2VzLmNhbGwoZSkuZmluZCgoZnVuY3Rpb24odCl7cmV0dXJuIHQuZ2V0QXR0cmlidXRlKFwic3JjXCIpPT09ZS5zb3VyY2V9KSk7cmV0dXJuIHQmJk51bWJlcih0LmdldEF0dHJpYnV0ZShcInNpemVcIikpfSxzZXQ6ZnVuY3Rpb24odCl7aWYoZS5xdWFsaXR5IT09dCl7aWYoZS5jb25maWcucXVhbGl0eS5mb3JjZWQmJlgoZS5jb25maWcucXVhbGl0eS5vbkNoYW5nZSkpZS5jb25maWcucXVhbGl0eS5vbkNoYW5nZSh0KTtlbHNle3ZhciBpPVZlLmdldFNvdXJjZXMuY2FsbChlKS5maW5kKChmdW5jdGlvbihlKXtyZXR1cm4gTnVtYmVyKGUuZ2V0QXR0cmlidXRlKFwic2l6ZVwiKSk9PT10fSkpO2lmKCFpKXJldHVybjt2YXIgbj1lLm1lZGlhLGE9bi5jdXJyZW50VGltZSxzPW4ucGF1c2VkLHI9bi5wcmVsb2FkLG89bi5yZWFkeVN0YXRlLGw9bi5wbGF5YmFja1JhdGU7ZS5tZWRpYS5zcmM9aS5nZXRBdHRyaWJ1dGUoXCJzcmNcIiksKFwibm9uZVwiIT09cnx8bykmJihlLm9uY2UoXCJsb2FkZWRtZXRhZGF0YVwiLChmdW5jdGlvbigpe2Uuc3BlZWQ9bCxlLmN1cnJlbnRUaW1lPWEsc3x8RGUoZS5wbGF5KCkpfSkpLGUubWVkaWEubG9hZCgpKX1PZS5jYWxsKGUsZS5tZWRpYSxcInF1YWxpdHljaGFuZ2VcIiwhMSx7cXVhbGl0eTp0fSl9fX0pfX0sY2FuY2VsUmVxdWVzdHM6ZnVuY3Rpb24oKXt0aGlzLmlzSFRNTDUmJihwZShWZS5nZXRTb3VyY2VzLmNhbGwodGhpcykpLHRoaXMubWVkaWEuc2V0QXR0cmlidXRlKFwic3JjXCIsdGhpcy5jb25maWcuYmxhbmtWaWRlbyksdGhpcy5tZWRpYS5sb2FkKCksdGhpcy5kZWJ1Zy5sb2coXCJDYW5jZWxsZWQgbmV0d29yayByZXF1ZXN0c1wiKSl9fTtmdW5jdGlvbiBCZShlKXtyZXR1cm4gJChlKT9lLmZpbHRlcigoZnVuY3Rpb24odCxpKXtyZXR1cm4gZS5pbmRleE9mKHQpPT09aX0pKTplfWZ1bmN0aW9uIFVlKGUpe2Zvcih2YXIgdD1hcmd1bWVudHMubGVuZ3RoLGk9bmV3IEFycmF5KHQ+MT90LTE6MCksbj0xO248dDtuKyspaVtuLTFdPWFyZ3VtZW50c1tuXTtyZXR1cm4gYWUoZSk/ZTplLnRvU3RyaW5nKCkucmVwbGFjZSgveyhcXGQrKX0vZywoZnVuY3Rpb24oZSx0KXtyZXR1cm4gaVt0XS50b1N0cmluZygpfSkpfXZhciBXZT1mdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTpcIlwiLHQ9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0/YXJndW1lbnRzWzFdOlwiXCIsaT1hcmd1bWVudHMubGVuZ3RoPjImJnZvaWQgMCE9PWFyZ3VtZW50c1syXT9hcmd1bWVudHNbMl06XCJcIjtyZXR1cm4gZS5yZXBsYWNlKG5ldyBSZWdFeHAodC50b1N0cmluZygpLnJlcGxhY2UoLyhbLiorP149IToke30oKXxbXFxdL1xcXFxdKS9nLFwiXFxcXCQxXCIpLFwiZ1wiKSxpLnRvU3RyaW5nKCkpfSx6ZT1mdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTpcIlwiO3JldHVybiBlLnRvU3RyaW5nKCkucmVwbGFjZSgvXFx3XFxTKi9nLChmdW5jdGlvbihlKXtyZXR1cm4gZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKStlLnN1YnN0cigxKS50b0xvd2VyQ2FzZSgpfSkpfTtmdW5jdGlvbiBLZSgpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTpcIlwiLHQ9ZS50b1N0cmluZygpO3JldHVybiB0PVdlKHQsXCItXCIsXCIgXCIpLHQ9V2UodCxcIl9cIixcIiBcIiksdD16ZSh0KSxXZSh0LFwiIFwiLFwiXCIpfWZ1bmN0aW9uIFllKGUpe3ZhciB0PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7cmV0dXJuIHQuYXBwZW5kQ2hpbGQoZSksdC5pbm5lckhUTUx9dmFyIFFlPXtwaXA6XCJQSVBcIixhaXJwbGF5OlwiQWlyUGxheVwiLGh0bWw1OlwiSFRNTDVcIix2aW1lbzpcIlZpbWVvXCIseW91dHViZTpcIllvdVR1YmVcIn0sWGU9ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXT9hcmd1bWVudHNbMF06XCJcIix0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdP2FyZ3VtZW50c1sxXTp7fTtpZihhZShlKXx8YWUodCkpcmV0dXJuXCJcIjt2YXIgaT1sZSh0LmkxOG4sZSk7aWYoYWUoaSkpcmV0dXJuIE9iamVjdC5rZXlzKFFlKS5pbmNsdWRlcyhlKT9RZVtlXTpcIlwiO3ZhciBuPXtcIntzZWVrdGltZX1cIjp0LnNlZWtUaW1lLFwie3RpdGxlfVwiOnQudGl0bGV9O3JldHVybiBPYmplY3QuZW50cmllcyhuKS5mb3JFYWNoKChmdW5jdGlvbihlKXt2YXIgdD1vKGUsMiksbj10WzBdLGE9dFsxXTtpPVdlKGksbixhKX0pKSxpfSwkZT1mdW5jdGlvbigpe2Z1bmN0aW9uIHQoaSl7ZSh0aGlzLHQpLHRoaXMuZW5hYmxlZD1pLmNvbmZpZy5zdG9yYWdlLmVuYWJsZWQsdGhpcy5rZXk9aS5jb25maWcuc3RvcmFnZS5rZXl9cmV0dXJuIGkodCxbe2tleTpcImdldFwiLHZhbHVlOmZ1bmN0aW9uKGUpe2lmKCF0LnN1cHBvcnRlZHx8IXRoaXMuZW5hYmxlZClyZXR1cm4gbnVsbDt2YXIgaT13aW5kb3cubG9jYWxTdG9yYWdlLmdldEl0ZW0odGhpcy5rZXkpO2lmKGFlKGkpKXJldHVybiBudWxsO3ZhciBuPUpTT04ucGFyc2UoaSk7cmV0dXJuIFkoZSkmJmUubGVuZ3RoP25bZV06bn19LHtrZXk6XCJzZXRcIix2YWx1ZTpmdW5jdGlvbihlKXtpZih0LnN1cHBvcnRlZCYmdGhpcy5lbmFibGVkJiZ6KGUpKXt2YXIgaT10aGlzLmdldCgpO2FlKGkpJiYoaT17fSksY2UoaSxlKSx3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0odGhpcy5rZXksSlNPTi5zdHJpbmdpZnkoaSkpfX19XSxbe2tleTpcInN1cHBvcnRlZFwiLGdldDpmdW5jdGlvbigpe3RyeXtpZighKFwibG9jYWxTdG9yYWdlXCJpbiB3aW5kb3cpKXJldHVybiExO3ZhciBlPVwiX19fdGVzdFwiO3JldHVybiB3aW5kb3cubG9jYWxTdG9yYWdlLnNldEl0ZW0oZSxlKSx3aW5kb3cubG9jYWxTdG9yYWdlLnJlbW92ZUl0ZW0oZSksITB9Y2F0Y2goZSl7cmV0dXJuITF9fX1dKSx0fSgpO2Z1bmN0aW9uIEplKGUpe3ZhciB0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdP2FyZ3VtZW50c1sxXTpcInRleHRcIjtyZXR1cm4gbmV3IFByb21pc2UoKGZ1bmN0aW9uKGksbil7dHJ5e3ZhciBhPW5ldyBYTUxIdHRwUmVxdWVzdDtpZighKFwid2l0aENyZWRlbnRpYWxzXCJpbiBhKSlyZXR1cm47YS5hZGRFdmVudExpc3RlbmVyKFwibG9hZFwiLChmdW5jdGlvbigpe2lmKFwidGV4dFwiPT09dCl0cnl7aShKU09OLnBhcnNlKGEucmVzcG9uc2VUZXh0KSl9Y2F0Y2goZSl7aShhLnJlc3BvbnNlVGV4dCl9ZWxzZSBpKGEucmVzcG9uc2UpfSkpLGEuYWRkRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsKGZ1bmN0aW9uKCl7dGhyb3cgbmV3IEVycm9yKGEuc3RhdHVzKX0pKSxhLm9wZW4oXCJHRVRcIixlLCEwKSxhLnJlc3BvbnNlVHlwZT10LGEuc2VuZCgpfWNhdGNoKGUpe24oZSl9fSkpfWZ1bmN0aW9uIEdlKGUsdCl7aWYoWShlKSl7dmFyIGk9XCJjYWNoZVwiLG49WSh0KSxhPWZ1bmN0aW9uKCl7cmV0dXJuIG51bGwhPT1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0KX0scz1mdW5jdGlvbihlLHQpe2UuaW5uZXJIVE1MPXQsbiYmYSgpfHxkb2N1bWVudC5ib2R5Lmluc2VydEFkamFjZW50RWxlbWVudChcImFmdGVyYmVnaW5cIixlKX07aWYoIW58fCFhKCkpe3ZhciByPSRlLnN1cHBvcnRlZCxvPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7aWYoby5zZXRBdHRyaWJ1dGUoXCJoaWRkZW5cIixcIlwiKSxuJiZvLnNldEF0dHJpYnV0ZShcImlkXCIsdCkscil7dmFyIGw9d2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKFwiXCIuY29uY2F0KGksXCItXCIpLmNvbmNhdCh0KSk7aWYobnVsbCE9PWwpe3ZhciBjPUpTT04ucGFyc2UobCk7cyhvLGMuY29udGVudCl9fUplKGUpLnRoZW4oKGZ1bmN0aW9uKGUpe2FlKGUpfHwociYmd2luZG93LmxvY2FsU3RvcmFnZS5zZXRJdGVtKFwiXCIuY29uY2F0KGksXCItXCIpLmNvbmNhdCh0KSxKU09OLnN0cmluZ2lmeSh7Y29udGVudDplfSkpLHMobyxlKSl9KSkuY2F0Y2goKGZ1bmN0aW9uKCl7fSkpfX19dmFyIFplPWZ1bmN0aW9uKGUpe3JldHVybiBNYXRoLnRydW5jKGUvNjAvNjAlNjAsMTApfSxldD1mdW5jdGlvbihlKXtyZXR1cm4gTWF0aC50cnVuYyhlLzYwJTYwLDEwKX0sdHQ9ZnVuY3Rpb24oZSl7cmV0dXJuIE1hdGgudHJ1bmMoZSU2MCwxMCl9O2Z1bmN0aW9uIGl0KCl7dmFyIGU9YXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0/YXJndW1lbnRzWzBdOjAsdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSYmYXJndW1lbnRzWzFdLGk9YXJndW1lbnRzLmxlbmd0aD4yJiZ2b2lkIDAhPT1hcmd1bWVudHNbMl0mJmFyZ3VtZW50c1syXTtpZighSyhlKSlyZXR1cm4gaXQodm9pZCAwLHQsaSk7dmFyIG49ZnVuY3Rpb24oZSl7cmV0dXJuXCIwXCIuY29uY2F0KGUpLnNsaWNlKC0yKX0sYT1aZShlKSxzPWV0KGUpLHI9dHQoZSk7cmV0dXJuIGE9dHx8YT4wP1wiXCIuY29uY2F0KGEsXCI6XCIpOlwiXCIsXCJcIi5jb25jYXQoaSYmZT4wP1wiLVwiOlwiXCIpLmNvbmNhdChhKS5jb25jYXQobihzKSxcIjpcIikuY29uY2F0KG4ocikpfXZhciBudD17Z2V0SWNvblVybDpmdW5jdGlvbigpe3ZhciBlPW5ldyBVUkwodGhpcy5jb25maWcuaWNvblVybCx3aW5kb3cubG9jYXRpb24pLmhvc3QhPT13aW5kb3cubG9jYXRpb24uaG9zdHx8b2UuaXNJRSYmIXdpbmRvdy5zdmc0ZXZlcnlib2R5O3JldHVybnt1cmw6dGhpcy5jb25maWcuaWNvblVybCxjb3JzOmV9fSxmaW5kRWxlbWVudHM6ZnVuY3Rpb24oKXt0cnl7cmV0dXJuIHRoaXMuZWxlbWVudHMuY29udHJvbHM9Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5jb250cm9scy53cmFwcGVyKSx0aGlzLmVsZW1lbnRzLmJ1dHRvbnM9e3BsYXk6VGUuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5idXR0b25zLnBsYXkpLHBhdXNlOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5wYXVzZSkscmVzdGFydDpDZS5jYWxsKHRoaXMsdGhpcy5jb25maWcuc2VsZWN0b3JzLmJ1dHRvbnMucmVzdGFydCkscmV3aW5kOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5yZXdpbmQpLGZhc3RGb3J3YXJkOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5mYXN0Rm9yd2FyZCksbXV0ZTpDZS5jYWxsKHRoaXMsdGhpcy5jb25maWcuc2VsZWN0b3JzLmJ1dHRvbnMubXV0ZSkscGlwOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5waXApLGFpcnBsYXk6Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5idXR0b25zLmFpcnBsYXkpLHNldHRpbmdzOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5zZXR0aW5ncyksY2FwdGlvbnM6Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5idXR0b25zLmNhcHRpb25zKSxmdWxsc2NyZWVuOkNlLmNhbGwodGhpcyx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuYnV0dG9ucy5mdWxsc2NyZWVuKX0sdGhpcy5lbGVtZW50cy5wcm9ncmVzcz1DZS5jYWxsKHRoaXMsdGhpcy5jb25maWcuc2VsZWN0b3JzLnByb2dyZXNzKSx0aGlzLmVsZW1lbnRzLmlucHV0cz17c2VlazpDZS5jYWxsKHRoaXMsdGhpcy5jb25maWcuc2VsZWN0b3JzLmlucHV0cy5zZWVrKSx2b2x1bWU6Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5pbnB1dHMudm9sdW1lKX0sdGhpcy5lbGVtZW50cy5kaXNwbGF5PXtidWZmZXI6Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5kaXNwbGF5LmJ1ZmZlciksY3VycmVudFRpbWU6Q2UuY2FsbCh0aGlzLHRoaXMuY29uZmlnLnNlbGVjdG9ycy5kaXNwbGF5LmN1cnJlbnRUaW1lKSxkdXJhdGlvbjpDZS5jYWxsKHRoaXMsdGhpcy5jb25maWcuc2VsZWN0b3JzLmRpc3BsYXkuZHVyYXRpb24pfSxHKHRoaXMuZWxlbWVudHMucHJvZ3Jlc3MpJiYodGhpcy5lbGVtZW50cy5kaXNwbGF5LnNlZWtUb29sdGlwPXRoaXMuZWxlbWVudHMucHJvZ3Jlc3MucXVlcnlTZWxlY3RvcihcIi5cIi5jb25jYXQodGhpcy5jb25maWcuY2xhc3NOYW1lcy50b29sdGlwKSkpLCEwfWNhdGNoKGUpe3JldHVybiB0aGlzLmRlYnVnLndhcm4oXCJJdCBsb29rcyBsaWtlIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoIHlvdXIgY3VzdG9tIGNvbnRyb2xzIEhUTUxcIixlKSx0aGlzLnRvZ2dsZU5hdGl2ZUNvbnRyb2xzKCEwKSwhMX19LGNyZWF0ZUljb246ZnVuY3Rpb24oZSx0KXt2YXIgaT1cImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnXCIsbj1udC5nZXRJY29uVXJsLmNhbGwodGhpcyksYT1cIlwiLmNvbmNhdChuLmNvcnM/XCJcIjpuLnVybCxcIiNcIikuY29uY2F0KHRoaXMuY29uZmlnLmljb25QcmVmaXgpLHM9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudE5TKGksXCJzdmdcIik7ZGUocyxjZSh0LHtcImFyaWEtaGlkZGVuXCI6XCJ0cnVlXCIsZm9jdXNhYmxlOlwiZmFsc2VcIn0pKTt2YXIgcj1kb2N1bWVudC5jcmVhdGVFbGVtZW50TlMoaSxcInVzZVwiKSxvPVwiXCIuY29uY2F0KGEsXCItXCIpLmNvbmNhdChlKTtyZXR1cm5cImhyZWZcImluIHImJnIuc2V0QXR0cmlidXRlTlMoXCJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rXCIsXCJocmVmXCIsbyksci5zZXRBdHRyaWJ1dGVOUyhcImh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmtcIixcInhsaW5rOmhyZWZcIixvKSxzLmFwcGVuZENoaWxkKHIpLHN9LGNyZWF0ZUxhYmVsOmZ1bmN0aW9uKGUpe3ZhciB0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdP2FyZ3VtZW50c1sxXTp7fSxpPVhlKGUsdGhpcy5jb25maWcpLG49cyhzKHt9LHQpLHt9LHtjbGFzczpbdC5jbGFzcyx0aGlzLmNvbmZpZy5jbGFzc05hbWVzLmhpZGRlbl0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oXCIgXCIpfSk7cmV0dXJuIGhlKFwic3BhblwiLG4saSl9LGNyZWF0ZUJhZGdlOmZ1bmN0aW9uKGUpe2lmKGFlKGUpKXJldHVybiBudWxsO3ZhciB0PWhlKFwic3BhblwiLHtjbGFzczp0aGlzLmNvbmZpZy5jbGFzc05hbWVzLm1lbnUudmFsdWV9KTtyZXR1cm4gdC5hcHBlbmRDaGlsZChoZShcInNwYW5cIix7Y2xhc3M6dGhpcy5jb25maWcuY2xhc3NOYW1lcy5tZW51LmJhZGdlfSxlKSksdH0sY3JlYXRlQnV0dG9uOmZ1bmN0aW9uKGUsdCl7dmFyIGk9dGhpcyxuPWNlKHt9LHQpLGE9ZnVuY3Rpb24oKXt2YXIgZT0oYXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0/YXJndW1lbnRzWzBdOlwiXCIpLnRvU3RyaW5nKCk7cmV0dXJuKGU9S2UoZSkpLmNoYXJBdCgwKS50b0xvd2VyQ2FzZSgpK2Uuc2xpY2UoMSl9KGUpLHM9e2VsZW1lbnQ6XCJidXR0b25cIix0b2dnbGU6ITEsbGFiZWw6bnVsbCxpY29uOm51bGwsbGFiZWxQcmVzc2VkOm51bGwsaWNvblByZXNzZWQ6bnVsbH07c3dpdGNoKFtcImVsZW1lbnRcIixcImljb25cIixcImxhYmVsXCJdLmZvckVhY2goKGZ1bmN0aW9uKGUpe09iamVjdC5rZXlzKG4pLmluY2x1ZGVzKGUpJiYoc1tlXT1uW2VdLGRlbGV0ZSBuW2VdKX0pKSxcImJ1dHRvblwiIT09cy5lbGVtZW50fHxPYmplY3Qua2V5cyhuKS5pbmNsdWRlcyhcInR5cGVcIil8fChuLnR5cGU9XCJidXR0b25cIiksT2JqZWN0LmtleXMobikuaW5jbHVkZXMoXCJjbGFzc1wiKT9uLmNsYXNzLnNwbGl0KFwiIFwiKS5zb21lKChmdW5jdGlvbihlKXtyZXR1cm4gZT09PWkuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbH0pKXx8Y2Uobix7Y2xhc3M6XCJcIi5jb25jYXQobi5jbGFzcyxcIiBcIikuY29uY2F0KHRoaXMuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbCl9KTpuLmNsYXNzPXRoaXMuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbCxlKXtjYXNlXCJwbGF5XCI6cy50b2dnbGU9ITAscy5sYWJlbD1cInBsYXlcIixzLmxhYmVsUHJlc3NlZD1cInBhdXNlXCIscy5pY29uPVwicGxheVwiLHMuaWNvblByZXNzZWQ9XCJwYXVzZVwiO2JyZWFrO2Nhc2VcIm11dGVcIjpzLnRvZ2dsZT0hMCxzLmxhYmVsPVwibXV0ZVwiLHMubGFiZWxQcmVzc2VkPVwidW5tdXRlXCIscy5pY29uPVwidm9sdW1lXCIscy5pY29uUHJlc3NlZD1cIm11dGVkXCI7YnJlYWs7Y2FzZVwiY2FwdGlvbnNcIjpzLnRvZ2dsZT0hMCxzLmxhYmVsPVwiZW5hYmxlQ2FwdGlvbnNcIixzLmxhYmVsUHJlc3NlZD1cImRpc2FibGVDYXB0aW9uc1wiLHMuaWNvbj1cImNhcHRpb25zLW9mZlwiLHMuaWNvblByZXNzZWQ9XCJjYXB0aW9ucy1vblwiO2JyZWFrO2Nhc2VcImZ1bGxzY3JlZW5cIjpzLnRvZ2dsZT0hMCxzLmxhYmVsPVwiZW50ZXJGdWxsc2NyZWVuXCIscy5sYWJlbFByZXNzZWQ9XCJleGl0RnVsbHNjcmVlblwiLHMuaWNvbj1cImVudGVyLWZ1bGxzY3JlZW5cIixzLmljb25QcmVzc2VkPVwiZXhpdC1mdWxsc2NyZWVuXCI7YnJlYWs7Y2FzZVwicGxheS1sYXJnZVwiOm4uY2xhc3MrPVwiIFwiLmNvbmNhdCh0aGlzLmNvbmZpZy5jbGFzc05hbWVzLmNvbnRyb2wsXCItLW92ZXJsYWlkXCIpLGE9XCJwbGF5XCIscy5sYWJlbD1cInBsYXlcIixzLmljb249XCJwbGF5XCI7YnJlYWs7ZGVmYXVsdDphZShzLmxhYmVsKSYmKHMubGFiZWw9YSksYWUocy5pY29uKSYmKHMuaWNvbj1lKX12YXIgcj1oZShzLmVsZW1lbnQpO3JldHVybiBzLnRvZ2dsZT8oci5hcHBlbmRDaGlsZChudC5jcmVhdGVJY29uLmNhbGwodGhpcyxzLmljb25QcmVzc2VkLHtjbGFzczpcImljb24tLXByZXNzZWRcIn0pKSxyLmFwcGVuZENoaWxkKG50LmNyZWF0ZUljb24uY2FsbCh0aGlzLHMuaWNvbix7Y2xhc3M6XCJpY29uLS1ub3QtcHJlc3NlZFwifSkpLHIuYXBwZW5kQ2hpbGQobnQuY3JlYXRlTGFiZWwuY2FsbCh0aGlzLHMubGFiZWxQcmVzc2VkLHtjbGFzczpcImxhYmVsLS1wcmVzc2VkXCJ9KSksci5hcHBlbmRDaGlsZChudC5jcmVhdGVMYWJlbC5jYWxsKHRoaXMscy5sYWJlbCx7Y2xhc3M6XCJsYWJlbC0tbm90LXByZXNzZWRcIn0pKSk6KHIuYXBwZW5kQ2hpbGQobnQuY3JlYXRlSWNvbi5jYWxsKHRoaXMscy5pY29uKSksci5hcHBlbmRDaGlsZChudC5jcmVhdGVMYWJlbC5jYWxsKHRoaXMscy5sYWJlbCkpKSxjZShuLHllKHRoaXMuY29uZmlnLnNlbGVjdG9ycy5idXR0b25zW2FdLG4pKSxkZShyLG4pLFwicGxheVwiPT09YT8oJCh0aGlzLmVsZW1lbnRzLmJ1dHRvbnNbYV0pfHwodGhpcy5lbGVtZW50cy5idXR0b25zW2FdPVtdKSx0aGlzLmVsZW1lbnRzLmJ1dHRvbnNbYV0ucHVzaChyKSk6dGhpcy5lbGVtZW50cy5idXR0b25zW2FdPXIscn0sY3JlYXRlUmFuZ2U6ZnVuY3Rpb24oZSx0KXt2YXIgaT1oZShcImlucHV0XCIsY2UoeWUodGhpcy5jb25maWcuc2VsZWN0b3JzLmlucHV0c1tlXSkse3R5cGU6XCJyYW5nZVwiLG1pbjowLG1heDoxMDAsc3RlcDouMDEsdmFsdWU6MCxhdXRvY29tcGxldGU6XCJvZmZcIixyb2xlOlwic2xpZGVyXCIsXCJhcmlhLWxhYmVsXCI6WGUoZSx0aGlzLmNvbmZpZyksXCJhcmlhLXZhbHVlbWluXCI6MCxcImFyaWEtdmFsdWVtYXhcIjoxMDAsXCJhcmlhLXZhbHVlbm93XCI6MH0sdCkpO3JldHVybiB0aGlzLmVsZW1lbnRzLmlucHV0c1tlXT1pLG50LnVwZGF0ZVJhbmdlRmlsbC5jYWxsKHRoaXMsaSksXy5zZXR1cChpKSxpfSxjcmVhdGVQcm9ncmVzczpmdW5jdGlvbihlLHQpe3ZhciBpPWhlKFwicHJvZ3Jlc3NcIixjZSh5ZSh0aGlzLmNvbmZpZy5zZWxlY3RvcnMuZGlzcGxheVtlXSkse21pbjowLG1heDoxMDAsdmFsdWU6MCxyb2xlOlwicHJvZ3Jlc3NiYXJcIixcImFyaWEtaGlkZGVuXCI6ITB9LHQpKTtpZihcInZvbHVtZVwiIT09ZSl7aS5hcHBlbmRDaGlsZChoZShcInNwYW5cIixudWxsLFwiMFwiKSk7dmFyIG49e3BsYXllZDpcInBsYXllZFwiLGJ1ZmZlcjpcImJ1ZmZlcmVkXCJ9W2VdLGE9bj9YZShuLHRoaXMuY29uZmlnKTpcIlwiO2kuaW5uZXJUZXh0PVwiJSBcIi5jb25jYXQoYS50b0xvd2VyQ2FzZSgpKX1yZXR1cm4gdGhpcy5lbGVtZW50cy5kaXNwbGF5W2VdPWksaX0sY3JlYXRlVGltZTpmdW5jdGlvbihlLHQpe3ZhciBpPXllKHRoaXMuY29uZmlnLnNlbGVjdG9ycy5kaXNwbGF5W2VdLHQpLG49aGUoXCJkaXZcIixjZShpLHtjbGFzczpcIlwiLmNvbmNhdChpLmNsYXNzP2kuY2xhc3M6XCJcIixcIiBcIikuY29uY2F0KHRoaXMuY29uZmlnLmNsYXNzTmFtZXMuZGlzcGxheS50aW1lLFwiIFwiKS50cmltKCksXCJhcmlhLWxhYmVsXCI6WGUoZSx0aGlzLmNvbmZpZyl9KSxcIjAwOjAwXCIpO3JldHVybiB0aGlzLmVsZW1lbnRzLmRpc3BsYXlbZV09bixufSxiaW5kTWVudUl0ZW1TaG9ydGN1dHM6ZnVuY3Rpb24oZSx0KXt2YXIgaT10aGlzO3hlLmNhbGwodGhpcyxlLFwia2V5ZG93biBrZXl1cFwiLChmdW5jdGlvbihuKXtpZihbMzIsMzgsMzksNDBdLmluY2x1ZGVzKG4ud2hpY2gpJiYobi5wcmV2ZW50RGVmYXVsdCgpLG4uc3RvcFByb3BhZ2F0aW9uKCksXCJrZXlkb3duXCIhPT1uLnR5cGUpKXt2YXIgYSxzPWtlKGUsJ1tyb2xlPVwibWVudWl0ZW1yYWRpb1wiXScpO2lmKCFzJiZbMzIsMzldLmluY2x1ZGVzKG4ud2hpY2gpKW50LnNob3dNZW51UGFuZWwuY2FsbChpLHQsITApO2Vsc2UgMzIhPT1uLndoaWNoJiYoNDA9PT1uLndoaWNofHxzJiYzOT09PW4ud2hpY2g/KGE9ZS5uZXh0RWxlbWVudFNpYmxpbmcsRyhhKXx8KGE9ZS5wYXJlbnROb2RlLmZpcnN0RWxlbWVudENoaWxkKSk6KGE9ZS5wcmV2aW91c0VsZW1lbnRTaWJsaW5nLEcoYSl8fChhPWUucGFyZW50Tm9kZS5sYXN0RWxlbWVudENoaWxkKSksQWUuY2FsbChpLGEsITApKX19KSwhMSkseGUuY2FsbCh0aGlzLGUsXCJrZXl1cFwiLChmdW5jdGlvbihlKXsxMz09PWUud2hpY2gmJm50LmZvY3VzRmlyc3RNZW51SXRlbS5jYWxsKGksbnVsbCwhMCl9KSl9LGNyZWF0ZU1lbnVJdGVtOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMsaT1lLnZhbHVlLG49ZS5saXN0LGE9ZS50eXBlLHM9ZS50aXRsZSxyPWUuYmFkZ2Usbz12b2lkIDA9PT1yP251bGw6cixsPWUuY2hlY2tlZCxjPXZvaWQgMCE9PWwmJmwsdT15ZSh0aGlzLmNvbmZpZy5zZWxlY3RvcnMuaW5wdXRzW2FdKSxkPWhlKFwiYnV0dG9uXCIsY2UodSx7dHlwZTpcImJ1dHRvblwiLHJvbGU6XCJtZW51aXRlbXJhZGlvXCIsY2xhc3M6XCJcIi5jb25jYXQodGhpcy5jb25maWcuY2xhc3NOYW1lcy5jb250cm9sLFwiIFwiKS5jb25jYXQodS5jbGFzcz91LmNsYXNzOlwiXCIpLnRyaW0oKSxcImFyaWEtY2hlY2tlZFwiOmMsdmFsdWU6aX0pKSxoPWhlKFwic3BhblwiKTtoLmlubmVySFRNTD1zLEcobykmJmguYXBwZW5kQ2hpbGQobyksZC5hcHBlbmRDaGlsZChoKSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZCxcImNoZWNrZWRcIix7ZW51bWVyYWJsZTohMCxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm5cInRydWVcIj09PWQuZ2V0QXR0cmlidXRlKFwiYXJpYS1jaGVja2VkXCIpfSxzZXQ6ZnVuY3Rpb24oZSl7ZSYmQXJyYXkuZnJvbShkLnBhcmVudE5vZGUuY2hpbGRyZW4pLmZpbHRlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGtlKGUsJ1tyb2xlPVwibWVudWl0ZW1yYWRpb1wiXScpfSkpLmZvckVhY2goKGZ1bmN0aW9uKGUpe3JldHVybiBlLnNldEF0dHJpYnV0ZShcImFyaWEtY2hlY2tlZFwiLFwiZmFsc2VcIil9KSksZC5zZXRBdHRyaWJ1dGUoXCJhcmlhLWNoZWNrZWRcIixlP1widHJ1ZVwiOlwiZmFsc2VcIil9fSksdGhpcy5saXN0ZW5lcnMuYmluZChkLFwiY2xpY2sga2V5dXBcIiwoZnVuY3Rpb24oZSl7aWYoIWVlKGUpfHwzMj09PWUud2hpY2gpe3N3aXRjaChlLnByZXZlbnREZWZhdWx0KCksZS5zdG9wUHJvcGFnYXRpb24oKSxkLmNoZWNrZWQ9ITAsYSl7Y2FzZVwibGFuZ3VhZ2VcIjp0LmN1cnJlbnRUcmFjaz1OdW1iZXIoaSk7YnJlYWs7Y2FzZVwicXVhbGl0eVwiOnQucXVhbGl0eT1pO2JyZWFrO2Nhc2VcInNwZWVkXCI6dC5zcGVlZD1wYXJzZUZsb2F0KGkpfW50LnNob3dNZW51UGFuZWwuY2FsbCh0LFwiaG9tZVwiLGVlKGUpKX19KSxhLCExKSxudC5iaW5kTWVudUl0ZW1TaG9ydGN1dHMuY2FsbCh0aGlzLGQsYSksbi5hcHBlbmRDaGlsZChkKX0sZm9ybWF0VGltZTpmdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTowLHQ9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0mJmFyZ3VtZW50c1sxXTtpZighSyhlKSlyZXR1cm4gZTt2YXIgaT1aZSh0aGlzLmR1cmF0aW9uKT4wO3JldHVybiBpdChlLGksdCl9LHVwZGF0ZVRpbWVEaXNwbGF5OmZ1bmN0aW9uKCl7dmFyIGU9YXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0/YXJndW1lbnRzWzBdOm51bGwsdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXT9hcmd1bWVudHNbMV06MCxpPWFyZ3VtZW50cy5sZW5ndGg+MiYmdm9pZCAwIT09YXJndW1lbnRzWzJdJiZhcmd1bWVudHNbMl07RyhlKSYmSyh0KSYmKGUuaW5uZXJUZXh0PW50LmZvcm1hdFRpbWUodCxpKSl9LHVwZGF0ZVZvbHVtZTpmdW5jdGlvbigpe3RoaXMuc3VwcG9ydGVkLnVpJiYoRyh0aGlzLmVsZW1lbnRzLmlucHV0cy52b2x1bWUpJiZudC5zZXRSYW5nZS5jYWxsKHRoaXMsdGhpcy5lbGVtZW50cy5pbnB1dHMudm9sdW1lLHRoaXMubXV0ZWQ/MDp0aGlzLnZvbHVtZSksRyh0aGlzLmVsZW1lbnRzLmJ1dHRvbnMubXV0ZSkmJih0aGlzLmVsZW1lbnRzLmJ1dHRvbnMubXV0ZS5wcmVzc2VkPXRoaXMubXV0ZWR8fDA9PT10aGlzLnZvbHVtZSkpfSxzZXRSYW5nZTpmdW5jdGlvbihlKXt2YXIgdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXT9hcmd1bWVudHNbMV06MDtHKGUpJiYoZS52YWx1ZT10LG50LnVwZGF0ZVJhbmdlRmlsbC5jYWxsKHRoaXMsZSkpfSx1cGRhdGVQcm9ncmVzczpmdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuc3VwcG9ydGVkLnVpJiZaKGUpKXt2YXIgaSxuLGE9MDtpZihlKXN3aXRjaChlLnR5cGUpe2Nhc2VcInRpbWV1cGRhdGVcIjpjYXNlXCJzZWVraW5nXCI6Y2FzZVwic2Vla2VkXCI6aT10aGlzLmN1cnJlbnRUaW1lLG49dGhpcy5kdXJhdGlvbixhPTA9PT1pfHwwPT09bnx8TnVtYmVyLmlzTmFOKGkpfHxOdW1iZXIuaXNOYU4obik/MDooaS9uKjEwMCkudG9GaXhlZCgyKSxcInRpbWV1cGRhdGVcIj09PWUudHlwZSYmbnQuc2V0UmFuZ2UuY2FsbCh0aGlzLHRoaXMuZWxlbWVudHMuaW5wdXRzLnNlZWssYSk7YnJlYWs7Y2FzZVwicGxheWluZ1wiOmNhc2VcInByb2dyZXNzXCI6IWZ1bmN0aW9uKGUsaSl7dmFyIG49SyhpKT9pOjAsYT1HKGUpP2U6dC5lbGVtZW50cy5kaXNwbGF5LmJ1ZmZlcjtpZihHKGEpKXthLnZhbHVlPW47dmFyIHM9YS5nZXRFbGVtZW50c0J5VGFnTmFtZShcInNwYW5cIilbMF07RyhzKSYmKHMuY2hpbGROb2Rlc1swXS5ub2RlVmFsdWU9bil9fSh0aGlzLmVsZW1lbnRzLmRpc3BsYXkuYnVmZmVyLDEwMCp0aGlzLmJ1ZmZlcmVkKX19fSx1cGRhdGVSYW5nZUZpbGw6ZnVuY3Rpb24oZSl7dmFyIHQ9WihlKT9lLnRhcmdldDplO2lmKEcodCkmJlwicmFuZ2VcIj09PXQuZ2V0QXR0cmlidXRlKFwidHlwZVwiKSl7aWYoa2UodCx0aGlzLmNvbmZpZy5zZWxlY3RvcnMuaW5wdXRzLnNlZWspKXt0LnNldEF0dHJpYnV0ZShcImFyaWEtdmFsdWVub3dcIix0aGlzLmN1cnJlbnRUaW1lKTt2YXIgaT1udC5mb3JtYXRUaW1lKHRoaXMuY3VycmVudFRpbWUpLG49bnQuZm9ybWF0VGltZSh0aGlzLmR1cmF0aW9uKSxhPVhlKFwic2Vla0xhYmVsXCIsdGhpcy5jb25maWcpO3Quc2V0QXR0cmlidXRlKFwiYXJpYS12YWx1ZXRleHRcIixhLnJlcGxhY2UoXCJ7Y3VycmVudFRpbWV9XCIsaSkucmVwbGFjZShcIntkdXJhdGlvbn1cIixuKSl9ZWxzZSBpZihrZSh0LHRoaXMuY29uZmlnLnNlbGVjdG9ycy5pbnB1dHMudm9sdW1lKSl7dmFyIHM9MTAwKnQudmFsdWU7dC5zZXRBdHRyaWJ1dGUoXCJhcmlhLXZhbHVlbm93XCIscyksdC5zZXRBdHRyaWJ1dGUoXCJhcmlhLXZhbHVldGV4dFwiLFwiXCIuY29uY2F0KHMudG9GaXhlZCgxKSxcIiVcIikpfWVsc2UgdC5zZXRBdHRyaWJ1dGUoXCJhcmlhLXZhbHVlbm93XCIsdC52YWx1ZSk7b2UuaXNXZWJraXQmJnQuc3R5bGUuc2V0UHJvcGVydHkoXCItLXZhbHVlXCIsXCJcIi5jb25jYXQodC52YWx1ZS90Lm1heCoxMDAsXCIlXCIpKX19LHVwZGF0ZVNlZWtUb29sdGlwOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7aWYodGhpcy5jb25maWcudG9vbHRpcHMuc2VlayYmRyh0aGlzLmVsZW1lbnRzLmlucHV0cy5zZWVrKSYmRyh0aGlzLmVsZW1lbnRzLmRpc3BsYXkuc2Vla1Rvb2x0aXApJiYwIT09dGhpcy5kdXJhdGlvbil7dmFyIGk9XCJcIi5jb25jYXQodGhpcy5jb25maWcuY2xhc3NOYW1lcy50b29sdGlwLFwiLS12aXNpYmxlXCIpLG49ZnVuY3Rpb24oZSl7cmV0dXJuIGJlKHQuZWxlbWVudHMuZGlzcGxheS5zZWVrVG9vbHRpcCxpLGUpfTtpZih0aGlzLnRvdWNoKW4oITEpO2Vsc2V7dmFyIGE9MCxzPXRoaXMuZWxlbWVudHMucHJvZ3Jlc3MuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7aWYoWihlKSlhPTEwMC9zLndpZHRoKihlLnBhZ2VYLXMubGVmdCk7ZWxzZXtpZighd2UodGhpcy5lbGVtZW50cy5kaXNwbGF5LnNlZWtUb29sdGlwLGkpKXJldHVybjthPXBhcnNlRmxvYXQodGhpcy5lbGVtZW50cy5kaXNwbGF5LnNlZWtUb29sdGlwLnN0eWxlLmxlZnQsMTApfWE8MD9hPTA6YT4xMDAmJihhPTEwMCksbnQudXBkYXRlVGltZURpc3BsYXkuY2FsbCh0aGlzLHRoaXMuZWxlbWVudHMuZGlzcGxheS5zZWVrVG9vbHRpcCx0aGlzLmR1cmF0aW9uLzEwMCphKSx0aGlzLmVsZW1lbnRzLmRpc3BsYXkuc2Vla1Rvb2x0aXAuc3R5bGUubGVmdD1cIlwiLmNvbmNhdChhLFwiJVwiKSxaKGUpJiZbXCJtb3VzZWVudGVyXCIsXCJtb3VzZWxlYXZlXCJdLmluY2x1ZGVzKGUudHlwZSkmJm4oXCJtb3VzZWVudGVyXCI9PT1lLnR5cGUpfX19LHRpbWVVcGRhdGU6ZnVuY3Rpb24oZSl7dmFyIHQ9IUcodGhpcy5lbGVtZW50cy5kaXNwbGF5LmR1cmF0aW9uKSYmdGhpcy5jb25maWcuaW52ZXJ0VGltZTtudC51cGRhdGVUaW1lRGlzcGxheS5jYWxsKHRoaXMsdGhpcy5lbGVtZW50cy5kaXNwbGF5LmN1cnJlbnRUaW1lLHQ/dGhpcy5kdXJhdGlvbi10aGlzLmN1cnJlbnRUaW1lOnRoaXMuY3VycmVudFRpbWUsdCksZSYmXCJ0aW1ldXBkYXRlXCI9PT1lLnR5cGUmJnRoaXMubWVkaWEuc2Vla2luZ3x8bnQudXBkYXRlUHJvZ3Jlc3MuY2FsbCh0aGlzLGUpfSxkdXJhdGlvblVwZGF0ZTpmdW5jdGlvbigpe2lmKHRoaXMuc3VwcG9ydGVkLnVpJiYodGhpcy5jb25maWcuaW52ZXJ0VGltZXx8IXRoaXMuY3VycmVudFRpbWUpKXtpZih0aGlzLmR1cmF0aW9uPj1NYXRoLnBvdygyLDMyKSlyZXR1cm4gdmUodGhpcy5lbGVtZW50cy5kaXNwbGF5LmN1cnJlbnRUaW1lLCEwKSx2b2lkIHZlKHRoaXMuZWxlbWVudHMucHJvZ3Jlc3MsITApO0codGhpcy5lbGVtZW50cy5pbnB1dHMuc2VlaykmJnRoaXMuZWxlbWVudHMuaW5wdXRzLnNlZWsuc2V0QXR0cmlidXRlKFwiYXJpYS12YWx1ZW1heFwiLHRoaXMuZHVyYXRpb24pO3ZhciBlPUcodGhpcy5lbGVtZW50cy5kaXNwbGF5LmR1cmF0aW9uKTshZSYmdGhpcy5jb25maWcuZGlzcGxheUR1cmF0aW9uJiZ0aGlzLnBhdXNlZCYmbnQudXBkYXRlVGltZURpc3BsYXkuY2FsbCh0aGlzLHRoaXMuZWxlbWVudHMuZGlzcGxheS5jdXJyZW50VGltZSx0aGlzLmR1cmF0aW9uKSxlJiZudC51cGRhdGVUaW1lRGlzcGxheS5jYWxsKHRoaXMsdGhpcy5lbGVtZW50cy5kaXNwbGF5LmR1cmF0aW9uLHRoaXMuZHVyYXRpb24pLG50LnVwZGF0ZVNlZWtUb29sdGlwLmNhbGwodGhpcyl9fSx0b2dnbGVNZW51QnV0dG9uOmZ1bmN0aW9uKGUsdCl7dmUodGhpcy5lbGVtZW50cy5zZXR0aW5ncy5idXR0b25zW2VdLCF0KX0sdXBkYXRlU2V0dGluZzpmdW5jdGlvbihlLHQsaSl7dmFyIG49dGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wYW5lbHNbZV0sYT1udWxsLHM9dDtpZihcImNhcHRpb25zXCI9PT1lKWE9dGhpcy5jdXJyZW50VHJhY2s7ZWxzZXtpZihhPWFlKGkpP3RoaXNbZV06aSxhZShhKSYmKGE9dGhpcy5jb25maWdbZV0uZGVmYXVsdCksIWFlKHRoaXMub3B0aW9uc1tlXSkmJiF0aGlzLm9wdGlvbnNbZV0uaW5jbHVkZXMoYSkpcmV0dXJuIHZvaWQgdGhpcy5kZWJ1Zy53YXJuKFwiVW5zdXBwb3J0ZWQgdmFsdWUgb2YgJ1wiLmNvbmNhdChhLFwiJyBmb3IgXCIpLmNvbmNhdChlKSk7aWYoIXRoaXMuY29uZmlnW2VdLm9wdGlvbnMuaW5jbHVkZXMoYSkpcmV0dXJuIHZvaWQgdGhpcy5kZWJ1Zy53YXJuKFwiRGlzYWJsZWQgdmFsdWUgb2YgJ1wiLmNvbmNhdChhLFwiJyBmb3IgXCIpLmNvbmNhdChlKSl9aWYoRyhzKXx8KHM9biYmbi5xdWVyeVNlbGVjdG9yKCdbcm9sZT1cIm1lbnVcIl0nKSksRyhzKSl7dGhpcy5lbGVtZW50cy5zZXR0aW5ncy5idXR0b25zW2VdLnF1ZXJ5U2VsZWN0b3IoXCIuXCIuY29uY2F0KHRoaXMuY29uZmlnLmNsYXNzTmFtZXMubWVudS52YWx1ZSkpLmlubmVySFRNTD1udC5nZXRMYWJlbC5jYWxsKHRoaXMsZSxhKTt2YXIgcj1zJiZzLnF1ZXJ5U2VsZWN0b3IoJ1t2YWx1ZT1cIicuY29uY2F0KGEsJ1wiXScpKTtHKHIpJiYoci5jaGVja2VkPSEwKX19LGdldExhYmVsOmZ1bmN0aW9uKGUsdCl7c3dpdGNoKGUpe2Nhc2VcInNwZWVkXCI6cmV0dXJuIDE9PT10P1hlKFwibm9ybWFsXCIsdGhpcy5jb25maWcpOlwiXCIuY29uY2F0KHQsXCImdGltZXM7XCIpO2Nhc2VcInF1YWxpdHlcIjppZihLKHQpKXt2YXIgaT1YZShcInF1YWxpdHlMYWJlbC5cIi5jb25jYXQodCksdGhpcy5jb25maWcpO3JldHVybiBpLmxlbmd0aD9pOlwiXCIuY29uY2F0KHQsXCJwXCIpfXJldHVybiB6ZSh0KTtjYXNlXCJjYXB0aW9uc1wiOnJldHVybiBydC5nZXRMYWJlbC5jYWxsKHRoaXMpO2RlZmF1bHQ6cmV0dXJuIG51bGx9fSxzZXRRdWFsaXR5TWVudTpmdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKEcodGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wYW5lbHMucXVhbGl0eSkpe3ZhciBpPVwicXVhbGl0eVwiLG49dGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wYW5lbHMucXVhbGl0eS5xdWVyeVNlbGVjdG9yKCdbcm9sZT1cIm1lbnVcIl0nKTskKGUpJiYodGhpcy5vcHRpb25zLnF1YWxpdHk9QmUoZSkuZmlsdGVyKChmdW5jdGlvbihlKXtyZXR1cm4gdC5jb25maWcucXVhbGl0eS5vcHRpb25zLmluY2x1ZGVzKGUpfSkpKTt2YXIgYT0hYWUodGhpcy5vcHRpb25zLnF1YWxpdHkpJiZ0aGlzLm9wdGlvbnMucXVhbGl0eS5sZW5ndGg+MTtpZihudC50b2dnbGVNZW51QnV0dG9uLmNhbGwodGhpcyxpLGEpLGZlKG4pLG50LmNoZWNrTWVudS5jYWxsKHRoaXMpLGEpe3ZhciBzPWZ1bmN0aW9uKGUpe3ZhciBpPVhlKFwicXVhbGl0eUJhZGdlLlwiLmNvbmNhdChlKSx0LmNvbmZpZyk7cmV0dXJuIGkubGVuZ3RoP250LmNyZWF0ZUJhZGdlLmNhbGwodCxpKTpudWxsfTt0aGlzLm9wdGlvbnMucXVhbGl0eS5zb3J0KChmdW5jdGlvbihlLGkpe3ZhciBuPXQuY29uZmlnLnF1YWxpdHkub3B0aW9ucztyZXR1cm4gbi5pbmRleE9mKGUpPm4uaW5kZXhPZihpKT8xOi0xfSkpLmZvckVhY2goKGZ1bmN0aW9uKGUpe250LmNyZWF0ZU1lbnVJdGVtLmNhbGwodCx7dmFsdWU6ZSxsaXN0Om4sdHlwZTppLHRpdGxlOm50LmdldExhYmVsLmNhbGwodCxcInF1YWxpdHlcIixlKSxiYWRnZTpzKGUpfSl9KSksbnQudXBkYXRlU2V0dGluZy5jYWxsKHRoaXMsaSxuKX19fSxzZXRDYXB0aW9uc01lbnU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO2lmKEcodGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wYW5lbHMuY2FwdGlvbnMpKXt2YXIgdD1cImNhcHRpb25zXCIsaT10aGlzLmVsZW1lbnRzLnNldHRpbmdzLnBhbmVscy5jYXB0aW9ucy5xdWVyeVNlbGVjdG9yKCdbcm9sZT1cIm1lbnVcIl0nKSxuPXJ0LmdldFRyYWNrcy5jYWxsKHRoaXMpLGE9Qm9vbGVhbihuLmxlbmd0aCk7aWYobnQudG9nZ2xlTWVudUJ1dHRvbi5jYWxsKHRoaXMsdCxhKSxmZShpKSxudC5jaGVja01lbnUuY2FsbCh0aGlzKSxhKXt2YXIgcz1uLm1hcCgoZnVuY3Rpb24odCxuKXtyZXR1cm57dmFsdWU6bixjaGVja2VkOmUuY2FwdGlvbnMudG9nZ2xlZCYmZS5jdXJyZW50VHJhY2s9PT1uLHRpdGxlOnJ0LmdldExhYmVsLmNhbGwoZSx0KSxiYWRnZTp0Lmxhbmd1YWdlJiZudC5jcmVhdGVCYWRnZS5jYWxsKGUsdC5sYW5ndWFnZS50b1VwcGVyQ2FzZSgpKSxsaXN0OmksdHlwZTpcImxhbmd1YWdlXCJ9fSkpO3MudW5zaGlmdCh7dmFsdWU6LTEsY2hlY2tlZDohdGhpcy5jYXB0aW9ucy50b2dnbGVkLHRpdGxlOlhlKFwiZGlzYWJsZWRcIix0aGlzLmNvbmZpZyksbGlzdDppLHR5cGU6XCJsYW5ndWFnZVwifSkscy5mb3JFYWNoKG50LmNyZWF0ZU1lbnVJdGVtLmJpbmQodGhpcykpLG50LnVwZGF0ZVNldHRpbmcuY2FsbCh0aGlzLHQsaSl9fX0sc2V0U3BlZWRNZW51OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcztpZihHKHRoaXMuZWxlbWVudHMuc2V0dGluZ3MucGFuZWxzLnNwZWVkKSl7dmFyIHQ9XCJzcGVlZFwiLGk9dGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wYW5lbHMuc3BlZWQucXVlcnlTZWxlY3RvcignW3JvbGU9XCJtZW51XCJdJyk7dGhpcy5vcHRpb25zLnNwZWVkPXRoaXMub3B0aW9ucy5zcGVlZC5maWx0ZXIoKGZ1bmN0aW9uKHQpe3JldHVybiB0Pj1lLm1pbmltdW1TcGVlZCYmdDw9ZS5tYXhpbXVtU3BlZWR9KSk7dmFyIG49IWFlKHRoaXMub3B0aW9ucy5zcGVlZCkmJnRoaXMub3B0aW9ucy5zcGVlZC5sZW5ndGg+MTtudC50b2dnbGVNZW51QnV0dG9uLmNhbGwodGhpcyx0LG4pLGZlKGkpLG50LmNoZWNrTWVudS5jYWxsKHRoaXMpLG4mJih0aGlzLm9wdGlvbnMuc3BlZWQuZm9yRWFjaCgoZnVuY3Rpb24obil7bnQuY3JlYXRlTWVudUl0ZW0uY2FsbChlLHt2YWx1ZTpuLGxpc3Q6aSx0eXBlOnQsdGl0bGU6bnQuZ2V0TGFiZWwuY2FsbChlLFwic3BlZWRcIixuKX0pfSkpLG50LnVwZGF0ZVNldHRpbmcuY2FsbCh0aGlzLHQsaSkpfX0sY2hlY2tNZW51OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5lbGVtZW50cy5zZXR0aW5ncy5idXR0b25zLHQ9IWFlKGUpJiZPYmplY3QudmFsdWVzKGUpLnNvbWUoKGZ1bmN0aW9uKGUpe3JldHVybiFlLmhpZGRlbn0pKTt2ZSh0aGlzLmVsZW1lbnRzLnNldHRpbmdzLm1lbnUsIXQpfSxmb2N1c0ZpcnN0TWVudUl0ZW06ZnVuY3Rpb24oZSl7dmFyIHQ9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0mJmFyZ3VtZW50c1sxXTtpZighdGhpcy5lbGVtZW50cy5zZXR0aW5ncy5wb3B1cC5oaWRkZW4pe3ZhciBpPWU7RyhpKXx8KGk9T2JqZWN0LnZhbHVlcyh0aGlzLmVsZW1lbnRzLnNldHRpbmdzLnBhbmVscykuZmluZCgoZnVuY3Rpb24oZSl7cmV0dXJuIWUuaGlkZGVufSkpKTt2YXIgbj1pLnF1ZXJ5U2VsZWN0b3IoJ1tyb2xlXj1cIm1lbnVpdGVtXCJdJyk7QWUuY2FsbCh0aGlzLG4sdCl9fSx0b2dnbGVNZW51OmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuZWxlbWVudHMuc2V0dGluZ3MucG9wdXAsaT10aGlzLmVsZW1lbnRzLmJ1dHRvbnMuc2V0dGluZ3M7aWYoRyh0KSYmRyhpKSl7dmFyIG49dC5oaWRkZW4sYT1uO2lmKFEoZSkpYT1lO2Vsc2UgaWYoZWUoZSkmJjI3PT09ZS53aGljaClhPSExO2Vsc2UgaWYoWihlKSl7dmFyIHM9WChlLmNvbXBvc2VkUGF0aCk/ZS5jb21wb3NlZFBhdGgoKVswXTplLnRhcmdldCxyPXQuY29udGFpbnMocyk7aWYocnx8IXImJmUudGFyZ2V0IT09aSYmYSlyZXR1cm59aS5zZXRBdHRyaWJ1dGUoXCJhcmlhLWV4cGFuZGVkXCIsYSksdmUodCwhYSksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5tZW51Lm9wZW4sYSksYSYmZWUoZSk/bnQuZm9jdXNGaXJzdE1lbnVJdGVtLmNhbGwodGhpcyxudWxsLCEwKTphfHxufHxBZS5jYWxsKHRoaXMsaSxlZShlKSl9fSxnZXRNZW51U2l6ZTpmdW5jdGlvbihlKXt2YXIgdD1lLmNsb25lTm9kZSghMCk7dC5zdHlsZS5wb3NpdGlvbj1cImFic29sdXRlXCIsdC5zdHlsZS5vcGFjaXR5PTAsdC5yZW1vdmVBdHRyaWJ1dGUoXCJoaWRkZW5cIiksZS5wYXJlbnROb2RlLmFwcGVuZENoaWxkKHQpO3ZhciBpPXQuc2Nyb2xsV2lkdGgsbj10LnNjcm9sbEhlaWdodDtyZXR1cm4gcGUodCkse3dpZHRoOmksaGVpZ2h0Om59fSxzaG93TWVudVBhbmVsOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTpcIlwiLGk9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0mJmFyZ3VtZW50c1sxXSxuPXRoaXMuZWxlbWVudHMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoXCIjcGx5ci1zZXR0aW5ncy1cIi5jb25jYXQodGhpcy5pZCxcIi1cIikuY29uY2F0KHQpKTtpZihHKG4pKXt2YXIgYT1uLnBhcmVudE5vZGUscz1BcnJheS5mcm9tKGEuY2hpbGRyZW4pLmZpbmQoKGZ1bmN0aW9uKGUpe3JldHVybiFlLmhpZGRlbn0pKTtpZihFZS50cmFuc2l0aW9ucyYmIUVlLnJlZHVjZWRNb3Rpb24pe2Euc3R5bGUud2lkdGg9XCJcIi5jb25jYXQocy5zY3JvbGxXaWR0aCxcInB4XCIpLGEuc3R5bGUuaGVpZ2h0PVwiXCIuY29uY2F0KHMuc2Nyb2xsSGVpZ2h0LFwicHhcIik7dmFyIHI9bnQuZ2V0TWVudVNpemUuY2FsbCh0aGlzLG4pLG89ZnVuY3Rpb24gdChpKXtpLnRhcmdldD09PWEmJltcIndpZHRoXCIsXCJoZWlnaHRcIl0uaW5jbHVkZXMoaS5wcm9wZXJ0eU5hbWUpJiYoYS5zdHlsZS53aWR0aD1cIlwiLGEuc3R5bGUuaGVpZ2h0PVwiXCIsSWUuY2FsbChlLGEsc2UsdCkpfTt4ZS5jYWxsKHRoaXMsYSxzZSxvKSxhLnN0eWxlLndpZHRoPVwiXCIuY29uY2F0KHIud2lkdGgsXCJweFwiKSxhLnN0eWxlLmhlaWdodD1cIlwiLmNvbmNhdChyLmhlaWdodCxcInB4XCIpfXZlKHMsITApLHZlKG4sITEpLG50LmZvY3VzRmlyc3RNZW51SXRlbS5jYWxsKHRoaXMsbixpKX19LHNldERvd25sb2FkVXJsOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5lbGVtZW50cy5idXR0b25zLmRvd25sb2FkO0coZSkmJmUuc2V0QXR0cmlidXRlKFwiaHJlZlwiLHRoaXMuZG93bmxvYWQpfSxjcmVhdGU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPW50LmJpbmRNZW51SXRlbVNob3J0Y3V0cyxuPW50LmNyZWF0ZUJ1dHRvbixhPW50LmNyZWF0ZVByb2dyZXNzLHM9bnQuY3JlYXRlUmFuZ2Uscj1udC5jcmVhdGVUaW1lLG89bnQuc2V0UXVhbGl0eU1lbnUsbD1udC5zZXRTcGVlZE1lbnUsYz1udC5zaG93TWVudVBhbmVsO3RoaXMuZWxlbWVudHMuY29udHJvbHM9bnVsbCwkKHRoaXMuY29uZmlnLmNvbnRyb2xzKSYmdGhpcy5jb25maWcuY29udHJvbHMuaW5jbHVkZXMoXCJwbGF5LWxhcmdlXCIpJiZ0aGlzLmVsZW1lbnRzLmNvbnRhaW5lci5hcHBlbmRDaGlsZChuLmNhbGwodGhpcyxcInBsYXktbGFyZ2VcIikpO3ZhciB1PWhlKFwiZGl2XCIseWUodGhpcy5jb25maWcuc2VsZWN0b3JzLmNvbnRyb2xzLndyYXBwZXIpKTt0aGlzLmVsZW1lbnRzLmNvbnRyb2xzPXU7dmFyIGQ9e2NsYXNzOlwicGx5cl9fY29udHJvbHNfX2l0ZW1cIn07cmV0dXJuIEJlKCQodGhpcy5jb25maWcuY29udHJvbHMpP3RoaXMuY29uZmlnLmNvbnRyb2xzOltdKS5mb3JFYWNoKChmdW5jdGlvbihvKXtpZihcInJlc3RhcnRcIj09PW8mJnUuYXBwZW5kQ2hpbGQobi5jYWxsKHQsXCJyZXN0YXJ0XCIsZCkpLFwicmV3aW5kXCI9PT1vJiZ1LmFwcGVuZENoaWxkKG4uY2FsbCh0LFwicmV3aW5kXCIsZCkpLFwicGxheVwiPT09byYmdS5hcHBlbmRDaGlsZChuLmNhbGwodCxcInBsYXlcIixkKSksXCJmYXN0LWZvcndhcmRcIj09PW8mJnUuYXBwZW5kQ2hpbGQobi5jYWxsKHQsXCJmYXN0LWZvcndhcmRcIixkKSksXCJwcm9ncmVzc1wiPT09byl7dmFyIGw9aGUoXCJkaXZcIix7Y2xhc3M6XCJcIi5jb25jYXQoZC5jbGFzcyxcIiBwbHlyX19wcm9ncmVzc19fY29udGFpbmVyXCIpfSksaD1oZShcImRpdlwiLHllKHQuY29uZmlnLnNlbGVjdG9ycy5wcm9ncmVzcykpO2lmKGguYXBwZW5kQ2hpbGQocy5jYWxsKHQsXCJzZWVrXCIse2lkOlwicGx5ci1zZWVrLVwiLmNvbmNhdChlLmlkKX0pKSxoLmFwcGVuZENoaWxkKGEuY2FsbCh0LFwiYnVmZmVyXCIpKSx0LmNvbmZpZy50b29sdGlwcy5zZWVrKXt2YXIgbT1oZShcInNwYW5cIix7Y2xhc3M6dC5jb25maWcuY2xhc3NOYW1lcy50b29sdGlwfSxcIjAwOjAwXCIpO2guYXBwZW5kQ2hpbGQobSksdC5lbGVtZW50cy5kaXNwbGF5LnNlZWtUb29sdGlwPW19dC5lbGVtZW50cy5wcm9ncmVzcz1oLGwuYXBwZW5kQ2hpbGQodC5lbGVtZW50cy5wcm9ncmVzcyksdS5hcHBlbmRDaGlsZChsKX1pZihcImN1cnJlbnQtdGltZVwiPT09byYmdS5hcHBlbmRDaGlsZChyLmNhbGwodCxcImN1cnJlbnRUaW1lXCIsZCkpLFwiZHVyYXRpb25cIj09PW8mJnUuYXBwZW5kQ2hpbGQoci5jYWxsKHQsXCJkdXJhdGlvblwiLGQpKSxcIm11dGVcIj09PW98fFwidm9sdW1lXCI9PT1vKXt2YXIgcD10LmVsZW1lbnRzLnZvbHVtZTtpZihHKHApJiZ1LmNvbnRhaW5zKHApfHwocD1oZShcImRpdlwiLGNlKHt9LGQse2NsYXNzOlwiXCIuY29uY2F0KGQuY2xhc3MsXCIgcGx5cl9fdm9sdW1lXCIpLnRyaW0oKX0pKSx0LmVsZW1lbnRzLnZvbHVtZT1wLHUuYXBwZW5kQ2hpbGQocCkpLFwibXV0ZVwiPT09byYmcC5hcHBlbmRDaGlsZChuLmNhbGwodCxcIm11dGVcIikpLFwidm9sdW1lXCI9PT1vJiYhb2UuaXNJb3Mpe3ZhciBmPXttYXg6MSxzdGVwOi4wNSx2YWx1ZTp0LmNvbmZpZy52b2x1bWV9O3AuYXBwZW5kQ2hpbGQocy5jYWxsKHQsXCJ2b2x1bWVcIixjZShmLHtpZDpcInBseXItdm9sdW1lLVwiLmNvbmNhdChlLmlkKX0pKSl9fWlmKFwiY2FwdGlvbnNcIj09PW8mJnUuYXBwZW5kQ2hpbGQobi5jYWxsKHQsXCJjYXB0aW9uc1wiLGQpKSxcInNldHRpbmdzXCI9PT1vJiYhYWUodC5jb25maWcuc2V0dGluZ3MpKXt2YXIgZz1oZShcImRpdlwiLGNlKHt9LGQse2NsYXNzOlwiXCIuY29uY2F0KGQuY2xhc3MsXCIgcGx5cl9fbWVudVwiKS50cmltKCksaGlkZGVuOlwiXCJ9KSk7Zy5hcHBlbmRDaGlsZChuLmNhbGwodCxcInNldHRpbmdzXCIse1wiYXJpYS1oYXNwb3B1cFwiOiEwLFwiYXJpYS1jb250cm9sc1wiOlwicGx5ci1zZXR0aW5ncy1cIi5jb25jYXQoZS5pZCksXCJhcmlhLWV4cGFuZGVkXCI6ITF9KSk7dmFyIHk9aGUoXCJkaXZcIix7Y2xhc3M6XCJwbHlyX19tZW51X19jb250YWluZXJcIixpZDpcInBseXItc2V0dGluZ3MtXCIuY29uY2F0KGUuaWQpLGhpZGRlbjpcIlwifSksdj1oZShcImRpdlwiKSxiPWhlKFwiZGl2XCIse2lkOlwicGx5ci1zZXR0aW5ncy1cIi5jb25jYXQoZS5pZCxcIi1ob21lXCIpfSksdz1oZShcImRpdlwiLHtyb2xlOlwibWVudVwifSk7Yi5hcHBlbmRDaGlsZCh3KSx2LmFwcGVuZENoaWxkKGIpLHQuZWxlbWVudHMuc2V0dGluZ3MucGFuZWxzLmhvbWU9Yix0LmNvbmZpZy5zZXR0aW5ncy5mb3JFYWNoKChmdW5jdGlvbihuKXt2YXIgYT1oZShcImJ1dHRvblwiLGNlKHllKHQuY29uZmlnLnNlbGVjdG9ycy5idXR0b25zLnNldHRpbmdzKSx7dHlwZTpcImJ1dHRvblwiLGNsYXNzOlwiXCIuY29uY2F0KHQuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbCxcIiBcIikuY29uY2F0KHQuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbCxcIi0tZm9yd2FyZFwiKSxyb2xlOlwibWVudWl0ZW1cIixcImFyaWEtaGFzcG9wdXBcIjohMCxoaWRkZW46XCJcIn0pKTtpLmNhbGwodCxhLG4pLHhlLmNhbGwodCxhLFwiY2xpY2tcIiwoZnVuY3Rpb24oKXtjLmNhbGwodCxuLCExKX0pKTt2YXIgcz1oZShcInNwYW5cIixudWxsLFhlKG4sdC5jb25maWcpKSxyPWhlKFwic3BhblwiLHtjbGFzczp0LmNvbmZpZy5jbGFzc05hbWVzLm1lbnUudmFsdWV9KTtyLmlubmVySFRNTD1lW25dLHMuYXBwZW5kQ2hpbGQociksYS5hcHBlbmRDaGlsZChzKSx3LmFwcGVuZENoaWxkKGEpO3ZhciBvPWhlKFwiZGl2XCIse2lkOlwicGx5ci1zZXR0aW5ncy1cIi5jb25jYXQoZS5pZCxcIi1cIikuY29uY2F0KG4pLGhpZGRlbjpcIlwifSksbD1oZShcImJ1dHRvblwiLHt0eXBlOlwiYnV0dG9uXCIsY2xhc3M6XCJcIi5jb25jYXQodC5jb25maWcuY2xhc3NOYW1lcy5jb250cm9sLFwiIFwiKS5jb25jYXQodC5jb25maWcuY2xhc3NOYW1lcy5jb250cm9sLFwiLS1iYWNrXCIpfSk7bC5hcHBlbmRDaGlsZChoZShcInNwYW5cIix7XCJhcmlhLWhpZGRlblwiOiEwfSxYZShuLHQuY29uZmlnKSkpLGwuYXBwZW5kQ2hpbGQoaGUoXCJzcGFuXCIse2NsYXNzOnQuY29uZmlnLmNsYXNzTmFtZXMuaGlkZGVufSxYZShcIm1lbnVCYWNrXCIsdC5jb25maWcpKSkseGUuY2FsbCh0LG8sXCJrZXlkb3duXCIsKGZ1bmN0aW9uKGUpezM3PT09ZS53aGljaCYmKGUucHJldmVudERlZmF1bHQoKSxlLnN0b3BQcm9wYWdhdGlvbigpLGMuY2FsbCh0LFwiaG9tZVwiLCEwKSl9KSwhMSkseGUuY2FsbCh0LGwsXCJjbGlja1wiLChmdW5jdGlvbigpe2MuY2FsbCh0LFwiaG9tZVwiLCExKX0pKSxvLmFwcGVuZENoaWxkKGwpLG8uYXBwZW5kQ2hpbGQoaGUoXCJkaXZcIix7cm9sZTpcIm1lbnVcIn0pKSx2LmFwcGVuZENoaWxkKG8pLHQuZWxlbWVudHMuc2V0dGluZ3MuYnV0dG9uc1tuXT1hLHQuZWxlbWVudHMuc2V0dGluZ3MucGFuZWxzW25dPW99KSkseS5hcHBlbmRDaGlsZCh2KSxnLmFwcGVuZENoaWxkKHkpLHUuYXBwZW5kQ2hpbGQoZyksdC5lbGVtZW50cy5zZXR0aW5ncy5wb3B1cD15LHQuZWxlbWVudHMuc2V0dGluZ3MubWVudT1nfWlmKFwicGlwXCI9PT1vJiZFZS5waXAmJnUuYXBwZW5kQ2hpbGQobi5jYWxsKHQsXCJwaXBcIixkKSksXCJhaXJwbGF5XCI9PT1vJiZFZS5haXJwbGF5JiZ1LmFwcGVuZENoaWxkKG4uY2FsbCh0LFwiYWlycGxheVwiLGQpKSxcImRvd25sb2FkXCI9PT1vKXt2YXIgaz1jZSh7fSxkLHtlbGVtZW50OlwiYVwiLGhyZWY6dC5kb3dubG9hZCx0YXJnZXQ6XCJfYmxhbmtcIn0pO3QuaXNIVE1MNSYmKGsuZG93bmxvYWQ9XCJcIik7dmFyIFQ9dC5jb25maWcudXJscy5kb3dubG9hZDshbmUoVCkmJnQuaXNFbWJlZCYmY2Uoayx7aWNvbjpcImxvZ28tXCIuY29uY2F0KHQucHJvdmlkZXIpLGxhYmVsOnQucHJvdmlkZXJ9KSx1LmFwcGVuZENoaWxkKG4uY2FsbCh0LFwiZG93bmxvYWRcIixrKSl9XCJmdWxsc2NyZWVuXCI9PT1vJiZ1LmFwcGVuZENoaWxkKG4uY2FsbCh0LFwiZnVsbHNjcmVlblwiLGQpKX0pKSx0aGlzLmlzSFRNTDUmJm8uY2FsbCh0aGlzLFZlLmdldFF1YWxpdHlPcHRpb25zLmNhbGwodGhpcykpLGwuY2FsbCh0aGlzKSx1fSxpbmplY3Q6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO2lmKHRoaXMuY29uZmlnLmxvYWRTcHJpdGUpe3ZhciB0PW50LmdldEljb25VcmwuY2FsbCh0aGlzKTt0LmNvcnMmJkdlKHQudXJsLFwic3ByaXRlLXBseXJcIil9dGhpcy5pZD1NYXRoLmZsb29yKDFlNCpNYXRoLnJhbmRvbSgpKTt2YXIgaT1udWxsO3RoaXMuZWxlbWVudHMuY29udHJvbHM9bnVsbDt2YXIgbj17aWQ6dGhpcy5pZCxzZWVrdGltZTp0aGlzLmNvbmZpZy5zZWVrVGltZSx0aXRsZTp0aGlzLmNvbmZpZy50aXRsZX0sYT0hMDtYKHRoaXMuY29uZmlnLmNvbnRyb2xzKSYmKHRoaXMuY29uZmlnLmNvbnRyb2xzPXRoaXMuY29uZmlnLmNvbnRyb2xzLmNhbGwodGhpcyxuKSksdGhpcy5jb25maWcuY29udHJvbHN8fCh0aGlzLmNvbmZpZy5jb250cm9scz1bXSksRyh0aGlzLmNvbmZpZy5jb250cm9scyl8fFkodGhpcy5jb25maWcuY29udHJvbHMpP2k9dGhpcy5jb25maWcuY29udHJvbHM6KGk9bnQuY3JlYXRlLmNhbGwodGhpcyx7aWQ6dGhpcy5pZCxzZWVrdGltZTp0aGlzLmNvbmZpZy5zZWVrVGltZSxzcGVlZDp0aGlzLnNwZWVkLHF1YWxpdHk6dGhpcy5xdWFsaXR5LGNhcHRpb25zOnJ0LmdldExhYmVsLmNhbGwodGhpcyl9KSxhPSExKTt2YXIgcyxyO2lmKGEmJlkodGhpcy5jb25maWcuY29udHJvbHMpJiYocz1pLE9iamVjdC5lbnRyaWVzKG4pLmZvckVhY2goKGZ1bmN0aW9uKGUpe3ZhciB0PW8oZSwyKSxpPXRbMF0sbj10WzFdO3M9V2UocyxcIntcIi5jb25jYXQoaSxcIn1cIiksbil9KSksaT1zKSxZKHRoaXMuY29uZmlnLnNlbGVjdG9ycy5jb250cm9scy5jb250YWluZXIpJiYocj1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKHRoaXMuY29uZmlnLnNlbGVjdG9ycy5jb250cm9scy5jb250YWluZXIpKSxHKHIpfHwocj10aGlzLmVsZW1lbnRzLmNvbnRhaW5lcikscltHKGkpP1wiaW5zZXJ0QWRqYWNlbnRFbGVtZW50XCI6XCJpbnNlcnRBZGphY2VudEhUTUxcIl0oXCJhZnRlcmJlZ2luXCIsaSksRyh0aGlzLmVsZW1lbnRzLmNvbnRyb2xzKXx8bnQuZmluZEVsZW1lbnRzLmNhbGwodGhpcyksIWFlKHRoaXMuZWxlbWVudHMuYnV0dG9ucykpe3ZhciBsPWZ1bmN0aW9uKHQpe3ZhciBpPWUuY29uZmlnLmNsYXNzTmFtZXMuY29udHJvbFByZXNzZWQ7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsXCJwcmVzc2VkXCIse2VudW1lcmFibGU6ITAsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHdlKHQsaSl9LHNldDpmdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdJiZhcmd1bWVudHNbMF07YmUodCxpLGUpfX0pfTtPYmplY3QudmFsdWVzKHRoaXMuZWxlbWVudHMuYnV0dG9ucykuZmlsdGVyKEJvb2xlYW4pLmZvckVhY2goKGZ1bmN0aW9uKGUpeyQoZSl8fEooZSk/QXJyYXkuZnJvbShlKS5maWx0ZXIoQm9vbGVhbikuZm9yRWFjaChsKTpsKGUpfSkpfWlmKG9lLmlzRWRnZSYmcmUociksdGhpcy5jb25maWcudG9vbHRpcHMuY29udHJvbHMpe3ZhciBjPXRoaXMuY29uZmlnLHU9Yy5jbGFzc05hbWVzLGQ9Yy5zZWxlY3RvcnMsaD1cIlwiLmNvbmNhdChkLmNvbnRyb2xzLndyYXBwZXIsXCIgXCIpLmNvbmNhdChkLmxhYmVscyxcIiAuXCIpLmNvbmNhdCh1LmhpZGRlbiksbT1UZS5jYWxsKHRoaXMsaCk7QXJyYXkuZnJvbShtKS5mb3JFYWNoKChmdW5jdGlvbih0KXtiZSh0LGUuY29uZmlnLmNsYXNzTmFtZXMuaGlkZGVuLCExKSxiZSh0LGUuY29uZmlnLmNsYXNzTmFtZXMudG9vbHRpcCwhMCl9KSl9fX07ZnVuY3Rpb24gYXQoZSl7dmFyIHQ9IShhcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSl8fGFyZ3VtZW50c1sxXSxpPWU7aWYodCl7dmFyIG49ZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImFcIik7bi5ocmVmPWksaT1uLmhyZWZ9dHJ5e3JldHVybiBuZXcgVVJMKGkpfWNhdGNoKGUpe3JldHVybiBudWxsfX1mdW5jdGlvbiBzdChlKXt2YXIgdD1uZXcgVVJMU2VhcmNoUGFyYW1zO3JldHVybiB6KGUpJiZPYmplY3QuZW50cmllcyhlKS5mb3JFYWNoKChmdW5jdGlvbihlKXt2YXIgaT1vKGUsMiksbj1pWzBdLGE9aVsxXTt0LnNldChuLGEpfSkpLHR9dmFyIHJ0PXtzZXR1cDpmdW5jdGlvbigpe2lmKHRoaXMuc3VwcG9ydGVkLnVpKWlmKCF0aGlzLmlzVmlkZW98fHRoaXMuaXNZb3VUdWJlfHx0aGlzLmlzSFRNTDUmJiFFZS50ZXh0VHJhY2tzKSQodGhpcy5jb25maWcuY29udHJvbHMpJiZ0aGlzLmNvbmZpZy5jb250cm9scy5pbmNsdWRlcyhcInNldHRpbmdzXCIpJiZ0aGlzLmNvbmZpZy5zZXR0aW5ncy5pbmNsdWRlcyhcImNhcHRpb25zXCIpJiZudC5zZXRDYXB0aW9uc01lbnUuY2FsbCh0aGlzKTtlbHNle2lmKEcodGhpcy5lbGVtZW50cy5jYXB0aW9ucyl8fCh0aGlzLmVsZW1lbnRzLmNhcHRpb25zPWhlKFwiZGl2XCIseWUodGhpcy5jb25maWcuc2VsZWN0b3JzLmNhcHRpb25zKSksZnVuY3Rpb24oZSx0KXtHKGUpJiZHKHQpJiZ0LnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGUsdC5uZXh0U2libGluZyl9KHRoaXMuZWxlbWVudHMuY2FwdGlvbnMsdGhpcy5lbGVtZW50cy53cmFwcGVyKSksb2UuaXNJRSYmd2luZG93LlVSTCl7dmFyIGU9dGhpcy5tZWRpYS5xdWVyeVNlbGVjdG9yQWxsKFwidHJhY2tcIik7QXJyYXkuZnJvbShlKS5mb3JFYWNoKChmdW5jdGlvbihlKXt2YXIgdD1lLmdldEF0dHJpYnV0ZShcInNyY1wiKSxpPWF0KHQpO251bGwhPT1pJiZpLmhvc3RuYW1lIT09d2luZG93LmxvY2F0aW9uLmhyZWYuaG9zdG5hbWUmJltcImh0dHA6XCIsXCJodHRwczpcIl0uaW5jbHVkZXMoaS5wcm90b2NvbCkmJkplKHQsXCJibG9iXCIpLnRoZW4oKGZ1bmN0aW9uKHQpe2Uuc2V0QXR0cmlidXRlKFwic3JjXCIsd2luZG93LlVSTC5jcmVhdGVPYmplY3RVUkwodCkpfSkpLmNhdGNoKChmdW5jdGlvbigpe3BlKGUpfSkpfSkpfXZhciB0PUJlKChuYXZpZ2F0b3IubGFuZ3VhZ2VzfHxbbmF2aWdhdG9yLmxhbmd1YWdlfHxuYXZpZ2F0b3IudXNlckxhbmd1YWdlfHxcImVuXCJdKS5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBlLnNwbGl0KFwiLVwiKVswXX0pKSksaT0odGhpcy5zdG9yYWdlLmdldChcImxhbmd1YWdlXCIpfHx0aGlzLmNvbmZpZy5jYXB0aW9ucy5sYW5ndWFnZXx8XCJhdXRvXCIpLnRvTG93ZXJDYXNlKCk7aWYoXCJhdXRvXCI9PT1pKWk9byh0LDEpWzBdO3ZhciBuPXRoaXMuc3RvcmFnZS5nZXQoXCJjYXB0aW9uc1wiKTtpZihRKG4pfHwobj10aGlzLmNvbmZpZy5jYXB0aW9ucy5hY3RpdmUpLE9iamVjdC5hc3NpZ24odGhpcy5jYXB0aW9ucyx7dG9nZ2xlZDohMSxhY3RpdmU6bixsYW5ndWFnZTppLGxhbmd1YWdlczp0fSksdGhpcy5pc0hUTUw1KXt2YXIgYT10aGlzLmNvbmZpZy5jYXB0aW9ucy51cGRhdGU/XCJhZGR0cmFjayByZW1vdmV0cmFja1wiOlwicmVtb3ZldHJhY2tcIjt4ZS5jYWxsKHRoaXMsdGhpcy5tZWRpYS50ZXh0VHJhY2tzLGEscnQudXBkYXRlLmJpbmQodGhpcykpfXNldFRpbWVvdXQocnQudXBkYXRlLmJpbmQodGhpcyksMCl9fSx1cGRhdGU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLHQ9cnQuZ2V0VHJhY2tzLmNhbGwodGhpcywhMCksaT10aGlzLmNhcHRpb25zLG49aS5hY3RpdmUsYT1pLmxhbmd1YWdlLHM9aS5tZXRhLHI9aS5jdXJyZW50VHJhY2tOb2RlLG89Qm9vbGVhbih0LmZpbmQoKGZ1bmN0aW9uKGUpe3JldHVybiBlLmxhbmd1YWdlPT09YX0pKSk7dGhpcy5pc0hUTUw1JiZ0aGlzLmlzVmlkZW8mJnQuZmlsdGVyKChmdW5jdGlvbihlKXtyZXR1cm4hcy5nZXQoZSl9KSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7ZS5kZWJ1Zy5sb2coXCJUcmFjayBhZGRlZFwiLHQpLHMuc2V0KHQse2RlZmF1bHQ6XCJzaG93aW5nXCI9PT10Lm1vZGV9KSxcInNob3dpbmdcIj09PXQubW9kZSYmKHQubW9kZT1cImhpZGRlblwiKSx4ZS5jYWxsKGUsdCxcImN1ZWNoYW5nZVwiLChmdW5jdGlvbigpe3JldHVybiBydC51cGRhdGVDdWVzLmNhbGwoZSl9KSl9KSksKG8mJnRoaXMubGFuZ3VhZ2UhPT1hfHwhdC5pbmNsdWRlcyhyKSkmJihydC5zZXRMYW5ndWFnZS5jYWxsKHRoaXMsYSkscnQudG9nZ2xlLmNhbGwodGhpcyxuJiZvKSksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5jYXB0aW9ucy5lbmFibGVkLCFhZSh0KSksJCh0aGlzLmNvbmZpZy5jb250cm9scykmJnRoaXMuY29uZmlnLmNvbnRyb2xzLmluY2x1ZGVzKFwic2V0dGluZ3NcIikmJnRoaXMuY29uZmlnLnNldHRpbmdzLmluY2x1ZGVzKFwiY2FwdGlvbnNcIikmJm50LnNldENhcHRpb25zTWVudS5jYWxsKHRoaXMpfSx0b2dnbGU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPSEoYXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0pfHxhcmd1bWVudHNbMV07aWYodGhpcy5zdXBwb3J0ZWQudWkpe3ZhciBuPXRoaXMuY2FwdGlvbnMudG9nZ2xlZCxhPXRoaXMuY29uZmlnLmNsYXNzTmFtZXMuY2FwdGlvbnMuYWN0aXZlLHM9VyhlKT8hbjplO2lmKHMhPT1uKXtpZihpfHwodGhpcy5jYXB0aW9ucy5hY3RpdmU9cyx0aGlzLnN0b3JhZ2Uuc2V0KHtjYXB0aW9uczpzfSkpLCF0aGlzLmxhbmd1YWdlJiZzJiYhaSl7dmFyIHI9cnQuZ2V0VHJhY2tzLmNhbGwodGhpcyksbz1ydC5maW5kVHJhY2suY2FsbCh0aGlzLFt0aGlzLmNhcHRpb25zLmxhbmd1YWdlXS5jb25jYXQobCh0aGlzLmNhcHRpb25zLmxhbmd1YWdlcykpLCEwKTtyZXR1cm4gdGhpcy5jYXB0aW9ucy5sYW5ndWFnZT1vLmxhbmd1YWdlLHZvaWQgcnQuc2V0LmNhbGwodGhpcyxyLmluZGV4T2YobykpfXRoaXMuZWxlbWVudHMuYnV0dG9ucy5jYXB0aW9ucyYmKHRoaXMuZWxlbWVudHMuYnV0dG9ucy5jYXB0aW9ucy5wcmVzc2VkPXMpLGJlKHRoaXMuZWxlbWVudHMuY29udGFpbmVyLGEscyksdGhpcy5jYXB0aW9ucy50b2dnbGVkPXMsbnQudXBkYXRlU2V0dGluZy5jYWxsKHRoaXMsXCJjYXB0aW9uc1wiKSxPZS5jYWxsKHRoaXMsdGhpcy5tZWRpYSxzP1wiY2FwdGlvbnNlbmFibGVkXCI6XCJjYXB0aW9uc2Rpc2FibGVkXCIpfXNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cyYmdC5jYXB0aW9ucy50b2dnbGVkJiYodC5jYXB0aW9ucy5jdXJyZW50VHJhY2tOb2RlLm1vZGU9XCJoaWRkZW5cIil9KSl9fSxzZXQ6ZnVuY3Rpb24oZSl7dmFyIHQ9IShhcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSl8fGFyZ3VtZW50c1sxXSxpPXJ0LmdldFRyYWNrcy5jYWxsKHRoaXMpO2lmKC0xIT09ZSlpZihLKGUpKWlmKGUgaW4gaSl7aWYodGhpcy5jYXB0aW9ucy5jdXJyZW50VHJhY2shPT1lKXt0aGlzLmNhcHRpb25zLmN1cnJlbnRUcmFjaz1lO3ZhciBuPWlbZV0sYT1ufHx7fSxzPWEubGFuZ3VhZ2U7dGhpcy5jYXB0aW9ucy5jdXJyZW50VHJhY2tOb2RlPW4sbnQudXBkYXRlU2V0dGluZy5jYWxsKHRoaXMsXCJjYXB0aW9uc1wiKSx0fHwodGhpcy5jYXB0aW9ucy5sYW5ndWFnZT1zLHRoaXMuc3RvcmFnZS5zZXQoe2xhbmd1YWdlOnN9KSksdGhpcy5pc1ZpbWVvJiZ0aGlzLmVtYmVkLmVuYWJsZVRleHRUcmFjayhzKSxPZS5jYWxsKHRoaXMsdGhpcy5tZWRpYSxcImxhbmd1YWdlY2hhbmdlXCIpfXJ0LnRvZ2dsZS5jYWxsKHRoaXMsITAsdCksdGhpcy5pc0hUTUw1JiZ0aGlzLmlzVmlkZW8mJnJ0LnVwZGF0ZUN1ZXMuY2FsbCh0aGlzKX1lbHNlIHRoaXMuZGVidWcud2FybihcIlRyYWNrIG5vdCBmb3VuZFwiLGUpO2Vsc2UgdGhpcy5kZWJ1Zy53YXJuKFwiSW52YWxpZCBjYXB0aW9uIGFyZ3VtZW50XCIsZSk7ZWxzZSBydC50b2dnbGUuY2FsbCh0aGlzLCExLHQpfSxzZXRMYW5ndWFnZTpmdW5jdGlvbihlKXt2YXIgdD0hKGFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdKXx8YXJndW1lbnRzWzFdO2lmKFkoZSkpe3ZhciBpPWUudG9Mb3dlckNhc2UoKTt0aGlzLmNhcHRpb25zLmxhbmd1YWdlPWk7dmFyIG49cnQuZ2V0VHJhY2tzLmNhbGwodGhpcyksYT1ydC5maW5kVHJhY2suY2FsbCh0aGlzLFtpXSk7cnQuc2V0LmNhbGwodGhpcyxuLmluZGV4T2YoYSksdCl9ZWxzZSB0aGlzLmRlYnVnLndhcm4oXCJJbnZhbGlkIGxhbmd1YWdlIGFyZ3VtZW50XCIsZSl9LGdldFRyYWNrczpmdW5jdGlvbigpe3ZhciBlPXRoaXMsdD1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXSYmYXJndW1lbnRzWzBdLGk9QXJyYXkuZnJvbSgodGhpcy5tZWRpYXx8e30pLnRleHRUcmFja3N8fFtdKTtyZXR1cm4gaS5maWx0ZXIoKGZ1bmN0aW9uKGkpe3JldHVybiFlLmlzSFRNTDV8fHR8fGUuY2FwdGlvbnMubWV0YS5oYXMoaSl9KSkuZmlsdGVyKChmdW5jdGlvbihlKXtyZXR1cm5bXCJjYXB0aW9uc1wiLFwic3VidGl0bGVzXCJdLmluY2x1ZGVzKGUua2luZCl9KSl9LGZpbmRUcmFjazpmdW5jdGlvbihlKXt2YXIgdCxpPXRoaXMsbj1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSYmYXJndW1lbnRzWzFdLGE9cnQuZ2V0VHJhY2tzLmNhbGwodGhpcykscz1mdW5jdGlvbihlKXtyZXR1cm4gTnVtYmVyKChpLmNhcHRpb25zLm1ldGEuZ2V0KGUpfHx7fSkuZGVmYXVsdCl9LHI9QXJyYXkuZnJvbShhKS5zb3J0KChmdW5jdGlvbihlLHQpe3JldHVybiBzKHQpLXMoZSl9KSk7cmV0dXJuIGUuZXZlcnkoKGZ1bmN0aW9uKGUpe3JldHVybiEodD1yLmZpbmQoKGZ1bmN0aW9uKHQpe3JldHVybiB0Lmxhbmd1YWdlPT09ZX0pKSl9KSksdHx8KG4/clswXTp2b2lkIDApfSxnZXRDdXJyZW50VHJhY2s6ZnVuY3Rpb24oKXtyZXR1cm4gcnQuZ2V0VHJhY2tzLmNhbGwodGhpcylbdGhpcy5jdXJyZW50VHJhY2tdfSxnZXRMYWJlbDpmdW5jdGlvbihlKXt2YXIgdD1lO3JldHVybiF0ZSh0KSYmRWUudGV4dFRyYWNrcyYmdGhpcy5jYXB0aW9ucy50b2dnbGVkJiYodD1ydC5nZXRDdXJyZW50VHJhY2suY2FsbCh0aGlzKSksdGUodCk/YWUodC5sYWJlbCk/YWUodC5sYW5ndWFnZSk/WGUoXCJlbmFibGVkXCIsdGhpcy5jb25maWcpOmUubGFuZ3VhZ2UudG9VcHBlckNhc2UoKTp0LmxhYmVsOlhlKFwiZGlzYWJsZWRcIix0aGlzLmNvbmZpZyl9LHVwZGF0ZUN1ZXM6ZnVuY3Rpb24oZSl7aWYodGhpcy5zdXBwb3J0ZWQudWkpaWYoRyh0aGlzLmVsZW1lbnRzLmNhcHRpb25zKSlpZihXKGUpfHxBcnJheS5pc0FycmF5KGUpKXt2YXIgdD1lO2lmKCF0KXt2YXIgaT1ydC5nZXRDdXJyZW50VHJhY2suY2FsbCh0aGlzKTt0PUFycmF5LmZyb20oKGl8fHt9KS5hY3RpdmVDdWVzfHxbXSkubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS5nZXRDdWVBc0hUTUwoKX0pKS5tYXAoWWUpfXZhciBuPXQubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS50cmltKCl9KSkuam9pbihcIlxcblwiKTtpZihuIT09dGhpcy5lbGVtZW50cy5jYXB0aW9ucy5pbm5lckhUTUwpe2ZlKHRoaXMuZWxlbWVudHMuY2FwdGlvbnMpO3ZhciBhPWhlKFwic3BhblwiLHllKHRoaXMuY29uZmlnLnNlbGVjdG9ycy5jYXB0aW9uKSk7YS5pbm5lckhUTUw9bix0aGlzLmVsZW1lbnRzLmNhcHRpb25zLmFwcGVuZENoaWxkKGEpLE9lLmNhbGwodGhpcyx0aGlzLm1lZGlhLFwiY3VlY2hhbmdlXCIpfX1lbHNlIHRoaXMuZGVidWcud2FybihcInVwZGF0ZUN1ZXM6IEludmFsaWQgaW5wdXRcIixlKTtlbHNlIHRoaXMuZGVidWcud2FybihcIk5vIGNhcHRpb25zIGVsZW1lbnQgdG8gcmVuZGVyIHRvXCIpfX0sb3Q9e2VuYWJsZWQ6ITAsdGl0bGU6XCJcIixkZWJ1ZzohMSxhdXRvcGxheTohMSxhdXRvcGF1c2U6ITAscGxheXNpbmxpbmU6ITAsc2Vla1RpbWU6MTAsdm9sdW1lOjEsbXV0ZWQ6ITEsZHVyYXRpb246bnVsbCxkaXNwbGF5RHVyYXRpb246ITAsaW52ZXJ0VGltZTohMCx0b2dnbGVJbnZlcnQ6ITAscmF0aW86bnVsbCxjbGlja1RvUGxheTohMCxoaWRlQ29udHJvbHM6ITAscmVzZXRPbkVuZDohMSxkaXNhYmxlQ29udGV4dE1lbnU6ITAsbG9hZFNwcml0ZTohMCxpY29uUHJlZml4OlwicGx5clwiLGljb25Vcmw6XCJodHRwczovL2Nkbi5wbHlyLmlvLzMuNi4zL3BseXIuc3ZnXCIsYmxhbmtWaWRlbzpcImh0dHBzOi8vY2RuLnBseXIuaW8vc3RhdGljL2JsYW5rLm1wNFwiLHF1YWxpdHk6e2RlZmF1bHQ6NTc2LG9wdGlvbnM6WzQzMjAsMjg4MCwyMTYwLDE0NDAsMTA4MCw3MjAsNTc2LDQ4MCwzNjAsMjQwXSxmb3JjZWQ6ITEsb25DaGFuZ2U6bnVsbH0sbG9vcDp7YWN0aXZlOiExfSxzcGVlZDp7c2VsZWN0ZWQ6MSxvcHRpb25zOlsuNSwuNzUsMSwxLjI1LDEuNSwxLjc1LDIsNF19LGtleWJvYXJkOntmb2N1c2VkOiEwLGdsb2JhbDohMX0sdG9vbHRpcHM6e2NvbnRyb2xzOiExLHNlZWs6ITB9LGNhcHRpb25zOnthY3RpdmU6ITEsbGFuZ3VhZ2U6XCJhdXRvXCIsdXBkYXRlOiExfSxmdWxsc2NyZWVuOntlbmFibGVkOiEwLGZhbGxiYWNrOiEwLGlvc05hdGl2ZTohMX0sc3RvcmFnZTp7ZW5hYmxlZDohMCxrZXk6XCJwbHlyXCJ9LGNvbnRyb2xzOltcInBsYXktbGFyZ2VcIixcInBsYXlcIixcInByb2dyZXNzXCIsXCJjdXJyZW50LXRpbWVcIixcIm11dGVcIixcInZvbHVtZVwiLFwiY2FwdGlvbnNcIixcInNldHRpbmdzXCIsXCJwaXBcIixcImFpcnBsYXlcIixcImZ1bGxzY3JlZW5cIl0sc2V0dGluZ3M6W1wiY2FwdGlvbnNcIixcInF1YWxpdHlcIixcInNwZWVkXCJdLGkxOG46e3Jlc3RhcnQ6XCJSZXN0YXJ0XCIscmV3aW5kOlwiUmV3aW5kIHtzZWVrdGltZX1zXCIscGxheTpcIlBsYXlcIixwYXVzZTpcIlBhdXNlXCIsZmFzdEZvcndhcmQ6XCJGb3J3YXJkIHtzZWVrdGltZX1zXCIsc2VlazpcIlNlZWtcIixzZWVrTGFiZWw6XCJ7Y3VycmVudFRpbWV9IG9mIHtkdXJhdGlvbn1cIixwbGF5ZWQ6XCJQbGF5ZWRcIixidWZmZXJlZDpcIkJ1ZmZlcmVkXCIsY3VycmVudFRpbWU6XCJDdXJyZW50IHRpbWVcIixkdXJhdGlvbjpcIkR1cmF0aW9uXCIsdm9sdW1lOlwiVm9sdW1lXCIsbXV0ZTpcIk11dGVcIix1bm11dGU6XCJVbm11dGVcIixlbmFibGVDYXB0aW9uczpcIkVuYWJsZSBjYXB0aW9uc1wiLGRpc2FibGVDYXB0aW9uczpcIkRpc2FibGUgY2FwdGlvbnNcIixkb3dubG9hZDpcIkRvd25sb2FkXCIsZW50ZXJGdWxsc2NyZWVuOlwiRW50ZXIgZnVsbHNjcmVlblwiLGV4aXRGdWxsc2NyZWVuOlwiRXhpdCBmdWxsc2NyZWVuXCIsZnJhbWVUaXRsZTpcIlBsYXllciBmb3Ige3RpdGxlfVwiLGNhcHRpb25zOlwiQ2FwdGlvbnNcIixzZXR0aW5nczpcIlNldHRpbmdzXCIscGlwOlwiUElQXCIsbWVudUJhY2s6XCJHbyBiYWNrIHRvIHByZXZpb3VzIG1lbnVcIixzcGVlZDpcIlNwZWVkXCIsbm9ybWFsOlwiTm9ybWFsXCIscXVhbGl0eTpcIlF1YWxpdHlcIixsb29wOlwiTG9vcFwiLHN0YXJ0OlwiU3RhcnRcIixlbmQ6XCJFbmRcIixhbGw6XCJBbGxcIixyZXNldDpcIlJlc2V0XCIsZGlzYWJsZWQ6XCJEaXNhYmxlZFwiLGVuYWJsZWQ6XCJFbmFibGVkXCIsYWR2ZXJ0aXNlbWVudDpcIkFkXCIscXVhbGl0eUJhZGdlOnsyMTYwOlwiNEtcIiwxNDQwOlwiSERcIiwxMDgwOlwiSERcIiw3MjA6XCJIRFwiLDU3NjpcIlNEXCIsNDgwOlwiU0RcIn19LHVybHM6e2Rvd25sb2FkOm51bGwsdmltZW86e3NkazpcImh0dHBzOi8vcGxheWVyLnZpbWVvLmNvbS9hcGkvcGxheWVyLmpzXCIsaWZyYW1lOlwiaHR0cHM6Ly9wbGF5ZXIudmltZW8uY29tL3ZpZGVvL3swfT97MX1cIixhcGk6XCJodHRwczovL3ZpbWVvLmNvbS9hcGkvb2VtYmVkLmpzb24/dXJsPXswfVwifSx5b3V0dWJlOntzZGs6XCJodHRwczovL3d3dy55b3V0dWJlLmNvbS9pZnJhbWVfYXBpXCIsYXBpOlwiaHR0cHM6Ly9ub2VtYmVkLmNvbS9lbWJlZD91cmw9aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj17MH1cIn0sZ29vZ2xlSU1BOntzZGs6XCJodHRwczovL2ltYXNkay5nb29nbGVhcGlzLmNvbS9qcy9zZGtsb2FkZXIvaW1hMy5qc1wifX0sbGlzdGVuZXJzOntzZWVrOm51bGwscGxheTpudWxsLHBhdXNlOm51bGwscmVzdGFydDpudWxsLHJld2luZDpudWxsLGZhc3RGb3J3YXJkOm51bGwsbXV0ZTpudWxsLHZvbHVtZTpudWxsLGNhcHRpb25zOm51bGwsZG93bmxvYWQ6bnVsbCxmdWxsc2NyZWVuOm51bGwscGlwOm51bGwsYWlycGxheTpudWxsLHNwZWVkOm51bGwscXVhbGl0eTpudWxsLGxvb3A6bnVsbCxsYW5ndWFnZTpudWxsfSxldmVudHM6W1wiZW5kZWRcIixcInByb2dyZXNzXCIsXCJzdGFsbGVkXCIsXCJwbGF5aW5nXCIsXCJ3YWl0aW5nXCIsXCJjYW5wbGF5XCIsXCJjYW5wbGF5dGhyb3VnaFwiLFwibG9hZHN0YXJ0XCIsXCJsb2FkZWRkYXRhXCIsXCJsb2FkZWRtZXRhZGF0YVwiLFwidGltZXVwZGF0ZVwiLFwidm9sdW1lY2hhbmdlXCIsXCJwbGF5XCIsXCJwYXVzZVwiLFwiZXJyb3JcIixcInNlZWtpbmdcIixcInNlZWtlZFwiLFwiZW1wdGllZFwiLFwicmF0ZWNoYW5nZVwiLFwiY3VlY2hhbmdlXCIsXCJkb3dubG9hZFwiLFwiZW50ZXJmdWxsc2NyZWVuXCIsXCJleGl0ZnVsbHNjcmVlblwiLFwiY2FwdGlvbnNlbmFibGVkXCIsXCJjYXB0aW9uc2Rpc2FibGVkXCIsXCJsYW5ndWFnZWNoYW5nZVwiLFwiY29udHJvbHNoaWRkZW5cIixcImNvbnRyb2xzc2hvd25cIixcInJlYWR5XCIsXCJzdGF0ZWNoYW5nZVwiLFwicXVhbGl0eWNoYW5nZVwiLFwiYWRzbG9hZGVkXCIsXCJhZHNjb250ZW50cGF1c2VcIixcImFkc2NvbnRlbnRyZXN1bWVcIixcImFkc3RhcnRlZFwiLFwiYWRzbWlkcG9pbnRcIixcImFkc2NvbXBsZXRlXCIsXCJhZHNhbGxjb21wbGV0ZVwiLFwiYWRzaW1wcmVzc2lvblwiLFwiYWRzY2xpY2tcIl0sc2VsZWN0b3JzOntlZGl0YWJsZTpcImlucHV0LCB0ZXh0YXJlYSwgc2VsZWN0LCBbY29udGVudGVkaXRhYmxlXVwiLGNvbnRhaW5lcjpcIi5wbHlyXCIsY29udHJvbHM6e2NvbnRhaW5lcjpudWxsLHdyYXBwZXI6XCIucGx5cl9fY29udHJvbHNcIn0sbGFiZWxzOlwiW2RhdGEtcGx5cl1cIixidXR0b25zOntwbGF5OidbZGF0YS1wbHlyPVwicGxheVwiXScscGF1c2U6J1tkYXRhLXBseXI9XCJwYXVzZVwiXScscmVzdGFydDonW2RhdGEtcGx5cj1cInJlc3RhcnRcIl0nLHJld2luZDonW2RhdGEtcGx5cj1cInJld2luZFwiXScsZmFzdEZvcndhcmQ6J1tkYXRhLXBseXI9XCJmYXN0LWZvcndhcmRcIl0nLG11dGU6J1tkYXRhLXBseXI9XCJtdXRlXCJdJyxjYXB0aW9uczonW2RhdGEtcGx5cj1cImNhcHRpb25zXCJdJyxkb3dubG9hZDonW2RhdGEtcGx5cj1cImRvd25sb2FkXCJdJyxmdWxsc2NyZWVuOidbZGF0YS1wbHlyPVwiZnVsbHNjcmVlblwiXScscGlwOidbZGF0YS1wbHlyPVwicGlwXCJdJyxhaXJwbGF5OidbZGF0YS1wbHlyPVwiYWlycGxheVwiXScsc2V0dGluZ3M6J1tkYXRhLXBseXI9XCJzZXR0aW5nc1wiXScsbG9vcDonW2RhdGEtcGx5cj1cImxvb3BcIl0nfSxpbnB1dHM6e3NlZWs6J1tkYXRhLXBseXI9XCJzZWVrXCJdJyx2b2x1bWU6J1tkYXRhLXBseXI9XCJ2b2x1bWVcIl0nLHNwZWVkOidbZGF0YS1wbHlyPVwic3BlZWRcIl0nLGxhbmd1YWdlOidbZGF0YS1wbHlyPVwibGFuZ3VhZ2VcIl0nLHF1YWxpdHk6J1tkYXRhLXBseXI9XCJxdWFsaXR5XCJdJ30sZGlzcGxheTp7Y3VycmVudFRpbWU6XCIucGx5cl9fdGltZS0tY3VycmVudFwiLGR1cmF0aW9uOlwiLnBseXJfX3RpbWUtLWR1cmF0aW9uXCIsYnVmZmVyOlwiLnBseXJfX3Byb2dyZXNzX19idWZmZXJcIixsb29wOlwiLnBseXJfX3Byb2dyZXNzX19sb29wXCIsdm9sdW1lOlwiLnBseXJfX3ZvbHVtZS0tZGlzcGxheVwifSxwcm9ncmVzczpcIi5wbHlyX19wcm9ncmVzc1wiLGNhcHRpb25zOlwiLnBseXJfX2NhcHRpb25zXCIsY2FwdGlvbjpcIi5wbHlyX19jYXB0aW9uXCJ9LGNsYXNzTmFtZXM6e3R5cGU6XCJwbHlyLS17MH1cIixwcm92aWRlcjpcInBseXItLXswfVwiLHZpZGVvOlwicGx5cl9fdmlkZW8td3JhcHBlclwiLGVtYmVkOlwicGx5cl9fdmlkZW8tZW1iZWRcIix2aWRlb0ZpeGVkUmF0aW86XCJwbHlyX192aWRlby13cmFwcGVyLS1maXhlZC1yYXRpb1wiLGVtYmVkQ29udGFpbmVyOlwicGx5cl9fdmlkZW8tZW1iZWRfX2NvbnRhaW5lclwiLHBvc3RlcjpcInBseXJfX3Bvc3RlclwiLHBvc3RlckVuYWJsZWQ6XCJwbHlyX19wb3N0ZXItZW5hYmxlZFwiLGFkczpcInBseXJfX2Fkc1wiLGNvbnRyb2w6XCJwbHlyX19jb250cm9sXCIsY29udHJvbFByZXNzZWQ6XCJwbHlyX19jb250cm9sLS1wcmVzc2VkXCIscGxheWluZzpcInBseXItLXBsYXlpbmdcIixwYXVzZWQ6XCJwbHlyLS1wYXVzZWRcIixzdG9wcGVkOlwicGx5ci0tc3RvcHBlZFwiLGxvYWRpbmc6XCJwbHlyLS1sb2FkaW5nXCIsaG92ZXI6XCJwbHlyLS1ob3ZlclwiLHRvb2x0aXA6XCJwbHlyX190b29sdGlwXCIsY3VlczpcInBseXJfX2N1ZXNcIixoaWRkZW46XCJwbHlyX19zci1vbmx5XCIsaGlkZUNvbnRyb2xzOlwicGx5ci0taGlkZS1jb250cm9sc1wiLGlzSW9zOlwicGx5ci0taXMtaW9zXCIsaXNUb3VjaDpcInBseXItLWlzLXRvdWNoXCIsdWlTdXBwb3J0ZWQ6XCJwbHlyLS1mdWxsLXVpXCIsbm9UcmFuc2l0aW9uOlwicGx5ci0tbm8tdHJhbnNpdGlvblwiLGRpc3BsYXk6e3RpbWU6XCJwbHlyX190aW1lXCJ9LG1lbnU6e3ZhbHVlOlwicGx5cl9fbWVudV9fdmFsdWVcIixiYWRnZTpcInBseXJfX2JhZGdlXCIsb3BlbjpcInBseXItLW1lbnUtb3BlblwifSxjYXB0aW9uczp7ZW5hYmxlZDpcInBseXItLWNhcHRpb25zLWVuYWJsZWRcIixhY3RpdmU6XCJwbHlyLS1jYXB0aW9ucy1hY3RpdmVcIn0sZnVsbHNjcmVlbjp7ZW5hYmxlZDpcInBseXItLWZ1bGxzY3JlZW4tZW5hYmxlZFwiLGZhbGxiYWNrOlwicGx5ci0tZnVsbHNjcmVlbi1mYWxsYmFja1wifSxwaXA6e3N1cHBvcnRlZDpcInBseXItLXBpcC1zdXBwb3J0ZWRcIixhY3RpdmU6XCJwbHlyLS1waXAtYWN0aXZlXCJ9LGFpcnBsYXk6e3N1cHBvcnRlZDpcInBseXItLWFpcnBsYXktc3VwcG9ydGVkXCIsYWN0aXZlOlwicGx5ci0tYWlycGxheS1hY3RpdmVcIn0sdGFiRm9jdXM6XCJwbHlyX190YWItZm9jdXNcIixwcmV2aWV3VGh1bWJuYWlsczp7dGh1bWJDb250YWluZXI6XCJwbHlyX19wcmV2aWV3LXRodW1iXCIsdGh1bWJDb250YWluZXJTaG93bjpcInBseXJfX3ByZXZpZXctdGh1bWItLWlzLXNob3duXCIsaW1hZ2VDb250YWluZXI6XCJwbHlyX19wcmV2aWV3LXRodW1iX19pbWFnZS1jb250YWluZXJcIix0aW1lQ29udGFpbmVyOlwicGx5cl9fcHJldmlldy10aHVtYl9fdGltZS1jb250YWluZXJcIixzY3J1YmJpbmdDb250YWluZXI6XCJwbHlyX19wcmV2aWV3LXNjcnViYmluZ1wiLHNjcnViYmluZ0NvbnRhaW5lclNob3duOlwicGx5cl9fcHJldmlldy1zY3J1YmJpbmctLWlzLXNob3duXCJ9fSxhdHRyaWJ1dGVzOntlbWJlZDp7cHJvdmlkZXI6XCJkYXRhLXBseXItcHJvdmlkZXJcIixpZDpcImRhdGEtcGx5ci1lbWJlZC1pZFwifX0sYWRzOntlbmFibGVkOiExLHB1Ymxpc2hlcklkOlwiXCIsdGFnVXJsOlwiXCJ9LHByZXZpZXdUaHVtYm5haWxzOntlbmFibGVkOiExLHNyYzpcIlwifSx2aW1lbzp7YnlsaW5lOiExLHBvcnRyYWl0OiExLHRpdGxlOiExLHNwZWVkOiEwLHRyYW5zcGFyZW50OiExLGN1c3RvbUNvbnRyb2xzOiEwLHJlZmVycmVyUG9saWN5Om51bGwscHJlbWl1bTohMX0seW91dHViZTp7cmVsOjAsc2hvd2luZm86MCxpdl9sb2FkX3BvbGljeTozLG1vZGVzdGJyYW5kaW5nOjEsY3VzdG9tQ29udHJvbHM6ITAsbm9Db29raWU6ITF9fSxsdD1cInBpY3R1cmUtaW4tcGljdHVyZVwiLGN0PVwiaW5saW5lXCIsdXQ9e2h0bWw1OlwiaHRtbDVcIix5b3V0dWJlOlwieW91dHViZVwiLHZpbWVvOlwidmltZW9cIn0sZHQ9XCJhdWRpb1wiLGh0PVwidmlkZW9cIjt2YXIgbXQ9ZnVuY3Rpb24oKXt9LHB0PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gdCgpe3ZhciBpPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdJiZhcmd1bWVudHNbMF07ZSh0aGlzLHQpLHRoaXMuZW5hYmxlZD13aW5kb3cuY29uc29sZSYmaSx0aGlzLmVuYWJsZWQmJnRoaXMubG9nKFwiRGVidWdnaW5nIGVuYWJsZWRcIil9cmV0dXJuIGkodCxbe2tleTpcImxvZ1wiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmVuYWJsZWQ/RnVuY3Rpb24ucHJvdG90eXBlLmJpbmQuY2FsbChjb25zb2xlLmxvZyxjb25zb2xlKTptdH19LHtrZXk6XCJ3YXJuXCIsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuZW5hYmxlZD9GdW5jdGlvbi5wcm90b3R5cGUuYmluZC5jYWxsKGNvbnNvbGUud2Fybixjb25zb2xlKTptdH19LHtrZXk6XCJlcnJvclwiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmVuYWJsZWQ/RnVuY3Rpb24ucHJvdG90eXBlLmJpbmQuY2FsbChjb25zb2xlLmVycm9yLGNvbnNvbGUpOm10fX1dKSx0fSgpLGZ0PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gdChpKXt2YXIgbj10aGlzO2UodGhpcyx0KSx0aGlzLnBsYXllcj1pLHRoaXMucHJlZml4PXQucHJlZml4LHRoaXMucHJvcGVydHk9dC5wcm9wZXJ0eSx0aGlzLnNjcm9sbFBvc2l0aW9uPXt4OjAseTowfSx0aGlzLmZvcmNlRmFsbGJhY2s9XCJmb3JjZVwiPT09aS5jb25maWcuZnVsbHNjcmVlbi5mYWxsYmFjayx0aGlzLnBsYXllci5lbGVtZW50cy5mdWxsc2NyZWVuPWkuY29uZmlnLmZ1bGxzY3JlZW4uY29udGFpbmVyJiZmdW5jdGlvbihlLHQpe3JldHVybihFbGVtZW50LnByb3RvdHlwZS5jbG9zZXN0fHxmdW5jdGlvbigpe3ZhciBlPXRoaXM7ZG97aWYoa2UubWF0Y2hlcyhlLHQpKXJldHVybiBlO2U9ZS5wYXJlbnRFbGVtZW50fHxlLnBhcmVudE5vZGV9d2hpbGUobnVsbCE9PWUmJjE9PT1lLm5vZGVUeXBlKTtyZXR1cm4gbnVsbH0pLmNhbGwoZSx0KX0odGhpcy5wbGF5ZXIuZWxlbWVudHMuY29udGFpbmVyLGkuY29uZmlnLmZ1bGxzY3JlZW4uY29udGFpbmVyKSx4ZS5jYWxsKHRoaXMucGxheWVyLGRvY3VtZW50LFwibXNcIj09PXRoaXMucHJlZml4P1wiTVNGdWxsc2NyZWVuQ2hhbmdlXCI6XCJcIi5jb25jYXQodGhpcy5wcmVmaXgsXCJmdWxsc2NyZWVuY2hhbmdlXCIpLChmdW5jdGlvbigpe24ub25DaGFuZ2UoKX0pKSx4ZS5jYWxsKHRoaXMucGxheWVyLHRoaXMucGxheWVyLmVsZW1lbnRzLmNvbnRhaW5lcixcImRibGNsaWNrXCIsKGZ1bmN0aW9uKGUpe0cobi5wbGF5ZXIuZWxlbWVudHMuY29udHJvbHMpJiZuLnBsYXllci5lbGVtZW50cy5jb250cm9scy5jb250YWlucyhlLnRhcmdldCl8fG4ucGxheWVyLmxpc3RlbmVycy5wcm94eShlLG4udG9nZ2xlLFwiZnVsbHNjcmVlblwiKX0pKSx4ZS5jYWxsKHRoaXMsdGhpcy5wbGF5ZXIuZWxlbWVudHMuY29udGFpbmVyLFwia2V5ZG93blwiLChmdW5jdGlvbihlKXtyZXR1cm4gbi50cmFwRm9jdXMoZSl9KSksdGhpcy51cGRhdGUoKX1yZXR1cm4gaSh0LFt7a2V5Olwib25DaGFuZ2VcIix2YWx1ZTpmdW5jdGlvbigpe2lmKHRoaXMuZW5hYmxlZCl7dmFyIGU9dGhpcy5wbGF5ZXIuZWxlbWVudHMuYnV0dG9ucy5mdWxsc2NyZWVuO0coZSkmJihlLnByZXNzZWQ9dGhpcy5hY3RpdmUpO3ZhciB0PXRoaXMudGFyZ2V0PT09dGhpcy5wbGF5ZXIubWVkaWE/dGhpcy50YXJnZXQ6dGhpcy5wbGF5ZXIuZWxlbWVudHMuY29udGFpbmVyO09lLmNhbGwodGhpcy5wbGF5ZXIsdCx0aGlzLmFjdGl2ZT9cImVudGVyZnVsbHNjcmVlblwiOlwiZXhpdGZ1bGxzY3JlZW5cIiwhMCl9fX0se2tleTpcInRvZ2dsZUZhbGxiYWNrXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXSYmYXJndW1lbnRzWzBdO2lmKGU/dGhpcy5zY3JvbGxQb3NpdGlvbj17eDp3aW5kb3cuc2Nyb2xsWHx8MCx5OndpbmRvdy5zY3JvbGxZfHwwfTp3aW5kb3cuc2Nyb2xsVG8odGhpcy5zY3JvbGxQb3NpdGlvbi54LHRoaXMuc2Nyb2xsUG9zaXRpb24ueSksZG9jdW1lbnQuYm9keS5zdHlsZS5vdmVyZmxvdz1lP1wiaGlkZGVuXCI6XCJcIixiZSh0aGlzLnRhcmdldCx0aGlzLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5mdWxsc2NyZWVuLmZhbGxiYWNrLGUpLG9lLmlzSW9zKXt2YXIgdD1kb2N1bWVudC5oZWFkLnF1ZXJ5U2VsZWN0b3IoJ21ldGFbbmFtZT1cInZpZXdwb3J0XCJdJyksaT1cInZpZXdwb3J0LWZpdD1jb3ZlclwiO3R8fCh0PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJtZXRhXCIpKS5zZXRBdHRyaWJ1dGUoXCJuYW1lXCIsXCJ2aWV3cG9ydFwiKTt2YXIgbj1ZKHQuY29udGVudCkmJnQuY29udGVudC5pbmNsdWRlcyhpKTtlPyh0aGlzLmNsZWFudXBWaWV3cG9ydD0hbixufHwodC5jb250ZW50Kz1cIixcIi5jb25jYXQoaSkpKTp0aGlzLmNsZWFudXBWaWV3cG9ydCYmKHQuY29udGVudD10LmNvbnRlbnQuc3BsaXQoXCIsXCIpLmZpbHRlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGUudHJpbSgpIT09aX0pKS5qb2luKFwiLFwiKSl9dGhpcy5vbkNoYW5nZSgpfX0se2tleTpcInRyYXBGb2N1c1wiLHZhbHVlOmZ1bmN0aW9uKGUpe2lmKCFvZS5pc0lvcyYmdGhpcy5hY3RpdmUmJlwiVGFiXCI9PT1lLmtleSYmOT09PWUua2V5Q29kZSl7dmFyIHQ9ZG9jdW1lbnQuYWN0aXZlRWxlbWVudCxpPVRlLmNhbGwodGhpcy5wbGF5ZXIsXCJhW2hyZWZdLCBidXR0b246bm90KDpkaXNhYmxlZCksIGlucHV0Om5vdCg6ZGlzYWJsZWQpLCBbdGFiaW5kZXhdXCIpLG49byhpLDEpWzBdLGE9aVtpLmxlbmd0aC0xXTt0IT09YXx8ZS5zaGlmdEtleT90PT09biYmZS5zaGlmdEtleSYmKGEuZm9jdXMoKSxlLnByZXZlbnREZWZhdWx0KCkpOihuLmZvY3VzKCksZS5wcmV2ZW50RGVmYXVsdCgpKX19fSx7a2V5OlwidXBkYXRlXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZTt0aGlzLmVuYWJsZWQ/KGU9dGhpcy5mb3JjZUZhbGxiYWNrP1wiRmFsbGJhY2sgKGZvcmNlZClcIjp0Lm5hdGl2ZT9cIk5hdGl2ZVwiOlwiRmFsbGJhY2tcIix0aGlzLnBsYXllci5kZWJ1Zy5sb2coXCJcIi5jb25jYXQoZSxcIiBmdWxsc2NyZWVuIGVuYWJsZWRcIikpKTp0aGlzLnBsYXllci5kZWJ1Zy5sb2coXCJGdWxsc2NyZWVuIG5vdCBzdXBwb3J0ZWQgYW5kIGZhbGxiYWNrIGRpc2FibGVkXCIpO2JlKHRoaXMucGxheWVyLmVsZW1lbnRzLmNvbnRhaW5lcix0aGlzLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5mdWxsc2NyZWVuLmVuYWJsZWQsdGhpcy5lbmFibGVkKX19LHtrZXk6XCJlbnRlclwiLHZhbHVlOmZ1bmN0aW9uKCl7dGhpcy5lbmFibGVkJiYob2UuaXNJb3MmJnRoaXMucGxheWVyLmNvbmZpZy5mdWxsc2NyZWVuLmlvc05hdGl2ZT90aGlzLnRhcmdldC53ZWJraXRFbnRlckZ1bGxzY3JlZW4oKTohdC5uYXRpdmV8fHRoaXMuZm9yY2VGYWxsYmFjaz90aGlzLnRvZ2dsZUZhbGxiYWNrKCEwKTp0aGlzLnByZWZpeD9hZSh0aGlzLnByZWZpeCl8fHRoaXMudGFyZ2V0W1wiXCIuY29uY2F0KHRoaXMucHJlZml4LFwiUmVxdWVzdFwiKS5jb25jYXQodGhpcy5wcm9wZXJ0eSldKCk6dGhpcy50YXJnZXQucmVxdWVzdEZ1bGxzY3JlZW4oe25hdmlnYXRpb25VSTpcImhpZGVcIn0pKX19LHtrZXk6XCJleGl0XCIsdmFsdWU6ZnVuY3Rpb24oKXtpZih0aGlzLmVuYWJsZWQpaWYob2UuaXNJb3MmJnRoaXMucGxheWVyLmNvbmZpZy5mdWxsc2NyZWVuLmlvc05hdGl2ZSl0aGlzLnRhcmdldC53ZWJraXRFeGl0RnVsbHNjcmVlbigpLERlKHRoaXMucGxheWVyLnBsYXkoKSk7ZWxzZSBpZighdC5uYXRpdmV8fHRoaXMuZm9yY2VGYWxsYmFjayl0aGlzLnRvZ2dsZUZhbGxiYWNrKCExKTtlbHNlIGlmKHRoaXMucHJlZml4KXtpZighYWUodGhpcy5wcmVmaXgpKXt2YXIgZT1cIm1velwiPT09dGhpcy5wcmVmaXg/XCJDYW5jZWxcIjpcIkV4aXRcIjtkb2N1bWVudFtcIlwiLmNvbmNhdCh0aGlzLnByZWZpeCkuY29uY2F0KGUpLmNvbmNhdCh0aGlzLnByb3BlcnR5KV0oKX19ZWxzZShkb2N1bWVudC5jYW5jZWxGdWxsU2NyZWVufHxkb2N1bWVudC5leGl0RnVsbHNjcmVlbikuY2FsbChkb2N1bWVudCl9fSx7a2V5OlwidG9nZ2xlXCIsdmFsdWU6ZnVuY3Rpb24oKXt0aGlzLmFjdGl2ZT90aGlzLmV4aXQoKTp0aGlzLmVudGVyKCl9fSx7a2V5OlwidXNpbmdOYXRpdmVcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdC5uYXRpdmUmJiF0aGlzLmZvcmNlRmFsbGJhY2t9fSx7a2V5OlwiZW5hYmxlZFwiLGdldDpmdW5jdGlvbigpe3JldHVybih0Lm5hdGl2ZXx8dGhpcy5wbGF5ZXIuY29uZmlnLmZ1bGxzY3JlZW4uZmFsbGJhY2spJiZ0aGlzLnBsYXllci5jb25maWcuZnVsbHNjcmVlbi5lbmFibGVkJiZ0aGlzLnBsYXllci5zdXBwb3J0ZWQudWkmJnRoaXMucGxheWVyLmlzVmlkZW99fSx7a2V5OlwiYWN0aXZlXCIsZ2V0OmZ1bmN0aW9uKCl7aWYoIXRoaXMuZW5hYmxlZClyZXR1cm4hMTtpZighdC5uYXRpdmV8fHRoaXMuZm9yY2VGYWxsYmFjaylyZXR1cm4gd2UodGhpcy50YXJnZXQsdGhpcy5wbGF5ZXIuY29uZmlnLmNsYXNzTmFtZXMuZnVsbHNjcmVlbi5mYWxsYmFjayk7dmFyIGU9dGhpcy5wcmVmaXg/ZG9jdW1lbnRbXCJcIi5jb25jYXQodGhpcy5wcmVmaXgpLmNvbmNhdCh0aGlzLnByb3BlcnR5LFwiRWxlbWVudFwiKV06ZG9jdW1lbnQuZnVsbHNjcmVlbkVsZW1lbnQ7cmV0dXJuIGUmJmUuc2hhZG93Um9vdD9lPT09dGhpcy50YXJnZXQuZ2V0Um9vdE5vZGUoKS5ob3N0OmU9PT10aGlzLnRhcmdldH19LHtrZXk6XCJ0YXJnZXRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gb2UuaXNJb3MmJnRoaXMucGxheWVyLmNvbmZpZy5mdWxsc2NyZWVuLmlvc05hdGl2ZT90aGlzLnBsYXllci5tZWRpYTp0aGlzLnBsYXllci5lbGVtZW50cy5mdWxsc2NyZWVufHx0aGlzLnBsYXllci5lbGVtZW50cy5jb250YWluZXJ9fV0sW3trZXk6XCJuYXRpdmVcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4hIShkb2N1bWVudC5mdWxsc2NyZWVuRW5hYmxlZHx8ZG9jdW1lbnQud2Via2l0RnVsbHNjcmVlbkVuYWJsZWR8fGRvY3VtZW50Lm1vekZ1bGxTY3JlZW5FbmFibGVkfHxkb2N1bWVudC5tc0Z1bGxzY3JlZW5FbmFibGVkKX19LHtrZXk6XCJwcmVmaXhcIixnZXQ6ZnVuY3Rpb24oKXtpZihYKGRvY3VtZW50LmV4aXRGdWxsc2NyZWVuKSlyZXR1cm5cIlwiO3ZhciBlPVwiXCI7cmV0dXJuW1wid2Via2l0XCIsXCJtb3pcIixcIm1zXCJdLnNvbWUoKGZ1bmN0aW9uKHQpe3JldHVybiEoIVgoZG9jdW1lbnRbXCJcIi5jb25jYXQodCxcIkV4aXRGdWxsc2NyZWVuXCIpXSkmJiFYKGRvY3VtZW50W1wiXCIuY29uY2F0KHQsXCJDYW5jZWxGdWxsU2NyZWVuXCIpXSkpJiYoZT10LCEwKX0pKSxlfX0se2tleTpcInByb3BlcnR5XCIsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuXCJtb3pcIj09PXRoaXMucHJlZml4P1wiRnVsbFNjcmVlblwiOlwiRnVsbHNjcmVlblwifX1dKSx0fSgpO2Z1bmN0aW9uIGd0KGUpe3ZhciB0PWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdP2FyZ3VtZW50c1sxXToxO3JldHVybiBuZXcgUHJvbWlzZSgoZnVuY3Rpb24oaSxuKXt2YXIgYT1uZXcgSW1hZ2Uscz1mdW5jdGlvbigpe2RlbGV0ZSBhLm9ubG9hZCxkZWxldGUgYS5vbmVycm9yLChhLm5hdHVyYWxXaWR0aD49dD9pOm4pKGEpfTtPYmplY3QuYXNzaWduKGEse29ubG9hZDpzLG9uZXJyb3I6cyxzcmM6ZX0pfSkpfXZhciB5dD17YWRkU3R5bGVIb29rOmZ1bmN0aW9uKCl7YmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuc2VsZWN0b3JzLmNvbnRhaW5lci5yZXBsYWNlKFwiLlwiLFwiXCIpLCEwKSxiZSh0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcix0aGlzLmNvbmZpZy5jbGFzc05hbWVzLnVpU3VwcG9ydGVkLHRoaXMuc3VwcG9ydGVkLnVpKX0sdG9nZ2xlTmF0aXZlQ29udHJvbHM6ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXSYmYXJndW1lbnRzWzBdO2UmJnRoaXMuaXNIVE1MNT90aGlzLm1lZGlhLnNldEF0dHJpYnV0ZShcImNvbnRyb2xzXCIsXCJcIik6dGhpcy5tZWRpYS5yZW1vdmVBdHRyaWJ1dGUoXCJjb250cm9sc1wiKX0sYnVpbGQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO2lmKHRoaXMubGlzdGVuZXJzLm1lZGlhKCksIXRoaXMuc3VwcG9ydGVkLnVpKXJldHVybiB0aGlzLmRlYnVnLndhcm4oXCJCYXNpYyBzdXBwb3J0IG9ubHkgZm9yIFwiLmNvbmNhdCh0aGlzLnByb3ZpZGVyLFwiIFwiKS5jb25jYXQodGhpcy50eXBlKSksdm9pZCB5dC50b2dnbGVOYXRpdmVDb250cm9scy5jYWxsKHRoaXMsITApO0codGhpcy5lbGVtZW50cy5jb250cm9scyl8fChudC5pbmplY3QuY2FsbCh0aGlzKSx0aGlzLmxpc3RlbmVycy5jb250cm9scygpKSx5dC50b2dnbGVOYXRpdmVDb250cm9scy5jYWxsKHRoaXMpLHRoaXMuaXNIVE1MNSYmcnQuc2V0dXAuY2FsbCh0aGlzKSx0aGlzLnZvbHVtZT1udWxsLHRoaXMubXV0ZWQ9bnVsbCx0aGlzLmxvb3A9bnVsbCx0aGlzLnF1YWxpdHk9bnVsbCx0aGlzLnNwZWVkPW51bGwsbnQudXBkYXRlVm9sdW1lLmNhbGwodGhpcyksbnQudGltZVVwZGF0ZS5jYWxsKHRoaXMpLHl0LmNoZWNrUGxheWluZy5jYWxsKHRoaXMpLGJlKHRoaXMuZWxlbWVudHMuY29udGFpbmVyLHRoaXMuY29uZmlnLmNsYXNzTmFtZXMucGlwLnN1cHBvcnRlZCxFZS5waXAmJnRoaXMuaXNIVE1MNSYmdGhpcy5pc1ZpZGVvKSxiZSh0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcix0aGlzLmNvbmZpZy5jbGFzc05hbWVzLmFpcnBsYXkuc3VwcG9ydGVkLEVlLmFpcnBsYXkmJnRoaXMuaXNIVE1MNSksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5pc0lvcyxvZS5pc0lvcyksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5pc1RvdWNoLHRoaXMudG91Y2gpLHRoaXMucmVhZHk9ITAsc2V0VGltZW91dCgoZnVuY3Rpb24oKXtPZS5jYWxsKGUsZS5tZWRpYSxcInJlYWR5XCIpfSksMCkseXQuc2V0VGl0bGUuY2FsbCh0aGlzKSx0aGlzLnBvc3RlciYmeXQuc2V0UG9zdGVyLmNhbGwodGhpcyx0aGlzLnBvc3RlciwhMSkuY2F0Y2goKGZ1bmN0aW9uKCl7fSkpLHRoaXMuY29uZmlnLmR1cmF0aW9uJiZudC5kdXJhdGlvblVwZGF0ZS5jYWxsKHRoaXMpfSxzZXRUaXRsZTpmdW5jdGlvbigpe3ZhciBlPVhlKFwicGxheVwiLHRoaXMuY29uZmlnKTtpZihZKHRoaXMuY29uZmlnLnRpdGxlKSYmIWFlKHRoaXMuY29uZmlnLnRpdGxlKSYmKGUrPVwiLCBcIi5jb25jYXQodGhpcy5jb25maWcudGl0bGUpKSxBcnJheS5mcm9tKHRoaXMuZWxlbWVudHMuYnV0dG9ucy5wbGF5fHxbXSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7dC5zZXRBdHRyaWJ1dGUoXCJhcmlhLWxhYmVsXCIsZSl9KSksdGhpcy5pc0VtYmVkKXt2YXIgdD1DZS5jYWxsKHRoaXMsXCJpZnJhbWVcIik7aWYoIUcodCkpcmV0dXJuO3ZhciBpPWFlKHRoaXMuY29uZmlnLnRpdGxlKT9cInZpZGVvXCI6dGhpcy5jb25maWcudGl0bGUsbj1YZShcImZyYW1lVGl0bGVcIix0aGlzLmNvbmZpZyk7dC5zZXRBdHRyaWJ1dGUoXCJ0aXRsZVwiLG4ucmVwbGFjZShcInt0aXRsZX1cIixpKSl9fSx0b2dnbGVQb3N0ZXI6ZnVuY3Rpb24oZSl7YmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5wb3N0ZXJFbmFibGVkLGUpfSxzZXRQb3N0ZXI6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPSEoYXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0pfHxhcmd1bWVudHNbMV07cmV0dXJuIGkmJnRoaXMucG9zdGVyP1Byb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIlBvc3RlciBhbHJlYWR5IHNldFwiKSk6KHRoaXMubWVkaWEuc2V0QXR0cmlidXRlKFwiZGF0YS1wb3N0ZXJcIixlKSx0aGlzLmVsZW1lbnRzLnBvc3Rlci5yZW1vdmVBdHRyaWJ1dGUoXCJoaWRkZW5cIiksamUuY2FsbCh0aGlzKS50aGVuKChmdW5jdGlvbigpe3JldHVybiBndChlKX0pKS5jYXRjaCgoZnVuY3Rpb24oaSl7dGhyb3cgZT09PXQucG9zdGVyJiZ5dC50b2dnbGVQb3N0ZXIuY2FsbCh0LCExKSxpfSkpLnRoZW4oKGZ1bmN0aW9uKCl7aWYoZSE9PXQucG9zdGVyKXRocm93IG5ldyBFcnJvcihcInNldFBvc3RlciBjYW5jZWxsZWQgYnkgbGF0ZXIgY2FsbCB0byBzZXRQb3N0ZXJcIil9KSkudGhlbigoZnVuY3Rpb24oKXtyZXR1cm4gT2JqZWN0LmFzc2lnbih0LmVsZW1lbnRzLnBvc3Rlci5zdHlsZSx7YmFja2dyb3VuZEltYWdlOlwidXJsKCdcIi5jb25jYXQoZSxcIicpXCIpLGJhY2tncm91bmRTaXplOlwiXCJ9KSx5dC50b2dnbGVQb3N0ZXIuY2FsbCh0LCEwKSxlfSkpKX0sY2hlY2tQbGF5aW5nOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7YmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5wbGF5aW5nLHRoaXMucGxheWluZyksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5wYXVzZWQsdGhpcy5wYXVzZWQpLGJlKHRoaXMuZWxlbWVudHMuY29udGFpbmVyLHRoaXMuY29uZmlnLmNsYXNzTmFtZXMuc3RvcHBlZCx0aGlzLnN0b3BwZWQpLEFycmF5LmZyb20odGhpcy5lbGVtZW50cy5idXR0b25zLnBsYXl8fFtdKS5mb3JFYWNoKChmdW5jdGlvbihlKXtPYmplY3QuYXNzaWduKGUse3ByZXNzZWQ6dC5wbGF5aW5nfSksZS5zZXRBdHRyaWJ1dGUoXCJhcmlhLWxhYmVsXCIsWGUodC5wbGF5aW5nP1wicGF1c2VcIjpcInBsYXlcIix0LmNvbmZpZykpfSkpLFooZSkmJlwidGltZXVwZGF0ZVwiPT09ZS50eXBlfHx5dC50b2dnbGVDb250cm9scy5jYWxsKHRoaXMpfSxjaGVja0xvYWRpbmc6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpczt0aGlzLmxvYWRpbmc9W1wic3RhbGxlZFwiLFwid2FpdGluZ1wiXS5pbmNsdWRlcyhlLnR5cGUpLGNsZWFyVGltZW91dCh0aGlzLnRpbWVycy5sb2FkaW5nKSx0aGlzLnRpbWVycy5sb2FkaW5nPXNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7YmUodC5lbGVtZW50cy5jb250YWluZXIsdC5jb25maWcuY2xhc3NOYW1lcy5sb2FkaW5nLHQubG9hZGluZykseXQudG9nZ2xlQ29udHJvbHMuY2FsbCh0KX0pLHRoaXMubG9hZGluZz8yNTA6MCl9LHRvZ2dsZUNvbnRyb2xzOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuZWxlbWVudHMuY29udHJvbHM7aWYodCYmdGhpcy5jb25maWcuaGlkZUNvbnRyb2xzKXt2YXIgaT10aGlzLnRvdWNoJiZ0aGlzLmxhc3RTZWVrVGltZSsyZTM+RGF0ZS5ub3coKTt0aGlzLnRvZ2dsZUNvbnRyb2xzKEJvb2xlYW4oZXx8dGhpcy5sb2FkaW5nfHx0aGlzLnBhdXNlZHx8dC5wcmVzc2VkfHx0LmhvdmVyfHxpKSl9fSxtaWdyYXRlU3R5bGVzOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcztPYmplY3QudmFsdWVzKHMoe30sdGhpcy5tZWRpYS5zdHlsZSkpLmZpbHRlcigoZnVuY3Rpb24oZSl7cmV0dXJuIWFlKGUpJiZZKGUpJiZlLnN0YXJ0c1dpdGgoXCItLXBseXJcIil9KSkuZm9yRWFjaCgoZnVuY3Rpb24odCl7ZS5lbGVtZW50cy5jb250YWluZXIuc3R5bGUuc2V0UHJvcGVydHkodCxlLm1lZGlhLnN0eWxlLmdldFByb3BlcnR5VmFsdWUodCkpLGUubWVkaWEuc3R5bGUucmVtb3ZlUHJvcGVydHkodCl9KSksYWUodGhpcy5tZWRpYS5zdHlsZSkmJnRoaXMubWVkaWEucmVtb3ZlQXR0cmlidXRlKFwic3R5bGVcIil9fSx2dD1mdW5jdGlvbigpe2Z1bmN0aW9uIHQoaSl7ZSh0aGlzLHQpLHRoaXMucGxheWVyPWksdGhpcy5sYXN0S2V5PW51bGwsdGhpcy5mb2N1c1RpbWVyPW51bGwsdGhpcy5sYXN0S2V5RG93bj1udWxsLHRoaXMuaGFuZGxlS2V5PXRoaXMuaGFuZGxlS2V5LmJpbmQodGhpcyksdGhpcy50b2dnbGVNZW51PXRoaXMudG9nZ2xlTWVudS5iaW5kKHRoaXMpLHRoaXMuc2V0VGFiRm9jdXM9dGhpcy5zZXRUYWJGb2N1cy5iaW5kKHRoaXMpLHRoaXMuZmlyc3RUb3VjaD10aGlzLmZpcnN0VG91Y2guYmluZCh0aGlzKX1yZXR1cm4gaSh0LFt7a2V5OlwiaGFuZGxlS2V5XCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5wbGF5ZXIsaT10LmVsZW1lbnRzLG49ZS5rZXlDb2RlP2Uua2V5Q29kZTplLndoaWNoLGE9XCJrZXlkb3duXCI9PT1lLnR5cGUscz1hJiZuPT09dGhpcy5sYXN0S2V5O2lmKCEoZS5hbHRLZXl8fGUuY3RybEtleXx8ZS5tZXRhS2V5fHxlLnNoaWZ0S2V5KSYmSyhuKSl7aWYoYSl7dmFyIHI9ZG9jdW1lbnQuYWN0aXZlRWxlbWVudDtpZihHKHIpKXt2YXIgbz10LmNvbmZpZy5zZWxlY3RvcnMuZWRpdGFibGU7aWYociE9PWkuaW5wdXRzLnNlZWsmJmtlKHIsbykpcmV0dXJuO2lmKDMyPT09ZS53aGljaCYma2UociwnYnV0dG9uLCBbcm9sZV49XCJtZW51aXRlbVwiXScpKXJldHVybn1zd2l0Y2goWzMyLDM3LDM4LDM5LDQwLDQ4LDQ5LDUwLDUxLDUyLDUzLDU0LDU2LDU3LDY3LDcwLDczLDc1LDc2LDc3LDc5XS5pbmNsdWRlcyhuKSYmKGUucHJldmVudERlZmF1bHQoKSxlLnN0b3BQcm9wYWdhdGlvbigpKSxuKXtjYXNlIDQ4OmNhc2UgNDk6Y2FzZSA1MDpjYXNlIDUxOmNhc2UgNTI6Y2FzZSA1MzpjYXNlIDU0OmNhc2UgNTU6Y2FzZSA1NjpjYXNlIDU3OnN8fCh0LmN1cnJlbnRUaW1lPXQuZHVyYXRpb24vMTAqKG4tNDgpKTticmVhaztjYXNlIDMyOmNhc2UgNzU6c3x8RGUodC50b2dnbGVQbGF5KCkpO2JyZWFrO2Nhc2UgMzg6dC5pbmNyZWFzZVZvbHVtZSguMSk7YnJlYWs7Y2FzZSA0MDp0LmRlY3JlYXNlVm9sdW1lKC4xKTticmVhaztjYXNlIDc3OnN8fCh0Lm11dGVkPSF0Lm11dGVkKTticmVhaztjYXNlIDM5OnQuZm9yd2FyZCgpO2JyZWFrO2Nhc2UgMzc6dC5yZXdpbmQoKTticmVhaztjYXNlIDcwOnQuZnVsbHNjcmVlbi50b2dnbGUoKTticmVhaztjYXNlIDY3OnN8fHQudG9nZ2xlQ2FwdGlvbnMoKTticmVhaztjYXNlIDc2OnQubG9vcD0hdC5sb29wfTI3PT09biYmIXQuZnVsbHNjcmVlbi51c2luZ05hdGl2ZSYmdC5mdWxsc2NyZWVuLmFjdGl2ZSYmdC5mdWxsc2NyZWVuLnRvZ2dsZSgpLHRoaXMubGFzdEtleT1ufWVsc2UgdGhpcy5sYXN0S2V5PW51bGx9fX0se2tleTpcInRvZ2dsZU1lbnVcIix2YWx1ZTpmdW5jdGlvbihlKXtudC50b2dnbGVNZW51LmNhbGwodGhpcy5wbGF5ZXIsZSl9fSx7a2V5OlwiZmlyc3RUb3VjaFwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5wbGF5ZXIsdD1lLmVsZW1lbnRzO2UudG91Y2g9ITAsYmUodC5jb250YWluZXIsZS5jb25maWcuY2xhc3NOYW1lcy5pc1RvdWNoLCEwKX19LHtrZXk6XCJzZXRUYWJGb2N1c1wiLHZhbHVlOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMucGxheWVyLGk9dC5lbGVtZW50cztpZihjbGVhclRpbWVvdXQodGhpcy5mb2N1c1RpbWVyKSxcImtleWRvd25cIiE9PWUudHlwZXx8OT09PWUud2hpY2gpe1wia2V5ZG93blwiPT09ZS50eXBlJiYodGhpcy5sYXN0S2V5RG93bj1lLnRpbWVTdGFtcCk7dmFyIG4sYT1lLnRpbWVTdGFtcC10aGlzLmxhc3RLZXlEb3duPD0yMDtpZihcImZvY3VzXCIhPT1lLnR5cGV8fGEpbj10LmNvbmZpZy5jbGFzc05hbWVzLnRhYkZvY3VzLGJlKFRlLmNhbGwodCxcIi5cIi5jb25jYXQobikpLG4sITEpLFwiZm9jdXNvdXRcIiE9PWUudHlwZSYmKHRoaXMuZm9jdXNUaW1lcj1zZXRUaW1lb3V0KChmdW5jdGlvbigpe3ZhciBlPWRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7aS5jb250YWluZXIuY29udGFpbnMoZSkmJmJlKGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQsdC5jb25maWcuY2xhc3NOYW1lcy50YWJGb2N1cywhMCl9KSwxMCkpfX19LHtrZXk6XCJnbG9iYWxcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPSEoYXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0pfHxhcmd1bWVudHNbMF0sdD10aGlzLnBsYXllcjt0LmNvbmZpZy5rZXlib2FyZC5nbG9iYWwmJk1lLmNhbGwodCx3aW5kb3csXCJrZXlkb3duIGtleXVwXCIsdGhpcy5oYW5kbGVLZXksZSwhMSksTWUuY2FsbCh0LGRvY3VtZW50LmJvZHksXCJjbGlja1wiLHRoaXMudG9nZ2xlTWVudSxlKSxMZS5jYWxsKHQsZG9jdW1lbnQuYm9keSxcInRvdWNoc3RhcnRcIix0aGlzLmZpcnN0VG91Y2gpLE1lLmNhbGwodCxkb2N1bWVudC5ib2R5LFwia2V5ZG93biBmb2N1cyBibHVyIGZvY3Vzb3V0XCIsdGhpcy5zZXRUYWJGb2N1cyxlLCExLCEwKX19LHtrZXk6XCJjb250YWluZXJcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPXRoaXMucGxheWVyLHQ9ZS5jb25maWcsaT1lLmVsZW1lbnRzLG49ZS50aW1lcnM7IXQua2V5Ym9hcmQuZ2xvYmFsJiZ0LmtleWJvYXJkLmZvY3VzZWQmJnhlLmNhbGwoZSxpLmNvbnRhaW5lcixcImtleWRvd24ga2V5dXBcIix0aGlzLmhhbmRsZUtleSwhMSkseGUuY2FsbChlLGkuY29udGFpbmVyLFwibW91c2Vtb3ZlIG1vdXNlbGVhdmUgdG91Y2hzdGFydCB0b3VjaG1vdmUgZW50ZXJmdWxsc2NyZWVuIGV4aXRmdWxsc2NyZWVuXCIsKGZ1bmN0aW9uKHQpe3ZhciBhPWkuY29udHJvbHM7YSYmXCJlbnRlcmZ1bGxzY3JlZW5cIj09PXQudHlwZSYmKGEucHJlc3NlZD0hMSxhLmhvdmVyPSExKTt2YXIgcz0wO1tcInRvdWNoc3RhcnRcIixcInRvdWNobW92ZVwiLFwibW91c2Vtb3ZlXCJdLmluY2x1ZGVzKHQudHlwZSkmJih5dC50b2dnbGVDb250cm9scy5jYWxsKGUsITApLHM9ZS50b3VjaD8zZTM6MmUzKSxjbGVhclRpbWVvdXQobi5jb250cm9scyksbi5jb250cm9scz1zZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB5dC50b2dnbGVDb250cm9scy5jYWxsKGUsITEpfSkscyl9KSk7dmFyIGE9ZnVuY3Rpb24odCl7aWYoIXQpcmV0dXJuIFJlLmNhbGwoZSk7dmFyIG49aS5jb250YWluZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksYT1uLndpZHRoLHM9bi5oZWlnaHQ7cmV0dXJuIFJlLmNhbGwoZSxcIlwiLmNvbmNhdChhLFwiOlwiKS5jb25jYXQocykpfSxzPWZ1bmN0aW9uKCl7Y2xlYXJUaW1lb3V0KG4ucmVzaXplZCksbi5yZXNpemVkPXNldFRpbWVvdXQoYSw1MCl9O3hlLmNhbGwoZSxpLmNvbnRhaW5lcixcImVudGVyZnVsbHNjcmVlbiBleGl0ZnVsbHNjcmVlblwiLChmdW5jdGlvbih0KXt2YXIgbj1lLmZ1bGxzY3JlZW4scj1uLnRhcmdldCxsPW4udXNpbmdOYXRpdmU7aWYocj09PWkuY29udGFpbmVyJiYoZS5pc0VtYmVkfHwhYWUoZS5jb25maWcucmF0aW8pKSl7dmFyIGM9XCJlbnRlcmZ1bGxzY3JlZW5cIj09PXQudHlwZSx1PWEoYyk7dS5wYWRkaW5nOyFmdW5jdGlvbih0LGksbil7aWYoZS5pc1ZpbWVvJiYhZS5jb25maWcudmltZW8ucHJlbWl1bSl7dmFyIGE9ZS5lbGVtZW50cy53cmFwcGVyLmZpcnN0Q2hpbGQscz1vKHQsMilbMV0scj1vKEZlLmNhbGwoZSksMiksbD1yWzBdLGM9clsxXTthLnN0eWxlLm1heFdpZHRoPW4/XCJcIi5jb25jYXQocy9jKmwsXCJweFwiKTpudWxsLGEuc3R5bGUubWFyZ2luPW4/XCIwIGF1dG9cIjpudWxsfX0odS5yYXRpbywwLGMpLGMmJnNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHJlKGkuY29udGFpbmVyKX0pLDEwMCksbHx8KGM/eGUuY2FsbChlLHdpbmRvdyxcInJlc2l6ZVwiLHMpOkllLmNhbGwoZSx3aW5kb3csXCJyZXNpemVcIixzKSl9fSkpfX0se2tleTpcIm1lZGlhXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLHQ9dGhpcy5wbGF5ZXIsaT10LmVsZW1lbnRzO2lmKHhlLmNhbGwodCx0Lm1lZGlhLFwidGltZXVwZGF0ZSBzZWVraW5nIHNlZWtlZFwiLChmdW5jdGlvbihlKXtyZXR1cm4gbnQudGltZVVwZGF0ZS5jYWxsKHQsZSl9KSkseGUuY2FsbCh0LHQubWVkaWEsXCJkdXJhdGlvbmNoYW5nZSBsb2FkZWRkYXRhIGxvYWRlZG1ldGFkYXRhXCIsKGZ1bmN0aW9uKGUpe3JldHVybiBudC5kdXJhdGlvblVwZGF0ZS5jYWxsKHQsZSl9KSkseGUuY2FsbCh0LHQubWVkaWEsXCJlbmRlZFwiLChmdW5jdGlvbigpe3QuaXNIVE1MNSYmdC5pc1ZpZGVvJiZ0LmNvbmZpZy5yZXNldE9uRW5kJiYodC5yZXN0YXJ0KCksdC5wYXVzZSgpKX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInByb2dyZXNzIHBsYXlpbmcgc2Vla2luZyBzZWVrZWRcIiwoZnVuY3Rpb24oZSl7cmV0dXJuIG50LnVwZGF0ZVByb2dyZXNzLmNhbGwodCxlKX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInZvbHVtZWNoYW5nZVwiLChmdW5jdGlvbihlKXtyZXR1cm4gbnQudXBkYXRlVm9sdW1lLmNhbGwodCxlKX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInBsYXlpbmcgcGxheSBwYXVzZSBlbmRlZCBlbXB0aWVkIHRpbWV1cGRhdGVcIiwoZnVuY3Rpb24oZSl7cmV0dXJuIHl0LmNoZWNrUGxheWluZy5jYWxsKHQsZSl9KSkseGUuY2FsbCh0LHQubWVkaWEsXCJ3YWl0aW5nIGNhbnBsYXkgc2Vla2VkIHBsYXlpbmdcIiwoZnVuY3Rpb24oZSl7cmV0dXJuIHl0LmNoZWNrTG9hZGluZy5jYWxsKHQsZSl9KSksdC5zdXBwb3J0ZWQudWkmJnQuY29uZmlnLmNsaWNrVG9QbGF5JiYhdC5pc0F1ZGlvKXt2YXIgbj1DZS5jYWxsKHQsXCIuXCIuY29uY2F0KHQuY29uZmlnLmNsYXNzTmFtZXMudmlkZW8pKTtpZighRyhuKSlyZXR1cm47eGUuY2FsbCh0LGkuY29udGFpbmVyLFwiY2xpY2tcIiwoZnVuY3Rpb24oYSl7KFtpLmNvbnRhaW5lcixuXS5pbmNsdWRlcyhhLnRhcmdldCl8fG4uY29udGFpbnMoYS50YXJnZXQpKSYmKHQudG91Y2gmJnQuY29uZmlnLmhpZGVDb250cm9sc3x8KHQuZW5kZWQ/KGUucHJveHkoYSx0LnJlc3RhcnQsXCJyZXN0YXJ0XCIpLGUucHJveHkoYSwoZnVuY3Rpb24oKXtEZSh0LnBsYXkoKSl9KSxcInBsYXlcIikpOmUucHJveHkoYSwoZnVuY3Rpb24oKXtEZSh0LnRvZ2dsZVBsYXkoKSl9KSxcInBsYXlcIikpKX0pKX10LnN1cHBvcnRlZC51aSYmdC5jb25maWcuZGlzYWJsZUNvbnRleHRNZW51JiZ4ZS5jYWxsKHQsaS53cmFwcGVyLFwiY29udGV4dG1lbnVcIiwoZnVuY3Rpb24oZSl7ZS5wcmV2ZW50RGVmYXVsdCgpfSksITEpLHhlLmNhbGwodCx0Lm1lZGlhLFwidm9sdW1lY2hhbmdlXCIsKGZ1bmN0aW9uKCl7dC5zdG9yYWdlLnNldCh7dm9sdW1lOnQudm9sdW1lLG11dGVkOnQubXV0ZWR9KX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInJhdGVjaGFuZ2VcIiwoZnVuY3Rpb24oKXtudC51cGRhdGVTZXR0aW5nLmNhbGwodCxcInNwZWVkXCIpLHQuc3RvcmFnZS5zZXQoe3NwZWVkOnQuc3BlZWR9KX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInF1YWxpdHljaGFuZ2VcIiwoZnVuY3Rpb24oZSl7bnQudXBkYXRlU2V0dGluZy5jYWxsKHQsXCJxdWFsaXR5XCIsbnVsbCxlLmRldGFpbC5xdWFsaXR5KX0pKSx4ZS5jYWxsKHQsdC5tZWRpYSxcInJlYWR5IHF1YWxpdHljaGFuZ2VcIiwoZnVuY3Rpb24oKXtudC5zZXREb3dubG9hZFVybC5jYWxsKHQpfSkpO3ZhciBhPXQuY29uZmlnLmV2ZW50cy5jb25jYXQoW1wia2V5dXBcIixcImtleWRvd25cIl0pLmpvaW4oXCIgXCIpO3hlLmNhbGwodCx0Lm1lZGlhLGEsKGZ1bmN0aW9uKGUpe3ZhciBuPWUuZGV0YWlsLGE9dm9pZCAwPT09bj97fTpuO1wiZXJyb3JcIj09PWUudHlwZSYmKGE9dC5tZWRpYS5lcnJvciksT2UuY2FsbCh0LGkuY29udGFpbmVyLGUudHlwZSwhMCxhKX0pKX19LHtrZXk6XCJwcm94eVwiLHZhbHVlOmZ1bmN0aW9uKGUsdCxpKXt2YXIgbj10aGlzLnBsYXllcixhPW4uY29uZmlnLmxpc3RlbmVyc1tpXSxzPSEwO1goYSkmJihzPWEuY2FsbChuLGUpKSwhMSE9PXMmJlgodCkmJnQuY2FsbChuLGUpfX0se2tleTpcImJpbmRcIix2YWx1ZTpmdW5jdGlvbihlLHQsaSxuKXt2YXIgYT10aGlzLHM9IShhcmd1bWVudHMubGVuZ3RoPjQmJnZvaWQgMCE9PWFyZ3VtZW50c1s0XSl8fGFyZ3VtZW50c1s0XSxyPXRoaXMucGxheWVyLG89ci5jb25maWcubGlzdGVuZXJzW25dLGw9WChvKTt4ZS5jYWxsKHIsZSx0LChmdW5jdGlvbihlKXtyZXR1cm4gYS5wcm94eShlLGksbil9KSxzJiYhbCl9fSx7a2V5OlwiY29udHJvbHNcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPXRoaXMsdD10aGlzLnBsYXllcixpPXQuZWxlbWVudHMsbj1vZS5pc0lFP1wiY2hhbmdlXCI6XCJpbnB1dFwiO2lmKGkuYnV0dG9ucy5wbGF5JiZBcnJheS5mcm9tKGkuYnV0dG9ucy5wbGF5KS5mb3JFYWNoKChmdW5jdGlvbihpKXtlLmJpbmQoaSxcImNsaWNrXCIsKGZ1bmN0aW9uKCl7RGUodC50b2dnbGVQbGF5KCkpfSksXCJwbGF5XCIpfSkpLHRoaXMuYmluZChpLmJ1dHRvbnMucmVzdGFydCxcImNsaWNrXCIsdC5yZXN0YXJ0LFwicmVzdGFydFwiKSx0aGlzLmJpbmQoaS5idXR0b25zLnJld2luZCxcImNsaWNrXCIsKGZ1bmN0aW9uKCl7dC5sYXN0U2Vla1RpbWU9RGF0ZS5ub3coKSx0LnJld2luZCgpfSksXCJyZXdpbmRcIiksdGhpcy5iaW5kKGkuYnV0dG9ucy5mYXN0Rm9yd2FyZCxcImNsaWNrXCIsKGZ1bmN0aW9uKCl7dC5sYXN0U2Vla1RpbWU9RGF0ZS5ub3coKSx0LmZvcndhcmQoKX0pLFwiZmFzdEZvcndhcmRcIiksdGhpcy5iaW5kKGkuYnV0dG9ucy5tdXRlLFwiY2xpY2tcIiwoZnVuY3Rpb24oKXt0Lm11dGVkPSF0Lm11dGVkfSksXCJtdXRlXCIpLHRoaXMuYmluZChpLmJ1dHRvbnMuY2FwdGlvbnMsXCJjbGlja1wiLChmdW5jdGlvbigpe3JldHVybiB0LnRvZ2dsZUNhcHRpb25zKCl9KSksdGhpcy5iaW5kKGkuYnV0dG9ucy5kb3dubG9hZCxcImNsaWNrXCIsKGZ1bmN0aW9uKCl7T2UuY2FsbCh0LHQubWVkaWEsXCJkb3dubG9hZFwiKX0pLFwiZG93bmxvYWRcIiksdGhpcy5iaW5kKGkuYnV0dG9ucy5mdWxsc2NyZWVuLFwiY2xpY2tcIiwoZnVuY3Rpb24oKXt0LmZ1bGxzY3JlZW4udG9nZ2xlKCl9KSxcImZ1bGxzY3JlZW5cIiksdGhpcy5iaW5kKGkuYnV0dG9ucy5waXAsXCJjbGlja1wiLChmdW5jdGlvbigpe3QucGlwPVwidG9nZ2xlXCJ9KSxcInBpcFwiKSx0aGlzLmJpbmQoaS5idXR0b25zLmFpcnBsYXksXCJjbGlja1wiLHQuYWlycGxheSxcImFpcnBsYXlcIiksdGhpcy5iaW5kKGkuYnV0dG9ucy5zZXR0aW5ncyxcImNsaWNrXCIsKGZ1bmN0aW9uKGUpe2Uuc3RvcFByb3BhZ2F0aW9uKCksZS5wcmV2ZW50RGVmYXVsdCgpLG50LnRvZ2dsZU1lbnUuY2FsbCh0LGUpfSksbnVsbCwhMSksdGhpcy5iaW5kKGkuYnV0dG9ucy5zZXR0aW5ncyxcImtleXVwXCIsKGZ1bmN0aW9uKGUpe3ZhciBpPWUud2hpY2g7WzEzLDMyXS5pbmNsdWRlcyhpKSYmKDEzIT09aT8oZS5wcmV2ZW50RGVmYXVsdCgpLGUuc3RvcFByb3BhZ2F0aW9uKCksbnQudG9nZ2xlTWVudS5jYWxsKHQsZSkpOm50LmZvY3VzRmlyc3RNZW51SXRlbS5jYWxsKHQsbnVsbCwhMCkpfSksbnVsbCwhMSksdGhpcy5iaW5kKGkuc2V0dGluZ3MubWVudSxcImtleWRvd25cIiwoZnVuY3Rpb24oZSl7Mjc9PT1lLndoaWNoJiZudC50b2dnbGVNZW51LmNhbGwodCxlKX0pKSx0aGlzLmJpbmQoaS5pbnB1dHMuc2VlayxcIm1vdXNlZG93biBtb3VzZW1vdmVcIiwoZnVuY3Rpb24oZSl7dmFyIHQ9aS5wcm9ncmVzcy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxuPTEwMC90LndpZHRoKihlLnBhZ2VYLXQubGVmdCk7ZS5jdXJyZW50VGFyZ2V0LnNldEF0dHJpYnV0ZShcInNlZWstdmFsdWVcIixuKX0pKSx0aGlzLmJpbmQoaS5pbnB1dHMuc2VlayxcIm1vdXNlZG93biBtb3VzZXVwIGtleWRvd24ga2V5dXAgdG91Y2hzdGFydCB0b3VjaGVuZFwiLChmdW5jdGlvbihlKXt2YXIgaT1lLmN1cnJlbnRUYXJnZXQsbj1lLmtleUNvZGU/ZS5rZXlDb2RlOmUud2hpY2gsYT1cInBsYXktb24tc2Vla2VkXCI7aWYoIWVlKGUpfHwzOT09PW58fDM3PT09bil7dC5sYXN0U2Vla1RpbWU9RGF0ZS5ub3coKTt2YXIgcz1pLmhhc0F0dHJpYnV0ZShhKSxyPVtcIm1vdXNldXBcIixcInRvdWNoZW5kXCIsXCJrZXl1cFwiXS5pbmNsdWRlcyhlLnR5cGUpO3MmJnI/KGkucmVtb3ZlQXR0cmlidXRlKGEpLERlKHQucGxheSgpKSk6IXImJnQucGxheWluZyYmKGkuc2V0QXR0cmlidXRlKGEsXCJcIiksdC5wYXVzZSgpKX19KSksb2UuaXNJb3Mpe3ZhciBhPVRlLmNhbGwodCwnaW5wdXRbdHlwZT1cInJhbmdlXCJdJyk7QXJyYXkuZnJvbShhKS5mb3JFYWNoKChmdW5jdGlvbih0KXtyZXR1cm4gZS5iaW5kKHQsbiwoZnVuY3Rpb24oZSl7cmV0dXJuIHJlKGUudGFyZ2V0KX0pKX0pKX10aGlzLmJpbmQoaS5pbnB1dHMuc2VlayxuLChmdW5jdGlvbihlKXt2YXIgaT1lLmN1cnJlbnRUYXJnZXQsbj1pLmdldEF0dHJpYnV0ZShcInNlZWstdmFsdWVcIik7YWUobikmJihuPWkudmFsdWUpLGkucmVtb3ZlQXR0cmlidXRlKFwic2Vlay12YWx1ZVwiKSx0LmN1cnJlbnRUaW1lPW4vaS5tYXgqdC5kdXJhdGlvbn0pLFwic2Vla1wiKSx0aGlzLmJpbmQoaS5wcm9ncmVzcyxcIm1vdXNlZW50ZXIgbW91c2VsZWF2ZSBtb3VzZW1vdmVcIiwoZnVuY3Rpb24oZSl7cmV0dXJuIG50LnVwZGF0ZVNlZWtUb29sdGlwLmNhbGwodCxlKX0pKSx0aGlzLmJpbmQoaS5wcm9ncmVzcyxcIm1vdXNlbW92ZSB0b3VjaG1vdmVcIiwoZnVuY3Rpb24oZSl7dmFyIGk9dC5wcmV2aWV3VGh1bWJuYWlscztpJiZpLmxvYWRlZCYmaS5zdGFydE1vdmUoZSl9KSksdGhpcy5iaW5kKGkucHJvZ3Jlc3MsXCJtb3VzZWxlYXZlIHRvdWNoZW5kIGNsaWNrXCIsKGZ1bmN0aW9uKCl7dmFyIGU9dC5wcmV2aWV3VGh1bWJuYWlscztlJiZlLmxvYWRlZCYmZS5lbmRNb3ZlKCExLCEwKX0pKSx0aGlzLmJpbmQoaS5wcm9ncmVzcyxcIm1vdXNlZG93biB0b3VjaHN0YXJ0XCIsKGZ1bmN0aW9uKGUpe3ZhciBpPXQucHJldmlld1RodW1ibmFpbHM7aSYmaS5sb2FkZWQmJmkuc3RhcnRTY3J1YmJpbmcoZSl9KSksdGhpcy5iaW5kKGkucHJvZ3Jlc3MsXCJtb3VzZXVwIHRvdWNoZW5kXCIsKGZ1bmN0aW9uKGUpe3ZhciBpPXQucHJldmlld1RodW1ibmFpbHM7aSYmaS5sb2FkZWQmJmkuZW5kU2NydWJiaW5nKGUpfSkpLG9lLmlzV2Via2l0JiZBcnJheS5mcm9tKFRlLmNhbGwodCwnaW5wdXRbdHlwZT1cInJhbmdlXCJdJykpLmZvckVhY2goKGZ1bmN0aW9uKGkpe2UuYmluZChpLFwiaW5wdXRcIiwoZnVuY3Rpb24oZSl7cmV0dXJuIG50LnVwZGF0ZVJhbmdlRmlsbC5jYWxsKHQsZS50YXJnZXQpfSkpfSkpLHQuY29uZmlnLnRvZ2dsZUludmVydCYmIUcoaS5kaXNwbGF5LmR1cmF0aW9uKSYmdGhpcy5iaW5kKGkuZGlzcGxheS5jdXJyZW50VGltZSxcImNsaWNrXCIsKGZ1bmN0aW9uKCl7MCE9PXQuY3VycmVudFRpbWUmJih0LmNvbmZpZy5pbnZlcnRUaW1lPSF0LmNvbmZpZy5pbnZlcnRUaW1lLG50LnRpbWVVcGRhdGUuY2FsbCh0KSl9KSksdGhpcy5iaW5kKGkuaW5wdXRzLnZvbHVtZSxuLChmdW5jdGlvbihlKXt0LnZvbHVtZT1lLnRhcmdldC52YWx1ZX0pLFwidm9sdW1lXCIpLHRoaXMuYmluZChpLmNvbnRyb2xzLFwibW91c2VlbnRlciBtb3VzZWxlYXZlXCIsKGZ1bmN0aW9uKGUpe2kuY29udHJvbHMuaG92ZXI9IXQudG91Y2gmJlwibW91c2VlbnRlclwiPT09ZS50eXBlfSkpLGkuZnVsbHNjcmVlbiYmQXJyYXkuZnJvbShpLmZ1bGxzY3JlZW4uY2hpbGRyZW4pLmZpbHRlcigoZnVuY3Rpb24oZSl7cmV0dXJuIWUuY29udGFpbnMoaS5jb250YWluZXIpfSkpLmZvckVhY2goKGZ1bmN0aW9uKG4pe2UuYmluZChuLFwibW91c2VlbnRlciBtb3VzZWxlYXZlXCIsKGZ1bmN0aW9uKGUpe2kuY29udHJvbHMuaG92ZXI9IXQudG91Y2gmJlwibW91c2VlbnRlclwiPT09ZS50eXBlfSkpfSkpLHRoaXMuYmluZChpLmNvbnRyb2xzLFwibW91c2Vkb3duIG1vdXNldXAgdG91Y2hzdGFydCB0b3VjaGVuZCB0b3VjaGNhbmNlbFwiLChmdW5jdGlvbihlKXtpLmNvbnRyb2xzLnByZXNzZWQ9W1wibW91c2Vkb3duXCIsXCJ0b3VjaHN0YXJ0XCJdLmluY2x1ZGVzKGUudHlwZSl9KSksdGhpcy5iaW5kKGkuY29udHJvbHMsXCJmb2N1c2luXCIsKGZ1bmN0aW9uKCl7dmFyIG49dC5jb25maWcsYT10LnRpbWVycztiZShpLmNvbnRyb2xzLG4uY2xhc3NOYW1lcy5ub1RyYW5zaXRpb24sITApLHl0LnRvZ2dsZUNvbnRyb2xzLmNhbGwodCwhMCksc2V0VGltZW91dCgoZnVuY3Rpb24oKXtiZShpLmNvbnRyb2xzLG4uY2xhc3NOYW1lcy5ub1RyYW5zaXRpb24sITEpfSksMCk7dmFyIHM9ZS50b3VjaD8zZTM6NGUzO2NsZWFyVGltZW91dChhLmNvbnRyb2xzKSxhLmNvbnRyb2xzPXNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHl0LnRvZ2dsZUNvbnRyb2xzLmNhbGwodCwhMSl9KSxzKX0pKSx0aGlzLmJpbmQoaS5pbnB1dHMudm9sdW1lLFwid2hlZWxcIiwoZnVuY3Rpb24oZSl7dmFyIGk9ZS53ZWJraXREaXJlY3Rpb25JbnZlcnRlZEZyb21EZXZpY2Usbj1vKFtlLmRlbHRhWCwtZS5kZWx0YVldLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIGk/LWU6ZX0pKSwyKSxhPW5bMF0scz1uWzFdLHI9TWF0aC5zaWduKE1hdGguYWJzKGEpPk1hdGguYWJzKHMpP2E6cyk7dC5pbmNyZWFzZVZvbHVtZShyLzUwKTt2YXIgbD10Lm1lZGlhLnZvbHVtZTsoMT09PXImJmw8MXx8LTE9PT1yJiZsPjApJiZlLnByZXZlbnREZWZhdWx0KCl9KSxcInZvbHVtZVwiLCExKX19XSksdH0oKTtcInVuZGVmaW5lZFwiIT10eXBlb2YgZ2xvYmFsVGhpcz9nbG9iYWxUaGlzOlwidW5kZWZpbmVkXCIhPXR5cGVvZiB3aW5kb3c/d2luZG93OlwidW5kZWZpbmVkXCIhPXR5cGVvZiBnbG9iYWw/Z2xvYmFsOlwidW5kZWZpbmVkXCIhPXR5cGVvZiBzZWxmJiZzZWxmO3ZhciBidD1mdW5jdGlvbihlLHQpe3JldHVybiBlKHQ9e2V4cG9ydHM6e319LHQuZXhwb3J0cyksdC5leHBvcnRzfSgoZnVuY3Rpb24oZSx0KXtlLmV4cG9ydHM9ZnVuY3Rpb24oKXt2YXIgZT1mdW5jdGlvbigpe30sdD17fSxpPXt9LG49e307ZnVuY3Rpb24gYShlLHQpe2U9ZS5wdXNoP2U6W2VdO3ZhciBhLHMscixvPVtdLGw9ZS5sZW5ndGgsYz1sO2ZvcihhPWZ1bmN0aW9uKGUsaSl7aS5sZW5ndGgmJm8ucHVzaChlKSwtLWN8fHQobyl9O2wtLTspcz1lW2xdLChyPWlbc10pP2EocyxyKToobltzXT1uW3NdfHxbXSkucHVzaChhKX1mdW5jdGlvbiBzKGUsdCl7aWYoZSl7dmFyIGE9bltlXTtpZihpW2VdPXQsYSlmb3IoO2EubGVuZ3RoOylhWzBdKGUsdCksYS5zcGxpY2UoMCwxKX19ZnVuY3Rpb24gcih0LGkpe3QuY2FsbCYmKHQ9e3N1Y2Nlc3M6dH0pLGkubGVuZ3RoPyh0LmVycm9yfHxlKShpKToodC5zdWNjZXNzfHxlKSh0KX1mdW5jdGlvbiBvKHQsaSxuLGEpe3ZhciBzLHIsbD1kb2N1bWVudCxjPW4uYXN5bmMsdT0obi5udW1SZXRyaWVzfHwwKSsxLGQ9bi5iZWZvcmV8fGUsaD10LnJlcGxhY2UoL1tcXD98I10uKiQvLFwiXCIpLG09dC5yZXBsYWNlKC9eKGNzc3xpbWcpIS8sXCJcIik7YT1hfHwwLC8oXmNzcyF8XFwuY3NzJCkvLnRlc3QoaCk/KChyPWwuY3JlYXRlRWxlbWVudChcImxpbmtcIikpLnJlbD1cInN0eWxlc2hlZXRcIixyLmhyZWY9bSwocz1cImhpZGVGb2N1c1wiaW4gcikmJnIucmVsTGlzdCYmKHM9MCxyLnJlbD1cInByZWxvYWRcIixyLmFzPVwic3R5bGVcIikpOi8oXmltZyF8XFwuKHBuZ3xnaWZ8anBnfHN2Z3x3ZWJwKSQpLy50ZXN0KGgpPyhyPWwuY3JlYXRlRWxlbWVudChcImltZ1wiKSkuc3JjPW06KChyPWwuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKSkuc3JjPXQsci5hc3luYz12b2lkIDA9PT1jfHxjKSxyLm9ubG9hZD1yLm9uZXJyb3I9ci5vbmJlZm9yZWxvYWQ9ZnVuY3Rpb24oZSl7dmFyIGw9ZS50eXBlWzBdO2lmKHMpdHJ5e3Iuc2hlZXQuY3NzVGV4dC5sZW5ndGh8fChsPVwiZVwiKX1jYXRjaChlKXsxOCE9ZS5jb2RlJiYobD1cImVcIil9aWYoXCJlXCI9PWwpe2lmKChhKz0xKTx1KXJldHVybiBvKHQsaSxuLGEpfWVsc2UgaWYoXCJwcmVsb2FkXCI9PXIucmVsJiZcInN0eWxlXCI9PXIuYXMpcmV0dXJuIHIucmVsPVwic3R5bGVzaGVldFwiO2kodCxsLGUuZGVmYXVsdFByZXZlbnRlZCl9LCExIT09ZCh0LHIpJiZsLmhlYWQuYXBwZW5kQ2hpbGQocil9ZnVuY3Rpb24gbChlLHQsaSl7dmFyIG4sYSxzPShlPWUucHVzaD9lOltlXSkubGVuZ3RoLHI9cyxsPVtdO2ZvcihuPWZ1bmN0aW9uKGUsaSxuKXtpZihcImVcIj09aSYmbC5wdXNoKGUpLFwiYlwiPT1pKXtpZighbilyZXR1cm47bC5wdXNoKGUpfS0tc3x8dChsKX0sYT0wO2E8cjthKyspbyhlW2FdLG4saSl9ZnVuY3Rpb24gYyhlLGksbil7dmFyIGEsbztpZihpJiZpLnRyaW0mJihhPWkpLG89KGE/bjppKXx8e30sYSl7aWYoYSBpbiB0KXRocm93XCJMb2FkSlNcIjt0W2FdPSEwfWZ1bmN0aW9uIGModCxpKXtsKGUsKGZ1bmN0aW9uKGUpe3IobyxlKSx0JiZyKHtzdWNjZXNzOnQsZXJyb3I6aX0sZSkscyhhLGUpfSksbyl9aWYoby5yZXR1cm5Qcm9taXNlKXJldHVybiBuZXcgUHJvbWlzZShjKTtjKCl9cmV0dXJuIGMucmVhZHk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gYShlLChmdW5jdGlvbihlKXtyKHQsZSl9KSksY30sYy5kb25lPWZ1bmN0aW9uKGUpe3MoZSxbXSl9LGMucmVzZXQ9ZnVuY3Rpb24oKXt0PXt9LGk9e30sbj17fX0sYy5pc0RlZmluZWQ9ZnVuY3Rpb24oZSl7cmV0dXJuIGUgaW4gdH0sY30oKX0pKTtmdW5jdGlvbiB3dChlKXtyZXR1cm4gbmV3IFByb21pc2UoKGZ1bmN0aW9uKHQsaSl7YnQoZSx7c3VjY2Vzczp0LGVycm9yOml9KX0pKX1mdW5jdGlvbiBrdChlKXtlJiYhdGhpcy5lbWJlZC5oYXNQbGF5ZWQmJih0aGlzLmVtYmVkLmhhc1BsYXllZD0hMCksdGhpcy5tZWRpYS5wYXVzZWQ9PT1lJiYodGhpcy5tZWRpYS5wYXVzZWQ9IWUsT2UuY2FsbCh0aGlzLHRoaXMubWVkaWEsZT9cInBsYXlcIjpcInBhdXNlXCIpKX12YXIgVHQ9e3NldHVwOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcztiZShlLmVsZW1lbnRzLndyYXBwZXIsZS5jb25maWcuY2xhc3NOYW1lcy5lbWJlZCwhMCksZS5vcHRpb25zLnNwZWVkPWUuY29uZmlnLnNwZWVkLm9wdGlvbnMsUmUuY2FsbChlKSx6KHdpbmRvdy5WaW1lbyk/VHQucmVhZHkuY2FsbChlKTp3dChlLmNvbmZpZy51cmxzLnZpbWVvLnNkaykudGhlbigoZnVuY3Rpb24oKXtUdC5yZWFkeS5jYWxsKGUpfSkpLmNhdGNoKChmdW5jdGlvbih0KXtlLmRlYnVnLndhcm4oXCJWaW1lbyBTREsgKHBsYXllci5qcykgZmFpbGVkIHRvIGxvYWRcIix0KX0pKX0scmVhZHk6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLHQ9dGhpcyxpPXQuY29uZmlnLnZpbWVvLG49aS5wcmVtaXVtLGE9aS5yZWZlcnJlclBvbGljeSxsPXIoaSxbXCJwcmVtaXVtXCIsXCJyZWZlcnJlclBvbGljeVwiXSk7biYmT2JqZWN0LmFzc2lnbihsLHtjb250cm9sczohMSxzaWRlZG9jazohMX0pO3ZhciBjPXN0KHMoe2xvb3A6dC5jb25maWcubG9vcC5hY3RpdmUsYXV0b3BsYXk6dC5hdXRvcGxheSxtdXRlZDp0Lm11dGVkLGdlc3R1cmU6XCJtZWRpYVwiLHBsYXlzaW5saW5lOiF0aGlzLmNvbmZpZy5mdWxsc2NyZWVuLmlvc05hdGl2ZX0sbCkpLHU9dC5tZWRpYS5nZXRBdHRyaWJ1dGUoXCJzcmNcIik7YWUodSkmJih1PXQubWVkaWEuZ2V0QXR0cmlidXRlKHQuY29uZmlnLmF0dHJpYnV0ZXMuZW1iZWQuaWQpKTt2YXIgZCxoPWFlKGQ9dSk/bnVsbDpLKE51bWJlcihkKSk/ZDpkLm1hdGNoKC9eLioodmltZW8uY29tXFwvfHZpZGVvXFwvKShcXGQrKS4qLyk/UmVnRXhwLiQyOmQsbT1oZShcImlmcmFtZVwiKSxwPVVlKHQuY29uZmlnLnVybHMudmltZW8uaWZyYW1lLGgsYyk7aWYobS5zZXRBdHRyaWJ1dGUoXCJzcmNcIixwKSxtLnNldEF0dHJpYnV0ZShcImFsbG93ZnVsbHNjcmVlblwiLFwiXCIpLG0uc2V0QXR0cmlidXRlKFwiYWxsb3dcIixcImF1dG9wbGF5LGZ1bGxzY3JlZW4scGljdHVyZS1pbi1waWN0dXJlXCIpLGFlKGEpfHxtLnNldEF0dHJpYnV0ZShcInJlZmVycmVyUG9saWN5XCIsYSksbnx8IWkuY3VzdG9tQ29udHJvbHMpbS5zZXRBdHRyaWJ1dGUoXCJkYXRhLXBvc3RlclwiLHQucG9zdGVyKSx0Lm1lZGlhPWdlKG0sdC5tZWRpYSk7ZWxzZXt2YXIgZj1oZShcImRpdlwiLHtjbGFzczp0LmNvbmZpZy5jbGFzc05hbWVzLmVtYmVkQ29udGFpbmVyLFwiZGF0YS1wb3N0ZXJcIjp0LnBvc3Rlcn0pO2YuYXBwZW5kQ2hpbGQobSksdC5tZWRpYT1nZShmLHQubWVkaWEpfWkuY3VzdG9tQ29udHJvbHN8fEplKFVlKHQuY29uZmlnLnVybHMudmltZW8uYXBpLHApKS50aGVuKChmdW5jdGlvbihlKXshYWUoZSkmJmUudGh1bWJuYWlsX3VybCYmeXQuc2V0UG9zdGVyLmNhbGwodCxlLnRodW1ibmFpbF91cmwpLmNhdGNoKChmdW5jdGlvbigpe30pKX0pKSx0LmVtYmVkPW5ldyB3aW5kb3cuVmltZW8uUGxheWVyKG0se2F1dG9wYXVzZTp0LmNvbmZpZy5hdXRvcGF1c2UsbXV0ZWQ6dC5tdXRlZH0pLHQubWVkaWEucGF1c2VkPSEwLHQubWVkaWEuY3VycmVudFRpbWU9MCx0LnN1cHBvcnRlZC51aSYmdC5lbWJlZC5kaXNhYmxlVGV4dFRyYWNrKCksdC5tZWRpYS5wbGF5PWZ1bmN0aW9uKCl7cmV0dXJuIGt0LmNhbGwodCwhMCksdC5lbWJlZC5wbGF5KCl9LHQubWVkaWEucGF1c2U9ZnVuY3Rpb24oKXtyZXR1cm4ga3QuY2FsbCh0LCExKSx0LmVtYmVkLnBhdXNlKCl9LHQubWVkaWEuc3RvcD1mdW5jdGlvbigpe3QucGF1c2UoKSx0LmN1cnJlbnRUaW1lPTB9O3ZhciBnPXQubWVkaWEuY3VycmVudFRpbWU7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQubWVkaWEsXCJjdXJyZW50VGltZVwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gZ30sc2V0OmZ1bmN0aW9uKGUpe3ZhciBpPXQuZW1iZWQsbj10Lm1lZGlhLGE9dC5wYXVzZWQscz10LnZvbHVtZSxyPWEmJiFpLmhhc1BsYXllZDtuLnNlZWtpbmc9ITAsT2UuY2FsbCh0LG4sXCJzZWVraW5nXCIpLFByb21pc2UucmVzb2x2ZShyJiZpLnNldFZvbHVtZSgwKSkudGhlbigoZnVuY3Rpb24oKXtyZXR1cm4gaS5zZXRDdXJyZW50VGltZShlKX0pKS50aGVuKChmdW5jdGlvbigpe3JldHVybiByJiZpLnBhdXNlKCl9KSkudGhlbigoZnVuY3Rpb24oKXtyZXR1cm4gciYmaS5zZXRWb2x1bWUocyl9KSkuY2F0Y2goKGZ1bmN0aW9uKCl7fSkpfX0pO3ZhciB5PXQuY29uZmlnLnNwZWVkLnNlbGVjdGVkO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0Lm1lZGlhLFwicGxheWJhY2tSYXRlXCIse2dldDpmdW5jdGlvbigpe3JldHVybiB5fSxzZXQ6ZnVuY3Rpb24oZSl7dC5lbWJlZC5zZXRQbGF5YmFja1JhdGUoZSkudGhlbigoZnVuY3Rpb24oKXt5PWUsT2UuY2FsbCh0LHQubWVkaWEsXCJyYXRlY2hhbmdlXCIpfSkpLmNhdGNoKChmdW5jdGlvbigpe3Qub3B0aW9ucy5zcGVlZD1bMV19KSl9fSk7dmFyIHY9dC5jb25maWcudm9sdW1lO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0Lm1lZGlhLFwidm9sdW1lXCIse2dldDpmdW5jdGlvbigpe3JldHVybiB2fSxzZXQ6ZnVuY3Rpb24oZSl7dC5lbWJlZC5zZXRWb2x1bWUoZSkudGhlbigoZnVuY3Rpb24oKXt2PWUsT2UuY2FsbCh0LHQubWVkaWEsXCJ2b2x1bWVjaGFuZ2VcIil9KSl9fSk7dmFyIGI9dC5jb25maWcubXV0ZWQ7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQubWVkaWEsXCJtdXRlZFwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gYn0sc2V0OmZ1bmN0aW9uKGUpe3ZhciBpPSEhUShlKSYmZTt0LmVtYmVkLnNldFZvbHVtZShpPzA6dC5jb25maWcudm9sdW1lKS50aGVuKChmdW5jdGlvbigpe2I9aSxPZS5jYWxsKHQsdC5tZWRpYSxcInZvbHVtZWNoYW5nZVwiKX0pKX19KTt2YXIgdyxrPXQuY29uZmlnLmxvb3A7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQubWVkaWEsXCJsb29wXCIse2dldDpmdW5jdGlvbigpe3JldHVybiBrfSxzZXQ6ZnVuY3Rpb24oZSl7dmFyIGk9UShlKT9lOnQuY29uZmlnLmxvb3AuYWN0aXZlO3QuZW1iZWQuc2V0TG9vcChpKS50aGVuKChmdW5jdGlvbigpe2s9aX0pKX19KSx0LmVtYmVkLmdldFZpZGVvVXJsKCkudGhlbigoZnVuY3Rpb24oZSl7dz1lLG50LnNldERvd25sb2FkVXJsLmNhbGwodCl9KSkuY2F0Y2goKGZ1bmN0aW9uKHQpe2UuZGVidWcud2Fybih0KX0pKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5tZWRpYSxcImN1cnJlbnRTcmNcIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHd9fSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQubWVkaWEsXCJlbmRlZFwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdC5jdXJyZW50VGltZT09PXQuZHVyYXRpb259fSksUHJvbWlzZS5hbGwoW3QuZW1iZWQuZ2V0VmlkZW9XaWR0aCgpLHQuZW1iZWQuZ2V0VmlkZW9IZWlnaHQoKV0pLnRoZW4oKGZ1bmN0aW9uKGkpe3ZhciBuPW8oaSwyKSxhPW5bMF0scz1uWzFdO3QuZW1iZWQucmF0aW89W2Esc10sUmUuY2FsbChlKX0pKSx0LmVtYmVkLnNldEF1dG9wYXVzZSh0LmNvbmZpZy5hdXRvcGF1c2UpLnRoZW4oKGZ1bmN0aW9uKGUpe3QuY29uZmlnLmF1dG9wYXVzZT1lfSkpLHQuZW1iZWQuZ2V0VmlkZW9UaXRsZSgpLnRoZW4oKGZ1bmN0aW9uKGkpe3QuY29uZmlnLnRpdGxlPWkseXQuc2V0VGl0bGUuY2FsbChlKX0pKSx0LmVtYmVkLmdldEN1cnJlbnRUaW1lKCkudGhlbigoZnVuY3Rpb24oZSl7Zz1lLE9lLmNhbGwodCx0Lm1lZGlhLFwidGltZXVwZGF0ZVwiKX0pKSx0LmVtYmVkLmdldER1cmF0aW9uKCkudGhlbigoZnVuY3Rpb24oZSl7dC5tZWRpYS5kdXJhdGlvbj1lLE9lLmNhbGwodCx0Lm1lZGlhLFwiZHVyYXRpb25jaGFuZ2VcIil9KSksdC5lbWJlZC5nZXRUZXh0VHJhY2tzKCkudGhlbigoZnVuY3Rpb24oZSl7dC5tZWRpYS50ZXh0VHJhY2tzPWUscnQuc2V0dXAuY2FsbCh0KX0pKSx0LmVtYmVkLm9uKFwiY3VlY2hhbmdlXCIsKGZ1bmN0aW9uKGUpe3ZhciBpPWUuY3VlcyxuPSh2b2lkIDA9PT1pP1tdOmkpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIGZ1bmN0aW9uKGUpe3ZhciB0PWRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKSxpPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7cmV0dXJuIHQuYXBwZW5kQ2hpbGQoaSksaS5pbm5lckhUTUw9ZSx0LmZpcnN0Q2hpbGQuaW5uZXJUZXh0fShlLnRleHQpfSkpO3J0LnVwZGF0ZUN1ZXMuY2FsbCh0LG4pfSkpLHQuZW1iZWQub24oXCJsb2FkZWRcIiwoZnVuY3Rpb24oKXsodC5lbWJlZC5nZXRQYXVzZWQoKS50aGVuKChmdW5jdGlvbihlKXtrdC5jYWxsKHQsIWUpLGV8fE9lLmNhbGwodCx0Lm1lZGlhLFwicGxheWluZ1wiKX0pKSxHKHQuZW1iZWQuZWxlbWVudCkmJnQuc3VwcG9ydGVkLnVpKSYmdC5lbWJlZC5lbGVtZW50LnNldEF0dHJpYnV0ZShcInRhYmluZGV4XCIsLTEpfSkpLHQuZW1iZWQub24oXCJidWZmZXJzdGFydFwiLChmdW5jdGlvbigpe09lLmNhbGwodCx0Lm1lZGlhLFwid2FpdGluZ1wiKX0pKSx0LmVtYmVkLm9uKFwiYnVmZmVyZW5kXCIsKGZ1bmN0aW9uKCl7T2UuY2FsbCh0LHQubWVkaWEsXCJwbGF5aW5nXCIpfSkpLHQuZW1iZWQub24oXCJwbGF5XCIsKGZ1bmN0aW9uKCl7a3QuY2FsbCh0LCEwKSxPZS5jYWxsKHQsdC5tZWRpYSxcInBsYXlpbmdcIil9KSksdC5lbWJlZC5vbihcInBhdXNlXCIsKGZ1bmN0aW9uKCl7a3QuY2FsbCh0LCExKX0pKSx0LmVtYmVkLm9uKFwidGltZXVwZGF0ZVwiLChmdW5jdGlvbihlKXt0Lm1lZGlhLnNlZWtpbmc9ITEsZz1lLnNlY29uZHMsT2UuY2FsbCh0LHQubWVkaWEsXCJ0aW1ldXBkYXRlXCIpfSkpLHQuZW1iZWQub24oXCJwcm9ncmVzc1wiLChmdW5jdGlvbihlKXt0Lm1lZGlhLmJ1ZmZlcmVkPWUucGVyY2VudCxPZS5jYWxsKHQsdC5tZWRpYSxcInByb2dyZXNzXCIpLDE9PT1wYXJzZUludChlLnBlcmNlbnQsMTApJiZPZS5jYWxsKHQsdC5tZWRpYSxcImNhbnBsYXl0aHJvdWdoXCIpLHQuZW1iZWQuZ2V0RHVyYXRpb24oKS50aGVuKChmdW5jdGlvbihlKXtlIT09dC5tZWRpYS5kdXJhdGlvbiYmKHQubWVkaWEuZHVyYXRpb249ZSxPZS5jYWxsKHQsdC5tZWRpYSxcImR1cmF0aW9uY2hhbmdlXCIpKX0pKX0pKSx0LmVtYmVkLm9uKFwic2Vla2VkXCIsKGZ1bmN0aW9uKCl7dC5tZWRpYS5zZWVraW5nPSExLE9lLmNhbGwodCx0Lm1lZGlhLFwic2Vla2VkXCIpfSkpLHQuZW1iZWQub24oXCJlbmRlZFwiLChmdW5jdGlvbigpe3QubWVkaWEucGF1c2VkPSEwLE9lLmNhbGwodCx0Lm1lZGlhLFwiZW5kZWRcIil9KSksdC5lbWJlZC5vbihcImVycm9yXCIsKGZ1bmN0aW9uKGUpe3QubWVkaWEuZXJyb3I9ZSxPZS5jYWxsKHQsdC5tZWRpYSxcImVycm9yXCIpfSkpLGkuY3VzdG9tQ29udHJvbHMmJnNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHl0LmJ1aWxkLmNhbGwodCl9KSwwKX19O2Z1bmN0aW9uIEN0KGUpe2UmJiF0aGlzLmVtYmVkLmhhc1BsYXllZCYmKHRoaXMuZW1iZWQuaGFzUGxheWVkPSEwKSx0aGlzLm1lZGlhLnBhdXNlZD09PWUmJih0aGlzLm1lZGlhLnBhdXNlZD0hZSxPZS5jYWxsKHRoaXMsdGhpcy5tZWRpYSxlP1wicGxheVwiOlwicGF1c2VcIikpfWZ1bmN0aW9uIEF0KGUpe3JldHVybiBlLm5vQ29va2llP1wiaHR0cHM6Ly93d3cueW91dHViZS1ub2Nvb2tpZS5jb21cIjpcImh0dHA6XCI9PT13aW5kb3cubG9jYXRpb24ucHJvdG9jb2w/XCJodHRwOi8vd3d3LnlvdXR1YmUuY29tXCI6dm9pZCAwfXZhciBTdD17c2V0dXA6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO2lmKGJlKHRoaXMuZWxlbWVudHMud3JhcHBlcix0aGlzLmNvbmZpZy5jbGFzc05hbWVzLmVtYmVkLCEwKSx6KHdpbmRvdy5ZVCkmJlgod2luZG93LllULlBsYXllcikpU3QucmVhZHkuY2FsbCh0aGlzKTtlbHNle3ZhciB0PXdpbmRvdy5vbllvdVR1YmVJZnJhbWVBUElSZWFkeTt3aW5kb3cub25Zb3VUdWJlSWZyYW1lQVBJUmVhZHk9ZnVuY3Rpb24oKXtYKHQpJiZ0KCksU3QucmVhZHkuY2FsbChlKX0sd3QodGhpcy5jb25maWcudXJscy55b3V0dWJlLnNkaykuY2F0Y2goKGZ1bmN0aW9uKHQpe2UuZGVidWcud2FybihcIllvdVR1YmUgQVBJIGZhaWxlZCB0byBsb2FkXCIsdCl9KSl9fSxnZXRUaXRsZTpmdW5jdGlvbihlKXt2YXIgdD10aGlzO0plKFVlKHRoaXMuY29uZmlnLnVybHMueW91dHViZS5hcGksZSkpLnRoZW4oKGZ1bmN0aW9uKGUpe2lmKHooZSkpe3ZhciBpPWUudGl0bGUsbj1lLmhlaWdodCxhPWUud2lkdGg7dC5jb25maWcudGl0bGU9aSx5dC5zZXRUaXRsZS5jYWxsKHQpLHQuZW1iZWQucmF0aW89W2Esbl19UmUuY2FsbCh0KX0pKS5jYXRjaCgoZnVuY3Rpb24oKXtSZS5jYWxsKHQpfSkpfSxyZWFkeTpmdW5jdGlvbigpe3ZhciBlPXRoaXMsdD1lLmNvbmZpZy55b3V0dWJlLGk9ZS5tZWRpYSYmZS5tZWRpYS5nZXRBdHRyaWJ1dGUoXCJpZFwiKTtpZihhZShpKXx8IWkuc3RhcnRzV2l0aChcInlvdXR1YmUtXCIpKXt2YXIgbj1lLm1lZGlhLmdldEF0dHJpYnV0ZShcInNyY1wiKTthZShuKSYmKG49ZS5tZWRpYS5nZXRBdHRyaWJ1dGUodGhpcy5jb25maWcuYXR0cmlidXRlcy5lbWJlZC5pZCkpO3ZhciBhLHMscj1hZShhPW4pP251bGw6YS5tYXRjaCgvXi4qKHlvdXR1LmJlXFwvfHZcXC98dVxcL1xcd1xcL3xlbWJlZFxcL3x3YXRjaFxcP3Y9fCZ2PSkoW14jJj9dKikuKi8pP1JlZ0V4cC4kMjphLG89aGUoXCJkaXZcIix7aWQ6KHM9ZS5wcm92aWRlcixcIlwiLmNvbmNhdChzLFwiLVwiKS5jb25jYXQoTWF0aC5mbG9vcigxZTQqTWF0aC5yYW5kb20oKSkpKSxcImRhdGEtcG9zdGVyXCI6dC5jdXN0b21Db250cm9scz9lLnBvc3Rlcjp2b2lkIDB9KTtpZihlLm1lZGlhPWdlKG8sZS5tZWRpYSksdC5jdXN0b21Db250cm9scyl7dmFyIGw9ZnVuY3Rpb24oZSl7cmV0dXJuXCJodHRwczovL2kueXRpbWcuY29tL3ZpL1wiLmNvbmNhdChyLFwiL1wiKS5jb25jYXQoZSxcImRlZmF1bHQuanBnXCIpfTtndChsKFwibWF4cmVzXCIpLDEyMSkuY2F0Y2goKGZ1bmN0aW9uKCl7cmV0dXJuIGd0KGwoXCJzZFwiKSwxMjEpfSkpLmNhdGNoKChmdW5jdGlvbigpe3JldHVybiBndChsKFwiaHFcIikpfSkpLnRoZW4oKGZ1bmN0aW9uKHQpe3JldHVybiB5dC5zZXRQb3N0ZXIuY2FsbChlLHQuc3JjKX0pKS50aGVuKChmdW5jdGlvbih0KXt0LmluY2x1ZGVzKFwibWF4cmVzXCIpfHwoZS5lbGVtZW50cy5wb3N0ZXIuc3R5bGUuYmFja2dyb3VuZFNpemU9XCJjb3ZlclwiKX0pKS5jYXRjaCgoZnVuY3Rpb24oKXt9KSl9ZS5lbWJlZD1uZXcgd2luZG93LllULlBsYXllcihlLm1lZGlhLHt2aWRlb0lkOnIsaG9zdDpBdCh0KSxwbGF5ZXJWYXJzOmNlKHt9LHthdXRvcGxheTplLmNvbmZpZy5hdXRvcGxheT8xOjAsaGw6ZS5jb25maWcuaGwsY29udHJvbHM6ZS5zdXBwb3J0ZWQudWkmJnQuY3VzdG9tQ29udHJvbHM/MDoxLGRpc2FibGVrYjoxLHBsYXlzaW5saW5lOmUuY29uZmlnLmZ1bGxzY3JlZW4uaW9zTmF0aXZlPzA6MSxjY19sb2FkX3BvbGljeTplLmNhcHRpb25zLmFjdGl2ZT8xOjAsY2NfbGFuZ19wcmVmOmUuY29uZmlnLmNhcHRpb25zLmxhbmd1YWdlLHdpZGdldF9yZWZlcnJlcjp3aW5kb3c/d2luZG93LmxvY2F0aW9uLmhyZWY6bnVsbH0sdCksZXZlbnRzOntvbkVycm9yOmZ1bmN0aW9uKHQpe2lmKCFlLm1lZGlhLmVycm9yKXt2YXIgaT10LmRhdGEsbj17MjpcIlRoZSByZXF1ZXN0IGNvbnRhaW5zIGFuIGludmFsaWQgcGFyYW1ldGVyIHZhbHVlLiBGb3IgZXhhbXBsZSwgdGhpcyBlcnJvciBvY2N1cnMgaWYgeW91IHNwZWNpZnkgYSB2aWRlbyBJRCB0aGF0IGRvZXMgbm90IGhhdmUgMTEgY2hhcmFjdGVycywgb3IgaWYgdGhlIHZpZGVvIElEIGNvbnRhaW5zIGludmFsaWQgY2hhcmFjdGVycywgc3VjaCBhcyBleGNsYW1hdGlvbiBwb2ludHMgb3IgYXN0ZXJpc2tzLlwiLDU6XCJUaGUgcmVxdWVzdGVkIGNvbnRlbnQgY2Fubm90IGJlIHBsYXllZCBpbiBhbiBIVE1MNSBwbGF5ZXIgb3IgYW5vdGhlciBlcnJvciByZWxhdGVkIHRvIHRoZSBIVE1MNSBwbGF5ZXIgaGFzIG9jY3VycmVkLlwiLDEwMDpcIlRoZSB2aWRlbyByZXF1ZXN0ZWQgd2FzIG5vdCBmb3VuZC4gVGhpcyBlcnJvciBvY2N1cnMgd2hlbiBhIHZpZGVvIGhhcyBiZWVuIHJlbW92ZWQgKGZvciBhbnkgcmVhc29uKSBvciBoYXMgYmVlbiBtYXJrZWQgYXMgcHJpdmF0ZS5cIiwxMDE6XCJUaGUgb3duZXIgb2YgdGhlIHJlcXVlc3RlZCB2aWRlbyBkb2VzIG5vdCBhbGxvdyBpdCB0byBiZSBwbGF5ZWQgaW4gZW1iZWRkZWQgcGxheWVycy5cIiwxNTA6XCJUaGUgb3duZXIgb2YgdGhlIHJlcXVlc3RlZCB2aWRlbyBkb2VzIG5vdCBhbGxvdyBpdCB0byBiZSBwbGF5ZWQgaW4gZW1iZWRkZWQgcGxheWVycy5cIn1baV18fFwiQW4gdW5rbm93biBlcnJvciBvY2N1cmVkXCI7ZS5tZWRpYS5lcnJvcj17Y29kZTppLG1lc3NhZ2U6bn0sT2UuY2FsbChlLGUubWVkaWEsXCJlcnJvclwiKX19LG9uUGxheWJhY2tSYXRlQ2hhbmdlOmZ1bmN0aW9uKHQpe3ZhciBpPXQudGFyZ2V0O2UubWVkaWEucGxheWJhY2tSYXRlPWkuZ2V0UGxheWJhY2tSYXRlKCksT2UuY2FsbChlLGUubWVkaWEsXCJyYXRlY2hhbmdlXCIpfSxvblJlYWR5OmZ1bmN0aW9uKGkpe2lmKCFYKGUubWVkaWEucGxheSkpe3ZhciBuPWkudGFyZ2V0O1N0LmdldFRpdGxlLmNhbGwoZSxyKSxlLm1lZGlhLnBsYXk9ZnVuY3Rpb24oKXtDdC5jYWxsKGUsITApLG4ucGxheVZpZGVvKCl9LGUubWVkaWEucGF1c2U9ZnVuY3Rpb24oKXtDdC5jYWxsKGUsITEpLG4ucGF1c2VWaWRlbygpfSxlLm1lZGlhLnN0b3A9ZnVuY3Rpb24oKXtuLnN0b3BWaWRlbygpfSxlLm1lZGlhLmR1cmF0aW9uPW4uZ2V0RHVyYXRpb24oKSxlLm1lZGlhLnBhdXNlZD0hMCxlLm1lZGlhLmN1cnJlbnRUaW1lPTAsT2JqZWN0LmRlZmluZVByb3BlcnR5KGUubWVkaWEsXCJjdXJyZW50VGltZVwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gTnVtYmVyKG4uZ2V0Q3VycmVudFRpbWUoKSl9LHNldDpmdW5jdGlvbih0KXtlLnBhdXNlZCYmIWUuZW1iZWQuaGFzUGxheWVkJiZlLmVtYmVkLm11dGUoKSxlLm1lZGlhLnNlZWtpbmc9ITAsT2UuY2FsbChlLGUubWVkaWEsXCJzZWVraW5nXCIpLG4uc2Vla1RvKHQpfX0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLm1lZGlhLFwicGxheWJhY2tSYXRlXCIse2dldDpmdW5jdGlvbigpe3JldHVybiBuLmdldFBsYXliYWNrUmF0ZSgpfSxzZXQ6ZnVuY3Rpb24oZSl7bi5zZXRQbGF5YmFja1JhdGUoZSl9fSk7dmFyIGE9ZS5jb25maWcudm9sdW1lO09iamVjdC5kZWZpbmVQcm9wZXJ0eShlLm1lZGlhLFwidm9sdW1lXCIse2dldDpmdW5jdGlvbigpe3JldHVybiBhfSxzZXQ6ZnVuY3Rpb24odCl7YT10LG4uc2V0Vm9sdW1lKDEwMCphKSxPZS5jYWxsKGUsZS5tZWRpYSxcInZvbHVtZWNoYW5nZVwiKX19KTt2YXIgcz1lLmNvbmZpZy5tdXRlZDtPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5tZWRpYSxcIm11dGVkXCIse2dldDpmdW5jdGlvbigpe3JldHVybiBzfSxzZXQ6ZnVuY3Rpb24odCl7dmFyIGk9USh0KT90OnM7cz1pLG5baT9cIm11dGVcIjpcInVuTXV0ZVwiXSgpLG4uc2V0Vm9sdW1lKDEwMCphKSxPZS5jYWxsKGUsZS5tZWRpYSxcInZvbHVtZWNoYW5nZVwiKX19KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5tZWRpYSxcImN1cnJlbnRTcmNcIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIG4uZ2V0VmlkZW9VcmwoKX19KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5tZWRpYSxcImVuZGVkXCIse2dldDpmdW5jdGlvbigpe3JldHVybiBlLmN1cnJlbnRUaW1lPT09ZS5kdXJhdGlvbn19KTt2YXIgbz1uLmdldEF2YWlsYWJsZVBsYXliYWNrUmF0ZXMoKTtlLm9wdGlvbnMuc3BlZWQ9by5maWx0ZXIoKGZ1bmN0aW9uKHQpe3JldHVybiBlLmNvbmZpZy5zcGVlZC5vcHRpb25zLmluY2x1ZGVzKHQpfSkpLGUuc3VwcG9ydGVkLnVpJiZ0LmN1c3RvbUNvbnRyb2xzJiZlLm1lZGlhLnNldEF0dHJpYnV0ZShcInRhYmluZGV4XCIsLTEpLE9lLmNhbGwoZSxlLm1lZGlhLFwidGltZXVwZGF0ZVwiKSxPZS5jYWxsKGUsZS5tZWRpYSxcImR1cmF0aW9uY2hhbmdlXCIpLGNsZWFySW50ZXJ2YWwoZS50aW1lcnMuYnVmZmVyaW5nKSxlLnRpbWVycy5idWZmZXJpbmc9c2V0SW50ZXJ2YWwoKGZ1bmN0aW9uKCl7ZS5tZWRpYS5idWZmZXJlZD1uLmdldFZpZGVvTG9hZGVkRnJhY3Rpb24oKSwobnVsbD09PWUubWVkaWEubGFzdEJ1ZmZlcmVkfHxlLm1lZGlhLmxhc3RCdWZmZXJlZDxlLm1lZGlhLmJ1ZmZlcmVkKSYmT2UuY2FsbChlLGUubWVkaWEsXCJwcm9ncmVzc1wiKSxlLm1lZGlhLmxhc3RCdWZmZXJlZD1lLm1lZGlhLmJ1ZmZlcmVkLDE9PT1lLm1lZGlhLmJ1ZmZlcmVkJiYoY2xlYXJJbnRlcnZhbChlLnRpbWVycy5idWZmZXJpbmcpLE9lLmNhbGwoZSxlLm1lZGlhLFwiY2FucGxheXRocm91Z2hcIikpfSksMjAwKSx0LmN1c3RvbUNvbnRyb2xzJiZzZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB5dC5idWlsZC5jYWxsKGUpfSksNTApfX0sb25TdGF0ZUNoYW5nZTpmdW5jdGlvbihpKXt2YXIgbj1pLnRhcmdldDtzd2l0Y2goY2xlYXJJbnRlcnZhbChlLnRpbWVycy5wbGF5aW5nKSxlLm1lZGlhLnNlZWtpbmcmJlsxLDJdLmluY2x1ZGVzKGkuZGF0YSkmJihlLm1lZGlhLnNlZWtpbmc9ITEsT2UuY2FsbChlLGUubWVkaWEsXCJzZWVrZWRcIikpLGkuZGF0YSl7Y2FzZS0xOk9lLmNhbGwoZSxlLm1lZGlhLFwidGltZXVwZGF0ZVwiKSxlLm1lZGlhLmJ1ZmZlcmVkPW4uZ2V0VmlkZW9Mb2FkZWRGcmFjdGlvbigpLE9lLmNhbGwoZSxlLm1lZGlhLFwicHJvZ3Jlc3NcIik7YnJlYWs7Y2FzZSAwOkN0LmNhbGwoZSwhMSksZS5tZWRpYS5sb29wPyhuLnN0b3BWaWRlbygpLG4ucGxheVZpZGVvKCkpOk9lLmNhbGwoZSxlLm1lZGlhLFwiZW5kZWRcIik7YnJlYWs7Y2FzZSAxOnQuY3VzdG9tQ29udHJvbHMmJiFlLmNvbmZpZy5hdXRvcGxheSYmZS5tZWRpYS5wYXVzZWQmJiFlLmVtYmVkLmhhc1BsYXllZD9lLm1lZGlhLnBhdXNlKCk6KEN0LmNhbGwoZSwhMCksT2UuY2FsbChlLGUubWVkaWEsXCJwbGF5aW5nXCIpLGUudGltZXJzLnBsYXlpbmc9c2V0SW50ZXJ2YWwoKGZ1bmN0aW9uKCl7T2UuY2FsbChlLGUubWVkaWEsXCJ0aW1ldXBkYXRlXCIpfSksNTApLGUubWVkaWEuZHVyYXRpb24hPT1uLmdldER1cmF0aW9uKCkmJihlLm1lZGlhLmR1cmF0aW9uPW4uZ2V0RHVyYXRpb24oKSxPZS5jYWxsKGUsZS5tZWRpYSxcImR1cmF0aW9uY2hhbmdlXCIpKSk7YnJlYWs7Y2FzZSAyOmUubXV0ZWR8fGUuZW1iZWQudW5NdXRlKCksQ3QuY2FsbChlLCExKTticmVhaztjYXNlIDM6T2UuY2FsbChlLGUubWVkaWEsXCJ3YWl0aW5nXCIpfU9lLmNhbGwoZSxlLmVsZW1lbnRzLmNvbnRhaW5lcixcInN0YXRlY2hhbmdlXCIsITEse2NvZGU6aS5kYXRhfSl9fX0pfX19LFB0PXtzZXR1cDpmdW5jdGlvbigpe3RoaXMubWVkaWE/KGJlKHRoaXMuZWxlbWVudHMuY29udGFpbmVyLHRoaXMuY29uZmlnLmNsYXNzTmFtZXMudHlwZS5yZXBsYWNlKFwiezB9XCIsdGhpcy50eXBlKSwhMCksYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5wcm92aWRlci5yZXBsYWNlKFwiezB9XCIsdGhpcy5wcm92aWRlciksITApLHRoaXMuaXNFbWJlZCYmYmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy50eXBlLnJlcGxhY2UoXCJ7MH1cIixcInZpZGVvXCIpLCEwKSx0aGlzLmlzVmlkZW8mJih0aGlzLmVsZW1lbnRzLndyYXBwZXI9aGUoXCJkaXZcIix7Y2xhc3M6dGhpcy5jb25maWcuY2xhc3NOYW1lcy52aWRlb30pLHVlKHRoaXMubWVkaWEsdGhpcy5lbGVtZW50cy53cmFwcGVyKSx0aGlzLmVsZW1lbnRzLnBvc3Rlcj1oZShcImRpdlwiLHtjbGFzczp0aGlzLmNvbmZpZy5jbGFzc05hbWVzLnBvc3RlcixoaWRkZW46XCJcIn0pLHRoaXMuZWxlbWVudHMud3JhcHBlci5hcHBlbmRDaGlsZCh0aGlzLmVsZW1lbnRzLnBvc3RlcikpLHRoaXMuaXNIVE1MNT9WZS5zZXR1cC5jYWxsKHRoaXMpOnRoaXMuaXNZb3VUdWJlP1N0LnNldHVwLmNhbGwodGhpcyk6dGhpcy5pc1ZpbWVvJiZUdC5zZXR1cC5jYWxsKHRoaXMpKTp0aGlzLmRlYnVnLndhcm4oXCJObyBtZWRpYSBlbGVtZW50IGZvdW5kIVwiKX19LEV0PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gdChpKXt2YXIgbj10aGlzO2UodGhpcyx0KSx0aGlzLnBsYXllcj1pLHRoaXMuY29uZmlnPWkuY29uZmlnLmFkcyx0aGlzLnBsYXlpbmc9ITEsdGhpcy5pbml0aWFsaXplZD0hMSx0aGlzLmVsZW1lbnRzPXtjb250YWluZXI6bnVsbCxkaXNwbGF5Q29udGFpbmVyOm51bGx9LHRoaXMubWFuYWdlcj1udWxsLHRoaXMubG9hZGVyPW51bGwsdGhpcy5jdWVQb2ludHM9bnVsbCx0aGlzLmV2ZW50cz17fSx0aGlzLnNhZmV0eVRpbWVyPW51bGwsdGhpcy5jb3VudGRvd25UaW1lcj1udWxsLHRoaXMubWFuYWdlclByb21pc2U9bmV3IFByb21pc2UoKGZ1bmN0aW9uKGUsdCl7bi5vbihcImxvYWRlZFwiLGUpLG4ub24oXCJlcnJvclwiLHQpfSkpLHRoaXMubG9hZCgpfXJldHVybiBpKHQsW3trZXk6XCJsb2FkXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMuZW5hYmxlZCYmKHood2luZG93Lmdvb2dsZSkmJnood2luZG93Lmdvb2dsZS5pbWEpP3RoaXMucmVhZHkoKTp3dCh0aGlzLnBsYXllci5jb25maWcudXJscy5nb29nbGVJTUEuc2RrKS50aGVuKChmdW5jdGlvbigpe2UucmVhZHkoKX0pKS5jYXRjaCgoZnVuY3Rpb24oKXtlLnRyaWdnZXIoXCJlcnJvclwiLG5ldyBFcnJvcihcIkdvb2dsZSBJTUEgU0RLIGZhaWxlZCB0byBsb2FkXCIpKX0pKSl9fSx7a2V5OlwicmVhZHlcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlLHQ9dGhpczt0aGlzLmVuYWJsZWR8fCgoZT10aGlzKS5tYW5hZ2VyJiZlLm1hbmFnZXIuZGVzdHJveSgpLGUuZWxlbWVudHMuZGlzcGxheUNvbnRhaW5lciYmZS5lbGVtZW50cy5kaXNwbGF5Q29udGFpbmVyLmRlc3Ryb3koKSxlLmVsZW1lbnRzLmNvbnRhaW5lci5yZW1vdmUoKSksdGhpcy5zdGFydFNhZmV0eVRpbWVyKDEyZTMsXCJyZWFkeSgpXCIpLHRoaXMubWFuYWdlclByb21pc2UudGhlbigoZnVuY3Rpb24oKXt0LmNsZWFyU2FmZXR5VGltZXIoXCJvbkFkc01hbmFnZXJMb2FkZWQoKVwiKX0pKSx0aGlzLmxpc3RlbmVycygpLHRoaXMuc2V0dXBJTUEoKX19LHtrZXk6XCJzZXR1cElNQVwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcj1oZShcImRpdlwiLHtjbGFzczp0aGlzLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5hZHN9KSx0aGlzLnBsYXllci5lbGVtZW50cy5jb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50cy5jb250YWluZXIpLGdvb2dsZS5pbWEuc2V0dGluZ3Muc2V0VnBhaWRNb2RlKGdvb2dsZS5pbWEuSW1hU2RrU2V0dGluZ3MuVnBhaWRNb2RlLkVOQUJMRUQpLGdvb2dsZS5pbWEuc2V0dGluZ3Muc2V0TG9jYWxlKHRoaXMucGxheWVyLmNvbmZpZy5hZHMubGFuZ3VhZ2UpLGdvb2dsZS5pbWEuc2V0dGluZ3Muc2V0RGlzYWJsZUN1c3RvbVBsYXliYWNrRm9ySU9TMTBQbHVzKHRoaXMucGxheWVyLmNvbmZpZy5wbGF5c2lubGluZSksdGhpcy5lbGVtZW50cy5kaXNwbGF5Q29udGFpbmVyPW5ldyBnb29nbGUuaW1hLkFkRGlzcGxheUNvbnRhaW5lcih0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcix0aGlzLnBsYXllci5tZWRpYSksdGhpcy5sb2FkZXI9bmV3IGdvb2dsZS5pbWEuQWRzTG9hZGVyKHRoaXMuZWxlbWVudHMuZGlzcGxheUNvbnRhaW5lciksdGhpcy5sb2FkZXIuYWRkRXZlbnRMaXN0ZW5lcihnb29nbGUuaW1hLkFkc01hbmFnZXJMb2FkZWRFdmVudC5UeXBlLkFEU19NQU5BR0VSX0xPQURFRCwoZnVuY3Rpb24odCl7cmV0dXJuIGUub25BZHNNYW5hZ2VyTG9hZGVkKHQpfSksITEpLHRoaXMubG9hZGVyLmFkZEV2ZW50TGlzdGVuZXIoZ29vZ2xlLmltYS5BZEVycm9yRXZlbnQuVHlwZS5BRF9FUlJPUiwoZnVuY3Rpb24odCl7cmV0dXJuIGUub25BZEVycm9yKHQpfSksITEpLHRoaXMucmVxdWVzdEFkcygpfX0se2tleTpcInJlcXVlc3RBZHNcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPXRoaXMucGxheWVyLmVsZW1lbnRzLmNvbnRhaW5lcjt0cnl7dmFyIHQ9bmV3IGdvb2dsZS5pbWEuQWRzUmVxdWVzdDt0LmFkVGFnVXJsPXRoaXMudGFnVXJsLHQubGluZWFyQWRTbG90V2lkdGg9ZS5vZmZzZXRXaWR0aCx0LmxpbmVhckFkU2xvdEhlaWdodD1lLm9mZnNldEhlaWdodCx0Lm5vbkxpbmVhckFkU2xvdFdpZHRoPWUub2Zmc2V0V2lkdGgsdC5ub25MaW5lYXJBZFNsb3RIZWlnaHQ9ZS5vZmZzZXRIZWlnaHQsdC5mb3JjZU5vbkxpbmVhckZ1bGxTbG90PSExLHQuc2V0QWRXaWxsUGxheU11dGVkKCF0aGlzLnBsYXllci5tdXRlZCksdGhpcy5sb2FkZXIucmVxdWVzdEFkcyh0KX1jYXRjaChlKXt0aGlzLm9uQWRFcnJvcihlKX19fSx7a2V5OlwicG9sbENvdW50ZG93blwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdJiZhcmd1bWVudHNbMF07aWYoIXQpcmV0dXJuIGNsZWFySW50ZXJ2YWwodGhpcy5jb3VudGRvd25UaW1lciksdm9pZCB0aGlzLmVsZW1lbnRzLmNvbnRhaW5lci5yZW1vdmVBdHRyaWJ1dGUoXCJkYXRhLWJhZGdlLXRleHRcIik7dmFyIGk9ZnVuY3Rpb24oKXt2YXIgdD1pdChNYXRoLm1heChlLm1hbmFnZXIuZ2V0UmVtYWluaW5nVGltZSgpLDApKSxpPVwiXCIuY29uY2F0KFhlKFwiYWR2ZXJ0aXNlbWVudFwiLGUucGxheWVyLmNvbmZpZyksXCIgLSBcIikuY29uY2F0KHQpO2UuZWxlbWVudHMuY29udGFpbmVyLnNldEF0dHJpYnV0ZShcImRhdGEtYmFkZ2UtdGV4dFwiLGkpfTt0aGlzLmNvdW50ZG93blRpbWVyPXNldEludGVydmFsKGksMTAwKX19LHtrZXk6XCJvbkFkc01hbmFnZXJMb2FkZWRcIix2YWx1ZTpmdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuZW5hYmxlZCl7dmFyIGk9bmV3IGdvb2dsZS5pbWEuQWRzUmVuZGVyaW5nU2V0dGluZ3M7aS5yZXN0b3JlQ3VzdG9tUGxheWJhY2tTdGF0ZU9uQWRCcmVha0NvbXBsZXRlPSEwLGkuZW5hYmxlUHJlbG9hZGluZz0hMCx0aGlzLm1hbmFnZXI9ZS5nZXRBZHNNYW5hZ2VyKHRoaXMucGxheWVyLGkpLHRoaXMuY3VlUG9pbnRzPXRoaXMubWFuYWdlci5nZXRDdWVQb2ludHMoKSx0aGlzLm1hbmFnZXIuYWRkRXZlbnRMaXN0ZW5lcihnb29nbGUuaW1hLkFkRXJyb3JFdmVudC5UeXBlLkFEX0VSUk9SLChmdW5jdGlvbihlKXtyZXR1cm4gdC5vbkFkRXJyb3IoZSl9KSksT2JqZWN0LmtleXMoZ29vZ2xlLmltYS5BZEV2ZW50LlR5cGUpLmZvckVhY2goKGZ1bmN0aW9uKGUpe3QubWFuYWdlci5hZGRFdmVudExpc3RlbmVyKGdvb2dsZS5pbWEuQWRFdmVudC5UeXBlW2VdLChmdW5jdGlvbihlKXtyZXR1cm4gdC5vbkFkRXZlbnQoZSl9KSl9KSksdGhpcy50cmlnZ2VyKFwibG9hZGVkXCIpfX19LHtrZXk6XCJhZGRDdWVQb2ludHNcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPXRoaXM7YWUodGhpcy5jdWVQb2ludHMpfHx0aGlzLmN1ZVBvaW50cy5mb3JFYWNoKChmdW5jdGlvbih0KXtpZigwIT09dCYmLTEhPT10JiZ0PGUucGxheWVyLmR1cmF0aW9uKXt2YXIgaT1lLnBsYXllci5lbGVtZW50cy5wcm9ncmVzcztpZihHKGkpKXt2YXIgbj0xMDAvZS5wbGF5ZXIuZHVyYXRpb24qdCxhPWhlKFwic3BhblwiLHtjbGFzczplLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5jdWVzfSk7YS5zdHlsZS5sZWZ0PVwiXCIuY29uY2F0KG4udG9TdHJpbmcoKSxcIiVcIiksaS5hcHBlbmRDaGlsZChhKX19fSkpfX0se2tleTpcIm9uQWRFdmVudFwiLHZhbHVlOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMsaT10aGlzLnBsYXllci5lbGVtZW50cy5jb250YWluZXIsbj1lLmdldEFkKCksYT1lLmdldEFkRGF0YSgpO3N3aXRjaChmdW5jdGlvbihlKXtPZS5jYWxsKHQucGxheWVyLHQucGxheWVyLm1lZGlhLFwiYWRzXCIuY29uY2F0KGUucmVwbGFjZSgvXy9nLFwiXCIpLnRvTG93ZXJDYXNlKCkpKX0oZS50eXBlKSxlLnR5cGUpe2Nhc2UgZ29vZ2xlLmltYS5BZEV2ZW50LlR5cGUuTE9BREVEOnRoaXMudHJpZ2dlcihcImxvYWRlZFwiKSx0aGlzLnBvbGxDb3VudGRvd24oITApLG4uaXNMaW5lYXIoKXx8KG4ud2lkdGg9aS5vZmZzZXRXaWR0aCxuLmhlaWdodD1pLm9mZnNldEhlaWdodCk7YnJlYWs7Y2FzZSBnb29nbGUuaW1hLkFkRXZlbnQuVHlwZS5TVEFSVEVEOnRoaXMubWFuYWdlci5zZXRWb2x1bWUodGhpcy5wbGF5ZXIudm9sdW1lKTticmVhaztjYXNlIGdvb2dsZS5pbWEuQWRFdmVudC5UeXBlLkFMTF9BRFNfQ09NUExFVEVEOnRoaXMucGxheWVyLmVuZGVkP3RoaXMubG9hZEFkcygpOnRoaXMubG9hZGVyLmNvbnRlbnRDb21wbGV0ZSgpO2JyZWFrO2Nhc2UgZ29vZ2xlLmltYS5BZEV2ZW50LlR5cGUuQ09OVEVOVF9QQVVTRV9SRVFVRVNURUQ6dGhpcy5wYXVzZUNvbnRlbnQoKTticmVhaztjYXNlIGdvb2dsZS5pbWEuQWRFdmVudC5UeXBlLkNPTlRFTlRfUkVTVU1FX1JFUVVFU1RFRDp0aGlzLnBvbGxDb3VudGRvd24oKSx0aGlzLnJlc3VtZUNvbnRlbnQoKTticmVhaztjYXNlIGdvb2dsZS5pbWEuQWRFdmVudC5UeXBlLkxPRzphLmFkRXJyb3ImJnRoaXMucGxheWVyLmRlYnVnLndhcm4oXCJOb24tZmF0YWwgYWQgZXJyb3I6IFwiLmNvbmNhdChhLmFkRXJyb3IuZ2V0TWVzc2FnZSgpKSl9fX0se2tleTpcIm9uQWRFcnJvclwiLHZhbHVlOmZ1bmN0aW9uKGUpe3RoaXMuY2FuY2VsKCksdGhpcy5wbGF5ZXIuZGVidWcud2FybihcIkFkcyBlcnJvclwiLGUpfX0se2tleTpcImxpc3RlbmVyc1wiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGUsdD10aGlzLGk9dGhpcy5wbGF5ZXIuZWxlbWVudHMuY29udGFpbmVyO3RoaXMucGxheWVyLm9uKFwiY2FucGxheVwiLChmdW5jdGlvbigpe3QuYWRkQ3VlUG9pbnRzKCl9KSksdGhpcy5wbGF5ZXIub24oXCJlbmRlZFwiLChmdW5jdGlvbigpe3QubG9hZGVyLmNvbnRlbnRDb21wbGV0ZSgpfSkpLHRoaXMucGxheWVyLm9uKFwidGltZXVwZGF0ZVwiLChmdW5jdGlvbigpe2U9dC5wbGF5ZXIuY3VycmVudFRpbWV9KSksdGhpcy5wbGF5ZXIub24oXCJzZWVrZWRcIiwoZnVuY3Rpb24oKXt2YXIgaT10LnBsYXllci5jdXJyZW50VGltZTthZSh0LmN1ZVBvaW50cyl8fHQuY3VlUG9pbnRzLmZvckVhY2goKGZ1bmN0aW9uKG4sYSl7ZTxuJiZuPGkmJih0Lm1hbmFnZXIuZGlzY2FyZEFkQnJlYWsoKSx0LmN1ZVBvaW50cy5zcGxpY2UoYSwxKSl9KSl9KSksd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJyZXNpemVcIiwoZnVuY3Rpb24oKXt0Lm1hbmFnZXImJnQubWFuYWdlci5yZXNpemUoaS5vZmZzZXRXaWR0aCxpLm9mZnNldEhlaWdodCxnb29nbGUuaW1hLlZpZXdNb2RlLk5PUk1BTCl9KSl9fSx7a2V5OlwicGxheVwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PXRoaXMucGxheWVyLmVsZW1lbnRzLmNvbnRhaW5lcjt0aGlzLm1hbmFnZXJQcm9taXNlfHx0aGlzLnJlc3VtZUNvbnRlbnQoKSx0aGlzLm1hbmFnZXJQcm9taXNlLnRoZW4oKGZ1bmN0aW9uKCl7ZS5tYW5hZ2VyLnNldFZvbHVtZShlLnBsYXllci52b2x1bWUpLGUuZWxlbWVudHMuZGlzcGxheUNvbnRhaW5lci5pbml0aWFsaXplKCk7dHJ5e2UuaW5pdGlhbGl6ZWR8fChlLm1hbmFnZXIuaW5pdCh0Lm9mZnNldFdpZHRoLHQub2Zmc2V0SGVpZ2h0LGdvb2dsZS5pbWEuVmlld01vZGUuTk9STUFMKSxlLm1hbmFnZXIuc3RhcnQoKSksZS5pbml0aWFsaXplZD0hMH1jYXRjaCh0KXtlLm9uQWRFcnJvcih0KX19KSkuY2F0Y2goKGZ1bmN0aW9uKCl7fSkpfX0se2tleTpcInJlc3VtZUNvbnRlbnRcIix2YWx1ZTpmdW5jdGlvbigpe3RoaXMuZWxlbWVudHMuY29udGFpbmVyLnN0eWxlLnpJbmRleD1cIlwiLHRoaXMucGxheWluZz0hMSxEZSh0aGlzLnBsYXllci5tZWRpYS5wbGF5KCkpfX0se2tleTpcInBhdXNlQ29udGVudFwiLHZhbHVlOmZ1bmN0aW9uKCl7dGhpcy5lbGVtZW50cy5jb250YWluZXIuc3R5bGUuekluZGV4PTMsdGhpcy5wbGF5aW5nPSEwLHRoaXMucGxheWVyLm1lZGlhLnBhdXNlKCl9fSx7a2V5OlwiY2FuY2VsXCIsdmFsdWU6ZnVuY3Rpb24oKXt0aGlzLmluaXRpYWxpemVkJiZ0aGlzLnJlc3VtZUNvbnRlbnQoKSx0aGlzLnRyaWdnZXIoXCJlcnJvclwiKSx0aGlzLmxvYWRBZHMoKX19LHtrZXk6XCJsb2FkQWRzXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMubWFuYWdlclByb21pc2UudGhlbigoZnVuY3Rpb24oKXtlLm1hbmFnZXImJmUubWFuYWdlci5kZXN0cm95KCksZS5tYW5hZ2VyUHJvbWlzZT1uZXcgUHJvbWlzZSgoZnVuY3Rpb24odCl7ZS5vbihcImxvYWRlZFwiLHQpLGUucGxheWVyLmRlYnVnLmxvZyhlLm1hbmFnZXIpfSkpLGUuaW5pdGlhbGl6ZWQ9ITEsZS5yZXF1ZXN0QWRzKCl9KSkuY2F0Y2goKGZ1bmN0aW9uKCl7fSkpfX0se2tleTpcInRyaWdnZXJcIix2YWx1ZTpmdW5jdGlvbihlKXtmb3IodmFyIHQ9dGhpcyxpPWFyZ3VtZW50cy5sZW5ndGgsbj1uZXcgQXJyYXkoaT4xP2ktMTowKSxhPTE7YTxpO2ErKyluW2EtMV09YXJndW1lbnRzW2FdO3ZhciBzPXRoaXMuZXZlbnRzW2VdOyQocykmJnMuZm9yRWFjaCgoZnVuY3Rpb24oZSl7WChlKSYmZS5hcHBseSh0LG4pfSkpfX0se2tleTpcIm9uXCIsdmFsdWU6ZnVuY3Rpb24oZSx0KXtyZXR1cm4gJCh0aGlzLmV2ZW50c1tlXSl8fCh0aGlzLmV2ZW50c1tlXT1bXSksdGhpcy5ldmVudHNbZV0ucHVzaCh0KSx0aGlzfX0se2tleTpcInN0YXJ0U2FmZXR5VGltZXJcIix2YWx1ZTpmdW5jdGlvbihlLHQpe3ZhciBpPXRoaXM7dGhpcy5wbGF5ZXIuZGVidWcubG9nKFwiU2FmZXR5IHRpbWVyIGludm9rZWQgZnJvbTogXCIuY29uY2F0KHQpKSx0aGlzLnNhZmV0eVRpbWVyPXNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7aS5jYW5jZWwoKSxpLmNsZWFyU2FmZXR5VGltZXIoXCJzdGFydFNhZmV0eVRpbWVyKClcIil9KSxlKX19LHtrZXk6XCJjbGVhclNhZmV0eVRpbWVyXCIsdmFsdWU6ZnVuY3Rpb24oZSl7Vyh0aGlzLnNhZmV0eVRpbWVyKXx8KHRoaXMucGxheWVyLmRlYnVnLmxvZyhcIlNhZmV0eSB0aW1lciBjbGVhcmVkIGZyb206IFwiLmNvbmNhdChlKSksY2xlYXJUaW1lb3V0KHRoaXMuc2FmZXR5VGltZXIpLHRoaXMuc2FmZXR5VGltZXI9bnVsbCl9fSx7a2V5OlwiZW5hYmxlZFwiLGdldDpmdW5jdGlvbigpe3ZhciBlPXRoaXMuY29uZmlnO3JldHVybiB0aGlzLnBsYXllci5pc0hUTUw1JiZ0aGlzLnBsYXllci5pc1ZpZGVvJiZlLmVuYWJsZWQmJighYWUoZS5wdWJsaXNoZXJJZCl8fG5lKGUudGFnVXJsKSl9fSx7a2V5OlwidGFnVXJsXCIsZ2V0OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5jb25maWc7aWYobmUoZS50YWdVcmwpKXJldHVybiBlLnRhZ1VybDt2YXIgdD17QVZfUFVCTElTSEVSSUQ6XCI1OGMyNWJiMDA3M2VmNDQ4YjEwODdhZDZcIixBVl9DSEFOTkVMSUQ6XCI1YTA0NThkYzI4YTA2MTQ1ZTQ1MTlkMjFcIixBVl9VUkw6d2luZG93LmxvY2F0aW9uLmhvc3RuYW1lLGNiOkRhdGUubm93KCksQVZfV0lEVEg6NjQwLEFWX0hFSUdIVDo0ODAsQVZfQ0RJTTI6ZS5wdWJsaXNoZXJJZH07cmV0dXJuXCJcIi5jb25jYXQoXCJodHRwczovL2dvLmFuaXZpZXcuY29tL2FwaS9hZHNlcnZlcjYvdmFzdC9cIixcIj9cIikuY29uY2F0KHN0KHQpKX19XSksdH0oKSxOdD1mdW5jdGlvbihlLHQpe3ZhciBpPXt9O3JldHVybiBlPnQud2lkdGgvdC5oZWlnaHQ/KGkud2lkdGg9dC53aWR0aCxpLmhlaWdodD0xL2UqdC53aWR0aCk6KGkuaGVpZ2h0PXQuaGVpZ2h0LGkud2lkdGg9ZSp0LmhlaWdodCksaX0sTXQ9ZnVuY3Rpb24oKXtmdW5jdGlvbiB0KGkpe2UodGhpcyx0KSx0aGlzLnBsYXllcj1pLHRoaXMudGh1bWJuYWlscz1bXSx0aGlzLmxvYWRlZD0hMSx0aGlzLmxhc3RNb3VzZU1vdmVUaW1lPURhdGUubm93KCksdGhpcy5tb3VzZURvd249ITEsdGhpcy5sb2FkZWRJbWFnZXM9W10sdGhpcy5lbGVtZW50cz17dGh1bWI6e30sc2NydWJiaW5nOnt9fSx0aGlzLmxvYWQoKX1yZXR1cm4gaSh0LFt7a2V5OlwibG9hZFwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLnBsYXllci5lbGVtZW50cy5kaXNwbGF5LnNlZWtUb29sdGlwJiYodGhpcy5wbGF5ZXIuZWxlbWVudHMuZGlzcGxheS5zZWVrVG9vbHRpcC5oaWRkZW49dGhpcy5lbmFibGVkKSx0aGlzLmVuYWJsZWQmJnRoaXMuZ2V0VGh1bWJuYWlscygpLnRoZW4oKGZ1bmN0aW9uKCl7ZS5lbmFibGVkJiYoZS5yZW5kZXIoKSxlLmRldGVybWluZUNvbnRhaW5lckF1dG9TaXppbmcoKSxlLmxvYWRlZD0hMCl9KSl9fSx7a2V5OlwiZ2V0VGh1bWJuYWlsc1wiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcztyZXR1cm4gbmV3IFByb21pc2UoKGZ1bmN0aW9uKHQpe3ZhciBpPWUucGxheWVyLmNvbmZpZy5wcmV2aWV3VGh1bWJuYWlscy5zcmM7aWYoYWUoaSkpdGhyb3cgbmV3IEVycm9yKFwiTWlzc2luZyBwcmV2aWV3VGh1bWJuYWlscy5zcmMgY29uZmlnIGF0dHJpYnV0ZVwiKTt2YXIgbj1mdW5jdGlvbigpe2UudGh1bWJuYWlscy5zb3J0KChmdW5jdGlvbihlLHQpe3JldHVybiBlLmhlaWdodC10LmhlaWdodH0pKSxlLnBsYXllci5kZWJ1Zy5sb2coXCJQcmV2aWV3IHRodW1ibmFpbHNcIixlLnRodW1ibmFpbHMpLHQoKX07aWYoWChpKSlpKChmdW5jdGlvbih0KXtlLnRodW1ibmFpbHM9dCxuKCl9KSk7ZWxzZXt2YXIgYT0oWShpKT9baV06aSkubWFwKChmdW5jdGlvbih0KXtyZXR1cm4gZS5nZXRUaHVtYm5haWwodCl9KSk7UHJvbWlzZS5hbGwoYSkudGhlbihuKX19KSl9fSx7a2V5OlwiZ2V0VGh1bWJuYWlsXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztyZXR1cm4gbmV3IFByb21pc2UoKGZ1bmN0aW9uKGkpe0plKGUpLnRoZW4oKGZ1bmN0aW9uKG4pe3ZhciBhLHMscj17ZnJhbWVzOihhPW4scz1bXSxhLnNwbGl0KC9cXHJcXG5cXHJcXG58XFxuXFxufFxcclxcci8pLmZvckVhY2goKGZ1bmN0aW9uKGUpe3ZhciB0PXt9O2Uuc3BsaXQoL1xcclxcbnxcXG58XFxyLykuZm9yRWFjaCgoZnVuY3Rpb24oZSl7aWYoSyh0LnN0YXJ0VGltZSkpe2lmKCFhZShlLnRyaW0oKSkmJmFlKHQudGV4dCkpe3ZhciBpPWUudHJpbSgpLnNwbGl0KFwiI3h5d2g9XCIpLG49byhpLDEpO2lmKHQudGV4dD1uWzBdLGlbMV0pe3ZhciBhPW8oaVsxXS5zcGxpdChcIixcIiksNCk7dC54PWFbMF0sdC55PWFbMV0sdC53PWFbMl0sdC5oPWFbM119fX1lbHNle3ZhciBzPWUubWF0Y2goLyhbMC05XXsyfSk/Oj8oWzAtOV17Mn0pOihbMC05XXsyfSkuKFswLTldezIsM30pKCA/LS0+ID8pKFswLTldezJ9KT86PyhbMC05XXsyfSk6KFswLTldezJ9KS4oWzAtOV17MiwzfSkvKTtzJiYodC5zdGFydFRpbWU9NjAqTnVtYmVyKHNbMV18fDApKjYwKzYwKk51bWJlcihzWzJdKStOdW1iZXIoc1szXSkrTnVtYmVyKFwiMC5cIi5jb25jYXQoc1s0XSkpLHQuZW5kVGltZT02MCpOdW1iZXIoc1s2XXx8MCkqNjArNjAqTnVtYmVyKHNbN10pK051bWJlcihzWzhdKStOdW1iZXIoXCIwLlwiLmNvbmNhdChzWzldKSkpfX0pKSx0LnRleHQmJnMucHVzaCh0KX0pKSxzKSxoZWlnaHQ6bnVsbCx1cmxQcmVmaXg6XCJcIn07ci5mcmFtZXNbMF0udGV4dC5zdGFydHNXaXRoKFwiL1wiKXx8ci5mcmFtZXNbMF0udGV4dC5zdGFydHNXaXRoKFwiaHR0cDovL1wiKXx8ci5mcmFtZXNbMF0udGV4dC5zdGFydHNXaXRoKFwiaHR0cHM6Ly9cIil8fChyLnVybFByZWZpeD1lLnN1YnN0cmluZygwLGUubGFzdEluZGV4T2YoXCIvXCIpKzEpKTt2YXIgbD1uZXcgSW1hZ2U7bC5vbmxvYWQ9ZnVuY3Rpb24oKXtyLmhlaWdodD1sLm5hdHVyYWxIZWlnaHQsci53aWR0aD1sLm5hdHVyYWxXaWR0aCx0LnRodW1ibmFpbHMucHVzaChyKSxpKCl9LGwuc3JjPXIudXJsUHJlZml4K3IuZnJhbWVzWzBdLnRleHR9KSl9KSl9fSx7a2V5Olwic3RhcnRNb3ZlXCIsdmFsdWU6ZnVuY3Rpb24oZSl7aWYodGhpcy5sb2FkZWQmJlooZSkmJltcInRvdWNobW92ZVwiLFwibW91c2Vtb3ZlXCJdLmluY2x1ZGVzKGUudHlwZSkmJnRoaXMucGxheWVyLm1lZGlhLmR1cmF0aW9uKXtpZihcInRvdWNobW92ZVwiPT09ZS50eXBlKXRoaXMuc2Vla1RpbWU9dGhpcy5wbGF5ZXIubWVkaWEuZHVyYXRpb24qKHRoaXMucGxheWVyLmVsZW1lbnRzLmlucHV0cy5zZWVrLnZhbHVlLzEwMCk7ZWxzZXt2YXIgdD10aGlzLnBsYXllci5lbGVtZW50cy5wcm9ncmVzcy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSxpPTEwMC90LndpZHRoKihlLnBhZ2VYLXQubGVmdCk7dGhpcy5zZWVrVGltZT10aGlzLnBsYXllci5tZWRpYS5kdXJhdGlvbiooaS8xMDApLHRoaXMuc2Vla1RpbWU8MCYmKHRoaXMuc2Vla1RpbWU9MCksdGhpcy5zZWVrVGltZT50aGlzLnBsYXllci5tZWRpYS5kdXJhdGlvbi0xJiYodGhpcy5zZWVrVGltZT10aGlzLnBsYXllci5tZWRpYS5kdXJhdGlvbi0xKSx0aGlzLm1vdXNlUG9zWD1lLnBhZ2VYLHRoaXMuZWxlbWVudHMudGh1bWIudGltZS5pbm5lclRleHQ9aXQodGhpcy5zZWVrVGltZSl9dGhpcy5zaG93SW1hZ2VBdEN1cnJlbnRUaW1lKCl9fX0se2tleTpcImVuZE1vdmVcIix2YWx1ZTpmdW5jdGlvbigpe3RoaXMudG9nZ2xlVGh1bWJDb250YWluZXIoITEsITApfX0se2tleTpcInN0YXJ0U2NydWJiaW5nXCIsdmFsdWU6ZnVuY3Rpb24oZSl7KFcoZS5idXR0b24pfHwhMT09PWUuYnV0dG9ufHwwPT09ZS5idXR0b24pJiYodGhpcy5tb3VzZURvd249ITAsdGhpcy5wbGF5ZXIubWVkaWEuZHVyYXRpb24mJih0aGlzLnRvZ2dsZVNjcnViYmluZ0NvbnRhaW5lcighMCksdGhpcy50b2dnbGVUaHVtYkNvbnRhaW5lcighMSwhMCksdGhpcy5zaG93SW1hZ2VBdEN1cnJlbnRUaW1lKCkpKX19LHtrZXk6XCJlbmRTY3J1YmJpbmdcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPXRoaXM7dGhpcy5tb3VzZURvd249ITEsTWF0aC5jZWlsKHRoaXMubGFzdFRpbWUpPT09TWF0aC5jZWlsKHRoaXMucGxheWVyLm1lZGlhLmN1cnJlbnRUaW1lKT90aGlzLnRvZ2dsZVNjcnViYmluZ0NvbnRhaW5lcighMSk6TGUuY2FsbCh0aGlzLnBsYXllcix0aGlzLnBsYXllci5tZWRpYSxcInRpbWV1cGRhdGVcIiwoZnVuY3Rpb24oKXtlLm1vdXNlRG93bnx8ZS50b2dnbGVTY3J1YmJpbmdDb250YWluZXIoITEpfSkpfX0se2tleTpcImxpc3RlbmVyc1wiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLnBsYXllci5vbihcInBsYXlcIiwoZnVuY3Rpb24oKXtlLnRvZ2dsZVRodW1iQ29udGFpbmVyKCExLCEwKX0pKSx0aGlzLnBsYXllci5vbihcInNlZWtlZFwiLChmdW5jdGlvbigpe2UudG9nZ2xlVGh1bWJDb250YWluZXIoITEpfSkpLHRoaXMucGxheWVyLm9uKFwidGltZXVwZGF0ZVwiLChmdW5jdGlvbigpe2UubGFzdFRpbWU9ZS5wbGF5ZXIubWVkaWEuY3VycmVudFRpbWV9KSl9fSx7a2V5OlwicmVuZGVyXCIsdmFsdWU6ZnVuY3Rpb24oKXt0aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lcj1oZShcImRpdlwiLHtjbGFzczp0aGlzLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5wcmV2aWV3VGh1bWJuYWlscy50aHVtYkNvbnRhaW5lcn0pLHRoaXMuZWxlbWVudHMudGh1bWIuaW1hZ2VDb250YWluZXI9aGUoXCJkaXZcIix7Y2xhc3M6dGhpcy5wbGF5ZXIuY29uZmlnLmNsYXNzTmFtZXMucHJldmlld1RodW1ibmFpbHMuaW1hZ2VDb250YWluZXJ9KSx0aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lci5hcHBlbmRDaGlsZCh0aGlzLmVsZW1lbnRzLnRodW1iLmltYWdlQ29udGFpbmVyKTt2YXIgZT1oZShcImRpdlwiLHtjbGFzczp0aGlzLnBsYXllci5jb25maWcuY2xhc3NOYW1lcy5wcmV2aWV3VGh1bWJuYWlscy50aW1lQ29udGFpbmVyfSk7dGhpcy5lbGVtZW50cy50aHVtYi50aW1lPWhlKFwic3BhblwiLHt9LFwiMDA6MDBcIiksZS5hcHBlbmRDaGlsZCh0aGlzLmVsZW1lbnRzLnRodW1iLnRpbWUpLHRoaXMuZWxlbWVudHMudGh1bWIuY29udGFpbmVyLmFwcGVuZENoaWxkKGUpLEcodGhpcy5wbGF5ZXIuZWxlbWVudHMucHJvZ3Jlc3MpJiZ0aGlzLnBsYXllci5lbGVtZW50cy5wcm9ncmVzcy5hcHBlbmRDaGlsZCh0aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lciksdGhpcy5lbGVtZW50cy5zY3J1YmJpbmcuY29udGFpbmVyPWhlKFwiZGl2XCIse2NsYXNzOnRoaXMucGxheWVyLmNvbmZpZy5jbGFzc05hbWVzLnByZXZpZXdUaHVtYm5haWxzLnNjcnViYmluZ0NvbnRhaW5lcn0pLHRoaXMucGxheWVyLmVsZW1lbnRzLndyYXBwZXIuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50cy5zY3J1YmJpbmcuY29udGFpbmVyKX19LHtrZXk6XCJkZXN0cm95XCIsdmFsdWU6ZnVuY3Rpb24oKXt0aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lciYmdGhpcy5lbGVtZW50cy50aHVtYi5jb250YWluZXIucmVtb3ZlKCksdGhpcy5lbGVtZW50cy5zY3J1YmJpbmcuY29udGFpbmVyJiZ0aGlzLmVsZW1lbnRzLnNjcnViYmluZy5jb250YWluZXIucmVtb3ZlKCl9fSx7a2V5Olwic2hvd0ltYWdlQXRDdXJyZW50VGltZVwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLm1vdXNlRG93bj90aGlzLnNldFNjcnViYmluZ0NvbnRhaW5lclNpemUoKTp0aGlzLnNldFRodW1iQ29udGFpbmVyU2l6ZUFuZFBvcygpO3ZhciB0PXRoaXMudGh1bWJuYWlsc1swXS5mcmFtZXMuZmluZEluZGV4KChmdW5jdGlvbih0KXtyZXR1cm4gZS5zZWVrVGltZT49dC5zdGFydFRpbWUmJmUuc2Vla1RpbWU8PXQuZW5kVGltZX0pKSxpPXQ+PTAsbj0wO3RoaXMubW91c2VEb3dufHx0aGlzLnRvZ2dsZVRodW1iQ29udGFpbmVyKGkpLGkmJih0aGlzLnRodW1ibmFpbHMuZm9yRWFjaCgoZnVuY3Rpb24oaSxhKXtlLmxvYWRlZEltYWdlcy5pbmNsdWRlcyhpLmZyYW1lc1t0XS50ZXh0KSYmKG49YSl9KSksdCE9PXRoaXMuc2hvd2luZ1RodW1iJiYodGhpcy5zaG93aW5nVGh1bWI9dCx0aGlzLmxvYWRJbWFnZShuKSkpfX0se2tleTpcImxvYWRJbWFnZVwiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdP2FyZ3VtZW50c1swXTowLGk9dGhpcy5zaG93aW5nVGh1bWIsbj10aGlzLnRodW1ibmFpbHNbdF0sYT1uLnVybFByZWZpeCxzPW4uZnJhbWVzW2ldLHI9bi5mcmFtZXNbaV0udGV4dCxvPWErcjtpZih0aGlzLmN1cnJlbnRJbWFnZUVsZW1lbnQmJnRoaXMuY3VycmVudEltYWdlRWxlbWVudC5kYXRhc2V0LmZpbGVuYW1lPT09cil0aGlzLnNob3dJbWFnZSh0aGlzLmN1cnJlbnRJbWFnZUVsZW1lbnQscyx0LGksciwhMSksdGhpcy5jdXJyZW50SW1hZ2VFbGVtZW50LmRhdGFzZXQuaW5kZXg9aSx0aGlzLnJlbW92ZU9sZEltYWdlcyh0aGlzLmN1cnJlbnRJbWFnZUVsZW1lbnQpO2Vsc2V7dGhpcy5sb2FkaW5nSW1hZ2UmJnRoaXMudXNpbmdTcHJpdGVzJiYodGhpcy5sb2FkaW5nSW1hZ2Uub25sb2FkPW51bGwpO3ZhciBsPW5ldyBJbWFnZTtsLnNyYz1vLGwuZGF0YXNldC5pbmRleD1pLGwuZGF0YXNldC5maWxlbmFtZT1yLHRoaXMuc2hvd2luZ1RodW1iRmlsZW5hbWU9cix0aGlzLnBsYXllci5kZWJ1Zy5sb2coXCJMb2FkaW5nIGltYWdlOiBcIi5jb25jYXQobykpLGwub25sb2FkPWZ1bmN0aW9uKCl7cmV0dXJuIGUuc2hvd0ltYWdlKGwscyx0LGksciwhMCl9LHRoaXMubG9hZGluZ0ltYWdlPWwsdGhpcy5yZW1vdmVPbGRJbWFnZXMobCl9fX0se2tleTpcInNob3dJbWFnZVwiLHZhbHVlOmZ1bmN0aW9uKGUsdCxpLG4sYSl7dmFyIHM9IShhcmd1bWVudHMubGVuZ3RoPjUmJnZvaWQgMCE9PWFyZ3VtZW50c1s1XSl8fGFyZ3VtZW50c1s1XTt0aGlzLnBsYXllci5kZWJ1Zy5sb2coXCJTaG93aW5nIHRodW1iOiBcIi5jb25jYXQoYSxcIi4gbnVtOiBcIikuY29uY2F0KG4sXCIuIHF1YWw6IFwiKS5jb25jYXQoaSxcIi4gbmV3aW1nOiBcIikuY29uY2F0KHMpKSx0aGlzLnNldEltYWdlU2l6ZUFuZE9mZnNldChlLHQpLHMmJih0aGlzLmN1cnJlbnRJbWFnZUNvbnRhaW5lci5hcHBlbmRDaGlsZChlKSx0aGlzLmN1cnJlbnRJbWFnZUVsZW1lbnQ9ZSx0aGlzLmxvYWRlZEltYWdlcy5pbmNsdWRlcyhhKXx8dGhpcy5sb2FkZWRJbWFnZXMucHVzaChhKSksdGhpcy5wcmVsb2FkTmVhcmJ5KG4sITApLnRoZW4odGhpcy5wcmVsb2FkTmVhcmJ5KG4sITEpKS50aGVuKHRoaXMuZ2V0SGlnaGVyUXVhbGl0eShpLGUsdCxhKSl9fSx7a2V5OlwicmVtb3ZlT2xkSW1hZ2VzXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztBcnJheS5mcm9tKHRoaXMuY3VycmVudEltYWdlQ29udGFpbmVyLmNoaWxkcmVuKS5mb3JFYWNoKChmdW5jdGlvbihpKXtpZihcImltZ1wiPT09aS50YWdOYW1lLnRvTG93ZXJDYXNlKCkpe3ZhciBuPXQudXNpbmdTcHJpdGVzPzUwMDoxZTM7aWYoaS5kYXRhc2V0LmluZGV4IT09ZS5kYXRhc2V0LmluZGV4JiYhaS5kYXRhc2V0LmRlbGV0aW5nKXtpLmRhdGFzZXQuZGVsZXRpbmc9ITA7dmFyIGE9dC5jdXJyZW50SW1hZ2VDb250YWluZXI7c2V0VGltZW91dCgoZnVuY3Rpb24oKXthLnJlbW92ZUNoaWxkKGkpLHQucGxheWVyLmRlYnVnLmxvZyhcIlJlbW92aW5nIHRodW1iOiBcIi5jb25jYXQoaS5kYXRhc2V0LmZpbGVuYW1lKSl9KSxuKX19fSkpfX0se2tleTpcInByZWxvYWROZWFyYnlcIix2YWx1ZTpmdW5jdGlvbihlKXt2YXIgdD10aGlzLGk9IShhcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSl8fGFyZ3VtZW50c1sxXTtyZXR1cm4gbmV3IFByb21pc2UoKGZ1bmN0aW9uKG4pe3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dmFyIGE9dC50aHVtYm5haWxzWzBdLmZyYW1lc1tlXS50ZXh0O2lmKHQuc2hvd2luZ1RodW1iRmlsZW5hbWU9PT1hKXt2YXIgcztzPWk/dC50aHVtYm5haWxzWzBdLmZyYW1lcy5zbGljZShlKTp0LnRodW1ibmFpbHNbMF0uZnJhbWVzLnNsaWNlKDAsZSkucmV2ZXJzZSgpO3ZhciByPSExO3MuZm9yRWFjaCgoZnVuY3Rpb24oZSl7dmFyIGk9ZS50ZXh0O2lmKGkhPT1hJiYhdC5sb2FkZWRJbWFnZXMuaW5jbHVkZXMoaSkpe3I9ITAsdC5wbGF5ZXIuZGVidWcubG9nKFwiUHJlbG9hZGluZyB0aHVtYiBmaWxlbmFtZTogXCIuY29uY2F0KGkpKTt2YXIgcz10LnRodW1ibmFpbHNbMF0udXJsUHJlZml4K2ksbz1uZXcgSW1hZ2U7by5zcmM9cyxvLm9ubG9hZD1mdW5jdGlvbigpe3QucGxheWVyLmRlYnVnLmxvZyhcIlByZWxvYWRlZCB0aHVtYiBmaWxlbmFtZTogXCIuY29uY2F0KGkpKSx0LmxvYWRlZEltYWdlcy5pbmNsdWRlcyhpKXx8dC5sb2FkZWRJbWFnZXMucHVzaChpKSxuKCl9fX0pKSxyfHxuKCl9fSksMzAwKX0pKX19LHtrZXk6XCJnZXRIaWdoZXJRdWFsaXR5XCIsdmFsdWU6ZnVuY3Rpb24oZSx0LGksbil7dmFyIGE9dGhpcztpZihlPHRoaXMudGh1bWJuYWlscy5sZW5ndGgtMSl7dmFyIHM9dC5uYXR1cmFsSGVpZ2h0O3RoaXMudXNpbmdTcHJpdGVzJiYocz1pLmgpLHM8dGhpcy50aHVtYkNvbnRhaW5lckhlaWdodCYmc2V0VGltZW91dCgoZnVuY3Rpb24oKXthLnNob3dpbmdUaHVtYkZpbGVuYW1lPT09biYmKGEucGxheWVyLmRlYnVnLmxvZyhcIlNob3dpbmcgaGlnaGVyIHF1YWxpdHkgdGh1bWIgZm9yOiBcIi5jb25jYXQobikpLGEubG9hZEltYWdlKGUrMSkpfSksMzAwKX19fSx7a2V5OlwidG9nZ2xlVGh1bWJDb250YWluZXJcIix2YWx1ZTpmdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cy5sZW5ndGg+MCYmdm9pZCAwIT09YXJndW1lbnRzWzBdJiZhcmd1bWVudHNbMF0sdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXSYmYXJndW1lbnRzWzFdLGk9dGhpcy5wbGF5ZXIuY29uZmlnLmNsYXNzTmFtZXMucHJldmlld1RodW1ibmFpbHMudGh1bWJDb250YWluZXJTaG93bjt0aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lci5jbGFzc0xpc3QudG9nZ2xlKGksZSksIWUmJnQmJih0aGlzLnNob3dpbmdUaHVtYj1udWxsLHRoaXMuc2hvd2luZ1RodW1iRmlsZW5hbWU9bnVsbCl9fSx7a2V5OlwidG9nZ2xlU2NydWJiaW5nQ29udGFpbmVyXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoPjAmJnZvaWQgMCE9PWFyZ3VtZW50c1swXSYmYXJndW1lbnRzWzBdLHQ9dGhpcy5wbGF5ZXIuY29uZmlnLmNsYXNzTmFtZXMucHJldmlld1RodW1ibmFpbHMuc2NydWJiaW5nQ29udGFpbmVyU2hvd247dGhpcy5lbGVtZW50cy5zY3J1YmJpbmcuY29udGFpbmVyLmNsYXNzTGlzdC50b2dnbGUodCxlKSxlfHwodGhpcy5zaG93aW5nVGh1bWI9bnVsbCx0aGlzLnNob3dpbmdUaHVtYkZpbGVuYW1lPW51bGwpfX0se2tleTpcImRldGVybWluZUNvbnRhaW5lckF1dG9TaXppbmdcIix2YWx1ZTpmdW5jdGlvbigpeyh0aGlzLmVsZW1lbnRzLnRodW1iLmltYWdlQ29udGFpbmVyLmNsaWVudEhlaWdodD4yMHx8dGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lci5jbGllbnRXaWR0aD4yMCkmJih0aGlzLnNpemVTcGVjaWZpZWRJbkNTUz0hMCl9fSx7a2V5Olwic2V0VGh1bWJDb250YWluZXJTaXplQW5kUG9zXCIsdmFsdWU6ZnVuY3Rpb24oKXtpZih0aGlzLnNpemVTcGVjaWZpZWRJbkNTUyl7aWYodGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lci5jbGllbnRIZWlnaHQ+MjAmJnRoaXMuZWxlbWVudHMudGh1bWIuaW1hZ2VDb250YWluZXIuY2xpZW50V2lkdGg8MjApe3ZhciBlPU1hdGguZmxvb3IodGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lci5jbGllbnRIZWlnaHQqdGhpcy50aHVtYkFzcGVjdFJhdGlvKTt0aGlzLmVsZW1lbnRzLnRodW1iLmltYWdlQ29udGFpbmVyLnN0eWxlLndpZHRoPVwiXCIuY29uY2F0KGUsXCJweFwiKX1lbHNlIGlmKHRoaXMuZWxlbWVudHMudGh1bWIuaW1hZ2VDb250YWluZXIuY2xpZW50SGVpZ2h0PDIwJiZ0aGlzLmVsZW1lbnRzLnRodW1iLmltYWdlQ29udGFpbmVyLmNsaWVudFdpZHRoPjIwKXt2YXIgdD1NYXRoLmZsb29yKHRoaXMuZWxlbWVudHMudGh1bWIuaW1hZ2VDb250YWluZXIuY2xpZW50V2lkdGgvdGhpcy50aHVtYkFzcGVjdFJhdGlvKTt0aGlzLmVsZW1lbnRzLnRodW1iLmltYWdlQ29udGFpbmVyLnN0eWxlLmhlaWdodD1cIlwiLmNvbmNhdCh0LFwicHhcIil9fWVsc2V7dmFyIGk9TWF0aC5mbG9vcih0aGlzLnRodW1iQ29udGFpbmVySGVpZ2h0KnRoaXMudGh1bWJBc3BlY3RSYXRpbyk7dGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lci5zdHlsZS5oZWlnaHQ9XCJcIi5jb25jYXQodGhpcy50aHVtYkNvbnRhaW5lckhlaWdodCxcInB4XCIpLHRoaXMuZWxlbWVudHMudGh1bWIuaW1hZ2VDb250YWluZXIuc3R5bGUud2lkdGg9XCJcIi5jb25jYXQoaSxcInB4XCIpfXRoaXMuc2V0VGh1bWJDb250YWluZXJQb3MoKX19LHtrZXk6XCJzZXRUaHVtYkNvbnRhaW5lclBvc1wiLHZhbHVlOmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5wbGF5ZXIuZWxlbWVudHMucHJvZ3Jlc3MuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksdD10aGlzLnBsYXllci5lbGVtZW50cy5jb250YWluZXIuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksaT10aGlzLmVsZW1lbnRzLnRodW1iLmNvbnRhaW5lcixuPXQubGVmdC1lLmxlZnQrMTAsYT10LnJpZ2h0LWUubGVmdC1pLmNsaWVudFdpZHRoLTEwLHM9dGhpcy5tb3VzZVBvc1gtZS5sZWZ0LWkuY2xpZW50V2lkdGgvMjtzPG4mJihzPW4pLHM+YSYmKHM9YSksaS5zdHlsZS5sZWZ0PVwiXCIuY29uY2F0KHMsXCJweFwiKX19LHtrZXk6XCJzZXRTY3J1YmJpbmdDb250YWluZXJTaXplXCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT1OdCh0aGlzLnRodW1iQXNwZWN0UmF0aW8se3dpZHRoOnRoaXMucGxheWVyLm1lZGlhLmNsaWVudFdpZHRoLGhlaWdodDp0aGlzLnBsYXllci5tZWRpYS5jbGllbnRIZWlnaHR9KSx0PWUud2lkdGgsaT1lLmhlaWdodDt0aGlzLmVsZW1lbnRzLnNjcnViYmluZy5jb250YWluZXIuc3R5bGUud2lkdGg9XCJcIi5jb25jYXQodCxcInB4XCIpLHRoaXMuZWxlbWVudHMuc2NydWJiaW5nLmNvbnRhaW5lci5zdHlsZS5oZWlnaHQ9XCJcIi5jb25jYXQoaSxcInB4XCIpfX0se2tleTpcInNldEltYWdlU2l6ZUFuZE9mZnNldFwiLHZhbHVlOmZ1bmN0aW9uKGUsdCl7aWYodGhpcy51c2luZ1Nwcml0ZXMpe3ZhciBpPXRoaXMudGh1bWJDb250YWluZXJIZWlnaHQvdC5oO2Uuc3R5bGUuaGVpZ2h0PVwiXCIuY29uY2F0KGUubmF0dXJhbEhlaWdodCppLFwicHhcIiksZS5zdHlsZS53aWR0aD1cIlwiLmNvbmNhdChlLm5hdHVyYWxXaWR0aCppLFwicHhcIiksZS5zdHlsZS5sZWZ0PVwiLVwiLmNvbmNhdCh0LngqaSxcInB4XCIpLGUuc3R5bGUudG9wPVwiLVwiLmNvbmNhdCh0LnkqaSxcInB4XCIpfX19LHtrZXk6XCJlbmFibGVkXCIsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucGxheWVyLmlzSFRNTDUmJnRoaXMucGxheWVyLmlzVmlkZW8mJnRoaXMucGxheWVyLmNvbmZpZy5wcmV2aWV3VGh1bWJuYWlscy5lbmFibGVkfX0se2tleTpcImN1cnJlbnRJbWFnZUNvbnRhaW5lclwiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1vdXNlRG93bj90aGlzLmVsZW1lbnRzLnNjcnViYmluZy5jb250YWluZXI6dGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lcn19LHtrZXk6XCJ1c2luZ1Nwcml0ZXNcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gT2JqZWN0LmtleXModGhpcy50aHVtYm5haWxzWzBdLmZyYW1lc1swXSkuaW5jbHVkZXMoXCJ3XCIpfX0se2tleTpcInRodW1iQXNwZWN0UmF0aW9cIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy51c2luZ1Nwcml0ZXM/dGhpcy50aHVtYm5haWxzWzBdLmZyYW1lc1swXS53L3RoaXMudGh1bWJuYWlsc1swXS5mcmFtZXNbMF0uaDp0aGlzLnRodW1ibmFpbHNbMF0ud2lkdGgvdGhpcy50aHVtYm5haWxzWzBdLmhlaWdodH19LHtrZXk6XCJ0aHVtYkNvbnRhaW5lckhlaWdodFwiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1vdXNlRG93bj9OdCh0aGlzLnRodW1iQXNwZWN0UmF0aW8se3dpZHRoOnRoaXMucGxheWVyLm1lZGlhLmNsaWVudFdpZHRoLGhlaWdodDp0aGlzLnBsYXllci5tZWRpYS5jbGllbnRIZWlnaHR9KS5oZWlnaHQ6dGhpcy5zaXplU3BlY2lmaWVkSW5DU1M/dGhpcy5lbGVtZW50cy50aHVtYi5pbWFnZUNvbnRhaW5lci5jbGllbnRIZWlnaHQ6TWF0aC5mbG9vcih0aGlzLnBsYXllci5tZWRpYS5jbGllbnRXaWR0aC90aGlzLnRodW1iQXNwZWN0UmF0aW8vNCl9fSx7a2V5OlwiY3VycmVudEltYWdlRWxlbWVudFwiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1vdXNlRG93bj90aGlzLmN1cnJlbnRTY3J1YmJpbmdJbWFnZUVsZW1lbnQ6dGhpcy5jdXJyZW50VGh1bWJuYWlsSW1hZ2VFbGVtZW50fSxzZXQ6ZnVuY3Rpb24oZSl7dGhpcy5tb3VzZURvd24/dGhpcy5jdXJyZW50U2NydWJiaW5nSW1hZ2VFbGVtZW50PWU6dGhpcy5jdXJyZW50VGh1bWJuYWlsSW1hZ2VFbGVtZW50PWV9fV0pLHR9KCkseHQ9e2luc2VydEVsZW1lbnRzOmZ1bmN0aW9uKGUsdCl7dmFyIGk9dGhpcztZKHQpP21lKGUsdGhpcy5tZWRpYSx7c3JjOnR9KTokKHQpJiZ0LmZvckVhY2goKGZ1bmN0aW9uKHQpe21lKGUsaS5tZWRpYSx0KX0pKX0sY2hhbmdlOmZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7bGUoZSxcInNvdXJjZXMubGVuZ3RoXCIpPyhWZS5jYW5jZWxSZXF1ZXN0cy5jYWxsKHRoaXMpLHRoaXMuZGVzdHJveS5jYWxsKHRoaXMsKGZ1bmN0aW9uKCl7dC5vcHRpb25zLnF1YWxpdHk9W10scGUodC5tZWRpYSksdC5tZWRpYT1udWxsLEcodC5lbGVtZW50cy5jb250YWluZXIpJiZ0LmVsZW1lbnRzLmNvbnRhaW5lci5yZW1vdmVBdHRyaWJ1dGUoXCJjbGFzc1wiKTt2YXIgaT1lLnNvdXJjZXMsbj1lLnR5cGUsYT1vKGksMSlbMF0scz1hLnByb3ZpZGVyLHI9dm9pZCAwPT09cz91dC5odG1sNTpzLGw9YS5zcmMsYz1cImh0bWw1XCI9PT1yP246XCJkaXZcIix1PVwiaHRtbDVcIj09PXI/e306e3NyYzpsfTtPYmplY3QuYXNzaWduKHQse3Byb3ZpZGVyOnIsdHlwZTpuLHN1cHBvcnRlZDpFZS5jaGVjayhuLHIsdC5jb25maWcucGxheXNpbmxpbmUpLG1lZGlhOmhlKGMsdSl9KSx0LmVsZW1lbnRzLmNvbnRhaW5lci5hcHBlbmRDaGlsZCh0Lm1lZGlhKSxRKGUuYXV0b3BsYXkpJiYodC5jb25maWcuYXV0b3BsYXk9ZS5hdXRvcGxheSksdC5pc0hUTUw1JiYodC5jb25maWcuY3Jvc3NvcmlnaW4mJnQubWVkaWEuc2V0QXR0cmlidXRlKFwiY3Jvc3NvcmlnaW5cIixcIlwiKSx0LmNvbmZpZy5hdXRvcGxheSYmdC5tZWRpYS5zZXRBdHRyaWJ1dGUoXCJhdXRvcGxheVwiLFwiXCIpLGFlKGUucG9zdGVyKXx8KHQucG9zdGVyPWUucG9zdGVyKSx0LmNvbmZpZy5sb29wLmFjdGl2ZSYmdC5tZWRpYS5zZXRBdHRyaWJ1dGUoXCJsb29wXCIsXCJcIiksdC5jb25maWcubXV0ZWQmJnQubWVkaWEuc2V0QXR0cmlidXRlKFwibXV0ZWRcIixcIlwiKSx0LmNvbmZpZy5wbGF5c2lubGluZSYmdC5tZWRpYS5zZXRBdHRyaWJ1dGUoXCJwbGF5c2lubGluZVwiLFwiXCIpKSx5dC5hZGRTdHlsZUhvb2suY2FsbCh0KSx0LmlzSFRNTDUmJnh0Lmluc2VydEVsZW1lbnRzLmNhbGwodCxcInNvdXJjZVwiLGkpLHQuY29uZmlnLnRpdGxlPWUudGl0bGUsUHQuc2V0dXAuY2FsbCh0KSx0LmlzSFRNTDUmJk9iamVjdC5rZXlzKGUpLmluY2x1ZGVzKFwidHJhY2tzXCIpJiZ4dC5pbnNlcnRFbGVtZW50cy5jYWxsKHQsXCJ0cmFja1wiLGUudHJhY2tzKSwodC5pc0hUTUw1fHx0LmlzRW1iZWQmJiF0LnN1cHBvcnRlZC51aSkmJnl0LmJ1aWxkLmNhbGwodCksdC5pc0hUTUw1JiZ0Lm1lZGlhLmxvYWQoKSxhZShlLnByZXZpZXdUaHVtYm5haWxzKXx8KE9iamVjdC5hc3NpZ24odC5jb25maWcucHJldmlld1RodW1ibmFpbHMsZS5wcmV2aWV3VGh1bWJuYWlscyksdC5wcmV2aWV3VGh1bWJuYWlscyYmdC5wcmV2aWV3VGh1bWJuYWlscy5sb2FkZWQmJih0LnByZXZpZXdUaHVtYm5haWxzLmRlc3Ryb3koKSx0LnByZXZpZXdUaHVtYm5haWxzPW51bGwpLHQuY29uZmlnLnByZXZpZXdUaHVtYm5haWxzLmVuYWJsZWQmJih0LnByZXZpZXdUaHVtYm5haWxzPW5ldyBNdCh0KSkpLHQuZnVsbHNjcmVlbi51cGRhdGUoKX0pLCEwKSk6dGhpcy5kZWJ1Zy53YXJuKFwiSW52YWxpZCBzb3VyY2UgZm9ybWF0XCIpfX07dmFyIEl0LEx0PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gdChpLG4pe3ZhciBhPXRoaXM7aWYoZSh0aGlzLHQpLHRoaXMudGltZXJzPXt9LHRoaXMucmVhZHk9ITEsdGhpcy5sb2FkaW5nPSExLHRoaXMuZmFpbGVkPSExLHRoaXMudG91Y2g9RWUudG91Y2gsdGhpcy5tZWRpYT1pLFkodGhpcy5tZWRpYSkmJih0aGlzLm1lZGlhPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwodGhpcy5tZWRpYSkpLCh3aW5kb3cualF1ZXJ5JiZ0aGlzLm1lZGlhIGluc3RhbmNlb2YgalF1ZXJ5fHxKKHRoaXMubWVkaWEpfHwkKHRoaXMubWVkaWEpKSYmKHRoaXMubWVkaWE9dGhpcy5tZWRpYVswXSksdGhpcy5jb25maWc9Y2Uoe30sb3QsdC5kZWZhdWx0cyxufHx7fSxmdW5jdGlvbigpe3RyeXtyZXR1cm4gSlNPTi5wYXJzZShhLm1lZGlhLmdldEF0dHJpYnV0ZShcImRhdGEtcGx5ci1jb25maWdcIikpfWNhdGNoKGUpe3JldHVybnt9fX0oKSksdGhpcy5lbGVtZW50cz17Y29udGFpbmVyOm51bGwsZnVsbHNjcmVlbjpudWxsLGNhcHRpb25zOm51bGwsYnV0dG9uczp7fSxkaXNwbGF5Ont9LHByb2dyZXNzOnt9LGlucHV0czp7fSxzZXR0aW5nczp7cG9wdXA6bnVsbCxtZW51Om51bGwscGFuZWxzOnt9LGJ1dHRvbnM6e319fSx0aGlzLmNhcHRpb25zPXthY3RpdmU6bnVsbCxjdXJyZW50VHJhY2s6LTEsbWV0YTpuZXcgV2Vha01hcH0sdGhpcy5mdWxsc2NyZWVuPXthY3RpdmU6ITF9LHRoaXMub3B0aW9ucz17c3BlZWQ6W10scXVhbGl0eTpbXX0sdGhpcy5kZWJ1Zz1uZXcgcHQodGhpcy5jb25maWcuZGVidWcpLHRoaXMuZGVidWcubG9nKFwiQ29uZmlnXCIsdGhpcy5jb25maWcpLHRoaXMuZGVidWcubG9nKFwiU3VwcG9ydFwiLEVlKSwhVyh0aGlzLm1lZGlhKSYmRyh0aGlzLm1lZGlhKSlpZih0aGlzLm1lZGlhLnBseXIpdGhpcy5kZWJ1Zy53YXJuKFwiVGFyZ2V0IGFscmVhZHkgc2V0dXBcIik7ZWxzZSBpZih0aGlzLmNvbmZpZy5lbmFibGVkKWlmKEVlLmNoZWNrKCkuYXBpKXt2YXIgcz10aGlzLm1lZGlhLmNsb25lTm9kZSghMCk7cy5hdXRvcGxheT0hMSx0aGlzLmVsZW1lbnRzLm9yaWdpbmFsPXM7dmFyIHI9dGhpcy5tZWRpYS50YWdOYW1lLnRvTG93ZXJDYXNlKCksbz1udWxsLGw9bnVsbDtzd2l0Y2gocil7Y2FzZVwiZGl2XCI6aWYobz10aGlzLm1lZGlhLnF1ZXJ5U2VsZWN0b3IoXCJpZnJhbWVcIiksRyhvKSl7aWYobD1hdChvLmdldEF0dHJpYnV0ZShcInNyY1wiKSksdGhpcy5wcm92aWRlcj1mdW5jdGlvbihlKXtyZXR1cm4vXihodHRwcz86XFwvXFwvKT8od3d3XFwuKT8oeW91dHViZVxcLmNvbXx5b3V0dWJlLW5vY29va2llXFwuY29tfHlvdXR1XFwuP2JlKVxcLy4rJC8udGVzdChlKT91dC55b3V0dWJlOi9eaHR0cHM/OlxcL1xcL3BsYXllci52aW1lby5jb21cXC92aWRlb1xcL1xcZHswLDl9KD89XFxifFxcLykvLnRlc3QoZSk/dXQudmltZW86bnVsbH0obC50b1N0cmluZygpKSx0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcj10aGlzLm1lZGlhLHRoaXMubWVkaWE9byx0aGlzLmVsZW1lbnRzLmNvbnRhaW5lci5jbGFzc05hbWU9XCJcIixsLnNlYXJjaC5sZW5ndGgpe3ZhciBjPVtcIjFcIixcInRydWVcIl07Yy5pbmNsdWRlcyhsLnNlYXJjaFBhcmFtcy5nZXQoXCJhdXRvcGxheVwiKSkmJih0aGlzLmNvbmZpZy5hdXRvcGxheT0hMCksYy5pbmNsdWRlcyhsLnNlYXJjaFBhcmFtcy5nZXQoXCJsb29wXCIpKSYmKHRoaXMuY29uZmlnLmxvb3AuYWN0aXZlPSEwKSx0aGlzLmlzWW91VHViZT8odGhpcy5jb25maWcucGxheXNpbmxpbmU9Yy5pbmNsdWRlcyhsLnNlYXJjaFBhcmFtcy5nZXQoXCJwbGF5c2lubGluZVwiKSksdGhpcy5jb25maWcueW91dHViZS5obD1sLnNlYXJjaFBhcmFtcy5nZXQoXCJobFwiKSk6dGhpcy5jb25maWcucGxheXNpbmxpbmU9ITB9fWVsc2UgdGhpcy5wcm92aWRlcj10aGlzLm1lZGlhLmdldEF0dHJpYnV0ZSh0aGlzLmNvbmZpZy5hdHRyaWJ1dGVzLmVtYmVkLnByb3ZpZGVyKSx0aGlzLm1lZGlhLnJlbW92ZUF0dHJpYnV0ZSh0aGlzLmNvbmZpZy5hdHRyaWJ1dGVzLmVtYmVkLnByb3ZpZGVyKTtpZihhZSh0aGlzLnByb3ZpZGVyKXx8IU9iamVjdC5rZXlzKHV0KS5pbmNsdWRlcyh0aGlzLnByb3ZpZGVyKSlyZXR1cm4gdm9pZCB0aGlzLmRlYnVnLmVycm9yKFwiU2V0dXAgZmFpbGVkOiBJbnZhbGlkIHByb3ZpZGVyXCIpO3RoaXMudHlwZT1odDticmVhaztjYXNlXCJ2aWRlb1wiOmNhc2VcImF1ZGlvXCI6dGhpcy50eXBlPXIsdGhpcy5wcm92aWRlcj11dC5odG1sNSx0aGlzLm1lZGlhLmhhc0F0dHJpYnV0ZShcImNyb3Nzb3JpZ2luXCIpJiYodGhpcy5jb25maWcuY3Jvc3NvcmlnaW49ITApLHRoaXMubWVkaWEuaGFzQXR0cmlidXRlKFwiYXV0b3BsYXlcIikmJih0aGlzLmNvbmZpZy5hdXRvcGxheT0hMCksKHRoaXMubWVkaWEuaGFzQXR0cmlidXRlKFwicGxheXNpbmxpbmVcIil8fHRoaXMubWVkaWEuaGFzQXR0cmlidXRlKFwid2Via2l0LXBsYXlzaW5saW5lXCIpKSYmKHRoaXMuY29uZmlnLnBsYXlzaW5saW5lPSEwKSx0aGlzLm1lZGlhLmhhc0F0dHJpYnV0ZShcIm11dGVkXCIpJiYodGhpcy5jb25maWcubXV0ZWQ9ITApLHRoaXMubWVkaWEuaGFzQXR0cmlidXRlKFwibG9vcFwiKSYmKHRoaXMuY29uZmlnLmxvb3AuYWN0aXZlPSEwKTticmVhaztkZWZhdWx0OnJldHVybiB2b2lkIHRoaXMuZGVidWcuZXJyb3IoXCJTZXR1cCBmYWlsZWQ6IHVuc3VwcG9ydGVkIHR5cGVcIil9dGhpcy5zdXBwb3J0ZWQ9RWUuY2hlY2sodGhpcy50eXBlLHRoaXMucHJvdmlkZXIsdGhpcy5jb25maWcucGxheXNpbmxpbmUpLHRoaXMuc3VwcG9ydGVkLmFwaT8odGhpcy5ldmVudExpc3RlbmVycz1bXSx0aGlzLmxpc3RlbmVycz1uZXcgdnQodGhpcyksdGhpcy5zdG9yYWdlPW5ldyAkZSh0aGlzKSx0aGlzLm1lZGlhLnBseXI9dGhpcyxHKHRoaXMuZWxlbWVudHMuY29udGFpbmVyKXx8KHRoaXMuZWxlbWVudHMuY29udGFpbmVyPWhlKFwiZGl2XCIse3RhYmluZGV4OjB9KSx1ZSh0aGlzLm1lZGlhLHRoaXMuZWxlbWVudHMuY29udGFpbmVyKSkseXQubWlncmF0ZVN0eWxlcy5jYWxsKHRoaXMpLHl0LmFkZFN0eWxlSG9vay5jYWxsKHRoaXMpLFB0LnNldHVwLmNhbGwodGhpcyksdGhpcy5jb25maWcuZGVidWcmJnhlLmNhbGwodGhpcyx0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcix0aGlzLmNvbmZpZy5ldmVudHMuam9pbihcIiBcIiksKGZ1bmN0aW9uKGUpe2EuZGVidWcubG9nKFwiZXZlbnQ6IFwiLmNvbmNhdChlLnR5cGUpKX0pKSx0aGlzLmZ1bGxzY3JlZW49bmV3IGZ0KHRoaXMpLCh0aGlzLmlzSFRNTDV8fHRoaXMuaXNFbWJlZCYmIXRoaXMuc3VwcG9ydGVkLnVpKSYmeXQuYnVpbGQuY2FsbCh0aGlzKSx0aGlzLmxpc3RlbmVycy5jb250YWluZXIoKSx0aGlzLmxpc3RlbmVycy5nbG9iYWwoKSx0aGlzLmNvbmZpZy5hZHMuZW5hYmxlZCYmKHRoaXMuYWRzPW5ldyBFdCh0aGlzKSksdGhpcy5pc0hUTUw1JiZ0aGlzLmNvbmZpZy5hdXRvcGxheSYmdGhpcy5vbmNlKFwiY2FucGxheVwiLChmdW5jdGlvbigpe3JldHVybiBEZShhLnBsYXkoKSl9KSksdGhpcy5sYXN0U2Vla1RpbWU9MCx0aGlzLmNvbmZpZy5wcmV2aWV3VGh1bWJuYWlscy5lbmFibGVkJiYodGhpcy5wcmV2aWV3VGh1bWJuYWlscz1uZXcgTXQodGhpcykpKTp0aGlzLmRlYnVnLmVycm9yKFwiU2V0dXAgZmFpbGVkOiBubyBzdXBwb3J0XCIpfWVsc2UgdGhpcy5kZWJ1Zy5lcnJvcihcIlNldHVwIGZhaWxlZDogbm8gc3VwcG9ydFwiKTtlbHNlIHRoaXMuZGVidWcuZXJyb3IoXCJTZXR1cCBmYWlsZWQ6IGRpc2FibGVkIGJ5IGNvbmZpZ1wiKTtlbHNlIHRoaXMuZGVidWcuZXJyb3IoXCJTZXR1cCBmYWlsZWQ6IG5vIHN1aXRhYmxlIGVsZW1lbnQgcGFzc2VkXCIpfXJldHVybiBpKHQsW3trZXk6XCJwbGF5XCIsdmFsdWU6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3JldHVybiBYKHRoaXMubWVkaWEucGxheSk/KHRoaXMuYWRzJiZ0aGlzLmFkcy5lbmFibGVkJiZ0aGlzLmFkcy5tYW5hZ2VyUHJvbWlzZS50aGVuKChmdW5jdGlvbigpe3JldHVybiBlLmFkcy5wbGF5KCl9KSkuY2F0Y2goKGZ1bmN0aW9uKCl7cmV0dXJuIERlKGUubWVkaWEucGxheSgpKX0pKSx0aGlzLm1lZGlhLnBsYXkoKSk6bnVsbH19LHtrZXk6XCJwYXVzZVwiLHZhbHVlOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucGxheWluZyYmWCh0aGlzLm1lZGlhLnBhdXNlKT90aGlzLm1lZGlhLnBhdXNlKCk6bnVsbH19LHtrZXk6XCJ0b2dnbGVQbGF5XCIsdmFsdWU6ZnVuY3Rpb24oZSl7cmV0dXJuKFEoZSk/ZTohdGhpcy5wbGF5aW5nKT90aGlzLnBsYXkoKTp0aGlzLnBhdXNlKCl9fSx7a2V5Olwic3RvcFwiLHZhbHVlOmZ1bmN0aW9uKCl7dGhpcy5pc0hUTUw1Pyh0aGlzLnBhdXNlKCksdGhpcy5yZXN0YXJ0KCkpOlgodGhpcy5tZWRpYS5zdG9wKSYmdGhpcy5tZWRpYS5zdG9wKCl9fSx7a2V5OlwicmVzdGFydFwiLHZhbHVlOmZ1bmN0aW9uKCl7dGhpcy5jdXJyZW50VGltZT0wfX0se2tleTpcInJld2luZFwiLHZhbHVlOmZ1bmN0aW9uKGUpe3RoaXMuY3VycmVudFRpbWUtPUsoZSk/ZTp0aGlzLmNvbmZpZy5zZWVrVGltZX19LHtrZXk6XCJmb3J3YXJkXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dGhpcy5jdXJyZW50VGltZSs9SyhlKT9lOnRoaXMuY29uZmlnLnNlZWtUaW1lfX0se2tleTpcImluY3JlYXNlVm9sdW1lXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5tZWRpYS5tdXRlZD8wOnRoaXMudm9sdW1lO3RoaXMudm9sdW1lPXQrKEsoZSk/ZTowKX19LHtrZXk6XCJkZWNyZWFzZVZvbHVtZVwiLHZhbHVlOmZ1bmN0aW9uKGUpe3RoaXMuaW5jcmVhc2VWb2x1bWUoLWUpfX0se2tleTpcInRvZ2dsZUNhcHRpb25zXCIsdmFsdWU6ZnVuY3Rpb24oZSl7cnQudG9nZ2xlLmNhbGwodGhpcyxlLCExKX19LHtrZXk6XCJhaXJwbGF5XCIsdmFsdWU6ZnVuY3Rpb24oKXtFZS5haXJwbGF5JiZ0aGlzLm1lZGlhLndlYmtpdFNob3dQbGF5YmFja1RhcmdldFBpY2tlcigpfX0se2tleTpcInRvZ2dsZUNvbnRyb2xzXCIsdmFsdWU6ZnVuY3Rpb24oZSl7aWYodGhpcy5zdXBwb3J0ZWQudWkmJiF0aGlzLmlzQXVkaW8pe3ZhciB0PXdlKHRoaXMuZWxlbWVudHMuY29udGFpbmVyLHRoaXMuY29uZmlnLmNsYXNzTmFtZXMuaGlkZUNvbnRyb2xzKSxpPXZvaWQgMD09PWU/dm9pZCAwOiFlLG49YmUodGhpcy5lbGVtZW50cy5jb250YWluZXIsdGhpcy5jb25maWcuY2xhc3NOYW1lcy5oaWRlQ29udHJvbHMsaSk7aWYobiYmJCh0aGlzLmNvbmZpZy5jb250cm9scykmJnRoaXMuY29uZmlnLmNvbnRyb2xzLmluY2x1ZGVzKFwic2V0dGluZ3NcIikmJiFhZSh0aGlzLmNvbmZpZy5zZXR0aW5ncykmJm50LnRvZ2dsZU1lbnUuY2FsbCh0aGlzLCExKSxuIT09dCl7dmFyIGE9bj9cImNvbnRyb2xzaGlkZGVuXCI6XCJjb250cm9sc3Nob3duXCI7T2UuY2FsbCh0aGlzLHRoaXMubWVkaWEsYSl9cmV0dXJuIW59cmV0dXJuITF9fSx7a2V5Olwib25cIix2YWx1ZTpmdW5jdGlvbihlLHQpe3hlLmNhbGwodGhpcyx0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcixlLHQpfX0se2tleTpcIm9uY2VcIix2YWx1ZTpmdW5jdGlvbihlLHQpe0xlLmNhbGwodGhpcyx0aGlzLmVsZW1lbnRzLmNvbnRhaW5lcixlLHQpfX0se2tleTpcIm9mZlwiLHZhbHVlOmZ1bmN0aW9uKGUsdCl7SWUodGhpcy5lbGVtZW50cy5jb250YWluZXIsZSx0KX19LHtrZXk6XCJkZXN0cm95XCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPWFyZ3VtZW50cy5sZW5ndGg+MSYmdm9pZCAwIT09YXJndW1lbnRzWzFdJiZhcmd1bWVudHNbMV07aWYodGhpcy5yZWFkeSl7dmFyIG49ZnVuY3Rpb24oKXtkb2N1bWVudC5ib2R5LnN0eWxlLm92ZXJmbG93PVwiXCIsdC5lbWJlZD1udWxsLGk/KE9iamVjdC5rZXlzKHQuZWxlbWVudHMpLmxlbmd0aCYmKHBlKHQuZWxlbWVudHMuYnV0dG9ucy5wbGF5KSxwZSh0LmVsZW1lbnRzLmNhcHRpb25zKSxwZSh0LmVsZW1lbnRzLmNvbnRyb2xzKSxwZSh0LmVsZW1lbnRzLndyYXBwZXIpLHQuZWxlbWVudHMuYnV0dG9ucy5wbGF5PW51bGwsdC5lbGVtZW50cy5jYXB0aW9ucz1udWxsLHQuZWxlbWVudHMuY29udHJvbHM9bnVsbCx0LmVsZW1lbnRzLndyYXBwZXI9bnVsbCksWChlKSYmZSgpKTooX2UuY2FsbCh0KSxWZS5jYW5jZWxSZXF1ZXN0cy5jYWxsKHQpLGdlKHQuZWxlbWVudHMub3JpZ2luYWwsdC5lbGVtZW50cy5jb250YWluZXIpLE9lLmNhbGwodCx0LmVsZW1lbnRzLm9yaWdpbmFsLFwiZGVzdHJveWVkXCIsITApLFgoZSkmJmUuY2FsbCh0LmVsZW1lbnRzLm9yaWdpbmFsKSx0LnJlYWR5PSExLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dC5lbGVtZW50cz1udWxsLHQubWVkaWE9bnVsbH0pLDIwMCkpfTt0aGlzLnN0b3AoKSxjbGVhclRpbWVvdXQodGhpcy50aW1lcnMubG9hZGluZyksY2xlYXJUaW1lb3V0KHRoaXMudGltZXJzLmNvbnRyb2xzKSxjbGVhclRpbWVvdXQodGhpcy50aW1lcnMucmVzaXplZCksdGhpcy5pc0hUTUw1Pyh5dC50b2dnbGVOYXRpdmVDb250cm9scy5jYWxsKHRoaXMsITApLG4oKSk6dGhpcy5pc1lvdVR1YmU/KGNsZWFySW50ZXJ2YWwodGhpcy50aW1lcnMuYnVmZmVyaW5nKSxjbGVhckludGVydmFsKHRoaXMudGltZXJzLnBsYXlpbmcpLG51bGwhPT10aGlzLmVtYmVkJiZYKHRoaXMuZW1iZWQuZGVzdHJveSkmJnRoaXMuZW1iZWQuZGVzdHJveSgpLG4oKSk6dGhpcy5pc1ZpbWVvJiYobnVsbCE9PXRoaXMuZW1iZWQmJnRoaXMuZW1iZWQudW5sb2FkKCkudGhlbihuKSxzZXRUaW1lb3V0KG4sMjAwKSl9fX0se2tleTpcInN1cHBvcnRzXCIsdmFsdWU6ZnVuY3Rpb24oZSl7cmV0dXJuIEVlLm1pbWUuY2FsbCh0aGlzLGUpfX0se2tleTpcImlzSFRNTDVcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5wcm92aWRlcj09PXV0Lmh0bWw1fX0se2tleTpcImlzRW1iZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1lvdVR1YmV8fHRoaXMuaXNWaW1lb319LHtrZXk6XCJpc1lvdVR1YmVcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5wcm92aWRlcj09PXV0LnlvdXR1YmV9fSx7a2V5OlwiaXNWaW1lb1wiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLnByb3ZpZGVyPT09dXQudmltZW99fSx7a2V5OlwiaXNWaWRlb1wiLGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLnR5cGU9PT1odH19LHtrZXk6XCJpc0F1ZGlvXCIsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudHlwZT09PWR0fX0se2tleTpcInBsYXlpbmdcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gQm9vbGVhbih0aGlzLnJlYWR5JiYhdGhpcy5wYXVzZWQmJiF0aGlzLmVuZGVkKX19LHtrZXk6XCJwYXVzZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gQm9vbGVhbih0aGlzLm1lZGlhLnBhdXNlZCl9fSx7a2V5Olwic3RvcHBlZFwiLGdldDpmdW5jdGlvbigpe3JldHVybiBCb29sZWFuKHRoaXMucGF1c2VkJiYwPT09dGhpcy5jdXJyZW50VGltZSl9fSx7a2V5OlwiZW5kZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gQm9vbGVhbih0aGlzLm1lZGlhLmVuZGVkKX19LHtrZXk6XCJjdXJyZW50VGltZVwiLHNldDpmdW5jdGlvbihlKXtpZih0aGlzLmR1cmF0aW9uKXt2YXIgdD1LKGUpJiZlPjA7dGhpcy5tZWRpYS5jdXJyZW50VGltZT10P01hdGgubWluKGUsdGhpcy5kdXJhdGlvbik6MCx0aGlzLmRlYnVnLmxvZyhcIlNlZWtpbmcgdG8gXCIuY29uY2F0KHRoaXMuY3VycmVudFRpbWUsXCIgc2Vjb25kc1wiKSl9fSxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gTnVtYmVyKHRoaXMubWVkaWEuY3VycmVudFRpbWUpfX0se2tleTpcImJ1ZmZlcmVkXCIsZ2V0OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5tZWRpYS5idWZmZXJlZDtyZXR1cm4gSyhlKT9lOmUmJmUubGVuZ3RoJiZ0aGlzLmR1cmF0aW9uPjA/ZS5lbmQoMCkvdGhpcy5kdXJhdGlvbjowfX0se2tleTpcInNlZWtpbmdcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gQm9vbGVhbih0aGlzLm1lZGlhLnNlZWtpbmcpfX0se2tleTpcImR1cmF0aW9uXCIsZ2V0OmZ1bmN0aW9uKCl7dmFyIGU9cGFyc2VGbG9hdCh0aGlzLmNvbmZpZy5kdXJhdGlvbiksdD0odGhpcy5tZWRpYXx8e30pLmR1cmF0aW9uLGk9Syh0KSYmdCE9PTEvMD90OjA7cmV0dXJuIGV8fGl9fSx7a2V5Olwidm9sdW1lXCIsc2V0OmZ1bmN0aW9uKGUpe3ZhciB0PWU7WSh0KSYmKHQ9TnVtYmVyKHQpKSxLKHQpfHwodD10aGlzLnN0b3JhZ2UuZ2V0KFwidm9sdW1lXCIpKSxLKHQpfHwodD10aGlzLmNvbmZpZy52b2x1bWUpLHQ+MSYmKHQ9MSksdDwwJiYodD0wKSx0aGlzLmNvbmZpZy52b2x1bWU9dCx0aGlzLm1lZGlhLnZvbHVtZT10LCFhZShlKSYmdGhpcy5tdXRlZCYmdD4wJiYodGhpcy5tdXRlZD0hMSl9LGdldDpmdW5jdGlvbigpe3JldHVybiBOdW1iZXIodGhpcy5tZWRpYS52b2x1bWUpfX0se2tleTpcIm11dGVkXCIsc2V0OmZ1bmN0aW9uKGUpe3ZhciB0PWU7USh0KXx8KHQ9dGhpcy5zdG9yYWdlLmdldChcIm11dGVkXCIpKSxRKHQpfHwodD10aGlzLmNvbmZpZy5tdXRlZCksdGhpcy5jb25maWcubXV0ZWQ9dCx0aGlzLm1lZGlhLm11dGVkPXR9LGdldDpmdW5jdGlvbigpe3JldHVybiBCb29sZWFuKHRoaXMubWVkaWEubXV0ZWQpfX0se2tleTpcImhhc0F1ZGlvXCIsZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIXRoaXMuaXNIVE1MNXx8KCEhdGhpcy5pc0F1ZGlvfHwoQm9vbGVhbih0aGlzLm1lZGlhLm1vekhhc0F1ZGlvKXx8Qm9vbGVhbih0aGlzLm1lZGlhLndlYmtpdEF1ZGlvRGVjb2RlZEJ5dGVDb3VudCl8fEJvb2xlYW4odGhpcy5tZWRpYS5hdWRpb1RyYWNrcyYmdGhpcy5tZWRpYS5hdWRpb1RyYWNrcy5sZW5ndGgpKSl9fSx7a2V5Olwic3BlZWRcIixzZXQ6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxpPW51bGw7SyhlKSYmKGk9ZSksSyhpKXx8KGk9dGhpcy5zdG9yYWdlLmdldChcInNwZWVkXCIpKSxLKGkpfHwoaT10aGlzLmNvbmZpZy5zcGVlZC5zZWxlY3RlZCk7dmFyIG49dGhpcy5taW5pbXVtU3BlZWQsYT10aGlzLm1heGltdW1TcGVlZDtpPWZ1bmN0aW9uKCl7dmFyIGU9YXJndW1lbnRzLmxlbmd0aD4wJiZ2b2lkIDAhPT1hcmd1bWVudHNbMF0/YXJndW1lbnRzWzBdOjAsdD1hcmd1bWVudHMubGVuZ3RoPjEmJnZvaWQgMCE9PWFyZ3VtZW50c1sxXT9hcmd1bWVudHNbMV06MCxpPWFyZ3VtZW50cy5sZW5ndGg+MiYmdm9pZCAwIT09YXJndW1lbnRzWzJdP2FyZ3VtZW50c1syXToyNTU7cmV0dXJuIE1hdGgubWluKE1hdGgubWF4KGUsdCksaSl9KGksbixhKSx0aGlzLmNvbmZpZy5zcGVlZC5zZWxlY3RlZD1pLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dC5tZWRpYS5wbGF5YmFja1JhdGU9aX0pLDApfSxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gTnVtYmVyKHRoaXMubWVkaWEucGxheWJhY2tSYXRlKX19LHtrZXk6XCJtaW5pbXVtU3BlZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1lvdVR1YmU/TWF0aC5taW4uYXBwbHkoTWF0aCxsKHRoaXMub3B0aW9ucy5zcGVlZCkpOnRoaXMuaXNWaW1lbz8uNTouMDYyNX19LHtrZXk6XCJtYXhpbXVtU3BlZWRcIixnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1lvdVR1YmU/TWF0aC5tYXguYXBwbHkoTWF0aCxsKHRoaXMub3B0aW9ucy5zcGVlZCkpOnRoaXMuaXNWaW1lbz8yOjE2fX0se2tleTpcInF1YWxpdHlcIixzZXQ6ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5jb25maWcucXVhbGl0eSxpPXRoaXMub3B0aW9ucy5xdWFsaXR5O2lmKGkubGVuZ3RoKXt2YXIgbj1bIWFlKGUpJiZOdW1iZXIoZSksdGhpcy5zdG9yYWdlLmdldChcInF1YWxpdHlcIiksdC5zZWxlY3RlZCx0LmRlZmF1bHRdLmZpbmQoSyksYT0hMDtpZighaS5pbmNsdWRlcyhuKSl7dmFyIHM9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gJChlKSYmZS5sZW5ndGg/ZS5yZWR1Y2UoKGZ1bmN0aW9uKGUsaSl7cmV0dXJuIE1hdGguYWJzKGktdCk8TWF0aC5hYnMoZS10KT9pOmV9KSk6bnVsbH0oaSxuKTt0aGlzLmRlYnVnLndhcm4oXCJVbnN1cHBvcnRlZCBxdWFsaXR5IG9wdGlvbjogXCIuY29uY2F0KG4sXCIsIHVzaW5nIFwiKS5jb25jYXQocyxcIiBpbnN0ZWFkXCIpKSxuPXMsYT0hMX10LnNlbGVjdGVkPW4sdGhpcy5tZWRpYS5xdWFsaXR5PW4sYSYmdGhpcy5zdG9yYWdlLnNldCh7cXVhbGl0eTpufSl9fSxnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5tZWRpYS5xdWFsaXR5fX0se2tleTpcImxvb3BcIixzZXQ6ZnVuY3Rpb24oZSl7dmFyIHQ9UShlKT9lOnRoaXMuY29uZmlnLmxvb3AuYWN0aXZlO3RoaXMuY29uZmlnLmxvb3AuYWN0aXZlPXQsdGhpcy5tZWRpYS5sb29wPXR9LGdldDpmdW5jdGlvbigpe3JldHVybiBCb29sZWFuKHRoaXMubWVkaWEubG9vcCl9fSx7a2V5Olwic291cmNlXCIsc2V0OmZ1bmN0aW9uKGUpe3h0LmNoYW5nZS5jYWxsKHRoaXMsZSl9LGdldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLm1lZGlhLmN1cnJlbnRTcmN9fSx7a2V5OlwiZG93bmxvYWRcIixnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLmNvbmZpZy51cmxzLmRvd25sb2FkO3JldHVybiBuZShlKT9lOnRoaXMuc291cmNlfSxzZXQ6ZnVuY3Rpb24oZSl7bmUoZSkmJih0aGlzLmNvbmZpZy51cmxzLmRvd25sb2FkPWUsbnQuc2V0RG93bmxvYWRVcmwuY2FsbCh0aGlzKSl9fSx7a2V5OlwicG9zdGVyXCIsc2V0OmZ1bmN0aW9uKGUpe3RoaXMuaXNWaWRlbz95dC5zZXRQb3N0ZXIuY2FsbCh0aGlzLGUsITEpLmNhdGNoKChmdW5jdGlvbigpe30pKTp0aGlzLmRlYnVnLndhcm4oXCJQb3N0ZXIgY2FuIG9ubHkgYmUgc2V0IGZvciB2aWRlb1wiKX0sZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaXNWaWRlbz90aGlzLm1lZGlhLmdldEF0dHJpYnV0ZShcInBvc3RlclwiKXx8dGhpcy5tZWRpYS5nZXRBdHRyaWJ1dGUoXCJkYXRhLXBvc3RlclwiKTpudWxsfX0se2tleTpcInJhdGlvXCIsZ2V0OmZ1bmN0aW9uKCl7aWYoIXRoaXMuaXNWaWRlbylyZXR1cm4gbnVsbDt2YXIgZT1IZShGZS5jYWxsKHRoaXMpKTtyZXR1cm4gJChlKT9lLmpvaW4oXCI6XCIpOmV9LHNldDpmdW5jdGlvbihlKXt0aGlzLmlzVmlkZW8/WShlKSYmcWUoZSk/KHRoaXMuY29uZmlnLnJhdGlvPWUsUmUuY2FsbCh0aGlzKSk6dGhpcy5kZWJ1Zy5lcnJvcihcIkludmFsaWQgYXNwZWN0IHJhdGlvIHNwZWNpZmllZCAoXCIuY29uY2F0KGUsXCIpXCIpKTp0aGlzLmRlYnVnLndhcm4oXCJBc3BlY3QgcmF0aW8gY2FuIG9ubHkgYmUgc2V0IGZvciB2aWRlb1wiKX19LHtrZXk6XCJhdXRvcGxheVwiLHNldDpmdW5jdGlvbihlKXt2YXIgdD1RKGUpP2U6dGhpcy5jb25maWcuYXV0b3BsYXk7dGhpcy5jb25maWcuYXV0b3BsYXk9dH0sZ2V0OmZ1bmN0aW9uKCl7cmV0dXJuIEJvb2xlYW4odGhpcy5jb25maWcuYXV0b3BsYXkpfX0se2tleTpcImN1cnJlbnRUcmFja1wiLHNldDpmdW5jdGlvbihlKXtydC5zZXQuY2FsbCh0aGlzLGUsITEpfSxnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLmNhcHRpb25zLHQ9ZS50b2dnbGVkLGk9ZS5jdXJyZW50VHJhY2s7cmV0dXJuIHQ/aTotMX19LHtrZXk6XCJsYW5ndWFnZVwiLHNldDpmdW5jdGlvbihlKXtydC5zZXRMYW5ndWFnZS5jYWxsKHRoaXMsZSwhMSl9LGdldDpmdW5jdGlvbigpe3JldHVybihydC5nZXRDdXJyZW50VHJhY2suY2FsbCh0aGlzKXx8e30pLmxhbmd1YWdlfX0se2tleTpcInBpcFwiLHNldDpmdW5jdGlvbihlKXtpZihFZS5waXApe3ZhciB0PVEoZSk/ZTohdGhpcy5waXA7WCh0aGlzLm1lZGlhLndlYmtpdFNldFByZXNlbnRhdGlvbk1vZGUpJiZ0aGlzLm1lZGlhLndlYmtpdFNldFByZXNlbnRhdGlvbk1vZGUodD9sdDpjdCksWCh0aGlzLm1lZGlhLnJlcXVlc3RQaWN0dXJlSW5QaWN0dXJlKSYmKCF0aGlzLnBpcCYmdD90aGlzLm1lZGlhLnJlcXVlc3RQaWN0dXJlSW5QaWN0dXJlKCk6dGhpcy5waXAmJiF0JiZkb2N1bWVudC5leGl0UGljdHVyZUluUGljdHVyZSgpKX19LGdldDpmdW5jdGlvbigpe3JldHVybiBFZS5waXA/YWUodGhpcy5tZWRpYS53ZWJraXRQcmVzZW50YXRpb25Nb2RlKT90aGlzLm1lZGlhPT09ZG9jdW1lbnQucGljdHVyZUluUGljdHVyZUVsZW1lbnQ6dGhpcy5tZWRpYS53ZWJraXRQcmVzZW50YXRpb25Nb2RlPT09bHQ6bnVsbH19XSxbe2tleTpcInN1cHBvcnRlZFwiLHZhbHVlOmZ1bmN0aW9uKGUsdCxpKXtyZXR1cm4gRWUuY2hlY2soZSx0LGkpfX0se2tleTpcImxvYWRTcHJpdGVcIix2YWx1ZTpmdW5jdGlvbihlLHQpe3JldHVybiBHZShlLHQpfX0se2tleTpcInNldHVwXCIsdmFsdWU6ZnVuY3Rpb24oZSl7dmFyIGk9YXJndW1lbnRzLmxlbmd0aD4xJiZ2b2lkIDAhPT1hcmd1bWVudHNbMV0/YXJndW1lbnRzWzFdOnt9LG49bnVsbDtyZXR1cm4gWShlKT9uPUFycmF5LmZyb20oZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChlKSk6SihlKT9uPUFycmF5LmZyb20oZSk6JChlKSYmKG49ZS5maWx0ZXIoRykpLGFlKG4pP251bGw6bi5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBuZXcgdChlLGkpfSkpfX1dKSx0fSgpO3JldHVybiBMdC5kZWZhdWx0cz0oSXQ9b3QsSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShJdCkpKSxMdH0pKTtcbi8vIyBzb3VyY2VNYXBwaW5nVVJMPXBseXIubWluLmpzLm1hcFxuIiwiLyohXG4gKiBVbmlkcmFnZ2VyIHYyLjMuMFxuICogRHJhZ2dhYmxlIGJhc2UgY2xhc3NcbiAqIE1JVCBsaWNlbnNlXG4gKi9cblxuLypqc2hpbnQgYnJvd3NlcjogdHJ1ZSwgdW51c2VkOiB0cnVlLCB1bmRlZjogdHJ1ZSwgc3RyaWN0OiB0cnVlICovXG5cbiggZnVuY3Rpb24oIHdpbmRvdywgZmFjdG9yeSApIHtcbiAgLy8gdW5pdmVyc2FsIG1vZHVsZSBkZWZpbml0aW9uXG4gIC8qanNoaW50IHN0cmljdDogZmFsc2UgKi8gLypnbG9iYWxzIGRlZmluZSwgbW9kdWxlLCByZXF1aXJlICovXG5cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICd1bmlwb2ludGVyL3VuaXBvaW50ZXInXG4gICAgXSwgZnVuY3Rpb24oIFVuaXBvaW50ZXIgKSB7XG4gICAgICByZXR1cm4gZmFjdG9yeSggd2luZG93LCBVbmlwb2ludGVyICk7XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAoIHR5cGVvZiBtb2R1bGUgPT0gJ29iamVjdCcgJiYgbW9kdWxlLmV4cG9ydHMgKSB7XG4gICAgLy8gQ29tbW9uSlNcbiAgICBtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoXG4gICAgICB3aW5kb3csXG4gICAgICByZXF1aXJlKCd1bmlwb2ludGVyJylcbiAgICApO1xuICB9IGVsc2Uge1xuICAgIC8vIGJyb3dzZXIgZ2xvYmFsXG4gICAgd2luZG93LlVuaWRyYWdnZXIgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgd2luZG93LlVuaXBvaW50ZXJcbiAgICApO1xuICB9XG5cbn0oIHdpbmRvdywgZnVuY3Rpb24gZmFjdG9yeSggd2luZG93LCBVbmlwb2ludGVyICkge1xuXG4ndXNlIHN0cmljdCc7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFVuaWRyYWdnZXIgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuZnVuY3Rpb24gVW5pZHJhZ2dlcigpIHt9XG5cbi8vIGluaGVyaXQgVW5pcG9pbnRlciAmIEV2RW1pdHRlclxudmFyIHByb3RvID0gVW5pZHJhZ2dlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBVbmlwb2ludGVyLnByb3RvdHlwZSApO1xuXG4vLyAtLS0tLSBiaW5kIHN0YXJ0IC0tLS0tIC8vXG5cbnByb3RvLmJpbmRIYW5kbGVzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2JpbmRIYW5kbGVzKCB0cnVlICk7XG59O1xuXG5wcm90by51bmJpbmRIYW5kbGVzID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2JpbmRIYW5kbGVzKCBmYWxzZSApO1xufTtcblxuLyoqXG4gKiBBZGQgb3IgcmVtb3ZlIHN0YXJ0IGV2ZW50XG4gKiBAcGFyYW0ge0Jvb2xlYW59IGlzQWRkXG4gKi9cbnByb3RvLl9iaW5kSGFuZGxlcyA9IGZ1bmN0aW9uKCBpc0FkZCApIHtcbiAgLy8gbXVuZ2UgaXNBZGQsIGRlZmF1bHQgdG8gdHJ1ZVxuICBpc0FkZCA9IGlzQWRkID09PSB1bmRlZmluZWQgPyB0cnVlIDogaXNBZGQ7XG4gIC8vIGJpbmQgZWFjaCBoYW5kbGVcbiAgdmFyIGJpbmRNZXRob2QgPSBpc0FkZCA/ICdhZGRFdmVudExpc3RlbmVyJyA6ICdyZW1vdmVFdmVudExpc3RlbmVyJztcbiAgdmFyIHRvdWNoQWN0aW9uID0gaXNBZGQgPyB0aGlzLl90b3VjaEFjdGlvblZhbHVlIDogJyc7XG4gIGZvciAoIHZhciBpPTA7IGkgPCB0aGlzLmhhbmRsZXMubGVuZ3RoOyBpKysgKSB7XG4gICAgdmFyIGhhbmRsZSA9IHRoaXMuaGFuZGxlc1tpXTtcbiAgICB0aGlzLl9iaW5kU3RhcnRFdmVudCggaGFuZGxlLCBpc0FkZCApO1xuICAgIGhhbmRsZVsgYmluZE1ldGhvZCBdKCAnY2xpY2snLCB0aGlzICk7XG4gICAgLy8gdG91Y2gtYWN0aW9uOiBub25lIHRvIG92ZXJyaWRlIGJyb3dzZXIgdG91Y2ggZ2VzdHVyZXMuIG1ldGFmaXp6eS9mbGlja2l0eSM1NDBcbiAgICBpZiAoIHdpbmRvdy5Qb2ludGVyRXZlbnQgKSB7XG4gICAgICBoYW5kbGUuc3R5bGUudG91Y2hBY3Rpb24gPSB0b3VjaEFjdGlvbjtcbiAgICB9XG4gIH1cbn07XG5cbi8vIHByb3RvdHlwZSBzbyBpdCBjYW4gYmUgb3ZlcndyaXRlYWJsZSBieSBGbGlja2l0eVxucHJvdG8uX3RvdWNoQWN0aW9uVmFsdWUgPSAnbm9uZSc7XG5cbi8vIC0tLS0tIHN0YXJ0IGV2ZW50IC0tLS0tIC8vXG5cbi8qKlxuICogcG9pbnRlciBzdGFydFxuICogQHBhcmFtIHtFdmVudH0gZXZlbnRcbiAqIEBwYXJhbSB7RXZlbnQgb3IgVG91Y2h9IHBvaW50ZXJcbiAqL1xucHJvdG8ucG9pbnRlckRvd24gPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIHZhciBpc09rYXkgPSB0aGlzLm9rYXlQb2ludGVyRG93biggZXZlbnQgKTtcbiAgaWYgKCAhaXNPa2F5ICkge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyB0cmFjayBzdGFydCBldmVudCBwb3NpdGlvblxuICB0aGlzLnBvaW50ZXJEb3duUG9pbnRlciA9IHBvaW50ZXI7XG5cbiAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgdGhpcy5wb2ludGVyRG93bkJsdXIoKTtcbiAgLy8gYmluZCBtb3ZlIGFuZCBlbmQgZXZlbnRzXG4gIHRoaXMuX2JpbmRQb3N0U3RhcnRFdmVudHMoIGV2ZW50ICk7XG4gIHRoaXMuZW1pdEV2ZW50KCAncG9pbnRlckRvd24nLCBbIGV2ZW50LCBwb2ludGVyIF0gKTtcbn07XG5cbi8vIG5vZGVzIHRoYXQgaGF2ZSB0ZXh0IGZpZWxkc1xudmFyIGN1cnNvck5vZGVzID0ge1xuICBURVhUQVJFQTogdHJ1ZSxcbiAgSU5QVVQ6IHRydWUsXG4gIFNFTEVDVDogdHJ1ZSxcbiAgT1BUSU9OOiB0cnVlLFxufTtcblxuLy8gaW5wdXQgdHlwZXMgdGhhdCBkbyBub3QgaGF2ZSB0ZXh0IGZpZWxkc1xudmFyIGNsaWNrVHlwZXMgPSB7XG4gIHJhZGlvOiB0cnVlLFxuICBjaGVja2JveDogdHJ1ZSxcbiAgYnV0dG9uOiB0cnVlLFxuICBzdWJtaXQ6IHRydWUsXG4gIGltYWdlOiB0cnVlLFxuICBmaWxlOiB0cnVlLFxufTtcblxuLy8gZGlzbWlzcyBpbnB1dHMgd2l0aCB0ZXh0IGZpZWxkcy4gZmxpY2tpdHkjNDAzLCBmbGlja2l0eSM0MDRcbnByb3RvLm9rYXlQb2ludGVyRG93biA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgdmFyIGlzQ3Vyc29yTm9kZSA9IGN1cnNvck5vZGVzWyBldmVudC50YXJnZXQubm9kZU5hbWUgXTtcbiAgdmFyIGlzQ2xpY2tUeXBlID0gY2xpY2tUeXBlc1sgZXZlbnQudGFyZ2V0LnR5cGUgXTtcbiAgdmFyIGlzT2theSA9ICFpc0N1cnNvck5vZGUgfHwgaXNDbGlja1R5cGU7XG4gIGlmICggIWlzT2theSApIHtcbiAgICB0aGlzLl9wb2ludGVyUmVzZXQoKTtcbiAgfVxuICByZXR1cm4gaXNPa2F5O1xufTtcblxuLy8ga2x1ZGdlIHRvIGJsdXIgcHJldmlvdXNseSBmb2N1c2VkIGlucHV0XG5wcm90by5wb2ludGVyRG93bkJsdXIgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGZvY3VzZWQgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50O1xuICAvLyBkbyBub3QgYmx1ciBib2R5IGZvciBJRTEwLCBtZXRhZml6enkvZmxpY2tpdHkjMTE3XG4gIHZhciBjYW5CbHVyID0gZm9jdXNlZCAmJiBmb2N1c2VkLmJsdXIgJiYgZm9jdXNlZCAhPSBkb2N1bWVudC5ib2R5O1xuICBpZiAoIGNhbkJsdXIgKSB7XG4gICAgZm9jdXNlZC5ibHVyKCk7XG4gIH1cbn07XG5cbi8vIC0tLS0tIG1vdmUgZXZlbnQgLS0tLS0gLy9cblxuLyoqXG4gKiBkcmFnIG1vdmVcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50XG4gKiBAcGFyYW0ge0V2ZW50IG9yIFRvdWNofSBwb2ludGVyXG4gKi9cbnByb3RvLnBvaW50ZXJNb3ZlID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB2YXIgbW92ZVZlY3RvciA9IHRoaXMuX2RyYWdQb2ludGVyTW92ZSggZXZlbnQsIHBvaW50ZXIgKTtcbiAgdGhpcy5lbWl0RXZlbnQoICdwb2ludGVyTW92ZScsIFsgZXZlbnQsIHBvaW50ZXIsIG1vdmVWZWN0b3IgXSApO1xuICB0aGlzLl9kcmFnTW92ZSggZXZlbnQsIHBvaW50ZXIsIG1vdmVWZWN0b3IgKTtcbn07XG5cbi8vIGJhc2UgcG9pbnRlciBtb3ZlIGxvZ2ljXG5wcm90by5fZHJhZ1BvaW50ZXJNb3ZlID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB2YXIgbW92ZVZlY3RvciA9IHtcbiAgICB4OiBwb2ludGVyLnBhZ2VYIC0gdGhpcy5wb2ludGVyRG93blBvaW50ZXIucGFnZVgsXG4gICAgeTogcG9pbnRlci5wYWdlWSAtIHRoaXMucG9pbnRlckRvd25Qb2ludGVyLnBhZ2VZXG4gIH07XG4gIC8vIHN0YXJ0IGRyYWcgaWYgcG9pbnRlciBoYXMgbW92ZWQgZmFyIGVub3VnaCB0byBzdGFydCBkcmFnXG4gIGlmICggIXRoaXMuaXNEcmFnZ2luZyAmJiB0aGlzLmhhc0RyYWdTdGFydGVkKCBtb3ZlVmVjdG9yICkgKSB7XG4gICAgdGhpcy5fZHJhZ1N0YXJ0KCBldmVudCwgcG9pbnRlciApO1xuICB9XG4gIHJldHVybiBtb3ZlVmVjdG9yO1xufTtcblxuLy8gY29uZGl0aW9uIGlmIHBvaW50ZXIgaGFzIG1vdmVkIGZhciBlbm91Z2ggdG8gc3RhcnQgZHJhZ1xucHJvdG8uaGFzRHJhZ1N0YXJ0ZWQgPSBmdW5jdGlvbiggbW92ZVZlY3RvciApIHtcbiAgcmV0dXJuIE1hdGguYWJzKCBtb3ZlVmVjdG9yLnggKSA+IDMgfHwgTWF0aC5hYnMoIG1vdmVWZWN0b3IueSApID4gMztcbn07XG5cbi8vIC0tLS0tIGVuZCBldmVudCAtLS0tLSAvL1xuXG4vKipcbiAqIHBvaW50ZXIgdXBcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50XG4gKiBAcGFyYW0ge0V2ZW50IG9yIFRvdWNofSBwb2ludGVyXG4gKi9cbnByb3RvLnBvaW50ZXJVcCA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgdGhpcy5lbWl0RXZlbnQoICdwb2ludGVyVXAnLCBbIGV2ZW50LCBwb2ludGVyIF0gKTtcbiAgdGhpcy5fZHJhZ1BvaW50ZXJVcCggZXZlbnQsIHBvaW50ZXIgKTtcbn07XG5cbnByb3RvLl9kcmFnUG9pbnRlclVwID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICBpZiAoIHRoaXMuaXNEcmFnZ2luZyApIHtcbiAgICB0aGlzLl9kcmFnRW5kKCBldmVudCwgcG9pbnRlciApO1xuICB9IGVsc2Uge1xuICAgIC8vIHBvaW50ZXIgZGlkbid0IG1vdmUgZW5vdWdoIGZvciBkcmFnIHRvIHN0YXJ0XG4gICAgdGhpcy5fc3RhdGljQ2xpY2soIGV2ZW50LCBwb2ludGVyICk7XG4gIH1cbn07XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIGRyYWcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gLy9cblxuLy8gZHJhZ1N0YXJ0XG5wcm90by5fZHJhZ1N0YXJ0ID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLmlzRHJhZ2dpbmcgPSB0cnVlO1xuICAvLyBwcmV2ZW50IGNsaWNrc1xuICB0aGlzLmlzUHJldmVudGluZ0NsaWNrcyA9IHRydWU7XG4gIHRoaXMuZHJhZ1N0YXJ0KCBldmVudCwgcG9pbnRlciApO1xufTtcblxucHJvdG8uZHJhZ1N0YXJ0ID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLmVtaXRFdmVudCggJ2RyYWdTdGFydCcsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gZHJhZ01vdmVcbnByb3RvLl9kcmFnTW92ZSA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciwgbW92ZVZlY3RvciApIHtcbiAgLy8gZG8gbm90IGRyYWcgaWYgbm90IGRyYWdnaW5nIHlldFxuICBpZiAoICF0aGlzLmlzRHJhZ2dpbmcgKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgdGhpcy5kcmFnTW92ZSggZXZlbnQsIHBvaW50ZXIsIG1vdmVWZWN0b3IgKTtcbn07XG5cbnByb3RvLmRyYWdNb3ZlID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyLCBtb3ZlVmVjdG9yICkge1xuICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICB0aGlzLmVtaXRFdmVudCggJ2RyYWdNb3ZlJywgWyBldmVudCwgcG9pbnRlciwgbW92ZVZlY3RvciBdICk7XG59O1xuXG4vLyBkcmFnRW5kXG5wcm90by5fZHJhZ0VuZCA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgLy8gc2V0IGZsYWdzXG4gIHRoaXMuaXNEcmFnZ2luZyA9IGZhbHNlO1xuICAvLyByZS1lbmFibGUgY2xpY2tpbmcgYXN5bmNcbiAgc2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG4gICAgZGVsZXRlIHRoaXMuaXNQcmV2ZW50aW5nQ2xpY2tzO1xuICB9LmJpbmQoIHRoaXMgKSApO1xuXG4gIHRoaXMuZHJhZ0VuZCggZXZlbnQsIHBvaW50ZXIgKTtcbn07XG5cbnByb3RvLmRyYWdFbmQgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIHRoaXMuZW1pdEV2ZW50KCAnZHJhZ0VuZCcsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gLS0tLS0gb25jbGljayAtLS0tLSAvL1xuXG4vLyBoYW5kbGUgYWxsIGNsaWNrcyBhbmQgcHJldmVudCBjbGlja3Mgd2hlbiBkcmFnZ2luZ1xucHJvdG8ub25jbGljayA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgaWYgKCB0aGlzLmlzUHJldmVudGluZ0NsaWNrcyApIHtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICB9XG59O1xuXG4vLyAtLS0tLSBzdGF0aWNDbGljayAtLS0tLSAvL1xuXG4vLyB0cmlnZ2VyZWQgYWZ0ZXIgcG9pbnRlciBkb3duICYgdXAgd2l0aCBuby90aW55IG1vdmVtZW50XG5wcm90by5fc3RhdGljQ2xpY2sgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIC8vIGlnbm9yZSBlbXVsYXRlZCBtb3VzZSB1cCBjbGlja3NcbiAgaWYgKCB0aGlzLmlzSWdub3JpbmdNb3VzZVVwICYmIGV2ZW50LnR5cGUgPT0gJ21vdXNldXAnICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMuc3RhdGljQ2xpY2soIGV2ZW50LCBwb2ludGVyICk7XG5cbiAgLy8gc2V0IGZsYWcgZm9yIGVtdWxhdGVkIGNsaWNrcyAzMDBtcyBhZnRlciB0b3VjaGVuZFxuICBpZiAoIGV2ZW50LnR5cGUgIT0gJ21vdXNldXAnICkge1xuICAgIHRoaXMuaXNJZ25vcmluZ01vdXNlVXAgPSB0cnVlO1xuICAgIC8vIHJlc2V0IGZsYWcgYWZ0ZXIgMzAwbXNcbiAgICBzZXRUaW1lb3V0KCBmdW5jdGlvbigpIHtcbiAgICAgIGRlbGV0ZSB0aGlzLmlzSWdub3JpbmdNb3VzZVVwO1xuICAgIH0uYmluZCggdGhpcyApLCA0MDAgKTtcbiAgfVxufTtcblxucHJvdG8uc3RhdGljQ2xpY2sgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIHRoaXMuZW1pdEV2ZW50KCAnc3RhdGljQ2xpY2snLCBbIGV2ZW50LCBwb2ludGVyIF0gKTtcbn07XG5cbi8vIC0tLS0tIHV0aWxzIC0tLS0tIC8vXG5cblVuaWRyYWdnZXIuZ2V0UG9pbnRlclBvaW50ID0gVW5pcG9pbnRlci5nZXRQb2ludGVyUG9pbnQ7XG5cbi8vIC0tLS0tICAtLS0tLSAvL1xuXG5yZXR1cm4gVW5pZHJhZ2dlcjtcblxufSkpO1xuIiwiLyohXG4gKiBVbmlwb2ludGVyIHYyLjMuMFxuICogYmFzZSBjbGFzcyBmb3IgZG9pbmcgb25lIHRoaW5nIHdpdGggcG9pbnRlciBldmVudFxuICogTUlUIGxpY2Vuc2VcbiAqL1xuXG4vKmpzaGludCBicm93c2VyOiB0cnVlLCB1bmRlZjogdHJ1ZSwgdW51c2VkOiB0cnVlLCBzdHJpY3Q6IHRydWUgKi9cblxuKCBmdW5jdGlvbiggd2luZG93LCBmYWN0b3J5ICkge1xuICAvLyB1bml2ZXJzYWwgbW9kdWxlIGRlZmluaXRpb25cbiAgLyoganNoaW50IHN0cmljdDogZmFsc2UgKi8gLypnbG9iYWwgZGVmaW5lLCBtb2R1bGUsIHJlcXVpcmUgKi9cbiAgaWYgKCB0eXBlb2YgZGVmaW5lID09ICdmdW5jdGlvbicgJiYgZGVmaW5lLmFtZCApIHtcbiAgICAvLyBBTURcbiAgICBkZWZpbmUoIFtcbiAgICAgICdldi1lbWl0dGVyL2V2LWVtaXR0ZXInXG4gICAgXSwgZnVuY3Rpb24oIEV2RW1pdHRlciApIHtcbiAgICAgIHJldHVybiBmYWN0b3J5KCB3aW5kb3csIEV2RW1pdHRlciApO1xuICAgIH0pO1xuICB9IGVsc2UgaWYgKCB0eXBlb2YgbW9kdWxlID09ICdvYmplY3QnICYmIG1vZHVsZS5leHBvcnRzICkge1xuICAgIC8vIENvbW1vbkpTXG4gICAgbW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KFxuICAgICAgd2luZG93LFxuICAgICAgcmVxdWlyZSgnZXYtZW1pdHRlcicpXG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICAvLyBicm93c2VyIGdsb2JhbFxuICAgIHdpbmRvdy5Vbmlwb2ludGVyID0gZmFjdG9yeShcbiAgICAgIHdpbmRvdyxcbiAgICAgIHdpbmRvdy5FdkVtaXR0ZXJcbiAgICApO1xuICB9XG5cbn0oIHdpbmRvdywgZnVuY3Rpb24gZmFjdG9yeSggd2luZG93LCBFdkVtaXR0ZXIgKSB7XG5cbid1c2Ugc3RyaWN0JztcblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbmZ1bmN0aW9uIFVuaXBvaW50ZXIoKSB7fVxuXG4vLyBpbmhlcml0IEV2RW1pdHRlclxudmFyIHByb3RvID0gVW5pcG9pbnRlci5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKCBFdkVtaXR0ZXIucHJvdG90eXBlICk7XG5cbnByb3RvLmJpbmRTdGFydEV2ZW50ID0gZnVuY3Rpb24oIGVsZW0gKSB7XG4gIHRoaXMuX2JpbmRTdGFydEV2ZW50KCBlbGVtLCB0cnVlICk7XG59O1xuXG5wcm90by51bmJpbmRTdGFydEV2ZW50ID0gZnVuY3Rpb24oIGVsZW0gKSB7XG4gIHRoaXMuX2JpbmRTdGFydEV2ZW50KCBlbGVtLCBmYWxzZSApO1xufTtcblxuLyoqXG4gKiBBZGQgb3IgcmVtb3ZlIHN0YXJ0IGV2ZW50XG4gKiBAcGFyYW0ge0Jvb2xlYW59IGlzQWRkIC0gcmVtb3ZlIGlmIGZhbHNleVxuICovXG5wcm90by5fYmluZFN0YXJ0RXZlbnQgPSBmdW5jdGlvbiggZWxlbSwgaXNBZGQgKSB7XG4gIC8vIG11bmdlIGlzQWRkLCBkZWZhdWx0IHRvIHRydWVcbiAgaXNBZGQgPSBpc0FkZCA9PT0gdW5kZWZpbmVkID8gdHJ1ZSA6IGlzQWRkO1xuICB2YXIgYmluZE1ldGhvZCA9IGlzQWRkID8gJ2FkZEV2ZW50TGlzdGVuZXInIDogJ3JlbW92ZUV2ZW50TGlzdGVuZXInO1xuXG4gIC8vIGRlZmF1bHQgdG8gbW91c2UgZXZlbnRzXG4gIHZhciBzdGFydEV2ZW50ID0gJ21vdXNlZG93bic7XG4gIGlmICggd2luZG93LlBvaW50ZXJFdmVudCApIHtcbiAgICAvLyBQb2ludGVyIEV2ZW50c1xuICAgIHN0YXJ0RXZlbnQgPSAncG9pbnRlcmRvd24nO1xuICB9IGVsc2UgaWYgKCAnb250b3VjaHN0YXJ0JyBpbiB3aW5kb3cgKSB7XG4gICAgLy8gVG91Y2ggRXZlbnRzLiBpT1MgU2FmYXJpXG4gICAgc3RhcnRFdmVudCA9ICd0b3VjaHN0YXJ0JztcbiAgfVxuICBlbGVtWyBiaW5kTWV0aG9kIF0oIHN0YXJ0RXZlbnQsIHRoaXMgKTtcbn07XG5cbi8vIHRyaWdnZXIgaGFuZGxlciBtZXRob2RzIGZvciBldmVudHNcbnByb3RvLmhhbmRsZUV2ZW50ID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICB2YXIgbWV0aG9kID0gJ29uJyArIGV2ZW50LnR5cGU7XG4gIGlmICggdGhpc1sgbWV0aG9kIF0gKSB7XG4gICAgdGhpc1sgbWV0aG9kIF0oIGV2ZW50ICk7XG4gIH1cbn07XG5cbi8vIHJldHVybnMgdGhlIHRvdWNoIHRoYXQgd2UncmUga2VlcGluZyB0cmFjayBvZlxucHJvdG8uZ2V0VG91Y2ggPSBmdW5jdGlvbiggdG91Y2hlcyApIHtcbiAgZm9yICggdmFyIGk9MDsgaSA8IHRvdWNoZXMubGVuZ3RoOyBpKysgKSB7XG4gICAgdmFyIHRvdWNoID0gdG91Y2hlc1tpXTtcbiAgICBpZiAoIHRvdWNoLmlkZW50aWZpZXIgPT0gdGhpcy5wb2ludGVySWRlbnRpZmllciApIHtcbiAgICAgIHJldHVybiB0b3VjaDtcbiAgICB9XG4gIH1cbn07XG5cbi8vIC0tLS0tIHN0YXJ0IGV2ZW50IC0tLS0tIC8vXG5cbnByb3RvLm9ubW91c2Vkb3duID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICAvLyBkaXNtaXNzIGNsaWNrcyBmcm9tIHJpZ2h0IG9yIG1pZGRsZSBidXR0b25zXG4gIHZhciBidXR0b24gPSBldmVudC5idXR0b247XG4gIGlmICggYnV0dG9uICYmICggYnV0dG9uICE9PSAwICYmIGJ1dHRvbiAhPT0gMSApICkge1xuICAgIHJldHVybjtcbiAgfVxuICB0aGlzLl9wb2ludGVyRG93biggZXZlbnQsIGV2ZW50ICk7XG59O1xuXG5wcm90by5vbnRvdWNoc3RhcnQgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIHRoaXMuX3BvaW50ZXJEb3duKCBldmVudCwgZXZlbnQuY2hhbmdlZFRvdWNoZXNbMF0gKTtcbn07XG5cbnByb3RvLm9ucG9pbnRlcmRvd24gPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIHRoaXMuX3BvaW50ZXJEb3duKCBldmVudCwgZXZlbnQgKTtcbn07XG5cbi8qKlxuICogcG9pbnRlciBzdGFydFxuICogQHBhcmFtIHtFdmVudH0gZXZlbnRcbiAqIEBwYXJhbSB7RXZlbnQgb3IgVG91Y2h9IHBvaW50ZXJcbiAqL1xucHJvdG8uX3BvaW50ZXJEb3duID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICAvLyBkaXNtaXNzIHJpZ2h0IGNsaWNrIGFuZCBvdGhlciBwb2ludGVyc1xuICAvLyBidXR0b24gPSAwIGlzIG9rYXksIDEtNCBub3RcbiAgaWYgKCBldmVudC5idXR0b24gfHwgdGhpcy5pc1BvaW50ZXJEb3duICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIHRoaXMuaXNQb2ludGVyRG93biA9IHRydWU7XG4gIC8vIHNhdmUgcG9pbnRlciBpZGVudGlmaWVyIHRvIG1hdGNoIHVwIHRvdWNoIGV2ZW50c1xuICB0aGlzLnBvaW50ZXJJZGVudGlmaWVyID0gcG9pbnRlci5wb2ludGVySWQgIT09IHVuZGVmaW5lZCA/XG4gICAgLy8gcG9pbnRlcklkIGZvciBwb2ludGVyIGV2ZW50cywgdG91Y2guaW5kZW50aWZpZXIgZm9yIHRvdWNoIGV2ZW50c1xuICAgIHBvaW50ZXIucG9pbnRlcklkIDogcG9pbnRlci5pZGVudGlmaWVyO1xuXG4gIHRoaXMucG9pbnRlckRvd24oIGV2ZW50LCBwb2ludGVyICk7XG59O1xuXG5wcm90by5wb2ludGVyRG93biA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgdGhpcy5fYmluZFBvc3RTdGFydEV2ZW50cyggZXZlbnQgKTtcbiAgdGhpcy5lbWl0RXZlbnQoICdwb2ludGVyRG93bicsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gaGFzaCBvZiBldmVudHMgdG8gYmUgYm91bmQgYWZ0ZXIgc3RhcnQgZXZlbnRcbnZhciBwb3N0U3RhcnRFdmVudHMgPSB7XG4gIG1vdXNlZG93bjogWyAnbW91c2Vtb3ZlJywgJ21vdXNldXAnIF0sXG4gIHRvdWNoc3RhcnQ6IFsgJ3RvdWNobW92ZScsICd0b3VjaGVuZCcsICd0b3VjaGNhbmNlbCcgXSxcbiAgcG9pbnRlcmRvd246IFsgJ3BvaW50ZXJtb3ZlJywgJ3BvaW50ZXJ1cCcsICdwb2ludGVyY2FuY2VsJyBdLFxufTtcblxucHJvdG8uX2JpbmRQb3N0U3RhcnRFdmVudHMgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIGlmICggIWV2ZW50ICkge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBnZXQgcHJvcGVyIGV2ZW50cyB0byBtYXRjaCBzdGFydCBldmVudFxuICB2YXIgZXZlbnRzID0gcG9zdFN0YXJ0RXZlbnRzWyBldmVudC50eXBlIF07XG4gIC8vIGJpbmQgZXZlbnRzIHRvIG5vZGVcbiAgZXZlbnRzLmZvckVhY2goIGZ1bmN0aW9uKCBldmVudE5hbWUgKSB7XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgdGhpcyApO1xuICB9LCB0aGlzICk7XG4gIC8vIHNhdmUgdGhlc2UgYXJndW1lbnRzXG4gIHRoaXMuX2JvdW5kUG9pbnRlckV2ZW50cyA9IGV2ZW50cztcbn07XG5cbnByb3RvLl91bmJpbmRQb3N0U3RhcnRFdmVudHMgPSBmdW5jdGlvbigpIHtcbiAgLy8gY2hlY2sgZm9yIF9ib3VuZEV2ZW50cywgaW4gY2FzZSBkcmFnRW5kIHRyaWdnZXJlZCB0d2ljZSAob2xkIElFOCBidWcpXG4gIGlmICggIXRoaXMuX2JvdW5kUG9pbnRlckV2ZW50cyApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdGhpcy5fYm91bmRQb2ludGVyRXZlbnRzLmZvckVhY2goIGZ1bmN0aW9uKCBldmVudE5hbWUgKSB7XG4gICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgdGhpcyApO1xuICB9LCB0aGlzICk7XG5cbiAgZGVsZXRlIHRoaXMuX2JvdW5kUG9pbnRlckV2ZW50cztcbn07XG5cbi8vIC0tLS0tIG1vdmUgZXZlbnQgLS0tLS0gLy9cblxucHJvdG8ub25tb3VzZW1vdmUgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIHRoaXMuX3BvaW50ZXJNb3ZlKCBldmVudCwgZXZlbnQgKTtcbn07XG5cbnByb3RvLm9ucG9pbnRlcm1vdmUgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIGlmICggZXZlbnQucG9pbnRlcklkID09IHRoaXMucG9pbnRlcklkZW50aWZpZXIgKSB7XG4gICAgdGhpcy5fcG9pbnRlck1vdmUoIGV2ZW50LCBldmVudCApO1xuICB9XG59O1xuXG5wcm90by5vbnRvdWNobW92ZSA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgdmFyIHRvdWNoID0gdGhpcy5nZXRUb3VjaCggZXZlbnQuY2hhbmdlZFRvdWNoZXMgKTtcbiAgaWYgKCB0b3VjaCApIHtcbiAgICB0aGlzLl9wb2ludGVyTW92ZSggZXZlbnQsIHRvdWNoICk7XG4gIH1cbn07XG5cbi8qKlxuICogcG9pbnRlciBtb3ZlXG4gKiBAcGFyYW0ge0V2ZW50fSBldmVudFxuICogQHBhcmFtIHtFdmVudCBvciBUb3VjaH0gcG9pbnRlclxuICogQHByaXZhdGVcbiAqL1xucHJvdG8uX3BvaW50ZXJNb3ZlID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLnBvaW50ZXJNb3ZlKCBldmVudCwgcG9pbnRlciApO1xufTtcblxuLy8gcHVibGljXG5wcm90by5wb2ludGVyTW92ZSA9IGZ1bmN0aW9uKCBldmVudCwgcG9pbnRlciApIHtcbiAgdGhpcy5lbWl0RXZlbnQoICdwb2ludGVyTW92ZScsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gLS0tLS0gZW5kIGV2ZW50IC0tLS0tIC8vXG5cblxucHJvdG8ub25tb3VzZXVwID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuICB0aGlzLl9wb2ludGVyVXAoIGV2ZW50LCBldmVudCApO1xufTtcblxucHJvdG8ub25wb2ludGVydXAgPSBmdW5jdGlvbiggZXZlbnQgKSB7XG4gIGlmICggZXZlbnQucG9pbnRlcklkID09IHRoaXMucG9pbnRlcklkZW50aWZpZXIgKSB7XG4gICAgdGhpcy5fcG9pbnRlclVwKCBldmVudCwgZXZlbnQgKTtcbiAgfVxufTtcblxucHJvdG8ub250b3VjaGVuZCA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgdmFyIHRvdWNoID0gdGhpcy5nZXRUb3VjaCggZXZlbnQuY2hhbmdlZFRvdWNoZXMgKTtcbiAgaWYgKCB0b3VjaCApIHtcbiAgICB0aGlzLl9wb2ludGVyVXAoIGV2ZW50LCB0b3VjaCApO1xuICB9XG59O1xuXG4vKipcbiAqIHBvaW50ZXIgdXBcbiAqIEBwYXJhbSB7RXZlbnR9IGV2ZW50XG4gKiBAcGFyYW0ge0V2ZW50IG9yIFRvdWNofSBwb2ludGVyXG4gKiBAcHJpdmF0ZVxuICovXG5wcm90by5fcG9pbnRlclVwID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLl9wb2ludGVyRG9uZSgpO1xuICB0aGlzLnBvaW50ZXJVcCggZXZlbnQsIHBvaW50ZXIgKTtcbn07XG5cbi8vIHB1YmxpY1xucHJvdG8ucG9pbnRlclVwID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLmVtaXRFdmVudCggJ3BvaW50ZXJVcCcsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gLS0tLS0gcG9pbnRlciBkb25lIC0tLS0tIC8vXG5cbi8vIHRyaWdnZXJlZCBvbiBwb2ludGVyIHVwICYgcG9pbnRlciBjYW5jZWxcbnByb3RvLl9wb2ludGVyRG9uZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLl9wb2ludGVyUmVzZXQoKTtcbiAgdGhpcy5fdW5iaW5kUG9zdFN0YXJ0RXZlbnRzKCk7XG4gIHRoaXMucG9pbnRlckRvbmUoKTtcbn07XG5cbnByb3RvLl9wb2ludGVyUmVzZXQgPSBmdW5jdGlvbigpIHtcbiAgLy8gcmVzZXQgcHJvcGVydGllc1xuICB0aGlzLmlzUG9pbnRlckRvd24gPSBmYWxzZTtcbiAgZGVsZXRlIHRoaXMucG9pbnRlcklkZW50aWZpZXI7XG59O1xuXG5wcm90by5wb2ludGVyRG9uZSA9IG5vb3A7XG5cbi8vIC0tLS0tIHBvaW50ZXIgY2FuY2VsIC0tLS0tIC8vXG5cbnByb3RvLm9ucG9pbnRlcmNhbmNlbCA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgaWYgKCBldmVudC5wb2ludGVySWQgPT0gdGhpcy5wb2ludGVySWRlbnRpZmllciApIHtcbiAgICB0aGlzLl9wb2ludGVyQ2FuY2VsKCBldmVudCwgZXZlbnQgKTtcbiAgfVxufTtcblxucHJvdG8ub250b3VjaGNhbmNlbCA9IGZ1bmN0aW9uKCBldmVudCApIHtcbiAgdmFyIHRvdWNoID0gdGhpcy5nZXRUb3VjaCggZXZlbnQuY2hhbmdlZFRvdWNoZXMgKTtcbiAgaWYgKCB0b3VjaCApIHtcbiAgICB0aGlzLl9wb2ludGVyQ2FuY2VsKCBldmVudCwgdG91Y2ggKTtcbiAgfVxufTtcblxuLyoqXG4gKiBwb2ludGVyIGNhbmNlbFxuICogQHBhcmFtIHtFdmVudH0gZXZlbnRcbiAqIEBwYXJhbSB7RXZlbnQgb3IgVG91Y2h9IHBvaW50ZXJcbiAqIEBwcml2YXRlXG4gKi9cbnByb3RvLl9wb2ludGVyQ2FuY2VsID0gZnVuY3Rpb24oIGV2ZW50LCBwb2ludGVyICkge1xuICB0aGlzLl9wb2ludGVyRG9uZSgpO1xuICB0aGlzLnBvaW50ZXJDYW5jZWwoIGV2ZW50LCBwb2ludGVyICk7XG59O1xuXG4vLyBwdWJsaWNcbnByb3RvLnBvaW50ZXJDYW5jZWwgPSBmdW5jdGlvbiggZXZlbnQsIHBvaW50ZXIgKSB7XG4gIHRoaXMuZW1pdEV2ZW50KCAncG9pbnRlckNhbmNlbCcsIFsgZXZlbnQsIHBvaW50ZXIgXSApO1xufTtcblxuLy8gLS0tLS0gIC0tLS0tIC8vXG5cbi8vIHV0aWxpdHkgZnVuY3Rpb24gZm9yIGdldHRpbmcgeC95IGNvb3JkcyBmcm9tIGV2ZW50XG5Vbmlwb2ludGVyLmdldFBvaW50ZXJQb2ludCA9IGZ1bmN0aW9uKCBwb2ludGVyICkge1xuICByZXR1cm4ge1xuICAgIHg6IHBvaW50ZXIucGFnZVgsXG4gICAgeTogcG9pbnRlci5wYWdlWVxuICB9O1xufTtcblxuLy8gLS0tLS0gIC0tLS0tIC8vXG5cbnJldHVybiBVbmlwb2ludGVyO1xuXG59KSk7XG4iLCJpbXBvcnQgRmxpY2tpdHkgZnJvbSAnZmxpY2tpdHknO1xuXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgQ2Fyb3VzZWxcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IENhcm91c2VsID0gZnVuY3Rpb24gKCkge1xuICBjb25zdCBjYXJvdXNlbHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtY2Fyb3VzZWwtY29udGFpbmVyJyk7XG4gIGNvbnN0IHNsaWRlc2hvd3MgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtc2xpZGVzaG93LWNvbnRhaW5lcicpO1xuXG4gIC8vIFNldCB1cCBjYXJvdXNlbHNcbiAgW10uZm9yRWFjaC5jYWxsKGNhcm91c2VscywgZnVuY3Rpb24gKGVsZW0pIHtcbiAgICBsZXQgY2Fyb3VzZWxFbGVtID0gZWxlbS5xdWVyeVNlbGVjdG9yKCcuanMtY2Fyb3VzZWwnKTtcbiAgICBsZXQgbmV4dEJ1dHRvbiA9IGVsZW0ucXVlcnlTZWxlY3RvcignLmpzLWNhcm91c2VsLW5leHQnKTtcbiAgICBsZXQgcHJldkJ1dHRvbiA9IGVsZW0ucXVlcnlTZWxlY3RvcignLmpzLWNhcm91c2VsLXByZXYnKTtcblxuICAgIGxldCBjYXJvdXNlbCA9IG5ldyBGbGlja2l0eShjYXJvdXNlbEVsZW0sIHtcbiAgICAgIHByZXZOZXh0QnV0dG9uczogZmFsc2UsXG4gICAgICBwYWdlRG90czogZmFsc2UsXG4gICAgICBpbml0aWFsSW5kZXg6IDEsXG4gICAgICBsYXp5TG9hZDogMSxcbiAgICAgIHdyYXBBcm91bmQ6IHRydWUsXG4gICAgICBhdXRvUGxheTogMjAwMFxuICAgIH0pO1xuXG4gICAgbmV4dEJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNhcm91c2VsLm5leHQoKTtcbiAgICB9KTtcblxuICAgIHByZXZCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbiAoKSB7XG4gICAgICBjYXJvdXNlbC5wcmV2aW91cygpO1xuICAgIH0pO1xuICB9KTtcblxuICAvLyBTZXQgdXAgc2xpZGVzaG93c1xuICBbXS5mb3JFYWNoLmNhbGwoc2xpZGVzaG93cywgZnVuY3Rpb24gKGVsZW0pIHtcbiAgICBsZXQgc2xpZGVzaG93RWxlbSA9IGVsZW0ucXVlcnlTZWxlY3RvcignLmpzLXNsaWRlc2hvdycpO1xuICAgIGxldCBuZXh0QnV0dG9uID0gZWxlbS5xdWVyeVNlbGVjdG9yKCcuanMtc2xpZGVzaG93LW5leHQnKTtcbiAgICBsZXQgcHJldkJ1dHRvbiA9IGVsZW0ucXVlcnlTZWxlY3RvcignLmpzLXNsaWRlc2hvdy1wcmV2Jyk7XG4gICAgbGV0IGluZGljYXRvcnMgPSBlbGVtLnF1ZXJ5U2VsZWN0b3JBbGwoJy5qcy1zbGlkZXNob3ctaW5kaWNhdG9yJyk7XG5cbiAgICBsZXQgc2xpZGVzaG93ID0gbmV3IEZsaWNraXR5KHNsaWRlc2hvd0VsZW0sIHtcbiAgICAgIHByZXZOZXh0QnV0dG9uczogZmFsc2UsXG4gICAgICBwYWdlRG90czogZmFsc2UsXG4gICAgICB3cmFwQXJvdW5kOiB0cnVlLFxuICAgICAgYXV0b1BsYXk6IDMwMDBcbiAgICB9KTtcblxuICAgIHNsaWRlc2hvdy5vbignZHJhZ1N0YXJ0LmZsaWNraXR5JywgZnVuY3Rpb24gKGV2ZW50LCBwb2ludGVyKSB7XG4gICAgICBkb2N1bWVudC5vbnRvdWNobW92ZSA9IGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHNsaWRlc2hvdy5vbignZHJhZ0VuZC5mbGlja2l0eScsIGZ1bmN0aW9uIChldmVudCwgcG9pbnRlcikge1xuICAgICAgZG9jdW1lbnQub250b3VjaG1vdmUgPSBmdW5jdGlvbiAoZSkge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHNsaWRlc2hvdy5vbignc2VsZWN0JywgZnVuY3Rpb24gKCkge1xuICAgICAgbGV0IHNlbGVjdGVkQnV0dG9uID0gaW5kaWNhdG9yc1tzbGlkZXNob3cuc2VsZWN0ZWRJbmRleF07XG4gICAgICBsZXQgcHJldmlvdXNTZWxlY3RlZEJ1dHRvbiA9IGVsZW0ucXVlcnlTZWxlY3RvcignLnNsaWRlc2hvdy1pbmRpY2F0b3JzX19pdGVtLS1hY3RpdmUnKTtcblxuICAgICAgcHJldmlvdXNTZWxlY3RlZEJ1dHRvbi5jbGFzc0xpc3QucmVtb3ZlKCdzbGlkZXNob3ctaW5kaWNhdG9yc19faXRlbS0tYWN0aXZlJyk7XG4gICAgICBzZWxlY3RlZEJ1dHRvbi5jbGFzc0xpc3QuYWRkKCdzbGlkZXNob3ctaW5kaWNhdG9yc19faXRlbS0tYWN0aXZlJyk7XG4gICAgfSk7XG5cbiAgICBuZXh0QnV0dG9uLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZnVuY3Rpb24gKCkge1xuICAgICAgc2xpZGVzaG93Lm5leHQoKTtcbiAgICB9KTtcblxuICAgIHByZXZCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbiAoKSB7XG4gICAgICBzbGlkZXNob3cucHJldmlvdXMoKTtcbiAgICB9KTtcblxuICAgIFtdLmZvckVhY2guY2FsbChpbmRpY2F0b3JzLCBmdW5jdGlvbiAoaW5kaWNhdG9yKSB7XG4gICAgICBpbmRpY2F0b3IuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbihlKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgbGV0IGluZGV4ID0gaW5kaWNhdG9yLmRhdGFzZXQuaWQ7XG4gICAgICAgIHNsaWRlc2hvdy5zZWxlY3QoaW5kZXgpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH0pO1xufVxuXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgRE9NQ29udGVudExvYWRlZCBFdmVudExpc3RlbmVyXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZnVuY3Rpb24gKCkge1xuICBDYXJvdXNlbCgpO1xufSk7XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgQ292ZXJcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IENvdmVyID0gZnVuY3Rpb24gKCkge1xuICBsZXQgdmggPSB3aW5kb3cuaW5uZXJIZWlnaHQgKiAwLjAxO1xuICBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tdmgnLCBgJHt2aH1weGApO1xufVxuXG5jb25zdCBQYXJhbGxheENvdmVyID0gZnVuY3Rpb24gKCkge1xuICBsZXQgY292ZXIgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuY292ZXJfX2NvbnRlbnQnKTtcblxuICBpZiAoY292ZXIpIHtcbiAgICBsZXQgc2Nyb2xsVG9wID0gMDtcbiAgICBsZXQgdHdlZW5lZCA9IDA7XG4gICAgc2Nyb2xsVG9wID0gTWF0aC5tYXgoMCwgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNjcm9sbFRvcCB8fCB3aW5kb3cucGFnZVlPZmZzZXQgfHwgMCk7XG5cbiAgICBpZiAoTWF0aC5hYnMoc2Nyb2xsVG9wIC0gdHdlZW5lZCkgPiAwKSB7XG4gICAgICAvLyB5b3UgY2FuIGNoYW5nZSB0aGUgbnVtYmVyIGZvciB0aGUgYWNjZWxlcmF0aW9uIG9mIHNjcm9sbFxuICAgICAgbGV0IHRvcCA9IHR3ZWVuZWQgKz0gMC4xICogKHNjcm9sbFRvcCAtIHR3ZWVuZWQpOyAvLyB1cGRhdGUgdmFsdWUgb2YgWSB0cmFuc2xhdGlvblxuXG4gICAgICBjb3Zlci5zdHlsZS50cmFuc2Zvcm0gPSBgdHJhbnNsYXRlWSgkeyh0b3AgKiAtMSl9cHgpYDtcbiAgICB9IGVsc2Uge1xuICAgICAgY292ZXIuc3R5bGUudHJhbnNmb3JtID0gYHRyYW5zbGF0ZVkoMHB4KWA7XG4gICAgfVxuICB9XG59XG5cbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi8qICBET01Db250ZW50TG9hZGVkIEV2ZW50TGlzdGVuZXJcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmdW5jdGlvbiAoKSB7XG4gIFBhcmFsbGF4Q292ZXIoKTtcbiAgQ292ZXIoKTtcbn0pO1xuXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigncmVzaXplJywgZnVuY3Rpb24gKCkge1xuICBQYXJhbGxheENvdmVyKCk7XG4gIENvdmVyKCk7XG59KTtcblxud2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIGZ1bmN0aW9uICgpIHtcbiAgUGFyYWxsYXhDb3ZlcigpO1xufSk7XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgRmlsdGVyXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5jb25zdCBnZXRQb3N0cyA9ICgpID0+IHtcbiAgY29uc3QgcGFnaW5hdGlvbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5qcy1wYWdpbmF0aW9uJyk7XG4gIGNvbnN0IHBvc3RBcmVhID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmpzLXBvc3RzJyk7XG4gIGNvbnN0IHBvc3RzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignI2pzLXBvc3RzLWNvbnRhaW5lcicpO1xuICBjb25zdCBwb3N0VHlwZSA9IHBvc3RzLnF1ZXJ5U2VsZWN0b3IoJyNqcy1wb3N0LXR5cGUnKS52YWx1ZTtcbiAgY29uc3QgZmlsdGVycyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5qcy1maWx0ZXInKTtcbiAgbGV0IHRheG9ub21pZXMgPSB7fTtcbiAgbGV0IG1ldGEgPSB7fTtcbiAgbGV0IGZvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7XG4gIGxldCB1cmxQYXRoID0ganNEYXRhLmN1cnJlbnRVcmw7XG5cbiAgZm9ybURhdGEuYXBwZW5kKCdhY3Rpb24nLCAnZmlsdGVyX3Bvc3RzJyk7XG4gIGZvcm1EYXRhLmFwcGVuZCgncG9zdF90eXBlJywgcG9zdFR5cGUpO1xuICBmb3JtRGF0YS5hcHBlbmQoJ3F1ZXJ5JywganNEYXRhLnBvc3RzKTsgLy8gdGhpcyBpcyBob3cgd2UgZ2V0IHBhcmFtcyBmcm9tIHdwX2xvY2FsaXplX3NjcmlwdCgpIGZ1bmN0aW9uXG4gIGZvcm1EYXRhLmFwcGVuZCgncGFnZScsIGpzRGF0YS5jdXJyZW50UGFnZSk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKGZpbHRlcnMsIChmaWx0ZXIpID0+IHtcbiAgICBpZiAoZmlsdGVyLmRhdGFzZXQudHlwZSA9PT0gJ3RheG9ub215Jykge1xuICAgICAgdGF4b25vbWllc1tmaWx0ZXIuZGF0YXNldC50YXhvbm9teV0gPSB7fTtcbiAgICAgIHRheG9ub21pZXNbZmlsdGVyLmRhdGFzZXQudGF4b25vbXldWyd0eXBlJ10gPSBmaWx0ZXIuZGF0YXNldC50YXhvbm9teTtcbiAgICAgIHRheG9ub21pZXNbZmlsdGVyLmRhdGFzZXQudGF4b25vbXldWyd2YWx1ZSddID0gZmlsdGVyLnZhbHVlO1xuICAgICAgdGF4b25vbWllc1tmaWx0ZXIuZGF0YXNldC50YXhvbm9teV1bJ3NsdWcnXSA9IGZpbHRlci5kYXRhc2V0LnNsdWc7XG4gICAgfVxuXG4gICAgaWYgKGZpbHRlci5kYXRhc2V0LnR5cGUgPT09ICdtZXRhJykge1xuICAgICAgbWV0YVtmaWx0ZXIuZGF0YXNldC5tZXRhXSA9IHt9O1xuICAgICAgbWV0YVtmaWx0ZXIuZGF0YXNldC5tZXRhXVsndHlwZSddID0gZmlsdGVyLmRhdGFzZXQubWV0YTtcbiAgICAgIG1ldGFbZmlsdGVyLmRhdGFzZXQubWV0YV1bJ3ZhbHVlJ10gPSBmaWx0ZXIudmFsdWU7XG5cbiAgICAgIC8vIEFwcGVuZCBkYXRhIHRvIHRoZSBmb3JtXG4gICAgICBmb3JtRGF0YS5hcHBlbmQoJ21ldGEnLCBKU09OLnN0cmluZ2lmeShtZXRhKSk7XG4gICAgfVxuXG4gICAgaWYgKGZpbHRlci5kYXRhc2V0LnR5cGUgPT09ICdzZWFyY2gnKSB7XG4gICAgICBjb25zdCBzZWFyY2ggPSBmaWx0ZXIudmFsdWU7XG5cbiAgICAgIC8vIEFwcGVuZCBkYXRhIHRvIHRoZSBmb3JtXG4gICAgICBmb3JtRGF0YS5hcHBlbmQoJ3NlYXJjaCcsIHNlYXJjaCk7XG4gICAgfVxuICB9KTtcblxuICBpZiAoT2JqZWN0LmtleXModGF4b25vbWllcykubGVuZ3RoICE9PSAwICYmIHRheG9ub21pZXMuY29uc3RydWN0b3IgPT09IE9iamVjdCkge1xuICAgIGxldCB1cmxPcGVyYXRvciA9ICcnO1xuXG4gICAgZm9ybURhdGEuYXBwZW5kKCd0YXhvbm9taWVzJywgSlNPTi5zdHJpbmdpZnkodGF4b25vbWllcykpO1xuXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXModGF4b25vbWllcykpIHtcbiAgICAgIGlmICh1cmxQYXRoLmluY2x1ZGVzKCc/JykpIHtcbiAgICAgICAgdXJsT3BlcmF0b3IgPSAnJic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB1cmxPcGVyYXRvciA9ICc/JztcbiAgICAgIH1cblxuICAgICAgaWYgKHZhbHVlLnZhbHVlICE9PSAnJykge1xuICAgICAgICB1cmxQYXRoID0gdXJsUGF0aCArIHVybE9wZXJhdG9yICsgdmFsdWUuc2x1ZyArICc9JyArIHZhbHVlLnZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGxldCBmb3JtRGF0YVN0cmluZyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoZm9ybURhdGEpLnRvU3RyaW5nKCk7XG5cbiAgZmV0Y2goanNEYXRhLmFqYXhVcmwsIHtcbiAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICBjcmVkZW50aWFsczogJ3NhbWUtb3JpZ2luJyxcbiAgICBoZWFkZXJzOiBuZXcgSGVhZGVycyh7XG4gICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcsXG4gICAgfSksXG4gICAgYm9keTogZm9ybURhdGFTdHJpbmcsXG4gIH0pXG4gICAgLnRoZW4ocmVzcCA9PiByZXNwLmpzb24oKSlcbiAgICAudGhlbigoZGF0YSkgPT4ge1xuICAgICAgcG9zdEFyZWEuaW5uZXJIVE1MID0gZGF0YS5wb3N0cztcblxuICAgICAgLy8gVXBkYXRlIHRoZSBwYWdpbmF0aW9uIGlmIHRoZXJlIGlzIGFueVxuICAgICAgaWYgKHBhZ2luYXRpb24pIHtcbiAgICAgICAgcGFnaW5hdGlvbi5pbm5lckhUTUwgPSBkYXRhLnBhZ2luYXRpb247XG5cbiAgICAgICAgaWYgKGRhdGEucGFnZWQgIT09IDEpIHtcbiAgICAgICAgICBsZXQgdXJsT3BlcmF0b3IgPSAnJztcblxuICAgICAgICAgIGlmICh1cmxQYXRoLmNvbnRhaW5zKCc/JykpIHtcbiAgICAgICAgICAgIHVybE9wZXJhdG9yID0gJyYnO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB1cmxPcGVyYXRvciA9ICc/JztcbiAgICAgICAgICB9XG5cbiAgICAgICAgICB1cmxQYXRoID0gdXJsUGF0aCArIHVybE9wZXJhdG9yICsgJ3BhZ2U9JyArIGRhdGEucGFnZWQ7XG4gICAgICAgIH1cblxuICAgICAgICB3aW5kb3cuaGlzdG9yeS5wdXNoU3RhdGUobnVsbCwgbnVsbCwgdXJsUGF0aCk7XG5cbiAgICAgICAgcG9zdHMuc2Nyb2xsSW50b1ZpZXcoKTtcbiAgICAgICAgUGFnaW5hdGlvbigpO1xuICAgICAgfVxuICAgIH0pXG4gICAgLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgY29uc29sZS5sb2coZXJyb3IpO1xuICAgIH0pO1xufTtcblxuY29uc3QgRmlsdGVyID0gKCkgPT4ge1xuICBjb25zdCBmaWx0ZXJzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmpzLWZpbHRlcicpO1xuICBjb25zdCBzZWFyY2hGaWVsZHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtc2VhcmNoJyk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKGZpbHRlcnMsIChmaWx0ZXIpID0+IHtcbiAgICBmaWx0ZXIuYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKGUpID0+IHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcblxuICAgICAganNEYXRhLmN1cnJlbnRQYWdlID0gMTtcblxuICAgICAgZ2V0UG9zdHMoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKHNlYXJjaEZpZWxkcywgKHNlYXJjaEZpZWxkKSA9PiB7XG4gICAgc2VhcmNoRmllbGQuYWRkRXZlbnRMaXN0ZW5lcignaW5wdXQnLCAoKSA9PiB7XG4gICAgICBqc0RhdGEuY3VycmVudFBhZ2UgPSAxO1xuXG4gICAgICBnZXRQb3N0cygpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbmNvbnN0IFBhZ2luYXRpb24gPSAoKSA9PiB7XG4gIGNvbnN0IHBhZ2luYXRpb25MaW5rcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2EuanMtcGFnaW5hdGlvbi1saW5rJyk7XG4gIGNvbnN0IHBhZ2luYXRpb25QcmV2ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmpzLXBhZ2luYXRpb24tcHJldi1saW5rJyk7XG4gIGNvbnN0IHBhZ2luYXRpb25OZXh0ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmpzLXBhZ2luYXRpb24tbmV4dC1saW5rJyk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKHBhZ2luYXRpb25MaW5rcywgKHBhZ2luYXRpb25MaW5rKSA9PiB7XG4gICAgcGFnaW5hdGlvbkxpbmsuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoZSkgPT4ge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuXG4gICAgICAvLyBVcGRhdGUgdGhlIGN1cnJlbnQgcGFnZVxuICAgICAgaWYgKHBhZ2luYXRpb25MaW5rLmlubmVySFRNTCAhPT0ganNEYXRhLmN1cnJlbnRQYWdlKSB7XG4gICAgICAgIGpzRGF0YS5jdXJyZW50UGFnZSA9IHBhZ2luYXRpb25MaW5rLmlubmVySFRNTDtcbiAgICAgIH1cblxuICAgICAgZ2V0UG9zdHMoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgaWYgKHBhZ2luYXRpb25OZXh0KSB7XG4gICAgcGFnaW5hdGlvbk5leHQuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoZSkgPT4ge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuXG4gICAgICBqc0RhdGEuY3VycmVudFBhZ2UrKztcblxuICAgICAgZ2V0UG9zdHMoKTtcbiAgICB9KTtcbiAgfVxuXG4gIGlmIChwYWdpbmF0aW9uUHJldikge1xuICAgIHBhZ2luYXRpb25QcmV2LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKTtcblxuICAgICAganNEYXRhLmN1cnJlbnRQYWdlLS07XG5cbiAgICAgIGdldFBvc3RzKCk7XG4gICAgfSk7XG4gIH1cbn07XG5cbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi8qICBET01Db250ZW50TG9hZGVkIEV2ZW50TGlzdGVuZXJcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG4gIEZpbHRlcigpO1xuICBQYWdpbmF0aW9uKCk7XG59KTtcbiIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi8qIFRvZ2dsZSBtZW51XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5jb25zdCB0b2dnbGVNYWluTWVudSA9IGZ1bmN0aW9uICgpIHtcbiAgY29uc3QgYm9keSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdCT0RZJylbMF07XG4gIGNvbnN0IHRvZ2dsZU1lbnVCdG5zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmpzLXRvZ2dsZS1tZW51Jyk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKHRvZ2dsZU1lbnVCdG5zLCBmdW5jdGlvbiAodG9nZ2xlTWVudUJ0bikge1xuICAgIHRvZ2dsZU1lbnVCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbiAoZSkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgaWYgKGJvZHkuY2xhc3NMaXN0LmNvbnRhaW5zKCdzZWFyY2gtb3BlbicpKSB7XG4gICAgICAgIGJvZHkuY2xhc3NMaXN0LnRvZ2dsZSgnc2VhcmNoLW9wZW4nKTtcbiAgICAgIH1cbiAgICAgIGJvZHkuY2xhc3NMaXN0LnRvZ2dsZSgnbWVudS1vcGVuJyk7XG4gICAgfSk7XG4gIH0pO1xufTtcblxuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuLyogU3ViIG1lbnVcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IHN1Yk1lbnUgPSBmdW5jdGlvbiAoKSB7XG4gIGNvbnN0IHN1Yk1lbnVJdGVtcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5tZW51LWl0ZW0taGFzLWNoaWxkcmVuJyk7XG5cbiAgW10uZm9yRWFjaC5jYWxsKHN1Yk1lbnVJdGVtcywgZnVuY3Rpb24gKHN1Yk1lbnVJdGVtKSB7XG4gICAgY29uc3Qgc3ViTWVudUNoZXZyb24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG4gICAgY29uc3Qgc3ViTWVudUxpbmsgPSBzdWJNZW51SXRlbS5xdWVyeVNlbGVjdG9yKCdhJyk7XG5cbiAgICBzdWJNZW51Q2hldnJvbi5jbGFzc05hbWUgPSAnY2hldnJvbic7XG4gICAgc3ViTWVudUl0ZW0uYXBwZW5kQ2hpbGQoc3ViTWVudUNoZXZyb24pO1xuXG4gICAgc3ViTWVudUNoZXZyb24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCBmdW5jdGlvbiAoZSkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgc3ViTWVudUl0ZW0uY2xhc3NMaXN0LnRvZ2dsZSgnc3ViLW1lbnUtb3BlbicpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi8qICBET01Db250ZW50TG9hZGVkIEV2ZW50TGlzdGVuZXJcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmdW5jdGlvbiAoKSB7XG4gIHRvZ2dsZU1haW5NZW51KCk7XG4gIHN1Yk1lbnUoKTtcbn0pO1xuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuLyogUmVhY2hcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmNvbnN0IHJlYWNoID0gZnVuY3Rpb24gKCkge1xuICBjb25zdCBib2R5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ0JPRFknKVswXTtcbiAgY29uc3QgZWxlbXMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtcmVhY2gnKTtcblxuICBbXS5mb3JFYWNoLmNhbGwoZWxlbXMsIGZ1bmN0aW9uIChlbGVtKSB7XG4gICAgbGV0IHJlYWNoVG9nZ2xlID0gZWxlbS5xdWVyeVNlbGVjdG9yKCcuanMtcmVhY2gtdG9nZ2xlJyk7XG5cbiAgICByZWFjaFRvZ2dsZS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uIChlKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBlbGVtLmNsYXNzTGlzdC50b2dnbGUoJ3JlYWNoLS1vcGVuJyk7XG4gICAgICBib2R5LmNsYXNzTGlzdC50b2dnbGUoJ3JlYWNoLW9wZW4nKTtcbiAgICB9KTtcbiAgfSk7XG59O1xuXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgRE9NQ29udGVudExvYWRlZCBFdmVudExpc3RlbmVyXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgZnVuY3Rpb24gKCkge1xuICByZWFjaCgpO1xufSk7XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiBUb2dnbGUgc2VhcmNob3ZlcmxheVxuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuY29uc3QgdG9nZ2xlU2VhcmNoTWVudSA9IGZ1bmN0aW9uICgpIHtcbiAgY29uc3QgYm9keSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdCT0RZJylbMF07XG4gIGNvbnN0IHRvZ2dsZVNlYXJjaEJ0bnMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtdG9nZ2xlLXNlYXJjaCcpO1xuICBjb25zdCBzZWFyY2hJbnB1dCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5zZWFyY2gtaW5wdXQnKTtcblxuICBbXS5mb3JFYWNoLmNhbGwodG9nZ2xlU2VhcmNoQnRucywgZnVuY3Rpb24gKHRvZ2dsZVNlYXJjaEJ0bikge1xuICAgIHRvZ2dsZVNlYXJjaEJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGZ1bmN0aW9uIChlKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICBpZiAoYm9keS5jbGFzc0xpc3QuY29udGFpbnMoJ21lbnUtb3BlbicpKSB7XG4gICAgICAgIGJvZHkuY2xhc3NMaXN0LnRvZ2dsZSgnbWVudS1vcGVuJyk7XG4gICAgICB9XG4gICAgICBib2R5LmNsYXNzTGlzdC50b2dnbGUoJ3NlYXJjaC1vcGVuJyk7XG4gICAgICBzZWFyY2hJbnB1dC5mb2N1cygpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbi8qICBET01Db250ZW50TG9hZGVkIEV2ZW50TGlzdGVuZXJcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmdW5jdGlvbiAoKSB7XG4gIHRvZ2dsZVNlYXJjaE1lbnUoKTtcbn0pO1xuIiwiLyoqXG4gKiBEZXRlY3RzIGlmIHRoZSB2aXNpdG9yIGlzIHVzaW5nIGEgTVNJRSBicm93c2VyXG4gKlxuICogQHJldHVybnMge3RydWV8ZmFsc2V9IFRydWUgaWYgTVNJRSwgZmFsc2UgaWYgbm90XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc01TSUUoKSB7XG5cdHJldHVybiAvKiBAY2Nfb24hQCAqLyBmYWxzZSB8fCAhIWRvY3VtZW50LmRvY3VtZW50TW9kZTtcbn1cbiIsImltcG9ydCAnLi92ZW5kb3IvbGF6eXNpemVzLmpzJztcbmltcG9ydCAnLi92ZW5kb3IvcGx5ci5qcyc7XG5cbmltcG9ydCAnLi9jb21wb25lbnRzL2NvdmVyLmpzJztcbmltcG9ydCAnLi9jb21wb25lbnRzL25hdmlnYXRpb24uanMnO1xuaW1wb3J0ICcuL2NvbXBvbmVudHMvcmVhY2guanMnO1xuaW1wb3J0ICcuL2NvbXBvbmVudHMvY2Fyb3VzZWwuanMnO1xuaW1wb3J0ICcuL2NvbXBvbmVudHMvZmlsdGVyLmpzJztcbmltcG9ydCAnLi9jb21wb25lbnRzL3NlYXJjaC5qcyc7XG4iLCJpbXBvcnQgJ2xhenlzaXplcyc7XG5cbmlmICgnbG9hZGluZycgaW4gSFRNTEltYWdlRWxlbWVudC5wcm90b3R5cGUpIHtcbiAgY29uc3QgaW1hZ2VzID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnaW1nLmxhenlsb2FkJyk7XG5cbiAgaW1hZ2VzLmZvckVhY2goaW1nID0+IHtcbiAgICBpZiAodHlwZW9mIGltZy5kYXRhc2V0LnNyYyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGltZy5zcmMgPSBpbWcuZGF0YXNldC5zcmM7XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBpbWcuZGF0YXNldC5zcmNzZXQgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBpbWcuc3Jjc2V0ID0gaW1nLmRhdGFzZXQuc3Jjc2V0O1xuICAgIH1cbiAgfSk7XG59IGVsc2Uge1xuICAvLyBJbml0aWF0ZSBMYXp5U2l6ZXMgKHJlYWRzIGRhdGEtc3JjICYgY2xhc3M9bGF6eWxvYWQpXG4gIGxhenlTaXplcy5pbml0KCk7IC8vIGxhenlTaXplcyB3b3JrcyBvZmYgYSBnbG9iYWwuXG59XG4iLCJpbXBvcnQgUGx5ciBmcm9tIFwicGx5clwiO1xuaW1wb3J0IEhscyBmcm9tIFwiaGxzLmpzXCI7XG5pbXBvcnQgeyBpc01TSUUgfSBmcm9tIFwiLi4vaGVscGVycy9mdW5jdGlvbnMuanNcIjtcblxuLy8gbWlzc2luZyBmb3JFYWNoIG9uIE5vZGVMaXN0IGZvciBJRTExXG5pZiAod2luZG93Lk5vZGVMaXN0ICYmICFOb2RlTGlzdC5wcm90b3R5cGUuZm9yRWFjaCkge1xuICBOb2RlTGlzdC5wcm90b3R5cGUuZm9yRWFjaCA9IEFycmF5LnByb3RvdHlwZS5mb3JFYWNoO1xufVxuXG5jb25zdCB2aWRlb1BsYXllckhvbGRlcnMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCcuanMtdmlkZW8tcGxheWVyLWhvbGRlcicpO1xuXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG4vKiAgVmlkZW8gUGxheWVycyBCYWNrZ3JvdW5kXG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5pZiAodmlkZW9QbGF5ZXJIb2xkZXJzKSB7XG5cbiAgdmlkZW9QbGF5ZXJIb2xkZXJzLmZvckVhY2godmlkZW9QbGF5ZXJIb2xkZXIgPT4ge1xuICAgIGxldCB2aWRlb0JnUGxheWVyID0gdmlkZW9QbGF5ZXJIb2xkZXIucXVlcnlTZWxlY3RvcignLmpzLWJhY2tncm91bmQtdmlkZW8nKTtcbiAgICBsZXQgdmlkZW9Tb3VyY2UgPSB2aWRlb0JnUGxheWVyLmRhdGFzZXQudmlkZW87XG4gICAgbGV0IGhsc1R5cGUgPSB2aWRlb0JnUGxheWVyLmRhdGFzZXQuaGxzO1xuXG4gICAgaWYgKGhsc1R5cGUgPT09ICd0cnVlJyAmJiBIbHMuaXNTdXBwb3J0ZWQoKSkge1xuICAgICAgbGV0IGhscyA9IG5ldyBIbHMoKTtcbiAgICAgIGhscy5sb2FkU291cmNlKHZpZGVvU291cmNlKTtcbiAgICAgIGhscy5hdHRhY2hNZWRpYSh2aWRlb0JnUGxheWVyKTtcbiAgICB9XG5cbiAgICBjb25zdCBwbHlySW5zdGFuY2UgPSBuZXcgUGx5cih2aWRlb0JnUGxheWVyLCB7XG4gICAgICBsb2FkU3ByaXRlOiBpc01TSUUoKSxcbiAgICAgIGNsaWNrVG9QbGF5OiBmYWxzZSxcbiAgICAgIHZvbHVtZTogMCxcbiAgICAgIGNvbnRyb2xzOiBmYWxzZSxcbiAgICB9KTtcblxuICAgIHBseXJJbnN0YW5jZS5vbigncmVhZHknLCAoKSA9PiB7XG4gICAgICBwbHlySW5zdGFuY2UucGxheSgpO1xuICAgIH0pO1xuXG4gICAgcGx5ckluc3RhbmNlLm9uKCdwbGF5aW5nJywgKCkgPT4ge1xuICAgICAgdmlkZW9QbGF5ZXJIb2xkZXIuY2xhc3NMaXN0LmFkZCgnY292ZXItLXZpZGVvLXJlYWR5Jyk7XG4gICAgfSk7XG5cbiAgICBwbHlySW5zdGFuY2Uub24oJ2VuZGVkJywgKCkgPT4ge1xuICAgICAgcGx5ckluc3RhbmNlLnBsYXkoKTtcbiAgICB9KTtcbiAgfSk7XG59XG4iXX0=