jiskattema/spot

View on GitHub
dist/js/npm.intro.js.chunk.bf89601530a738a8df09.js

Summary

Maintainability
A
0 mins
Test Coverage
(window.webpackJsonp=window.webpackJsonp||[]).push([["npm.intro.js"],{"0444":function(module,exports,__webpack_require__){eval("/**\n * Intro.js v2.9.3\n * https://github.com/usablica/intro.js\n *\n * Copyright (C) 2017 Afshin Mehrabani (@afshinmeh)\n */\n\n(function(f) {\n    if (true) {\n        module.exports = f();\n        // deprecated function\n        // @since 2.8.0\n        module.exports.introJs = function () {\n          console.warn('Deprecated: please use require(\"intro.js\") directly, instead of the introJs method of the function');\n          // introJs()\n          return f().apply(this, arguments);\n        };\n    } else { var g; }\n})(function () {\n  //Default config/variables\n  var VERSION = '2.9.3';\n\n  /**\n   * IntroJs main class\n   *\n   * @class IntroJs\n   */\n  function IntroJs(obj) {\n    this._targetElement = obj;\n    this._introItems = [];\n\n    this._options = {\n      /* Next button label in tooltip box */\n      nextLabel: 'Next &rarr;',\n      /* Previous button label in tooltip box */\n      prevLabel: '&larr; Back',\n      /* Skip button label in tooltip box */\n      skipLabel: 'Skip',\n      /* Done button label in tooltip box */\n      doneLabel: 'Done',\n      /* Hide previous button in the first step? Otherwise, it will be disabled button. */\n      hidePrev: false,\n      /* Hide next button in the last step? Otherwise, it will be disabled button. */\n      hideNext: false,\n      /* Default tooltip box position */\n      tooltipPosition: 'bottom',\n      /* Next CSS class for tooltip boxes */\n      tooltipClass: '',\n      /* CSS class that is added to the helperLayer */\n      highlightClass: '',\n      /* Close introduction when pressing Escape button? */\n      exitOnEsc: true,\n      /* Close introduction when clicking on overlay layer? */\n      exitOnOverlayClick: true,\n      /* Show step numbers in introduction? */\n      showStepNumbers: true,\n      /* Let user use keyboard to navigate the tour? */\n      keyboardNavigation: true,\n      /* Show tour control buttons? */\n      showButtons: true,\n      /* Show tour bullets? */\n      showBullets: true,\n      /* Show tour progress? */\n      showProgress: false,\n      /* Scroll to highlighted element? */\n      scrollToElement: true,\n      /*\n       * Should we scroll the tooltip or target element?\n       *\n       * Options are: 'element' or 'tooltip'\n       */\n      scrollTo: 'element',\n      /* Padding to add after scrolling when element is not in the viewport (in pixels) */\n      scrollPadding: 30,\n      /* Set the overlay opacity */\n      overlayOpacity: 0.8,\n      /* Precedence of positions, when auto is enabled */\n      positionPrecedence: [\"bottom\", \"top\", \"right\", \"left\"],\n      /* Disable an interaction with element? */\n      disableInteraction: false,\n      /* Set how much padding to be used around helper element */\n      helperElementPadding: 10,\n      /* Default hint position */\n      hintPosition: 'top-middle',\n      /* Hint button label */\n      hintButtonLabel: 'Got it',\n      /* Adding animation to hints? */\n      hintAnimation: true,\n      /* additional classes to put on the buttons */\n      buttonClass: \"introjs-button\"\n    };\n  }\n\n  /**\n   * Initiate a new introduction/guide from an element in the page\n   *\n   * @api private\n   * @method _introForElement\n   * @param {Object} targetElm\n   * @param {String} group\n   * @returns {Boolean} Success or not?\n   */\n  function _introForElement(targetElm, group) {\n    var allIntroSteps = targetElm.querySelectorAll(\"*[data-intro]\"),\n        introItems = [];\n\n    if (this._options.steps) {\n      //use steps passed programmatically\n      _forEach(this._options.steps, function (step) {\n        var currentItem = _cloneObject(step);\n\n        //set the step\n        currentItem.step = introItems.length + 1;\n\n        //use querySelector function only when developer used CSS selector\n        if (typeof (currentItem.element) === 'string') {\n          //grab the element with given selector from the page\n          currentItem.element = document.querySelector(currentItem.element);\n        }\n\n        //intro without element\n        if (typeof (currentItem.element) === 'undefined' || currentItem.element === null) {\n          var floatingElementQuery = document.querySelector(\".introjsFloatingElement\");\n\n          if (floatingElementQuery === null) {\n            floatingElementQuery = document.createElement('div');\n            floatingElementQuery.className = 'introjsFloatingElement';\n\n            document.body.appendChild(floatingElementQuery);\n          }\n\n          currentItem.element  = floatingElementQuery;\n          currentItem.position = 'floating';\n        }\n\n        currentItem.scrollTo = currentItem.scrollTo || this._options.scrollTo;\n\n        if (typeof (currentItem.disableInteraction) === 'undefined') {\n          currentItem.disableInteraction = this._options.disableInteraction;\n        }\n\n        if (currentItem.element !== null) {\n          introItems.push(currentItem);\n        }        \n      }.bind(this));\n\n    } else {\n      //use steps from data-* annotations\n      var elmsLength = allIntroSteps.length;\n      var disableInteraction;\n      \n      //if there's no element to intro\n      if (elmsLength < 1) {\n        return false;\n      }\n\n      _forEach(allIntroSteps, function (currentElement) {\n        \n        // PR #80\n        // start intro for groups of elements\n        if (group && (currentElement.getAttribute(\"data-intro-group\") !== group)) {\n          return;\n        }\n\n        // skip hidden elements\n        if (currentElement.style.display === 'none') {\n          return;\n        }\n\n        var step = parseInt(currentElement.getAttribute('data-step'), 10);\n\n        if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') {\n          disableInteraction = !!currentElement.getAttribute('data-disable-interaction');\n        } else {\n          disableInteraction = this._options.disableInteraction;\n        }\n\n        if (step > 0) {\n          introItems[step - 1] = {\n            element: currentElement,\n            intro: currentElement.getAttribute('data-intro'),\n            step: parseInt(currentElement.getAttribute('data-step'), 10),\n            tooltipClass: currentElement.getAttribute('data-tooltipclass'),\n            highlightClass: currentElement.getAttribute('data-highlightclass'),\n            position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,\n            scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo,\n            disableInteraction: disableInteraction\n          };\n        }\n      }.bind(this));\n\n      //next add intro items without data-step\n      //todo: we need a cleanup here, two loops are redundant\n      var nextStep = 0;\n\n      _forEach(allIntroSteps, function (currentElement) {\n        \n        // PR #80\n        // start intro for groups of elements\n        if (group && (currentElement.getAttribute(\"data-intro-group\") !== group)) {\n          return;\n        }\n        \n        if (currentElement.getAttribute('data-step') === null) {\n\n          while (true) {\n            if (typeof introItems[nextStep] === 'undefined') {\n              break;\n            } else {\n              nextStep++;\n            }\n          } \n\n          if (typeof (currentElement.getAttribute('data-disable-interaction')) !== 'undefined') {\n            disableInteraction = !!currentElement.getAttribute('data-disable-interaction');\n          } else {\n            disableInteraction = this._options.disableInteraction;\n          }\n\n          introItems[nextStep] = {\n            element: currentElement,\n            intro: currentElement.getAttribute('data-intro'),\n            step: nextStep + 1,\n            tooltipClass: currentElement.getAttribute('data-tooltipclass'),\n            highlightClass: currentElement.getAttribute('data-highlightclass'),\n            position: currentElement.getAttribute('data-position') || this._options.tooltipPosition,\n            scrollTo: currentElement.getAttribute('data-scrollto') || this._options.scrollTo,\n            disableInteraction: disableInteraction\n          };\n        }\n      }.bind(this));\n    }\n\n    //removing undefined/null elements\n    var tempIntroItems = [];\n    for (var z = 0; z < introItems.length; z++) {\n      if (introItems[z]) {\n        // copy non-falsy values to the end of the array\n        tempIntroItems.push(introItems[z]);  \n      } \n    }\n\n    introItems = tempIntroItems;\n\n    //Ok, sort all items with given steps\n    introItems.sort(function (a, b) {\n      return a.step - b.step;\n    });\n\n    //set it to the introJs object\n    this._introItems = introItems;\n\n    //add overlay layer to the page\n    if(_addOverlayLayer.call(this, targetElm)) {\n      //then, start the show\n      _nextStep.call(this);\n\n      if (this._options.keyboardNavigation) {\n        DOMEvent.on(window, 'keydown', _onKeyDown, this, true);\n      }\n      //for window resize\n      DOMEvent.on(window, 'resize', _onResize, this, true);\n    }\n    return false;\n  }\n\n  function _onResize () {\n    this.refresh.call(this);\n  }\n\n  /**\n  * on keyCode:\n  * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode\n  * This feature has been removed from the Web standards.\n  * Though some browsers may still support it, it is in\n  * the process of being dropped.\n  * Instead, you should use KeyboardEvent.code,\n  * if it's implemented.\n  *\n  * jQuery's approach is to test for\n  *   (1) e.which, then\n  *   (2) e.charCode, then\n  *   (3) e.keyCode\n  * https://github.com/jquery/jquery/blob/a6b0705294d336ae2f63f7276de0da1195495363/src/event.js#L638\n  *\n  * @param type var\n  * @return type\n  */\n  function _onKeyDown (e) {\n    var code = (e.code === null) ? e.which : e.code;\n\n    // if code/e.which is null\n    if (code === null) {\n      code = (e.charCode === null) ? e.keyCode : e.charCode;\n    }\n    \n    if ((code === 'Escape' || code === 27) && this._options.exitOnEsc === true) {\n      //escape key pressed, exit the intro\n      //check if exit callback is defined\n      _exitIntro.call(this, this._targetElement);\n    } else if (code === 'ArrowLeft' || code === 37) {\n      //left arrow\n      _previousStep.call(this);\n    } else if (code === 'ArrowRight' || code === 39) {\n      //right arrow\n      _nextStep.call(this);\n    } else if (code === 'Enter' || code === 13) {\n      //srcElement === ie\n      var target = e.target || e.srcElement;\n      if (target && target.className.match('introjs-prevbutton')) {\n        //user hit enter while focusing on previous button\n        _previousStep.call(this);\n      } else if (target && target.className.match('introjs-skipbutton')) {\n        //user hit enter while focusing on skip button\n        if (this._introItems.length - 1 === this._currentStep && typeof (this._introCompleteCallback) === 'function') {\n            this._introCompleteCallback.call(this);\n        }\n\n        _exitIntro.call(this, this._targetElement);\n      } else if (target && target.getAttribute('data-stepnumber')) {\n        // user hit enter while focusing on step bullet\n        target.click();\n      } else {\n        //default behavior for responding to enter\n        _nextStep.call(this);\n      }\n\n      //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers\n      if(e.preventDefault) {\n        e.preventDefault();\n      } else {\n        e.returnValue = false;\n      }\n    }\n  }\n\n /*\n   * makes a copy of the object\n   * @api private\n   * @method _cloneObject\n  */\n  function _cloneObject(object) {\n      if (object === null || typeof (object) !== 'object' || typeof (object.nodeType) !== 'undefined') {\n        return object;\n      }\n      var temp = {};\n      for (var key in object) {\n        if (typeof(window.jQuery) !== 'undefined' && object[key] instanceof window.jQuery) {\n          temp[key] = object[key];\n        } else {\n          temp[key] = _cloneObject(object[key]);\n        }\n      }\n      return temp;\n  }\n  /**\n   * Go to specific step of introduction\n   *\n   * @api private\n   * @method _goToStep\n   */\n  function _goToStep(step) {\n    //because steps starts with zero\n    this._currentStep = step - 2;\n    if (typeof (this._introItems) !== 'undefined') {\n      _nextStep.call(this);\n    }\n  }\n\n  /**\n   * Go to the specific step of introduction with the explicit [data-step] number\n   *\n   * @api private\n   * @method _goToStepNumber\n   */\n  function _goToStepNumber(step) {\n    this._currentStepNumber = step;\n    if (typeof (this._introItems) !== 'undefined') {\n      _nextStep.call(this);\n    }\n  }\n\n  /**\n   * Go to next step on intro\n   *\n   * @api private\n   * @method _nextStep\n   */\n  function _nextStep() {\n    this._direction = 'forward';\n\n    if (typeof (this._currentStepNumber) !== 'undefined') {\n      _forEach(this._introItems, function (item, i) {\n        if( item.step === this._currentStepNumber ) {\n          this._currentStep = i - 1;\n          this._currentStepNumber = undefined;\n        }\n      }.bind(this));\n    }\n\n    if (typeof (this._currentStep) === 'undefined') {\n      this._currentStep = 0;\n    } else {\n      ++this._currentStep;\n    }\n\n    var nextStep = this._introItems[this._currentStep];\n    var continueStep = true;\n\n    if (typeof (this._introBeforeChangeCallback) !== 'undefined') {\n      continueStep = this._introBeforeChangeCallback.call(this, nextStep.element);\n    }\n\n    // if `onbeforechange` returned `false`, stop displaying the element\n    if (continueStep === false) {\n      --this._currentStep;\n      return false;\n    }\n\n    if ((this._introItems.length) <= this._currentStep) {\n      //end of the intro\n      //check if any callback is defined\n      if (typeof (this._introCompleteCallback) === 'function') {\n        this._introCompleteCallback.call(this);\n      }\n      _exitIntro.call(this, this._targetElement);\n      return;\n    }\n\n    _showElement.call(this, nextStep);\n  }\n\n  /**\n   * Go to previous step on intro\n   *\n   * @api private\n   * @method _previousStep\n   */\n  function _previousStep() {\n    this._direction = 'backward';\n\n    if (this._currentStep === 0) {\n      return false;\n    }\n\n    --this._currentStep;\n\n    var nextStep = this._introItems[this._currentStep];\n    var continueStep = true;\n\n    if (typeof (this._introBeforeChangeCallback) !== 'undefined') {\n      continueStep = this._introBeforeChangeCallback.call(this, nextStep.element);\n    }\n\n    // if `onbeforechange` returned `false`, stop displaying the element\n    if (continueStep === false) {\n      ++this._currentStep;\n      return false;\n    }\n\n    _showElement.call(this, nextStep);\n  }\n\n  /**\n   * Update placement of the intro objects on the screen\n   * @api private\n   */\n  function _refresh() {\n    // re-align intros\n    _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer'));\n    _setHelperLayerPosition.call(this, document.querySelector('.introjs-tooltipReferenceLayer'));\n    _setHelperLayerPosition.call(this, document.querySelector('.introjs-disableInteraction'));\n\n    // re-align tooltip\n    if(this._currentStep !== undefined && this._currentStep !== null) {\n      var oldHelperNumberLayer = document.querySelector('.introjs-helperNumberLayer'),\n        oldArrowLayer        = document.querySelector('.introjs-arrow'),\n        oldtooltipContainer  = document.querySelector('.introjs-tooltip');\n      _placeTooltip.call(this, this._introItems[this._currentStep].element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);\n    }\n\n    //re-align hints\n    _reAlignHints.call(this);\n    return this;\n  }\n\n  /**\n   * Exit from intro\n   *\n   * @api private\n   * @method _exitIntro\n   * @param {Object} targetElement\n   * @param {Boolean} force - Setting to `true` will skip the result of beforeExit callback\n   */\n  function _exitIntro(targetElement, force) {\n    var continueExit = true;\n\n    // calling onbeforeexit callback\n    //\n    // If this callback return `false`, it would halt the process\n    if (this._introBeforeExitCallback !== undefined) {\n      continueExit = this._introBeforeExitCallback.call(this);\n    }\n\n    // skip this check if `force` parameter is `true`\n    // otherwise, if `onbeforeexit` returned `false`, don't exit the intro\n    if (!force && continueExit === false) return;\n\n    //remove overlay layers from the page\n    var overlayLayers = targetElement.querySelectorAll('.introjs-overlay');\n\n    if (overlayLayers && overlayLayers.length) {\n      _forEach(overlayLayers, function (overlayLayer) {\n        overlayLayer.style.opacity = 0;\n        window.setTimeout(function () {\n          if (this.parentNode) {\n            this.parentNode.removeChild(this);\n          }\n        }.bind(overlayLayer), 500);\n      }.bind(this));\n    }\n\n    //remove all helper layers\n    var helperLayer = targetElement.querySelector('.introjs-helperLayer');\n    if (helperLayer) {\n      helperLayer.parentNode.removeChild(helperLayer);\n    }\n\n    var referenceLayer = targetElement.querySelector('.introjs-tooltipReferenceLayer');\n    if (referenceLayer) {\n      referenceLayer.parentNode.removeChild(referenceLayer);\n    }\n\n    //remove disableInteractionLayer\n    var disableInteractionLayer = targetElement.querySelector('.introjs-disableInteraction');\n    if (disableInteractionLayer) {\n      disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);\n    }\n\n    //remove intro floating element\n    var floatingElement = document.querySelector('.introjsFloatingElement');\n    if (floatingElement) {\n      floatingElement.parentNode.removeChild(floatingElement);\n    }\n\n    _removeShowElement();\n\n    //remove `introjs-fixParent` class from the elements\n    var fixParents = document.querySelectorAll('.introjs-fixParent');\n    _forEach(fixParents, function (parent) {\n      _removeClass(parent, /introjs-fixParent/g);\n    });\n\n    //clean listeners\n    DOMEvent.off(window, 'keydown', _onKeyDown, this, true);\n    DOMEvent.off(window, 'resize', _onResize, this, true);\n\n    //check if any callback is defined\n    if (this._introExitCallback !== undefined) {\n      this._introExitCallback.call(this);\n    }\n\n    //set the step to zero\n    this._currentStep = undefined;\n  }\n\n  /**\n   * Render tooltip box in the page\n   *\n   * @api private\n   * @method _placeTooltip\n   * @param {HTMLElement} targetElement\n   * @param {HTMLElement} tooltipLayer\n   * @param {HTMLElement} arrowLayer\n   * @param {HTMLElement} helperNumberLayer\n   * @param {Boolean} hintMode\n   */\n  function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer, hintMode) {\n    var tooltipCssClass = '',\n        currentStepObj,\n        tooltipOffset,\n        targetOffset,\n        windowSize,\n        currentTooltipPosition;\n\n    hintMode = hintMode || false;\n\n    //reset the old style\n    tooltipLayer.style.top        = null;\n    tooltipLayer.style.right      = null;\n    tooltipLayer.style.bottom     = null;\n    tooltipLayer.style.left       = null;\n    tooltipLayer.style.marginLeft = null;\n    tooltipLayer.style.marginTop  = null;\n\n    arrowLayer.style.display = 'inherit';\n\n    if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) {\n      helperNumberLayer.style.top  = null;\n      helperNumberLayer.style.left = null;\n    }\n\n    //prevent error when `this._currentStep` is undefined\n    if (!this._introItems[this._currentStep]) return;\n\n    //if we have a custom css class for each step\n    currentStepObj = this._introItems[this._currentStep];\n    if (typeof (currentStepObj.tooltipClass) === 'string') {\n      tooltipCssClass = currentStepObj.tooltipClass;\n    } else {\n      tooltipCssClass = this._options.tooltipClass;\n    }\n\n    tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\\s+|\\s+$/g, '');\n    tooltipLayer.setAttribute('role', 'dialog');\n\n    currentTooltipPosition = this._introItems[this._currentStep].position;\n\n    // Floating is always valid, no point in calculating\n    if (currentTooltipPosition !== \"floating\") { \n      currentTooltipPosition = _determineAutoPosition.call(this, targetElement, tooltipLayer, currentTooltipPosition);\n    }\n\n    var tooltipLayerStyleLeft;\n    targetOffset  = _getOffset(targetElement);\n    tooltipOffset = _getOffset(tooltipLayer);\n    windowSize    = _getWinSize();\n\n    _addClass(tooltipLayer, 'introjs-' + currentTooltipPosition);\n\n    switch (currentTooltipPosition) {\n      case 'top-right-aligned':\n        arrowLayer.className      = 'introjs-arrow bottom-right';\n\n        var tooltipLayerStyleRight = 0;\n        _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);\n        tooltipLayer.style.bottom    = (targetOffset.height +  20) + 'px';\n        break;\n\n      case 'top-middle-aligned':\n        arrowLayer.className      = 'introjs-arrow bottom-middle';\n\n        var tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;\n\n        // a fix for middle aligned hints\n        if (hintMode) {\n          tooltipLayerStyleLeftRight += 5;\n        }\n\n        if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {\n          tooltipLayer.style.right = null;\n          _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);\n        }\n        tooltipLayer.style.bottom = (targetOffset.height + 20) + 'px';\n        break;\n\n      case 'top-left-aligned':\n      // top-left-aligned is the same as the default top\n      case 'top':\n        arrowLayer.className = 'introjs-arrow bottom';\n\n        tooltipLayerStyleLeft = (hintMode) ? 0 : 15;\n\n        _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);\n        tooltipLayer.style.bottom = (targetOffset.height +  20) + 'px';\n        break;\n      case 'right':\n        tooltipLayer.style.left = (targetOffset.width + 20) + 'px';\n        if (targetOffset.top + tooltipOffset.height > windowSize.height) {\n          // In this case, right would have fallen below the bottom of the screen.\n          // Modify so that the bottom of the tooltip connects with the target\n          arrowLayer.className = \"introjs-arrow left-bottom\";\n          tooltipLayer.style.top = \"-\" + (tooltipOffset.height - targetOffset.height - 20) + \"px\";\n        } else {\n          arrowLayer.className = 'introjs-arrow left';\n        }\n        break;\n      case 'left':\n        if (!hintMode && this._options.showStepNumbers === true) {\n          tooltipLayer.style.top = '15px';\n        }\n\n        if (targetOffset.top + tooltipOffset.height > windowSize.height) {\n          // In this case, left would have fallen below the bottom of the screen.\n          // Modify so that the bottom of the tooltip connects with the target\n          tooltipLayer.style.top = \"-\" + (tooltipOffset.height - targetOffset.height - 20) + \"px\";\n          arrowLayer.className = 'introjs-arrow right-bottom';\n        } else {\n          arrowLayer.className = 'introjs-arrow right';\n        }\n        tooltipLayer.style.right = (targetOffset.width + 20) + 'px';\n\n        break;\n      case 'floating':\n        arrowLayer.style.display = 'none';\n\n        //we have to adjust the top and left of layer manually for intro items without element\n        tooltipLayer.style.left   = '50%';\n        tooltipLayer.style.top    = '50%';\n        tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2)  + 'px';\n        tooltipLayer.style.marginTop  = '-' + (tooltipOffset.height / 2) + 'px';\n\n        if (typeof(helperNumberLayer) !== 'undefined' && helperNumberLayer !== null) {\n          helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px';\n          helperNumberLayer.style.top  = '-' + ((tooltipOffset.height / 2) + 18) + 'px';\n        }\n\n        break;\n      case 'bottom-right-aligned':\n        arrowLayer.className      = 'introjs-arrow top-right';\n\n        tooltipLayerStyleRight = 0;\n        _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer);\n        tooltipLayer.style.top    = (targetOffset.height +  20) + 'px';\n        break;\n\n      case 'bottom-middle-aligned':\n        arrowLayer.className      = 'introjs-arrow top-middle';\n\n        tooltipLayerStyleLeftRight = targetOffset.width / 2 - tooltipOffset.width / 2;\n\n        // a fix for middle aligned hints\n        if (hintMode) {\n          tooltipLayerStyleLeftRight += 5;\n        }\n\n        if (_checkLeft(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, tooltipLayer)) {\n          tooltipLayer.style.right = null;\n          _checkRight(targetOffset, tooltipLayerStyleLeftRight, tooltipOffset, windowSize, tooltipLayer);\n        }\n        tooltipLayer.style.top = (targetOffset.height + 20) + 'px';\n        break;\n\n      // case 'bottom-left-aligned':\n      // Bottom-left-aligned is the same as the default bottom\n      // case 'bottom':\n      // Bottom going to follow the default behavior\n      default:\n        arrowLayer.className = 'introjs-arrow top';\n\n        tooltipLayerStyleLeft = 0;\n        _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer);\n        tooltipLayer.style.top    = (targetOffset.height +  20) + 'px';\n    }\n  }\n\n  /**\n   * Set tooltip left so it doesn't go off the right side of the window\n   *\n   * @return boolean true, if tooltipLayerStyleLeft is ok.  false, otherwise.\n   */\n  function _checkRight(targetOffset, tooltipLayerStyleLeft, tooltipOffset, windowSize, tooltipLayer) {\n    if (targetOffset.left + tooltipLayerStyleLeft + tooltipOffset.width > windowSize.width) {\n      // off the right side of the window\n      tooltipLayer.style.left = (windowSize.width - tooltipOffset.width - targetOffset.left) + 'px';\n      return false;\n    }\n    tooltipLayer.style.left = tooltipLayerStyleLeft + 'px';\n    return true;\n  }\n\n  /**\n   * Set tooltip right so it doesn't go off the left side of the window\n   *\n   * @return boolean true, if tooltipLayerStyleRight is ok.  false, otherwise.\n   */\n  function _checkLeft(targetOffset, tooltipLayerStyleRight, tooltipOffset, tooltipLayer) {\n    if (targetOffset.left + targetOffset.width - tooltipLayerStyleRight - tooltipOffset.width < 0) {\n      // off the left side of the window\n      tooltipLayer.style.left = (-targetOffset.left) + 'px';\n      return false;\n    }\n    tooltipLayer.style.right = tooltipLayerStyleRight + 'px';\n    return true;\n  }\n\n  /**\n   * Determines the position of the tooltip based on the position precedence and availability\n   * of screen space.\n   *\n   * @param {Object}    targetElement\n   * @param {Object}    tooltipLayer\n   * @param {String}    desiredTooltipPosition\n   * @return {String}   calculatedPosition\n   */\n  function _determineAutoPosition(targetElement, tooltipLayer, desiredTooltipPosition) {\n\n    // Take a clone of position precedence. These will be the available\n    var possiblePositions = this._options.positionPrecedence.slice();\n\n    var windowSize = _getWinSize();\n    var tooltipHeight = _getOffset(tooltipLayer).height + 10;\n    var tooltipWidth = _getOffset(tooltipLayer).width + 20;\n    var targetElementRect = targetElement.getBoundingClientRect();\n\n    // If we check all the possible areas, and there are no valid places for the tooltip, the element\n    // must take up most of the screen real estate. Show the tooltip floating in the middle of the screen.\n    var calculatedPosition = \"floating\";\n\n    /*\n    * auto determine position \n    */\n\n    // Check for space below\n    if (targetElementRect.bottom + tooltipHeight + tooltipHeight > windowSize.height) {\n      _removeEntry(possiblePositions, \"bottom\");\n    }\n\n    // Check for space above\n    if (targetElementRect.top - tooltipHeight < 0) {\n      _removeEntry(possiblePositions, \"top\");\n    }\n\n    // Check for space to the right\n    if (targetElementRect.right + tooltipWidth > windowSize.width) {\n      _removeEntry(possiblePositions, \"right\");\n    }\n\n    // Check for space to the left\n    if (targetElementRect.left - tooltipWidth < 0) {\n      _removeEntry(possiblePositions, \"left\");\n    }\n\n    // @var {String}  ex: 'right-aligned'\n    var desiredAlignment = (function (pos) {\n      var hyphenIndex = pos.indexOf('-');\n      if (hyphenIndex !== -1) {\n        // has alignment\n        return pos.substr(hyphenIndex);\n      }\n      return '';\n    })(desiredTooltipPosition || '');\n\n    // strip alignment from position\n    if (desiredTooltipPosition) {\n      // ex: \"bottom-right-aligned\"\n      // should return 'bottom'\n      desiredTooltipPosition = desiredTooltipPosition.split('-')[0];\n    }\n\n    if (possiblePositions.length) {\n      if (desiredTooltipPosition !== \"auto\" &&\n          possiblePositions.indexOf(desiredTooltipPosition) > -1) {\n        // If the requested position is in the list, choose that\n        calculatedPosition = desiredTooltipPosition;\n      } else {\n        // Pick the first valid position, in order\n        calculatedPosition = possiblePositions[0];\n      }\n    }\n\n    // only top and bottom positions have optional alignments\n    if (['top', 'bottom'].indexOf(calculatedPosition) !== -1) {\n      calculatedPosition += _determineAutoAlignment(targetElementRect.left, tooltipWidth, windowSize, desiredAlignment);\n    }\n\n    return calculatedPosition;\n  }\n\n  /**\n  * auto-determine alignment\n  * @param {Integer}  offsetLeft\n  * @param {Integer}  tooltipWidth\n  * @param {Object}   windowSize\n  * @param {String}   desiredAlignment\n  * @return {String}  calculatedAlignment\n  */\n  function _determineAutoAlignment (offsetLeft, tooltipWidth, windowSize, desiredAlignment) {\n    var halfTooltipWidth = tooltipWidth / 2,\n      winWidth = Math.min(windowSize.width, window.screen.width),\n      possibleAlignments = ['-left-aligned', '-middle-aligned', '-right-aligned'],\n      calculatedAlignment = '';\n    \n    // valid left must be at least a tooltipWidth\n    // away from right side\n    if (winWidth - offsetLeft < tooltipWidth) {\n      _removeEntry(possibleAlignments, '-left-aligned');\n    }\n\n    // valid middle must be at least half \n    // width away from both sides\n    if (offsetLeft < halfTooltipWidth || \n      winWidth - offsetLeft < halfTooltipWidth) {\n      _removeEntry(possibleAlignments, '-middle-aligned');\n    }\n\n    // valid right must be at least a tooltipWidth\n    // width away from left side\n    if (offsetLeft < tooltipWidth) {\n      _removeEntry(possibleAlignments, '-right-aligned');\n    }\n\n    if (possibleAlignments.length) {\n      if (possibleAlignments.indexOf(desiredAlignment) !== -1) {\n        // the desired alignment is valid\n        calculatedAlignment = desiredAlignment;\n      } else {\n        // pick the first valid position, in order\n        calculatedAlignment = possibleAlignments[0];\n      }\n    } else {\n      // if screen width is too small \n      // for ANY alignment, middle is \n      // probably the best for visibility\n      calculatedAlignment = '-middle-aligned';\n    }\n\n    return calculatedAlignment;\n  }\n\n  /**\n   * Remove an entry from a string array if it's there, does nothing if it isn't there.\n   *\n   * @param {Array} stringArray\n   * @param {String} stringToRemove\n   */\n  function _removeEntry(stringArray, stringToRemove) {\n    if (stringArray.indexOf(stringToRemove) > -1) {\n      stringArray.splice(stringArray.indexOf(stringToRemove), 1);\n    }\n  }\n\n  /**\n   * Update the position of the helper layer on the screen\n   *\n   * @api private\n   * @method _setHelperLayerPosition\n   * @param {Object} helperLayer\n   */\n  function _setHelperLayerPosition(helperLayer) {\n    if (helperLayer) {\n      //prevent error when `this._currentStep` in undefined\n      if (!this._introItems[this._currentStep]) return;\n\n      var currentElement  = this._introItems[this._currentStep],\n          elementPosition = _getOffset(currentElement.element),\n          widthHeightPadding = this._options.helperElementPadding;\n\n      // If the target element is fixed, the tooltip should be fixed as well.\n      // Otherwise, remove a fixed class that may be left over from the previous\n      // step.\n      if (_isFixed(currentElement.element)) {\n        _addClass(helperLayer, 'introjs-fixedTooltip');\n      } else {\n        _removeClass(helperLayer, 'introjs-fixedTooltip');\n      }\n\n      if (currentElement.position === 'floating') {\n        widthHeightPadding = 0;\n      }\n\n      //set new position to helper layer\n      helperLayer.style.cssText = 'width: ' + (elementPosition.width  + widthHeightPadding)  + 'px; ' +\n                                        'height:' + (elementPosition.height + widthHeightPadding)  + 'px; ' +\n                                        'top:'    + (elementPosition.top    - widthHeightPadding / 2)   + 'px;' +\n                                        'left: '  + (elementPosition.left   - widthHeightPadding / 2)   + 'px;';\n\n    }\n  }\n\n  /**\n   * Add disableinteraction layer and adjust the size and position of the layer\n   *\n   * @api private\n   * @method _disableInteraction\n   */\n  function _disableInteraction() {\n    var disableInteractionLayer = document.querySelector('.introjs-disableInteraction');\n\n    if (disableInteractionLayer === null) {\n      disableInteractionLayer = document.createElement('div');\n      disableInteractionLayer.className = 'introjs-disableInteraction';\n      this._targetElement.appendChild(disableInteractionLayer);\n    }\n\n    _setHelperLayerPosition.call(this, disableInteractionLayer);\n  }\n\n  /**\n   * Setting anchors to behave like buttons\n   *\n   * @api private\n   * @method _setAnchorAsButton\n   */\n  function _setAnchorAsButton(anchor){\n    anchor.setAttribute('role', 'button');\n    anchor.tabIndex = 0;\n  }\n\n  /**\n   * Show an element on the page\n   *\n   * @api private\n   * @method _showElement\n   * @param {Object} targetElement\n   */\n  function _showElement(targetElement) {\n    if (typeof (this._introChangeCallback) !== 'undefined') {\n      this._introChangeCallback.call(this, targetElement.element);\n    }\n\n    var self = this,\n        oldHelperLayer = document.querySelector('.introjs-helperLayer'),\n        oldReferenceLayer = document.querySelector('.introjs-tooltipReferenceLayer'),\n        highlightClass = 'introjs-helperLayer',\n        nextTooltipButton,\n        prevTooltipButton,\n        skipTooltipButton,\n        scrollParent;\n\n    //check for a current step highlight class\n    if (typeof (targetElement.highlightClass) === 'string') {\n      highlightClass += (' ' + targetElement.highlightClass);\n    }\n    //check for options highlight class\n    if (typeof (this._options.highlightClass) === 'string') {\n      highlightClass += (' ' + this._options.highlightClass);\n    }\n\n    if (oldHelperLayer !== null) {\n      var oldHelperNumberLayer = oldReferenceLayer.querySelector('.introjs-helperNumberLayer'),\n          oldtooltipLayer      = oldReferenceLayer.querySelector('.introjs-tooltiptext'),\n          oldArrowLayer        = oldReferenceLayer.querySelector('.introjs-arrow'),\n          oldtooltipContainer  = oldReferenceLayer.querySelector('.introjs-tooltip');\n          \n      skipTooltipButton    = oldReferenceLayer.querySelector('.introjs-skipbutton');\n      prevTooltipButton    = oldReferenceLayer.querySelector('.introjs-prevbutton');\n      nextTooltipButton    = oldReferenceLayer.querySelector('.introjs-nextbutton');\n\n      //update or reset the helper highlight class\n      oldHelperLayer.className = highlightClass;\n      //hide the tooltip\n      oldtooltipContainer.style.opacity = 0;\n      oldtooltipContainer.style.display = \"none\";\n\n      if (oldHelperNumberLayer !== null) {\n        var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)];\n\n        if (lastIntroItem !== null && (this._direction === 'forward' && lastIntroItem.position === 'floating') || (this._direction === 'backward' && targetElement.position === 'floating')) {\n          oldHelperNumberLayer.style.opacity = 0;\n        }\n      }\n\n      // scroll to element\n      scrollParent = _getScrollParent( targetElement.element );\n\n      if (scrollParent !== document.body) {\n        // target is within a scrollable element\n        _scrollParentToElement(scrollParent, targetElement.element);\n      }\n\n      // set new position to helper layer\n      _setHelperLayerPosition.call(self, oldHelperLayer);\n      _setHelperLayerPosition.call(self, oldReferenceLayer);\n\n      //remove `introjs-fixParent` class from the elements\n      var fixParents = document.querySelectorAll('.introjs-fixParent');\n      _forEach(fixParents, function (parent) {\n        _removeClass(parent, /introjs-fixParent/g);\n      });\n      \n      //remove old classes if the element still exist\n      _removeShowElement();\n\n      //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation\n      if (self._lastShowElementTimer) {\n        window.clearTimeout(self._lastShowElementTimer);\n      }\n\n      self._lastShowElementTimer = window.setTimeout(function() {\n        //set current step to the label\n        if (oldHelperNumberLayer !== null) {\n          oldHelperNumberLayer.innerHTML = targetElement.step;\n        }\n        //set current tooltip text\n        oldtooltipLayer.innerHTML = targetElement.intro;\n        //set the tooltip position\n        oldtooltipContainer.style.display = \"block\";\n        _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer);\n\n        //change active bullet\n        if (self._options.showBullets) {\n            oldReferenceLayer.querySelector('.introjs-bullets li > a.active').className = '';\n            oldReferenceLayer.querySelector('.introjs-bullets li > a[data-stepnumber=\"' + targetElement.step + '\"]').className = 'active';\n        }\n        oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').style.cssText = 'width:' + _getProgress.call(self) + '%;';\n        oldReferenceLayer.querySelector('.introjs-progress .introjs-progressbar').setAttribute('aria-valuenow', _getProgress.call(self));\n\n        //show the tooltip\n        oldtooltipContainer.style.opacity = 1;\n        if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1;\n\n        //reset button focus\n        if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null && /introjs-donebutton/gi.test(skipTooltipButton.className)) {\n          // skip button is now \"done\" button\n          skipTooltipButton.focus();\n        } else if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n          //still in the tour, focus on next\n          nextTooltipButton.focus();\n        }\n\n        // change the scroll of the window, if needed\n        _scrollTo.call(self, targetElement.scrollTo, targetElement, oldtooltipLayer);\n      }, 350);\n\n      // end of old element if-else condition\n    } else {\n      var helperLayer       = document.createElement('div'),\n          referenceLayer    = document.createElement('div'),\n          arrowLayer        = document.createElement('div'),\n          tooltipLayer      = document.createElement('div'),\n          tooltipTextLayer  = document.createElement('div'),\n          bulletsLayer      = document.createElement('div'),\n          progressLayer     = document.createElement('div'),\n          buttonsLayer      = document.createElement('div');\n\n      helperLayer.className = highlightClass;\n      referenceLayer.className = 'introjs-tooltipReferenceLayer';\n\n      // scroll to element\n      scrollParent = _getScrollParent( targetElement.element );\n\n      if (scrollParent !== document.body) {\n        // target is within a scrollable element\n        _scrollParentToElement(scrollParent, targetElement.element);\n      }\n\n      //set new position to helper layer\n      _setHelperLayerPosition.call(self, helperLayer);\n      _setHelperLayerPosition.call(self, referenceLayer);\n\n      //add helper layer to target element\n      this._targetElement.appendChild(helperLayer);\n      this._targetElement.appendChild(referenceLayer);\n\n      arrowLayer.className = 'introjs-arrow';\n\n      tooltipTextLayer.className = 'introjs-tooltiptext';\n      tooltipTextLayer.innerHTML = targetElement.intro;\n\n      bulletsLayer.className = 'introjs-bullets';\n\n      if (this._options.showBullets === false) {\n        bulletsLayer.style.display = 'none';\n      }\n\n      var ulContainer = document.createElement('ul');\n      ulContainer.setAttribute('role', 'tablist');\n\n      var anchorClick = function () {\n          self.goToStep(this.getAttribute('data-stepnumber'));\n      };\n\n      _forEach(this._introItems, function (item, i) {\n        var innerLi    = document.createElement('li');\n        var anchorLink = document.createElement('a');\n        \n        innerLi.setAttribute('role', 'presentation');\n        anchorLink.setAttribute('role', 'tab');\n\n        anchorLink.onclick = anchorClick;\n\n        if (i === (targetElement.step-1)) {\n          anchorLink.className = 'active';\n        } \n\n        _setAnchorAsButton(anchorLink);\n        anchorLink.innerHTML = \"&nbsp;\";\n        anchorLink.setAttribute('data-stepnumber', item.step);\n\n        innerLi.appendChild(anchorLink);\n        ulContainer.appendChild(innerLi);\n      });\n\n      bulletsLayer.appendChild(ulContainer);\n\n      progressLayer.className = 'introjs-progress';\n\n      if (this._options.showProgress === false) {\n        progressLayer.style.display = 'none';\n      }\n      var progressBar = document.createElement('div');\n      progressBar.className = 'introjs-progressbar';\n      progressBar.setAttribute('role', 'progress');\n      progressBar.setAttribute('aria-valuemin', 0);\n      progressBar.setAttribute('aria-valuemax', 100);\n      progressBar.setAttribute('aria-valuenow', _getProgress.call(this));\n      progressBar.style.cssText = 'width:' + _getProgress.call(this) + '%;';\n\n      progressLayer.appendChild(progressBar);\n\n      buttonsLayer.className = 'introjs-tooltipbuttons';\n      if (this._options.showButtons === false) {\n        buttonsLayer.style.display = 'none';\n      }\n\n      tooltipLayer.className = 'introjs-tooltip';\n      tooltipLayer.appendChild(tooltipTextLayer);\n      tooltipLayer.appendChild(bulletsLayer);\n      tooltipLayer.appendChild(progressLayer);\n\n      //add helper layer number\n      var helperNumberLayer = document.createElement('span');\n      if (this._options.showStepNumbers === true) {\n        helperNumberLayer.className = 'introjs-helperNumberLayer';\n        helperNumberLayer.innerHTML = targetElement.step;\n        referenceLayer.appendChild(helperNumberLayer);\n      }\n\n      tooltipLayer.appendChild(arrowLayer);\n      referenceLayer.appendChild(tooltipLayer);\n\n      //next button\n      nextTooltipButton = document.createElement('a');\n\n      nextTooltipButton.onclick = function() {\n        if (self._introItems.length - 1 !== self._currentStep) {\n          _nextStep.call(self);\n        }\n      };\n\n      _setAnchorAsButton(nextTooltipButton);\n      nextTooltipButton.innerHTML = this._options.nextLabel;\n\n      //previous button\n      prevTooltipButton = document.createElement('a');\n\n      prevTooltipButton.onclick = function() {\n        if (self._currentStep !== 0) {\n          _previousStep.call(self);\n        }\n      };\n\n      _setAnchorAsButton(prevTooltipButton);\n      prevTooltipButton.innerHTML = this._options.prevLabel;\n\n      //skip button\n      skipTooltipButton = document.createElement('a');\n      skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton ';\n      _setAnchorAsButton(skipTooltipButton);\n      skipTooltipButton.innerHTML = this._options.skipLabel;\n\n      skipTooltipButton.onclick = function() {\n        if (self._introItems.length - 1 === self._currentStep && typeof (self._introCompleteCallback) === 'function') {\n          self._introCompleteCallback.call(self);\n        }\n\n        if (self._introItems.length - 1 !== self._currentStep && typeof (self._introExitCallback) === 'function') {\n          self._introExitCallback.call(self);\n        }\n\n        if (typeof(self._introSkipCallback) === 'function') {\n          self._introSkipCallback.call(self);\n        }\n\n        _exitIntro.call(self, self._targetElement);\n      };\n\n      buttonsLayer.appendChild(skipTooltipButton);\n\n      //in order to prevent displaying next/previous button always\n      if (this._introItems.length > 1) {\n        buttonsLayer.appendChild(prevTooltipButton);\n        buttonsLayer.appendChild(nextTooltipButton);\n      }\n\n      tooltipLayer.appendChild(buttonsLayer);\n\n      //set proper position\n      _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer);\n\n      // change the scroll of the window, if needed\n      _scrollTo.call(this, targetElement.scrollTo, targetElement, tooltipLayer);\n\n      //end of new element if-else condition\n    }\n\n    // removing previous disable interaction layer\n    var disableInteractionLayer = self._targetElement.querySelector('.introjs-disableInteraction');\n    if (disableInteractionLayer) {\n      disableInteractionLayer.parentNode.removeChild(disableInteractionLayer);\n    }\n\n    //disable interaction\n    if (targetElement.disableInteraction) {\n      _disableInteraction.call(self);\n    }\n\n    // when it's the first step of tour\n    if (this._currentStep === 0 && this._introItems.length > 1) {\n      if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null) {\n        skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton';\n      }\n      if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n        nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton';\n      }\n\n      if (this._options.hidePrev === true) {\n        if (typeof prevTooltipButton !== \"undefined\" && prevTooltipButton !== null) {\n          prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-hidden';\n        }\n        if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n          _addClass(nextTooltipButton, 'introjs-fullbutton');\n        }\n      } else {\n        if (typeof prevTooltipButton !== \"undefined\" && prevTooltipButton !== null) {\n          prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton introjs-disabled';\n        }\n      }\n\n      if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null) {\n        skipTooltipButton.innerHTML = this._options.skipLabel;\n      }\n    } else if (this._introItems.length - 1 === this._currentStep || this._introItems.length === 1) {\n      // last step of tour\n      if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null) {\n        skipTooltipButton.innerHTML = this._options.doneLabel;\n        // adding donebutton class in addition to skipbutton\n        _addClass(skipTooltipButton, 'introjs-donebutton');\n      }\n      if (typeof prevTooltipButton !== \"undefined\" && prevTooltipButton !== null) {\n        prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton';\n      }\n\n      if (this._options.hideNext === true) {\n        if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n          nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-hidden';\n        }\n        if (typeof prevTooltipButton !== \"undefined\" && prevTooltipButton !== null) {\n          _addClass(prevTooltipButton, 'introjs-fullbutton');\n        }\n      } else {\n        if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n          nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton introjs-disabled';\n        }\n      }\n    } else {\n      // steps between start and end\n      if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null) {\n        skipTooltipButton.className = this._options.buttonClass + ' introjs-skipbutton';\n      }\n      if (typeof prevTooltipButton !== \"undefined\" && prevTooltipButton !== null) {\n        prevTooltipButton.className = this._options.buttonClass + ' introjs-prevbutton';\n      }\n      if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n        nextTooltipButton.className = this._options.buttonClass + ' introjs-nextbutton';\n      }\n      if (typeof skipTooltipButton !== \"undefined\" && skipTooltipButton !== null) {\n        skipTooltipButton.innerHTML = this._options.skipLabel;\n      }\n    }\n\n    prevTooltipButton.setAttribute('role', 'button');\n    nextTooltipButton.setAttribute('role', 'button');\n    skipTooltipButton.setAttribute('role', 'button');\n\n    //Set focus on \"next\" button, so that hitting Enter always moves you onto the next step\n    if (typeof nextTooltipButton !== \"undefined\" && nextTooltipButton !== null) {\n      nextTooltipButton.focus();\n    }\n\n    _setShowElement(targetElement);\n\n    if (typeof (this._introAfterChangeCallback) !== 'undefined') {\n      this._introAfterChangeCallback.call(this, targetElement.element);\n    }\n  }\n\n  /**\n   * To change the scroll of `window` after highlighting an element\n   *\n   * @api private\n   * @method _scrollTo\n   * @param {String} scrollTo\n   * @param {Object} targetElement\n   * @param {Object} tooltipLayer\n   */\n  function _scrollTo(scrollTo, targetElement, tooltipLayer) {\n    if (scrollTo === 'off') return;  \n    var rect;\n\n    if (!this._options.scrollToElement) return;\n\n    if (scrollTo === 'tooltip') {\n      rect = tooltipLayer.getBoundingClientRect();\n    } else {\n      rect = targetElement.element.getBoundingClientRect();\n    }\n\n    if (!_elementInViewport(targetElement.element)) {\n      var winHeight = _getWinSize().height;\n      var top = rect.bottom - (rect.bottom - rect.top);\n\n      // TODO (afshinm): do we need scroll padding now?\n      // I have changed the scroll option and now it scrolls the window to\n      // the center of the target element or tooltip.\n\n      if (top < 0 || targetElement.element.clientHeight > winHeight) {\n        window.scrollBy(0, rect.top - ((winHeight / 2) -  (rect.height / 2)) - this._options.scrollPadding); // 30px padding from edge to look nice\n\n      //Scroll down\n      } else {\n        window.scrollBy(0, rect.top - ((winHeight / 2) -  (rect.height / 2)) + this._options.scrollPadding); // 30px padding from edge to look nice\n      }\n    }\n  }\n\n  /**\n   * To remove all show element(s)\n   *\n   * @api private\n   * @method _removeShowElement\n   */\n  function _removeShowElement() {\n    var elms = document.querySelectorAll('.introjs-showElement');\n\n    _forEach(elms, function (elm) {\n      _removeClass(elm, /introjs-[a-zA-Z]+/g);\n    });\n  }\n\n  /**\n   * To set the show element\n   * This function set a relative (in most cases) position and changes the z-index\n   *\n   * @api private\n   * @method _setShowElement\n   * @param {Object} targetElement\n   */\n  function _setShowElement(targetElement) {\n    var parentElm;\n    // we need to add this show element class to the parent of SVG elements\n    // because the SVG elements can't have independent z-index\n    if (targetElement.element instanceof SVGElement) {\n      parentElm = targetElement.element.parentNode;\n\n      while (targetElement.element.parentNode !== null) {\n        if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;\n\n        if (parentElm.tagName.toLowerCase() === 'svg') {\n          _addClass(parentElm, 'introjs-showElement introjs-relativePosition');\n        }\n\n        parentElm = parentElm.parentNode;\n      }\n    }\n\n    _addClass(targetElement.element, 'introjs-showElement');\n\n    var currentElementPosition = _getPropValue(targetElement.element, 'position');\n    if (currentElementPosition !== 'absolute' &&\n        currentElementPosition !== 'relative' &&\n        currentElementPosition !== 'fixed') {\n      //change to new intro item\n      _addClass(targetElement.element, 'introjs-relativePosition');\n    }\n\n    parentElm = targetElement.element.parentNode;\n    while (parentElm !== null) {\n      if (!parentElm.tagName || parentElm.tagName.toLowerCase() === 'body') break;\n\n      //fix The Stacking Context problem.\n      //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context\n      var zIndex = _getPropValue(parentElm, 'z-index');\n      var opacity = parseFloat(_getPropValue(parentElm, 'opacity'));\n      var transform = _getPropValue(parentElm, 'transform') || _getPropValue(parentElm, '-webkit-transform') || _getPropValue(parentElm, '-moz-transform') || _getPropValue(parentElm, '-ms-transform') || _getPropValue(parentElm, '-o-transform');\n      if (/[0-9]+/.test(zIndex) || opacity < 1 || (transform !== 'none' && transform !== undefined)) {\n        _addClass(parentElm, 'introjs-fixParent');\n      }\n\n      parentElm = parentElm.parentNode;\n    }\n  }\n\n  /**\n  * Iterates arrays\n  *\n  * @param {Array} arr\n  * @param {Function} forEachFnc\n  * @param {Function} completeFnc\n  * @return {Null}\n  */\n  function _forEach(arr, forEachFnc, completeFnc) {\n    // in case arr is an empty query selector node list\n    if (arr) {\n      for (var i = 0, len = arr.length; i < len; i++) {\n        forEachFnc(arr[i], i);\n      }\n    }\n\n    if (typeof(completeFnc) === 'function') {\n      completeFnc();\n    }\n  }\n\n  /**\n  * Mark any object with an incrementing number\n  * used for keeping track of objects\n  *\n  * @param Object obj   Any object or DOM Element\n  * @param String key\n  * @return Object\n  */\n  var _stamp = (function () {\n    var keys = {};\n    return function stamp (obj, key) {\n      \n      // get group key\n      key = key || 'introjs-stamp';\n\n      // each group increments from 0\n      keys[key] = keys[key] || 0;\n\n      // stamp only once per object\n      if (obj[key] === undefined) {\n        // increment key for each new object\n        obj[key] = keys[key]++;\n      }\n\n      return obj[key];\n    };\n  })();\n\n  /**\n  * DOMEvent Handles all DOM events\n  *\n  * methods:\n  *\n  * on - add event handler\n  * off - remove event\n  */\n  var DOMEvent = (function () {\n    function DOMEvent () {\n      var events_key = 'introjs_event';\n      \n      /**\n      * Gets a unique ID for an event listener\n      *\n      * @param Object obj\n      * @param String type        event type\n      * @param Function listener\n      * @param Object context\n      * @return String\n      */\n      this._id = function (obj, type, listener, context) {\n        return type + _stamp(listener) + (context ? '_' + _stamp(context) : '');\n      };\n\n      /**\n      * Adds event listener\n      *\n      * @param Object obj\n      * @param String type        event type\n      * @param Function listener\n      * @param Object context\n      * @param Boolean useCapture\n      * @return null\n      */\n      this.on = function (obj, type, listener, context, useCapture) {\n        var id = this._id.apply(this, arguments),\n            handler = function (e) {\n              return listener.call(context || obj, e || window.event);\n            };\n\n        if ('addEventListener' in obj) {\n          obj.addEventListener(type, handler, useCapture);\n        } else if ('attachEvent' in obj) {\n          obj.attachEvent('on' + type, handler);\n        }\n\n        obj[events_key] = obj[events_key] || {};\n        obj[events_key][id] = handler;\n      };\n\n      /**\n      * Removes event listener\n      *\n      * @param Object obj\n      * @param String type        event type\n      * @param Function listener\n      * @param Object context\n      * @param Boolean useCapture\n      * @return null\n      */\n      this.off = function (obj, type, listener, context, useCapture) {\n        var id = this._id.apply(this, arguments),\n            handler = obj[events_key] && obj[events_key][id];\n\n        if (!handler) {\n          return;\n        }\n\n        if ('removeEventListener' in obj) {\n          obj.removeEventListener(type, handler, useCapture);\n        } else if ('detachEvent' in obj) {\n          obj.detachEvent('on' + type, handler);\n        }\n\n        obj[events_key][id] = null;\n      };\n    }\n\n    return new DOMEvent();\n  })();\n\n  /**\n   * Append a class to an element\n   *\n   * @api private\n   * @method _addClass\n   * @param {Object} element\n   * @param {String} className\n   * @returns null\n   */\n  function _addClass(element, className) {\n    if (element instanceof SVGElement) {\n      // svg\n      var pre = element.getAttribute('class') || '';\n\n      element.setAttribute('class', pre + ' ' + className);\n    } else {\n      if (element.classList !== undefined) {\n        // check for modern classList property\n        var classes = className.split(' ');\n        _forEach(classes, function (cls) {\n          element.classList.add( cls );\n        });\n      } else if (!element.className.match( className )) {\n        // check if element doesn't already have className\n        element.className += ' ' + className;\n      }\n    }\n  }\n\n  /**\n   * Remove a class from an element\n   *\n   * @api private\n   * @method _removeClass\n   * @param {Object} element\n   * @param {RegExp|String} classNameRegex can be regex or string\n   * @returns null\n   */\n  function _removeClass(element, classNameRegex) {\n    if (element instanceof SVGElement) {\n      var pre = element.getAttribute('class') || '';\n\n      element.setAttribute('class', pre.replace(classNameRegex, '').replace(/^\\s+|\\s+$/g, ''));\n    } else {\n      element.className = element.className.replace(classNameRegex, '').replace(/^\\s+|\\s+$/g, '');\n    }\n  }\n\n  /**\n   * Get an element CSS property on the page\n   * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml\n   *\n   * @api private\n   * @method _getPropValue\n   * @param {Object} element\n   * @param {String} propName\n   * @returns Element's property value\n   */\n  function _getPropValue (element, propName) {\n    var propValue = '';\n    if (element.currentStyle) { //IE\n      propValue = element.currentStyle[propName];\n    } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others\n      propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);\n    }\n\n    //Prevent exception in IE\n    if (propValue && propValue.toLowerCase) {\n      return propValue.toLowerCase();\n    } else {\n      return propValue;\n    }\n  }\n\n  /**\n   * Checks to see if target element (or parents) position is fixed or not\n   *\n   * @api private\n   * @method _isFixed\n   * @param {Object} element\n   * @returns Boolean\n   */\n  function _isFixed (element) {\n    var p = element.parentNode;\n\n    if (!p || p.nodeName === 'HTML') {\n      return false;\n    }\n\n    if (_getPropValue(element, 'position') === 'fixed') {\n      return true;\n    }\n\n    return _isFixed(p);\n  }\n\n  /**\n   * Provides a cross-browser way to get the screen dimensions\n   * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight\n   *\n   * @api private\n   * @method _getWinSize\n   * @returns {Object} width and height attributes\n   */\n  function _getWinSize() {\n    if (window.innerWidth !== undefined) {\n      return { width: window.innerWidth, height: window.innerHeight };\n    } else {\n      var D = document.documentElement;\n      return { width: D.clientWidth, height: D.clientHeight };\n    }\n  }\n\n  /**\n   * Check to see if the element is in the viewport or not\n   * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport\n   *\n   * @api private\n   * @method _elementInViewport\n   * @param {Object} el\n   */\n  function _elementInViewport(el) {\n    var rect = el.getBoundingClientRect();\n\n    return (\n      rect.top >= 0 &&\n      rect.left >= 0 &&\n      (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right\n      rect.right <= window.innerWidth\n    );\n  }\n\n  /**\n   * Add overlay layer to the page\n   *\n   * @api private\n   * @method _addOverlayLayer\n   * @param {Object} targetElm\n   */\n  function _addOverlayLayer(targetElm) {\n    var overlayLayer = document.createElement('div'),\n        styleText = '',\n        self = this;\n\n    //set css class name\n    overlayLayer.className = 'introjs-overlay';\n\n    //check if the target element is body, we should calculate the size of overlay layer in a better way\n    if (!targetElm.tagName || targetElm.tagName.toLowerCase() === 'body') {\n      styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';\n      overlayLayer.style.cssText = styleText;\n    } else {\n      //set overlay layer position\n      var elementPosition = _getOffset(targetElm);\n      if (elementPosition) {\n        styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';\n        overlayLayer.style.cssText = styleText;\n      }\n    }\n\n    targetElm.appendChild(overlayLayer);\n\n    overlayLayer.onclick = function() {\n      if (self._options.exitOnOverlayClick === true) {\n        _exitIntro.call(self, targetElm);\n      }\n    };\n\n    window.setTimeout(function() {\n      styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';';\n      overlayLayer.style.cssText = styleText;\n    }, 10);\n\n    return true;\n  }\n\n  /**\n   * Removes open hint (tooltip hint)\n   *\n   * @api private\n   * @method _removeHintTooltip\n   */\n  function _removeHintTooltip() {\n    var tooltip = document.querySelector('.introjs-hintReference');\n\n    if (tooltip) {\n      var step = tooltip.getAttribute('data-step');\n      tooltip.parentNode.removeChild(tooltip);\n      return step;\n    }\n  }\n\n  /**\n   * Start parsing hint items\n   *\n   * @api private\n   * @param {Object} targetElm\n   * @method _startHint\n   */\n  function _populateHints(targetElm) {\n\n    this._introItems = [];\n\n    if (this._options.hints) {\n      _forEach(this._options.hints, function (hint) {\n        var currentItem = _cloneObject(hint);\n\n        if (typeof(currentItem.element) === 'string') {\n          //grab the element with given selector from the page\n          currentItem.element = document.querySelector(currentItem.element);\n        }\n\n        currentItem.hintPosition = currentItem.hintPosition || this._options.hintPosition;\n        currentItem.hintAnimation = currentItem.hintAnimation || this._options.hintAnimation;\n\n        if (currentItem.element !== null) {\n          this._introItems.push(currentItem);\n        }\n      }.bind(this));\n    } else {\n      var hints = targetElm.querySelectorAll('*[data-hint]');\n\n      if (!hints || !hints.length) {\n        return false;\n      }\n\n      //first add intro items with data-step\n      _forEach(hints, function (currentElement) {\n        // hint animation\n        var hintAnimation = currentElement.getAttribute('data-hintanimation');\n\n        if (hintAnimation) {\n          hintAnimation = (hintAnimation === 'true');\n        } else {\n          hintAnimation = this._options.hintAnimation;\n        }\n\n        this._introItems.push({\n          element: currentElement,\n          hint: currentElement.getAttribute('data-hint'),\n          hintPosition: currentElement.getAttribute('data-hintposition') || this._options.hintPosition,\n          hintAnimation: hintAnimation,\n          tooltipClass: currentElement.getAttribute('data-tooltipclass'),\n          position: currentElement.getAttribute('data-position') || this._options.tooltipPosition\n        });\n      }.bind(this));\n    }\n\n    _addHints.call(this);\n\n    /* \n    todo:\n    these events should be removed at some point \n    */\n    DOMEvent.on(document, 'click', _removeHintTooltip, this, false);\n    DOMEvent.on(window, 'resize', _reAlignHints, this, true);\n  }\n\n  /**\n   * Re-aligns all hint elements\n   *\n   * @api private\n   * @method _reAlignHints\n   */\n  function _reAlignHints() {\n    _forEach(this._introItems, function (item) {\n      if (typeof(item.targetElement) === 'undefined') {\n        return;\n      }\n\n      _alignHintPosition.call(this, item.hintPosition, item.element, item.targetElement);\n    }.bind(this));\n  }\n\n  /**\n  * Get a queryselector within the hint wrapper\n  *\n  * @param {String} selector\n  * @return {NodeList|Array}\n  */\n  function _hintQuerySelectorAll(selector) {\n    var hintsWrapper = document.querySelector('.introjs-hints');\n    return (hintsWrapper) ? hintsWrapper.querySelectorAll(selector) : [];\n  }\n\n  /**\n   * Hide a hint\n   *\n   * @api private\n   * @method _hideHint\n   */\n  function _hideHint(stepId) {\n    var hint = _hintQuerySelectorAll('.introjs-hint[data-step=\"' + stepId + '\"]')[0];\n    \n    _removeHintTooltip.call(this);\n\n    if (hint) {\n      _addClass(hint, 'introjs-hidehint');\n    }\n\n    // call the callback function (if any)\n    if (typeof (this._hintCloseCallback) !== 'undefined') {\n      this._hintCloseCallback.call(this, stepId);\n    }\n  }\n\n  /**\n   * Hide all hints\n   *\n   * @api private\n   * @method _hideHints\n   */\n  function _hideHints() {\n    var hints = _hintQuerySelectorAll('.introjs-hint');\n\n    _forEach(hints, function (hint) {\n      _hideHint.call(this, hint.getAttribute('data-step'));\n    }.bind(this));\n  }\n\n  /**\n   * Show all hints\n   *\n   * @api private\n   * @method _showHints\n   */\n  function _showHints() {\n    var hints = _hintQuerySelectorAll('.introjs-hint');\n\n    if (hints && hints.length) {\n      _forEach(hints, function (hint) {\n        _showHint.call(this, hint.getAttribute('data-step'));\n      }.bind(this));\n    } else {\n      _populateHints.call(this, this._targetElement);\n    }\n  }\n\n  /**\n   * Show a hint\n   *\n   * @api private\n   * @method _showHint\n   */\n  function _showHint(stepId) {\n    var hint = _hintQuerySelectorAll('.introjs-hint[data-step=\"' + stepId + '\"]')[0];\n\n    if (hint) {\n      _removeClass(hint, /introjs-hidehint/g);\n    }\n  }\n\n  /**\n   * Removes all hint elements on the page\n   * Useful when you want to destroy the elements and add them again (e.g. a modal or popup)\n   *\n   * @api private\n   * @method _removeHints\n   */\n  function _removeHints() {\n    var hints = _hintQuerySelectorAll('.introjs-hint');\n\n    _forEach(hints, function (hint) {\n      _removeHint.call(this, hint.getAttribute('data-step'));\n    }.bind(this));\n  }\n\n  /**\n   * Remove one single hint element from the page\n   * Useful when you want to destroy the element and add them again (e.g. a modal or popup)\n   * Use removeHints if you want to remove all elements.\n   *\n   * @api private\n   * @method _removeHint\n   */\n  function _removeHint(stepId) {\n    var hint = _hintQuerySelectorAll('.introjs-hint[data-step=\"' + stepId + '\"]')[0];\n\n    if (hint) {\n      hint.parentNode.removeChild(hint);\n    }\n  }\n\n  /**\n   * Add all available hints to the page\n   *\n   * @api private\n   * @method _addHints\n   */\n  function _addHints() {\n    var self = this;\n\n    var hintsWrapper = document.querySelector('.introjs-hints');\n\n    if (hintsWrapper === null) {\n      hintsWrapper = document.createElement('div');\n      hintsWrapper.className = 'introjs-hints';\n    }\n\n    /**\n    * Returns an event handler unique to the hint iteration\n    * \n    * @param {Integer} i\n    * @return {Function}\n    */\n    var getHintClick = function (i) {\n      return function(e) {\n        var evt = e ? e : window.event;\n        \n        if (evt.stopPropagation) {\n          evt.stopPropagation();\n        }\n\n        if (evt.cancelBubble !== null) {\n          evt.cancelBubble = true;\n        }\n\n        _showHintDialog.call(self, i);\n      };\n    };\n\n    _forEach(this._introItems, function(item, i) {\n      // avoid append a hint twice\n      if (document.querySelector('.introjs-hint[data-step=\"' + i + '\"]')) {\n        return;\n      }\n\n      var hint = document.createElement('a');\n      _setAnchorAsButton(hint);\n\n      hint.onclick = getHintClick(i);\n\n      hint.className = 'introjs-hint';\n\n      if (!item.hintAnimation) {\n        _addClass(hint, 'introjs-hint-no-anim');\n      }\n\n      // hint's position should be fixed if the target element's position is fixed\n      if (_isFixed(item.element)) {\n        _addClass(hint, 'introjs-fixedhint');\n      }\n\n      var hintDot = document.createElement('div');\n      hintDot.className = 'introjs-hint-dot';\n      var hintPulse = document.createElement('div');\n      hintPulse.className = 'introjs-hint-pulse';\n\n      hint.appendChild(hintDot);\n      hint.appendChild(hintPulse);\n      hint.setAttribute('data-step', i);\n\n      // we swap the hint element with target element\n      // because _setHelperLayerPosition uses `element` property\n      item.targetElement = item.element;\n      item.element = hint;\n\n      // align the hint position\n      _alignHintPosition.call(this, item.hintPosition, hint, item.targetElement);\n\n      hintsWrapper.appendChild(hint);\n    }.bind(this));\n\n    // adding the hints wrapper\n    document.body.appendChild(hintsWrapper);\n\n    // call the callback function (if any)\n    if (typeof (this._hintsAddedCallback) !== 'undefined') {\n      this._hintsAddedCallback.call(this);\n    }\n  }\n\n  /**\n   * Aligns hint position\n   *\n   * @api private\n   * @method _alignHintPosition\n   * @param {String} position\n   * @param {Object} hint\n   * @param {Object} element\n   */\n  function _alignHintPosition(position, hint, element) {\n    // get/calculate offset of target element\n    var offset = _getOffset.call(this, element);\n    var iconWidth = 20;\n    var iconHeight = 20;\n\n    // align the hint element\n    switch (position) {\n      default:\n      case 'top-left':\n        hint.style.left = offset.left + 'px';\n        hint.style.top = offset.top + 'px';\n        break;\n      case 'top-right':\n        hint.style.left = (offset.left + offset.width - iconWidth) + 'px';\n        hint.style.top = offset.top + 'px';\n        break;\n      case 'bottom-left':\n        hint.style.left = offset.left + 'px';\n        hint.style.top = (offset.top + offset.height - iconHeight) + 'px';\n        break;\n      case 'bottom-right':\n        hint.style.left = (offset.left + offset.width - iconWidth) + 'px';\n        hint.style.top = (offset.top + offset.height - iconHeight) + 'px';\n        break;\n      case 'middle-left':\n        hint.style.left = offset.left + 'px';\n        hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';\n        break;\n      case 'middle-right':\n        hint.style.left = (offset.left + offset.width - iconWidth) + 'px';\n        hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';\n        break;\n      case 'middle-middle':\n        hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';\n        hint.style.top = (offset.top + (offset.height - iconHeight) / 2) + 'px';\n        break;\n      case 'bottom-middle':\n        hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';\n        hint.style.top = (offset.top + offset.height - iconHeight) + 'px';\n        break;\n      case 'top-middle':\n        hint.style.left = (offset.left + (offset.width - iconWidth) / 2) + 'px';\n        hint.style.top = offset.top + 'px';\n        break;\n    }\n  }\n\n  /**\n   * Triggers when user clicks on the hint element\n   *\n   * @api private\n   * @method _showHintDialog\n   * @param {Number} stepId\n   */\n  function _showHintDialog(stepId) {\n    var hintElement = document.querySelector('.introjs-hint[data-step=\"' + stepId + '\"]');\n    var item = this._introItems[stepId];\n\n    // call the callback function (if any)\n    if (typeof (this._hintClickCallback) !== 'undefined') {\n      this._hintClickCallback.call(this, hintElement, item, stepId);\n    }\n\n    // remove all open tooltips\n    var removedStep = _removeHintTooltip.call(this);\n\n    // to toggle the tooltip\n    if (parseInt(removedStep, 10) === stepId) {\n      return;\n    }\n\n    var tooltipLayer = document.createElement('div');\n    var tooltipTextLayer = document.createElement('div');\n    var arrowLayer = document.createElement('div');\n    var referenceLayer = document.createElement('div');\n\n    tooltipLayer.className = 'introjs-tooltip';\n\n    tooltipLayer.onclick = function (e) {\n      //IE9 & Other Browsers\n      if (e.stopPropagation) {\n        e.stopPropagation();\n      }\n      //IE8 and Lower\n      else {\n        e.cancelBubble = true;\n      }\n    };\n\n    tooltipTextLayer.className = 'introjs-tooltiptext';\n\n    var tooltipWrapper = document.createElement('p');\n    tooltipWrapper.innerHTML = item.hint;\n\n    var closeButton = document.createElement('a');\n    closeButton.className = this._options.buttonClass;\n    closeButton.setAttribute('role', 'button');\n    closeButton.innerHTML = this._options.hintButtonLabel;\n    closeButton.onclick = _hideHint.bind(this, stepId);\n\n    tooltipTextLayer.appendChild(tooltipWrapper);\n    tooltipTextLayer.appendChild(closeButton);\n\n    arrowLayer.className = 'introjs-arrow';\n    tooltipLayer.appendChild(arrowLayer);\n\n    tooltipLayer.appendChild(tooltipTextLayer);\n\n    // set current step for _placeTooltip function\n    this._currentStep = hintElement.getAttribute('data-step');\n\n    // align reference layer position\n    referenceLayer.className = 'introjs-tooltipReferenceLayer introjs-hintReference';\n    referenceLayer.setAttribute('data-step', hintElement.getAttribute('data-step'));\n    _setHelperLayerPosition.call(this, referenceLayer);\n\n    referenceLayer.appendChild(tooltipLayer);\n    document.body.appendChild(referenceLayer);\n\n    //set proper position\n    _placeTooltip.call(this, hintElement, tooltipLayer, arrowLayer, null, true);\n  }\n\n  /**\n   * Get an element position on the page\n   * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966\n   *\n   * @api private\n   * @method _getOffset\n   * @param {Object} element\n   * @returns Element's position info\n   */\n  function _getOffset(element) {\n    var body = document.body;\n    var docEl = document.documentElement;\n    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;\n    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;\n    var x = element.getBoundingClientRect();\n    return {\n      top: x.top + scrollTop,\n      width: x.width,\n      height: x.height,\n      left: x.left + scrollLeft\n    };\n  }\n\n  /**\n  * Find the nearest scrollable parent\n  * copied from https://stackoverflow.com/questions/35939886/find-first-scrollable-parent\n  *\n  * @param Element element\n  * @return Element\n  */\n  function _getScrollParent(element) {\n    var style = window.getComputedStyle(element);\n    var excludeStaticParent = (style.position === \"absolute\");\n    var overflowRegex = /(auto|scroll)/;\n\n    if (style.position === \"fixed\") return document.body;\n    \n    for (var parent = element; (parent = parent.parentElement);) {\n      style = window.getComputedStyle(parent);\n      if (excludeStaticParent && style.position === \"static\") {\n        continue;\n      }\n      if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;\n    }\n\n    return document.body;\n  }\n\n  /**\n  * scroll a scrollable element to a child element\n  *\n  * @param Element parent\n  * @param Element element\n  * @return Null\n  */\n  function _scrollParentToElement (parent, element) {\n    parent.scrollTop = element.offsetTop - parent.offsetTop;\n  }\n\n  /**\n   * Gets the current progress percentage\n   *\n   * @api private\n   * @method _getProgress\n   * @returns current progress percentage\n   */\n  function _getProgress() {\n    // Steps are 0 indexed\n    var currentStep = parseInt((this._currentStep + 1), 10);\n    return ((currentStep / this._introItems.length) * 100);\n  }\n\n  /**\n   * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1\n   * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically\n   *\n   * @param obj1\n   * @param obj2\n   * @returns obj3 a new object based on obj1 and obj2\n   */\n  function _mergeOptions(obj1,obj2) {\n    var obj3 = {},\n      attrname;\n    for (attrname in obj1) { obj3[attrname] = obj1[attrname]; }\n    for (attrname in obj2) { obj3[attrname] = obj2[attrname]; }\n    return obj3;\n  }\n\n  var introJs = function (targetElm) {\n    var instance;\n\n    if (typeof (targetElm) === 'object') {\n      //Ok, create a new instance\n      instance = new IntroJs(targetElm);\n\n    } else if (typeof (targetElm) === 'string') {\n      //select the target element with query selector\n      var targetElement = document.querySelector(targetElm);\n\n      if (targetElement) {\n        instance = new IntroJs(targetElement);\n      } else {\n        throw new Error('There is no element with given selector.');\n      }\n    } else {\n      instance = new IntroJs(document.body);\n    }\n    // add instance to list of _instances\n    // passing group to _stamp to increment\n    // from 0 onward somewhat reliably\n    introJs.instances[ _stamp(instance, 'introjs-instance') ] = instance;\n\n    return instance;\n  };\n\n  /**\n   * Current IntroJs version\n   *\n   * @property version\n   * @type String\n   */\n  introJs.version = VERSION;\n\n  /**\n  * key-val object helper for introJs instances\n  *\n  * @property instances\n  * @type Object\n  */\n  introJs.instances = {};\n\n  //Prototype\n  introJs.fn = IntroJs.prototype = {\n    clone: function () {\n      return new IntroJs(this);\n    },\n    setOption: function(option, value) {\n      this._options[option] = value;\n      return this;\n    },\n    setOptions: function(options) {\n      this._options = _mergeOptions(this._options, options);\n      return this;\n    },\n    start: function (group) {\n      _introForElement.call(this, this._targetElement, group);\n      return this;\n    },\n    goToStep: function(step) {\n      _goToStep.call(this, step);\n      return this;\n    },\n    addStep: function(options) {\n      if (!this._options.steps) {\n        this._options.steps = [];\n      }\n\n      this._options.steps.push(options);\n\n      return this;\n    },\n    addSteps: function(steps) {\n      if (!steps.length) return;\n\n      for(var index = 0; index < steps.length; index++) {\n        this.addStep(steps[index]);\n      }\n\n      return this;\n    },\n    goToStepNumber: function(step) {\n      _goToStepNumber.call(this, step);\n\n      return this;\n    },\n    nextStep: function() {\n      _nextStep.call(this);\n      return this;\n    },\n    previousStep: function() {\n      _previousStep.call(this);\n      return this;\n    },\n    exit: function(force) {\n      _exitIntro.call(this, this._targetElement, force);\n      return this;\n    },\n    refresh: function() {\n      _refresh.call(this);\n      return this;\n    },\n    onbeforechange: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introBeforeChangeCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onbeforechange was not a function');\n      }\n      return this;\n    },\n    onchange: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introChangeCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onchange was not a function.');\n      }\n      return this;\n    },\n    onafterchange: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introAfterChangeCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onafterchange was not a function');\n      }\n      return this;\n    },\n    oncomplete: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introCompleteCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for oncomplete was not a function.');\n      }\n      return this;\n    },\n    onhintsadded: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._hintsAddedCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onhintsadded was not a function.');\n      }\n      return this;\n    },\n    onhintclick: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._hintClickCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onhintclick was not a function.');\n      }\n      return this;\n    },\n    onhintclose: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._hintCloseCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onhintclose was not a function.');\n      }\n      return this;\n    },\n    onexit: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introExitCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onexit was not a function.');\n      }\n      return this;\n    },\n    onskip: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introSkipCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onskip was not a function.');\n      }\n      return this;\n    },\n    onbeforeexit: function(providedCallback) {\n      if (typeof (providedCallback) === 'function') {\n        this._introBeforeExitCallback = providedCallback;\n      } else {\n        throw new Error('Provided callback for onbeforeexit was not a function.');\n      }\n      return this;\n    },\n    addHints: function() {\n      _populateHints.call(this, this._targetElement);\n      return this;\n    },\n    hideHint: function (stepId) {\n      _hideHint.call(this, stepId);\n      return this;\n    },\n    hideHints: function () {\n      _hideHints.call(this);\n      return this;\n    },\n    showHint: function (stepId) {\n      _showHint.call(this, stepId);\n      return this;\n    },\n    showHints: function () {\n      _showHints.call(this);\n      return this;\n    },\n    removeHints: function () {\n      _removeHints.call(this);\n      return this;\n    },\n    removeHint: function (stepId) {\n      _removeHint.call(this, stepId);\n      return this;\n    },\n    showHintDialog: function (stepId) {\n      _showHintDialog.call(this, stepId);\n      return this;\n    }\n  };\n\n  return introJs;\n});\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDQ0NC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9pbnRyby5qcy9pbnRyby5qcz80NzlmIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogSW50cm8uanMgdjIuOS4zXG4gKiBodHRwczovL2dpdGh1Yi5jb20vdXNhYmxpY2EvaW50cm8uanNcbiAqXG4gKiBDb3B5cmlnaHQgKEMpIDIwMTcgQWZzaGluIE1laHJhYmFuaSAoQGFmc2hpbm1laClcbiAqL1xuXG4oZnVuY3Rpb24oZikge1xuICAgIGlmICh0eXBlb2YgZXhwb3J0cyA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgbW9kdWxlICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgIG1vZHVsZS5leHBvcnRzID0gZigpO1xuICAgICAgICAvLyBkZXByZWNhdGVkIGZ1bmN0aW9uXG4gICAgICAgIC8vIEBzaW5jZSAyLjguMFxuICAgICAgICBtb2R1bGUuZXhwb3J0cy5pbnRyb0pzID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGNvbnNvbGUud2FybignRGVwcmVjYXRlZDogcGxlYXNlIHVzZSByZXF1aXJlKFwiaW50cm8uanNcIikgZGlyZWN0bHksIGluc3RlYWQgb2YgdGhlIGludHJvSnMgbWV0aG9kIG9mIHRoZSBmdW5jdGlvbicpO1xuICAgICAgICAgIC8vIGludHJvSnMoKVxuICAgICAgICAgIHJldHVybiBmKCkuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBkZWZpbmUgPT09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kKSB7XG4gICAgICAgIGRlZmluZShbXSwgZik7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIGc7XG4gICAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgICAgICBnID0gd2luZG93O1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBnbG9iYWwgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAgICAgICAgIGcgPSBnbG9iYWw7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHNlbGYgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAgICAgICAgIGcgPSBzZWxmO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZyA9IHRoaXM7XG4gICAgICAgIH1cbiAgICAgICAgZy5pbnRyb0pzID0gZigpO1xuICAgIH1cbn0pKGZ1bmN0aW9uICgpIHtcbiAgLy9EZWZhdWx0IGNvbmZpZy92YXJpYWJsZXNcbiAgdmFyIFZFUlNJT04gPSAnMi45LjMnO1xuXG4gIC8qKlxuICAgKiBJbnRyb0pzIG1haW4gY2xhc3NcbiAgICpcbiAgICogQGNsYXNzIEludHJvSnNcbiAgICovXG4gIGZ1bmN0aW9uIEludHJvSnMob2JqKSB7XG4gICAgdGhpcy5fdGFyZ2V0RWxlbWVudCA9IG9iajtcbiAgICB0aGlzLl9pbnRyb0l0ZW1zID0gW107XG5cbiAgICB0aGlzLl9vcHRpb25zID0ge1xuICAgICAgLyogTmV4dCBidXR0b24gbGFiZWwgaW4gdG9vbHRpcCBib3ggKi9cbiAgICAgIG5leHRMYWJlbDogJ05leHQgJnJhcnI7JyxcbiAgICAgIC8qIFByZXZpb3VzIGJ1dHRvbiBsYWJlbCBpbiB0b29sdGlwIGJveCAqL1xuICAgICAgcHJldkxhYmVsOiAnJmxhcnI7IEJhY2snLFxuICAgICAgLyogU2tpcCBidXR0b24gbGFiZWwgaW4gdG9vbHRpcCBib3ggKi9cbiAgICAgIHNraXBMYWJlbDogJ1NraXAnLFxuICAgICAgLyogRG9uZSBidXR0b24gbGFiZWwgaW4gdG9vbHRpcCBib3ggKi9cbiAgICAgIGRvbmVMYWJlbDogJ0RvbmUnLFxuICAgICAgLyogSGlkZSBwcmV2aW91cyBidXR0b24gaW4gdGhlIGZpcnN0IHN0ZXA/IE90aGVyd2lzZSwgaXQgd2lsbCBiZSBkaXNhYmxlZCBidXR0b24uICovXG4gICAgICBoaWRlUHJldjogZmFsc2UsXG4gICAgICAvKiBIaWRlIG5leHQgYnV0dG9uIGluIHRoZSBsYXN0IHN0ZXA/IE90aGVyd2lzZSwgaXQgd2lsbCBiZSBkaXNhYmxlZCBidXR0b24uICovXG4gICAgICBoaWRlTmV4dDogZmFsc2UsXG4gICAgICAvKiBEZWZhdWx0IHRvb2x0aXAgYm94IHBvc2l0aW9uICovXG4gICAgICB0b29sdGlwUG9zaXRpb246ICdib3R0b20nLFxuICAgICAgLyogTmV4dCBDU1MgY2xhc3MgZm9yIHRvb2x0aXAgYm94ZXMgKi9cbiAgICAgIHRvb2x0aXBDbGFzczogJycsXG4gICAgICAvKiBDU1MgY2xhc3MgdGhhdCBpcyBhZGRlZCB0byB0aGUgaGVscGVyTGF5ZXIgKi9cbiAgICAgIGhpZ2hsaWdodENsYXNzOiAnJyxcbiAgICAgIC8qIENsb3NlIGludHJvZHVjdGlvbiB3aGVuIHByZXNzaW5nIEVzY2FwZSBidXR0b24/ICovXG4gICAgICBleGl0T25Fc2M6IHRydWUsXG4gICAgICAvKiBDbG9zZSBpbnRyb2R1Y3Rpb24gd2hlbiBjbGlja2luZyBvbiBvdmVybGF5IGxheWVyPyAqL1xuICAgICAgZXhpdE9uT3ZlcmxheUNsaWNrOiB0cnVlLFxuICAgICAgLyogU2hvdyBzdGVwIG51bWJlcnMgaW4gaW50cm9kdWN0aW9uPyAqL1xuICAgICAgc2hvd1N0ZXBOdW1iZXJzOiB0cnVlLFxuICAgICAgLyogTGV0IHVzZXIgdXNlIGtleWJvYXJkIHRvIG5hdmlnYXRlIHRoZSB0b3VyPyAqL1xuICAgICAga2V5Ym9hcmROYXZpZ2F0aW9uOiB0cnVlLFxuICAgICAgLyogU2hvdyB0b3VyIGNvbnRyb2wgYnV0dG9ucz8gKi9cbiAgICAgIHNob3dCdXR0b25zOiB0cnVlLFxuICAgICAgLyogU2hvdyB0b3VyIGJ1bGxldHM/ICovXG4gICAgICBzaG93QnVsbGV0czogdHJ1ZSxcbiAgICAgIC8qIFNob3cgdG91ciBwcm9ncmVzcz8gKi9cbiAgICAgIHNob3dQcm9ncmVzczogZmFsc2UsXG4gICAgICAvKiBTY3JvbGwgdG8gaGlnaGxpZ2h0ZWQgZWxlbWVudD8gKi9cbiAgICAgIHNjcm9sbFRvRWxlbWVudDogdHJ1ZSxcbiAgICAgIC8qXG4gICAgICAgKiBTaG91bGQgd2Ugc2Nyb2xsIHRoZSB0b29sdGlwIG9yIHRhcmdldCBlbGVtZW50P1xuICAgICAgICpcbiAgICAgICAqIE9wdGlvbnMgYXJlOiAnZWxlbWVudCcgb3IgJ3Rvb2x0aXAnXG4gICAgICAgKi9cbiAgICAgIHNjcm9sbFRvOiAnZWxlbWVudCcsXG4gICAgICAvKiBQYWRkaW5nIHRvIGFkZCBhZnRlciBzY3JvbGxpbmcgd2hlbiBlbGVtZW50IGlzIG5vdCBpbiB0aGUgdmlld3BvcnQgKGluIHBpeGVscykgKi9cbiAgICAgIHNjcm9sbFBhZGRpbmc6IDMwLFxuICAgICAgLyogU2V0IHRoZSBvdmVybGF5IG9wYWNpdHkgKi9cbiAgICAgIG92ZXJsYXlPcGFjaXR5OiAwLjgsXG4gICAgICAvKiBQcmVjZWRlbmNlIG9mIHBvc2l0aW9ucywgd2hlbiBhdXRvIGlzIGVuYWJsZWQgKi9cbiAgICAgIHBvc2l0aW9uUHJlY2VkZW5jZTogW1wiYm90dG9tXCIsIFwidG9wXCIsIFwicmlnaHRcIiwgXCJsZWZ0XCJdLFxuICAgICAgLyogRGlzYWJsZSBhbiBpbnRlcmFjdGlvbiB3aXRoIGVsZW1lbnQ/ICovXG4gICAgICBkaXNhYmxlSW50ZXJhY3Rpb246IGZhbHNlLFxuICAgICAgLyogU2V0IGhvdyBtdWNoIHBhZGRpbmcgdG8gYmUgdXNlZCBhcm91bmQgaGVscGVyIGVsZW1lbnQgKi9cbiAgICAgIGhlbHBlckVsZW1lbnRQYWRkaW5nOiAxMCxcbiAgICAgIC8qIERlZmF1bHQgaGludCBwb3NpdGlvbiAqL1xuICAgICAgaGludFBvc2l0aW9uOiAndG9wLW1pZGRsZScsXG4gICAgICAvKiBIaW50IGJ1dHRvbiBsYWJlbCAqL1xuICAgICAgaGludEJ1dHRvbkxhYmVsOiAnR290IGl0JyxcbiAgICAgIC8qIEFkZGluZyBhbmltYXRpb24gdG8gaGludHM/ICovXG4gICAgICBoaW50QW5pbWF0aW9uOiB0cnVlLFxuICAgICAgLyogYWRkaXRpb25hbCBjbGFzc2VzIHRvIHB1dCBvbiB0aGUgYnV0dG9ucyAqL1xuICAgICAgYnV0dG9uQ2xhc3M6IFwiaW50cm9qcy1idXR0b25cIlxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhdGUgYSBuZXcgaW50cm9kdWN0aW9uL2d1aWRlIGZyb20gYW4gZWxlbWVudCBpbiB0aGUgcGFnZVxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfaW50cm9Gb3JFbGVtZW50XG4gICAqIEBwYXJhbSB7T2JqZWN0fSB0YXJnZXRFbG1cbiAgICogQHBhcmFtIHtTdHJpbmd9IGdyb3VwXG4gICAqIEByZXR1cm5zIHtCb29sZWFufSBTdWNjZXNzIG9yIG5vdD9cbiAgICovXG4gIGZ1bmN0aW9uIF9pbnRyb0ZvckVsZW1lbnQodGFyZ2V0RWxtLCBncm91cCkge1xuICAgIHZhciBhbGxJbnRyb1N0ZXBzID0gdGFyZ2V0RWxtLnF1ZXJ5U2VsZWN0b3JBbGwoXCIqW2RhdGEtaW50cm9dXCIpLFxuICAgICAgICBpbnRyb0l0ZW1zID0gW107XG5cbiAgICBpZiAodGhpcy5fb3B0aW9ucy5zdGVwcykge1xuICAgICAgLy91c2Ugc3RlcHMgcGFzc2VkIHByb2dyYW1tYXRpY2FsbHlcbiAgICAgIF9mb3JFYWNoKHRoaXMuX29wdGlvbnMuc3RlcHMsIGZ1bmN0aW9uIChzdGVwKSB7XG4gICAgICAgIHZhciBjdXJyZW50SXRlbSA9IF9jbG9uZU9iamVjdChzdGVwKTtcblxuICAgICAgICAvL3NldCB0aGUgc3RlcFxuICAgICAgICBjdXJyZW50SXRlbS5zdGVwID0gaW50cm9JdGVtcy5sZW5ndGggKyAxO1xuXG4gICAgICAgIC8vdXNlIHF1ZXJ5U2VsZWN0b3IgZnVuY3Rpb24gb25seSB3aGVuIGRldmVsb3BlciB1c2VkIENTUyBzZWxlY3RvclxuICAgICAgICBpZiAodHlwZW9mIChjdXJyZW50SXRlbS5lbGVtZW50KSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICAvL2dyYWIgdGhlIGVsZW1lbnQgd2l0aCBnaXZlbiBzZWxlY3RvciBmcm9tIHRoZSBwYWdlXG4gICAgICAgICAgY3VycmVudEl0ZW0uZWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoY3VycmVudEl0ZW0uZWxlbWVudCk7XG4gICAgICAgIH1cblxuICAgICAgICAvL2ludHJvIHdpdGhvdXQgZWxlbWVudFxuICAgICAgICBpZiAodHlwZW9mIChjdXJyZW50SXRlbS5lbGVtZW50KSA9PT0gJ3VuZGVmaW5lZCcgfHwgY3VycmVudEl0ZW0uZWxlbWVudCA9PT0gbnVsbCkge1xuICAgICAgICAgIHZhciBmbG9hdGluZ0VsZW1lbnRRdWVyeSA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXCIuaW50cm9qc0Zsb2F0aW5nRWxlbWVudFwiKTtcblxuICAgICAgICAgIGlmIChmbG9hdGluZ0VsZW1lbnRRdWVyeSA9PT0gbnVsbCkge1xuICAgICAgICAgICAgZmxvYXRpbmdFbGVtZW50UXVlcnkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgICAgICAgIGZsb2F0aW5nRWxlbWVudFF1ZXJ5LmNsYXNzTmFtZSA9ICdpbnRyb2pzRmxvYXRpbmdFbGVtZW50JztcblxuICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChmbG9hdGluZ0VsZW1lbnRRdWVyeSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY3VycmVudEl0ZW0uZWxlbWVudCAgPSBmbG9hdGluZ0VsZW1lbnRRdWVyeTtcbiAgICAgICAgICBjdXJyZW50SXRlbS5wb3NpdGlvbiA9ICdmbG9hdGluZyc7XG4gICAgICAgIH1cblxuICAgICAgICBjdXJyZW50SXRlbS5zY3JvbGxUbyA9IGN1cnJlbnRJdGVtLnNjcm9sbFRvIHx8IHRoaXMuX29wdGlvbnMuc2Nyb2xsVG87XG5cbiAgICAgICAgaWYgKHR5cGVvZiAoY3VycmVudEl0ZW0uZGlzYWJsZUludGVyYWN0aW9uKSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICBjdXJyZW50SXRlbS5kaXNhYmxlSW50ZXJhY3Rpb24gPSB0aGlzLl9vcHRpb25zLmRpc2FibGVJbnRlcmFjdGlvbjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjdXJyZW50SXRlbS5lbGVtZW50ICE9PSBudWxsKSB7XG4gICAgICAgICAgaW50cm9JdGVtcy5wdXNoKGN1cnJlbnRJdGVtKTtcbiAgICAgICAgfSAgICAgICAgXG4gICAgICB9LmJpbmQodGhpcykpO1xuXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vdXNlIHN0ZXBzIGZyb20gZGF0YS0qIGFubm90YXRpb25zXG4gICAgICB2YXIgZWxtc0xlbmd0aCA9IGFsbEludHJvU3RlcHMubGVuZ3RoO1xuICAgICAgdmFyIGRpc2FibGVJbnRlcmFjdGlvbjtcbiAgICAgIFxuICAgICAgLy9pZiB0aGVyZSdzIG5vIGVsZW1lbnQgdG8gaW50cm9cbiAgICAgIGlmIChlbG1zTGVuZ3RoIDwgMSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIF9mb3JFYWNoKGFsbEludHJvU3RlcHMsIGZ1bmN0aW9uIChjdXJyZW50RWxlbWVudCkge1xuICAgICAgICBcbiAgICAgICAgLy8gUFIgIzgwXG4gICAgICAgIC8vIHN0YXJ0IGludHJvIGZvciBncm91cHMgb2YgZWxlbWVudHNcbiAgICAgICAgaWYgKGdyb3VwICYmIChjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoXCJkYXRhLWludHJvLWdyb3VwXCIpICE9PSBncm91cCkpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBza2lwIGhpZGRlbiBlbGVtZW50c1xuICAgICAgICBpZiAoY3VycmVudEVsZW1lbnQuc3R5bGUuZGlzcGxheSA9PT0gJ25vbmUnKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdmFyIHN0ZXAgPSBwYXJzZUludChjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RlcCcpLCAxMCk7XG5cbiAgICAgICAgaWYgKHR5cGVvZiAoY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWRpc2FibGUtaW50ZXJhY3Rpb24nKSkgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgZGlzYWJsZUludGVyYWN0aW9uID0gISFjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtZGlzYWJsZS1pbnRlcmFjdGlvbicpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGRpc2FibGVJbnRlcmFjdGlvbiA9IHRoaXMuX29wdGlvbnMuZGlzYWJsZUludGVyYWN0aW9uO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHN0ZXAgPiAwKSB7XG4gICAgICAgICAgaW50cm9JdGVtc1tzdGVwIC0gMV0gPSB7XG4gICAgICAgICAgICBlbGVtZW50OiBjdXJyZW50RWxlbWVudCxcbiAgICAgICAgICAgIGludHJvOiBjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtaW50cm8nKSxcbiAgICAgICAgICAgIHN0ZXA6IHBhcnNlSW50KGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1zdGVwJyksIDEwKSxcbiAgICAgICAgICAgIHRvb2x0aXBDbGFzczogY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXRvb2x0aXBjbGFzcycpLFxuICAgICAgICAgICAgaGlnaGxpZ2h0Q2xhc3M6IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1oaWdobGlnaHRjbGFzcycpLFxuICAgICAgICAgICAgcG9zaXRpb246IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1wb3NpdGlvbicpIHx8IHRoaXMuX29wdGlvbnMudG9vbHRpcFBvc2l0aW9uLFxuICAgICAgICAgICAgc2Nyb2xsVG86IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1zY3JvbGx0bycpIHx8IHRoaXMuX29wdGlvbnMuc2Nyb2xsVG8sXG4gICAgICAgICAgICBkaXNhYmxlSW50ZXJhY3Rpb246IGRpc2FibGVJbnRlcmFjdGlvblxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0uYmluZCh0aGlzKSk7XG5cbiAgICAgIC8vbmV4dCBhZGQgaW50cm8gaXRlbXMgd2l0aG91dCBkYXRhLXN0ZXBcbiAgICAgIC8vdG9kbzogd2UgbmVlZCBhIGNsZWFudXAgaGVyZSwgdHdvIGxvb3BzIGFyZSByZWR1bmRhbnRcbiAgICAgIHZhciBuZXh0U3RlcCA9IDA7XG5cbiAgICAgIF9mb3JFYWNoKGFsbEludHJvU3RlcHMsIGZ1bmN0aW9uIChjdXJyZW50RWxlbWVudCkge1xuICAgICAgICBcbiAgICAgICAgLy8gUFIgIzgwXG4gICAgICAgIC8vIHN0YXJ0IGludHJvIGZvciBncm91cHMgb2YgZWxlbWVudHNcbiAgICAgICAgaWYgKGdyb3VwICYmIChjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoXCJkYXRhLWludHJvLWdyb3VwXCIpICE9PSBncm91cCkpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmIChjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RlcCcpID09PSBudWxsKSB7XG5cbiAgICAgICAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgICAgICAgaWYgKHR5cGVvZiBpbnRyb0l0ZW1zW25leHRTdGVwXSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBuZXh0U3RlcCsrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gXG5cbiAgICAgICAgICBpZiAodHlwZW9mIChjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtZGlzYWJsZS1pbnRlcmFjdGlvbicpKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgIGRpc2FibGVJbnRlcmFjdGlvbiA9ICEhY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWRpc2FibGUtaW50ZXJhY3Rpb24nKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZGlzYWJsZUludGVyYWN0aW9uID0gdGhpcy5fb3B0aW9ucy5kaXNhYmxlSW50ZXJhY3Rpb247XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaW50cm9JdGVtc1tuZXh0U3RlcF0gPSB7XG4gICAgICAgICAgICBlbGVtZW50OiBjdXJyZW50RWxlbWVudCxcbiAgICAgICAgICAgIGludHJvOiBjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtaW50cm8nKSxcbiAgICAgICAgICAgIHN0ZXA6IG5leHRTdGVwICsgMSxcbiAgICAgICAgICAgIHRvb2x0aXBDbGFzczogY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXRvb2x0aXBjbGFzcycpLFxuICAgICAgICAgICAgaGlnaGxpZ2h0Q2xhc3M6IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1oaWdobGlnaHRjbGFzcycpLFxuICAgICAgICAgICAgcG9zaXRpb246IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1wb3NpdGlvbicpIHx8IHRoaXMuX29wdGlvbnMudG9vbHRpcFBvc2l0aW9uLFxuICAgICAgICAgICAgc2Nyb2xsVG86IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1zY3JvbGx0bycpIHx8IHRoaXMuX29wdGlvbnMuc2Nyb2xsVG8sXG4gICAgICAgICAgICBkaXNhYmxlSW50ZXJhY3Rpb246IGRpc2FibGVJbnRlcmFjdGlvblxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH0uYmluZCh0aGlzKSk7XG4gICAgfVxuXG4gICAgLy9yZW1vdmluZyB1bmRlZmluZWQvbnVsbCBlbGVtZW50c1xuICAgIHZhciB0ZW1wSW50cm9JdGVtcyA9IFtdO1xuICAgIGZvciAodmFyIHogPSAwOyB6IDwgaW50cm9JdGVtcy5sZW5ndGg7IHorKykge1xuICAgICAgaWYgKGludHJvSXRlbXNbel0pIHtcbiAgICAgICAgLy8gY29weSBub24tZmFsc3kgdmFsdWVzIHRvIHRoZSBlbmQgb2YgdGhlIGFycmF5XG4gICAgICAgIHRlbXBJbnRyb0l0ZW1zLnB1c2goaW50cm9JdGVtc1t6XSk7ICBcbiAgICAgIH0gXG4gICAgfVxuXG4gICAgaW50cm9JdGVtcyA9IHRlbXBJbnRyb0l0ZW1zO1xuXG4gICAgLy9Paywgc29ydCBhbGwgaXRlbXMgd2l0aCBnaXZlbiBzdGVwc1xuICAgIGludHJvSXRlbXMuc29ydChmdW5jdGlvbiAoYSwgYikge1xuICAgICAgcmV0dXJuIGEuc3RlcCAtIGIuc3RlcDtcbiAgICB9KTtcblxuICAgIC8vc2V0IGl0IHRvIHRoZSBpbnRyb0pzIG9iamVjdFxuICAgIHRoaXMuX2ludHJvSXRlbXMgPSBpbnRyb0l0ZW1zO1xuXG4gICAgLy9hZGQgb3ZlcmxheSBsYXllciB0byB0aGUgcGFnZVxuICAgIGlmKF9hZGRPdmVybGF5TGF5ZXIuY2FsbCh0aGlzLCB0YXJnZXRFbG0pKSB7XG4gICAgICAvL3RoZW4sIHN0YXJ0IHRoZSBzaG93XG4gICAgICBfbmV4dFN0ZXAuY2FsbCh0aGlzKTtcblxuICAgICAgaWYgKHRoaXMuX29wdGlvbnMua2V5Ym9hcmROYXZpZ2F0aW9uKSB7XG4gICAgICAgIERPTUV2ZW50Lm9uKHdpbmRvdywgJ2tleWRvd24nLCBfb25LZXlEb3duLCB0aGlzLCB0cnVlKTtcbiAgICAgIH1cbiAgICAgIC8vZm9yIHdpbmRvdyByZXNpemVcbiAgICAgIERPTUV2ZW50Lm9uKHdpbmRvdywgJ3Jlc2l6ZScsIF9vblJlc2l6ZSwgdGhpcywgdHJ1ZSk7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGZ1bmN0aW9uIF9vblJlc2l6ZSAoKSB7XG4gICAgdGhpcy5yZWZyZXNoLmNhbGwodGhpcyk7XG4gIH1cblxuICAvKipcbiAgKiBvbiBrZXlDb2RlOlxuICAqIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9LZXlib2FyZEV2ZW50L2tleUNvZGVcbiAgKiBUaGlzIGZlYXR1cmUgaGFzIGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBXZWIgc3RhbmRhcmRzLlxuICAqIFRob3VnaCBzb21lIGJyb3dzZXJzIG1heSBzdGlsbCBzdXBwb3J0IGl0LCBpdCBpcyBpblxuICAqIHRoZSBwcm9jZXNzIG9mIGJlaW5nIGRyb3BwZWQuXG4gICogSW5zdGVhZCwgeW91IHNob3VsZCB1c2UgS2V5Ym9hcmRFdmVudC5jb2RlLFxuICAqIGlmIGl0J3MgaW1wbGVtZW50ZWQuXG4gICpcbiAgKiBqUXVlcnkncyBhcHByb2FjaCBpcyB0byB0ZXN0IGZvclxuICAqICAgKDEpIGUud2hpY2gsIHRoZW5cbiAgKiAgICgyKSBlLmNoYXJDb2RlLCB0aGVuXG4gICogICAoMykgZS5rZXlDb2RlXG4gICogaHR0cHM6Ly9naXRodWIuY29tL2pxdWVyeS9qcXVlcnkvYmxvYi9hNmIwNzA1Mjk0ZDMzNmFlMmY2M2Y3Mjc2ZGUwZGExMTk1NDk1MzYzL3NyYy9ldmVudC5qcyNMNjM4XG4gICpcbiAgKiBAcGFyYW0gdHlwZSB2YXJcbiAgKiBAcmV0dXJuIHR5cGVcbiAgKi9cbiAgZnVuY3Rpb24gX29uS2V5RG93biAoZSkge1xuICAgIHZhciBjb2RlID0gKGUuY29kZSA9PT0gbnVsbCkgPyBlLndoaWNoIDogZS5jb2RlO1xuXG4gICAgLy8gaWYgY29kZS9lLndoaWNoIGlzIG51bGxcbiAgICBpZiAoY29kZSA9PT0gbnVsbCkge1xuICAgICAgY29kZSA9IChlLmNoYXJDb2RlID09PSBudWxsKSA/IGUua2V5Q29kZSA6IGUuY2hhckNvZGU7XG4gICAgfVxuICAgIFxuICAgIGlmICgoY29kZSA9PT0gJ0VzY2FwZScgfHwgY29kZSA9PT0gMjcpICYmIHRoaXMuX29wdGlvbnMuZXhpdE9uRXNjID09PSB0cnVlKSB7XG4gICAgICAvL2VzY2FwZSBrZXkgcHJlc3NlZCwgZXhpdCB0aGUgaW50cm9cbiAgICAgIC8vY2hlY2sgaWYgZXhpdCBjYWxsYmFjayBpcyBkZWZpbmVkXG4gICAgICBfZXhpdEludHJvLmNhbGwodGhpcywgdGhpcy5fdGFyZ2V0RWxlbWVudCk7XG4gICAgfSBlbHNlIGlmIChjb2RlID09PSAnQXJyb3dMZWZ0JyB8fCBjb2RlID09PSAzNykge1xuICAgICAgLy9sZWZ0IGFycm93XG4gICAgICBfcHJldmlvdXNTdGVwLmNhbGwodGhpcyk7XG4gICAgfSBlbHNlIGlmIChjb2RlID09PSAnQXJyb3dSaWdodCcgfHwgY29kZSA9PT0gMzkpIHtcbiAgICAgIC8vcmlnaHQgYXJyb3dcbiAgICAgIF9uZXh0U3RlcC5jYWxsKHRoaXMpO1xuICAgIH0gZWxzZSBpZiAoY29kZSA9PT0gJ0VudGVyJyB8fCBjb2RlID09PSAxMykge1xuICAgICAgLy9zcmNFbGVtZW50ID09PSBpZVxuICAgICAgdmFyIHRhcmdldCA9IGUudGFyZ2V0IHx8IGUuc3JjRWxlbWVudDtcbiAgICAgIGlmICh0YXJnZXQgJiYgdGFyZ2V0LmNsYXNzTmFtZS5tYXRjaCgnaW50cm9qcy1wcmV2YnV0dG9uJykpIHtcbiAgICAgICAgLy91c2VyIGhpdCBlbnRlciB3aGlsZSBmb2N1c2luZyBvbiBwcmV2aW91cyBidXR0b25cbiAgICAgICAgX3ByZXZpb3VzU3RlcC5jYWxsKHRoaXMpO1xuICAgICAgfSBlbHNlIGlmICh0YXJnZXQgJiYgdGFyZ2V0LmNsYXNzTmFtZS5tYXRjaCgnaW50cm9qcy1za2lwYnV0dG9uJykpIHtcbiAgICAgICAgLy91c2VyIGhpdCBlbnRlciB3aGlsZSBmb2N1c2luZyBvbiBza2lwIGJ1dHRvblxuICAgICAgICBpZiAodGhpcy5faW50cm9JdGVtcy5sZW5ndGggLSAxID09PSB0aGlzLl9jdXJyZW50U3RlcCAmJiB0eXBlb2YgKHRoaXMuX2ludHJvQ29tcGxldGVDYWxsYmFjaykgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgIHRoaXMuX2ludHJvQ29tcGxldGVDYWxsYmFjay5jYWxsKHRoaXMpO1xuICAgICAgICB9XG5cbiAgICAgICAgX2V4aXRJbnRyby5jYWxsKHRoaXMsIHRoaXMuX3RhcmdldEVsZW1lbnQpO1xuICAgICAgfSBlbHNlIGlmICh0YXJnZXQgJiYgdGFyZ2V0LmdldEF0dHJpYnV0ZSgnZGF0YS1zdGVwbnVtYmVyJykpIHtcbiAgICAgICAgLy8gdXNlciBoaXQgZW50ZXIgd2hpbGUgZm9jdXNpbmcgb24gc3RlcCBidWxsZXRcbiAgICAgICAgdGFyZ2V0LmNsaWNrKCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvL2RlZmF1bHQgYmVoYXZpb3IgZm9yIHJlc3BvbmRpbmcgdG8gZW50ZXJcbiAgICAgICAgX25leHRTdGVwLmNhbGwodGhpcyk7XG4gICAgICB9XG5cbiAgICAgIC8vcHJldmVudCBkZWZhdWx0IGJlaGF2aW91ciBvbiBoaXR0aW5nIEVudGVyLCB0byBwcmV2ZW50IHN0ZXBzIGJlaW5nIHNraXBwZWQgaW4gc29tZSBicm93c2Vyc1xuICAgICAgaWYoZS5wcmV2ZW50RGVmYXVsdCkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBlLnJldHVyblZhbHVlID0gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAvKlxuICAgKiBtYWtlcyBhIGNvcHkgb2YgdGhlIG9iamVjdFxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfY2xvbmVPYmplY3RcbiAgKi9cbiAgZnVuY3Rpb24gX2Nsb25lT2JqZWN0KG9iamVjdCkge1xuICAgICAgaWYgKG9iamVjdCA9PT0gbnVsbCB8fCB0eXBlb2YgKG9iamVjdCkgIT09ICdvYmplY3QnIHx8IHR5cGVvZiAob2JqZWN0Lm5vZGVUeXBlKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcmV0dXJuIG9iamVjdDtcbiAgICAgIH1cbiAgICAgIHZhciB0ZW1wID0ge307XG4gICAgICBmb3IgKHZhciBrZXkgaW4gb2JqZWN0KSB7XG4gICAgICAgIGlmICh0eXBlb2Yod2luZG93LmpRdWVyeSkgIT09ICd1bmRlZmluZWQnICYmIG9iamVjdFtrZXldIGluc3RhbmNlb2Ygd2luZG93LmpRdWVyeSkge1xuICAgICAgICAgIHRlbXBba2V5XSA9IG9iamVjdFtrZXldO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRlbXBba2V5XSA9IF9jbG9uZU9iamVjdChvYmplY3Rba2V5XSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB0ZW1wO1xuICB9XG4gIC8qKlxuICAgKiBHbyB0byBzcGVjaWZpYyBzdGVwIG9mIGludHJvZHVjdGlvblxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfZ29Ub1N0ZXBcbiAgICovXG4gIGZ1bmN0aW9uIF9nb1RvU3RlcChzdGVwKSB7XG4gICAgLy9iZWNhdXNlIHN0ZXBzIHN0YXJ0cyB3aXRoIHplcm9cbiAgICB0aGlzLl9jdXJyZW50U3RlcCA9IHN0ZXAgLSAyO1xuICAgIGlmICh0eXBlb2YgKHRoaXMuX2ludHJvSXRlbXMpICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgX25leHRTdGVwLmNhbGwodGhpcyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEdvIHRvIHRoZSBzcGVjaWZpYyBzdGVwIG9mIGludHJvZHVjdGlvbiB3aXRoIHRoZSBleHBsaWNpdCBbZGF0YS1zdGVwXSBudW1iZXJcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2dvVG9TdGVwTnVtYmVyXG4gICAqL1xuICBmdW5jdGlvbiBfZ29Ub1N0ZXBOdW1iZXIoc3RlcCkge1xuICAgIHRoaXMuX2N1cnJlbnRTdGVwTnVtYmVyID0gc3RlcDtcbiAgICBpZiAodHlwZW9mICh0aGlzLl9pbnRyb0l0ZW1zKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIF9uZXh0U3RlcC5jYWxsKHRoaXMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHbyB0byBuZXh0IHN0ZXAgb24gaW50cm9cbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX25leHRTdGVwXG4gICAqL1xuICBmdW5jdGlvbiBfbmV4dFN0ZXAoKSB7XG4gICAgdGhpcy5fZGlyZWN0aW9uID0gJ2ZvcndhcmQnO1xuXG4gICAgaWYgKHR5cGVvZiAodGhpcy5fY3VycmVudFN0ZXBOdW1iZXIpICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgX2ZvckVhY2godGhpcy5faW50cm9JdGVtcywgZnVuY3Rpb24gKGl0ZW0sIGkpIHtcbiAgICAgICAgaWYoIGl0ZW0uc3RlcCA9PT0gdGhpcy5fY3VycmVudFN0ZXBOdW1iZXIgKSB7XG4gICAgICAgICAgdGhpcy5fY3VycmVudFN0ZXAgPSBpIC0gMTtcbiAgICAgICAgICB0aGlzLl9jdXJyZW50U3RlcE51bWJlciA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgfS5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mICh0aGlzLl9jdXJyZW50U3RlcCkgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0aGlzLl9jdXJyZW50U3RlcCA9IDA7XG4gICAgfSBlbHNlIHtcbiAgICAgICsrdGhpcy5fY3VycmVudFN0ZXA7XG4gICAgfVxuXG4gICAgdmFyIG5leHRTdGVwID0gdGhpcy5faW50cm9JdGVtc1t0aGlzLl9jdXJyZW50U3RlcF07XG4gICAgdmFyIGNvbnRpbnVlU3RlcCA9IHRydWU7XG5cbiAgICBpZiAodHlwZW9mICh0aGlzLl9pbnRyb0JlZm9yZUNoYW5nZUNhbGxiYWNrKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGNvbnRpbnVlU3RlcCA9IHRoaXMuX2ludHJvQmVmb3JlQ2hhbmdlQ2FsbGJhY2suY2FsbCh0aGlzLCBuZXh0U3RlcC5lbGVtZW50KTtcbiAgICB9XG5cbiAgICAvLyBpZiBgb25iZWZvcmVjaGFuZ2VgIHJldHVybmVkIGBmYWxzZWAsIHN0b3AgZGlzcGxheWluZyB0aGUgZWxlbWVudFxuICAgIGlmIChjb250aW51ZVN0ZXAgPT09IGZhbHNlKSB7XG4gICAgICAtLXRoaXMuX2N1cnJlbnRTdGVwO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGlmICgodGhpcy5faW50cm9JdGVtcy5sZW5ndGgpIDw9IHRoaXMuX2N1cnJlbnRTdGVwKSB7XG4gICAgICAvL2VuZCBvZiB0aGUgaW50cm9cbiAgICAgIC8vY2hlY2sgaWYgYW55IGNhbGxiYWNrIGlzIGRlZmluZWRcbiAgICAgIGlmICh0eXBlb2YgKHRoaXMuX2ludHJvQ29tcGxldGVDYWxsYmFjaykgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdGhpcy5faW50cm9Db21wbGV0ZUNhbGxiYWNrLmNhbGwodGhpcyk7XG4gICAgICB9XG4gICAgICBfZXhpdEludHJvLmNhbGwodGhpcywgdGhpcy5fdGFyZ2V0RWxlbWVudCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgX3Nob3dFbGVtZW50LmNhbGwodGhpcywgbmV4dFN0ZXApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdvIHRvIHByZXZpb3VzIHN0ZXAgb24gaW50cm9cbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX3ByZXZpb3VzU3RlcFxuICAgKi9cbiAgZnVuY3Rpb24gX3ByZXZpb3VzU3RlcCgpIHtcbiAgICB0aGlzLl9kaXJlY3Rpb24gPSAnYmFja3dhcmQnO1xuXG4gICAgaWYgKHRoaXMuX2N1cnJlbnRTdGVwID09PSAwKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLS10aGlzLl9jdXJyZW50U3RlcDtcblxuICAgIHZhciBuZXh0U3RlcCA9IHRoaXMuX2ludHJvSXRlbXNbdGhpcy5fY3VycmVudFN0ZXBdO1xuICAgIHZhciBjb250aW51ZVN0ZXAgPSB0cnVlO1xuXG4gICAgaWYgKHR5cGVvZiAodGhpcy5faW50cm9CZWZvcmVDaGFuZ2VDYWxsYmFjaykgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBjb250aW51ZVN0ZXAgPSB0aGlzLl9pbnRyb0JlZm9yZUNoYW5nZUNhbGxiYWNrLmNhbGwodGhpcywgbmV4dFN0ZXAuZWxlbWVudCk7XG4gICAgfVxuXG4gICAgLy8gaWYgYG9uYmVmb3JlY2hhbmdlYCByZXR1cm5lZCBgZmFsc2VgLCBzdG9wIGRpc3BsYXlpbmcgdGhlIGVsZW1lbnRcbiAgICBpZiAoY29udGludWVTdGVwID09PSBmYWxzZSkge1xuICAgICAgKyt0aGlzLl9jdXJyZW50U3RlcDtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBfc2hvd0VsZW1lbnQuY2FsbCh0aGlzLCBuZXh0U3RlcCk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIHBsYWNlbWVudCBvZiB0aGUgaW50cm8gb2JqZWN0cyBvbiB0aGUgc2NyZWVuXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKi9cbiAgZnVuY3Rpb24gX3JlZnJlc2goKSB7XG4gICAgLy8gcmUtYWxpZ24gaW50cm9zXG4gICAgX3NldEhlbHBlckxheWVyUG9zaXRpb24uY2FsbCh0aGlzLCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oZWxwZXJMYXllcicpKTtcbiAgICBfc2V0SGVscGVyTGF5ZXJQb3NpdGlvbi5jYWxsKHRoaXMsIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLXRvb2x0aXBSZWZlcmVuY2VMYXllcicpKTtcbiAgICBfc2V0SGVscGVyTGF5ZXJQb3NpdGlvbi5jYWxsKHRoaXMsIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWRpc2FibGVJbnRlcmFjdGlvbicpKTtcblxuICAgIC8vIHJlLWFsaWduIHRvb2x0aXBcbiAgICBpZih0aGlzLl9jdXJyZW50U3RlcCAhPT0gdW5kZWZpbmVkICYmIHRoaXMuX2N1cnJlbnRTdGVwICE9PSBudWxsKSB7XG4gICAgICB2YXIgb2xkSGVscGVyTnVtYmVyTGF5ZXIgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oZWxwZXJOdW1iZXJMYXllcicpLFxuICAgICAgICBvbGRBcnJvd0xheWVyICAgICAgICA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWFycm93JyksXG4gICAgICAgIG9sZHRvb2x0aXBDb250YWluZXIgID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmludHJvanMtdG9vbHRpcCcpO1xuICAgICAgX3BsYWNlVG9vbHRpcC5jYWxsKHRoaXMsIHRoaXMuX2ludHJvSXRlbXNbdGhpcy5fY3VycmVudFN0ZXBdLmVsZW1lbnQsIG9sZHRvb2x0aXBDb250YWluZXIsIG9sZEFycm93TGF5ZXIsIG9sZEhlbHBlck51bWJlckxheWVyKTtcbiAgICB9XG5cbiAgICAvL3JlLWFsaWduIGhpbnRzXG4gICAgX3JlQWxpZ25IaW50cy5jYWxsKHRoaXMpO1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4aXQgZnJvbSBpbnRyb1xuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfZXhpdEludHJvXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB0YXJnZXRFbGVtZW50XG4gICAqIEBwYXJhbSB7Qm9vbGVhbn0gZm9yY2UgLSBTZXR0aW5nIHRvIGB0cnVlYCB3aWxsIHNraXAgdGhlIHJlc3VsdCBvZiBiZWZvcmVFeGl0IGNhbGxiYWNrXG4gICAqL1xuICBmdW5jdGlvbiBfZXhpdEludHJvKHRhcmdldEVsZW1lbnQsIGZvcmNlKSB7XG4gICAgdmFyIGNvbnRpbnVlRXhpdCA9IHRydWU7XG5cbiAgICAvLyBjYWxsaW5nIG9uYmVmb3JlZXhpdCBjYWxsYmFja1xuICAgIC8vXG4gICAgLy8gSWYgdGhpcyBjYWxsYmFjayByZXR1cm4gYGZhbHNlYCwgaXQgd291bGQgaGFsdCB0aGUgcHJvY2Vzc1xuICAgIGlmICh0aGlzLl9pbnRyb0JlZm9yZUV4aXRDYWxsYmFjayAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb250aW51ZUV4aXQgPSB0aGlzLl9pbnRyb0JlZm9yZUV4aXRDYWxsYmFjay5jYWxsKHRoaXMpO1xuICAgIH1cblxuICAgIC8vIHNraXAgdGhpcyBjaGVjayBpZiBgZm9yY2VgIHBhcmFtZXRlciBpcyBgdHJ1ZWBcbiAgICAvLyBvdGhlcndpc2UsIGlmIGBvbmJlZm9yZWV4aXRgIHJldHVybmVkIGBmYWxzZWAsIGRvbid0IGV4aXQgdGhlIGludHJvXG4gICAgaWYgKCFmb3JjZSAmJiBjb250aW51ZUV4aXQgPT09IGZhbHNlKSByZXR1cm47XG5cbiAgICAvL3JlbW92ZSBvdmVybGF5IGxheWVycyBmcm9tIHRoZSBwYWdlXG4gICAgdmFyIG92ZXJsYXlMYXllcnMgPSB0YXJnZXRFbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLW92ZXJsYXknKTtcblxuICAgIGlmIChvdmVybGF5TGF5ZXJzICYmIG92ZXJsYXlMYXllcnMubGVuZ3RoKSB7XG4gICAgICBfZm9yRWFjaChvdmVybGF5TGF5ZXJzLCBmdW5jdGlvbiAob3ZlcmxheUxheWVyKSB7XG4gICAgICAgIG92ZXJsYXlMYXllci5zdHlsZS5vcGFjaXR5ID0gMDtcbiAgICAgICAgd2luZG93LnNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGlmICh0aGlzLnBhcmVudE5vZGUpIHtcbiAgICAgICAgICAgIHRoaXMucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCh0aGlzKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0uYmluZChvdmVybGF5TGF5ZXIpLCA1MDApO1xuICAgICAgfS5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICAvL3JlbW92ZSBhbGwgaGVscGVyIGxheWVyc1xuICAgIHZhciBoZWxwZXJMYXllciA9IHRhcmdldEVsZW1lbnQucXVlcnlTZWxlY3RvcignLmludHJvanMtaGVscGVyTGF5ZXInKTtcbiAgICBpZiAoaGVscGVyTGF5ZXIpIHtcbiAgICAgIGhlbHBlckxheWVyLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoaGVscGVyTGF5ZXIpO1xuICAgIH1cblxuICAgIHZhciByZWZlcmVuY2VMYXllciA9IHRhcmdldEVsZW1lbnQucXVlcnlTZWxlY3RvcignLmludHJvanMtdG9vbHRpcFJlZmVyZW5jZUxheWVyJyk7XG4gICAgaWYgKHJlZmVyZW5jZUxheWVyKSB7XG4gICAgICByZWZlcmVuY2VMYXllci5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKHJlZmVyZW5jZUxheWVyKTtcbiAgICB9XG5cbiAgICAvL3JlbW92ZSBkaXNhYmxlSW50ZXJhY3Rpb25MYXllclxuICAgIHZhciBkaXNhYmxlSW50ZXJhY3Rpb25MYXllciA9IHRhcmdldEVsZW1lbnQucXVlcnlTZWxlY3RvcignLmludHJvanMtZGlzYWJsZUludGVyYWN0aW9uJyk7XG4gICAgaWYgKGRpc2FibGVJbnRlcmFjdGlvbkxheWVyKSB7XG4gICAgICBkaXNhYmxlSW50ZXJhY3Rpb25MYXllci5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGRpc2FibGVJbnRlcmFjdGlvbkxheWVyKTtcbiAgICB9XG5cbiAgICAvL3JlbW92ZSBpbnRybyBmbG9hdGluZyBlbGVtZW50XG4gICAgdmFyIGZsb2F0aW5nRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzRmxvYXRpbmdFbGVtZW50Jyk7XG4gICAgaWYgKGZsb2F0aW5nRWxlbWVudCkge1xuICAgICAgZmxvYXRpbmdFbGVtZW50LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoZmxvYXRpbmdFbGVtZW50KTtcbiAgICB9XG5cbiAgICBfcmVtb3ZlU2hvd0VsZW1lbnQoKTtcblxuICAgIC8vcmVtb3ZlIGBpbnRyb2pzLWZpeFBhcmVudGAgY2xhc3MgZnJvbSB0aGUgZWxlbWVudHNcbiAgICB2YXIgZml4UGFyZW50cyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLWZpeFBhcmVudCcpO1xuICAgIF9mb3JFYWNoKGZpeFBhcmVudHMsIGZ1bmN0aW9uIChwYXJlbnQpIHtcbiAgICAgIF9yZW1vdmVDbGFzcyhwYXJlbnQsIC9pbnRyb2pzLWZpeFBhcmVudC9nKTtcbiAgICB9KTtcblxuICAgIC8vY2xlYW4gbGlzdGVuZXJzXG4gICAgRE9NRXZlbnQub2ZmKHdpbmRvdywgJ2tleWRvd24nLCBfb25LZXlEb3duLCB0aGlzLCB0cnVlKTtcbiAgICBET01FdmVudC5vZmYod2luZG93LCAncmVzaXplJywgX29uUmVzaXplLCB0aGlzLCB0cnVlKTtcblxuICAgIC8vY2hlY2sgaWYgYW55IGNhbGxiYWNrIGlzIGRlZmluZWRcbiAgICBpZiAodGhpcy5faW50cm9FeGl0Q2FsbGJhY2sgIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5faW50cm9FeGl0Q2FsbGJhY2suY2FsbCh0aGlzKTtcbiAgICB9XG5cbiAgICAvL3NldCB0aGUgc3RlcCB0byB6ZXJvXG4gICAgdGhpcy5fY3VycmVudFN0ZXAgPSB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogUmVuZGVyIHRvb2x0aXAgYm94IGluIHRoZSBwYWdlXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9wbGFjZVRvb2x0aXBcbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gdGFyZ2V0RWxlbWVudFxuICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSB0b29sdGlwTGF5ZXJcbiAgICogQHBhcmFtIHtIVE1MRWxlbWVudH0gYXJyb3dMYXllclxuICAgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBoZWxwZXJOdW1iZXJMYXllclxuICAgKiBAcGFyYW0ge0Jvb2xlYW59IGhpbnRNb2RlXG4gICAqL1xuICBmdW5jdGlvbiBfcGxhY2VUb29sdGlwKHRhcmdldEVsZW1lbnQsIHRvb2x0aXBMYXllciwgYXJyb3dMYXllciwgaGVscGVyTnVtYmVyTGF5ZXIsIGhpbnRNb2RlKSB7XG4gICAgdmFyIHRvb2x0aXBDc3NDbGFzcyA9ICcnLFxuICAgICAgICBjdXJyZW50U3RlcE9iaixcbiAgICAgICAgdG9vbHRpcE9mZnNldCxcbiAgICAgICAgdGFyZ2V0T2Zmc2V0LFxuICAgICAgICB3aW5kb3dTaXplLFxuICAgICAgICBjdXJyZW50VG9vbHRpcFBvc2l0aW9uO1xuXG4gICAgaGludE1vZGUgPSBoaW50TW9kZSB8fCBmYWxzZTtcblxuICAgIC8vcmVzZXQgdGhlIG9sZCBzdHlsZVxuICAgIHRvb2x0aXBMYXllci5zdHlsZS50b3AgICAgICAgID0gbnVsbDtcbiAgICB0b29sdGlwTGF5ZXIuc3R5bGUucmlnaHQgICAgICA9IG51bGw7XG4gICAgdG9vbHRpcExheWVyLnN0eWxlLmJvdHRvbSAgICAgPSBudWxsO1xuICAgIHRvb2x0aXBMYXllci5zdHlsZS5sZWZ0ICAgICAgID0gbnVsbDtcbiAgICB0b29sdGlwTGF5ZXIuc3R5bGUubWFyZ2luTGVmdCA9IG51bGw7XG4gICAgdG9vbHRpcExheWVyLnN0eWxlLm1hcmdpblRvcCAgPSBudWxsO1xuXG4gICAgYXJyb3dMYXllci5zdHlsZS5kaXNwbGF5ID0gJ2luaGVyaXQnO1xuXG4gICAgaWYgKHR5cGVvZihoZWxwZXJOdW1iZXJMYXllcikgIT09ICd1bmRlZmluZWQnICYmIGhlbHBlck51bWJlckxheWVyICE9PSBudWxsKSB7XG4gICAgICBoZWxwZXJOdW1iZXJMYXllci5zdHlsZS50b3AgID0gbnVsbDtcbiAgICAgIGhlbHBlck51bWJlckxheWVyLnN0eWxlLmxlZnQgPSBudWxsO1xuICAgIH1cblxuICAgIC8vcHJldmVudCBlcnJvciB3aGVuIGB0aGlzLl9jdXJyZW50U3RlcGAgaXMgdW5kZWZpbmVkXG4gICAgaWYgKCF0aGlzLl9pbnRyb0l0ZW1zW3RoaXMuX2N1cnJlbnRTdGVwXSkgcmV0dXJuO1xuXG4gICAgLy9pZiB3ZSBoYXZlIGEgY3VzdG9tIGNzcyBjbGFzcyBmb3IgZWFjaCBzdGVwXG4gICAgY3VycmVudFN0ZXBPYmogPSB0aGlzLl9pbnRyb0l0ZW1zW3RoaXMuX2N1cnJlbnRTdGVwXTtcbiAgICBpZiAodHlwZW9mIChjdXJyZW50U3RlcE9iai50b29sdGlwQ2xhc3MpID09PSAnc3RyaW5nJykge1xuICAgICAgdG9vbHRpcENzc0NsYXNzID0gY3VycmVudFN0ZXBPYmoudG9vbHRpcENsYXNzO1xuICAgIH0gZWxzZSB7XG4gICAgICB0b29sdGlwQ3NzQ2xhc3MgPSB0aGlzLl9vcHRpb25zLnRvb2x0aXBDbGFzcztcbiAgICB9XG5cbiAgICB0b29sdGlwTGF5ZXIuY2xhc3NOYW1lID0gKCdpbnRyb2pzLXRvb2x0aXAgJyArIHRvb2x0aXBDc3NDbGFzcykucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpO1xuICAgIHRvb2x0aXBMYXllci5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnZGlhbG9nJyk7XG5cbiAgICBjdXJyZW50VG9vbHRpcFBvc2l0aW9uID0gdGhpcy5faW50cm9JdGVtc1t0aGlzLl9jdXJyZW50U3RlcF0ucG9zaXRpb247XG5cbiAgICAvLyBGbG9hdGluZyBpcyBhbHdheXMgdmFsaWQsIG5vIHBvaW50IGluIGNhbGN1bGF0aW5nXG4gICAgaWYgKGN1cnJlbnRUb29sdGlwUG9zaXRpb24gIT09IFwiZmxvYXRpbmdcIikgeyBcbiAgICAgIGN1cnJlbnRUb29sdGlwUG9zaXRpb24gPSBfZGV0ZXJtaW5lQXV0b1Bvc2l0aW9uLmNhbGwodGhpcywgdGFyZ2V0RWxlbWVudCwgdG9vbHRpcExheWVyLCBjdXJyZW50VG9vbHRpcFBvc2l0aW9uKTtcbiAgICB9XG5cbiAgICB2YXIgdG9vbHRpcExheWVyU3R5bGVMZWZ0O1xuICAgIHRhcmdldE9mZnNldCAgPSBfZ2V0T2Zmc2V0KHRhcmdldEVsZW1lbnQpO1xuICAgIHRvb2x0aXBPZmZzZXQgPSBfZ2V0T2Zmc2V0KHRvb2x0aXBMYXllcik7XG4gICAgd2luZG93U2l6ZSAgICA9IF9nZXRXaW5TaXplKCk7XG5cbiAgICBfYWRkQ2xhc3ModG9vbHRpcExheWVyLCAnaW50cm9qcy0nICsgY3VycmVudFRvb2x0aXBQb3NpdGlvbik7XG5cbiAgICBzd2l0Y2ggKGN1cnJlbnRUb29sdGlwUG9zaXRpb24pIHtcbiAgICAgIGNhc2UgJ3RvcC1yaWdodC1hbGlnbmVkJzpcbiAgICAgICAgYXJyb3dMYXllci5jbGFzc05hbWUgICAgICA9ICdpbnRyb2pzLWFycm93IGJvdHRvbS1yaWdodCc7XG5cbiAgICAgICAgdmFyIHRvb2x0aXBMYXllclN0eWxlUmlnaHQgPSAwO1xuICAgICAgICBfY2hlY2tMZWZ0KHRhcmdldE9mZnNldCwgdG9vbHRpcExheWVyU3R5bGVSaWdodCwgdG9vbHRpcE9mZnNldCwgdG9vbHRpcExheWVyKTtcbiAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLmJvdHRvbSAgICA9ICh0YXJnZXRPZmZzZXQuaGVpZ2h0ICsgIDIwKSArICdweCc7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlICd0b3AtbWlkZGxlLWFsaWduZWQnOlxuICAgICAgICBhcnJvd0xheWVyLmNsYXNzTmFtZSAgICAgID0gJ2ludHJvanMtYXJyb3cgYm90dG9tLW1pZGRsZSc7XG5cbiAgICAgICAgdmFyIHRvb2x0aXBMYXllclN0eWxlTGVmdFJpZ2h0ID0gdGFyZ2V0T2Zmc2V0LndpZHRoIC8gMiAtIHRvb2x0aXBPZmZzZXQud2lkdGggLyAyO1xuXG4gICAgICAgIC8vIGEgZml4IGZvciBtaWRkbGUgYWxpZ25lZCBoaW50c1xuICAgICAgICBpZiAoaGludE1vZGUpIHtcbiAgICAgICAgICB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCArPSA1O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKF9jaGVja0xlZnQodGFyZ2V0T2Zmc2V0LCB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCwgdG9vbHRpcE9mZnNldCwgdG9vbHRpcExheWVyKSkge1xuICAgICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS5yaWdodCA9IG51bGw7XG4gICAgICAgICAgX2NoZWNrUmlnaHQodGFyZ2V0T2Zmc2V0LCB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCwgdG9vbHRpcE9mZnNldCwgd2luZG93U2l6ZSwgdG9vbHRpcExheWVyKTtcbiAgICAgICAgfVxuICAgICAgICB0b29sdGlwTGF5ZXIuc3R5bGUuYm90dG9tID0gKHRhcmdldE9mZnNldC5oZWlnaHQgKyAyMCkgKyAncHgnO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSAndG9wLWxlZnQtYWxpZ25lZCc6XG4gICAgICAvLyB0b3AtbGVmdC1hbGlnbmVkIGlzIHRoZSBzYW1lIGFzIHRoZSBkZWZhdWx0IHRvcFxuICAgICAgY2FzZSAndG9wJzpcbiAgICAgICAgYXJyb3dMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1hcnJvdyBib3R0b20nO1xuXG4gICAgICAgIHRvb2x0aXBMYXllclN0eWxlTGVmdCA9IChoaW50TW9kZSkgPyAwIDogMTU7XG5cbiAgICAgICAgX2NoZWNrUmlnaHQodGFyZ2V0T2Zmc2V0LCB0b29sdGlwTGF5ZXJTdHlsZUxlZnQsIHRvb2x0aXBPZmZzZXQsIHdpbmRvd1NpemUsIHRvb2x0aXBMYXllcik7XG4gICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS5ib3R0b20gPSAodGFyZ2V0T2Zmc2V0LmhlaWdodCArICAyMCkgKyAncHgnO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3JpZ2h0JzpcbiAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLmxlZnQgPSAodGFyZ2V0T2Zmc2V0LndpZHRoICsgMjApICsgJ3B4JztcbiAgICAgICAgaWYgKHRhcmdldE9mZnNldC50b3AgKyB0b29sdGlwT2Zmc2V0LmhlaWdodCA+IHdpbmRvd1NpemUuaGVpZ2h0KSB7XG4gICAgICAgICAgLy8gSW4gdGhpcyBjYXNlLCByaWdodCB3b3VsZCBoYXZlIGZhbGxlbiBiZWxvdyB0aGUgYm90dG9tIG9mIHRoZSBzY3JlZW4uXG4gICAgICAgICAgLy8gTW9kaWZ5IHNvIHRoYXQgdGhlIGJvdHRvbSBvZiB0aGUgdG9vbHRpcCBjb25uZWN0cyB3aXRoIHRoZSB0YXJnZXRcbiAgICAgICAgICBhcnJvd0xheWVyLmNsYXNzTmFtZSA9IFwiaW50cm9qcy1hcnJvdyBsZWZ0LWJvdHRvbVwiO1xuICAgICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS50b3AgPSBcIi1cIiArICh0b29sdGlwT2Zmc2V0LmhlaWdodCAtIHRhcmdldE9mZnNldC5oZWlnaHQgLSAyMCkgKyBcInB4XCI7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYXJyb3dMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1hcnJvdyBsZWZ0JztcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2xlZnQnOlxuICAgICAgICBpZiAoIWhpbnRNb2RlICYmIHRoaXMuX29wdGlvbnMuc2hvd1N0ZXBOdW1iZXJzID09PSB0cnVlKSB7XG4gICAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLnRvcCA9ICcxNXB4JztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0YXJnZXRPZmZzZXQudG9wICsgdG9vbHRpcE9mZnNldC5oZWlnaHQgPiB3aW5kb3dTaXplLmhlaWdodCkge1xuICAgICAgICAgIC8vIEluIHRoaXMgY2FzZSwgbGVmdCB3b3VsZCBoYXZlIGZhbGxlbiBiZWxvdyB0aGUgYm90dG9tIG9mIHRoZSBzY3JlZW4uXG4gICAgICAgICAgLy8gTW9kaWZ5IHNvIHRoYXQgdGhlIGJvdHRvbSBvZiB0aGUgdG9vbHRpcCBjb25uZWN0cyB3aXRoIHRoZSB0YXJnZXRcbiAgICAgICAgICB0b29sdGlwTGF5ZXIuc3R5bGUudG9wID0gXCItXCIgKyAodG9vbHRpcE9mZnNldC5oZWlnaHQgLSB0YXJnZXRPZmZzZXQuaGVpZ2h0IC0gMjApICsgXCJweFwiO1xuICAgICAgICAgIGFycm93TGF5ZXIuY2xhc3NOYW1lID0gJ2ludHJvanMtYXJyb3cgcmlnaHQtYm90dG9tJztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBhcnJvd0xheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLWFycm93IHJpZ2h0JztcbiAgICAgICAgfVxuICAgICAgICB0b29sdGlwTGF5ZXIuc3R5bGUucmlnaHQgPSAodGFyZ2V0T2Zmc2V0LndpZHRoICsgMjApICsgJ3B4JztcblxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2Zsb2F0aW5nJzpcbiAgICAgICAgYXJyb3dMYXllci5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuXG4gICAgICAgIC8vd2UgaGF2ZSB0byBhZGp1c3QgdGhlIHRvcCBhbmQgbGVmdCBvZiBsYXllciBtYW51YWxseSBmb3IgaW50cm8gaXRlbXMgd2l0aG91dCBlbGVtZW50XG4gICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS5sZWZ0ICAgPSAnNTAlJztcbiAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLnRvcCAgICA9ICc1MCUnO1xuICAgICAgICB0b29sdGlwTGF5ZXIuc3R5bGUubWFyZ2luTGVmdCA9ICctJyArICh0b29sdGlwT2Zmc2V0LndpZHRoIC8gMikgICsgJ3B4JztcbiAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLm1hcmdpblRvcCAgPSAnLScgKyAodG9vbHRpcE9mZnNldC5oZWlnaHQgLyAyKSArICdweCc7XG5cbiAgICAgICAgaWYgKHR5cGVvZihoZWxwZXJOdW1iZXJMYXllcikgIT09ICd1bmRlZmluZWQnICYmIGhlbHBlck51bWJlckxheWVyICE9PSBudWxsKSB7XG4gICAgICAgICAgaGVscGVyTnVtYmVyTGF5ZXIuc3R5bGUubGVmdCA9ICctJyArICgodG9vbHRpcE9mZnNldC53aWR0aCAvIDIpICsgMTgpICsgJ3B4JztcbiAgICAgICAgICBoZWxwZXJOdW1iZXJMYXllci5zdHlsZS50b3AgID0gJy0nICsgKCh0b29sdGlwT2Zmc2V0LmhlaWdodCAvIDIpICsgMTgpICsgJ3B4JztcbiAgICAgICAgfVxuXG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnYm90dG9tLXJpZ2h0LWFsaWduZWQnOlxuICAgICAgICBhcnJvd0xheWVyLmNsYXNzTmFtZSAgICAgID0gJ2ludHJvanMtYXJyb3cgdG9wLXJpZ2h0JztcblxuICAgICAgICB0b29sdGlwTGF5ZXJTdHlsZVJpZ2h0ID0gMDtcbiAgICAgICAgX2NoZWNrTGVmdCh0YXJnZXRPZmZzZXQsIHRvb2x0aXBMYXllclN0eWxlUmlnaHQsIHRvb2x0aXBPZmZzZXQsIHRvb2x0aXBMYXllcik7XG4gICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS50b3AgICAgPSAodGFyZ2V0T2Zmc2V0LmhlaWdodCArICAyMCkgKyAncHgnO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSAnYm90dG9tLW1pZGRsZS1hbGlnbmVkJzpcbiAgICAgICAgYXJyb3dMYXllci5jbGFzc05hbWUgICAgICA9ICdpbnRyb2pzLWFycm93IHRvcC1taWRkbGUnO1xuXG4gICAgICAgIHRvb2x0aXBMYXllclN0eWxlTGVmdFJpZ2h0ID0gdGFyZ2V0T2Zmc2V0LndpZHRoIC8gMiAtIHRvb2x0aXBPZmZzZXQud2lkdGggLyAyO1xuXG4gICAgICAgIC8vIGEgZml4IGZvciBtaWRkbGUgYWxpZ25lZCBoaW50c1xuICAgICAgICBpZiAoaGludE1vZGUpIHtcbiAgICAgICAgICB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCArPSA1O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKF9jaGVja0xlZnQodGFyZ2V0T2Zmc2V0LCB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCwgdG9vbHRpcE9mZnNldCwgdG9vbHRpcExheWVyKSkge1xuICAgICAgICAgIHRvb2x0aXBMYXllci5zdHlsZS5yaWdodCA9IG51bGw7XG4gICAgICAgICAgX2NoZWNrUmlnaHQodGFyZ2V0T2Zmc2V0LCB0b29sdGlwTGF5ZXJTdHlsZUxlZnRSaWdodCwgdG9vbHRpcE9mZnNldCwgd2luZG93U2l6ZSwgdG9vbHRpcExheWVyKTtcbiAgICAgICAgfVxuICAgICAgICB0b29sdGlwTGF5ZXIuc3R5bGUudG9wID0gKHRhcmdldE9mZnNldC5oZWlnaHQgKyAyMCkgKyAncHgnO1xuICAgICAgICBicmVhaztcblxuICAgICAgLy8gY2FzZSAnYm90dG9tLWxlZnQtYWxpZ25lZCc6XG4gICAgICAvLyBCb3R0b20tbGVmdC1hbGlnbmVkIGlzIHRoZSBzYW1lIGFzIHRoZSBkZWZhdWx0IGJvdHRvbVxuICAgICAgLy8gY2FzZSAnYm90dG9tJzpcbiAgICAgIC8vIEJvdHRvbSBnb2luZyB0byBmb2xsb3cgdGhlIGRlZmF1bHQgYmVoYXZpb3JcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGFycm93TGF5ZXIuY2xhc3NOYW1lID0gJ2ludHJvanMtYXJyb3cgdG9wJztcblxuICAgICAgICB0b29sdGlwTGF5ZXJTdHlsZUxlZnQgPSAwO1xuICAgICAgICBfY2hlY2tSaWdodCh0YXJnZXRPZmZzZXQsIHRvb2x0aXBMYXllclN0eWxlTGVmdCwgdG9vbHRpcE9mZnNldCwgd2luZG93U2l6ZSwgdG9vbHRpcExheWVyKTtcbiAgICAgICAgdG9vbHRpcExheWVyLnN0eWxlLnRvcCAgICA9ICh0YXJnZXRPZmZzZXQuaGVpZ2h0ICsgIDIwKSArICdweCc7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNldCB0b29sdGlwIGxlZnQgc28gaXQgZG9lc24ndCBnbyBvZmYgdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIHdpbmRvd1xuICAgKlxuICAgKiBAcmV0dXJuIGJvb2xlYW4gdHJ1ZSwgaWYgdG9vbHRpcExheWVyU3R5bGVMZWZ0IGlzIG9rLiAgZmFsc2UsIG90aGVyd2lzZS5cbiAgICovXG4gIGZ1bmN0aW9uIF9jaGVja1JpZ2h0KHRhcmdldE9mZnNldCwgdG9vbHRpcExheWVyU3R5bGVMZWZ0LCB0b29sdGlwT2Zmc2V0LCB3aW5kb3dTaXplLCB0b29sdGlwTGF5ZXIpIHtcbiAgICBpZiAodGFyZ2V0T2Zmc2V0LmxlZnQgKyB0b29sdGlwTGF5ZXJTdHlsZUxlZnQgKyB0b29sdGlwT2Zmc2V0LndpZHRoID4gd2luZG93U2l6ZS53aWR0aCkge1xuICAgICAgLy8gb2ZmIHRoZSByaWdodCBzaWRlIG9mIHRoZSB3aW5kb3dcbiAgICAgIHRvb2x0aXBMYXllci5zdHlsZS5sZWZ0ID0gKHdpbmRvd1NpemUud2lkdGggLSB0b29sdGlwT2Zmc2V0LndpZHRoIC0gdGFyZ2V0T2Zmc2V0LmxlZnQpICsgJ3B4JztcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgdG9vbHRpcExheWVyLnN0eWxlLmxlZnQgPSB0b29sdGlwTGF5ZXJTdHlsZUxlZnQgKyAncHgnO1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCB0b29sdGlwIHJpZ2h0IHNvIGl0IGRvZXNuJ3QgZ28gb2ZmIHRoZSBsZWZ0IHNpZGUgb2YgdGhlIHdpbmRvd1xuICAgKlxuICAgKiBAcmV0dXJuIGJvb2xlYW4gdHJ1ZSwgaWYgdG9vbHRpcExheWVyU3R5bGVSaWdodCBpcyBvay4gIGZhbHNlLCBvdGhlcndpc2UuXG4gICAqL1xuICBmdW5jdGlvbiBfY2hlY2tMZWZ0KHRhcmdldE9mZnNldCwgdG9vbHRpcExheWVyU3R5bGVSaWdodCwgdG9vbHRpcE9mZnNldCwgdG9vbHRpcExheWVyKSB7XG4gICAgaWYgKHRhcmdldE9mZnNldC5sZWZ0ICsgdGFyZ2V0T2Zmc2V0LndpZHRoIC0gdG9vbHRpcExheWVyU3R5bGVSaWdodCAtIHRvb2x0aXBPZmZzZXQud2lkdGggPCAwKSB7XG4gICAgICAvLyBvZmYgdGhlIGxlZnQgc2lkZSBvZiB0aGUgd2luZG93XG4gICAgICB0b29sdGlwTGF5ZXIuc3R5bGUubGVmdCA9ICgtdGFyZ2V0T2Zmc2V0LmxlZnQpICsgJ3B4JztcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgdG9vbHRpcExheWVyLnN0eWxlLnJpZ2h0ID0gdG9vbHRpcExheWVyU3R5bGVSaWdodCArICdweCc7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB0aGUgcG9zaXRpb24gb2YgdGhlIHRvb2x0aXAgYmFzZWQgb24gdGhlIHBvc2l0aW9uIHByZWNlZGVuY2UgYW5kIGF2YWlsYWJpbGl0eVxuICAgKiBvZiBzY3JlZW4gc3BhY2UuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSAgICB0YXJnZXRFbGVtZW50XG4gICAqIEBwYXJhbSB7T2JqZWN0fSAgICB0b29sdGlwTGF5ZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9ICAgIGRlc2lyZWRUb29sdGlwUG9zaXRpb25cbiAgICogQHJldHVybiB7U3RyaW5nfSAgIGNhbGN1bGF0ZWRQb3NpdGlvblxuICAgKi9cbiAgZnVuY3Rpb24gX2RldGVybWluZUF1dG9Qb3NpdGlvbih0YXJnZXRFbGVtZW50LCB0b29sdGlwTGF5ZXIsIGRlc2lyZWRUb29sdGlwUG9zaXRpb24pIHtcblxuICAgIC8vIFRha2UgYSBjbG9uZSBvZiBwb3NpdGlvbiBwcmVjZWRlbmNlLiBUaGVzZSB3aWxsIGJlIHRoZSBhdmFpbGFibGVcbiAgICB2YXIgcG9zc2libGVQb3NpdGlvbnMgPSB0aGlzLl9vcHRpb25zLnBvc2l0aW9uUHJlY2VkZW5jZS5zbGljZSgpO1xuXG4gICAgdmFyIHdpbmRvd1NpemUgPSBfZ2V0V2luU2l6ZSgpO1xuICAgIHZhciB0b29sdGlwSGVpZ2h0ID0gX2dldE9mZnNldCh0b29sdGlwTGF5ZXIpLmhlaWdodCArIDEwO1xuICAgIHZhciB0b29sdGlwV2lkdGggPSBfZ2V0T2Zmc2V0KHRvb2x0aXBMYXllcikud2lkdGggKyAyMDtcbiAgICB2YXIgdGFyZ2V0RWxlbWVudFJlY3QgPSB0YXJnZXRFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG4gICAgLy8gSWYgd2UgY2hlY2sgYWxsIHRoZSBwb3NzaWJsZSBhcmVhcywgYW5kIHRoZXJlIGFyZSBubyB2YWxpZCBwbGFjZXMgZm9yIHRoZSB0b29sdGlwLCB0aGUgZWxlbWVudFxuICAgIC8vIG11c3QgdGFrZSB1cCBtb3N0IG9mIHRoZSBzY3JlZW4gcmVhbCBlc3RhdGUuIFNob3cgdGhlIHRvb2x0aXAgZmxvYXRpbmcgaW4gdGhlIG1pZGRsZSBvZiB0aGUgc2NyZWVuLlxuICAgIHZhciBjYWxjdWxhdGVkUG9zaXRpb24gPSBcImZsb2F0aW5nXCI7XG5cbiAgICAvKlxuICAgICogYXV0byBkZXRlcm1pbmUgcG9zaXRpb24gXG4gICAgKi9cblxuICAgIC8vIENoZWNrIGZvciBzcGFjZSBiZWxvd1xuICAgIGlmICh0YXJnZXRFbGVtZW50UmVjdC5ib3R0b20gKyB0b29sdGlwSGVpZ2h0ICsgdG9vbHRpcEhlaWdodCA+IHdpbmRvd1NpemUuaGVpZ2h0KSB7XG4gICAgICBfcmVtb3ZlRW50cnkocG9zc2libGVQb3NpdGlvbnMsIFwiYm90dG9tXCIpO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGZvciBzcGFjZSBhYm92ZVxuICAgIGlmICh0YXJnZXRFbGVtZW50UmVjdC50b3AgLSB0b29sdGlwSGVpZ2h0IDwgMCkge1xuICAgICAgX3JlbW92ZUVudHJ5KHBvc3NpYmxlUG9zaXRpb25zLCBcInRvcFwiKTtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBmb3Igc3BhY2UgdG8gdGhlIHJpZ2h0XG4gICAgaWYgKHRhcmdldEVsZW1lbnRSZWN0LnJpZ2h0ICsgdG9vbHRpcFdpZHRoID4gd2luZG93U2l6ZS53aWR0aCkge1xuICAgICAgX3JlbW92ZUVudHJ5KHBvc3NpYmxlUG9zaXRpb25zLCBcInJpZ2h0XCIpO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGZvciBzcGFjZSB0byB0aGUgbGVmdFxuICAgIGlmICh0YXJnZXRFbGVtZW50UmVjdC5sZWZ0IC0gdG9vbHRpcFdpZHRoIDwgMCkge1xuICAgICAgX3JlbW92ZUVudHJ5KHBvc3NpYmxlUG9zaXRpb25zLCBcImxlZnRcIik7XG4gICAgfVxuXG4gICAgLy8gQHZhciB7U3RyaW5nfSAgZXg6ICdyaWdodC1hbGlnbmVkJ1xuICAgIHZhciBkZXNpcmVkQWxpZ25tZW50ID0gKGZ1bmN0aW9uIChwb3MpIHtcbiAgICAgIHZhciBoeXBoZW5JbmRleCA9IHBvcy5pbmRleE9mKCctJyk7XG4gICAgICBpZiAoaHlwaGVuSW5kZXggIT09IC0xKSB7XG4gICAgICAgIC8vIGhhcyBhbGlnbm1lbnRcbiAgICAgICAgcmV0dXJuIHBvcy5zdWJzdHIoaHlwaGVuSW5kZXgpO1xuICAgICAgfVxuICAgICAgcmV0dXJuICcnO1xuICAgIH0pKGRlc2lyZWRUb29sdGlwUG9zaXRpb24gfHwgJycpO1xuXG4gICAgLy8gc3RyaXAgYWxpZ25tZW50IGZyb20gcG9zaXRpb25cbiAgICBpZiAoZGVzaXJlZFRvb2x0aXBQb3NpdGlvbikge1xuICAgICAgLy8gZXg6IFwiYm90dG9tLXJpZ2h0LWFsaWduZWRcIlxuICAgICAgLy8gc2hvdWxkIHJldHVybiAnYm90dG9tJ1xuICAgICAgZGVzaXJlZFRvb2x0aXBQb3NpdGlvbiA9IGRlc2lyZWRUb29sdGlwUG9zaXRpb24uc3BsaXQoJy0nKVswXTtcbiAgICB9XG5cbiAgICBpZiAocG9zc2libGVQb3NpdGlvbnMubGVuZ3RoKSB7XG4gICAgICBpZiAoZGVzaXJlZFRvb2x0aXBQb3NpdGlvbiAhPT0gXCJhdXRvXCIgJiZcbiAgICAgICAgICBwb3NzaWJsZVBvc2l0aW9ucy5pbmRleE9mKGRlc2lyZWRUb29sdGlwUG9zaXRpb24pID4gLTEpIHtcbiAgICAgICAgLy8gSWYgdGhlIHJlcXVlc3RlZCBwb3NpdGlvbiBpcyBpbiB0aGUgbGlzdCwgY2hvb3NlIHRoYXRcbiAgICAgICAgY2FsY3VsYXRlZFBvc2l0aW9uID0gZGVzaXJlZFRvb2x0aXBQb3NpdGlvbjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFBpY2sgdGhlIGZpcnN0IHZhbGlkIHBvc2l0aW9uLCBpbiBvcmRlclxuICAgICAgICBjYWxjdWxhdGVkUG9zaXRpb24gPSBwb3NzaWJsZVBvc2l0aW9uc1swXTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBvbmx5IHRvcCBhbmQgYm90dG9tIHBvc2l0aW9ucyBoYXZlIG9wdGlvbmFsIGFsaWdubWVudHNcbiAgICBpZiAoWyd0b3AnLCAnYm90dG9tJ10uaW5kZXhPZihjYWxjdWxhdGVkUG9zaXRpb24pICE9PSAtMSkge1xuICAgICAgY2FsY3VsYXRlZFBvc2l0aW9uICs9IF9kZXRlcm1pbmVBdXRvQWxpZ25tZW50KHRhcmdldEVsZW1lbnRSZWN0LmxlZnQsIHRvb2x0aXBXaWR0aCwgd2luZG93U2l6ZSwgZGVzaXJlZEFsaWdubWVudCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNhbGN1bGF0ZWRQb3NpdGlvbjtcbiAgfVxuXG4gIC8qKlxuICAqIGF1dG8tZGV0ZXJtaW5lIGFsaWdubWVudFxuICAqIEBwYXJhbSB7SW50ZWdlcn0gIG9mZnNldExlZnRcbiAgKiBAcGFyYW0ge0ludGVnZXJ9ICB0b29sdGlwV2lkdGhcbiAgKiBAcGFyYW0ge09iamVjdH0gICB3aW5kb3dTaXplXG4gICogQHBhcmFtIHtTdHJpbmd9ICAgZGVzaXJlZEFsaWdubWVudFxuICAqIEByZXR1cm4ge1N0cmluZ30gIGNhbGN1bGF0ZWRBbGlnbm1lbnRcbiAgKi9cbiAgZnVuY3Rpb24gX2RldGVybWluZUF1dG9BbGlnbm1lbnQgKG9mZnNldExlZnQsIHRvb2x0aXBXaWR0aCwgd2luZG93U2l6ZSwgZGVzaXJlZEFsaWdubWVudCkge1xuICAgIHZhciBoYWxmVG9vbHRpcFdpZHRoID0gdG9vbHRpcFdpZHRoIC8gMixcbiAgICAgIHdpbldpZHRoID0gTWF0aC5taW4od2luZG93U2l6ZS53aWR0aCwgd2luZG93LnNjcmVlbi53aWR0aCksXG4gICAgICBwb3NzaWJsZUFsaWdubWVudHMgPSBbJy1sZWZ0LWFsaWduZWQnLCAnLW1pZGRsZS1hbGlnbmVkJywgJy1yaWdodC1hbGlnbmVkJ10sXG4gICAgICBjYWxjdWxhdGVkQWxpZ25tZW50ID0gJyc7XG4gICAgXG4gICAgLy8gdmFsaWQgbGVmdCBtdXN0IGJlIGF0IGxlYXN0IGEgdG9vbHRpcFdpZHRoXG4gICAgLy8gYXdheSBmcm9tIHJpZ2h0IHNpZGVcbiAgICBpZiAod2luV2lkdGggLSBvZmZzZXRMZWZ0IDwgdG9vbHRpcFdpZHRoKSB7XG4gICAgICBfcmVtb3ZlRW50cnkocG9zc2libGVBbGlnbm1lbnRzLCAnLWxlZnQtYWxpZ25lZCcpO1xuICAgIH1cblxuICAgIC8vIHZhbGlkIG1pZGRsZSBtdXN0IGJlIGF0IGxlYXN0IGhhbGYgXG4gICAgLy8gd2lkdGggYXdheSBmcm9tIGJvdGggc2lkZXNcbiAgICBpZiAob2Zmc2V0TGVmdCA8IGhhbGZUb29sdGlwV2lkdGggfHwgXG4gICAgICB3aW5XaWR0aCAtIG9mZnNldExlZnQgPCBoYWxmVG9vbHRpcFdpZHRoKSB7XG4gICAgICBfcmVtb3ZlRW50cnkocG9zc2libGVBbGlnbm1lbnRzLCAnLW1pZGRsZS1hbGlnbmVkJyk7XG4gICAgfVxuXG4gICAgLy8gdmFsaWQgcmlnaHQgbXVzdCBiZSBhdCBsZWFzdCBhIHRvb2x0aXBXaWR0aFxuICAgIC8vIHdpZHRoIGF3YXkgZnJvbSBsZWZ0IHNpZGVcbiAgICBpZiAob2Zmc2V0TGVmdCA8IHRvb2x0aXBXaWR0aCkge1xuICAgICAgX3JlbW92ZUVudHJ5KHBvc3NpYmxlQWxpZ25tZW50cywgJy1yaWdodC1hbGlnbmVkJyk7XG4gICAgfVxuXG4gICAgaWYgKHBvc3NpYmxlQWxpZ25tZW50cy5sZW5ndGgpIHtcbiAgICAgIGlmIChwb3NzaWJsZUFsaWdubWVudHMuaW5kZXhPZihkZXNpcmVkQWxpZ25tZW50KSAhPT0gLTEpIHtcbiAgICAgICAgLy8gdGhlIGRlc2lyZWQgYWxpZ25tZW50IGlzIHZhbGlkXG4gICAgICAgIGNhbGN1bGF0ZWRBbGlnbm1lbnQgPSBkZXNpcmVkQWxpZ25tZW50O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gcGljayB0aGUgZmlyc3QgdmFsaWQgcG9zaXRpb24sIGluIG9yZGVyXG4gICAgICAgIGNhbGN1bGF0ZWRBbGlnbm1lbnQgPSBwb3NzaWJsZUFsaWdubWVudHNbMF07XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGlmIHNjcmVlbiB3aWR0aCBpcyB0b28gc21hbGwgXG4gICAgICAvLyBmb3IgQU5ZIGFsaWdubWVudCwgbWlkZGxlIGlzIFxuICAgICAgLy8gcHJvYmFibHkgdGhlIGJlc3QgZm9yIHZpc2liaWxpdHlcbiAgICAgIGNhbGN1bGF0ZWRBbGlnbm1lbnQgPSAnLW1pZGRsZS1hbGlnbmVkJztcbiAgICB9XG5cbiAgICByZXR1cm4gY2FsY3VsYXRlZEFsaWdubWVudDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgYW4gZW50cnkgZnJvbSBhIHN0cmluZyBhcnJheSBpZiBpdCdzIHRoZXJlLCBkb2VzIG5vdGhpbmcgaWYgaXQgaXNuJ3QgdGhlcmUuXG4gICAqXG4gICAqIEBwYXJhbSB7QXJyYXl9IHN0cmluZ0FycmF5XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzdHJpbmdUb1JlbW92ZVxuICAgKi9cbiAgZnVuY3Rpb24gX3JlbW92ZUVudHJ5KHN0cmluZ0FycmF5LCBzdHJpbmdUb1JlbW92ZSkge1xuICAgIGlmIChzdHJpbmdBcnJheS5pbmRleE9mKHN0cmluZ1RvUmVtb3ZlKSA+IC0xKSB7XG4gICAgICBzdHJpbmdBcnJheS5zcGxpY2Uoc3RyaW5nQXJyYXkuaW5kZXhPZihzdHJpbmdUb1JlbW92ZSksIDEpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIHBvc2l0aW9uIG9mIHRoZSBoZWxwZXIgbGF5ZXIgb24gdGhlIHNjcmVlblxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfc2V0SGVscGVyTGF5ZXJQb3NpdGlvblxuICAgKiBAcGFyYW0ge09iamVjdH0gaGVscGVyTGF5ZXJcbiAgICovXG4gIGZ1bmN0aW9uIF9zZXRIZWxwZXJMYXllclBvc2l0aW9uKGhlbHBlckxheWVyKSB7XG4gICAgaWYgKGhlbHBlckxheWVyKSB7XG4gICAgICAvL3ByZXZlbnQgZXJyb3Igd2hlbiBgdGhpcy5fY3VycmVudFN0ZXBgIGluIHVuZGVmaW5lZFxuICAgICAgaWYgKCF0aGlzLl9pbnRyb0l0ZW1zW3RoaXMuX2N1cnJlbnRTdGVwXSkgcmV0dXJuO1xuXG4gICAgICB2YXIgY3VycmVudEVsZW1lbnQgID0gdGhpcy5faW50cm9JdGVtc1t0aGlzLl9jdXJyZW50U3RlcF0sXG4gICAgICAgICAgZWxlbWVudFBvc2l0aW9uID0gX2dldE9mZnNldChjdXJyZW50RWxlbWVudC5lbGVtZW50KSxcbiAgICAgICAgICB3aWR0aEhlaWdodFBhZGRpbmcgPSB0aGlzLl9vcHRpb25zLmhlbHBlckVsZW1lbnRQYWRkaW5nO1xuXG4gICAgICAvLyBJZiB0aGUgdGFyZ2V0IGVsZW1lbnQgaXMgZml4ZWQsIHRoZSB0b29sdGlwIHNob3VsZCBiZSBmaXhlZCBhcyB3ZWxsLlxuICAgICAgLy8gT3RoZXJ3aXNlLCByZW1vdmUgYSBmaXhlZCBjbGFzcyB0aGF0IG1heSBiZSBsZWZ0IG92ZXIgZnJvbSB0aGUgcHJldmlvdXNcbiAgICAgIC8vIHN0ZXAuXG4gICAgICBpZiAoX2lzRml4ZWQoY3VycmVudEVsZW1lbnQuZWxlbWVudCkpIHtcbiAgICAgICAgX2FkZENsYXNzKGhlbHBlckxheWVyLCAnaW50cm9qcy1maXhlZFRvb2x0aXAnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIF9yZW1vdmVDbGFzcyhoZWxwZXJMYXllciwgJ2ludHJvanMtZml4ZWRUb29sdGlwJyk7XG4gICAgICB9XG5cbiAgICAgIGlmIChjdXJyZW50RWxlbWVudC5wb3NpdGlvbiA9PT0gJ2Zsb2F0aW5nJykge1xuICAgICAgICB3aWR0aEhlaWdodFBhZGRpbmcgPSAwO1xuICAgICAgfVxuXG4gICAgICAvL3NldCBuZXcgcG9zaXRpb24gdG8gaGVscGVyIGxheWVyXG4gICAgICBoZWxwZXJMYXllci5zdHlsZS5jc3NUZXh0ID0gJ3dpZHRoOiAnICsgKGVsZW1lbnRQb3NpdGlvbi53aWR0aCAgKyB3aWR0aEhlaWdodFBhZGRpbmcpICArICdweDsgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2hlaWdodDonICsgKGVsZW1lbnRQb3NpdGlvbi5oZWlnaHQgKyB3aWR0aEhlaWdodFBhZGRpbmcpICArICdweDsgJyArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3RvcDonICAgICsgKGVsZW1lbnRQb3NpdGlvbi50b3AgICAgLSB3aWR0aEhlaWdodFBhZGRpbmcgLyAyKSAgICsgJ3B4OycgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdsZWZ0OiAnICArIChlbGVtZW50UG9zaXRpb24ubGVmdCAgIC0gd2lkdGhIZWlnaHRQYWRkaW5nIC8gMikgICArICdweDsnO1xuXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBkaXNhYmxlaW50ZXJhY3Rpb24gbGF5ZXIgYW5kIGFkanVzdCB0aGUgc2l6ZSBhbmQgcG9zaXRpb24gb2YgdGhlIGxheWVyXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9kaXNhYmxlSW50ZXJhY3Rpb25cbiAgICovXG4gIGZ1bmN0aW9uIF9kaXNhYmxlSW50ZXJhY3Rpb24oKSB7XG4gICAgdmFyIGRpc2FibGVJbnRlcmFjdGlvbkxheWVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmludHJvanMtZGlzYWJsZUludGVyYWN0aW9uJyk7XG5cbiAgICBpZiAoZGlzYWJsZUludGVyYWN0aW9uTGF5ZXIgPT09IG51bGwpIHtcbiAgICAgIGRpc2FibGVJbnRlcmFjdGlvbkxheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBkaXNhYmxlSW50ZXJhY3Rpb25MYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1kaXNhYmxlSW50ZXJhY3Rpb24nO1xuICAgICAgdGhpcy5fdGFyZ2V0RWxlbWVudC5hcHBlbmRDaGlsZChkaXNhYmxlSW50ZXJhY3Rpb25MYXllcik7XG4gICAgfVxuXG4gICAgX3NldEhlbHBlckxheWVyUG9zaXRpb24uY2FsbCh0aGlzLCBkaXNhYmxlSW50ZXJhY3Rpb25MYXllcik7XG4gIH1cblxuICAvKipcbiAgICogU2V0dGluZyBhbmNob3JzIHRvIGJlaGF2ZSBsaWtlIGJ1dHRvbnNcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX3NldEFuY2hvckFzQnV0dG9uXG4gICAqL1xuICBmdW5jdGlvbiBfc2V0QW5jaG9yQXNCdXR0b24oYW5jaG9yKXtcbiAgICBhbmNob3Iuc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xuICAgIGFuY2hvci50YWJJbmRleCA9IDA7XG4gIH1cblxuICAvKipcbiAgICogU2hvdyBhbiBlbGVtZW50IG9uIHRoZSBwYWdlXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9zaG93RWxlbWVudFxuICAgKiBAcGFyYW0ge09iamVjdH0gdGFyZ2V0RWxlbWVudFxuICAgKi9cbiAgZnVuY3Rpb24gX3Nob3dFbGVtZW50KHRhcmdldEVsZW1lbnQpIHtcbiAgICBpZiAodHlwZW9mICh0aGlzLl9pbnRyb0NoYW5nZUNhbGxiYWNrKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRoaXMuX2ludHJvQ2hhbmdlQ2FsbGJhY2suY2FsbCh0aGlzLCB0YXJnZXRFbGVtZW50LmVsZW1lbnQpO1xuICAgIH1cblxuICAgIHZhciBzZWxmID0gdGhpcyxcbiAgICAgICAgb2xkSGVscGVyTGF5ZXIgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oZWxwZXJMYXllcicpLFxuICAgICAgICBvbGRSZWZlcmVuY2VMYXllciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLXRvb2x0aXBSZWZlcmVuY2VMYXllcicpLFxuICAgICAgICBoaWdobGlnaHRDbGFzcyA9ICdpbnRyb2pzLWhlbHBlckxheWVyJyxcbiAgICAgICAgbmV4dFRvb2x0aXBCdXR0b24sXG4gICAgICAgIHByZXZUb29sdGlwQnV0dG9uLFxuICAgICAgICBza2lwVG9vbHRpcEJ1dHRvbixcbiAgICAgICAgc2Nyb2xsUGFyZW50O1xuXG4gICAgLy9jaGVjayBmb3IgYSBjdXJyZW50IHN0ZXAgaGlnaGxpZ2h0IGNsYXNzXG4gICAgaWYgKHR5cGVvZiAodGFyZ2V0RWxlbWVudC5oaWdobGlnaHRDbGFzcykgPT09ICdzdHJpbmcnKSB7XG4gICAgICBoaWdobGlnaHRDbGFzcyArPSAoJyAnICsgdGFyZ2V0RWxlbWVudC5oaWdobGlnaHRDbGFzcyk7XG4gICAgfVxuICAgIC8vY2hlY2sgZm9yIG9wdGlvbnMgaGlnaGxpZ2h0IGNsYXNzXG4gICAgaWYgKHR5cGVvZiAodGhpcy5fb3B0aW9ucy5oaWdobGlnaHRDbGFzcykgPT09ICdzdHJpbmcnKSB7XG4gICAgICBoaWdobGlnaHRDbGFzcyArPSAoJyAnICsgdGhpcy5fb3B0aW9ucy5oaWdobGlnaHRDbGFzcyk7XG4gICAgfVxuXG4gICAgaWYgKG9sZEhlbHBlckxheWVyICE9PSBudWxsKSB7XG4gICAgICB2YXIgb2xkSGVscGVyTnVtYmVyTGF5ZXIgPSBvbGRSZWZlcmVuY2VMYXllci5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oZWxwZXJOdW1iZXJMYXllcicpLFxuICAgICAgICAgIG9sZHRvb2x0aXBMYXllciAgICAgID0gb2xkUmVmZXJlbmNlTGF5ZXIucXVlcnlTZWxlY3RvcignLmludHJvanMtdG9vbHRpcHRleHQnKSxcbiAgICAgICAgICBvbGRBcnJvd0xheWVyICAgICAgICA9IG9sZFJlZmVyZW5jZUxheWVyLnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWFycm93JyksXG4gICAgICAgICAgb2xkdG9vbHRpcENvbnRhaW5lciAgPSBvbGRSZWZlcmVuY2VMYXllci5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy10b29sdGlwJyk7XG4gICAgICAgICAgXG4gICAgICBza2lwVG9vbHRpcEJ1dHRvbiAgICA9IG9sZFJlZmVyZW5jZUxheWVyLnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLXNraXBidXR0b24nKTtcbiAgICAgIHByZXZUb29sdGlwQnV0dG9uICAgID0gb2xkUmVmZXJlbmNlTGF5ZXIucXVlcnlTZWxlY3RvcignLmludHJvanMtcHJldmJ1dHRvbicpO1xuICAgICAgbmV4dFRvb2x0aXBCdXR0b24gICAgPSBvbGRSZWZlcmVuY2VMYXllci5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1uZXh0YnV0dG9uJyk7XG5cbiAgICAgIC8vdXBkYXRlIG9yIHJlc2V0IHRoZSBoZWxwZXIgaGlnaGxpZ2h0IGNsYXNzXG4gICAgICBvbGRIZWxwZXJMYXllci5jbGFzc05hbWUgPSBoaWdobGlnaHRDbGFzcztcbiAgICAgIC8vaGlkZSB0aGUgdG9vbHRpcFxuICAgICAgb2xkdG9vbHRpcENvbnRhaW5lci5zdHlsZS5vcGFjaXR5ID0gMDtcbiAgICAgIG9sZHRvb2x0aXBDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwibm9uZVwiO1xuXG4gICAgICBpZiAob2xkSGVscGVyTnVtYmVyTGF5ZXIgIT09IG51bGwpIHtcbiAgICAgICAgdmFyIGxhc3RJbnRyb0l0ZW0gPSB0aGlzLl9pbnRyb0l0ZW1zWyh0YXJnZXRFbGVtZW50LnN0ZXAgLSAyID49IDAgPyB0YXJnZXRFbGVtZW50LnN0ZXAgLSAyIDogMCldO1xuXG4gICAgICAgIGlmIChsYXN0SW50cm9JdGVtICE9PSBudWxsICYmICh0aGlzLl9kaXJlY3Rpb24gPT09ICdmb3J3YXJkJyAmJiBsYXN0SW50cm9JdGVtLnBvc2l0aW9uID09PSAnZmxvYXRpbmcnKSB8fCAodGhpcy5fZGlyZWN0aW9uID09PSAnYmFja3dhcmQnICYmIHRhcmdldEVsZW1lbnQucG9zaXRpb24gPT09ICdmbG9hdGluZycpKSB7XG4gICAgICAgICAgb2xkSGVscGVyTnVtYmVyTGF5ZXIuc3R5bGUub3BhY2l0eSA9IDA7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gc2Nyb2xsIHRvIGVsZW1lbnRcbiAgICAgIHNjcm9sbFBhcmVudCA9IF9nZXRTY3JvbGxQYXJlbnQoIHRhcmdldEVsZW1lbnQuZWxlbWVudCApO1xuXG4gICAgICBpZiAoc2Nyb2xsUGFyZW50ICE9PSBkb2N1bWVudC5ib2R5KSB7XG4gICAgICAgIC8vIHRhcmdldCBpcyB3aXRoaW4gYSBzY3JvbGxhYmxlIGVsZW1lbnRcbiAgICAgICAgX3Njcm9sbFBhcmVudFRvRWxlbWVudChzY3JvbGxQYXJlbnQsIHRhcmdldEVsZW1lbnQuZWxlbWVudCk7XG4gICAgICB9XG5cbiAgICAgIC8vIHNldCBuZXcgcG9zaXRpb24gdG8gaGVscGVyIGxheWVyXG4gICAgICBfc2V0SGVscGVyTGF5ZXJQb3NpdGlvbi5jYWxsKHNlbGYsIG9sZEhlbHBlckxheWVyKTtcbiAgICAgIF9zZXRIZWxwZXJMYXllclBvc2l0aW9uLmNhbGwoc2VsZiwgb2xkUmVmZXJlbmNlTGF5ZXIpO1xuXG4gICAgICAvL3JlbW92ZSBgaW50cm9qcy1maXhQYXJlbnRgIGNsYXNzIGZyb20gdGhlIGVsZW1lbnRzXG4gICAgICB2YXIgZml4UGFyZW50cyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLWZpeFBhcmVudCcpO1xuICAgICAgX2ZvckVhY2goZml4UGFyZW50cywgZnVuY3Rpb24gKHBhcmVudCkge1xuICAgICAgICBfcmVtb3ZlQ2xhc3MocGFyZW50LCAvaW50cm9qcy1maXhQYXJlbnQvZyk7XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgLy9yZW1vdmUgb2xkIGNsYXNzZXMgaWYgdGhlIGVsZW1lbnQgc3RpbGwgZXhpc3RcbiAgICAgIF9yZW1vdmVTaG93RWxlbWVudCgpO1xuXG4gICAgICAvL3dlIHNob3VsZCB3YWl0IHVudGlsIHRoZSBDU1MzIHRyYW5zaXRpb24gaXMgY29tcGV0ZWQgKGl0J3MgMC4zIHNlYykgdG8gcHJldmVudCBpbmNvcnJlY3QgYGhlaWdodGAgYW5kIGB3aWR0aGAgY2FsY3VsYXRpb25cbiAgICAgIGlmIChzZWxmLl9sYXN0U2hvd0VsZW1lbnRUaW1lcikge1xuICAgICAgICB3aW5kb3cuY2xlYXJUaW1lb3V0KHNlbGYuX2xhc3RTaG93RWxlbWVudFRpbWVyKTtcbiAgICAgIH1cblxuICAgICAgc2VsZi5fbGFzdFNob3dFbGVtZW50VGltZXIgPSB3aW5kb3cuc2V0VGltZW91dChmdW5jdGlvbigpIHtcbiAgICAgICAgLy9zZXQgY3VycmVudCBzdGVwIHRvIHRoZSBsYWJlbFxuICAgICAgICBpZiAob2xkSGVscGVyTnVtYmVyTGF5ZXIgIT09IG51bGwpIHtcbiAgICAgICAgICBvbGRIZWxwZXJOdW1iZXJMYXllci5pbm5lckhUTUwgPSB0YXJnZXRFbGVtZW50LnN0ZXA7XG4gICAgICAgIH1cbiAgICAgICAgLy9zZXQgY3VycmVudCB0b29sdGlwIHRleHRcbiAgICAgICAgb2xkdG9vbHRpcExheWVyLmlubmVySFRNTCA9IHRhcmdldEVsZW1lbnQuaW50cm87XG4gICAgICAgIC8vc2V0IHRoZSB0b29sdGlwIHBvc2l0aW9uXG4gICAgICAgIG9sZHRvb2x0aXBDb250YWluZXIuc3R5bGUuZGlzcGxheSA9IFwiYmxvY2tcIjtcbiAgICAgICAgX3BsYWNlVG9vbHRpcC5jYWxsKHNlbGYsIHRhcmdldEVsZW1lbnQuZWxlbWVudCwgb2xkdG9vbHRpcENvbnRhaW5lciwgb2xkQXJyb3dMYXllciwgb2xkSGVscGVyTnVtYmVyTGF5ZXIpO1xuXG4gICAgICAgIC8vY2hhbmdlIGFjdGl2ZSBidWxsZXRcbiAgICAgICAgaWYgKHNlbGYuX29wdGlvbnMuc2hvd0J1bGxldHMpIHtcbiAgICAgICAgICAgIG9sZFJlZmVyZW5jZUxheWVyLnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWJ1bGxldHMgbGkgPiBhLmFjdGl2ZScpLmNsYXNzTmFtZSA9ICcnO1xuICAgICAgICAgICAgb2xkUmVmZXJlbmNlTGF5ZXIucXVlcnlTZWxlY3RvcignLmludHJvanMtYnVsbGV0cyBsaSA+IGFbZGF0YS1zdGVwbnVtYmVyPVwiJyArIHRhcmdldEVsZW1lbnQuc3RlcCArICdcIl0nKS5jbGFzc05hbWUgPSAnYWN0aXZlJztcbiAgICAgICAgfVxuICAgICAgICBvbGRSZWZlcmVuY2VMYXllci5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1wcm9ncmVzcyAuaW50cm9qcy1wcm9ncmVzc2JhcicpLnN0eWxlLmNzc1RleHQgPSAnd2lkdGg6JyArIF9nZXRQcm9ncmVzcy5jYWxsKHNlbGYpICsgJyU7JztcbiAgICAgICAgb2xkUmVmZXJlbmNlTGF5ZXIucXVlcnlTZWxlY3RvcignLmludHJvanMtcHJvZ3Jlc3MgLmludHJvanMtcHJvZ3Jlc3NiYXInKS5zZXRBdHRyaWJ1dGUoJ2FyaWEtdmFsdWVub3cnLCBfZ2V0UHJvZ3Jlc3MuY2FsbChzZWxmKSk7XG5cbiAgICAgICAgLy9zaG93IHRoZSB0b29sdGlwXG4gICAgICAgIG9sZHRvb2x0aXBDb250YWluZXIuc3R5bGUub3BhY2l0eSA9IDE7XG4gICAgICAgIGlmIChvbGRIZWxwZXJOdW1iZXJMYXllcikgb2xkSGVscGVyTnVtYmVyTGF5ZXIuc3R5bGUub3BhY2l0eSA9IDE7XG5cbiAgICAgICAgLy9yZXNldCBidXR0b24gZm9jdXNcbiAgICAgICAgaWYgKHR5cGVvZiBza2lwVG9vbHRpcEJ1dHRvbiAhPT0gXCJ1bmRlZmluZWRcIiAmJiBza2lwVG9vbHRpcEJ1dHRvbiAhPT0gbnVsbCAmJiAvaW50cm9qcy1kb25lYnV0dG9uL2dpLnRlc3Qoc2tpcFRvb2x0aXBCdXR0b24uY2xhc3NOYW1lKSkge1xuICAgICAgICAgIC8vIHNraXAgYnV0dG9uIGlzIG5vdyBcImRvbmVcIiBidXR0b25cbiAgICAgICAgICBza2lwVG9vbHRpcEJ1dHRvbi5mb2N1cygpO1xuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBuZXh0VG9vbHRpcEJ1dHRvbiAhPT0gXCJ1bmRlZmluZWRcIiAmJiBuZXh0VG9vbHRpcEJ1dHRvbiAhPT0gbnVsbCkge1xuICAgICAgICAgIC8vc3RpbGwgaW4gdGhlIHRvdXIsIGZvY3VzIG9uIG5leHRcbiAgICAgICAgICBuZXh0VG9vbHRpcEJ1dHRvbi5mb2N1cygpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gY2hhbmdlIHRoZSBzY3JvbGwgb2YgdGhlIHdpbmRvdywgaWYgbmVlZGVkXG4gICAgICAgIF9zY3JvbGxUby5jYWxsKHNlbGYsIHRhcmdldEVsZW1lbnQuc2Nyb2xsVG8sIHRhcmdldEVsZW1lbnQsIG9sZHRvb2x0aXBMYXllcik7XG4gICAgICB9LCAzNTApO1xuXG4gICAgICAvLyBlbmQgb2Ygb2xkIGVsZW1lbnQgaWYtZWxzZSBjb25kaXRpb25cbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIGhlbHBlckxheWVyICAgICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG4gICAgICAgICAgcmVmZXJlbmNlTGF5ZXIgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKSxcbiAgICAgICAgICBhcnJvd0xheWVyICAgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxuICAgICAgICAgIHRvb2x0aXBMYXllciAgICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG4gICAgICAgICAgdG9vbHRpcFRleHRMYXllciAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKSxcbiAgICAgICAgICBidWxsZXRzTGF5ZXIgICAgICA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxuICAgICAgICAgIHByb2dyZXNzTGF5ZXIgICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2JyksXG4gICAgICAgICAgYnV0dG9uc0xheWVyICAgICAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcblxuICAgICAgaGVscGVyTGF5ZXIuY2xhc3NOYW1lID0gaGlnaGxpZ2h0Q2xhc3M7XG4gICAgICByZWZlcmVuY2VMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy10b29sdGlwUmVmZXJlbmNlTGF5ZXInO1xuXG4gICAgICAvLyBzY3JvbGwgdG8gZWxlbWVudFxuICAgICAgc2Nyb2xsUGFyZW50ID0gX2dldFNjcm9sbFBhcmVudCggdGFyZ2V0RWxlbWVudC5lbGVtZW50ICk7XG5cbiAgICAgIGlmIChzY3JvbGxQYXJlbnQgIT09IGRvY3VtZW50LmJvZHkpIHtcbiAgICAgICAgLy8gdGFyZ2V0IGlzIHdpdGhpbiBhIHNjcm9sbGFibGUgZWxlbWVudFxuICAgICAgICBfc2Nyb2xsUGFyZW50VG9FbGVtZW50KHNjcm9sbFBhcmVudCwgdGFyZ2V0RWxlbWVudC5lbGVtZW50KTtcbiAgICAgIH1cblxuICAgICAgLy9zZXQgbmV3IHBvc2l0aW9uIHRvIGhlbHBlciBsYXllclxuICAgICAgX3NldEhlbHBlckxheWVyUG9zaXRpb24uY2FsbChzZWxmLCBoZWxwZXJMYXllcik7XG4gICAgICBfc2V0SGVscGVyTGF5ZXJQb3NpdGlvbi5jYWxsKHNlbGYsIHJlZmVyZW5jZUxheWVyKTtcblxuICAgICAgLy9hZGQgaGVscGVyIGxheWVyIHRvIHRhcmdldCBlbGVtZW50XG4gICAgICB0aGlzLl90YXJnZXRFbGVtZW50LmFwcGVuZENoaWxkKGhlbHBlckxheWVyKTtcbiAgICAgIHRoaXMuX3RhcmdldEVsZW1lbnQuYXBwZW5kQ2hpbGQocmVmZXJlbmNlTGF5ZXIpO1xuXG4gICAgICBhcnJvd0xheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLWFycm93JztcblxuICAgICAgdG9vbHRpcFRleHRMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy10b29sdGlwdGV4dCc7XG4gICAgICB0b29sdGlwVGV4dExheWVyLmlubmVySFRNTCA9IHRhcmdldEVsZW1lbnQuaW50cm87XG5cbiAgICAgIGJ1bGxldHNMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1idWxsZXRzJztcblxuICAgICAgaWYgKHRoaXMuX29wdGlvbnMuc2hvd0J1bGxldHMgPT09IGZhbHNlKSB7XG4gICAgICAgIGJ1bGxldHNMYXllci5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuICAgICAgfVxuXG4gICAgICB2YXIgdWxDb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd1bCcpO1xuICAgICAgdWxDb250YWluZXIuc2V0QXR0cmlidXRlKCdyb2xlJywgJ3RhYmxpc3QnKTtcblxuICAgICAgdmFyIGFuY2hvckNsaWNrID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgIHNlbGYuZ29Ub1N0ZXAodGhpcy5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RlcG51bWJlcicpKTtcbiAgICAgIH07XG5cbiAgICAgIF9mb3JFYWNoKHRoaXMuX2ludHJvSXRlbXMsIGZ1bmN0aW9uIChpdGVtLCBpKSB7XG4gICAgICAgIHZhciBpbm5lckxpICAgID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnbGknKTtcbiAgICAgICAgdmFyIGFuY2hvckxpbmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICAgIFxuICAgICAgICBpbm5lckxpLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcmVzZW50YXRpb24nKTtcbiAgICAgICAgYW5jaG9yTGluay5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAndGFiJyk7XG5cbiAgICAgICAgYW5jaG9yTGluay5vbmNsaWNrID0gYW5jaG9yQ2xpY2s7XG5cbiAgICAgICAgaWYgKGkgPT09ICh0YXJnZXRFbGVtZW50LnN0ZXAtMSkpIHtcbiAgICAgICAgICBhbmNob3JMaW5rLmNsYXNzTmFtZSA9ICdhY3RpdmUnO1xuICAgICAgICB9IFxuXG4gICAgICAgIF9zZXRBbmNob3JBc0J1dHRvbihhbmNob3JMaW5rKTtcbiAgICAgICAgYW5jaG9yTGluay5pbm5lckhUTUwgPSBcIiZuYnNwO1wiO1xuICAgICAgICBhbmNob3JMaW5rLnNldEF0dHJpYnV0ZSgnZGF0YS1zdGVwbnVtYmVyJywgaXRlbS5zdGVwKTtcblxuICAgICAgICBpbm5lckxpLmFwcGVuZENoaWxkKGFuY2hvckxpbmspO1xuICAgICAgICB1bENvbnRhaW5lci5hcHBlbmRDaGlsZChpbm5lckxpKTtcbiAgICAgIH0pO1xuXG4gICAgICBidWxsZXRzTGF5ZXIuYXBwZW5kQ2hpbGQodWxDb250YWluZXIpO1xuXG4gICAgICBwcm9ncmVzc0xheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXByb2dyZXNzJztcblxuICAgICAgaWYgKHRoaXMuX29wdGlvbnMuc2hvd1Byb2dyZXNzID09PSBmYWxzZSkge1xuICAgICAgICBwcm9ncmVzc0xheWVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XG4gICAgICB9XG4gICAgICB2YXIgcHJvZ3Jlc3NCYXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIHByb2dyZXNzQmFyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXByb2dyZXNzYmFyJztcbiAgICAgIHByb2dyZXNzQmFyLnNldEF0dHJpYnV0ZSgncm9sZScsICdwcm9ncmVzcycpO1xuICAgICAgcHJvZ3Jlc3NCYXIuc2V0QXR0cmlidXRlKCdhcmlhLXZhbHVlbWluJywgMCk7XG4gICAgICBwcm9ncmVzc0Jhci5zZXRBdHRyaWJ1dGUoJ2FyaWEtdmFsdWVtYXgnLCAxMDApO1xuICAgICAgcHJvZ3Jlc3NCYXIuc2V0QXR0cmlidXRlKCdhcmlhLXZhbHVlbm93JywgX2dldFByb2dyZXNzLmNhbGwodGhpcykpO1xuICAgICAgcHJvZ3Jlc3NCYXIuc3R5bGUuY3NzVGV4dCA9ICd3aWR0aDonICsgX2dldFByb2dyZXNzLmNhbGwodGhpcykgKyAnJTsnO1xuXG4gICAgICBwcm9ncmVzc0xheWVyLmFwcGVuZENoaWxkKHByb2dyZXNzQmFyKTtcblxuICAgICAgYnV0dG9uc0xheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXRvb2x0aXBidXR0b25zJztcbiAgICAgIGlmICh0aGlzLl9vcHRpb25zLnNob3dCdXR0b25zID09PSBmYWxzZSkge1xuICAgICAgICBidXR0b25zTGF5ZXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgICAgIH1cblxuICAgICAgdG9vbHRpcExheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXRvb2x0aXAnO1xuICAgICAgdG9vbHRpcExheWVyLmFwcGVuZENoaWxkKHRvb2x0aXBUZXh0TGF5ZXIpO1xuICAgICAgdG9vbHRpcExheWVyLmFwcGVuZENoaWxkKGJ1bGxldHNMYXllcik7XG4gICAgICB0b29sdGlwTGF5ZXIuYXBwZW5kQ2hpbGQocHJvZ3Jlc3NMYXllcik7XG5cbiAgICAgIC8vYWRkIGhlbHBlciBsYXllciBudW1iZXJcbiAgICAgIHZhciBoZWxwZXJOdW1iZXJMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3NwYW4nKTtcbiAgICAgIGlmICh0aGlzLl9vcHRpb25zLnNob3dTdGVwTnVtYmVycyA9PT0gdHJ1ZSkge1xuICAgICAgICBoZWxwZXJOdW1iZXJMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1oZWxwZXJOdW1iZXJMYXllcic7XG4gICAgICAgIGhlbHBlck51bWJlckxheWVyLmlubmVySFRNTCA9IHRhcmdldEVsZW1lbnQuc3RlcDtcbiAgICAgICAgcmVmZXJlbmNlTGF5ZXIuYXBwZW5kQ2hpbGQoaGVscGVyTnVtYmVyTGF5ZXIpO1xuICAgICAgfVxuXG4gICAgICB0b29sdGlwTGF5ZXIuYXBwZW5kQ2hpbGQoYXJyb3dMYXllcik7XG4gICAgICByZWZlcmVuY2VMYXllci5hcHBlbmRDaGlsZCh0b29sdGlwTGF5ZXIpO1xuXG4gICAgICAvL25leHQgYnV0dG9uXG4gICAgICBuZXh0VG9vbHRpcEJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcblxuICAgICAgbmV4dFRvb2x0aXBCdXR0b24ub25jbGljayA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoc2VsZi5faW50cm9JdGVtcy5sZW5ndGggLSAxICE9PSBzZWxmLl9jdXJyZW50U3RlcCkge1xuICAgICAgICAgIF9uZXh0U3RlcC5jYWxsKHNlbGYpO1xuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBfc2V0QW5jaG9yQXNCdXR0b24obmV4dFRvb2x0aXBCdXR0b24pO1xuICAgICAgbmV4dFRvb2x0aXBCdXR0b24uaW5uZXJIVE1MID0gdGhpcy5fb3B0aW9ucy5uZXh0TGFiZWw7XG5cbiAgICAgIC8vcHJldmlvdXMgYnV0dG9uXG4gICAgICBwcmV2VG9vbHRpcEJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcblxuICAgICAgcHJldlRvb2x0aXBCdXR0b24ub25jbGljayA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoc2VsZi5fY3VycmVudFN0ZXAgIT09IDApIHtcbiAgICAgICAgICBfcHJldmlvdXNTdGVwLmNhbGwoc2VsZik7XG4gICAgICAgIH1cbiAgICAgIH07XG5cbiAgICAgIF9zZXRBbmNob3JBc0J1dHRvbihwcmV2VG9vbHRpcEJ1dHRvbik7XG4gICAgICBwcmV2VG9vbHRpcEJ1dHRvbi5pbm5lckhUTUwgPSB0aGlzLl9vcHRpb25zLnByZXZMYWJlbDtcblxuICAgICAgLy9za2lwIGJ1dHRvblxuICAgICAgc2tpcFRvb2x0aXBCdXR0b24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICBza2lwVG9vbHRpcEJ1dHRvbi5jbGFzc05hbWUgPSB0aGlzLl9vcHRpb25zLmJ1dHRvbkNsYXNzICsgJyBpbnRyb2pzLXNraXBidXR0b24gJztcbiAgICAgIF9zZXRBbmNob3JBc0J1dHRvbihza2lwVG9vbHRpcEJ1dHRvbik7XG4gICAgICBza2lwVG9vbHRpcEJ1dHRvbi5pbm5lckhUTUwgPSB0aGlzLl9vcHRpb25zLnNraXBMYWJlbDtcblxuICAgICAgc2tpcFRvb2x0aXBCdXR0b24ub25jbGljayA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBpZiAoc2VsZi5faW50cm9JdGVtcy5sZW5ndGggLSAxID09PSBzZWxmLl9jdXJyZW50U3RlcCAmJiB0eXBlb2YgKHNlbGYuX2ludHJvQ29tcGxldGVDYWxsYmFjaykgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICBzZWxmLl9pbnRyb0NvbXBsZXRlQ2FsbGJhY2suY2FsbChzZWxmKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChzZWxmLl9pbnRyb0l0ZW1zLmxlbmd0aCAtIDEgIT09IHNlbGYuX2N1cnJlbnRTdGVwICYmIHR5cGVvZiAoc2VsZi5faW50cm9FeGl0Q2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgc2VsZi5faW50cm9FeGl0Q2FsbGJhY2suY2FsbChzZWxmKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0eXBlb2Yoc2VsZi5faW50cm9Ta2lwQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgc2VsZi5faW50cm9Ta2lwQ2FsbGJhY2suY2FsbChzZWxmKTtcbiAgICAgICAgfVxuXG4gICAgICAgIF9leGl0SW50cm8uY2FsbChzZWxmLCBzZWxmLl90YXJnZXRFbGVtZW50KTtcbiAgICAgIH07XG5cbiAgICAgIGJ1dHRvbnNMYXllci5hcHBlbmRDaGlsZChza2lwVG9vbHRpcEJ1dHRvbik7XG5cbiAgICAgIC8vaW4gb3JkZXIgdG8gcHJldmVudCBkaXNwbGF5aW5nIG5leHQvcHJldmlvdXMgYnV0dG9uIGFsd2F5c1xuICAgICAgaWYgKHRoaXMuX2ludHJvSXRlbXMubGVuZ3RoID4gMSkge1xuICAgICAgICBidXR0b25zTGF5ZXIuYXBwZW5kQ2hpbGQocHJldlRvb2x0aXBCdXR0b24pO1xuICAgICAgICBidXR0b25zTGF5ZXIuYXBwZW5kQ2hpbGQobmV4dFRvb2x0aXBCdXR0b24pO1xuICAgICAgfVxuXG4gICAgICB0b29sdGlwTGF5ZXIuYXBwZW5kQ2hpbGQoYnV0dG9uc0xheWVyKTtcblxuICAgICAgLy9zZXQgcHJvcGVyIHBvc2l0aW9uXG4gICAgICBfcGxhY2VUb29sdGlwLmNhbGwoc2VsZiwgdGFyZ2V0RWxlbWVudC5lbGVtZW50LCB0b29sdGlwTGF5ZXIsIGFycm93TGF5ZXIsIGhlbHBlck51bWJlckxheWVyKTtcblxuICAgICAgLy8gY2hhbmdlIHRoZSBzY3JvbGwgb2YgdGhlIHdpbmRvdywgaWYgbmVlZGVkXG4gICAgICBfc2Nyb2xsVG8uY2FsbCh0aGlzLCB0YXJnZXRFbGVtZW50LnNjcm9sbFRvLCB0YXJnZXRFbGVtZW50LCB0b29sdGlwTGF5ZXIpO1xuXG4gICAgICAvL2VuZCBvZiBuZXcgZWxlbWVudCBpZi1lbHNlIGNvbmRpdGlvblxuICAgIH1cblxuICAgIC8vIHJlbW92aW5nIHByZXZpb3VzIGRpc2FibGUgaW50ZXJhY3Rpb24gbGF5ZXJcbiAgICB2YXIgZGlzYWJsZUludGVyYWN0aW9uTGF5ZXIgPSBzZWxmLl90YXJnZXRFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWRpc2FibGVJbnRlcmFjdGlvbicpO1xuICAgIGlmIChkaXNhYmxlSW50ZXJhY3Rpb25MYXllcikge1xuICAgICAgZGlzYWJsZUludGVyYWN0aW9uTGF5ZXIucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChkaXNhYmxlSW50ZXJhY3Rpb25MYXllcik7XG4gICAgfVxuXG4gICAgLy9kaXNhYmxlIGludGVyYWN0aW9uXG4gICAgaWYgKHRhcmdldEVsZW1lbnQuZGlzYWJsZUludGVyYWN0aW9uKSB7XG4gICAgICBfZGlzYWJsZUludGVyYWN0aW9uLmNhbGwoc2VsZik7XG4gICAgfVxuXG4gICAgLy8gd2hlbiBpdCdzIHRoZSBmaXJzdCBzdGVwIG9mIHRvdXJcbiAgICBpZiAodGhpcy5fY3VycmVudFN0ZXAgPT09IDAgJiYgdGhpcy5faW50cm9JdGVtcy5sZW5ndGggPiAxKSB7XG4gICAgICBpZiAodHlwZW9mIHNraXBUb29sdGlwQnV0dG9uICE9PSBcInVuZGVmaW5lZFwiICYmIHNraXBUb29sdGlwQnV0dG9uICE9PSBudWxsKSB7XG4gICAgICAgIHNraXBUb29sdGlwQnV0dG9uLmNsYXNzTmFtZSA9IHRoaXMuX29wdGlvbnMuYnV0dG9uQ2xhc3MgKyAnIGludHJvanMtc2tpcGJ1dHRvbic7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIG5leHRUb29sdGlwQnV0dG9uICE9PSBcInVuZGVmaW5lZFwiICYmIG5leHRUb29sdGlwQnV0dG9uICE9PSBudWxsKSB7XG4gICAgICAgIG5leHRUb29sdGlwQnV0dG9uLmNsYXNzTmFtZSA9IHRoaXMuX29wdGlvbnMuYnV0dG9uQ2xhc3MgKyAnIGludHJvanMtbmV4dGJ1dHRvbic7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLl9vcHRpb25zLmhpZGVQcmV2ID09PSB0cnVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgcHJldlRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgcHJldlRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgICBwcmV2VG9vbHRpcEJ1dHRvbi5jbGFzc05hbWUgPSB0aGlzLl9vcHRpb25zLmJ1dHRvbkNsYXNzICsgJyBpbnRyb2pzLXByZXZidXR0b24gaW50cm9qcy1oaWRkZW4nO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0eXBlb2YgbmV4dFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgbmV4dFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgICBfYWRkQ2xhc3MobmV4dFRvb2x0aXBCdXR0b24sICdpbnRyb2pzLWZ1bGxidXR0b24nKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHR5cGVvZiBwcmV2VG9vbHRpcEJ1dHRvbiAhPT0gXCJ1bmRlZmluZWRcIiAmJiBwcmV2VG9vbHRpcEJ1dHRvbiAhPT0gbnVsbCkge1xuICAgICAgICAgIHByZXZUb29sdGlwQnV0dG9uLmNsYXNzTmFtZSA9IHRoaXMuX29wdGlvbnMuYnV0dG9uQ2xhc3MgKyAnIGludHJvanMtcHJldmJ1dHRvbiBpbnRyb2pzLWRpc2FibGVkJztcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAodHlwZW9mIHNraXBUb29sdGlwQnV0dG9uICE9PSBcInVuZGVmaW5lZFwiICYmIHNraXBUb29sdGlwQnV0dG9uICE9PSBudWxsKSB7XG4gICAgICAgIHNraXBUb29sdGlwQnV0dG9uLmlubmVySFRNTCA9IHRoaXMuX29wdGlvbnMuc2tpcExhYmVsO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodGhpcy5faW50cm9JdGVtcy5sZW5ndGggLSAxID09PSB0aGlzLl9jdXJyZW50U3RlcCB8fCB0aGlzLl9pbnRyb0l0ZW1zLmxlbmd0aCA9PT0gMSkge1xuICAgICAgLy8gbGFzdCBzdGVwIG9mIHRvdXJcbiAgICAgIGlmICh0eXBlb2Ygc2tpcFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgc2tpcFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgc2tpcFRvb2x0aXBCdXR0b24uaW5uZXJIVE1MID0gdGhpcy5fb3B0aW9ucy5kb25lTGFiZWw7XG4gICAgICAgIC8vIGFkZGluZyBkb25lYnV0dG9uIGNsYXNzIGluIGFkZGl0aW9uIHRvIHNraXBidXR0b25cbiAgICAgICAgX2FkZENsYXNzKHNraXBUb29sdGlwQnV0dG9uLCAnaW50cm9qcy1kb25lYnV0dG9uJyk7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHByZXZUb29sdGlwQnV0dG9uICE9PSBcInVuZGVmaW5lZFwiICYmIHByZXZUb29sdGlwQnV0dG9uICE9PSBudWxsKSB7XG4gICAgICAgIHByZXZUb29sdGlwQnV0dG9uLmNsYXNzTmFtZSA9IHRoaXMuX29wdGlvbnMuYnV0dG9uQ2xhc3MgKyAnIGludHJvanMtcHJldmJ1dHRvbic7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLl9vcHRpb25zLmhpZGVOZXh0ID09PSB0cnVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgbmV4dFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgbmV4dFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgICBuZXh0VG9vbHRpcEJ1dHRvbi5jbGFzc05hbWUgPSB0aGlzLl9vcHRpb25zLmJ1dHRvbkNsYXNzICsgJyBpbnRyb2pzLW5leHRidXR0b24gaW50cm9qcy1oaWRkZW4nO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0eXBlb2YgcHJldlRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgcHJldlRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgICBfYWRkQ2xhc3MocHJldlRvb2x0aXBCdXR0b24sICdpbnRyb2pzLWZ1bGxidXR0b24nKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKHR5cGVvZiBuZXh0VG9vbHRpcEJ1dHRvbiAhPT0gXCJ1bmRlZmluZWRcIiAmJiBuZXh0VG9vbHRpcEJ1dHRvbiAhPT0gbnVsbCkge1xuICAgICAgICAgIG5leHRUb29sdGlwQnV0dG9uLmNsYXNzTmFtZSA9IHRoaXMuX29wdGlvbnMuYnV0dG9uQ2xhc3MgKyAnIGludHJvanMtbmV4dGJ1dHRvbiBpbnRyb2pzLWRpc2FibGVkJztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBzdGVwcyBiZXR3ZWVuIHN0YXJ0IGFuZCBlbmRcbiAgICAgIGlmICh0eXBlb2Ygc2tpcFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgc2tpcFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgc2tpcFRvb2x0aXBCdXR0b24uY2xhc3NOYW1lID0gdGhpcy5fb3B0aW9ucy5idXR0b25DbGFzcyArICcgaW50cm9qcy1za2lwYnV0dG9uJztcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcHJldlRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgcHJldlRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgcHJldlRvb2x0aXBCdXR0b24uY2xhc3NOYW1lID0gdGhpcy5fb3B0aW9ucy5idXR0b25DbGFzcyArICcgaW50cm9qcy1wcmV2YnV0dG9uJztcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgbmV4dFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgbmV4dFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgbmV4dFRvb2x0aXBCdXR0b24uY2xhc3NOYW1lID0gdGhpcy5fb3B0aW9ucy5idXR0b25DbGFzcyArICcgaW50cm9qcy1uZXh0YnV0dG9uJztcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2Ygc2tpcFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgc2tpcFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgICAgc2tpcFRvb2x0aXBCdXR0b24uaW5uZXJIVE1MID0gdGhpcy5fb3B0aW9ucy5za2lwTGFiZWw7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcHJldlRvb2x0aXBCdXR0b24uc2V0QXR0cmlidXRlKCdyb2xlJywgJ2J1dHRvbicpO1xuICAgIG5leHRUb29sdGlwQnV0dG9uLnNldEF0dHJpYnV0ZSgncm9sZScsICdidXR0b24nKTtcbiAgICBza2lwVG9vbHRpcEJ1dHRvbi5zZXRBdHRyaWJ1dGUoJ3JvbGUnLCAnYnV0dG9uJyk7XG5cbiAgICAvL1NldCBmb2N1cyBvbiBcIm5leHRcIiBidXR0b24sIHNvIHRoYXQgaGl0dGluZyBFbnRlciBhbHdheXMgbW92ZXMgeW91IG9udG8gdGhlIG5leHQgc3RlcFxuICAgIGlmICh0eXBlb2YgbmV4dFRvb2x0aXBCdXR0b24gIT09IFwidW5kZWZpbmVkXCIgJiYgbmV4dFRvb2x0aXBCdXR0b24gIT09IG51bGwpIHtcbiAgICAgIG5leHRUb29sdGlwQnV0dG9uLmZvY3VzKCk7XG4gICAgfVxuXG4gICAgX3NldFNob3dFbGVtZW50KHRhcmdldEVsZW1lbnQpO1xuXG4gICAgaWYgKHR5cGVvZiAodGhpcy5faW50cm9BZnRlckNoYW5nZUNhbGxiYWNrKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRoaXMuX2ludHJvQWZ0ZXJDaGFuZ2VDYWxsYmFjay5jYWxsKHRoaXMsIHRhcmdldEVsZW1lbnQuZWxlbWVudCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRvIGNoYW5nZSB0aGUgc2Nyb2xsIG9mIGB3aW5kb3dgIGFmdGVyIGhpZ2hsaWdodGluZyBhbiBlbGVtZW50XG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9zY3JvbGxUb1xuICAgKiBAcGFyYW0ge1N0cmluZ30gc2Nyb2xsVG9cbiAgICogQHBhcmFtIHtPYmplY3R9IHRhcmdldEVsZW1lbnRcbiAgICogQHBhcmFtIHtPYmplY3R9IHRvb2x0aXBMYXllclxuICAgKi9cbiAgZnVuY3Rpb24gX3Njcm9sbFRvKHNjcm9sbFRvLCB0YXJnZXRFbGVtZW50LCB0b29sdGlwTGF5ZXIpIHtcbiAgICBpZiAoc2Nyb2xsVG8gPT09ICdvZmYnKSByZXR1cm47ICBcbiAgICB2YXIgcmVjdDtcblxuICAgIGlmICghdGhpcy5fb3B0aW9ucy5zY3JvbGxUb0VsZW1lbnQpIHJldHVybjtcblxuICAgIGlmIChzY3JvbGxUbyA9PT0gJ3Rvb2x0aXAnKSB7XG4gICAgICByZWN0ID0gdG9vbHRpcExheWVyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZWN0ID0gdGFyZ2V0RWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIH1cblxuICAgIGlmICghX2VsZW1lbnRJblZpZXdwb3J0KHRhcmdldEVsZW1lbnQuZWxlbWVudCkpIHtcbiAgICAgIHZhciB3aW5IZWlnaHQgPSBfZ2V0V2luU2l6ZSgpLmhlaWdodDtcbiAgICAgIHZhciB0b3AgPSByZWN0LmJvdHRvbSAtIChyZWN0LmJvdHRvbSAtIHJlY3QudG9wKTtcblxuICAgICAgLy8gVE9ETyAoYWZzaGlubSk6IGRvIHdlIG5lZWQgc2Nyb2xsIHBhZGRpbmcgbm93P1xuICAgICAgLy8gSSBoYXZlIGNoYW5nZWQgdGhlIHNjcm9sbCBvcHRpb24gYW5kIG5vdyBpdCBzY3JvbGxzIHRoZSB3aW5kb3cgdG9cbiAgICAgIC8vIHRoZSBjZW50ZXIgb2YgdGhlIHRhcmdldCBlbGVtZW50IG9yIHRvb2x0aXAuXG5cbiAgICAgIGlmICh0b3AgPCAwIHx8IHRhcmdldEVsZW1lbnQuZWxlbWVudC5jbGllbnRIZWlnaHQgPiB3aW5IZWlnaHQpIHtcbiAgICAgICAgd2luZG93LnNjcm9sbEJ5KDAsIHJlY3QudG9wIC0gKCh3aW5IZWlnaHQgLyAyKSAtICAocmVjdC5oZWlnaHQgLyAyKSkgLSB0aGlzLl9vcHRpb25zLnNjcm9sbFBhZGRpbmcpOyAvLyAzMHB4IHBhZGRpbmcgZnJvbSBlZGdlIHRvIGxvb2sgbmljZVxuXG4gICAgICAvL1Njcm9sbCBkb3duXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB3aW5kb3cuc2Nyb2xsQnkoMCwgcmVjdC50b3AgLSAoKHdpbkhlaWdodCAvIDIpIC0gIChyZWN0LmhlaWdodCAvIDIpKSArIHRoaXMuX29wdGlvbnMuc2Nyb2xsUGFkZGluZyk7IC8vIDMwcHggcGFkZGluZyBmcm9tIGVkZ2UgdG8gbG9vayBuaWNlXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRvIHJlbW92ZSBhbGwgc2hvdyBlbGVtZW50KHMpXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9yZW1vdmVTaG93RWxlbWVudFxuICAgKi9cbiAgZnVuY3Rpb24gX3JlbW92ZVNob3dFbGVtZW50KCkge1xuICAgIHZhciBlbG1zID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCgnLmludHJvanMtc2hvd0VsZW1lbnQnKTtcblxuICAgIF9mb3JFYWNoKGVsbXMsIGZ1bmN0aW9uIChlbG0pIHtcbiAgICAgIF9yZW1vdmVDbGFzcyhlbG0sIC9pbnRyb2pzLVthLXpBLVpdKy9nKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUbyBzZXQgdGhlIHNob3cgZWxlbWVudFxuICAgKiBUaGlzIGZ1bmN0aW9uIHNldCBhIHJlbGF0aXZlIChpbiBtb3N0IGNhc2VzKSBwb3NpdGlvbiBhbmQgY2hhbmdlcyB0aGUgei1pbmRleFxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfc2V0U2hvd0VsZW1lbnRcbiAgICogQHBhcmFtIHtPYmplY3R9IHRhcmdldEVsZW1lbnRcbiAgICovXG4gIGZ1bmN0aW9uIF9zZXRTaG93RWxlbWVudCh0YXJnZXRFbGVtZW50KSB7XG4gICAgdmFyIHBhcmVudEVsbTtcbiAgICAvLyB3ZSBuZWVkIHRvIGFkZCB0aGlzIHNob3cgZWxlbWVudCBjbGFzcyB0byB0aGUgcGFyZW50IG9mIFNWRyBlbGVtZW50c1xuICAgIC8vIGJlY2F1c2UgdGhlIFNWRyBlbGVtZW50cyBjYW4ndCBoYXZlIGluZGVwZW5kZW50IHotaW5kZXhcbiAgICBpZiAodGFyZ2V0RWxlbWVudC5lbGVtZW50IGluc3RhbmNlb2YgU1ZHRWxlbWVudCkge1xuICAgICAgcGFyZW50RWxtID0gdGFyZ2V0RWxlbWVudC5lbGVtZW50LnBhcmVudE5vZGU7XG5cbiAgICAgIHdoaWxlICh0YXJnZXRFbGVtZW50LmVsZW1lbnQucGFyZW50Tm9kZSAhPT0gbnVsbCkge1xuICAgICAgICBpZiAoIXBhcmVudEVsbS50YWdOYW1lIHx8IHBhcmVudEVsbS50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdib2R5JykgYnJlYWs7XG5cbiAgICAgICAgaWYgKHBhcmVudEVsbS50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdzdmcnKSB7XG4gICAgICAgICAgX2FkZENsYXNzKHBhcmVudEVsbSwgJ2ludHJvanMtc2hvd0VsZW1lbnQgaW50cm9qcy1yZWxhdGl2ZVBvc2l0aW9uJyk7XG4gICAgICAgIH1cblxuICAgICAgICBwYXJlbnRFbG0gPSBwYXJlbnRFbG0ucGFyZW50Tm9kZTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBfYWRkQ2xhc3ModGFyZ2V0RWxlbWVudC5lbGVtZW50LCAnaW50cm9qcy1zaG93RWxlbWVudCcpO1xuXG4gICAgdmFyIGN1cnJlbnRFbGVtZW50UG9zaXRpb24gPSBfZ2V0UHJvcFZhbHVlKHRhcmdldEVsZW1lbnQuZWxlbWVudCwgJ3Bvc2l0aW9uJyk7XG4gICAgaWYgKGN1cnJlbnRFbGVtZW50UG9zaXRpb24gIT09ICdhYnNvbHV0ZScgJiZcbiAgICAgICAgY3VycmVudEVsZW1lbnRQb3NpdGlvbiAhPT0gJ3JlbGF0aXZlJyAmJlxuICAgICAgICBjdXJyZW50RWxlbWVudFBvc2l0aW9uICE9PSAnZml4ZWQnKSB7XG4gICAgICAvL2NoYW5nZSB0byBuZXcgaW50cm8gaXRlbVxuICAgICAgX2FkZENsYXNzKHRhcmdldEVsZW1lbnQuZWxlbWVudCwgJ2ludHJvanMtcmVsYXRpdmVQb3NpdGlvbicpO1xuICAgIH1cblxuICAgIHBhcmVudEVsbSA9IHRhcmdldEVsZW1lbnQuZWxlbWVudC5wYXJlbnROb2RlO1xuICAgIHdoaWxlIChwYXJlbnRFbG0gIT09IG51bGwpIHtcbiAgICAgIGlmICghcGFyZW50RWxtLnRhZ05hbWUgfHwgcGFyZW50RWxtLnRhZ05hbWUudG9Mb3dlckNhc2UoKSA9PT0gJ2JvZHknKSBicmVhaztcblxuICAgICAgLy9maXggVGhlIFN0YWNraW5nIENvbnRleHQgcHJvYmxlbS5cbiAgICAgIC8vTW9yZSBkZXRhaWw6IGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0d1aWRlL0NTUy9VbmRlcnN0YW5kaW5nX3pfaW5kZXgvVGhlX3N0YWNraW5nX2NvbnRleHRcbiAgICAgIHZhciB6SW5kZXggPSBfZ2V0UHJvcFZhbHVlKHBhcmVudEVsbSwgJ3otaW5kZXgnKTtcbiAgICAgIHZhciBvcGFjaXR5ID0gcGFyc2VGbG9hdChfZ2V0UHJvcFZhbHVlKHBhcmVudEVsbSwgJ29wYWNpdHknKSk7XG4gICAgICB2YXIgdHJhbnNmb3JtID0gX2dldFByb3BWYWx1ZShwYXJlbnRFbG0sICd0cmFuc2Zvcm0nKSB8fCBfZ2V0UHJvcFZhbHVlKHBhcmVudEVsbSwgJy13ZWJraXQtdHJhbnNmb3JtJykgfHwgX2dldFByb3BWYWx1ZShwYXJlbnRFbG0sICctbW96LXRyYW5zZm9ybScpIHx8IF9nZXRQcm9wVmFsdWUocGFyZW50RWxtLCAnLW1zLXRyYW5zZm9ybScpIHx8IF9nZXRQcm9wVmFsdWUocGFyZW50RWxtLCAnLW8tdHJhbnNmb3JtJyk7XG4gICAgICBpZiAoL1swLTldKy8udGVzdCh6SW5kZXgpIHx8IG9wYWNpdHkgPCAxIHx8ICh0cmFuc2Zvcm0gIT09ICdub25lJyAmJiB0cmFuc2Zvcm0gIT09IHVuZGVmaW5lZCkpIHtcbiAgICAgICAgX2FkZENsYXNzKHBhcmVudEVsbSwgJ2ludHJvanMtZml4UGFyZW50Jyk7XG4gICAgICB9XG5cbiAgICAgIHBhcmVudEVsbSA9IHBhcmVudEVsbS5wYXJlbnROb2RlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAqIEl0ZXJhdGVzIGFycmF5c1xuICAqXG4gICogQHBhcmFtIHtBcnJheX0gYXJyXG4gICogQHBhcmFtIHtGdW5jdGlvbn0gZm9yRWFjaEZuY1xuICAqIEBwYXJhbSB7RnVuY3Rpb259IGNvbXBsZXRlRm5jXG4gICogQHJldHVybiB7TnVsbH1cbiAgKi9cbiAgZnVuY3Rpb24gX2ZvckVhY2goYXJyLCBmb3JFYWNoRm5jLCBjb21wbGV0ZUZuYykge1xuICAgIC8vIGluIGNhc2UgYXJyIGlzIGFuIGVtcHR5IHF1ZXJ5IHNlbGVjdG9yIG5vZGUgbGlzdFxuICAgIGlmIChhcnIpIHtcbiAgICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBhcnIubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgICAgZm9yRWFjaEZuYyhhcnJbaV0sIGkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0eXBlb2YoY29tcGxldGVGbmMpID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICBjb21wbGV0ZUZuYygpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAqIE1hcmsgYW55IG9iamVjdCB3aXRoIGFuIGluY3JlbWVudGluZyBudW1iZXJcbiAgKiB1c2VkIGZvciBrZWVwaW5nIHRyYWNrIG9mIG9iamVjdHNcbiAgKlxuICAqIEBwYXJhbSBPYmplY3Qgb2JqICAgQW55IG9iamVjdCBvciBET00gRWxlbWVudFxuICAqIEBwYXJhbSBTdHJpbmcga2V5XG4gICogQHJldHVybiBPYmplY3RcbiAgKi9cbiAgdmFyIF9zdGFtcCA9IChmdW5jdGlvbiAoKSB7XG4gICAgdmFyIGtleXMgPSB7fTtcbiAgICByZXR1cm4gZnVuY3Rpb24gc3RhbXAgKG9iaiwga2V5KSB7XG4gICAgICBcbiAgICAgIC8vIGdldCBncm91cCBrZXlcbiAgICAgIGtleSA9IGtleSB8fCAnaW50cm9qcy1zdGFtcCc7XG5cbiAgICAgIC8vIGVhY2ggZ3JvdXAgaW5jcmVtZW50cyBmcm9tIDBcbiAgICAgIGtleXNba2V5XSA9IGtleXNba2V5XSB8fCAwO1xuXG4gICAgICAvLyBzdGFtcCBvbmx5IG9uY2UgcGVyIG9iamVjdFxuICAgICAgaWYgKG9ialtrZXldID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gaW5jcmVtZW50IGtleSBmb3IgZWFjaCBuZXcgb2JqZWN0XG4gICAgICAgIG9ialtrZXldID0ga2V5c1trZXldKys7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBvYmpba2V5XTtcbiAgICB9O1xuICB9KSgpO1xuXG4gIC8qKlxuICAqIERPTUV2ZW50IEhhbmRsZXMgYWxsIERPTSBldmVudHNcbiAgKlxuICAqIG1ldGhvZHM6XG4gICpcbiAgKiBvbiAtIGFkZCBldmVudCBoYW5kbGVyXG4gICogb2ZmIC0gcmVtb3ZlIGV2ZW50XG4gICovXG4gIHZhciBET01FdmVudCA9IChmdW5jdGlvbiAoKSB7XG4gICAgZnVuY3Rpb24gRE9NRXZlbnQgKCkge1xuICAgICAgdmFyIGV2ZW50c19rZXkgPSAnaW50cm9qc19ldmVudCc7XG4gICAgICBcbiAgICAgIC8qKlxuICAgICAgKiBHZXRzIGEgdW5pcXVlIElEIGZvciBhbiBldmVudCBsaXN0ZW5lclxuICAgICAgKlxuICAgICAgKiBAcGFyYW0gT2JqZWN0IG9ialxuICAgICAgKiBAcGFyYW0gU3RyaW5nIHR5cGUgICAgICAgIGV2ZW50IHR5cGVcbiAgICAgICogQHBhcmFtIEZ1bmN0aW9uIGxpc3RlbmVyXG4gICAgICAqIEBwYXJhbSBPYmplY3QgY29udGV4dFxuICAgICAgKiBAcmV0dXJuIFN0cmluZ1xuICAgICAgKi9cbiAgICAgIHRoaXMuX2lkID0gZnVuY3Rpb24gKG9iaiwgdHlwZSwgbGlzdGVuZXIsIGNvbnRleHQpIHtcbiAgICAgICAgcmV0dXJuIHR5cGUgKyBfc3RhbXAobGlzdGVuZXIpICsgKGNvbnRleHQgPyAnXycgKyBfc3RhbXAoY29udGV4dCkgOiAnJyk7XG4gICAgICB9O1xuXG4gICAgICAvKipcbiAgICAgICogQWRkcyBldmVudCBsaXN0ZW5lclxuICAgICAgKlxuICAgICAgKiBAcGFyYW0gT2JqZWN0IG9ialxuICAgICAgKiBAcGFyYW0gU3RyaW5nIHR5cGUgICAgICAgIGV2ZW50IHR5cGVcbiAgICAgICogQHBhcmFtIEZ1bmN0aW9uIGxpc3RlbmVyXG4gICAgICAqIEBwYXJhbSBPYmplY3QgY29udGV4dFxuICAgICAgKiBAcGFyYW0gQm9vbGVhbiB1c2VDYXB0dXJlXG4gICAgICAqIEByZXR1cm4gbnVsbFxuICAgICAgKi9cbiAgICAgIHRoaXMub24gPSBmdW5jdGlvbiAob2JqLCB0eXBlLCBsaXN0ZW5lciwgY29udGV4dCwgdXNlQ2FwdHVyZSkge1xuICAgICAgICB2YXIgaWQgPSB0aGlzLl9pZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpLFxuICAgICAgICAgICAgaGFuZGxlciA9IGZ1bmN0aW9uIChlKSB7XG4gICAgICAgICAgICAgIHJldHVybiBsaXN0ZW5lci5jYWxsKGNvbnRleHQgfHwgb2JqLCBlIHx8IHdpbmRvdy5ldmVudCk7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgIGlmICgnYWRkRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XG4gICAgICAgICAgb2JqLmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlciwgdXNlQ2FwdHVyZSk7XG4gICAgICAgIH0gZWxzZSBpZiAoJ2F0dGFjaEV2ZW50JyBpbiBvYmopIHtcbiAgICAgICAgICBvYmouYXR0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgb2JqW2V2ZW50c19rZXldID0gb2JqW2V2ZW50c19rZXldIHx8IHt9O1xuICAgICAgICBvYmpbZXZlbnRzX2tleV1baWRdID0gaGFuZGxlcjtcbiAgICAgIH07XG5cbiAgICAgIC8qKlxuICAgICAgKiBSZW1vdmVzIGV2ZW50IGxpc3RlbmVyXG4gICAgICAqXG4gICAgICAqIEBwYXJhbSBPYmplY3Qgb2JqXG4gICAgICAqIEBwYXJhbSBTdHJpbmcgdHlwZSAgICAgICAgZXZlbnQgdHlwZVxuICAgICAgKiBAcGFyYW0gRnVuY3Rpb24gbGlzdGVuZXJcbiAgICAgICogQHBhcmFtIE9iamVjdCBjb250ZXh0XG4gICAgICAqIEBwYXJhbSBCb29sZWFuIHVzZUNhcHR1cmVcbiAgICAgICogQHJldHVybiBudWxsXG4gICAgICAqL1xuICAgICAgdGhpcy5vZmYgPSBmdW5jdGlvbiAob2JqLCB0eXBlLCBsaXN0ZW5lciwgY29udGV4dCwgdXNlQ2FwdHVyZSkge1xuICAgICAgICB2YXIgaWQgPSB0aGlzLl9pZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpLFxuICAgICAgICAgICAgaGFuZGxlciA9IG9ialtldmVudHNfa2V5XSAmJiBvYmpbZXZlbnRzX2tleV1baWRdO1xuXG4gICAgICAgIGlmICghaGFuZGxlcikge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgncmVtb3ZlRXZlbnRMaXN0ZW5lcicgaW4gb2JqKSB7XG4gICAgICAgICAgb2JqLnJlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgaGFuZGxlciwgdXNlQ2FwdHVyZSk7XG4gICAgICAgIH0gZWxzZSBpZiAoJ2RldGFjaEV2ZW50JyBpbiBvYmopIHtcbiAgICAgICAgICBvYmouZGV0YWNoRXZlbnQoJ29uJyArIHR5cGUsIGhhbmRsZXIpO1xuICAgICAgICB9XG5cbiAgICAgICAgb2JqW2V2ZW50c19rZXldW2lkXSA9IG51bGw7XG4gICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiBuZXcgRE9NRXZlbnQoKTtcbiAgfSkoKTtcblxuICAvKipcbiAgICogQXBwZW5kIGEgY2xhc3MgdG8gYW4gZWxlbWVudFxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfYWRkQ2xhc3NcbiAgICogQHBhcmFtIHtPYmplY3R9IGVsZW1lbnRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGNsYXNzTmFtZVxuICAgKiBAcmV0dXJucyBudWxsXG4gICAqL1xuICBmdW5jdGlvbiBfYWRkQ2xhc3MoZWxlbWVudCwgY2xhc3NOYW1lKSB7XG4gICAgaWYgKGVsZW1lbnQgaW5zdGFuY2VvZiBTVkdFbGVtZW50KSB7XG4gICAgICAvLyBzdmdcbiAgICAgIHZhciBwcmUgPSBlbGVtZW50LmdldEF0dHJpYnV0ZSgnY2xhc3MnKSB8fCAnJztcblxuICAgICAgZWxlbWVudC5zZXRBdHRyaWJ1dGUoJ2NsYXNzJywgcHJlICsgJyAnICsgY2xhc3NOYW1lKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGVsZW1lbnQuY2xhc3NMaXN0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gY2hlY2sgZm9yIG1vZGVybiBjbGFzc0xpc3QgcHJvcGVydHlcbiAgICAgICAgdmFyIGNsYXNzZXMgPSBjbGFzc05hbWUuc3BsaXQoJyAnKTtcbiAgICAgICAgX2ZvckVhY2goY2xhc3NlcywgZnVuY3Rpb24gKGNscykge1xuICAgICAgICAgIGVsZW1lbnQuY2xhc3NMaXN0LmFkZCggY2xzICk7XG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIGlmICghZWxlbWVudC5jbGFzc05hbWUubWF0Y2goIGNsYXNzTmFtZSApKSB7XG4gICAgICAgIC8vIGNoZWNrIGlmIGVsZW1lbnQgZG9lc24ndCBhbHJlYWR5IGhhdmUgY2xhc3NOYW1lXG4gICAgICAgIGVsZW1lbnQuY2xhc3NOYW1lICs9ICcgJyArIGNsYXNzTmFtZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGEgY2xhc3MgZnJvbSBhbiBlbGVtZW50XG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9yZW1vdmVDbGFzc1xuICAgKiBAcGFyYW0ge09iamVjdH0gZWxlbWVudFxuICAgKiBAcGFyYW0ge1JlZ0V4cHxTdHJpbmd9IGNsYXNzTmFtZVJlZ2V4IGNhbiBiZSByZWdleCBvciBzdHJpbmdcbiAgICogQHJldHVybnMgbnVsbFxuICAgKi9cbiAgZnVuY3Rpb24gX3JlbW92ZUNsYXNzKGVsZW1lbnQsIGNsYXNzTmFtZVJlZ2V4KSB7XG4gICAgaWYgKGVsZW1lbnQgaW5zdGFuY2VvZiBTVkdFbGVtZW50KSB7XG4gICAgICB2YXIgcHJlID0gZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2NsYXNzJykgfHwgJyc7XG5cbiAgICAgIGVsZW1lbnQuc2V0QXR0cmlidXRlKCdjbGFzcycsIHByZS5yZXBsYWNlKGNsYXNzTmFtZVJlZ2V4LCAnJykucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKGNsYXNzTmFtZVJlZ2V4LCAnJykucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgYW4gZWxlbWVudCBDU1MgcHJvcGVydHkgb24gdGhlIHBhZ2VcbiAgICogVGhhbmtzIHRvIEphdmFTY3JpcHQgS2l0OiBodHRwOi8vd3d3LmphdmFzY3JpcHRraXQuY29tL2RodG1sdHV0b3JzL2RodG1sY2FzY2FkZTQuc2h0bWxcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2dldFByb3BWYWx1ZVxuICAgKiBAcGFyYW0ge09iamVjdH0gZWxlbWVudFxuICAgKiBAcGFyYW0ge1N0cmluZ30gcHJvcE5hbWVcbiAgICogQHJldHVybnMgRWxlbWVudCdzIHByb3BlcnR5IHZhbHVlXG4gICAqL1xuICBmdW5jdGlvbiBfZ2V0UHJvcFZhbHVlIChlbGVtZW50LCBwcm9wTmFtZSkge1xuICAgIHZhciBwcm9wVmFsdWUgPSAnJztcbiAgICBpZiAoZWxlbWVudC5jdXJyZW50U3R5bGUpIHsgLy9JRVxuICAgICAgcHJvcFZhbHVlID0gZWxlbWVudC5jdXJyZW50U3R5bGVbcHJvcE5hbWVdO1xuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQuZGVmYXVsdFZpZXcgJiYgZG9jdW1lbnQuZGVmYXVsdFZpZXcuZ2V0Q29tcHV0ZWRTdHlsZSkgeyAvL090aGVyc1xuICAgICAgcHJvcFZhbHVlID0gZG9jdW1lbnQuZGVmYXVsdFZpZXcuZ2V0Q29tcHV0ZWRTdHlsZShlbGVtZW50LCBudWxsKS5nZXRQcm9wZXJ0eVZhbHVlKHByb3BOYW1lKTtcbiAgICB9XG5cbiAgICAvL1ByZXZlbnQgZXhjZXB0aW9uIGluIElFXG4gICAgaWYgKHByb3BWYWx1ZSAmJiBwcm9wVmFsdWUudG9Mb3dlckNhc2UpIHtcbiAgICAgIHJldHVybiBwcm9wVmFsdWUudG9Mb3dlckNhc2UoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHByb3BWYWx1ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIHRvIHNlZSBpZiB0YXJnZXQgZWxlbWVudCAob3IgcGFyZW50cykgcG9zaXRpb24gaXMgZml4ZWQgb3Igbm90XG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9pc0ZpeGVkXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBlbGVtZW50XG4gICAqIEByZXR1cm5zIEJvb2xlYW5cbiAgICovXG4gIGZ1bmN0aW9uIF9pc0ZpeGVkIChlbGVtZW50KSB7XG4gICAgdmFyIHAgPSBlbGVtZW50LnBhcmVudE5vZGU7XG5cbiAgICBpZiAoIXAgfHwgcC5ub2RlTmFtZSA9PT0gJ0hUTUwnKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKF9nZXRQcm9wVmFsdWUoZWxlbWVudCwgJ3Bvc2l0aW9uJykgPT09ICdmaXhlZCcpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHJldHVybiBfaXNGaXhlZChwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm92aWRlcyBhIGNyb3NzLWJyb3dzZXIgd2F5IHRvIGdldCB0aGUgc2NyZWVuIGRpbWVuc2lvbnNcbiAgICogdmlhOiBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU4NjQ0NjcvaW50ZXJuZXQtZXhwbG9yZXItaW5uZXJoZWlnaHRcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2dldFdpblNpemVcbiAgICogQHJldHVybnMge09iamVjdH0gd2lkdGggYW5kIGhlaWdodCBhdHRyaWJ1dGVzXG4gICAqL1xuICBmdW5jdGlvbiBfZ2V0V2luU2l6ZSgpIHtcbiAgICBpZiAod2luZG93LmlubmVyV2lkdGggIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHsgd2lkdGg6IHdpbmRvdy5pbm5lcldpZHRoLCBoZWlnaHQ6IHdpbmRvdy5pbm5lckhlaWdodCB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgRCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcbiAgICAgIHJldHVybiB7IHdpZHRoOiBELmNsaWVudFdpZHRoLCBoZWlnaHQ6IEQuY2xpZW50SGVpZ2h0IH07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIHRvIHNlZSBpZiB0aGUgZWxlbWVudCBpcyBpbiB0aGUgdmlld3BvcnQgb3Igbm90XG4gICAqIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTIzOTk5L2hvdy10by10ZWxsLWlmLWEtZG9tLWVsZW1lbnQtaXMtdmlzaWJsZS1pbi10aGUtY3VycmVudC12aWV3cG9ydFxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfZWxlbWVudEluVmlld3BvcnRcbiAgICogQHBhcmFtIHtPYmplY3R9IGVsXG4gICAqL1xuICBmdW5jdGlvbiBfZWxlbWVudEluVmlld3BvcnQoZWwpIHtcbiAgICB2YXIgcmVjdCA9IGVsLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG4gICAgcmV0dXJuIChcbiAgICAgIHJlY3QudG9wID49IDAgJiZcbiAgICAgIHJlY3QubGVmdCA+PSAwICYmXG4gICAgICAocmVjdC5ib3R0b20rODApIDw9IHdpbmRvdy5pbm5lckhlaWdodCAmJiAvLyBhZGQgODAgdG8gZ2V0IHRoZSB0ZXh0IHJpZ2h0XG4gICAgICByZWN0LnJpZ2h0IDw9IHdpbmRvdy5pbm5lcldpZHRoXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgb3ZlcmxheSBsYXllciB0byB0aGUgcGFnZVxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfYWRkT3ZlcmxheUxheWVyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB0YXJnZXRFbG1cbiAgICovXG4gIGZ1bmN0aW9uIF9hZGRPdmVybGF5TGF5ZXIodGFyZ2V0RWxtKSB7XG4gICAgdmFyIG92ZXJsYXlMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpLFxuICAgICAgICBzdHlsZVRleHQgPSAnJyxcbiAgICAgICAgc2VsZiA9IHRoaXM7XG5cbiAgICAvL3NldCBjc3MgY2xhc3MgbmFtZVxuICAgIG92ZXJsYXlMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1vdmVybGF5JztcblxuICAgIC8vY2hlY2sgaWYgdGhlIHRhcmdldCBlbGVtZW50IGlzIGJvZHksIHdlIHNob3VsZCBjYWxjdWxhdGUgdGhlIHNpemUgb2Ygb3ZlcmxheSBsYXllciBpbiBhIGJldHRlciB3YXlcbiAgICBpZiAoIXRhcmdldEVsbS50YWdOYW1lIHx8IHRhcmdldEVsbS50YWdOYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdib2R5Jykge1xuICAgICAgc3R5bGVUZXh0ICs9ICd0b3A6IDA7Ym90dG9tOiAwOyBsZWZ0OiAwO3JpZ2h0OiAwO3Bvc2l0aW9uOiBmaXhlZDsnO1xuICAgICAgb3ZlcmxheUxheWVyLnN0eWxlLmNzc1RleHQgPSBzdHlsZVRleHQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vc2V0IG92ZXJsYXkgbGF5ZXIgcG9zaXRpb25cbiAgICAgIHZhciBlbGVtZW50UG9zaXRpb24gPSBfZ2V0T2Zmc2V0KHRhcmdldEVsbSk7XG4gICAgICBpZiAoZWxlbWVudFBvc2l0aW9uKSB7XG4gICAgICAgIHN0eWxlVGV4dCArPSAnd2lkdGg6ICcgKyBlbGVtZW50UG9zaXRpb24ud2lkdGggKyAncHg7IGhlaWdodDonICsgZWxlbWVudFBvc2l0aW9uLmhlaWdodCArICdweDsgdG9wOicgKyBlbGVtZW50UG9zaXRpb24udG9wICsgJ3B4O2xlZnQ6ICcgKyBlbGVtZW50UG9zaXRpb24ubGVmdCArICdweDsnO1xuICAgICAgICBvdmVybGF5TGF5ZXIuc3R5bGUuY3NzVGV4dCA9IHN0eWxlVGV4dDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0YXJnZXRFbG0uYXBwZW5kQ2hpbGQob3ZlcmxheUxheWVyKTtcblxuICAgIG92ZXJsYXlMYXllci5vbmNsaWNrID0gZnVuY3Rpb24oKSB7XG4gICAgICBpZiAoc2VsZi5fb3B0aW9ucy5leGl0T25PdmVybGF5Q2xpY2sgPT09IHRydWUpIHtcbiAgICAgICAgX2V4aXRJbnRyby5jYWxsKHNlbGYsIHRhcmdldEVsbSk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHdpbmRvdy5zZXRUaW1lb3V0KGZ1bmN0aW9uKCkge1xuICAgICAgc3R5bGVUZXh0ICs9ICdvcGFjaXR5OiAnICsgc2VsZi5fb3B0aW9ucy5vdmVybGF5T3BhY2l0eS50b1N0cmluZygpICsgJzsnO1xuICAgICAgb3ZlcmxheUxheWVyLnN0eWxlLmNzc1RleHQgPSBzdHlsZVRleHQ7XG4gICAgfSwgMTApO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBvcGVuIGhpbnQgKHRvb2x0aXAgaGludClcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX3JlbW92ZUhpbnRUb29sdGlwXG4gICAqL1xuICBmdW5jdGlvbiBfcmVtb3ZlSGludFRvb2x0aXAoKSB7XG4gICAgdmFyIHRvb2x0aXAgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oaW50UmVmZXJlbmNlJyk7XG5cbiAgICBpZiAodG9vbHRpcCkge1xuICAgICAgdmFyIHN0ZXAgPSB0b29sdGlwLmdldEF0dHJpYnV0ZSgnZGF0YS1zdGVwJyk7XG4gICAgICB0b29sdGlwLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQodG9vbHRpcCk7XG4gICAgICByZXR1cm4gc3RlcDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgcGFyc2luZyBoaW50IGl0ZW1zXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAcGFyYW0ge09iamVjdH0gdGFyZ2V0RWxtXG4gICAqIEBtZXRob2QgX3N0YXJ0SGludFxuICAgKi9cbiAgZnVuY3Rpb24gX3BvcHVsYXRlSGludHModGFyZ2V0RWxtKSB7XG5cbiAgICB0aGlzLl9pbnRyb0l0ZW1zID0gW107XG5cbiAgICBpZiAodGhpcy5fb3B0aW9ucy5oaW50cykge1xuICAgICAgX2ZvckVhY2godGhpcy5fb3B0aW9ucy5oaW50cywgZnVuY3Rpb24gKGhpbnQpIHtcbiAgICAgICAgdmFyIGN1cnJlbnRJdGVtID0gX2Nsb25lT2JqZWN0KGhpbnQpO1xuXG4gICAgICAgIGlmICh0eXBlb2YoY3VycmVudEl0ZW0uZWxlbWVudCkgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgLy9ncmFiIHRoZSBlbGVtZW50IHdpdGggZ2l2ZW4gc2VsZWN0b3IgZnJvbSB0aGUgcGFnZVxuICAgICAgICAgIGN1cnJlbnRJdGVtLmVsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGN1cnJlbnRJdGVtLmVsZW1lbnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgY3VycmVudEl0ZW0uaGludFBvc2l0aW9uID0gY3VycmVudEl0ZW0uaGludFBvc2l0aW9uIHx8IHRoaXMuX29wdGlvbnMuaGludFBvc2l0aW9uO1xuICAgICAgICBjdXJyZW50SXRlbS5oaW50QW5pbWF0aW9uID0gY3VycmVudEl0ZW0uaGludEFuaW1hdGlvbiB8fCB0aGlzLl9vcHRpb25zLmhpbnRBbmltYXRpb247XG5cbiAgICAgICAgaWYgKGN1cnJlbnRJdGVtLmVsZW1lbnQgIT09IG51bGwpIHtcbiAgICAgICAgICB0aGlzLl9pbnRyb0l0ZW1zLnB1c2goY3VycmVudEl0ZW0pO1xuICAgICAgICB9XG4gICAgICB9LmJpbmQodGhpcykpO1xuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgaGludHMgPSB0YXJnZXRFbG0ucXVlcnlTZWxlY3RvckFsbCgnKltkYXRhLWhpbnRdJyk7XG5cbiAgICAgIGlmICghaGludHMgfHwgIWhpbnRzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIC8vZmlyc3QgYWRkIGludHJvIGl0ZW1zIHdpdGggZGF0YS1zdGVwXG4gICAgICBfZm9yRWFjaChoaW50cywgZnVuY3Rpb24gKGN1cnJlbnRFbGVtZW50KSB7XG4gICAgICAgIC8vIGhpbnQgYW5pbWF0aW9uXG4gICAgICAgIHZhciBoaW50QW5pbWF0aW9uID0gY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWhpbnRhbmltYXRpb24nKTtcblxuICAgICAgICBpZiAoaGludEFuaW1hdGlvbikge1xuICAgICAgICAgIGhpbnRBbmltYXRpb24gPSAoaGludEFuaW1hdGlvbiA9PT0gJ3RydWUnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBoaW50QW5pbWF0aW9uID0gdGhpcy5fb3B0aW9ucy5oaW50QW5pbWF0aW9uO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5faW50cm9JdGVtcy5wdXNoKHtcbiAgICAgICAgICBlbGVtZW50OiBjdXJyZW50RWxlbWVudCxcbiAgICAgICAgICBoaW50OiBjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtaGludCcpLFxuICAgICAgICAgIGhpbnRQb3NpdGlvbjogY3VycmVudEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLWhpbnRwb3NpdGlvbicpIHx8IHRoaXMuX29wdGlvbnMuaGludFBvc2l0aW9uLFxuICAgICAgICAgIGhpbnRBbmltYXRpb246IGhpbnRBbmltYXRpb24sXG4gICAgICAgICAgdG9vbHRpcENsYXNzOiBjdXJyZW50RWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtdG9vbHRpcGNsYXNzJyksXG4gICAgICAgICAgcG9zaXRpb246IGN1cnJlbnRFbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1wb3NpdGlvbicpIHx8IHRoaXMuX29wdGlvbnMudG9vbHRpcFBvc2l0aW9uXG4gICAgICAgIH0pO1xuICAgICAgfS5iaW5kKHRoaXMpKTtcbiAgICB9XG5cbiAgICBfYWRkSGludHMuY2FsbCh0aGlzKTtcblxuICAgIC8qIFxuICAgIHRvZG86XG4gICAgdGhlc2UgZXZlbnRzIHNob3VsZCBiZSByZW1vdmVkIGF0IHNvbWUgcG9pbnQgXG4gICAgKi9cbiAgICBET01FdmVudC5vbihkb2N1bWVudCwgJ2NsaWNrJywgX3JlbW92ZUhpbnRUb29sdGlwLCB0aGlzLCBmYWxzZSk7XG4gICAgRE9NRXZlbnQub24od2luZG93LCAncmVzaXplJywgX3JlQWxpZ25IaW50cywgdGhpcywgdHJ1ZSk7XG4gIH1cblxuICAvKipcbiAgICogUmUtYWxpZ25zIGFsbCBoaW50IGVsZW1lbnRzXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9yZUFsaWduSGludHNcbiAgICovXG4gIGZ1bmN0aW9uIF9yZUFsaWduSGludHMoKSB7XG4gICAgX2ZvckVhY2godGhpcy5faW50cm9JdGVtcywgZnVuY3Rpb24gKGl0ZW0pIHtcbiAgICAgIGlmICh0eXBlb2YoaXRlbS50YXJnZXRFbGVtZW50KSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBfYWxpZ25IaW50UG9zaXRpb24uY2FsbCh0aGlzLCBpdGVtLmhpbnRQb3NpdGlvbiwgaXRlbS5lbGVtZW50LCBpdGVtLnRhcmdldEVsZW1lbnQpO1xuICAgIH0uYmluZCh0aGlzKSk7XG4gIH1cblxuICAvKipcbiAgKiBHZXQgYSBxdWVyeXNlbGVjdG9yIHdpdGhpbiB0aGUgaGludCB3cmFwcGVyXG4gICpcbiAgKiBAcGFyYW0ge1N0cmluZ30gc2VsZWN0b3JcbiAgKiBAcmV0dXJuIHtOb2RlTGlzdHxBcnJheX1cbiAgKi9cbiAgZnVuY3Rpb24gX2hpbnRRdWVyeVNlbGVjdG9yQWxsKHNlbGVjdG9yKSB7XG4gICAgdmFyIGhpbnRzV3JhcHBlciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWhpbnRzJyk7XG4gICAgcmV0dXJuIChoaW50c1dyYXBwZXIpID8gaGludHNXcmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoc2VsZWN0b3IpIDogW107XG4gIH1cblxuICAvKipcbiAgICogSGlkZSBhIGhpbnRcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2hpZGVIaW50XG4gICAqL1xuICBmdW5jdGlvbiBfaGlkZUhpbnQoc3RlcElkKSB7XG4gICAgdmFyIGhpbnQgPSBfaGludFF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLWhpbnRbZGF0YS1zdGVwPVwiJyArIHN0ZXBJZCArICdcIl0nKVswXTtcbiAgICBcbiAgICBfcmVtb3ZlSGludFRvb2x0aXAuY2FsbCh0aGlzKTtcblxuICAgIGlmIChoaW50KSB7XG4gICAgICBfYWRkQ2xhc3MoaGludCwgJ2ludHJvanMtaGlkZWhpbnQnKTtcbiAgICB9XG5cbiAgICAvLyBjYWxsIHRoZSBjYWxsYmFjayBmdW5jdGlvbiAoaWYgYW55KVxuICAgIGlmICh0eXBlb2YgKHRoaXMuX2hpbnRDbG9zZUNhbGxiYWNrKSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHRoaXMuX2hpbnRDbG9zZUNhbGxiYWNrLmNhbGwodGhpcywgc3RlcElkKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGlkZSBhbGwgaGludHNcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2hpZGVIaW50c1xuICAgKi9cbiAgZnVuY3Rpb24gX2hpZGVIaW50cygpIHtcbiAgICB2YXIgaGludHMgPSBfaGludFF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLWhpbnQnKTtcblxuICAgIF9mb3JFYWNoKGhpbnRzLCBmdW5jdGlvbiAoaGludCkge1xuICAgICAgX2hpZGVIaW50LmNhbGwodGhpcywgaGludC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RlcCcpKTtcbiAgICB9LmJpbmQodGhpcykpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNob3cgYWxsIGhpbnRzXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9zaG93SGludHNcbiAgICovXG4gIGZ1bmN0aW9uIF9zaG93SGludHMoKSB7XG4gICAgdmFyIGhpbnRzID0gX2hpbnRRdWVyeVNlbGVjdG9yQWxsKCcuaW50cm9qcy1oaW50Jyk7XG5cbiAgICBpZiAoaGludHMgJiYgaGludHMubGVuZ3RoKSB7XG4gICAgICBfZm9yRWFjaChoaW50cywgZnVuY3Rpb24gKGhpbnQpIHtcbiAgICAgICAgX3Nob3dIaW50LmNhbGwodGhpcywgaGludC5nZXRBdHRyaWJ1dGUoJ2RhdGEtc3RlcCcpKTtcbiAgICAgIH0uYmluZCh0aGlzKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIF9wb3B1bGF0ZUhpbnRzLmNhbGwodGhpcywgdGhpcy5fdGFyZ2V0RWxlbWVudCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNob3cgYSBoaW50XG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9zaG93SGludFxuICAgKi9cbiAgZnVuY3Rpb24gX3Nob3dIaW50KHN0ZXBJZCkge1xuICAgIHZhciBoaW50ID0gX2hpbnRRdWVyeVNlbGVjdG9yQWxsKCcuaW50cm9qcy1oaW50W2RhdGEtc3RlcD1cIicgKyBzdGVwSWQgKyAnXCJdJylbMF07XG5cbiAgICBpZiAoaGludCkge1xuICAgICAgX3JlbW92ZUNsYXNzKGhpbnQsIC9pbnRyb2pzLWhpZGVoaW50L2cpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGFsbCBoaW50IGVsZW1lbnRzIG9uIHRoZSBwYWdlXG4gICAqIFVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIGRlc3Ryb3kgdGhlIGVsZW1lbnRzIGFuZCBhZGQgdGhlbSBhZ2FpbiAoZS5nLiBhIG1vZGFsIG9yIHBvcHVwKVxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfcmVtb3ZlSGludHNcbiAgICovXG4gIGZ1bmN0aW9uIF9yZW1vdmVIaW50cygpIHtcbiAgICB2YXIgaGludHMgPSBfaGludFF1ZXJ5U2VsZWN0b3JBbGwoJy5pbnRyb2pzLWhpbnQnKTtcblxuICAgIF9mb3JFYWNoKGhpbnRzLCBmdW5jdGlvbiAoaGludCkge1xuICAgICAgX3JlbW92ZUhpbnQuY2FsbCh0aGlzLCBoaW50LmdldEF0dHJpYnV0ZSgnZGF0YS1zdGVwJykpO1xuICAgIH0uYmluZCh0aGlzKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIG9uZSBzaW5nbGUgaGludCBlbGVtZW50IGZyb20gdGhlIHBhZ2VcbiAgICogVXNlZnVsIHdoZW4geW91IHdhbnQgdG8gZGVzdHJveSB0aGUgZWxlbWVudCBhbmQgYWRkIHRoZW0gYWdhaW4gKGUuZy4gYSBtb2RhbCBvciBwb3B1cClcbiAgICogVXNlIHJlbW92ZUhpbnRzIGlmIHlvdSB3YW50IHRvIHJlbW92ZSBhbGwgZWxlbWVudHMuXG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9yZW1vdmVIaW50XG4gICAqL1xuICBmdW5jdGlvbiBfcmVtb3ZlSGludChzdGVwSWQpIHtcbiAgICB2YXIgaGludCA9IF9oaW50UXVlcnlTZWxlY3RvckFsbCgnLmludHJvanMtaGludFtkYXRhLXN0ZXA9XCInICsgc3RlcElkICsgJ1wiXScpWzBdO1xuXG4gICAgaWYgKGhpbnQpIHtcbiAgICAgIGhpbnQucGFyZW50Tm9kZS5yZW1vdmVDaGlsZChoaW50KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWRkIGFsbCBhdmFpbGFibGUgaGludHMgdG8gdGhlIHBhZ2VcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2FkZEhpbnRzXG4gICAqL1xuICBmdW5jdGlvbiBfYWRkSGludHMoKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gICAgdmFyIGhpbnRzV3JhcHBlciA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWhpbnRzJyk7XG5cbiAgICBpZiAoaGludHNXcmFwcGVyID09PSBudWxsKSB7XG4gICAgICBoaW50c1dyYXBwZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIGhpbnRzV3JhcHBlci5jbGFzc05hbWUgPSAnaW50cm9qcy1oaW50cyc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgKiBSZXR1cm5zIGFuIGV2ZW50IGhhbmRsZXIgdW5pcXVlIHRvIHRoZSBoaW50IGl0ZXJhdGlvblxuICAgICogXG4gICAgKiBAcGFyYW0ge0ludGVnZXJ9IGlcbiAgICAqIEByZXR1cm4ge0Z1bmN0aW9ufVxuICAgICovXG4gICAgdmFyIGdldEhpbnRDbGljayA9IGZ1bmN0aW9uIChpKSB7XG4gICAgICByZXR1cm4gZnVuY3Rpb24oZSkge1xuICAgICAgICB2YXIgZXZ0ID0gZSA/IGUgOiB3aW5kb3cuZXZlbnQ7XG4gICAgICAgIFxuICAgICAgICBpZiAoZXZ0LnN0b3BQcm9wYWdhdGlvbikge1xuICAgICAgICAgIGV2dC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChldnQuY2FuY2VsQnViYmxlICE9PSBudWxsKSB7XG4gICAgICAgICAgZXZ0LmNhbmNlbEJ1YmJsZSA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICBfc2hvd0hpbnREaWFsb2cuY2FsbChzZWxmLCBpKTtcbiAgICAgIH07XG4gICAgfTtcblxuICAgIF9mb3JFYWNoKHRoaXMuX2ludHJvSXRlbXMsIGZ1bmN0aW9uKGl0ZW0sIGkpIHtcbiAgICAgIC8vIGF2b2lkIGFwcGVuZCBhIGhpbnQgdHdpY2VcbiAgICAgIGlmIChkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcuaW50cm9qcy1oaW50W2RhdGEtc3RlcD1cIicgKyBpICsgJ1wiXScpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdmFyIGhpbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICBfc2V0QW5jaG9yQXNCdXR0b24oaGludCk7XG5cbiAgICAgIGhpbnQub25jbGljayA9IGdldEhpbnRDbGljayhpKTtcblxuICAgICAgaGludC5jbGFzc05hbWUgPSAnaW50cm9qcy1oaW50JztcblxuICAgICAgaWYgKCFpdGVtLmhpbnRBbmltYXRpb24pIHtcbiAgICAgICAgX2FkZENsYXNzKGhpbnQsICdpbnRyb2pzLWhpbnQtbm8tYW5pbScpO1xuICAgICAgfVxuXG4gICAgICAvLyBoaW50J3MgcG9zaXRpb24gc2hvdWxkIGJlIGZpeGVkIGlmIHRoZSB0YXJnZXQgZWxlbWVudCdzIHBvc2l0aW9uIGlzIGZpeGVkXG4gICAgICBpZiAoX2lzRml4ZWQoaXRlbS5lbGVtZW50KSkge1xuICAgICAgICBfYWRkQ2xhc3MoaGludCwgJ2ludHJvanMtZml4ZWRoaW50Jyk7XG4gICAgICB9XG5cbiAgICAgIHZhciBoaW50RG90ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgICBoaW50RG90LmNsYXNzTmFtZSA9ICdpbnRyb2pzLWhpbnQtZG90JztcbiAgICAgIHZhciBoaW50UHVsc2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICAgIGhpbnRQdWxzZS5jbGFzc05hbWUgPSAnaW50cm9qcy1oaW50LXB1bHNlJztcblxuICAgICAgaGludC5hcHBlbmRDaGlsZChoaW50RG90KTtcbiAgICAgIGhpbnQuYXBwZW5kQ2hpbGQoaGludFB1bHNlKTtcbiAgICAgIGhpbnQuc2V0QXR0cmlidXRlKCdkYXRhLXN0ZXAnLCBpKTtcblxuICAgICAgLy8gd2Ugc3dhcCB0aGUgaGludCBlbGVtZW50IHdpdGggdGFyZ2V0IGVsZW1lbnRcbiAgICAgIC8vIGJlY2F1c2UgX3NldEhlbHBlckxheWVyUG9zaXRpb24gdXNlcyBgZWxlbWVudGAgcHJvcGVydHlcbiAgICAgIGl0ZW0udGFyZ2V0RWxlbWVudCA9IGl0ZW0uZWxlbWVudDtcbiAgICAgIGl0ZW0uZWxlbWVudCA9IGhpbnQ7XG5cbiAgICAgIC8vIGFsaWduIHRoZSBoaW50IHBvc2l0aW9uXG4gICAgICBfYWxpZ25IaW50UG9zaXRpb24uY2FsbCh0aGlzLCBpdGVtLmhpbnRQb3NpdGlvbiwgaGludCwgaXRlbS50YXJnZXRFbGVtZW50KTtcblxuICAgICAgaGludHNXcmFwcGVyLmFwcGVuZENoaWxkKGhpbnQpO1xuICAgIH0uYmluZCh0aGlzKSk7XG5cbiAgICAvLyBhZGRpbmcgdGhlIGhpbnRzIHdyYXBwZXJcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGhpbnRzV3JhcHBlcik7XG5cbiAgICAvLyBjYWxsIHRoZSBjYWxsYmFjayBmdW5jdGlvbiAoaWYgYW55KVxuICAgIGlmICh0eXBlb2YgKHRoaXMuX2hpbnRzQWRkZWRDYWxsYmFjaykgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB0aGlzLl9oaW50c0FkZGVkQ2FsbGJhY2suY2FsbCh0aGlzKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWxpZ25zIGhpbnQgcG9zaXRpb25cbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2FsaWduSGludFBvc2l0aW9uXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBwb3NpdGlvblxuICAgKiBAcGFyYW0ge09iamVjdH0gaGludFxuICAgKiBAcGFyYW0ge09iamVjdH0gZWxlbWVudFxuICAgKi9cbiAgZnVuY3Rpb24gX2FsaWduSGludFBvc2l0aW9uKHBvc2l0aW9uLCBoaW50LCBlbGVtZW50KSB7XG4gICAgLy8gZ2V0L2NhbGN1bGF0ZSBvZmZzZXQgb2YgdGFyZ2V0IGVsZW1lbnRcbiAgICB2YXIgb2Zmc2V0ID0gX2dldE9mZnNldC5jYWxsKHRoaXMsIGVsZW1lbnQpO1xuICAgIHZhciBpY29uV2lkdGggPSAyMDtcbiAgICB2YXIgaWNvbkhlaWdodCA9IDIwO1xuXG4gICAgLy8gYWxpZ24gdGhlIGhpbnQgZWxlbWVudFxuICAgIHN3aXRjaCAocG9zaXRpb24pIHtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICBjYXNlICd0b3AtbGVmdCc6XG4gICAgICAgIGhpbnQuc3R5bGUubGVmdCA9IG9mZnNldC5sZWZ0ICsgJ3B4JztcbiAgICAgICAgaGludC5zdHlsZS50b3AgPSBvZmZzZXQudG9wICsgJ3B4JztcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0b3AtcmlnaHQnOlxuICAgICAgICBoaW50LnN0eWxlLmxlZnQgPSAob2Zmc2V0LmxlZnQgKyBvZmZzZXQud2lkdGggLSBpY29uV2lkdGgpICsgJ3B4JztcbiAgICAgICAgaGludC5zdHlsZS50b3AgPSBvZmZzZXQudG9wICsgJ3B4JztcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdib3R0b20tbGVmdCc6XG4gICAgICAgIGhpbnQuc3R5bGUubGVmdCA9IG9mZnNldC5sZWZ0ICsgJ3B4JztcbiAgICAgICAgaGludC5zdHlsZS50b3AgPSAob2Zmc2V0LnRvcCArIG9mZnNldC5oZWlnaHQgLSBpY29uSGVpZ2h0KSArICdweCc7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnYm90dG9tLXJpZ2h0JzpcbiAgICAgICAgaGludC5zdHlsZS5sZWZ0ID0gKG9mZnNldC5sZWZ0ICsgb2Zmc2V0LndpZHRoIC0gaWNvbldpZHRoKSArICdweCc7XG4gICAgICAgIGhpbnQuc3R5bGUudG9wID0gKG9mZnNldC50b3AgKyBvZmZzZXQuaGVpZ2h0IC0gaWNvbkhlaWdodCkgKyAncHgnO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ21pZGRsZS1sZWZ0JzpcbiAgICAgICAgaGludC5zdHlsZS5sZWZ0ID0gb2Zmc2V0LmxlZnQgKyAncHgnO1xuICAgICAgICBoaW50LnN0eWxlLnRvcCA9IChvZmZzZXQudG9wICsgKG9mZnNldC5oZWlnaHQgLSBpY29uSGVpZ2h0KSAvIDIpICsgJ3B4JztcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdtaWRkbGUtcmlnaHQnOlxuICAgICAgICBoaW50LnN0eWxlLmxlZnQgPSAob2Zmc2V0LmxlZnQgKyBvZmZzZXQud2lkdGggLSBpY29uV2lkdGgpICsgJ3B4JztcbiAgICAgICAgaGludC5zdHlsZS50b3AgPSAob2Zmc2V0LnRvcCArIChvZmZzZXQuaGVpZ2h0IC0gaWNvbkhlaWdodCkgLyAyKSArICdweCc7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnbWlkZGxlLW1pZGRsZSc6XG4gICAgICAgIGhpbnQuc3R5bGUubGVmdCA9IChvZmZzZXQubGVmdCArIChvZmZzZXQud2lkdGggLSBpY29uV2lkdGgpIC8gMikgKyAncHgnO1xuICAgICAgICBoaW50LnN0eWxlLnRvcCA9IChvZmZzZXQudG9wICsgKG9mZnNldC5oZWlnaHQgLSBpY29uSGVpZ2h0KSAvIDIpICsgJ3B4JztcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdib3R0b20tbWlkZGxlJzpcbiAgICAgICAgaGludC5zdHlsZS5sZWZ0ID0gKG9mZnNldC5sZWZ0ICsgKG9mZnNldC53aWR0aCAtIGljb25XaWR0aCkgLyAyKSArICdweCc7XG4gICAgICAgIGhpbnQuc3R5bGUudG9wID0gKG9mZnNldC50b3AgKyBvZmZzZXQuaGVpZ2h0IC0gaWNvbkhlaWdodCkgKyAncHgnO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3RvcC1taWRkbGUnOlxuICAgICAgICBoaW50LnN0eWxlLmxlZnQgPSAob2Zmc2V0LmxlZnQgKyAob2Zmc2V0LndpZHRoIC0gaWNvbldpZHRoKSAvIDIpICsgJ3B4JztcbiAgICAgICAgaGludC5zdHlsZS50b3AgPSBvZmZzZXQudG9wICsgJ3B4JztcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXJzIHdoZW4gdXNlciBjbGlja3Mgb24gdGhlIGhpbnQgZWxlbWVudFxuICAgKlxuICAgKiBAYXBpIHByaXZhdGVcbiAgICogQG1ldGhvZCBfc2hvd0hpbnREaWFsb2dcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHN0ZXBJZFxuICAgKi9cbiAgZnVuY3Rpb24gX3Nob3dIaW50RGlhbG9nKHN0ZXBJZCkge1xuICAgIHZhciBoaW50RWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5pbnRyb2pzLWhpbnRbZGF0YS1zdGVwPVwiJyArIHN0ZXBJZCArICdcIl0nKTtcbiAgICB2YXIgaXRlbSA9IHRoaXMuX2ludHJvSXRlbXNbc3RlcElkXTtcblxuICAgIC8vIGNhbGwgdGhlIGNhbGxiYWNrIGZ1bmN0aW9uIChpZiBhbnkpXG4gICAgaWYgKHR5cGVvZiAodGhpcy5faGludENsaWNrQ2FsbGJhY2spICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgdGhpcy5faGludENsaWNrQ2FsbGJhY2suY2FsbCh0aGlzLCBoaW50RWxlbWVudCwgaXRlbSwgc3RlcElkKTtcbiAgICB9XG5cbiAgICAvLyByZW1vdmUgYWxsIG9wZW4gdG9vbHRpcHNcbiAgICB2YXIgcmVtb3ZlZFN0ZXAgPSBfcmVtb3ZlSGludFRvb2x0aXAuY2FsbCh0aGlzKTtcblxuICAgIC8vIHRvIHRvZ2dsZSB0aGUgdG9vbHRpcFxuICAgIGlmIChwYXJzZUludChyZW1vdmVkU3RlcCwgMTApID09PSBzdGVwSWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB2YXIgdG9vbHRpcExheWVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XG4gICAgdmFyIHRvb2x0aXBUZXh0TGF5ZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICB2YXIgYXJyb3dMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuICAgIHZhciByZWZlcmVuY2VMYXllciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpO1xuXG4gICAgdG9vbHRpcExheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXRvb2x0aXAnO1xuXG4gICAgdG9vbHRpcExheWVyLm9uY2xpY2sgPSBmdW5jdGlvbiAoZSkge1xuICAgICAgLy9JRTkgJiBPdGhlciBCcm93c2Vyc1xuICAgICAgaWYgKGUuc3RvcFByb3BhZ2F0aW9uKSB7XG4gICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICB9XG4gICAgICAvL0lFOCBhbmQgTG93ZXJcbiAgICAgIGVsc2Uge1xuICAgICAgICBlLmNhbmNlbEJ1YmJsZSA9IHRydWU7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHRvb2x0aXBUZXh0TGF5ZXIuY2xhc3NOYW1lID0gJ2ludHJvanMtdG9vbHRpcHRleHQnO1xuXG4gICAgdmFyIHRvb2x0aXBXcmFwcGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgncCcpO1xuICAgIHRvb2x0aXBXcmFwcGVyLmlubmVySFRNTCA9IGl0ZW0uaGludDtcblxuICAgIHZhciBjbG9zZUJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2EnKTtcbiAgICBjbG9zZUJ1dHRvbi5jbGFzc05hbWUgPSB0aGlzLl9vcHRpb25zLmJ1dHRvbkNsYXNzO1xuICAgIGNsb3NlQnV0dG9uLnNldEF0dHJpYnV0ZSgncm9sZScsICdidXR0b24nKTtcbiAgICBjbG9zZUJ1dHRvbi5pbm5lckhUTUwgPSB0aGlzLl9vcHRpb25zLmhpbnRCdXR0b25MYWJlbDtcbiAgICBjbG9zZUJ1dHRvbi5vbmNsaWNrID0gX2hpZGVIaW50LmJpbmQodGhpcywgc3RlcElkKTtcblxuICAgIHRvb2x0aXBUZXh0TGF5ZXIuYXBwZW5kQ2hpbGQodG9vbHRpcFdyYXBwZXIpO1xuICAgIHRvb2x0aXBUZXh0TGF5ZXIuYXBwZW5kQ2hpbGQoY2xvc2VCdXR0b24pO1xuXG4gICAgYXJyb3dMYXllci5jbGFzc05hbWUgPSAnaW50cm9qcy1hcnJvdyc7XG4gICAgdG9vbHRpcExheWVyLmFwcGVuZENoaWxkKGFycm93TGF5ZXIpO1xuXG4gICAgdG9vbHRpcExheWVyLmFwcGVuZENoaWxkKHRvb2x0aXBUZXh0TGF5ZXIpO1xuXG4gICAgLy8gc2V0IGN1cnJlbnQgc3RlcCBmb3IgX3BsYWNlVG9vbHRpcCBmdW5jdGlvblxuICAgIHRoaXMuX2N1cnJlbnRTdGVwID0gaGludEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXN0ZXAnKTtcblxuICAgIC8vIGFsaWduIHJlZmVyZW5jZSBsYXllciBwb3NpdGlvblxuICAgIHJlZmVyZW5jZUxheWVyLmNsYXNzTmFtZSA9ICdpbnRyb2pzLXRvb2x0aXBSZWZlcmVuY2VMYXllciBpbnRyb2pzLWhpbnRSZWZlcmVuY2UnO1xuICAgIHJlZmVyZW5jZUxheWVyLnNldEF0dHJpYnV0ZSgnZGF0YS1zdGVwJywgaGludEVsZW1lbnQuZ2V0QXR0cmlidXRlKCdkYXRhLXN0ZXAnKSk7XG4gICAgX3NldEhlbHBlckxheWVyUG9zaXRpb24uY2FsbCh0aGlzLCByZWZlcmVuY2VMYXllcik7XG5cbiAgICByZWZlcmVuY2VMYXllci5hcHBlbmRDaGlsZCh0b29sdGlwTGF5ZXIpO1xuICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQocmVmZXJlbmNlTGF5ZXIpO1xuXG4gICAgLy9zZXQgcHJvcGVyIHBvc2l0aW9uXG4gICAgX3BsYWNlVG9vbHRpcC5jYWxsKHRoaXMsIGhpbnRFbGVtZW50LCB0b29sdGlwTGF5ZXIsIGFycm93TGF5ZXIsIG51bGwsIHRydWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbiBlbGVtZW50IHBvc2l0aW9uIG9uIHRoZSBwYWdlXG4gICAqIFRoYW5rcyB0byBgbWVvdXdgOiBodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vYS80NDI0NzQvMzc1OTY2XG4gICAqXG4gICAqIEBhcGkgcHJpdmF0ZVxuICAgKiBAbWV0aG9kIF9nZXRPZmZzZXRcbiAgICogQHBhcmFtIHtPYmplY3R9IGVsZW1lbnRcbiAgICogQHJldHVybnMgRWxlbWVudCdzIHBvc2l0aW9uIGluZm9cbiAgICovXG4gIGZ1bmN0aW9uIF9nZXRPZmZzZXQoZWxlbWVudCkge1xuICAgIHZhciBib2R5ID0gZG9jdW1lbnQuYm9keTtcbiAgICB2YXIgZG9jRWwgPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQ7XG4gICAgdmFyIHNjcm9sbFRvcCA9IHdpbmRvdy5wYWdlWU9mZnNldCB8fCBkb2NFbC5zY3JvbGxUb3AgfHwgYm9keS5zY3JvbGxUb3A7XG4gICAgdmFyIHNjcm9sbExlZnQgPSB3aW5kb3cucGFnZVhPZmZzZXQgfHwgZG9jRWwuc2Nyb2xsTGVmdCB8fCBib2R5LnNjcm9sbExlZnQ7XG4gICAgdmFyIHggPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIHJldHVybiB7XG4gICAgICB0b3A6IHgudG9wICsgc2Nyb2xsVG9wLFxuICAgICAgd2lkdGg6IHgud2lkdGgsXG4gICAgICBoZWlnaHQ6IHguaGVpZ2h0LFxuICAgICAgbGVmdDogeC5sZWZ0ICsgc2Nyb2xsTGVmdFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgKiBGaW5kIHRoZSBuZWFyZXN0IHNjcm9sbGFibGUgcGFyZW50XG4gICogY29waWVkIGZyb20gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzU5Mzk4ODYvZmluZC1maXJzdC1zY3JvbGxhYmxlLXBhcmVudFxuICAqXG4gICogQHBhcmFtIEVsZW1lbnQgZWxlbWVudFxuICAqIEByZXR1cm4gRWxlbWVudFxuICAqL1xuICBmdW5jdGlvbiBfZ2V0U2Nyb2xsUGFyZW50KGVsZW1lbnQpIHtcbiAgICB2YXIgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShlbGVtZW50KTtcbiAgICB2YXIgZXhjbHVkZVN0YXRpY1BhcmVudCA9IChzdHlsZS5wb3NpdGlvbiA9PT0gXCJhYnNvbHV0ZVwiKTtcbiAgICB2YXIgb3ZlcmZsb3dSZWdleCA9IC8oYXV0b3xzY3JvbGwpLztcblxuICAgIGlmIChzdHlsZS5wb3NpdGlvbiA9PT0gXCJmaXhlZFwiKSByZXR1cm4gZG9jdW1lbnQuYm9keTtcbiAgICBcbiAgICBmb3IgKHZhciBwYXJlbnQgPSBlbGVtZW50OyAocGFyZW50ID0gcGFyZW50LnBhcmVudEVsZW1lbnQpOykge1xuICAgICAgc3R5bGUgPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZShwYXJlbnQpO1xuICAgICAgaWYgKGV4Y2x1ZGVTdGF0aWNQYXJlbnQgJiYgc3R5bGUucG9zaXRpb24gPT09IFwic3RhdGljXCIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBpZiAob3ZlcmZsb3dSZWdleC50ZXN0KHN0eWxlLm92ZXJmbG93ICsgc3R5bGUub3ZlcmZsb3dZICsgc3R5bGUub3ZlcmZsb3dYKSkgcmV0dXJuIHBhcmVudDtcbiAgICB9XG5cbiAgICByZXR1cm4gZG9jdW1lbnQuYm9keTtcbiAgfVxuXG4gIC8qKlxuICAqIHNjcm9sbCBhIHNjcm9sbGFibGUgZWxlbWVudCB0byBhIGNoaWxkIGVsZW1lbnRcbiAgKlxuICAqIEBwYXJhbSBFbGVtZW50IHBhcmVudFxuICAqIEBwYXJhbSBFbGVtZW50IGVsZW1lbnRcbiAgKiBAcmV0dXJuIE51bGxcbiAgKi9cbiAgZnVuY3Rpb24gX3Njcm9sbFBhcmVudFRvRWxlbWVudCAocGFyZW50LCBlbGVtZW50KSB7XG4gICAgcGFyZW50LnNjcm9sbFRvcCA9IGVsZW1lbnQub2Zmc2V0VG9wIC0gcGFyZW50Lm9mZnNldFRvcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXRzIHRoZSBjdXJyZW50IHByb2dyZXNzIHBlcmNlbnRhZ2VcbiAgICpcbiAgICogQGFwaSBwcml2YXRlXG4gICAqIEBtZXRob2QgX2dldFByb2dyZXNzXG4gICAqIEByZXR1cm5zIGN1cnJlbnQgcHJvZ3Jlc3MgcGVyY2VudGFnZVxuICAgKi9cbiAgZnVuY3Rpb24gX2dldFByb2dyZXNzKCkge1xuICAgIC8vIFN0ZXBzIGFyZSAwIGluZGV4ZWRcbiAgICB2YXIgY3VycmVudFN0ZXAgPSBwYXJzZUludCgodGhpcy5fY3VycmVudFN0ZXAgKyAxKSwgMTApO1xuICAgIHJldHVybiAoKGN1cnJlbnRTdGVwIC8gdGhpcy5faW50cm9JdGVtcy5sZW5ndGgpICogMTAwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBPdmVyd3JpdGVzIG9iajEncyB2YWx1ZXMgd2l0aCBvYmoyJ3MgYW5kIGFkZHMgb2JqMidzIGlmIG5vbiBleGlzdGVudCBpbiBvYmoxXG4gICAqIHZpYTogaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNzEyNTEvaG93LWNhbi1pLW1lcmdlLXByb3BlcnRpZXMtb2YtdHdvLWphdmFzY3JpcHQtb2JqZWN0cy1keW5hbWljYWxseVxuICAgKlxuICAgKiBAcGFyYW0gb2JqMVxuICAgKiBAcGFyYW0gb2JqMlxuICAgKiBAcmV0dXJucyBvYmozIGEgbmV3IG9iamVjdCBiYXNlZCBvbiBvYmoxIGFuZCBvYmoyXG4gICAqL1xuICBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9iajEsb2JqMikge1xuICAgIHZhciBvYmozID0ge30sXG4gICAgICBhdHRybmFtZTtcbiAgICBmb3IgKGF0dHJuYW1lIGluIG9iajEpIHsgb2JqM1thdHRybmFtZV0gPSBvYmoxW2F0dHJuYW1lXTsgfVxuICAgIGZvciAoYXR0cm5hbWUgaW4gb2JqMikgeyBvYmozW2F0dHJuYW1lXSA9IG9iajJbYXR0cm5hbWVdOyB9XG4gICAgcmV0dXJuIG9iajM7XG4gIH1cblxuICB2YXIgaW50cm9KcyA9IGZ1bmN0aW9uICh0YXJnZXRFbG0pIHtcbiAgICB2YXIgaW5zdGFuY2U7XG5cbiAgICBpZiAodHlwZW9mICh0YXJnZXRFbG0pID09PSAnb2JqZWN0Jykge1xuICAgICAgLy9PaywgY3JlYXRlIGEgbmV3IGluc3RhbmNlXG4gICAgICBpbnN0YW5jZSA9IG5ldyBJbnRyb0pzKHRhcmdldEVsbSk7XG5cbiAgICB9IGVsc2UgaWYgKHR5cGVvZiAodGFyZ2V0RWxtKSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vc2VsZWN0IHRoZSB0YXJnZXQgZWxlbWVudCB3aXRoIHF1ZXJ5IHNlbGVjdG9yXG4gICAgICB2YXIgdGFyZ2V0RWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IodGFyZ2V0RWxtKTtcblxuICAgICAgaWYgKHRhcmdldEVsZW1lbnQpIHtcbiAgICAgICAgaW5zdGFuY2UgPSBuZXcgSW50cm9Kcyh0YXJnZXRFbGVtZW50KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignVGhlcmUgaXMgbm8gZWxlbWVudCB3aXRoIGdpdmVuIHNlbGVjdG9yLicpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpbnN0YW5jZSA9IG5ldyBJbnRyb0pzKGRvY3VtZW50LmJvZHkpO1xuICAgIH1cbiAgICAvLyBhZGQgaW5zdGFuY2UgdG8gbGlzdCBvZiBfaW5zdGFuY2VzXG4gICAgLy8gcGFzc2luZyBncm91cCB0byBfc3RhbXAgdG8gaW5jcmVtZW50XG4gICAgLy8gZnJvbSAwIG9ud2FyZCBzb21ld2hhdCByZWxpYWJseVxuICAgIGludHJvSnMuaW5zdGFuY2VzWyBfc3RhbXAoaW5zdGFuY2UsICdpbnRyb2pzLWluc3RhbmNlJykgXSA9IGluc3RhbmNlO1xuXG4gICAgcmV0dXJuIGluc3RhbmNlO1xuICB9O1xuXG4gIC8qKlxuICAgKiBDdXJyZW50IEludHJvSnMgdmVyc2lvblxuICAgKlxuICAgKiBAcHJvcGVydHkgdmVyc2lvblxuICAgKiBAdHlwZSBTdHJpbmdcbiAgICovXG4gIGludHJvSnMudmVyc2lvbiA9IFZFUlNJT047XG5cbiAgLyoqXG4gICoga2V5LXZhbCBvYmplY3QgaGVscGVyIGZvciBpbnRyb0pzIGluc3RhbmNlc1xuICAqXG4gICogQHByb3BlcnR5IGluc3RhbmNlc1xuICAqIEB0eXBlIE9iamVjdFxuICAqL1xuICBpbnRyb0pzLmluc3RhbmNlcyA9IHt9O1xuXG4gIC8vUHJvdG90eXBlXG4gIGludHJvSnMuZm4gPSBJbnRyb0pzLnByb3RvdHlwZSA9IHtcbiAgICBjbG9uZTogZnVuY3Rpb24gKCkge1xuICAgICAgcmV0dXJuIG5ldyBJbnRyb0pzKHRoaXMpO1xuICAgIH0sXG4gICAgc2V0T3B0aW9uOiBmdW5jdGlvbihvcHRpb24sIHZhbHVlKSB7XG4gICAgICB0aGlzLl9vcHRpb25zW29wdGlvbl0gPSB2YWx1ZTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgc2V0T3B0aW9uczogZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgdGhpcy5fb3B0aW9ucyA9IF9tZXJnZU9wdGlvbnModGhpcy5fb3B0aW9ucywgb3B0aW9ucyk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIHN0YXJ0OiBmdW5jdGlvbiAoZ3JvdXApIHtcbiAgICAgIF9pbnRyb0ZvckVsZW1lbnQuY2FsbCh0aGlzLCB0aGlzLl90YXJnZXRFbGVtZW50LCBncm91cCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIGdvVG9TdGVwOiBmdW5jdGlvbihzdGVwKSB7XG4gICAgICBfZ29Ub1N0ZXAuY2FsbCh0aGlzLCBzdGVwKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgYWRkU3RlcDogZnVuY3Rpb24ob3B0aW9ucykge1xuICAgICAgaWYgKCF0aGlzLl9vcHRpb25zLnN0ZXBzKSB7XG4gICAgICAgIHRoaXMuX29wdGlvbnMuc3RlcHMgPSBbXTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5fb3B0aW9ucy5zdGVwcy5wdXNoKG9wdGlvbnMpO1xuXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIGFkZFN0ZXBzOiBmdW5jdGlvbihzdGVwcykge1xuICAgICAgaWYgKCFzdGVwcy5sZW5ndGgpIHJldHVybjtcblxuICAgICAgZm9yKHZhciBpbmRleCA9IDA7IGluZGV4IDwgc3RlcHMubGVuZ3RoOyBpbmRleCsrKSB7XG4gICAgICAgIHRoaXMuYWRkU3RlcChzdGVwc1tpbmRleF0pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIGdvVG9TdGVwTnVtYmVyOiBmdW5jdGlvbihzdGVwKSB7XG4gICAgICBfZ29Ub1N0ZXBOdW1iZXIuY2FsbCh0aGlzLCBzdGVwKTtcblxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcbiAgICBuZXh0U3RlcDogZnVuY3Rpb24oKSB7XG4gICAgICBfbmV4dFN0ZXAuY2FsbCh0aGlzKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgcHJldmlvdXNTdGVwOiBmdW5jdGlvbigpIHtcbiAgICAgIF9wcmV2aW91c1N0ZXAuY2FsbCh0aGlzKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgZXhpdDogZnVuY3Rpb24oZm9yY2UpIHtcbiAgICAgIF9leGl0SW50cm8uY2FsbCh0aGlzLCB0aGlzLl90YXJnZXRFbGVtZW50LCBmb3JjZSk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIHJlZnJlc2g6IGZ1bmN0aW9uKCkge1xuICAgICAgX3JlZnJlc2guY2FsbCh0aGlzKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25iZWZvcmVjaGFuZ2U6IGZ1bmN0aW9uKHByb3ZpZGVkQ2FsbGJhY2spIHtcbiAgICAgIGlmICh0eXBlb2YgKHByb3ZpZGVkQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRoaXMuX2ludHJvQmVmb3JlQ2hhbmdlQ2FsbGJhY2sgPSBwcm92aWRlZENhbGxiYWNrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQcm92aWRlZCBjYWxsYmFjayBmb3Igb25iZWZvcmVjaGFuZ2Ugd2FzIG5vdCBhIGZ1bmN0aW9uJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIG9uY2hhbmdlOiBmdW5jdGlvbihwcm92aWRlZENhbGxiYWNrKSB7XG4gICAgICBpZiAodHlwZW9mIChwcm92aWRlZENhbGxiYWNrKSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB0aGlzLl9pbnRyb0NoYW5nZUNhbGxiYWNrID0gcHJvdmlkZWRDYWxsYmFjaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgY2FsbGJhY2sgZm9yIG9uY2hhbmdlIHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25hZnRlcmNoYW5nZTogZnVuY3Rpb24ocHJvdmlkZWRDYWxsYmFjaykge1xuICAgICAgaWYgKHR5cGVvZiAocHJvdmlkZWRDYWxsYmFjaykgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdGhpcy5faW50cm9BZnRlckNoYW5nZUNhbGxiYWNrID0gcHJvdmlkZWRDYWxsYmFjaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgY2FsbGJhY2sgZm9yIG9uYWZ0ZXJjaGFuZ2Ugd2FzIG5vdCBhIGZ1bmN0aW9uJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIG9uY29tcGxldGU6IGZ1bmN0aW9uKHByb3ZpZGVkQ2FsbGJhY2spIHtcbiAgICAgIGlmICh0eXBlb2YgKHByb3ZpZGVkQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRoaXMuX2ludHJvQ29tcGxldGVDYWxsYmFjayA9IHByb3ZpZGVkQ2FsbGJhY2s7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Byb3ZpZGVkIGNhbGxiYWNrIGZvciBvbmNvbXBsZXRlIHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25oaW50c2FkZGVkOiBmdW5jdGlvbihwcm92aWRlZENhbGxiYWNrKSB7XG4gICAgICBpZiAodHlwZW9mIChwcm92aWRlZENhbGxiYWNrKSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB0aGlzLl9oaW50c0FkZGVkQ2FsbGJhY2sgPSBwcm92aWRlZENhbGxiYWNrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQcm92aWRlZCBjYWxsYmFjayBmb3Igb25oaW50c2FkZGVkIHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25oaW50Y2xpY2s6IGZ1bmN0aW9uKHByb3ZpZGVkQ2FsbGJhY2spIHtcbiAgICAgIGlmICh0eXBlb2YgKHByb3ZpZGVkQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRoaXMuX2hpbnRDbGlja0NhbGxiYWNrID0gcHJvdmlkZWRDYWxsYmFjaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgY2FsbGJhY2sgZm9yIG9uaGludGNsaWNrIHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25oaW50Y2xvc2U6IGZ1bmN0aW9uKHByb3ZpZGVkQ2FsbGJhY2spIHtcbiAgICAgIGlmICh0eXBlb2YgKHByb3ZpZGVkQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRoaXMuX2hpbnRDbG9zZUNhbGxiYWNrID0gcHJvdmlkZWRDYWxsYmFjaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgY2FsbGJhY2sgZm9yIG9uaGludGNsb3NlIHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgb25leGl0OiBmdW5jdGlvbihwcm92aWRlZENhbGxiYWNrKSB7XG4gICAgICBpZiAodHlwZW9mIChwcm92aWRlZENhbGxiYWNrKSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB0aGlzLl9pbnRyb0V4aXRDYWxsYmFjayA9IHByb3ZpZGVkQ2FsbGJhY2s7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1Byb3ZpZGVkIGNhbGxiYWNrIGZvciBvbmV4aXQgd2FzIG5vdCBhIGZ1bmN0aW9uLicpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcbiAgICBvbnNraXA6IGZ1bmN0aW9uKHByb3ZpZGVkQ2FsbGJhY2spIHtcbiAgICAgIGlmICh0eXBlb2YgKHByb3ZpZGVkQ2FsbGJhY2spID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHRoaXMuX2ludHJvU2tpcENhbGxiYWNrID0gcHJvdmlkZWRDYWxsYmFjaztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignUHJvdmlkZWQgY2FsbGJhY2sgZm9yIG9uc2tpcCB3YXMgbm90IGEgZnVuY3Rpb24uJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIG9uYmVmb3JlZXhpdDogZnVuY3Rpb24ocHJvdmlkZWRDYWxsYmFjaykge1xuICAgICAgaWYgKHR5cGVvZiAocHJvdmlkZWRDYWxsYmFjaykgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdGhpcy5faW50cm9CZWZvcmVFeGl0Q2FsbGJhY2sgPSBwcm92aWRlZENhbGxiYWNrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQcm92aWRlZCBjYWxsYmFjayBmb3Igb25iZWZvcmVleGl0IHdhcyBub3QgYSBmdW5jdGlvbi4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgYWRkSGludHM6IGZ1bmN0aW9uKCkge1xuICAgICAgX3BvcHVsYXRlSGludHMuY2FsbCh0aGlzLCB0aGlzLl90YXJnZXRFbGVtZW50KTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgaGlkZUhpbnQ6IGZ1bmN0aW9uIChzdGVwSWQpIHtcbiAgICAgIF9oaWRlSGludC5jYWxsKHRoaXMsIHN0ZXBJZCk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIGhpZGVIaW50czogZnVuY3Rpb24gKCkge1xuICAgICAgX2hpZGVIaW50cy5jYWxsKHRoaXMpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcbiAgICBzaG93SGludDogZnVuY3Rpb24gKHN0ZXBJZCkge1xuICAgICAgX3Nob3dIaW50LmNhbGwodGhpcywgc3RlcElkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgc2hvd0hpbnRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICBfc2hvd0hpbnRzLmNhbGwodGhpcyk7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9LFxuICAgIHJlbW92ZUhpbnRzOiBmdW5jdGlvbiAoKSB7XG4gICAgICBfcmVtb3ZlSGludHMuY2FsbCh0aGlzKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0sXG4gICAgcmVtb3ZlSGludDogZnVuY3Rpb24gKHN0ZXBJZCkge1xuICAgICAgX3JlbW92ZUhpbnQuY2FsbCh0aGlzLCBzdGVwSWQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfSxcbiAgICBzaG93SGludERpYWxvZzogZnVuY3Rpb24gKHN0ZXBJZCkge1xuICAgICAgX3Nob3dIaW50RGlhbG9nLmNhbGwodGhpcywgc3RlcElkKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgfTtcblxuICByZXR1cm4gaW50cm9Kcztcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUJBY0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Iiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///0444\n")}}]);