legacy-presentation.bundle.js 444 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /******/ (function(modules) { // webpackBootstrap
  2. /******/ // install a JSONP callback for chunk loading
  3. /******/ function webpackJsonpCallback(data) {
  4. /******/ var chunkIds = data[0];
  5. /******/ var moreModules = data[1];
  6. /******/
  7. /******/
  8. /******/ // add "moreModules" to the modules object,
  9. /******/ // then flag all "chunkIds" as loaded and fire callback
  10. /******/ var moduleId, chunkId, i = 0, resolves = [];
  11. /******/ for(;i < chunkIds.length; i++) {
  12. /******/ chunkId = chunkIds[i];
  13. /******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
  14. /******/ resolves.push(installedChunks[chunkId][0]);
  15. /******/ }
  16. /******/ installedChunks[chunkId] = 0;
  17. /******/ }
  18. /******/ for(moduleId in moreModules) {
  19. /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
  20. /******/ modules[moduleId] = moreModules[moduleId];
  21. /******/ }
  22. /******/ }
  23. /******/ if(parentJsonpFunction) parentJsonpFunction(data);
  24. /******/
  25. /******/ while(resolves.length) {
  26. /******/ resolves.shift()();
  27. /******/ }
  28. /******/
  29. /******/ };
  30. /******/
  31. /******/
  32. /******/ // The module cache
  33. /******/ var installedModules = {};
  34. /******/
  35. /******/ // object to store loaded and loading chunks
  36. /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
  37. /******/ // Promise = chunk loading, 0 = chunk loaded
  38. /******/ var installedChunks = {
  39. /******/ "js/legacy-presentation": 0
  40. /******/ };
  41. /******/
  42. /******/
  43. /******/
  44. /******/ // script path function
  45. /******/ function jsonpScriptSrc(chunkId) {
  46. /******/ return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".bundle.js"
  47. /******/ }
  48. /******/
  49. /******/ // The require function
  50. /******/ function __webpack_require__(moduleId) {
  51. /******/
  52. /******/ // Check if module is in cache
  53. /******/ if(installedModules[moduleId]) {
  54. /******/ return installedModules[moduleId].exports;
  55. /******/ }
  56. /******/ // Create a new module (and put it into the cache)
  57. /******/ var module = installedModules[moduleId] = {
  58. /******/ i: moduleId,
  59. /******/ l: false,
  60. /******/ exports: {}
  61. /******/ };
  62. /******/
  63. /******/ // Execute the module function
  64. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  65. /******/
  66. /******/ // Flag the module as loaded
  67. /******/ module.l = true;
  68. /******/
  69. /******/ // Return the exports of the module
  70. /******/ return module.exports;
  71. /******/ }
  72. /******/
  73. /******/ // This file contains only the entry chunk.
  74. /******/ // The chunk loading function for additional chunks
  75. /******/ __webpack_require__.e = function requireEnsure(chunkId) {
  76. /******/ var promises = [];
  77. /******/
  78. /******/
  79. /******/ // JSONP chunk loading for javascript
  80. /******/
  81. /******/ var installedChunkData = installedChunks[chunkId];
  82. /******/ if(installedChunkData !== 0) { // 0 means "already installed".
  83. /******/
  84. /******/ // a Promise means "currently loading".
  85. /******/ if(installedChunkData) {
  86. /******/ promises.push(installedChunkData[2]);
  87. /******/ } else {
  88. /******/ // setup Promise in chunk cache
  89. /******/ var promise = new Promise(function(resolve, reject) {
  90. /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
  91. /******/ });
  92. /******/ promises.push(installedChunkData[2] = promise);
  93. /******/
  94. /******/ // start chunk loading
  95. /******/ var script = document.createElement('script');
  96. /******/ var onScriptComplete;
  97. /******/
  98. /******/ script.charset = 'utf-8';
  99. /******/ script.timeout = 120;
  100. /******/ if (__webpack_require__.nc) {
  101. /******/ script.setAttribute("nonce", __webpack_require__.nc);
  102. /******/ }
  103. /******/ script.src = jsonpScriptSrc(chunkId);
  104. /******/
  105. /******/ // create error before stack unwound to get useful stacktrace later
  106. /******/ var error = new Error();
  107. /******/ onScriptComplete = function (event) {
  108. /******/ // avoid mem leaks in IE.
  109. /******/ script.onerror = script.onload = null;
  110. /******/ clearTimeout(timeout);
  111. /******/ var chunk = installedChunks[chunkId];
  112. /******/ if(chunk !== 0) {
  113. /******/ if(chunk) {
  114. /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
  115. /******/ var realSrc = event && event.target && event.target.src;
  116. /******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
  117. /******/ error.name = 'ChunkLoadError';
  118. /******/ error.type = errorType;
  119. /******/ error.request = realSrc;
  120. /******/ chunk[1](error);
  121. /******/ }
  122. /******/ installedChunks[chunkId] = undefined;
  123. /******/ }
  124. /******/ };
  125. /******/ var timeout = setTimeout(function(){
  126. /******/ onScriptComplete({ type: 'timeout', target: script });
  127. /******/ }, 120000);
  128. /******/ script.onerror = script.onload = onScriptComplete;
  129. /******/ document.head.appendChild(script);
  130. /******/ }
  131. /******/ }
  132. /******/ return Promise.all(promises);
  133. /******/ };
  134. /******/
  135. /******/ // expose the modules object (__webpack_modules__)
  136. /******/ __webpack_require__.m = modules;
  137. /******/
  138. /******/ // expose the module cache
  139. /******/ __webpack_require__.c = installedModules;
  140. /******/
  141. /******/ // define getter function for harmony exports
  142. /******/ __webpack_require__.d = function(exports, name, getter) {
  143. /******/ if(!__webpack_require__.o(exports, name)) {
  144. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  145. /******/ }
  146. /******/ };
  147. /******/
  148. /******/ // define __esModule on exports
  149. /******/ __webpack_require__.r = function(exports) {
  150. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  151. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  152. /******/ }
  153. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  154. /******/ };
  155. /******/
  156. /******/ // create a fake namespace object
  157. /******/ // mode & 1: value is a module id, require it
  158. /******/ // mode & 2: merge all properties of value into the ns
  159. /******/ // mode & 4: return value when already ns object
  160. /******/ // mode & 8|1: behave like require
  161. /******/ __webpack_require__.t = function(value, mode) {
  162. /******/ if(mode & 1) value = __webpack_require__(value);
  163. /******/ if(mode & 8) return value;
  164. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  165. /******/ var ns = Object.create(null);
  166. /******/ __webpack_require__.r(ns);
  167. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  168. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  169. /******/ return ns;
  170. /******/ };
  171. /******/
  172. /******/ // getDefaultExport function for compatibility with non-harmony modules
  173. /******/ __webpack_require__.n = function(module) {
  174. /******/ var getter = module && module.__esModule ?
  175. /******/ function getDefault() { return module['default']; } :
  176. /******/ function getModuleExports() { return module; };
  177. /******/ __webpack_require__.d(getter, 'a', getter);
  178. /******/ return getter;
  179. /******/ };
  180. /******/
  181. /******/ // Object.prototype.hasOwnProperty.call
  182. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  183. /******/
  184. /******/ // __webpack_public_path__
  185. /******/ __webpack_require__.p = "/";
  186. /******/
  187. /******/ // on error function for async loading
  188. /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
  189. /******/
  190. /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
  191. /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
  192. /******/ jsonpArray.push = webpackJsonpCallback;
  193. /******/ jsonpArray = jsonpArray.slice();
  194. /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
  195. /******/ var parentJsonpFunction = oldJsonpFunction;
  196. /******/
  197. /******/
  198. /******/ // Load entry module and return exports
  199. /******/ return __webpack_require__(__webpack_require__.s = "./src/client/js/legacy/crowi-presentation.js");
  200. /******/ })
  201. /************************************************************************/
  202. /******/ ({
  203. /***/ "./node_modules/reveal.js/js/reveal.js":
  204. /*!*********************************************!*\
  205. !*** ./node_modules/reveal.js/js/reveal.js ***!
  206. \*********************************************/
  207. /*! no static exports found */
  208. /***/ (function(module, exports, __webpack_require__) {
  209. eval("var __WEBPACK_AMD_DEFINE_RESULT__;/*!\n * reveal.js\n * http://revealjs.com\n * MIT licensed\n *\n * Copyright (C) 2017 Hakim El Hattab, http://hakim.se\n */\n(function( root, factory ) {\n\tif( true ) {\n\t\t// AMD. Register as an anonymous module.\n\t\t!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {\n\t\t\troot.Reveal = factory();\n\t\t\treturn root.Reveal;\n\t\t}).call(exports, __webpack_require__, exports, module),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t} else {}\n}( this, function() {\n\n\t'use strict';\n\n\tvar Reveal;\n\n\t// The reveal.js version\n\tvar VERSION = '3.6.0';\n\n\tvar SLIDES_SELECTOR = '.slides section',\n\t\tHORIZONTAL_SLIDES_SELECTOR = '.slides>section',\n\t\tVERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',\n\t\tHOME_SLIDE_SELECTOR = '.slides>section:first-of-type',\n\t\tUA = navigator.userAgent,\n\n\t\t// Configuration defaults, can be overridden at initialization time\n\t\tconfig = {\n\n\t\t\t// The \"normal\" size of the presentation, aspect ratio will be preserved\n\t\t\t// when the presentation is scaled to fit different resolutions\n\t\t\twidth: 960,\n\t\t\theight: 700,\n\n\t\t\t// Factor of the display size that should remain empty around the content\n\t\t\tmargin: 0.04,\n\n\t\t\t// Bounds for smallest/largest possible scale to apply to content\n\t\t\tminScale: 0.2,\n\t\t\tmaxScale: 2.0,\n\n\t\t\t// Display presentation control arrows\n\t\t\tcontrols: true,\n\n\t\t\t// Help the user learn the controls by providing hints, for example by\n\t\t\t// bouncing the down arrow when they first encounter a vertical slide\n\t\t\tcontrolsTutorial: true,\n\n\t\t\t// Determines where controls appear, \"edges\" or \"bottom-right\"\n\t\t\tcontrolsLayout: 'bottom-right',\n\n\t\t\t// Visibility rule for backwards navigation arrows; \"faded\", \"hidden\"\n\t\t\t// or \"visible\"\n\t\t\tcontrolsBackArrows: 'faded',\n\n\t\t\t// Display a presentation progress bar\n\t\t\tprogress: true,\n\n\t\t\t// Display the page number of the current slide\n\t\t\tslideNumber: false,\n\n\t\t\t// Determine which displays to show the slide number on\n\t\t\tshowSlideNumber: 'all',\n\n\t\t\t// Push each slide change to the browser history\n\t\t\thistory: false,\n\n\t\t\t// Enable keyboard shortcuts for navigation\n\t\t\tkeyboard: true,\n\n\t\t\t// Optional function that blocks keyboard events when retuning false\n\t\t\tkeyboardCondition: null,\n\n\t\t\t// Enable the slide overview mode\n\t\t\toverview: true,\n\n\t\t\t// Vertical centering of slides\n\t\t\tcenter: true,\n\n\t\t\t// Enables touch navigation on devices with touch input\n\t\t\ttouch: true,\n\n\t\t\t// Loop the presentation\n\t\t\tloop: false,\n\n\t\t\t// Change the presentation direction to be RTL\n\t\t\trtl: false,\n\n\t\t\t// Randomizes the order of slides each time the presentation loads\n\t\t\tshuffle: false,\n\n\t\t\t// Turns fragments on and off globally\n\t\t\tfragments: true,\n\n\t\t\t// Flags if the presentation is running in an embedded mode,\n\t\t\t// i.e. contained within a limited portion of the screen\n\t\t\tembedded: false,\n\n\t\t\t// Flags if we should show a help overlay when the question-mark\n\t\t\t// key is pressed\n\t\t\thelp: true,\n\n\t\t\t// Flags if it should be possible to pause the presentation (blackout)\n\t\t\tpause: true,\n\n\t\t\t// Flags if speaker notes should be visible to all viewers\n\t\t\tshowNotes: false,\n\n\t\t\t// Global override for autolaying embedded media (video/audio/iframe)\n\t\t\t// - null: Media will only autoplay if data-autoplay is present\n\t\t\t// - true: All media will autoplay, regardless of individual setting\n\t\t\t// - false: No media will autoplay, regardless of individual setting\n\t\t\tautoPlayMedia: null,\n\n\t\t\t// Controls automatic progression to the next slide\n\t\t\t// - 0: Auto-sliding only happens if the data-autoslide HTML attribute\n\t\t\t// is present on the current slide or fragment\n\t\t\t// - 1+: All slides will progress automatically at the given interval\n\t\t\t// - false: No auto-sliding, even if data-autoslide is present\n\t\t\tautoSlide: 0,\n\n\t\t\t// Stop auto-sliding after user input\n\t\t\tautoSlideStoppable: true,\n\n\t\t\t// Use this method for navigation when auto-sliding (defaults to navigateNext)\n\t\t\tautoSlideMethod: null,\n\n\t\t\t// Enable slide navigation via mouse wheel\n\t\t\tmouseWheel: false,\n\n\t\t\t// Apply a 3D roll to links on hover\n\t\t\trollingLinks: false,\n\n\t\t\t// Hides the address bar on mobile devices\n\t\t\thideAddressBar: true,\n\n\t\t\t// Opens links in an iframe preview overlay\n\t\t\tpreviewLinks: false,\n\n\t\t\t// Exposes the reveal.js API through window.postMessage\n\t\t\tpostMessage: true,\n\n\t\t\t// Dispatches all reveal.js events to the parent window through postMessage\n\t\t\tpostMessageEvents: false,\n\n\t\t\t// Focuses body when page changes visibility to ensure keyboard shortcuts work\n\t\t\tfocusBodyOnPageVisibilityChange: true,\n\n\t\t\t// Transition style\n\t\t\ttransition: 'slide', // none/fade/slide/convex/concave/zoom\n\n\t\t\t// Transition speed\n\t\t\ttransitionSpeed: 'default', // default/fast/slow\n\n\t\t\t// Transition style for full page slide backgrounds\n\t\t\tbackgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom\n\n\t\t\t// Parallax background image\n\t\t\tparallaxBackgroundImage: '', // CSS syntax, e.g. \"a.jpg\"\n\n\t\t\t// Parallax background size\n\t\t\tparallaxBackgroundSize: '', // CSS syntax, e.g. \"3000px 2000px\"\n\n\t\t\t// Amount of pixels to move the parallax background per slide step\n\t\t\tparallaxBackgroundHorizontal: null,\n\t\t\tparallaxBackgroundVertical: null,\n\n\t\t\t// The maximum number of pages a single slide can expand onto when printing\n\t\t\t// to PDF, unlimited by default\n\t\t\tpdfMaxPagesPerSlide: Number.POSITIVE_INFINITY,\n\n\t\t\t// Offset used to reduce the height of content within exported PDF pages.\n\t\t\t// This exists to account for environment differences based on how you\n\t\t\t// print to PDF. CLI printing options, like phantomjs and wkpdf, can end\n\t\t\t// on precisely the total height of the document whereas in-browser\n\t\t\t// printing has to end one pixel before.\n\t\t\tpdfPageHeightOffset: -1,\n\n\t\t\t// Number of slides away from the current that are visible\n\t\t\tviewDistance: 3,\n\n\t\t\t// The display mode that will be used to show slides\n\t\t\tdisplay: 'block',\n\n\t\t\t// Script dependencies to load\n\t\t\tdependencies: []\n\n\t\t},\n\n\t\t// Flags if Reveal.initialize() has been called\n\t\tinitialized = false,\n\n\t\t// Flags if reveal.js is loaded (has dispatched the 'ready' event)\n\t\tloaded = false,\n\n\t\t// Flags if the overview mode is currently active\n\t\toverview = false,\n\n\t\t// Holds the dimensions of our overview slides, including margins\n\t\toverviewSlideWidth = null,\n\t\toverviewSlideHeight = null,\n\n\t\t// The horizontal and vertical index of the currently active slide\n\t\tindexh,\n\t\tindexv,\n\n\t\t// The previous and current slide HTML elements\n\t\tpreviousSlide,\n\t\tcurrentSlide,\n\n\t\tpreviousBackground,\n\n\t\t// Remember which directions that the user has navigated towards\n\t\thasNavigatedRight = false,\n\t\thasNavigatedDown = false,\n\n\t\t// Slides may hold a data-state attribute which we pick up and apply\n\t\t// as a class to the body. This list contains the combined state of\n\t\t// all current slides.\n\t\tstate = [],\n\n\t\t// The current scale of the presentation (see width/height config)\n\t\tscale = 1,\n\n\t\t// CSS transform that is currently applied to the slides container,\n\t\t// split into two groups\n\t\tslidesTransform = { layout: '', overview: '' },\n\n\t\t// Cached references to DOM elements\n\t\tdom = {},\n\n\t\t// Features supported by the browser, see #checkCapabilities()\n\t\tfeatures = {},\n\n\t\t// Client is a mobile device, see #checkCapabilities()\n\t\tisMobileDevice,\n\n\t\t// Client is a desktop Chrome, see #checkCapabilities()\n\t\tisChrome,\n\n\t\t// Throttles mouse wheel navigation\n\t\tlastMouseWheelStep = 0,\n\n\t\t// Delays updates to the URL due to a Chrome thumbnailer bug\n\t\twriteURLTimeout = 0,\n\n\t\t// Flags if the interaction event listeners are bound\n\t\teventsAreBound = false,\n\n\t\t// The current auto-slide duration\n\t\tautoSlide = 0,\n\n\t\t// Auto slide properties\n\t\tautoSlidePlayer,\n\t\tautoSlideTimeout = 0,\n\t\tautoSlideStartTime = -1,\n\t\tautoSlidePaused = false,\n\n\t\t// Holds information about the currently ongoing touch input\n\t\ttouch = {\n\t\t\tstartX: 0,\n\t\t\tstartY: 0,\n\t\t\tstartSpan: 0,\n\t\t\tstartCount: 0,\n\t\t\tcaptured: false,\n\t\t\tthreshold: 40\n\t\t},\n\n\t\t// Holds information about the keyboard shortcuts\n\t\tkeyboardShortcuts = {\n\t\t\t'N , SPACE':\t\t\t'Next slide',\n\t\t\t'P':\t\t\t\t\t'Previous slide',\n\t\t\t'&#8592; , H':\t\t'Navigate left',\n\t\t\t'&#8594; , L':\t\t'Navigate right',\n\t\t\t'&#8593; , K':\t\t'Navigate up',\n\t\t\t'&#8595; , J':\t\t'Navigate down',\n\t\t\t'Home':\t\t\t\t\t'First slide',\n\t\t\t'End':\t\t\t\t\t'Last slide',\n\t\t\t'B , .':\t\t\t\t'Pause',\n\t\t\t'F':\t\t\t\t\t'Fullscreen',\n\t\t\t'ESC, O':\t\t\t\t'Slide overview'\n\t\t};\n\n\t/**\n\t * Starts up the presentation if the client is capable.\n\t */\n\tfunction initialize( options ) {\n\n\t\t// Make sure we only initialize once\n\t\tif( initialized === true ) return;\n\n\t\tinitialized = true;\n\n\t\tcheckCapabilities();\n\n\t\tif( !features.transforms2d && !features.transforms3d ) {\n\t\t\tdocument.body.setAttribute( 'class', 'no-transforms' );\n\n\t\t\t// Since JS won't be running any further, we load all lazy\n\t\t\t// loading elements upfront\n\t\t\tvar images = toArray( document.getElementsByTagName( 'img' ) ),\n\t\t\t\tiframes = toArray( document.getElementsByTagName( 'iframe' ) );\n\n\t\t\tvar lazyLoadable = images.concat( iframes );\n\n\t\t\tfor( var i = 0, len = lazyLoadable.length; i < len; i++ ) {\n\t\t\t\tvar element = lazyLoadable[i];\n\t\t\t\tif( element.getAttribute( 'data-src' ) ) {\n\t\t\t\t\telement.setAttribute( 'src', element.getAttribute( 'data-src' ) );\n\t\t\t\t\telement.removeAttribute( 'data-src' );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If the browser doesn't support core features we won't be\n\t\t\t// using JavaScript to control the presentation\n\t\t\treturn;\n\t\t}\n\n\t\t// Cache references to key DOM elements\n\t\tdom.wrapper = document.querySelector( '.reveal' );\n\t\tdom.slides = document.querySelector( '.reveal .slides' );\n\n\t\t// Force a layout when the whole page, incl fonts, has loaded\n\t\twindow.addEventListener( 'load', layout, false );\n\n\t\tvar query = Reveal.getQueryHash();\n\n\t\t// Do not accept new dependencies via query config to avoid\n\t\t// the potential of malicious script injection\n\t\tif( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];\n\n\t\t// Copy options over to our config object\n\t\textend( config, options );\n\t\textend( config, query );\n\n\t\t// Hide the address bar in mobile browsers\n\t\thideAddressBar();\n\n\t\t// Loads the dependencies and continues to #start() once done\n\t\tload();\n\n\t}\n\n\t/**\n\t * Inspect the client to see what it's capable of, this\n\t * should only happens once per runtime.\n\t */\n\tfunction checkCapabilities() {\n\n\t\tisMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA );\n\t\tisChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );\n\n\t\tvar testElement = document.createElement( 'div' );\n\n\t\tfeatures.transforms3d = 'WebkitPerspective' in testElement.style ||\n\t\t\t\t\t\t\t\t'MozPerspective' in testElement.style ||\n\t\t\t\t\t\t\t\t'msPerspective' in testElement.style ||\n\t\t\t\t\t\t\t\t'OPerspective' in testElement.style ||\n\t\t\t\t\t\t\t\t'perspective' in testElement.style;\n\n\t\tfeatures.transforms2d = 'WebkitTransform' in testElement.style ||\n\t\t\t\t\t\t\t\t'MozTransform' in testElement.style ||\n\t\t\t\t\t\t\t\t'msTransform' in testElement.style ||\n\t\t\t\t\t\t\t\t'OTransform' in testElement.style ||\n\t\t\t\t\t\t\t\t'transform' in testElement.style;\n\n\t\tfeatures.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;\n\t\tfeatures.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function';\n\n\t\tfeatures.canvas = !!document.createElement( 'canvas' ).getContext;\n\n\t\t// Transitions in the overview are disabled in desktop and\n\t\t// Safari due to lag\n\t\tfeatures.overviewTransitions = !/Version\\/[\\d\\.]+.*Safari/.test( UA );\n\n\t\t// Flags if we should use zoom instead of transform to scale\n\t\t// up slides. Zoom produces crisper results but has a lot of\n\t\t// xbrowser quirks so we only use it in whitelsited browsers.\n\t\tfeatures.zoom = 'zoom' in testElement.style && !isMobileDevice &&\n\t\t\t\t\t\t( isChrome || /Version\\/[\\d\\.]+.*Safari/.test( UA ) );\n\n\t}\n\n /**\n * Loads the dependencies of reveal.js. Dependencies are\n * defined via the configuration option 'dependencies'\n * and will be loaded prior to starting/binding reveal.js.\n * Some dependencies may have an 'async' flag, if so they\n * will load after reveal.js has been started up.\n */\n\tfunction load() {\n\n\t\tvar scripts = [],\n\t\t\tscriptsAsync = [],\n\t\t\tscriptsToPreload = 0;\n\n\t\t// Called once synchronous scripts finish loading\n\t\tfunction proceed() {\n\t\t\tif( scriptsAsync.length ) {\n\t\t\t\t// Load asynchronous scripts\n\t\t\t\thead.js.apply( null, scriptsAsync );\n\t\t\t}\n\n\t\t\tstart();\n\t\t}\n\n\t\tfunction loadScript( s ) {\n\t\t\thead.ready( s.src.match( /([\\w\\d_\\-]*)\\.?js$|[^\\\\\\/]*$/i )[0], function() {\n\t\t\t\t// Extension may contain callback functions\n\t\t\t\tif( typeof s.callback === 'function' ) {\n\t\t\t\t\ts.callback.apply( this );\n\t\t\t\t}\n\n\t\t\t\tif( --scriptsToPreload === 0 ) {\n\t\t\t\t\tproceed();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tfor( var i = 0, len = config.dependencies.length; i < len; i++ ) {\n\t\t\tvar s = config.dependencies[i];\n\n\t\t\t// Load if there's no condition or the condition is truthy\n\t\t\tif( !s.condition || s.condition() ) {\n\t\t\t\tif( s.async ) {\n\t\t\t\t\tscriptsAsync.push( s.src );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tscripts.push( s.src );\n\t\t\t\t}\n\n\t\t\t\tloadScript( s );\n\t\t\t}\n\t\t}\n\n\t\tif( scripts.length ) {\n\t\t\tscriptsToPreload = scripts.length;\n\n\t\t\t// Load synchronous scripts\n\t\t\thead.js.apply( null, scripts );\n\t\t}\n\t\telse {\n\t\t\tproceed();\n\t\t}\n\n\t}\n\n\t/**\n\t * Starts up reveal.js by binding input events and navigating\n\t * to the current URL deeplink if there is one.\n\t */\n\tfunction start() {\n\n\t\tloaded = true;\n\n\t\t// Make sure we've got all the DOM elements we need\n\t\tsetupDOM();\n\n\t\t// Listen to messages posted to this window\n\t\tsetupPostMessage();\n\n\t\t// Prevent the slides from being scrolled out of view\n\t\tsetupScrollPrevention();\n\n\t\t// Resets all vertical slides so that only the first is visible\n\t\tresetVerticalSlides();\n\n\t\t// Updates the presentation to match the current configuration values\n\t\tconfigure();\n\n\t\t// Read the initial hash\n\t\treadURL();\n\n\t\t// Update all backgrounds\n\t\tupdateBackground( true );\n\n\t\t// Notify listeners that the presentation is ready but use a 1ms\n\t\t// timeout to ensure it's not fired synchronously after #initialize()\n\t\tsetTimeout( function() {\n\t\t\t// Enable transitions now that we're loaded\n\t\t\tdom.slides.classList.remove( 'no-transition' );\n\n\t\t\tdom.wrapper.classList.add( 'ready' );\n\n\t\t\tdispatchEvent( 'ready', {\n\t\t\t\t'indexh': indexh,\n\t\t\t\t'indexv': indexv,\n\t\t\t\t'currentSlide': currentSlide\n\t\t\t} );\n\t\t}, 1 );\n\n\t\t// Special setup and config is required when printing to PDF\n\t\tif( isPrintingPDF() ) {\n\t\t\tremoveEventListeners();\n\n\t\t\t// The document needs to have loaded for the PDF layout\n\t\t\t// measurements to be accurate\n\t\t\tif( document.readyState === 'complete' ) {\n\t\t\t\tsetupPDF();\n\t\t\t}\n\t\t\telse {\n\t\t\t\twindow.addEventListener( 'load', setupPDF );\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Finds and stores references to DOM elements which are\n\t * required by the presentation. If a required element is\n\t * not found, it is created.\n\t */\n\tfunction setupDOM() {\n\n\t\t// Prevent transitions while we're loading\n\t\tdom.slides.classList.add( 'no-transition' );\n\n\t\tif( isMobileDevice ) {\n\t\t\tdom.wrapper.classList.add( 'no-hover' );\n\t\t}\n\t\telse {\n\t\t\tdom.wrapper.classList.remove( 'no-hover' );\n\t\t}\n\n\t\tif( /iphone/gi.test( UA ) ) {\n\t\t\tdom.wrapper.classList.add( 'ua-iphone' );\n\t\t}\n\t\telse {\n\t\t\tdom.wrapper.classList.remove( 'ua-iphone' );\n\t\t}\n\n\t\t// Background element\n\t\tdom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );\n\n\t\t// Progress bar\n\t\tdom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '<span></span>' );\n\t\tdom.progressbar = dom.progress.querySelector( 'span' );\n\n\t\t// Arrow controls\n\t\tdom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls',\n\t\t\t'<button class=\"navigate-left\" aria-label=\"previous slide\"><div class=\"controls-arrow\"></div></button>' +\n\t\t\t'<button class=\"navigate-right\" aria-label=\"next slide\"><div class=\"controls-arrow\"></div></button>' +\n\t\t\t'<button class=\"navigate-up\" aria-label=\"above slide\"><div class=\"controls-arrow\"></div></button>' +\n\t\t\t'<button class=\"navigate-down\" aria-label=\"below slide\"><div class=\"controls-arrow\"></div></button>' );\n\n\t\t// Slide number\n\t\tdom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );\n\n\t\t// Element containing notes that are visible to the audience\n\t\tdom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null );\n\t\tdom.speakerNotes.setAttribute( 'data-prevent-swipe', '' );\n\t\tdom.speakerNotes.setAttribute( 'tabindex', '0' );\n\n\t\t// Overlay graphic which is displayed during the paused mode\n\t\tcreateSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );\n\n\t\tdom.wrapper.setAttribute( 'role', 'application' );\n\n\t\t// There can be multiple instances of controls throughout the page\n\t\tdom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );\n\t\tdom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );\n\t\tdom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );\n\t\tdom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );\n\t\tdom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );\n\t\tdom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );\n\n\t\t// The right and down arrows in the standard reveal.js controls\n\t\tdom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' );\n\t\tdom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' );\n\n\t\tdom.statusDiv = createStatusDiv();\n\t}\n\n\t/**\n\t * Creates a hidden div with role aria-live to announce the\n\t * current slide content. Hide the div off-screen to make it\n\t * available only to Assistive Technologies.\n\t *\n\t * @return {HTMLElement}\n\t */\n\tfunction createStatusDiv() {\n\n\t\tvar statusDiv = document.getElementById( 'aria-status-div' );\n\t\tif( !statusDiv ) {\n\t\t\tstatusDiv = document.createElement( 'div' );\n\t\t\tstatusDiv.style.position = 'absolute';\n\t\t\tstatusDiv.style.height = '1px';\n\t\t\tstatusDiv.style.width = '1px';\n\t\t\tstatusDiv.style.overflow = 'hidden';\n\t\t\tstatusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )';\n\t\t\tstatusDiv.setAttribute( 'id', 'aria-status-div' );\n\t\t\tstatusDiv.setAttribute( 'aria-live', 'polite' );\n\t\t\tstatusDiv.setAttribute( 'aria-atomic','true' );\n\t\t\tdom.wrapper.appendChild( statusDiv );\n\t\t}\n\t\treturn statusDiv;\n\n\t}\n\n\t/**\n\t * Converts the given HTML element into a string of text\n\t * that can be announced to a screen reader. Hidden\n\t * elements are excluded.\n\t */\n\tfunction getStatusText( node ) {\n\n\t\tvar text = '';\n\n\t\t// Text node\n\t\tif( node.nodeType === 3 ) {\n\t\t\ttext += node.textContent;\n\t\t}\n\t\t// Element node\n\t\telse if( node.nodeType === 1 ) {\n\n\t\t\tvar isAriaHidden = node.getAttribute( 'aria-hidden' );\n\t\t\tvar isDisplayHidden = window.getComputedStyle( node )['display'] === 'none';\n\t\t\tif( isAriaHidden !== 'true' && !isDisplayHidden ) {\n\n\t\t\t\ttoArray( node.childNodes ).forEach( function( child ) {\n\t\t\t\t\ttext += getStatusText( child );\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn text;\n\n\t}\n\n\t/**\n\t * Configures the presentation for printing to a static\n\t * PDF.\n\t */\n\tfunction setupPDF() {\n\n\t\tvar slideSize = getComputedSlideSize( window.innerWidth, window.innerHeight );\n\n\t\t// Dimensions of the PDF pages\n\t\tvar pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),\n\t\t\tpageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );\n\n\t\t// Dimensions of slides within the pages\n\t\tvar slideWidth = slideSize.width,\n\t\t\tslideHeight = slideSize.height;\n\n\t\t// Let the browser know what page size we want to print\n\t\tinjectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );\n\n\t\t// Limit the size of certain elements to the dimensions of the slide\n\t\tinjectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );\n\n\t\tdocument.body.classList.add( 'print-pdf' );\n\t\tdocument.body.style.width = pageWidth + 'px';\n\t\tdocument.body.style.height = pageHeight + 'px';\n\n\t\t// Make sure stretch elements fit on slide\n\t\tlayoutSlideContents( slideWidth, slideHeight );\n\n\t\t// Add each slide's index as attributes on itself, we need these\n\t\t// indices to generate slide numbers below\n\t\ttoArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {\n\t\t\thslide.setAttribute( 'data-index-h', h );\n\n\t\t\tif( hslide.classList.contains( 'stack' ) ) {\n\t\t\t\ttoArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {\n\t\t\t\t\tvslide.setAttribute( 'data-index-h', h );\n\t\t\t\t\tvslide.setAttribute( 'data-index-v', v );\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\n\t\t// Slide and slide background layout\n\t\ttoArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {\n\n\t\t\t// Vertical stacks are not centred since their section\n\t\t\t// children will be\n\t\t\tif( slide.classList.contains( 'stack' ) === false ) {\n\t\t\t\t// Center the slide inside of the page, giving the slide some margin\n\t\t\t\tvar left = ( pageWidth - slideWidth ) / 2,\n\t\t\t\t\ttop = ( pageHeight - slideHeight ) / 2;\n\n\t\t\t\tvar contentHeight = slide.scrollHeight;\n\t\t\t\tvar numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );\n\n\t\t\t\t// Adhere to configured pages per slide limit\n\t\t\t\tnumberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide );\n\n\t\t\t\t// Center slides vertically\n\t\t\t\tif( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) {\n\t\t\t\t\ttop = Math.max( ( pageHeight - contentHeight ) / 2, 0 );\n\t\t\t\t}\n\n\t\t\t\t// Wrap the slide in a page element and hide its overflow\n\t\t\t\t// so that no page ever flows onto another\n\t\t\t\tvar page = document.createElement( 'div' );\n\t\t\t\tpage.className = 'pdf-page';\n\t\t\t\tpage.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';\n\t\t\t\tslide.parentNode.insertBefore( page, slide );\n\t\t\t\tpage.appendChild( slide );\n\n\t\t\t\t// Position the slide inside of the page\n\t\t\t\tslide.style.left = left + 'px';\n\t\t\t\tslide.style.top = top + 'px';\n\t\t\t\tslide.style.width = slideWidth + 'px';\n\n\t\t\t\tif( slide.slideBackgroundElement ) {\n\t\t\t\t\tpage.insertBefore( slide.slideBackgroundElement, slide );\n\t\t\t\t}\n\n\t\t\t\t// Inject notes if `showNotes` is enabled\n\t\t\t\tif( config.showNotes ) {\n\n\t\t\t\t\t// Are there notes for this slide?\n\t\t\t\t\tvar notes = getSlideNotes( slide );\n\t\t\t\t\tif( notes ) {\n\n\t\t\t\t\t\tvar notesSpacing = 8;\n\t\t\t\t\t\tvar notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';\n\t\t\t\t\t\tvar notesElement = document.createElement( 'div' );\n\t\t\t\t\t\tnotesElement.classList.add( 'speaker-notes' );\n\t\t\t\t\t\tnotesElement.classList.add( 'speaker-notes-pdf' );\n\t\t\t\t\t\tnotesElement.setAttribute( 'data-layout', notesLayout );\n\t\t\t\t\t\tnotesElement.innerHTML = notes;\n\n\t\t\t\t\t\tif( notesLayout === 'separate-page' ) {\n\t\t\t\t\t\t\tpage.parentNode.insertBefore( notesElement, page.nextSibling );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tnotesElement.style.left = notesSpacing + 'px';\n\t\t\t\t\t\t\tnotesElement.style.bottom = notesSpacing + 'px';\n\t\t\t\t\t\t\tnotesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px';\n\t\t\t\t\t\t\tpage.appendChild( notesElement );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// Inject slide numbers if `slideNumbers` are enabled\n\t\t\t\tif( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) {\n\t\t\t\t\tvar slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,\n\t\t\t\t\t\tslideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;\n\n\t\t\t\t\tvar numberElement = document.createElement( 'div' );\n\t\t\t\t\tnumberElement.classList.add( 'slide-number' );\n\t\t\t\t\tnumberElement.classList.add( 'slide-number-pdf' );\n\t\t\t\t\tnumberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV );\n\t\t\t\t\tpage.appendChild( numberElement );\n\t\t\t\t}\n\t\t\t}\n\n\t\t} );\n\n\t\t// Show all fragments\n\t\ttoArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' .fragment' ) ).forEach( function( fragment ) {\n\t\t\tfragment.classList.add( 'visible' );\n\t\t} );\n\n\t\t// Notify subscribers that the PDF layout is good to go\n\t\tdispatchEvent( 'pdf-ready' );\n\n\t}\n\n\t/**\n\t * This is an unfortunate necessity. Some actions – such as\n\t * an input field being focused in an iframe or using the\n\t * keyboard to expand text selection beyond the bounds of\n\t * a slide – can trigger our content to be pushed out of view.\n\t * This scrolling can not be prevented by hiding overflow in\n\t * CSS (we already do) so we have to resort to repeatedly\n\t * checking if the slides have been offset :(\n\t */\n\tfunction setupScrollPrevention() {\n\n\t\tsetInterval( function() {\n\t\t\tif( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) {\n\t\t\t\tdom.wrapper.scrollTop = 0;\n\t\t\t\tdom.wrapper.scrollLeft = 0;\n\t\t\t}\n\t\t}, 1000 );\n\n\t}\n\n\t/**\n\t * Creates an HTML element and returns a reference to it.\n\t * If the element already exists the existing instance will\n\t * be returned.\n\t *\n\t * @param {HTMLElement} container\n\t * @param {string} tagname\n\t * @param {string} classname\n\t * @param {string} innerHTML\n\t *\n\t * @return {HTMLElement}\n\t */\n\tfunction createSingletonNode( container, tagname, classname, innerHTML ) {\n\n\t\t// Find all nodes matching the description\n\t\tvar nodes = container.querySelectorAll( '.' + classname );\n\n\t\t// Check all matches to find one which is a direct child of\n\t\t// the specified container\n\t\tfor( var i = 0; i < nodes.length; i++ ) {\n\t\t\tvar testNode = nodes[i];\n\t\t\tif( testNode.parentNode === container ) {\n\t\t\t\treturn testNode;\n\t\t\t}\n\t\t}\n\n\t\t// If no node was found, create it now\n\t\tvar node = document.createElement( tagname );\n\t\tnode.className = classname;\n\t\tif( typeof innerHTML === 'string' ) {\n\t\t\tnode.innerHTML = innerHTML;\n\t\t}\n\t\tcontainer.appendChild( node );\n\n\t\treturn node;\n\n\t}\n\n\t/**\n\t * Creates the slide background elements and appends them\n\t * to the background container. One element is created per\n\t * slide no matter if the given slide has visible background.\n\t */\n\tfunction createBackgrounds() {\n\n\t\tvar printMode = isPrintingPDF();\n\n\t\t// Clear prior backgrounds\n\t\tdom.background.innerHTML = '';\n\t\tdom.background.classList.add( 'no-transition' );\n\n\t\t// Iterate over all horizontal slides\n\t\ttoArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) {\n\n\t\t\tvar backgroundStack = createBackground( slideh, dom.background );\n\n\t\t\t// Iterate over all vertical slides\n\t\t\ttoArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) {\n\n\t\t\t\tcreateBackground( slidev, backgroundStack );\n\n\t\t\t\tbackgroundStack.classList.add( 'stack' );\n\n\t\t\t} );\n\n\t\t} );\n\n\t\t// Add parallax background if specified\n\t\tif( config.parallaxBackgroundImage ) {\n\n\t\t\tdom.background.style.backgroundImage = 'url(\"' + config.parallaxBackgroundImage + '\")';\n\t\t\tdom.background.style.backgroundSize = config.parallaxBackgroundSize;\n\n\t\t\t// Make sure the below properties are set on the element - these properties are\n\t\t\t// needed for proper transitions to be set on the element via CSS. To remove\n\t\t\t// annoying background slide-in effect when the presentation starts, apply\n\t\t\t// these properties after short time delay\n\t\t\tsetTimeout( function() {\n\t\t\t\tdom.wrapper.classList.add( 'has-parallax-background' );\n\t\t\t}, 1 );\n\n\t\t}\n\t\telse {\n\n\t\t\tdom.background.style.backgroundImage = '';\n\t\t\tdom.wrapper.classList.remove( 'has-parallax-background' );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a background for the given slide.\n\t *\n\t * @param {HTMLElement} slide\n\t * @param {HTMLElement} container The element that the background\n\t * should be appended to\n\t * @return {HTMLElement} New background div\n\t */\n\tfunction createBackground( slide, container ) {\n\n\t\tvar data = {\n\t\t\tbackground: slide.getAttribute( 'data-background' ),\n\t\t\tbackgroundSize: slide.getAttribute( 'data-background-size' ),\n\t\t\tbackgroundImage: slide.getAttribute( 'data-background-image' ),\n\t\t\tbackgroundVideo: slide.getAttribute( 'data-background-video' ),\n\t\t\tbackgroundIframe: slide.getAttribute( 'data-background-iframe' ),\n\t\t\tbackgroundColor: slide.getAttribute( 'data-background-color' ),\n\t\t\tbackgroundRepeat: slide.getAttribute( 'data-background-repeat' ),\n\t\t\tbackgroundPosition: slide.getAttribute( 'data-background-position' ),\n\t\t\tbackgroundTransition: slide.getAttribute( 'data-background-transition' )\n\t\t};\n\n\t\tvar element = document.createElement( 'div' );\n\n\t\t// Carry over custom classes from the slide to the background\n\t\telement.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' );\n\n\t\tif( data.background ) {\n\t\t\t// Auto-wrap image urls in url(...)\n\t\t\tif( /^(http|file|\\/\\/)/gi.test( data.background ) || /\\.(svg|png|jpg|jpeg|gif|bmp)([?#]|$)/gi.test( data.background ) ) {\n\t\t\t\tslide.setAttribute( 'data-background-image', data.background );\n\t\t\t}\n\t\t\telse {\n\t\t\t\telement.style.background = data.background;\n\t\t\t}\n\t\t}\n\n\t\t// Create a hash for this combination of background settings.\n\t\t// This is used to determine when two slide backgrounds are\n\t\t// the same.\n\t\tif( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {\n\t\t\telement.setAttribute( 'data-background-hash', data.background +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundSize +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundImage +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundVideo +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundIframe +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundColor +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundRepeat +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundPosition +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundTransition );\n\t\t}\n\n\t\t// Additional and optional background properties\n\t\tif( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize;\n\t\tif( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize );\n\t\tif( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;\n\t\tif( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat;\n\t\tif( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition;\n\t\tif( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );\n\n\t\tcontainer.appendChild( element );\n\n\t\t// If backgrounds are being recreated, clear old classes\n\t\tslide.classList.remove( 'has-dark-background' );\n\t\tslide.classList.remove( 'has-light-background' );\n\n\t\tslide.slideBackgroundElement = element;\n\n\t\t// If this slide has a background color, add a class that\n\t\t// signals if it is light or dark. If the slide has no background\n\t\t// color, no class will be set\n\t\tvar computedBackgroundStyle = window.getComputedStyle( element );\n\t\tif( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) {\n\t\t\tvar rgb = colorToRgb( computedBackgroundStyle.backgroundColor );\n\n\t\t\t// Ignore fully transparent backgrounds. Some browsers return\n\t\t\t// rgba(0,0,0,0) when reading the computed background color of\n\t\t\t// an element with no background\n\t\t\tif( rgb && rgb.a !== 0 ) {\n\t\t\t\tif( colorBrightness( computedBackgroundStyle.backgroundColor ) < 128 ) {\n\t\t\t\t\tslide.classList.add( 'has-dark-background' );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tslide.classList.add( 'has-light-background' );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn element;\n\n\t}\n\n\t/**\n\t * Registers a listener to postMessage events, this makes it\n\t * possible to call all reveal.js API methods from another\n\t * window. For example:\n\t *\n\t * revealWindow.postMessage( JSON.stringify({\n\t * method: 'slide',\n\t * args: [ 2 ]\n\t * }), '*' );\n\t */\n\tfunction setupPostMessage() {\n\n\t\tif( config.postMessage ) {\n\t\t\twindow.addEventListener( 'message', function ( event ) {\n\t\t\t\tvar data = event.data;\n\n\t\t\t\t// Make sure we're dealing with JSON\n\t\t\t\tif( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {\n\t\t\t\t\tdata = JSON.parse( data );\n\n\t\t\t\t\t// Check if the requested method can be found\n\t\t\t\t\tif( data.method && typeof Reveal[data.method] === 'function' ) {\n\t\t\t\t\t\tReveal[data.method].apply( Reveal, data.args );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, false );\n\t\t}\n\n\t}\n\n\t/**\n\t * Applies the configuration settings from the config\n\t * object. May be called multiple times.\n\t *\n\t * @param {object} options\n\t */\n\tfunction configure( options ) {\n\n\t\tvar oldTransition = config.transition;\n\n\t\t// New config options may be passed when this method\n\t\t// is invoked through the API after initialization\n\t\tif( typeof options === 'object' ) extend( config, options );\n\n\t\t// Abort if reveal.js hasn't finished loading, config\n\t\t// changes will be applied automatically once loading\n\t\t// finishes\n\t\tif( loaded === false ) return;\n\n\t\tvar numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;\n\n\t\t// Remove the previously configured transition class\n\t\tdom.wrapper.classList.remove( oldTransition );\n\n\t\t// Force linear transition based on browser capabilities\n\t\tif( features.transforms3d === false ) config.transition = 'linear';\n\n\t\tdom.wrapper.classList.add( config.transition );\n\n\t\tdom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );\n\t\tdom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );\n\n\t\tdom.controls.style.display = config.controls ? 'block' : 'none';\n\t\tdom.progress.style.display = config.progress ? 'block' : 'none';\n\n\t\tdom.controls.setAttribute( 'data-controls-layout', config.controlsLayout );\n\t\tdom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows );\n\n\t\tif( config.shuffle ) {\n\t\t\tshuffle();\n\t\t}\n\n\t\tif( config.rtl ) {\n\t\t\tdom.wrapper.classList.add( 'rtl' );\n\t\t}\n\t\telse {\n\t\t\tdom.wrapper.classList.remove( 'rtl' );\n\t\t}\n\n\t\tif( config.center ) {\n\t\t\tdom.wrapper.classList.add( 'center' );\n\t\t}\n\t\telse {\n\t\t\tdom.wrapper.classList.remove( 'center' );\n\t\t}\n\n\t\t// Exit the paused mode if it was configured off\n\t\tif( config.pause === false ) {\n\t\t\tresume();\n\t\t}\n\n\t\tif( config.showNotes ) {\n\t\t\tdom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' );\n\t\t}\n\n\t\tif( config.mouseWheel ) {\n\t\t\tdocument.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF\n\t\t\tdocument.addEventListener( 'mousewheel', onDocumentMouseScroll, false );\n\t\t}\n\t\telse {\n\t\t\tdocument.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF\n\t\t\tdocument.removeEventListener( 'mousewheel', onDocumentMouseScroll, false );\n\t\t}\n\n\t\t// Rolling 3D links\n\t\tif( config.rollingLinks ) {\n\t\t\tenableRollingLinks();\n\t\t}\n\t\telse {\n\t\t\tdisableRollingLinks();\n\t\t}\n\n\t\t// Iframe link previews\n\t\tif( config.previewLinks ) {\n\t\t\tenablePreviewLinks();\n\t\t\tdisablePreviewLinks( '[data-preview-link=false]' );\n\t\t}\n\t\telse {\n\t\t\tdisablePreviewLinks();\n\t\t\tenablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' );\n\t\t}\n\n\t\t// Remove existing auto-slide controls\n\t\tif( autoSlidePlayer ) {\n\t\t\tautoSlidePlayer.destroy();\n\t\t\tautoSlidePlayer = null;\n\t\t}\n\n\t\t// Generate auto-slide controls if needed\n\t\tif( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) {\n\t\t\tautoSlidePlayer = new Playback( dom.wrapper, function() {\n\t\t\t\treturn Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 );\n\t\t\t} );\n\n\t\t\tautoSlidePlayer.on( 'click', onAutoSlidePlayerClick );\n\t\t\tautoSlidePaused = false;\n\t\t}\n\n\t\t// When fragments are turned off they should be visible\n\t\tif( config.fragments === false ) {\n\t\t\ttoArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) {\n\t\t\t\telement.classList.add( 'visible' );\n\t\t\t\telement.classList.remove( 'current-fragment' );\n\t\t\t} );\n\t\t}\n\n\t\t// Slide numbers\n\t\tvar slideNumberDisplay = 'none';\n\t\tif( config.slideNumber && !isPrintingPDF() ) {\n\t\t\tif( config.showSlideNumber === 'all' ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t\telse if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t}\n\n\t\tdom.slideNumber.style.display = slideNumberDisplay;\n\n\t\tsync();\n\n\t}\n\n\t/**\n\t * Binds all event listeners.\n\t */\n\tfunction addEventListeners() {\n\n\t\teventsAreBound = true;\n\n\t\twindow.addEventListener( 'hashchange', onWindowHashChange, false );\n\t\twindow.addEventListener( 'resize', onWindowResize, false );\n\n\t\tif( config.touch ) {\n\t\t\tdom.wrapper.addEventListener( 'touchstart', onTouchStart, false );\n\t\t\tdom.wrapper.addEventListener( 'touchmove', onTouchMove, false );\n\t\t\tdom.wrapper.addEventListener( 'touchend', onTouchEnd, false );\n\n\t\t\t// Support pointer-style touch interaction as well\n\t\t\tif( window.navigator.pointerEnabled ) {\n\t\t\t\t// IE 11 uses un-prefixed version of pointer events\n\t\t\t\tdom.wrapper.addEventListener( 'pointerdown', onPointerDown, false );\n\t\t\t\tdom.wrapper.addEventListener( 'pointermove', onPointerMove, false );\n\t\t\t\tdom.wrapper.addEventListener( 'pointerup', onPointerUp, false );\n\t\t\t}\n\t\t\telse if( window.navigator.msPointerEnabled ) {\n\t\t\t\t// IE 10 uses prefixed version of pointer events\n\t\t\t\tdom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false );\n\t\t\t\tdom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false );\n\t\t\t\tdom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false );\n\t\t\t}\n\t\t}\n\n\t\tif( config.keyboard ) {\n\t\t\tdocument.addEventListener( 'keydown', onDocumentKeyDown, false );\n\t\t\tdocument.addEventListener( 'keypress', onDocumentKeyPress, false );\n\t\t}\n\n\t\tif( config.progress && dom.progress ) {\n\t\t\tdom.progress.addEventListener( 'click', onProgressClicked, false );\n\t\t}\n\n\t\tif( config.focusBodyOnPageVisibilityChange ) {\n\t\t\tvar visibilityChange;\n\n\t\t\tif( 'hidden' in document ) {\n\t\t\t\tvisibilityChange = 'visibilitychange';\n\t\t\t}\n\t\t\telse if( 'msHidden' in document ) {\n\t\t\t\tvisibilityChange = 'msvisibilitychange';\n\t\t\t}\n\t\t\telse if( 'webkitHidden' in document ) {\n\t\t\t\tvisibilityChange = 'webkitvisibilitychange';\n\t\t\t}\n\n\t\t\tif( visibilityChange ) {\n\t\t\t\tdocument.addEventListener( visibilityChange, onPageVisibilityChange, false );\n\t\t\t}\n\t\t}\n\n\t\t// Listen to both touch and click events, in case the device\n\t\t// supports both\n\t\tvar pointerEvents = [ 'touchstart', 'click' ];\n\n\t\t// Only support touch for Android, fixes double navigations in\n\t\t// stock browser\n\t\tif( UA.match( /android/gi ) ) {\n\t\t\tpointerEvents = [ 'touchstart' ];\n\t\t}\n\n\t\tpointerEvents.forEach( function( eventName ) {\n\t\t\tdom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );\n\t\t\tdom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );\n\t\t\tdom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );\n\t\t\tdom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );\n\t\t\tdom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );\n\t\t\tdom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Unbinds all event listeners.\n\t */\n\tfunction removeEventListeners() {\n\n\t\teventsAreBound = false;\n\n\t\tdocument.removeEventListener( 'keydown', onDocumentKeyDown, false );\n\t\tdocument.removeEventListener( 'keypress', onDocumentKeyPress, false );\n\t\twindow.removeEventListener( 'hashchange', onWindowHashChange, false );\n\t\twindow.removeEventListener( 'resize', onWindowResize, false );\n\n\t\tdom.wrapper.removeEventListener( 'touchstart', onTouchStart, false );\n\t\tdom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );\n\t\tdom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );\n\n\t\t// IE11\n\t\tif( window.navigator.pointerEnabled ) {\n\t\t\tdom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false );\n\t\t\tdom.wrapper.removeEventListener( 'pointermove', onPointerMove, false );\n\t\t\tdom.wrapper.removeEventListener( 'pointerup', onPointerUp, false );\n\t\t}\n\t\t// IE10\n\t\telse if( window.navigator.msPointerEnabled ) {\n\t\t\tdom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false );\n\t\t\tdom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false );\n\t\t\tdom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false );\n\t\t}\n\n\t\tif ( config.progress && dom.progress ) {\n\t\t\tdom.progress.removeEventListener( 'click', onProgressClicked, false );\n\t\t}\n\n\t\t[ 'touchstart', 'click' ].forEach( function( eventName ) {\n\t\t\tdom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );\n\t\t\tdom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );\n\t\t\tdom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );\n\t\t\tdom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );\n\t\t\tdom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );\n\t\t\tdom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Extend object a with the properties of object b.\n\t * If there's a conflict, object b takes precedence.\n\t *\n\t * @param {object} a\n\t * @param {object} b\n\t */\n\tfunction extend( a, b ) {\n\n\t\tfor( var i in b ) {\n\t\t\ta[ i ] = b[ i ];\n\t\t}\n\n\t\treturn a;\n\n\t}\n\n\t/**\n\t * Converts the target object to an array.\n\t *\n\t * @param {object} o\n\t * @return {object[]}\n\t */\n\tfunction toArray( o ) {\n\n\t\treturn Array.prototype.slice.call( o );\n\n\t}\n\n\t/**\n\t * Utility for deserializing a value.\n\t *\n\t * @param {*} value\n\t * @return {*}\n\t */\n\tfunction deserialize( value ) {\n\n\t\tif( typeof value === 'string' ) {\n\t\t\tif( value === 'null' ) return null;\n\t\t\telse if( value === 'true' ) return true;\n\t\t\telse if( value === 'false' ) return false;\n\t\t\telse if( value.match( /^-?[\\d\\.]+$/ ) ) return parseFloat( value );\n\t\t}\n\n\t\treturn value;\n\n\t}\n\n\t/**\n\t * Measures the distance in pixels between point a\n\t * and point b.\n\t *\n\t * @param {object} a point with x/y properties\n\t * @param {object} b point with x/y properties\n\t *\n\t * @return {number}\n\t */\n\tfunction distanceBetween( a, b ) {\n\n\t\tvar dx = a.x - b.x,\n\t\t\tdy = a.y - b.y;\n\n\t\treturn Math.sqrt( dx*dx + dy*dy );\n\n\t}\n\n\t/**\n\t * Applies a CSS transform to the target element.\n\t *\n\t * @param {HTMLElement} element\n\t * @param {string} transform\n\t */\n\tfunction transformElement( element, transform ) {\n\n\t\telement.style.WebkitTransform = transform;\n\t\telement.style.MozTransform = transform;\n\t\telement.style.msTransform = transform;\n\t\telement.style.transform = transform;\n\n\t}\n\n\t/**\n\t * Applies CSS transforms to the slides container. The container\n\t * is transformed from two separate sources: layout and the overview\n\t * mode.\n\t *\n\t * @param {object} transforms\n\t */\n\tfunction transformSlides( transforms ) {\n\n\t\t// Pick up new transforms from arguments\n\t\tif( typeof transforms.layout === 'string' ) slidesTransform.layout = transforms.layout;\n\t\tif( typeof transforms.overview === 'string' ) slidesTransform.overview = transforms.overview;\n\n\t\t// Apply the transforms to the slides container\n\t\tif( slidesTransform.layout ) {\n\t\t\ttransformElement( dom.slides, slidesTransform.layout + ' ' + slidesTransform.overview );\n\t\t}\n\t\telse {\n\t\t\ttransformElement( dom.slides, slidesTransform.overview );\n\t\t}\n\n\t}\n\n\t/**\n\t * Injects the given CSS styles into the DOM.\n\t *\n\t * @param {string} value\n\t */\n\tfunction injectStyleSheet( value ) {\n\n\t\tvar tag = document.createElement( 'style' );\n\t\ttag.type = 'text/css';\n\t\tif( tag.styleSheet ) {\n\t\t\ttag.styleSheet.cssText = value;\n\t\t}\n\t\telse {\n\t\t\ttag.appendChild( document.createTextNode( value ) );\n\t\t}\n\t\tdocument.getElementsByTagName( 'head' )[0].appendChild( tag );\n\n\t}\n\n\t/**\n\t * Find the closest parent that matches the given\n\t * selector.\n\t *\n\t * @param {HTMLElement} target The child element\n\t * @param {String} selector The CSS selector to match\n\t * the parents against\n\t *\n\t * @return {HTMLElement} The matched parent or null\n\t * if no matching parent was found\n\t */\n\tfunction closestParent( target, selector ) {\n\n\t\tvar parent = target.parentNode;\n\n\t\twhile( parent ) {\n\n\t\t\t// There's some overhead doing this each time, we don't\n\t\t\t// want to rewrite the element prototype but should still\n\t\t\t// be enough to feature detect once at startup...\n\t\t\tvar matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector;\n\n\t\t\t// If we find a match, we're all set\n\t\t\tif( matchesMethod && matchesMethod.call( parent, selector ) ) {\n\t\t\t\treturn parent;\n\t\t\t}\n\n\t\t\t// Keep searching\n\t\t\tparent = parent.parentNode;\n\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * Converts various color input formats to an {r:0,g:0,b:0} object.\n\t *\n\t * @param {string} color The string representation of a color\n\t * @example\n\t * colorToRgb('#000');\n\t * @example\n\t * colorToRgb('#000000');\n\t * @example\n\t * colorToRgb('rgb(0,0,0)');\n\t * @example\n\t * colorToRgb('rgba(0,0,0)');\n\t *\n\t * @return {{r: number, g: number, b: number, [a]: number}|null}\n\t */\n\tfunction colorToRgb( color ) {\n\n\t\tvar hex3 = color.match( /^#([0-9a-f]{3})$/i );\n\t\tif( hex3 && hex3[1] ) {\n\t\t\thex3 = hex3[1];\n\t\t\treturn {\n\t\t\t\tr: parseInt( hex3.charAt( 0 ), 16 ) * 0x11,\n\t\t\t\tg: parseInt( hex3.charAt( 1 ), 16 ) * 0x11,\n\t\t\t\tb: parseInt( hex3.charAt( 2 ), 16 ) * 0x11\n\t\t\t};\n\t\t}\n\n\t\tvar hex6 = color.match( /^#([0-9a-f]{6})$/i );\n\t\tif( hex6 && hex6[1] ) {\n\t\t\thex6 = hex6[1];\n\t\t\treturn {\n\t\t\t\tr: parseInt( hex6.substr( 0, 2 ), 16 ),\n\t\t\t\tg: parseInt( hex6.substr( 2, 2 ), 16 ),\n\t\t\t\tb: parseInt( hex6.substr( 4, 2 ), 16 )\n\t\t\t};\n\t\t}\n\n\t\tvar rgb = color.match( /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i );\n\t\tif( rgb ) {\n\t\t\treturn {\n\t\t\t\tr: parseInt( rgb[1], 10 ),\n\t\t\t\tg: parseInt( rgb[2], 10 ),\n\t\t\t\tb: parseInt( rgb[3], 10 )\n\t\t\t};\n\t\t}\n\n\t\tvar rgba = color.match( /^rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\,\\s*([\\d]+|[\\d]*.[\\d]+)\\s*\\)$/i );\n\t\tif( rgba ) {\n\t\t\treturn {\n\t\t\t\tr: parseInt( rgba[1], 10 ),\n\t\t\t\tg: parseInt( rgba[2], 10 ),\n\t\t\t\tb: parseInt( rgba[3], 10 ),\n\t\t\t\ta: parseFloat( rgba[4] )\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * Calculates brightness on a scale of 0-255.\n\t *\n\t * @param {string} color See colorToRgb for supported formats.\n\t * @see {@link colorToRgb}\n\t */\n\tfunction colorBrightness( color ) {\n\n\t\tif( typeof color === 'string' ) color = colorToRgb( color );\n\n\t\tif( color ) {\n\t\t\treturn ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000;\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * Returns the remaining height within the parent of the\n\t * target element.\n\t *\n\t * remaining height = [ configured parent height ] - [ current parent height ]\n\t *\n\t * @param {HTMLElement} element\n\t * @param {number} [height]\n\t */\n\tfunction getRemainingHeight( element, height ) {\n\n\t\theight = height || 0;\n\n\t\tif( element ) {\n\t\t\tvar newHeight, oldHeight = element.style.height;\n\n\t\t\t// Change the .stretch element height to 0 in order find the height of all\n\t\t\t// the other elements\n\t\t\telement.style.height = '0px';\n\t\t\tnewHeight = height - element.parentNode.offsetHeight;\n\n\t\t\t// Restore the old height, just in case\n\t\t\telement.style.height = oldHeight + 'px';\n\n\t\t\treturn newHeight;\n\t\t}\n\n\t\treturn height;\n\n\t}\n\n\t/**\n\t * Checks if this instance is being used to print a PDF.\n\t */\n\tfunction isPrintingPDF() {\n\n\t\treturn ( /print-pdf/gi ).test( window.location.search );\n\n\t}\n\n\t/**\n\t * Hides the address bar if we're on a mobile device.\n\t */\n\tfunction hideAddressBar() {\n\n\t\tif( config.hideAddressBar && isMobileDevice ) {\n\t\t\t// Events that should trigger the address bar to hide\n\t\t\twindow.addEventListener( 'load', removeAddressBar, false );\n\t\t\twindow.addEventListener( 'orientationchange', removeAddressBar, false );\n\t\t}\n\n\t}\n\n\t/**\n\t * Causes the address bar to hide on mobile devices,\n\t * more vertical space ftw.\n\t */\n\tfunction removeAddressBar() {\n\n\t\tsetTimeout( function() {\n\t\t\twindow.scrollTo( 0, 1 );\n\t\t}, 10 );\n\n\t}\n\n\t/**\n\t * Dispatches an event of the specified type from the\n\t * reveal DOM element.\n\t */\n\tfunction dispatchEvent( type, args ) {\n\n\t\tvar event = document.createEvent( 'HTMLEvents', 1, 2 );\n\t\tevent.initEvent( type, true, true );\n\t\textend( event, args );\n\t\tdom.wrapper.dispatchEvent( event );\n\n\t\t// If we're in an iframe, post each reveal.js event to the\n\t\t// parent window. Used by the notes plugin\n\t\tif( config.postMessageEvents && window.parent !== window.self ) {\n\t\t\twindow.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Wrap all links in 3D goodness.\n\t */\n\tfunction enableRollingLinks() {\n\n\t\tif( features.transforms3d && !( 'msPerspective' in document.body.style ) ) {\n\t\t\tvar anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a' );\n\n\t\t\tfor( var i = 0, len = anchors.length; i < len; i++ ) {\n\t\t\t\tvar anchor = anchors[i];\n\n\t\t\t\tif( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) {\n\t\t\t\t\tvar span = document.createElement('span');\n\t\t\t\t\tspan.setAttribute('data-title', anchor.text);\n\t\t\t\t\tspan.innerHTML = anchor.innerHTML;\n\n\t\t\t\t\tanchor.classList.add( 'roll' );\n\t\t\t\t\tanchor.innerHTML = '';\n\t\t\t\t\tanchor.appendChild(span);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Unwrap all 3D links.\n\t */\n\tfunction disableRollingLinks() {\n\n\t\tvar anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a.roll' );\n\n\t\tfor( var i = 0, len = anchors.length; i < len; i++ ) {\n\t\t\tvar anchor = anchors[i];\n\t\t\tvar span = anchor.querySelector( 'span' );\n\n\t\t\tif( span ) {\n\t\t\t\tanchor.classList.remove( 'roll' );\n\t\t\t\tanchor.innerHTML = span.innerHTML;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Bind preview frame links.\n\t *\n\t * @param {string} [selector=a] - selector for anchors\n\t */\n\tfunction enablePreviewLinks( selector ) {\n\n\t\tvar anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );\n\n\t\tanchors.forEach( function( element ) {\n\t\t\tif( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {\n\t\t\t\telement.addEventListener( 'click', onPreviewLinkClicked, false );\n\t\t\t}\n\t\t} );\n\n\t}\n\n\t/**\n\t * Unbind preview frame links.\n\t */\n\tfunction disablePreviewLinks( selector ) {\n\n\t\tvar anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );\n\n\t\tanchors.forEach( function( element ) {\n\t\t\tif( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {\n\t\t\t\telement.removeEventListener( 'click', onPreviewLinkClicked, false );\n\t\t\t}\n\t\t} );\n\n\t}\n\n\t/**\n\t * Opens a preview window for the target URL.\n\t *\n\t * @param {string} url - url for preview iframe src\n\t */\n\tfunction showPreview( url ) {\n\n\t\tcloseOverlay();\n\n\t\tdom.overlay = document.createElement( 'div' );\n\t\tdom.overlay.classList.add( 'overlay' );\n\t\tdom.overlay.classList.add( 'overlay-preview' );\n\t\tdom.wrapper.appendChild( dom.overlay );\n\n\t\tdom.overlay.innerHTML = [\n\t\t\t'<header>',\n\t\t\t\t'<a class=\"close\" href=\"#\"><span class=\"icon\"></span></a>',\n\t\t\t\t'<a class=\"external\" href=\"'+ url +'\" target=\"_blank\"><span class=\"icon\"></span></a>',\n\t\t\t'</header>',\n\t\t\t'<div class=\"spinner\"></div>',\n\t\t\t'<div class=\"viewport\">',\n\t\t\t\t'<iframe src=\"'+ url +'\"></iframe>',\n\t\t\t\t'<small class=\"viewport-inner\">',\n\t\t\t\t\t'<span class=\"x-frame-error\">Unable to load iframe. This is likely due to the site\\'s policy (x-frame-options).</span>',\n\t\t\t\t'</small>',\n\t\t\t'</div>'\n\t\t].join('');\n\n\t\tdom.overlay.querySelector( 'iframe' ).addEventListener( 'load', function( event ) {\n\t\t\tdom.overlay.classList.add( 'loaded' );\n\t\t}, false );\n\n\t\tdom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) {\n\t\t\tcloseOverlay();\n\t\t\tevent.preventDefault();\n\t\t}, false );\n\n\t\tdom.overlay.querySelector( '.external' ).addEventListener( 'click', function( event ) {\n\t\t\tcloseOverlay();\n\t\t}, false );\n\n\t\tsetTimeout( function() {\n\t\t\tdom.overlay.classList.add( 'visible' );\n\t\t}, 1 );\n\n\t}\n\n\t/**\n\t * Open or close help overlay window.\n\t *\n\t * @param {Boolean} [override] Flag which overrides the\n\t * toggle logic and forcibly sets the desired state. True means\n\t * help is open, false means it's closed.\n\t */\n\tfunction toggleHelp( override ){\n\n\t\tif( typeof override === 'boolean' ) {\n\t\t\toverride ? showHelp() : closeOverlay();\n\t\t}\n\t\telse {\n\t\t\tif( dom.overlay ) {\n\t\t\t\tcloseOverlay();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tshowHelp();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Opens an overlay window with help material.\n\t */\n\tfunction showHelp() {\n\n\t\tif( config.help ) {\n\n\t\t\tcloseOverlay();\n\n\t\t\tdom.overlay = document.createElement( 'div' );\n\t\t\tdom.overlay.classList.add( 'overlay' );\n\t\t\tdom.overlay.classList.add( 'overlay-help' );\n\t\t\tdom.wrapper.appendChild( dom.overlay );\n\n\t\t\tvar html = '<p class=\"title\">Keyboard Shortcuts</p><br/>';\n\n\t\t\thtml += '<table><th>KEY</th><th>ACTION</th>';\n\t\t\tfor( var key in keyboardShortcuts ) {\n\t\t\t\thtml += '<tr><td>' + key + '</td><td>' + keyboardShortcuts[ key ] + '</td></tr>';\n\t\t\t}\n\n\t\t\thtml += '</table>';\n\n\t\t\tdom.overlay.innerHTML = [\n\t\t\t\t'<header>',\n\t\t\t\t\t'<a class=\"close\" href=\"#\"><span class=\"icon\"></span></a>',\n\t\t\t\t'</header>',\n\t\t\t\t'<div class=\"viewport\">',\n\t\t\t\t\t'<div class=\"viewport-inner\">'+ html +'</div>',\n\t\t\t\t'</div>'\n\t\t\t].join('');\n\n\t\t\tdom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) {\n\t\t\t\tcloseOverlay();\n\t\t\t\tevent.preventDefault();\n\t\t\t}, false );\n\n\t\t\tsetTimeout( function() {\n\t\t\t\tdom.overlay.classList.add( 'visible' );\n\t\t\t}, 1 );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Closes any currently open overlay.\n\t */\n\tfunction closeOverlay() {\n\n\t\tif( dom.overlay ) {\n\t\t\tdom.overlay.parentNode.removeChild( dom.overlay );\n\t\t\tdom.overlay = null;\n\t\t}\n\n\t}\n\n\t/**\n\t * Applies JavaScript-controlled layout rules to the\n\t * presentation.\n\t */\n\tfunction layout() {\n\n\t\tif( dom.wrapper && !isPrintingPDF() ) {\n\n\t\t\tvar size = getComputedSlideSize();\n\n\t\t\t// Layout the contents of the slides\n\t\t\tlayoutSlideContents( config.width, config.height );\n\n\t\t\tdom.slides.style.width = size.width + 'px';\n\t\t\tdom.slides.style.height = size.height + 'px';\n\n\t\t\t// Determine scale of content to fit within available space\n\t\t\tscale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height );\n\n\t\t\t// Respect max/min scale settings\n\t\t\tscale = Math.max( scale, config.minScale );\n\t\t\tscale = Math.min( scale, config.maxScale );\n\n\t\t\t// Don't apply any scaling styles if scale is 1\n\t\t\tif( scale === 1 ) {\n\t\t\t\tdom.slides.style.zoom = '';\n\t\t\t\tdom.slides.style.left = '';\n\t\t\t\tdom.slides.style.top = '';\n\t\t\t\tdom.slides.style.bottom = '';\n\t\t\t\tdom.slides.style.right = '';\n\t\t\t\ttransformSlides( { layout: '' } );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Prefer zoom for scaling up so that content remains crisp.\n\t\t\t\t// Don't use zoom to scale down since that can lead to shifts\n\t\t\t\t// in text layout/line breaks.\n\t\t\t\tif( scale > 1 && features.zoom ) {\n\t\t\t\t\tdom.slides.style.zoom = scale;\n\t\t\t\t\tdom.slides.style.left = '';\n\t\t\t\t\tdom.slides.style.top = '';\n\t\t\t\t\tdom.slides.style.bottom = '';\n\t\t\t\t\tdom.slides.style.right = '';\n\t\t\t\t\ttransformSlides( { layout: '' } );\n\t\t\t\t}\n\t\t\t\t// Apply scale transform as a fallback\n\t\t\t\telse {\n\t\t\t\t\tdom.slides.style.zoom = '';\n\t\t\t\t\tdom.slides.style.left = '50%';\n\t\t\t\t\tdom.slides.style.top = '50%';\n\t\t\t\t\tdom.slides.style.bottom = 'auto';\n\t\t\t\t\tdom.slides.style.right = 'auto';\n\t\t\t\t\ttransformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Select all slides, vertical and horizontal\n\t\t\tvar slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) );\n\n\t\t\tfor( var i = 0, len = slides.length; i < len; i++ ) {\n\t\t\t\tvar slide = slides[ i ];\n\n\t\t\t\t// Don't bother updating invisible slides\n\t\t\t\tif( slide.style.display === 'none' ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif( config.center || slide.classList.contains( 'center' ) ) {\n\t\t\t\t\t// Vertical stacks are not centred since their section\n\t\t\t\t\t// children will be\n\t\t\t\t\tif( slide.classList.contains( 'stack' ) ) {\n\t\t\t\t\t\tslide.style.top = 0;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tslide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tslide.style.top = '';\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tupdateProgress();\n\t\t\tupdateParallax();\n\n\t\t\tif( isOverview() ) {\n\t\t\t\tupdateOverview();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Applies layout logic to the contents of all slides in\n\t * the presentation.\n\t *\n\t * @param {string|number} width\n\t * @param {string|number} height\n\t */\n\tfunction layoutSlideContents( width, height ) {\n\n\t\t// Handle sizing of elements with the 'stretch' class\n\t\ttoArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) {\n\n\t\t\t// Determine how much vertical space we can use\n\t\t\tvar remainingHeight = getRemainingHeight( element, height );\n\n\t\t\t// Consider the aspect ratio of media elements\n\t\t\tif( /(img|video)/gi.test( element.nodeName ) ) {\n\t\t\t\tvar nw = element.naturalWidth || element.videoWidth,\n\t\t\t\t\tnh = element.naturalHeight || element.videoHeight;\n\n\t\t\t\tvar es = Math.min( width / nw, remainingHeight / nh );\n\n\t\t\t\telement.style.width = ( nw * es ) + 'px';\n\t\t\t\telement.style.height = ( nh * es ) + 'px';\n\n\t\t\t}\n\t\t\telse {\n\t\t\t\telement.style.width = width + 'px';\n\t\t\t\telement.style.height = remainingHeight + 'px';\n\t\t\t}\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Calculates the computed pixel size of our slides. These\n\t * values are based on the width and height configuration\n\t * options.\n\t *\n\t * @param {number} [presentationWidth=dom.wrapper.offsetWidth]\n\t * @param {number} [presentationHeight=dom.wrapper.offsetHeight]\n\t */\n\tfunction getComputedSlideSize( presentationWidth, presentationHeight ) {\n\n\t\tvar size = {\n\t\t\t// Slide size\n\t\t\twidth: config.width,\n\t\t\theight: config.height,\n\n\t\t\t// Presentation size\n\t\t\tpresentationWidth: presentationWidth || dom.wrapper.offsetWidth,\n\t\t\tpresentationHeight: presentationHeight || dom.wrapper.offsetHeight\n\t\t};\n\n\t\t// Reduce available space by margin\n\t\tsize.presentationWidth -= ( size.presentationWidth * config.margin );\n\t\tsize.presentationHeight -= ( size.presentationHeight * config.margin );\n\n\t\t// Slide width may be a percentage of available width\n\t\tif( typeof size.width === 'string' && /%$/.test( size.width ) ) {\n\t\t\tsize.width = parseInt( size.width, 10 ) / 100 * size.presentationWidth;\n\t\t}\n\n\t\t// Slide height may be a percentage of available height\n\t\tif( typeof size.height === 'string' && /%$/.test( size.height ) ) {\n\t\t\tsize.height = parseInt( size.height, 10 ) / 100 * size.presentationHeight;\n\t\t}\n\n\t\treturn size;\n\n\t}\n\n\t/**\n\t * Stores the vertical index of a stack so that the same\n\t * vertical slide can be selected when navigating to and\n\t * from the stack.\n\t *\n\t * @param {HTMLElement} stack The vertical stack element\n\t * @param {string|number} [v=0] Index to memorize\n\t */\n\tfunction setPreviousVerticalIndex( stack, v ) {\n\n\t\tif( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) {\n\t\t\tstack.setAttribute( 'data-previous-indexv', v || 0 );\n\t\t}\n\n\t}\n\n\t/**\n\t * Retrieves the vertical index which was stored using\n\t * #setPreviousVerticalIndex() or 0 if no previous index\n\t * exists.\n\t *\n\t * @param {HTMLElement} stack The vertical stack element\n\t */\n\tfunction getPreviousVerticalIndex( stack ) {\n\n\t\tif( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {\n\t\t\t// Prefer manually defined start-indexv\n\t\t\tvar attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv';\n\n\t\t\treturn parseInt( stack.getAttribute( attributeName ) || 0, 10 );\n\t\t}\n\n\t\treturn 0;\n\n\t}\n\n\t/**\n\t * Displays the overview of slides (quick nav) by scaling\n\t * down and arranging all slide elements.\n\t */\n\tfunction activateOverview() {\n\n\t\t// Only proceed if enabled in config\n\t\tif( config.overview && !isOverview() ) {\n\n\t\t\toverview = true;\n\n\t\t\tdom.wrapper.classList.add( 'overview' );\n\t\t\tdom.wrapper.classList.remove( 'overview-deactivating' );\n\n\t\t\tif( features.overviewTransitions ) {\n\t\t\t\tsetTimeout( function() {\n\t\t\t\t\tdom.wrapper.classList.add( 'overview-animated' );\n\t\t\t\t}, 1 );\n\t\t\t}\n\n\t\t\t// Don't auto-slide while in overview mode\n\t\t\tcancelAutoSlide();\n\n\t\t\t// Move the backgrounds element into the slide container to\n\t\t\t// that the same scaling is applied\n\t\t\tdom.slides.appendChild( dom.background );\n\n\t\t\t// Clicking on an overview slide navigates to it\n\t\t\ttoArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {\n\t\t\t\tif( !slide.classList.contains( 'stack' ) ) {\n\t\t\t\t\tslide.addEventListener( 'click', onOverviewSlideClicked, true );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Calculate slide sizes\n\t\t\tvar margin = 70;\n\t\t\tvar slideSize = getComputedSlideSize();\n\t\t\toverviewSlideWidth = slideSize.width + margin;\n\t\t\toverviewSlideHeight = slideSize.height + margin;\n\n\t\t\t// Reverse in RTL mode\n\t\t\tif( config.rtl ) {\n\t\t\t\toverviewSlideWidth = -overviewSlideWidth;\n\t\t\t}\n\n\t\t\tupdateSlidesVisibility();\n\t\t\tlayoutOverview();\n\t\t\tupdateOverview();\n\n\t\t\tlayout();\n\n\t\t\t// Notify observers of the overview showing\n\t\t\tdispatchEvent( 'overviewshown', {\n\t\t\t\t'indexh': indexh,\n\t\t\t\t'indexv': indexv,\n\t\t\t\t'currentSlide': currentSlide\n\t\t\t} );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Uses CSS transforms to position all slides in a grid for\n\t * display inside of the overview mode.\n\t */\n\tfunction layoutOverview() {\n\n\t\t// Layout slides\n\t\ttoArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {\n\t\t\thslide.setAttribute( 'data-index-h', h );\n\t\t\ttransformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' );\n\n\t\t\tif( hslide.classList.contains( 'stack' ) ) {\n\n\t\t\t\ttoArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {\n\t\t\t\t\tvslide.setAttribute( 'data-index-h', h );\n\t\t\t\t\tvslide.setAttribute( 'data-index-v', v );\n\n\t\t\t\t\ttransformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' );\n\t\t\t\t} );\n\n\t\t\t}\n\t\t} );\n\n\t\t// Layout slide backgrounds\n\t\ttoArray( dom.background.childNodes ).forEach( function( hbackground, h ) {\n\t\t\ttransformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' );\n\n\t\t\ttoArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) {\n\t\t\t\ttransformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' );\n\t\t\t} );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Moves the overview viewport to the current slides.\n\t * Called each time the current slide changes.\n\t */\n\tfunction updateOverview() {\n\n\t\tvar vmin = Math.min( window.innerWidth, window.innerHeight );\n\t\tvar scale = Math.max( vmin / 5, 150 ) / vmin;\n\n\t\ttransformSlides( {\n\t\t\toverview: [\n\t\t\t\t'scale('+ scale +')',\n\t\t\t\t'translateX('+ ( -indexh * overviewSlideWidth ) +'px)',\n\t\t\t\t'translateY('+ ( -indexv * overviewSlideHeight ) +'px)'\n\t\t\t].join( ' ' )\n\t\t} );\n\n\t}\n\n\t/**\n\t * Exits the slide overview and enters the currently\n\t * active slide.\n\t */\n\tfunction deactivateOverview() {\n\n\t\t// Only proceed if enabled in config\n\t\tif( config.overview ) {\n\n\t\t\toverview = false;\n\n\t\t\tdom.wrapper.classList.remove( 'overview' );\n\t\t\tdom.wrapper.classList.remove( 'overview-animated' );\n\n\t\t\t// Temporarily add a class so that transitions can do different things\n\t\t\t// depending on whether they are exiting/entering overview, or just\n\t\t\t// moving from slide to slide\n\t\t\tdom.wrapper.classList.add( 'overview-deactivating' );\n\n\t\t\tsetTimeout( function () {\n\t\t\t\tdom.wrapper.classList.remove( 'overview-deactivating' );\n\t\t\t}, 1 );\n\n\t\t\t// Move the background element back out\n\t\t\tdom.wrapper.appendChild( dom.background );\n\n\t\t\t// Clean up changes made to slides\n\t\t\ttoArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {\n\t\t\t\ttransformElement( slide, '' );\n\n\t\t\t\tslide.removeEventListener( 'click', onOverviewSlideClicked, true );\n\t\t\t} );\n\n\t\t\t// Clean up changes made to backgrounds\n\t\t\ttoArray( dom.background.querySelectorAll( '.slide-background' ) ).forEach( function( background ) {\n\t\t\t\ttransformElement( background, '' );\n\t\t\t} );\n\n\t\t\ttransformSlides( { overview: '' } );\n\n\t\t\tslide( indexh, indexv );\n\n\t\t\tlayout();\n\n\t\t\tcueAutoSlide();\n\n\t\t\t// Notify observers of the overview hiding\n\t\t\tdispatchEvent( 'overviewhidden', {\n\t\t\t\t'indexh': indexh,\n\t\t\t\t'indexv': indexv,\n\t\t\t\t'currentSlide': currentSlide\n\t\t\t} );\n\n\t\t}\n\t}\n\n\t/**\n\t * Toggles the slide overview mode on and off.\n\t *\n\t * @param {Boolean} [override] Flag which overrides the\n\t * toggle logic and forcibly sets the desired state. True means\n\t * overview is open, false means it's closed.\n\t */\n\tfunction toggleOverview( override ) {\n\n\t\tif( typeof override === 'boolean' ) {\n\t\t\toverride ? activateOverview() : deactivateOverview();\n\t\t}\n\t\telse {\n\t\t\tisOverview() ? deactivateOverview() : activateOverview();\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if the overview is currently active.\n\t *\n\t * @return {Boolean} true if the overview is active,\n\t * false otherwise\n\t */\n\tfunction isOverview() {\n\n\t\treturn overview;\n\n\t}\n\n\t/**\n\t * Checks if the current or specified slide is vertical\n\t * (nested within another slide).\n\t *\n\t * @param {HTMLElement} [slide=currentSlide] The slide to check\n\t * orientation of\n\t * @return {Boolean}\n\t */\n\tfunction isVerticalSlide( slide ) {\n\n\t\t// Prefer slide argument, otherwise use current slide\n\t\tslide = slide ? slide : currentSlide;\n\n\t\treturn slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i );\n\n\t}\n\n\t/**\n\t * Handling the fullscreen functionality via the fullscreen API\n\t *\n\t * @see http://fullscreen.spec.whatwg.org/\n\t * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode\n\t */\n\tfunction enterFullscreen() {\n\n\t\tvar element = document.documentElement;\n\n\t\t// Check which implementation is available\n\t\tvar requestMethod = element.requestFullscreen ||\n\t\t\t\t\t\t\telement.webkitRequestFullscreen ||\n\t\t\t\t\t\t\telement.webkitRequestFullScreen ||\n\t\t\t\t\t\t\telement.mozRequestFullScreen ||\n\t\t\t\t\t\t\telement.msRequestFullscreen;\n\n\t\tif( requestMethod ) {\n\t\t\trequestMethod.apply( element );\n\t\t}\n\n\t}\n\n\t/**\n\t * Enters the paused mode which fades everything on screen to\n\t * black.\n\t */\n\tfunction pause() {\n\n\t\tif( config.pause ) {\n\t\t\tvar wasPaused = dom.wrapper.classList.contains( 'paused' );\n\n\t\t\tcancelAutoSlide();\n\t\t\tdom.wrapper.classList.add( 'paused' );\n\n\t\t\tif( wasPaused === false ) {\n\t\t\t\tdispatchEvent( 'paused' );\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Exits from the paused mode.\n\t */\n\tfunction resume() {\n\n\t\tvar wasPaused = dom.wrapper.classList.contains( 'paused' );\n\t\tdom.wrapper.classList.remove( 'paused' );\n\n\t\tcueAutoSlide();\n\n\t\tif( wasPaused ) {\n\t\t\tdispatchEvent( 'resumed' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Toggles the paused mode on and off.\n\t */\n\tfunction togglePause( override ) {\n\n\t\tif( typeof override === 'boolean' ) {\n\t\t\toverride ? pause() : resume();\n\t\t}\n\t\telse {\n\t\t\tisPaused() ? resume() : pause();\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if we are currently in the paused mode.\n\t *\n\t * @return {Boolean}\n\t */\n\tfunction isPaused() {\n\n\t\treturn dom.wrapper.classList.contains( 'paused' );\n\n\t}\n\n\t/**\n\t * Toggles the auto slide mode on and off.\n\t *\n\t * @param {Boolean} [override] Flag which sets the desired state.\n\t * True means autoplay starts, false means it stops.\n\t */\n\n\tfunction toggleAutoSlide( override ) {\n\n\t\tif( typeof override === 'boolean' ) {\n\t\t\toverride ? resumeAutoSlide() : pauseAutoSlide();\n\t\t}\n\n\t\telse {\n\t\t\tautoSlidePaused ? resumeAutoSlide() : pauseAutoSlide();\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if the auto slide mode is currently on.\n\t *\n\t * @return {Boolean}\n\t */\n\tfunction isAutoSliding() {\n\n\t\treturn !!( autoSlide && !autoSlidePaused );\n\n\t}\n\n\t/**\n\t * Steps from the current point in the presentation to the\n\t * slide which matches the specified horizontal and vertical\n\t * indices.\n\t *\n\t * @param {number} [h=indexh] Horizontal index of the target slide\n\t * @param {number} [v=indexv] Vertical index of the target slide\n\t * @param {number} [f] Index of a fragment within the\n\t * target slide to activate\n\t * @param {number} [o] Origin for use in multimaster environments\n\t */\n\tfunction slide( h, v, f, o ) {\n\n\t\t// Remember where we were at before\n\t\tpreviousSlide = currentSlide;\n\n\t\t// Query all horizontal slides in the deck\n\t\tvar horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );\n\n\t\t// Abort if there are no slides\n\t\tif( horizontalSlides.length === 0 ) return;\n\n\t\t// If no vertical index is specified and the upcoming slide is a\n\t\t// stack, resume at its previous vertical index\n\t\tif( v === undefined && !isOverview() ) {\n\t\t\tv = getPreviousVerticalIndex( horizontalSlides[ h ] );\n\t\t}\n\n\t\t// If we were on a vertical stack, remember what vertical index\n\t\t// it was on so we can resume at the same position when returning\n\t\tif( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) {\n\t\t\tsetPreviousVerticalIndex( previousSlide.parentNode, indexv );\n\t\t}\n\n\t\t// Remember the state before this slide\n\t\tvar stateBefore = state.concat();\n\n\t\t// Reset the state array\n\t\tstate.length = 0;\n\n\t\tvar indexhBefore = indexh || 0,\n\t\t\tindexvBefore = indexv || 0;\n\n\t\t// Activate and transition to the new slide\n\t\tindexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );\n\t\tindexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );\n\n\t\t// Update the visibility of slides now that the indices have changed\n\t\tupdateSlidesVisibility();\n\n\t\tlayout();\n\n\t\t// Apply the new state\n\t\tstateLoop: for( var i = 0, len = state.length; i < len; i++ ) {\n\t\t\t// Check if this state existed on the previous slide. If it\n\t\t\t// did, we will avoid adding it repeatedly\n\t\t\tfor( var j = 0; j < stateBefore.length; j++ ) {\n\t\t\t\tif( stateBefore[j] === state[i] ) {\n\t\t\t\t\tstateBefore.splice( j, 1 );\n\t\t\t\t\tcontinue stateLoop;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdocument.documentElement.classList.add( state[i] );\n\n\t\t\t// Dispatch custom event matching the state's name\n\t\t\tdispatchEvent( state[i] );\n\t\t}\n\n\t\t// Clean up the remains of the previous state\n\t\twhile( stateBefore.length ) {\n\t\t\tdocument.documentElement.classList.remove( stateBefore.pop() );\n\t\t}\n\n\t\t// Update the overview if it's currently active\n\t\tif( isOverview() ) {\n\t\t\tupdateOverview();\n\t\t}\n\n\t\t// Find the current horizontal slide and any possible vertical slides\n\t\t// within it\n\t\tvar currentHorizontalSlide = horizontalSlides[ indexh ],\n\t\t\tcurrentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );\n\n\t\t// Store references to the previous and current slides\n\t\tcurrentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide;\n\n\t\t// Show fragment, if specified\n\t\tif( typeof f !== 'undefined' ) {\n\t\t\tnavigateFragment( f );\n\t\t}\n\n\t\t// Dispatch an event if the slide changed\n\t\tvar slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore );\n\t\tif( slideChanged ) {\n\t\t\tdispatchEvent( 'slidechanged', {\n\t\t\t\t'indexh': indexh,\n\t\t\t\t'indexv': indexv,\n\t\t\t\t'previousSlide': previousSlide,\n\t\t\t\t'currentSlide': currentSlide,\n\t\t\t\t'origin': o\n\t\t\t} );\n\t\t}\n\t\telse {\n\t\t\t// Ensure that the previous slide is never the same as the current\n\t\t\tpreviousSlide = null;\n\t\t}\n\n\t\t// Solves an edge case where the previous slide maintains the\n\t\t// 'present' class when navigating between adjacent vertical\n\t\t// stacks\n\t\tif( previousSlide ) {\n\t\t\tpreviousSlide.classList.remove( 'present' );\n\t\t\tpreviousSlide.setAttribute( 'aria-hidden', 'true' );\n\n\t\t\t// Reset all slides upon navigate to home\n\t\t\t// Issue: #285\n\t\t\tif ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) {\n\t\t\t\t// Launch async task\n\t\t\t\tsetTimeout( function () {\n\t\t\t\t\tvar slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i;\n\t\t\t\t\tfor( i in slides ) {\n\t\t\t\t\t\tif( slides[i] ) {\n\t\t\t\t\t\t\t// Reset stack\n\t\t\t\t\t\t\tsetPreviousVerticalIndex( slides[i], 0 );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 0 );\n\t\t\t}\n\t\t}\n\n\t\t// Handle embedded content\n\t\tif( slideChanged || !previousSlide ) {\n\t\t\tstopEmbeddedContent( previousSlide );\n\t\t\tstartEmbeddedContent( currentSlide );\n\t\t}\n\n\t\t// Announce the current slide contents, for screen readers\n\t\tdom.statusDiv.textContent = getStatusText( currentSlide );\n\n\t\tupdateControls();\n\t\tupdateProgress();\n\t\tupdateBackground();\n\t\tupdateParallax();\n\t\tupdateSlideNumber();\n\t\tupdateNotes();\n\n\t\t// Update the URL hash\n\t\twriteURL();\n\n\t\tcueAutoSlide();\n\n\t}\n\n\t/**\n\t * Syncs the presentation with the current DOM. Useful\n\t * when new slides or control elements are added or when\n\t * the configuration has changed.\n\t */\n\tfunction sync() {\n\n\t\t// Subscribe to input\n\t\tremoveEventListeners();\n\t\taddEventListeners();\n\n\t\t// Force a layout to make sure the current config is accounted for\n\t\tlayout();\n\n\t\t// Reflect the current autoSlide value\n\t\tautoSlide = config.autoSlide;\n\n\t\t// Start auto-sliding if it's enabled\n\t\tcueAutoSlide();\n\n\t\t// Re-create the slide backgrounds\n\t\tcreateBackgrounds();\n\n\t\t// Write the current hash to the URL\n\t\twriteURL();\n\n\t\tsortAllFragments();\n\n\t\tupdateControls();\n\t\tupdateProgress();\n\t\tupdateSlideNumber();\n\t\tupdateSlidesVisibility();\n\t\tupdateBackground( true );\n\t\tupdateNotesVisibility();\n\t\tupdateNotes();\n\n\t\tformatEmbeddedContent();\n\n\t\t// Start or stop embedded content depending on global config\n\t\tif( config.autoPlayMedia === false ) {\n\t\t\tstopEmbeddedContent( currentSlide, { unloadIframes: false } );\n\t\t}\n\t\telse {\n\t\t\tstartEmbeddedContent( currentSlide );\n\t\t}\n\n\t\tif( isOverview() ) {\n\t\t\tlayoutOverview();\n\t\t}\n\n\t}\n\n\t/**\n\t * Resets all vertical slides so that only the first\n\t * is visible.\n\t */\n\tfunction resetVerticalSlides() {\n\n\t\tvar horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );\n\t\thorizontalSlides.forEach( function( horizontalSlide ) {\n\n\t\t\tvar verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );\n\t\t\tverticalSlides.forEach( function( verticalSlide, y ) {\n\n\t\t\t\tif( y > 0 ) {\n\t\t\t\t\tverticalSlide.classList.remove( 'present' );\n\t\t\t\t\tverticalSlide.classList.remove( 'past' );\n\t\t\t\t\tverticalSlide.classList.add( 'future' );\n\t\t\t\t\tverticalSlide.setAttribute( 'aria-hidden', 'true' );\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Sorts and formats all of fragments in the\n\t * presentation.\n\t */\n\tfunction sortAllFragments() {\n\n\t\tvar horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );\n\t\thorizontalSlides.forEach( function( horizontalSlide ) {\n\n\t\t\tvar verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );\n\t\t\tverticalSlides.forEach( function( verticalSlide, y ) {\n\n\t\t\t\tsortFragments( verticalSlide.querySelectorAll( '.fragment' ) );\n\n\t\t\t} );\n\n\t\t\tif( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) );\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Randomly shuffles all slides in the deck.\n\t */\n\tfunction shuffle() {\n\n\t\tvar slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );\n\n\t\tslides.forEach( function( slide ) {\n\n\t\t\t// Insert this slide next to another random slide. This may\n\t\t\t// cause the slide to insert before itself but that's fine.\n\t\t\tdom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] );\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Updates one dimension of slides by showing the slide\n\t * with the specified index.\n\t *\n\t * @param {string} selector A CSS selector that will fetch\n\t * the group of slides we are working with\n\t * @param {number} index The index of the slide that should be\n\t * shown\n\t *\n\t * @return {number} The index of the slide that is now shown,\n\t * might differ from the passed in index if it was out of\n\t * bounds.\n\t */\n\tfunction updateSlides( selector, index ) {\n\n\t\t// Select all slides and convert the NodeList result to\n\t\t// an array\n\t\tvar slides = toArray( dom.wrapper.querySelectorAll( selector ) ),\n\t\t\tslidesLength = slides.length;\n\n\t\tvar printMode = isPrintingPDF();\n\n\t\tif( slidesLength ) {\n\n\t\t\t// Should the index loop?\n\t\t\tif( config.loop ) {\n\t\t\t\tindex %= slidesLength;\n\n\t\t\t\tif( index < 0 ) {\n\t\t\t\t\tindex = slidesLength + index;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Enforce max and minimum index bounds\n\t\t\tindex = Math.max( Math.min( index, slidesLength - 1 ), 0 );\n\n\t\t\tfor( var i = 0; i < slidesLength; i++ ) {\n\t\t\t\tvar element = slides[i];\n\n\t\t\t\tvar reverse = config.rtl && !isVerticalSlide( element );\n\n\t\t\t\telement.classList.remove( 'past' );\n\t\t\t\telement.classList.remove( 'present' );\n\t\t\t\telement.classList.remove( 'future' );\n\n\t\t\t\t// http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute\n\t\t\t\telement.setAttribute( 'hidden', '' );\n\t\t\t\telement.setAttribute( 'aria-hidden', 'true' );\n\n\t\t\t\t// If this element contains vertical slides\n\t\t\t\tif( element.querySelector( 'section' ) ) {\n\t\t\t\t\telement.classList.add( 'stack' );\n\t\t\t\t}\n\n\t\t\t\t// If we're printing static slides, all slides are \"present\"\n\t\t\t\tif( printMode ) {\n\t\t\t\t\telement.classList.add( 'present' );\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif( i < index ) {\n\t\t\t\t\t// Any element previous to index is given the 'past' class\n\t\t\t\t\telement.classList.add( reverse ? 'future' : 'past' );\n\n\t\t\t\t\tif( config.fragments ) {\n\t\t\t\t\t\tvar pastFragments = toArray( element.querySelectorAll( '.fragment' ) );\n\n\t\t\t\t\t\t// Show all fragments on prior slides\n\t\t\t\t\t\twhile( pastFragments.length ) {\n\t\t\t\t\t\t\tvar pastFragment = pastFragments.pop();\n\t\t\t\t\t\t\tpastFragment.classList.add( 'visible' );\n\t\t\t\t\t\t\tpastFragment.classList.remove( 'current-fragment' );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if( i > index ) {\n\t\t\t\t\t// Any element subsequent to index is given the 'future' class\n\t\t\t\t\telement.classList.add( reverse ? 'past' : 'future' );\n\n\t\t\t\t\tif( config.fragments ) {\n\t\t\t\t\t\tvar futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) );\n\n\t\t\t\t\t\t// No fragments in future slides should be visible ahead of time\n\t\t\t\t\t\twhile( futureFragments.length ) {\n\t\t\t\t\t\t\tvar futureFragment = futureFragments.pop();\n\t\t\t\t\t\t\tfutureFragment.classList.remove( 'visible' );\n\t\t\t\t\t\t\tfutureFragment.classList.remove( 'current-fragment' );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Mark the current slide as present\n\t\t\tslides[index].classList.add( 'present' );\n\t\t\tslides[index].removeAttribute( 'hidden' );\n\t\t\tslides[index].removeAttribute( 'aria-hidden' );\n\n\t\t\t// If this slide has a state associated with it, add it\n\t\t\t// onto the current state of the deck\n\t\t\tvar slideState = slides[index].getAttribute( 'data-state' );\n\t\t\tif( slideState ) {\n\t\t\t\tstate = state.concat( slideState.split( ' ' ) );\n\t\t\t}\n\n\t\t}\n\t\telse {\n\t\t\t// Since there are no slides we can't be anywhere beyond the\n\t\t\t// zeroth index\n\t\t\tindex = 0;\n\t\t}\n\n\t\treturn index;\n\n\t}\n\n\t/**\n\t * Optimization method; hide all slides that are far away\n\t * from the present slide.\n\t */\n\tfunction updateSlidesVisibility() {\n\n\t\t// Select all slides and convert the NodeList result to\n\t\t// an array\n\t\tvar horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ),\n\t\t\thorizontalSlidesLength = horizontalSlides.length,\n\t\t\tdistanceX,\n\t\t\tdistanceY;\n\n\t\tif( horizontalSlidesLength && typeof indexh !== 'undefined' ) {\n\n\t\t\t// The number of steps away from the present slide that will\n\t\t\t// be visible\n\t\t\tvar viewDistance = isOverview() ? 10 : config.viewDistance;\n\n\t\t\t// Limit view distance on weaker devices\n\t\t\tif( isMobileDevice ) {\n\t\t\t\tviewDistance = isOverview() ? 6 : 2;\n\t\t\t}\n\n\t\t\t// All slides need to be visible when exporting to PDF\n\t\t\tif( isPrintingPDF() ) {\n\t\t\t\tviewDistance = Number.MAX_VALUE;\n\t\t\t}\n\n\t\t\tfor( var x = 0; x < horizontalSlidesLength; x++ ) {\n\t\t\t\tvar horizontalSlide = horizontalSlides[x];\n\n\t\t\t\tvar verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),\n\t\t\t\t\tverticalSlidesLength = verticalSlides.length;\n\n\t\t\t\t// Determine how far away this slide is from the present\n\t\t\t\tdistanceX = Math.abs( ( indexh || 0 ) - x ) || 0;\n\n\t\t\t\t// If the presentation is looped, distance should measure\n\t\t\t\t// 1 between the first and last slides\n\t\t\t\tif( config.loop ) {\n\t\t\t\t\tdistanceX = Math.abs( ( ( indexh || 0 ) - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;\n\t\t\t\t}\n\n\t\t\t\t// Show the horizontal slide if it's within the view distance\n\t\t\t\tif( distanceX < viewDistance ) {\n\t\t\t\t\tloadSlide( horizontalSlide );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tunloadSlide( horizontalSlide );\n\t\t\t\t}\n\n\t\t\t\tif( verticalSlidesLength ) {\n\n\t\t\t\t\tvar oy = getPreviousVerticalIndex( horizontalSlide );\n\n\t\t\t\t\tfor( var y = 0; y < verticalSlidesLength; y++ ) {\n\t\t\t\t\t\tvar verticalSlide = verticalSlides[y];\n\n\t\t\t\t\t\tdistanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy );\n\n\t\t\t\t\t\tif( distanceX + distanceY < viewDistance ) {\n\t\t\t\t\t\t\tloadSlide( verticalSlide );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tunloadSlide( verticalSlide );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Flag if there are ANY vertical slides, anywhere in the deck\n\t\t\tif( dom.wrapper.querySelectorAll( '.slides>section>section' ).length ) {\n\t\t\t\tdom.wrapper.classList.add( 'has-vertical-slides' );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdom.wrapper.classList.remove( 'has-vertical-slides' );\n\t\t\t}\n\n\t\t\t// Flag if there are ANY horizontal slides, anywhere in the deck\n\t\t\tif( dom.wrapper.querySelectorAll( '.slides>section' ).length > 1 ) {\n\t\t\t\tdom.wrapper.classList.add( 'has-horizontal-slides' );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdom.wrapper.classList.remove( 'has-horizontal-slides' );\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Pick up notes from the current slide and display them\n\t * to the viewer.\n\t *\n\t * @see {@link config.showNotes}\n\t */\n\tfunction updateNotes() {\n\n\t\tif( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) {\n\n\t\t\tdom.speakerNotes.innerHTML = getSlideNotes() || '<span class=\"notes-placeholder\">No notes on this slide.</span>';\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the visibility of the speaker notes sidebar that\n\t * is used to share annotated slides. The notes sidebar is\n\t * only visible if showNotes is true and there are notes on\n\t * one or more slides in the deck.\n\t */\n\tfunction updateNotesVisibility() {\n\n\t\tif( config.showNotes && hasNotes() ) {\n\t\t\tdom.wrapper.classList.add( 'show-notes' );\n\t\t}\n\t\telse {\n\t\t\tdom.wrapper.classList.remove( 'show-notes' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if there are speaker notes for ANY slide in the\n\t * presentation.\n\t */\n\tfunction hasNotes() {\n\n\t\treturn dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0;\n\n\t}\n\n\t/**\n\t * Updates the progress bar to reflect the current slide.\n\t */\n\tfunction updateProgress() {\n\n\t\t// Update progress if enabled\n\t\tif( config.progress && dom.progressbar ) {\n\n\t\t\tdom.progressbar.style.width = getProgress() * dom.wrapper.offsetWidth + 'px';\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the slide number div to reflect the current slide.\n\t *\n\t * The following slide number formats are available:\n\t * \"h.v\":\thorizontal . vertical slide number (default)\n\t * \"h/v\":\thorizontal / vertical slide number\n\t * \"c\":\tflattened slide number\n\t * \"c/t\":\tflattened slide number / total slides\n\t */\n\tfunction updateSlideNumber() {\n\n\t\t// Update slide number if enabled\n\t\tif( config.slideNumber && dom.slideNumber ) {\n\n\t\t\tvar value = [];\n\t\t\tvar format = 'h.v';\n\n\t\t\t// Check if a custom number format is available\n\t\t\tif( typeof config.slideNumber === 'string' ) {\n\t\t\t\tformat = config.slideNumber;\n\t\t\t}\n\n\t\t\tswitch( format ) {\n\t\t\t\tcase 'c':\n\t\t\t\t\tvalue.push( getSlidePastCount() + 1 );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'c/t':\n\t\t\t\t\tvalue.push( getSlidePastCount() + 1, '/', getTotalSlides() );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'h/v':\n\t\t\t\t\tvalue.push( indexh + 1 );\n\t\t\t\t\tif( isVerticalSlide() ) value.push( '/', indexv + 1 );\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tvalue.push( indexh + 1 );\n\t\t\t\t\tif( isVerticalSlide() ) value.push( '.', indexv + 1 );\n\t\t\t}\n\n\t\t\tdom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] );\n\t\t}\n\n\t}\n\n\t/**\n\t * Applies HTML formatting to a slide number before it's\n\t * written to the DOM.\n\t *\n\t * @param {number} a Current slide\n\t * @param {string} delimiter Character to separate slide numbers\n\t * @param {(number|*)} b Total slides\n\t * @return {string} HTML string fragment\n\t */\n\tfunction formatSlideNumber( a, delimiter, b ) {\n\n\t\tif( typeof b === 'number' && !isNaN( b ) ) {\n\t\t\treturn '<span class=\"slide-number-a\">'+ a +'</span>' +\n\t\t\t\t\t'<span class=\"slide-number-delimiter\">'+ delimiter +'</span>' +\n\t\t\t\t\t'<span class=\"slide-number-b\">'+ b +'</span>';\n\t\t}\n\t\telse {\n\t\t\treturn '<span class=\"slide-number-a\">'+ a +'</span>';\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the state of all control/navigation arrows.\n\t */\n\tfunction updateControls() {\n\n\t\tvar routes = availableRoutes();\n\t\tvar fragments = availableFragments();\n\n\t\t// Remove the 'enabled' class from all directions\n\t\tdom.controlsLeft.concat( dom.controlsRight )\n\t\t\t\t\t\t.concat( dom.controlsUp )\n\t\t\t\t\t\t.concat( dom.controlsDown )\n\t\t\t\t\t\t.concat( dom.controlsPrev )\n\t\t\t\t\t\t.concat( dom.controlsNext ).forEach( function( node ) {\n\t\t\tnode.classList.remove( 'enabled' );\n\t\t\tnode.classList.remove( 'fragmented' );\n\n\t\t\t// Set 'disabled' attribute on all directions\n\t\t\tnode.setAttribute( 'disabled', 'disabled' );\n\t\t} );\n\n\t\t// Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons\n\t\tif( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t// Prev/next buttons\n\t\tif( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t// Highlight fragment directions\n\t\tif( currentSlide ) {\n\n\t\t\t// Always apply fragment decorator to prev/next buttons\n\t\t\tif( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\tif( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t\t// Apply fragment decorators to directional buttons based on\n\t\t\t// what slide axis they are in\n\t\t\tif( isVerticalSlide( currentSlide ) ) {\n\t\t\t\tif( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t\tif( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t\tif( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t}\n\n\t\t}\n\n\t\tif( config.controlsTutorial ) {\n\n\t\t\t// Highlight control arrows with an animation to ensure\n\t\t\t// that the viewer knows how to navigate\n\t\t\tif( !hasNavigatedDown && routes.down ) {\n\t\t\t\tdom.controlsDownArrow.classList.add( 'highlight' );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdom.controlsDownArrow.classList.remove( 'highlight' );\n\n\t\t\t\tif( !hasNavigatedRight && routes.right && indexv === 0 ) {\n\t\t\t\t\tdom.controlsRightArrow.classList.add( 'highlight' );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdom.controlsRightArrow.classList.remove( 'highlight' );\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the background elements to reflect the current\n\t * slide.\n\t *\n\t * @param {boolean} includeAll If true, the backgrounds of\n\t * all vertical slides (not just the present) will be updated.\n\t */\n\tfunction updateBackground( includeAll ) {\n\n\t\tvar currentBackground = null;\n\n\t\t// Reverse past/future classes when in RTL mode\n\t\tvar horizontalPast = config.rtl ? 'future' : 'past',\n\t\t\thorizontalFuture = config.rtl ? 'past' : 'future';\n\n\t\t// Update the classes of all backgrounds to match the\n\t\t// states of their slides (past/present/future)\n\t\ttoArray( dom.background.childNodes ).forEach( function( backgroundh, h ) {\n\n\t\t\tbackgroundh.classList.remove( 'past' );\n\t\t\tbackgroundh.classList.remove( 'present' );\n\t\t\tbackgroundh.classList.remove( 'future' );\n\n\t\t\tif( h < indexh ) {\n\t\t\t\tbackgroundh.classList.add( horizontalPast );\n\t\t\t}\n\t\t\telse if ( h > indexh ) {\n\t\t\t\tbackgroundh.classList.add( horizontalFuture );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbackgroundh.classList.add( 'present' );\n\n\t\t\t\t// Store a reference to the current background element\n\t\t\t\tcurrentBackground = backgroundh;\n\t\t\t}\n\n\t\t\tif( includeAll || h === indexh ) {\n\t\t\t\ttoArray( backgroundh.querySelectorAll( '.slide-background' ) ).forEach( function( backgroundv, v ) {\n\n\t\t\t\t\tbackgroundv.classList.remove( 'past' );\n\t\t\t\t\tbackgroundv.classList.remove( 'present' );\n\t\t\t\t\tbackgroundv.classList.remove( 'future' );\n\n\t\t\t\t\tif( v < indexv ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'past' );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( v > indexv ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'future' );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tbackgroundv.classList.add( 'present' );\n\n\t\t\t\t\t\t// Only if this is the present horizontal and vertical slide\n\t\t\t\t\t\tif( h === indexh ) currentBackground = backgroundv;\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\t\t\t}\n\n\t\t} );\n\n\t\t// Stop content inside of previous backgrounds\n\t\tif( previousBackground ) {\n\n\t\t\tstopEmbeddedContent( previousBackground );\n\n\t\t}\n\n\t\t// Start content in the current background\n\t\tif( currentBackground ) {\n\n\t\t\tstartEmbeddedContent( currentBackground );\n\n\t\t\tvar backgroundImageURL = currentBackground.style.backgroundImage || '';\n\n\t\t\t// Restart GIFs (doesn't work in Firefox)\n\t\t\tif( /\\.gif/i.test( backgroundImageURL ) ) {\n\t\t\t\tcurrentBackground.style.backgroundImage = '';\n\t\t\t\twindow.getComputedStyle( currentBackground ).opacity;\n\t\t\t\tcurrentBackground.style.backgroundImage = backgroundImageURL;\n\t\t\t}\n\n\t\t\t// Don't transition between identical backgrounds. This\n\t\t\t// prevents unwanted flicker.\n\t\t\tvar previousBackgroundHash = previousBackground ? previousBackground.getAttribute( 'data-background-hash' ) : null;\n\t\t\tvar currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );\n\t\t\tif( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== previousBackground ) {\n\t\t\t\tdom.background.classList.add( 'no-transition' );\n\t\t\t}\n\n\t\t\tpreviousBackground = currentBackground;\n\n\t\t}\n\n\t\t// If there's a background brightness flag for this slide,\n\t\t// bubble it to the .reveal container\n\t\tif( currentSlide ) {\n\t\t\t[ 'has-light-background', 'has-dark-background' ].forEach( function( classToBubble ) {\n\t\t\t\tif( currentSlide.classList.contains( classToBubble ) ) {\n\t\t\t\t\tdom.wrapper.classList.add( classToBubble );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tdom.wrapper.classList.remove( classToBubble );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t// Allow the first background to apply without transition\n\t\tsetTimeout( function() {\n\t\t\tdom.background.classList.remove( 'no-transition' );\n\t\t}, 1 );\n\n\t}\n\n\t/**\n\t * Updates the position of the parallax background based\n\t * on the current slide index.\n\t */\n\tfunction updateParallax() {\n\n\t\tif( config.parallaxBackgroundImage ) {\n\n\t\t\tvar horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),\n\t\t\t\tverticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR );\n\n\t\t\tvar backgroundSize = dom.background.style.backgroundSize.split( ' ' ),\n\t\t\t\tbackgroundWidth, backgroundHeight;\n\n\t\t\tif( backgroundSize.length === 1 ) {\n\t\t\t\tbackgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbackgroundWidth = parseInt( backgroundSize[0], 10 );\n\t\t\t\tbackgroundHeight = parseInt( backgroundSize[1], 10 );\n\t\t\t}\n\n\t\t\tvar slideWidth = dom.background.offsetWidth,\n\t\t\t\thorizontalSlideCount = horizontalSlides.length,\n\t\t\t\thorizontalOffsetMultiplier,\n\t\t\t\thorizontalOffset;\n\n\t\t\tif( typeof config.parallaxBackgroundHorizontal === 'number' ) {\n\t\t\t\thorizontalOffsetMultiplier = config.parallaxBackgroundHorizontal;\n\t\t\t}\n\t\t\telse {\n\t\t\t\thorizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0;\n\t\t\t}\n\n\t\t\thorizontalOffset = horizontalOffsetMultiplier * indexh * -1;\n\n\t\t\tvar slideHeight = dom.background.offsetHeight,\n\t\t\t\tverticalSlideCount = verticalSlides.length,\n\t\t\t\tverticalOffsetMultiplier,\n\t\t\t\tverticalOffset;\n\n\t\t\tif( typeof config.parallaxBackgroundVertical === 'number' ) {\n\t\t\t\tverticalOffsetMultiplier = config.parallaxBackgroundVertical;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tverticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 );\n\t\t\t}\n\n\t\t\tverticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv : 0;\n\n\t\t\tdom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px';\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Called when the given slide is within the configured view\n\t * distance. Shows the slide element and loads any content\n\t * that is set to load lazily (data-src).\n\t *\n\t * @param {HTMLElement} slide Slide to show\n\t */\n\tfunction loadSlide( slide, options ) {\n\n\t\toptions = options || {};\n\n\t\t// Show the slide element\n\t\tslide.style.display = config.display;\n\n\t\t// Media elements with data-src attributes\n\t\ttoArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src]' ) ).forEach( function( element ) {\n\t\t\telement.setAttribute( 'src', element.getAttribute( 'data-src' ) );\n\t\t\telement.setAttribute( 'data-lazy-loaded', '' );\n\t\t\telement.removeAttribute( 'data-src' );\n\t\t} );\n\n\t\t// Media elements with <source> children\n\t\ttoArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( media ) {\n\t\t\tvar sources = 0;\n\n\t\t\ttoArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) {\n\t\t\t\tsource.setAttribute( 'src', source.getAttribute( 'data-src' ) );\n\t\t\t\tsource.removeAttribute( 'data-src' );\n\t\t\t\tsource.setAttribute( 'data-lazy-loaded', '' );\n\t\t\t\tsources += 1;\n\t\t\t} );\n\n\t\t\t// If we rewrote sources for this video/audio element, we need\n\t\t\t// to manually tell it to load from its new origin\n\t\t\tif( sources > 0 ) {\n\t\t\t\tmedia.load();\n\t\t\t}\n\t\t} );\n\n\n\t\t// Show the corresponding background element\n\t\tvar indices = getIndices( slide );\n\t\tvar background = getSlideBackground( indices.h, indices.v );\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'block';\n\n\t\t\t// If the background contains media, load it\n\t\t\tif( background.hasAttribute( 'data-loaded' ) === false ) {\n\t\t\t\tbackground.setAttribute( 'data-loaded', 'true' );\n\n\t\t\t\tvar backgroundImage = slide.getAttribute( 'data-background-image' ),\n\t\t\t\t\tbackgroundVideo = slide.getAttribute( 'data-background-video' ),\n\t\t\t\t\tbackgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),\n\t\t\t\t\tbackgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ),\n\t\t\t\t\tbackgroundIframe = slide.getAttribute( 'data-background-iframe' );\n\n\t\t\t\t// Images\n\t\t\t\tif( backgroundImage ) {\n\t\t\t\t\tbackground.style.backgroundImage = 'url('+ backgroundImage +')';\n\t\t\t\t}\n\t\t\t\t// Videos\n\t\t\t\telse if ( backgroundVideo && !isSpeakerNotes() ) {\n\t\t\t\t\tvar video = document.createElement( 'video' );\n\n\t\t\t\t\tif( backgroundVideoLoop ) {\n\t\t\t\t\t\tvideo.setAttribute( 'loop', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\tif( backgroundVideoMuted ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Inline video playback works (at least in Mobile Safari) as\n\t\t\t\t\t// long as the video is muted and the `playsinline` attribute is\n\t\t\t\t\t// present\n\t\t\t\t\tif( isMobileDevice ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t\tvideo.autoplay = true;\n\t\t\t\t\t\tvideo.setAttribute( 'playsinline', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support comma separated lists of video sources\n\t\t\t\t\tbackgroundVideo.split( ',' ).forEach( function( source ) {\n\t\t\t\t\t\tvideo.innerHTML += '<source src=\"'+ source +'\">';\n\t\t\t\t\t} );\n\n\t\t\t\t\tbackground.appendChild( video );\n\t\t\t\t}\n\t\t\t\t// Iframes\n\t\t\t\telse if( backgroundIframe && options.excludeIframes !== true ) {\n\t\t\t\t\tvar iframe = document.createElement( 'iframe' );\n\t\t\t\t\tiframe.setAttribute( 'allowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'mozallowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'webkitallowfullscreen', '' );\n\n\t\t\t\t\t// Only load autoplaying content when the slide is shown to\n\t\t\t\t\t// avoid having it play in the background\n\t\t\t\t\tif( /autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {\n\t\t\t\t\t\tiframe.setAttribute( 'data-src', backgroundIframe );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tiframe.setAttribute( 'src', backgroundIframe );\n\t\t\t\t\t}\n\n\t\t\t\t\tiframe.style.width = '100%';\n\t\t\t\t\tiframe.style.height = '100%';\n\t\t\t\t\tiframe.style.maxHeight = '100%';\n\t\t\t\t\tiframe.style.maxWidth = '100%';\n\n\t\t\t\t\tbackground.appendChild( iframe );\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Unloads and hides the given slide. This is called when the\n\t * slide is moved outside of the configured view distance.\n\t *\n\t * @param {HTMLElement} slide\n\t */\n\tfunction unloadSlide( slide ) {\n\n\t\t// Hide the slide element\n\t\tslide.style.display = 'none';\n\n\t\t// Hide the corresponding background element\n\t\tvar indices = getIndices( slide );\n\t\tvar background = getSlideBackground( indices.h, indices.v );\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'none';\n\t\t}\n\n\t\t// Reset lazy-loaded media elements with src attributes\n\t\ttoArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src]' ) ).forEach( function( element ) {\n\t\t\telement.setAttribute( 'data-src', element.getAttribute( 'src' ) );\n\t\t\telement.removeAttribute( 'src' );\n\t\t} );\n\n\t\t// Reset lazy-loaded media elements with <source> children\n\t\ttoArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) {\n\t\t\tsource.setAttribute( 'data-src', source.getAttribute( 'src' ) );\n\t\t\tsource.removeAttribute( 'src' );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Determine what available routes there are for navigation.\n\t *\n\t * @return {{left: boolean, right: boolean, up: boolean, down: boolean}}\n\t */\n\tfunction availableRoutes() {\n\n\t\tvar horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),\n\t\t\tverticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR );\n\n\t\tvar routes = {\n\t\t\tleft: indexh > 0 || config.loop,\n\t\t\tright: indexh < horizontalSlides.length - 1 || config.loop,\n\t\t\tup: indexv > 0,\n\t\t\tdown: indexv < verticalSlides.length - 1\n\t\t};\n\n\t\t// reverse horizontal controls for rtl\n\t\tif( config.rtl ) {\n\t\t\tvar left = routes.left;\n\t\t\troutes.left = routes.right;\n\t\t\troutes.right = left;\n\t\t}\n\n\t\treturn routes;\n\n\t}\n\n\t/**\n\t * Returns an object describing the available fragment\n\t * directions.\n\t *\n\t * @return {{prev: boolean, next: boolean}}\n\t */\n\tfunction availableFragments() {\n\n\t\tif( currentSlide && config.fragments ) {\n\t\t\tvar fragments = currentSlide.querySelectorAll( '.fragment' );\n\t\t\tvar hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' );\n\n\t\t\treturn {\n\t\t\t\tprev: fragments.length - hiddenFragments.length > 0,\n\t\t\t\tnext: !!hiddenFragments.length\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\treturn { prev: false, next: false };\n\t\t}\n\n\t}\n\n\t/**\n\t * Enforces origin-specific format rules for embedded media.\n\t */\n\tfunction formatEmbeddedContent() {\n\n\t\tvar _appendParamToIframeSource = function( sourceAttribute, sourceURL, param ) {\n\t\t\ttoArray( dom.slides.querySelectorAll( 'iframe['+ sourceAttribute +'*=\"'+ sourceURL +'\"]' ) ).forEach( function( el ) {\n\t\t\t\tvar src = el.getAttribute( sourceAttribute );\n\t\t\t\tif( src && src.indexOf( param ) === -1 ) {\n\t\t\t\t\tel.setAttribute( sourceAttribute, src + ( !/\\?/.test( src ) ? '?' : '&' ) + param );\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\t// YouTube frames must include \"?enablejsapi=1\"\n\t\t_appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' );\n\n\t\t// Vimeo frames must include \"?api=1\"\n\t\t_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );\n\n\t\t// Always show media controls on mobile devices\n\t\tif( isMobileDevice ) {\n\t\t\ttoArray( dom.slides.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {\n\t\t\t\tel.controls = true;\n\t\t\t} );\n\t\t}\n\n\t}\n\n\t/**\n\t * Start playback of any embedded content inside of\n\t * the given element.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tfunction startEmbeddedContent( element ) {\n\n\t\tif( element && !isSpeakerNotes() ) {\n\n\t\t\t// Restart GIFs\n\t\t\ttoArray( element.querySelectorAll( 'img[src$=\".gif\"]' ) ).forEach( function( el ) {\n\t\t\t\t// Setting the same unchanged source like this was confirmed\n\t\t\t\t// to work in Chrome, FF & Safari\n\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'src' ) );\n\t\t\t} );\n\n\t\t\t// HTML5 media elements\n\t\t\ttoArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {\n\t\t\t\tif( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tvar autoplay = config.autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\tif( autoplay && typeof el.play === 'function' ) {\n\n\t\t\t\t\tif( el.readyState > 1 ) {\n\t\t\t\t\t\tstartEmbeddedMedia( { target: el } );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes\n\t\t\t\t\t\tel.addEventListener( 'loadeddata', startEmbeddedMedia );\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Normal iframes\n\t\t\ttoArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) {\n\t\t\t\tif( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tstartEmbeddedIframe( { target: el } );\n\t\t\t} );\n\n\t\t\t// Lazy loading iframes\n\t\t\ttoArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {\n\t\t\t\tif( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {\n\t\t\t\t\tel.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes\n\t\t\t\t\tel.addEventListener( 'load', startEmbeddedIframe );\n\t\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'data-src' ) );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Starts playing an embedded video/audio element after\n\t * it has finished loading.\n\t *\n\t * @param {object} event\n\t */\n\tfunction startEmbeddedMedia( event ) {\n\n\t\tvar isAttachedToDOM = !!closestParent( event.target, 'html' ),\n\t\t\tisVisible \t\t= !!closestParent( event.target, '.present' );\n\n\t\tif( isAttachedToDOM && isVisible ) {\n\t\t\tevent.target.currentTime = 0;\n\t\t\tevent.target.play();\n\t\t}\n\n\t\tevent.target.removeEventListener( 'loadeddata', startEmbeddedMedia );\n\n\t}\n\n\t/**\n\t * \"Starts\" the content of an embedded iframe using the\n\t * postMessage API.\n\t *\n\t * @param {object} event\n\t */\n\tfunction startEmbeddedIframe( event ) {\n\n\t\tvar iframe = event.target;\n\n\t\tif( iframe && iframe.contentWindow ) {\n\n\t\t\tvar isAttachedToDOM = !!closestParent( event.target, 'html' ),\n\t\t\t\tisVisible \t\t= !!closestParent( event.target, '.present' );\n\n\t\t\tif( isAttachedToDOM && isVisible ) {\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tvar autoplay = config.autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\t// YouTube postMessage API\n\t\t\t\tif( /youtube\\.com\\/embed\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"playVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Vimeo postMessage API\n\t\t\t\telse if( /player\\.vimeo\\.com\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"method\":\"play\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Generic postMessage API\n\t\t\t\telse {\n\t\t\t\t\tiframe.contentWindow.postMessage( 'slide:start', '*' );\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Stop playback of any embedded content inside of\n\t * the targeted slide.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tfunction stopEmbeddedContent( element, options ) {\n\n\t\toptions = extend( {\n\t\t\t// Defaults\n\t\t\tunloadIframes: true\n\t\t}, options || {} );\n\n\t\tif( element && element.parentNode ) {\n\t\t\t// HTML5 media elements\n\t\t\ttoArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {\n\t\t\t\t\tel.setAttribute('data-paused-by-reveal', '');\n\t\t\t\t\tel.pause();\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Generic postMessage API for non-lazy loaded iframes\n\t\t\ttoArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) {\n\t\t\t\tif( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' );\n\t\t\t\tel.removeEventListener( 'load', startEmbeddedIframe );\n\t\t\t});\n\n\t\t\t// YouTube postMessage API\n\t\t\ttoArray( element.querySelectorAll( 'iframe[src*=\"youtube.com/embed/\"]' ) ).forEach( function( el ) {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"pauseVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Vimeo postMessage API\n\t\t\ttoArray( element.querySelectorAll( 'iframe[src*=\"player.vimeo.com/\"]' ) ).forEach( function( el ) {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"method\":\"pause\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif( options.unloadIframes === true ) {\n\t\t\t\t// Unload lazy-loaded iframes\n\t\t\t\ttoArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) {\n\t\t\t\t\t// Only removing the src doesn't actually unload the frame\n\t\t\t\t\t// in all browsers (Firefox) so we set it to blank first\n\t\t\t\t\tel.setAttribute( 'src', 'about:blank' );\n\t\t\t\t\tel.removeAttribute( 'src' );\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns the number of past slides. This can be used as a global\n\t * flattened index for slides.\n\t *\n\t * @return {number} Past slide count\n\t */\n\tfunction getSlidePastCount() {\n\n\t\tvar horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );\n\n\t\t// The number of past slides\n\t\tvar pastCount = 0;\n\n\t\t// Step through all slides and count the past ones\n\t\tmainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) {\n\n\t\t\tvar horizontalSlide = horizontalSlides[i];\n\t\t\tvar verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );\n\n\t\t\tfor( var j = 0; j < verticalSlides.length; j++ ) {\n\n\t\t\t\t// Stop as soon as we arrive at the present\n\t\t\t\tif( verticalSlides[j].classList.contains( 'present' ) ) {\n\t\t\t\t\tbreak mainLoop;\n\t\t\t\t}\n\n\t\t\t\tpastCount++;\n\n\t\t\t}\n\n\t\t\t// Stop as soon as we arrive at the present\n\t\t\tif( horizontalSlide.classList.contains( 'present' ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Don't count the wrapping section for vertical slides\n\t\t\tif( horizontalSlide.classList.contains( 'stack' ) === false ) {\n\t\t\t\tpastCount++;\n\t\t\t}\n\n\t\t}\n\n\t\treturn pastCount;\n\n\t}\n\n\t/**\n\t * Returns a value ranging from 0-1 that represents\n\t * how far into the presentation we have navigated.\n\t *\n\t * @return {number}\n\t */\n\tfunction getProgress() {\n\n\t\t// The number of past and total slides\n\t\tvar totalCount = getTotalSlides();\n\t\tvar pastCount = getSlidePastCount();\n\n\t\tif( currentSlide ) {\n\n\t\t\tvar allFragments = currentSlide.querySelectorAll( '.fragment' );\n\n\t\t\t// If there are fragments in the current slide those should be\n\t\t\t// accounted for in the progress.\n\t\t\tif( allFragments.length > 0 ) {\n\t\t\t\tvar visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );\n\n\t\t\t\t// This value represents how big a portion of the slide progress\n\t\t\t\t// that is made up by its fragments (0-1)\n\t\t\t\tvar fragmentWeight = 0.9;\n\n\t\t\t\t// Add fragment progress to the past slide count\n\t\t\t\tpastCount += ( visibleFragments.length / allFragments.length ) * fragmentWeight;\n\t\t\t}\n\n\t\t}\n\n\t\treturn pastCount / ( totalCount - 1 );\n\n\t}\n\n\t/**\n\t * Checks if this presentation is running inside of the\n\t * speaker notes window.\n\t *\n\t * @return {boolean}\n\t */\n\tfunction isSpeakerNotes() {\n\n\t\treturn !!window.location.search.match( /receiver/gi );\n\n\t}\n\n\t/**\n\t * Reads the current URL (hash) and navigates accordingly.\n\t */\n\tfunction readURL() {\n\n\t\tvar hash = window.location.hash;\n\n\t\t// Attempt to parse the hash as either an index or name\n\t\tvar bits = hash.slice( 2 ).split( '/' ),\n\t\t\tname = hash.replace( /#|\\//gi, '' );\n\n\t\t// If the first bit is invalid and there is a name we can\n\t\t// assume that this is a named link\n\t\tif( isNaN( parseInt( bits[0], 10 ) ) && name.length ) {\n\t\t\tvar element;\n\n\t\t\t// Ensure the named link is a valid HTML ID attribute\n\t\t\tif( /^[a-zA-Z][\\w:.-]*$/.test( name ) ) {\n\t\t\t\t// Find the slide with the specified ID\n\t\t\t\telement = document.getElementById( name );\n\t\t\t}\n\n\t\t\tif( element ) {\n\t\t\t\t// Find the position of the named slide and navigate to it\n\t\t\t\tvar indices = Reveal.getIndices( element );\n\t\t\t\tslide( indices.h, indices.v );\n\t\t\t}\n\t\t\t// If the slide doesn't exist, navigate to the current slide\n\t\t\telse {\n\t\t\t\tslide( indexh || 0, indexv || 0 );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\t// Read the index components of the hash\n\t\t\tvar h = parseInt( bits[0], 10 ) || 0,\n\t\t\t\tv = parseInt( bits[1], 10 ) || 0;\n\n\t\t\tif( h !== indexh || v !== indexv ) {\n\t\t\t\tslide( h, v );\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the page URL (hash) to reflect the current\n\t * state.\n\t *\n\t * @param {number} delay The time in ms to wait before\n\t * writing the hash\n\t */\n\tfunction writeURL( delay ) {\n\n\t\tif( config.history ) {\n\n\t\t\t// Make sure there's never more than one timeout running\n\t\t\tclearTimeout( writeURLTimeout );\n\n\t\t\t// If a delay is specified, timeout this call\n\t\t\tif( typeof delay === 'number' ) {\n\t\t\t\twriteURLTimeout = setTimeout( writeURL, delay );\n\t\t\t}\n\t\t\telse if( currentSlide ) {\n\t\t\t\tvar url = '/';\n\n\t\t\t\t// Attempt to create a named link based on the slide's ID\n\t\t\t\tvar id = currentSlide.getAttribute( 'id' );\n\t\t\t\tif( id ) {\n\t\t\t\t\tid = id.replace( /[^a-zA-Z0-9\\-\\_\\:\\.]/g, '' );\n\t\t\t\t}\n\n\t\t\t\t// If the current slide has an ID, use that as a named link\n\t\t\t\tif( typeof id === 'string' && id.length ) {\n\t\t\t\t\turl = '/' + id;\n\t\t\t\t}\n\t\t\t\t// Otherwise use the /h/v index\n\t\t\t\telse {\n\t\t\t\t\tif( indexh > 0 || indexv > 0 ) url += indexh;\n\t\t\t\t\tif( indexv > 0 ) url += '/' + indexv;\n\t\t\t\t}\n\n\t\t\t\twindow.location.hash = url;\n\t\t\t}\n\t\t}\n\n\t}\n\t/**\n\t * Retrieves the h/v location and fragment of the current,\n\t * or specified, slide.\n\t *\n\t * @param {HTMLElement} [slide] If specified, the returned\n\t * index will be for this slide rather than the currently\n\t * active one\n\t *\n\t * @return {{h: number, v: number, f: number}}\n\t */\n\tfunction getIndices( slide ) {\n\n\t\t// By default, return the current indices\n\t\tvar h = indexh,\n\t\t\tv = indexv,\n\t\t\tf;\n\n\t\t// If a slide is specified, return the indices of that slide\n\t\tif( slide ) {\n\t\t\tvar isVertical = isVerticalSlide( slide );\n\t\t\tvar slideh = isVertical ? slide.parentNode : slide;\n\n\t\t\t// Select all horizontal slides\n\t\t\tvar horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );\n\n\t\t\t// Now that we know which the horizontal slide is, get its index\n\t\t\th = Math.max( horizontalSlides.indexOf( slideh ), 0 );\n\n\t\t\t// Assume we're not vertical\n\t\t\tv = undefined;\n\n\t\t\t// If this is a vertical slide, grab the vertical index\n\t\t\tif( isVertical ) {\n\t\t\t\tv = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 );\n\t\t\t}\n\t\t}\n\n\t\tif( !slide && currentSlide ) {\n\t\t\tvar hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0;\n\t\t\tif( hasFragments ) {\n\t\t\t\tvar currentFragment = currentSlide.querySelector( '.current-fragment' );\n\t\t\t\tif( currentFragment && currentFragment.hasAttribute( 'data-fragment-index' ) ) {\n\t\t\t\t\tf = parseInt( currentFragment.getAttribute( 'data-fragment-index' ), 10 );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tf = currentSlide.querySelectorAll( '.fragment.visible' ).length - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { h: h, v: v, f: f };\n\n\t}\n\n\t/**\n\t * Retrieves all slides in this presentation.\n\t */\n\tfunction getSlides() {\n\n\t\treturn toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ));\n\n\t}\n\n\t/**\n\t * Retrieves the total number of slides in this presentation.\n\t *\n\t * @return {number}\n\t */\n\tfunction getTotalSlides() {\n\n\t\treturn getSlides().length;\n\n\t}\n\n\t/**\n\t * Returns the slide element matching the specified index.\n\t *\n\t * @return {HTMLElement}\n\t */\n\tfunction getSlide( x, y ) {\n\n\t\tvar horizontalSlide = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ];\n\t\tvar verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' );\n\n\t\tif( verticalSlides && verticalSlides.length && typeof y === 'number' ) {\n\t\t\treturn verticalSlides ? verticalSlides[ y ] : undefined;\n\t\t}\n\n\t\treturn horizontalSlide;\n\n\t}\n\n\t/**\n\t * Returns the background element for the given slide.\n\t * All slides, even the ones with no background properties\n\t * defined, have a background element so as long as the\n\t * index is valid an element will be returned.\n\t *\n\t * @param {number} x Horizontal background index\n\t * @param {number} y Vertical background index\n\t * @return {(HTMLElement[]|*)}\n\t */\n\tfunction getSlideBackground( x, y ) {\n\n\t\tvar slide = getSlide( x, y );\n\t\tif( slide ) {\n\t\t\treturn slide.slideBackgroundElement;\n\t\t}\n\n\t\treturn undefined;\n\n\t}\n\n\t/**\n\t * Retrieves the speaker notes from a slide. Notes can be\n\t * defined in two ways:\n\t * 1. As a data-notes attribute on the slide <section>\n\t * 2. As an <aside class=\"notes\"> inside of the slide\n\t *\n\t * @param {HTMLElement} [slide=currentSlide]\n\t * @return {(string|null)}\n\t */\n\tfunction getSlideNotes( slide ) {\n\n\t\t// Default to the current slide\n\t\tslide = slide || currentSlide;\n\n\t\t// Notes can be specified via the data-notes attribute...\n\t\tif( slide.hasAttribute( 'data-notes' ) ) {\n\t\t\treturn slide.getAttribute( 'data-notes' );\n\t\t}\n\n\t\t// ... or using an <aside class=\"notes\"> element\n\t\tvar notesElement = slide.querySelector( 'aside.notes' );\n\t\tif( notesElement ) {\n\t\t\treturn notesElement.innerHTML;\n\t\t}\n\n\t\treturn null;\n\n\t}\n\n\t/**\n\t * Retrieves the current state of the presentation as\n\t * an object. This state can then be restored at any\n\t * time.\n\t *\n\t * @return {{indexh: number, indexv: number, indexf: number, paused: boolean, overview: boolean}}\n\t */\n\tfunction getState() {\n\n\t\tvar indices = getIndices();\n\n\t\treturn {\n\t\t\tindexh: indices.h,\n\t\t\tindexv: indices.v,\n\t\t\tindexf: indices.f,\n\t\t\tpaused: isPaused(),\n\t\t\toverview: isOverview()\n\t\t};\n\n\t}\n\n\t/**\n\t * Restores the presentation to the given state.\n\t *\n\t * @param {object} state As generated by getState()\n\t * @see {@link getState} generates the parameter `state`\n\t */\n\tfunction setState( state ) {\n\n\t\tif( typeof state === 'object' ) {\n\t\t\tslide( deserialize( state.indexh ), deserialize( state.indexv ), deserialize( state.indexf ) );\n\n\t\t\tvar pausedFlag = deserialize( state.paused ),\n\t\t\t\toverviewFlag = deserialize( state.overview );\n\n\t\t\tif( typeof pausedFlag === 'boolean' && pausedFlag !== isPaused() ) {\n\t\t\t\ttogglePause( pausedFlag );\n\t\t\t}\n\n\t\t\tif( typeof overviewFlag === 'boolean' && overviewFlag !== isOverview() ) {\n\t\t\t\ttoggleOverview( overviewFlag );\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Return a sorted fragments list, ordered by an increasing\n\t * \"data-fragment-index\" attribute.\n\t *\n\t * Fragments will be revealed in the order that they are returned by\n\t * this function, so you can use the index attributes to control the\n\t * order of fragment appearance.\n\t *\n\t * To maintain a sensible default fragment order, fragments are presumed\n\t * to be passed in document order. This function adds a \"fragment-index\"\n\t * attribute to each node if such an attribute is not already present,\n\t * and sets that attribute to an integer value which is the position of\n\t * the fragment within the fragments list.\n\t *\n\t * @param {object[]|*} fragments\n\t * @return {object[]} sorted Sorted array of fragments\n\t */\n\tfunction sortFragments( fragments ) {\n\n\t\tfragments = toArray( fragments );\n\n\t\tvar ordered = [],\n\t\t\tunordered = [],\n\t\t\tsorted = [];\n\n\t\t// Group ordered and unordered elements\n\t\tfragments.forEach( function( fragment, i ) {\n\t\t\tif( fragment.hasAttribute( 'data-fragment-index' ) ) {\n\t\t\t\tvar index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 );\n\n\t\t\t\tif( !ordered[index] ) {\n\t\t\t\t\tordered[index] = [];\n\t\t\t\t}\n\n\t\t\t\tordered[index].push( fragment );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tunordered.push( [ fragment ] );\n\t\t\t}\n\t\t} );\n\n\t\t// Append fragments without explicit indices in their\n\t\t// DOM order\n\t\tordered = ordered.concat( unordered );\n\n\t\t// Manually count the index up per group to ensure there\n\t\t// are no gaps\n\t\tvar index = 0;\n\n\t\t// Push all fragments in their sorted order to an array,\n\t\t// this flattens the groups\n\t\tordered.forEach( function( group ) {\n\t\t\tgroup.forEach( function( fragment ) {\n\t\t\t\tsorted.push( fragment );\n\t\t\t\tfragment.setAttribute( 'data-fragment-index', index );\n\t\t\t} );\n\n\t\t\tindex ++;\n\t\t} );\n\n\t\treturn sorted;\n\n\t}\n\n\t/**\n\t * Navigate to the specified slide fragment.\n\t *\n\t * @param {?number} index The index of the fragment that\n\t * should be shown, -1 means all are invisible\n\t * @param {number} offset Integer offset to apply to the\n\t * fragment index\n\t *\n\t * @return {boolean} true if a change was made in any\n\t * fragments visibility as part of this call\n\t */\n\tfunction navigateFragment( index, offset ) {\n\n\t\tif( currentSlide && config.fragments ) {\n\n\t\t\tvar fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) );\n\t\t\tif( fragments.length ) {\n\n\t\t\t\t// If no index is specified, find the current\n\t\t\t\tif( typeof index !== 'number' ) {\n\t\t\t\t\tvar lastVisibleFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();\n\n\t\t\t\t\tif( lastVisibleFragment ) {\n\t\t\t\t\t\tindex = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tindex = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If an offset is specified, apply it to the index\n\t\t\t\tif( typeof offset === 'number' ) {\n\t\t\t\t\tindex += offset;\n\t\t\t\t}\n\n\t\t\t\tvar fragmentsShown = [],\n\t\t\t\t\tfragmentsHidden = [];\n\n\t\t\t\ttoArray( fragments ).forEach( function( element, i ) {\n\n\t\t\t\t\tif( element.hasAttribute( 'data-fragment-index' ) ) {\n\t\t\t\t\t\ti = parseInt( element.getAttribute( 'data-fragment-index' ), 10 );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Visible fragments\n\t\t\t\t\tif( i <= index ) {\n\t\t\t\t\t\tif( !element.classList.contains( 'visible' ) ) fragmentsShown.push( element );\n\t\t\t\t\t\telement.classList.add( 'visible' );\n\t\t\t\t\t\telement.classList.remove( 'current-fragment' );\n\n\t\t\t\t\t\t// Announce the fragments one by one to the Screen Reader\n\t\t\t\t\t\tdom.statusDiv.textContent = getStatusText( element );\n\n\t\t\t\t\t\tif( i === index ) {\n\t\t\t\t\t\t\telement.classList.add( 'current-fragment' );\n\t\t\t\t\t\t\tstartEmbeddedContent( element );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Hidden fragments\n\t\t\t\t\telse {\n\t\t\t\t\t\tif( element.classList.contains( 'visible' ) ) fragmentsHidden.push( element );\n\t\t\t\t\t\telement.classList.remove( 'visible' );\n\t\t\t\t\t\telement.classList.remove( 'current-fragment' );\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\tif( fragmentsHidden.length ) {\n\t\t\t\t\tdispatchEvent( 'fragmenthidden', { fragment: fragmentsHidden[0], fragments: fragmentsHidden } );\n\t\t\t\t}\n\n\t\t\t\tif( fragmentsShown.length ) {\n\t\t\t\t\tdispatchEvent( 'fragmentshown', { fragment: fragmentsShown[0], fragments: fragmentsShown } );\n\t\t\t\t}\n\n\t\t\t\tupdateControls();\n\t\t\t\tupdateProgress();\n\n\t\t\t\treturn !!( fragmentsShown.length || fragmentsHidden.length );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\t/**\n\t * Navigate to the next slide fragment.\n\t *\n\t * @return {boolean} true if there was a next fragment,\n\t * false otherwise\n\t */\n\tfunction nextFragment() {\n\n\t\treturn navigateFragment( null, 1 );\n\n\t}\n\n\t/**\n\t * Navigate to the previous slide fragment.\n\t *\n\t * @return {boolean} true if there was a previous fragment,\n\t * false otherwise\n\t */\n\tfunction previousFragment() {\n\n\t\treturn navigateFragment( null, -1 );\n\n\t}\n\n\t/**\n\t * Cues a new automated slide if enabled in the config.\n\t */\n\tfunction cueAutoSlide() {\n\n\t\tcancelAutoSlide();\n\n\t\tif( currentSlide && config.autoSlide !== false ) {\n\n\t\t\tvar fragment = currentSlide.querySelector( '.current-fragment' );\n\n\t\t\t// When the slide first appears there is no \"current\" fragment so\n\t\t\t// we look for a data-autoslide timing on the first fragment\n\t\t\tif( !fragment ) fragment = currentSlide.querySelector( '.fragment' );\n\n\t\t\tvar fragmentAutoSlide = fragment ? fragment.getAttribute( 'data-autoslide' ) : null;\n\t\t\tvar parentAutoSlide = currentSlide.parentNode ? currentSlide.parentNode.getAttribute( 'data-autoslide' ) : null;\n\t\t\tvar slideAutoSlide = currentSlide.getAttribute( 'data-autoslide' );\n\n\t\t\t// Pick value in the following priority order:\n\t\t\t// 1. Current fragment's data-autoslide\n\t\t\t// 2. Current slide's data-autoslide\n\t\t\t// 3. Parent slide's data-autoslide\n\t\t\t// 4. Global autoSlide setting\n\t\t\tif( fragmentAutoSlide ) {\n\t\t\t\tautoSlide = parseInt( fragmentAutoSlide, 10 );\n\t\t\t}\n\t\t\telse if( slideAutoSlide ) {\n\t\t\t\tautoSlide = parseInt( slideAutoSlide, 10 );\n\t\t\t}\n\t\t\telse if( parentAutoSlide ) {\n\t\t\t\tautoSlide = parseInt( parentAutoSlide, 10 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tautoSlide = config.autoSlide;\n\t\t\t}\n\n\t\t\t// If there are media elements with data-autoplay,\n\t\t\t// automatically set the autoSlide duration to the\n\t\t\t// length of that media. Not applicable if the slide\n\t\t\t// is divided up into fragments.\n\t\t\t// playbackRate is accounted for in the duration.\n\t\t\tif( currentSlide.querySelectorAll( '.fragment' ).length === 0 ) {\n\t\t\t\ttoArray( currentSlide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {\n\t\t\t\t\tif( el.hasAttribute( 'data-autoplay' ) ) {\n\t\t\t\t\t\tif( autoSlide && (el.duration * 1000 / el.playbackRate ) > autoSlide ) {\n\t\t\t\t\t\t\tautoSlide = ( el.duration * 1000 / el.playbackRate ) + 1000;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\t// Cue the next auto-slide if:\n\t\t\t// - There is an autoSlide value\n\t\t\t// - Auto-sliding isn't paused by the user\n\t\t\t// - The presentation isn't paused\n\t\t\t// - The overview isn't active\n\t\t\t// - The presentation isn't over\n\t\t\tif( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || availableFragments().next || config.loop === true ) ) {\n\t\t\t\tautoSlideTimeout = setTimeout( function() {\n\t\t\t\t\ttypeof config.autoSlideMethod === 'function' ? config.autoSlideMethod() : navigateNext();\n\t\t\t\t\tcueAutoSlide();\n\t\t\t\t}, autoSlide );\n\t\t\t\tautoSlideStartTime = Date.now();\n\t\t\t}\n\n\t\t\tif( autoSlidePlayer ) {\n\t\t\t\tautoSlidePlayer.setPlaying( autoSlideTimeout !== -1 );\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Cancels any ongoing request to auto-slide.\n\t */\n\tfunction cancelAutoSlide() {\n\n\t\tclearTimeout( autoSlideTimeout );\n\t\tautoSlideTimeout = -1;\n\n\t}\n\n\tfunction pauseAutoSlide() {\n\n\t\tif( autoSlide && !autoSlidePaused ) {\n\t\t\tautoSlidePaused = true;\n\t\t\tdispatchEvent( 'autoslidepaused' );\n\t\t\tclearTimeout( autoSlideTimeout );\n\n\t\t\tif( autoSlidePlayer ) {\n\t\t\t\tautoSlidePlayer.setPlaying( false );\n\t\t\t}\n\t\t}\n\n\t}\n\n\tfunction resumeAutoSlide() {\n\n\t\tif( autoSlide && autoSlidePaused ) {\n\t\t\tautoSlidePaused = false;\n\t\t\tdispatchEvent( 'autoslideresumed' );\n\t\t\tcueAutoSlide();\n\t\t}\n\n\t}\n\n\tfunction navigateLeft() {\n\n\t\t// Reverse for RTL\n\t\tif( config.rtl ) {\n\t\t\tif( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) {\n\t\t\t\tslide( indexh + 1 );\n\t\t\t}\n\t\t}\n\t\t// Normal navigation\n\t\telse if( ( isOverview() || previousFragment() === false ) && availableRoutes().left ) {\n\t\t\tslide( indexh - 1 );\n\t\t}\n\n\t}\n\n\tfunction navigateRight() {\n\n\t\thasNavigatedRight = true;\n\n\t\t// Reverse for RTL\n\t\tif( config.rtl ) {\n\t\t\tif( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) {\n\t\t\t\tslide( indexh - 1 );\n\t\t\t}\n\t\t}\n\t\t// Normal navigation\n\t\telse if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) {\n\t\t\tslide( indexh + 1 );\n\t\t}\n\n\t}\n\n\tfunction navigateUp() {\n\n\t\t// Prioritize hiding fragments\n\t\tif( ( isOverview() || previousFragment() === false ) && availableRoutes().up ) {\n\t\t\tslide( indexh, indexv - 1 );\n\t\t}\n\n\t}\n\n\tfunction navigateDown() {\n\n\t\thasNavigatedDown = true;\n\n\t\t// Prioritize revealing fragments\n\t\tif( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) {\n\t\t\tslide( indexh, indexv + 1 );\n\t\t}\n\n\t}\n\n\t/**\n\t * Navigates backwards, prioritized in the following order:\n\t * 1) Previous fragment\n\t * 2) Previous vertical slide\n\t * 3) Previous horizontal slide\n\t */\n\tfunction navigatePrev() {\n\n\t\t// Prioritize revealing fragments\n\t\tif( previousFragment() === false ) {\n\t\t\tif( availableRoutes().up ) {\n\t\t\t\tnavigateUp();\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Fetch the previous horizontal slide, if there is one\n\t\t\t\tvar previousSlide;\n\n\t\t\t\tif( config.rtl ) {\n\t\t\t\t\tpreviousSlide = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.future' ) ).pop();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpreviousSlide = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.past' ) ).pop();\n\t\t\t\t}\n\n\t\t\t\tif( previousSlide ) {\n\t\t\t\t\tvar v = ( previousSlide.querySelectorAll( 'section' ).length - 1 ) || undefined;\n\t\t\t\t\tvar h = indexh - 1;\n\t\t\t\t\tslide( h, v );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * The reverse of #navigatePrev().\n\t */\n\tfunction navigateNext() {\n\n\t\thasNavigatedRight = true;\n\t\thasNavigatedDown = true;\n\n\t\t// Prioritize revealing fragments\n\t\tif( nextFragment() === false ) {\n\t\t\tif( availableRoutes().down ) {\n\t\t\t\tnavigateDown();\n\t\t\t}\n\t\t\telse if( config.rtl ) {\n\t\t\t\tnavigateLeft();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tnavigateRight();\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if the target element prevents the triggering of\n\t * swipe navigation.\n\t */\n\tfunction isSwipePrevented( target ) {\n\n\t\twhile( target && typeof target.hasAttribute === 'function' ) {\n\t\t\tif( target.hasAttribute( 'data-prevent-swipe' ) ) return true;\n\t\t\ttarget = target.parentNode;\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\n\t// --------------------------------------------------------------------//\n\t// ----------------------------- EVENTS -------------------------------//\n\t// --------------------------------------------------------------------//\n\n\t/**\n\t * Called by all event handlers that are based on user\n\t * input.\n\t *\n\t * @param {object} [event]\n\t */\n\tfunction onUserInput( event ) {\n\n\t\tif( config.autoSlideStoppable ) {\n\t\t\tpauseAutoSlide();\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the document level 'keypress' event.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onDocumentKeyPress( event ) {\n\n\t\t// Check if the pressed key is question mark\n\t\tif( event.shiftKey && event.charCode === 63 ) {\n\t\t\ttoggleHelp();\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the document level 'keydown' event.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onDocumentKeyDown( event ) {\n\n\t\t// If there's a condition specified and it returns false,\n\t\t// ignore this event\n\t\tif( typeof config.keyboardCondition === 'function' && config.keyboardCondition() === false ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Remember if auto-sliding was paused so we can toggle it\n\t\tvar autoSlideWasPaused = autoSlidePaused;\n\n\t\tonUserInput( event );\n\n\t\t// Check if there's a focused element that could be using\n\t\t// the keyboard\n\t\tvar activeElementIsCE = document.activeElement && document.activeElement.contentEditable !== 'inherit';\n\t\tvar activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );\n\t\tvar activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);\n\n\t\t// Disregard the event if there's a focused element or a\n\t\t// keyboard modifier key is present\n\t\tif( activeElementIsCE || activeElementIsInput || activeElementIsNotes || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;\n\n\t\t// While paused only allow resume keyboard events; 'b', 'v', '.'\n\t\tvar resumeKeyCodes = [66,86,190,191];\n\t\tvar key;\n\n\t\t// Custom key bindings for togglePause should be able to resume\n\t\tif( typeof config.keyboard === 'object' ) {\n\t\t\tfor( key in config.keyboard ) {\n\t\t\t\tif( config.keyboard[key] === 'togglePause' ) {\n\t\t\t\t\tresumeKeyCodes.push( parseInt( key, 10 ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif( isPaused() && resumeKeyCodes.indexOf( event.keyCode ) === -1 ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tvar triggered = false;\n\n\t\t// 1. User defined key bindings\n\t\tif( typeof config.keyboard === 'object' ) {\n\n\t\t\tfor( key in config.keyboard ) {\n\n\t\t\t\t// Check if this binding matches the pressed key\n\t\t\t\tif( parseInt( key, 10 ) === event.keyCode ) {\n\n\t\t\t\t\tvar value = config.keyboard[ key ];\n\n\t\t\t\t\t// Callback function\n\t\t\t\t\tif( typeof value === 'function' ) {\n\t\t\t\t\t\tvalue.apply( null, [ event ] );\n\t\t\t\t\t}\n\t\t\t\t\t// String shortcuts to reveal.js API\n\t\t\t\t\telse if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) {\n\t\t\t\t\t\tReveal[ value ].call();\n\t\t\t\t\t}\n\n\t\t\t\t\ttriggered = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// 2. System defined key bindings\n\t\tif( triggered === false ) {\n\n\t\t\t// Assume true and try to prove false\n\t\t\ttriggered = true;\n\n\t\t\tswitch( event.keyCode ) {\n\t\t\t\t// p, page up\n\t\t\t\tcase 80: case 33: navigatePrev(); break;\n\t\t\t\t// n, page down\n\t\t\t\tcase 78: case 34: navigateNext(); break;\n\t\t\t\t// h, left\n\t\t\t\tcase 72: case 37: navigateLeft(); break;\n\t\t\t\t// l, right\n\t\t\t\tcase 76: case 39: navigateRight(); break;\n\t\t\t\t// k, up\n\t\t\t\tcase 75: case 38: navigateUp(); break;\n\t\t\t\t// j, down\n\t\t\t\tcase 74: case 40: navigateDown(); break;\n\t\t\t\t// home\n\t\t\t\tcase 36: slide( 0 ); break;\n\t\t\t\t// end\n\t\t\t\tcase 35: slide( Number.MAX_VALUE ); break;\n\t\t\t\t// space\n\t\t\t\tcase 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;\n\t\t\t\t// return\n\t\t\t\tcase 13: isOverview() ? deactivateOverview() : triggered = false; break;\n\t\t\t\t// two-spot, semicolon, b, v, period, Logitech presenter tools \"black screen\" button\n\t\t\t\tcase 58: case 59: case 66: case 86: case 190: case 191: togglePause(); break;\n\t\t\t\t// f\n\t\t\t\tcase 70: enterFullscreen(); break;\n\t\t\t\t// a\n\t\t\t\tcase 65: if ( config.autoSlideStoppable ) toggleAutoSlide( autoSlideWasPaused ); break;\n\t\t\t\tdefault:\n\t\t\t\t\ttriggered = false;\n\t\t\t}\n\n\t\t}\n\n\t\t// If the input resulted in a triggered action we should prevent\n\t\t// the browsers default behavior\n\t\tif( triggered ) {\n\t\t\tevent.preventDefault && event.preventDefault();\n\t\t}\n\t\t// ESC or O key\n\t\telse if ( ( event.keyCode === 27 || event.keyCode === 79 ) && features.transforms3d ) {\n\t\t\tif( dom.overlay ) {\n\t\t\t\tcloseOverlay();\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttoggleOverview();\n\t\t\t}\n\n\t\t\tevent.preventDefault && event.preventDefault();\n\t\t}\n\n\t\t// If auto-sliding is enabled we need to cue up\n\t\t// another timeout\n\t\tcueAutoSlide();\n\n\t}\n\n\t/**\n\t * Handler for the 'touchstart' event, enables support for\n\t * swipe and pinch gestures.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onTouchStart( event ) {\n\n\t\tif( isSwipePrevented( event.target ) ) return true;\n\n\t\ttouch.startX = event.touches[0].clientX;\n\t\ttouch.startY = event.touches[0].clientY;\n\t\ttouch.startCount = event.touches.length;\n\n\t\t// If there's two touches we need to memorize the distance\n\t\t// between those two points to detect pinching\n\t\tif( event.touches.length === 2 && config.overview ) {\n\t\t\ttouch.startSpan = distanceBetween( {\n\t\t\t\tx: event.touches[1].clientX,\n\t\t\t\ty: event.touches[1].clientY\n\t\t\t}, {\n\t\t\t\tx: touch.startX,\n\t\t\t\ty: touch.startY\n\t\t\t} );\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the 'touchmove' event.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onTouchMove( event ) {\n\n\t\tif( isSwipePrevented( event.target ) ) return true;\n\n\t\t// Each touch should only trigger one action\n\t\tif( !touch.captured ) {\n\t\t\tonUserInput( event );\n\n\t\t\tvar currentX = event.touches[0].clientX;\n\t\t\tvar currentY = event.touches[0].clientY;\n\n\t\t\t// If the touch started with two points and still has\n\t\t\t// two active touches; test for the pinch gesture\n\t\t\tif( event.touches.length === 2 && touch.startCount === 2 && config.overview ) {\n\n\t\t\t\t// The current distance in pixels between the two touch points\n\t\t\t\tvar currentSpan = distanceBetween( {\n\t\t\t\t\tx: event.touches[1].clientX,\n\t\t\t\t\ty: event.touches[1].clientY\n\t\t\t\t}, {\n\t\t\t\t\tx: touch.startX,\n\t\t\t\t\ty: touch.startY\n\t\t\t\t} );\n\n\t\t\t\t// If the span is larger than the desire amount we've got\n\t\t\t\t// ourselves a pinch\n\t\t\t\tif( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {\n\t\t\t\t\ttouch.captured = true;\n\n\t\t\t\t\tif( currentSpan < touch.startSpan ) {\n\t\t\t\t\t\tactivateOverview();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tdeactivateOverview();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tevent.preventDefault();\n\n\t\t\t}\n\t\t\t// There was only one touch point, look for a swipe\n\t\t\telse if( event.touches.length === 1 && touch.startCount !== 2 ) {\n\n\t\t\t\tvar deltaX = currentX - touch.startX,\n\t\t\t\t\tdeltaY = currentY - touch.startY;\n\n\t\t\t\tif( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {\n\t\t\t\t\ttouch.captured = true;\n\t\t\t\t\tnavigateLeft();\n\t\t\t\t}\n\t\t\t\telse if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {\n\t\t\t\t\ttouch.captured = true;\n\t\t\t\t\tnavigateRight();\n\t\t\t\t}\n\t\t\t\telse if( deltaY > touch.threshold ) {\n\t\t\t\t\ttouch.captured = true;\n\t\t\t\t\tnavigateUp();\n\t\t\t\t}\n\t\t\t\telse if( deltaY < -touch.threshold ) {\n\t\t\t\t\ttouch.captured = true;\n\t\t\t\t\tnavigateDown();\n\t\t\t\t}\n\n\t\t\t\t// If we're embedded, only block touch events if they have\n\t\t\t\t// triggered an action\n\t\t\t\tif( config.embedded ) {\n\t\t\t\t\tif( touch.captured || isVerticalSlide( currentSlide ) ) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Not embedded? Block them all to avoid needless tossing\n\t\t\t\t// around of the viewport in iOS\n\t\t\t\telse {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\t// There's a bug with swiping on some Android devices unless\n\t\t// the default action is always prevented\n\t\telse if( UA.match( /android/gi ) ) {\n\t\t\tevent.preventDefault();\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the 'touchend' event.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onTouchEnd( event ) {\n\n\t\ttouch.captured = false;\n\n\t}\n\n\t/**\n\t * Convert pointer down to touch start.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onPointerDown( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tonTouchStart( event );\n\t\t}\n\n\t}\n\n\t/**\n\t * Convert pointer move to touch move.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onPointerMove( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tonTouchMove( event );\n\t\t}\n\n\t}\n\n\t/**\n\t * Convert pointer up to touch end.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onPointerUp( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tonTouchEnd( event );\n\t\t}\n\n\t}\n\n\t/**\n\t * Handles mouse wheel scrolling, throttled to avoid skipping\n\t * multiple slides.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onDocumentMouseScroll( event ) {\n\n\t\tif( Date.now() - lastMouseWheelStep > 600 ) {\n\n\t\t\tlastMouseWheelStep = Date.now();\n\n\t\t\tvar delta = event.detail || -event.wheelDelta;\n\t\t\tif( delta > 0 ) {\n\t\t\t\tnavigateNext();\n\t\t\t}\n\t\t\telse if( delta < 0 ) {\n\t\t\t\tnavigatePrev();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Clicking on the progress bar results in a navigation to the\n\t * closest approximate horizontal slide using this equation:\n\t *\n\t * ( clickX / presentationWidth ) * numberOfSlides\n\t *\n\t * @param {object} event\n\t */\n\tfunction onProgressClicked( event ) {\n\n\t\tonUserInput( event );\n\n\t\tevent.preventDefault();\n\n\t\tvar slidesTotal = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length;\n\t\tvar slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal );\n\n\t\tif( config.rtl ) {\n\t\t\tslideIndex = slidesTotal - slideIndex;\n\t\t}\n\n\t\tslide( slideIndex );\n\n\t}\n\n\t/**\n\t * Event handler for navigation control buttons.\n\t */\n\tfunction onNavigateLeftClicked( event ) { event.preventDefault(); onUserInput(); navigateLeft(); }\n\tfunction onNavigateRightClicked( event ) { event.preventDefault(); onUserInput(); navigateRight(); }\n\tfunction onNavigateUpClicked( event ) { event.preventDefault(); onUserInput(); navigateUp(); }\n\tfunction onNavigateDownClicked( event ) { event.preventDefault(); onUserInput(); navigateDown(); }\n\tfunction onNavigatePrevClicked( event ) { event.preventDefault(); onUserInput(); navigatePrev(); }\n\tfunction onNavigateNextClicked( event ) { event.preventDefault(); onUserInput(); navigateNext(); }\n\n\t/**\n\t * Handler for the window level 'hashchange' event.\n\t *\n\t * @param {object} [event]\n\t */\n\tfunction onWindowHashChange( event ) {\n\n\t\treadURL();\n\n\t}\n\n\t/**\n\t * Handler for the window level 'resize' event.\n\t *\n\t * @param {object} [event]\n\t */\n\tfunction onWindowResize( event ) {\n\n\t\tlayout();\n\n\t}\n\n\t/**\n\t * Handle for the window level 'visibilitychange' event.\n\t *\n\t * @param {object} [event]\n\t */\n\tfunction onPageVisibilityChange( event ) {\n\n\t\tvar isHidden = document.webkitHidden ||\n\t\t\t\t\t\tdocument.msHidden ||\n\t\t\t\t\t\tdocument.hidden;\n\n\t\t// If, after clicking a link or similar and we're coming back,\n\t\t// focus the document.body to ensure we can use keyboard shortcuts\n\t\tif( isHidden === false && document.activeElement !== document.body ) {\n\t\t\t// Not all elements support .blur() - SVGs among them.\n\t\t\tif( typeof document.activeElement.blur === 'function' ) {\n\t\t\t\tdocument.activeElement.blur();\n\t\t\t}\n\t\t\tdocument.body.focus();\n\t\t}\n\n\t}\n\n\t/**\n\t * Invoked when a slide is and we're in the overview.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onOverviewSlideClicked( event ) {\n\n\t\t// TODO There's a bug here where the event listeners are not\n\t\t// removed after deactivating the overview.\n\t\tif( eventsAreBound && isOverview() ) {\n\t\t\tevent.preventDefault();\n\n\t\t\tvar element = event.target;\n\n\t\t\twhile( element && !element.nodeName.match( /section/gi ) ) {\n\t\t\t\telement = element.parentNode;\n\t\t\t}\n\n\t\t\tif( element && !element.classList.contains( 'disabled' ) ) {\n\n\t\t\t\tdeactivateOverview();\n\n\t\t\t\tif( element.nodeName.match( /section/gi ) ) {\n\t\t\t\t\tvar h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),\n\t\t\t\t\t\tv = parseInt( element.getAttribute( 'data-index-v' ), 10 );\n\n\t\t\t\t\tslide( h, v );\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Handles clicks on links that are set to preview in the\n\t * iframe overlay.\n\t *\n\t * @param {object} event\n\t */\n\tfunction onPreviewLinkClicked( event ) {\n\n\t\tif( event.currentTarget && event.currentTarget.hasAttribute( 'href' ) ) {\n\t\t\tvar url = event.currentTarget.getAttribute( 'href' );\n\t\t\tif( url ) {\n\t\t\t\tshowPreview( url );\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Handles click on the auto-sliding controls element.\n\t *\n\t * @param {object} [event]\n\t */\n\tfunction onAutoSlidePlayerClick( event ) {\n\n\t\t// Replay\n\t\tif( Reveal.isLastSlide() && config.loop === false ) {\n\t\t\tslide( 0, 0 );\n\t\t\tresumeAutoSlide();\n\t\t}\n\t\t// Resume\n\t\telse if( autoSlidePaused ) {\n\t\t\tresumeAutoSlide();\n\t\t}\n\t\t// Pause\n\t\telse {\n\t\t\tpauseAutoSlide();\n\t\t}\n\n\t}\n\n\n\t// --------------------------------------------------------------------//\n\t// ------------------------ PLAYBACK COMPONENT ------------------------//\n\t// --------------------------------------------------------------------//\n\n\n\t/**\n\t * Constructor for the playback component, which displays\n\t * play/pause/progress controls.\n\t *\n\t * @param {HTMLElement} container The component will append\n\t * itself to this\n\t * @param {function} progressCheck A method which will be\n\t * called frequently to get the current progress on a range\n\t * of 0-1\n\t */\n\tfunction Playback( container, progressCheck ) {\n\n\t\t// Cosmetics\n\t\tthis.diameter = 100;\n\t\tthis.diameter2 = this.diameter/2;\n\t\tthis.thickness = 6;\n\n\t\t// Flags if we are currently playing\n\t\tthis.playing = false;\n\n\t\t// Current progress on a 0-1 range\n\t\tthis.progress = 0;\n\n\t\t// Used to loop the animation smoothly\n\t\tthis.progressOffset = 1;\n\n\t\tthis.container = container;\n\t\tthis.progressCheck = progressCheck;\n\n\t\tthis.canvas = document.createElement( 'canvas' );\n\t\tthis.canvas.className = 'playback';\n\t\tthis.canvas.width = this.diameter;\n\t\tthis.canvas.height = this.diameter;\n\t\tthis.canvas.style.width = this.diameter2 + 'px';\n\t\tthis.canvas.style.height = this.diameter2 + 'px';\n\t\tthis.context = this.canvas.getContext( '2d' );\n\n\t\tthis.container.appendChild( this.canvas );\n\n\t\tthis.render();\n\n\t}\n\n\t/**\n\t * @param value\n\t */\n\tPlayback.prototype.setPlaying = function( value ) {\n\n\t\tvar wasPlaying = this.playing;\n\n\t\tthis.playing = value;\n\n\t\t// Start repainting if we weren't already\n\t\tif( !wasPlaying && this.playing ) {\n\t\t\tthis.animate();\n\t\t}\n\t\telse {\n\t\t\tthis.render();\n\t\t}\n\n\t};\n\n\tPlayback.prototype.animate = function() {\n\n\t\tvar progressBefore = this.progress;\n\n\t\tthis.progress = this.progressCheck();\n\n\t\t// When we loop, offset the progress so that it eases\n\t\t// smoothly rather than immediately resetting\n\t\tif( progressBefore > 0.8 && this.progress < 0.2 ) {\n\t\t\tthis.progressOffset = this.progress;\n\t\t}\n\n\t\tthis.render();\n\n\t\tif( this.playing ) {\n\t\t\tfeatures.requestAnimationFrameMethod.call( window, this.animate.bind( this ) );\n\t\t}\n\n\t};\n\n\t/**\n\t * Renders the current progress and playback state.\n\t */\n\tPlayback.prototype.render = function() {\n\n\t\tvar progress = this.playing ? this.progress : 0,\n\t\t\tradius = ( this.diameter2 ) - this.thickness,\n\t\t\tx = this.diameter2,\n\t\t\ty = this.diameter2,\n\t\t\ticonSize = 28;\n\n\t\t// Ease towards 1\n\t\tthis.progressOffset += ( 1 - this.progressOffset ) * 0.1;\n\n\t\tvar endAngle = ( - Math.PI / 2 ) + ( progress * ( Math.PI * 2 ) );\n\t\tvar startAngle = ( - Math.PI / 2 ) + ( this.progressOffset * ( Math.PI * 2 ) );\n\n\t\tthis.context.save();\n\t\tthis.context.clearRect( 0, 0, this.diameter, this.diameter );\n\n\t\t// Solid background color\n\t\tthis.context.beginPath();\n\t\tthis.context.arc( x, y, radius + 4, 0, Math.PI * 2, false );\n\t\tthis.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )';\n\t\tthis.context.fill();\n\n\t\t// Draw progress track\n\t\tthis.context.beginPath();\n\t\tthis.context.arc( x, y, radius, 0, Math.PI * 2, false );\n\t\tthis.context.lineWidth = this.thickness;\n\t\tthis.context.strokeStyle = 'rgba( 255, 255, 255, 0.2 )';\n\t\tthis.context.stroke();\n\n\t\tif( this.playing ) {\n\t\t\t// Draw progress on top of track\n\t\t\tthis.context.beginPath();\n\t\t\tthis.context.arc( x, y, radius, startAngle, endAngle, false );\n\t\t\tthis.context.lineWidth = this.thickness;\n\t\t\tthis.context.strokeStyle = '#fff';\n\t\t\tthis.context.stroke();\n\t\t}\n\n\t\tthis.context.translate( x - ( iconSize / 2 ), y - ( iconSize / 2 ) );\n\n\t\t// Draw play/pause icons\n\t\tif( this.playing ) {\n\t\t\tthis.context.fillStyle = '#fff';\n\t\t\tthis.context.fillRect( 0, 0, iconSize / 2 - 4, iconSize );\n\t\t\tthis.context.fillRect( iconSize / 2 + 4, 0, iconSize / 2 - 4, iconSize );\n\t\t}\n\t\telse {\n\t\t\tthis.context.beginPath();\n\t\t\tthis.context.translate( 4, 0 );\n\t\t\tthis.context.moveTo( 0, 0 );\n\t\t\tthis.context.lineTo( iconSize - 4, iconSize / 2 );\n\t\t\tthis.context.lineTo( 0, iconSize );\n\t\t\tthis.context.fillStyle = '#fff';\n\t\t\tthis.context.fill();\n\t\t}\n\n\t\tthis.context.restore();\n\n\t};\n\n\tPlayback.prototype.on = function( type, listener ) {\n\t\tthis.canvas.addEventListener( type, listener, false );\n\t};\n\n\tPlayback.prototype.off = function( type, listener ) {\n\t\tthis.canvas.removeEventListener( type, listener, false );\n\t};\n\n\tPlayback.prototype.destroy = function() {\n\n\t\tthis.playing = false;\n\n\t\tif( this.canvas.parentNode ) {\n\t\t\tthis.container.removeChild( this.canvas );\n\t\t}\n\n\t};\n\n\n\t// --------------------------------------------------------------------//\n\t// ------------------------------- API --------------------------------//\n\t// --------------------------------------------------------------------//\n\n\n\tReveal = {\n\t\tVERSION: VERSION,\n\n\t\tinitialize: initialize,\n\t\tconfigure: configure,\n\t\tsync: sync,\n\n\t\t// Navigation methods\n\t\tslide: slide,\n\t\tleft: navigateLeft,\n\t\tright: navigateRight,\n\t\tup: navigateUp,\n\t\tdown: navigateDown,\n\t\tprev: navigatePrev,\n\t\tnext: navigateNext,\n\n\t\t// Fragment methods\n\t\tnavigateFragment: navigateFragment,\n\t\tprevFragment: previousFragment,\n\t\tnextFragment: nextFragment,\n\n\t\t// Deprecated aliases\n\t\tnavigateTo: slide,\n\t\tnavigateLeft: navigateLeft,\n\t\tnavigateRight: navigateRight,\n\t\tnavigateUp: navigateUp,\n\t\tnavigateDown: navigateDown,\n\t\tnavigatePrev: navigatePrev,\n\t\tnavigateNext: navigateNext,\n\n\t\t// Forces an update in slide layout\n\t\tlayout: layout,\n\n\t\t// Randomizes the order of slides\n\t\tshuffle: shuffle,\n\n\t\t// Returns an object with the available routes as booleans (left/right/top/bottom)\n\t\tavailableRoutes: availableRoutes,\n\n\t\t// Returns an object with the available fragments as booleans (prev/next)\n\t\tavailableFragments: availableFragments,\n\n\t\t// Toggles a help overlay with keyboard shortcuts\n\t\ttoggleHelp: toggleHelp,\n\n\t\t// Toggles the overview mode on/off\n\t\ttoggleOverview: toggleOverview,\n\n\t\t// Toggles the \"black screen\" mode on/off\n\t\ttogglePause: togglePause,\n\n\t\t// Toggles the auto slide mode on/off\n\t\ttoggleAutoSlide: toggleAutoSlide,\n\n\t\t// State checks\n\t\tisOverview: isOverview,\n\t\tisPaused: isPaused,\n\t\tisAutoSliding: isAutoSliding,\n\t\tisSpeakerNotes: isSpeakerNotes,\n\n\t\t// Slide preloading\n\t\tloadSlide: loadSlide,\n\t\tunloadSlide: unloadSlide,\n\n\t\t// Adds or removes all internal event listeners (such as keyboard)\n\t\taddEventListeners: addEventListeners,\n\t\tremoveEventListeners: removeEventListeners,\n\n\t\t// Facility for persisting and restoring the presentation state\n\t\tgetState: getState,\n\t\tsetState: setState,\n\n\t\t// Presentation progress\n\t\tgetSlidePastCount: getSlidePastCount,\n\n\t\t// Presentation progress on range of 0-1\n\t\tgetProgress: getProgress,\n\n\t\t// Returns the indices of the current, or specified, slide\n\t\tgetIndices: getIndices,\n\n\t\t// Returns an Array of all slides\n\t\tgetSlides: getSlides,\n\n\t\t// Returns the total number of slides\n\t\tgetTotalSlides: getTotalSlides,\n\n\t\t// Returns the slide element at the specified index\n\t\tgetSlide: getSlide,\n\n\t\t// Returns the slide background element at the specified index\n\t\tgetSlideBackground: getSlideBackground,\n\n\t\t// Returns the speaker notes string for a slide, or null\n\t\tgetSlideNotes: getSlideNotes,\n\n\t\t// Returns the previous slide element, may be null\n\t\tgetPreviousSlide: function() {\n\t\t\treturn previousSlide;\n\t\t},\n\n\t\t// Returns the current slide element\n\t\tgetCurrentSlide: function() {\n\t\t\treturn currentSlide;\n\t\t},\n\n\t\t// Returns the current scale of the presentation content\n\t\tgetScale: function() {\n\t\t\treturn scale;\n\t\t},\n\n\t\t// Returns the current configuration object\n\t\tgetConfig: function() {\n\t\t\treturn config;\n\t\t},\n\n\t\t// Helper method, retrieves query string as a key/value hash\n\t\tgetQueryHash: function() {\n\t\t\tvar query = {};\n\n\t\t\tlocation.search.replace( /[A-Z0-9]+?=([\\w\\.%-]*)/gi, function(a) {\n\t\t\t\tquery[ a.split( '=' ).shift() ] = a.split( '=' ).pop();\n\t\t\t} );\n\n\t\t\t// Basic deserialization\n\t\t\tfor( var i in query ) {\n\t\t\t\tvar value = query[ i ];\n\n\t\t\t\tquery[ i ] = deserialize( unescape( value ) );\n\t\t\t}\n\n\t\t\treturn query;\n\t\t},\n\n\t\t// Returns true if we're currently on the first slide\n\t\tisFirstSlide: function() {\n\t\t\treturn ( indexh === 0 && indexv === 0 );\n\t\t},\n\n\t\t// Returns true if we're currently on the last slide\n\t\tisLastSlide: function() {\n\t\t\tif( currentSlide ) {\n\t\t\t\t// Does this slide has next a sibling?\n\t\t\t\tif( currentSlide.nextElementSibling ) return false;\n\n\t\t\t\t// If it's vertical, does its parent have a next sibling?\n\t\t\t\tif( isVerticalSlide( currentSlide ) && currentSlide.parentNode.nextElementSibling ) return false;\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\t// Checks if reveal.js has been loaded and is ready for use\n\t\tisReady: function() {\n\t\t\treturn loaded;\n\t\t},\n\n\t\t// Forward event binding to the reveal DOM element\n\t\taddEventListener: function( type, listener, useCapture ) {\n\t\t\tif( 'addEventListener' in window ) {\n\t\t\t\t( dom.wrapper || document.querySelector( '.reveal' ) ).addEventListener( type, listener, useCapture );\n\t\t\t}\n\t\t},\n\t\tremoveEventListener: function( type, listener, useCapture ) {\n\t\t\tif( 'addEventListener' in window ) {\n\t\t\t\t( dom.wrapper || document.querySelector( '.reveal' ) ).removeEventListener( type, listener, useCapture );\n\t\t\t}\n\t\t},\n\n\t\t// Programatically triggers a keyboard event\n\t\ttriggerKey: function( keyCode ) {\n\t\t\tonDocumentKeyDown( { keyCode: keyCode } );\n\t\t},\n\n\t\t// Registers a new shortcut to include in the help overlay\n\t\tregisterKeyboardShortcut: function( key, value ) {\n\t\t\tkeyboardShortcuts[key] = value;\n\t\t}\n\t};\n\n\treturn Reveal;\n\n}));\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvcmV2ZWFsLmpzL2pzL3JldmVhbC5qcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9yZXZlYWwuanMvanMvcmV2ZWFsLmpzPzg0ZjAiXSwic291cmNlc0NvbnRlbnQiOlsiLyohXG4gKiByZXZlYWwuanNcbiAqIGh0dHA6Ly9yZXZlYWxqcy5jb21cbiAqIE1JVCBsaWNlbnNlZFxuICpcbiAqIENvcHlyaWdodCAoQykgMjAxNyBIYWtpbSBFbCBIYXR0YWIsIGh0dHA6Ly9oYWtpbS5zZVxuICovXG4oZnVuY3Rpb24oIHJvb3QsIGZhY3RvcnkgKSB7XG5cdGlmKCB0eXBlb2YgZGVmaW5lID09PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQgKSB7XG5cdFx0Ly8gQU1ELiBSZWdpc3RlciBhcyBhbiBhbm9ueW1vdXMgbW9kdWxlLlxuXHRcdGRlZmluZSggZnVuY3Rpb24oKSB7XG5cdFx0XHRyb290LlJldmVhbCA9IGZhY3RvcnkoKTtcblx0XHRcdHJldHVybiByb290LlJldmVhbDtcblx0XHR9ICk7XG5cdH0gZWxzZSBpZiggdHlwZW9mIGV4cG9ydHMgPT09ICdvYmplY3QnICkge1xuXHRcdC8vIE5vZGUuIERvZXMgbm90IHdvcmsgd2l0aCBzdHJpY3QgQ29tbW9uSlMuXG5cdFx0bW9kdWxlLmV4cG9ydHMgPSBmYWN0b3J5KCk7XG5cdH0gZWxzZSB7XG5cdFx0Ly8gQnJvd3NlciBnbG9iYWxzLlxuXHRcdHJvb3QuUmV2ZWFsID0gZmFjdG9yeSgpO1xuXHR9XG59KCB0aGlzLCBmdW5jdGlvbigpIHtcblxuXHQndXNlIHN0cmljdCc7XG5cblx0dmFyIFJldmVhbDtcblxuXHQvLyBUaGUgcmV2ZWFsLmpzIHZlcnNpb25cblx0dmFyIFZFUlNJT04gPSAnMy42LjAnO1xuXG5cdHZhciBTTElERVNfU0VMRUNUT1IgPSAnLnNsaWRlcyBzZWN0aW9uJyxcblx0XHRIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiA9ICcuc2xpZGVzPnNlY3Rpb24nLFxuXHRcdFZFUlRJQ0FMX1NMSURFU19TRUxFQ1RPUiA9ICcuc2xpZGVzPnNlY3Rpb24ucHJlc2VudD5zZWN0aW9uJyxcblx0XHRIT01FX1NMSURFX1NFTEVDVE9SID0gJy5zbGlkZXM+c2VjdGlvbjpmaXJzdC1vZi10eXBlJyxcblx0XHRVQSA9IG5hdmlnYXRvci51c2VyQWdlbnQsXG5cblx0XHQvLyBDb25maWd1cmF0aW9uIGRlZmF1bHRzLCBjYW4gYmUgb3ZlcnJpZGRlbiBhdCBpbml0aWFsaXphdGlvbiB0aW1lXG5cdFx0Y29uZmlnID0ge1xuXG5cdFx0XHQvLyBUaGUgXCJub3JtYWxcIiBzaXplIG9mIHRoZSBwcmVzZW50YXRpb24sIGFzcGVjdCByYXRpbyB3aWxsIGJlIHByZXNlcnZlZFxuXHRcdFx0Ly8gd2hlbiB0aGUgcHJlc2VudGF0aW9uIGlzIHNjYWxlZCB0byBmaXQgZGlmZmVyZW50IHJlc29sdXRpb25zXG5cdFx0XHR3aWR0aDogOTYwLFxuXHRcdFx0aGVpZ2h0OiA3MDAsXG5cblx0XHRcdC8vIEZhY3RvciBvZiB0aGUgZGlzcGxheSBzaXplIHRoYXQgc2hvdWxkIHJlbWFpbiBlbXB0eSBhcm91bmQgdGhlIGNvbnRlbnRcblx0XHRcdG1hcmdpbjogMC4wNCxcblxuXHRcdFx0Ly8gQm91bmRzIGZvciBzbWFsbGVzdC9sYXJnZXN0IHBvc3NpYmxlIHNjYWxlIHRvIGFwcGx5IHRvIGNvbnRlbnRcblx0XHRcdG1pblNjYWxlOiAwLjIsXG5cdFx0XHRtYXhTY2FsZTogMi4wLFxuXG5cdFx0XHQvLyBEaXNwbGF5IHByZXNlbnRhdGlvbiBjb250cm9sIGFycm93c1xuXHRcdFx0Y29udHJvbHM6IHRydWUsXG5cblx0XHRcdC8vIEhlbHAgdGhlIHVzZXIgbGVhcm4gdGhlIGNvbnRyb2xzIGJ5IHByb3ZpZGluZyBoaW50cywgZm9yIGV4YW1wbGUgYnlcblx0XHRcdC8vIGJvdW5jaW5nIHRoZSBkb3duIGFycm93IHdoZW4gdGhleSBmaXJzdCBlbmNvdW50ZXIgYSB2ZXJ0aWNhbCBzbGlkZVxuXHRcdFx0Y29udHJvbHNUdXRvcmlhbDogdHJ1ZSxcblxuXHRcdFx0Ly8gRGV0ZXJtaW5lcyB3aGVyZSBjb250cm9scyBhcHBlYXIsIFwiZWRnZXNcIiBvciBcImJvdHRvbS1yaWdodFwiXG5cdFx0XHRjb250cm9sc0xheW91dDogJ2JvdHRvbS1yaWdodCcsXG5cblx0XHRcdC8vIFZpc2liaWxpdHkgcnVsZSBmb3IgYmFja3dhcmRzIG5hdmlnYXRpb24gYXJyb3dzOyBcImZhZGVkXCIsIFwiaGlkZGVuXCJcblx0XHRcdC8vIG9yIFwidmlzaWJsZVwiXG5cdFx0XHRjb250cm9sc0JhY2tBcnJvd3M6ICdmYWRlZCcsXG5cblx0XHRcdC8vIERpc3BsYXkgYSBwcmVzZW50YXRpb24gcHJvZ3Jlc3MgYmFyXG5cdFx0XHRwcm9ncmVzczogdHJ1ZSxcblxuXHRcdFx0Ly8gRGlzcGxheSB0aGUgcGFnZSBudW1iZXIgb2YgdGhlIGN1cnJlbnQgc2xpZGVcblx0XHRcdHNsaWRlTnVtYmVyOiBmYWxzZSxcblxuXHRcdFx0Ly8gRGV0ZXJtaW5lIHdoaWNoIGRpc3BsYXlzIHRvIHNob3cgdGhlIHNsaWRlIG51bWJlciBvblxuXHRcdFx0c2hvd1NsaWRlTnVtYmVyOiAnYWxsJyxcblxuXHRcdFx0Ly8gUHVzaCBlYWNoIHNsaWRlIGNoYW5nZSB0byB0aGUgYnJvd3NlciBoaXN0b3J5XG5cdFx0XHRoaXN0b3J5OiBmYWxzZSxcblxuXHRcdFx0Ly8gRW5hYmxlIGtleWJvYXJkIHNob3J0Y3V0cyBmb3IgbmF2aWdhdGlvblxuXHRcdFx0a2V5Ym9hcmQ6IHRydWUsXG5cblx0XHRcdC8vIE9wdGlvbmFsIGZ1bmN0aW9uIHRoYXQgYmxvY2tzIGtleWJvYXJkIGV2ZW50cyB3aGVuIHJldHVuaW5nIGZhbHNlXG5cdFx0XHRrZXlib2FyZENvbmRpdGlvbjogbnVsbCxcblxuXHRcdFx0Ly8gRW5hYmxlIHRoZSBzbGlkZSBvdmVydmlldyBtb2RlXG5cdFx0XHRvdmVydmlldzogdHJ1ZSxcblxuXHRcdFx0Ly8gVmVydGljYWwgY2VudGVyaW5nIG9mIHNsaWRlc1xuXHRcdFx0Y2VudGVyOiB0cnVlLFxuXG5cdFx0XHQvLyBFbmFibGVzIHRvdWNoIG5hdmlnYXRpb24gb24gZGV2aWNlcyB3aXRoIHRvdWNoIGlucHV0XG5cdFx0XHR0b3VjaDogdHJ1ZSxcblxuXHRcdFx0Ly8gTG9vcCB0aGUgcHJlc2VudGF0aW9uXG5cdFx0XHRsb29wOiBmYWxzZSxcblxuXHRcdFx0Ly8gQ2hhbmdlIHRoZSBwcmVzZW50YXRpb24gZGlyZWN0aW9uIHRvIGJlIFJUTFxuXHRcdFx0cnRsOiBmYWxzZSxcblxuXHRcdFx0Ly8gUmFuZG9taXplcyB0aGUgb3JkZXIgb2Ygc2xpZGVzIGVhY2ggdGltZSB0aGUgcHJlc2VudGF0aW9uIGxvYWRzXG5cdFx0XHRzaHVmZmxlOiBmYWxzZSxcblxuXHRcdFx0Ly8gVHVybnMgZnJhZ21lbnRzIG9uIGFuZCBvZmYgZ2xvYmFsbHlcblx0XHRcdGZyYWdtZW50czogdHJ1ZSxcblxuXHRcdFx0Ly8gRmxhZ3MgaWYgdGhlIHByZXNlbnRhdGlvbiBpcyBydW5uaW5nIGluIGFuIGVtYmVkZGVkIG1vZGUsXG5cdFx0XHQvLyBpLmUuIGNvbnRhaW5lZCB3aXRoaW4gYSBsaW1pdGVkIHBvcnRpb24gb2YgdGhlIHNjcmVlblxuXHRcdFx0ZW1iZWRkZWQ6IGZhbHNlLFxuXG5cdFx0XHQvLyBGbGFncyBpZiB3ZSBzaG91bGQgc2hvdyBhIGhlbHAgb3ZlcmxheSB3aGVuIHRoZSBxdWVzdGlvbi1tYXJrXG5cdFx0XHQvLyBrZXkgaXMgcHJlc3NlZFxuXHRcdFx0aGVscDogdHJ1ZSxcblxuXHRcdFx0Ly8gRmxhZ3MgaWYgaXQgc2hvdWxkIGJlIHBvc3NpYmxlIHRvIHBhdXNlIHRoZSBwcmVzZW50YXRpb24gKGJsYWNrb3V0KVxuXHRcdFx0cGF1c2U6IHRydWUsXG5cblx0XHRcdC8vIEZsYWdzIGlmIHNwZWFrZXIgbm90ZXMgc2hvdWxkIGJlIHZpc2libGUgdG8gYWxsIHZpZXdlcnNcblx0XHRcdHNob3dOb3RlczogZmFsc2UsXG5cblx0XHRcdC8vIEdsb2JhbCBvdmVycmlkZSBmb3IgYXV0b2xheWluZyBlbWJlZGRlZCBtZWRpYSAodmlkZW8vYXVkaW8vaWZyYW1lKVxuXHRcdFx0Ly8gLSBudWxsOiAgIE1lZGlhIHdpbGwgb25seSBhdXRvcGxheSBpZiBkYXRhLWF1dG9wbGF5IGlzIHByZXNlbnRcblx0XHRcdC8vIC0gdHJ1ZTogICBBbGwgbWVkaWEgd2lsbCBhdXRvcGxheSwgcmVnYXJkbGVzcyBvZiBpbmRpdmlkdWFsIHNldHRpbmdcblx0XHRcdC8vIC0gZmFsc2U6ICBObyBtZWRpYSB3aWxsIGF1dG9wbGF5LCByZWdhcmRsZXNzIG9mIGluZGl2aWR1YWwgc2V0dGluZ1xuXHRcdFx0YXV0b1BsYXlNZWRpYTogbnVsbCxcblxuXHRcdFx0Ly8gQ29udHJvbHMgYXV0b21hdGljIHByb2dyZXNzaW9uIHRvIHRoZSBuZXh0IHNsaWRlXG5cdFx0XHQvLyAtIDA6ICAgICAgQXV0by1zbGlkaW5nIG9ubHkgaGFwcGVucyBpZiB0aGUgZGF0YS1hdXRvc2xpZGUgSFRNTCBhdHRyaWJ1dGVcblx0XHRcdC8vICAgICAgICAgICBpcyBwcmVzZW50IG9uIHRoZSBjdXJyZW50IHNsaWRlIG9yIGZyYWdtZW50XG5cdFx0XHQvLyAtIDErOiAgICAgQWxsIHNsaWRlcyB3aWxsIHByb2dyZXNzIGF1dG9tYXRpY2FsbHkgYXQgdGhlIGdpdmVuIGludGVydmFsXG5cdFx0XHQvLyAtIGZhbHNlOiAgTm8gYXV0by1zbGlkaW5nLCBldmVuIGlmIGRhdGEtYXV0b3NsaWRlIGlzIHByZXNlbnRcblx0XHRcdGF1dG9TbGlkZTogMCxcblxuXHRcdFx0Ly8gU3RvcCBhdXRvLXNsaWRpbmcgYWZ0ZXIgdXNlciBpbnB1dFxuXHRcdFx0YXV0b1NsaWRlU3RvcHBhYmxlOiB0cnVlLFxuXG5cdFx0XHQvLyBVc2UgdGhpcyBtZXRob2QgZm9yIG5hdmlnYXRpb24gd2hlbiBhdXRvLXNsaWRpbmcgKGRlZmF1bHRzIHRvIG5hdmlnYXRlTmV4dClcblx0XHRcdGF1dG9TbGlkZU1ldGhvZDogbnVsbCxcblxuXHRcdFx0Ly8gRW5hYmxlIHNsaWRlIG5hdmlnYXRpb24gdmlhIG1vdXNlIHdoZWVsXG5cdFx0XHRtb3VzZVdoZWVsOiBmYWxzZSxcblxuXHRcdFx0Ly8gQXBwbHkgYSAzRCByb2xsIHRvIGxpbmtzIG9uIGhvdmVyXG5cdFx0XHRyb2xsaW5nTGlua3M6IGZhbHNlLFxuXG5cdFx0XHQvLyBIaWRlcyB0aGUgYWRkcmVzcyBiYXIgb24gbW9iaWxlIGRldmljZXNcblx0XHRcdGhpZGVBZGRyZXNzQmFyOiB0cnVlLFxuXG5cdFx0XHQvLyBPcGVucyBsaW5rcyBpbiBhbiBpZnJhbWUgcHJldmlldyBvdmVybGF5XG5cdFx0XHRwcmV2aWV3TGlua3M6IGZhbHNlLFxuXG5cdFx0XHQvLyBFeHBvc2VzIHRoZSByZXZlYWwuanMgQVBJIHRocm91Z2ggd2luZG93LnBvc3RNZXNzYWdlXG5cdFx0XHRwb3N0TWVzc2FnZTogdHJ1ZSxcblxuXHRcdFx0Ly8gRGlzcGF0Y2hlcyBhbGwgcmV2ZWFsLmpzIGV2ZW50cyB0byB0aGUgcGFyZW50IHdpbmRvdyB0aHJvdWdoIHBvc3RNZXNzYWdlXG5cdFx0XHRwb3N0TWVzc2FnZUV2ZW50czogZmFsc2UsXG5cblx0XHRcdC8vIEZvY3VzZXMgYm9keSB3aGVuIHBhZ2UgY2hhbmdlcyB2aXNpYmlsaXR5IHRvIGVuc3VyZSBrZXlib2FyZCBzaG9ydGN1dHMgd29ya1xuXHRcdFx0Zm9jdXNCb2R5T25QYWdlVmlzaWJpbGl0eUNoYW5nZTogdHJ1ZSxcblxuXHRcdFx0Ly8gVHJhbnNpdGlvbiBzdHlsZVxuXHRcdFx0dHJhbnNpdGlvbjogJ3NsaWRlJywgLy8gbm9uZS9mYWRlL3NsaWRlL2NvbnZleC9jb25jYXZlL3pvb21cblxuXHRcdFx0Ly8gVHJhbnNpdGlvbiBzcGVlZFxuXHRcdFx0dHJhbnNpdGlvblNwZWVkOiAnZGVmYXVsdCcsIC8vIGRlZmF1bHQvZmFzdC9zbG93XG5cblx0XHRcdC8vIFRyYW5zaXRpb24gc3R5bGUgZm9yIGZ1bGwgcGFnZSBzbGlkZSBiYWNrZ3JvdW5kc1xuXHRcdFx0YmFja2dyb3VuZFRyYW5zaXRpb246ICdmYWRlJywgLy8gbm9uZS9mYWRlL3NsaWRlL2NvbnZleC9jb25jYXZlL3pvb21cblxuXHRcdFx0Ly8gUGFyYWxsYXggYmFja2dyb3VuZCBpbWFnZVxuXHRcdFx0cGFyYWxsYXhCYWNrZ3JvdW5kSW1hZ2U6ICcnLCAvLyBDU1Mgc3ludGF4LCBlLmcuIFwiYS5qcGdcIlxuXG5cdFx0XHQvLyBQYXJhbGxheCBiYWNrZ3JvdW5kIHNpemVcblx0XHRcdHBhcmFsbGF4QmFja2dyb3VuZFNpemU6ICcnLCAvLyBDU1Mgc3ludGF4LCBlLmcuIFwiMzAwMHB4IDIwMDBweFwiXG5cblx0XHRcdC8vIEFtb3VudCBvZiBwaXhlbHMgdG8gbW92ZSB0aGUgcGFyYWxsYXggYmFja2dyb3VuZCBwZXIgc2xpZGUgc3RlcFxuXHRcdFx0cGFyYWxsYXhCYWNrZ3JvdW5kSG9yaXpvbnRhbDogbnVsbCxcblx0XHRcdHBhcmFsbGF4QmFja2dyb3VuZFZlcnRpY2FsOiBudWxsLFxuXG5cdFx0XHQvLyBUaGUgbWF4aW11bSBudW1iZXIgb2YgcGFnZXMgYSBzaW5nbGUgc2xpZGUgY2FuIGV4cGFuZCBvbnRvIHdoZW4gcHJpbnRpbmdcblx0XHRcdC8vIHRvIFBERiwgdW5saW1pdGVkIGJ5IGRlZmF1bHRcblx0XHRcdHBkZk1heFBhZ2VzUGVyU2xpZGU6IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSxcblxuXHRcdFx0Ly8gT2Zmc2V0IHVzZWQgdG8gcmVkdWNlIHRoZSBoZWlnaHQgb2YgY29udGVudCB3aXRoaW4gZXhwb3J0ZWQgUERGIHBhZ2VzLlxuXHRcdFx0Ly8gVGhpcyBleGlzdHMgdG8gYWNjb3VudCBmb3IgZW52aXJvbm1lbnQgZGlmZmVyZW5jZXMgYmFzZWQgb24gaG93IHlvdVxuXHRcdFx0Ly8gcHJpbnQgdG8gUERGLiBDTEkgcHJpbnRpbmcgb3B0aW9ucywgbGlrZSBwaGFudG9tanMgYW5kIHdrcGRmLCBjYW4gZW5kXG5cdFx0XHQvLyBvbiBwcmVjaXNlbHkgdGhlIHRvdGFsIGhlaWdodCBvZiB0aGUgZG9jdW1lbnQgd2hlcmVhcyBpbi1icm93c2VyXG5cdFx0XHQvLyBwcmludGluZyBoYXMgdG8gZW5kIG9uZSBwaXhlbCBiZWZvcmUuXG5cdFx0XHRwZGZQYWdlSGVpZ2h0T2Zmc2V0OiAtMSxcblxuXHRcdFx0Ly8gTnVtYmVyIG9mIHNsaWRlcyBhd2F5IGZyb20gdGhlIGN1cnJlbnQgdGhhdCBhcmUgdmlzaWJsZVxuXHRcdFx0dmlld0Rpc3RhbmNlOiAzLFxuXG5cdFx0XHQvLyBUaGUgZGlzcGxheSBtb2RlIHRoYXQgd2lsbCBiZSB1c2VkIHRvIHNob3cgc2xpZGVzXG5cdFx0XHRkaXNwbGF5OiAnYmxvY2snLFxuXG5cdFx0XHQvLyBTY3JpcHQgZGVwZW5kZW5jaWVzIHRvIGxvYWRcblx0XHRcdGRlcGVuZGVuY2llczogW11cblxuXHRcdH0sXG5cblx0XHQvLyBGbGFncyBpZiBSZXZlYWwuaW5pdGlhbGl6ZSgpIGhhcyBiZWVuIGNhbGxlZFxuXHRcdGluaXRpYWxpemVkID0gZmFsc2UsXG5cblx0XHQvLyBGbGFncyBpZiByZXZlYWwuanMgaXMgbG9hZGVkIChoYXMgZGlzcGF0Y2hlZCB0aGUgJ3JlYWR5JyBldmVudClcblx0XHRsb2FkZWQgPSBmYWxzZSxcblxuXHRcdC8vIEZsYWdzIGlmIHRoZSBvdmVydmlldyBtb2RlIGlzIGN1cnJlbnRseSBhY3RpdmVcblx0XHRvdmVydmlldyA9IGZhbHNlLFxuXG5cdFx0Ly8gSG9sZHMgdGhlIGRpbWVuc2lvbnMgb2Ygb3VyIG92ZXJ2aWV3IHNsaWRlcywgaW5jbHVkaW5nIG1hcmdpbnNcblx0XHRvdmVydmlld1NsaWRlV2lkdGggPSBudWxsLFxuXHRcdG92ZXJ2aWV3U2xpZGVIZWlnaHQgPSBudWxsLFxuXG5cdFx0Ly8gVGhlIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGluZGV4IG9mIHRoZSBjdXJyZW50bHkgYWN0aXZlIHNsaWRlXG5cdFx0aW5kZXhoLFxuXHRcdGluZGV4dixcblxuXHRcdC8vIFRoZSBwcmV2aW91cyBhbmQgY3VycmVudCBzbGlkZSBIVE1MIGVsZW1lbnRzXG5cdFx0cHJldmlvdXNTbGlkZSxcblx0XHRjdXJyZW50U2xpZGUsXG5cblx0XHRwcmV2aW91c0JhY2tncm91bmQsXG5cblx0XHQvLyBSZW1lbWJlciB3aGljaCBkaXJlY3Rpb25zIHRoYXQgdGhlIHVzZXIgaGFzIG5hdmlnYXRlZCB0b3dhcmRzXG5cdFx0aGFzTmF2aWdhdGVkUmlnaHQgPSBmYWxzZSxcblx0XHRoYXNOYXZpZ2F0ZWREb3duID0gZmFsc2UsXG5cblx0XHQvLyBTbGlkZXMgbWF5IGhvbGQgYSBkYXRhLXN0YXRlIGF0dHJpYnV0ZSB3aGljaCB3ZSBwaWNrIHVwIGFuZCBhcHBseVxuXHRcdC8vIGFzIGEgY2xhc3MgdG8gdGhlIGJvZHkuIFRoaXMgbGlzdCBjb250YWlucyB0aGUgY29tYmluZWQgc3RhdGUgb2Zcblx0XHQvLyBhbGwgY3VycmVudCBzbGlkZXMuXG5cdFx0c3RhdGUgPSBbXSxcblxuXHRcdC8vIFRoZSBjdXJyZW50IHNjYWxlIG9mIHRoZSBwcmVzZW50YXRpb24gKHNlZSB3aWR0aC9oZWlnaHQgY29uZmlnKVxuXHRcdHNjYWxlID0gMSxcblxuXHRcdC8vIENTUyB0cmFuc2Zvcm0gdGhhdCBpcyBjdXJyZW50bHkgYXBwbGllZCB0byB0aGUgc2xpZGVzIGNvbnRhaW5lcixcblx0XHQvLyBzcGxpdCBpbnRvIHR3byBncm91cHNcblx0XHRzbGlkZXNUcmFuc2Zvcm0gPSB7IGxheW91dDogJycsIG92ZXJ2aWV3OiAnJyB9LFxuXG5cdFx0Ly8gQ2FjaGVkIHJlZmVyZW5jZXMgdG8gRE9NIGVsZW1lbnRzXG5cdFx0ZG9tID0ge30sXG5cblx0XHQvLyBGZWF0dXJlcyBzdXBwb3J0ZWQgYnkgdGhlIGJyb3dzZXIsIHNlZSAjY2hlY2tDYXBhYmlsaXRpZXMoKVxuXHRcdGZlYXR1cmVzID0ge30sXG5cblx0XHQvLyBDbGllbnQgaXMgYSBtb2JpbGUgZGV2aWNlLCBzZWUgI2NoZWNrQ2FwYWJpbGl0aWVzKClcblx0XHRpc01vYmlsZURldmljZSxcblxuXHRcdC8vIENsaWVudCBpcyBhIGRlc2t0b3AgQ2hyb21lLCBzZWUgI2NoZWNrQ2FwYWJpbGl0aWVzKClcblx0XHRpc0Nocm9tZSxcblxuXHRcdC8vIFRocm90dGxlcyBtb3VzZSB3aGVlbCBuYXZpZ2F0aW9uXG5cdFx0bGFzdE1vdXNlV2hlZWxTdGVwID0gMCxcblxuXHRcdC8vIERlbGF5cyB1cGRhdGVzIHRvIHRoZSBVUkwgZHVlIHRvIGEgQ2hyb21lIHRodW1ibmFpbGVyIGJ1Z1xuXHRcdHdyaXRlVVJMVGltZW91dCA9IDAsXG5cblx0XHQvLyBGbGFncyBpZiB0aGUgaW50ZXJhY3Rpb24gZXZlbnQgbGlzdGVuZXJzIGFyZSBib3VuZFxuXHRcdGV2ZW50c0FyZUJvdW5kID0gZmFsc2UsXG5cblx0XHQvLyBUaGUgY3VycmVudCBhdXRvLXNsaWRlIGR1cmF0aW9uXG5cdFx0YXV0b1NsaWRlID0gMCxcblxuXHRcdC8vIEF1dG8gc2xpZGUgcHJvcGVydGllc1xuXHRcdGF1dG9TbGlkZVBsYXllcixcblx0XHRhdXRvU2xpZGVUaW1lb3V0ID0gMCxcblx0XHRhdXRvU2xpZGVTdGFydFRpbWUgPSAtMSxcblx0XHRhdXRvU2xpZGVQYXVzZWQgPSBmYWxzZSxcblxuXHRcdC8vIEhvbGRzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50bHkgb25nb2luZyB0b3VjaCBpbnB1dFxuXHRcdHRvdWNoID0ge1xuXHRcdFx0c3RhcnRYOiAwLFxuXHRcdFx0c3RhcnRZOiAwLFxuXHRcdFx0c3RhcnRTcGFuOiAwLFxuXHRcdFx0c3RhcnRDb3VudDogMCxcblx0XHRcdGNhcHR1cmVkOiBmYWxzZSxcblx0XHRcdHRocmVzaG9sZDogNDBcblx0XHR9LFxuXG5cdFx0Ly8gSG9sZHMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGtleWJvYXJkIHNob3J0Y3V0c1xuXHRcdGtleWJvYXJkU2hvcnRjdXRzID0ge1xuXHRcdFx0J04gICwgIFNQQUNFJzpcdFx0XHQnTmV4dCBzbGlkZScsXG5cdFx0XHQnUCc6XHRcdFx0XHRcdCdQcmV2aW91cyBzbGlkZScsXG5cdFx0XHQnJiM4NTkyOyAgLCAgSCc6XHRcdCdOYXZpZ2F0ZSBsZWZ0Jyxcblx0XHRcdCcmIzg1OTQ7ICAsICBMJzpcdFx0J05hdmlnYXRlIHJpZ2h0Jyxcblx0XHRcdCcmIzg1OTM7ICAsICBLJzpcdFx0J05hdmlnYXRlIHVwJyxcblx0XHRcdCcmIzg1OTU7ICAsICBKJzpcdFx0J05hdmlnYXRlIGRvd24nLFxuXHRcdFx0J0hvbWUnOlx0XHRcdFx0XHQnRmlyc3Qgc2xpZGUnLFxuXHRcdFx0J0VuZCc6XHRcdFx0XHRcdCdMYXN0IHNsaWRlJyxcblx0XHRcdCdCICAsICAuJzpcdFx0XHRcdCdQYXVzZScsXG5cdFx0XHQnRic6XHRcdFx0XHRcdCdGdWxsc2NyZWVuJyxcblx0XHRcdCdFU0MsIE8nOlx0XHRcdFx0J1NsaWRlIG92ZXJ2aWV3J1xuXHRcdH07XG5cblx0LyoqXG5cdCAqIFN0YXJ0cyB1cCB0aGUgcHJlc2VudGF0aW9uIGlmIHRoZSBjbGllbnQgaXMgY2FwYWJsZS5cblx0ICovXG5cdGZ1bmN0aW9uIGluaXRpYWxpemUoIG9wdGlvbnMgKSB7XG5cblx0XHQvLyBNYWtlIHN1cmUgd2Ugb25seSBpbml0aWFsaXplIG9uY2Vcblx0XHRpZiggaW5pdGlhbGl6ZWQgPT09IHRydWUgKSByZXR1cm47XG5cblx0XHRpbml0aWFsaXplZCA9IHRydWU7XG5cblx0XHRjaGVja0NhcGFiaWxpdGllcygpO1xuXG5cdFx0aWYoICFmZWF0dXJlcy50cmFuc2Zvcm1zMmQgJiYgIWZlYXR1cmVzLnRyYW5zZm9ybXMzZCApIHtcblx0XHRcdGRvY3VtZW50LmJvZHkuc2V0QXR0cmlidXRlKCAnY2xhc3MnLCAnbm8tdHJhbnNmb3JtcycgKTtcblxuXHRcdFx0Ly8gU2luY2UgSlMgd29uJ3QgYmUgcnVubmluZyBhbnkgZnVydGhlciwgd2UgbG9hZCBhbGwgbGF6eVxuXHRcdFx0Ly8gbG9hZGluZyBlbGVtZW50cyB1cGZyb250XG5cdFx0XHR2YXIgaW1hZ2VzID0gdG9BcnJheSggZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoICdpbWcnICkgKSxcblx0XHRcdFx0aWZyYW1lcyA9IHRvQXJyYXkoIGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCAnaWZyYW1lJyApICk7XG5cblx0XHRcdHZhciBsYXp5TG9hZGFibGUgPSBpbWFnZXMuY29uY2F0KCBpZnJhbWVzICk7XG5cblx0XHRcdGZvciggdmFyIGkgPSAwLCBsZW4gPSBsYXp5TG9hZGFibGUubGVuZ3RoOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdHZhciBlbGVtZW50ID0gbGF6eUxvYWRhYmxlW2ldO1xuXHRcdFx0XHRpZiggZWxlbWVudC5nZXRBdHRyaWJ1dGUoICdkYXRhLXNyYycgKSApIHtcblx0XHRcdFx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZSggJ3NyYycsIGVsZW1lbnQuZ2V0QXR0cmlidXRlKCAnZGF0YS1zcmMnICkgKTtcblx0XHRcdFx0XHRlbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSggJ2RhdGEtc3JjJyApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdC8vIElmIHRoZSBicm93c2VyIGRvZXNuJ3Qgc3VwcG9ydCBjb3JlIGZlYXR1cmVzIHdlIHdvbid0IGJlXG5cdFx0XHQvLyB1c2luZyBKYXZhU2NyaXB0IHRvIGNvbnRyb2wgdGhlIHByZXNlbnRhdGlvblxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIENhY2hlIHJlZmVyZW5jZXMgdG8ga2V5IERPTSBlbGVtZW50c1xuXHRcdGRvbS53cmFwcGVyID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5yZXZlYWwnICk7XG5cdFx0ZG9tLnNsaWRlcyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoICcucmV2ZWFsIC5zbGlkZXMnICk7XG5cblx0XHQvLyBGb3JjZSBhIGxheW91dCB3aGVuIHRoZSB3aG9sZSBwYWdlLCBpbmNsIGZvbnRzLCBoYXMgbG9hZGVkXG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoICdsb2FkJywgbGF5b3V0LCBmYWxzZSApO1xuXG5cdFx0dmFyIHF1ZXJ5ID0gUmV2ZWFsLmdldFF1ZXJ5SGFzaCgpO1xuXG5cdFx0Ly8gRG8gbm90IGFjY2VwdCBuZXcgZGVwZW5kZW5jaWVzIHZpYSBxdWVyeSBjb25maWcgdG8gYXZvaWRcblx0XHQvLyB0aGUgcG90ZW50aWFsIG9mIG1hbGljaW91cyBzY3JpcHQgaW5qZWN0aW9uXG5cdFx0aWYoIHR5cGVvZiBxdWVyeVsnZGVwZW5kZW5jaWVzJ10gIT09ICd1bmRlZmluZWQnICkgZGVsZXRlIHF1ZXJ5WydkZXBlbmRlbmNpZXMnXTtcblxuXHRcdC8vIENvcHkgb3B0aW9ucyBvdmVyIHRvIG91ciBjb25maWcgb2JqZWN0XG5cdFx0ZXh0ZW5kKCBjb25maWcsIG9wdGlvbnMgKTtcblx0XHRleHRlbmQoIGNvbmZpZywgcXVlcnkgKTtcblxuXHRcdC8vIEhpZGUgdGhlIGFkZHJlc3MgYmFyIGluIG1vYmlsZSBicm93c2Vyc1xuXHRcdGhpZGVBZGRyZXNzQmFyKCk7XG5cblx0XHQvLyBMb2FkcyB0aGUgZGVwZW5kZW5jaWVzIGFuZCBjb250aW51ZXMgdG8gI3N0YXJ0KCkgb25jZSBkb25lXG5cdFx0bG9hZCgpO1xuXG5cdH1cblxuXHQvKipcblx0ICogSW5zcGVjdCB0aGUgY2xpZW50IHRvIHNlZSB3aGF0IGl0J3MgY2FwYWJsZSBvZiwgdGhpc1xuXHQgKiBzaG91bGQgb25seSBoYXBwZW5zIG9uY2UgcGVyIHJ1bnRpbWUuXG5cdCAqL1xuXHRmdW5jdGlvbiBjaGVja0NhcGFiaWxpdGllcygpIHtcblxuXHRcdGlzTW9iaWxlRGV2aWNlID0gLyhpcGhvbmV8aXBvZHxpcGFkfGFuZHJvaWQpL2dpLnRlc3QoIFVBICk7XG5cdFx0aXNDaHJvbWUgPSAvY2hyb21lL2kudGVzdCggVUEgKSAmJiAhL2VkZ2UvaS50ZXN0KCBVQSApO1xuXG5cdFx0dmFyIHRlc3RFbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblxuXHRcdGZlYXR1cmVzLnRyYW5zZm9ybXMzZCA9ICdXZWJraXRQZXJzcGVjdGl2ZScgaW4gdGVzdEVsZW1lbnQuc3R5bGUgfHxcblx0XHRcdFx0XHRcdFx0XHQnTW96UGVyc3BlY3RpdmUnIGluIHRlc3RFbGVtZW50LnN0eWxlIHx8XG5cdFx0XHRcdFx0XHRcdFx0J21zUGVyc3BlY3RpdmUnIGluIHRlc3RFbGVtZW50LnN0eWxlIHx8XG5cdFx0XHRcdFx0XHRcdFx0J09QZXJzcGVjdGl2ZScgaW4gdGVzdEVsZW1lbnQuc3R5bGUgfHxcblx0XHRcdFx0XHRcdFx0XHQncGVyc3BlY3RpdmUnIGluIHRlc3RFbGVtZW50LnN0eWxlO1xuXG5cdFx0ZmVhdHVyZXMudHJhbnNmb3JtczJkID0gJ1dlYmtpdFRyYW5zZm9ybScgaW4gdGVzdEVsZW1lbnQuc3R5bGUgfHxcblx0XHRcdFx0XHRcdFx0XHQnTW96VHJhbnNmb3JtJyBpbiB0ZXN0RWxlbWVudC5zdHlsZSB8fFxuXHRcdFx0XHRcdFx0XHRcdCdtc1RyYW5zZm9ybScgaW4gdGVzdEVsZW1lbnQuc3R5bGUgfHxcblx0XHRcdFx0XHRcdFx0XHQnT1RyYW5zZm9ybScgaW4gdGVzdEVsZW1lbnQuc3R5bGUgfHxcblx0XHRcdFx0XHRcdFx0XHQndHJhbnNmb3JtJyBpbiB0ZXN0RWxlbWVudC5zdHlsZTtcblxuXHRcdGZlYXR1cmVzLnJlcXVlc3RBbmltYXRpb25GcmFtZU1ldGhvZCA9IHdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgfHwgd2luZG93LndlYmtpdFJlcXVlc3RBbmltYXRpb25GcmFtZSB8fCB3aW5kb3cubW96UmVxdWVzdEFuaW1hdGlvbkZyYW1lO1xuXHRcdGZlYXR1cmVzLnJlcXVlc3RBbmltYXRpb25GcmFtZSA9IHR5cGVvZiBmZWF0dXJlcy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWVNZXRob2QgPT09ICdmdW5jdGlvbic7XG5cblx0XHRmZWF0dXJlcy5jYW52YXMgPSAhIWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdjYW52YXMnICkuZ2V0Q29udGV4dDtcblxuXHRcdC8vIFRyYW5zaXRpb25zIGluIHRoZSBvdmVydmlldyBhcmUgZGlzYWJsZWQgaW4gZGVza3RvcCBhbmRcblx0XHQvLyBTYWZhcmkgZHVlIHRvIGxhZ1xuXHRcdGZlYXR1cmVzLm92ZXJ2aWV3VHJhbnNpdGlvbnMgPSAhL1ZlcnNpb25cXC9bXFxkXFwuXSsuKlNhZmFyaS8udGVzdCggVUEgKTtcblxuXHRcdC8vIEZsYWdzIGlmIHdlIHNob3VsZCB1c2Ugem9vbSBpbnN0ZWFkIG9mIHRyYW5zZm9ybSB0byBzY2FsZVxuXHRcdC8vIHVwIHNsaWRlcy4gWm9vbSBwcm9kdWNlcyBjcmlzcGVyIHJlc3VsdHMgYnV0IGhhcyBhIGxvdCBvZlxuXHRcdC8vIHhicm93c2VyIHF1aXJrcyBzbyB3ZSBvbmx5IHVzZSBpdCBpbiB3aGl0ZWxzaXRlZCBicm93c2Vycy5cblx0XHRmZWF0dXJlcy56b29tID0gJ3pvb20nIGluIHRlc3RFbGVtZW50LnN0eWxlICYmICFpc01vYmlsZURldmljZSAmJlxuXHRcdFx0XHRcdFx0KCBpc0Nocm9tZSB8fCAvVmVyc2lvblxcL1tcXGRcXC5dKy4qU2FmYXJpLy50ZXN0KCBVQSApICk7XG5cblx0fVxuXG4gICAgLyoqXG4gICAgICogTG9hZHMgdGhlIGRlcGVuZGVuY2llcyBvZiByZXZlYWwuanMuIERlcGVuZGVuY2llcyBhcmVcbiAgICAgKiBkZWZpbmVkIHZpYSB0aGUgY29uZmlndXJhdGlvbiBvcHRpb24gJ2RlcGVuZGVuY2llcydcbiAgICAgKiBhbmQgd2lsbCBiZSBsb2FkZWQgcHJpb3IgdG8gc3RhcnRpbmcvYmluZGluZyByZXZlYWwuanMuXG4gICAgICogU29tZSBkZXBlbmRlbmNpZXMgbWF5IGhhdmUgYW4gJ2FzeW5jJyBmbGFnLCBpZiBzbyB0aGV5XG4gICAgICogd2lsbCBsb2FkIGFmdGVyIHJldmVhbC5qcyBoYXMgYmVlbiBzdGFydGVkIHVwLlxuICAgICAqL1xuXHRmdW5jdGlvbiBsb2FkKCkge1xuXG5cdFx0dmFyIHNjcmlwdHMgPSBbXSxcblx0XHRcdHNjcmlwdHNBc3luYyA9IFtdLFxuXHRcdFx0c2NyaXB0c1RvUHJlbG9hZCA9IDA7XG5cblx0XHQvLyBDYWxsZWQgb25jZSBzeW5jaHJvbm91cyBzY3JpcHRzIGZpbmlzaCBsb2FkaW5nXG5cdFx0ZnVuY3Rpb24gcHJvY2VlZCgpIHtcblx0XHRcdGlmKCBzY3JpcHRzQXN5bmMubGVuZ3RoICkge1xuXHRcdFx0XHQvLyBMb2FkIGFzeW5jaHJvbm91cyBzY3JpcHRzXG5cdFx0XHRcdGhlYWQuanMuYXBwbHkoIG51bGwsIHNjcmlwdHNBc3luYyApO1xuXHRcdFx0fVxuXG5cdFx0XHRzdGFydCgpO1xuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIGxvYWRTY3JpcHQoIHMgKSB7XG5cdFx0XHRoZWFkLnJlYWR5KCBzLnNyYy5tYXRjaCggLyhbXFx3XFxkX1xcLV0qKVxcLj9qcyR8W15cXFxcXFwvXSokL2kgKVswXSwgZnVuY3Rpb24oKSB7XG5cdFx0XHRcdC8vIEV4dGVuc2lvbiBtYXkgY29udGFpbiBjYWxsYmFjayBmdW5jdGlvbnNcblx0XHRcdFx0aWYoIHR5cGVvZiBzLmNhbGxiYWNrID09PSAnZnVuY3Rpb24nICkge1xuXHRcdFx0XHRcdHMuY2FsbGJhY2suYXBwbHkoIHRoaXMgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCAtLXNjcmlwdHNUb1ByZWxvYWQgPT09IDAgKSB7XG5cdFx0XHRcdFx0cHJvY2VlZCgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblx0XHR9XG5cblx0XHRmb3IoIHZhciBpID0gMCwgbGVuID0gY29uZmlnLmRlcGVuZGVuY2llcy5sZW5ndGg7IGkgPCBsZW47IGkrKyApIHtcblx0XHRcdHZhciBzID0gY29uZmlnLmRlcGVuZGVuY2llc1tpXTtcblxuXHRcdFx0Ly8gTG9hZCBpZiB0aGVyZSdzIG5vIGNvbmRpdGlvbiBvciB0aGUgY29uZGl0aW9uIGlzIHRydXRoeVxuXHRcdFx0aWYoICFzLmNvbmRpdGlvbiB8fCBzLmNvbmRpdGlvbigpICkge1xuXHRcdFx0XHRpZiggcy5hc3luYyApIHtcblx0XHRcdFx0XHRzY3JpcHRzQXN5bmMucHVzaCggcy5zcmMgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRzY3JpcHRzLnB1c2goIHMuc3JjICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRsb2FkU2NyaXB0KCBzICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYoIHNjcmlwdHMubGVuZ3RoICkge1xuXHRcdFx0c2NyaXB0c1RvUHJlbG9hZCA9IHNjcmlwdHMubGVuZ3RoO1xuXG5cdFx0XHQvLyBMb2FkIHN5bmNocm9ub3VzIHNjcmlwdHNcblx0XHRcdGhlYWQuanMuYXBwbHkoIG51bGwsIHNjcmlwdHMgKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRwcm9jZWVkKCk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogU3RhcnRzIHVwIHJldmVhbC5qcyBieSBiaW5kaW5nIGlucHV0IGV2ZW50cyBhbmQgbmF2aWdhdGluZ1xuXHQgKiB0byB0aGUgY3VycmVudCBVUkwgZGVlcGxpbmsgaWYgdGhlcmUgaXMgb25lLlxuXHQgKi9cblx0ZnVuY3Rpb24gc3RhcnQoKSB7XG5cblx0XHRsb2FkZWQgPSB0cnVlO1xuXG5cdFx0Ly8gTWFrZSBzdXJlIHdlJ3ZlIGdvdCBhbGwgdGhlIERPTSBlbGVtZW50cyB3ZSBuZWVkXG5cdFx0c2V0dXBET00oKTtcblxuXHRcdC8vIExpc3RlbiB0byBtZXNzYWdlcyBwb3N0ZWQgdG8gdGhpcyB3aW5kb3dcblx0XHRzZXR1cFBvc3RNZXNzYWdlKCk7XG5cblx0XHQvLyBQcmV2ZW50IHRoZSBzbGlkZXMgZnJvbSBiZWluZyBzY3JvbGxlZCBvdXQgb2Ygdmlld1xuXHRcdHNldHVwU2Nyb2xsUHJldmVudGlvbigpO1xuXG5cdFx0Ly8gUmVzZXRzIGFsbCB2ZXJ0aWNhbCBzbGlkZXMgc28gdGhhdCBvbmx5IHRoZSBmaXJzdCBpcyB2aXNpYmxlXG5cdFx0cmVzZXRWZXJ0aWNhbFNsaWRlcygpO1xuXG5cdFx0Ly8gVXBkYXRlcyB0aGUgcHJlc2VudGF0aW9uIHRvIG1hdGNoIHRoZSBjdXJyZW50IGNvbmZpZ3VyYXRpb24gdmFsdWVzXG5cdFx0Y29uZmlndXJlKCk7XG5cblx0XHQvLyBSZWFkIHRoZSBpbml0aWFsIGhhc2hcblx0XHRyZWFkVVJMKCk7XG5cblx0XHQvLyBVcGRhdGUgYWxsIGJhY2tncm91bmRzXG5cdFx0dXBkYXRlQmFja2dyb3VuZCggdHJ1ZSApO1xuXG5cdFx0Ly8gTm90aWZ5IGxpc3RlbmVycyB0aGF0IHRoZSBwcmVzZW50YXRpb24gaXMgcmVhZHkgYnV0IHVzZSBhIDFtc1xuXHRcdC8vIHRpbWVvdXQgdG8gZW5zdXJlIGl0J3Mgbm90IGZpcmVkIHN5bmNocm9ub3VzbHkgYWZ0ZXIgI2luaXRpYWxpemUoKVxuXHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uKCkge1xuXHRcdFx0Ly8gRW5hYmxlIHRyYW5zaXRpb25zIG5vdyB0aGF0IHdlJ3JlIGxvYWRlZFxuXHRcdFx0ZG9tLnNsaWRlcy5jbGFzc0xpc3QucmVtb3ZlKCAnbm8tdHJhbnNpdGlvbicgKTtcblxuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ3JlYWR5JyApO1xuXG5cdFx0XHRkaXNwYXRjaEV2ZW50KCAncmVhZHknLCB7XG5cdFx0XHRcdCdpbmRleGgnOiBpbmRleGgsXG5cdFx0XHRcdCdpbmRleHYnOiBpbmRleHYsXG5cdFx0XHRcdCdjdXJyZW50U2xpZGUnOiBjdXJyZW50U2xpZGVcblx0XHRcdH0gKTtcblx0XHR9LCAxICk7XG5cblx0XHQvLyBTcGVjaWFsIHNldHVwIGFuZCBjb25maWcgaXMgcmVxdWlyZWQgd2hlbiBwcmludGluZyB0byBQREZcblx0XHRpZiggaXNQcmludGluZ1BERigpICkge1xuXHRcdFx0cmVtb3ZlRXZlbnRMaXN0ZW5lcnMoKTtcblxuXHRcdFx0Ly8gVGhlIGRvY3VtZW50IG5lZWRzIHRvIGhhdmUgbG9hZGVkIGZvciB0aGUgUERGIGxheW91dFxuXHRcdFx0Ly8gbWVhc3VyZW1lbnRzIHRvIGJlIGFjY3VyYXRlXG5cdFx0XHRpZiggZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2NvbXBsZXRlJyApIHtcblx0XHRcdFx0c2V0dXBQREYoKTtcblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lciggJ2xvYWQnLCBzZXR1cFBERiApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEZpbmRzIGFuZCBzdG9yZXMgcmVmZXJlbmNlcyB0byBET00gZWxlbWVudHMgd2hpY2ggYXJlXG5cdCAqIHJlcXVpcmVkIGJ5IHRoZSBwcmVzZW50YXRpb24uIElmIGEgcmVxdWlyZWQgZWxlbWVudCBpc1xuXHQgKiBub3QgZm91bmQsIGl0IGlzIGNyZWF0ZWQuXG5cdCAqL1xuXHRmdW5jdGlvbiBzZXR1cERPTSgpIHtcblxuXHRcdC8vIFByZXZlbnQgdHJhbnNpdGlvbnMgd2hpbGUgd2UncmUgbG9hZGluZ1xuXHRcdGRvbS5zbGlkZXMuY2xhc3NMaXN0LmFkZCggJ25vLXRyYW5zaXRpb24nICk7XG5cblx0XHRpZiggaXNNb2JpbGVEZXZpY2UgKSB7XG5cdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QuYWRkKCAnbm8taG92ZXInICk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ25vLWhvdmVyJyApO1xuXHRcdH1cblxuXHRcdGlmKCAvaXBob25lL2dpLnRlc3QoIFVBICkgKSB7XG5cdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QuYWRkKCAndWEtaXBob25lJyApO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoICd1YS1pcGhvbmUnICk7XG5cdFx0fVxuXG5cdFx0Ly8gQmFja2dyb3VuZCBlbGVtZW50XG5cdFx0ZG9tLmJhY2tncm91bmQgPSBjcmVhdGVTaW5nbGV0b25Ob2RlKCBkb20ud3JhcHBlciwgJ2RpdicsICdiYWNrZ3JvdW5kcycsIG51bGwgKTtcblxuXHRcdC8vIFByb2dyZXNzIGJhclxuXHRcdGRvbS5wcm9ncmVzcyA9IGNyZWF0ZVNpbmdsZXRvbk5vZGUoIGRvbS53cmFwcGVyLCAnZGl2JywgJ3Byb2dyZXNzJywgJzxzcGFuPjwvc3Bhbj4nICk7XG5cdFx0ZG9tLnByb2dyZXNzYmFyID0gZG9tLnByb2dyZXNzLnF1ZXJ5U2VsZWN0b3IoICdzcGFuJyApO1xuXG5cdFx0Ly8gQXJyb3cgY29udHJvbHNcblx0XHRkb20uY29udHJvbHMgPSBjcmVhdGVTaW5nbGV0b25Ob2RlKCBkb20ud3JhcHBlciwgJ2FzaWRlJywgJ2NvbnRyb2xzJyxcblx0XHRcdCc8YnV0dG9uIGNsYXNzPVwibmF2aWdhdGUtbGVmdFwiIGFyaWEtbGFiZWw9XCJwcmV2aW91cyBzbGlkZVwiPjxkaXYgY2xhc3M9XCJjb250cm9scy1hcnJvd1wiPjwvZGl2PjwvYnV0dG9uPicgK1xuXHRcdFx0JzxidXR0b24gY2xhc3M9XCJuYXZpZ2F0ZS1yaWdodFwiIGFyaWEtbGFiZWw9XCJuZXh0IHNsaWRlXCI+PGRpdiBjbGFzcz1cImNvbnRyb2xzLWFycm93XCI+PC9kaXY+PC9idXR0b24+JyArXG5cdFx0XHQnPGJ1dHRvbiBjbGFzcz1cIm5hdmlnYXRlLXVwXCIgYXJpYS1sYWJlbD1cImFib3ZlIHNsaWRlXCI+PGRpdiBjbGFzcz1cImNvbnRyb2xzLWFycm93XCI+PC9kaXY+PC9idXR0b24+JyArXG5cdFx0XHQnPGJ1dHRvbiBjbGFzcz1cIm5hdmlnYXRlLWRvd25cIiBhcmlhLWxhYmVsPVwiYmVsb3cgc2xpZGVcIj48ZGl2IGNsYXNzPVwiY29udHJvbHMtYXJyb3dcIj48L2Rpdj48L2J1dHRvbj4nICk7XG5cblx0XHQvLyBTbGlkZSBudW1iZXJcblx0XHRkb20uc2xpZGVOdW1iZXIgPSBjcmVhdGVTaW5nbGV0b25Ob2RlKCBkb20ud3JhcHBlciwgJ2RpdicsICdzbGlkZS1udW1iZXInLCAnJyApO1xuXG5cdFx0Ly8gRWxlbWVudCBjb250YWluaW5nIG5vdGVzIHRoYXQgYXJlIHZpc2libGUgdG8gdGhlIGF1ZGllbmNlXG5cdFx0ZG9tLnNwZWFrZXJOb3RlcyA9IGNyZWF0ZVNpbmdsZXRvbk5vZGUoIGRvbS53cmFwcGVyLCAnZGl2JywgJ3NwZWFrZXItbm90ZXMnLCBudWxsICk7XG5cdFx0ZG9tLnNwZWFrZXJOb3Rlcy5zZXRBdHRyaWJ1dGUoICdkYXRhLXByZXZlbnQtc3dpcGUnLCAnJyApO1xuXHRcdGRvbS5zcGVha2VyTm90ZXMuc2V0QXR0cmlidXRlKCAndGFiaW5kZXgnLCAnMCcgKTtcblxuXHRcdC8vIE92ZXJsYXkgZ3JhcGhpYyB3aGljaCBpcyBkaXNwbGF5ZWQgZHVyaW5nIHRoZSBwYXVzZWQgbW9kZVxuXHRcdGNyZWF0ZVNpbmdsZXRvbk5vZGUoIGRvbS53cmFwcGVyLCAnZGl2JywgJ3BhdXNlLW92ZXJsYXknLCBudWxsICk7XG5cblx0XHRkb20ud3JhcHBlci5zZXRBdHRyaWJ1dGUoICdyb2xlJywgJ2FwcGxpY2F0aW9uJyApO1xuXG5cdFx0Ly8gVGhlcmUgY2FuIGJlIG11bHRpcGxlIGluc3RhbmNlcyBvZiBjb250cm9scyB0aHJvdWdob3V0IHRoZSBwYWdlXG5cdFx0ZG9tLmNvbnRyb2xzTGVmdCA9IHRvQXJyYXkoIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICcubmF2aWdhdGUtbGVmdCcgKSApO1xuXHRcdGRvbS5jb250cm9sc1JpZ2h0ID0gdG9BcnJheSggZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJy5uYXZpZ2F0ZS1yaWdodCcgKSApO1xuXHRcdGRvbS5jb250cm9sc1VwID0gdG9BcnJheSggZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJy5uYXZpZ2F0ZS11cCcgKSApO1xuXHRcdGRvbS5jb250cm9sc0Rvd24gPSB0b0FycmF5KCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnLm5hdmlnYXRlLWRvd24nICkgKTtcblx0XHRkb20uY29udHJvbHNQcmV2ID0gdG9BcnJheSggZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggJy5uYXZpZ2F0ZS1wcmV2JyApICk7XG5cdFx0ZG9tLmNvbnRyb2xzTmV4dCA9IHRvQXJyYXkoIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICcubmF2aWdhdGUtbmV4dCcgKSApO1xuXG5cdFx0Ly8gVGhlIHJpZ2h0IGFuZCBkb3duIGFycm93cyBpbiB0aGUgc3RhbmRhcmQgcmV2ZWFsLmpzIGNvbnRyb2xzXG5cdFx0ZG9tLmNvbnRyb2xzUmlnaHRBcnJvdyA9IGRvbS5jb250cm9scy5xdWVyeVNlbGVjdG9yKCAnLm5hdmlnYXRlLXJpZ2h0JyApO1xuXHRcdGRvbS5jb250cm9sc0Rvd25BcnJvdyA9IGRvbS5jb250cm9scy5xdWVyeVNlbGVjdG9yKCAnLm5hdmlnYXRlLWRvd24nICk7XG5cblx0XHRkb20uc3RhdHVzRGl2ID0gY3JlYXRlU3RhdHVzRGl2KCk7XG5cdH1cblxuXHQvKipcblx0ICogQ3JlYXRlcyBhIGhpZGRlbiBkaXYgd2l0aCByb2xlIGFyaWEtbGl2ZSB0byBhbm5vdW5jZSB0aGVcblx0ICogY3VycmVudCBzbGlkZSBjb250ZW50LiBIaWRlIHRoZSBkaXYgb2ZmLXNjcmVlbiB0byBtYWtlIGl0XG5cdCAqIGF2YWlsYWJsZSBvbmx5IHRvIEFzc2lzdGl2ZSBUZWNobm9sb2dpZXMuXG5cdCAqXG5cdCAqIEByZXR1cm4ge0hUTUxFbGVtZW50fVxuXHQgKi9cblx0ZnVuY3Rpb24gY3JlYXRlU3RhdHVzRGl2KCkge1xuXG5cdFx0dmFyIHN0YXR1c0RpdiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCAnYXJpYS1zdGF0dXMtZGl2JyApO1xuXHRcdGlmKCAhc3RhdHVzRGl2ICkge1xuXHRcdFx0c3RhdHVzRGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblx0XHRcdHN0YXR1c0Rpdi5zdHlsZS5wb3NpdGlvbiA9ICdhYnNvbHV0ZSc7XG5cdFx0XHRzdGF0dXNEaXYuc3R5bGUuaGVpZ2h0ID0gJzFweCc7XG5cdFx0XHRzdGF0dXNEaXYuc3R5bGUud2lkdGggPSAnMXB4Jztcblx0XHRcdHN0YXR1c0Rpdi5zdHlsZS5vdmVyZmxvdyA9ICdoaWRkZW4nO1xuXHRcdFx0c3RhdHVzRGl2LnN0eWxlLmNsaXAgPSAncmVjdCggMXB4LCAxcHgsIDFweCwgMXB4ICknO1xuXHRcdFx0c3RhdHVzRGl2LnNldEF0dHJpYnV0ZSggJ2lkJywgJ2FyaWEtc3RhdHVzLWRpdicgKTtcblx0XHRcdHN0YXR1c0Rpdi5zZXRBdHRyaWJ1dGUoICdhcmlhLWxpdmUnLCAncG9saXRlJyApO1xuXHRcdFx0c3RhdHVzRGl2LnNldEF0dHJpYnV0ZSggJ2FyaWEtYXRvbWljJywndHJ1ZScgKTtcblx0XHRcdGRvbS53cmFwcGVyLmFwcGVuZENoaWxkKCBzdGF0dXNEaXYgKTtcblx0XHR9XG5cdFx0cmV0dXJuIHN0YXR1c0RpdjtcblxuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlcnRzIHRoZSBnaXZlbiBIVE1MIGVsZW1lbnQgaW50byBhIHN0cmluZyBvZiB0ZXh0XG5cdCAqIHRoYXQgY2FuIGJlIGFubm91bmNlZCB0byBhIHNjcmVlbiByZWFkZXIuIEhpZGRlblxuXHQgKiBlbGVtZW50cyBhcmUgZXhjbHVkZWQuXG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRTdGF0dXNUZXh0KCBub2RlICkge1xuXG5cdFx0dmFyIHRleHQgPSAnJztcblxuXHRcdC8vIFRleHQgbm9kZVxuXHRcdGlmKCBub2RlLm5vZGVUeXBlID09PSAzICkge1xuXHRcdFx0dGV4dCArPSBub2RlLnRleHRDb250ZW50O1xuXHRcdH1cblx0XHQvLyBFbGVtZW50IG5vZGVcblx0XHRlbHNlIGlmKCBub2RlLm5vZGVUeXBlID09PSAxICkge1xuXG5cdFx0XHR2YXIgaXNBcmlhSGlkZGVuID0gbm9kZS5nZXRBdHRyaWJ1dGUoICdhcmlhLWhpZGRlbicgKTtcblx0XHRcdHZhciBpc0Rpc3BsYXlIaWRkZW4gPSB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSggbm9kZSApWydkaXNwbGF5J10gPT09ICdub25lJztcblx0XHRcdGlmKCBpc0FyaWFIaWRkZW4gIT09ICd0cnVlJyAmJiAhaXNEaXNwbGF5SGlkZGVuICkge1xuXG5cdFx0XHRcdHRvQXJyYXkoIG5vZGUuY2hpbGROb2RlcyApLmZvckVhY2goIGZ1bmN0aW9uKCBjaGlsZCApIHtcblx0XHRcdFx0XHR0ZXh0ICs9IGdldFN0YXR1c1RleHQoIGNoaWxkICk7XG5cdFx0XHRcdH0gKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRleHQ7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDb25maWd1cmVzIHRoZSBwcmVzZW50YXRpb24gZm9yIHByaW50aW5nIHRvIGEgc3RhdGljXG5cdCAqIFBERi5cblx0ICovXG5cdGZ1bmN0aW9uIHNldHVwUERGKCkge1xuXG5cdFx0dmFyIHNsaWRlU2l6ZSA9IGdldENvbXB1dGVkU2xpZGVTaXplKCB3aW5kb3cuaW5uZXJXaWR0aCwgd2luZG93LmlubmVySGVpZ2h0ICk7XG5cblx0XHQvLyBEaW1lbnNpb25zIG9mIHRoZSBQREYgcGFnZXNcblx0XHR2YXIgcGFnZVdpZHRoID0gTWF0aC5mbG9vciggc2xpZGVTaXplLndpZHRoICogKCAxICsgY29uZmlnLm1hcmdpbiApICksXG5cdFx0XHRwYWdlSGVpZ2h0ID0gTWF0aC5mbG9vciggc2xpZGVTaXplLmhlaWdodCAqICggMSArIGNvbmZpZy5tYXJnaW4gKSApO1xuXG5cdFx0Ly8gRGltZW5zaW9ucyBvZiBzbGlkZXMgd2l0aGluIHRoZSBwYWdlc1xuXHRcdHZhciBzbGlkZVdpZHRoID0gc2xpZGVTaXplLndpZHRoLFxuXHRcdFx0c2xpZGVIZWlnaHQgPSBzbGlkZVNpemUuaGVpZ2h0O1xuXG5cdFx0Ly8gTGV0IHRoZSBicm93c2VyIGtub3cgd2hhdCBwYWdlIHNpemUgd2Ugd2FudCB0byBwcmludFxuXHRcdGluamVjdFN0eWxlU2hlZXQoICdAcGFnZXtzaXplOicrIHBhZ2VXaWR0aCArJ3B4ICcrIHBhZ2VIZWlnaHQgKydweDsgbWFyZ2luOiAwcHg7fScgKTtcblxuXHRcdC8vIExpbWl0IHRoZSBzaXplIG9mIGNlcnRhaW4gZWxlbWVudHMgdG8gdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHNsaWRlXG5cdFx0aW5qZWN0U3R5bGVTaGVldCggJy5yZXZlYWwgc2VjdGlvbj5pbWcsIC5yZXZlYWwgc2VjdGlvbj52aWRlbywgLnJldmVhbCBzZWN0aW9uPmlmcmFtZXttYXgtd2lkdGg6ICcrIHNsaWRlV2lkdGggKydweDsgbWF4LWhlaWdodDonKyBzbGlkZUhlaWdodCArJ3B4fScgKTtcblxuXHRcdGRvY3VtZW50LmJvZHkuY2xhc3NMaXN0LmFkZCggJ3ByaW50LXBkZicgKTtcblx0XHRkb2N1bWVudC5ib2R5LnN0eWxlLndpZHRoID0gcGFnZVdpZHRoICsgJ3B4Jztcblx0XHRkb2N1bWVudC5ib2R5LnN0eWxlLmhlaWdodCA9IHBhZ2VIZWlnaHQgKyAncHgnO1xuXG5cdFx0Ly8gTWFrZSBzdXJlIHN0cmV0Y2ggZWxlbWVudHMgZml0IG9uIHNsaWRlXG5cdFx0bGF5b3V0U2xpZGVDb250ZW50cyggc2xpZGVXaWR0aCwgc2xpZGVIZWlnaHQgKTtcblxuXHRcdC8vIEFkZCBlYWNoIHNsaWRlJ3MgaW5kZXggYXMgYXR0cmlidXRlcyBvbiBpdHNlbGYsIHdlIG5lZWQgdGhlc2Vcblx0XHQvLyBpbmRpY2VzIHRvIGdlbmVyYXRlIHNsaWRlIG51bWJlcnMgYmVsb3dcblx0XHR0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApICkuZm9yRWFjaCggZnVuY3Rpb24oIGhzbGlkZSwgaCApIHtcblx0XHRcdGhzbGlkZS5zZXRBdHRyaWJ1dGUoICdkYXRhLWluZGV4LWgnLCBoICk7XG5cblx0XHRcdGlmKCBoc2xpZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnc3RhY2snICkgKSB7XG5cdFx0XHRcdHRvQXJyYXkoIGhzbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnc2VjdGlvbicgKSApLmZvckVhY2goIGZ1bmN0aW9uKCB2c2xpZGUsIHYgKSB7XG5cdFx0XHRcdFx0dnNsaWRlLnNldEF0dHJpYnV0ZSggJ2RhdGEtaW5kZXgtaCcsIGggKTtcblx0XHRcdFx0XHR2c2xpZGUuc2V0QXR0cmlidXRlKCAnZGF0YS1pbmRleC12JywgdiApO1xuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cdFx0Ly8gU2xpZGUgYW5kIHNsaWRlIGJhY2tncm91bmQgbGF5b3V0XG5cdFx0dG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggc2xpZGUgKSB7XG5cblx0XHRcdC8vIFZlcnRpY2FsIHN0YWNrcyBhcmUgbm90IGNlbnRyZWQgc2luY2UgdGhlaXIgc2VjdGlvblxuXHRcdFx0Ly8gY2hpbGRyZW4gd2lsbCBiZVxuXHRcdFx0aWYoIHNsaWRlLmNsYXNzTGlzdC5jb250YWlucyggJ3N0YWNrJyApID09PSBmYWxzZSApIHtcblx0XHRcdFx0Ly8gQ2VudGVyIHRoZSBzbGlkZSBpbnNpZGUgb2YgdGhlIHBhZ2UsIGdpdmluZyB0aGUgc2xpZGUgc29tZSBtYXJnaW5cblx0XHRcdFx0dmFyIGxlZnQgPSAoIHBhZ2VXaWR0aCAtIHNsaWRlV2lkdGggKSAvIDIsXG5cdFx0XHRcdFx0dG9wID0gKCBwYWdlSGVpZ2h0IC0gc2xpZGVIZWlnaHQgKSAvIDI7XG5cblx0XHRcdFx0dmFyIGNvbnRlbnRIZWlnaHQgPSBzbGlkZS5zY3JvbGxIZWlnaHQ7XG5cdFx0XHRcdHZhciBudW1iZXJPZlBhZ2VzID0gTWF0aC5tYXgoIE1hdGguY2VpbCggY29udGVudEhlaWdodCAvIHBhZ2VIZWlnaHQgKSwgMSApO1xuXG5cdFx0XHRcdC8vIEFkaGVyZSB0byBjb25maWd1cmVkIHBhZ2VzIHBlciBzbGlkZSBsaW1pdFxuXHRcdFx0XHRudW1iZXJPZlBhZ2VzID0gTWF0aC5taW4oIG51bWJlck9mUGFnZXMsIGNvbmZpZy5wZGZNYXhQYWdlc1BlclNsaWRlICk7XG5cblx0XHRcdFx0Ly8gQ2VudGVyIHNsaWRlcyB2ZXJ0aWNhbGx5XG5cdFx0XHRcdGlmKCBudW1iZXJPZlBhZ2VzID09PSAxICYmIGNvbmZpZy5jZW50ZXIgfHwgc2xpZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnY2VudGVyJyApICkge1xuXHRcdFx0XHRcdHRvcCA9IE1hdGgubWF4KCAoIHBhZ2VIZWlnaHQgLSBjb250ZW50SGVpZ2h0ICkgLyAyLCAwICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBXcmFwIHRoZSBzbGlkZSBpbiBhIHBhZ2UgZWxlbWVudCBhbmQgaGlkZSBpdHMgb3ZlcmZsb3dcblx0XHRcdFx0Ly8gc28gdGhhdCBubyBwYWdlIGV2ZXIgZmxvd3Mgb250byBhbm90aGVyXG5cdFx0XHRcdHZhciBwYWdlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblx0XHRcdFx0cGFnZS5jbGFzc05hbWUgPSAncGRmLXBhZ2UnO1xuXHRcdFx0XHRwYWdlLnN0eWxlLmhlaWdodCA9ICggKCBwYWdlSGVpZ2h0ICsgY29uZmlnLnBkZlBhZ2VIZWlnaHRPZmZzZXQgKSAqIG51bWJlck9mUGFnZXMgKSArICdweCc7XG5cdFx0XHRcdHNsaWRlLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKCBwYWdlLCBzbGlkZSApO1xuXHRcdFx0XHRwYWdlLmFwcGVuZENoaWxkKCBzbGlkZSApO1xuXG5cdFx0XHRcdC8vIFBvc2l0aW9uIHRoZSBzbGlkZSBpbnNpZGUgb2YgdGhlIHBhZ2Vcblx0XHRcdFx0c2xpZGUuc3R5bGUubGVmdCA9IGxlZnQgKyAncHgnO1xuXHRcdFx0XHRzbGlkZS5zdHlsZS50b3AgPSB0b3AgKyAncHgnO1xuXHRcdFx0XHRzbGlkZS5zdHlsZS53aWR0aCA9IHNsaWRlV2lkdGggKyAncHgnO1xuXG5cdFx0XHRcdGlmKCBzbGlkZS5zbGlkZUJhY2tncm91bmRFbGVtZW50ICkge1xuXHRcdFx0XHRcdHBhZ2UuaW5zZXJ0QmVmb3JlKCBzbGlkZS5zbGlkZUJhY2tncm91bmRFbGVtZW50LCBzbGlkZSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gSW5qZWN0IG5vdGVzIGlmIGBzaG93Tm90ZXNgIGlzIGVuYWJsZWRcblx0XHRcdFx0aWYoIGNvbmZpZy5zaG93Tm90ZXMgKSB7XG5cblx0XHRcdFx0XHQvLyBBcmUgdGhlcmUgbm90ZXMgZm9yIHRoaXMgc2xpZGU/XG5cdFx0XHRcdFx0dmFyIG5vdGVzID0gZ2V0U2xpZGVOb3Rlcyggc2xpZGUgKTtcblx0XHRcdFx0XHRpZiggbm90ZXMgKSB7XG5cblx0XHRcdFx0XHRcdHZhciBub3Rlc1NwYWNpbmcgPSA4O1xuXHRcdFx0XHRcdFx0dmFyIG5vdGVzTGF5b3V0ID0gdHlwZW9mIGNvbmZpZy5zaG93Tm90ZXMgPT09ICdzdHJpbmcnID8gY29uZmlnLnNob3dOb3RlcyA6ICdpbmxpbmUnO1xuXHRcdFx0XHRcdFx0dmFyIG5vdGVzRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XG5cdFx0XHRcdFx0XHRub3Rlc0VsZW1lbnQuY2xhc3NMaXN0LmFkZCggJ3NwZWFrZXItbm90ZXMnICk7XG5cdFx0XHRcdFx0XHRub3Rlc0VsZW1lbnQuY2xhc3NMaXN0LmFkZCggJ3NwZWFrZXItbm90ZXMtcGRmJyApO1xuXHRcdFx0XHRcdFx0bm90ZXNFbGVtZW50LnNldEF0dHJpYnV0ZSggJ2RhdGEtbGF5b3V0Jywgbm90ZXNMYXlvdXQgKTtcblx0XHRcdFx0XHRcdG5vdGVzRWxlbWVudC5pbm5lckhUTUwgPSBub3RlcztcblxuXHRcdFx0XHRcdFx0aWYoIG5vdGVzTGF5b3V0ID09PSAnc2VwYXJhdGUtcGFnZScgKSB7XG5cdFx0XHRcdFx0XHRcdHBhZ2UucGFyZW50Tm9kZS5pbnNlcnRCZWZvcmUoIG5vdGVzRWxlbWVudCwgcGFnZS5uZXh0U2libGluZyApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0XHRcdG5vdGVzRWxlbWVudC5zdHlsZS5sZWZ0ID0gbm90ZXNTcGFjaW5nICsgJ3B4Jztcblx0XHRcdFx0XHRcdFx0bm90ZXNFbGVtZW50LnN0eWxlLmJvdHRvbSA9IG5vdGVzU3BhY2luZyArICdweCc7XG5cdFx0XHRcdFx0XHRcdG5vdGVzRWxlbWVudC5zdHlsZS53aWR0aCA9ICggcGFnZVdpZHRoIC0gbm90ZXNTcGFjaW5nKjIgKSArICdweCc7XG5cdFx0XHRcdFx0XHRcdHBhZ2UuYXBwZW5kQ2hpbGQoIG5vdGVzRWxlbWVudCApO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBJbmplY3Qgc2xpZGUgbnVtYmVycyBpZiBgc2xpZGVOdW1iZXJzYCBhcmUgZW5hYmxlZFxuXHRcdFx0XHRpZiggY29uZmlnLnNsaWRlTnVtYmVyICYmIC9hbGx8cHJpbnQvaS50ZXN0KCBjb25maWcuc2hvd1NsaWRlTnVtYmVyICkgKSB7XG5cdFx0XHRcdFx0dmFyIHNsaWRlTnVtYmVySCA9IHBhcnNlSW50KCBzbGlkZS5nZXRBdHRyaWJ1dGUoICdkYXRhLWluZGV4LWgnICksIDEwICkgKyAxLFxuXHRcdFx0XHRcdFx0c2xpZGVOdW1iZXJWID0gcGFyc2VJbnQoIHNsaWRlLmdldEF0dHJpYnV0ZSggJ2RhdGEtaW5kZXgtdicgKSwgMTAgKSArIDE7XG5cblx0XHRcdFx0XHR2YXIgbnVtYmVyRWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XG5cdFx0XHRcdFx0bnVtYmVyRWxlbWVudC5jbGFzc0xpc3QuYWRkKCAnc2xpZGUtbnVtYmVyJyApO1xuXHRcdFx0XHRcdG51bWJlckVsZW1lbnQuY2xhc3NMaXN0LmFkZCggJ3NsaWRlLW51bWJlci1wZGYnICk7XG5cdFx0XHRcdFx0bnVtYmVyRWxlbWVudC5pbm5lckhUTUwgPSBmb3JtYXRTbGlkZU51bWJlciggc2xpZGVOdW1iZXJILCAnLicsIHNsaWRlTnVtYmVyViApO1xuXHRcdFx0XHRcdHBhZ2UuYXBwZW5kQ2hpbGQoIG51bWJlckVsZW1lbnQgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0fSApO1xuXG5cdFx0Ly8gU2hvdyBhbGwgZnJhZ21lbnRzXG5cdFx0dG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICsgJyAuZnJhZ21lbnQnICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZnJhZ21lbnQgKSB7XG5cdFx0XHRmcmFnbWVudC5jbGFzc0xpc3QuYWRkKCAndmlzaWJsZScgKTtcblx0XHR9ICk7XG5cblx0XHQvLyBOb3RpZnkgc3Vic2NyaWJlcnMgdGhhdCB0aGUgUERGIGxheW91dCBpcyBnb29kIHRvIGdvXG5cdFx0ZGlzcGF0Y2hFdmVudCggJ3BkZi1yZWFkeScgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIFRoaXMgaXMgYW4gdW5mb3J0dW5hdGUgbmVjZXNzaXR5LiBTb21lIGFjdGlvbnMg4oCTIHN1Y2ggYXNcblx0ICogYW4gaW5wdXQgZmllbGQgYmVpbmcgZm9jdXNlZCBpbiBhbiBpZnJhbWUgb3IgdXNpbmcgdGhlXG5cdCAqIGtleWJvYXJkIHRvIGV4cGFuZCB0ZXh0IHNlbGVjdGlvbiBiZXlvbmQgdGhlIGJvdW5kcyBvZlxuXHQgKiBhIHNsaWRlIOKAkyBjYW4gdHJpZ2dlciBvdXIgY29udGVudCB0byBiZSBwdXNoZWQgb3V0IG9mIHZpZXcuXG5cdCAqIFRoaXMgc2Nyb2xsaW5nIGNhbiBub3QgYmUgcHJldmVudGVkIGJ5IGhpZGluZyBvdmVyZmxvdyBpblxuXHQgKiBDU1MgKHdlIGFscmVhZHkgZG8pIHNvIHdlIGhhdmUgdG8gcmVzb3J0IHRvIHJlcGVhdGVkbHlcblx0ICogY2hlY2tpbmcgaWYgdGhlIHNsaWRlcyBoYXZlIGJlZW4gb2Zmc2V0IDooXG5cdCAqL1xuXHRmdW5jdGlvbiBzZXR1cFNjcm9sbFByZXZlbnRpb24oKSB7XG5cblx0XHRzZXRJbnRlcnZhbCggZnVuY3Rpb24oKSB7XG5cdFx0XHRpZiggZG9tLndyYXBwZXIuc2Nyb2xsVG9wICE9PSAwIHx8IGRvbS53cmFwcGVyLnNjcm9sbExlZnQgIT09IDAgKSB7XG5cdFx0XHRcdGRvbS53cmFwcGVyLnNjcm9sbFRvcCA9IDA7XG5cdFx0XHRcdGRvbS53cmFwcGVyLnNjcm9sbExlZnQgPSAwO1xuXHRcdFx0fVxuXHRcdH0sIDEwMDAgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIENyZWF0ZXMgYW4gSFRNTCBlbGVtZW50IGFuZCByZXR1cm5zIGEgcmVmZXJlbmNlIHRvIGl0LlxuXHQgKiBJZiB0aGUgZWxlbWVudCBhbHJlYWR5IGV4aXN0cyB0aGUgZXhpc3RpbmcgaW5zdGFuY2Ugd2lsbFxuXHQgKiBiZSByZXR1cm5lZC5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gY29udGFpbmVyXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB0YWduYW1lXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBjbGFzc25hbWVcblx0ICogQHBhcmFtIHtzdHJpbmd9IGlubmVySFRNTFxuXHQgKlxuXHQgKiBAcmV0dXJuIHtIVE1MRWxlbWVudH1cblx0ICovXG5cdGZ1bmN0aW9uIGNyZWF0ZVNpbmdsZXRvbk5vZGUoIGNvbnRhaW5lciwgdGFnbmFtZSwgY2xhc3NuYW1lLCBpbm5lckhUTUwgKSB7XG5cblx0XHQvLyBGaW5kIGFsbCBub2RlcyBtYXRjaGluZyB0aGUgZGVzY3JpcHRpb25cblx0XHR2YXIgbm9kZXMgPSBjb250YWluZXIucXVlcnlTZWxlY3RvckFsbCggJy4nICsgY2xhc3NuYW1lICk7XG5cblx0XHQvLyBDaGVjayBhbGwgbWF0Y2hlcyB0byBmaW5kIG9uZSB3aGljaCBpcyBhIGRpcmVjdCBjaGlsZCBvZlxuXHRcdC8vIHRoZSBzcGVjaWZpZWQgY29udGFpbmVyXG5cdFx0Zm9yKCB2YXIgaSA9IDA7IGkgPCBub2Rlcy5sZW5ndGg7IGkrKyApIHtcblx0XHRcdHZhciB0ZXN0Tm9kZSA9IG5vZGVzW2ldO1xuXHRcdFx0aWYoIHRlc3ROb2RlLnBhcmVudE5vZGUgPT09IGNvbnRhaW5lciApIHtcblx0XHRcdFx0cmV0dXJuIHRlc3ROb2RlO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIElmIG5vIG5vZGUgd2FzIGZvdW5kLCBjcmVhdGUgaXQgbm93XG5cdFx0dmFyIG5vZGUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCB0YWduYW1lICk7XG5cdFx0bm9kZS5jbGFzc05hbWUgPSBjbGFzc25hbWU7XG5cdFx0aWYoIHR5cGVvZiBpbm5lckhUTUwgPT09ICdzdHJpbmcnICkge1xuXHRcdFx0bm9kZS5pbm5lckhUTUwgPSBpbm5lckhUTUw7XG5cdFx0fVxuXHRcdGNvbnRhaW5lci5hcHBlbmRDaGlsZCggbm9kZSApO1xuXG5cdFx0cmV0dXJuIG5vZGU7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIHRoZSBzbGlkZSBiYWNrZ3JvdW5kIGVsZW1lbnRzIGFuZCBhcHBlbmRzIHRoZW1cblx0ICogdG8gdGhlIGJhY2tncm91bmQgY29udGFpbmVyLiBPbmUgZWxlbWVudCBpcyBjcmVhdGVkIHBlclxuXHQgKiBzbGlkZSBubyBtYXR0ZXIgaWYgdGhlIGdpdmVuIHNsaWRlIGhhcyB2aXNpYmxlIGJhY2tncm91bmQuXG5cdCAqL1xuXHRmdW5jdGlvbiBjcmVhdGVCYWNrZ3JvdW5kcygpIHtcblxuXHRcdHZhciBwcmludE1vZGUgPSBpc1ByaW50aW5nUERGKCk7XG5cblx0XHQvLyBDbGVhciBwcmlvciBiYWNrZ3JvdW5kc1xuXHRcdGRvbS5iYWNrZ3JvdW5kLmlubmVySFRNTCA9ICcnO1xuXHRcdGRvbS5iYWNrZ3JvdW5kLmNsYXNzTGlzdC5hZGQoICduby10cmFuc2l0aW9uJyApO1xuXG5cdFx0Ly8gSXRlcmF0ZSBvdmVyIGFsbCBob3Jpem9udGFsIHNsaWRlc1xuXHRcdHRvQXJyYXkoIGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIEhPUklaT05UQUxfU0xJREVTX1NFTEVDVE9SICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggc2xpZGVoICkge1xuXG5cdFx0XHR2YXIgYmFja2dyb3VuZFN0YWNrID0gY3JlYXRlQmFja2dyb3VuZCggc2xpZGVoLCBkb20uYmFja2dyb3VuZCApO1xuXG5cdFx0XHQvLyBJdGVyYXRlIG92ZXIgYWxsIHZlcnRpY2FsIHNsaWRlc1xuXHRcdFx0dG9BcnJheSggc2xpZGVoLnF1ZXJ5U2VsZWN0b3JBbGwoICdzZWN0aW9uJyApICkuZm9yRWFjaCggZnVuY3Rpb24oIHNsaWRldiApIHtcblxuXHRcdFx0XHRjcmVhdGVCYWNrZ3JvdW5kKCBzbGlkZXYsIGJhY2tncm91bmRTdGFjayApO1xuXG5cdFx0XHRcdGJhY2tncm91bmRTdGFjay5jbGFzc0xpc3QuYWRkKCAnc3RhY2snICk7XG5cblx0XHRcdH0gKTtcblxuXHRcdH0gKTtcblxuXHRcdC8vIEFkZCBwYXJhbGxheCBiYWNrZ3JvdW5kIGlmIHNwZWNpZmllZFxuXHRcdGlmKCBjb25maWcucGFyYWxsYXhCYWNrZ3JvdW5kSW1hZ2UgKSB7XG5cblx0XHRcdGRvbS5iYWNrZ3JvdW5kLnN0eWxlLmJhY2tncm91bmRJbWFnZSA9ICd1cmwoXCInICsgY29uZmlnLnBhcmFsbGF4QmFja2dyb3VuZEltYWdlICsgJ1wiKSc7XG5cdFx0XHRkb20uYmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kU2l6ZSA9IGNvbmZpZy5wYXJhbGxheEJhY2tncm91bmRTaXplO1xuXG5cdFx0XHQvLyBNYWtlIHN1cmUgdGhlIGJlbG93IHByb3BlcnRpZXMgYXJlIHNldCBvbiB0aGUgZWxlbWVudCAtIHRoZXNlIHByb3BlcnRpZXMgYXJlXG5cdFx0XHQvLyBuZWVkZWQgZm9yIHByb3BlciB0cmFuc2l0aW9ucyB0byBiZSBzZXQgb24gdGhlIGVsZW1lbnQgdmlhIENTUy4gVG8gcmVtb3ZlXG5cdFx0XHQvLyBhbm5veWluZyBiYWNrZ3JvdW5kIHNsaWRlLWluIGVmZmVjdCB3aGVuIHRoZSBwcmVzZW50YXRpb24gc3RhcnRzLCBhcHBseVxuXHRcdFx0Ly8gdGhlc2UgcHJvcGVydGllcyBhZnRlciBzaG9ydCB0aW1lIGRlbGF5XG5cdFx0XHRzZXRUaW1lb3V0KCBmdW5jdGlvbigpIHtcblx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ2hhcy1wYXJhbGxheC1iYWNrZ3JvdW5kJyApO1xuXHRcdFx0fSwgMSApO1xuXG5cdFx0fVxuXHRcdGVsc2Uge1xuXG5cdFx0XHRkb20uYmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2UgPSAnJztcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoICdoYXMtcGFyYWxsYXgtYmFja2dyb3VuZCcgKTtcblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIENyZWF0ZXMgYSBiYWNrZ3JvdW5kIGZvciB0aGUgZ2l2ZW4gc2xpZGUuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHNsaWRlXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciBUaGUgZWxlbWVudCB0aGF0IHRoZSBiYWNrZ3JvdW5kXG5cdCAqIHNob3VsZCBiZSBhcHBlbmRlZCB0b1xuXHQgKiBAcmV0dXJuIHtIVE1MRWxlbWVudH0gTmV3IGJhY2tncm91bmQgZGl2XG5cdCAqL1xuXHRmdW5jdGlvbiBjcmVhdGVCYWNrZ3JvdW5kKCBzbGlkZSwgY29udGFpbmVyICkge1xuXG5cdFx0dmFyIGRhdGEgPSB7XG5cdFx0XHRiYWNrZ3JvdW5kOiBzbGlkZS5nZXRBdHRyaWJ1dGUoICdkYXRhLWJhY2tncm91bmQnICksXG5cdFx0XHRiYWNrZ3JvdW5kU2l6ZTogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXNpemUnICksXG5cdFx0XHRiYWNrZ3JvdW5kSW1hZ2U6IHNsaWRlLmdldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC1pbWFnZScgKSxcblx0XHRcdGJhY2tncm91bmRWaWRlbzogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXZpZGVvJyApLFxuXHRcdFx0YmFja2dyb3VuZElmcmFtZTogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLWlmcmFtZScgKSxcblx0XHRcdGJhY2tncm91bmRDb2xvcjogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLWNvbG9yJyApLFxuXHRcdFx0YmFja2dyb3VuZFJlcGVhdDogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXJlcGVhdCcgKSxcblx0XHRcdGJhY2tncm91bmRQb3NpdGlvbjogc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXBvc2l0aW9uJyApLFxuXHRcdFx0YmFja2dyb3VuZFRyYW5zaXRpb246IHNsaWRlLmdldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC10cmFuc2l0aW9uJyApXG5cdFx0fTtcblxuXHRcdHZhciBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblxuXHRcdC8vIENhcnJ5IG92ZXIgY3VzdG9tIGNsYXNzZXMgZnJvbSB0aGUgc2xpZGUgdG8gdGhlIGJhY2tncm91bmRcblx0XHRlbGVtZW50LmNsYXNzTmFtZSA9ICdzbGlkZS1iYWNrZ3JvdW5kICcgKyBzbGlkZS5jbGFzc05hbWUucmVwbGFjZSggL3ByZXNlbnR8cGFzdHxmdXR1cmUvLCAnJyApO1xuXG5cdFx0aWYoIGRhdGEuYmFja2dyb3VuZCApIHtcblx0XHRcdC8vIEF1dG8td3JhcCBpbWFnZSB1cmxzIGluIHVybCguLi4pXG5cdFx0XHRpZiggL14oaHR0cHxmaWxlfFxcL1xcLykvZ2kudGVzdCggZGF0YS5iYWNrZ3JvdW5kICkgfHwgL1xcLihzdmd8cG5nfGpwZ3xqcGVnfGdpZnxibXApKFs/I118JCkvZ2kudGVzdCggZGF0YS5iYWNrZ3JvdW5kICkgKSB7XG5cdFx0XHRcdHNsaWRlLnNldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC1pbWFnZScsIGRhdGEuYmFja2dyb3VuZCApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdGVsZW1lbnQuc3R5bGUuYmFja2dyb3VuZCA9IGRhdGEuYmFja2dyb3VuZDtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBDcmVhdGUgYSBoYXNoIGZvciB0aGlzIGNvbWJpbmF0aW9uIG9mIGJhY2tncm91bmQgc2V0dGluZ3MuXG5cdFx0Ly8gVGhpcyBpcyB1c2VkIHRvIGRldGVybWluZSB3aGVuIHR3byBzbGlkZSBiYWNrZ3JvdW5kcyBhcmVcblx0XHQvLyB0aGUgc2FtZS5cblx0XHRpZiggZGF0YS5iYWNrZ3JvdW5kIHx8IGRhdGEuYmFja2dyb3VuZENvbG9yIHx8IGRhdGEuYmFja2dyb3VuZEltYWdlIHx8IGRhdGEuYmFja2dyb3VuZFZpZGVvIHx8IGRhdGEuYmFja2dyb3VuZElmcmFtZSApIHtcblx0XHRcdGVsZW1lbnQuc2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLWhhc2gnLCBkYXRhLmJhY2tncm91bmQgK1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0ZGF0YS5iYWNrZ3JvdW5kU2l6ZSArXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRkYXRhLmJhY2tncm91bmRJbWFnZSArXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRkYXRhLmJhY2tncm91bmRWaWRlbyArXG5cdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRkYXRhLmJhY2tncm91bmRJZnJhbWUgK1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0ZGF0YS5iYWNrZ3JvdW5kQ29sb3IgK1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0ZGF0YS5iYWNrZ3JvdW5kUmVwZWF0ICtcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdGRhdGEuYmFja2dyb3VuZFBvc2l0aW9uICtcblx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdFx0XHRcdGRhdGEuYmFja2dyb3VuZFRyYW5zaXRpb24gKTtcblx0XHR9XG5cblx0XHQvLyBBZGRpdGlvbmFsIGFuZCBvcHRpb25hbCBiYWNrZ3JvdW5kIHByb3BlcnRpZXNcblx0XHRpZiggZGF0YS5iYWNrZ3JvdW5kU2l6ZSApIGVsZW1lbnQuc3R5bGUuYmFja2dyb3VuZFNpemUgPSBkYXRhLmJhY2tncm91bmRTaXplO1xuXHRcdGlmKCBkYXRhLmJhY2tncm91bmRTaXplICkgZWxlbWVudC5zZXRBdHRyaWJ1dGUoICdkYXRhLWJhY2tncm91bmQtc2l6ZScsIGRhdGEuYmFja2dyb3VuZFNpemUgKTtcblx0XHRpZiggZGF0YS5iYWNrZ3JvdW5kQ29sb3IgKSBlbGVtZW50LnN0eWxlLmJhY2tncm91bmRDb2xvciA9IGRhdGEuYmFja2dyb3VuZENvbG9yO1xuXHRcdGlmKCBkYXRhLmJhY2tncm91bmRSZXBlYXQgKSBlbGVtZW50LnN0eWxlLmJhY2tncm91bmRSZXBlYXQgPSBkYXRhLmJhY2tncm91bmRSZXBlYXQ7XG5cdFx0aWYoIGRhdGEuYmFja2dyb3VuZFBvc2l0aW9uICkgZWxlbWVudC5zdHlsZS5iYWNrZ3JvdW5kUG9zaXRpb24gPSBkYXRhLmJhY2tncm91bmRQb3NpdGlvbjtcblx0XHRpZiggZGF0YS5iYWNrZ3JvdW5kVHJhbnNpdGlvbiApIGVsZW1lbnQuc2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXRyYW5zaXRpb24nLCBkYXRhLmJhY2tncm91bmRUcmFuc2l0aW9uICk7XG5cblx0XHRjb250YWluZXIuYXBwZW5kQ2hpbGQoIGVsZW1lbnQgKTtcblxuXHRcdC8vIElmIGJhY2tncm91bmRzIGFyZSBiZWluZyByZWNyZWF0ZWQsIGNsZWFyIG9sZCBjbGFzc2VzXG5cdFx0c2xpZGUuY2xhc3NMaXN0LnJlbW92ZSggJ2hhcy1kYXJrLWJhY2tncm91bmQnICk7XG5cdFx0c2xpZGUuY2xhc3NMaXN0LnJlbW92ZSggJ2hhcy1saWdodC1iYWNrZ3JvdW5kJyApO1xuXG5cdFx0c2xpZGUuc2xpZGVCYWNrZ3JvdW5kRWxlbWVudCA9IGVsZW1lbnQ7XG5cblx0XHQvLyBJZiB0aGlzIHNsaWRlIGhhcyBhIGJhY2tncm91bmQgY29sb3IsIGFkZCBhIGNsYXNzIHRoYXRcblx0XHQvLyBzaWduYWxzIGlmIGl0IGlzIGxpZ2h0IG9yIGRhcmsuIElmIHRoZSBzbGlkZSBoYXMgbm8gYmFja2dyb3VuZFxuXHRcdC8vIGNvbG9yLCBubyBjbGFzcyB3aWxsIGJlIHNldFxuXHRcdHZhciBjb21wdXRlZEJhY2tncm91bmRTdHlsZSA9IHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKCBlbGVtZW50ICk7XG5cdFx0aWYoIGNvbXB1dGVkQmFja2dyb3VuZFN0eWxlICYmIGNvbXB1dGVkQmFja2dyb3VuZFN0eWxlLmJhY2tncm91bmRDb2xvciApIHtcblx0XHRcdHZhciByZ2IgPSBjb2xvclRvUmdiKCBjb21wdXRlZEJhY2tncm91bmRTdHlsZS5iYWNrZ3JvdW5kQ29sb3IgKTtcblxuXHRcdFx0Ly8gSWdub3JlIGZ1bGx5IHRyYW5zcGFyZW50IGJhY2tncm91bmRzLiBTb21lIGJyb3dzZXJzIHJldHVyblxuXHRcdFx0Ly8gcmdiYSgwLDAsMCwwKSB3aGVuIHJlYWRpbmcgdGhlIGNvbXB1dGVkIGJhY2tncm91bmQgY29sb3Igb2Zcblx0XHRcdC8vIGFuIGVsZW1lbnQgd2l0aCBubyBiYWNrZ3JvdW5kXG5cdFx0XHRpZiggcmdiICYmIHJnYi5hICE9PSAwICkge1xuXHRcdFx0XHRpZiggY29sb3JCcmlnaHRuZXNzKCBjb21wdXRlZEJhY2tncm91bmRTdHlsZS5iYWNrZ3JvdW5kQ29sb3IgKSA8IDEyOCApIHtcblx0XHRcdFx0XHRzbGlkZS5jbGFzc0xpc3QuYWRkKCAnaGFzLWRhcmstYmFja2dyb3VuZCcgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRzbGlkZS5jbGFzc0xpc3QuYWRkKCAnaGFzLWxpZ2h0LWJhY2tncm91bmQnICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gZWxlbWVudDtcblxuXHR9XG5cblx0LyoqXG5cdCAqIFJlZ2lzdGVycyBhIGxpc3RlbmVyIHRvIHBvc3RNZXNzYWdlIGV2ZW50cywgdGhpcyBtYWtlcyBpdFxuXHQgKiBwb3NzaWJsZSB0byBjYWxsIGFsbCByZXZlYWwuanMgQVBJIG1ldGhvZHMgZnJvbSBhbm90aGVyXG5cdCAqIHdpbmRvdy4gRm9yIGV4YW1wbGU6XG5cdCAqXG5cdCAqIHJldmVhbFdpbmRvdy5wb3N0TWVzc2FnZSggSlNPTi5zdHJpbmdpZnkoe1xuXHQgKiAgIG1ldGhvZDogJ3NsaWRlJyxcblx0ICogICBhcmdzOiBbIDIgXVxuXHQgKiB9KSwgJyonICk7XG5cdCAqL1xuXHRmdW5jdGlvbiBzZXR1cFBvc3RNZXNzYWdlKCkge1xuXG5cdFx0aWYoIGNvbmZpZy5wb3N0TWVzc2FnZSApIHtcblx0XHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCAnbWVzc2FnZScsIGZ1bmN0aW9uICggZXZlbnQgKSB7XG5cdFx0XHRcdHZhciBkYXRhID0gZXZlbnQuZGF0YTtcblxuXHRcdFx0XHQvLyBNYWtlIHN1cmUgd2UncmUgZGVhbGluZyB3aXRoIEpTT05cblx0XHRcdFx0aWYoIHR5cGVvZiBkYXRhID09PSAnc3RyaW5nJyAmJiBkYXRhLmNoYXJBdCggMCApID09PSAneycgJiYgZGF0YS5jaGFyQXQoIGRhdGEubGVuZ3RoIC0gMSApID09PSAnfScgKSB7XG5cdFx0XHRcdFx0ZGF0YSA9IEpTT04ucGFyc2UoIGRhdGEgKTtcblxuXHRcdFx0XHRcdC8vIENoZWNrIGlmIHRoZSByZXF1ZXN0ZWQgbWV0aG9kIGNhbiBiZSBmb3VuZFxuXHRcdFx0XHRcdGlmKCBkYXRhLm1ldGhvZCAmJiB0eXBlb2YgUmV2ZWFsW2RhdGEubWV0aG9kXSA9PT0gJ2Z1bmN0aW9uJyApIHtcblx0XHRcdFx0XHRcdFJldmVhbFtkYXRhLm1ldGhvZF0uYXBwbHkoIFJldmVhbCwgZGF0YS5hcmdzICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9LCBmYWxzZSApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgdGhlIGNvbmZpZ3VyYXRpb24gc2V0dGluZ3MgZnJvbSB0aGUgY29uZmlnXG5cdCAqIG9iamVjdC4gTWF5IGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcy5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnNcblx0ICovXG5cdGZ1bmN0aW9uIGNvbmZpZ3VyZSggb3B0aW9ucyApIHtcblxuXHRcdHZhciBvbGRUcmFuc2l0aW9uID0gY29uZmlnLnRyYW5zaXRpb247XG5cblx0XHQvLyBOZXcgY29uZmlnIG9wdGlvbnMgbWF5IGJlIHBhc3NlZCB3aGVuIHRoaXMgbWV0aG9kXG5cdFx0Ly8gaXMgaW52b2tlZCB0aHJvdWdoIHRoZSBBUEkgYWZ0ZXIgaW5pdGlhbGl6YXRpb25cblx0XHRpZiggdHlwZW9mIG9wdGlvbnMgPT09ICdvYmplY3QnICkgZXh0ZW5kKCBjb25maWcsIG9wdGlvbnMgKTtcblxuXHRcdC8vIEFib3J0IGlmIHJldmVhbC5qcyBoYXNuJ3QgZmluaXNoZWQgbG9hZGluZywgY29uZmlnXG5cdFx0Ly8gY2hhbmdlcyB3aWxsIGJlIGFwcGxpZWQgYXV0b21hdGljYWxseSBvbmNlIGxvYWRpbmdcblx0XHQvLyBmaW5pc2hlc1xuXHRcdGlmKCBsb2FkZWQgPT09IGZhbHNlICkgcmV0dXJuO1xuXG5cdFx0dmFyIG51bWJlck9mU2xpZGVzID0gZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICkubGVuZ3RoO1xuXG5cdFx0Ly8gUmVtb3ZlIHRoZSBwcmV2aW91c2x5IGNvbmZpZ3VyZWQgdHJhbnNpdGlvbiBjbGFzc1xuXHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoIG9sZFRyYW5zaXRpb24gKTtcblxuXHRcdC8vIEZvcmNlIGxpbmVhciB0cmFuc2l0aW9uIGJhc2VkIG9uIGJyb3dzZXIgY2FwYWJpbGl0aWVzXG5cdFx0aWYoIGZlYXR1cmVzLnRyYW5zZm9ybXMzZCA9PT0gZmFsc2UgKSBjb25maWcudHJhbnNpdGlvbiA9ICdsaW5lYXInO1xuXG5cdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggY29uZmlnLnRyYW5zaXRpb24gKTtcblxuXHRcdGRvbS53cmFwcGVyLnNldEF0dHJpYnV0ZSggJ2RhdGEtdHJhbnNpdGlvbi1zcGVlZCcsIGNvbmZpZy50cmFuc2l0aW9uU3BlZWQgKTtcblx0XHRkb20ud3JhcHBlci5zZXRBdHRyaWJ1dGUoICdkYXRhLWJhY2tncm91bmQtdHJhbnNpdGlvbicsIGNvbmZpZy5iYWNrZ3JvdW5kVHJhbnNpdGlvbiApO1xuXG5cdFx0ZG9tLmNvbnRyb2xzLnN0eWxlLmRpc3BsYXkgPSBjb25maWcuY29udHJvbHMgPyAnYmxvY2snIDogJ25vbmUnO1xuXHRcdGRvbS5wcm9ncmVzcy5zdHlsZS5kaXNwbGF5ID0gY29uZmlnLnByb2dyZXNzID8gJ2Jsb2NrJyA6ICdub25lJztcblxuXHRcdGRvbS5jb250cm9scy5zZXRBdHRyaWJ1dGUoICdkYXRhLWNvbnRyb2xzLWxheW91dCcsIGNvbmZpZy5jb250cm9sc0xheW91dCApO1xuXHRcdGRvbS5jb250cm9scy5zZXRBdHRyaWJ1dGUoICdkYXRhLWNvbnRyb2xzLWJhY2stYXJyb3dzJywgY29uZmlnLmNvbnRyb2xzQmFja0Fycm93cyApO1xuXG5cdFx0aWYoIGNvbmZpZy5zaHVmZmxlICkge1xuXHRcdFx0c2h1ZmZsZSgpO1xuXHRcdH1cblxuXHRcdGlmKCBjb25maWcucnRsICkge1xuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ3J0bCcgKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QucmVtb3ZlKCAncnRsJyApO1xuXHRcdH1cblxuXHRcdGlmKCBjb25maWcuY2VudGVyICkge1xuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ2NlbnRlcicgKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QucmVtb3ZlKCAnY2VudGVyJyApO1xuXHRcdH1cblxuXHRcdC8vIEV4aXQgdGhlIHBhdXNlZCBtb2RlIGlmIGl0IHdhcyBjb25maWd1cmVkIG9mZlxuXHRcdGlmKCBjb25maWcucGF1c2UgPT09IGZhbHNlICkge1xuXHRcdFx0cmVzdW1lKCk7XG5cdFx0fVxuXG5cdFx0aWYoIGNvbmZpZy5zaG93Tm90ZXMgKSB7XG5cdFx0XHRkb20uc3BlYWtlck5vdGVzLnNldEF0dHJpYnV0ZSggJ2RhdGEtbGF5b3V0JywgdHlwZW9mIGNvbmZpZy5zaG93Tm90ZXMgPT09ICdzdHJpbmcnID8gY29uZmlnLnNob3dOb3RlcyA6ICdpbmxpbmUnICk7XG5cdFx0fVxuXG5cdFx0aWYoIGNvbmZpZy5tb3VzZVdoZWVsICkge1xuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ0RPTU1vdXNlU2Nyb2xsJywgb25Eb2N1bWVudE1vdXNlU2Nyb2xsLCBmYWxzZSApOyAvLyBGRlxuXHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggJ21vdXNld2hlZWwnLCBvbkRvY3VtZW50TW91c2VTY3JvbGwsIGZhbHNlICk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ0RPTU1vdXNlU2Nyb2xsJywgb25Eb2N1bWVudE1vdXNlU2Nyb2xsLCBmYWxzZSApOyAvLyBGRlxuXHRcdFx0ZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ21vdXNld2hlZWwnLCBvbkRvY3VtZW50TW91c2VTY3JvbGwsIGZhbHNlICk7XG5cdFx0fVxuXG5cdFx0Ly8gUm9sbGluZyAzRCBsaW5rc1xuXHRcdGlmKCBjb25maWcucm9sbGluZ0xpbmtzICkge1xuXHRcdFx0ZW5hYmxlUm9sbGluZ0xpbmtzKCk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0ZGlzYWJsZVJvbGxpbmdMaW5rcygpO1xuXHRcdH1cblxuXHRcdC8vIElmcmFtZSBsaW5rIHByZXZpZXdzXG5cdFx0aWYoIGNvbmZpZy5wcmV2aWV3TGlua3MgKSB7XG5cdFx0XHRlbmFibGVQcmV2aWV3TGlua3MoKTtcblx0XHRcdGRpc2FibGVQcmV2aWV3TGlua3MoICdbZGF0YS1wcmV2aWV3LWxpbms9ZmFsc2VdJyApO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGRpc2FibGVQcmV2aWV3TGlua3MoKTtcblx0XHRcdGVuYWJsZVByZXZpZXdMaW5rcyggJ1tkYXRhLXByZXZpZXctbGlua106bm90KFtkYXRhLXByZXZpZXctbGluaz1mYWxzZV0pJyApO1xuXHRcdH1cblxuXHRcdC8vIFJlbW92ZSBleGlzdGluZyBhdXRvLXNsaWRlIGNvbnRyb2xzXG5cdFx0aWYoIGF1dG9TbGlkZVBsYXllciApIHtcblx0XHRcdGF1dG9TbGlkZVBsYXllci5kZXN0cm95KCk7XG5cdFx0XHRhdXRvU2xpZGVQbGF5ZXIgPSBudWxsO1xuXHRcdH1cblxuXHRcdC8vIEdlbmVyYXRlIGF1dG8tc2xpZGUgY29udHJvbHMgaWYgbmVlZGVkXG5cdFx0aWYoIG51bWJlck9mU2xpZGVzID4gMSAmJiBjb25maWcuYXV0b1NsaWRlICYmIGNvbmZpZy5hdXRvU2xpZGVTdG9wcGFibGUgJiYgZmVhdHVyZXMuY2FudmFzICYmIGZlYXR1cmVzLnJlcXVlc3RBbmltYXRpb25GcmFtZSApIHtcblx0XHRcdGF1dG9TbGlkZVBsYXllciA9IG5ldyBQbGF5YmFjayggZG9tLndyYXBwZXIsIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gTWF0aC5taW4oIE1hdGgubWF4KCAoIERhdGUubm93KCkgLSBhdXRvU2xpZGVTdGFydFRpbWUgKSAvIGF1dG9TbGlkZSwgMCApLCAxICk7XG5cdFx0XHR9ICk7XG5cblx0XHRcdGF1dG9TbGlkZVBsYXllci5vbiggJ2NsaWNrJywgb25BdXRvU2xpZGVQbGF5ZXJDbGljayApO1xuXHRcdFx0YXV0b1NsaWRlUGF1c2VkID0gZmFsc2U7XG5cdFx0fVxuXG5cdFx0Ly8gV2hlbiBmcmFnbWVudHMgYXJlIHR1cm5lZCBvZmYgdGhleSBzaG91bGQgYmUgdmlzaWJsZVxuXHRcdGlmKCBjb25maWcuZnJhZ21lbnRzID09PSBmYWxzZSApIHtcblx0XHRcdHRvQXJyYXkoIGRvbS5zbGlkZXMucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudCcgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5hZGQoICd2aXNpYmxlJyApO1xuXHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoICdjdXJyZW50LWZyYWdtZW50JyApO1xuXHRcdFx0fSApO1xuXHRcdH1cblxuXHRcdC8vIFNsaWRlIG51bWJlcnNcblx0XHR2YXIgc2xpZGVOdW1iZXJEaXNwbGF5ID0gJ25vbmUnO1xuXHRcdGlmKCBjb25maWcuc2xpZGVOdW1iZXIgJiYgIWlzUHJpbnRpbmdQREYoKSApIHtcblx0XHRcdGlmKCBjb25maWcuc2hvd1NsaWRlTnVtYmVyID09PSAnYWxsJyApIHtcblx0XHRcdFx0c2xpZGVOdW1iZXJEaXNwbGF5ID0gJ2Jsb2NrJztcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoIGNvbmZpZy5zaG93U2xpZGVOdW1iZXIgPT09ICdzcGVha2VyJyAmJiBpc1NwZWFrZXJOb3RlcygpICkge1xuXHRcdFx0XHRzbGlkZU51bWJlckRpc3BsYXkgPSAnYmxvY2snO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGRvbS5zbGlkZU51bWJlci5zdHlsZS5kaXNwbGF5ID0gc2xpZGVOdW1iZXJEaXNwbGF5O1xuXG5cdFx0c3luYygpO1xuXG5cdH1cblxuXHQvKipcblx0ICogQmluZHMgYWxsIGV2ZW50IGxpc3RlbmVycy5cblx0ICovXG5cdGZ1bmN0aW9uIGFkZEV2ZW50TGlzdGVuZXJzKCkge1xuXG5cdFx0ZXZlbnRzQXJlQm91bmQgPSB0cnVlO1xuXG5cdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoICdoYXNoY2hhbmdlJywgb25XaW5kb3dIYXNoQ2hhbmdlLCBmYWxzZSApO1xuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCAncmVzaXplJywgb25XaW5kb3dSZXNpemUsIGZhbHNlICk7XG5cblx0XHRpZiggY29uZmlnLnRvdWNoICkge1xuXHRcdFx0ZG9tLndyYXBwZXIuYWRkRXZlbnRMaXN0ZW5lciggJ3RvdWNoc3RhcnQnLCBvblRvdWNoU3RhcnQsIGZhbHNlICk7XG5cdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAndG91Y2htb3ZlJywgb25Ub3VjaE1vdmUsIGZhbHNlICk7XG5cdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAndG91Y2hlbmQnLCBvblRvdWNoRW5kLCBmYWxzZSApO1xuXG5cdFx0XHQvLyBTdXBwb3J0IHBvaW50ZXItc3R5bGUgdG91Y2ggaW50ZXJhY3Rpb24gYXMgd2VsbFxuXHRcdFx0aWYoIHdpbmRvdy5uYXZpZ2F0b3IucG9pbnRlckVuYWJsZWQgKSB7XG5cdFx0XHRcdC8vIElFIDExIHVzZXMgdW4tcHJlZml4ZWQgdmVyc2lvbiBvZiBwb2ludGVyIGV2ZW50c1xuXHRcdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAncG9pbnRlcmRvd24nLCBvblBvaW50ZXJEb3duLCBmYWxzZSApO1xuXHRcdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAncG9pbnRlcm1vdmUnLCBvblBvaW50ZXJNb3ZlLCBmYWxzZSApO1xuXHRcdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAncG9pbnRlcnVwJywgb25Qb2ludGVyVXAsIGZhbHNlICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIGlmKCB3aW5kb3cubmF2aWdhdG9yLm1zUG9pbnRlckVuYWJsZWQgKSB7XG5cdFx0XHRcdC8vIElFIDEwIHVzZXMgcHJlZml4ZWQgdmVyc2lvbiBvZiBwb2ludGVyIGV2ZW50c1xuXHRcdFx0XHRkb20ud3JhcHBlci5hZGRFdmVudExpc3RlbmVyKCAnTVNQb2ludGVyRG93bicsIG9uUG9pbnRlckRvd24sIGZhbHNlICk7XG5cdFx0XHRcdGRvbS53cmFwcGVyLmFkZEV2ZW50TGlzdGVuZXIoICdNU1BvaW50ZXJNb3ZlJywgb25Qb2ludGVyTW92ZSwgZmFsc2UgKTtcblx0XHRcdFx0ZG9tLndyYXBwZXIuYWRkRXZlbnRMaXN0ZW5lciggJ01TUG9pbnRlclVwJywgb25Qb2ludGVyVXAsIGZhbHNlICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0aWYoIGNvbmZpZy5rZXlib2FyZCApIHtcblx0XHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICdrZXlkb3duJywgb25Eb2N1bWVudEtleURvd24sIGZhbHNlICk7XG5cdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCAna2V5cHJlc3MnLCBvbkRvY3VtZW50S2V5UHJlc3MsIGZhbHNlICk7XG5cdFx0fVxuXG5cdFx0aWYoIGNvbmZpZy5wcm9ncmVzcyAmJiBkb20ucHJvZ3Jlc3MgKSB7XG5cdFx0XHRkb20ucHJvZ3Jlc3MuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgb25Qcm9ncmVzc0NsaWNrZWQsIGZhbHNlICk7XG5cdFx0fVxuXG5cdFx0aWYoIGNvbmZpZy5mb2N1c0JvZHlPblBhZ2VWaXNpYmlsaXR5Q2hhbmdlICkge1xuXHRcdFx0dmFyIHZpc2liaWxpdHlDaGFuZ2U7XG5cblx0XHRcdGlmKCAnaGlkZGVuJyBpbiBkb2N1bWVudCApIHtcblx0XHRcdFx0dmlzaWJpbGl0eUNoYW5nZSA9ICd2aXNpYmlsaXR5Y2hhbmdlJztcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoICdtc0hpZGRlbicgaW4gZG9jdW1lbnQgKSB7XG5cdFx0XHRcdHZpc2liaWxpdHlDaGFuZ2UgPSAnbXN2aXNpYmlsaXR5Y2hhbmdlJztcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoICd3ZWJraXRIaWRkZW4nIGluIGRvY3VtZW50ICkge1xuXHRcdFx0XHR2aXNpYmlsaXR5Q2hhbmdlID0gJ3dlYmtpdHZpc2liaWxpdHljaGFuZ2UnO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiggdmlzaWJpbGl0eUNoYW5nZSApIHtcblx0XHRcdFx0ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lciggdmlzaWJpbGl0eUNoYW5nZSwgb25QYWdlVmlzaWJpbGl0eUNoYW5nZSwgZmFsc2UgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBMaXN0ZW4gdG8gYm90aCB0b3VjaCBhbmQgY2xpY2sgZXZlbnRzLCBpbiBjYXNlIHRoZSBkZXZpY2Vcblx0XHQvLyBzdXBwb3J0cyBib3RoXG5cdFx0dmFyIHBvaW50ZXJFdmVudHMgPSBbICd0b3VjaHN0YXJ0JywgJ2NsaWNrJyBdO1xuXG5cdFx0Ly8gT25seSBzdXBwb3J0IHRvdWNoIGZvciBBbmRyb2lkLCBmaXhlcyBkb3VibGUgbmF2aWdhdGlvbnMgaW5cblx0XHQvLyBzdG9jayBicm93c2VyXG5cdFx0aWYoIFVBLm1hdGNoKCAvYW5kcm9pZC9naSApICkge1xuXHRcdFx0cG9pbnRlckV2ZW50cyA9IFsgJ3RvdWNoc3RhcnQnIF07XG5cdFx0fVxuXG5cdFx0cG9pbnRlckV2ZW50cy5mb3JFYWNoKCBmdW5jdGlvbiggZXZlbnROYW1lICkge1xuXHRcdFx0ZG9tLmNvbnRyb2xzTGVmdC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmFkZEV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZUxlZnRDbGlja2VkLCBmYWxzZSApOyB9ICk7XG5cdFx0XHRkb20uY29udHJvbHNSaWdodC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmFkZEV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZVJpZ2h0Q2xpY2tlZCwgZmFsc2UgKTsgfSApO1xuXHRcdFx0ZG9tLmNvbnRyb2xzVXAuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5hZGRFdmVudExpc3RlbmVyKCBldmVudE5hbWUsIG9uTmF2aWdhdGVVcENsaWNrZWQsIGZhbHNlICk7IH0gKTtcblx0XHRcdGRvbS5jb250cm9sc0Rvd24uZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5hZGRFdmVudExpc3RlbmVyKCBldmVudE5hbWUsIG9uTmF2aWdhdGVEb3duQ2xpY2tlZCwgZmFsc2UgKTsgfSApO1xuXHRcdFx0ZG9tLmNvbnRyb2xzUHJldi5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmFkZEV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZVByZXZDbGlja2VkLCBmYWxzZSApOyB9ICk7XG5cdFx0XHRkb20uY29udHJvbHNOZXh0LmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHsgZWwuYWRkRXZlbnRMaXN0ZW5lciggZXZlbnROYW1lLCBvbk5hdmlnYXRlTmV4dENsaWNrZWQsIGZhbHNlICk7IH0gKTtcblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVbmJpbmRzIGFsbCBldmVudCBsaXN0ZW5lcnMuXG5cdCAqL1xuXHRmdW5jdGlvbiByZW1vdmVFdmVudExpc3RlbmVycygpIHtcblxuXHRcdGV2ZW50c0FyZUJvdW5kID0gZmFsc2U7XG5cblx0XHRkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCAna2V5ZG93bicsIG9uRG9jdW1lbnRLZXlEb3duLCBmYWxzZSApO1xuXHRcdGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdrZXlwcmVzcycsIG9uRG9jdW1lbnRLZXlQcmVzcywgZmFsc2UgKTtcblx0XHR3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2hhc2hjaGFuZ2UnLCBvbldpbmRvd0hhc2hDaGFuZ2UsIGZhbHNlICk7XG5cdFx0d2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdyZXNpemUnLCBvbldpbmRvd1Jlc2l6ZSwgZmFsc2UgKTtcblxuXHRcdGRvbS53cmFwcGVyLnJlbW92ZUV2ZW50TGlzdGVuZXIoICd0b3VjaHN0YXJ0Jywgb25Ub3VjaFN0YXJ0LCBmYWxzZSApO1xuXHRcdGRvbS53cmFwcGVyLnJlbW92ZUV2ZW50TGlzdGVuZXIoICd0b3VjaG1vdmUnLCBvblRvdWNoTW92ZSwgZmFsc2UgKTtcblx0XHRkb20ud3JhcHBlci5yZW1vdmVFdmVudExpc3RlbmVyKCAndG91Y2hlbmQnLCBvblRvdWNoRW5kLCBmYWxzZSApO1xuXG5cdFx0Ly8gSUUxMVxuXHRcdGlmKCB3aW5kb3cubmF2aWdhdG9yLnBvaW50ZXJFbmFibGVkICkge1xuXHRcdFx0ZG9tLndyYXBwZXIucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ3BvaW50ZXJkb3duJywgb25Qb2ludGVyRG93biwgZmFsc2UgKTtcblx0XHRcdGRvbS53cmFwcGVyLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdwb2ludGVybW92ZScsIG9uUG9pbnRlck1vdmUsIGZhbHNlICk7XG5cdFx0XHRkb20ud3JhcHBlci5yZW1vdmVFdmVudExpc3RlbmVyKCAncG9pbnRlcnVwJywgb25Qb2ludGVyVXAsIGZhbHNlICk7XG5cdFx0fVxuXHRcdC8vIElFMTBcblx0XHRlbHNlIGlmKCB3aW5kb3cubmF2aWdhdG9yLm1zUG9pbnRlckVuYWJsZWQgKSB7XG5cdFx0XHRkb20ud3JhcHBlci5yZW1vdmVFdmVudExpc3RlbmVyKCAnTVNQb2ludGVyRG93bicsIG9uUG9pbnRlckRvd24sIGZhbHNlICk7XG5cdFx0XHRkb20ud3JhcHBlci5yZW1vdmVFdmVudExpc3RlbmVyKCAnTVNQb2ludGVyTW92ZScsIG9uUG9pbnRlck1vdmUsIGZhbHNlICk7XG5cdFx0XHRkb20ud3JhcHBlci5yZW1vdmVFdmVudExpc3RlbmVyKCAnTVNQb2ludGVyVXAnLCBvblBvaW50ZXJVcCwgZmFsc2UgKTtcblx0XHR9XG5cblx0XHRpZiAoIGNvbmZpZy5wcm9ncmVzcyAmJiBkb20ucHJvZ3Jlc3MgKSB7XG5cdFx0XHRkb20ucHJvZ3Jlc3MucmVtb3ZlRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgb25Qcm9ncmVzc0NsaWNrZWQsIGZhbHNlICk7XG5cdFx0fVxuXG5cdFx0WyAndG91Y2hzdGFydCcsICdjbGljaycgXS5mb3JFYWNoKCBmdW5jdGlvbiggZXZlbnROYW1lICkge1xuXHRcdFx0ZG9tLmNvbnRyb2xzTGVmdC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZUxlZnRDbGlja2VkLCBmYWxzZSApOyB9ICk7XG5cdFx0XHRkb20uY29udHJvbHNSaWdodC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZVJpZ2h0Q2xpY2tlZCwgZmFsc2UgKTsgfSApO1xuXHRcdFx0ZG9tLmNvbnRyb2xzVXAuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5yZW1vdmVFdmVudExpc3RlbmVyKCBldmVudE5hbWUsIG9uTmF2aWdhdGVVcENsaWNrZWQsIGZhbHNlICk7IH0gKTtcblx0XHRcdGRvbS5jb250cm9sc0Rvd24uZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5yZW1vdmVFdmVudExpc3RlbmVyKCBldmVudE5hbWUsIG9uTmF2aWdhdGVEb3duQ2xpY2tlZCwgZmFsc2UgKTsgfSApO1xuXHRcdFx0ZG9tLmNvbnRyb2xzUHJldi5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoIGV2ZW50TmFtZSwgb25OYXZpZ2F0ZVByZXZDbGlja2VkLCBmYWxzZSApOyB9ICk7XG5cdFx0XHRkb20uY29udHJvbHNOZXh0LmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHsgZWwucmVtb3ZlRXZlbnRMaXN0ZW5lciggZXZlbnROYW1lLCBvbk5hdmlnYXRlTmV4dENsaWNrZWQsIGZhbHNlICk7IH0gKTtcblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBFeHRlbmQgb2JqZWN0IGEgd2l0aCB0aGUgcHJvcGVydGllcyBvZiBvYmplY3QgYi5cblx0ICogSWYgdGhlcmUncyBhIGNvbmZsaWN0LCBvYmplY3QgYiB0YWtlcyBwcmVjZWRlbmNlLlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gYVxuXHQgKiBAcGFyYW0ge29iamVjdH0gYlxuXHQgKi9cblx0ZnVuY3Rpb24gZXh0ZW5kKCBhLCBiICkge1xuXG5cdFx0Zm9yKCB2YXIgaSBpbiBiICkge1xuXHRcdFx0YVsgaSBdID0gYlsgaSBdO1xuXHRcdH1cblxuXHRcdHJldHVybiBhO1xuXG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgdGhlIHRhcmdldCBvYmplY3QgdG8gYW4gYXJyYXkuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBvXG5cdCAqIEByZXR1cm4ge29iamVjdFtdfVxuXHQgKi9cblx0ZnVuY3Rpb24gdG9BcnJheSggbyApIHtcblxuXHRcdHJldHVybiBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCggbyApO1xuXG5cdH1cblxuXHQvKipcblx0ICogVXRpbGl0eSBmb3IgZGVzZXJpYWxpemluZyBhIHZhbHVlLlxuXHQgKlxuXHQgKiBAcGFyYW0geyp9IHZhbHVlXG5cdCAqIEByZXR1cm4geyp9XG5cdCAqL1xuXHRmdW5jdGlvbiBkZXNlcmlhbGl6ZSggdmFsdWUgKSB7XG5cblx0XHRpZiggdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyApIHtcblx0XHRcdGlmKCB2YWx1ZSA9PT0gJ251bGwnICkgcmV0dXJuIG51bGw7XG5cdFx0XHRlbHNlIGlmKCB2YWx1ZSA9PT0gJ3RydWUnICkgcmV0dXJuIHRydWU7XG5cdFx0XHRlbHNlIGlmKCB2YWx1ZSA9PT0gJ2ZhbHNlJyApIHJldHVybiBmYWxzZTtcblx0XHRcdGVsc2UgaWYoIHZhbHVlLm1hdGNoKCAvXi0/W1xcZFxcLl0rJC8gKSApIHJldHVybiBwYXJzZUZsb2F0KCB2YWx1ZSApO1xuXHRcdH1cblxuXHRcdHJldHVybiB2YWx1ZTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIE1lYXN1cmVzIHRoZSBkaXN0YW5jZSBpbiBwaXhlbHMgYmV0d2VlbiBwb2ludCBhXG5cdCAqIGFuZCBwb2ludCBiLlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gYSBwb2ludCB3aXRoIHgveSBwcm9wZXJ0aWVzXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBiIHBvaW50IHdpdGggeC95IHByb3BlcnRpZXNcblx0ICpcblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0ZnVuY3Rpb24gZGlzdGFuY2VCZXR3ZWVuKCBhLCBiICkge1xuXG5cdFx0dmFyIGR4ID0gYS54IC0gYi54LFxuXHRcdFx0ZHkgPSBhLnkgLSBiLnk7XG5cblx0XHRyZXR1cm4gTWF0aC5zcXJ0KCBkeCpkeCArIGR5KmR5ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBBcHBsaWVzIGEgQ1NTIHRyYW5zZm9ybSB0byB0aGUgdGFyZ2V0IGVsZW1lbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsZW1lbnRcblx0ICogQHBhcmFtIHtzdHJpbmd9IHRyYW5zZm9ybVxuXHQgKi9cblx0ZnVuY3Rpb24gdHJhbnNmb3JtRWxlbWVudCggZWxlbWVudCwgdHJhbnNmb3JtICkge1xuXG5cdFx0ZWxlbWVudC5zdHlsZS5XZWJraXRUcmFuc2Zvcm0gPSB0cmFuc2Zvcm07XG5cdFx0ZWxlbWVudC5zdHlsZS5Nb3pUcmFuc2Zvcm0gPSB0cmFuc2Zvcm07XG5cdFx0ZWxlbWVudC5zdHlsZS5tc1RyYW5zZm9ybSA9IHRyYW5zZm9ybTtcblx0XHRlbGVtZW50LnN0eWxlLnRyYW5zZm9ybSA9IHRyYW5zZm9ybTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgQ1NTIHRyYW5zZm9ybXMgdG8gdGhlIHNsaWRlcyBjb250YWluZXIuIFRoZSBjb250YWluZXJcblx0ICogaXMgdHJhbnNmb3JtZWQgZnJvbSB0d28gc2VwYXJhdGUgc291cmNlczogbGF5b3V0IGFuZCB0aGUgb3ZlcnZpZXdcblx0ICogbW9kZS5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IHRyYW5zZm9ybXNcblx0ICovXG5cdGZ1bmN0aW9uIHRyYW5zZm9ybVNsaWRlcyggdHJhbnNmb3JtcyApIHtcblxuXHRcdC8vIFBpY2sgdXAgbmV3IHRyYW5zZm9ybXMgZnJvbSBhcmd1bWVudHNcblx0XHRpZiggdHlwZW9mIHRyYW5zZm9ybXMubGF5b3V0ID09PSAnc3RyaW5nJyApIHNsaWRlc1RyYW5zZm9ybS5sYXlvdXQgPSB0cmFuc2Zvcm1zLmxheW91dDtcblx0XHRpZiggdHlwZW9mIHRyYW5zZm9ybXMub3ZlcnZpZXcgPT09ICdzdHJpbmcnICkgc2xpZGVzVHJhbnNmb3JtLm92ZXJ2aWV3ID0gdHJhbnNmb3Jtcy5vdmVydmlldztcblxuXHRcdC8vIEFwcGx5IHRoZSB0cmFuc2Zvcm1zIHRvIHRoZSBzbGlkZXMgY29udGFpbmVyXG5cdFx0aWYoIHNsaWRlc1RyYW5zZm9ybS5sYXlvdXQgKSB7XG5cdFx0XHR0cmFuc2Zvcm1FbGVtZW50KCBkb20uc2xpZGVzLCBzbGlkZXNUcmFuc2Zvcm0ubGF5b3V0ICsgJyAnICsgc2xpZGVzVHJhbnNmb3JtLm92ZXJ2aWV3ICk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0dHJhbnNmb3JtRWxlbWVudCggZG9tLnNsaWRlcywgc2xpZGVzVHJhbnNmb3JtLm92ZXJ2aWV3ICk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogSW5qZWN0cyB0aGUgZ2l2ZW4gQ1NTIHN0eWxlcyBpbnRvIHRoZSBET00uXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZVxuXHQgKi9cblx0ZnVuY3Rpb24gaW5qZWN0U3R5bGVTaGVldCggdmFsdWUgKSB7XG5cblx0XHR2YXIgdGFnID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ3N0eWxlJyApO1xuXHRcdHRhZy50eXBlID0gJ3RleHQvY3NzJztcblx0XHRpZiggdGFnLnN0eWxlU2hlZXQgKSB7XG5cdFx0XHR0YWcuc3R5bGVTaGVldC5jc3NUZXh0ID0gdmFsdWU7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0dGFnLmFwcGVuZENoaWxkKCBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSggdmFsdWUgKSApO1xuXHRcdH1cblx0XHRkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSggJ2hlYWQnIClbMF0uYXBwZW5kQ2hpbGQoIHRhZyApO1xuXG5cdH1cblxuXHQvKipcblx0ICogRmluZCB0aGUgY2xvc2VzdCBwYXJlbnQgdGhhdCBtYXRjaGVzIHRoZSBnaXZlblxuXHQgKiBzZWxlY3Rvci5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gdGFyZ2V0IFRoZSBjaGlsZCBlbGVtZW50XG5cdCAqIEBwYXJhbSB7U3RyaW5nfSBzZWxlY3RvciBUaGUgQ1NTIHNlbGVjdG9yIHRvIG1hdGNoXG5cdCAqIHRoZSBwYXJlbnRzIGFnYWluc3Rcblx0ICpcblx0ICogQHJldHVybiB7SFRNTEVsZW1lbnR9IFRoZSBtYXRjaGVkIHBhcmVudCBvciBudWxsXG5cdCAqIGlmIG5vIG1hdGNoaW5nIHBhcmVudCB3YXMgZm91bmRcblx0ICovXG5cdGZ1bmN0aW9uIGNsb3Nlc3RQYXJlbnQoIHRhcmdldCwgc2VsZWN0b3IgKSB7XG5cblx0XHR2YXIgcGFyZW50ID0gdGFyZ2V0LnBhcmVudE5vZGU7XG5cblx0XHR3aGlsZSggcGFyZW50ICkge1xuXG5cdFx0XHQvLyBUaGVyZSdzIHNvbWUgb3ZlcmhlYWQgZG9pbmcgdGhpcyBlYWNoIHRpbWUsIHdlIGRvbid0XG5cdFx0XHQvLyB3YW50IHRvIHJld3JpdGUgdGhlIGVsZW1lbnQgcHJvdG90eXBlIGJ1dCBzaG91bGQgc3RpbGxcblx0XHRcdC8vIGJlIGVub3VnaCB0byBmZWF0dXJlIGRldGVjdCBvbmNlIGF0IHN0YXJ0dXAuLi5cblx0XHRcdHZhciBtYXRjaGVzTWV0aG9kID0gcGFyZW50Lm1hdGNoZXMgfHwgcGFyZW50Lm1hdGNoZXNTZWxlY3RvciB8fCBwYXJlbnQubXNNYXRjaGVzU2VsZWN0b3I7XG5cblx0XHRcdC8vIElmIHdlIGZpbmQgYSBtYXRjaCwgd2UncmUgYWxsIHNldFxuXHRcdFx0aWYoIG1hdGNoZXNNZXRob2QgJiYgbWF0Y2hlc01ldGhvZC5jYWxsKCBwYXJlbnQsIHNlbGVjdG9yICkgKSB7XG5cdFx0XHRcdHJldHVybiBwYXJlbnQ7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEtlZXAgc2VhcmNoaW5nXG5cdFx0XHRwYXJlbnQgPSBwYXJlbnQucGFyZW50Tm9kZTtcblxuXHRcdH1cblxuXHRcdHJldHVybiBudWxsO1xuXG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgdmFyaW91cyBjb2xvciBpbnB1dCBmb3JtYXRzIHRvIGFuIHtyOjAsZzowLGI6MH0gb2JqZWN0LlxuXHQgKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gY29sb3IgVGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBhIGNvbG9yXG5cdCAqIEBleGFtcGxlXG5cdCAqIGNvbG9yVG9SZ2IoJyMwMDAnKTtcblx0ICogQGV4YW1wbGVcblx0ICogY29sb3JUb1JnYignIzAwMDAwMCcpO1xuXHQgKiBAZXhhbXBsZVxuXHQgKiBjb2xvclRvUmdiKCdyZ2IoMCwwLDApJyk7XG5cdCAqIEBleGFtcGxlXG5cdCAqIGNvbG9yVG9SZ2IoJ3JnYmEoMCwwLDApJyk7XG5cdCAqXG5cdCAqIEByZXR1cm4ge3tyOiBudW1iZXIsIGc6IG51bWJlciwgYjogbnVtYmVyLCBbYV06IG51bWJlcn18bnVsbH1cblx0ICovXG5cdGZ1bmN0aW9uIGNvbG9yVG9SZ2IoIGNvbG9yICkge1xuXG5cdFx0dmFyIGhleDMgPSBjb2xvci5tYXRjaCggL14jKFswLTlhLWZdezN9KSQvaSApO1xuXHRcdGlmKCBoZXgzICYmIGhleDNbMV0gKSB7XG5cdFx0XHRoZXgzID0gaGV4M1sxXTtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdHI6IHBhcnNlSW50KCBoZXgzLmNoYXJBdCggMCApLCAxNiApICogMHgxMSxcblx0XHRcdFx0ZzogcGFyc2VJbnQoIGhleDMuY2hhckF0KCAxICksIDE2ICkgKiAweDExLFxuXHRcdFx0XHRiOiBwYXJzZUludCggaGV4My5jaGFyQXQoIDIgKSwgMTYgKSAqIDB4MTFcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0dmFyIGhleDYgPSBjb2xvci5tYXRjaCggL14jKFswLTlhLWZdezZ9KSQvaSApO1xuXHRcdGlmKCBoZXg2ICYmIGhleDZbMV0gKSB7XG5cdFx0XHRoZXg2ID0gaGV4NlsxXTtcblx0XHRcdHJldHVybiB7XG5cdFx0XHRcdHI6IHBhcnNlSW50KCBoZXg2LnN1YnN0ciggMCwgMiApLCAxNiApLFxuXHRcdFx0XHRnOiBwYXJzZUludCggaGV4Ni5zdWJzdHIoIDIsIDIgKSwgMTYgKSxcblx0XHRcdFx0YjogcGFyc2VJbnQoIGhleDYuc3Vic3RyKCA0LCAyICksIDE2IClcblx0XHRcdH07XG5cdFx0fVxuXG5cdFx0dmFyIHJnYiA9IGNvbG9yLm1hdGNoKCAvXnJnYlxccypcXChcXHMqKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKlxcKSQvaSApO1xuXHRcdGlmKCByZ2IgKSB7XG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRyOiBwYXJzZUludCggcmdiWzFdLCAxMCApLFxuXHRcdFx0XHRnOiBwYXJzZUludCggcmdiWzJdLCAxMCApLFxuXHRcdFx0XHRiOiBwYXJzZUludCggcmdiWzNdLCAxMCApXG5cdFx0XHR9O1xuXHRcdH1cblxuXHRcdHZhciByZ2JhID0gY29sb3IubWF0Y2goIC9ecmdiYVxccypcXChcXHMqKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKixcXHMqKFxcZCspXFxzKlxcLFxccyooW1xcZF0rfFtcXGRdKi5bXFxkXSspXFxzKlxcKSQvaSApO1xuXHRcdGlmKCByZ2JhICkge1xuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0cjogcGFyc2VJbnQoIHJnYmFbMV0sIDEwICksXG5cdFx0XHRcdGc6IHBhcnNlSW50KCByZ2JhWzJdLCAxMCApLFxuXHRcdFx0XHRiOiBwYXJzZUludCggcmdiYVszXSwgMTAgKSxcblx0XHRcdFx0YTogcGFyc2VGbG9hdCggcmdiYVs0XSApXG5cdFx0XHR9O1xuXHRcdH1cblxuXHRcdHJldHVybiBudWxsO1xuXG5cdH1cblxuXHQvKipcblx0ICogQ2FsY3VsYXRlcyBicmlnaHRuZXNzIG9uIGEgc2NhbGUgb2YgMC0yNTUuXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBjb2xvciBTZWUgY29sb3JUb1JnYiBmb3Igc3VwcG9ydGVkIGZvcm1hdHMuXG5cdCAqIEBzZWUge0BsaW5rIGNvbG9yVG9SZ2J9XG5cdCAqL1xuXHRmdW5jdGlvbiBjb2xvckJyaWdodG5lc3MoIGNvbG9yICkge1xuXG5cdFx0aWYoIHR5cGVvZiBjb2xvciA9PT0gJ3N0cmluZycgKSBjb2xvciA9IGNvbG9yVG9SZ2IoIGNvbG9yICk7XG5cblx0XHRpZiggY29sb3IgKSB7XG5cdFx0XHRyZXR1cm4gKCBjb2xvci5yICogMjk5ICsgY29sb3IuZyAqIDU4NyArIGNvbG9yLmIgKiAxMTQgKSAvIDEwMDA7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG51bGw7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXR1cm5zIHRoZSByZW1haW5pbmcgaGVpZ2h0IHdpdGhpbiB0aGUgcGFyZW50IG9mIHRoZVxuXHQgKiB0YXJnZXQgZWxlbWVudC5cblx0ICpcblx0ICogcmVtYWluaW5nIGhlaWdodCA9IFsgY29uZmlndXJlZCBwYXJlbnQgaGVpZ2h0IF0gLSBbIGN1cnJlbnQgcGFyZW50IGhlaWdodCBdXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGVsZW1lbnRcblx0ICogQHBhcmFtIHtudW1iZXJ9IFtoZWlnaHRdXG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRSZW1haW5pbmdIZWlnaHQoIGVsZW1lbnQsIGhlaWdodCApIHtcblxuXHRcdGhlaWdodCA9IGhlaWdodCB8fCAwO1xuXG5cdFx0aWYoIGVsZW1lbnQgKSB7XG5cdFx0XHR2YXIgbmV3SGVpZ2h0LCBvbGRIZWlnaHQgPSBlbGVtZW50LnN0eWxlLmhlaWdodDtcblxuXHRcdFx0Ly8gQ2hhbmdlIHRoZSAuc3RyZXRjaCBlbGVtZW50IGhlaWdodCB0byAwIGluIG9yZGVyIGZpbmQgdGhlIGhlaWdodCBvZiBhbGxcblx0XHRcdC8vIHRoZSBvdGhlciBlbGVtZW50c1xuXHRcdFx0ZWxlbWVudC5zdHlsZS5oZWlnaHQgPSAnMHB4Jztcblx0XHRcdG5ld0hlaWdodCA9IGhlaWdodCAtIGVsZW1lbnQucGFyZW50Tm9kZS5vZmZzZXRIZWlnaHQ7XG5cblx0XHRcdC8vIFJlc3RvcmUgdGhlIG9sZCBoZWlnaHQsIGp1c3QgaW4gY2FzZVxuXHRcdFx0ZWxlbWVudC5zdHlsZS5oZWlnaHQgPSBvbGRIZWlnaHQgKyAncHgnO1xuXG5cdFx0XHRyZXR1cm4gbmV3SGVpZ2h0O1xuXHRcdH1cblxuXHRcdHJldHVybiBoZWlnaHQ7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgdGhpcyBpbnN0YW5jZSBpcyBiZWluZyB1c2VkIHRvIHByaW50IGEgUERGLlxuXHQgKi9cblx0ZnVuY3Rpb24gaXNQcmludGluZ1BERigpIHtcblxuXHRcdHJldHVybiAoIC9wcmludC1wZGYvZ2kgKS50ZXN0KCB3aW5kb3cubG9jYXRpb24uc2VhcmNoICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIaWRlcyB0aGUgYWRkcmVzcyBiYXIgaWYgd2UncmUgb24gYSBtb2JpbGUgZGV2aWNlLlxuXHQgKi9cblx0ZnVuY3Rpb24gaGlkZUFkZHJlc3NCYXIoKSB7XG5cblx0XHRpZiggY29uZmlnLmhpZGVBZGRyZXNzQmFyICYmIGlzTW9iaWxlRGV2aWNlICkge1xuXHRcdFx0Ly8gRXZlbnRzIHRoYXQgc2hvdWxkIHRyaWdnZXIgdGhlIGFkZHJlc3MgYmFyIHRvIGhpZGVcblx0XHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCAnbG9hZCcsIHJlbW92ZUFkZHJlc3NCYXIsIGZhbHNlICk7XG5cdFx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lciggJ29yaWVudGF0aW9uY2hhbmdlJywgcmVtb3ZlQWRkcmVzc0JhciwgZmFsc2UgKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDYXVzZXMgdGhlIGFkZHJlc3MgYmFyIHRvIGhpZGUgb24gbW9iaWxlIGRldmljZXMsXG5cdCAqIG1vcmUgdmVydGljYWwgc3BhY2UgZnR3LlxuXHQgKi9cblx0ZnVuY3Rpb24gcmVtb3ZlQWRkcmVzc0JhcigpIHtcblxuXHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uKCkge1xuXHRcdFx0d2luZG93LnNjcm9sbFRvKCAwLCAxICk7XG5cdFx0fSwgMTAgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIERpc3BhdGNoZXMgYW4gZXZlbnQgb2YgdGhlIHNwZWNpZmllZCB0eXBlIGZyb20gdGhlXG5cdCAqIHJldmVhbCBET00gZWxlbWVudC5cblx0ICovXG5cdGZ1bmN0aW9uIGRpc3BhdGNoRXZlbnQoIHR5cGUsIGFyZ3MgKSB7XG5cblx0XHR2YXIgZXZlbnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudCggJ0hUTUxFdmVudHMnLCAxLCAyICk7XG5cdFx0ZXZlbnQuaW5pdEV2ZW50KCB0eXBlLCB0cnVlLCB0cnVlICk7XG5cdFx0ZXh0ZW5kKCBldmVudCwgYXJncyApO1xuXHRcdGRvbS53cmFwcGVyLmRpc3BhdGNoRXZlbnQoIGV2ZW50ICk7XG5cblx0XHQvLyBJZiB3ZSdyZSBpbiBhbiBpZnJhbWUsIHBvc3QgZWFjaCByZXZlYWwuanMgZXZlbnQgdG8gdGhlXG5cdFx0Ly8gcGFyZW50IHdpbmRvdy4gVXNlZCBieSB0aGUgbm90ZXMgcGx1Z2luXG5cdFx0aWYoIGNvbmZpZy5wb3N0TWVzc2FnZUV2ZW50cyAmJiB3aW5kb3cucGFyZW50ICE9PSB3aW5kb3cuc2VsZiApIHtcblx0XHRcdHdpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UoIEpTT04uc3RyaW5naWZ5KHsgbmFtZXNwYWNlOiAncmV2ZWFsJywgZXZlbnROYW1lOiB0eXBlLCBzdGF0ZTogZ2V0U3RhdGUoKSB9KSwgJyonICk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogV3JhcCBhbGwgbGlua3MgaW4gM0QgZ29vZG5lc3MuXG5cdCAqL1xuXHRmdW5jdGlvbiBlbmFibGVSb2xsaW5nTGlua3MoKSB7XG5cblx0XHRpZiggZmVhdHVyZXMudHJhbnNmb3JtczNkICYmICEoICdtc1BlcnNwZWN0aXZlJyBpbiBkb2N1bWVudC5ib2R5LnN0eWxlICkgKSB7XG5cdFx0XHR2YXIgYW5jaG9ycyA9IGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIFNMSURFU19TRUxFQ1RPUiArICcgYScgKTtcblxuXHRcdFx0Zm9yKCB2YXIgaSA9IDAsIGxlbiA9IGFuY2hvcnMubGVuZ3RoOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdHZhciBhbmNob3IgPSBhbmNob3JzW2ldO1xuXG5cdFx0XHRcdGlmKCBhbmNob3IudGV4dENvbnRlbnQgJiYgIWFuY2hvci5xdWVyeVNlbGVjdG9yKCAnKicgKSAmJiAoICFhbmNob3IuY2xhc3NOYW1lIHx8ICFhbmNob3IuY2xhc3NMaXN0LmNvbnRhaW5zKCBhbmNob3IsICdyb2xsJyApICkgKSB7XG5cdFx0XHRcdFx0dmFyIHNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzcGFuJyk7XG5cdFx0XHRcdFx0c3Bhbi5zZXRBdHRyaWJ1dGUoJ2RhdGEtdGl0bGUnLCBhbmNob3IudGV4dCk7XG5cdFx0XHRcdFx0c3Bhbi5pbm5lckhUTUwgPSBhbmNob3IuaW5uZXJIVE1MO1xuXG5cdFx0XHRcdFx0YW5jaG9yLmNsYXNzTGlzdC5hZGQoICdyb2xsJyApO1xuXHRcdFx0XHRcdGFuY2hvci5pbm5lckhUTUwgPSAnJztcblx0XHRcdFx0XHRhbmNob3IuYXBwZW5kQ2hpbGQoc3Bhbik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVbndyYXAgYWxsIDNEIGxpbmtzLlxuXHQgKi9cblx0ZnVuY3Rpb24gZGlzYWJsZVJvbGxpbmdMaW5rcygpIHtcblxuXHRcdHZhciBhbmNob3JzID0gZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICsgJyBhLnJvbGwnICk7XG5cblx0XHRmb3IoIHZhciBpID0gMCwgbGVuID0gYW5jaG9ycy5sZW5ndGg7IGkgPCBsZW47IGkrKyApIHtcblx0XHRcdHZhciBhbmNob3IgPSBhbmNob3JzW2ldO1xuXHRcdFx0dmFyIHNwYW4gPSBhbmNob3IucXVlcnlTZWxlY3RvciggJ3NwYW4nICk7XG5cblx0XHRcdGlmKCBzcGFuICkge1xuXHRcdFx0XHRhbmNob3IuY2xhc3NMaXN0LnJlbW92ZSggJ3JvbGwnICk7XG5cdFx0XHRcdGFuY2hvci5pbm5lckhUTUwgPSBzcGFuLmlubmVySFRNTDtcblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBCaW5kIHByZXZpZXcgZnJhbWUgbGlua3MuXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBbc2VsZWN0b3I9YV0gLSBzZWxlY3RvciBmb3IgYW5jaG9yc1xuXHQgKi9cblx0ZnVuY3Rpb24gZW5hYmxlUHJldmlld0xpbmtzKCBzZWxlY3RvciApIHtcblxuXHRcdHZhciBhbmNob3JzID0gdG9BcnJheSggZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbCggc2VsZWN0b3IgPyBzZWxlY3RvciA6ICdhJyApICk7XG5cblx0XHRhbmNob3JzLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdFx0aWYoIC9eKGh0dHB8d3d3KS9naS50ZXN0KCBlbGVtZW50LmdldEF0dHJpYnV0ZSggJ2hyZWYnICkgKSApIHtcblx0XHRcdFx0ZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCBvblByZXZpZXdMaW5rQ2xpY2tlZCwgZmFsc2UgKTtcblx0XHRcdH1cblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVbmJpbmQgcHJldmlldyBmcmFtZSBsaW5rcy5cblx0ICovXG5cdGZ1bmN0aW9uIGRpc2FibGVQcmV2aWV3TGlua3MoIHNlbGVjdG9yICkge1xuXG5cdFx0dmFyIGFuY2hvcnMgPSB0b0FycmF5KCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCBzZWxlY3RvciA/IHNlbGVjdG9yIDogJ2EnICkgKTtcblxuXHRcdGFuY2hvcnMuZm9yRWFjaCggZnVuY3Rpb24oIGVsZW1lbnQgKSB7XG5cdFx0XHRpZiggL14oaHR0cHx3d3cpL2dpLnRlc3QoIGVsZW1lbnQuZ2V0QXR0cmlidXRlKCAnaHJlZicgKSApICkge1xuXHRcdFx0XHRlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdjbGljaycsIG9uUHJldmlld0xpbmtDbGlja2VkLCBmYWxzZSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIE9wZW5zIGEgcHJldmlldyB3aW5kb3cgZm9yIHRoZSB0YXJnZXQgVVJMLlxuXHQgKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gdXJsIGZvciBwcmV2aWV3IGlmcmFtZSBzcmNcblx0ICovXG5cdGZ1bmN0aW9uIHNob3dQcmV2aWV3KCB1cmwgKSB7XG5cblx0XHRjbG9zZU92ZXJsYXkoKTtcblxuXHRcdGRvbS5vdmVybGF5ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2RpdicgKTtcblx0XHRkb20ub3ZlcmxheS5jbGFzc0xpc3QuYWRkKCAnb3ZlcmxheScgKTtcblx0XHRkb20ub3ZlcmxheS5jbGFzc0xpc3QuYWRkKCAnb3ZlcmxheS1wcmV2aWV3JyApO1xuXHRcdGRvbS53cmFwcGVyLmFwcGVuZENoaWxkKCBkb20ub3ZlcmxheSApO1xuXG5cdFx0ZG9tLm92ZXJsYXkuaW5uZXJIVE1MID0gW1xuXHRcdFx0JzxoZWFkZXI+Jyxcblx0XHRcdFx0JzxhIGNsYXNzPVwiY2xvc2VcIiBocmVmPVwiI1wiPjxzcGFuIGNsYXNzPVwiaWNvblwiPjwvc3Bhbj48L2E+Jyxcblx0XHRcdFx0JzxhIGNsYXNzPVwiZXh0ZXJuYWxcIiBocmVmPVwiJysgdXJsICsnXCIgdGFyZ2V0PVwiX2JsYW5rXCI+PHNwYW4gY2xhc3M9XCJpY29uXCI+PC9zcGFuPjwvYT4nLFxuXHRcdFx0JzwvaGVhZGVyPicsXG5cdFx0XHQnPGRpdiBjbGFzcz1cInNwaW5uZXJcIj48L2Rpdj4nLFxuXHRcdFx0JzxkaXYgY2xhc3M9XCJ2aWV3cG9ydFwiPicsXG5cdFx0XHRcdCc8aWZyYW1lIHNyYz1cIicrIHVybCArJ1wiPjwvaWZyYW1lPicsXG5cdFx0XHRcdCc8c21hbGwgY2xhc3M9XCJ2aWV3cG9ydC1pbm5lclwiPicsXG5cdFx0XHRcdFx0JzxzcGFuIGNsYXNzPVwieC1mcmFtZS1lcnJvclwiPlVuYWJsZSB0byBsb2FkIGlmcmFtZS4gVGhpcyBpcyBsaWtlbHkgZHVlIHRvIHRoZSBzaXRlXFwncyBwb2xpY3kgKHgtZnJhbWUtb3B0aW9ucykuPC9zcGFuPicsXG5cdFx0XHRcdCc8L3NtYWxsPicsXG5cdFx0XHQnPC9kaXY+J1xuXHRcdF0uam9pbignJyk7XG5cblx0XHRkb20ub3ZlcmxheS5xdWVyeVNlbGVjdG9yKCAnaWZyYW1lJyApLmFkZEV2ZW50TGlzdGVuZXIoICdsb2FkJywgZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0ZG9tLm92ZXJsYXkuY2xhc3NMaXN0LmFkZCggJ2xvYWRlZCcgKTtcblx0XHR9LCBmYWxzZSApO1xuXG5cdFx0ZG9tLm92ZXJsYXkucXVlcnlTZWxlY3RvciggJy5jbG9zZScgKS5hZGRFdmVudExpc3RlbmVyKCAnY2xpY2snLCBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHRjbG9zZU92ZXJsYXkoKTtcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fSwgZmFsc2UgKTtcblxuXHRcdGRvbS5vdmVybGF5LnF1ZXJ5U2VsZWN0b3IoICcuZXh0ZXJuYWwnICkuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0Y2xvc2VPdmVybGF5KCk7XG5cdFx0fSwgZmFsc2UgKTtcblxuXHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uKCkge1xuXHRcdFx0ZG9tLm92ZXJsYXkuY2xhc3NMaXN0LmFkZCggJ3Zpc2libGUnICk7XG5cdFx0fSwgMSApO1xuXG5cdH1cblxuXHQvKipcblx0ICogT3BlbiBvciBjbG9zZSBoZWxwIG92ZXJsYXkgd2luZG93LlxuXHQgKlxuXHQgKiBAcGFyYW0ge0Jvb2xlYW59IFtvdmVycmlkZV0gRmxhZyB3aGljaCBvdmVycmlkZXMgdGhlXG5cdCAqIHRvZ2dsZSBsb2dpYyBhbmQgZm9yY2libHkgc2V0cyB0aGUgZGVzaXJlZCBzdGF0ZS4gVHJ1ZSBtZWFuc1xuXHQgKiBoZWxwIGlzIG9wZW4sIGZhbHNlIG1lYW5zIGl0J3MgY2xvc2VkLlxuXHQgKi9cblx0ZnVuY3Rpb24gdG9nZ2xlSGVscCggb3ZlcnJpZGUgKXtcblxuXHRcdGlmKCB0eXBlb2Ygb3ZlcnJpZGUgPT09ICdib29sZWFuJyApIHtcblx0XHRcdG92ZXJyaWRlID8gc2hvd0hlbHAoKSA6IGNsb3NlT3ZlcmxheSgpO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGlmKCBkb20ub3ZlcmxheSApIHtcblx0XHRcdFx0Y2xvc2VPdmVybGF5KCk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0c2hvd0hlbHAoKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogT3BlbnMgYW4gb3ZlcmxheSB3aW5kb3cgd2l0aCBoZWxwIG1hdGVyaWFsLlxuXHQgKi9cblx0ZnVuY3Rpb24gc2hvd0hlbHAoKSB7XG5cblx0XHRpZiggY29uZmlnLmhlbHAgKSB7XG5cblx0XHRcdGNsb3NlT3ZlcmxheSgpO1xuXG5cdFx0XHRkb20ub3ZlcmxheSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdkaXYnICk7XG5cdFx0XHRkb20ub3ZlcmxheS5jbGFzc0xpc3QuYWRkKCAnb3ZlcmxheScgKTtcblx0XHRcdGRvbS5vdmVybGF5LmNsYXNzTGlzdC5hZGQoICdvdmVybGF5LWhlbHAnICk7XG5cdFx0XHRkb20ud3JhcHBlci5hcHBlbmRDaGlsZCggZG9tLm92ZXJsYXkgKTtcblxuXHRcdFx0dmFyIGh0bWwgPSAnPHAgY2xhc3M9XCJ0aXRsZVwiPktleWJvYXJkIFNob3J0Y3V0czwvcD48YnIvPic7XG5cblx0XHRcdGh0bWwgKz0gJzx0YWJsZT48dGg+S0VZPC90aD48dGg+QUNUSU9OPC90aD4nO1xuXHRcdFx0Zm9yKCB2YXIga2V5IGluIGtleWJvYXJkU2hvcnRjdXRzICkge1xuXHRcdFx0XHRodG1sICs9ICc8dHI+PHRkPicgKyBrZXkgKyAnPC90ZD48dGQ+JyArIGtleWJvYXJkU2hvcnRjdXRzWyBrZXkgXSArICc8L3RkPjwvdHI+Jztcblx0XHRcdH1cblxuXHRcdFx0aHRtbCArPSAnPC90YWJsZT4nO1xuXG5cdFx0XHRkb20ub3ZlcmxheS5pbm5lckhUTUwgPSBbXG5cdFx0XHRcdCc8aGVhZGVyPicsXG5cdFx0XHRcdFx0JzxhIGNsYXNzPVwiY2xvc2VcIiBocmVmPVwiI1wiPjxzcGFuIGNsYXNzPVwiaWNvblwiPjwvc3Bhbj48L2E+Jyxcblx0XHRcdFx0JzwvaGVhZGVyPicsXG5cdFx0XHRcdCc8ZGl2IGNsYXNzPVwidmlld3BvcnRcIj4nLFxuXHRcdFx0XHRcdCc8ZGl2IGNsYXNzPVwidmlld3BvcnQtaW5uZXJcIj4nKyBodG1sICsnPC9kaXY+Jyxcblx0XHRcdFx0JzwvZGl2Pidcblx0XHRcdF0uam9pbignJyk7XG5cblx0XHRcdGRvbS5vdmVybGF5LnF1ZXJ5U2VsZWN0b3IoICcuY2xvc2UnICkuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRjbG9zZU92ZXJsYXkoKTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdH0sIGZhbHNlICk7XG5cblx0XHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRkb20ub3ZlcmxheS5jbGFzc0xpc3QuYWRkKCAndmlzaWJsZScgKTtcblx0XHRcdH0sIDEgKTtcblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIENsb3NlcyBhbnkgY3VycmVudGx5IG9wZW4gb3ZlcmxheS5cblx0ICovXG5cdGZ1bmN0aW9uIGNsb3NlT3ZlcmxheSgpIHtcblxuXHRcdGlmKCBkb20ub3ZlcmxheSApIHtcblx0XHRcdGRvbS5vdmVybGF5LnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIGRvbS5vdmVybGF5ICk7XG5cdFx0XHRkb20ub3ZlcmxheSA9IG51bGw7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQXBwbGllcyBKYXZhU2NyaXB0LWNvbnRyb2xsZWQgbGF5b3V0IHJ1bGVzIHRvIHRoZVxuXHQgKiBwcmVzZW50YXRpb24uXG5cdCAqL1xuXHRmdW5jdGlvbiBsYXlvdXQoKSB7XG5cblx0XHRpZiggZG9tLndyYXBwZXIgJiYgIWlzUHJpbnRpbmdQREYoKSApIHtcblxuXHRcdFx0dmFyIHNpemUgPSBnZXRDb21wdXRlZFNsaWRlU2l6ZSgpO1xuXG5cdFx0XHQvLyBMYXlvdXQgdGhlIGNvbnRlbnRzIG9mIHRoZSBzbGlkZXNcblx0XHRcdGxheW91dFNsaWRlQ29udGVudHMoIGNvbmZpZy53aWR0aCwgY29uZmlnLmhlaWdodCApO1xuXG5cdFx0XHRkb20uc2xpZGVzLnN0eWxlLndpZHRoID0gc2l6ZS53aWR0aCArICdweCc7XG5cdFx0XHRkb20uc2xpZGVzLnN0eWxlLmhlaWdodCA9IHNpemUuaGVpZ2h0ICsgJ3B4JztcblxuXHRcdFx0Ly8gRGV0ZXJtaW5lIHNjYWxlIG9mIGNvbnRlbnQgdG8gZml0IHdpdGhpbiBhdmFpbGFibGUgc3BhY2Vcblx0XHRcdHNjYWxlID0gTWF0aC5taW4oIHNpemUucHJlc2VudGF0aW9uV2lkdGggLyBzaXplLndpZHRoLCBzaXplLnByZXNlbnRhdGlvbkhlaWdodCAvIHNpemUuaGVpZ2h0ICk7XG5cblx0XHRcdC8vIFJlc3BlY3QgbWF4L21pbiBzY2FsZSBzZXR0aW5nc1xuXHRcdFx0c2NhbGUgPSBNYXRoLm1heCggc2NhbGUsIGNvbmZpZy5taW5TY2FsZSApO1xuXHRcdFx0c2NhbGUgPSBNYXRoLm1pbiggc2NhbGUsIGNvbmZpZy5tYXhTY2FsZSApO1xuXG5cdFx0XHQvLyBEb24ndCBhcHBseSBhbnkgc2NhbGluZyBzdHlsZXMgaWYgc2NhbGUgaXMgMVxuXHRcdFx0aWYoIHNjYWxlID09PSAxICkge1xuXHRcdFx0XHRkb20uc2xpZGVzLnN0eWxlLnpvb20gPSAnJztcblx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS5sZWZ0ID0gJyc7XG5cdFx0XHRcdGRvbS5zbGlkZXMuc3R5bGUudG9wID0gJyc7XG5cdFx0XHRcdGRvbS5zbGlkZXMuc3R5bGUuYm90dG9tID0gJyc7XG5cdFx0XHRcdGRvbS5zbGlkZXMuc3R5bGUucmlnaHQgPSAnJztcblx0XHRcdFx0dHJhbnNmb3JtU2xpZGVzKCB7IGxheW91dDogJycgfSApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdC8vIFByZWZlciB6b29tIGZvciBzY2FsaW5nIHVwIHNvIHRoYXQgY29udGVudCByZW1haW5zIGNyaXNwLlxuXHRcdFx0XHQvLyBEb24ndCB1c2Ugem9vbSB0byBzY2FsZSBkb3duIHNpbmNlIHRoYXQgY2FuIGxlYWQgdG8gc2hpZnRzXG5cdFx0XHRcdC8vIGluIHRleHQgbGF5b3V0L2xpbmUgYnJlYWtzLlxuXHRcdFx0XHRpZiggc2NhbGUgPiAxICYmIGZlYXR1cmVzLnpvb20gKSB7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS56b29tID0gc2NhbGU7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS5sZWZ0ID0gJyc7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS50b3AgPSAnJztcblx0XHRcdFx0XHRkb20uc2xpZGVzLnN0eWxlLmJvdHRvbSA9ICcnO1xuXHRcdFx0XHRcdGRvbS5zbGlkZXMuc3R5bGUucmlnaHQgPSAnJztcblx0XHRcdFx0XHR0cmFuc2Zvcm1TbGlkZXMoIHsgbGF5b3V0OiAnJyB9ICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0Ly8gQXBwbHkgc2NhbGUgdHJhbnNmb3JtIGFzIGEgZmFsbGJhY2tcblx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS56b29tID0gJyc7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS5sZWZ0ID0gJzUwJSc7XG5cdFx0XHRcdFx0ZG9tLnNsaWRlcy5zdHlsZS50b3AgPSAnNTAlJztcblx0XHRcdFx0XHRkb20uc2xpZGVzLnN0eWxlLmJvdHRvbSA9ICdhdXRvJztcblx0XHRcdFx0XHRkb20uc2xpZGVzLnN0eWxlLnJpZ2h0ID0gJ2F1dG8nO1xuXHRcdFx0XHRcdHRyYW5zZm9ybVNsaWRlcyggeyBsYXlvdXQ6ICd0cmFuc2xhdGUoLTUwJSwgLTUwJSkgc2NhbGUoJysgc2NhbGUgKycpJyB9ICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gU2VsZWN0IGFsbCBzbGlkZXMsIHZlcnRpY2FsIGFuZCBob3Jpem9udGFsXG5cdFx0XHR2YXIgc2xpZGVzID0gdG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICkgKTtcblxuXHRcdFx0Zm9yKCB2YXIgaSA9IDAsIGxlbiA9IHNsaWRlcy5sZW5ndGg7IGkgPCBsZW47IGkrKyApIHtcblx0XHRcdFx0dmFyIHNsaWRlID0gc2xpZGVzWyBpIF07XG5cblx0XHRcdFx0Ly8gRG9uJ3QgYm90aGVyIHVwZGF0aW5nIGludmlzaWJsZSBzbGlkZXNcblx0XHRcdFx0aWYoIHNsaWRlLnN0eWxlLmRpc3BsYXkgPT09ICdub25lJyApIHtcblx0XHRcdFx0XHRjb250aW51ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCBjb25maWcuY2VudGVyIHx8IHNsaWRlLmNsYXNzTGlzdC5jb250YWlucyggJ2NlbnRlcicgKSApIHtcblx0XHRcdFx0XHQvLyBWZXJ0aWNhbCBzdGFja3MgYXJlIG5vdCBjZW50cmVkIHNpbmNlIHRoZWlyIHNlY3Rpb25cblx0XHRcdFx0XHQvLyBjaGlsZHJlbiB3aWxsIGJlXG5cdFx0XHRcdFx0aWYoIHNsaWRlLmNsYXNzTGlzdC5jb250YWlucyggJ3N0YWNrJyApICkge1xuXHRcdFx0XHRcdFx0c2xpZGUuc3R5bGUudG9wID0gMDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0XHRzbGlkZS5zdHlsZS50b3AgPSBNYXRoLm1heCggKCBzaXplLmhlaWdodCAtIHNsaWRlLnNjcm9sbEhlaWdodCApIC8gMiwgMCApICsgJ3B4Jztcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0c2xpZGUuc3R5bGUudG9wID0gJyc7XG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0XHR1cGRhdGVQcm9ncmVzcygpO1xuXHRcdFx0dXBkYXRlUGFyYWxsYXgoKTtcblxuXHRcdFx0aWYoIGlzT3ZlcnZpZXcoKSApIHtcblx0XHRcdFx0dXBkYXRlT3ZlcnZpZXcoKTtcblx0XHRcdH1cblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEFwcGxpZXMgbGF5b3V0IGxvZ2ljIHRvIHRoZSBjb250ZW50cyBvZiBhbGwgc2xpZGVzIGluXG5cdCAqIHRoZSBwcmVzZW50YXRpb24uXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfG51bWJlcn0gd2lkdGhcblx0ICogQHBhcmFtIHtzdHJpbmd8bnVtYmVyfSBoZWlnaHRcblx0ICovXG5cdGZ1bmN0aW9uIGxheW91dFNsaWRlQ29udGVudHMoIHdpZHRoLCBoZWlnaHQgKSB7XG5cblx0XHQvLyBIYW5kbGUgc2l6aW5nIG9mIGVsZW1lbnRzIHdpdGggdGhlICdzdHJldGNoJyBjbGFzc1xuXHRcdHRvQXJyYXkoIGRvbS5zbGlkZXMucXVlcnlTZWxlY3RvckFsbCggJ3NlY3Rpb24gPiAuc3RyZXRjaCcgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXG5cdFx0XHQvLyBEZXRlcm1pbmUgaG93IG11Y2ggdmVydGljYWwgc3BhY2Ugd2UgY2FuIHVzZVxuXHRcdFx0dmFyIHJlbWFpbmluZ0hlaWdodCA9IGdldFJlbWFpbmluZ0hlaWdodCggZWxlbWVudCwgaGVpZ2h0ICk7XG5cblx0XHRcdC8vIENvbnNpZGVyIHRoZSBhc3BlY3QgcmF0aW8gb2YgbWVkaWEgZWxlbWVudHNcblx0XHRcdGlmKCAvKGltZ3x2aWRlbykvZ2kudGVzdCggZWxlbWVudC5ub2RlTmFtZSApICkge1xuXHRcdFx0XHR2YXIgbncgPSBlbGVtZW50Lm5hdHVyYWxXaWR0aCB8fCBlbGVtZW50LnZpZGVvV2lkdGgsXG5cdFx0XHRcdFx0bmggPSBlbGVtZW50Lm5hdHVyYWxIZWlnaHQgfHwgZWxlbWVudC52aWRlb0hlaWdodDtcblxuXHRcdFx0XHR2YXIgZXMgPSBNYXRoLm1pbiggd2lkdGggLyBudywgcmVtYWluaW5nSGVpZ2h0IC8gbmggKTtcblxuXHRcdFx0XHRlbGVtZW50LnN0eWxlLndpZHRoID0gKCBudyAqIGVzICkgKyAncHgnO1xuXHRcdFx0XHRlbGVtZW50LnN0eWxlLmhlaWdodCA9ICggbmggKiBlcyApICsgJ3B4JztcblxuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdGVsZW1lbnQuc3R5bGUud2lkdGggPSB3aWR0aCArICdweCc7XG5cdFx0XHRcdGVsZW1lbnQuc3R5bGUuaGVpZ2h0ID0gcmVtYWluaW5nSGVpZ2h0ICsgJ3B4Jztcblx0XHRcdH1cblxuXHRcdH0gKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIENhbGN1bGF0ZXMgdGhlIGNvbXB1dGVkIHBpeGVsIHNpemUgb2Ygb3VyIHNsaWRlcy4gVGhlc2Vcblx0ICogdmFsdWVzIGFyZSBiYXNlZCBvbiB0aGUgd2lkdGggYW5kIGhlaWdodCBjb25maWd1cmF0aW9uXG5cdCAqIG9wdGlvbnMuXG5cdCAqXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBbcHJlc2VudGF0aW9uV2lkdGg9ZG9tLndyYXBwZXIub2Zmc2V0V2lkdGhdXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBbcHJlc2VudGF0aW9uSGVpZ2h0PWRvbS53cmFwcGVyLm9mZnNldEhlaWdodF1cblx0ICovXG5cdGZ1bmN0aW9uIGdldENvbXB1dGVkU2xpZGVTaXplKCBwcmVzZW50YXRpb25XaWR0aCwgcHJlc2VudGF0aW9uSGVpZ2h0ICkge1xuXG5cdFx0dmFyIHNpemUgPSB7XG5cdFx0XHQvLyBTbGlkZSBzaXplXG5cdFx0XHR3aWR0aDogY29uZmlnLndpZHRoLFxuXHRcdFx0aGVpZ2h0OiBjb25maWcuaGVpZ2h0LFxuXG5cdFx0XHQvLyBQcmVzZW50YXRpb24gc2l6ZVxuXHRcdFx0cHJlc2VudGF0aW9uV2lkdGg6IHByZXNlbnRhdGlvbldpZHRoIHx8IGRvbS53cmFwcGVyLm9mZnNldFdpZHRoLFxuXHRcdFx0cHJlc2VudGF0aW9uSGVpZ2h0OiBwcmVzZW50YXRpb25IZWlnaHQgfHwgZG9tLndyYXBwZXIub2Zmc2V0SGVpZ2h0XG5cdFx0fTtcblxuXHRcdC8vIFJlZHVjZSBhdmFpbGFibGUgc3BhY2UgYnkgbWFyZ2luXG5cdFx0c2l6ZS5wcmVzZW50YXRpb25XaWR0aCAtPSAoIHNpemUucHJlc2VudGF0aW9uV2lkdGggKiBjb25maWcubWFyZ2luICk7XG5cdFx0c2l6ZS5wcmVzZW50YXRpb25IZWlnaHQgLT0gKCBzaXplLnByZXNlbnRhdGlvbkhlaWdodCAqIGNvbmZpZy5tYXJnaW4gKTtcblxuXHRcdC8vIFNsaWRlIHdpZHRoIG1heSBiZSBhIHBlcmNlbnRhZ2Ugb2YgYXZhaWxhYmxlIHdpZHRoXG5cdFx0aWYoIHR5cGVvZiBzaXplLndpZHRoID09PSAnc3RyaW5nJyAmJiAvJSQvLnRlc3QoIHNpemUud2lkdGggKSApIHtcblx0XHRcdHNpemUud2lkdGggPSBwYXJzZUludCggc2l6ZS53aWR0aCwgMTAgKSAvIDEwMCAqIHNpemUucHJlc2VudGF0aW9uV2lkdGg7XG5cdFx0fVxuXG5cdFx0Ly8gU2xpZGUgaGVpZ2h0IG1heSBiZSBhIHBlcmNlbnRhZ2Ugb2YgYXZhaWxhYmxlIGhlaWdodFxuXHRcdGlmKCB0eXBlb2Ygc2l6ZS5oZWlnaHQgPT09ICdzdHJpbmcnICYmIC8lJC8udGVzdCggc2l6ZS5oZWlnaHQgKSApIHtcblx0XHRcdHNpemUuaGVpZ2h0ID0gcGFyc2VJbnQoIHNpemUuaGVpZ2h0LCAxMCApIC8gMTAwICogc2l6ZS5wcmVzZW50YXRpb25IZWlnaHQ7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHNpemU7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBTdG9yZXMgdGhlIHZlcnRpY2FsIGluZGV4IG9mIGEgc3RhY2sgc28gdGhhdCB0aGUgc2FtZVxuXHQgKiB2ZXJ0aWNhbCBzbGlkZSBjYW4gYmUgc2VsZWN0ZWQgd2hlbiBuYXZpZ2F0aW5nIHRvIGFuZFxuXHQgKiBmcm9tIHRoZSBzdGFjay5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gc3RhY2sgVGhlIHZlcnRpY2FsIHN0YWNrIGVsZW1lbnRcblx0ICogQHBhcmFtIHtzdHJpbmd8bnVtYmVyfSBbdj0wXSBJbmRleCB0byBtZW1vcml6ZVxuXHQgKi9cblx0ZnVuY3Rpb24gc2V0UHJldmlvdXNWZXJ0aWNhbEluZGV4KCBzdGFjaywgdiApIHtcblxuXHRcdGlmKCB0eXBlb2Ygc3RhY2sgPT09ICdvYmplY3QnICYmIHR5cGVvZiBzdGFjay5zZXRBdHRyaWJ1dGUgPT09ICdmdW5jdGlvbicgKSB7XG5cdFx0XHRzdGFjay5zZXRBdHRyaWJ1dGUoICdkYXRhLXByZXZpb3VzLWluZGV4dicsIHYgfHwgMCApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFJldHJpZXZlcyB0aGUgdmVydGljYWwgaW5kZXggd2hpY2ggd2FzIHN0b3JlZCB1c2luZ1xuXHQgKiAjc2V0UHJldmlvdXNWZXJ0aWNhbEluZGV4KCkgb3IgMCBpZiBubyBwcmV2aW91cyBpbmRleFxuXHQgKiBleGlzdHMuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHN0YWNrIFRoZSB2ZXJ0aWNhbCBzdGFjayBlbGVtZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRQcmV2aW91c1ZlcnRpY2FsSW5kZXgoIHN0YWNrICkge1xuXG5cdFx0aWYoIHR5cGVvZiBzdGFjayA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIHN0YWNrLnNldEF0dHJpYnV0ZSA9PT0gJ2Z1bmN0aW9uJyAmJiBzdGFjay5jbGFzc0xpc3QuY29udGFpbnMoICdzdGFjaycgKSApIHtcblx0XHRcdC8vIFByZWZlciBtYW51YWxseSBkZWZpbmVkIHN0YXJ0LWluZGV4dlxuXHRcdFx0dmFyIGF0dHJpYnV0ZU5hbWUgPSBzdGFjay5oYXNBdHRyaWJ1dGUoICdkYXRhLXN0YXJ0LWluZGV4dicgKSA/ICdkYXRhLXN0YXJ0LWluZGV4dicgOiAnZGF0YS1wcmV2aW91cy1pbmRleHYnO1xuXG5cdFx0XHRyZXR1cm4gcGFyc2VJbnQoIHN0YWNrLmdldEF0dHJpYnV0ZSggYXR0cmlidXRlTmFtZSApIHx8IDAsIDEwICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIDA7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBEaXNwbGF5cyB0aGUgb3ZlcnZpZXcgb2Ygc2xpZGVzIChxdWljayBuYXYpIGJ5IHNjYWxpbmdcblx0ICogZG93biBhbmQgYXJyYW5naW5nIGFsbCBzbGlkZSBlbGVtZW50cy5cblx0ICovXG5cdGZ1bmN0aW9uIGFjdGl2YXRlT3ZlcnZpZXcoKSB7XG5cblx0XHQvLyBPbmx5IHByb2NlZWQgaWYgZW5hYmxlZCBpbiBjb25maWdcblx0XHRpZiggY29uZmlnLm92ZXJ2aWV3ICYmICFpc092ZXJ2aWV3KCkgKSB7XG5cblx0XHRcdG92ZXJ2aWV3ID0gdHJ1ZTtcblxuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ292ZXJ2aWV3JyApO1xuXHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ292ZXJ2aWV3LWRlYWN0aXZhdGluZycgKTtcblxuXHRcdFx0aWYoIGZlYXR1cmVzLm92ZXJ2aWV3VHJhbnNpdGlvbnMgKSB7XG5cdFx0XHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5hZGQoICdvdmVydmlldy1hbmltYXRlZCcgKTtcblx0XHRcdFx0fSwgMSApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBEb24ndCBhdXRvLXNsaWRlIHdoaWxlIGluIG92ZXJ2aWV3IG1vZGVcblx0XHRcdGNhbmNlbEF1dG9TbGlkZSgpO1xuXG5cdFx0XHQvLyBNb3ZlIHRoZSBiYWNrZ3JvdW5kcyBlbGVtZW50IGludG8gdGhlIHNsaWRlIGNvbnRhaW5lciB0b1xuXHRcdFx0Ly8gdGhhdCB0aGUgc2FtZSBzY2FsaW5nIGlzIGFwcGxpZWRcblx0XHRcdGRvbS5zbGlkZXMuYXBwZW5kQ2hpbGQoIGRvbS5iYWNrZ3JvdW5kICk7XG5cblx0XHRcdC8vIENsaWNraW5nIG9uIGFuIG92ZXJ2aWV3IHNsaWRlIG5hdmlnYXRlcyB0byBpdFxuXHRcdFx0dG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggc2xpZGUgKSB7XG5cdFx0XHRcdGlmKCAhc2xpZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnc3RhY2snICkgKSB7XG5cdFx0XHRcdFx0c2xpZGUuYWRkRXZlbnRMaXN0ZW5lciggJ2NsaWNrJywgb25PdmVydmlld1NsaWRlQ2xpY2tlZCwgdHJ1ZSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cblx0XHRcdC8vIENhbGN1bGF0ZSBzbGlkZSBzaXplc1xuXHRcdFx0dmFyIG1hcmdpbiA9IDcwO1xuXHRcdFx0dmFyIHNsaWRlU2l6ZSA9IGdldENvbXB1dGVkU2xpZGVTaXplKCk7XG5cdFx0XHRvdmVydmlld1NsaWRlV2lkdGggPSBzbGlkZVNpemUud2lkdGggKyBtYXJnaW47XG5cdFx0XHRvdmVydmlld1NsaWRlSGVpZ2h0ID0gc2xpZGVTaXplLmhlaWdodCArIG1hcmdpbjtcblxuXHRcdFx0Ly8gUmV2ZXJzZSBpbiBSVEwgbW9kZVxuXHRcdFx0aWYoIGNvbmZpZy5ydGwgKSB7XG5cdFx0XHRcdG92ZXJ2aWV3U2xpZGVXaWR0aCA9IC1vdmVydmlld1NsaWRlV2lkdGg7XG5cdFx0XHR9XG5cblx0XHRcdHVwZGF0ZVNsaWRlc1Zpc2liaWxpdHkoKTtcblx0XHRcdGxheW91dE92ZXJ2aWV3KCk7XG5cdFx0XHR1cGRhdGVPdmVydmlldygpO1xuXG5cdFx0XHRsYXlvdXQoKTtcblxuXHRcdFx0Ly8gTm90aWZ5IG9ic2VydmVycyBvZiB0aGUgb3ZlcnZpZXcgc2hvd2luZ1xuXHRcdFx0ZGlzcGF0Y2hFdmVudCggJ292ZXJ2aWV3c2hvd24nLCB7XG5cdFx0XHRcdCdpbmRleGgnOiBpbmRleGgsXG5cdFx0XHRcdCdpbmRleHYnOiBpbmRleHYsXG5cdFx0XHRcdCdjdXJyZW50U2xpZGUnOiBjdXJyZW50U2xpZGVcblx0XHRcdH0gKTtcblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFVzZXMgQ1NTIHRyYW5zZm9ybXMgdG8gcG9zaXRpb24gYWxsIHNsaWRlcyBpbiBhIGdyaWQgZm9yXG5cdCAqIGRpc3BsYXkgaW5zaWRlIG9mIHRoZSBvdmVydmlldyBtb2RlLlxuXHQgKi9cblx0ZnVuY3Rpb24gbGF5b3V0T3ZlcnZpZXcoKSB7XG5cblx0XHQvLyBMYXlvdXQgc2xpZGVzXG5cdFx0dG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggSE9SSVpPTlRBTF9TTElERVNfU0VMRUNUT1IgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBoc2xpZGUsIGggKSB7XG5cdFx0XHRoc2xpZGUuc2V0QXR0cmlidXRlKCAnZGF0YS1pbmRleC1oJywgaCApO1xuXHRcdFx0dHJhbnNmb3JtRWxlbWVudCggaHNsaWRlLCAndHJhbnNsYXRlM2QoJyArICggaCAqIG92ZXJ2aWV3U2xpZGVXaWR0aCApICsgJ3B4LCAwLCAwKScgKTtcblxuXHRcdFx0aWYoIGhzbGlkZS5jbGFzc0xpc3QuY29udGFpbnMoICdzdGFjaycgKSApIHtcblxuXHRcdFx0XHR0b0FycmF5KCBoc2xpZGUucXVlcnlTZWxlY3RvckFsbCggJ3NlY3Rpb24nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggdnNsaWRlLCB2ICkge1xuXHRcdFx0XHRcdHZzbGlkZS5zZXRBdHRyaWJ1dGUoICdkYXRhLWluZGV4LWgnLCBoICk7XG5cdFx0XHRcdFx0dnNsaWRlLnNldEF0dHJpYnV0ZSggJ2RhdGEtaW5kZXgtdicsIHYgKTtcblxuXHRcdFx0XHRcdHRyYW5zZm9ybUVsZW1lbnQoIHZzbGlkZSwgJ3RyYW5zbGF0ZTNkKDAsICcgKyAoIHYgKiBvdmVydmlld1NsaWRlSGVpZ2h0ICkgKyAncHgsIDApJyApO1xuXHRcdFx0XHR9ICk7XG5cblx0XHRcdH1cblx0XHR9ICk7XG5cblx0XHQvLyBMYXlvdXQgc2xpZGUgYmFja2dyb3VuZHNcblx0XHR0b0FycmF5KCBkb20uYmFja2dyb3VuZC5jaGlsZE5vZGVzICkuZm9yRWFjaCggZnVuY3Rpb24oIGhiYWNrZ3JvdW5kLCBoICkge1xuXHRcdFx0dHJhbnNmb3JtRWxlbWVudCggaGJhY2tncm91bmQsICd0cmFuc2xhdGUzZCgnICsgKCBoICogb3ZlcnZpZXdTbGlkZVdpZHRoICkgKyAncHgsIDAsIDApJyApO1xuXG5cdFx0XHR0b0FycmF5KCBoYmFja2dyb3VuZC5xdWVyeVNlbGVjdG9yQWxsKCAnLnNsaWRlLWJhY2tncm91bmQnICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggdmJhY2tncm91bmQsIHYgKSB7XG5cdFx0XHRcdHRyYW5zZm9ybUVsZW1lbnQoIHZiYWNrZ3JvdW5kLCAndHJhbnNsYXRlM2QoMCwgJyArICggdiAqIG92ZXJ2aWV3U2xpZGVIZWlnaHQgKSArICdweCwgMCknICk7XG5cdFx0XHR9ICk7XG5cdFx0fSApO1xuXG5cdH1cblxuXHQvKipcblx0ICogTW92ZXMgdGhlIG92ZXJ2aWV3IHZpZXdwb3J0IHRvIHRoZSBjdXJyZW50IHNsaWRlcy5cblx0ICogQ2FsbGVkIGVhY2ggdGltZSB0aGUgY3VycmVudCBzbGlkZSBjaGFuZ2VzLlxuXHQgKi9cblx0ZnVuY3Rpb24gdXBkYXRlT3ZlcnZpZXcoKSB7XG5cblx0XHR2YXIgdm1pbiA9IE1hdGgubWluKCB3aW5kb3cuaW5uZXJXaWR0aCwgd2luZG93LmlubmVySGVpZ2h0ICk7XG5cdFx0dmFyIHNjYWxlID0gTWF0aC5tYXgoIHZtaW4gLyA1LCAxNTAgKSAvIHZtaW47XG5cblx0XHR0cmFuc2Zvcm1TbGlkZXMoIHtcblx0XHRcdG92ZXJ2aWV3OiBbXG5cdFx0XHRcdCdzY2FsZSgnKyBzY2FsZSArJyknLFxuXHRcdFx0XHQndHJhbnNsYXRlWCgnKyAoIC1pbmRleGggKiBvdmVydmlld1NsaWRlV2lkdGggKSArJ3B4KScsXG5cdFx0XHRcdCd0cmFuc2xhdGVZKCcrICggLWluZGV4diAqIG92ZXJ2aWV3U2xpZGVIZWlnaHQgKSArJ3B4KSdcblx0XHRcdF0uam9pbiggJyAnIClcblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBFeGl0cyB0aGUgc2xpZGUgb3ZlcnZpZXcgYW5kIGVudGVycyB0aGUgY3VycmVudGx5XG5cdCAqIGFjdGl2ZSBzbGlkZS5cblx0ICovXG5cdGZ1bmN0aW9uIGRlYWN0aXZhdGVPdmVydmlldygpIHtcblxuXHRcdC8vIE9ubHkgcHJvY2VlZCBpZiBlbmFibGVkIGluIGNvbmZpZ1xuXHRcdGlmKCBjb25maWcub3ZlcnZpZXcgKSB7XG5cblx0XHRcdG92ZXJ2aWV3ID0gZmFsc2U7XG5cblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoICdvdmVydmlldycgKTtcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoICdvdmVydmlldy1hbmltYXRlZCcgKTtcblxuXHRcdFx0Ly8gVGVtcG9yYXJpbHkgYWRkIGEgY2xhc3Mgc28gdGhhdCB0cmFuc2l0aW9ucyBjYW4gZG8gZGlmZmVyZW50IHRoaW5nc1xuXHRcdFx0Ly8gZGVwZW5kaW5nIG9uIHdoZXRoZXIgdGhleSBhcmUgZXhpdGluZy9lbnRlcmluZyBvdmVydmlldywgb3IganVzdFxuXHRcdFx0Ly8gbW92aW5nIGZyb20gc2xpZGUgdG8gc2xpZGVcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5hZGQoICdvdmVydmlldy1kZWFjdGl2YXRpbmcnICk7XG5cblx0XHRcdHNldFRpbWVvdXQoIGZ1bmN0aW9uICgpIHtcblx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ292ZXJ2aWV3LWRlYWN0aXZhdGluZycgKTtcblx0XHRcdH0sIDEgKTtcblxuXHRcdFx0Ly8gTW92ZSB0aGUgYmFja2dyb3VuZCBlbGVtZW50IGJhY2sgb3V0XG5cdFx0XHRkb20ud3JhcHBlci5hcHBlbmRDaGlsZCggZG9tLmJhY2tncm91bmQgKTtcblxuXHRcdFx0Ly8gQ2xlYW4gdXAgY2hhbmdlcyBtYWRlIHRvIHNsaWRlc1xuXHRcdFx0dG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggU0xJREVTX1NFTEVDVE9SICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggc2xpZGUgKSB7XG5cdFx0XHRcdHRyYW5zZm9ybUVsZW1lbnQoIHNsaWRlLCAnJyApO1xuXG5cdFx0XHRcdHNsaWRlLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdjbGljaycsIG9uT3ZlcnZpZXdTbGlkZUNsaWNrZWQsIHRydWUgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0Ly8gQ2xlYW4gdXAgY2hhbmdlcyBtYWRlIHRvIGJhY2tncm91bmRzXG5cdFx0XHR0b0FycmF5KCBkb20uYmFja2dyb3VuZC5xdWVyeVNlbGVjdG9yQWxsKCAnLnNsaWRlLWJhY2tncm91bmQnICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggYmFja2dyb3VuZCApIHtcblx0XHRcdFx0dHJhbnNmb3JtRWxlbWVudCggYmFja2dyb3VuZCwgJycgKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0dHJhbnNmb3JtU2xpZGVzKCB7IG92ZXJ2aWV3OiAnJyB9ICk7XG5cblx0XHRcdHNsaWRlKCBpbmRleGgsIGluZGV4diApO1xuXG5cdFx0XHRsYXlvdXQoKTtcblxuXHRcdFx0Y3VlQXV0b1NsaWRlKCk7XG5cblx0XHRcdC8vIE5vdGlmeSBvYnNlcnZlcnMgb2YgdGhlIG92ZXJ2aWV3IGhpZGluZ1xuXHRcdFx0ZGlzcGF0Y2hFdmVudCggJ292ZXJ2aWV3aGlkZGVuJywge1xuXHRcdFx0XHQnaW5kZXhoJzogaW5kZXhoLFxuXHRcdFx0XHQnaW5kZXh2JzogaW5kZXh2LFxuXHRcdFx0XHQnY3VycmVudFNsaWRlJzogY3VycmVudFNsaWRlXG5cdFx0XHR9ICk7XG5cblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogVG9nZ2xlcyB0aGUgc2xpZGUgb3ZlcnZpZXcgbW9kZSBvbiBhbmQgb2ZmLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0Jvb2xlYW59IFtvdmVycmlkZV0gRmxhZyB3aGljaCBvdmVycmlkZXMgdGhlXG5cdCAqIHRvZ2dsZSBsb2dpYyBhbmQgZm9yY2libHkgc2V0cyB0aGUgZGVzaXJlZCBzdGF0ZS4gVHJ1ZSBtZWFuc1xuXHQgKiBvdmVydmlldyBpcyBvcGVuLCBmYWxzZSBtZWFucyBpdCdzIGNsb3NlZC5cblx0ICovXG5cdGZ1bmN0aW9uIHRvZ2dsZU92ZXJ2aWV3KCBvdmVycmlkZSApIHtcblxuXHRcdGlmKCB0eXBlb2Ygb3ZlcnJpZGUgPT09ICdib29sZWFuJyApIHtcblx0XHRcdG92ZXJyaWRlID8gYWN0aXZhdGVPdmVydmlldygpIDogZGVhY3RpdmF0ZU92ZXJ2aWV3KCk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0aXNPdmVydmlldygpID8gZGVhY3RpdmF0ZU92ZXJ2aWV3KCkgOiBhY3RpdmF0ZU92ZXJ2aWV3KCk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQ2hlY2tzIGlmIHRoZSBvdmVydmlldyBpcyBjdXJyZW50bHkgYWN0aXZlLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtCb29sZWFufSB0cnVlIGlmIHRoZSBvdmVydmlldyBpcyBhY3RpdmUsXG5cdCAqIGZhbHNlIG90aGVyd2lzZVxuXHQgKi9cblx0ZnVuY3Rpb24gaXNPdmVydmlldygpIHtcblxuXHRcdHJldHVybiBvdmVydmlldztcblxuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGUgY3VycmVudCBvciBzcGVjaWZpZWQgc2xpZGUgaXMgdmVydGljYWxcblx0ICogKG5lc3RlZCB3aXRoaW4gYW5vdGhlciBzbGlkZSkuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IFtzbGlkZT1jdXJyZW50U2xpZGVdIFRoZSBzbGlkZSB0byBjaGVja1xuXHQgKiBvcmllbnRhdGlvbiBvZlxuXHQgKiBAcmV0dXJuIHtCb29sZWFufVxuXHQgKi9cblx0ZnVuY3Rpb24gaXNWZXJ0aWNhbFNsaWRlKCBzbGlkZSApIHtcblxuXHRcdC8vIFByZWZlciBzbGlkZSBhcmd1bWVudCwgb3RoZXJ3aXNlIHVzZSBjdXJyZW50IHNsaWRlXG5cdFx0c2xpZGUgPSBzbGlkZSA/IHNsaWRlIDogY3VycmVudFNsaWRlO1xuXG5cdFx0cmV0dXJuIHNsaWRlICYmIHNsaWRlLnBhcmVudE5vZGUgJiYgISFzbGlkZS5wYXJlbnROb2RlLm5vZGVOYW1lLm1hdGNoKCAvc2VjdGlvbi9pICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGluZyB0aGUgZnVsbHNjcmVlbiBmdW5jdGlvbmFsaXR5IHZpYSB0aGUgZnVsbHNjcmVlbiBBUElcblx0ICpcblx0ICogQHNlZSBodHRwOi8vZnVsbHNjcmVlbi5zcGVjLndoYXR3Zy5vcmcvXG5cdCAqIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9ET00vVXNpbmdfZnVsbHNjcmVlbl9tb2RlXG5cdCAqL1xuXHRmdW5jdGlvbiBlbnRlckZ1bGxzY3JlZW4oKSB7XG5cblx0XHR2YXIgZWxlbWVudCA9IGRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcblxuXHRcdC8vIENoZWNrIHdoaWNoIGltcGxlbWVudGF0aW9uIGlzIGF2YWlsYWJsZVxuXHRcdHZhciByZXF1ZXN0TWV0aG9kID0gZWxlbWVudC5yZXF1ZXN0RnVsbHNjcmVlbiB8fFxuXHRcdFx0XHRcdFx0XHRlbGVtZW50LndlYmtpdFJlcXVlc3RGdWxsc2NyZWVuIHx8XG5cdFx0XHRcdFx0XHRcdGVsZW1lbnQud2Via2l0UmVxdWVzdEZ1bGxTY3JlZW4gfHxcblx0XHRcdFx0XHRcdFx0ZWxlbWVudC5tb3pSZXF1ZXN0RnVsbFNjcmVlbiB8fFxuXHRcdFx0XHRcdFx0XHRlbGVtZW50Lm1zUmVxdWVzdEZ1bGxzY3JlZW47XG5cblx0XHRpZiggcmVxdWVzdE1ldGhvZCApIHtcblx0XHRcdHJlcXVlc3RNZXRob2QuYXBwbHkoIGVsZW1lbnQgKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBFbnRlcnMgdGhlIHBhdXNlZCBtb2RlIHdoaWNoIGZhZGVzIGV2ZXJ5dGhpbmcgb24gc2NyZWVuIHRvXG5cdCAqIGJsYWNrLlxuXHQgKi9cblx0ZnVuY3Rpb24gcGF1c2UoKSB7XG5cblx0XHRpZiggY29uZmlnLnBhdXNlICkge1xuXHRcdFx0dmFyIHdhc1BhdXNlZCA9IGRvbS53cmFwcGVyLmNsYXNzTGlzdC5jb250YWlucyggJ3BhdXNlZCcgKTtcblxuXHRcdFx0Y2FuY2VsQXV0b1NsaWRlKCk7XG5cdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QuYWRkKCAncGF1c2VkJyApO1xuXG5cdFx0XHRpZiggd2FzUGF1c2VkID09PSBmYWxzZSApIHtcblx0XHRcdFx0ZGlzcGF0Y2hFdmVudCggJ3BhdXNlZCcgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBFeGl0cyBmcm9tIHRoZSBwYXVzZWQgbW9kZS5cblx0ICovXG5cdGZ1bmN0aW9uIHJlc3VtZSgpIHtcblxuXHRcdHZhciB3YXNQYXVzZWQgPSBkb20ud3JhcHBlci5jbGFzc0xpc3QuY29udGFpbnMoICdwYXVzZWQnICk7XG5cdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ3BhdXNlZCcgKTtcblxuXHRcdGN1ZUF1dG9TbGlkZSgpO1xuXG5cdFx0aWYoIHdhc1BhdXNlZCApIHtcblx0XHRcdGRpc3BhdGNoRXZlbnQoICdyZXN1bWVkJyApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFRvZ2dsZXMgdGhlIHBhdXNlZCBtb2RlIG9uIGFuZCBvZmYuXG5cdCAqL1xuXHRmdW5jdGlvbiB0b2dnbGVQYXVzZSggb3ZlcnJpZGUgKSB7XG5cblx0XHRpZiggdHlwZW9mIG92ZXJyaWRlID09PSAnYm9vbGVhbicgKSB7XG5cdFx0XHRvdmVycmlkZSA/IHBhdXNlKCkgOiByZXN1bWUoKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRpc1BhdXNlZCgpID8gcmVzdW1lKCkgOiBwYXVzZSgpO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB3ZSBhcmUgY3VycmVudGx5IGluIHRoZSBwYXVzZWQgbW9kZS5cblx0ICpcblx0ICogQHJldHVybiB7Qm9vbGVhbn1cblx0ICovXG5cdGZ1bmN0aW9uIGlzUGF1c2VkKCkge1xuXG5cdFx0cmV0dXJuIGRvbS53cmFwcGVyLmNsYXNzTGlzdC5jb250YWlucyggJ3BhdXNlZCcgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIFRvZ2dsZXMgdGhlIGF1dG8gc2xpZGUgbW9kZSBvbiBhbmQgb2ZmLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0Jvb2xlYW59IFtvdmVycmlkZV0gRmxhZyB3aGljaCBzZXRzIHRoZSBkZXNpcmVkIHN0YXRlLlxuXHQgKiBUcnVlIG1lYW5zIGF1dG9wbGF5IHN0YXJ0cywgZmFsc2UgbWVhbnMgaXQgc3RvcHMuXG5cdCAqL1xuXG5cdGZ1bmN0aW9uIHRvZ2dsZUF1dG9TbGlkZSggb3ZlcnJpZGUgKSB7XG5cblx0XHRpZiggdHlwZW9mIG92ZXJyaWRlID09PSAnYm9vbGVhbicgKSB7XG5cdFx0XHRvdmVycmlkZSA/IHJlc3VtZUF1dG9TbGlkZSgpIDogcGF1c2VBdXRvU2xpZGUoKTtcblx0XHR9XG5cblx0XHRlbHNlIHtcblx0XHRcdGF1dG9TbGlkZVBhdXNlZCA/IHJlc3VtZUF1dG9TbGlkZSgpIDogcGF1c2VBdXRvU2xpZGUoKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgdGhlIGF1dG8gc2xpZGUgbW9kZSBpcyBjdXJyZW50bHkgb24uXG5cdCAqXG5cdCAqIEByZXR1cm4ge0Jvb2xlYW59XG5cdCAqL1xuXHRmdW5jdGlvbiBpc0F1dG9TbGlkaW5nKCkge1xuXG5cdFx0cmV0dXJuICEhKCBhdXRvU2xpZGUgJiYgIWF1dG9TbGlkZVBhdXNlZCApO1xuXG5cdH1cblxuXHQvKipcblx0ICogU3RlcHMgZnJvbSB0aGUgY3VycmVudCBwb2ludCBpbiB0aGUgcHJlc2VudGF0aW9uIHRvIHRoZVxuXHQgKiBzbGlkZSB3aGljaCBtYXRjaGVzIHRoZSBzcGVjaWZpZWQgaG9yaXpvbnRhbCBhbmQgdmVydGljYWxcblx0ICogaW5kaWNlcy5cblx0ICpcblx0ICogQHBhcmFtIHtudW1iZXJ9IFtoPWluZGV4aF0gSG9yaXpvbnRhbCBpbmRleCBvZiB0aGUgdGFyZ2V0IHNsaWRlXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBbdj1pbmRleHZdIFZlcnRpY2FsIGluZGV4IG9mIHRoZSB0YXJnZXQgc2xpZGVcblx0ICogQHBhcmFtIHtudW1iZXJ9IFtmXSBJbmRleCBvZiBhIGZyYWdtZW50IHdpdGhpbiB0aGVcblx0ICogdGFyZ2V0IHNsaWRlIHRvIGFjdGl2YXRlXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBbb10gT3JpZ2luIGZvciB1c2UgaW4gbXVsdGltYXN0ZXIgZW52aXJvbm1lbnRzXG5cdCAqL1xuXHRmdW5jdGlvbiBzbGlkZSggaCwgdiwgZiwgbyApIHtcblxuXHRcdC8vIFJlbWVtYmVyIHdoZXJlIHdlIHdlcmUgYXQgYmVmb3JlXG5cdFx0cHJldmlvdXNTbGlkZSA9IGN1cnJlbnRTbGlkZTtcblxuXHRcdC8vIFF1ZXJ5IGFsbCBob3Jpem9udGFsIHNsaWRlcyBpbiB0aGUgZGVja1xuXHRcdHZhciBob3Jpem9udGFsU2xpZGVzID0gZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggSE9SSVpPTlRBTF9TTElERVNfU0VMRUNUT1IgKTtcblxuXHRcdC8vIEFib3J0IGlmIHRoZXJlIGFyZSBubyBzbGlkZXNcblx0XHRpZiggaG9yaXpvbnRhbFNsaWRlcy5sZW5ndGggPT09IDAgKSByZXR1cm47XG5cblx0XHQvLyBJZiBubyB2ZXJ0aWNhbCBpbmRleCBpcyBzcGVjaWZpZWQgYW5kIHRoZSB1cGNvbWluZyBzbGlkZSBpcyBhXG5cdFx0Ly8gc3RhY2ssIHJlc3VtZSBhdCBpdHMgcHJldmlvdXMgdmVydGljYWwgaW5kZXhcblx0XHRpZiggdiA9PT0gdW5kZWZpbmVkICYmICFpc092ZXJ2aWV3KCkgKSB7XG5cdFx0XHR2ID0gZ2V0UHJldmlvdXNWZXJ0aWNhbEluZGV4KCBob3Jpem9udGFsU2xpZGVzWyBoIF0gKTtcblx0XHR9XG5cblx0XHQvLyBJZiB3ZSB3ZXJlIG9uIGEgdmVydGljYWwgc3RhY2ssIHJlbWVtYmVyIHdoYXQgdmVydGljYWwgaW5kZXhcblx0XHQvLyBpdCB3YXMgb24gc28gd2UgY2FuIHJlc3VtZSBhdCB0aGUgc2FtZSBwb3NpdGlvbiB3aGVuIHJldHVybmluZ1xuXHRcdGlmKCBwcmV2aW91c1NsaWRlICYmIHByZXZpb3VzU2xpZGUucGFyZW50Tm9kZSAmJiBwcmV2aW91c1NsaWRlLnBhcmVudE5vZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnc3RhY2snICkgKSB7XG5cdFx0XHRzZXRQcmV2aW91c1ZlcnRpY2FsSW5kZXgoIHByZXZpb3VzU2xpZGUucGFyZW50Tm9kZSwgaW5kZXh2ICk7XG5cdFx0fVxuXG5cdFx0Ly8gUmVtZW1iZXIgdGhlIHN0YXRlIGJlZm9yZSB0aGlzIHNsaWRlXG5cdFx0dmFyIHN0YXRlQmVmb3JlID0gc3RhdGUuY29uY2F0KCk7XG5cblx0XHQvLyBSZXNldCB0aGUgc3RhdGUgYXJyYXlcblx0XHRzdGF0ZS5sZW5ndGggPSAwO1xuXG5cdFx0dmFyIGluZGV4aEJlZm9yZSA9IGluZGV4aCB8fCAwLFxuXHRcdFx0aW5kZXh2QmVmb3JlID0gaW5kZXh2IHx8IDA7XG5cblx0XHQvLyBBY3RpdmF0ZSBhbmQgdHJhbnNpdGlvbiB0byB0aGUgbmV3IHNsaWRlXG5cdFx0aW5kZXhoID0gdXBkYXRlU2xpZGVzKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiwgaCA9PT0gdW5kZWZpbmVkID8gaW5kZXhoIDogaCApO1xuXHRcdGluZGV4diA9IHVwZGF0ZVNsaWRlcyggVkVSVElDQUxfU0xJREVTX1NFTEVDVE9SLCB2ID09PSB1bmRlZmluZWQgPyBpbmRleHYgOiB2ICk7XG5cblx0XHQvLyBVcGRhdGUgdGhlIHZpc2liaWxpdHkgb2Ygc2xpZGVzIG5vdyB0aGF0IHRoZSBpbmRpY2VzIGhhdmUgY2hhbmdlZFxuXHRcdHVwZGF0ZVNsaWRlc1Zpc2liaWxpdHkoKTtcblxuXHRcdGxheW91dCgpO1xuXG5cdFx0Ly8gQXBwbHkgdGhlIG5ldyBzdGF0ZVxuXHRcdHN0YXRlTG9vcDogZm9yKCB2YXIgaSA9IDAsIGxlbiA9IHN0YXRlLmxlbmd0aDsgaSA8IGxlbjsgaSsrICkge1xuXHRcdFx0Ly8gQ2hlY2sgaWYgdGhpcyBzdGF0ZSBleGlzdGVkIG9uIHRoZSBwcmV2aW91cyBzbGlkZS4gSWYgaXRcblx0XHRcdC8vIGRpZCwgd2Ugd2lsbCBhdm9pZCBhZGRpbmcgaXQgcmVwZWF0ZWRseVxuXHRcdFx0Zm9yKCB2YXIgaiA9IDA7IGogPCBzdGF0ZUJlZm9yZS5sZW5ndGg7IGorKyApIHtcblx0XHRcdFx0aWYoIHN0YXRlQmVmb3JlW2pdID09PSBzdGF0ZVtpXSApIHtcblx0XHRcdFx0XHRzdGF0ZUJlZm9yZS5zcGxpY2UoIGosIDEgKTtcblx0XHRcdFx0XHRjb250aW51ZSBzdGF0ZUxvb3A7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0ZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LmNsYXNzTGlzdC5hZGQoIHN0YXRlW2ldICk7XG5cblx0XHRcdC8vIERpc3BhdGNoIGN1c3RvbSBldmVudCBtYXRjaGluZyB0aGUgc3RhdGUncyBuYW1lXG5cdFx0XHRkaXNwYXRjaEV2ZW50KCBzdGF0ZVtpXSApO1xuXHRcdH1cblxuXHRcdC8vIENsZWFuIHVwIHRoZSByZW1haW5zIG9mIHRoZSBwcmV2aW91cyBzdGF0ZVxuXHRcdHdoaWxlKCBzdGF0ZUJlZm9yZS5sZW5ndGggKSB7XG5cdFx0XHRkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSggc3RhdGVCZWZvcmUucG9wKCkgKTtcblx0XHR9XG5cblx0XHQvLyBVcGRhdGUgdGhlIG92ZXJ2aWV3IGlmIGl0J3MgY3VycmVudGx5IGFjdGl2ZVxuXHRcdGlmKCBpc092ZXJ2aWV3KCkgKSB7XG5cdFx0XHR1cGRhdGVPdmVydmlldygpO1xuXHRcdH1cblxuXHRcdC8vIEZpbmQgdGhlIGN1cnJlbnQgaG9yaXpvbnRhbCBzbGlkZSBhbmQgYW55IHBvc3NpYmxlIHZlcnRpY2FsIHNsaWRlc1xuXHRcdC8vIHdpdGhpbiBpdFxuXHRcdHZhciBjdXJyZW50SG9yaXpvbnRhbFNsaWRlID0gaG9yaXpvbnRhbFNsaWRlc1sgaW5kZXhoIF0sXG5cdFx0XHRjdXJyZW50VmVydGljYWxTbGlkZXMgPSBjdXJyZW50SG9yaXpvbnRhbFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICdzZWN0aW9uJyApO1xuXG5cdFx0Ly8gU3RvcmUgcmVmZXJlbmNlcyB0byB0aGUgcHJldmlvdXMgYW5kIGN1cnJlbnQgc2xpZGVzXG5cdFx0Y3VycmVudFNsaWRlID0gY3VycmVudFZlcnRpY2FsU2xpZGVzWyBpbmRleHYgXSB8fCBjdXJyZW50SG9yaXpvbnRhbFNsaWRlO1xuXG5cdFx0Ly8gU2hvdyBmcmFnbWVudCwgaWYgc3BlY2lmaWVkXG5cdFx0aWYoIHR5cGVvZiBmICE9PSAndW5kZWZpbmVkJyApIHtcblx0XHRcdG5hdmlnYXRlRnJhZ21lbnQoIGYgKTtcblx0XHR9XG5cblx0XHQvLyBEaXNwYXRjaCBhbiBldmVudCBpZiB0aGUgc2xpZGUgY2hhbmdlZFxuXHRcdHZhciBzbGlkZUNoYW5nZWQgPSAoIGluZGV4aCAhPT0gaW5kZXhoQmVmb3JlIHx8IGluZGV4diAhPT0gaW5kZXh2QmVmb3JlICk7XG5cdFx0aWYoIHNsaWRlQ2hhbmdlZCApIHtcblx0XHRcdGRpc3BhdGNoRXZlbnQoICdzbGlkZWNoYW5nZWQnLCB7XG5cdFx0XHRcdCdpbmRleGgnOiBpbmRleGgsXG5cdFx0XHRcdCdpbmRleHYnOiBpbmRleHYsXG5cdFx0XHRcdCdwcmV2aW91c1NsaWRlJzogcHJldmlvdXNTbGlkZSxcblx0XHRcdFx0J2N1cnJlbnRTbGlkZSc6IGN1cnJlbnRTbGlkZSxcblx0XHRcdFx0J29yaWdpbic6IG9cblx0XHRcdH0gKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHQvLyBFbnN1cmUgdGhhdCB0aGUgcHJldmlvdXMgc2xpZGUgaXMgbmV2ZXIgdGhlIHNhbWUgYXMgdGhlIGN1cnJlbnRcblx0XHRcdHByZXZpb3VzU2xpZGUgPSBudWxsO1xuXHRcdH1cblxuXHRcdC8vIFNvbHZlcyBhbiBlZGdlIGNhc2Ugd2hlcmUgdGhlIHByZXZpb3VzIHNsaWRlIG1haW50YWlucyB0aGVcblx0XHQvLyAncHJlc2VudCcgY2xhc3Mgd2hlbiBuYXZpZ2F0aW5nIGJldHdlZW4gYWRqYWNlbnQgdmVydGljYWxcblx0XHQvLyBzdGFja3Ncblx0XHRpZiggcHJldmlvdXNTbGlkZSApIHtcblx0XHRcdHByZXZpb3VzU2xpZGUuY2xhc3NMaXN0LnJlbW92ZSggJ3ByZXNlbnQnICk7XG5cdFx0XHRwcmV2aW91c1NsaWRlLnNldEF0dHJpYnV0ZSggJ2FyaWEtaGlkZGVuJywgJ3RydWUnICk7XG5cblx0XHRcdC8vIFJlc2V0IGFsbCBzbGlkZXMgdXBvbiBuYXZpZ2F0ZSB0byBob21lXG5cdFx0XHQvLyBJc3N1ZTogIzI4NVxuXHRcdFx0aWYgKCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yKCBIT01FX1NMSURFX1NFTEVDVE9SICkuY2xhc3NMaXN0LmNvbnRhaW5zKCAncHJlc2VudCcgKSApIHtcblx0XHRcdFx0Ly8gTGF1bmNoIGFzeW5jIHRhc2tcblx0XHRcdFx0c2V0VGltZW91dCggZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRcdHZhciBzbGlkZXMgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiArICcuc3RhY2snKSApLCBpO1xuXHRcdFx0XHRcdGZvciggaSBpbiBzbGlkZXMgKSB7XG5cdFx0XHRcdFx0XHRpZiggc2xpZGVzW2ldICkge1xuXHRcdFx0XHRcdFx0XHQvLyBSZXNldCBzdGFja1xuXHRcdFx0XHRcdFx0XHRzZXRQcmV2aW91c1ZlcnRpY2FsSW5kZXgoIHNsaWRlc1tpXSwgMCApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSwgMCApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIEhhbmRsZSBlbWJlZGRlZCBjb250ZW50XG5cdFx0aWYoIHNsaWRlQ2hhbmdlZCB8fCAhcHJldmlvdXNTbGlkZSApIHtcblx0XHRcdHN0b3BFbWJlZGRlZENvbnRlbnQoIHByZXZpb3VzU2xpZGUgKTtcblx0XHRcdHN0YXJ0RW1iZWRkZWRDb250ZW50KCBjdXJyZW50U2xpZGUgKTtcblx0XHR9XG5cblx0XHQvLyBBbm5vdW5jZSB0aGUgY3VycmVudCBzbGlkZSBjb250ZW50cywgZm9yIHNjcmVlbiByZWFkZXJzXG5cdFx0ZG9tLnN0YXR1c0Rpdi50ZXh0Q29udGVudCA9IGdldFN0YXR1c1RleHQoIGN1cnJlbnRTbGlkZSApO1xuXG5cdFx0dXBkYXRlQ29udHJvbHMoKTtcblx0XHR1cGRhdGVQcm9ncmVzcygpO1xuXHRcdHVwZGF0ZUJhY2tncm91bmQoKTtcblx0XHR1cGRhdGVQYXJhbGxheCgpO1xuXHRcdHVwZGF0ZVNsaWRlTnVtYmVyKCk7XG5cdFx0dXBkYXRlTm90ZXMoKTtcblxuXHRcdC8vIFVwZGF0ZSB0aGUgVVJMIGhhc2hcblx0XHR3cml0ZVVSTCgpO1xuXG5cdFx0Y3VlQXV0b1NsaWRlKCk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBTeW5jcyB0aGUgcHJlc2VudGF0aW9uIHdpdGggdGhlIGN1cnJlbnQgRE9NLiBVc2VmdWxcblx0ICogd2hlbiBuZXcgc2xpZGVzIG9yIGNvbnRyb2wgZWxlbWVudHMgYXJlIGFkZGVkIG9yIHdoZW5cblx0ICogdGhlIGNvbmZpZ3VyYXRpb24gaGFzIGNoYW5nZWQuXG5cdCAqL1xuXHRmdW5jdGlvbiBzeW5jKCkge1xuXG5cdFx0Ly8gU3Vic2NyaWJlIHRvIGlucHV0XG5cdFx0cmVtb3ZlRXZlbnRMaXN0ZW5lcnMoKTtcblx0XHRhZGRFdmVudExpc3RlbmVycygpO1xuXG5cdFx0Ly8gRm9yY2UgYSBsYXlvdXQgdG8gbWFrZSBzdXJlIHRoZSBjdXJyZW50IGNvbmZpZyBpcyBhY2NvdW50ZWQgZm9yXG5cdFx0bGF5b3V0KCk7XG5cblx0XHQvLyBSZWZsZWN0IHRoZSBjdXJyZW50IGF1dG9TbGlkZSB2YWx1ZVxuXHRcdGF1dG9TbGlkZSA9IGNvbmZpZy5hdXRvU2xpZGU7XG5cblx0XHQvLyBTdGFydCBhdXRvLXNsaWRpbmcgaWYgaXQncyBlbmFibGVkXG5cdFx0Y3VlQXV0b1NsaWRlKCk7XG5cblx0XHQvLyBSZS1jcmVhdGUgdGhlIHNsaWRlIGJhY2tncm91bmRzXG5cdFx0Y3JlYXRlQmFja2dyb3VuZHMoKTtcblxuXHRcdC8vIFdyaXRlIHRoZSBjdXJyZW50IGhhc2ggdG8gdGhlIFVSTFxuXHRcdHdyaXRlVVJMKCk7XG5cblx0XHRzb3J0QWxsRnJhZ21lbnRzKCk7XG5cblx0XHR1cGRhdGVDb250cm9scygpO1xuXHRcdHVwZGF0ZVByb2dyZXNzKCk7XG5cdFx0dXBkYXRlU2xpZGVOdW1iZXIoKTtcblx0XHR1cGRhdGVTbGlkZXNWaXNpYmlsaXR5KCk7XG5cdFx0dXBkYXRlQmFja2dyb3VuZCggdHJ1ZSApO1xuXHRcdHVwZGF0ZU5vdGVzVmlzaWJpbGl0eSgpO1xuXHRcdHVwZGF0ZU5vdGVzKCk7XG5cblx0XHRmb3JtYXRFbWJlZGRlZENvbnRlbnQoKTtcblxuXHRcdC8vIFN0YXJ0IG9yIHN0b3AgZW1iZWRkZWQgY29udGVudCBkZXBlbmRpbmcgb24gZ2xvYmFsIGNvbmZpZ1xuXHRcdGlmKCBjb25maWcuYXV0b1BsYXlNZWRpYSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRzdG9wRW1iZWRkZWRDb250ZW50KCBjdXJyZW50U2xpZGUsIHsgdW5sb2FkSWZyYW1lczogZmFsc2UgfSApO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdHN0YXJ0RW1iZWRkZWRDb250ZW50KCBjdXJyZW50U2xpZGUgKTtcblx0XHR9XG5cblx0XHRpZiggaXNPdmVydmlldygpICkge1xuXHRcdFx0bGF5b3V0T3ZlcnZpZXcoKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXNldHMgYWxsIHZlcnRpY2FsIHNsaWRlcyBzbyB0aGF0IG9ubHkgdGhlIGZpcnN0XG5cdCAqIGlzIHZpc2libGUuXG5cdCAqL1xuXHRmdW5jdGlvbiByZXNldFZlcnRpY2FsU2xpZGVzKCkge1xuXG5cdFx0dmFyIGhvcml6b250YWxTbGlkZXMgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApICk7XG5cdFx0aG9yaXpvbnRhbFNsaWRlcy5mb3JFYWNoKCBmdW5jdGlvbiggaG9yaXpvbnRhbFNsaWRlICkge1xuXG5cdFx0XHR2YXIgdmVydGljYWxTbGlkZXMgPSB0b0FycmF5KCBob3Jpem9udGFsU2xpZGUucXVlcnlTZWxlY3RvckFsbCggJ3NlY3Rpb24nICkgKTtcblx0XHRcdHZlcnRpY2FsU2xpZGVzLmZvckVhY2goIGZ1bmN0aW9uKCB2ZXJ0aWNhbFNsaWRlLCB5ICkge1xuXG5cdFx0XHRcdGlmKCB5ID4gMCApIHtcblx0XHRcdFx0XHR2ZXJ0aWNhbFNsaWRlLmNsYXNzTGlzdC5yZW1vdmUoICdwcmVzZW50JyApO1xuXHRcdFx0XHRcdHZlcnRpY2FsU2xpZGUuY2xhc3NMaXN0LnJlbW92ZSggJ3Bhc3QnICk7XG5cdFx0XHRcdFx0dmVydGljYWxTbGlkZS5jbGFzc0xpc3QuYWRkKCAnZnV0dXJlJyApO1xuXHRcdFx0XHRcdHZlcnRpY2FsU2xpZGUuc2V0QXR0cmlidXRlKCAnYXJpYS1oaWRkZW4nLCAndHJ1ZScgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHR9ICk7XG5cblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBTb3J0cyBhbmQgZm9ybWF0cyBhbGwgb2YgZnJhZ21lbnRzIGluIHRoZVxuXHQgKiBwcmVzZW50YXRpb24uXG5cdCAqL1xuXHRmdW5jdGlvbiBzb3J0QWxsRnJhZ21lbnRzKCkge1xuXG5cdFx0dmFyIGhvcml6b250YWxTbGlkZXMgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApICk7XG5cdFx0aG9yaXpvbnRhbFNsaWRlcy5mb3JFYWNoKCBmdW5jdGlvbiggaG9yaXpvbnRhbFNsaWRlICkge1xuXG5cdFx0XHR2YXIgdmVydGljYWxTbGlkZXMgPSB0b0FycmF5KCBob3Jpem9udGFsU2xpZGUucXVlcnlTZWxlY3RvckFsbCggJ3NlY3Rpb24nICkgKTtcblx0XHRcdHZlcnRpY2FsU2xpZGVzLmZvckVhY2goIGZ1bmN0aW9uKCB2ZXJ0aWNhbFNsaWRlLCB5ICkge1xuXG5cdFx0XHRcdHNvcnRGcmFnbWVudHMoIHZlcnRpY2FsU2xpZGUucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudCcgKSApO1xuXG5cdFx0XHR9ICk7XG5cblx0XHRcdGlmKCB2ZXJ0aWNhbFNsaWRlcy5sZW5ndGggPT09IDAgKSBzb3J0RnJhZ21lbnRzKCBob3Jpem9udGFsU2xpZGUucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudCcgKSApO1xuXG5cdFx0fSApO1xuXG5cdH1cblxuXHQvKipcblx0ICogUmFuZG9tbHkgc2h1ZmZsZXMgYWxsIHNsaWRlcyBpbiB0aGUgZGVjay5cblx0ICovXG5cdGZ1bmN0aW9uIHNodWZmbGUoKSB7XG5cblx0XHR2YXIgc2xpZGVzID0gdG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggSE9SSVpPTlRBTF9TTElERVNfU0VMRUNUT1IgKSApO1xuXG5cdFx0c2xpZGVzLmZvckVhY2goIGZ1bmN0aW9uKCBzbGlkZSApIHtcblxuXHRcdFx0Ly8gSW5zZXJ0IHRoaXMgc2xpZGUgbmV4dCB0byBhbm90aGVyIHJhbmRvbSBzbGlkZS4gVGhpcyBtYXlcblx0XHRcdC8vIGNhdXNlIHRoZSBzbGlkZSB0byBpbnNlcnQgYmVmb3JlIGl0c2VsZiBidXQgdGhhdCdzIGZpbmUuXG5cdFx0XHRkb20uc2xpZGVzLmluc2VydEJlZm9yZSggc2xpZGUsIHNsaWRlc1sgTWF0aC5mbG9vciggTWF0aC5yYW5kb20oKSAqIHNsaWRlcy5sZW5ndGggKSBdICk7XG5cblx0XHR9ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVcGRhdGVzIG9uZSBkaW1lbnNpb24gb2Ygc2xpZGVzIGJ5IHNob3dpbmcgdGhlIHNsaWRlXG5cdCAqIHdpdGggdGhlIHNwZWNpZmllZCBpbmRleC5cblx0ICpcblx0ICogQHBhcmFtIHtzdHJpbmd9IHNlbGVjdG9yIEEgQ1NTIHNlbGVjdG9yIHRoYXQgd2lsbCBmZXRjaFxuXHQgKiB0aGUgZ3JvdXAgb2Ygc2xpZGVzIHdlIGFyZSB3b3JraW5nIHdpdGhcblx0ICogQHBhcmFtIHtudW1iZXJ9IGluZGV4IFRoZSBpbmRleCBvZiB0aGUgc2xpZGUgdGhhdCBzaG91bGQgYmVcblx0ICogc2hvd25cblx0ICpcblx0ICogQHJldHVybiB7bnVtYmVyfSBUaGUgaW5kZXggb2YgdGhlIHNsaWRlIHRoYXQgaXMgbm93IHNob3duLFxuXHQgKiBtaWdodCBkaWZmZXIgZnJvbSB0aGUgcGFzc2VkIGluIGluZGV4IGlmIGl0IHdhcyBvdXQgb2Zcblx0ICogYm91bmRzLlxuXHQgKi9cblx0ZnVuY3Rpb24gdXBkYXRlU2xpZGVzKCBzZWxlY3RvciwgaW5kZXggKSB7XG5cblx0XHQvLyBTZWxlY3QgYWxsIHNsaWRlcyBhbmQgY29udmVydCB0aGUgTm9kZUxpc3QgcmVzdWx0IHRvXG5cdFx0Ly8gYW4gYXJyYXlcblx0XHR2YXIgc2xpZGVzID0gdG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggc2VsZWN0b3IgKSApLFxuXHRcdFx0c2xpZGVzTGVuZ3RoID0gc2xpZGVzLmxlbmd0aDtcblxuXHRcdHZhciBwcmludE1vZGUgPSBpc1ByaW50aW5nUERGKCk7XG5cblx0XHRpZiggc2xpZGVzTGVuZ3RoICkge1xuXG5cdFx0XHQvLyBTaG91bGQgdGhlIGluZGV4IGxvb3A/XG5cdFx0XHRpZiggY29uZmlnLmxvb3AgKSB7XG5cdFx0XHRcdGluZGV4ICU9IHNsaWRlc0xlbmd0aDtcblxuXHRcdFx0XHRpZiggaW5kZXggPCAwICkge1xuXHRcdFx0XHRcdGluZGV4ID0gc2xpZGVzTGVuZ3RoICsgaW5kZXg7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0Ly8gRW5mb3JjZSBtYXggYW5kIG1pbmltdW0gaW5kZXggYm91bmRzXG5cdFx0XHRpbmRleCA9IE1hdGgubWF4KCBNYXRoLm1pbiggaW5kZXgsIHNsaWRlc0xlbmd0aCAtIDEgKSwgMCApO1xuXG5cdFx0XHRmb3IoIHZhciBpID0gMDsgaSA8IHNsaWRlc0xlbmd0aDsgaSsrICkge1xuXHRcdFx0XHR2YXIgZWxlbWVudCA9IHNsaWRlc1tpXTtcblxuXHRcdFx0XHR2YXIgcmV2ZXJzZSA9IGNvbmZpZy5ydGwgJiYgIWlzVmVydGljYWxTbGlkZSggZWxlbWVudCApO1xuXG5cdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSggJ3Bhc3QnICk7XG5cdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSggJ3ByZXNlbnQnICk7XG5cdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSggJ2Z1dHVyZScgKTtcblxuXHRcdFx0XHQvLyBodHRwOi8vd3d3LnczLm9yZy9odG1sL3dnL2RyYWZ0cy9odG1sL21hc3Rlci9lZGl0aW5nLmh0bWwjdGhlLWhpZGRlbi1hdHRyaWJ1dGVcblx0XHRcdFx0ZWxlbWVudC5zZXRBdHRyaWJ1dGUoICdoaWRkZW4nLCAnJyApO1xuXHRcdFx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZSggJ2FyaWEtaGlkZGVuJywgJ3RydWUnICk7XG5cblx0XHRcdFx0Ly8gSWYgdGhpcyBlbGVtZW50IGNvbnRhaW5zIHZlcnRpY2FsIHNsaWRlc1xuXHRcdFx0XHRpZiggZWxlbWVudC5xdWVyeVNlbGVjdG9yKCAnc2VjdGlvbicgKSApIHtcblx0XHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5hZGQoICdzdGFjaycgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIElmIHdlJ3JlIHByaW50aW5nIHN0YXRpYyBzbGlkZXMsIGFsbCBzbGlkZXMgYXJlIFwicHJlc2VudFwiXG5cdFx0XHRcdGlmKCBwcmludE1vZGUgKSB7XG5cdFx0XHRcdFx0ZWxlbWVudC5jbGFzc0xpc3QuYWRkKCAncHJlc2VudCcgKTtcblx0XHRcdFx0XHRjb250aW51ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCBpIDwgaW5kZXggKSB7XG5cdFx0XHRcdFx0Ly8gQW55IGVsZW1lbnQgcHJldmlvdXMgdG8gaW5kZXggaXMgZ2l2ZW4gdGhlICdwYXN0JyBjbGFzc1xuXHRcdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LmFkZCggcmV2ZXJzZSA/ICdmdXR1cmUnIDogJ3Bhc3QnICk7XG5cblx0XHRcdFx0XHRpZiggY29uZmlnLmZyYWdtZW50cyApIHtcblx0XHRcdFx0XHRcdHZhciBwYXN0RnJhZ21lbnRzID0gdG9BcnJheSggZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnLmZyYWdtZW50JyApICk7XG5cblx0XHRcdFx0XHRcdC8vIFNob3cgYWxsIGZyYWdtZW50cyBvbiBwcmlvciBzbGlkZXNcblx0XHRcdFx0XHRcdHdoaWxlKCBwYXN0RnJhZ21lbnRzLmxlbmd0aCApIHtcblx0XHRcdFx0XHRcdFx0dmFyIHBhc3RGcmFnbWVudCA9IHBhc3RGcmFnbWVudHMucG9wKCk7XG5cdFx0XHRcdFx0XHRcdHBhc3RGcmFnbWVudC5jbGFzc0xpc3QuYWRkKCAndmlzaWJsZScgKTtcblx0XHRcdFx0XHRcdFx0cGFzdEZyYWdtZW50LmNsYXNzTGlzdC5yZW1vdmUoICdjdXJyZW50LWZyYWdtZW50JyApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0XHRlbHNlIGlmKCBpID4gaW5kZXggKSB7XG5cdFx0XHRcdFx0Ly8gQW55IGVsZW1lbnQgc3Vic2VxdWVudCB0byBpbmRleCBpcyBnaXZlbiB0aGUgJ2Z1dHVyZScgY2xhc3Ncblx0XHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5hZGQoIHJldmVyc2UgPyAncGFzdCcgOiAnZnV0dXJlJyApO1xuXG5cdFx0XHRcdFx0aWYoIGNvbmZpZy5mcmFnbWVudHMgKSB7XG5cdFx0XHRcdFx0XHR2YXIgZnV0dXJlRnJhZ21lbnRzID0gdG9BcnJheSggZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnLmZyYWdtZW50LnZpc2libGUnICkgKTtcblxuXHRcdFx0XHRcdFx0Ly8gTm8gZnJhZ21lbnRzIGluIGZ1dHVyZSBzbGlkZXMgc2hvdWxkIGJlIHZpc2libGUgYWhlYWQgb2YgdGltZVxuXHRcdFx0XHRcdFx0d2hpbGUoIGZ1dHVyZUZyYWdtZW50cy5sZW5ndGggKSB7XG5cdFx0XHRcdFx0XHRcdHZhciBmdXR1cmVGcmFnbWVudCA9IGZ1dHVyZUZyYWdtZW50cy5wb3AoKTtcblx0XHRcdFx0XHRcdFx0ZnV0dXJlRnJhZ21lbnQuY2xhc3NMaXN0LnJlbW92ZSggJ3Zpc2libGUnICk7XG5cdFx0XHRcdFx0XHRcdGZ1dHVyZUZyYWdtZW50LmNsYXNzTGlzdC5yZW1vdmUoICdjdXJyZW50LWZyYWdtZW50JyApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBNYXJrIHRoZSBjdXJyZW50IHNsaWRlIGFzIHByZXNlbnRcblx0XHRcdHNsaWRlc1tpbmRleF0uY2xhc3NMaXN0LmFkZCggJ3ByZXNlbnQnICk7XG5cdFx0XHRzbGlkZXNbaW5kZXhdLnJlbW92ZUF0dHJpYnV0ZSggJ2hpZGRlbicgKTtcblx0XHRcdHNsaWRlc1tpbmRleF0ucmVtb3ZlQXR0cmlidXRlKCAnYXJpYS1oaWRkZW4nICk7XG5cblx0XHRcdC8vIElmIHRoaXMgc2xpZGUgaGFzIGEgc3RhdGUgYXNzb2NpYXRlZCB3aXRoIGl0LCBhZGQgaXRcblx0XHRcdC8vIG9udG8gdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIGRlY2tcblx0XHRcdHZhciBzbGlkZVN0YXRlID0gc2xpZGVzW2luZGV4XS5nZXRBdHRyaWJ1dGUoICdkYXRhLXN0YXRlJyApO1xuXHRcdFx0aWYoIHNsaWRlU3RhdGUgKSB7XG5cdFx0XHRcdHN0YXRlID0gc3RhdGUuY29uY2F0KCBzbGlkZVN0YXRlLnNwbGl0KCAnICcgKSApO1xuXHRcdFx0fVxuXG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0Ly8gU2luY2UgdGhlcmUgYXJlIG5vIHNsaWRlcyB3ZSBjYW4ndCBiZSBhbnl3aGVyZSBiZXlvbmQgdGhlXG5cdFx0XHQvLyB6ZXJvdGggaW5kZXhcblx0XHRcdGluZGV4ID0gMDtcblx0XHR9XG5cblx0XHRyZXR1cm4gaW5kZXg7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBPcHRpbWl6YXRpb24gbWV0aG9kOyBoaWRlIGFsbCBzbGlkZXMgdGhhdCBhcmUgZmFyIGF3YXlcblx0ICogZnJvbSB0aGUgcHJlc2VudCBzbGlkZS5cblx0ICovXG5cdGZ1bmN0aW9uIHVwZGF0ZVNsaWRlc1Zpc2liaWxpdHkoKSB7XG5cblx0XHQvLyBTZWxlY3QgYWxsIHNsaWRlcyBhbmQgY29udmVydCB0aGUgTm9kZUxpc3QgcmVzdWx0IHRvXG5cdFx0Ly8gYW4gYXJyYXlcblx0XHR2YXIgaG9yaXpvbnRhbFNsaWRlcyA9IHRvQXJyYXkoIGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIEhPUklaT05UQUxfU0xJREVTX1NFTEVDVE9SICkgKSxcblx0XHRcdGhvcml6b250YWxTbGlkZXNMZW5ndGggPSBob3Jpem9udGFsU2xpZGVzLmxlbmd0aCxcblx0XHRcdGRpc3RhbmNlWCxcblx0XHRcdGRpc3RhbmNlWTtcblxuXHRcdGlmKCBob3Jpem9udGFsU2xpZGVzTGVuZ3RoICYmIHR5cGVvZiBpbmRleGggIT09ICd1bmRlZmluZWQnICkge1xuXG5cdFx0XHQvLyBUaGUgbnVtYmVyIG9mIHN0ZXBzIGF3YXkgZnJvbSB0aGUgcHJlc2VudCBzbGlkZSB0aGF0IHdpbGxcblx0XHRcdC8vIGJlIHZpc2libGVcblx0XHRcdHZhciB2aWV3RGlzdGFuY2UgPSBpc092ZXJ2aWV3KCkgPyAxMCA6IGNvbmZpZy52aWV3RGlzdGFuY2U7XG5cblx0XHRcdC8vIExpbWl0IHZpZXcgZGlzdGFuY2Ugb24gd2Vha2VyIGRldmljZXNcblx0XHRcdGlmKCBpc01vYmlsZURldmljZSApIHtcblx0XHRcdFx0dmlld0Rpc3RhbmNlID0gaXNPdmVydmlldygpID8gNiA6IDI7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEFsbCBzbGlkZXMgbmVlZCB0byBiZSB2aXNpYmxlIHdoZW4gZXhwb3J0aW5nIHRvIFBERlxuXHRcdFx0aWYoIGlzUHJpbnRpbmdQREYoKSApIHtcblx0XHRcdFx0dmlld0Rpc3RhbmNlID0gTnVtYmVyLk1BWF9WQUxVRTtcblx0XHRcdH1cblxuXHRcdFx0Zm9yKCB2YXIgeCA9IDA7IHggPCBob3Jpem9udGFsU2xpZGVzTGVuZ3RoOyB4KysgKSB7XG5cdFx0XHRcdHZhciBob3Jpem9udGFsU2xpZGUgPSBob3Jpem9udGFsU2xpZGVzW3hdO1xuXG5cdFx0XHRcdHZhciB2ZXJ0aWNhbFNsaWRlcyA9IHRvQXJyYXkoIGhvcml6b250YWxTbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnc2VjdGlvbicgKSApLFxuXHRcdFx0XHRcdHZlcnRpY2FsU2xpZGVzTGVuZ3RoID0gdmVydGljYWxTbGlkZXMubGVuZ3RoO1xuXG5cdFx0XHRcdC8vIERldGVybWluZSBob3cgZmFyIGF3YXkgdGhpcyBzbGlkZSBpcyBmcm9tIHRoZSBwcmVzZW50XG5cdFx0XHRcdGRpc3RhbmNlWCA9IE1hdGguYWJzKCAoIGluZGV4aCB8fCAwICkgLSB4ICkgfHwgMDtcblxuXHRcdFx0XHQvLyBJZiB0aGUgcHJlc2VudGF0aW9uIGlzIGxvb3BlZCwgZGlzdGFuY2Ugc2hvdWxkIG1lYXN1cmVcblx0XHRcdFx0Ly8gMSBiZXR3ZWVuIHRoZSBmaXJzdCBhbmQgbGFzdCBzbGlkZXNcblx0XHRcdFx0aWYoIGNvbmZpZy5sb29wICkge1xuXHRcdFx0XHRcdGRpc3RhbmNlWCA9IE1hdGguYWJzKCAoICggaW5kZXhoIHx8IDAgKSAtIHggKSAlICggaG9yaXpvbnRhbFNsaWRlc0xlbmd0aCAtIHZpZXdEaXN0YW5jZSApICkgfHwgMDtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFNob3cgdGhlIGhvcml6b250YWwgc2xpZGUgaWYgaXQncyB3aXRoaW4gdGhlIHZpZXcgZGlzdGFuY2Vcblx0XHRcdFx0aWYoIGRpc3RhbmNlWCA8IHZpZXdEaXN0YW5jZSApIHtcblx0XHRcdFx0XHRsb2FkU2xpZGUoIGhvcml6b250YWxTbGlkZSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdHVubG9hZFNsaWRlKCBob3Jpem9udGFsU2xpZGUgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCB2ZXJ0aWNhbFNsaWRlc0xlbmd0aCApIHtcblxuXHRcdFx0XHRcdHZhciBveSA9IGdldFByZXZpb3VzVmVydGljYWxJbmRleCggaG9yaXpvbnRhbFNsaWRlICk7XG5cblx0XHRcdFx0XHRmb3IoIHZhciB5ID0gMDsgeSA8IHZlcnRpY2FsU2xpZGVzTGVuZ3RoOyB5KysgKSB7XG5cdFx0XHRcdFx0XHR2YXIgdmVydGljYWxTbGlkZSA9IHZlcnRpY2FsU2xpZGVzW3ldO1xuXG5cdFx0XHRcdFx0XHRkaXN0YW5jZVkgPSB4ID09PSAoIGluZGV4aCB8fCAwICkgPyBNYXRoLmFicyggKCBpbmRleHYgfHwgMCApIC0geSApIDogTWF0aC5hYnMoIHkgLSBveSApO1xuXG5cdFx0XHRcdFx0XHRpZiggZGlzdGFuY2VYICsgZGlzdGFuY2VZIDwgdmlld0Rpc3RhbmNlICkge1xuXHRcdFx0XHRcdFx0XHRsb2FkU2xpZGUoIHZlcnRpY2FsU2xpZGUgKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdFx0XHR1bmxvYWRTbGlkZSggdmVydGljYWxTbGlkZSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgaWYgdGhlcmUgYXJlIEFOWSB2ZXJ0aWNhbCBzbGlkZXMsIGFueXdoZXJlIGluIHRoZSBkZWNrXG5cdFx0XHRpZiggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggJy5zbGlkZXM+c2VjdGlvbj5zZWN0aW9uJyApLmxlbmd0aCApIHtcblx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LmFkZCggJ2hhcy12ZXJ0aWNhbC1zbGlkZXMnICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ2hhcy12ZXJ0aWNhbC1zbGlkZXMnICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEZsYWcgaWYgdGhlcmUgYXJlIEFOWSBob3Jpem9udGFsIHNsaWRlcywgYW55d2hlcmUgaW4gdGhlIGRlY2tcblx0XHRcdGlmKCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCAnLnNsaWRlcz5zZWN0aW9uJyApLmxlbmd0aCA+IDEgKSB7XG5cdFx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5hZGQoICdoYXMtaG9yaXpvbnRhbC1zbGlkZXMnICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggJ2hhcy1ob3Jpem9udGFsLXNsaWRlcycgKTtcblx0XHRcdH1cblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFBpY2sgdXAgbm90ZXMgZnJvbSB0aGUgY3VycmVudCBzbGlkZSBhbmQgZGlzcGxheSB0aGVtXG5cdCAqIHRvIHRoZSB2aWV3ZXIuXG5cdCAqXG5cdCAqIEBzZWUge0BsaW5rIGNvbmZpZy5zaG93Tm90ZXN9XG5cdCAqL1xuXHRmdW5jdGlvbiB1cGRhdGVOb3RlcygpIHtcblxuXHRcdGlmKCBjb25maWcuc2hvd05vdGVzICYmIGRvbS5zcGVha2VyTm90ZXMgJiYgY3VycmVudFNsaWRlICYmICFpc1ByaW50aW5nUERGKCkgKSB7XG5cblx0XHRcdGRvbS5zcGVha2VyTm90ZXMuaW5uZXJIVE1MID0gZ2V0U2xpZGVOb3RlcygpIHx8ICc8c3BhbiBjbGFzcz1cIm5vdGVzLXBsYWNlaG9sZGVyXCI+Tm8gbm90ZXMgb24gdGhpcyBzbGlkZS48L3NwYW4+JztcblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFVwZGF0ZXMgdGhlIHZpc2liaWxpdHkgb2YgdGhlIHNwZWFrZXIgbm90ZXMgc2lkZWJhciB0aGF0XG5cdCAqIGlzIHVzZWQgdG8gc2hhcmUgYW5ub3RhdGVkIHNsaWRlcy4gVGhlIG5vdGVzIHNpZGViYXIgaXNcblx0ICogb25seSB2aXNpYmxlIGlmIHNob3dOb3RlcyBpcyB0cnVlIGFuZCB0aGVyZSBhcmUgbm90ZXMgb25cblx0ICogb25lIG9yIG1vcmUgc2xpZGVzIGluIHRoZSBkZWNrLlxuXHQgKi9cblx0ZnVuY3Rpb24gdXBkYXRlTm90ZXNWaXNpYmlsaXR5KCkge1xuXG5cdFx0aWYoIGNvbmZpZy5zaG93Tm90ZXMgJiYgaGFzTm90ZXMoKSApIHtcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5hZGQoICdzaG93LW5vdGVzJyApO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGRvbS53cmFwcGVyLmNsYXNzTGlzdC5yZW1vdmUoICdzaG93LW5vdGVzJyApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGVyZSBhcmUgc3BlYWtlciBub3RlcyBmb3IgQU5ZIHNsaWRlIGluIHRoZVxuXHQgKiBwcmVzZW50YXRpb24uXG5cdCAqL1xuXHRmdW5jdGlvbiBoYXNOb3RlcygpIHtcblxuXHRcdHJldHVybiBkb20uc2xpZGVzLnF1ZXJ5U2VsZWN0b3JBbGwoICdbZGF0YS1ub3Rlc10sIGFzaWRlLm5vdGVzJyApLmxlbmd0aCA+IDA7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVcGRhdGVzIHRoZSBwcm9ncmVzcyBiYXIgdG8gcmVmbGVjdCB0aGUgY3VycmVudCBzbGlkZS5cblx0ICovXG5cdGZ1bmN0aW9uIHVwZGF0ZVByb2dyZXNzKCkge1xuXG5cdFx0Ly8gVXBkYXRlIHByb2dyZXNzIGlmIGVuYWJsZWRcblx0XHRpZiggY29uZmlnLnByb2dyZXNzICYmIGRvbS5wcm9ncmVzc2JhciApIHtcblxuXHRcdFx0ZG9tLnByb2dyZXNzYmFyLnN0eWxlLndpZHRoID0gZ2V0UHJvZ3Jlc3MoKSAqIGRvbS53cmFwcGVyLm9mZnNldFdpZHRoICsgJ3B4JztcblxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFVwZGF0ZXMgdGhlIHNsaWRlIG51bWJlciBkaXYgdG8gcmVmbGVjdCB0aGUgY3VycmVudCBzbGlkZS5cblx0ICpcblx0ICogVGhlIGZvbGxvd2luZyBzbGlkZSBudW1iZXIgZm9ybWF0cyBhcmUgYXZhaWxhYmxlOlxuXHQgKiAgXCJoLnZcIjpcdGhvcml6b250YWwgLiB2ZXJ0aWNhbCBzbGlkZSBudW1iZXIgKGRlZmF1bHQpXG5cdCAqICBcImgvdlwiOlx0aG9yaXpvbnRhbCAvIHZlcnRpY2FsIHNsaWRlIG51bWJlclxuXHQgKiAgICBcImNcIjpcdGZsYXR0ZW5lZCBzbGlkZSBudW1iZXJcblx0ICogIFwiYy90XCI6XHRmbGF0dGVuZWQgc2xpZGUgbnVtYmVyIC8gdG90YWwgc2xpZGVzXG5cdCAqL1xuXHRmdW5jdGlvbiB1cGRhdGVTbGlkZU51bWJlcigpIHtcblxuXHRcdC8vIFVwZGF0ZSBzbGlkZSBudW1iZXIgaWYgZW5hYmxlZFxuXHRcdGlmKCBjb25maWcuc2xpZGVOdW1iZXIgJiYgZG9tLnNsaWRlTnVtYmVyICkge1xuXG5cdFx0XHR2YXIgdmFsdWUgPSBbXTtcblx0XHRcdHZhciBmb3JtYXQgPSAnaC52JztcblxuXHRcdFx0Ly8gQ2hlY2sgaWYgYSBjdXN0b20gbnVtYmVyIGZvcm1hdCBpcyBhdmFpbGFibGVcblx0XHRcdGlmKCB0eXBlb2YgY29uZmlnLnNsaWRlTnVtYmVyID09PSAnc3RyaW5nJyApIHtcblx0XHRcdFx0Zm9ybWF0ID0gY29uZmlnLnNsaWRlTnVtYmVyO1xuXHRcdFx0fVxuXG5cdFx0XHRzd2l0Y2goIGZvcm1hdCApIHtcblx0XHRcdFx0Y2FzZSAnYyc6XG5cdFx0XHRcdFx0dmFsdWUucHVzaCggZ2V0U2xpZGVQYXN0Q291bnQoKSArIDEgKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAnYy90Jzpcblx0XHRcdFx0XHR2YWx1ZS5wdXNoKCBnZXRTbGlkZVBhc3RDb3VudCgpICsgMSwgJy8nLCBnZXRUb3RhbFNsaWRlcygpICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgJ2gvdic6XG5cdFx0XHRcdFx0dmFsdWUucHVzaCggaW5kZXhoICsgMSApO1xuXHRcdFx0XHRcdGlmKCBpc1ZlcnRpY2FsU2xpZGUoKSApIHZhbHVlLnB1c2goICcvJywgaW5kZXh2ICsgMSApO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdHZhbHVlLnB1c2goIGluZGV4aCArIDEgKTtcblx0XHRcdFx0XHRpZiggaXNWZXJ0aWNhbFNsaWRlKCkgKSB2YWx1ZS5wdXNoKCAnLicsIGluZGV4diArIDEgKTtcblx0XHRcdH1cblxuXHRcdFx0ZG9tLnNsaWRlTnVtYmVyLmlubmVySFRNTCA9IGZvcm1hdFNsaWRlTnVtYmVyKCB2YWx1ZVswXSwgdmFsdWVbMV0sIHZhbHVlWzJdICk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQXBwbGllcyBIVE1MIGZvcm1hdHRpbmcgdG8gYSBzbGlkZSBudW1iZXIgYmVmb3JlIGl0J3Ncblx0ICogd3JpdHRlbiB0byB0aGUgRE9NLlxuXHQgKlxuXHQgKiBAcGFyYW0ge251bWJlcn0gYSBDdXJyZW50IHNsaWRlXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBkZWxpbWl0ZXIgQ2hhcmFjdGVyIHRvIHNlcGFyYXRlIHNsaWRlIG51bWJlcnNcblx0ICogQHBhcmFtIHsobnVtYmVyfCopfSBiIFRvdGFsIHNsaWRlc1xuXHQgKiBAcmV0dXJuIHtzdHJpbmd9IEhUTUwgc3RyaW5nIGZyYWdtZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBmb3JtYXRTbGlkZU51bWJlciggYSwgZGVsaW1pdGVyLCBiICkge1xuXG5cdFx0aWYoIHR5cGVvZiBiID09PSAnbnVtYmVyJyAmJiAhaXNOYU4oIGIgKSApIHtcblx0XHRcdHJldHVybiAgJzxzcGFuIGNsYXNzPVwic2xpZGUtbnVtYmVyLWFcIj4nKyBhICsnPC9zcGFuPicgK1xuXHRcdFx0XHRcdCc8c3BhbiBjbGFzcz1cInNsaWRlLW51bWJlci1kZWxpbWl0ZXJcIj4nKyBkZWxpbWl0ZXIgKyc8L3NwYW4+JyArXG5cdFx0XHRcdFx0JzxzcGFuIGNsYXNzPVwic2xpZGUtbnVtYmVyLWJcIj4nKyBiICsnPC9zcGFuPic7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0cmV0dXJuICc8c3BhbiBjbGFzcz1cInNsaWRlLW51bWJlci1hXCI+JysgYSArJzwvc3Bhbj4nO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFVwZGF0ZXMgdGhlIHN0YXRlIG9mIGFsbCBjb250cm9sL25hdmlnYXRpb24gYXJyb3dzLlxuXHQgKi9cblx0ZnVuY3Rpb24gdXBkYXRlQ29udHJvbHMoKSB7XG5cblx0XHR2YXIgcm91dGVzID0gYXZhaWxhYmxlUm91dGVzKCk7XG5cdFx0dmFyIGZyYWdtZW50cyA9IGF2YWlsYWJsZUZyYWdtZW50cygpO1xuXG5cdFx0Ly8gUmVtb3ZlIHRoZSAnZW5hYmxlZCcgY2xhc3MgZnJvbSBhbGwgZGlyZWN0aW9uc1xuXHRcdGRvbS5jb250cm9sc0xlZnQuY29uY2F0KCBkb20uY29udHJvbHNSaWdodCApXG5cdFx0XHRcdFx0XHQuY29uY2F0KCBkb20uY29udHJvbHNVcCApXG5cdFx0XHRcdFx0XHQuY29uY2F0KCBkb20uY29udHJvbHNEb3duIClcblx0XHRcdFx0XHRcdC5jb25jYXQoIGRvbS5jb250cm9sc1ByZXYgKVxuXHRcdFx0XHRcdFx0LmNvbmNhdCggZG9tLmNvbnRyb2xzTmV4dCApLmZvckVhY2goIGZ1bmN0aW9uKCBub2RlICkge1xuXHRcdFx0bm9kZS5jbGFzc0xpc3QucmVtb3ZlKCAnZW5hYmxlZCcgKTtcblx0XHRcdG5vZGUuY2xhc3NMaXN0LnJlbW92ZSggJ2ZyYWdtZW50ZWQnICk7XG5cblx0XHRcdC8vIFNldCAnZGlzYWJsZWQnIGF0dHJpYnV0ZSBvbiBhbGwgZGlyZWN0aW9uc1xuXHRcdFx0bm9kZS5zZXRBdHRyaWJ1dGUoICdkaXNhYmxlZCcsICdkaXNhYmxlZCcgKTtcblx0XHR9ICk7XG5cblx0XHQvLyBBZGQgdGhlICdlbmFibGVkJyBjbGFzcyB0byB0aGUgYXZhaWxhYmxlIHJvdXRlczsgcmVtb3ZlICdkaXNhYmxlZCcgYXR0cmlidXRlIHRvIGVuYWJsZSBidXR0b25zXG5cdFx0aWYoIHJvdXRlcy5sZWZ0ICkgZG9tLmNvbnRyb2xzTGVmdC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdlbmFibGVkJyApOyBlbC5yZW1vdmVBdHRyaWJ1dGUoICdkaXNhYmxlZCcgKTsgfSApO1xuXHRcdGlmKCByb3V0ZXMucmlnaHQgKSBkb20uY29udHJvbHNSaWdodC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdlbmFibGVkJyApOyBlbC5yZW1vdmVBdHRyaWJ1dGUoICdkaXNhYmxlZCcgKTsgfSApO1xuXHRcdGlmKCByb3V0ZXMudXAgKSBkb20uY29udHJvbHNVcC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdlbmFibGVkJyApOyBlbC5yZW1vdmVBdHRyaWJ1dGUoICdkaXNhYmxlZCcgKTsgfSApO1xuXHRcdGlmKCByb3V0ZXMuZG93biApIGRvbS5jb250cm9sc0Rvd24uZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5jbGFzc0xpc3QuYWRkKCAnZW5hYmxlZCcgKTsgZWwucmVtb3ZlQXR0cmlidXRlKCAnZGlzYWJsZWQnICk7IH0gKTtcblxuXHRcdC8vIFByZXYvbmV4dCBidXR0b25zXG5cdFx0aWYoIHJvdXRlcy5sZWZ0IHx8IHJvdXRlcy51cCApIGRvbS5jb250cm9sc1ByZXYuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5jbGFzc0xpc3QuYWRkKCAnZW5hYmxlZCcgKTsgZWwucmVtb3ZlQXR0cmlidXRlKCAnZGlzYWJsZWQnICk7IH0gKTtcblx0XHRpZiggcm91dGVzLnJpZ2h0IHx8IHJvdXRlcy5kb3duICkgZG9tLmNvbnRyb2xzTmV4dC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdlbmFibGVkJyApOyBlbC5yZW1vdmVBdHRyaWJ1dGUoICdkaXNhYmxlZCcgKTsgfSApO1xuXG5cdFx0Ly8gSGlnaGxpZ2h0IGZyYWdtZW50IGRpcmVjdGlvbnNcblx0XHRpZiggY3VycmVudFNsaWRlICkge1xuXG5cdFx0XHQvLyBBbHdheXMgYXBwbHkgZnJhZ21lbnQgZGVjb3JhdG9yIHRvIHByZXYvbmV4dCBidXR0b25zXG5cdFx0XHRpZiggZnJhZ21lbnRzLnByZXYgKSBkb20uY29udHJvbHNQcmV2LmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHsgZWwuY2xhc3NMaXN0LmFkZCggJ2ZyYWdtZW50ZWQnLCAnZW5hYmxlZCcgKTsgZWwucmVtb3ZlQXR0cmlidXRlKCAnZGlzYWJsZWQnICk7IH0gKTtcblx0XHRcdGlmKCBmcmFnbWVudHMubmV4dCApIGRvbS5jb250cm9sc05leHQuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkgeyBlbC5jbGFzc0xpc3QuYWRkKCAnZnJhZ21lbnRlZCcsICdlbmFibGVkJyApOyBlbC5yZW1vdmVBdHRyaWJ1dGUoICdkaXNhYmxlZCcgKTsgfSApO1xuXG5cdFx0XHQvLyBBcHBseSBmcmFnbWVudCBkZWNvcmF0b3JzIHRvIGRpcmVjdGlvbmFsIGJ1dHRvbnMgYmFzZWQgb25cblx0XHRcdC8vIHdoYXQgc2xpZGUgYXhpcyB0aGV5IGFyZSBpblxuXHRcdFx0aWYoIGlzVmVydGljYWxTbGlkZSggY3VycmVudFNsaWRlICkgKSB7XG5cdFx0XHRcdGlmKCBmcmFnbWVudHMucHJldiApIGRvbS5jb250cm9sc1VwLmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHsgZWwuY2xhc3NMaXN0LmFkZCggJ2ZyYWdtZW50ZWQnLCAnZW5hYmxlZCcgKTsgZWwucmVtb3ZlQXR0cmlidXRlKCAnZGlzYWJsZWQnICk7IH0gKTtcblx0XHRcdFx0aWYoIGZyYWdtZW50cy5uZXh0ICkgZG9tLmNvbnRyb2xzRG93bi5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdmcmFnbWVudGVkJywgJ2VuYWJsZWQnICk7IGVsLnJlbW92ZUF0dHJpYnV0ZSggJ2Rpc2FibGVkJyApOyB9ICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0aWYoIGZyYWdtZW50cy5wcmV2ICkgZG9tLmNvbnRyb2xzTGVmdC5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7IGVsLmNsYXNzTGlzdC5hZGQoICdmcmFnbWVudGVkJywgJ2VuYWJsZWQnICk7IGVsLnJlbW92ZUF0dHJpYnV0ZSggJ2Rpc2FibGVkJyApOyB9ICk7XG5cdFx0XHRcdGlmKCBmcmFnbWVudHMubmV4dCApIGRvbS5jb250cm9sc1JpZ2h0LmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHsgZWwuY2xhc3NMaXN0LmFkZCggJ2ZyYWdtZW50ZWQnLCAnZW5hYmxlZCcgKTsgZWwucmVtb3ZlQXR0cmlidXRlKCAnZGlzYWJsZWQnICk7IH0gKTtcblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdGlmKCBjb25maWcuY29udHJvbHNUdXRvcmlhbCApIHtcblxuXHRcdFx0Ly8gSGlnaGxpZ2h0IGNvbnRyb2wgYXJyb3dzIHdpdGggYW4gYW5pbWF0aW9uIHRvIGVuc3VyZVxuXHRcdFx0Ly8gdGhhdCB0aGUgdmlld2VyIGtub3dzIGhvdyB0byBuYXZpZ2F0ZVxuXHRcdFx0aWYoICFoYXNOYXZpZ2F0ZWREb3duICYmIHJvdXRlcy5kb3duICkge1xuXHRcdFx0XHRkb20uY29udHJvbHNEb3duQXJyb3cuY2xhc3NMaXN0LmFkZCggJ2hpZ2hsaWdodCcgKTtcblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHRkb20uY29udHJvbHNEb3duQXJyb3cuY2xhc3NMaXN0LnJlbW92ZSggJ2hpZ2hsaWdodCcgKTtcblxuXHRcdFx0XHRpZiggIWhhc05hdmlnYXRlZFJpZ2h0ICYmIHJvdXRlcy5yaWdodCAmJiBpbmRleHYgPT09IDAgKSB7XG5cdFx0XHRcdFx0ZG9tLmNvbnRyb2xzUmlnaHRBcnJvdy5jbGFzc0xpc3QuYWRkKCAnaGlnaGxpZ2h0JyApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdGRvbS5jb250cm9sc1JpZ2h0QXJyb3cuY2xhc3NMaXN0LnJlbW92ZSggJ2hpZ2hsaWdodCcgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogVXBkYXRlcyB0aGUgYmFja2dyb3VuZCBlbGVtZW50cyB0byByZWZsZWN0IHRoZSBjdXJyZW50XG5cdCAqIHNsaWRlLlxuXHQgKlxuXHQgKiBAcGFyYW0ge2Jvb2xlYW59IGluY2x1ZGVBbGwgSWYgdHJ1ZSwgdGhlIGJhY2tncm91bmRzIG9mXG5cdCAqIGFsbCB2ZXJ0aWNhbCBzbGlkZXMgKG5vdCBqdXN0IHRoZSBwcmVzZW50KSB3aWxsIGJlIHVwZGF0ZWQuXG5cdCAqL1xuXHRmdW5jdGlvbiB1cGRhdGVCYWNrZ3JvdW5kKCBpbmNsdWRlQWxsICkge1xuXG5cdFx0dmFyIGN1cnJlbnRCYWNrZ3JvdW5kID0gbnVsbDtcblxuXHRcdC8vIFJldmVyc2UgcGFzdC9mdXR1cmUgY2xhc3NlcyB3aGVuIGluIFJUTCBtb2RlXG5cdFx0dmFyIGhvcml6b250YWxQYXN0ID0gY29uZmlnLnJ0bCA/ICdmdXR1cmUnIDogJ3Bhc3QnLFxuXHRcdFx0aG9yaXpvbnRhbEZ1dHVyZSA9IGNvbmZpZy5ydGwgPyAncGFzdCcgOiAnZnV0dXJlJztcblxuXHRcdC8vIFVwZGF0ZSB0aGUgY2xhc3NlcyBvZiBhbGwgYmFja2dyb3VuZHMgdG8gbWF0Y2ggdGhlXG5cdFx0Ly8gc3RhdGVzIG9mIHRoZWlyIHNsaWRlcyAocGFzdC9wcmVzZW50L2Z1dHVyZSlcblx0XHR0b0FycmF5KCBkb20uYmFja2dyb3VuZC5jaGlsZE5vZGVzICkuZm9yRWFjaCggZnVuY3Rpb24oIGJhY2tncm91bmRoLCBoICkge1xuXG5cdFx0XHRiYWNrZ3JvdW5kaC5jbGFzc0xpc3QucmVtb3ZlKCAncGFzdCcgKTtcblx0XHRcdGJhY2tncm91bmRoLmNsYXNzTGlzdC5yZW1vdmUoICdwcmVzZW50JyApO1xuXHRcdFx0YmFja2dyb3VuZGguY2xhc3NMaXN0LnJlbW92ZSggJ2Z1dHVyZScgKTtcblxuXHRcdFx0aWYoIGggPCBpbmRleGggKSB7XG5cdFx0XHRcdGJhY2tncm91bmRoLmNsYXNzTGlzdC5hZGQoIGhvcml6b250YWxQYXN0ICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIGlmICggaCA+IGluZGV4aCApIHtcblx0XHRcdFx0YmFja2dyb3VuZGguY2xhc3NMaXN0LmFkZCggaG9yaXpvbnRhbEZ1dHVyZSApO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdGJhY2tncm91bmRoLmNsYXNzTGlzdC5hZGQoICdwcmVzZW50JyApO1xuXG5cdFx0XHRcdC8vIFN0b3JlIGEgcmVmZXJlbmNlIHRvIHRoZSBjdXJyZW50IGJhY2tncm91bmQgZWxlbWVudFxuXHRcdFx0XHRjdXJyZW50QmFja2dyb3VuZCA9IGJhY2tncm91bmRoO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiggaW5jbHVkZUFsbCB8fCBoID09PSBpbmRleGggKSB7XG5cdFx0XHRcdHRvQXJyYXkoIGJhY2tncm91bmRoLnF1ZXJ5U2VsZWN0b3JBbGwoICcuc2xpZGUtYmFja2dyb3VuZCcgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBiYWNrZ3JvdW5kdiwgdiApIHtcblxuXHRcdFx0XHRcdGJhY2tncm91bmR2LmNsYXNzTGlzdC5yZW1vdmUoICdwYXN0JyApO1xuXHRcdFx0XHRcdGJhY2tncm91bmR2LmNsYXNzTGlzdC5yZW1vdmUoICdwcmVzZW50JyApO1xuXHRcdFx0XHRcdGJhY2tncm91bmR2LmNsYXNzTGlzdC5yZW1vdmUoICdmdXR1cmUnICk7XG5cblx0XHRcdFx0XHRpZiggdiA8IGluZGV4diApIHtcblx0XHRcdFx0XHRcdGJhY2tncm91bmR2LmNsYXNzTGlzdC5hZGQoICdwYXN0JyApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlIGlmICggdiA+IGluZGV4diApIHtcblx0XHRcdFx0XHRcdGJhY2tncm91bmR2LmNsYXNzTGlzdC5hZGQoICdmdXR1cmUnICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdFx0YmFja2dyb3VuZHYuY2xhc3NMaXN0LmFkZCggJ3ByZXNlbnQnICk7XG5cblx0XHRcdFx0XHRcdC8vIE9ubHkgaWYgdGhpcyBpcyB0aGUgcHJlc2VudCBob3Jpem9udGFsIGFuZCB2ZXJ0aWNhbCBzbGlkZVxuXHRcdFx0XHRcdFx0aWYoIGggPT09IGluZGV4aCApIGN1cnJlbnRCYWNrZ3JvdW5kID0gYmFja2dyb3VuZHY7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gKTtcblx0XHRcdH1cblxuXHRcdH0gKTtcblxuXHRcdC8vIFN0b3AgY29udGVudCBpbnNpZGUgb2YgcHJldmlvdXMgYmFja2dyb3VuZHNcblx0XHRpZiggcHJldmlvdXNCYWNrZ3JvdW5kICkge1xuXG5cdFx0XHRzdG9wRW1iZWRkZWRDb250ZW50KCBwcmV2aW91c0JhY2tncm91bmQgKTtcblxuXHRcdH1cblxuXHRcdC8vIFN0YXJ0IGNvbnRlbnQgaW4gdGhlIGN1cnJlbnQgYmFja2dyb3VuZFxuXHRcdGlmKCBjdXJyZW50QmFja2dyb3VuZCApIHtcblxuXHRcdFx0c3RhcnRFbWJlZGRlZENvbnRlbnQoIGN1cnJlbnRCYWNrZ3JvdW5kICk7XG5cblx0XHRcdHZhciBiYWNrZ3JvdW5kSW1hZ2VVUkwgPSBjdXJyZW50QmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2UgfHwgJyc7XG5cblx0XHRcdC8vIFJlc3RhcnQgR0lGcyAoZG9lc24ndCB3b3JrIGluIEZpcmVmb3gpXG5cdFx0XHRpZiggL1xcLmdpZi9pLnRlc3QoIGJhY2tncm91bmRJbWFnZVVSTCApICkge1xuXHRcdFx0XHRjdXJyZW50QmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2UgPSAnJztcblx0XHRcdFx0d2luZG93LmdldENvbXB1dGVkU3R5bGUoIGN1cnJlbnRCYWNrZ3JvdW5kICkub3BhY2l0eTtcblx0XHRcdFx0Y3VycmVudEJhY2tncm91bmQuc3R5bGUuYmFja2dyb3VuZEltYWdlID0gYmFja2dyb3VuZEltYWdlVVJMO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBEb24ndCB0cmFuc2l0aW9uIGJldHdlZW4gaWRlbnRpY2FsIGJhY2tncm91bmRzLiBUaGlzXG5cdFx0XHQvLyBwcmV2ZW50cyB1bndhbnRlZCBmbGlja2VyLlxuXHRcdFx0dmFyIHByZXZpb3VzQmFja2dyb3VuZEhhc2ggPSBwcmV2aW91c0JhY2tncm91bmQgPyBwcmV2aW91c0JhY2tncm91bmQuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLWhhc2gnICkgOiBudWxsO1xuXHRcdFx0dmFyIGN1cnJlbnRCYWNrZ3JvdW5kSGFzaCA9IGN1cnJlbnRCYWNrZ3JvdW5kLmdldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC1oYXNoJyApO1xuXHRcdFx0aWYoIGN1cnJlbnRCYWNrZ3JvdW5kSGFzaCAmJiBjdXJyZW50QmFja2dyb3VuZEhhc2ggPT09IHByZXZpb3VzQmFja2dyb3VuZEhhc2ggJiYgY3VycmVudEJhY2tncm91bmQgIT09IHByZXZpb3VzQmFja2dyb3VuZCApIHtcblx0XHRcdFx0ZG9tLmJhY2tncm91bmQuY2xhc3NMaXN0LmFkZCggJ25vLXRyYW5zaXRpb24nICk7XG5cdFx0XHR9XG5cblx0XHRcdHByZXZpb3VzQmFja2dyb3VuZCA9IGN1cnJlbnRCYWNrZ3JvdW5kO1xuXG5cdFx0fVxuXG5cdFx0Ly8gSWYgdGhlcmUncyBhIGJhY2tncm91bmQgYnJpZ2h0bmVzcyBmbGFnIGZvciB0aGlzIHNsaWRlLFxuXHRcdC8vIGJ1YmJsZSBpdCB0byB0aGUgLnJldmVhbCBjb250YWluZXJcblx0XHRpZiggY3VycmVudFNsaWRlICkge1xuXHRcdFx0WyAnaGFzLWxpZ2h0LWJhY2tncm91bmQnLCAnaGFzLWRhcmstYmFja2dyb3VuZCcgXS5mb3JFYWNoKCBmdW5jdGlvbiggY2xhc3NUb0J1YmJsZSApIHtcblx0XHRcdFx0aWYoIGN1cnJlbnRTbGlkZS5jbGFzc0xpc3QuY29udGFpbnMoIGNsYXNzVG9CdWJibGUgKSApIHtcblx0XHRcdFx0XHRkb20ud3JhcHBlci5jbGFzc0xpc3QuYWRkKCBjbGFzc1RvQnViYmxlICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0ZG9tLndyYXBwZXIuY2xhc3NMaXN0LnJlbW92ZSggY2xhc3NUb0J1YmJsZSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cdFx0fVxuXG5cdFx0Ly8gQWxsb3cgdGhlIGZpcnN0IGJhY2tncm91bmQgdG8gYXBwbHkgd2l0aG91dCB0cmFuc2l0aW9uXG5cdFx0c2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG5cdFx0XHRkb20uYmFja2dyb3VuZC5jbGFzc0xpc3QucmVtb3ZlKCAnbm8tdHJhbnNpdGlvbicgKTtcblx0XHR9LCAxICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBVcGRhdGVzIHRoZSBwb3NpdGlvbiBvZiB0aGUgcGFyYWxsYXggYmFja2dyb3VuZCBiYXNlZFxuXHQgKiBvbiB0aGUgY3VycmVudCBzbGlkZSBpbmRleC5cblx0ICovXG5cdGZ1bmN0aW9uIHVwZGF0ZVBhcmFsbGF4KCkge1xuXG5cdFx0aWYoIGNvbmZpZy5wYXJhbGxheEJhY2tncm91bmRJbWFnZSApIHtcblxuXHRcdFx0dmFyIGhvcml6b250YWxTbGlkZXMgPSBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApLFxuXHRcdFx0XHR2ZXJ0aWNhbFNsaWRlcyA9IGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIFZFUlRJQ0FMX1NMSURFU19TRUxFQ1RPUiApO1xuXG5cdFx0XHR2YXIgYmFja2dyb3VuZFNpemUgPSBkb20uYmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kU2l6ZS5zcGxpdCggJyAnICksXG5cdFx0XHRcdGJhY2tncm91bmRXaWR0aCwgYmFja2dyb3VuZEhlaWdodDtcblxuXHRcdFx0aWYoIGJhY2tncm91bmRTaXplLmxlbmd0aCA9PT0gMSApIHtcblx0XHRcdFx0YmFja2dyb3VuZFdpZHRoID0gYmFja2dyb3VuZEhlaWdodCA9IHBhcnNlSW50KCBiYWNrZ3JvdW5kU2l6ZVswXSwgMTAgKTtcblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHRiYWNrZ3JvdW5kV2lkdGggPSBwYXJzZUludCggYmFja2dyb3VuZFNpemVbMF0sIDEwICk7XG5cdFx0XHRcdGJhY2tncm91bmRIZWlnaHQgPSBwYXJzZUludCggYmFja2dyb3VuZFNpemVbMV0sIDEwICk7XG5cdFx0XHR9XG5cblx0XHRcdHZhciBzbGlkZVdpZHRoID0gZG9tLmJhY2tncm91bmQub2Zmc2V0V2lkdGgsXG5cdFx0XHRcdGhvcml6b250YWxTbGlkZUNvdW50ID0gaG9yaXpvbnRhbFNsaWRlcy5sZW5ndGgsXG5cdFx0XHRcdGhvcml6b250YWxPZmZzZXRNdWx0aXBsaWVyLFxuXHRcdFx0XHRob3Jpem9udGFsT2Zmc2V0O1xuXG5cdFx0XHRpZiggdHlwZW9mIGNvbmZpZy5wYXJhbGxheEJhY2tncm91bmRIb3Jpem9udGFsID09PSAnbnVtYmVyJyApIHtcblx0XHRcdFx0aG9yaXpvbnRhbE9mZnNldE11bHRpcGxpZXIgPSBjb25maWcucGFyYWxsYXhCYWNrZ3JvdW5kSG9yaXpvbnRhbDtcblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHRob3Jpem9udGFsT2Zmc2V0TXVsdGlwbGllciA9IGhvcml6b250YWxTbGlkZUNvdW50ID4gMSA/ICggYmFja2dyb3VuZFdpZHRoIC0gc2xpZGVXaWR0aCApIC8gKCBob3Jpem9udGFsU2xpZGVDb3VudC0xICkgOiAwO1xuXHRcdFx0fVxuXG5cdFx0XHRob3Jpem9udGFsT2Zmc2V0ID0gaG9yaXpvbnRhbE9mZnNldE11bHRpcGxpZXIgKiBpbmRleGggKiAtMTtcblxuXHRcdFx0dmFyIHNsaWRlSGVpZ2h0ID0gZG9tLmJhY2tncm91bmQub2Zmc2V0SGVpZ2h0LFxuXHRcdFx0XHR2ZXJ0aWNhbFNsaWRlQ291bnQgPSB2ZXJ0aWNhbFNsaWRlcy5sZW5ndGgsXG5cdFx0XHRcdHZlcnRpY2FsT2Zmc2V0TXVsdGlwbGllcixcblx0XHRcdFx0dmVydGljYWxPZmZzZXQ7XG5cblx0XHRcdGlmKCB0eXBlb2YgY29uZmlnLnBhcmFsbGF4QmFja2dyb3VuZFZlcnRpY2FsID09PSAnbnVtYmVyJyApIHtcblx0XHRcdFx0dmVydGljYWxPZmZzZXRNdWx0aXBsaWVyID0gY29uZmlnLnBhcmFsbGF4QmFja2dyb3VuZFZlcnRpY2FsO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdHZlcnRpY2FsT2Zmc2V0TXVsdGlwbGllciA9ICggYmFja2dyb3VuZEhlaWdodCAtIHNsaWRlSGVpZ2h0ICkgLyAoIHZlcnRpY2FsU2xpZGVDb3VudC0xICk7XG5cdFx0XHR9XG5cblx0XHRcdHZlcnRpY2FsT2Zmc2V0ID0gdmVydGljYWxTbGlkZUNvdW50ID4gMCA/ICB2ZXJ0aWNhbE9mZnNldE11bHRpcGxpZXIgKiBpbmRleHYgOiAwO1xuXG5cdFx0XHRkb20uYmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kUG9zaXRpb24gPSBob3Jpem9udGFsT2Zmc2V0ICsgJ3B4ICcgKyAtdmVydGljYWxPZmZzZXQgKyAncHgnO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQ2FsbGVkIHdoZW4gdGhlIGdpdmVuIHNsaWRlIGlzIHdpdGhpbiB0aGUgY29uZmlndXJlZCB2aWV3XG5cdCAqIGRpc3RhbmNlLiBTaG93cyB0aGUgc2xpZGUgZWxlbWVudCBhbmQgbG9hZHMgYW55IGNvbnRlbnRcblx0ICogdGhhdCBpcyBzZXQgdG8gbG9hZCBsYXppbHkgKGRhdGEtc3JjKS5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gc2xpZGUgU2xpZGUgdG8gc2hvd1xuXHQgKi9cblx0ZnVuY3Rpb24gbG9hZFNsaWRlKCBzbGlkZSwgb3B0aW9ucyApIHtcblxuXHRcdG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG5cdFx0Ly8gU2hvdyB0aGUgc2xpZGUgZWxlbWVudFxuXHRcdHNsaWRlLnN0eWxlLmRpc3BsYXkgPSBjb25maWcuZGlzcGxheTtcblxuXHRcdC8vIE1lZGlhIGVsZW1lbnRzIHdpdGggZGF0YS1zcmMgYXR0cmlidXRlc1xuXHRcdHRvQXJyYXkoIHNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICdpbWdbZGF0YS1zcmNdLCB2aWRlb1tkYXRhLXNyY10sIGF1ZGlvW2RhdGEtc3JjXScgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdFx0ZWxlbWVudC5zZXRBdHRyaWJ1dGUoICdzcmMnLCBlbGVtZW50LmdldEF0dHJpYnV0ZSggJ2RhdGEtc3JjJyApICk7XG5cdFx0XHRlbGVtZW50LnNldEF0dHJpYnV0ZSggJ2RhdGEtbGF6eS1sb2FkZWQnLCAnJyApO1xuXHRcdFx0ZWxlbWVudC5yZW1vdmVBdHRyaWJ1dGUoICdkYXRhLXNyYycgKTtcblx0XHR9ICk7XG5cblx0XHQvLyBNZWRpYSBlbGVtZW50cyB3aXRoIDxzb3VyY2U+IGNoaWxkcmVuXG5cdFx0dG9BcnJheSggc2xpZGUucXVlcnlTZWxlY3RvckFsbCggJ3ZpZGVvLCBhdWRpbycgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBtZWRpYSApIHtcblx0XHRcdHZhciBzb3VyY2VzID0gMDtcblxuXHRcdFx0dG9BcnJheSggbWVkaWEucXVlcnlTZWxlY3RvckFsbCggJ3NvdXJjZVtkYXRhLXNyY10nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggc291cmNlICkge1xuXHRcdFx0XHRzb3VyY2Uuc2V0QXR0cmlidXRlKCAnc3JjJywgc291cmNlLmdldEF0dHJpYnV0ZSggJ2RhdGEtc3JjJyApICk7XG5cdFx0XHRcdHNvdXJjZS5yZW1vdmVBdHRyaWJ1dGUoICdkYXRhLXNyYycgKTtcblx0XHRcdFx0c291cmNlLnNldEF0dHJpYnV0ZSggJ2RhdGEtbGF6eS1sb2FkZWQnLCAnJyApO1xuXHRcdFx0XHRzb3VyY2VzICs9IDE7XG5cdFx0XHR9ICk7XG5cblx0XHRcdC8vIElmIHdlIHJld3JvdGUgc291cmNlcyBmb3IgdGhpcyB2aWRlby9hdWRpbyBlbGVtZW50LCB3ZSBuZWVkXG5cdFx0XHQvLyB0byBtYW51YWxseSB0ZWxsIGl0IHRvIGxvYWQgZnJvbSBpdHMgbmV3IG9yaWdpblxuXHRcdFx0aWYoIHNvdXJjZXMgPiAwICkge1xuXHRcdFx0XHRtZWRpYS5sb2FkKCk7XG5cdFx0XHR9XG5cdFx0fSApO1xuXG5cblx0XHQvLyBTaG93IHRoZSBjb3JyZXNwb25kaW5nIGJhY2tncm91bmQgZWxlbWVudFxuXHRcdHZhciBpbmRpY2VzID0gZ2V0SW5kaWNlcyggc2xpZGUgKTtcblx0XHR2YXIgYmFja2dyb3VuZCA9IGdldFNsaWRlQmFja2dyb3VuZCggaW5kaWNlcy5oLCBpbmRpY2VzLnYgKTtcblx0XHRpZiggYmFja2dyb3VuZCApIHtcblx0XHRcdGJhY2tncm91bmQuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XG5cblx0XHRcdC8vIElmIHRoZSBiYWNrZ3JvdW5kIGNvbnRhaW5zIG1lZGlhLCBsb2FkIGl0XG5cdFx0XHRpZiggYmFja2dyb3VuZC5oYXNBdHRyaWJ1dGUoICdkYXRhLWxvYWRlZCcgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRcdGJhY2tncm91bmQuc2V0QXR0cmlidXRlKCAnZGF0YS1sb2FkZWQnLCAndHJ1ZScgKTtcblxuXHRcdFx0XHR2YXIgYmFja2dyb3VuZEltYWdlID0gc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLWltYWdlJyApLFxuXHRcdFx0XHRcdGJhY2tncm91bmRWaWRlbyA9IHNsaWRlLmdldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC12aWRlbycgKSxcblx0XHRcdFx0XHRiYWNrZ3JvdW5kVmlkZW9Mb29wID0gc2xpZGUuaGFzQXR0cmlidXRlKCAnZGF0YS1iYWNrZ3JvdW5kLXZpZGVvLWxvb3AnICksXG5cdFx0XHRcdFx0YmFja2dyb3VuZFZpZGVvTXV0ZWQgPSBzbGlkZS5oYXNBdHRyaWJ1dGUoICdkYXRhLWJhY2tncm91bmQtdmlkZW8tbXV0ZWQnICksXG5cdFx0XHRcdFx0YmFja2dyb3VuZElmcmFtZSA9IHNsaWRlLmdldEF0dHJpYnV0ZSggJ2RhdGEtYmFja2dyb3VuZC1pZnJhbWUnICk7XG5cblx0XHRcdFx0Ly8gSW1hZ2VzXG5cdFx0XHRcdGlmKCBiYWNrZ3JvdW5kSW1hZ2UgKSB7XG5cdFx0XHRcdFx0YmFja2dyb3VuZC5zdHlsZS5iYWNrZ3JvdW5kSW1hZ2UgPSAndXJsKCcrIGJhY2tncm91bmRJbWFnZSArJyknO1xuXHRcdFx0XHR9XG5cdFx0XHRcdC8vIFZpZGVvc1xuXHRcdFx0XHRlbHNlIGlmICggYmFja2dyb3VuZFZpZGVvICYmICFpc1NwZWFrZXJOb3RlcygpICkge1xuXHRcdFx0XHRcdHZhciB2aWRlbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICd2aWRlbycgKTtcblxuXHRcdFx0XHRcdGlmKCBiYWNrZ3JvdW5kVmlkZW9Mb29wICkge1xuXHRcdFx0XHRcdFx0dmlkZW8uc2V0QXR0cmlidXRlKCAnbG9vcCcsICcnICk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYoIGJhY2tncm91bmRWaWRlb011dGVkICkge1xuXHRcdFx0XHRcdFx0dmlkZW8ubXV0ZWQgPSB0cnVlO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIElubGluZSB2aWRlbyBwbGF5YmFjayB3b3JrcyAoYXQgbGVhc3QgaW4gTW9iaWxlIFNhZmFyaSkgYXNcblx0XHRcdFx0XHQvLyBsb25nIGFzIHRoZSB2aWRlbyBpcyBtdXRlZCBhbmQgdGhlIGBwbGF5c2lubGluZWAgYXR0cmlidXRlIGlzXG5cdFx0XHRcdFx0Ly8gcHJlc2VudFxuXHRcdFx0XHRcdGlmKCBpc01vYmlsZURldmljZSApIHtcblx0XHRcdFx0XHRcdHZpZGVvLm11dGVkID0gdHJ1ZTtcblx0XHRcdFx0XHRcdHZpZGVvLmF1dG9wbGF5ID0gdHJ1ZTtcblx0XHRcdFx0XHRcdHZpZGVvLnNldEF0dHJpYnV0ZSggJ3BsYXlzaW5saW5lJywgJycgKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBTdXBwb3J0IGNvbW1hIHNlcGFyYXRlZCBsaXN0cyBvZiB2aWRlbyBzb3VyY2VzXG5cdFx0XHRcdFx0YmFja2dyb3VuZFZpZGVvLnNwbGl0KCAnLCcgKS5mb3JFYWNoKCBmdW5jdGlvbiggc291cmNlICkge1xuXHRcdFx0XHRcdFx0dmlkZW8uaW5uZXJIVE1MICs9ICc8c291cmNlIHNyYz1cIicrIHNvdXJjZSArJ1wiPic7XG5cdFx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdFx0YmFja2dyb3VuZC5hcHBlbmRDaGlsZCggdmlkZW8gKTtcblx0XHRcdFx0fVxuXHRcdFx0XHQvLyBJZnJhbWVzXG5cdFx0XHRcdGVsc2UgaWYoIGJhY2tncm91bmRJZnJhbWUgJiYgb3B0aW9ucy5leGNsdWRlSWZyYW1lcyAhPT0gdHJ1ZSApIHtcblx0XHRcdFx0XHR2YXIgaWZyYW1lID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggJ2lmcmFtZScgKTtcblx0XHRcdFx0XHRpZnJhbWUuc2V0QXR0cmlidXRlKCAnYWxsb3dmdWxsc2NyZWVuJywgJycgKTtcblx0XHRcdFx0XHRpZnJhbWUuc2V0QXR0cmlidXRlKCAnbW96YWxsb3dmdWxsc2NyZWVuJywgJycgKTtcblx0XHRcdFx0XHRpZnJhbWUuc2V0QXR0cmlidXRlKCAnd2Via2l0YWxsb3dmdWxsc2NyZWVuJywgJycgKTtcblxuXHRcdFx0XHRcdC8vIE9ubHkgbG9hZCBhdXRvcGxheWluZyBjb250ZW50IHdoZW4gdGhlIHNsaWRlIGlzIHNob3duIHRvXG5cdFx0XHRcdFx0Ly8gYXZvaWQgaGF2aW5nIGl0IHBsYXkgaW4gdGhlIGJhY2tncm91bmRcblx0XHRcdFx0XHRpZiggL2F1dG9wbGF5PSgxfHRydWV8eWVzKS9naS50ZXN0KCBiYWNrZ3JvdW5kSWZyYW1lICkgKSB7XG5cdFx0XHRcdFx0XHRpZnJhbWUuc2V0QXR0cmlidXRlKCAnZGF0YS1zcmMnLCBiYWNrZ3JvdW5kSWZyYW1lICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdFx0aWZyYW1lLnNldEF0dHJpYnV0ZSggJ3NyYycsIGJhY2tncm91bmRJZnJhbWUgKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZnJhbWUuc3R5bGUud2lkdGggID0gJzEwMCUnO1xuXHRcdFx0XHRcdGlmcmFtZS5zdHlsZS5oZWlnaHQgPSAnMTAwJSc7XG5cdFx0XHRcdFx0aWZyYW1lLnN0eWxlLm1heEhlaWdodCA9ICcxMDAlJztcblx0XHRcdFx0XHRpZnJhbWUuc3R5bGUubWF4V2lkdGggPSAnMTAwJSc7XG5cblx0XHRcdFx0XHRiYWNrZ3JvdW5kLmFwcGVuZENoaWxkKCBpZnJhbWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogVW5sb2FkcyBhbmQgaGlkZXMgdGhlIGdpdmVuIHNsaWRlLiBUaGlzIGlzIGNhbGxlZCB3aGVuIHRoZVxuXHQgKiBzbGlkZSBpcyBtb3ZlZCBvdXRzaWRlIG9mIHRoZSBjb25maWd1cmVkIHZpZXcgZGlzdGFuY2UuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IHNsaWRlXG5cdCAqL1xuXHRmdW5jdGlvbiB1bmxvYWRTbGlkZSggc2xpZGUgKSB7XG5cblx0XHQvLyBIaWRlIHRoZSBzbGlkZSBlbGVtZW50XG5cdFx0c2xpZGUuc3R5bGUuZGlzcGxheSA9ICdub25lJztcblxuXHRcdC8vIEhpZGUgdGhlIGNvcnJlc3BvbmRpbmcgYmFja2dyb3VuZCBlbGVtZW50XG5cdFx0dmFyIGluZGljZXMgPSBnZXRJbmRpY2VzKCBzbGlkZSApO1xuXHRcdHZhciBiYWNrZ3JvdW5kID0gZ2V0U2xpZGVCYWNrZ3JvdW5kKCBpbmRpY2VzLmgsIGluZGljZXMudiApO1xuXHRcdGlmKCBiYWNrZ3JvdW5kICkge1xuXHRcdFx0YmFja2dyb3VuZC5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xuXHRcdH1cblxuXHRcdC8vIFJlc2V0IGxhenktbG9hZGVkIG1lZGlhIGVsZW1lbnRzIHdpdGggc3JjIGF0dHJpYnV0ZXNcblx0XHR0b0FycmF5KCBzbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAndmlkZW9bZGF0YS1sYXp5LWxvYWRlZF1bc3JjXSwgYXVkaW9bZGF0YS1sYXp5LWxvYWRlZF1bc3JjXScgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbGVtZW50ICkge1xuXHRcdFx0ZWxlbWVudC5zZXRBdHRyaWJ1dGUoICdkYXRhLXNyYycsIGVsZW1lbnQuZ2V0QXR0cmlidXRlKCAnc3JjJyApICk7XG5cdFx0XHRlbGVtZW50LnJlbW92ZUF0dHJpYnV0ZSggJ3NyYycgKTtcblx0XHR9ICk7XG5cblx0XHQvLyBSZXNldCBsYXp5LWxvYWRlZCBtZWRpYSBlbGVtZW50cyB3aXRoIDxzb3VyY2U+IGNoaWxkcmVuXG5cdFx0dG9BcnJheSggc2xpZGUucXVlcnlTZWxlY3RvckFsbCggJ3ZpZGVvW2RhdGEtbGF6eS1sb2FkZWRdIHNvdXJjZVtzcmNdLCBhdWRpbyBzb3VyY2Vbc3JjXScgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBzb3VyY2UgKSB7XG5cdFx0XHRzb3VyY2Uuc2V0QXR0cmlidXRlKCAnZGF0YS1zcmMnLCBzb3VyY2UuZ2V0QXR0cmlidXRlKCAnc3JjJyApICk7XG5cdFx0XHRzb3VyY2UucmVtb3ZlQXR0cmlidXRlKCAnc3JjJyApO1xuXHRcdH0gKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIERldGVybWluZSB3aGF0IGF2YWlsYWJsZSByb3V0ZXMgdGhlcmUgYXJlIGZvciBuYXZpZ2F0aW9uLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHt7bGVmdDogYm9vbGVhbiwgcmlnaHQ6IGJvb2xlYW4sIHVwOiBib29sZWFuLCBkb3duOiBib29sZWFufX1cblx0ICovXG5cdGZ1bmN0aW9uIGF2YWlsYWJsZVJvdXRlcygpIHtcblxuXHRcdHZhciBob3Jpem9udGFsU2xpZGVzID0gZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggSE9SSVpPTlRBTF9TTElERVNfU0VMRUNUT1IgKSxcblx0XHRcdHZlcnRpY2FsU2xpZGVzID0gZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggVkVSVElDQUxfU0xJREVTX1NFTEVDVE9SICk7XG5cblx0XHR2YXIgcm91dGVzID0ge1xuXHRcdFx0bGVmdDogaW5kZXhoID4gMCB8fCBjb25maWcubG9vcCxcblx0XHRcdHJpZ2h0OiBpbmRleGggPCBob3Jpem9udGFsU2xpZGVzLmxlbmd0aCAtIDEgfHwgY29uZmlnLmxvb3AsXG5cdFx0XHR1cDogaW5kZXh2ID4gMCxcblx0XHRcdGRvd246IGluZGV4diA8IHZlcnRpY2FsU2xpZGVzLmxlbmd0aCAtIDFcblx0XHR9O1xuXG5cdFx0Ly8gcmV2ZXJzZSBob3Jpem9udGFsIGNvbnRyb2xzIGZvciBydGxcblx0XHRpZiggY29uZmlnLnJ0bCApIHtcblx0XHRcdHZhciBsZWZ0ID0gcm91dGVzLmxlZnQ7XG5cdFx0XHRyb3V0ZXMubGVmdCA9IHJvdXRlcy5yaWdodDtcblx0XHRcdHJvdXRlcy5yaWdodCA9IGxlZnQ7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJvdXRlcztcblxuXHR9XG5cblx0LyoqXG5cdCAqIFJldHVybnMgYW4gb2JqZWN0IGRlc2NyaWJpbmcgdGhlIGF2YWlsYWJsZSBmcmFnbWVudFxuXHQgKiBkaXJlY3Rpb25zLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHt7cHJldjogYm9vbGVhbiwgbmV4dDogYm9vbGVhbn19XG5cdCAqL1xuXHRmdW5jdGlvbiBhdmFpbGFibGVGcmFnbWVudHMoKSB7XG5cblx0XHRpZiggY3VycmVudFNsaWRlICYmIGNvbmZpZy5mcmFnbWVudHMgKSB7XG5cdFx0XHR2YXIgZnJhZ21lbnRzID0gY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICcuZnJhZ21lbnQnICk7XG5cdFx0XHR2YXIgaGlkZGVuRnJhZ21lbnRzID0gY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICcuZnJhZ21lbnQ6bm90KC52aXNpYmxlKScgKTtcblxuXHRcdFx0cmV0dXJuIHtcblx0XHRcdFx0cHJldjogZnJhZ21lbnRzLmxlbmd0aCAtIGhpZGRlbkZyYWdtZW50cy5sZW5ndGggPiAwLFxuXHRcdFx0XHRuZXh0OiAhIWhpZGRlbkZyYWdtZW50cy5sZW5ndGhcblx0XHRcdH07XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0cmV0dXJuIHsgcHJldjogZmFsc2UsIG5leHQ6IGZhbHNlIH07XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogRW5mb3JjZXMgb3JpZ2luLXNwZWNpZmljIGZvcm1hdCBydWxlcyBmb3IgZW1iZWRkZWQgbWVkaWEuXG5cdCAqL1xuXHRmdW5jdGlvbiBmb3JtYXRFbWJlZGRlZENvbnRlbnQoKSB7XG5cblx0XHR2YXIgX2FwcGVuZFBhcmFtVG9JZnJhbWVTb3VyY2UgPSBmdW5jdGlvbiggc291cmNlQXR0cmlidXRlLCBzb3VyY2VVUkwsIHBhcmFtICkge1xuXHRcdFx0dG9BcnJheSggZG9tLnNsaWRlcy5xdWVyeVNlbGVjdG9yQWxsKCAnaWZyYW1lWycrIHNvdXJjZUF0dHJpYnV0ZSArJyo9XCInKyBzb3VyY2VVUkwgKydcIl0nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdHZhciBzcmMgPSBlbC5nZXRBdHRyaWJ1dGUoIHNvdXJjZUF0dHJpYnV0ZSApO1xuXHRcdFx0XHRpZiggc3JjICYmIHNyYy5pbmRleE9mKCBwYXJhbSApID09PSAtMSApIHtcblx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoIHNvdXJjZUF0dHJpYnV0ZSwgc3JjICsgKCAhL1xcPy8udGVzdCggc3JjICkgPyAnPycgOiAnJicgKSArIHBhcmFtICk7XG5cdFx0XHRcdH1cblx0XHRcdH0pO1xuXHRcdH07XG5cblx0XHQvLyBZb3VUdWJlIGZyYW1lcyBtdXN0IGluY2x1ZGUgXCI/ZW5hYmxlanNhcGk9MVwiXG5cdFx0X2FwcGVuZFBhcmFtVG9JZnJhbWVTb3VyY2UoICdzcmMnLCAneW91dHViZS5jb20vZW1iZWQvJywgJ2VuYWJsZWpzYXBpPTEnICk7XG5cdFx0X2FwcGVuZFBhcmFtVG9JZnJhbWVTb3VyY2UoICdkYXRhLXNyYycsICd5b3V0dWJlLmNvbS9lbWJlZC8nLCAnZW5hYmxlanNhcGk9MScgKTtcblxuXHRcdC8vIFZpbWVvIGZyYW1lcyBtdXN0IGluY2x1ZGUgXCI/YXBpPTFcIlxuXHRcdF9hcHBlbmRQYXJhbVRvSWZyYW1lU291cmNlKCAnc3JjJywgJ3BsYXllci52aW1lby5jb20vJywgJ2FwaT0xJyApO1xuXHRcdF9hcHBlbmRQYXJhbVRvSWZyYW1lU291cmNlKCAnZGF0YS1zcmMnLCAncGxheWVyLnZpbWVvLmNvbS8nLCAnYXBpPTEnICk7XG5cblx0XHQvLyBBbHdheXMgc2hvdyBtZWRpYSBjb250cm9scyBvbiBtb2JpbGUgZGV2aWNlc1xuXHRcdGlmKCBpc01vYmlsZURldmljZSApIHtcblx0XHRcdHRvQXJyYXkoIGRvbS5zbGlkZXMucXVlcnlTZWxlY3RvckFsbCggJ3ZpZGVvLCBhdWRpbycgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHtcblx0XHRcdFx0ZWwuY29udHJvbHMgPSB0cnVlO1xuXHRcdFx0fSApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIFN0YXJ0IHBsYXliYWNrIG9mIGFueSBlbWJlZGRlZCBjb250ZW50IGluc2lkZSBvZlxuXHQgKiB0aGUgZ2l2ZW4gZWxlbWVudC5cblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWxlbWVudFxuXHQgKi9cblx0ZnVuY3Rpb24gc3RhcnRFbWJlZGRlZENvbnRlbnQoIGVsZW1lbnQgKSB7XG5cblx0XHRpZiggZWxlbWVudCAmJiAhaXNTcGVha2VyTm90ZXMoKSApIHtcblxuXHRcdFx0Ly8gUmVzdGFydCBHSUZzXG5cdFx0XHR0b0FycmF5KCBlbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICdpbWdbc3JjJD1cIi5naWZcIl0nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdC8vIFNldHRpbmcgdGhlIHNhbWUgdW5jaGFuZ2VkIHNvdXJjZSBsaWtlIHRoaXMgd2FzIGNvbmZpcm1lZFxuXHRcdFx0XHQvLyB0byB3b3JrIGluIENocm9tZSwgRkYgJiBTYWZhcmlcblx0XHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnc3JjJywgZWwuZ2V0QXR0cmlidXRlKCAnc3JjJyApICk7XG5cdFx0XHR9ICk7XG5cblx0XHRcdC8vIEhUTUw1IG1lZGlhIGVsZW1lbnRzXG5cdFx0XHR0b0FycmF5KCBlbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICd2aWRlbywgYXVkaW8nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdGlmKCBjbG9zZXN0UGFyZW50KCBlbCwgJy5mcmFnbWVudCcgKSAmJiAhY2xvc2VzdFBhcmVudCggZWwsICcuZnJhZ21lbnQudmlzaWJsZScgKSApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBQcmVmZXIgYW4gZXhwbGljaXQgZ2xvYmFsIGF1dG9wbGF5IHNldHRpbmdcblx0XHRcdFx0dmFyIGF1dG9wbGF5ID0gY29uZmlnLmF1dG9QbGF5TWVkaWE7XG5cblx0XHRcdFx0Ly8gSWYgbm8gZ2xvYmFsIHNldHRpbmcgaXMgYXZhaWxhYmxlLCBmYWxsIGJhY2sgb24gdGhlIGVsZW1lbnQnc1xuXHRcdFx0XHQvLyBvd24gYXV0b3BsYXkgc2V0dGluZ1xuXHRcdFx0XHRpZiggdHlwZW9mIGF1dG9wbGF5ICE9PSAnYm9vbGVhbicgKSB7XG5cdFx0XHRcdFx0YXV0b3BsYXkgPSBlbC5oYXNBdHRyaWJ1dGUoICdkYXRhLWF1dG9wbGF5JyApIHx8ICEhY2xvc2VzdFBhcmVudCggZWwsICcuc2xpZGUtYmFja2dyb3VuZCcgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCBhdXRvcGxheSAmJiB0eXBlb2YgZWwucGxheSA9PT0gJ2Z1bmN0aW9uJyApIHtcblxuXHRcdFx0XHRcdGlmKCBlbC5yZWFkeVN0YXRlID4gMSApIHtcblx0XHRcdFx0XHRcdHN0YXJ0RW1iZWRkZWRNZWRpYSggeyB0YXJnZXQ6IGVsIH0gKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0XHRlbC5yZW1vdmVFdmVudExpc3RlbmVyKCAnbG9hZGVkZGF0YScsIHN0YXJ0RW1iZWRkZWRNZWRpYSApOyAvLyByZW1vdmUgZmlyc3QgdG8gYXZvaWQgZHVwZXNcblx0XHRcdFx0XHRcdGVsLmFkZEV2ZW50TGlzdGVuZXIoICdsb2FkZWRkYXRhJywgc3RhcnRFbWJlZGRlZE1lZGlhICk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH1cblx0XHRcdH0gKTtcblxuXHRcdFx0Ly8gTm9ybWFsIGlmcmFtZXNcblx0XHRcdHRvQXJyYXkoIGVsZW1lbnQucXVlcnlTZWxlY3RvckFsbCggJ2lmcmFtZVtzcmNdJyApICkuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkge1xuXHRcdFx0XHRpZiggY2xvc2VzdFBhcmVudCggZWwsICcuZnJhZ21lbnQnICkgJiYgIWNsb3Nlc3RQYXJlbnQoIGVsLCAnLmZyYWdtZW50LnZpc2libGUnICkgKSB7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0c3RhcnRFbWJlZGRlZElmcmFtZSggeyB0YXJnZXQ6IGVsIH0gKTtcblx0XHRcdH0gKTtcblxuXHRcdFx0Ly8gTGF6eSBsb2FkaW5nIGlmcmFtZXNcblx0XHRcdHRvQXJyYXkoIGVsZW1lbnQucXVlcnlTZWxlY3RvckFsbCggJ2lmcmFtZVtkYXRhLXNyY10nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdGlmKCBjbG9zZXN0UGFyZW50KCBlbCwgJy5mcmFnbWVudCcgKSAmJiAhY2xvc2VzdFBhcmVudCggZWwsICcuZnJhZ21lbnQudmlzaWJsZScgKSApIHtcblx0XHRcdFx0XHRyZXR1cm47XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiggZWwuZ2V0QXR0cmlidXRlKCAnc3JjJyApICE9PSBlbC5nZXRBdHRyaWJ1dGUoICdkYXRhLXNyYycgKSApIHtcblx0XHRcdFx0XHRlbC5yZW1vdmVFdmVudExpc3RlbmVyKCAnbG9hZCcsIHN0YXJ0RW1iZWRkZWRJZnJhbWUgKTsgLy8gcmVtb3ZlIGZpcnN0IHRvIGF2b2lkIGR1cGVzXG5cdFx0XHRcdFx0ZWwuYWRkRXZlbnRMaXN0ZW5lciggJ2xvYWQnLCBzdGFydEVtYmVkZGVkSWZyYW1lICk7XG5cdFx0XHRcdFx0ZWwuc2V0QXR0cmlidXRlKCAnc3JjJywgZWwuZ2V0QXR0cmlidXRlKCAnZGF0YS1zcmMnICkgKTtcblx0XHRcdFx0fVxuXHRcdFx0fSApO1xuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogU3RhcnRzIHBsYXlpbmcgYW4gZW1iZWRkZWQgdmlkZW8vYXVkaW8gZWxlbWVudCBhZnRlclxuXHQgKiBpdCBoYXMgZmluaXNoZWQgbG9hZGluZy5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IGV2ZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBzdGFydEVtYmVkZGVkTWVkaWEoIGV2ZW50ICkge1xuXG5cdFx0dmFyIGlzQXR0YWNoZWRUb0RPTSA9ICEhY2xvc2VzdFBhcmVudCggZXZlbnQudGFyZ2V0LCAnaHRtbCcgKSxcblx0XHRcdGlzVmlzaWJsZSAgXHRcdD0gISFjbG9zZXN0UGFyZW50KCBldmVudC50YXJnZXQsICcucHJlc2VudCcgKTtcblxuXHRcdGlmKCBpc0F0dGFjaGVkVG9ET00gJiYgaXNWaXNpYmxlICkge1xuXHRcdFx0ZXZlbnQudGFyZ2V0LmN1cnJlbnRUaW1lID0gMDtcblx0XHRcdGV2ZW50LnRhcmdldC5wbGF5KCk7XG5cdFx0fVxuXG5cdFx0ZXZlbnQudGFyZ2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoICdsb2FkZWRkYXRhJywgc3RhcnRFbWJlZGRlZE1lZGlhICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBcIlN0YXJ0c1wiIHRoZSBjb250ZW50IG9mIGFuIGVtYmVkZGVkIGlmcmFtZSB1c2luZyB0aGVcblx0ICogcG9zdE1lc3NhZ2UgQVBJLlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gZXZlbnRcblx0ICovXG5cdGZ1bmN0aW9uIHN0YXJ0RW1iZWRkZWRJZnJhbWUoIGV2ZW50ICkge1xuXG5cdFx0dmFyIGlmcmFtZSA9IGV2ZW50LnRhcmdldDtcblxuXHRcdGlmKCBpZnJhbWUgJiYgaWZyYW1lLmNvbnRlbnRXaW5kb3cgKSB7XG5cblx0XHRcdHZhciBpc0F0dGFjaGVkVG9ET00gPSAhIWNsb3Nlc3RQYXJlbnQoIGV2ZW50LnRhcmdldCwgJ2h0bWwnICksXG5cdFx0XHRcdGlzVmlzaWJsZSAgXHRcdD0gISFjbG9zZXN0UGFyZW50KCBldmVudC50YXJnZXQsICcucHJlc2VudCcgKTtcblxuXHRcdFx0aWYoIGlzQXR0YWNoZWRUb0RPTSAmJiBpc1Zpc2libGUgKSB7XG5cblx0XHRcdFx0Ly8gUHJlZmVyIGFuIGV4cGxpY2l0IGdsb2JhbCBhdXRvcGxheSBzZXR0aW5nXG5cdFx0XHRcdHZhciBhdXRvcGxheSA9IGNvbmZpZy5hdXRvUGxheU1lZGlhO1xuXG5cdFx0XHRcdC8vIElmIG5vIGdsb2JhbCBzZXR0aW5nIGlzIGF2YWlsYWJsZSwgZmFsbCBiYWNrIG9uIHRoZSBlbGVtZW50J3Ncblx0XHRcdFx0Ly8gb3duIGF1dG9wbGF5IHNldHRpbmdcblx0XHRcdFx0aWYoIHR5cGVvZiBhdXRvcGxheSAhPT0gJ2Jvb2xlYW4nICkge1xuXHRcdFx0XHRcdGF1dG9wbGF5ID0gaWZyYW1lLmhhc0F0dHJpYnV0ZSggJ2RhdGEtYXV0b3BsYXknICkgfHwgISFjbG9zZXN0UGFyZW50KCBpZnJhbWUsICcuc2xpZGUtYmFja2dyb3VuZCcgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFlvdVR1YmUgcG9zdE1lc3NhZ2UgQVBJXG5cdFx0XHRcdGlmKCAveW91dHViZVxcLmNvbVxcL2VtYmVkXFwvLy50ZXN0KCBpZnJhbWUuZ2V0QXR0cmlidXRlKCAnc3JjJyApICkgJiYgYXV0b3BsYXkgKSB7XG5cdFx0XHRcdFx0aWZyYW1lLmNvbnRlbnRXaW5kb3cucG9zdE1lc3NhZ2UoICd7XCJldmVudFwiOlwiY29tbWFuZFwiLFwiZnVuY1wiOlwicGxheVZpZGVvXCIsXCJhcmdzXCI6XCJcIn0nLCAnKicgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHQvLyBWaW1lbyBwb3N0TWVzc2FnZSBBUElcblx0XHRcdFx0ZWxzZSBpZiggL3BsYXllclxcLnZpbWVvXFwuY29tXFwvLy50ZXN0KCBpZnJhbWUuZ2V0QXR0cmlidXRlKCAnc3JjJyApICkgJiYgYXV0b3BsYXkgKSB7XG5cdFx0XHRcdFx0aWZyYW1lLmNvbnRlbnRXaW5kb3cucG9zdE1lc3NhZ2UoICd7XCJtZXRob2RcIjpcInBsYXlcIn0nLCAnKicgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHQvLyBHZW5lcmljIHBvc3RNZXNzYWdlIEFQSVxuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRpZnJhbWUuY29udGVudFdpbmRvdy5wb3N0TWVzc2FnZSggJ3NsaWRlOnN0YXJ0JywgJyonICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogU3RvcCBwbGF5YmFjayBvZiBhbnkgZW1iZWRkZWQgY29udGVudCBpbnNpZGUgb2Zcblx0ICogdGhlIHRhcmdldGVkIHNsaWRlLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBlbGVtZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBzdG9wRW1iZWRkZWRDb250ZW50KCBlbGVtZW50LCBvcHRpb25zICkge1xuXG5cdFx0b3B0aW9ucyA9IGV4dGVuZCgge1xuXHRcdFx0Ly8gRGVmYXVsdHNcblx0XHRcdHVubG9hZElmcmFtZXM6IHRydWVcblx0XHR9LCBvcHRpb25zIHx8IHt9ICk7XG5cblx0XHRpZiggZWxlbWVudCAmJiBlbGVtZW50LnBhcmVudE5vZGUgKSB7XG5cdFx0XHQvLyBIVE1MNSBtZWRpYSBlbGVtZW50c1xuXHRcdFx0dG9BcnJheSggZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCAndmlkZW8sIGF1ZGlvJyApICkuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkge1xuXHRcdFx0XHRpZiggIWVsLmhhc0F0dHJpYnV0ZSggJ2RhdGEtaWdub3JlJyApICYmIHR5cGVvZiBlbC5wYXVzZSA9PT0gJ2Z1bmN0aW9uJyApIHtcblx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoJ2RhdGEtcGF1c2VkLWJ5LXJldmVhbCcsICcnKTtcblx0XHRcdFx0XHRlbC5wYXVzZSgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9ICk7XG5cblx0XHRcdC8vIEdlbmVyaWMgcG9zdE1lc3NhZ2UgQVBJIGZvciBub24tbGF6eSBsb2FkZWQgaWZyYW1lc1xuXHRcdFx0dG9BcnJheSggZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnaWZyYW1lJyApICkuZm9yRWFjaCggZnVuY3Rpb24oIGVsICkge1xuXHRcdFx0XHRpZiggZWwuY29udGVudFdpbmRvdyApIGVsLmNvbnRlbnRXaW5kb3cucG9zdE1lc3NhZ2UoICdzbGlkZTpzdG9wJywgJyonICk7XG5cdFx0XHRcdGVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoICdsb2FkJywgc3RhcnRFbWJlZGRlZElmcmFtZSApO1xuXHRcdFx0fSk7XG5cblx0XHRcdC8vIFlvdVR1YmUgcG9zdE1lc3NhZ2UgQVBJXG5cdFx0XHR0b0FycmF5KCBlbGVtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoICdpZnJhbWVbc3JjKj1cInlvdXR1YmUuY29tL2VtYmVkL1wiXScgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHtcblx0XHRcdFx0aWYoICFlbC5oYXNBdHRyaWJ1dGUoICdkYXRhLWlnbm9yZScgKSAmJiBlbC5jb250ZW50V2luZG93ICYmIHR5cGVvZiBlbC5jb250ZW50V2luZG93LnBvc3RNZXNzYWdlID09PSAnZnVuY3Rpb24nICkge1xuXHRcdFx0XHRcdGVsLmNvbnRlbnRXaW5kb3cucG9zdE1lc3NhZ2UoICd7XCJldmVudFwiOlwiY29tbWFuZFwiLFwiZnVuY1wiOlwicGF1c2VWaWRlb1wiLFwiYXJnc1wiOlwiXCJ9JywgJyonICk7XG5cdFx0XHRcdH1cblx0XHRcdH0pO1xuXG5cdFx0XHQvLyBWaW1lbyBwb3N0TWVzc2FnZSBBUElcblx0XHRcdHRvQXJyYXkoIGVsZW1lbnQucXVlcnlTZWxlY3RvckFsbCggJ2lmcmFtZVtzcmMqPVwicGxheWVyLnZpbWVvLmNvbS9cIl0nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdGlmKCAhZWwuaGFzQXR0cmlidXRlKCAnZGF0YS1pZ25vcmUnICkgJiYgZWwuY29udGVudFdpbmRvdyAmJiB0eXBlb2YgZWwuY29udGVudFdpbmRvdy5wb3N0TWVzc2FnZSA9PT0gJ2Z1bmN0aW9uJyApIHtcblx0XHRcdFx0XHRlbC5jb250ZW50V2luZG93LnBvc3RNZXNzYWdlKCAne1wibWV0aG9kXCI6XCJwYXVzZVwifScsICcqJyApO1xuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblxuXHRcdFx0aWYoIG9wdGlvbnMudW5sb2FkSWZyYW1lcyA9PT0gdHJ1ZSApIHtcblx0XHRcdFx0Ly8gVW5sb2FkIGxhenktbG9hZGVkIGlmcmFtZXNcblx0XHRcdFx0dG9BcnJheSggZWxlbWVudC5xdWVyeVNlbGVjdG9yQWxsKCAnaWZyYW1lW2RhdGEtc3JjXScgKSApLmZvckVhY2goIGZ1bmN0aW9uKCBlbCApIHtcblx0XHRcdFx0XHQvLyBPbmx5IHJlbW92aW5nIHRoZSBzcmMgZG9lc24ndCBhY3R1YWxseSB1bmxvYWQgdGhlIGZyYW1lXG5cdFx0XHRcdFx0Ly8gaW4gYWxsIGJyb3dzZXJzIChGaXJlZm94KSBzbyB3ZSBzZXQgaXQgdG8gYmxhbmsgZmlyc3Rcblx0XHRcdFx0XHRlbC5zZXRBdHRyaWJ1dGUoICdzcmMnLCAnYWJvdXQ6YmxhbmsnICk7XG5cdFx0XHRcdFx0ZWwucmVtb3ZlQXR0cmlidXRlKCAnc3JjJyApO1xuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogUmV0dXJucyB0aGUgbnVtYmVyIG9mIHBhc3Qgc2xpZGVzLiBUaGlzIGNhbiBiZSB1c2VkIGFzIGEgZ2xvYmFsXG5cdCAqIGZsYXR0ZW5lZCBpbmRleCBmb3Igc2xpZGVzLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9IFBhc3Qgc2xpZGUgY291bnRcblx0ICovXG5cdGZ1bmN0aW9uIGdldFNsaWRlUGFzdENvdW50KCkge1xuXG5cdFx0dmFyIGhvcml6b250YWxTbGlkZXMgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApICk7XG5cblx0XHQvLyBUaGUgbnVtYmVyIG9mIHBhc3Qgc2xpZGVzXG5cdFx0dmFyIHBhc3RDb3VudCA9IDA7XG5cblx0XHQvLyBTdGVwIHRocm91Z2ggYWxsIHNsaWRlcyBhbmQgY291bnQgdGhlIHBhc3Qgb25lc1xuXHRcdG1haW5Mb29wOiBmb3IoIHZhciBpID0gMDsgaSA8IGhvcml6b250YWxTbGlkZXMubGVuZ3RoOyBpKysgKSB7XG5cblx0XHRcdHZhciBob3Jpem9udGFsU2xpZGUgPSBob3Jpem9udGFsU2xpZGVzW2ldO1xuXHRcdFx0dmFyIHZlcnRpY2FsU2xpZGVzID0gdG9BcnJheSggaG9yaXpvbnRhbFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICdzZWN0aW9uJyApICk7XG5cblx0XHRcdGZvciggdmFyIGogPSAwOyBqIDwgdmVydGljYWxTbGlkZXMubGVuZ3RoOyBqKysgKSB7XG5cblx0XHRcdFx0Ly8gU3RvcCBhcyBzb29uIGFzIHdlIGFycml2ZSBhdCB0aGUgcHJlc2VudFxuXHRcdFx0XHRpZiggdmVydGljYWxTbGlkZXNbal0uY2xhc3NMaXN0LmNvbnRhaW5zKCAncHJlc2VudCcgKSApIHtcblx0XHRcdFx0XHRicmVhayBtYWluTG9vcDtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHBhc3RDb3VudCsrO1xuXG5cdFx0XHR9XG5cblx0XHRcdC8vIFN0b3AgYXMgc29vbiBhcyB3ZSBhcnJpdmUgYXQgdGhlIHByZXNlbnRcblx0XHRcdGlmKCBob3Jpem9udGFsU2xpZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAncHJlc2VudCcgKSApIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cblx0XHRcdC8vIERvbid0IGNvdW50IHRoZSB3cmFwcGluZyBzZWN0aW9uIGZvciB2ZXJ0aWNhbCBzbGlkZXNcblx0XHRcdGlmKCBob3Jpem9udGFsU2xpZGUuY2xhc3NMaXN0LmNvbnRhaW5zKCAnc3RhY2snICkgPT09IGZhbHNlICkge1xuXHRcdFx0XHRwYXN0Q291bnQrKztcblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdHJldHVybiBwYXN0Q291bnQ7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXR1cm5zIGEgdmFsdWUgcmFuZ2luZyBmcm9tIDAtMSB0aGF0IHJlcHJlc2VudHNcblx0ICogaG93IGZhciBpbnRvIHRoZSBwcmVzZW50YXRpb24gd2UgaGF2ZSBuYXZpZ2F0ZWQuXG5cdCAqXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGZ1bmN0aW9uIGdldFByb2dyZXNzKCkge1xuXG5cdFx0Ly8gVGhlIG51bWJlciBvZiBwYXN0IGFuZCB0b3RhbCBzbGlkZXNcblx0XHR2YXIgdG90YWxDb3VudCA9IGdldFRvdGFsU2xpZGVzKCk7XG5cdFx0dmFyIHBhc3RDb3VudCA9IGdldFNsaWRlUGFzdENvdW50KCk7XG5cblx0XHRpZiggY3VycmVudFNsaWRlICkge1xuXG5cdFx0XHR2YXIgYWxsRnJhZ21lbnRzID0gY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICcuZnJhZ21lbnQnICk7XG5cblx0XHRcdC8vIElmIHRoZXJlIGFyZSBmcmFnbWVudHMgaW4gdGhlIGN1cnJlbnQgc2xpZGUgdGhvc2Ugc2hvdWxkIGJlXG5cdFx0XHQvLyBhY2NvdW50ZWQgZm9yIGluIHRoZSBwcm9ncmVzcy5cblx0XHRcdGlmKCBhbGxGcmFnbWVudHMubGVuZ3RoID4gMCApIHtcblx0XHRcdFx0dmFyIHZpc2libGVGcmFnbWVudHMgPSBjdXJyZW50U2xpZGUucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudC52aXNpYmxlJyApO1xuXG5cdFx0XHRcdC8vIFRoaXMgdmFsdWUgcmVwcmVzZW50cyBob3cgYmlnIGEgcG9ydGlvbiBvZiB0aGUgc2xpZGUgcHJvZ3Jlc3Ncblx0XHRcdFx0Ly8gdGhhdCBpcyBtYWRlIHVwIGJ5IGl0cyBmcmFnbWVudHMgKDAtMSlcblx0XHRcdFx0dmFyIGZyYWdtZW50V2VpZ2h0ID0gMC45O1xuXG5cdFx0XHRcdC8vIEFkZCBmcmFnbWVudCBwcm9ncmVzcyB0byB0aGUgcGFzdCBzbGlkZSBjb3VudFxuXHRcdFx0XHRwYXN0Q291bnQgKz0gKCB2aXNpYmxlRnJhZ21lbnRzLmxlbmd0aCAvIGFsbEZyYWdtZW50cy5sZW5ndGggKSAqIGZyYWdtZW50V2VpZ2h0O1xuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIHBhc3RDb3VudCAvICggdG90YWxDb3VudCAtIDEgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiB0aGlzIHByZXNlbnRhdGlvbiBpcyBydW5uaW5nIGluc2lkZSBvZiB0aGVcblx0ICogc3BlYWtlciBub3RlcyB3aW5kb3cuXG5cdCAqXG5cdCAqIEByZXR1cm4ge2Jvb2xlYW59XG5cdCAqL1xuXHRmdW5jdGlvbiBpc1NwZWFrZXJOb3RlcygpIHtcblxuXHRcdHJldHVybiAhIXdpbmRvdy5sb2NhdGlvbi5zZWFyY2gubWF0Y2goIC9yZWNlaXZlci9naSApO1xuXG5cdH1cblxuXHQvKipcblx0ICogUmVhZHMgdGhlIGN1cnJlbnQgVVJMIChoYXNoKSBhbmQgbmF2aWdhdGVzIGFjY29yZGluZ2x5LlxuXHQgKi9cblx0ZnVuY3Rpb24gcmVhZFVSTCgpIHtcblxuXHRcdHZhciBoYXNoID0gd2luZG93LmxvY2F0aW9uLmhhc2g7XG5cblx0XHQvLyBBdHRlbXB0IHRvIHBhcnNlIHRoZSBoYXNoIGFzIGVpdGhlciBhbiBpbmRleCBvciBuYW1lXG5cdFx0dmFyIGJpdHMgPSBoYXNoLnNsaWNlKCAyICkuc3BsaXQoICcvJyApLFxuXHRcdFx0bmFtZSA9IGhhc2gucmVwbGFjZSggLyN8XFwvL2dpLCAnJyApO1xuXG5cdFx0Ly8gSWYgdGhlIGZpcnN0IGJpdCBpcyBpbnZhbGlkIGFuZCB0aGVyZSBpcyBhIG5hbWUgd2UgY2FuXG5cdFx0Ly8gYXNzdW1lIHRoYXQgdGhpcyBpcyBhIG5hbWVkIGxpbmtcblx0XHRpZiggaXNOYU4oIHBhcnNlSW50KCBiaXRzWzBdLCAxMCApICkgJiYgbmFtZS5sZW5ndGggKSB7XG5cdFx0XHR2YXIgZWxlbWVudDtcblxuXHRcdFx0Ly8gRW5zdXJlIHRoZSBuYW1lZCBsaW5rIGlzIGEgdmFsaWQgSFRNTCBJRCBhdHRyaWJ1dGVcblx0XHRcdGlmKCAvXlthLXpBLVpdW1xcdzouLV0qJC8udGVzdCggbmFtZSApICkge1xuXHRcdFx0XHQvLyBGaW5kIHRoZSBzbGlkZSB3aXRoIHRoZSBzcGVjaWZpZWQgSURcblx0XHRcdFx0ZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCBuYW1lICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmKCBlbGVtZW50ICkge1xuXHRcdFx0XHQvLyBGaW5kIHRoZSBwb3NpdGlvbiBvZiB0aGUgbmFtZWQgc2xpZGUgYW5kIG5hdmlnYXRlIHRvIGl0XG5cdFx0XHRcdHZhciBpbmRpY2VzID0gUmV2ZWFsLmdldEluZGljZXMoIGVsZW1lbnQgKTtcblx0XHRcdFx0c2xpZGUoIGluZGljZXMuaCwgaW5kaWNlcy52ICk7XG5cdFx0XHR9XG5cdFx0XHQvLyBJZiB0aGUgc2xpZGUgZG9lc24ndCBleGlzdCwgbmF2aWdhdGUgdG8gdGhlIGN1cnJlbnQgc2xpZGVcblx0XHRcdGVsc2Uge1xuXHRcdFx0XHRzbGlkZSggaW5kZXhoIHx8IDAsIGluZGV4diB8fCAwICk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0Ly8gUmVhZCB0aGUgaW5kZXggY29tcG9uZW50cyBvZiB0aGUgaGFzaFxuXHRcdFx0dmFyIGggPSBwYXJzZUludCggYml0c1swXSwgMTAgKSB8fCAwLFxuXHRcdFx0XHR2ID0gcGFyc2VJbnQoIGJpdHNbMV0sIDEwICkgfHwgMDtcblxuXHRcdFx0aWYoIGggIT09IGluZGV4aCB8fCB2ICE9PSBpbmRleHYgKSB7XG5cdFx0XHRcdHNsaWRlKCBoLCB2ICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogVXBkYXRlcyB0aGUgcGFnZSBVUkwgKGhhc2gpIHRvIHJlZmxlY3QgdGhlIGN1cnJlbnRcblx0ICogc3RhdGUuXG5cdCAqXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBkZWxheSBUaGUgdGltZSBpbiBtcyB0byB3YWl0IGJlZm9yZVxuXHQgKiB3cml0aW5nIHRoZSBoYXNoXG5cdCAqL1xuXHRmdW5jdGlvbiB3cml0ZVVSTCggZGVsYXkgKSB7XG5cblx0XHRpZiggY29uZmlnLmhpc3RvcnkgKSB7XG5cblx0XHRcdC8vIE1ha2Ugc3VyZSB0aGVyZSdzIG5ldmVyIG1vcmUgdGhhbiBvbmUgdGltZW91dCBydW5uaW5nXG5cdFx0XHRjbGVhclRpbWVvdXQoIHdyaXRlVVJMVGltZW91dCApO1xuXG5cdFx0XHQvLyBJZiBhIGRlbGF5IGlzIHNwZWNpZmllZCwgdGltZW91dCB0aGlzIGNhbGxcblx0XHRcdGlmKCB0eXBlb2YgZGVsYXkgPT09ICdudW1iZXInICkge1xuXHRcdFx0XHR3cml0ZVVSTFRpbWVvdXQgPSBzZXRUaW1lb3V0KCB3cml0ZVVSTCwgZGVsYXkgKTtcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoIGN1cnJlbnRTbGlkZSApIHtcblx0XHRcdFx0dmFyIHVybCA9ICcvJztcblxuXHRcdFx0XHQvLyBBdHRlbXB0IHRvIGNyZWF0ZSBhIG5hbWVkIGxpbmsgYmFzZWQgb24gdGhlIHNsaWRlJ3MgSURcblx0XHRcdFx0dmFyIGlkID0gY3VycmVudFNsaWRlLmdldEF0dHJpYnV0ZSggJ2lkJyApO1xuXHRcdFx0XHRpZiggaWQgKSB7XG5cdFx0XHRcdFx0aWQgPSBpZC5yZXBsYWNlKCAvW15hLXpBLVowLTlcXC1cXF9cXDpcXC5dL2csICcnICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBJZiB0aGUgY3VycmVudCBzbGlkZSBoYXMgYW4gSUQsIHVzZSB0aGF0IGFzIGEgbmFtZWQgbGlua1xuXHRcdFx0XHRpZiggdHlwZW9mIGlkID09PSAnc3RyaW5nJyAmJiBpZC5sZW5ndGggKSB7XG5cdFx0XHRcdFx0dXJsID0gJy8nICsgaWQ7XG5cdFx0XHRcdH1cblx0XHRcdFx0Ly8gT3RoZXJ3aXNlIHVzZSB0aGUgL2gvdiBpbmRleFxuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRpZiggaW5kZXhoID4gMCB8fCBpbmRleHYgPiAwICkgdXJsICs9IGluZGV4aDtcblx0XHRcdFx0XHRpZiggaW5kZXh2ID4gMCApIHVybCArPSAnLycgKyBpbmRleHY7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR3aW5kb3cubG9jYXRpb24uaGFzaCA9IHVybDtcblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXHQvKipcblx0ICogUmV0cmlldmVzIHRoZSBoL3YgbG9jYXRpb24gYW5kIGZyYWdtZW50IG9mIHRoZSBjdXJyZW50LFxuXHQgKiBvciBzcGVjaWZpZWQsIHNsaWRlLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0hUTUxFbGVtZW50fSBbc2xpZGVdIElmIHNwZWNpZmllZCwgdGhlIHJldHVybmVkXG5cdCAqIGluZGV4IHdpbGwgYmUgZm9yIHRoaXMgc2xpZGUgcmF0aGVyIHRoYW4gdGhlIGN1cnJlbnRseVxuXHQgKiBhY3RpdmUgb25lXG5cdCAqXG5cdCAqIEByZXR1cm4ge3toOiBudW1iZXIsIHY6IG51bWJlciwgZjogbnVtYmVyfX1cblx0ICovXG5cdGZ1bmN0aW9uIGdldEluZGljZXMoIHNsaWRlICkge1xuXG5cdFx0Ly8gQnkgZGVmYXVsdCwgcmV0dXJuIHRoZSBjdXJyZW50IGluZGljZXNcblx0XHR2YXIgaCA9IGluZGV4aCxcblx0XHRcdHYgPSBpbmRleHYsXG5cdFx0XHRmO1xuXG5cdFx0Ly8gSWYgYSBzbGlkZSBpcyBzcGVjaWZpZWQsIHJldHVybiB0aGUgaW5kaWNlcyBvZiB0aGF0IHNsaWRlXG5cdFx0aWYoIHNsaWRlICkge1xuXHRcdFx0dmFyIGlzVmVydGljYWwgPSBpc1ZlcnRpY2FsU2xpZGUoIHNsaWRlICk7XG5cdFx0XHR2YXIgc2xpZGVoID0gaXNWZXJ0aWNhbCA/IHNsaWRlLnBhcmVudE5vZGUgOiBzbGlkZTtcblxuXHRcdFx0Ly8gU2VsZWN0IGFsbCBob3Jpem9udGFsIHNsaWRlc1xuXHRcdFx0dmFyIGhvcml6b250YWxTbGlkZXMgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiApICk7XG5cblx0XHRcdC8vIE5vdyB0aGF0IHdlIGtub3cgd2hpY2ggdGhlIGhvcml6b250YWwgc2xpZGUgaXMsIGdldCBpdHMgaW5kZXhcblx0XHRcdGggPSBNYXRoLm1heCggaG9yaXpvbnRhbFNsaWRlcy5pbmRleE9mKCBzbGlkZWggKSwgMCApO1xuXG5cdFx0XHQvLyBBc3N1bWUgd2UncmUgbm90IHZlcnRpY2FsXG5cdFx0XHR2ID0gdW5kZWZpbmVkO1xuXG5cdFx0XHQvLyBJZiB0aGlzIGlzIGEgdmVydGljYWwgc2xpZGUsIGdyYWIgdGhlIHZlcnRpY2FsIGluZGV4XG5cdFx0XHRpZiggaXNWZXJ0aWNhbCApIHtcblx0XHRcdFx0diA9IE1hdGgubWF4KCB0b0FycmF5KCBzbGlkZS5wYXJlbnROb2RlLnF1ZXJ5U2VsZWN0b3JBbGwoICdzZWN0aW9uJyApICkuaW5kZXhPZiggc2xpZGUgKSwgMCApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmKCAhc2xpZGUgJiYgY3VycmVudFNsaWRlICkge1xuXHRcdFx0dmFyIGhhc0ZyYWdtZW50cyA9IGN1cnJlbnRTbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnLmZyYWdtZW50JyApLmxlbmd0aCA+IDA7XG5cdFx0XHRpZiggaGFzRnJhZ21lbnRzICkge1xuXHRcdFx0XHR2YXIgY3VycmVudEZyYWdtZW50ID0gY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3IoICcuY3VycmVudC1mcmFnbWVudCcgKTtcblx0XHRcdFx0aWYoIGN1cnJlbnRGcmFnbWVudCAmJiBjdXJyZW50RnJhZ21lbnQuaGFzQXR0cmlidXRlKCAnZGF0YS1mcmFnbWVudC1pbmRleCcgKSApIHtcblx0XHRcdFx0XHRmID0gcGFyc2VJbnQoIGN1cnJlbnRGcmFnbWVudC5nZXRBdHRyaWJ1dGUoICdkYXRhLWZyYWdtZW50LWluZGV4JyApLCAxMCApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2Uge1xuXHRcdFx0XHRcdGYgPSBjdXJyZW50U2xpZGUucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudC52aXNpYmxlJyApLmxlbmd0aCAtIDE7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4geyBoOiBoLCB2OiB2LCBmOiBmIH07XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXRyaWV2ZXMgYWxsIHNsaWRlcyBpbiB0aGlzIHByZXNlbnRhdGlvbi5cblx0ICovXG5cdGZ1bmN0aW9uIGdldFNsaWRlcygpIHtcblxuXHRcdHJldHVybiB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBTTElERVNfU0VMRUNUT1IgKyAnOm5vdCguc3RhY2spJyApKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIFJldHJpZXZlcyB0aGUgdG90YWwgbnVtYmVyIG9mIHNsaWRlcyBpbiB0aGlzIHByZXNlbnRhdGlvbi5cblx0ICpcblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0ZnVuY3Rpb24gZ2V0VG90YWxTbGlkZXMoKSB7XG5cblx0XHRyZXR1cm4gZ2V0U2xpZGVzKCkubGVuZ3RoO1xuXG5cdH1cblxuXHQvKipcblx0ICogUmV0dXJucyB0aGUgc2xpZGUgZWxlbWVudCBtYXRjaGluZyB0aGUgc3BlY2lmaWVkIGluZGV4LlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtIVE1MRWxlbWVudH1cblx0ICovXG5cdGZ1bmN0aW9uIGdldFNsaWRlKCB4LCB5ICkge1xuXG5cdFx0dmFyIGhvcml6b250YWxTbGlkZSA9IGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIEhPUklaT05UQUxfU0xJREVTX1NFTEVDVE9SIClbIHggXTtcblx0XHR2YXIgdmVydGljYWxTbGlkZXMgPSBob3Jpem9udGFsU2xpZGUgJiYgaG9yaXpvbnRhbFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICdzZWN0aW9uJyApO1xuXG5cdFx0aWYoIHZlcnRpY2FsU2xpZGVzICYmIHZlcnRpY2FsU2xpZGVzLmxlbmd0aCAmJiB0eXBlb2YgeSA9PT0gJ251bWJlcicgKSB7XG5cdFx0XHRyZXR1cm4gdmVydGljYWxTbGlkZXMgPyB2ZXJ0aWNhbFNsaWRlc1sgeSBdIDogdW5kZWZpbmVkO1xuXHRcdH1cblxuXHRcdHJldHVybiBob3Jpem9udGFsU2xpZGU7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXR1cm5zIHRoZSBiYWNrZ3JvdW5kIGVsZW1lbnQgZm9yIHRoZSBnaXZlbiBzbGlkZS5cblx0ICogQWxsIHNsaWRlcywgZXZlbiB0aGUgb25lcyB3aXRoIG5vIGJhY2tncm91bmQgcHJvcGVydGllc1xuXHQgKiBkZWZpbmVkLCBoYXZlIGEgYmFja2dyb3VuZCBlbGVtZW50IHNvIGFzIGxvbmcgYXMgdGhlXG5cdCAqIGluZGV4IGlzIHZhbGlkIGFuIGVsZW1lbnQgd2lsbCBiZSByZXR1cm5lZC5cblx0ICpcblx0ICogQHBhcmFtIHtudW1iZXJ9IHggSG9yaXpvbnRhbCBiYWNrZ3JvdW5kIGluZGV4XG5cdCAqIEBwYXJhbSB7bnVtYmVyfSB5IFZlcnRpY2FsIGJhY2tncm91bmQgaW5kZXhcblx0ICogQHJldHVybiB7KEhUTUxFbGVtZW50W118Kil9XG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRTbGlkZUJhY2tncm91bmQoIHgsIHkgKSB7XG5cblx0XHR2YXIgc2xpZGUgPSBnZXRTbGlkZSggeCwgeSApO1xuXHRcdGlmKCBzbGlkZSApIHtcblx0XHRcdHJldHVybiBzbGlkZS5zbGlkZUJhY2tncm91bmRFbGVtZW50O1xuXHRcdH1cblxuXHRcdHJldHVybiB1bmRlZmluZWQ7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXRyaWV2ZXMgdGhlIHNwZWFrZXIgbm90ZXMgZnJvbSBhIHNsaWRlLiBOb3RlcyBjYW4gYmVcblx0ICogZGVmaW5lZCBpbiB0d28gd2F5czpcblx0ICogMS4gQXMgYSBkYXRhLW5vdGVzIGF0dHJpYnV0ZSBvbiB0aGUgc2xpZGUgPHNlY3Rpb24+XG5cdCAqIDIuIEFzIGFuIDxhc2lkZSBjbGFzcz1cIm5vdGVzXCI+IGluc2lkZSBvZiB0aGUgc2xpZGVcblx0ICpcblx0ICogQHBhcmFtIHtIVE1MRWxlbWVudH0gW3NsaWRlPWN1cnJlbnRTbGlkZV1cblx0ICogQHJldHVybiB7KHN0cmluZ3xudWxsKX1cblx0ICovXG5cdGZ1bmN0aW9uIGdldFNsaWRlTm90ZXMoIHNsaWRlICkge1xuXG5cdFx0Ly8gRGVmYXVsdCB0byB0aGUgY3VycmVudCBzbGlkZVxuXHRcdHNsaWRlID0gc2xpZGUgfHwgY3VycmVudFNsaWRlO1xuXG5cdFx0Ly8gTm90ZXMgY2FuIGJlIHNwZWNpZmllZCB2aWEgdGhlIGRhdGEtbm90ZXMgYXR0cmlidXRlLi4uXG5cdFx0aWYoIHNsaWRlLmhhc0F0dHJpYnV0ZSggJ2RhdGEtbm90ZXMnICkgKSB7XG5cdFx0XHRyZXR1cm4gc2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1ub3RlcycgKTtcblx0XHR9XG5cblx0XHQvLyAuLi4gb3IgdXNpbmcgYW4gPGFzaWRlIGNsYXNzPVwibm90ZXNcIj4gZWxlbWVudFxuXHRcdHZhciBub3Rlc0VsZW1lbnQgPSBzbGlkZS5xdWVyeVNlbGVjdG9yKCAnYXNpZGUubm90ZXMnICk7XG5cdFx0aWYoIG5vdGVzRWxlbWVudCApIHtcblx0XHRcdHJldHVybiBub3Rlc0VsZW1lbnQuaW5uZXJIVE1MO1xuXHRcdH1cblxuXHRcdHJldHVybiBudWxsO1xuXG5cdH1cblxuXHQvKipcblx0ICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwcmVzZW50YXRpb24gYXNcblx0ICogYW4gb2JqZWN0LiBUaGlzIHN0YXRlIGNhbiB0aGVuIGJlIHJlc3RvcmVkIGF0IGFueVxuXHQgKiB0aW1lLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHt7aW5kZXhoOiBudW1iZXIsIGluZGV4djogbnVtYmVyLCBpbmRleGY6IG51bWJlciwgcGF1c2VkOiBib29sZWFuLCBvdmVydmlldzogYm9vbGVhbn19XG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRTdGF0ZSgpIHtcblxuXHRcdHZhciBpbmRpY2VzID0gZ2V0SW5kaWNlcygpO1xuXG5cdFx0cmV0dXJuIHtcblx0XHRcdGluZGV4aDogaW5kaWNlcy5oLFxuXHRcdFx0aW5kZXh2OiBpbmRpY2VzLnYsXG5cdFx0XHRpbmRleGY6IGluZGljZXMuZixcblx0XHRcdHBhdXNlZDogaXNQYXVzZWQoKSxcblx0XHRcdG92ZXJ2aWV3OiBpc092ZXJ2aWV3KClcblx0XHR9O1xuXG5cdH1cblxuXHQvKipcblx0ICogUmVzdG9yZXMgdGhlIHByZXNlbnRhdGlvbiB0byB0aGUgZ2l2ZW4gc3RhdGUuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBzdGF0ZSBBcyBnZW5lcmF0ZWQgYnkgZ2V0U3RhdGUoKVxuXHQgKiBAc2VlIHtAbGluayBnZXRTdGF0ZX0gZ2VuZXJhdGVzIHRoZSBwYXJhbWV0ZXIgYHN0YXRlYFxuXHQgKi9cblx0ZnVuY3Rpb24gc2V0U3RhdGUoIHN0YXRlICkge1xuXG5cdFx0aWYoIHR5cGVvZiBzdGF0ZSA9PT0gJ29iamVjdCcgKSB7XG5cdFx0XHRzbGlkZSggZGVzZXJpYWxpemUoIHN0YXRlLmluZGV4aCApLCBkZXNlcmlhbGl6ZSggc3RhdGUuaW5kZXh2ICksIGRlc2VyaWFsaXplKCBzdGF0ZS5pbmRleGYgKSApO1xuXG5cdFx0XHR2YXIgcGF1c2VkRmxhZyA9IGRlc2VyaWFsaXplKCBzdGF0ZS5wYXVzZWQgKSxcblx0XHRcdFx0b3ZlcnZpZXdGbGFnID0gZGVzZXJpYWxpemUoIHN0YXRlLm92ZXJ2aWV3ICk7XG5cblx0XHRcdGlmKCB0eXBlb2YgcGF1c2VkRmxhZyA9PT0gJ2Jvb2xlYW4nICYmIHBhdXNlZEZsYWcgIT09IGlzUGF1c2VkKCkgKSB7XG5cdFx0XHRcdHRvZ2dsZVBhdXNlKCBwYXVzZWRGbGFnICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmKCB0eXBlb2Ygb3ZlcnZpZXdGbGFnID09PSAnYm9vbGVhbicgJiYgb3ZlcnZpZXdGbGFnICE9PSBpc092ZXJ2aWV3KCkgKSB7XG5cdFx0XHRcdHRvZ2dsZU92ZXJ2aWV3KCBvdmVydmlld0ZsYWcgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBSZXR1cm4gYSBzb3J0ZWQgZnJhZ21lbnRzIGxpc3QsIG9yZGVyZWQgYnkgYW4gaW5jcmVhc2luZ1xuXHQgKiBcImRhdGEtZnJhZ21lbnQtaW5kZXhcIiBhdHRyaWJ1dGUuXG5cdCAqXG5cdCAqIEZyYWdtZW50cyB3aWxsIGJlIHJldmVhbGVkIGluIHRoZSBvcmRlciB0aGF0IHRoZXkgYXJlIHJldHVybmVkIGJ5XG5cdCAqIHRoaXMgZnVuY3Rpb24sIHNvIHlvdSBjYW4gdXNlIHRoZSBpbmRleCBhdHRyaWJ1dGVzIHRvIGNvbnRyb2wgdGhlXG5cdCAqIG9yZGVyIG9mIGZyYWdtZW50IGFwcGVhcmFuY2UuXG5cdCAqXG5cdCAqIFRvIG1haW50YWluIGEgc2Vuc2libGUgZGVmYXVsdCBmcmFnbWVudCBvcmRlciwgZnJhZ21lbnRzIGFyZSBwcmVzdW1lZFxuXHQgKiB0byBiZSBwYXNzZWQgaW4gZG9jdW1lbnQgb3JkZXIuIFRoaXMgZnVuY3Rpb24gYWRkcyBhIFwiZnJhZ21lbnQtaW5kZXhcIlxuXHQgKiBhdHRyaWJ1dGUgdG8gZWFjaCBub2RlIGlmIHN1Y2ggYW4gYXR0cmlidXRlIGlzIG5vdCBhbHJlYWR5IHByZXNlbnQsXG5cdCAqIGFuZCBzZXRzIHRoYXQgYXR0cmlidXRlIHRvIGFuIGludGVnZXIgdmFsdWUgd2hpY2ggaXMgdGhlIHBvc2l0aW9uIG9mXG5cdCAqIHRoZSBmcmFnbWVudCB3aXRoaW4gdGhlIGZyYWdtZW50cyBsaXN0LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdFtdfCp9IGZyYWdtZW50c1xuXHQgKiBAcmV0dXJuIHtvYmplY3RbXX0gc29ydGVkIFNvcnRlZCBhcnJheSBvZiBmcmFnbWVudHNcblx0ICovXG5cdGZ1bmN0aW9uIHNvcnRGcmFnbWVudHMoIGZyYWdtZW50cyApIHtcblxuXHRcdGZyYWdtZW50cyA9IHRvQXJyYXkoIGZyYWdtZW50cyApO1xuXG5cdFx0dmFyIG9yZGVyZWQgPSBbXSxcblx0XHRcdHVub3JkZXJlZCA9IFtdLFxuXHRcdFx0c29ydGVkID0gW107XG5cblx0XHQvLyBHcm91cCBvcmRlcmVkIGFuZCB1bm9yZGVyZWQgZWxlbWVudHNcblx0XHRmcmFnbWVudHMuZm9yRWFjaCggZnVuY3Rpb24oIGZyYWdtZW50LCBpICkge1xuXHRcdFx0aWYoIGZyYWdtZW50Lmhhc0F0dHJpYnV0ZSggJ2RhdGEtZnJhZ21lbnQtaW5kZXgnICkgKSB7XG5cdFx0XHRcdHZhciBpbmRleCA9IHBhcnNlSW50KCBmcmFnbWVudC5nZXRBdHRyaWJ1dGUoICdkYXRhLWZyYWdtZW50LWluZGV4JyApLCAxMCApO1xuXG5cdFx0XHRcdGlmKCAhb3JkZXJlZFtpbmRleF0gKSB7XG5cdFx0XHRcdFx0b3JkZXJlZFtpbmRleF0gPSBbXTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdG9yZGVyZWRbaW5kZXhdLnB1c2goIGZyYWdtZW50ICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0dW5vcmRlcmVkLnB1c2goIFsgZnJhZ21lbnQgXSApO1xuXHRcdFx0fVxuXHRcdH0gKTtcblxuXHRcdC8vIEFwcGVuZCBmcmFnbWVudHMgd2l0aG91dCBleHBsaWNpdCBpbmRpY2VzIGluIHRoZWlyXG5cdFx0Ly8gRE9NIG9yZGVyXG5cdFx0b3JkZXJlZCA9IG9yZGVyZWQuY29uY2F0KCB1bm9yZGVyZWQgKTtcblxuXHRcdC8vIE1hbnVhbGx5IGNvdW50IHRoZSBpbmRleCB1cCBwZXIgZ3JvdXAgdG8gZW5zdXJlIHRoZXJlXG5cdFx0Ly8gYXJlIG5vIGdhcHNcblx0XHR2YXIgaW5kZXggPSAwO1xuXG5cdFx0Ly8gUHVzaCBhbGwgZnJhZ21lbnRzIGluIHRoZWlyIHNvcnRlZCBvcmRlciB0byBhbiBhcnJheSxcblx0XHQvLyB0aGlzIGZsYXR0ZW5zIHRoZSBncm91cHNcblx0XHRvcmRlcmVkLmZvckVhY2goIGZ1bmN0aW9uKCBncm91cCApIHtcblx0XHRcdGdyb3VwLmZvckVhY2goIGZ1bmN0aW9uKCBmcmFnbWVudCApIHtcblx0XHRcdFx0c29ydGVkLnB1c2goIGZyYWdtZW50ICk7XG5cdFx0XHRcdGZyYWdtZW50LnNldEF0dHJpYnV0ZSggJ2RhdGEtZnJhZ21lbnQtaW5kZXgnLCBpbmRleCApO1xuXHRcdFx0fSApO1xuXG5cdFx0XHRpbmRleCArKztcblx0XHR9ICk7XG5cblx0XHRyZXR1cm4gc29ydGVkO1xuXG5cdH1cblxuXHQvKipcblx0ICogTmF2aWdhdGUgdG8gdGhlIHNwZWNpZmllZCBzbGlkZSBmcmFnbWVudC5cblx0ICpcblx0ICogQHBhcmFtIHs/bnVtYmVyfSBpbmRleCBUaGUgaW5kZXggb2YgdGhlIGZyYWdtZW50IHRoYXRcblx0ICogc2hvdWxkIGJlIHNob3duLCAtMSBtZWFucyBhbGwgYXJlIGludmlzaWJsZVxuXHQgKiBAcGFyYW0ge251bWJlcn0gb2Zmc2V0IEludGVnZXIgb2Zmc2V0IHRvIGFwcGx5IHRvIHRoZVxuXHQgKiBmcmFnbWVudCBpbmRleFxuXHQgKlxuXHQgKiBAcmV0dXJuIHtib29sZWFufSB0cnVlIGlmIGEgY2hhbmdlIHdhcyBtYWRlIGluIGFueVxuXHQgKiBmcmFnbWVudHMgdmlzaWJpbGl0eSBhcyBwYXJ0IG9mIHRoaXMgY2FsbFxuXHQgKi9cblx0ZnVuY3Rpb24gbmF2aWdhdGVGcmFnbWVudCggaW5kZXgsIG9mZnNldCApIHtcblxuXHRcdGlmKCBjdXJyZW50U2xpZGUgJiYgY29uZmlnLmZyYWdtZW50cyApIHtcblxuXHRcdFx0dmFyIGZyYWdtZW50cyA9IHNvcnRGcmFnbWVudHMoIGN1cnJlbnRTbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnLmZyYWdtZW50JyApICk7XG5cdFx0XHRpZiggZnJhZ21lbnRzLmxlbmd0aCApIHtcblxuXHRcdFx0XHQvLyBJZiBubyBpbmRleCBpcyBzcGVjaWZpZWQsIGZpbmQgdGhlIGN1cnJlbnRcblx0XHRcdFx0aWYoIHR5cGVvZiBpbmRleCAhPT0gJ251bWJlcicgKSB7XG5cdFx0XHRcdFx0dmFyIGxhc3RWaXNpYmxlRnJhZ21lbnQgPSBzb3J0RnJhZ21lbnRzKCBjdXJyZW50U2xpZGUucXVlcnlTZWxlY3RvckFsbCggJy5mcmFnbWVudC52aXNpYmxlJyApICkucG9wKCk7XG5cblx0XHRcdFx0XHRpZiggbGFzdFZpc2libGVGcmFnbWVudCApIHtcblx0XHRcdFx0XHRcdGluZGV4ID0gcGFyc2VJbnQoIGxhc3RWaXNpYmxlRnJhZ21lbnQuZ2V0QXR0cmlidXRlKCAnZGF0YS1mcmFnbWVudC1pbmRleCcgKSB8fCAwLCAxMCApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRcdGluZGV4ID0gLTE7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gSWYgYW4gb2Zmc2V0IGlzIHNwZWNpZmllZCwgYXBwbHkgaXQgdG8gdGhlIGluZGV4XG5cdFx0XHRcdGlmKCB0eXBlb2Ygb2Zmc2V0ID09PSAnbnVtYmVyJyApIHtcblx0XHRcdFx0XHRpbmRleCArPSBvZmZzZXQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR2YXIgZnJhZ21lbnRzU2hvd24gPSBbXSxcblx0XHRcdFx0XHRmcmFnbWVudHNIaWRkZW4gPSBbXTtcblxuXHRcdFx0XHR0b0FycmF5KCBmcmFnbWVudHMgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWxlbWVudCwgaSApIHtcblxuXHRcdFx0XHRcdGlmKCBlbGVtZW50Lmhhc0F0dHJpYnV0ZSggJ2RhdGEtZnJhZ21lbnQtaW5kZXgnICkgKSB7XG5cdFx0XHRcdFx0XHRpID0gcGFyc2VJbnQoIGVsZW1lbnQuZ2V0QXR0cmlidXRlKCAnZGF0YS1mcmFnbWVudC1pbmRleCcgKSwgMTAgKTtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBWaXNpYmxlIGZyYWdtZW50c1xuXHRcdFx0XHRcdGlmKCBpIDw9IGluZGV4ICkge1xuXHRcdFx0XHRcdFx0aWYoICFlbGVtZW50LmNsYXNzTGlzdC5jb250YWlucyggJ3Zpc2libGUnICkgKSBmcmFnbWVudHNTaG93bi5wdXNoKCBlbGVtZW50ICk7XG5cdFx0XHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5hZGQoICd2aXNpYmxlJyApO1xuXHRcdFx0XHRcdFx0ZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCAnY3VycmVudC1mcmFnbWVudCcgKTtcblxuXHRcdFx0XHRcdFx0Ly8gQW5ub3VuY2UgdGhlIGZyYWdtZW50cyBvbmUgYnkgb25lIHRvIHRoZSBTY3JlZW4gUmVhZGVyXG5cdFx0XHRcdFx0XHRkb20uc3RhdHVzRGl2LnRleHRDb250ZW50ID0gZ2V0U3RhdHVzVGV4dCggZWxlbWVudCApO1xuXG5cdFx0XHRcdFx0XHRpZiggaSA9PT0gaW5kZXggKSB7XG5cdFx0XHRcdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LmFkZCggJ2N1cnJlbnQtZnJhZ21lbnQnICk7XG5cdFx0XHRcdFx0XHRcdHN0YXJ0RW1iZWRkZWRDb250ZW50KCBlbGVtZW50ICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdC8vIEhpZGRlbiBmcmFnbWVudHNcblx0XHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRcdGlmKCBlbGVtZW50LmNsYXNzTGlzdC5jb250YWlucyggJ3Zpc2libGUnICkgKSBmcmFnbWVudHNIaWRkZW4ucHVzaCggZWxlbWVudCApO1xuXHRcdFx0XHRcdFx0ZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCAndmlzaWJsZScgKTtcblx0XHRcdFx0XHRcdGVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSggJ2N1cnJlbnQtZnJhZ21lbnQnICk7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdH0gKTtcblxuXHRcdFx0XHRpZiggZnJhZ21lbnRzSGlkZGVuLmxlbmd0aCApIHtcblx0XHRcdFx0XHRkaXNwYXRjaEV2ZW50KCAnZnJhZ21lbnRoaWRkZW4nLCB7IGZyYWdtZW50OiBmcmFnbWVudHNIaWRkZW5bMF0sIGZyYWdtZW50czogZnJhZ21lbnRzSGlkZGVuIH0gKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmKCBmcmFnbWVudHNTaG93bi5sZW5ndGggKSB7XG5cdFx0XHRcdFx0ZGlzcGF0Y2hFdmVudCggJ2ZyYWdtZW50c2hvd24nLCB7IGZyYWdtZW50OiBmcmFnbWVudHNTaG93blswXSwgZnJhZ21lbnRzOiBmcmFnbWVudHNTaG93biB9ICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR1cGRhdGVDb250cm9scygpO1xuXHRcdFx0XHR1cGRhdGVQcm9ncmVzcygpO1xuXG5cdFx0XHRcdHJldHVybiAhISggZnJhZ21lbnRzU2hvd24ubGVuZ3RoIHx8IGZyYWdtZW50c0hpZGRlbi5sZW5ndGggKTtcblxuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdFx0cmV0dXJuIGZhbHNlO1xuXG5cdH1cblxuXHQvKipcblx0ICogTmF2aWdhdGUgdG8gdGhlIG5leHQgc2xpZGUgZnJhZ21lbnQuXG5cdCAqXG5cdCAqIEByZXR1cm4ge2Jvb2xlYW59IHRydWUgaWYgdGhlcmUgd2FzIGEgbmV4dCBmcmFnbWVudCxcblx0ICogZmFsc2Ugb3RoZXJ3aXNlXG5cdCAqL1xuXHRmdW5jdGlvbiBuZXh0RnJhZ21lbnQoKSB7XG5cblx0XHRyZXR1cm4gbmF2aWdhdGVGcmFnbWVudCggbnVsbCwgMSApO1xuXG5cdH1cblxuXHQvKipcblx0ICogTmF2aWdhdGUgdG8gdGhlIHByZXZpb3VzIHNsaWRlIGZyYWdtZW50LlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtib29sZWFufSB0cnVlIGlmIHRoZXJlIHdhcyBhIHByZXZpb3VzIGZyYWdtZW50LFxuXHQgKiBmYWxzZSBvdGhlcndpc2Vcblx0ICovXG5cdGZ1bmN0aW9uIHByZXZpb3VzRnJhZ21lbnQoKSB7XG5cblx0XHRyZXR1cm4gbmF2aWdhdGVGcmFnbWVudCggbnVsbCwgLTEgKTtcblxuXHR9XG5cblx0LyoqXG5cdCAqIEN1ZXMgYSBuZXcgYXV0b21hdGVkIHNsaWRlIGlmIGVuYWJsZWQgaW4gdGhlIGNvbmZpZy5cblx0ICovXG5cdGZ1bmN0aW9uIGN1ZUF1dG9TbGlkZSgpIHtcblxuXHRcdGNhbmNlbEF1dG9TbGlkZSgpO1xuXG5cdFx0aWYoIGN1cnJlbnRTbGlkZSAmJiBjb25maWcuYXV0b1NsaWRlICE9PSBmYWxzZSApIHtcblxuXHRcdFx0dmFyIGZyYWdtZW50ID0gY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3IoICcuY3VycmVudC1mcmFnbWVudCcgKTtcblxuXHRcdFx0Ly8gV2hlbiB0aGUgc2xpZGUgZmlyc3QgYXBwZWFycyB0aGVyZSBpcyBubyBcImN1cnJlbnRcIiBmcmFnbWVudCBzb1xuXHRcdFx0Ly8gd2UgbG9vayBmb3IgYSBkYXRhLWF1dG9zbGlkZSB0aW1pbmcgb24gdGhlIGZpcnN0IGZyYWdtZW50XG5cdFx0XHRpZiggIWZyYWdtZW50ICkgZnJhZ21lbnQgPSBjdXJyZW50U2xpZGUucXVlcnlTZWxlY3RvciggJy5mcmFnbWVudCcgKTtcblxuXHRcdFx0dmFyIGZyYWdtZW50QXV0b1NsaWRlID0gZnJhZ21lbnQgPyBmcmFnbWVudC5nZXRBdHRyaWJ1dGUoICdkYXRhLWF1dG9zbGlkZScgKSA6IG51bGw7XG5cdFx0XHR2YXIgcGFyZW50QXV0b1NsaWRlID0gY3VycmVudFNsaWRlLnBhcmVudE5vZGUgPyBjdXJyZW50U2xpZGUucGFyZW50Tm9kZS5nZXRBdHRyaWJ1dGUoICdkYXRhLWF1dG9zbGlkZScgKSA6IG51bGw7XG5cdFx0XHR2YXIgc2xpZGVBdXRvU2xpZGUgPSBjdXJyZW50U2xpZGUuZ2V0QXR0cmlidXRlKCAnZGF0YS1hdXRvc2xpZGUnICk7XG5cblx0XHRcdC8vIFBpY2sgdmFsdWUgaW4gdGhlIGZvbGxvd2luZyBwcmlvcml0eSBvcmRlcjpcblx0XHRcdC8vIDEuIEN1cnJlbnQgZnJhZ21lbnQncyBkYXRhLWF1dG9zbGlkZVxuXHRcdFx0Ly8gMi4gQ3VycmVudCBzbGlkZSdzIGRhdGEtYXV0b3NsaWRlXG5cdFx0XHQvLyAzLiBQYXJlbnQgc2xpZGUncyBkYXRhLWF1dG9zbGlkZVxuXHRcdFx0Ly8gNC4gR2xvYmFsIGF1dG9TbGlkZSBzZXR0aW5nXG5cdFx0XHRpZiggZnJhZ21lbnRBdXRvU2xpZGUgKSB7XG5cdFx0XHRcdGF1dG9TbGlkZSA9IHBhcnNlSW50KCBmcmFnbWVudEF1dG9TbGlkZSwgMTAgKTtcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoIHNsaWRlQXV0b1NsaWRlICkge1xuXHRcdFx0XHRhdXRvU2xpZGUgPSBwYXJzZUludCggc2xpZGVBdXRvU2xpZGUsIDEwICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIGlmKCBwYXJlbnRBdXRvU2xpZGUgKSB7XG5cdFx0XHRcdGF1dG9TbGlkZSA9IHBhcnNlSW50KCBwYXJlbnRBdXRvU2xpZGUsIDEwICk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0YXV0b1NsaWRlID0gY29uZmlnLmF1dG9TbGlkZTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSWYgdGhlcmUgYXJlIG1lZGlhIGVsZW1lbnRzIHdpdGggZGF0YS1hdXRvcGxheSxcblx0XHRcdC8vIGF1dG9tYXRpY2FsbHkgc2V0IHRoZSBhdXRvU2xpZGUgZHVyYXRpb24gdG8gdGhlXG5cdFx0XHQvLyBsZW5ndGggb2YgdGhhdCBtZWRpYS4gTm90IGFwcGxpY2FibGUgaWYgdGhlIHNsaWRlXG5cdFx0XHQvLyBpcyBkaXZpZGVkIHVwIGludG8gZnJhZ21lbnRzLlxuXHRcdFx0Ly8gcGxheWJhY2tSYXRlIGlzIGFjY291bnRlZCBmb3IgaW4gdGhlIGR1cmF0aW9uLlxuXHRcdFx0aWYoIGN1cnJlbnRTbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnLmZyYWdtZW50JyApLmxlbmd0aCA9PT0gMCApIHtcblx0XHRcdFx0dG9BcnJheSggY3VycmVudFNsaWRlLnF1ZXJ5U2VsZWN0b3JBbGwoICd2aWRlbywgYXVkaW8nICkgKS5mb3JFYWNoKCBmdW5jdGlvbiggZWwgKSB7XG5cdFx0XHRcdFx0aWYoIGVsLmhhc0F0dHJpYnV0ZSggJ2RhdGEtYXV0b3BsYXknICkgKSB7XG5cdFx0XHRcdFx0XHRpZiggYXV0b1NsaWRlICYmIChlbC5kdXJhdGlvbiAqIDEwMDAgLyBlbC5wbGF5YmFja1JhdGUgKSA+IGF1dG9TbGlkZSApIHtcblx0XHRcdFx0XHRcdFx0YXV0b1NsaWRlID0gKCBlbC5kdXJhdGlvbiAqIDEwMDAgLyBlbC5wbGF5YmFja1JhdGUgKSArIDEwMDA7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9ICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEN1ZSB0aGUgbmV4dCBhdXRvLXNsaWRlIGlmOlxuXHRcdFx0Ly8gLSBUaGVyZSBpcyBhbiBhdXRvU2xpZGUgdmFsdWVcblx0XHRcdC8vIC0gQXV0by1zbGlkaW5nIGlzbid0IHBhdXNlZCBieSB0aGUgdXNlclxuXHRcdFx0Ly8gLSBUaGUgcHJlc2VudGF0aW9uIGlzbid0IHBhdXNlZFxuXHRcdFx0Ly8gLSBUaGUgb3ZlcnZpZXcgaXNuJ3QgYWN0aXZlXG5cdFx0XHQvLyAtIFRoZSBwcmVzZW50YXRpb24gaXNuJ3Qgb3ZlclxuXHRcdFx0aWYoIGF1dG9TbGlkZSAmJiAhYXV0b1NsaWRlUGF1c2VkICYmICFpc1BhdXNlZCgpICYmICFpc092ZXJ2aWV3KCkgJiYgKCAhUmV2ZWFsLmlzTGFzdFNsaWRlKCkgfHwgYXZhaWxhYmxlRnJhZ21lbnRzKCkubmV4dCB8fCBjb25maWcubG9vcCA9PT0gdHJ1ZSApICkge1xuXHRcdFx0XHRhdXRvU2xpZGVUaW1lb3V0ID0gc2V0VGltZW91dCggZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0dHlwZW9mIGNvbmZpZy5hdXRvU2xpZGVNZXRob2QgPT09ICdmdW5jdGlvbicgPyBjb25maWcuYXV0b1NsaWRlTWV0aG9kKCkgOiBuYXZpZ2F0ZU5leHQoKTtcblx0XHRcdFx0XHRjdWVBdXRvU2xpZGUoKTtcblx0XHRcdFx0fSwgYXV0b1NsaWRlICk7XG5cdFx0XHRcdGF1dG9TbGlkZVN0YXJ0VGltZSA9IERhdGUubm93KCk7XG5cdFx0XHR9XG5cblx0XHRcdGlmKCBhdXRvU2xpZGVQbGF5ZXIgKSB7XG5cdFx0XHRcdGF1dG9TbGlkZVBsYXllci5zZXRQbGF5aW5nKCBhdXRvU2xpZGVUaW1lb3V0ICE9PSAtMSApO1xuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQ2FuY2VscyBhbnkgb25nb2luZyByZXF1ZXN0IHRvIGF1dG8tc2xpZGUuXG5cdCAqL1xuXHRmdW5jdGlvbiBjYW5jZWxBdXRvU2xpZGUoKSB7XG5cblx0XHRjbGVhclRpbWVvdXQoIGF1dG9TbGlkZVRpbWVvdXQgKTtcblx0XHRhdXRvU2xpZGVUaW1lb3V0ID0gLTE7XG5cblx0fVxuXG5cdGZ1bmN0aW9uIHBhdXNlQXV0b1NsaWRlKCkge1xuXG5cdFx0aWYoIGF1dG9TbGlkZSAmJiAhYXV0b1NsaWRlUGF1c2VkICkge1xuXHRcdFx0YXV0b1NsaWRlUGF1c2VkID0gdHJ1ZTtcblx0XHRcdGRpc3BhdGNoRXZlbnQoICdhdXRvc2xpZGVwYXVzZWQnICk7XG5cdFx0XHRjbGVhclRpbWVvdXQoIGF1dG9TbGlkZVRpbWVvdXQgKTtcblxuXHRcdFx0aWYoIGF1dG9TbGlkZVBsYXllciApIHtcblx0XHRcdFx0YXV0b1NsaWRlUGxheWVyLnNldFBsYXlpbmcoIGZhbHNlICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdH1cblxuXHRmdW5jdGlvbiByZXN1bWVBdXRvU2xpZGUoKSB7XG5cblx0XHRpZiggYXV0b1NsaWRlICYmIGF1dG9TbGlkZVBhdXNlZCApIHtcblx0XHRcdGF1dG9TbGlkZVBhdXNlZCA9IGZhbHNlO1xuXHRcdFx0ZGlzcGF0Y2hFdmVudCggJ2F1dG9zbGlkZXJlc3VtZWQnICk7XG5cdFx0XHRjdWVBdXRvU2xpZGUoKTtcblx0XHR9XG5cblx0fVxuXG5cdGZ1bmN0aW9uIG5hdmlnYXRlTGVmdCgpIHtcblxuXHRcdC8vIFJldmVyc2UgZm9yIFJUTFxuXHRcdGlmKCBjb25maWcucnRsICkge1xuXHRcdFx0aWYoICggaXNPdmVydmlldygpIHx8IG5leHRGcmFnbWVudCgpID09PSBmYWxzZSApICYmIGF2YWlsYWJsZVJvdXRlcygpLmxlZnQgKSB7XG5cdFx0XHRcdHNsaWRlKCBpbmRleGggKyAxICk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdC8vIE5vcm1hbCBuYXZpZ2F0aW9uXG5cdFx0ZWxzZSBpZiggKCBpc092ZXJ2aWV3KCkgfHwgcHJldmlvdXNGcmFnbWVudCgpID09PSBmYWxzZSApICYmIGF2YWlsYWJsZVJvdXRlcygpLmxlZnQgKSB7XG5cdFx0XHRzbGlkZSggaW5kZXhoIC0gMSApO1xuXHRcdH1cblxuXHR9XG5cblx0ZnVuY3Rpb24gbmF2aWdhdGVSaWdodCgpIHtcblxuXHRcdGhhc05hdmlnYXRlZFJpZ2h0ID0gdHJ1ZTtcblxuXHRcdC8vIFJldmVyc2UgZm9yIFJUTFxuXHRcdGlmKCBjb25maWcucnRsICkge1xuXHRcdFx0aWYoICggaXNPdmVydmlldygpIHx8IHByZXZpb3VzRnJhZ21lbnQoKSA9PT0gZmFsc2UgKSAmJiBhdmFpbGFibGVSb3V0ZXMoKS5yaWdodCApIHtcblx0XHRcdFx0c2xpZGUoIGluZGV4aCAtIDEgKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0Ly8gTm9ybWFsIG5hdmlnYXRpb25cblx0XHRlbHNlIGlmKCAoIGlzT3ZlcnZpZXcoKSB8fCBuZXh0RnJhZ21lbnQoKSA9PT0gZmFsc2UgKSAmJiBhdmFpbGFibGVSb3V0ZXMoKS5yaWdodCApIHtcblx0XHRcdHNsaWRlKCBpbmRleGggKyAxICk7XG5cdFx0fVxuXG5cdH1cblxuXHRmdW5jdGlvbiBuYXZpZ2F0ZVVwKCkge1xuXG5cdFx0Ly8gUHJpb3JpdGl6ZSBoaWRpbmcgZnJhZ21lbnRzXG5cdFx0aWYoICggaXNPdmVydmlldygpIHx8IHByZXZpb3VzRnJhZ21lbnQoKSA9PT0gZmFsc2UgKSAmJiBhdmFpbGFibGVSb3V0ZXMoKS51cCApIHtcblx0XHRcdHNsaWRlKCBpbmRleGgsIGluZGV4diAtIDEgKTtcblx0XHR9XG5cblx0fVxuXG5cdGZ1bmN0aW9uIG5hdmlnYXRlRG93bigpIHtcblxuXHRcdGhhc05hdmlnYXRlZERvd24gPSB0cnVlO1xuXG5cdFx0Ly8gUHJpb3JpdGl6ZSByZXZlYWxpbmcgZnJhZ21lbnRzXG5cdFx0aWYoICggaXNPdmVydmlldygpIHx8IG5leHRGcmFnbWVudCgpID09PSBmYWxzZSApICYmIGF2YWlsYWJsZVJvdXRlcygpLmRvd24gKSB7XG5cdFx0XHRzbGlkZSggaW5kZXhoLCBpbmRleHYgKyAxICk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogTmF2aWdhdGVzIGJhY2t3YXJkcywgcHJpb3JpdGl6ZWQgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcblx0ICogMSkgUHJldmlvdXMgZnJhZ21lbnRcblx0ICogMikgUHJldmlvdXMgdmVydGljYWwgc2xpZGVcblx0ICogMykgUHJldmlvdXMgaG9yaXpvbnRhbCBzbGlkZVxuXHQgKi9cblx0ZnVuY3Rpb24gbmF2aWdhdGVQcmV2KCkge1xuXG5cdFx0Ly8gUHJpb3JpdGl6ZSByZXZlYWxpbmcgZnJhZ21lbnRzXG5cdFx0aWYoIHByZXZpb3VzRnJhZ21lbnQoKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRpZiggYXZhaWxhYmxlUm91dGVzKCkudXAgKSB7XG5cdFx0XHRcdG5hdmlnYXRlVXAoKTtcblx0XHRcdH1cblx0XHRcdGVsc2Uge1xuXHRcdFx0XHQvLyBGZXRjaCB0aGUgcHJldmlvdXMgaG9yaXpvbnRhbCBzbGlkZSwgaWYgdGhlcmUgaXMgb25lXG5cdFx0XHRcdHZhciBwcmV2aW91c1NsaWRlO1xuXG5cdFx0XHRcdGlmKCBjb25maWcucnRsICkge1xuXHRcdFx0XHRcdHByZXZpb3VzU2xpZGUgPSB0b0FycmF5KCBkb20ud3JhcHBlci5xdWVyeVNlbGVjdG9yQWxsKCBIT1JJWk9OVEFMX1NMSURFU19TRUxFQ1RPUiArICcuZnV0dXJlJyApICkucG9wKCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSB7XG5cdFx0XHRcdFx0cHJldmlvdXNTbGlkZSA9IHRvQXJyYXkoIGRvbS53cmFwcGVyLnF1ZXJ5U2VsZWN0b3JBbGwoIEhPUklaT05UQUxfU0xJREVTX1NFTEVDVE9SICsgJy5wYXN0JyApICkucG9wKCk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRpZiggcHJldmlvdXNTbGlkZSApIHtcblx0XHRcdFx0XHR2YXIgdiA9ICggcHJldmlvdXNTbGlkZS5xdWVyeVNlbGVjdG9yQWxsKCAnc2VjdGlvbicgKS5sZW5ndGggLSAxICkgfHwgdW5kZWZpbmVkO1xuXHRcdFx0XHRcdHZhciBoID0gaW5kZXhoIC0gMTtcblx0XHRcdFx0XHRzbGlkZSggaCwgdiApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogVGhlIHJldmVyc2Ugb2YgI25hdmlnYXRlUHJldigpLlxuXHQgKi9cblx0ZnVuY3Rpb24gbmF2aWdhdGVOZXh0KCkge1xuXG5cdFx0aGFzTmF2aWdhdGVkUmlnaHQgPSB0cnVlO1xuXHRcdGhhc05hdmlnYXRlZERvd24gPSB0cnVlO1xuXG5cdFx0Ly8gUHJpb3JpdGl6ZSByZXZlYWxpbmcgZnJhZ21lbnRzXG5cdFx0aWYoIG5leHRGcmFnbWVudCgpID09PSBmYWxzZSApIHtcblx0XHRcdGlmKCBhdmFpbGFibGVSb3V0ZXMoKS5kb3duICkge1xuXHRcdFx0XHRuYXZpZ2F0ZURvd24oKTtcblx0XHRcdH1cblx0XHRcdGVsc2UgaWYoIGNvbmZpZy5ydGwgKSB7XG5cdFx0XHRcdG5hdmlnYXRlTGVmdCgpO1xuXHRcdFx0fVxuXHRcdFx0ZWxzZSB7XG5cdFx0XHRcdG5hdmlnYXRlUmlnaHQoKTtcblx0XHRcdH1cblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgdGhlIHRhcmdldCBlbGVtZW50IHByZXZlbnRzIHRoZSB0cmlnZ2VyaW5nIG9mXG5cdCAqIHN3aXBlIG5hdmlnYXRpb24uXG5cdCAqL1xuXHRmdW5jdGlvbiBpc1N3aXBlUHJldmVudGVkKCB0YXJnZXQgKSB7XG5cblx0XHR3aGlsZSggdGFyZ2V0ICYmIHR5cGVvZiB0YXJnZXQuaGFzQXR0cmlidXRlID09PSAnZnVuY3Rpb24nICkge1xuXHRcdFx0aWYoIHRhcmdldC5oYXNBdHRyaWJ1dGUoICdkYXRhLXByZXZlbnQtc3dpcGUnICkgKSByZXR1cm4gdHJ1ZTtcblx0XHRcdHRhcmdldCA9IHRhcmdldC5wYXJlbnROb2RlO1xuXHRcdH1cblxuXHRcdHJldHVybiBmYWxzZTtcblxuXHR9XG5cblxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS8vXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEVWRU5UUyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLy9cblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0vL1xuXG5cdC8qKlxuXHQgKiBDYWxsZWQgYnkgYWxsIGV2ZW50IGhhbmRsZXJzIHRoYXQgYXJlIGJhc2VkIG9uIHVzZXJcblx0ICogaW5wdXQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBbZXZlbnRdXG5cdCAqL1xuXHRmdW5jdGlvbiBvblVzZXJJbnB1dCggZXZlbnQgKSB7XG5cblx0XHRpZiggY29uZmlnLmF1dG9TbGlkZVN0b3BwYWJsZSApIHtcblx0XHRcdHBhdXNlQXV0b1NsaWRlKCk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogSGFuZGxlciBmb3IgdGhlIGRvY3VtZW50IGxldmVsICdrZXlwcmVzcycgZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Eb2N1bWVudEtleVByZXNzKCBldmVudCApIHtcblxuXHRcdC8vIENoZWNrIGlmIHRoZSBwcmVzc2VkIGtleSBpcyBxdWVzdGlvbiBtYXJrXG5cdFx0aWYoIGV2ZW50LnNoaWZ0S2V5ICYmIGV2ZW50LmNoYXJDb2RlID09PSA2MyApIHtcblx0XHRcdHRvZ2dsZUhlbHAoKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGVyIGZvciB0aGUgZG9jdW1lbnQgbGV2ZWwgJ2tleWRvd24nIGV2ZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gZXZlbnRcblx0ICovXG5cdGZ1bmN0aW9uIG9uRG9jdW1lbnRLZXlEb3duKCBldmVudCApIHtcblxuXHRcdC8vIElmIHRoZXJlJ3MgYSBjb25kaXRpb24gc3BlY2lmaWVkIGFuZCBpdCByZXR1cm5zIGZhbHNlLFxuXHRcdC8vIGlnbm9yZSB0aGlzIGV2ZW50XG5cdFx0aWYoIHR5cGVvZiBjb25maWcua2V5Ym9hcmRDb25kaXRpb24gPT09ICdmdW5jdGlvbicgJiYgY29uZmlnLmtleWJvYXJkQ29uZGl0aW9uKCkgPT09IGZhbHNlICkge1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXG5cdFx0Ly8gUmVtZW1iZXIgaWYgYXV0by1zbGlkaW5nIHdhcyBwYXVzZWQgc28gd2UgY2FuIHRvZ2dsZSBpdFxuXHRcdHZhciBhdXRvU2xpZGVXYXNQYXVzZWQgPSBhdXRvU2xpZGVQYXVzZWQ7XG5cblx0XHRvblVzZXJJbnB1dCggZXZlbnQgKTtcblxuXHRcdC8vIENoZWNrIGlmIHRoZXJlJ3MgYSBmb2N1c2VkIGVsZW1lbnQgdGhhdCBjb3VsZCBiZSB1c2luZ1xuXHRcdC8vIHRoZSBrZXlib2FyZFxuXHRcdHZhciBhY3RpdmVFbGVtZW50SXNDRSA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgJiYgZG9jdW1lbnQuYWN0aXZlRWxlbWVudC5jb250ZW50RWRpdGFibGUgIT09ICdpbmhlcml0Jztcblx0XHR2YXIgYWN0aXZlRWxlbWVudElzSW5wdXQgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50ICYmIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQudGFnTmFtZSAmJiAvaW5wdXR8dGV4dGFyZWEvaS50ZXN0KCBkb2N1bWVudC5hY3RpdmVFbGVtZW50LnRhZ05hbWUgKTtcblx0XHR2YXIgYWN0aXZlRWxlbWVudElzTm90ZXMgPSBkb2N1bWVudC5hY3RpdmVFbGVtZW50ICYmIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQuY2xhc3NOYW1lICYmIC9zcGVha2VyLW5vdGVzL2kudGVzdCggZG9jdW1lbnQuYWN0aXZlRWxlbWVudC5jbGFzc05hbWUpO1xuXG5cdFx0Ly8gRGlzcmVnYXJkIHRoZSBldmVudCBpZiB0aGVyZSdzIGEgZm9jdXNlZCBlbGVtZW50IG9yIGFcblx0XHQvLyBrZXlib2FyZCBtb2RpZmllciBrZXkgaXMgcHJlc2VudFxuXHRcdGlmKCBhY3RpdmVFbGVtZW50SXNDRSB8fCBhY3RpdmVFbGVtZW50SXNJbnB1dCB8fCBhY3RpdmVFbGVtZW50SXNOb3RlcyB8fCAoZXZlbnQuc2hpZnRLZXkgJiYgZXZlbnQua2V5Q29kZSAhPT0gMzIpIHx8IGV2ZW50LmFsdEtleSB8fCBldmVudC5jdHJsS2V5IHx8IGV2ZW50Lm1ldGFLZXkgKSByZXR1cm47XG5cblx0XHQvLyBXaGlsZSBwYXVzZWQgb25seSBhbGxvdyByZXN1bWUga2V5Ym9hcmQgZXZlbnRzOyAnYicsICd2JywgJy4nXG5cdFx0dmFyIHJlc3VtZUtleUNvZGVzID0gWzY2LDg2LDE5MCwxOTFdO1xuXHRcdHZhciBrZXk7XG5cblx0XHQvLyBDdXN0b20ga2V5IGJpbmRpbmdzIGZvciB0b2dnbGVQYXVzZSBzaG91bGQgYmUgYWJsZSB0byByZXN1bWVcblx0XHRpZiggdHlwZW9mIGNvbmZpZy5rZXlib2FyZCA9PT0gJ29iamVjdCcgKSB7XG5cdFx0XHRmb3IoIGtleSBpbiBjb25maWcua2V5Ym9hcmQgKSB7XG5cdFx0XHRcdGlmKCBjb25maWcua2V5Ym9hcmRba2V5XSA9PT0gJ3RvZ2dsZVBhdXNlJyApIHtcblx0XHRcdFx0XHRyZXN1bWVLZXlDb2Rlcy5wdXNoKCBwYXJzZUludCgga2V5LCAxMCApICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiggaXNQYXVzZWQoKSAmJiByZXN1bWVLZXlDb2Rlcy5pbmRleE9mKCBldmVudC5rZXlDb2RlICkgPT09IC0xICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdHZhciB0cmlnZ2VyZWQgPSBmYWxzZTtcblxuXHRcdC8vIDEuIFVzZXIgZGVmaW5lZCBrZXkgYmluZGluZ3Ncblx0XHRpZiggdHlwZW9mIGNvbmZpZy5rZXlib2FyZCA9PT0gJ29iamVjdCcgKSB7XG5cblx0XHRcdGZvcigga2V5IGluIGNvbmZpZy5rZXlib2FyZCApIHtcblxuXHRcdFx0XHQvLyBDaGVjayBpZiB0aGlzIGJpbmRpbmcgbWF0Y2hlcyB0aGUgcHJlc3NlZCBrZXlcblx0XHRcdFx0aWYoIHBhcnNlSW50KCBrZXksIDEwICkgPT09IGV2ZW50LmtleUNvZGUgKSB7XG5cblx0XHRcdFx0XHR2YXIgdmFsdWUgPSBjb25maWcua2V5Ym9hcmRbIGtleSBdO1xuXG5cdFx0XHRcdFx0Ly8gQ2FsbGJhY2sgZnVuY3Rpb25cblx0XHRcdFx0XHRpZiggdHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nICkge1xuXHRcdFx0XHRcdFx0dmFsdWUuYXBwbHkoIG51bGwsIFsgZXZlbnQgXSApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHQvLyBTdHJpbmcgc2hvcnRjdXRzIHRvIHJldmVhbC5qcyBBUElcblx0XHRcdFx0XHRlbHNlIGlmKCB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHR5cGVvZiBSZXZlYWxbIHZhbHVlIF0gPT09ICdmdW5jdGlvbicgKSB7XG5cdFx0XHRcdFx0XHRSZXZlYWxbIHZhbHVlIF0uY2FsbCgpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRyaWdnZXJlZCA9IHRydWU7XG5cblx0XHRcdFx0fVxuXG5cdFx0XHR9XG5cblx0XHR9XG5cblx0XHQvLyAyLiBTeXN0ZW0gZGVmaW5lZCBrZXkgYmluZGluZ3Ncblx0XHRpZiggdHJpZ2dlcmVkID09PSBmYWxzZSApIHtcblxuXHRcdFx0Ly8gQXNzdW1lIHRydWUgYW5kIHRyeSB0byBwcm92ZSBmYWxzZVxuXHRcdFx0dHJpZ2dlcmVkID0gdHJ1ZTtcblxuXHRcdFx0c3dpdGNoKCBldmVudC5rZXlDb2RlICkge1xuXHRcdFx0XHQvLyBwLCBwYWdlIHVwXG5cdFx0XHRcdGNhc2UgODA6IGNhc2UgMzM6IG5hdmlnYXRlUHJldigpOyBicmVhaztcblx0XHRcdFx0Ly8gbiwgcGFnZSBkb3duXG5cdFx0XHRcdGNhc2UgNzg6IGNhc2UgMzQ6IG5hdmlnYXRlTmV4dCgpOyBicmVhaztcblx0XHRcdFx0Ly8gaCwgbGVmdFxuXHRcdFx0XHRjYXNlIDcyOiBjYXNlIDM3OiBuYXZpZ2F0ZUxlZnQoKTsgYnJlYWs7XG5cdFx0XHRcdC8vIGwsIHJpZ2h0XG5cdFx0XHRcdGNhc2UgNzY6IGNhc2UgMzk6IG5hdmlnYXRlUmlnaHQoKTsgYnJlYWs7XG5cdFx0XHRcdC8vIGssIHVwXG5cdFx0XHRcdGNhc2UgNzU6IGNhc2UgMzg6IG5hdmlnYXRlVXAoKTsgYnJlYWs7XG5cdFx0XHRcdC8vIGosIGRvd25cblx0XHRcdFx0Y2FzZSA3NDogY2FzZSA0MDogbmF2aWdhdGVEb3duKCk7IGJyZWFrO1xuXHRcdFx0XHQvLyBob21lXG5cdFx0XHRcdGNhc2UgMzY6IHNsaWRlKCAwICk7IGJyZWFrO1xuXHRcdFx0XHQvLyBlbmRcblx0XHRcdFx0Y2FzZSAzNTogc2xpZGUoIE51bWJlci5NQVhfVkFMVUUgKTsgYnJlYWs7XG5cdFx0XHRcdC8vIHNwYWNlXG5cdFx0XHRcdGNhc2UgMzI6IGlzT3ZlcnZpZXcoKSA/IGRlYWN0aXZhdGVPdmVydmlldygpIDogZXZlbnQuc2hpZnRLZXkgPyBuYXZpZ2F0ZVByZXYoKSA6IG5hdmlnYXRlTmV4dCgpOyBicmVhaztcblx0XHRcdFx0Ly8gcmV0dXJuXG5cdFx0XHRcdGNhc2UgMTM6IGlzT3ZlcnZpZXcoKSA/IGRlYWN0aXZhdGVPdmVydmlldygpIDogdHJpZ2dlcmVkID0gZmFsc2U7IGJyZWFrO1xuXHRcdFx0XHQvLyB0d28tc3BvdCwgc2VtaWNvbG9uLCBiLCB2LCBwZXJpb2QsIExvZ2l0ZWNoIHByZXNlbnRlciB0b29scyBcImJsYWNrIHNjcmVlblwiIGJ1dHRvblxuXHRcdFx0XHRjYXNlIDU4OiBjYXNlIDU5OiBjYXNlIDY2OiBjYXNlIDg2OiBjYXNlIDE5MDogY2FzZSAxOTE6IHRvZ2dsZVBhdXNlKCk7IGJyZWFrO1xuXHRcdFx0XHQvLyBmXG5cdFx0XHRcdGNhc2UgNzA6IGVudGVyRnVsbHNjcmVlbigpOyBicmVhaztcblx0XHRcdFx0Ly8gYVxuXHRcdFx0XHRjYXNlIDY1OiBpZiAoIGNvbmZpZy5hdXRvU2xpZGVTdG9wcGFibGUgKSB0b2dnbGVBdXRvU2xpZGUoIGF1dG9TbGlkZVdhc1BhdXNlZCApOyBicmVhaztcblx0XHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0XHR0cmlnZ2VyZWQgPSBmYWxzZTtcblx0XHRcdH1cblxuXHRcdH1cblxuXHRcdC8vIElmIHRoZSBpbnB1dCByZXN1bHRlZCBpbiBhIHRyaWdnZXJlZCBhY3Rpb24gd2Ugc2hvdWxkIHByZXZlbnRcblx0XHQvLyB0aGUgYnJvd3NlcnMgZGVmYXVsdCBiZWhhdmlvclxuXHRcdGlmKCB0cmlnZ2VyZWQgKSB7XG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCAmJiBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdH1cblx0XHQvLyBFU0Mgb3IgTyBrZXlcblx0XHRlbHNlIGlmICggKCBldmVudC5rZXlDb2RlID09PSAyNyB8fCBldmVudC5rZXlDb2RlID09PSA3OSApICYmIGZlYXR1cmVzLnRyYW5zZm9ybXMzZCApIHtcblx0XHRcdGlmKCBkb20ub3ZlcmxheSApIHtcblx0XHRcdFx0Y2xvc2VPdmVybGF5KCk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIHtcblx0XHRcdFx0dG9nZ2xlT3ZlcnZpZXcoKTtcblx0XHRcdH1cblxuXHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQgJiYgZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHR9XG5cblx0XHQvLyBJZiBhdXRvLXNsaWRpbmcgaXMgZW5hYmxlZCB3ZSBuZWVkIHRvIGN1ZSB1cFxuXHRcdC8vIGFub3RoZXIgdGltZW91dFxuXHRcdGN1ZUF1dG9TbGlkZSgpO1xuXG5cdH1cblxuXHQvKipcblx0ICogSGFuZGxlciBmb3IgdGhlICd0b3VjaHN0YXJ0JyBldmVudCwgZW5hYmxlcyBzdXBwb3J0IGZvclxuXHQgKiBzd2lwZSBhbmQgcGluY2ggZ2VzdHVyZXMuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Ub3VjaFN0YXJ0KCBldmVudCApIHtcblxuXHRcdGlmKCBpc1N3aXBlUHJldmVudGVkKCBldmVudC50YXJnZXQgKSApIHJldHVybiB0cnVlO1xuXG5cdFx0dG91Y2guc3RhcnRYID0gZXZlbnQudG91Y2hlc1swXS5jbGllbnRYO1xuXHRcdHRvdWNoLnN0YXJ0WSA9IGV2ZW50LnRvdWNoZXNbMF0uY2xpZW50WTtcblx0XHR0b3VjaC5zdGFydENvdW50ID0gZXZlbnQudG91Y2hlcy5sZW5ndGg7XG5cblx0XHQvLyBJZiB0aGVyZSdzIHR3byB0b3VjaGVzIHdlIG5lZWQgdG8gbWVtb3JpemUgdGhlIGRpc3RhbmNlXG5cdFx0Ly8gYmV0d2VlbiB0aG9zZSB0d28gcG9pbnRzIHRvIGRldGVjdCBwaW5jaGluZ1xuXHRcdGlmKCBldmVudC50b3VjaGVzLmxlbmd0aCA9PT0gMiAmJiBjb25maWcub3ZlcnZpZXcgKSB7XG5cdFx0XHR0b3VjaC5zdGFydFNwYW4gPSBkaXN0YW5jZUJldHdlZW4oIHtcblx0XHRcdFx0eDogZXZlbnQudG91Y2hlc1sxXS5jbGllbnRYLFxuXHRcdFx0XHR5OiBldmVudC50b3VjaGVzWzFdLmNsaWVudFlcblx0XHRcdH0sIHtcblx0XHRcdFx0eDogdG91Y2guc3RhcnRYLFxuXHRcdFx0XHR5OiB0b3VjaC5zdGFydFlcblx0XHRcdH0gKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGVyIGZvciB0aGUgJ3RvdWNobW92ZScgZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Ub3VjaE1vdmUoIGV2ZW50ICkge1xuXG5cdFx0aWYoIGlzU3dpcGVQcmV2ZW50ZWQoIGV2ZW50LnRhcmdldCApICkgcmV0dXJuIHRydWU7XG5cblx0XHQvLyBFYWNoIHRvdWNoIHNob3VsZCBvbmx5IHRyaWdnZXIgb25lIGFjdGlvblxuXHRcdGlmKCAhdG91Y2guY2FwdHVyZWQgKSB7XG5cdFx0XHRvblVzZXJJbnB1dCggZXZlbnQgKTtcblxuXHRcdFx0dmFyIGN1cnJlbnRYID0gZXZlbnQudG91Y2hlc1swXS5jbGllbnRYO1xuXHRcdFx0dmFyIGN1cnJlbnRZID0gZXZlbnQudG91Y2hlc1swXS5jbGllbnRZO1xuXG5cdFx0XHQvLyBJZiB0aGUgdG91Y2ggc3RhcnRlZCB3aXRoIHR3byBwb2ludHMgYW5kIHN0aWxsIGhhc1xuXHRcdFx0Ly8gdHdvIGFjdGl2ZSB0b3VjaGVzOyB0ZXN0IGZvciB0aGUgcGluY2ggZ2VzdHVyZVxuXHRcdFx0aWYoIGV2ZW50LnRvdWNoZXMubGVuZ3RoID09PSAyICYmIHRvdWNoLnN0YXJ0Q291bnQgPT09IDIgJiYgY29uZmlnLm92ZXJ2aWV3ICkge1xuXG5cdFx0XHRcdC8vIFRoZSBjdXJyZW50IGRpc3RhbmNlIGluIHBpeGVscyBiZXR3ZWVuIHRoZSB0d28gdG91Y2ggcG9pbnRzXG5cdFx0XHRcdHZhciBjdXJyZW50U3BhbiA9IGRpc3RhbmNlQmV0d2Vlbigge1xuXHRcdFx0XHRcdHg6IGV2ZW50LnRvdWNoZXNbMV0uY2xpZW50WCxcblx0XHRcdFx0XHR5OiBldmVudC50b3VjaGVzWzFdLmNsaWVudFlcblx0XHRcdFx0fSwge1xuXHRcdFx0XHRcdHg6IHRvdWNoLnN0YXJ0WCxcblx0XHRcdFx0XHR5OiB0b3VjaC5zdGFydFlcblx0XHRcdFx0fSApO1xuXG5cdFx0XHRcdC8vIElmIHRoZSBzcGFuIGlzIGxhcmdlciB0aGFuIHRoZSBkZXNpcmUgYW1vdW50IHdlJ3ZlIGdvdFxuXHRcdFx0XHQvLyBvdXJzZWx2ZXMgYSBwaW5jaFxuXHRcdFx0XHRpZiggTWF0aC5hYnMoIHRvdWNoLnN0YXJ0U3BhbiAtIGN1cnJlbnRTcGFuICkgPiB0b3VjaC50aHJlc2hvbGQgKSB7XG5cdFx0XHRcdFx0dG91Y2guY2FwdHVyZWQgPSB0cnVlO1xuXG5cdFx0XHRcdFx0aWYoIGN1cnJlbnRTcGFuIDwgdG91Y2guc3RhcnRTcGFuICkge1xuXHRcdFx0XHRcdFx0YWN0aXZhdGVPdmVydmlldygpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRcdGRlYWN0aXZhdGVPdmVydmlldygpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cblx0XHRcdH1cblx0XHRcdC8vIFRoZXJlIHdhcyBvbmx5IG9uZSB0b3VjaCBwb2ludCwgbG9vayBmb3IgYSBzd2lwZVxuXHRcdFx0ZWxzZSBpZiggZXZlbnQudG91Y2hlcy5sZW5ndGggPT09IDEgJiYgdG91Y2guc3RhcnRDb3VudCAhPT0gMiApIHtcblxuXHRcdFx0XHR2YXIgZGVsdGFYID0gY3VycmVudFggLSB0b3VjaC5zdGFydFgsXG5cdFx0XHRcdFx0ZGVsdGFZID0gY3VycmVudFkgLSB0b3VjaC5zdGFydFk7XG5cblx0XHRcdFx0aWYoIGRlbHRhWCA+IHRvdWNoLnRocmVzaG9sZCAmJiBNYXRoLmFicyggZGVsdGFYICkgPiBNYXRoLmFicyggZGVsdGFZICkgKSB7XG5cdFx0XHRcdFx0dG91Y2guY2FwdHVyZWQgPSB0cnVlO1xuXHRcdFx0XHRcdG5hdmlnYXRlTGVmdCgpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGVsc2UgaWYoIGRlbHRhWCA8IC10b3VjaC50aHJlc2hvbGQgJiYgTWF0aC5hYnMoIGRlbHRhWCApID4gTWF0aC5hYnMoIGRlbHRhWSApICkge1xuXHRcdFx0XHRcdHRvdWNoLmNhcHR1cmVkID0gdHJ1ZTtcblx0XHRcdFx0XHRuYXZpZ2F0ZVJpZ2h0KCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSBpZiggZGVsdGFZID4gdG91Y2gudGhyZXNob2xkICkge1xuXHRcdFx0XHRcdHRvdWNoLmNhcHR1cmVkID0gdHJ1ZTtcblx0XHRcdFx0XHRuYXZpZ2F0ZVVwKCk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZWxzZSBpZiggZGVsdGFZIDwgLXRvdWNoLnRocmVzaG9sZCApIHtcblx0XHRcdFx0XHR0b3VjaC5jYXB0dXJlZCA9IHRydWU7XG5cdFx0XHRcdFx0bmF2aWdhdGVEb3duKCk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBJZiB3ZSdyZSBlbWJlZGRlZCwgb25seSBibG9jayB0b3VjaCBldmVudHMgaWYgdGhleSBoYXZlXG5cdFx0XHRcdC8vIHRyaWdnZXJlZCBhbiBhY3Rpb25cblx0XHRcdFx0aWYoIGNvbmZpZy5lbWJlZGRlZCApIHtcblx0XHRcdFx0XHRpZiggdG91Y2guY2FwdHVyZWQgfHwgaXNWZXJ0aWNhbFNsaWRlKCBjdXJyZW50U2xpZGUgKSApIHtcblx0XHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHRcdC8vIE5vdCBlbWJlZGRlZD8gQmxvY2sgdGhlbSBhbGwgdG8gYXZvaWQgbmVlZGxlc3MgdG9zc2luZ1xuXHRcdFx0XHQvLyBhcm91bmQgb2YgdGhlIHZpZXdwb3J0IGluIGlPU1xuXHRcdFx0XHRlbHNlIHtcblx0XHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0XHR9XG5cblx0XHRcdH1cblx0XHR9XG5cdFx0Ly8gVGhlcmUncyBhIGJ1ZyB3aXRoIHN3aXBpbmcgb24gc29tZSBBbmRyb2lkIGRldmljZXMgdW5sZXNzXG5cdFx0Ly8gdGhlIGRlZmF1bHQgYWN0aW9uIGlzIGFsd2F5cyBwcmV2ZW50ZWRcblx0XHRlbHNlIGlmKCBVQS5tYXRjaCggL2FuZHJvaWQvZ2kgKSApIHtcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogSGFuZGxlciBmb3IgdGhlICd0b3VjaGVuZCcgZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Ub3VjaEVuZCggZXZlbnQgKSB7XG5cblx0XHR0b3VjaC5jYXB0dXJlZCA9IGZhbHNlO1xuXG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydCBwb2ludGVyIGRvd24gdG8gdG91Y2ggc3RhcnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Qb2ludGVyRG93biggZXZlbnQgKSB7XG5cblx0XHRpZiggZXZlbnQucG9pbnRlclR5cGUgPT09IGV2ZW50Lk1TUE9JTlRFUl9UWVBFX1RPVUNIIHx8IGV2ZW50LnBvaW50ZXJUeXBlID09PSBcInRvdWNoXCIgKSB7XG5cdFx0XHRldmVudC50b3VjaGVzID0gW3sgY2xpZW50WDogZXZlbnQuY2xpZW50WCwgY2xpZW50WTogZXZlbnQuY2xpZW50WSB9XTtcblx0XHRcdG9uVG91Y2hTdGFydCggZXZlbnQgKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZXJ0IHBvaW50ZXIgbW92ZSB0byB0b3VjaCBtb3ZlLlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gZXZlbnRcblx0ICovXG5cdGZ1bmN0aW9uIG9uUG9pbnRlck1vdmUoIGV2ZW50ICkge1xuXG5cdFx0aWYoIGV2ZW50LnBvaW50ZXJUeXBlID09PSBldmVudC5NU1BPSU5URVJfVFlQRV9UT1VDSCB8fCBldmVudC5wb2ludGVyVHlwZSA9PT0gXCJ0b3VjaFwiICkgIHtcblx0XHRcdGV2ZW50LnRvdWNoZXMgPSBbeyBjbGllbnRYOiBldmVudC5jbGllbnRYLCBjbGllbnRZOiBldmVudC5jbGllbnRZIH1dO1xuXHRcdFx0b25Ub3VjaE1vdmUoIGV2ZW50ICk7XG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydCBwb2ludGVyIHVwIHRvIHRvdWNoIGVuZC5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IGV2ZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBvblBvaW50ZXJVcCggZXZlbnQgKSB7XG5cblx0XHRpZiggZXZlbnQucG9pbnRlclR5cGUgPT09IGV2ZW50Lk1TUE9JTlRFUl9UWVBFX1RPVUNIIHx8IGV2ZW50LnBvaW50ZXJUeXBlID09PSBcInRvdWNoXCIgKSAge1xuXHRcdFx0ZXZlbnQudG91Y2hlcyA9IFt7IGNsaWVudFg6IGV2ZW50LmNsaWVudFgsIGNsaWVudFk6IGV2ZW50LmNsaWVudFkgfV07XG5cdFx0XHRvblRvdWNoRW5kKCBldmVudCApO1xuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEhhbmRsZXMgbW91c2Ugd2hlZWwgc2Nyb2xsaW5nLCB0aHJvdHRsZWQgdG8gYXZvaWQgc2tpcHBpbmdcblx0ICogbXVsdGlwbGUgc2xpZGVzLlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gZXZlbnRcblx0ICovXG5cdGZ1bmN0aW9uIG9uRG9jdW1lbnRNb3VzZVNjcm9sbCggZXZlbnQgKSB7XG5cblx0XHRpZiggRGF0ZS5ub3coKSAtIGxhc3RNb3VzZVdoZWVsU3RlcCA+IDYwMCApIHtcblxuXHRcdFx0bGFzdE1vdXNlV2hlZWxTdGVwID0gRGF0ZS5ub3coKTtcblxuXHRcdFx0dmFyIGRlbHRhID0gZXZlbnQuZGV0YWlsIHx8IC1ldmVudC53aGVlbERlbHRhO1xuXHRcdFx0aWYoIGRlbHRhID4gMCApIHtcblx0XHRcdFx0bmF2aWdhdGVOZXh0KCk7XG5cdFx0XHR9XG5cdFx0XHRlbHNlIGlmKCBkZWx0YSA8IDAgKSB7XG5cdFx0XHRcdG5hdmlnYXRlUHJldigpO1xuXHRcdFx0fVxuXG5cdFx0fVxuXG5cdH1cblxuXHQvKipcblx0ICogQ2xpY2tpbmcgb24gdGhlIHByb2dyZXNzIGJhciByZXN1bHRzIGluIGEgbmF2aWdhdGlvbiB0byB0aGVcblx0ICogY2xvc2VzdCBhcHByb3hpbWF0ZSBob3Jpem9udGFsIHNsaWRlIHVzaW5nIHRoaXMgZXF1YXRpb246XG5cdCAqXG5cdCAqICggY2xpY2tYIC8gcHJlc2VudGF0aW9uV2lkdGggKSAqIG51bWJlck9mU2xpZGVzXG5cdCAqXG5cdCAqIEBwYXJhbSB7b2JqZWN0fSBldmVudFxuXHQgKi9cblx0ZnVuY3Rpb24gb25Qcm9ncmVzc0NsaWNrZWQoIGV2ZW50ICkge1xuXG5cdFx0b25Vc2VySW5wdXQoIGV2ZW50ICk7XG5cblx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0dmFyIHNsaWRlc1RvdGFsID0gdG9BcnJheSggZG9tLndyYXBwZXIucXVlcnlTZWxlY3RvckFsbCggSE9SSVpPTlRBTF9TTElERVNfU0VMRUNUT1IgKSApLmxlbmd0aDtcblx0XHR2YXIgc2xpZGVJbmRleCA9IE1hdGguZmxvb3IoICggZXZlbnQuY2xpZW50WCAvIGRvbS53cmFwcGVyLm9mZnNldFdpZHRoICkgKiBzbGlkZXNUb3RhbCApO1xuXG5cdFx0aWYoIGNvbmZpZy5ydGwgKSB7XG5cdFx0XHRzbGlkZUluZGV4ID0gc2xpZGVzVG90YWwgLSBzbGlkZUluZGV4O1xuXHRcdH1cblxuXHRcdHNsaWRlKCBzbGlkZUluZGV4ICk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBFdmVudCBoYW5kbGVyIGZvciBuYXZpZ2F0aW9uIGNvbnRyb2wgYnV0dG9ucy5cblx0ICovXG5cdGZ1bmN0aW9uIG9uTmF2aWdhdGVMZWZ0Q2xpY2tlZCggZXZlbnQgKSB7IGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IG9uVXNlcklucHV0KCk7IG5hdmlnYXRlTGVmdCgpOyB9XG5cdGZ1bmN0aW9uIG9uTmF2aWdhdGVSaWdodENsaWNrZWQoIGV2ZW50ICkgeyBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyBvblVzZXJJbnB1dCgpOyBuYXZpZ2F0ZVJpZ2h0KCk7IH1cblx0ZnVuY3Rpb24gb25OYXZpZ2F0ZVVwQ2xpY2tlZCggZXZlbnQgKSB7IGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IG9uVXNlcklucHV0KCk7IG5hdmlnYXRlVXAoKTsgfVxuXHRmdW5jdGlvbiBvbk5hdmlnYXRlRG93bkNsaWNrZWQoIGV2ZW50ICkgeyBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyBvblVzZXJJbnB1dCgpOyBuYXZpZ2F0ZURvd24oKTsgfVxuXHRmdW5jdGlvbiBvbk5hdmlnYXRlUHJldkNsaWNrZWQoIGV2ZW50ICkgeyBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyBvblVzZXJJbnB1dCgpOyBuYXZpZ2F0ZVByZXYoKTsgfVxuXHRmdW5jdGlvbiBvbk5hdmlnYXRlTmV4dENsaWNrZWQoIGV2ZW50ICkgeyBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyBvblVzZXJJbnB1dCgpOyBuYXZpZ2F0ZU5leHQoKTsgfVxuXG5cdC8qKlxuXHQgKiBIYW5kbGVyIGZvciB0aGUgd2luZG93IGxldmVsICdoYXNoY2hhbmdlJyBldmVudC5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IFtldmVudF1cblx0ICovXG5cdGZ1bmN0aW9uIG9uV2luZG93SGFzaENoYW5nZSggZXZlbnQgKSB7XG5cblx0XHRyZWFkVVJMKCk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGVyIGZvciB0aGUgd2luZG93IGxldmVsICdyZXNpemUnIGV2ZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gW2V2ZW50XVxuXHQgKi9cblx0ZnVuY3Rpb24gb25XaW5kb3dSZXNpemUoIGV2ZW50ICkge1xuXG5cdFx0bGF5b3V0KCk7XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgZm9yIHRoZSB3aW5kb3cgbGV2ZWwgJ3Zpc2liaWxpdHljaGFuZ2UnIGV2ZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gW2V2ZW50XVxuXHQgKi9cblx0ZnVuY3Rpb24gb25QYWdlVmlzaWJpbGl0eUNoYW5nZSggZXZlbnQgKSB7XG5cblx0XHR2YXIgaXNIaWRkZW4gPSAgZG9jdW1lbnQud2Via2l0SGlkZGVuIHx8XG5cdFx0XHRcdFx0XHRkb2N1bWVudC5tc0hpZGRlbiB8fFxuXHRcdFx0XHRcdFx0ZG9jdW1lbnQuaGlkZGVuO1xuXG5cdFx0Ly8gSWYsIGFmdGVyIGNsaWNraW5nIGEgbGluayBvciBzaW1pbGFyIGFuZCB3ZSdyZSBjb21pbmcgYmFjayxcblx0XHQvLyBmb2N1cyB0aGUgZG9jdW1lbnQuYm9keSB0byBlbnN1cmUgd2UgY2FuIHVzZSBrZXlib2FyZCBzaG9ydGN1dHNcblx0XHRpZiggaXNIaWRkZW4gPT09IGZhbHNlICYmIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgIT09IGRvY3VtZW50LmJvZHkgKSB7XG5cdFx0XHQvLyBOb3QgYWxsIGVsZW1lbnRzIHN1cHBvcnQgLmJsdXIoKSAtIFNWR3MgYW1vbmcgdGhlbS5cblx0XHRcdGlmKCB0eXBlb2YgZG9jdW1lbnQuYWN0aXZlRWxlbWVudC5ibHVyID09PSAnZnVuY3Rpb24nICkge1xuXHRcdFx0XHRkb2N1bWVudC5hY3RpdmVFbGVtZW50LmJsdXIoKTtcblx0XHRcdH1cblx0XHRcdGRvY3VtZW50LmJvZHkuZm9jdXMoKTtcblx0XHR9XG5cblx0fVxuXG5cdC8qKlxuXHQgKiBJbnZva2VkIHdoZW4gYSBzbGlkZSBpcyBhbmQgd2UncmUgaW4gdGhlIG92ZXJ2aWV3LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gZXZlbnRcblx0ICovXG5cdGZ1bmN0aW9uIG9uT3ZlcnZpZXdTbGlkZUNsaWNrZWQoIGV2ZW50ICkge1xuXG5cdFx0Ly8gVE9ETyBUaGVyZSdzIGEgYnVnIGhlcmUgd2hlcmUgdGhlIGV2ZW50IGxpc3RlbmVycyBhcmUgbm90XG5cdFx0Ly8gcmVtb3ZlZCBhZnRlciBkZWFjdGl2YXRpbmcgdGhlIG92ZXJ2aWV3LlxuXHRcdGlmKCBldmVudHNBcmVCb3VuZCAmJiBpc092ZXJ2aWV3KCkgKSB7XG5cdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHR2YXIgZWxlbWVudCA9IGV2ZW50LnRhcmdldDtcblxuXHRcdFx0d2hpbGUoIGVsZW1lbnQgJiYgIWVsZW1lbnQubm9kZU5hbWUubWF0Y2goIC9zZWN0aW9uL2dpICkgKSB7XG5cdFx0XHRcdGVsZW1lbnQgPSBlbGVtZW50LnBhcmVudE5vZGU7XG5cdFx0XHR9XG5cblx0XHRcdGlmKCBlbGVtZW50ICYmICFlbGVtZW50LmNsYXNzTGlzdC5jb250YWlucyggJ2Rpc2FibGVkJyApICkge1xuXG5cdFx0XHRcdGRlYWN0aXZhdGVPdmVydmlldygpO1xuXG5cdFx0XHRcdGlmKCBlbGVtZW50Lm5vZGVOYW1lLm1hdGNoKCAvc2VjdGlvbi9naSApICkge1xuXHRcdFx0XHRcdHZhciBoID0gcGFyc2VJbnQoIGVsZW1lbnQuZ2V0QXR0cmlidXRlKCAnZGF0YS1pbmRleC1oJyApLCAxMCApLFxuXHRcdFx0XHRcdFx0diA9IHBhcnNlSW50KCBlbGVtZW50LmdldEF0dHJpYnV0ZSggJ2RhdGEtaW5kZXgtdicgKSwgMTAgKTtcblxuXHRcdFx0XHRcdHNsaWRlKCBoLCB2ICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0fVxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEhhbmRsZXMgY2xpY2tzIG9uIGxpbmtzIHRoYXQgYXJlIHNldCB0byBwcmV2aWV3IGluIHRoZVxuXHQgKiBpZnJhbWUgb3ZlcmxheS5cblx0ICpcblx0ICogQHBhcmFtIHtvYmplY3R9IGV2ZW50XG5cdCAqL1xuXHRmdW5jdGlvbiBvblByZXZpZXdMaW5rQ2xpY2tlZCggZXZlbnQgKSB7XG5cblx0XHRpZiggZXZlbnQuY3VycmVudFRhcmdldCAmJiBldmVudC5jdXJyZW50VGFyZ2V0Lmhhc0F0dHJpYnV0ZSggJ2hyZWYnICkgKSB7XG5cdFx0XHR2YXIgdXJsID0gZXZlbnQuY3VycmVudFRhcmdldC5nZXRBdHRyaWJ1dGUoICdocmVmJyApO1xuXHRcdFx0aWYoIHVybCApIHtcblx0XHRcdFx0c2hvd1ByZXZpZXcoIHVybCApO1xuXHRcdFx0XHRldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHR9XG5cblx0LyoqXG5cdCAqIEhhbmRsZXMgY2xpY2sgb24gdGhlIGF1dG8tc2xpZGluZyBjb250cm9scyBlbGVtZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0ge29iamVjdH0gW2V2ZW50XVxuXHQgKi9cblx0ZnVuY3Rpb24gb25BdXRvU2xpZGVQbGF5ZXJDbGljayggZXZlbnQgKSB7XG5cblx0XHQvLyBSZXBsYXlcblx0XHRpZiggUmV2ZWFsLmlzTGFzdFNsaWRlKCkgJiYgY29uZmlnLmxvb3AgPT09IGZhbHNlICkge1xuXHRcdFx0c2xpZGUoIDAsIDAgKTtcblx0XHRcdHJlc3VtZUF1dG9TbGlkZSgpO1xuXHRcdH1cblx0XHQvLyBSZXN1bWVcblx0XHRlbHNlIGlmKCBhdXRvU2xpZGVQYXVzZWQgKSB7XG5cdFx0XHRyZXN1bWVBdXRvU2xpZGUoKTtcblx0XHR9XG5cdFx0Ly8gUGF1c2Vcblx0XHRlbHNlIHtcblx0XHRcdHBhdXNlQXV0b1NsaWRlKCk7XG5cdFx0fVxuXG5cdH1cblxuXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLy9cblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFBMQVlCQUNLIENPTVBPTkVOVCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0vL1xuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS8vXG5cblxuXHQvKipcblx0ICogQ29uc3RydWN0b3IgZm9yIHRoZSBwbGF5YmFjayBjb21wb25lbnQsIHdoaWNoIGRpc3BsYXlzXG5cdCAqIHBsYXkvcGF1c2UvcHJvZ3Jlc3MgY29udHJvbHMuXG5cdCAqXG5cdCAqIEBwYXJhbSB7SFRNTEVsZW1lbnR9IGNvbnRhaW5lciBUaGUgY29tcG9uZW50IHdpbGwgYXBwZW5kXG5cdCAqIGl0c2VsZiB0byB0aGlzXG5cdCAqIEBwYXJhbSB7ZnVuY3Rpb259IHByb2dyZXNzQ2hlY2sgQSBtZXRob2Qgd2hpY2ggd2lsbCBiZVxuXHQgKiBjYWxsZWQgZnJlcXVlbnRseSB0byBnZXQgdGhlIGN1cnJlbnQgcHJvZ3Jlc3Mgb24gYSByYW5nZVxuXHQgKiBvZiAwLTFcblx0ICovXG5cdGZ1bmN0aW9uIFBsYXliYWNrKCBjb250YWluZXIsIHByb2dyZXNzQ2hlY2sgKSB7XG5cblx0XHQvLyBDb3NtZXRpY3Ncblx0XHR0aGlzLmRpYW1ldGVyID0gMTAwO1xuXHRcdHRoaXMuZGlhbWV0ZXIyID0gdGhpcy5kaWFtZXRlci8yO1xuXHRcdHRoaXMudGhpY2tuZXNzID0gNjtcblxuXHRcdC8vIEZsYWdzIGlmIHdlIGFyZSBjdXJyZW50bHkgcGxheWluZ1xuXHRcdHRoaXMucGxheWluZyA9IGZhbHNlO1xuXG5cdFx0Ly8gQ3VycmVudCBwcm9ncmVzcyBvbiBhIDAtMSByYW5nZVxuXHRcdHRoaXMucHJvZ3Jlc3MgPSAwO1xuXG5cdFx0Ly8gVXNlZCB0byBsb29wIHRoZSBhbmltYXRpb24gc21vb3RobHlcblx0XHR0aGlzLnByb2dyZXNzT2Zmc2V0ID0gMTtcblxuXHRcdHRoaXMuY29udGFpbmVyID0gY29udGFpbmVyO1xuXHRcdHRoaXMucHJvZ3Jlc3NDaGVjayA9IHByb2dyZXNzQ2hlY2s7XG5cblx0XHR0aGlzLmNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoICdjYW52YXMnICk7XG5cdFx0dGhpcy5jYW52YXMuY2xhc3NOYW1lID0gJ3BsYXliYWNrJztcblx0XHR0aGlzLmNhbnZhcy53aWR0aCA9IHRoaXMuZGlhbWV0ZXI7XG5cdFx0dGhpcy5jYW52YXMuaGVpZ2h0ID0gdGhpcy5kaWFtZXRlcjtcblx0XHR0aGlzLmNhbnZhcy5zdHlsZS53aWR0aCA9IHRoaXMuZGlhbWV0ZXIyICsgJ3B4Jztcblx0XHR0aGlzLmNhbnZhcy5zdHlsZS5oZWlnaHQgPSB0aGlzLmRpYW1ldGVyMiArICdweCc7XG5cdFx0dGhpcy5jb250ZXh0ID0gdGhpcy5jYW52YXMuZ2V0Q29udGV4dCggJzJkJyApO1xuXG5cdFx0dGhpcy5jb250YWluZXIuYXBwZW5kQ2hpbGQoIHRoaXMuY2FudmFzICk7XG5cblx0XHR0aGlzLnJlbmRlcigpO1xuXG5cdH1cblxuXHQvKipcblx0ICogQHBhcmFtIHZhbHVlXG5cdCAqL1xuXHRQbGF5YmFjay5wcm90b3R5cGUuc2V0UGxheWluZyA9IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblxuXHRcdHZhciB3YXNQbGF5aW5nID0gdGhpcy5wbGF5aW5nO1xuXG5cdFx0dGhpcy5wbGF5aW5nID0gdmFsdWU7XG5cblx0XHQvLyBTdGFydCByZXBhaW50aW5nIGlmIHdlIHdlcmVuJ3QgYWxyZWFkeVxuXHRcdGlmKCAhd2FzUGxheWluZyAmJiB0aGlzLnBsYXlpbmcgKSB7XG5cdFx0XHR0aGlzLmFuaW1hdGUoKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHR0aGlzLnJlbmRlcigpO1xuXHRcdH1cblxuXHR9O1xuXG5cdFBsYXliYWNrLnByb3RvdHlwZS5hbmltYXRlID0gZnVuY3Rpb24oKSB7XG5cblx0XHR2YXIgcHJvZ3Jlc3NCZWZvcmUgPSB0aGlzLnByb2dyZXNzO1xuXG5cdFx0dGhpcy5wcm9ncmVzcyA9IHRoaXMucHJvZ3Jlc3NDaGVjaygpO1xuXG5cdFx0Ly8gV2hlbiB3ZSBsb29wLCBvZmZzZXQgdGhlIHByb2dyZXNzIHNvIHRoYXQgaXQgZWFzZXNcblx0XHQvLyBzbW9vdGhseSByYXRoZXIgdGhhbiBpbW1lZGlhdGVseSByZXNldHRpbmdcblx0XHRpZiggcHJvZ3Jlc3NCZWZvcmUgPiAwLjggJiYgdGhpcy5wcm9ncmVzcyA8IDAuMiApIHtcblx0XHRcdHRoaXMucHJvZ3Jlc3NPZmZzZXQgPSB0aGlzLnByb2dyZXNzO1xuXHRcdH1cblxuXHRcdHRoaXMucmVuZGVyKCk7XG5cblx0XHRpZiggdGhpcy5wbGF5aW5nICkge1xuXHRcdFx0ZmVhdHVyZXMucmVxdWVzdEFuaW1hdGlvbkZyYW1lTWV0aG9kLmNhbGwoIHdpbmRvdywgdGhpcy5hbmltYXRlLmJpbmQoIHRoaXMgKSApO1xuXHRcdH1cblxuXHR9O1xuXG5cdC8qKlxuXHQgKiBSZW5kZXJzIHRoZSBjdXJyZW50IHByb2dyZXNzIGFuZCBwbGF5YmFjayBzdGF0ZS5cblx0ICovXG5cdFBsYXliYWNrLnByb3RvdHlwZS5yZW5kZXIgPSBmdW5jdGlvbigpIHtcblxuXHRcdHZhciBwcm9ncmVzcyA9IHRoaXMucGxheWluZyA/IHRoaXMucHJvZ3Jlc3MgOiAwLFxuXHRcdFx0cmFkaXVzID0gKCB0aGlzLmRpYW1ldGVyMiApIC0gdGhpcy50aGlja25lc3MsXG5cdFx0XHR4ID0gdGhpcy5kaWFtZXRlcjIsXG5cdFx0XHR5ID0gdGhpcy5kaWFtZXRlcjIsXG5cdFx0XHRpY29uU2l6ZSA9IDI4O1xuXG5cdFx0Ly8gRWFzZSB0b3dhcmRzIDFcblx0XHR0aGlzLnByb2dyZXNzT2Zmc2V0ICs9ICggMSAtIHRoaXMucHJvZ3Jlc3NPZmZzZXQgKSAqIDAuMTtcblxuXHRcdHZhciBlbmRBbmdsZSA9ICggLSBNYXRoLlBJIC8gMiApICsgKCBwcm9ncmVzcyAqICggTWF0aC5QSSAqIDIgKSApO1xuXHRcdHZhciBzdGFydEFuZ2xlID0gKCAtIE1hdGguUEkgLyAyICkgKyAoIHRoaXMucHJvZ3Jlc3NPZmZzZXQgKiAoIE1hdGguUEkgKiAyICkgKTtcblxuXHRcdHRoaXMuY29udGV4dC5zYXZlKCk7XG5cdFx0dGhpcy5jb250ZXh0LmNsZWFyUmVjdCggMCwgMCwgdGhpcy5kaWFtZXRlciwgdGhpcy5kaWFtZXRlciApO1xuXG5cdFx0Ly8gU29saWQgYmFja2dyb3VuZCBjb2xvclxuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQuYXJjKCB4LCB5LCByYWRpdXMgKyA0LCAwLCBNYXRoLlBJICogMiwgZmFsc2UgKTtcblx0XHR0aGlzLmNvbnRleHQuZmlsbFN0eWxlID0gJ3JnYmEoIDAsIDAsIDAsIDAuNCApJztcblx0XHR0aGlzLmNvbnRleHQuZmlsbCgpO1xuXG5cdFx0Ly8gRHJhdyBwcm9ncmVzcyB0cmFja1xuXHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHR0aGlzLmNvbnRleHQuYXJjKCB4LCB5LCByYWRpdXMsIDAsIE1hdGguUEkgKiAyLCBmYWxzZSApO1xuXHRcdHRoaXMuY29udGV4dC5saW5lV2lkdGggPSB0aGlzLnRoaWNrbmVzcztcblx0XHR0aGlzLmNvbnRleHQuc3Ryb2tlU3R5bGUgPSAncmdiYSggMjU1LCAyNTUsIDI1NSwgMC4yICknO1xuXHRcdHRoaXMuY29udGV4dC5zdHJva2UoKTtcblxuXHRcdGlmKCB0aGlzLnBsYXlpbmcgKSB7XG5cdFx0XHQvLyBEcmF3IHByb2dyZXNzIG9uIHRvcCBvZiB0cmFja1xuXHRcdFx0dGhpcy5jb250ZXh0LmJlZ2luUGF0aCgpO1xuXHRcdFx0dGhpcy5jb250ZXh0LmFyYyggeCwgeSwgcmFkaXVzLCBzdGFydEFuZ2xlLCBlbmRBbmdsZSwgZmFsc2UgKTtcblx0XHRcdHRoaXMuY29udGV4dC5saW5lV2lkdGggPSB0aGlzLnRoaWNrbmVzcztcblx0XHRcdHRoaXMuY29udGV4dC5zdHJva2VTdHlsZSA9ICcjZmZmJztcblx0XHRcdHRoaXMuY29udGV4dC5zdHJva2UoKTtcblx0XHR9XG5cblx0XHR0aGlzLmNvbnRleHQudHJhbnNsYXRlKCB4IC0gKCBpY29uU2l6ZSAvIDIgKSwgeSAtICggaWNvblNpemUgLyAyICkgKTtcblxuXHRcdC8vIERyYXcgcGxheS9wYXVzZSBpY29uc1xuXHRcdGlmKCB0aGlzLnBsYXlpbmcgKSB7XG5cdFx0XHR0aGlzLmNvbnRleHQuZmlsbFN0eWxlID0gJyNmZmYnO1xuXHRcdFx0dGhpcy5jb250ZXh0LmZpbGxSZWN0KCAwLCAwLCBpY29uU2l6ZSAvIDIgLSA0LCBpY29uU2l6ZSApO1xuXHRcdFx0dGhpcy5jb250ZXh0LmZpbGxSZWN0KCBpY29uU2l6ZSAvIDIgKyA0LCAwLCBpY29uU2l6ZSAvIDIgLSA0LCBpY29uU2l6ZSApO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdHRoaXMuY29udGV4dC5iZWdpblBhdGgoKTtcblx0XHRcdHRoaXMuY29udGV4dC50cmFuc2xhdGUoIDQsIDAgKTtcblx0XHRcdHRoaXMuY29udGV4dC5tb3ZlVG8oIDAsIDAgKTtcblx0XHRcdHRoaXMuY29udGV4dC5saW5lVG8oIGljb25TaXplIC0gNCwgaWNvblNpemUgLyAyICk7XG5cdFx0XHR0aGlzLmNvbnRleHQubGluZVRvKCAwLCBpY29uU2l6ZSApO1xuXHRcdFx0dGhpcy5jb250ZXh0LmZpbGxTdHlsZSA9ICcjZmZmJztcblx0XHRcdHRoaXMuY29udGV4dC5maWxsKCk7XG5cdFx0fVxuXG5cdFx0dGhpcy5jb250ZXh0LnJlc3RvcmUoKTtcblxuXHR9O1xuXG5cdFBsYXliYWNrLnByb3RvdHlwZS5vbiA9IGZ1bmN0aW9uKCB0eXBlLCBsaXN0ZW5lciApIHtcblx0XHR0aGlzLmNhbnZhcy5hZGRFdmVudExpc3RlbmVyKCB0eXBlLCBsaXN0ZW5lciwgZmFsc2UgKTtcblx0fTtcblxuXHRQbGF5YmFjay5wcm90b3R5cGUub2ZmID0gZnVuY3Rpb24oIHR5cGUsIGxpc3RlbmVyICkge1xuXHRcdHRoaXMuY2FudmFzLnJlbW92ZUV2ZW50TGlzdGVuZXIoIHR5cGUsIGxpc3RlbmVyLCBmYWxzZSApO1xuXHR9O1xuXG5cdFBsYXliYWNrLnByb3RvdHlwZS5kZXN0cm95ID0gZnVuY3Rpb24oKSB7XG5cblx0XHR0aGlzLnBsYXlpbmcgPSBmYWxzZTtcblxuXHRcdGlmKCB0aGlzLmNhbnZhcy5wYXJlbnROb2RlICkge1xuXHRcdFx0dGhpcy5jb250YWluZXIucmVtb3ZlQ2hpbGQoIHRoaXMuY2FudmFzICk7XG5cdFx0fVxuXG5cdH07XG5cblxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS8vXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gQVBJIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLy9cblx0Ly8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0vL1xuXG5cblx0UmV2ZWFsID0ge1xuXHRcdFZFUlNJT046IFZFUlNJT04sXG5cblx0XHRpbml0aWFsaXplOiBpbml0aWFsaXplLFxuXHRcdGNvbmZpZ3VyZTogY29uZmlndXJlLFxuXHRcdHN5bmM6IHN5bmMsXG5cblx0XHQvLyBOYXZpZ2F0aW9uIG1ldGhvZHNcblx0XHRzbGlkZTogc2xpZGUsXG5cdFx0bGVmdDogbmF2aWdhdGVMZWZ0LFxuXHRcdHJpZ2h0OiBuYXZpZ2F0ZVJpZ2h0LFxuXHRcdHVwOiBuYXZpZ2F0ZVVwLFxuXHRcdGRvd246IG5hdmlnYXRlRG93bixcblx0XHRwcmV2OiBuYXZpZ2F0ZVByZXYsXG5cdFx0bmV4dDogbmF2aWdhdGVOZXh0LFxuXG5cdFx0Ly8gRnJhZ21lbnQgbWV0aG9kc1xuXHRcdG5hdmlnYXRlRnJhZ21lbnQ6IG5hdmlnYXRlRnJhZ21lbnQsXG5cdFx0cHJldkZyYWdtZW50OiBwcmV2aW91c0ZyYWdtZW50LFxuXHRcdG5leHRGcmFnbWVudDogbmV4dEZyYWdtZW50LFxuXG5cdFx0Ly8gRGVwcmVjYXRlZCBhbGlhc2VzXG5cdFx0bmF2aWdhdGVUbzogc2xpZGUsXG5cdFx0bmF2aWdhdGVMZWZ0OiBuYXZpZ2F0ZUxlZnQsXG5cdFx0bmF2aWdhdGVSaWdodDogbmF2aWdhdGVSaWdodCxcblx0XHRuYXZpZ2F0ZVVwOiBuYXZpZ2F0ZVVwLFxuXHRcdG5hdmlnYXRlRG93bjogbmF2aWdhdGVEb3duLFxuXHRcdG5hdmlnYXRlUHJldjogbmF2aWdhdGVQcmV2LFxuXHRcdG5hdmlnYXRlTmV4dDogbmF2aWdhdGVOZXh0LFxuXG5cdFx0Ly8gRm9yY2VzIGFuIHVwZGF0ZSBpbiBzbGlkZSBsYXlvdXRcblx0XHRsYXlvdXQ6IGxheW91dCxcblxuXHRcdC8vIFJhbmRvbWl6ZXMgdGhlIG9yZGVyIG9mIHNsaWRlc1xuXHRcdHNodWZmbGU6IHNodWZmbGUsXG5cblx0XHQvLyBSZXR1cm5zIGFuIG9iamVjdCB3aXRoIHRoZSBhdmFpbGFibGUgcm91dGVzIGFzIGJvb2xlYW5zIChsZWZ0L3JpZ2h0L3RvcC9ib3R0b20pXG5cdFx0YXZhaWxhYmxlUm91dGVzOiBhdmFpbGFibGVSb3V0ZXMsXG5cblx0XHQvLyBSZXR1cm5zIGFuIG9iamVjdCB3aXRoIHRoZSBhdmFpbGFibGUgZnJhZ21lbnRzIGFzIGJvb2xlYW5zIChwcmV2L25leHQpXG5cdFx0YXZhaWxhYmxlRnJhZ21lbnRzOiBhdmFpbGFibGVGcmFnbWVudHMsXG5cblx0XHQvLyBUb2dnbGVzIGEgaGVscCBvdmVybGF5IHdpdGgga2V5Ym9hcmQgc2hvcnRjdXRzXG5cdFx0dG9nZ2xlSGVscDogdG9nZ2xlSGVscCxcblxuXHRcdC8vIFRvZ2dsZXMgdGhlIG92ZXJ2aWV3IG1vZGUgb24vb2ZmXG5cdFx0dG9nZ2xlT3ZlcnZpZXc6IHRvZ2dsZU92ZXJ2aWV3LFxuXG5cdFx0Ly8gVG9nZ2xlcyB0aGUgXCJibGFjayBzY3JlZW5cIiBtb2RlIG9uL29mZlxuXHRcdHRvZ2dsZVBhdXNlOiB0b2dnbGVQYXVzZSxcblxuXHRcdC8vIFRvZ2dsZXMgdGhlIGF1dG8gc2xpZGUgbW9kZSBvbi9vZmZcblx0XHR0b2dnbGVBdXRvU2xpZGU6IHRvZ2dsZUF1dG9TbGlkZSxcblxuXHRcdC8vIFN0YXRlIGNoZWNrc1xuXHRcdGlzT3ZlcnZpZXc6IGlzT3ZlcnZpZXcsXG5cdFx0aXNQYXVzZWQ6IGlzUGF1c2VkLFxuXHRcdGlzQXV0b1NsaWRpbmc6IGlzQXV0b1NsaWRpbmcsXG5cdFx0aXNTcGVha2VyTm90ZXM6IGlzU3BlYWtlck5vdGVzLFxuXG5cdFx0Ly8gU2xpZGUgcHJlbG9hZGluZ1xuXHRcdGxvYWRTbGlkZTogbG9hZFNsaWRlLFxuXHRcdHVubG9hZFNsaWRlOiB1bmxvYWRTbGlkZSxcblxuXHRcdC8vIEFkZHMgb3IgcmVtb3ZlcyBhbGwgaW50ZXJuYWwgZXZlbnQgbGlzdGVuZXJzIChzdWNoIGFzIGtleWJvYXJkKVxuXHRcdGFkZEV2ZW50TGlzdGVuZXJzOiBhZGRFdmVudExpc3RlbmVycyxcblx0XHRyZW1vdmVFdmVudExpc3RlbmVyczogcmVtb3ZlRXZlbnRMaXN0ZW5lcnMsXG5cblx0XHQvLyBGYWNpbGl0eSBmb3IgcGVyc2lzdGluZyBhbmQgcmVzdG9yaW5nIHRoZSBwcmVzZW50YXRpb24gc3RhdGVcblx0XHRnZXRTdGF0ZTogZ2V0U3RhdGUsXG5cdFx0c2V0U3RhdGU6IHNldFN0YXRlLFxuXG5cdFx0Ly8gUHJlc2VudGF0aW9uIHByb2dyZXNzXG5cdFx0Z2V0U2xpZGVQYXN0Q291bnQ6IGdldFNsaWRlUGFzdENvdW50LFxuXG5cdFx0Ly8gUHJlc2VudGF0aW9uIHByb2dyZXNzIG9uIHJhbmdlIG9mIDAtMVxuXHRcdGdldFByb2dyZXNzOiBnZXRQcm9ncmVzcyxcblxuXHRcdC8vIFJldHVybnMgdGhlIGluZGljZXMgb2YgdGhlIGN1cnJlbnQsIG9yIHNwZWNpZmllZCwgc2xpZGVcblx0XHRnZXRJbmRpY2VzOiBnZXRJbmRpY2VzLFxuXG5cdFx0Ly8gUmV0dXJucyBhbiBBcnJheSBvZiBhbGwgc2xpZGVzXG5cdFx0Z2V0U2xpZGVzOiBnZXRTbGlkZXMsXG5cblx0XHQvLyBSZXR1cm5zIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2xpZGVzXG5cdFx0Z2V0VG90YWxTbGlkZXM6IGdldFRvdGFsU2xpZGVzLFxuXG5cdFx0Ly8gUmV0dXJucyB0aGUgc2xpZGUgZWxlbWVudCBhdCB0aGUgc3BlY2lmaWVkIGluZGV4XG5cdFx0Z2V0U2xpZGU6IGdldFNsaWRlLFxuXG5cdFx0Ly8gUmV0dXJucyB0aGUgc2xpZGUgYmFja2dyb3VuZCBlbGVtZW50IGF0IHRoZSBzcGVjaWZpZWQgaW5kZXhcblx0XHRnZXRTbGlkZUJhY2tncm91bmQ6IGdldFNsaWRlQmFja2dyb3VuZCxcblxuXHRcdC8vIFJldHVybnMgdGhlIHNwZWFrZXIgbm90ZXMgc3RyaW5nIGZvciBhIHNsaWRlLCBvciBudWxsXG5cdFx0Z2V0U2xpZGVOb3RlczogZ2V0U2xpZGVOb3RlcyxcblxuXHRcdC8vIFJldHVybnMgdGhlIHByZXZpb3VzIHNsaWRlIGVsZW1lbnQsIG1heSBiZSBudWxsXG5cdFx0Z2V0UHJldmlvdXNTbGlkZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gcHJldmlvdXNTbGlkZTtcblx0XHR9LFxuXG5cdFx0Ly8gUmV0dXJucyB0aGUgY3VycmVudCBzbGlkZSBlbGVtZW50XG5cdFx0Z2V0Q3VycmVudFNsaWRlOiBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiBjdXJyZW50U2xpZGU7XG5cdFx0fSxcblxuXHRcdC8vIFJldHVybnMgdGhlIGN1cnJlbnQgc2NhbGUgb2YgdGhlIHByZXNlbnRhdGlvbiBjb250ZW50XG5cdFx0Z2V0U2NhbGU6IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIHNjYWxlO1xuXHRcdH0sXG5cblx0XHQvLyBSZXR1cm5zIHRoZSBjdXJyZW50IGNvbmZpZ3VyYXRpb24gb2JqZWN0XG5cdFx0Z2V0Q29uZmlnOiBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiBjb25maWc7XG5cdFx0fSxcblxuXHRcdC8vIEhlbHBlciBtZXRob2QsIHJldHJpZXZlcyBxdWVyeSBzdHJpbmcgYXMgYSBrZXkvdmFsdWUgaGFzaFxuXHRcdGdldFF1ZXJ5SGFzaDogZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgcXVlcnkgPSB7fTtcblxuXHRcdFx0bG9jYXRpb24uc2VhcmNoLnJlcGxhY2UoIC9bQS1aMC05XSs/PShbXFx3XFwuJS1dKikvZ2ksIGZ1bmN0aW9uKGEpIHtcblx0XHRcdFx0cXVlcnlbIGEuc3BsaXQoICc9JyApLnNoaWZ0KCkgXSA9IGEuc3BsaXQoICc9JyApLnBvcCgpO1xuXHRcdFx0fSApO1xuXG5cdFx0XHQvLyBCYXNpYyBkZXNlcmlhbGl6YXRpb25cblx0XHRcdGZvciggdmFyIGkgaW4gcXVlcnkgKSB7XG5cdFx0XHRcdHZhciB2YWx1ZSA9IHF1ZXJ5WyBpIF07XG5cblx0XHRcdFx0cXVlcnlbIGkgXSA9IGRlc2VyaWFsaXplKCB1bmVzY2FwZSggdmFsdWUgKSApO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gcXVlcnk7XG5cdFx0fSxcblxuXHRcdC8vIFJldHVybnMgdHJ1ZSBpZiB3ZSdyZSBjdXJyZW50bHkgb24gdGhlIGZpcnN0IHNsaWRlXG5cdFx0aXNGaXJzdFNsaWRlOiBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiAoIGluZGV4aCA9PT0gMCAmJiBpbmRleHYgPT09IDAgKTtcblx0XHR9LFxuXG5cdFx0Ly8gUmV0dXJucyB0cnVlIGlmIHdlJ3JlIGN1cnJlbnRseSBvbiB0aGUgbGFzdCBzbGlkZVxuXHRcdGlzTGFzdFNsaWRlOiBmdW5jdGlvbigpIHtcblx0XHRcdGlmKCBjdXJyZW50U2xpZGUgKSB7XG5cdFx0XHRcdC8vIERvZXMgdGhpcyBzbGlkZSBoYXMgbmV4dCBhIHNpYmxpbmc/XG5cdFx0XHRcdGlmKCBjdXJyZW50U2xpZGUubmV4dEVsZW1lbnRTaWJsaW5nICkgcmV0dXJuIGZhbHNlO1xuXG5cdFx0XHRcdC8vIElmIGl0J3MgdmVydGljYWwsIGRvZXMgaXRzIHBhcmVudCBoYXZlIGEgbmV4dCBzaWJsaW5nP1xuXHRcdFx0XHRpZiggaXNWZXJ0aWNhbFNsaWRlKCBjdXJyZW50U2xpZGUgKSAmJiBjdXJyZW50U2xpZGUucGFyZW50Tm9kZS5uZXh0RWxlbWVudFNpYmxpbmcgKSByZXR1cm4gZmFsc2U7XG5cblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9LFxuXG5cdFx0Ly8gQ2hlY2tzIGlmIHJldmVhbC5qcyBoYXMgYmVlbiBsb2FkZWQgYW5kIGlzIHJlYWR5IGZvciB1c2Vcblx0XHRpc1JlYWR5OiBmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiBsb2FkZWQ7XG5cdFx0fSxcblxuXHRcdC8vIEZvcndhcmQgZXZlbnQgYmluZGluZyB0byB0aGUgcmV2ZWFsIERPTSBlbGVtZW50XG5cdFx0YWRkRXZlbnRMaXN0ZW5lcjogZnVuY3Rpb24oIHR5cGUsIGxpc3RlbmVyLCB1c2VDYXB0dXJlICkge1xuXHRcdFx0aWYoICdhZGRFdmVudExpc3RlbmVyJyBpbiB3aW5kb3cgKSB7XG5cdFx0XHRcdCggZG9tLndyYXBwZXIgfHwgZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5yZXZlYWwnICkgKS5hZGRFdmVudExpc3RlbmVyKCB0eXBlLCBsaXN0ZW5lciwgdXNlQ2FwdHVyZSApO1xuXHRcdFx0fVxuXHRcdH0sXG5cdFx0cmVtb3ZlRXZlbnRMaXN0ZW5lcjogZnVuY3Rpb24oIHR5cGUsIGxpc3RlbmVyLCB1c2VDYXB0dXJlICkge1xuXHRcdFx0aWYoICdhZGRFdmVudExpc3RlbmVyJyBpbiB3aW5kb3cgKSB7XG5cdFx0XHRcdCggZG9tLndyYXBwZXIgfHwgZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJy5yZXZlYWwnICkgKS5yZW1vdmVFdmVudExpc3RlbmVyKCB0eXBlLCBsaXN0ZW5lciwgdXNlQ2FwdHVyZSApO1xuXHRcdFx0fVxuXHRcdH0sXG5cblx0XHQvLyBQcm9ncmFtYXRpY2FsbHkgdHJpZ2dlcnMgYSBrZXlib2FyZCBldmVudFxuXHRcdHRyaWdnZXJLZXk6IGZ1bmN0aW9uKCBrZXlDb2RlICkge1xuXHRcdFx0b25Eb2N1bWVudEtleURvd24oIHsga2V5Q29kZToga2V5Q29kZSB9ICk7XG5cdFx0fSxcblxuXHRcdC8vIFJlZ2lzdGVycyBhIG5ldyBzaG9ydGN1dCB0byBpbmNsdWRlIGluIHRoZSBoZWxwIG92ZXJsYXlcblx0XHRyZWdpc3RlcktleWJvYXJkU2hvcnRjdXQ6IGZ1bmN0aW9uKCBrZXksIHZhbHVlICkge1xuXHRcdFx0a2V5Ym9hcmRTaG9ydGN1dHNba2V5XSA9IHZhbHVlO1xuXHRcdH1cblx0fTtcblxuXHRyZXR1cm4gUmV2ZWFsO1xuXG59KSk7XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUNBLFVBTUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTsiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/reveal.js/js/reveal.js\n");
  210. /***/ }),
  211. /***/ "./node_modules/reveal.js/lib/js/head.min.js":
  212. /*!***************************************************!*\
  213. !*** ./node_modules/reveal.js/lib/js/head.min.js ***!
  214. \***************************************************/
  215. /*! no static exports found */
  216. /***/ (function(module, exports) {
  217. eval("/*! head.core - v1.0.2 */\n(function(n,t){\"use strict\";function r(n){a[a.length]=n}function k(n){var t=new RegExp(\" ?\\\\b\"+n+\"\\\\b\");c.className=c.className.replace(t,\"\")}function p(n,t){for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}function tt(){var t,e,f,o;c.className=c.className.replace(/ (w-|eq-|gt-|gte-|lt-|lte-|portrait|no-portrait|landscape|no-landscape)\\d+/g,\"\");t=n.innerWidth||c.clientWidth;e=n.outerWidth||n.screen.width;u.screen.innerWidth=t;u.screen.outerWidth=e;r(\"w-\"+t);p(i.screens,function(n){t>n?(i.screensCss.gt&&r(\"gt-\"+n),i.screensCss.gte&&r(\"gte-\"+n)):t<n?(i.screensCss.lt&&r(\"lt-\"+n),i.screensCss.lte&&r(\"lte-\"+n)):t===n&&(i.screensCss.lte&&r(\"lte-\"+n),i.screensCss.eq&&r(\"e-q\"+n),i.screensCss.gte&&r(\"gte-\"+n))});f=n.innerHeight||c.clientHeight;o=n.outerHeight||n.screen.height;u.screen.innerHeight=f;u.screen.outerHeight=o;u.feature(\"portrait\",f>t);u.feature(\"landscape\",f<t)}function it(){n.clearTimeout(b);b=n.setTimeout(tt,50)}var y=n.document,rt=n.navigator,ut=n.location,c=y.documentElement,a=[],i={screens:[240,320,480,640,768,800,1024,1280,1440,1680,1920],screensCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!1},browsers:[{ie:{min:6,max:11}}],browserCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!0},html5:!0,page:\"-page\",section:\"-section\",head:\"head\"},v,u,s,w,o,h,l,d,f,g,nt,e,b;if(n.head_conf)for(v in n.head_conf)n.head_conf[v]!==t&&(i[v]=n.head_conf[v]);u=n[i.head]=function(){u.ready.apply(null,arguments)};u.feature=function(n,t,i){return n?(Object.prototype.toString.call(t)===\"[object Function]\"&&(t=t.call()),r((t?\"\":\"no-\")+n),u[n]=!!t,i||(k(\"no-\"+n),k(n),u.feature()),u):(c.className+=\" \"+a.join(\" \"),a=[],u)};u.feature(\"js\",!0);s=rt.userAgent.toLowerCase();w=/mobile|android|kindle|silk|midp|phone|(windows .+arm|touch)/.test(s);u.feature(\"mobile\",w,!0);u.feature(\"desktop\",!w,!0);s=/(chrome|firefox)[ \\/]([\\w.]+)/.exec(s)||/(iphone|ipad|ipod)(?:.*version)?[ \\/]([\\w.]+)/.exec(s)||/(android)(?:.*version)?[ \\/]([\\w.]+)/.exec(s)||/(webkit|opera)(?:.*version)?[ \\/]([\\w.]+)/.exec(s)||/(msie) ([\\w.]+)/.exec(s)||/(trident).+rv:(\\w.)+/.exec(s)||[];o=s[1];h=parseFloat(s[2]);switch(o){case\"msie\":case\"trident\":o=\"ie\";h=y.documentMode||h;break;case\"firefox\":o=\"ff\";break;case\"ipod\":case\"ipad\":case\"iphone\":o=\"ios\";break;case\"webkit\":o=\"safari\"}for(u.browser={name:o,version:h},u.browser[o]=!0,l=0,d=i.browsers.length;l<d;l++)for(f in i.browsers[l])if(o===f)for(r(f),g=i.browsers[l][f].min,nt=i.browsers[l][f].max,e=g;e<=nt;e++)h>e?(i.browserCss.gt&&r(\"gt-\"+f+e),i.browserCss.gte&&r(\"gte-\"+f+e)):h<e?(i.browserCss.lt&&r(\"lt-\"+f+e),i.browserCss.lte&&r(\"lte-\"+f+e)):h===e&&(i.browserCss.lte&&r(\"lte-\"+f+e),i.browserCss.eq&&r(\"eq-\"+f+e),i.browserCss.gte&&r(\"gte-\"+f+e));else r(\"no-\"+f);r(o);r(o+parseInt(h,10));i.html5&&o===\"ie\"&&h<9&&p(\"abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|progress|section|summary|time|video\".split(\"|\"),function(n){y.createElement(n)});p(ut.pathname.split(\"/\"),function(n,u){if(this.length>2&&this[u+1]!==t)u&&r(this.slice(u,u+1).join(\"-\").toLowerCase()+i.section);else{var f=n||\"index\",e=f.indexOf(\".\");e>0&&(f=f.substring(0,e));c.id=f.toLowerCase()+i.page;u||r(\"root\"+i.section)}});u.screen={height:n.screen.height,width:n.screen.width};tt();b=0;n.addEventListener?n.addEventListener(\"resize\",it,!1):n.attachEvent(\"onresize\",it)})(window);\n/*! head.css3 - v1.0.0 */\n(function(n,t){\"use strict\";function a(n){for(var r in n)if(i[n[r]]!==t)return!0;return!1}function r(n){var t=n.charAt(0).toUpperCase()+n.substr(1),i=(n+\" \"+c.join(t+\" \")+t).split(\" \");return!!a(i)}var h=n.document,o=h.createElement(\"i\"),i=o.style,s=\" -o- -moz- -ms- -webkit- -khtml- \".split(\" \"),c=\"Webkit Moz O ms Khtml\".split(\" \"),l=n.head_conf&&n.head_conf.head||\"head\",u=n[l],f={gradient:function(){var n=\"background-image:\";return i.cssText=(n+s.join(\"gradient(linear,left top,right bottom,from(#9f9),to(#fff));\"+n)+s.join(\"linear-gradient(left top,#eee,#fff);\"+n)).slice(0,-n.length),!!i.backgroundImage},rgba:function(){return i.cssText=\"background-color:rgba(0,0,0,0.5)\",!!i.backgroundColor},opacity:function(){return o.style.opacity===\"\"},textshadow:function(){return i.textShadow===\"\"},multiplebgs:function(){i.cssText=\"background:url(https://),url(https://),red url(https://)\";var n=(i.background||\"\").match(/url/g);return Object.prototype.toString.call(n)===\"[object Array]\"&&n.length===3},boxshadow:function(){return r(\"boxShadow\")},borderimage:function(){return r(\"borderImage\")},borderradius:function(){return r(\"borderRadius\")},cssreflections:function(){return r(\"boxReflect\")},csstransforms:function(){return r(\"transform\")},csstransitions:function(){return r(\"transition\")},touch:function(){return\"ontouchstart\"in n},retina:function(){return n.devicePixelRatio>1},fontface:function(){var t=u.browser.name,n=u.browser.version;switch(t){case\"ie\":return n>=9;case\"chrome\":return n>=13;case\"ff\":return n>=6;case\"ios\":return n>=5;case\"android\":return!1;case\"webkit\":return n>=5.1;case\"opera\":return n>=10;default:return!1}}};for(var e in f)f[e]&&u.feature(e,f[e].call(),!0);u.feature()})(window);\n/*! head.load - v1.0.3 */\n(function(n,t){\"use strict\";function w(){}function u(n,t){if(n){typeof n==\"object\"&&(n=[].slice.call(n));for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}}function it(n,i){var r=Object.prototype.toString.call(i).slice(8,-1);return i!==t&&i!==null&&r===n}function s(n){return it(\"Function\",n)}function a(n){return it(\"Array\",n)}function et(n){var i=n.split(\"/\"),t=i[i.length-1],r=t.indexOf(\"?\");return r!==-1?t.substring(0,r):t}function f(n){(n=n||w,n._done)||(n(),n._done=1)}function ot(n,t,r,u){var f=typeof n==\"object\"?n:{test:n,success:!t?!1:a(t)?t:[t],failure:!r?!1:a(r)?r:[r],callback:u||w},e=!!f.test;return e&&!!f.success?(f.success.push(f.callback),i.load.apply(null,f.success)):e||!f.failure?u():(f.failure.push(f.callback),i.load.apply(null,f.failure)),i}function v(n){var t={},i,r;if(typeof n==\"object\")for(i in n)!n[i]||(t={name:i,url:n[i]});else t={name:et(n),url:n};return(r=c[t.name],r&&r.url===t.url)?r:(c[t.name]=t,t)}function y(n){n=n||c;for(var t in n)if(n.hasOwnProperty(t)&&n[t].state!==l)return!1;return!0}function st(n){n.state=ft;u(n.onpreload,function(n){n.call()})}function ht(n){n.state===t&&(n.state=nt,n.onpreload=[],rt({url:n.url,type:\"cache\"},function(){st(n)}))}function ct(){var n=arguments,t=n[n.length-1],r=[].slice.call(n,1),f=r[0];return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(f?(u(r,function(n){s(n)||!n||ht(v(n))}),b(v(n[0]),s(f)?f:function(){i.load.apply(null,r)})):b(v(n[0])),i)}function lt(){var n=arguments,t=n[n.length-1],r={};return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(u(n,function(n){n!==t&&(n=v(n),r[n.name]=n)}),u(n,function(n){n!==t&&(n=v(n),b(n,function(){y(r)&&f(t)}))}),i)}function b(n,t){if(t=t||w,n.state===l){t();return}if(n.state===tt){i.ready(n.name,t);return}if(n.state===nt){n.onpreload.push(function(){b(n,t)});return}n.state=tt;rt(n,function(){n.state=l;t();u(h[n.name],function(n){f(n)});o&&y()&&u(h.ALL,function(n){f(n)})})}function at(n){n=n||\"\";var t=n.split(\"?\")[0].split(\".\");return t[t.length-1].toLowerCase()}function rt(t,i){function e(t){t=t||n.event;u.onload=u.onreadystatechange=u.onerror=null;i()}function o(f){f=f||n.event;(f.type===\"load\"||/loaded|complete/.test(u.readyState)&&(!r.documentMode||r.documentMode<9))&&(n.clearTimeout(t.errorTimeout),n.clearTimeout(t.cssTimeout),u.onload=u.onreadystatechange=u.onerror=null,i())}function s(){if(t.state!==l&&t.cssRetries<=20){for(var i=0,f=r.styleSheets.length;i<f;i++)if(r.styleSheets[i].href===u.href){o({type:\"load\"});return}t.cssRetries++;t.cssTimeout=n.setTimeout(s,250)}}var u,h,f;i=i||w;h=at(t.url);h===\"css\"?(u=r.createElement(\"link\"),u.type=\"text/\"+(t.type||\"css\"),u.rel=\"stylesheet\",u.href=t.url,t.cssRetries=0,t.cssTimeout=n.setTimeout(s,500)):(u=r.createElement(\"script\"),u.type=\"text/\"+(t.type||\"javascript\"),u.src=t.url);u.onload=u.onreadystatechange=o;u.onerror=e;u.async=!1;u.defer=!1;t.errorTimeout=n.setTimeout(function(){e({type:\"timeout\"})},7e3);f=r.head||r.getElementsByTagName(\"head\")[0];f.insertBefore(u,f.lastChild)}function vt(){for(var t,u=r.getElementsByTagName(\"script\"),n=0,f=u.length;n<f;n++)if(t=u[n].getAttribute(\"data-headjs-load\"),!!t){i.load(t);return}}function yt(n,t){var v,p,e;return n===r?(o?f(t):d.push(t),i):(s(n)&&(t=n,n=\"ALL\"),a(n))?(v={},u(n,function(n){v[n]=c[n];i.ready(n,function(){y(v)&&f(t)})}),i):typeof n!=\"string\"||!s(t)?i:(p=c[n],p&&p.state===l||n===\"ALL\"&&y()&&o)?(f(t),i):(e=h[n],e?e.push(t):e=h[n]=[t],i)}function e(){if(!r.body){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(e,50);return}o||(o=!0,vt(),u(d,function(n){f(n)}))}function k(){r.addEventListener?(r.removeEventListener(\"DOMContentLoaded\",k,!1),e()):r.readyState===\"complete\"&&(r.detachEvent(\"onreadystatechange\",k),e())}var r=n.document,d=[],h={},c={},ut=\"async\"in r.createElement(\"script\")||\"MozAppearance\"in r.documentElement.style||n.opera,o,g=n.head_conf&&n.head_conf.head||\"head\",i=n[g]=n[g]||function(){i.ready.apply(null,arguments)},nt=1,ft=2,tt=3,l=4,p;if(r.readyState===\"complete\")e();else if(r.addEventListener)r.addEventListener(\"DOMContentLoaded\",k,!1),n.addEventListener(\"load\",e,!1);else{r.attachEvent(\"onreadystatechange\",k);n.attachEvent(\"onload\",e);p=!1;try{p=!n.frameElement&&r.documentElement}catch(wt){}p&&p.doScroll&&function pt(){if(!o){try{p.doScroll(\"left\")}catch(t){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(pt,50);return}e()}}()}i.load=i.js=ut?lt:ct;i.test=ot;i.ready=yt;i.ready(r,function(){y()&&u(h.ALL,function(n){f(n)});i.feature&&i.feature(\"domloaded\",!0)})})(window);\n/*\n//# sourceMappingURL=head.min.js.map\n*///# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvcmV2ZWFsLmpzL2xpYi9qcy9oZWFkLm1pbi5qcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9yZXZlYWwuanMvbGliL2pzL2hlYWQubWluLmpzPzY4ODgiXSwic291cmNlc0NvbnRlbnQiOlsi77u/LyohIGhlYWQuY29yZSAtIHYxLjAuMiAqL1xuKGZ1bmN0aW9uKG4sdCl7XCJ1c2Ugc3RyaWN0XCI7ZnVuY3Rpb24gcihuKXthW2EubGVuZ3RoXT1ufWZ1bmN0aW9uIGsobil7dmFyIHQ9bmV3IFJlZ0V4cChcIiA/XFxcXGJcIituK1wiXFxcXGJcIik7Yy5jbGFzc05hbWU9Yy5jbGFzc05hbWUucmVwbGFjZSh0LFwiXCIpfWZ1bmN0aW9uIHAobix0KXtmb3IodmFyIGk9MCxyPW4ubGVuZ3RoO2k8cjtpKyspdC5jYWxsKG4sbltpXSxpKX1mdW5jdGlvbiB0dCgpe3ZhciB0LGUsZixvO2MuY2xhc3NOYW1lPWMuY2xhc3NOYW1lLnJlcGxhY2UoLyAody18ZXEtfGd0LXxndGUtfGx0LXxsdGUtfHBvcnRyYWl0fG5vLXBvcnRyYWl0fGxhbmRzY2FwZXxuby1sYW5kc2NhcGUpXFxkKy9nLFwiXCIpO3Q9bi5pbm5lcldpZHRofHxjLmNsaWVudFdpZHRoO2U9bi5vdXRlcldpZHRofHxuLnNjcmVlbi53aWR0aDt1LnNjcmVlbi5pbm5lcldpZHRoPXQ7dS5zY3JlZW4ub3V0ZXJXaWR0aD1lO3IoXCJ3LVwiK3QpO3AoaS5zY3JlZW5zLGZ1bmN0aW9uKG4pe3Q+bj8oaS5zY3JlZW5zQ3NzLmd0JiZyKFwiZ3QtXCIrbiksaS5zY3JlZW5zQ3NzLmd0ZSYmcihcImd0ZS1cIituKSk6dDxuPyhpLnNjcmVlbnNDc3MubHQmJnIoXCJsdC1cIituKSxpLnNjcmVlbnNDc3MubHRlJiZyKFwibHRlLVwiK24pKTp0PT09biYmKGkuc2NyZWVuc0Nzcy5sdGUmJnIoXCJsdGUtXCIrbiksaS5zY3JlZW5zQ3NzLmVxJiZyKFwiZS1xXCIrbiksaS5zY3JlZW5zQ3NzLmd0ZSYmcihcImd0ZS1cIituKSl9KTtmPW4uaW5uZXJIZWlnaHR8fGMuY2xpZW50SGVpZ2h0O289bi5vdXRlckhlaWdodHx8bi5zY3JlZW4uaGVpZ2h0O3Uuc2NyZWVuLmlubmVySGVpZ2h0PWY7dS5zY3JlZW4ub3V0ZXJIZWlnaHQ9bzt1LmZlYXR1cmUoXCJwb3J0cmFpdFwiLGY+dCk7dS5mZWF0dXJlKFwibGFuZHNjYXBlXCIsZjx0KX1mdW5jdGlvbiBpdCgpe24uY2xlYXJUaW1lb3V0KGIpO2I9bi5zZXRUaW1lb3V0KHR0LDUwKX12YXIgeT1uLmRvY3VtZW50LHJ0PW4ubmF2aWdhdG9yLHV0PW4ubG9jYXRpb24sYz15LmRvY3VtZW50RWxlbWVudCxhPVtdLGk9e3NjcmVlbnM6WzI0MCwzMjAsNDgwLDY0MCw3NjgsODAwLDEwMjQsMTI4MCwxNDQwLDE2ODAsMTkyMF0sc2NyZWVuc0Nzczp7Z3Q6ITAsZ3RlOiExLGx0OiEwLGx0ZTohMSxlcTohMX0sYnJvd3NlcnM6W3tpZTp7bWluOjYsbWF4OjExfX1dLGJyb3dzZXJDc3M6e2d0OiEwLGd0ZTohMSxsdDohMCxsdGU6ITEsZXE6ITB9LGh0bWw1OiEwLHBhZ2U6XCItcGFnZVwiLHNlY3Rpb246XCItc2VjdGlvblwiLGhlYWQ6XCJoZWFkXCJ9LHYsdSxzLHcsbyxoLGwsZCxmLGcsbnQsZSxiO2lmKG4uaGVhZF9jb25mKWZvcih2IGluIG4uaGVhZF9jb25mKW4uaGVhZF9jb25mW3ZdIT09dCYmKGlbdl09bi5oZWFkX2NvbmZbdl0pO3U9bltpLmhlYWRdPWZ1bmN0aW9uKCl7dS5yZWFkeS5hcHBseShudWxsLGFyZ3VtZW50cyl9O3UuZmVhdHVyZT1mdW5jdGlvbihuLHQsaSl7cmV0dXJuIG4/KE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbCh0KT09PVwiW29iamVjdCBGdW5jdGlvbl1cIiYmKHQ9dC5jYWxsKCkpLHIoKHQ/XCJcIjpcIm5vLVwiKStuKSx1W25dPSEhdCxpfHwoayhcIm5vLVwiK24pLGsobiksdS5mZWF0dXJlKCkpLHUpOihjLmNsYXNzTmFtZSs9XCIgXCIrYS5qb2luKFwiIFwiKSxhPVtdLHUpfTt1LmZlYXR1cmUoXCJqc1wiLCEwKTtzPXJ0LnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpO3c9L21vYmlsZXxhbmRyb2lkfGtpbmRsZXxzaWxrfG1pZHB8cGhvbmV8KHdpbmRvd3MgLithcm18dG91Y2gpLy50ZXN0KHMpO3UuZmVhdHVyZShcIm1vYmlsZVwiLHcsITApO3UuZmVhdHVyZShcImRlc2t0b3BcIiwhdywhMCk7cz0vKGNocm9tZXxmaXJlZm94KVsgXFwvXShbXFx3Ll0rKS8uZXhlYyhzKXx8LyhpcGhvbmV8aXBhZHxpcG9kKSg/Oi4qdmVyc2lvbik/WyBcXC9dKFtcXHcuXSspLy5leGVjKHMpfHwvKGFuZHJvaWQpKD86Lip2ZXJzaW9uKT9bIFxcL10oW1xcdy5dKykvLmV4ZWMocyl8fC8od2Via2l0fG9wZXJhKSg/Oi4qdmVyc2lvbik/WyBcXC9dKFtcXHcuXSspLy5leGVjKHMpfHwvKG1zaWUpIChbXFx3Ll0rKS8uZXhlYyhzKXx8Lyh0cmlkZW50KS4rcnY6KFxcdy4pKy8uZXhlYyhzKXx8W107bz1zWzFdO2g9cGFyc2VGbG9hdChzWzJdKTtzd2l0Y2gobyl7Y2FzZVwibXNpZVwiOmNhc2VcInRyaWRlbnRcIjpvPVwiaWVcIjtoPXkuZG9jdW1lbnRNb2RlfHxoO2JyZWFrO2Nhc2VcImZpcmVmb3hcIjpvPVwiZmZcIjticmVhaztjYXNlXCJpcG9kXCI6Y2FzZVwiaXBhZFwiOmNhc2VcImlwaG9uZVwiOm89XCJpb3NcIjticmVhaztjYXNlXCJ3ZWJraXRcIjpvPVwic2FmYXJpXCJ9Zm9yKHUuYnJvd3Nlcj17bmFtZTpvLHZlcnNpb246aH0sdS5icm93c2VyW29dPSEwLGw9MCxkPWkuYnJvd3NlcnMubGVuZ3RoO2w8ZDtsKyspZm9yKGYgaW4gaS5icm93c2Vyc1tsXSlpZihvPT09Zilmb3IocihmKSxnPWkuYnJvd3NlcnNbbF1bZl0ubWluLG50PWkuYnJvd3NlcnNbbF1bZl0ubWF4LGU9ZztlPD1udDtlKyspaD5lPyhpLmJyb3dzZXJDc3MuZ3QmJnIoXCJndC1cIitmK2UpLGkuYnJvd3NlckNzcy5ndGUmJnIoXCJndGUtXCIrZitlKSk6aDxlPyhpLmJyb3dzZXJDc3MubHQmJnIoXCJsdC1cIitmK2UpLGkuYnJvd3NlckNzcy5sdGUmJnIoXCJsdGUtXCIrZitlKSk6aD09PWUmJihpLmJyb3dzZXJDc3MubHRlJiZyKFwibHRlLVwiK2YrZSksaS5icm93c2VyQ3NzLmVxJiZyKFwiZXEtXCIrZitlKSxpLmJyb3dzZXJDc3MuZ3RlJiZyKFwiZ3RlLVwiK2YrZSkpO2Vsc2UgcihcIm5vLVwiK2YpO3Iobyk7cihvK3BhcnNlSW50KGgsMTApKTtpLmh0bWw1JiZvPT09XCJpZVwiJiZoPDkmJnAoXCJhYmJyfGFydGljbGV8YXNpZGV8YXVkaW98Y2FudmFzfGRldGFpbHN8ZmlnY2FwdGlvbnxmaWd1cmV8Zm9vdGVyfGhlYWRlcnxoZ3JvdXB8bWFpbnxtYXJrfG1ldGVyfG5hdnxvdXRwdXR8cHJvZ3Jlc3N8c2VjdGlvbnxzdW1tYXJ5fHRpbWV8dmlkZW9cIi5zcGxpdChcInxcIiksZnVuY3Rpb24obil7eS5jcmVhdGVFbGVtZW50KG4pfSk7cCh1dC5wYXRobmFtZS5zcGxpdChcIi9cIiksZnVuY3Rpb24obix1KXtpZih0aGlzLmxlbmd0aD4yJiZ0aGlzW3UrMV0hPT10KXUmJnIodGhpcy5zbGljZSh1LHUrMSkuam9pbihcIi1cIikudG9Mb3dlckNhc2UoKStpLnNlY3Rpb24pO2Vsc2V7dmFyIGY9bnx8XCJpbmRleFwiLGU9Zi5pbmRleE9mKFwiLlwiKTtlPjAmJihmPWYuc3Vic3RyaW5nKDAsZSkpO2MuaWQ9Zi50b0xvd2VyQ2FzZSgpK2kucGFnZTt1fHxyKFwicm9vdFwiK2kuc2VjdGlvbil9fSk7dS5zY3JlZW49e2hlaWdodDpuLnNjcmVlbi5oZWlnaHQsd2lkdGg6bi5zY3JlZW4ud2lkdGh9O3R0KCk7Yj0wO24uYWRkRXZlbnRMaXN0ZW5lcj9uLmFkZEV2ZW50TGlzdGVuZXIoXCJyZXNpemVcIixpdCwhMSk6bi5hdHRhY2hFdmVudChcIm9ucmVzaXplXCIsaXQpfSkod2luZG93KTtcbi8qISBoZWFkLmNzczMgLSB2MS4wLjAgKi9cbihmdW5jdGlvbihuLHQpe1widXNlIHN0cmljdFwiO2Z1bmN0aW9uIGEobil7Zm9yKHZhciByIGluIG4paWYoaVtuW3JdXSE9PXQpcmV0dXJuITA7cmV0dXJuITF9ZnVuY3Rpb24gcihuKXt2YXIgdD1uLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpK24uc3Vic3RyKDEpLGk9KG4rXCIgXCIrYy5qb2luKHQrXCIgXCIpK3QpLnNwbGl0KFwiIFwiKTtyZXR1cm4hIWEoaSl9dmFyIGg9bi5kb2N1bWVudCxvPWguY3JlYXRlRWxlbWVudChcImlcIiksaT1vLnN0eWxlLHM9XCIgLW8tIC1tb3otIC1tcy0gLXdlYmtpdC0gLWtodG1sLSBcIi5zcGxpdChcIiBcIiksYz1cIldlYmtpdCBNb3ogTyBtcyBLaHRtbFwiLnNwbGl0KFwiIFwiKSxsPW4uaGVhZF9jb25mJiZuLmhlYWRfY29uZi5oZWFkfHxcImhlYWRcIix1PW5bbF0sZj17Z3JhZGllbnQ6ZnVuY3Rpb24oKXt2YXIgbj1cImJhY2tncm91bmQtaW1hZ2U6XCI7cmV0dXJuIGkuY3NzVGV4dD0obitzLmpvaW4oXCJncmFkaWVudChsaW5lYXIsbGVmdCB0b3AscmlnaHQgYm90dG9tLGZyb20oIzlmOSksdG8oI2ZmZikpO1wiK24pK3Muam9pbihcImxpbmVhci1ncmFkaWVudChsZWZ0IHRvcCwjZWVlLCNmZmYpO1wiK24pKS5zbGljZSgwLC1uLmxlbmd0aCksISFpLmJhY2tncm91bmRJbWFnZX0scmdiYTpmdW5jdGlvbigpe3JldHVybiBpLmNzc1RleHQ9XCJiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMCwwLDAsMC41KVwiLCEhaS5iYWNrZ3JvdW5kQ29sb3J9LG9wYWNpdHk6ZnVuY3Rpb24oKXtyZXR1cm4gby5zdHlsZS5vcGFjaXR5PT09XCJcIn0sdGV4dHNoYWRvdzpmdW5jdGlvbigpe3JldHVybiBpLnRleHRTaGFkb3c9PT1cIlwifSxtdWx0aXBsZWJnczpmdW5jdGlvbigpe2kuY3NzVGV4dD1cImJhY2tncm91bmQ6dXJsKGh0dHBzOi8vKSx1cmwoaHR0cHM6Ly8pLHJlZCB1cmwoaHR0cHM6Ly8pXCI7dmFyIG49KGkuYmFja2dyb3VuZHx8XCJcIikubWF0Y2goL3VybC9nKTtyZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKG4pPT09XCJbb2JqZWN0IEFycmF5XVwiJiZuLmxlbmd0aD09PTN9LGJveHNoYWRvdzpmdW5jdGlvbigpe3JldHVybiByKFwiYm94U2hhZG93XCIpfSxib3JkZXJpbWFnZTpmdW5jdGlvbigpe3JldHVybiByKFwiYm9yZGVySW1hZ2VcIil9LGJvcmRlcnJhZGl1czpmdW5jdGlvbigpe3JldHVybiByKFwiYm9yZGVyUmFkaXVzXCIpfSxjc3NyZWZsZWN0aW9uczpmdW5jdGlvbigpe3JldHVybiByKFwiYm94UmVmbGVjdFwiKX0sY3NzdHJhbnNmb3JtczpmdW5jdGlvbigpe3JldHVybiByKFwidHJhbnNmb3JtXCIpfSxjc3N0cmFuc2l0aW9uczpmdW5jdGlvbigpe3JldHVybiByKFwidHJhbnNpdGlvblwiKX0sdG91Y2g6ZnVuY3Rpb24oKXtyZXR1cm5cIm9udG91Y2hzdGFydFwiaW4gbn0scmV0aW5hOmZ1bmN0aW9uKCl7cmV0dXJuIG4uZGV2aWNlUGl4ZWxSYXRpbz4xfSxmb250ZmFjZTpmdW5jdGlvbigpe3ZhciB0PXUuYnJvd3Nlci5uYW1lLG49dS5icm93c2VyLnZlcnNpb247c3dpdGNoKHQpe2Nhc2VcImllXCI6cmV0dXJuIG4+PTk7Y2FzZVwiY2hyb21lXCI6cmV0dXJuIG4+PTEzO2Nhc2VcImZmXCI6cmV0dXJuIG4+PTY7Y2FzZVwiaW9zXCI6cmV0dXJuIG4+PTU7Y2FzZVwiYW5kcm9pZFwiOnJldHVybiExO2Nhc2VcIndlYmtpdFwiOnJldHVybiBuPj01LjE7Y2FzZVwib3BlcmFcIjpyZXR1cm4gbj49MTA7ZGVmYXVsdDpyZXR1cm4hMX19fTtmb3IodmFyIGUgaW4gZilmW2VdJiZ1LmZlYXR1cmUoZSxmW2VdLmNhbGwoKSwhMCk7dS5mZWF0dXJlKCl9KSh3aW5kb3cpO1xuLyohIGhlYWQubG9hZCAtIHYxLjAuMyAqL1xuKGZ1bmN0aW9uKG4sdCl7XCJ1c2Ugc3RyaWN0XCI7ZnVuY3Rpb24gdygpe31mdW5jdGlvbiB1KG4sdCl7aWYobil7dHlwZW9mIG49PVwib2JqZWN0XCImJihuPVtdLnNsaWNlLmNhbGwobikpO2Zvcih2YXIgaT0wLHI9bi5sZW5ndGg7aTxyO2krKyl0LmNhbGwobixuW2ldLGkpfX1mdW5jdGlvbiBpdChuLGkpe3ZhciByPU9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpKS5zbGljZSg4LC0xKTtyZXR1cm4gaSE9PXQmJmkhPT1udWxsJiZyPT09bn1mdW5jdGlvbiBzKG4pe3JldHVybiBpdChcIkZ1bmN0aW9uXCIsbil9ZnVuY3Rpb24gYShuKXtyZXR1cm4gaXQoXCJBcnJheVwiLG4pfWZ1bmN0aW9uIGV0KG4pe3ZhciBpPW4uc3BsaXQoXCIvXCIpLHQ9aVtpLmxlbmd0aC0xXSxyPXQuaW5kZXhPZihcIj9cIik7cmV0dXJuIHIhPT0tMT90LnN1YnN0cmluZygwLHIpOnR9ZnVuY3Rpb24gZihuKXsobj1ufHx3LG4uX2RvbmUpfHwobigpLG4uX2RvbmU9MSl9ZnVuY3Rpb24gb3Qobix0LHIsdSl7dmFyIGY9dHlwZW9mIG49PVwib2JqZWN0XCI/bjp7dGVzdDpuLHN1Y2Nlc3M6IXQ/ITE6YSh0KT90Olt0XSxmYWlsdXJlOiFyPyExOmEocik/cjpbcl0sY2FsbGJhY2s6dXx8d30sZT0hIWYudGVzdDtyZXR1cm4gZSYmISFmLnN1Y2Nlc3M/KGYuc3VjY2Vzcy5wdXNoKGYuY2FsbGJhY2spLGkubG9hZC5hcHBseShudWxsLGYuc3VjY2VzcykpOmV8fCFmLmZhaWx1cmU/dSgpOihmLmZhaWx1cmUucHVzaChmLmNhbGxiYWNrKSxpLmxvYWQuYXBwbHkobnVsbCxmLmZhaWx1cmUpKSxpfWZ1bmN0aW9uIHYobil7dmFyIHQ9e30saSxyO2lmKHR5cGVvZiBuPT1cIm9iamVjdFwiKWZvcihpIGluIG4pIW5baV18fCh0PXtuYW1lOmksdXJsOm5baV19KTtlbHNlIHQ9e25hbWU6ZXQobiksdXJsOm59O3JldHVybihyPWNbdC5uYW1lXSxyJiZyLnVybD09PXQudXJsKT9yOihjW3QubmFtZV09dCx0KX1mdW5jdGlvbiB5KG4pe249bnx8Yztmb3IodmFyIHQgaW4gbilpZihuLmhhc093blByb3BlcnR5KHQpJiZuW3RdLnN0YXRlIT09bClyZXR1cm4hMTtyZXR1cm4hMH1mdW5jdGlvbiBzdChuKXtuLnN0YXRlPWZ0O3Uobi5vbnByZWxvYWQsZnVuY3Rpb24obil7bi5jYWxsKCl9KX1mdW5jdGlvbiBodChuKXtuLnN0YXRlPT09dCYmKG4uc3RhdGU9bnQsbi5vbnByZWxvYWQ9W10scnQoe3VybDpuLnVybCx0eXBlOlwiY2FjaGVcIn0sZnVuY3Rpb24oKXtzdChuKX0pKX1mdW5jdGlvbiBjdCgpe3ZhciBuPWFyZ3VtZW50cyx0PW5bbi5sZW5ndGgtMV0scj1bXS5zbGljZS5jYWxsKG4sMSksZj1yWzBdO3JldHVybihzKHQpfHwodD1udWxsKSxhKG5bMF0pKT8oblswXS5wdXNoKHQpLGkubG9hZC5hcHBseShudWxsLG5bMF0pLGkpOihmPyh1KHIsZnVuY3Rpb24obil7cyhuKXx8IW58fGh0KHYobikpfSksYih2KG5bMF0pLHMoZik/ZjpmdW5jdGlvbigpe2kubG9hZC5hcHBseShudWxsLHIpfSkpOmIodihuWzBdKSksaSl9ZnVuY3Rpb24gbHQoKXt2YXIgbj1hcmd1bWVudHMsdD1uW24ubGVuZ3RoLTFdLHI9e307cmV0dXJuKHModCl8fCh0PW51bGwpLGEoblswXSkpPyhuWzBdLnB1c2godCksaS5sb2FkLmFwcGx5KG51bGwsblswXSksaSk6KHUobixmdW5jdGlvbihuKXtuIT09dCYmKG49dihuKSxyW24ubmFtZV09bil9KSx1KG4sZnVuY3Rpb24obil7biE9PXQmJihuPXYobiksYihuLGZ1bmN0aW9uKCl7eShyKSYmZih0KX0pKX0pLGkpfWZ1bmN0aW9uIGIobix0KXtpZih0PXR8fHcsbi5zdGF0ZT09PWwpe3QoKTtyZXR1cm59aWYobi5zdGF0ZT09PXR0KXtpLnJlYWR5KG4ubmFtZSx0KTtyZXR1cm59aWYobi5zdGF0ZT09PW50KXtuLm9ucHJlbG9hZC5wdXNoKGZ1bmN0aW9uKCl7YihuLHQpfSk7cmV0dXJufW4uc3RhdGU9dHQ7cnQobixmdW5jdGlvbigpe24uc3RhdGU9bDt0KCk7dShoW24ubmFtZV0sZnVuY3Rpb24obil7ZihuKX0pO28mJnkoKSYmdShoLkFMTCxmdW5jdGlvbihuKXtmKG4pfSl9KX1mdW5jdGlvbiBhdChuKXtuPW58fFwiXCI7dmFyIHQ9bi5zcGxpdChcIj9cIilbMF0uc3BsaXQoXCIuXCIpO3JldHVybiB0W3QubGVuZ3RoLTFdLnRvTG93ZXJDYXNlKCl9ZnVuY3Rpb24gcnQodCxpKXtmdW5jdGlvbiBlKHQpe3Q9dHx8bi5ldmVudDt1Lm9ubG9hZD11Lm9ucmVhZHlzdGF0ZWNoYW5nZT11Lm9uZXJyb3I9bnVsbDtpKCl9ZnVuY3Rpb24gbyhmKXtmPWZ8fG4uZXZlbnQ7KGYudHlwZT09PVwibG9hZFwifHwvbG9hZGVkfGNvbXBsZXRlLy50ZXN0KHUucmVhZHlTdGF0ZSkmJighci5kb2N1bWVudE1vZGV8fHIuZG9jdW1lbnRNb2RlPDkpKSYmKG4uY2xlYXJUaW1lb3V0KHQuZXJyb3JUaW1lb3V0KSxuLmNsZWFyVGltZW91dCh0LmNzc1RpbWVvdXQpLHUub25sb2FkPXUub25yZWFkeXN0YXRlY2hhbmdlPXUub25lcnJvcj1udWxsLGkoKSl9ZnVuY3Rpb24gcygpe2lmKHQuc3RhdGUhPT1sJiZ0LmNzc1JldHJpZXM8PTIwKXtmb3IodmFyIGk9MCxmPXIuc3R5bGVTaGVldHMubGVuZ3RoO2k8ZjtpKyspaWYoci5zdHlsZVNoZWV0c1tpXS5ocmVmPT09dS5ocmVmKXtvKHt0eXBlOlwibG9hZFwifSk7cmV0dXJufXQuY3NzUmV0cmllcysrO3QuY3NzVGltZW91dD1uLnNldFRpbWVvdXQocywyNTApfX12YXIgdSxoLGY7aT1pfHx3O2g9YXQodC51cmwpO2g9PT1cImNzc1wiPyh1PXIuY3JlYXRlRWxlbWVudChcImxpbmtcIiksdS50eXBlPVwidGV4dC9cIisodC50eXBlfHxcImNzc1wiKSx1LnJlbD1cInN0eWxlc2hlZXRcIix1LmhyZWY9dC51cmwsdC5jc3NSZXRyaWVzPTAsdC5jc3NUaW1lb3V0PW4uc2V0VGltZW91dChzLDUwMCkpOih1PXIuY3JlYXRlRWxlbWVudChcInNjcmlwdFwiKSx1LnR5cGU9XCJ0ZXh0L1wiKyh0LnR5cGV8fFwiamF2YXNjcmlwdFwiKSx1LnNyYz10LnVybCk7dS5vbmxvYWQ9dS5vbnJlYWR5c3RhdGVjaGFuZ2U9bzt1Lm9uZXJyb3I9ZTt1LmFzeW5jPSExO3UuZGVmZXI9ITE7dC5lcnJvclRpbWVvdXQ9bi5zZXRUaW1lb3V0KGZ1bmN0aW9uKCl7ZSh7dHlwZTpcInRpbWVvdXRcIn0pfSw3ZTMpO2Y9ci5oZWFkfHxyLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwiaGVhZFwiKVswXTtmLmluc2VydEJlZm9yZSh1LGYubGFzdENoaWxkKX1mdW5jdGlvbiB2dCgpe2Zvcih2YXIgdCx1PXIuZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJzY3JpcHRcIiksbj0wLGY9dS5sZW5ndGg7bjxmO24rKylpZih0PXVbbl0uZ2V0QXR0cmlidXRlKFwiZGF0YS1oZWFkanMtbG9hZFwiKSwhIXQpe2kubG9hZCh0KTtyZXR1cm59fWZ1bmN0aW9uIHl0KG4sdCl7dmFyIHYscCxlO3JldHVybiBuPT09cj8obz9mKHQpOmQucHVzaCh0KSxpKToocyhuKSYmKHQ9bixuPVwiQUxMXCIpLGEobikpPyh2PXt9LHUobixmdW5jdGlvbihuKXt2W25dPWNbbl07aS5yZWFkeShuLGZ1bmN0aW9uKCl7eSh2KSYmZih0KX0pfSksaSk6dHlwZW9mIG4hPVwic3RyaW5nXCJ8fCFzKHQpP2k6KHA9Y1tuXSxwJiZwLnN0YXRlPT09bHx8bj09PVwiQUxMXCImJnkoKSYmbyk/KGYodCksaSk6KGU9aFtuXSxlP2UucHVzaCh0KTplPWhbbl09W3RdLGkpfWZ1bmN0aW9uIGUoKXtpZighci5ib2R5KXtuLmNsZWFyVGltZW91dChpLnJlYWR5VGltZW91dCk7aS5yZWFkeVRpbWVvdXQ9bi5zZXRUaW1lb3V0KGUsNTApO3JldHVybn1vfHwobz0hMCx2dCgpLHUoZCxmdW5jdGlvbihuKXtmKG4pfSkpfWZ1bmN0aW9uIGsoKXtyLmFkZEV2ZW50TGlzdGVuZXI/KHIucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIkRPTUNvbnRlbnRMb2FkZWRcIixrLCExKSxlKCkpOnIucmVhZHlTdGF0ZT09PVwiY29tcGxldGVcIiYmKHIuZGV0YWNoRXZlbnQoXCJvbnJlYWR5c3RhdGVjaGFuZ2VcIixrKSxlKCkpfXZhciByPW4uZG9jdW1lbnQsZD1bXSxoPXt9LGM9e30sdXQ9XCJhc3luY1wiaW4gci5jcmVhdGVFbGVtZW50KFwic2NyaXB0XCIpfHxcIk1vekFwcGVhcmFuY2VcImluIHIuZG9jdW1lbnRFbGVtZW50LnN0eWxlfHxuLm9wZXJhLG8sZz1uLmhlYWRfY29uZiYmbi5oZWFkX2NvbmYuaGVhZHx8XCJoZWFkXCIsaT1uW2ddPW5bZ118fGZ1bmN0aW9uKCl7aS5yZWFkeS5hcHBseShudWxsLGFyZ3VtZW50cyl9LG50PTEsZnQ9Mix0dD0zLGw9NCxwO2lmKHIucmVhZHlTdGF0ZT09PVwiY29tcGxldGVcIillKCk7ZWxzZSBpZihyLmFkZEV2ZW50TGlzdGVuZXIpci5hZGRFdmVudExpc3RlbmVyKFwiRE9NQ29udGVudExvYWRlZFwiLGssITEpLG4uYWRkRXZlbnRMaXN0ZW5lcihcImxvYWRcIixlLCExKTtlbHNle3IuYXR0YWNoRXZlbnQoXCJvbnJlYWR5c3RhdGVjaGFuZ2VcIixrKTtuLmF0dGFjaEV2ZW50KFwib25sb2FkXCIsZSk7cD0hMTt0cnl7cD0hbi5mcmFtZUVsZW1lbnQmJnIuZG9jdW1lbnRFbGVtZW50fWNhdGNoKHd0KXt9cCYmcC5kb1Njcm9sbCYmZnVuY3Rpb24gcHQoKXtpZighbyl7dHJ5e3AuZG9TY3JvbGwoXCJsZWZ0XCIpfWNhdGNoKHQpe24uY2xlYXJUaW1lb3V0KGkucmVhZHlUaW1lb3V0KTtpLnJlYWR5VGltZW91dD1uLnNldFRpbWVvdXQocHQsNTApO3JldHVybn1lKCl9fSgpfWkubG9hZD1pLmpzPXV0P2x0OmN0O2kudGVzdD1vdDtpLnJlYWR5PXl0O2kucmVhZHkocixmdW5jdGlvbigpe3koKSYmdShoLkFMTCxmdW5jdGlvbihuKXtmKG4pfSk7aS5mZWF0dXJlJiZpLmZlYXR1cmUoXCJkb21sb2FkZWRcIiwhMCl9KX0pKHdpbmRvdyk7XG4vKlxuLy8jIHNvdXJjZU1hcHBpbmdVUkw9aGVhZC5taW4uanMubWFwXG4qLyJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/reveal.js/lib/js/head.min.js\n");
  218. /***/ }),
  219. /***/ "./node_modules/reveal.js/lib/js/html5shiv.js":
  220. /*!****************************************************!*\
  221. !*** ./node_modules/reveal.js/lib/js/html5shiv.js ***!
  222. \****************************************************/
  223. /*! no static exports found */
  224. /***/ (function(module, exports) {
  225. eval("document.createElement('header');\ndocument.createElement('nav');\ndocument.createElement('section');\ndocument.createElement('article');\ndocument.createElement('aside');\ndocument.createElement('footer');\ndocument.createElement('hgroup');//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvcmV2ZWFsLmpzL2xpYi9qcy9odG1sNXNoaXYuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9ub2RlX21vZHVsZXMvcmV2ZWFsLmpzL2xpYi9qcy9odG1sNXNoaXYuanM/MTYwOSJdLCJzb3VyY2VzQ29udGVudCI6WyJkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdoZWFkZXInKTtcbmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ25hdicpO1xuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc2VjdGlvbicpO1xuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYXJ0aWNsZScpO1xuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYXNpZGUnKTtcbmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2Zvb3RlcicpO1xuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnaGdyb3VwJyk7Il0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/reveal.js/lib/js/html5shiv.js\n");
  226. /***/ }),
  227. /***/ "./src/client/js/legacy/crowi-presentation.js":
  228. /*!****************************************************!*\
  229. !*** ./src/client/js/legacy/crowi-presentation.js ***!
  230. \****************************************************/
  231. /*! no static exports found */
  232. /***/ (function(module, exports, __webpack_require__) {
  233. eval("/* WEBPACK VAR INJECTION */(function($) {const Reveal = __webpack_require__(/*! reveal.js */ \"./node_modules/reveal.js/js/reveal.js\");\n\n__webpack_require__(/*! reveal.js/lib/js/head.min.js */ \"./node_modules/reveal.js/lib/js/head.min.js\");\n\n__webpack_require__(/*! reveal.js/lib/js/html5shiv.js */ \"./node_modules/reveal.js/lib/js/html5shiv.js\");\n\nif (!window) {\n window = {};\n}\n\nwindow.Reveal = Reveal;\nReveal.initialize({\n controls: true,\n progress: true,\n history: true,\n center: true,\n transition: 'slide' // // This specification method can't be used\n // // sice deleting symlink prevented `src` from being resolved -- 2017.06.15 Yuki Takei\n //\n // Optional libraries used to extend on reveal.js\n // dependencies: [\n // { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },\n // { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },\n // { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },\n // { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },\n // { src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },\n // { src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }\n // ]\n\n});\n\n__webpack_require__.e(/*! require.ensure */ 0).then((() => {\n __webpack_require__(/*! reveal.js/lib/js/classList.js */ \"./node_modules/reveal.js/lib/js/classList.js\");\n\n __webpack_require__(/*! reveal.js/plugin/zoom-js/zoom.js */ \"./node_modules/reveal.js/plugin/zoom-js/zoom.js\");\n\n __webpack_require__(/*! reveal.js/plugin/notes/notes.js */ \"./node_modules/reveal.js/plugin/notes/notes.js\");\n\n __webpack_require__(/*! ../util/reveal/plugins/growi-renderer.js */ \"./src/client/js/util/reveal/plugins/growi-renderer.js\"); // fix https://github.com/weseek/crowi-plus/issues/96\n\n\n Reveal.slide(0, 0);\n Reveal.sync();\n}).bind(null, __webpack_require__)).catch(__webpack_require__.oe);\n\nReveal.addEventListener('ready', event => {\n // event.currentSlide, event.indexh, event.indexv\n $('.reveal section').each(function (e) {\n const $self = $(this);\n\n if ($self.children().length !== 1) {\n $self.addClass('only');\n }\n });\n});\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! jquery */ \"jquery\")))//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvY2xpZW50L2pzL2xlZ2FjeS9jcm93aS1wcmVzZW50YXRpb24uanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvY2xpZW50L2pzL2xlZ2FjeS9jcm93aS1wcmVzZW50YXRpb24uanM/NTMzZCJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBSZXZlYWwgPSByZXF1aXJlKCdyZXZlYWwuanMnKTtcblxucmVxdWlyZSgncmV2ZWFsLmpzL2xpYi9qcy9oZWFkLm1pbi5qcycpO1xucmVxdWlyZSgncmV2ZWFsLmpzL2xpYi9qcy9odG1sNXNoaXYuanMnKTtcblxuaWYgKCF3aW5kb3cpIHtcbiAgd2luZG93ID0ge307XG59XG53aW5kb3cuUmV2ZWFsID0gUmV2ZWFsO1xuXG5SZXZlYWwuaW5pdGlhbGl6ZSh7XG4gIGNvbnRyb2xzOiB0cnVlLFxuICBwcm9ncmVzczogdHJ1ZSxcbiAgaGlzdG9yeTogdHJ1ZSxcbiAgY2VudGVyOiB0cnVlLFxuICB0cmFuc2l0aW9uOiAnc2xpZGUnLFxuXG4gIC8vIC8vIFRoaXMgc3BlY2lmaWNhdGlvbiBtZXRob2QgY2FuJ3QgYmUgdXNlZFxuICAvLyAvLyAgIHNpY2UgZGVsZXRpbmcgc3ltbGluayBwcmV2ZW50ZWQgYHNyY2AgZnJvbSBiZWluZyByZXNvbHZlZCAtLSAyMDE3LjA2LjE1IFl1a2kgVGFrZWlcbiAgLy9cbiAgLy8gT3B0aW9uYWwgbGlicmFyaWVzIHVzZWQgdG8gZXh0ZW5kIG9uIHJldmVhbC5qc1xuICAvLyBkZXBlbmRlbmNpZXM6IFtcbiAgLy8gICB7IHNyYzogJ2xpYi9qcy9jbGFzc0xpc3QuanMnLCBjb25kaXRpb246IGZ1bmN0aW9uKCkgeyByZXR1cm4gIWRvY3VtZW50LmJvZHkuY2xhc3NMaXN0OyB9IH0sXG4gIC8vICAgeyBzcmM6ICdwbHVnaW4vbWFya2Rvd24vbWFya2VkLmpzJywgY29uZGl0aW9uOiBmdW5jdGlvbigpIHsgcmV0dXJuICEhZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJ1tkYXRhLW1hcmtkb3duXScgKTsgfSB9LFxuICAvLyAgIHsgc3JjOiAncGx1Z2luL21hcmtkb3duL21hcmtkb3duLmpzJywgY29uZGl0aW9uOiBmdW5jdGlvbigpIHsgcmV0dXJuICEhZG9jdW1lbnQucXVlcnlTZWxlY3RvciggJ1tkYXRhLW1hcmtkb3duXScgKTsgfSB9LFxuICAvLyAgIHsgc3JjOiAncGx1Z2luL2hpZ2hsaWdodC9oaWdobGlnaHQuanMnLCBhc3luYzogdHJ1ZSwgY2FsbGJhY2s6IGZ1bmN0aW9uKCkgeyBobGpzLmluaXRIaWdobGlnaHRpbmdPbkxvYWQoKTsgfSB9LFxuICAvLyAgIHsgc3JjOiAncGx1Z2luL3pvb20tanMvem9vbS5qcycsIGFzeW5jOiB0cnVlLCBjb25kaXRpb246IGZ1bmN0aW9uKCkgeyByZXR1cm4gISFkb2N1bWVudC5ib2R5LmNsYXNzTGlzdDsgfSB9LFxuICAvLyAgIHsgc3JjOiAncGx1Z2luL25vdGVzL25vdGVzLmpzJywgYXN5bmM6IHRydWUsIGNvbmRpdGlvbjogZnVuY3Rpb24oKSB7IHJldHVybiAhIWRvY3VtZW50LmJvZHkuY2xhc3NMaXN0OyB9IH1cbiAgLy8gXVxufSk7XG5cbnJlcXVpcmUuZW5zdXJlKFtdLCAoKSA9PiB7XG4gIHJlcXVpcmUoJ3JldmVhbC5qcy9saWIvanMvY2xhc3NMaXN0LmpzJyk7XG4gIHJlcXVpcmUoJ3JldmVhbC5qcy9wbHVnaW4vem9vbS1qcy96b29tLmpzJyk7XG4gIHJlcXVpcmUoJ3JldmVhbC5qcy9wbHVnaW4vbm90ZXMvbm90ZXMuanMnKTtcbiAgcmVxdWlyZSgnLi4vdXRpbC9yZXZlYWwvcGx1Z2lucy9ncm93aS1yZW5kZXJlci5qcycpO1xuXG4gIC8vIGZpeCBodHRwczovL2dpdGh1Yi5jb20vd2VzZWVrL2Nyb3dpLXBsdXMvaXNzdWVzLzk2XG4gIFJldmVhbC5zbGlkZSgwLCAwKTtcbiAgUmV2ZWFsLnN5bmMoKTtcbn0pO1xuXG5SZXZlYWwuYWRkRXZlbnRMaXN0ZW5lcigncmVhZHknLCAoZXZlbnQpID0+IHtcbiAgLy8gZXZlbnQuY3VycmVudFNsaWRlLCBldmVudC5pbmRleGgsIGV2ZW50LmluZGV4dlxuICAkKCcucmV2ZWFsIHNlY3Rpb24nKS5lYWNoKGZ1bmN0aW9uKGUpIHtcbiAgICBjb25zdCAkc2VsZiA9ICQodGhpcyk7XG4gICAgaWYgKCRzZWxmLmNoaWxkcmVuKCkubGVuZ3RoICE9PSAxKSB7XG4gICAgICAkc2VsZi5hZGRDbGFzcygnb25seScpO1xuICAgIH1cbiAgfSk7XG59KTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQW5CQTtBQUNBO0FBb0JBO0FBQ0E7QUFDQTtBQUFBO0FBQ0E7QUFBQTtBQUNBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/client/js/legacy/crowi-presentation.js\n");
  234. /***/ }),
  235. /***/ "jquery":
  236. /*!*************************!*\
  237. !*** external "jQuery" ***!
  238. \*************************/
  239. /*! no static exports found */
  240. /***/ (function(module, exports) {
  241. eval("module.exports = jQuery;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianF1ZXJ5LmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL2V4dGVybmFsIFwialF1ZXJ5XCI/Y2QwYyJdLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuZXhwb3J0cyA9IGpRdWVyeTsiXSwibWFwcGluZ3MiOiJBQUFBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///jquery\n");
  242. /***/ })
  243. /******/ });