diff --git a/app.js b/app.js index ccc9efa..e7ca3ad 100644 --- a/app.js +++ b/app.js @@ -1,8141 +1,667 @@ +/* + * ATTENTION: An "eval-source-map" devtool has been used. + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ /******/ (() => { // webpackBootstrap -/******/ "use strict"; /******/ var __webpack_modules__ = ({ -/***/ 401: +/***/ "./node_modules/@remix-run/router/dist/router.js": +/*!*******************************************************!*\ + !*** ./node_modules/@remix-run/router/dist/router.js ***! + \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ AbortedDeferredError: () => (/* binding */ AbortedDeferredError),\n/* harmony export */ Action: () => (/* binding */ Action),\n/* harmony export */ ErrorResponse: () => (/* binding */ ErrorResponse),\n/* harmony export */ IDLE_BLOCKER: () => (/* binding */ IDLE_BLOCKER),\n/* harmony export */ IDLE_FETCHER: () => (/* binding */ IDLE_FETCHER),\n/* harmony export */ IDLE_NAVIGATION: () => (/* binding */ IDLE_NAVIGATION),\n/* harmony export */ UNSAFE_DEFERRED_SYMBOL: () => (/* binding */ UNSAFE_DEFERRED_SYMBOL),\n/* harmony export */ UNSAFE_DeferredData: () => (/* binding */ DeferredData),\n/* harmony export */ UNSAFE_convertRoutesToDataRoutes: () => (/* binding */ convertRoutesToDataRoutes),\n/* harmony export */ UNSAFE_getPathContributingMatches: () => (/* binding */ getPathContributingMatches),\n/* harmony export */ UNSAFE_invariant: () => (/* binding */ invariant),\n/* harmony export */ UNSAFE_warning: () => (/* binding */ warning),\n/* harmony export */ createBrowserHistory: () => (/* binding */ createBrowserHistory),\n/* harmony export */ createHashHistory: () => (/* binding */ createHashHistory),\n/* harmony export */ createMemoryHistory: () => (/* binding */ createMemoryHistory),\n/* harmony export */ createPath: () => (/* binding */ createPath),\n/* harmony export */ createRouter: () => (/* binding */ createRouter),\n/* harmony export */ createStaticHandler: () => (/* binding */ createStaticHandler),\n/* harmony export */ defer: () => (/* binding */ defer),\n/* harmony export */ generatePath: () => (/* binding */ generatePath),\n/* harmony export */ getStaticContextFromError: () => (/* binding */ getStaticContextFromError),\n/* harmony export */ getToPathname: () => (/* binding */ getToPathname),\n/* harmony export */ isDeferredData: () => (/* binding */ isDeferredData),\n/* harmony export */ isRouteErrorResponse: () => (/* binding */ isRouteErrorResponse),\n/* harmony export */ joinPaths: () => (/* binding */ joinPaths),\n/* harmony export */ json: () => (/* binding */ json),\n/* harmony export */ matchPath: () => (/* binding */ matchPath),\n/* harmony export */ matchRoutes: () => (/* binding */ matchRoutes),\n/* harmony export */ normalizePathname: () => (/* binding */ normalizePathname),\n/* harmony export */ parsePath: () => (/* binding */ parsePath),\n/* harmony export */ redirect: () => (/* binding */ redirect),\n/* harmony export */ resolvePath: () => (/* binding */ resolvePath),\n/* harmony export */ resolveTo: () => (/* binding */ resolveTo),\n/* harmony export */ stripBasename: () => (/* binding */ stripBasename)\n/* harmony export */ });\n/**\n * @remix-run/router v1.7.0\n *\n * Copyright (c) Remix Software Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.md file in the root directory of this source tree.\n *\n * @license MIT\n */\nfunction _extends() {\n _extends = Object.assign ? Object.assign.bind() : function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n return _extends.apply(this, arguments);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//#region Types and Constants\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Actions represent the type of change to a location value.\n */\nvar Action;\n(function (Action) {\n /**\n * A POP indicates a change to an arbitrary index in the history stack, such\n * as a back or forward navigation. It does not describe the direction of the\n * navigation, only that the current index changed.\n *\n * Note: This is the default action for newly created history objects.\n */\n Action[\"Pop\"] = \"POP\";\n /**\n * A PUSH indicates a new entry being added to the history stack, such as when\n * a link is clicked and a new page loads. When this happens, all subsequent\n * entries in the stack are lost.\n */\n Action[\"Push\"] = \"PUSH\";\n /**\n * A REPLACE indicates the entry at the current index in the history stack\n * being replaced by a new one.\n */\n Action[\"Replace\"] = \"REPLACE\";\n})(Action || (Action = {}));\nconst PopStateEventType = \"popstate\";\n/**\n * Memory history stores the current location in memory. It is designed for use\n * in stateful non-browser environments like tests and React Native.\n */\nfunction createMemoryHistory(options) {\n if (options === void 0) {\n options = {};\n }\n let {\n initialEntries = [\"/\"],\n initialIndex,\n v5Compat = false\n } = options;\n let entries; // Declare so we can access from createMemoryLocation\n entries = initialEntries.map((entry, index) => createMemoryLocation(entry, typeof entry === \"string\" ? null : entry.state, index === 0 ? \"default\" : undefined));\n let index = clampIndex(initialIndex == null ? entries.length - 1 : initialIndex);\n let action = Action.Pop;\n let listener = null;\n function clampIndex(n) {\n return Math.min(Math.max(n, 0), entries.length - 1);\n }\n function getCurrentLocation() {\n return entries[index];\n }\n function createMemoryLocation(to, state, key) {\n if (state === void 0) {\n state = null;\n }\n let location = createLocation(entries ? getCurrentLocation().pathname : \"/\", to, state, key);\n warning(location.pathname.charAt(0) === \"/\", \"relative pathnames are not supported in memory history: \" + JSON.stringify(to));\n return location;\n }\n function createHref(to) {\n return typeof to === \"string\" ? to : createPath(to);\n }\n let history = {\n get index() {\n return index;\n },\n get action() {\n return action;\n },\n get location() {\n return getCurrentLocation();\n },\n createHref,\n createURL(to) {\n return new URL(createHref(to), \"http://localhost\");\n },\n encodeLocation(to) {\n let path = typeof to === \"string\" ? parsePath(to) : to;\n return {\n pathname: path.pathname || \"\",\n search: path.search || \"\",\n hash: path.hash || \"\"\n };\n },\n push(to, state) {\n action = Action.Push;\n let nextLocation = createMemoryLocation(to, state);\n index += 1;\n entries.splice(index, entries.length, nextLocation);\n if (v5Compat && listener) {\n listener({\n action,\n location: nextLocation,\n delta: 1\n });\n }\n },\n replace(to, state) {\n action = Action.Replace;\n let nextLocation = createMemoryLocation(to, state);\n entries[index] = nextLocation;\n if (v5Compat && listener) {\n listener({\n action,\n location: nextLocation,\n delta: 0\n });\n }\n },\n go(delta) {\n action = Action.Pop;\n let nextIndex = clampIndex(index + delta);\n let nextLocation = entries[nextIndex];\n index = nextIndex;\n if (listener) {\n listener({\n action,\n location: nextLocation,\n delta\n });\n }\n },\n listen(fn) {\n listener = fn;\n return () => {\n listener = null;\n };\n }\n };\n return history;\n}\n/**\n * Browser history stores the location in regular URLs. This is the standard for\n * most web apps, but it requires some configuration on the server to ensure you\n * serve the same app at multiple URLs.\n *\n * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory\n */\nfunction createBrowserHistory(options) {\n if (options === void 0) {\n options = {};\n }\n function createBrowserLocation(window, globalHistory) {\n let {\n pathname,\n search,\n hash\n } = window.location;\n return createLocation(\"\", {\n pathname,\n search,\n hash\n },\n // state defaults to `null` because `window.history.state` does\n globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || \"default\");\n }\n function createBrowserHref(window, to) {\n return typeof to === \"string\" ? to : createPath(to);\n }\n return getUrlBasedHistory(createBrowserLocation, createBrowserHref, null, options);\n}\n/**\n * Hash history stores the location in window.location.hash. This makes it ideal\n * for situations where you don't want to send the location to the server for\n * some reason, either because you do cannot configure it or the URL space is\n * reserved for something else.\n *\n * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory\n */\nfunction createHashHistory(options) {\n if (options === void 0) {\n options = {};\n }\n function createHashLocation(window, globalHistory) {\n let {\n pathname = \"/\",\n search = \"\",\n hash = \"\"\n } = parsePath(window.location.hash.substr(1));\n return createLocation(\"\", {\n pathname,\n search,\n hash\n },\n // state defaults to `null` because `window.history.state` does\n globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || \"default\");\n }\n function createHashHref(window, to) {\n let base = window.document.querySelector(\"base\");\n let href = \"\";\n if (base && base.getAttribute(\"href\")) {\n let url = window.location.href;\n let hashIndex = url.indexOf(\"#\");\n href = hashIndex === -1 ? url : url.slice(0, hashIndex);\n }\n return href + \"#\" + (typeof to === \"string\" ? to : createPath(to));\n }\n function validateHashLocation(location, to) {\n warning(location.pathname.charAt(0) === \"/\", \"relative pathnames are not supported in hash history.push(\" + JSON.stringify(to) + \")\");\n }\n return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options);\n}\nfunction invariant(value, message) {\n if (value === false || value === null || typeof value === \"undefined\") {\n throw new Error(message);\n }\n}\nfunction warning(cond, message) {\n if (!cond) {\n // eslint-disable-next-line no-console\n if (typeof console !== \"undefined\") console.warn(message);\n try {\n // Welcome to debugging history!\n //\n // This error is thrown as a convenience so you can more easily\n // find the source for a warning that appears in the console by\n // enabling \"pause on exceptions\" in your JavaScript debugger.\n throw new Error(message);\n // eslint-disable-next-line no-empty\n } catch (e) {}\n }\n}\nfunction createKey() {\n return Math.random().toString(36).substr(2, 8);\n}\n/**\n * For browser-based histories, we combine the state and key into an object\n */\nfunction getHistoryState(location, index) {\n return {\n usr: location.state,\n key: location.key,\n idx: index\n };\n}\n/**\n * Creates a Location object with a unique key from the given Path\n */\nfunction createLocation(current, to, state, key) {\n if (state === void 0) {\n state = null;\n }\n let location = _extends({\n pathname: typeof current === \"string\" ? current : current.pathname,\n search: \"\",\n hash: \"\"\n }, typeof to === \"string\" ? parsePath(to) : to, {\n state,\n // TODO: This could be cleaned up. push/replace should probably just take\n // full Locations now and avoid the need to run through this flow at all\n // But that's a pretty big refactor to the current test suite so going to\n // keep as is for the time being and just let any incoming keys take precedence\n key: to && to.key || key || createKey()\n });\n return location;\n}\n/**\n * Creates a string URL path from the given pathname, search, and hash components.\n */\nfunction createPath(_ref) {\n let {\n pathname = \"/\",\n search = \"\",\n hash = \"\"\n } = _ref;\n if (search && search !== \"?\") pathname += search.charAt(0) === \"?\" ? search : \"?\" + search;\n if (hash && hash !== \"#\") pathname += hash.charAt(0) === \"#\" ? hash : \"#\" + hash;\n return pathname;\n}\n/**\n * Parses a string URL path into its separate pathname, search, and hash components.\n */\nfunction parsePath(path) {\n let parsedPath = {};\n if (path) {\n let hashIndex = path.indexOf(\"#\");\n if (hashIndex >= 0) {\n parsedPath.hash = path.substr(hashIndex);\n path = path.substr(0, hashIndex);\n }\n let searchIndex = path.indexOf(\"?\");\n if (searchIndex >= 0) {\n parsedPath.search = path.substr(searchIndex);\n path = path.substr(0, searchIndex);\n }\n if (path) {\n parsedPath.pathname = path;\n }\n }\n return parsedPath;\n}\nfunction getUrlBasedHistory(getLocation, createHref, validateLocation, options) {\n if (options === void 0) {\n options = {};\n }\n let {\n window = document.defaultView,\n v5Compat = false\n } = options;\n let globalHistory = window.history;\n let action = Action.Pop;\n let listener = null;\n let index = getIndex();\n // Index should only be null when we initialize. If not, it's because the\n // user called history.pushState or history.replaceState directly, in which\n // case we should log a warning as it will result in bugs.\n if (index == null) {\n index = 0;\n globalHistory.replaceState(_extends({}, globalHistory.state, {\n idx: index\n }), \"\");\n }\n function getIndex() {\n let state = globalHistory.state || {\n idx: null\n };\n return state.idx;\n }\n function handlePop() {\n action = Action.Pop;\n let nextIndex = getIndex();\n let delta = nextIndex == null ? null : nextIndex - index;\n index = nextIndex;\n if (listener) {\n listener({\n action,\n location: history.location,\n delta\n });\n }\n }\n function push(to, state) {\n action = Action.Push;\n let location = createLocation(history.location, to, state);\n if (validateLocation) validateLocation(location, to);\n index = getIndex() + 1;\n let historyState = getHistoryState(location, index);\n let url = history.createHref(location);\n // try...catch because iOS limits us to 100 pushState calls :/\n try {\n globalHistory.pushState(historyState, \"\", url);\n } catch (error) {\n // If the exception is because `state` can't be serialized, let that throw\n // outwards just like a replace call would so the dev knows the cause\n // https://html.spec.whatwg.org/multipage/nav-history-apis.html#shared-history-push/replace-state-steps\n // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal\n if (error instanceof DOMException && error.name === \"DataCloneError\") {\n throw error;\n }\n // They are going to lose state here, but there is no real\n // way to warn them about it since the page will refresh...\n window.location.assign(url);\n }\n if (v5Compat && listener) {\n listener({\n action,\n location: history.location,\n delta: 1\n });\n }\n }\n function replace(to, state) {\n action = Action.Replace;\n let location = createLocation(history.location, to, state);\n if (validateLocation) validateLocation(location, to);\n index = getIndex();\n let historyState = getHistoryState(location, index);\n let url = history.createHref(location);\n globalHistory.replaceState(historyState, \"\", url);\n if (v5Compat && listener) {\n listener({\n action,\n location: history.location,\n delta: 0\n });\n }\n }\n function createURL(to) {\n // window.location.origin is \"null\" (the literal string value) in Firefox\n // under certain conditions, notably when serving from a local HTML file\n // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297\n let base = window.location.origin !== \"null\" ? window.location.origin : window.location.href;\n let href = typeof to === \"string\" ? to : createPath(to);\n invariant(base, \"No window.location.(origin|href) available to create URL for href: \" + href);\n return new URL(href, base);\n }\n let history = {\n get action() {\n return action;\n },\n get location() {\n return getLocation(window, globalHistory);\n },\n listen(fn) {\n if (listener) {\n throw new Error(\"A history only accepts one active listener\");\n }\n window.addEventListener(PopStateEventType, handlePop);\n listener = fn;\n return () => {\n window.removeEventListener(PopStateEventType, handlePop);\n listener = null;\n };\n },\n createHref(to) {\n return createHref(window, to);\n },\n createURL,\n encodeLocation(to) {\n // Encode a Location the same way window.location would\n let url = createURL(to);\n return {\n pathname: url.pathname,\n search: url.search,\n hash: url.hash\n };\n },\n push,\n replace,\n go(n) {\n return globalHistory.go(n);\n }\n };\n return history;\n}\n//#endregion\n\nvar ResultType;\n(function (ResultType) {\n ResultType[\"data\"] = \"data\";\n ResultType[\"deferred\"] = \"deferred\";\n ResultType[\"redirect\"] = \"redirect\";\n ResultType[\"error\"] = \"error\";\n})(ResultType || (ResultType = {}));\nconst immutableRouteKeys = new Set([\"lazy\", \"caseSensitive\", \"path\", \"id\", \"index\", \"children\"]);\nfunction isIndexRoute(route) {\n return route.index === true;\n}\n// Walk the route tree generating unique IDs where necessary so we are working\n// solely with AgnosticDataRouteObject's within the Router\nfunction convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath, manifest) {\n if (parentPath === void 0) {\n parentPath = [];\n }\n if (manifest === void 0) {\n manifest = {};\n }\n return routes.map((route, index) => {\n let treePath = [...parentPath, index];\n let id = typeof route.id === \"string\" ? route.id : treePath.join(\"-\");\n invariant(route.index !== true || !route.children, \"Cannot specify children on an index route\");\n invariant(!manifest[id], \"Found a route id collision on id \\\"\" + id + \"\\\". Route \" + \"id's must be globally unique within Data Router usages\");\n if (isIndexRoute(route)) {\n let indexRoute = _extends({}, route, mapRouteProperties(route), {\n id\n });\n manifest[id] = indexRoute;\n return indexRoute;\n } else {\n let pathOrLayoutRoute = _extends({}, route, mapRouteProperties(route), {\n id,\n children: undefined\n });\n manifest[id] = pathOrLayoutRoute;\n if (route.children) {\n pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest);\n }\n return pathOrLayoutRoute;\n }\n });\n}\n/**\n * Matches the given routes to a location and returns the match data.\n *\n * @see https://reactrouter.com/utils/match-routes\n */\nfunction matchRoutes(routes, locationArg, basename) {\n if (basename === void 0) {\n basename = \"/\";\n }\n let location = typeof locationArg === \"string\" ? parsePath(locationArg) : locationArg;\n let pathname = stripBasename(location.pathname || \"/\", basename);\n if (pathname == null) {\n return null;\n }\n let branches = flattenRoutes(routes);\n rankRouteBranches(branches);\n let matches = null;\n for (let i = 0; matches == null && i < branches.length; ++i) {\n matches = matchRouteBranch(branches[i],\n // Incoming pathnames are generally encoded from either window.location\n // or from router.navigate, but we want to match against the unencoded\n // paths in the route definitions. Memory router locations won't be\n // encoded here but there also shouldn't be anything to decode so this\n // should be a safe operation. This avoids needing matchRoutes to be\n // history-aware.\n safelyDecodeURI(pathname));\n }\n return matches;\n}\nfunction flattenRoutes(routes, branches, parentsMeta, parentPath) {\n if (branches === void 0) {\n branches = [];\n }\n if (parentsMeta === void 0) {\n parentsMeta = [];\n }\n if (parentPath === void 0) {\n parentPath = \"\";\n }\n let flattenRoute = (route, index, relativePath) => {\n let meta = {\n relativePath: relativePath === undefined ? route.path || \"\" : relativePath,\n caseSensitive: route.caseSensitive === true,\n childrenIndex: index,\n route\n };\n if (meta.relativePath.startsWith(\"/\")) {\n invariant(meta.relativePath.startsWith(parentPath), \"Absolute route path \\\"\" + meta.relativePath + \"\\\" nested under path \" + (\"\\\"\" + parentPath + \"\\\" is not valid. An absolute child route path \") + \"must start with the combined path of all its parent routes.\");\n meta.relativePath = meta.relativePath.slice(parentPath.length);\n }\n let path = joinPaths([parentPath, meta.relativePath]);\n let routesMeta = parentsMeta.concat(meta);\n // Add the children before adding this route to the array so we traverse the\n // route tree depth-first and child routes appear before their parents in\n // the \"flattened\" version.\n if (route.children && route.children.length > 0) {\n invariant(\n // Our types know better, but runtime JS may not!\n // @ts-expect-error\n route.index !== true, \"Index routes must not have child routes. Please remove \" + (\"all child routes from route path \\\"\" + path + \"\\\".\"));\n flattenRoutes(route.children, branches, routesMeta, path);\n }\n // Routes without a path shouldn't ever match by themselves unless they are\n // index routes, so don't add them to the list of possible branches.\n if (route.path == null && !route.index) {\n return;\n }\n branches.push({\n path,\n score: computeScore(path, route.index),\n routesMeta\n });\n };\n routes.forEach((route, index) => {\n var _route$path;\n // coarse-grain check for optional params\n if (route.path === \"\" || !((_route$path = route.path) != null && _route$path.includes(\"?\"))) {\n flattenRoute(route, index);\n } else {\n for (let exploded of explodeOptionalSegments(route.path)) {\n flattenRoute(route, index, exploded);\n }\n }\n });\n return branches;\n}\n/**\n * Computes all combinations of optional path segments for a given path,\n * excluding combinations that are ambiguous and of lower priority.\n *\n * For example, `/one/:two?/three/:four?/:five?` explodes to:\n * - `/one/three`\n * - `/one/:two/three`\n * - `/one/three/:four`\n * - `/one/three/:five`\n * - `/one/:two/three/:four`\n * - `/one/:two/three/:five`\n * - `/one/three/:four/:five`\n * - `/one/:two/three/:four/:five`\n */\nfunction explodeOptionalSegments(path) {\n let segments = path.split(\"/\");\n if (segments.length === 0) return [];\n let [first, ...rest] = segments;\n // Optional path segments are denoted by a trailing `?`\n let isOptional = first.endsWith(\"?\");\n // Compute the corresponding required segment: `foo?` -> `foo`\n let required = first.replace(/\\?$/, \"\");\n if (rest.length === 0) {\n // Intepret empty string as omitting an optional segment\n // `[\"one\", \"\", \"three\"]` corresponds to omitting `:two` from `/one/:two?/three` -> `/one/three`\n return isOptional ? [required, \"\"] : [required];\n }\n let restExploded = explodeOptionalSegments(rest.join(\"/\"));\n let result = [];\n // All child paths with the prefix. Do this for all children before the\n // optional version for all children so we get consistent ordering where the\n // parent optional aspect is preferred as required. Otherwise, we can get\n // child sections interspersed where deeper optional segments are higher than\n // parent optional segments, where for example, /:two would explodes _earlier_\n // then /:one. By always including the parent as required _for all children_\n // first, we avoid this issue\n result.push(...restExploded.map(subpath => subpath === \"\" ? required : [required, subpath].join(\"/\")));\n // Then if this is an optional value, add all child versions without\n if (isOptional) {\n result.push(...restExploded);\n }\n // for absolute paths, ensure `/` instead of empty segment\n return result.map(exploded => path.startsWith(\"/\") && exploded === \"\" ? \"/\" : exploded);\n}\nfunction rankRouteBranches(branches) {\n branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first\n : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));\n}\nconst paramRe = /^:\\w+$/;\nconst dynamicSegmentValue = 3;\nconst indexRouteValue = 2;\nconst emptySegmentValue = 1;\nconst staticSegmentValue = 10;\nconst splatPenalty = -2;\nconst isSplat = s => s === \"*\";\nfunction computeScore(path, index) {\n let segments = path.split(\"/\");\n let initialScore = segments.length;\n if (segments.some(isSplat)) {\n initialScore += splatPenalty;\n }\n if (index) {\n initialScore += indexRouteValue;\n }\n return segments.filter(s => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === \"\" ? emptySegmentValue : staticSegmentValue), initialScore);\n}\nfunction compareIndexes(a, b) {\n let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);\n return siblings ?\n // If two routes are siblings, we should try to match the earlier sibling\n // first. This allows people to have fine-grained control over the matching\n // behavior by simply putting routes with identical paths in the order they\n // want them tried.\n a[a.length - 1] - b[b.length - 1] :\n // Otherwise, it doesn't really make sense to rank non-siblings by index,\n // so they sort equally.\n 0;\n}\nfunction matchRouteBranch(branch, pathname) {\n let {\n routesMeta\n } = branch;\n let matchedParams = {};\n let matchedPathname = \"/\";\n let matches = [];\n for (let i = 0; i < routesMeta.length; ++i) {\n let meta = routesMeta[i];\n let end = i === routesMeta.length - 1;\n let remainingPathname = matchedPathname === \"/\" ? pathname : pathname.slice(matchedPathname.length) || \"/\";\n let match = matchPath({\n path: meta.relativePath,\n caseSensitive: meta.caseSensitive,\n end\n }, remainingPathname);\n if (!match) return null;\n Object.assign(matchedParams, match.params);\n let route = meta.route;\n matches.push({\n // TODO: Can this as be avoided?\n params: matchedParams,\n pathname: joinPaths([matchedPathname, match.pathname]),\n pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),\n route\n });\n if (match.pathnameBase !== \"/\") {\n matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);\n }\n }\n return matches;\n}\n/**\n * Returns a path with params interpolated.\n *\n * @see https://reactrouter.com/utils/generate-path\n */\nfunction generatePath(originalPath, params) {\n if (params === void 0) {\n params = {};\n }\n let path = originalPath;\n if (path.endsWith(\"*\") && path !== \"*\" && !path.endsWith(\"/*\")) {\n warning(false, \"Route path \\\"\" + path + \"\\\" will be treated as if it were \" + (\"\\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\" because the `*` character must \") + \"always follow a `/` in the pattern. To get rid of this warning, \" + (\"please change the route path to \\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\".\"));\n path = path.replace(/\\*$/, \"/*\");\n }\n // ensure `/` is added at the beginning if the path is absolute\n const prefix = path.startsWith(\"/\") ? \"/\" : \"\";\n const stringify = p => p == null ? \"\" : typeof p === \"string\" ? p : String(p);\n const segments = path.split(/\\/+/).map((segment, index, array) => {\n const isLastSegment = index === array.length - 1;\n // only apply the splat if it's the last segment\n if (isLastSegment && segment === \"*\") {\n const star = \"*\";\n // Apply the splat\n return stringify(params[star]);\n }\n const keyMatch = segment.match(/^:(\\w+)(\\??)$/);\n if (keyMatch) {\n const [, key, optional] = keyMatch;\n let param = params[key];\n invariant(optional === \"?\" || param != null, \"Missing \\\":\" + key + \"\\\" param\");\n return stringify(param);\n }\n // Remove any optional markers from optional static segments\n return segment.replace(/\\?$/g, \"\");\n })\n // Remove empty segments\n .filter(segment => !!segment);\n return prefix + segments.join(\"/\");\n}\n/**\n * Performs pattern matching on a URL pathname and returns information about\n * the match.\n *\n * @see https://reactrouter.com/utils/match-path\n */\nfunction matchPath(pattern, pathname) {\n if (typeof pattern === \"string\") {\n pattern = {\n path: pattern,\n caseSensitive: false,\n end: true\n };\n }\n let [matcher, paramNames] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);\n let match = pathname.match(matcher);\n if (!match) return null;\n let matchedPathname = match[0];\n let pathnameBase = matchedPathname.replace(/(.)\\/+$/, \"$1\");\n let captureGroups = match.slice(1);\n let params = paramNames.reduce((memo, paramName, index) => {\n // We need to compute the pathnameBase here using the raw splat value\n // instead of using params[\"*\"] later because it will be decoded then\n if (paramName === \"*\") {\n let splatValue = captureGroups[index] || \"\";\n pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\\/+$/, \"$1\");\n }\n memo[paramName] = safelyDecodeURIComponent(captureGroups[index] || \"\", paramName);\n return memo;\n }, {});\n return {\n params,\n pathname: matchedPathname,\n pathnameBase,\n pattern\n };\n}\nfunction compilePath(path, caseSensitive, end) {\n if (caseSensitive === void 0) {\n caseSensitive = false;\n }\n if (end === void 0) {\n end = true;\n }\n warning(path === \"*\" || !path.endsWith(\"*\") || path.endsWith(\"/*\"), \"Route path \\\"\" + path + \"\\\" will be treated as if it were \" + (\"\\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\" because the `*` character must \") + \"always follow a `/` in the pattern. To get rid of this warning, \" + (\"please change the route path to \\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\".\"));\n let paramNames = [];\n let regexpSource = \"^\" + path.replace(/\\/*\\*?$/, \"\") // Ignore trailing / and /*, we'll handle it below\n .replace(/^\\/*/, \"/\") // Make sure it has a leading /\n .replace(/[\\\\.*+^$?{}|()[\\]]/g, \"\\\\$&\") // Escape special regex chars\n .replace(/\\/:(\\w+)/g, (_, paramName) => {\n paramNames.push(paramName);\n return \"/([^\\\\/]+)\";\n });\n if (path.endsWith(\"*\")) {\n paramNames.push(\"*\");\n regexpSource += path === \"*\" || path === \"/*\" ? \"(.*)$\" // Already matched the initial /, just match the rest\n : \"(?:\\\\/(.+)|\\\\/*)$\"; // Don't include the / in params[\"*\"]\n } else if (end) {\n // When matching to the end, ignore trailing slashes\n regexpSource += \"\\\\/*$\";\n } else if (path !== \"\" && path !== \"/\") {\n // If our path is non-empty and contains anything beyond an initial slash,\n // then we have _some_ form of path in our regex so we should expect to\n // match only if we find the end of this path segment. Look for an optional\n // non-captured trailing slash (to match a portion of the URL) or the end\n // of the path (if we've matched to the end). We used to do this with a\n // word boundary but that gives false positives on routes like\n // /user-preferences since `-` counts as a word boundary.\n regexpSource += \"(?:(?=\\\\/|$))\";\n } else ;\n let matcher = new RegExp(regexpSource, caseSensitive ? undefined : \"i\");\n return [matcher, paramNames];\n}\nfunction safelyDecodeURI(value) {\n try {\n return decodeURI(value);\n } catch (error) {\n warning(false, \"The URL path \\\"\" + value + \"\\\" could not be decoded because it is is a \" + \"malformed URL segment. This is probably due to a bad percent \" + (\"encoding (\" + error + \").\"));\n return value;\n }\n}\nfunction safelyDecodeURIComponent(value, paramName) {\n try {\n return decodeURIComponent(value);\n } catch (error) {\n warning(false, \"The value for the URL param \\\"\" + paramName + \"\\\" will not be decoded because\" + (\" the string \\\"\" + value + \"\\\" is a malformed URL segment. This is probably\") + (\" due to a bad percent encoding (\" + error + \").\"));\n return value;\n }\n}\n/**\n * @private\n */\nfunction stripBasename(pathname, basename) {\n if (basename === \"/\") return pathname;\n if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {\n return null;\n }\n // We want to leave trailing slash behavior in the user's control, so if they\n // specify a basename with a trailing slash, we should support it\n let startIndex = basename.endsWith(\"/\") ? basename.length - 1 : basename.length;\n let nextChar = pathname.charAt(startIndex);\n if (nextChar && nextChar !== \"/\") {\n // pathname does not start with basename/\n return null;\n }\n return pathname.slice(startIndex) || \"/\";\n}\n/**\n * Returns a resolved path object relative to the given pathname.\n *\n * @see https://reactrouter.com/utils/resolve-path\n */\nfunction resolvePath(to, fromPathname) {\n if (fromPathname === void 0) {\n fromPathname = \"/\";\n }\n let {\n pathname: toPathname,\n search = \"\",\n hash = \"\"\n } = typeof to === \"string\" ? parsePath(to) : to;\n let pathname = toPathname ? toPathname.startsWith(\"/\") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname;\n return {\n pathname,\n search: normalizeSearch(search),\n hash: normalizeHash(hash)\n };\n}\nfunction resolvePathname(relativePath, fromPathname) {\n let segments = fromPathname.replace(/\\/+$/, \"\").split(\"/\");\n let relativeSegments = relativePath.split(\"/\");\n relativeSegments.forEach(segment => {\n if (segment === \"..\") {\n // Keep the root \"\" segment so the pathname starts at /\n if (segments.length > 1) segments.pop();\n } else if (segment !== \".\") {\n segments.push(segment);\n }\n });\n return segments.length > 1 ? segments.join(\"/\") : \"/\";\n}\nfunction getInvalidPathError(char, field, dest, path) {\n return \"Cannot include a '\" + char + \"' character in a manually specified \" + (\"`to.\" + field + \"` field [\" + JSON.stringify(path) + \"]. Please separate it out to the \") + (\"`to.\" + dest + \"` field. Alternatively you may provide the full path as \") + \"a string in and the router will parse it for you.\";\n}\n/**\n * @private\n *\n * When processing relative navigation we want to ignore ancestor routes that\n * do not contribute to the path, such that index/pathless layout routes don't\n * interfere.\n *\n * For example, when moving a route element into an index route and/or a\n * pathless layout route, relative link behavior contained within should stay\n * the same. Both of the following examples should link back to the root:\n *\n * \n * \n * \n *\n * \n * \n * }> // <-- Does not contribute\n * // <-- Does not contribute\n * \n * \n */\nfunction getPathContributingMatches(matches) {\n return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);\n}\n/**\n * @private\n */\nfunction resolveTo(toArg, routePathnames, locationPathname, isPathRelative) {\n if (isPathRelative === void 0) {\n isPathRelative = false;\n }\n let to;\n if (typeof toArg === \"string\") {\n to = parsePath(toArg);\n } else {\n to = _extends({}, toArg);\n invariant(!to.pathname || !to.pathname.includes(\"?\"), getInvalidPathError(\"?\", \"pathname\", \"search\", to));\n invariant(!to.pathname || !to.pathname.includes(\"#\"), getInvalidPathError(\"#\", \"pathname\", \"hash\", to));\n invariant(!to.search || !to.search.includes(\"#\"), getInvalidPathError(\"#\", \"search\", \"hash\", to));\n }\n let isEmptyPath = toArg === \"\" || to.pathname === \"\";\n let toPathname = isEmptyPath ? \"/\" : to.pathname;\n let from;\n // Routing is relative to the current pathname if explicitly requested.\n //\n // If a pathname is explicitly provided in `to`, it should be relative to the\n // route context. This is explained in `Note on `` values` in our\n // migration guide from v5 as a means of disambiguation between `to` values\n // that begin with `/` and those that do not. However, this is problematic for\n // `to` values that do not provide a pathname. `to` can simply be a search or\n // hash string, in which case we should assume that the navigation is relative\n // to the current location's pathname and *not* the route pathname.\n if (isPathRelative || toPathname == null) {\n from = locationPathname;\n } else {\n let routePathnameIndex = routePathnames.length - 1;\n if (toPathname.startsWith(\"..\")) {\n let toSegments = toPathname.split(\"/\");\n // Each leading .. segment means \"go up one route\" instead of \"go up one\n // URL segment\". This is a key difference from how works and a\n // major reason we call this a \"to\" value instead of a \"href\".\n while (toSegments[0] === \"..\") {\n toSegments.shift();\n routePathnameIndex -= 1;\n }\n to.pathname = toSegments.join(\"/\");\n }\n // If there are more \"..\" segments than parent routes, resolve relative to\n // the root / URL.\n from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : \"/\";\n }\n let path = resolvePath(to, from);\n // Ensure the pathname has a trailing slash if the original \"to\" had one\n let hasExplicitTrailingSlash = toPathname && toPathname !== \"/\" && toPathname.endsWith(\"/\");\n // Or if this was a link to the current path which has a trailing slash\n let hasCurrentTrailingSlash = (isEmptyPath || toPathname === \".\") && locationPathname.endsWith(\"/\");\n if (!path.pathname.endsWith(\"/\") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) {\n path.pathname += \"/\";\n }\n return path;\n}\n/**\n * @private\n */\nfunction getToPathname(to) {\n // Empty strings should be treated the same as / paths\n return to === \"\" || to.pathname === \"\" ? \"/\" : typeof to === \"string\" ? parsePath(to).pathname : to.pathname;\n}\n/**\n * @private\n */\nconst joinPaths = paths => paths.join(\"/\").replace(/\\/\\/+/g, \"/\");\n/**\n * @private\n */\nconst normalizePathname = pathname => pathname.replace(/\\/+$/, \"\").replace(/^\\/*/, \"/\");\n/**\n * @private\n */\nconst normalizeSearch = search => !search || search === \"?\" ? \"\" : search.startsWith(\"?\") ? search : \"?\" + search;\n/**\n * @private\n */\nconst normalizeHash = hash => !hash || hash === \"#\" ? \"\" : hash.startsWith(\"#\") ? hash : \"#\" + hash;\n/**\n * This is a shortcut for creating `application/json` responses. Converts `data`\n * to JSON and sets the `Content-Type` header.\n */\nconst json = function json(data, init) {\n if (init === void 0) {\n init = {};\n }\n let responseInit = typeof init === \"number\" ? {\n status: init\n } : init;\n let headers = new Headers(responseInit.headers);\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json; charset=utf-8\");\n }\n return new Response(JSON.stringify(data), _extends({}, responseInit, {\n headers\n }));\n};\nclass AbortedDeferredError extends Error {}\nclass DeferredData {\n constructor(data, responseInit) {\n this.pendingKeysSet = new Set();\n this.subscribers = new Set();\n this.deferredKeys = [];\n invariant(data && typeof data === \"object\" && !Array.isArray(data), \"defer() only accepts plain objects\");\n // Set up an AbortController + Promise we can race against to exit early\n // cancellation\n let reject;\n this.abortPromise = new Promise((_, r) => reject = r);\n this.controller = new AbortController();\n let onAbort = () => reject(new AbortedDeferredError(\"Deferred data aborted\"));\n this.unlistenAbortSignal = () => this.controller.signal.removeEventListener(\"abort\", onAbort);\n this.controller.signal.addEventListener(\"abort\", onAbort);\n this.data = Object.entries(data).reduce((acc, _ref) => {\n let [key, value] = _ref;\n return Object.assign(acc, {\n [key]: this.trackPromise(key, value)\n });\n }, {});\n if (this.done) {\n // All incoming values were resolved\n this.unlistenAbortSignal();\n }\n this.init = responseInit;\n }\n trackPromise(key, value) {\n if (!(value instanceof Promise)) {\n return value;\n }\n this.deferredKeys.push(key);\n this.pendingKeysSet.add(key);\n // We store a little wrapper promise that will be extended with\n // _data/_error props upon resolve/reject\n let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error));\n // Register rejection listeners to avoid uncaught promise rejections on\n // errors or aborted deferred values\n promise.catch(() => {});\n Object.defineProperty(promise, \"_tracked\", {\n get: () => true\n });\n return promise;\n }\n onSettle(promise, key, error, data) {\n if (this.controller.signal.aborted && error instanceof AbortedDeferredError) {\n this.unlistenAbortSignal();\n Object.defineProperty(promise, \"_error\", {\n get: () => error\n });\n return Promise.reject(error);\n }\n this.pendingKeysSet.delete(key);\n if (this.done) {\n // Nothing left to abort!\n this.unlistenAbortSignal();\n }\n if (error) {\n Object.defineProperty(promise, \"_error\", {\n get: () => error\n });\n this.emit(false, key);\n return Promise.reject(error);\n }\n Object.defineProperty(promise, \"_data\", {\n get: () => data\n });\n this.emit(false, key);\n return data;\n }\n emit(aborted, settledKey) {\n this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));\n }\n subscribe(fn) {\n this.subscribers.add(fn);\n return () => this.subscribers.delete(fn);\n }\n cancel() {\n this.controller.abort();\n this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));\n this.emit(true);\n }\n async resolveData(signal) {\n let aborted = false;\n if (!this.done) {\n let onAbort = () => this.cancel();\n signal.addEventListener(\"abort\", onAbort);\n aborted = await new Promise(resolve => {\n this.subscribe(aborted => {\n signal.removeEventListener(\"abort\", onAbort);\n if (aborted || this.done) {\n resolve(aborted);\n }\n });\n });\n }\n return aborted;\n }\n get done() {\n return this.pendingKeysSet.size === 0;\n }\n get unwrappedData() {\n invariant(this.data !== null && this.done, \"Can only unwrap data on initialized and settled deferreds\");\n return Object.entries(this.data).reduce((acc, _ref2) => {\n let [key, value] = _ref2;\n return Object.assign(acc, {\n [key]: unwrapTrackedPromise(value)\n });\n }, {});\n }\n get pendingKeys() {\n return Array.from(this.pendingKeysSet);\n }\n}\nfunction isTrackedPromise(value) {\n return value instanceof Promise && value._tracked === true;\n}\nfunction unwrapTrackedPromise(value) {\n if (!isTrackedPromise(value)) {\n return value;\n }\n if (value._error) {\n throw value._error;\n }\n return value._data;\n}\nconst defer = function defer(data, init) {\n if (init === void 0) {\n init = {};\n }\n let responseInit = typeof init === \"number\" ? {\n status: init\n } : init;\n return new DeferredData(data, responseInit);\n};\n/**\n * A redirect response. Sets the status code and the `Location` header.\n * Defaults to \"302 Found\".\n */\nconst redirect = function redirect(url, init) {\n if (init === void 0) {\n init = 302;\n }\n let responseInit = init;\n if (typeof responseInit === \"number\") {\n responseInit = {\n status: responseInit\n };\n } else if (typeof responseInit.status === \"undefined\") {\n responseInit.status = 302;\n }\n let headers = new Headers(responseInit.headers);\n headers.set(\"Location\", url);\n return new Response(null, _extends({}, responseInit, {\n headers\n }));\n};\n/**\n * @private\n * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies\n */\nclass ErrorResponse {\n constructor(status, statusText, data, internal) {\n if (internal === void 0) {\n internal = false;\n }\n this.status = status;\n this.statusText = statusText || \"\";\n this.internal = internal;\n if (data instanceof Error) {\n this.data = data.toString();\n this.error = data;\n } else {\n this.data = data;\n }\n }\n}\n/**\n * Check if the given error is an ErrorResponse generated from a 4xx/5xx\n * Response thrown from an action/loader\n */\nfunction isRouteErrorResponse(error) {\n return error != null && typeof error.status === \"number\" && typeof error.statusText === \"string\" && typeof error.internal === \"boolean\" && \"data\" in error;\n}\n\nconst validMutationMethodsArr = [\"post\", \"put\", \"patch\", \"delete\"];\nconst validMutationMethods = new Set(validMutationMethodsArr);\nconst validRequestMethodsArr = [\"get\", ...validMutationMethodsArr];\nconst validRequestMethods = new Set(validRequestMethodsArr);\nconst redirectStatusCodes = new Set([301, 302, 303, 307, 308]);\nconst redirectPreserveMethodStatusCodes = new Set([307, 308]);\nconst IDLE_NAVIGATION = {\n state: \"idle\",\n location: undefined,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n json: undefined,\n text: undefined\n};\nconst IDLE_FETCHER = {\n state: \"idle\",\n data: undefined,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n json: undefined,\n text: undefined\n};\nconst IDLE_BLOCKER = {\n state: \"unblocked\",\n proceed: undefined,\n reset: undefined,\n location: undefined\n};\nconst ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\nconst defaultMapRouteProperties = route => ({\n hasErrorBoundary: Boolean(route.hasErrorBoundary)\n});\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region createRouter\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Create a router and listen to history POP navigations\n */\nfunction createRouter(init) {\n const routerWindow = init.window ? init.window : typeof window !== \"undefined\" ? window : undefined;\n const isBrowser = typeof routerWindow !== \"undefined\" && typeof routerWindow.document !== \"undefined\" && typeof routerWindow.document.createElement !== \"undefined\";\n const isServer = !isBrowser;\n invariant(init.routes.length > 0, \"You must provide a non-empty routes array to createRouter\");\n let mapRouteProperties;\n if (init.mapRouteProperties) {\n mapRouteProperties = init.mapRouteProperties;\n } else if (init.detectErrorBoundary) {\n // If they are still using the deprecated version, wrap it with the new API\n let detectErrorBoundary = init.detectErrorBoundary;\n mapRouteProperties = route => ({\n hasErrorBoundary: detectErrorBoundary(route)\n });\n } else {\n mapRouteProperties = defaultMapRouteProperties;\n }\n // Routes keyed by ID\n let manifest = {};\n // Routes in tree format for matching\n let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest);\n let inFlightDataRoutes;\n let basename = init.basename || \"/\";\n // Config driven behavior flags\n let future = _extends({\n v7_normalizeFormMethod: false,\n v7_prependBasename: false\n }, init.future);\n // Cleanup function for history\n let unlistenHistory = null;\n // Externally-provided functions to call on all state changes\n let subscribers = new Set();\n // Externally-provided object to hold scroll restoration locations during routing\n let savedScrollPositions = null;\n // Externally-provided function to get scroll restoration keys\n let getScrollRestorationKey = null;\n // Externally-provided function to get current scroll position\n let getScrollPosition = null;\n // One-time flag to control the initial hydration scroll restoration. Because\n // we don't get the saved positions from until _after_\n // the initial render, we need to manually trigger a separate updateState to\n // send along the restoreScrollPosition\n // Set to true if we have `hydrationData` since we assume we were SSR'd and that\n // SSR did the initial scroll restoration.\n let initialScrollRestored = init.hydrationData != null;\n let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);\n let initialErrors = null;\n if (initialMatches == null) {\n // If we do not match a user-provided-route, fall back to the root\n // to allow the error boundary to take over\n let error = getInternalRouterError(404, {\n pathname: init.history.location.pathname\n });\n let {\n matches,\n route\n } = getShortCircuitMatches(dataRoutes);\n initialMatches = matches;\n initialErrors = {\n [route.id]: error\n };\n }\n let initialized =\n // All initialMatches need to be loaded before we're ready. If we have lazy\n // functions around still then we'll need to run them in initialize()\n !initialMatches.some(m => m.route.lazy) && (\n // And we have to either have no loaders or have been provided hydrationData\n !initialMatches.some(m => m.route.loader) || init.hydrationData != null);\n let router;\n let state = {\n historyAction: init.history.action,\n location: init.history.location,\n matches: initialMatches,\n initialized,\n navigation: IDLE_NAVIGATION,\n // Don't restore on initial updateState() if we were SSR'd\n restoreScrollPosition: init.hydrationData != null ? false : null,\n preventScrollReset: false,\n revalidation: \"idle\",\n loaderData: init.hydrationData && init.hydrationData.loaderData || {},\n actionData: init.hydrationData && init.hydrationData.actionData || null,\n errors: init.hydrationData && init.hydrationData.errors || initialErrors,\n fetchers: new Map(),\n blockers: new Map()\n };\n // -- Stateful internal variables to manage navigations --\n // Current navigation in progress (to be committed in completeNavigation)\n let pendingAction = Action.Pop;\n // Should the current navigation prevent the scroll reset if scroll cannot\n // be restored?\n let pendingPreventScrollReset = false;\n // AbortController for the active navigation\n let pendingNavigationController;\n // We use this to avoid touching history in completeNavigation if a\n // revalidation is entirely uninterrupted\n let isUninterruptedRevalidation = false;\n // Use this internal flag to force revalidation of all loaders:\n // - submissions (completed or interrupted)\n // - useRevalidator()\n // - X-Remix-Revalidate (from redirect)\n let isRevalidationRequired = false;\n // Use this internal array to capture routes that require revalidation due\n // to a cancelled deferred on action submission\n let cancelledDeferredRoutes = [];\n // Use this internal array to capture fetcher loads that were cancelled by an\n // action navigation and require revalidation\n let cancelledFetcherLoads = [];\n // AbortControllers for any in-flight fetchers\n let fetchControllers = new Map();\n // Track loads based on the order in which they started\n let incrementingLoadId = 0;\n // Track the outstanding pending navigation data load to be compared against\n // the globally incrementing load when a fetcher load lands after a completed\n // navigation\n let pendingNavigationLoadId = -1;\n // Fetchers that triggered data reloads as a result of their actions\n let fetchReloadIds = new Map();\n // Fetchers that triggered redirect navigations\n let fetchRedirectIds = new Set();\n // Most recent href/match for fetcher.load calls for fetchers\n let fetchLoadMatches = new Map();\n // Store DeferredData instances for active route matches. When a\n // route loader returns defer() we stick one in here. Then, when a nested\n // promise resolves we update loaderData. If a new navigation starts we\n // cancel active deferreds for eliminated routes.\n let activeDeferreds = new Map();\n // Store blocker functions in a separate Map outside of router state since\n // we don't need to update UI state if they change\n let blockerFunctions = new Map();\n // Flag to ignore the next history update, so we can revert the URL change on\n // a POP navigation that was blocked by the user without touching router state\n let ignoreNextHistoryUpdate = false;\n // Initialize the router, all side effects should be kicked off from here.\n // Implemented as a Fluent API for ease of:\n // let router = createRouter(init).initialize();\n function initialize() {\n // If history informs us of a POP navigation, start the navigation but do not update\n // state. We'll update our own state once the navigation completes\n unlistenHistory = init.history.listen(_ref => {\n let {\n action: historyAction,\n location,\n delta\n } = _ref;\n // Ignore this event if it was just us resetting the URL from a\n // blocked POP navigation\n if (ignoreNextHistoryUpdate) {\n ignoreNextHistoryUpdate = false;\n return;\n }\n warning(blockerFunctions.size === 0 || delta != null, \"You are trying to use a blocker on a POP navigation to a location \" + \"that was not created by @remix-run/router. This will fail silently in \" + \"production. This can happen if you are navigating outside the router \" + \"via `window.history.pushState`/`window.location.hash` instead of using \" + \"router navigation APIs. This can also happen if you are using \" + \"createHashRouter and the user manually changes the URL.\");\n let blockerKey = shouldBlockNavigation({\n currentLocation: state.location,\n nextLocation: location,\n historyAction\n });\n if (blockerKey && delta != null) {\n // Restore the URL to match the current UI, but don't update router state\n ignoreNextHistoryUpdate = true;\n init.history.go(delta * -1);\n // Put the blocker into a blocked state\n updateBlocker(blockerKey, {\n state: \"blocked\",\n location,\n proceed() {\n updateBlocker(blockerKey, {\n state: \"proceeding\",\n proceed: undefined,\n reset: undefined,\n location\n });\n // Re-do the same POP navigation we just blocked\n init.history.go(delta);\n },\n reset() {\n let blockers = new Map(state.blockers);\n blockers.set(blockerKey, IDLE_BLOCKER);\n updateState({\n blockers\n });\n }\n });\n return;\n }\n return startNavigation(historyAction, location);\n });\n // Kick off initial data load if needed. Use Pop to avoid modifying history\n // Note we don't do any handling of lazy here. For SPA's it'll get handled\n // in the normal navigation flow. For SSR it's expected that lazy modules are\n // resolved prior to router creation since we can't go into a fallbackElement\n // UI for SSR'd apps\n if (!state.initialized) {\n startNavigation(Action.Pop, state.location);\n }\n return router;\n }\n // Clean up a router and it's side effects\n function dispose() {\n if (unlistenHistory) {\n unlistenHistory();\n }\n subscribers.clear();\n pendingNavigationController && pendingNavigationController.abort();\n state.fetchers.forEach((_, key) => deleteFetcher(key));\n state.blockers.forEach((_, key) => deleteBlocker(key));\n }\n // Subscribe to state updates for the router\n function subscribe(fn) {\n subscribers.add(fn);\n return () => subscribers.delete(fn);\n }\n // Update our state and notify the calling context of the change\n function updateState(newState) {\n state = _extends({}, state, newState);\n subscribers.forEach(subscriber => subscriber(state));\n }\n // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION\n // and setting state.[historyAction/location/matches] to the new route.\n // - Location is a required param\n // - Navigation will always be set to IDLE_NAVIGATION\n // - Can pass any other state in newState\n function completeNavigation(location, newState) {\n var _location$state, _location$state2;\n // Deduce if we're in a loading/actionReload state:\n // - We have committed actionData in the store\n // - The current navigation was a mutation submission\n // - We're past the submitting state and into the loading state\n // - The location being loaded is not the result of a redirect\n let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === \"loading\" && ((_location$state = location.state) == null ? void 0 : _location$state._isRedirect) !== true;\n let actionData;\n if (newState.actionData) {\n if (Object.keys(newState.actionData).length > 0) {\n actionData = newState.actionData;\n } else {\n // Empty actionData -> clear prior actionData due to an action error\n actionData = null;\n }\n } else if (isActionReload) {\n // Keep the current data if we're wrapping up the action reload\n actionData = state.actionData;\n } else {\n // Clear actionData on any other completed navigations\n actionData = null;\n }\n // Always preserve any existing loaderData from re-used routes\n let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;\n // On a successful navigation we can assume we got through all blockers\n // so we can start fresh\n let blockers = new Map();\n blockerFunctions.clear();\n // Always respect the user flag. Otherwise don't reset on mutation\n // submission navigations unless they redirect\n let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;\n if (inFlightDataRoutes) {\n dataRoutes = inFlightDataRoutes;\n inFlightDataRoutes = undefined;\n }\n if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) {\n init.history.push(location, location.state);\n } else if (pendingAction === Action.Replace) {\n init.history.replace(location, location.state);\n }\n updateState(_extends({}, newState, {\n actionData,\n loaderData,\n historyAction: pendingAction,\n location,\n initialized: true,\n navigation: IDLE_NAVIGATION,\n revalidation: \"idle\",\n restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),\n preventScrollReset,\n blockers\n }));\n // Reset stateful navigation vars\n pendingAction = Action.Pop;\n pendingPreventScrollReset = false;\n isUninterruptedRevalidation = false;\n isRevalidationRequired = false;\n cancelledDeferredRoutes = [];\n cancelledFetcherLoads = [];\n }\n // Trigger a navigation event, which can either be a numerical POP or a PUSH\n // replace with an optional submission\n async function navigate(to, opts) {\n if (typeof to === \"number\") {\n init.history.go(to);\n return;\n }\n let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, to, opts == null ? void 0 : opts.fromRouteId, opts == null ? void 0 : opts.relative);\n let {\n path,\n submission,\n error\n } = normalizeNavigateOptions(future.v7_normalizeFormMethod, false, normalizedPath, opts);\n let currentLocation = state.location;\n let nextLocation = createLocation(state.location, path, opts && opts.state);\n // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded\n // URL from window.location, so we need to encode it here so the behavior\n // remains the same as POP and non-data-router usages. new URL() does all\n // the same encoding we'd get from a history.pushState/window.location read\n // without having to touch history\n nextLocation = _extends({}, nextLocation, init.history.encodeLocation(nextLocation));\n let userReplace = opts && opts.replace != null ? opts.replace : undefined;\n let historyAction = Action.Push;\n if (userReplace === true) {\n historyAction = Action.Replace;\n } else if (userReplace === false) ; else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) {\n // By default on submissions to the current location we REPLACE so that\n // users don't have to double-click the back button to get to the prior\n // location. If the user redirects to a different location from the\n // action/loader this will be ignored and the redirect will be a PUSH\n historyAction = Action.Replace;\n }\n let preventScrollReset = opts && \"preventScrollReset\" in opts ? opts.preventScrollReset === true : undefined;\n let blockerKey = shouldBlockNavigation({\n currentLocation,\n nextLocation,\n historyAction\n });\n if (blockerKey) {\n // Put the blocker into a blocked state\n updateBlocker(blockerKey, {\n state: \"blocked\",\n location: nextLocation,\n proceed() {\n updateBlocker(blockerKey, {\n state: \"proceeding\",\n proceed: undefined,\n reset: undefined,\n location: nextLocation\n });\n // Send the same navigation through\n navigate(to, opts);\n },\n reset() {\n let blockers = new Map(state.blockers);\n blockers.set(blockerKey, IDLE_BLOCKER);\n updateState({\n blockers\n });\n }\n });\n return;\n }\n return await startNavigation(historyAction, nextLocation, {\n submission,\n // Send through the formData serialization error if we have one so we can\n // render at the right error boundary after we match routes\n pendingError: error,\n preventScrollReset,\n replace: opts && opts.replace\n });\n }\n // Revalidate all current loaders. If a navigation is in progress or if this\n // is interrupted by a navigation, allow this to \"succeed\" by calling all\n // loaders during the next loader round\n function revalidate() {\n interruptActiveLoads();\n updateState({\n revalidation: \"loading\"\n });\n // If we're currently submitting an action, we don't need to start a new\n // navigation, we'll just let the follow up loader execution call all loaders\n if (state.navigation.state === \"submitting\") {\n return;\n }\n // If we're currently in an idle state, start a new navigation for the current\n // action/location and mark it as uninterrupted, which will skip the history\n // update in completeNavigation\n if (state.navigation.state === \"idle\") {\n startNavigation(state.historyAction, state.location, {\n startUninterruptedRevalidation: true\n });\n return;\n }\n // Otherwise, if we're currently in a loading state, just start a new\n // navigation to the navigation.location but do not trigger an uninterrupted\n // revalidation so that history correctly updates once the navigation completes\n startNavigation(pendingAction || state.historyAction, state.navigation.location, {\n overrideNavigation: state.navigation\n });\n }\n // Start a navigation to the given action/location. Can optionally provide a\n // overrideNavigation which will override the normalLoad in the case of a redirect\n // navigation\n async function startNavigation(historyAction, location, opts) {\n // Abort any in-progress navigations and start a new one. Unset any ongoing\n // uninterrupted revalidations unless told otherwise, since we want this\n // new navigation to update history normally\n pendingNavigationController && pendingNavigationController.abort();\n pendingNavigationController = null;\n pendingAction = historyAction;\n isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true;\n // Save the current scroll position every time we start a new navigation,\n // and track whether we should reset scroll on completion\n saveScrollPosition(state.location, state.matches);\n pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let loadingNavigation = opts && opts.overrideNavigation;\n let matches = matchRoutes(routesToUse, location, basename);\n // Short circuit with a 404 on the root error boundary if we match nothing\n if (!matches) {\n let error = getInternalRouterError(404, {\n pathname: location.pathname\n });\n let {\n matches: notFoundMatches,\n route\n } = getShortCircuitMatches(routesToUse);\n // Cancel all pending deferred on 404s since we don't keep any routes\n cancelActiveDeferreds();\n completeNavigation(location, {\n matches: notFoundMatches,\n loaderData: {},\n errors: {\n [route.id]: error\n }\n });\n return;\n }\n // Short circuit if it's only a hash change and not a revalidation or\n // mutation submission.\n //\n // Ignore on initial page loads because since the initial load will always\n // be \"same hash\". For example, on /page#hash and submit a
\n // which will default to a navigation to /page\n if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {\n completeNavigation(location, {\n matches\n });\n return;\n }\n // Create a controller/Request for this navigation\n pendingNavigationController = new AbortController();\n let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);\n let pendingActionData;\n let pendingError;\n if (opts && opts.pendingError) {\n // If we have a pendingError, it means the user attempted a GET submission\n // with binary FormData so assign here and skip to handleLoaders. That\n // way we handle calling loaders above the boundary etc. It's not really\n // different from an actionError in that sense.\n pendingError = {\n [findNearestBoundary(matches).route.id]: opts.pendingError\n };\n } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {\n // Call action if we received an action submission\n let actionOutput = await handleAction(request, location, opts.submission, matches, {\n replace: opts.replace\n });\n if (actionOutput.shortCircuited) {\n return;\n }\n pendingActionData = actionOutput.pendingActionData;\n pendingError = actionOutput.pendingActionError;\n loadingNavigation = getLoadingNavigation(location, opts.submission);\n // Create a GET request for the loaders\n request = new Request(request.url, {\n signal: request.signal\n });\n }\n // Call loaders\n let {\n shortCircuited,\n loaderData,\n errors\n } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, pendingActionData, pendingError);\n if (shortCircuited) {\n return;\n }\n // Clean up now that the action/loaders have completed. Don't clean up if\n // we short circuited because pendingNavigationController will have already\n // been assigned to a new controller for the next navigation\n pendingNavigationController = null;\n completeNavigation(location, _extends({\n matches\n }, pendingActionData ? {\n actionData: pendingActionData\n } : {}, {\n loaderData,\n errors\n }));\n }\n // Call the action matched by the leaf route for this navigation and handle\n // redirects/errors\n async function handleAction(request, location, submission, matches, opts) {\n if (opts === void 0) {\n opts = {};\n }\n interruptActiveLoads();\n // Put us in a submitting state\n let navigation = getSubmittingNavigation(location, submission);\n updateState({\n navigation\n });\n // Call our action and get the result\n let result;\n let actionMatch = getTargetMatch(matches, location);\n if (!actionMatch.route.action && !actionMatch.route.lazy) {\n result = {\n type: ResultType.error,\n error: getInternalRouterError(405, {\n method: request.method,\n pathname: location.pathname,\n routeId: actionMatch.route.id\n })\n };\n } else {\n result = await callLoaderOrAction(\"action\", request, actionMatch, matches, manifest, mapRouteProperties, basename);\n if (request.signal.aborted) {\n return {\n shortCircuited: true\n };\n }\n }\n if (isRedirectResult(result)) {\n let replace;\n if (opts && opts.replace != null) {\n replace = opts.replace;\n } else {\n // If the user didn't explicity indicate replace behavior, replace if\n // we redirected to the exact same location we're currently at to avoid\n // double back-buttons\n replace = result.location === state.location.pathname + state.location.search;\n }\n await startRedirectNavigation(state, result, {\n submission,\n replace\n });\n return {\n shortCircuited: true\n };\n }\n if (isErrorResult(result)) {\n // Store off the pending error - we use it to determine which loaders\n // to call and will commit it when we complete the navigation\n let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);\n // By default, all submissions are REPLACE navigations, but if the\n // action threw an error that'll be rendered in an errorElement, we fall\n // back to PUSH so that the user can use the back button to get back to\n // the pre-submission form location to try again\n if ((opts && opts.replace) !== true) {\n pendingAction = Action.Push;\n }\n return {\n // Send back an empty object we can use to clear out any prior actionData\n pendingActionData: {},\n pendingActionError: {\n [boundaryMatch.route.id]: result.error\n }\n };\n }\n if (isDeferredResult(result)) {\n throw getInternalRouterError(400, {\n type: \"defer-action\"\n });\n }\n return {\n pendingActionData: {\n [actionMatch.route.id]: result.data\n }\n };\n }\n // Call all applicable loaders for the given matches, handling redirects,\n // errors, etc.\n async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, pendingActionData, pendingError) {\n // Figure out the right navigation we want to use for data loading\n let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission);\n // If this was a redirect from an action we don't have a \"submission\" but\n // we have it on the loading navigation so use that if available\n let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError);\n // Cancel pending deferreds for no-longer-matched routes or routes we're\n // about to reload. Note that if this is an action reload we would have\n // already cancelled all pending deferreds so this would be a no-op\n cancelActiveDeferreds(routeId => !(matches && matches.some(m => m.route.id === routeId)) || matchesToLoad && matchesToLoad.some(m => m.route.id === routeId));\n // Short circuit if we have no loaders to run\n if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) {\n let updatedFetchers = markFetchRedirectsDone();\n completeNavigation(location, _extends({\n matches,\n loaderData: {},\n // Commit pending error if we're short circuiting\n errors: pendingError || null\n }, pendingActionData ? {\n actionData: pendingActionData\n } : {}, updatedFetchers ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n return {\n shortCircuited: true\n };\n }\n // If this is an uninterrupted revalidation, we remain in our current idle\n // state. If not, we need to switch to our loading state and load data,\n // preserving any new action data or existing action data (in the case of\n // a revalidation interrupting an actionReload)\n if (!isUninterruptedRevalidation) {\n revalidatingFetchers.forEach(rf => {\n let fetcher = state.fetchers.get(rf.key);\n let revalidatingFetcher = getLoadingFetcher(undefined, fetcher ? fetcher.data : undefined);\n state.fetchers.set(rf.key, revalidatingFetcher);\n });\n let actionData = pendingActionData || state.actionData;\n updateState(_extends({\n navigation: loadingNavigation\n }, actionData ? Object.keys(actionData).length === 0 ? {\n actionData: null\n } : {\n actionData\n } : {}, revalidatingFetchers.length > 0 ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n }\n pendingNavigationLoadId = ++incrementingLoadId;\n revalidatingFetchers.forEach(rf => {\n if (fetchControllers.has(rf.key)) {\n abortFetcher(rf.key);\n }\n if (rf.controller) {\n // Fetchers use an independent AbortController so that aborting a fetcher\n // (via deleteFetcher) does not abort the triggering navigation that\n // triggered the revalidation\n fetchControllers.set(rf.key, rf.controller);\n }\n });\n // Proxy navigation abort through to revalidation fetchers\n let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(f => abortFetcher(f.key));\n if (pendingNavigationController) {\n pendingNavigationController.signal.addEventListener(\"abort\", abortPendingFetchRevalidations);\n }\n let {\n results,\n loaderResults,\n fetcherResults\n } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);\n if (request.signal.aborted) {\n return {\n shortCircuited: true\n };\n }\n // Clean up _after_ loaders have completed. Don't clean up if we short\n // circuited because fetchControllers would have been aborted and\n // reassigned to new controllers for the next navigation\n if (pendingNavigationController) {\n pendingNavigationController.signal.removeEventListener(\"abort\", abortPendingFetchRevalidations);\n }\n revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key));\n // If any loaders returned a redirect Response, start a new REPLACE navigation\n let redirect = findRedirect(results);\n if (redirect) {\n await startRedirectNavigation(state, redirect, {\n replace\n });\n return {\n shortCircuited: true\n };\n }\n // Process and commit output from loaders\n let {\n loaderData,\n errors\n } = processLoaderData(state, matches, matchesToLoad, loaderResults, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds);\n // Wire up subscribers to update loaderData as promises settle\n activeDeferreds.forEach((deferredData, routeId) => {\n deferredData.subscribe(aborted => {\n // Note: No need to updateState here since the TrackedPromise on\n // loaderData is stable across resolve/reject\n // Remove this instance if we were aborted or if promises have settled\n if (aborted || deferredData.done) {\n activeDeferreds.delete(routeId);\n }\n });\n });\n let updatedFetchers = markFetchRedirectsDone();\n let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);\n let shouldUpdateFetchers = updatedFetchers || didAbortFetchLoads || revalidatingFetchers.length > 0;\n return _extends({\n loaderData,\n errors\n }, shouldUpdateFetchers ? {\n fetchers: new Map(state.fetchers)\n } : {});\n }\n function getFetcher(key) {\n return state.fetchers.get(key) || IDLE_FETCHER;\n }\n // Trigger a fetcher load/submit for the given fetcher key\n function fetch(key, routeId, href, opts) {\n if (isServer) {\n throw new Error(\"router.fetch() was called during the server render, but it shouldn't be. \" + \"You are likely calling a useFetcher() method in the body of your component. \" + \"Try moving it to a useEffect or a callback.\");\n }\n if (fetchControllers.has(key)) abortFetcher(key);\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, routeId, opts == null ? void 0 : opts.relative);\n let matches = matchRoutes(routesToUse, normalizedPath, basename);\n if (!matches) {\n setFetcherError(key, routeId, getInternalRouterError(404, {\n pathname: normalizedPath\n }));\n return;\n }\n let {\n path,\n submission,\n error\n } = normalizeNavigateOptions(future.v7_normalizeFormMethod, true, normalizedPath, opts);\n if (error) {\n setFetcherError(key, routeId, error);\n return;\n }\n let match = getTargetMatch(matches, path);\n pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;\n if (submission && isMutationMethod(submission.formMethod)) {\n handleFetcherAction(key, routeId, path, match, matches, submission);\n return;\n }\n // Store off the match so we can call it's shouldRevalidate on subsequent\n // revalidations\n fetchLoadMatches.set(key, {\n routeId,\n path\n });\n handleFetcherLoader(key, routeId, path, match, matches, submission);\n }\n // Call the action for the matched fetcher.submit(), and then handle redirects,\n // errors, and revalidation\n async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) {\n interruptActiveLoads();\n fetchLoadMatches.delete(key);\n if (!match.route.action && !match.route.lazy) {\n let error = getInternalRouterError(405, {\n method: submission.formMethod,\n pathname: path,\n routeId: routeId\n });\n setFetcherError(key, routeId, error);\n return;\n }\n // Put this fetcher into it's submitting state\n let existingFetcher = state.fetchers.get(key);\n let fetcher = getSubmittingFetcher(submission, existingFetcher);\n state.fetchers.set(key, fetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n // Call the action for the fetcher\n let abortController = new AbortController();\n let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);\n fetchControllers.set(key, abortController);\n let actionResult = await callLoaderOrAction(\"action\", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);\n if (fetchRequest.signal.aborted) {\n // We can delete this so long as we weren't aborted by ou our own fetcher\n // re-submit which would have put _new_ controller is in fetchControllers\n if (fetchControllers.get(key) === abortController) {\n fetchControllers.delete(key);\n }\n return;\n }\n if (isRedirectResult(actionResult)) {\n fetchControllers.delete(key);\n fetchRedirectIds.add(key);\n let loadingFetcher = getLoadingFetcher(submission);\n state.fetchers.set(key, loadingFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n return startRedirectNavigation(state, actionResult, {\n submission,\n isFetchActionRedirect: true\n });\n }\n // Process any non-redirect errors thrown\n if (isErrorResult(actionResult)) {\n setFetcherError(key, routeId, actionResult.error);\n return;\n }\n if (isDeferredResult(actionResult)) {\n throw getInternalRouterError(400, {\n type: \"defer-action\"\n });\n }\n // Start the data load for current matches, or the next location if we're\n // in the middle of a navigation\n let nextLocation = state.navigation.location || state.location;\n let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let matches = state.navigation.state !== \"idle\" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches;\n invariant(matches, \"Didn't find any matches after fetcher action\");\n let loadId = ++incrementingLoadId;\n fetchReloadIds.set(key, loadId);\n let loadFetcher = getLoadingFetcher(submission, actionResult.data);\n state.fetchers.set(key, loadFetcher);\n let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, {\n [match.route.id]: actionResult.data\n }, undefined // No need to send through errors since we short circuit above\n );\n // Put all revalidating fetchers into the loading state, except for the\n // current fetcher which we want to keep in it's current loading state which\n // contains it's action submission info + action data\n revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => {\n let staleKey = rf.key;\n let existingFetcher = state.fetchers.get(staleKey);\n let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined);\n state.fetchers.set(staleKey, revalidatingFetcher);\n if (fetchControllers.has(staleKey)) {\n abortFetcher(staleKey);\n }\n if (rf.controller) {\n fetchControllers.set(staleKey, rf.controller);\n }\n });\n updateState({\n fetchers: new Map(state.fetchers)\n });\n let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(rf => abortFetcher(rf.key));\n abortController.signal.addEventListener(\"abort\", abortPendingFetchRevalidations);\n let {\n results,\n loaderResults,\n fetcherResults\n } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);\n if (abortController.signal.aborted) {\n return;\n }\n abortController.signal.removeEventListener(\"abort\", abortPendingFetchRevalidations);\n fetchReloadIds.delete(key);\n fetchControllers.delete(key);\n revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));\n let redirect = findRedirect(results);\n if (redirect) {\n return startRedirectNavigation(state, redirect);\n }\n // Process and commit output from loaders\n let {\n loaderData,\n errors\n } = processLoaderData(state, state.matches, matchesToLoad, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);\n // Since we let revalidations complete even if the submitting fetcher was\n // deleted, only put it back to idle if it hasn't been deleted\n if (state.fetchers.has(key)) {\n let doneFetcher = getDoneFetcher(actionResult.data);\n state.fetchers.set(key, doneFetcher);\n }\n let didAbortFetchLoads = abortStaleFetchLoads(loadId);\n // If we are currently in a navigation loading state and this fetcher is\n // more recent than the navigation, we want the newer data so abort the\n // navigation and complete it with the fetcher data\n if (state.navigation.state === \"loading\" && loadId > pendingNavigationLoadId) {\n invariant(pendingAction, \"Expected pending action\");\n pendingNavigationController && pendingNavigationController.abort();\n completeNavigation(state.navigation.location, {\n matches,\n loaderData,\n errors,\n fetchers: new Map(state.fetchers)\n });\n } else {\n // otherwise just update with the fetcher data, preserving any existing\n // loaderData for loaders that did not need to reload. We have to\n // manually merge here since we aren't going through completeNavigation\n updateState(_extends({\n errors,\n loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors)\n }, didAbortFetchLoads || revalidatingFetchers.length > 0 ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n isRevalidationRequired = false;\n }\n }\n // Call the matched loader for fetcher.load(), handling redirects, errors, etc.\n async function handleFetcherLoader(key, routeId, path, match, matches, submission) {\n let existingFetcher = state.fetchers.get(key);\n // Put this fetcher into it's loading state\n let loadingFetcher = getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined);\n state.fetchers.set(key, loadingFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n // Call the loader for this fetcher route match\n let abortController = new AbortController();\n let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);\n fetchControllers.set(key, abortController);\n let result = await callLoaderOrAction(\"loader\", fetchRequest, match, matches, manifest, mapRouteProperties, basename);\n // Deferred isn't supported for fetcher loads, await everything and treat it\n // as a normal load. resolveDeferredData will return undefined if this\n // fetcher gets aborted, so we just leave result untouched and short circuit\n // below if that happens\n if (isDeferredResult(result)) {\n result = (await resolveDeferredData(result, fetchRequest.signal, true)) || result;\n }\n // We can delete this so long as we weren't aborted by our our own fetcher\n // re-load which would have put _new_ controller is in fetchControllers\n if (fetchControllers.get(key) === abortController) {\n fetchControllers.delete(key);\n }\n if (fetchRequest.signal.aborted) {\n return;\n }\n // If the loader threw a redirect Response, start a new REPLACE navigation\n if (isRedirectResult(result)) {\n fetchRedirectIds.add(key);\n await startRedirectNavigation(state, result);\n return;\n }\n // Process any non-redirect errors thrown\n if (isErrorResult(result)) {\n let boundaryMatch = findNearestBoundary(state.matches, routeId);\n state.fetchers.delete(key);\n // TODO: In remix, this would reset to IDLE_NAVIGATION if it was a catch -\n // do we need to behave any differently with our non-redirect errors?\n // What if it was a non-redirect Response?\n updateState({\n fetchers: new Map(state.fetchers),\n errors: {\n [boundaryMatch.route.id]: result.error\n }\n });\n return;\n }\n invariant(!isDeferredResult(result), \"Unhandled fetcher deferred data\");\n // Put the fetcher back into an idle state\n let doneFetcher = getDoneFetcher(result.data);\n state.fetchers.set(key, doneFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n }\n /**\n * Utility function to handle redirects returned from an action or loader.\n * Normally, a redirect \"replaces\" the navigation that triggered it. So, for\n * example:\n *\n * - user is on /a\n * - user clicks a link to /b\n * - loader for /b redirects to /c\n *\n * In a non-JS app the browser would track the in-flight navigation to /b and\n * then replace it with /c when it encountered the redirect response. In\n * the end it would only ever update the URL bar with /c.\n *\n * In client-side routing using pushState/replaceState, we aim to emulate\n * this behavior and we also do not update history until the end of the\n * navigation (including processed redirects). This means that we never\n * actually touch history until we've processed redirects, so we just use\n * the history action from the original navigation (PUSH or REPLACE).\n */\n async function startRedirectNavigation(state, redirect, _temp) {\n let {\n submission,\n replace,\n isFetchActionRedirect\n } = _temp === void 0 ? {} : _temp;\n if (redirect.revalidate) {\n isRevalidationRequired = true;\n }\n let redirectLocation = createLocation(state.location, redirect.location, // TODO: This can be removed once we get rid of useTransition in Remix v2\n _extends({\n _isRedirect: true\n }, isFetchActionRedirect ? {\n _isFetchActionRedirect: true\n } : {}));\n invariant(redirectLocation, \"Expected a location on the redirect navigation\");\n // Check if this an absolute external redirect that goes to a new origin\n if (ABSOLUTE_URL_REGEX.test(redirect.location) && isBrowser) {\n let url = init.history.createURL(redirect.location);\n let isDifferentBasename = stripBasename(url.pathname, basename) == null;\n if (routerWindow.location.origin !== url.origin || isDifferentBasename) {\n if (replace) {\n routerWindow.location.replace(redirect.location);\n } else {\n routerWindow.location.assign(redirect.location);\n }\n return;\n }\n }\n // There's no need to abort on redirects, since we don't detect the\n // redirect until the action/loaders have settled\n pendingNavigationController = null;\n let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;\n // Use the incoming submission if provided, fallback on the active one in\n // state.navigation\n let activeSubmission = submission || getSubmissionFromNavigation(state.navigation);\n // If this was a 307/308 submission we want to preserve the HTTP method and\n // re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the\n // redirected location\n if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {\n await startNavigation(redirectHistoryAction, redirectLocation, {\n submission: _extends({}, activeSubmission, {\n formAction: redirect.location\n }),\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n } else if (isFetchActionRedirect) {\n // For a fetch action redirect, we kick off a new loading navigation\n // without the fetcher submission, but we send it along for shouldRevalidate\n await startNavigation(redirectHistoryAction, redirectLocation, {\n overrideNavigation: getLoadingNavigation(redirectLocation),\n fetcherSubmission: activeSubmission,\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n } else {\n // If we have a submission, we will preserve it through the redirect navigation\n let overrideNavigation = getLoadingNavigation(redirectLocation, activeSubmission);\n await startNavigation(redirectHistoryAction, redirectLocation, {\n overrideNavigation,\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n }\n }\n async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {\n // Call all navigation loaders and revalidating fetcher loaders in parallel,\n // then slice off the results into separate arrays so we can handle them\n // accordingly\n let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction(\"loader\", request, match, matches, manifest, mapRouteProperties, basename)), ...fetchersToLoad.map(f => {\n if (f.matches && f.match && f.controller) {\n return callLoaderOrAction(\"loader\", createClientSideRequest(init.history, f.path, f.controller.signal), f.match, f.matches, manifest, mapRouteProperties, basename);\n } else {\n let error = {\n type: ResultType.error,\n error: getInternalRouterError(404, {\n pathname: f.path\n })\n };\n return error;\n }\n })]);\n let loaderResults = results.slice(0, matchesToLoad.length);\n let fetcherResults = results.slice(matchesToLoad.length);\n await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]);\n return {\n results,\n loaderResults,\n fetcherResults\n };\n }\n function interruptActiveLoads() {\n // Every interruption triggers a revalidation\n isRevalidationRequired = true;\n // Cancel pending route-level deferreds and mark cancelled routes for\n // revalidation\n cancelledDeferredRoutes.push(...cancelActiveDeferreds());\n // Abort in-flight fetcher loads\n fetchLoadMatches.forEach((_, key) => {\n if (fetchControllers.has(key)) {\n cancelledFetcherLoads.push(key);\n abortFetcher(key);\n }\n });\n }\n function setFetcherError(key, routeId, error) {\n let boundaryMatch = findNearestBoundary(state.matches, routeId);\n deleteFetcher(key);\n updateState({\n errors: {\n [boundaryMatch.route.id]: error\n },\n fetchers: new Map(state.fetchers)\n });\n }\n function deleteFetcher(key) {\n let fetcher = state.fetchers.get(key);\n // Don't abort the controller if this is a deletion of a fetcher.submit()\n // in it's loading phase since - we don't want to abort the corresponding\n // revalidation and want them to complete and land\n if (fetchControllers.has(key) && !(fetcher && fetcher.state === \"loading\" && fetchReloadIds.has(key))) {\n abortFetcher(key);\n }\n fetchLoadMatches.delete(key);\n fetchReloadIds.delete(key);\n fetchRedirectIds.delete(key);\n state.fetchers.delete(key);\n }\n function abortFetcher(key) {\n let controller = fetchControllers.get(key);\n invariant(controller, \"Expected fetch controller: \" + key);\n controller.abort();\n fetchControllers.delete(key);\n }\n function markFetchersDone(keys) {\n for (let key of keys) {\n let fetcher = getFetcher(key);\n let doneFetcher = getDoneFetcher(fetcher.data);\n state.fetchers.set(key, doneFetcher);\n }\n }\n function markFetchRedirectsDone() {\n let doneKeys = [];\n let updatedFetchers = false;\n for (let key of fetchRedirectIds) {\n let fetcher = state.fetchers.get(key);\n invariant(fetcher, \"Expected fetcher: \" + key);\n if (fetcher.state === \"loading\") {\n fetchRedirectIds.delete(key);\n doneKeys.push(key);\n updatedFetchers = true;\n }\n }\n markFetchersDone(doneKeys);\n return updatedFetchers;\n }\n function abortStaleFetchLoads(landedId) {\n let yeetedKeys = [];\n for (let [key, id] of fetchReloadIds) {\n if (id < landedId) {\n let fetcher = state.fetchers.get(key);\n invariant(fetcher, \"Expected fetcher: \" + key);\n if (fetcher.state === \"loading\") {\n abortFetcher(key);\n fetchReloadIds.delete(key);\n yeetedKeys.push(key);\n }\n }\n }\n markFetchersDone(yeetedKeys);\n return yeetedKeys.length > 0;\n }\n function getBlocker(key, fn) {\n let blocker = state.blockers.get(key) || IDLE_BLOCKER;\n if (blockerFunctions.get(key) !== fn) {\n blockerFunctions.set(key, fn);\n }\n return blocker;\n }\n function deleteBlocker(key) {\n state.blockers.delete(key);\n blockerFunctions.delete(key);\n }\n // Utility function to update blockers, ensuring valid state transitions\n function updateBlocker(key, newBlocker) {\n let blocker = state.blockers.get(key) || IDLE_BLOCKER;\n // Poor mans state machine :)\n // https://mermaid.live/edit#pako:eNqVkc9OwzAMxl8l8nnjAYrEtDIOHEBIgwvKJTReGy3_lDpIqO27k6awMG0XcrLlnz87nwdonESogKXXBuE79rq75XZO3-yHds0RJVuv70YrPlUrCEe2HfrORS3rubqZfuhtpg5C9wk5tZ4VKcRUq88q9Z8RS0-48cE1iHJkL0ugbHuFLus9L6spZy8nX9MP2CNdomVaposqu3fGayT8T8-jJQwhepo_UtpgBQaDEUom04dZhAN1aJBDlUKJBxE1ceB2Smj0Mln-IBW5AFU2dwUiktt_2Qaq2dBfaKdEup85UV7Yd-dKjlnkabl2Pvr0DTkTreM\n invariant(blocker.state === \"unblocked\" && newBlocker.state === \"blocked\" || blocker.state === \"blocked\" && newBlocker.state === \"blocked\" || blocker.state === \"blocked\" && newBlocker.state === \"proceeding\" || blocker.state === \"blocked\" && newBlocker.state === \"unblocked\" || blocker.state === \"proceeding\" && newBlocker.state === \"unblocked\", \"Invalid blocker state transition: \" + blocker.state + \" -> \" + newBlocker.state);\n let blockers = new Map(state.blockers);\n blockers.set(key, newBlocker);\n updateState({\n blockers\n });\n }\n function shouldBlockNavigation(_ref2) {\n let {\n currentLocation,\n nextLocation,\n historyAction\n } = _ref2;\n if (blockerFunctions.size === 0) {\n return;\n }\n // We ony support a single active blocker at the moment since we don't have\n // any compelling use cases for multi-blocker yet\n if (blockerFunctions.size > 1) {\n warning(false, \"A router only supports one blocker at a time\");\n }\n let entries = Array.from(blockerFunctions.entries());\n let [blockerKey, blockerFunction] = entries[entries.length - 1];\n let blocker = state.blockers.get(blockerKey);\n if (blocker && blocker.state === \"proceeding\") {\n // If the blocker is currently proceeding, we don't need to re-check\n // it and can let this navigation continue\n return;\n }\n // At this point, we know we're unblocked/blocked so we need to check the\n // user-provided blocker function\n if (blockerFunction({\n currentLocation,\n nextLocation,\n historyAction\n })) {\n return blockerKey;\n }\n }\n function cancelActiveDeferreds(predicate) {\n let cancelledRouteIds = [];\n activeDeferreds.forEach((dfd, routeId) => {\n if (!predicate || predicate(routeId)) {\n // Cancel the deferred - but do not remove from activeDeferreds here -\n // we rely on the subscribers to do that so our tests can assert proper\n // cleanup via _internalActiveDeferreds\n dfd.cancel();\n cancelledRouteIds.push(routeId);\n activeDeferreds.delete(routeId);\n }\n });\n return cancelledRouteIds;\n }\n // Opt in to capturing and reporting scroll positions during navigations,\n // used by the component\n function enableScrollRestoration(positions, getPosition, getKey) {\n savedScrollPositions = positions;\n getScrollPosition = getPosition;\n getScrollRestorationKey = getKey || null;\n // Perform initial hydration scroll restoration, since we miss the boat on\n // the initial updateState() because we've not yet rendered \n // and therefore have no savedScrollPositions available\n if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) {\n initialScrollRestored = true;\n let y = getSavedScrollPosition(state.location, state.matches);\n if (y != null) {\n updateState({\n restoreScrollPosition: y\n });\n }\n }\n return () => {\n savedScrollPositions = null;\n getScrollPosition = null;\n getScrollRestorationKey = null;\n };\n }\n function getScrollKey(location, matches) {\n if (getScrollRestorationKey) {\n let key = getScrollRestorationKey(location, matches.map(m => createUseMatchesMatch(m, state.loaderData)));\n return key || location.key;\n }\n return location.key;\n }\n function saveScrollPosition(location, matches) {\n if (savedScrollPositions && getScrollPosition) {\n let key = getScrollKey(location, matches);\n savedScrollPositions[key] = getScrollPosition();\n }\n }\n function getSavedScrollPosition(location, matches) {\n if (savedScrollPositions) {\n let key = getScrollKey(location, matches);\n let y = savedScrollPositions[key];\n if (typeof y === \"number\") {\n return y;\n }\n }\n return null;\n }\n function _internalSetRoutes(newRoutes) {\n manifest = {};\n inFlightDataRoutes = convertRoutesToDataRoutes(newRoutes, mapRouteProperties, undefined, manifest);\n }\n router = {\n get basename() {\n return basename;\n },\n get state() {\n return state;\n },\n get routes() {\n return dataRoutes;\n },\n initialize,\n subscribe,\n enableScrollRestoration,\n navigate,\n fetch,\n revalidate,\n // Passthrough to history-aware createHref used by useHref so we get proper\n // hash-aware URLs in DOM paths\n createHref: to => init.history.createHref(to),\n encodeLocation: to => init.history.encodeLocation(to),\n getFetcher,\n deleteFetcher,\n dispose,\n getBlocker,\n deleteBlocker,\n _internalFetchControllers: fetchControllers,\n _internalActiveDeferreds: activeDeferreds,\n // TODO: Remove setRoutes, it's temporary to avoid dealing with\n // updating the tree while validating the update algorithm.\n _internalSetRoutes\n };\n return router;\n}\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region createStaticHandler\n////////////////////////////////////////////////////////////////////////////////\nconst UNSAFE_DEFERRED_SYMBOL = Symbol(\"deferred\");\nfunction createStaticHandler(routes, opts) {\n invariant(routes.length > 0, \"You must provide a non-empty routes array to createStaticHandler\");\n let manifest = {};\n let basename = (opts ? opts.basename : null) || \"/\";\n let mapRouteProperties;\n if (opts != null && opts.mapRouteProperties) {\n mapRouteProperties = opts.mapRouteProperties;\n } else if (opts != null && opts.detectErrorBoundary) {\n // If they are still using the deprecated version, wrap it with the new API\n let detectErrorBoundary = opts.detectErrorBoundary;\n mapRouteProperties = route => ({\n hasErrorBoundary: detectErrorBoundary(route)\n });\n } else {\n mapRouteProperties = defaultMapRouteProperties;\n }\n let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest);\n /**\n * The query() method is intended for document requests, in which we want to\n * call an optional action and potentially multiple loaders for all nested\n * routes. It returns a StaticHandlerContext object, which is very similar\n * to the router state (location, loaderData, actionData, errors, etc.) and\n * also adds SSR-specific information such as the statusCode and headers\n * from action/loaders Responses.\n *\n * It _should_ never throw and should report all errors through the\n * returned context.errors object, properly associating errors to their error\n * boundary. Additionally, it tracks _deepestRenderedBoundaryId which can be\n * used to emulate React error boundaries during SSr by performing a second\n * pass only down to the boundaryId.\n *\n * The one exception where we do not return a StaticHandlerContext is when a\n * redirect response is returned or thrown from any action/loader. We\n * propagate that out and return the raw Response so the HTTP server can\n * return it directly.\n */\n async function query(request, _temp2) {\n let {\n requestContext\n } = _temp2 === void 0 ? {} : _temp2;\n let url = new URL(request.url);\n let method = request.method;\n let location = createLocation(\"\", createPath(url), null, \"default\");\n let matches = matchRoutes(dataRoutes, location, basename);\n // SSR supports HEAD requests while SPA doesn't\n if (!isValidMethod(method) && method !== \"HEAD\") {\n let error = getInternalRouterError(405, {\n method\n });\n let {\n matches: methodNotAllowedMatches,\n route\n } = getShortCircuitMatches(dataRoutes);\n return {\n basename,\n location,\n matches: methodNotAllowedMatches,\n loaderData: {},\n actionData: null,\n errors: {\n [route.id]: error\n },\n statusCode: error.status,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n } else if (!matches) {\n let error = getInternalRouterError(404, {\n pathname: location.pathname\n });\n let {\n matches: notFoundMatches,\n route\n } = getShortCircuitMatches(dataRoutes);\n return {\n basename,\n location,\n matches: notFoundMatches,\n loaderData: {},\n actionData: null,\n errors: {\n [route.id]: error\n },\n statusCode: error.status,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n }\n let result = await queryImpl(request, location, matches, requestContext);\n if (isResponse(result)) {\n return result;\n }\n // When returning StaticHandlerContext, we patch back in the location here\n // since we need it for React Context. But this helps keep our submit and\n // loadRouteData operating on a Request instead of a Location\n return _extends({\n location,\n basename\n }, result);\n }\n /**\n * The queryRoute() method is intended for targeted route requests, either\n * for fetch ?_data requests or resource route requests. In this case, we\n * are only ever calling a single action or loader, and we are returning the\n * returned value directly. In most cases, this will be a Response returned\n * from the action/loader, but it may be a primitive or other value as well -\n * and in such cases the calling context should handle that accordingly.\n *\n * We do respect the throw/return differentiation, so if an action/loader\n * throws, then this method will throw the value. This is important so we\n * can do proper boundary identification in Remix where a thrown Response\n * must go to the Catch Boundary but a returned Response is happy-path.\n *\n * One thing to note is that any Router-initiated Errors that make sense\n * to associate with a status code will be thrown as an ErrorResponse\n * instance which include the raw Error, such that the calling context can\n * serialize the error as they see fit while including the proper response\n * code. Examples here are 404 and 405 errors that occur prior to reaching\n * any user-defined loaders.\n */\n async function queryRoute(request, _temp3) {\n let {\n routeId,\n requestContext\n } = _temp3 === void 0 ? {} : _temp3;\n let url = new URL(request.url);\n let method = request.method;\n let location = createLocation(\"\", createPath(url), null, \"default\");\n let matches = matchRoutes(dataRoutes, location, basename);\n // SSR supports HEAD requests while SPA doesn't\n if (!isValidMethod(method) && method !== \"HEAD\" && method !== \"OPTIONS\") {\n throw getInternalRouterError(405, {\n method\n });\n } else if (!matches) {\n throw getInternalRouterError(404, {\n pathname: location.pathname\n });\n }\n let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);\n if (routeId && !match) {\n throw getInternalRouterError(403, {\n pathname: location.pathname,\n routeId\n });\n } else if (!match) {\n // This should never hit I don't think?\n throw getInternalRouterError(404, {\n pathname: location.pathname\n });\n }\n let result = await queryImpl(request, location, matches, requestContext, match);\n if (isResponse(result)) {\n return result;\n }\n let error = result.errors ? Object.values(result.errors)[0] : undefined;\n if (error !== undefined) {\n // If we got back result.errors, that means the loader/action threw\n // _something_ that wasn't a Response, but it's not guaranteed/required\n // to be an `instanceof Error` either, so we have to use throw here to\n // preserve the \"error\" state outside of queryImpl.\n throw error;\n }\n // Pick off the right state value to return\n if (result.actionData) {\n return Object.values(result.actionData)[0];\n }\n if (result.loaderData) {\n var _result$activeDeferre;\n let data = Object.values(result.loaderData)[0];\n if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {\n data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];\n }\n return data;\n }\n return undefined;\n }\n async function queryImpl(request, location, matches, requestContext, routeMatch) {\n invariant(request.signal, \"query()/queryRoute() requests must contain an AbortController signal\");\n try {\n if (isMutationMethod(request.method.toLowerCase())) {\n let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);\n return result;\n }\n let result = await loadRouteData(request, matches, requestContext, routeMatch);\n return isResponse(result) ? result : _extends({}, result, {\n actionData: null,\n actionHeaders: {}\n });\n } catch (e) {\n // If the user threw/returned a Response in callLoaderOrAction, we throw\n // it to bail out and then return or throw here based on whether the user\n // returned or threw\n if (isQueryRouteResponse(e)) {\n if (e.type === ResultType.error && !isRedirectResponse(e.response)) {\n throw e.response;\n }\n return e.response;\n }\n // Redirects are always returned since they don't propagate to catch\n // boundaries\n if (isRedirectResponse(e)) {\n return e;\n }\n throw e;\n }\n }\n async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {\n let result;\n if (!actionMatch.route.action && !actionMatch.route.lazy) {\n let error = getInternalRouterError(405, {\n method: request.method,\n pathname: new URL(request.url).pathname,\n routeId: actionMatch.route.id\n });\n if (isRouteRequest) {\n throw error;\n }\n result = {\n type: ResultType.error,\n error\n };\n } else {\n result = await callLoaderOrAction(\"action\", request, actionMatch, matches, manifest, mapRouteProperties, basename, {\n isStaticRequest: true,\n isRouteRequest,\n requestContext\n });\n if (request.signal.aborted) {\n let method = isRouteRequest ? \"queryRoute\" : \"query\";\n throw new Error(method + \"() call aborted\");\n }\n }\n if (isRedirectResult(result)) {\n // Uhhhh - this should never happen, we should always throw these from\n // callLoaderOrAction, but the type narrowing here keeps TS happy and we\n // can get back on the \"throw all redirect responses\" train here should\n // this ever happen :/\n throw new Response(null, {\n status: result.status,\n headers: {\n Location: result.location\n }\n });\n }\n if (isDeferredResult(result)) {\n let error = getInternalRouterError(400, {\n type: \"defer-action\"\n });\n if (isRouteRequest) {\n throw error;\n }\n result = {\n type: ResultType.error,\n error\n };\n }\n if (isRouteRequest) {\n // Note: This should only be non-Response values if we get here, since\n // isRouteRequest should throw any Response received in callLoaderOrAction\n if (isErrorResult(result)) {\n throw result.error;\n }\n return {\n matches: [actionMatch],\n loaderData: {},\n actionData: {\n [actionMatch.route.id]: result.data\n },\n errors: null,\n // Note: statusCode + headers are unused here since queryRoute will\n // return the raw Response or value\n statusCode: 200,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n }\n if (isErrorResult(result)) {\n // Store off the pending error - we use it to determine which loaders\n // to call and will commit it when we complete the navigation\n let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);\n let context = await loadRouteData(request, matches, requestContext, undefined, {\n [boundaryMatch.route.id]: result.error\n });\n // action status codes take precedence over loader status codes\n return _extends({}, context, {\n statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,\n actionData: null,\n actionHeaders: _extends({}, result.headers ? {\n [actionMatch.route.id]: result.headers\n } : {})\n });\n }\n // Create a GET request for the loaders\n let loaderRequest = new Request(request.url, {\n headers: request.headers,\n redirect: request.redirect,\n signal: request.signal\n });\n let context = await loadRouteData(loaderRequest, matches, requestContext);\n return _extends({}, context, result.statusCode ? {\n statusCode: result.statusCode\n } : {}, {\n actionData: {\n [actionMatch.route.id]: result.data\n },\n actionHeaders: _extends({}, result.headers ? {\n [actionMatch.route.id]: result.headers\n } : {})\n });\n }\n async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {\n let isRouteRequest = routeMatch != null;\n // Short circuit if we have no loaders to run (queryRoute())\n if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {\n throw getInternalRouterError(400, {\n method: request.method,\n pathname: new URL(request.url).pathname,\n routeId: routeMatch == null ? void 0 : routeMatch.route.id\n });\n }\n let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);\n let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy);\n // Short circuit if we have no loaders to run (query())\n if (matchesToLoad.length === 0) {\n return {\n matches,\n // Add a null for all matched routes for proper revalidation on the client\n loaderData: matches.reduce((acc, m) => Object.assign(acc, {\n [m.route.id]: null\n }), {}),\n errors: pendingActionError || null,\n statusCode: 200,\n loaderHeaders: {},\n activeDeferreds: null\n };\n }\n let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction(\"loader\", request, match, matches, manifest, mapRouteProperties, basename, {\n isStaticRequest: true,\n isRouteRequest,\n requestContext\n }))]);\n if (request.signal.aborted) {\n let method = isRouteRequest ? \"queryRoute\" : \"query\";\n throw new Error(method + \"() call aborted\");\n }\n // Process and commit output from loaders\n let activeDeferreds = new Map();\n let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds);\n // Add a null for any non-loader matches for proper revalidation on the client\n let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));\n matches.forEach(match => {\n if (!executedLoaders.has(match.route.id)) {\n context.loaderData[match.route.id] = null;\n }\n });\n return _extends({}, context, {\n matches,\n activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null\n });\n }\n return {\n dataRoutes,\n query,\n queryRoute\n };\n}\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region Helpers\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Given an existing StaticHandlerContext and an error thrown at render time,\n * provide an updated StaticHandlerContext suitable for a second SSR render\n */\nfunction getStaticContextFromError(routes, context, error) {\n let newContext = _extends({}, context, {\n statusCode: 500,\n errors: {\n [context._deepestRenderedBoundaryId || routes[0].id]: error\n }\n });\n return newContext;\n}\nfunction isSubmissionNavigation(opts) {\n return opts != null && (\"formData\" in opts && opts.formData != null || \"body\" in opts && opts.body !== undefined);\n}\nfunction normalizeTo(location, matches, basename, prependBasename, to, fromRouteId, relative) {\n let contextualMatches;\n let activeRouteMatch;\n if (fromRouteId != null && relative !== \"path\") {\n // Grab matches up to the calling route so our route-relative logic is\n // relative to the correct source route. When using relative:path,\n // fromRouteId is ignored since that is always relative to the current\n // location path\n contextualMatches = [];\n for (let match of matches) {\n contextualMatches.push(match);\n if (match.route.id === fromRouteId) {\n activeRouteMatch = match;\n break;\n }\n }\n } else {\n contextualMatches = matches;\n activeRouteMatch = matches[matches.length - 1];\n }\n // Resolve the relative path\n let path = resolveTo(to ? to : \".\", getPathContributingMatches(contextualMatches).map(m => m.pathnameBase), stripBasename(location.pathname, basename) || location.pathname, relative === \"path\");\n // When `to` is not specified we inherit search/hash from the current\n // location, unlike when to=\".\" and we just inherit the path.\n // See https://github.com/remix-run/remix/issues/927\n if (to == null) {\n path.search = location.search;\n path.hash = location.hash;\n }\n // Add an ?index param for matched index routes if we don't already have one\n if ((to == null || to === \"\" || to === \".\") && activeRouteMatch && activeRouteMatch.route.index && !hasNakedIndexQuery(path.search)) {\n path.search = path.search ? path.search.replace(/^\\?/, \"?index&\") : \"?index\";\n }\n // If we're operating within a basename, prepend it to the pathname. If\n // this is a root navigation, then just use the raw basename which allows\n // the basename to have full control over the presence of a trailing slash\n // on root actions\n if (prependBasename && basename !== \"/\") {\n path.pathname = path.pathname === \"/\" ? basename : joinPaths([basename, path.pathname]);\n }\n return createPath(path);\n}\n// Normalize navigation options by converting formMethod=GET formData objects to\n// URLSearchParams so they behave identically to links with query params\nfunction normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {\n // Return location verbatim on non-submission navigations\n if (!opts || !isSubmissionNavigation(opts)) {\n return {\n path\n };\n }\n if (opts.formMethod && !isValidMethod(opts.formMethod)) {\n return {\n path,\n error: getInternalRouterError(405, {\n method: opts.formMethod\n })\n };\n }\n let getInvalidBodyError = () => ({\n path,\n error: getInternalRouterError(400, {\n type: \"invalid-body\"\n })\n });\n // Create a Submission on non-GET navigations\n let rawFormMethod = opts.formMethod || \"get\";\n let formMethod = normalizeFormMethod ? rawFormMethod.toUpperCase() : rawFormMethod.toLowerCase();\n let formAction = stripHashFromPath(path);\n if (opts.body !== undefined) {\n if (opts.formEncType === \"text/plain\") {\n // text only support POST/PUT/PATCH/DELETE submissions\n if (!isMutationMethod(formMethod)) {\n return getInvalidBodyError();\n }\n let text = typeof opts.body === \"string\" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?\n // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data\n Array.from(opts.body.entries()).reduce((acc, _ref3) => {\n let [name, value] = _ref3;\n return \"\" + acc + name + \"=\" + value + \"\\n\";\n }, \"\") : String(opts.body);\n return {\n path,\n submission: {\n formMethod,\n formAction,\n formEncType: opts.formEncType,\n formData: undefined,\n json: undefined,\n text\n }\n };\n } else if (opts.formEncType === \"application/json\") {\n // json only supports POST/PUT/PATCH/DELETE submissions\n if (!isMutationMethod(formMethod)) {\n return getInvalidBodyError();\n }\n try {\n let json = typeof opts.body === \"string\" ? JSON.parse(opts.body) : opts.body;\n return {\n path,\n submission: {\n formMethod,\n formAction,\n formEncType: opts.formEncType,\n formData: undefined,\n json,\n text: undefined\n }\n };\n } catch (e) {\n return getInvalidBodyError();\n }\n }\n }\n invariant(typeof FormData === \"function\", \"FormData is not available in this environment\");\n let searchParams;\n let formData;\n if (opts.formData) {\n searchParams = convertFormDataToSearchParams(opts.formData);\n formData = opts.formData;\n } else if (opts.body instanceof FormData) {\n searchParams = convertFormDataToSearchParams(opts.body);\n formData = opts.body;\n } else if (opts.body instanceof URLSearchParams) {\n searchParams = opts.body;\n formData = convertSearchParamsToFormData(searchParams);\n } else if (opts.body == null) {\n searchParams = new URLSearchParams();\n formData = new FormData();\n } else {\n try {\n searchParams = new URLSearchParams(opts.body);\n formData = convertSearchParamsToFormData(searchParams);\n } catch (e) {\n return getInvalidBodyError();\n }\n }\n let submission = {\n formMethod,\n formAction,\n formEncType: opts && opts.formEncType || \"application/x-www-form-urlencoded\",\n formData,\n json: undefined,\n text: undefined\n };\n if (isMutationMethod(submission.formMethod)) {\n return {\n path,\n submission\n };\n }\n // Flatten submission onto URLSearchParams for GET submissions\n let parsedPath = parsePath(path);\n // On GET navigation submissions we can drop the ?index param from the\n // resulting location since all loaders will run. But fetcher GET submissions\n // only run a single loader so we need to preserve any incoming ?index params\n if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {\n searchParams.append(\"index\", \"\");\n }\n parsedPath.search = \"?\" + searchParams;\n return {\n path: createPath(parsedPath),\n submission\n };\n}\n// Filter out all routes below any caught error as they aren't going to\n// render so we don't need to load them\nfunction getLoaderMatchesUntilBoundary(matches, boundaryId) {\n let boundaryMatches = matches;\n if (boundaryId) {\n let index = matches.findIndex(m => m.route.id === boundaryId);\n if (index >= 0) {\n boundaryMatches = matches.slice(0, index);\n }\n }\n return boundaryMatches;\n}\nfunction getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) {\n let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;\n let currentUrl = history.createURL(state.location);\n let nextUrl = history.createURL(location);\n // Pick navigation matches that are net-new or qualify for revalidation\n let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;\n let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);\n let navigationMatches = boundaryMatches.filter((match, index) => {\n if (match.route.lazy) {\n // We haven't loaded this route yet so we don't know if it's got a loader!\n return true;\n }\n if (match.route.loader == null) {\n return false;\n }\n // Always call the loader on new route instances and pending defer cancellations\n if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {\n return true;\n }\n // This is the default implementation for when we revalidate. If the route\n // provides it's own implementation, then we give them full control but\n // provide this value so they can leverage it if needed after they check\n // their own specific use cases\n let currentRouteMatch = state.matches[index];\n let nextRouteMatch = match;\n return shouldRevalidateLoader(match, _extends({\n currentUrl,\n currentParams: currentRouteMatch.params,\n nextUrl,\n nextParams: nextRouteMatch.params\n }, submission, {\n actionResult,\n defaultShouldRevalidate:\n // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate\n isRevalidationRequired ||\n // Clicked the same link, resubmitted a GET form\n currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search ||\n // Search params affect all loaders\n currentUrl.search !== nextUrl.search || isNewRouteInstance(currentRouteMatch, nextRouteMatch)\n }));\n });\n // Pick fetcher.loads that need to be revalidated\n let revalidatingFetchers = [];\n fetchLoadMatches.forEach((f, key) => {\n // Don't revalidate if fetcher won't be present in the subsequent render\n if (!matches.some(m => m.route.id === f.routeId)) {\n return;\n }\n let fetcherMatches = matchRoutes(routesToUse, f.path, basename);\n // If the fetcher path no longer matches, push it in with null matches so\n // we can trigger a 404 in callLoadersAndMaybeResolveData\n if (!fetcherMatches) {\n revalidatingFetchers.push({\n key,\n routeId: f.routeId,\n path: f.path,\n matches: null,\n match: null,\n controller: null\n });\n return;\n }\n // Revalidating fetchers are decoupled from the route matches since they\n // load from a static href. They only set `defaultShouldRevalidate` on\n // explicit revalidation due to submission, useRevalidator, or X-Remix-Revalidate\n //\n // They automatically revalidate without even calling shouldRevalidate if:\n // - They were cancelled\n // - They're in the middle of their first load and therefore this is still\n // an initial load and not a revalidation\n //\n // If neither of those is true, then they _always_ check shouldRevalidate\n let fetcher = state.fetchers.get(key);\n let isPerformingInitialLoad = fetcher && fetcher.state !== \"idle\" && fetcher.data === undefined &&\n // If a fetcher.load redirected then it'll be \"loading\" without any data\n // so ensure we're not processing the redirect from this fetcher\n !fetchRedirectIds.has(key);\n let fetcherMatch = getTargetMatch(fetcherMatches, f.path);\n let shouldRevalidate = cancelledFetcherLoads.includes(key) || isPerformingInitialLoad || shouldRevalidateLoader(fetcherMatch, _extends({\n currentUrl,\n currentParams: state.matches[state.matches.length - 1].params,\n nextUrl,\n nextParams: matches[matches.length - 1].params\n }, submission, {\n actionResult,\n defaultShouldRevalidate: isRevalidationRequired\n }));\n if (shouldRevalidate) {\n revalidatingFetchers.push({\n key,\n routeId: f.routeId,\n path: f.path,\n matches: fetcherMatches,\n match: fetcherMatch,\n controller: new AbortController()\n });\n }\n });\n return [navigationMatches, revalidatingFetchers];\n}\nfunction isNewLoader(currentLoaderData, currentMatch, match) {\n let isNew =\n // [a] -> [a, b]\n !currentMatch ||\n // [a, b] -> [a, c]\n match.route.id !== currentMatch.route.id;\n // Handle the case that we don't have data for a re-used route, potentially\n // from a prior error or from a cancelled pending deferred\n let isMissingData = currentLoaderData[match.route.id] === undefined;\n // Always load if this is a net-new route or we don't yet have data\n return isNew || isMissingData;\n}\nfunction isNewRouteInstance(currentMatch, match) {\n let currentPath = currentMatch.route.path;\n return (\n // param change for this match, /users/123 -> /users/456\n currentMatch.pathname !== match.pathname ||\n // splat param changed, which is not present in match.path\n // e.g. /files/images/avatar.jpg -> files/finances.xls\n currentPath != null && currentPath.endsWith(\"*\") && currentMatch.params[\"*\"] !== match.params[\"*\"]\n );\n}\nfunction shouldRevalidateLoader(loaderMatch, arg) {\n if (loaderMatch.route.shouldRevalidate) {\n let routeChoice = loaderMatch.route.shouldRevalidate(arg);\n if (typeof routeChoice === \"boolean\") {\n return routeChoice;\n }\n }\n return arg.defaultShouldRevalidate;\n}\n/**\n * Execute route.lazy() methods to lazily load route modules (loader, action,\n * shouldRevalidate) and update the routeManifest in place which shares objects\n * with dataRoutes so those get updated as well.\n */\nasync function loadLazyRouteModule(route, mapRouteProperties, manifest) {\n if (!route.lazy) {\n return;\n }\n let lazyRoute = await route.lazy();\n // If the lazy route function was executed and removed by another parallel\n // call then we can return - first lazy() to finish wins because the return\n // value of lazy is expected to be static\n if (!route.lazy) {\n return;\n }\n let routeToUpdate = manifest[route.id];\n invariant(routeToUpdate, \"No route found in manifest\");\n // Update the route in place. This should be safe because there's no way\n // we could yet be sitting on this route as we can't get there without\n // resolving lazy() first.\n //\n // This is different than the HMR \"update\" use-case where we may actively be\n // on the route being updated. The main concern boils down to \"does this\n // mutation affect any ongoing navigations or any current state.matches\n // values?\". If not, it should be safe to update in place.\n let routeUpdates = {};\n for (let lazyRouteProperty in lazyRoute) {\n let staticRouteValue = routeToUpdate[lazyRouteProperty];\n let isPropertyStaticallyDefined = staticRouteValue !== undefined &&\n // This property isn't static since it should always be updated based\n // on the route updates\n lazyRouteProperty !== \"hasErrorBoundary\";\n warning(!isPropertyStaticallyDefined, \"Route \\\"\" + routeToUpdate.id + \"\\\" has a static property \\\"\" + lazyRouteProperty + \"\\\" \" + \"defined but its lazy function is also returning a value for this property. \" + (\"The lazy route property \\\"\" + lazyRouteProperty + \"\\\" will be ignored.\"));\n if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {\n routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];\n }\n }\n // Mutate the route with the provided updates. Do this first so we pass\n // the updated version to mapRouteProperties\n Object.assign(routeToUpdate, routeUpdates);\n // Mutate the `hasErrorBoundary` property on the route based on the route\n // updates and remove the `lazy` function so we don't resolve the lazy\n // route again.\n Object.assign(routeToUpdate, _extends({}, mapRouteProperties(routeToUpdate), {\n lazy: undefined\n }));\n}\nasync function callLoaderOrAction(type, request, match, matches, manifest, mapRouteProperties, basename, opts) {\n if (opts === void 0) {\n opts = {};\n }\n let resultType;\n let result;\n let onReject;\n let runHandler = handler => {\n // Setup a promise we can race against so that abort signals short circuit\n let reject;\n let abortPromise = new Promise((_, r) => reject = r);\n onReject = () => reject();\n request.signal.addEventListener(\"abort\", onReject);\n return Promise.race([handler({\n request,\n params: match.params,\n context: opts.requestContext\n }), abortPromise]);\n };\n try {\n let handler = match.route[type];\n if (match.route.lazy) {\n if (handler) {\n // Run statically defined handler in parallel with lazy()\n let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);\n result = values[0];\n } else {\n // Load lazy route module, then run any returned handler\n await loadLazyRouteModule(match.route, mapRouteProperties, manifest);\n handler = match.route[type];\n if (handler) {\n // Handler still run even if we got interrupted to maintain consistency\n // with un-abortable behavior of handler execution on non-lazy or\n // previously-lazy-loaded routes\n result = await runHandler(handler);\n } else if (type === \"action\") {\n let url = new URL(request.url);\n let pathname = url.pathname + url.search;\n throw getInternalRouterError(405, {\n method: request.method,\n pathname,\n routeId: match.route.id\n });\n } else {\n // lazy() route has no loader to run. Short circuit here so we don't\n // hit the invariant below that errors on returning undefined.\n return {\n type: ResultType.data,\n data: undefined\n };\n }\n }\n } else if (!handler) {\n let url = new URL(request.url);\n let pathname = url.pathname + url.search;\n throw getInternalRouterError(404, {\n pathname\n });\n } else {\n result = await runHandler(handler);\n }\n invariant(result !== undefined, \"You defined \" + (type === \"action\" ? \"an action\" : \"a loader\") + \" for route \" + (\"\\\"\" + match.route.id + \"\\\" but didn't return anything from your `\" + type + \"` \") + \"function. Please return a value or `null`.\");\n } catch (e) {\n resultType = ResultType.error;\n result = e;\n } finally {\n if (onReject) {\n request.signal.removeEventListener(\"abort\", onReject);\n }\n }\n if (isResponse(result)) {\n let status = result.status;\n // Process redirects\n if (redirectStatusCodes.has(status)) {\n let location = result.headers.get(\"Location\");\n invariant(location, \"Redirects returned/thrown from loaders/actions must have a Location header\");\n // Support relative routing in internal redirects\n if (!ABSOLUTE_URL_REGEX.test(location)) {\n location = normalizeTo(new URL(request.url), matches.slice(0, matches.indexOf(match) + 1), basename, true, location);\n } else if (!opts.isStaticRequest) {\n // Strip off the protocol+origin for same-origin + same-basename absolute\n // redirects. If this is a static request, we can let it go back to the\n // browser as-is\n let currentUrl = new URL(request.url);\n let url = location.startsWith(\"//\") ? new URL(currentUrl.protocol + location) : new URL(location);\n let isSameBasename = stripBasename(url.pathname, basename) != null;\n if (url.origin === currentUrl.origin && isSameBasename) {\n location = url.pathname + url.search + url.hash;\n }\n }\n // Don't process redirects in the router during static requests requests.\n // Instead, throw the Response and let the server handle it with an HTTP\n // redirect. We also update the Location header in place in this flow so\n // basename and relative routing is taken into account\n if (opts.isStaticRequest) {\n result.headers.set(\"Location\", location);\n throw result;\n }\n return {\n type: ResultType.redirect,\n status,\n location,\n revalidate: result.headers.get(\"X-Remix-Revalidate\") !== null\n };\n }\n // For SSR single-route requests, we want to hand Responses back directly\n // without unwrapping. We do this with the QueryRouteResponse wrapper\n // interface so we can know whether it was returned or thrown\n if (opts.isRouteRequest) {\n // eslint-disable-next-line no-throw-literal\n throw {\n type: resultType || ResultType.data,\n response: result\n };\n }\n let data;\n let contentType = result.headers.get(\"Content-Type\");\n // Check between word boundaries instead of startsWith() due to the last\n // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type\n if (contentType && /\\bapplication\\/json\\b/.test(contentType)) {\n data = await result.json();\n } else {\n data = await result.text();\n }\n if (resultType === ResultType.error) {\n return {\n type: resultType,\n error: new ErrorResponse(status, result.statusText, data),\n headers: result.headers\n };\n }\n return {\n type: ResultType.data,\n data,\n statusCode: result.status,\n headers: result.headers\n };\n }\n if (resultType === ResultType.error) {\n return {\n type: resultType,\n error: result\n };\n }\n if (isDeferredData(result)) {\n var _result$init, _result$init2;\n return {\n type: ResultType.deferred,\n deferredData: result,\n statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status,\n headers: ((_result$init2 = result.init) == null ? void 0 : _result$init2.headers) && new Headers(result.init.headers)\n };\n }\n return {\n type: ResultType.data,\n data: result\n };\n}\n// Utility method for creating the Request instances for loaders/actions during\n// client-side navigations and fetches. During SSR we will always have a\n// Request instance from the static handler (query/queryRoute)\nfunction createClientSideRequest(history, location, signal, submission) {\n let url = history.createURL(stripHashFromPath(location)).toString();\n let init = {\n signal\n };\n if (submission && isMutationMethod(submission.formMethod)) {\n let {\n formMethod,\n formEncType\n } = submission;\n // Didn't think we needed this but it turns out unlike other methods, patch\n // won't be properly normalized to uppercase and results in a 405 error.\n // See: https://fetch.spec.whatwg.org/#concept-method\n init.method = formMethod.toUpperCase();\n if (formEncType === \"application/json\") {\n init.headers = new Headers({\n \"Content-Type\": formEncType\n });\n init.body = JSON.stringify(submission.json);\n } else if (formEncType === \"text/plain\") {\n // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)\n init.body = submission.text;\n } else if (formEncType === \"application/x-www-form-urlencoded\" && submission.formData) {\n // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)\n init.body = convertFormDataToSearchParams(submission.formData);\n } else {\n // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)\n init.body = submission.formData;\n }\n }\n return new Request(url, init);\n}\nfunction convertFormDataToSearchParams(formData) {\n let searchParams = new URLSearchParams();\n for (let [key, value] of formData.entries()) {\n // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs\n searchParams.append(key, typeof value === \"string\" ? value : value.name);\n }\n return searchParams;\n}\nfunction convertSearchParamsToFormData(searchParams) {\n let formData = new FormData();\n for (let [key, value] of searchParams.entries()) {\n formData.append(key, value);\n }\n return formData;\n}\nfunction processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds) {\n // Fill in loaderData/errors from our loaders\n let loaderData = {};\n let errors = null;\n let statusCode;\n let foundError = false;\n let loaderHeaders = {};\n // Process loader results into state.loaderData/state.errors\n results.forEach((result, index) => {\n let id = matchesToLoad[index].route.id;\n invariant(!isRedirectResult(result), \"Cannot handle redirect results in processLoaderData\");\n if (isErrorResult(result)) {\n // Look upwards from the matched route for the closest ancestor\n // error boundary, defaulting to the root match\n let boundaryMatch = findNearestBoundary(matches, id);\n let error = result.error;\n // If we have a pending action error, we report it at the highest-route\n // that throws a loader error, and then clear it out to indicate that\n // it was consumed\n if (pendingError) {\n error = Object.values(pendingError)[0];\n pendingError = undefined;\n }\n errors = errors || {};\n // Prefer higher error values if lower errors bubble to the same boundary\n if (errors[boundaryMatch.route.id] == null) {\n errors[boundaryMatch.route.id] = error;\n }\n // Clear our any prior loaderData for the throwing route\n loaderData[id] = undefined;\n // Once we find our first (highest) error, we set the status code and\n // prevent deeper status codes from overriding\n if (!foundError) {\n foundError = true;\n statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;\n }\n if (result.headers) {\n loaderHeaders[id] = result.headers;\n }\n } else {\n if (isDeferredResult(result)) {\n activeDeferreds.set(id, result.deferredData);\n loaderData[id] = result.deferredData.data;\n } else {\n loaderData[id] = result.data;\n }\n // Error status codes always override success status codes, but if all\n // loaders are successful we take the deepest status code.\n if (result.statusCode != null && result.statusCode !== 200 && !foundError) {\n statusCode = result.statusCode;\n }\n if (result.headers) {\n loaderHeaders[id] = result.headers;\n }\n }\n });\n // If we didn't consume the pending action error (i.e., all loaders\n // resolved), then consume it here. Also clear out any loaderData for the\n // throwing route\n if (pendingError) {\n errors = pendingError;\n loaderData[Object.keys(pendingError)[0]] = undefined;\n }\n return {\n loaderData,\n errors,\n statusCode: statusCode || 200,\n loaderHeaders\n };\n}\nfunction processLoaderData(state, matches, matchesToLoad, results, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds) {\n let {\n loaderData,\n errors\n } = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds);\n // Process results from our revalidating fetchers\n for (let index = 0; index < revalidatingFetchers.length; index++) {\n let {\n key,\n match,\n controller\n } = revalidatingFetchers[index];\n invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, \"Did not find corresponding fetcher result\");\n let result = fetcherResults[index];\n // Process fetcher non-redirect errors\n if (controller && controller.signal.aborted) {\n // Nothing to do for aborted fetchers\n continue;\n } else if (isErrorResult(result)) {\n let boundaryMatch = findNearestBoundary(state.matches, match == null ? void 0 : match.route.id);\n if (!(errors && errors[boundaryMatch.route.id])) {\n errors = _extends({}, errors, {\n [boundaryMatch.route.id]: result.error\n });\n }\n state.fetchers.delete(key);\n } else if (isRedirectResult(result)) {\n // Should never get here, redirects should get processed above, but we\n // keep this to type narrow to a success result in the else\n invariant(false, \"Unhandled fetcher revalidation redirect\");\n } else if (isDeferredResult(result)) {\n // Should never get here, deferred data should be awaited for fetchers\n // in resolveDeferredResults\n invariant(false, \"Unhandled fetcher deferred data\");\n } else {\n let doneFetcher = getDoneFetcher(result.data);\n state.fetchers.set(key, doneFetcher);\n }\n }\n return {\n loaderData,\n errors\n };\n}\nfunction mergeLoaderData(loaderData, newLoaderData, matches, errors) {\n let mergedLoaderData = _extends({}, newLoaderData);\n for (let match of matches) {\n let id = match.route.id;\n if (newLoaderData.hasOwnProperty(id)) {\n if (newLoaderData[id] !== undefined) {\n mergedLoaderData[id] = newLoaderData[id];\n }\n } else if (loaderData[id] !== undefined && match.route.loader) {\n // Preserve existing keys not included in newLoaderData and where a loader\n // wasn't removed by HMR\n mergedLoaderData[id] = loaderData[id];\n }\n if (errors && errors.hasOwnProperty(id)) {\n // Don't keep any loader data below the boundary\n break;\n }\n }\n return mergedLoaderData;\n}\n// Find the nearest error boundary, looking upwards from the leaf route (or the\n// route specified by routeId) for the closest ancestor error boundary,\n// defaulting to the root match\nfunction findNearestBoundary(matches, routeId) {\n let eligibleMatches = routeId ? matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1) : [...matches];\n return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];\n}\nfunction getShortCircuitMatches(routes) {\n // Prefer a root layout route if present, otherwise shim in a route object\n let route = routes.find(r => r.index || !r.path || r.path === \"/\") || {\n id: \"__shim-error-route__\"\n };\n return {\n matches: [{\n params: {},\n pathname: \"\",\n pathnameBase: \"\",\n route\n }],\n route\n };\n}\nfunction getInternalRouterError(status, _temp4) {\n let {\n pathname,\n routeId,\n method,\n type\n } = _temp4 === void 0 ? {} : _temp4;\n let statusText = \"Unknown Server Error\";\n let errorMessage = \"Unknown @remix-run/router error\";\n if (status === 400) {\n statusText = \"Bad Request\";\n if (method && pathname && routeId) {\n errorMessage = \"You made a \" + method + \" request to \\\"\" + pathname + \"\\\" but \" + (\"did not provide a `loader` for route \\\"\" + routeId + \"\\\", \") + \"so there is no way to handle the request.\";\n } else if (type === \"defer-action\") {\n errorMessage = \"defer() is not supported in actions\";\n } else if (type === \"invalid-body\") {\n errorMessage = \"Unable to encode submission body\";\n }\n } else if (status === 403) {\n statusText = \"Forbidden\";\n errorMessage = \"Route \\\"\" + routeId + \"\\\" does not match URL \\\"\" + pathname + \"\\\"\";\n } else if (status === 404) {\n statusText = \"Not Found\";\n errorMessage = \"No route matches URL \\\"\" + pathname + \"\\\"\";\n } else if (status === 405) {\n statusText = \"Method Not Allowed\";\n if (method && pathname && routeId) {\n errorMessage = \"You made a \" + method.toUpperCase() + \" request to \\\"\" + pathname + \"\\\" but \" + (\"did not provide an `action` for route \\\"\" + routeId + \"\\\", \") + \"so there is no way to handle the request.\";\n } else if (method) {\n errorMessage = \"Invalid request method \\\"\" + method.toUpperCase() + \"\\\"\";\n }\n }\n return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);\n}\n// Find any returned redirect errors, starting from the lowest match\nfunction findRedirect(results) {\n for (let i = results.length - 1; i >= 0; i--) {\n let result = results[i];\n if (isRedirectResult(result)) {\n return result;\n }\n }\n}\nfunction stripHashFromPath(path) {\n let parsedPath = typeof path === \"string\" ? parsePath(path) : path;\n return createPath(_extends({}, parsedPath, {\n hash: \"\"\n }));\n}\nfunction isHashChangeOnly(a, b) {\n if (a.pathname !== b.pathname || a.search !== b.search) {\n return false;\n }\n if (a.hash === \"\") {\n // /page -> /page#hash\n return b.hash !== \"\";\n } else if (a.hash === b.hash) {\n // /page#hash -> /page#hash\n return true;\n } else if (b.hash !== \"\") {\n // /page#hash -> /page#other\n return true;\n }\n // If the hash is removed the browser will re-perform a request to the server\n // /page#hash -> /page\n return false;\n}\nfunction isDeferredResult(result) {\n return result.type === ResultType.deferred;\n}\nfunction isErrorResult(result) {\n return result.type === ResultType.error;\n}\nfunction isRedirectResult(result) {\n return (result && result.type) === ResultType.redirect;\n}\nfunction isDeferredData(value) {\n let deferred = value;\n return deferred && typeof deferred === \"object\" && typeof deferred.data === \"object\" && typeof deferred.subscribe === \"function\" && typeof deferred.cancel === \"function\" && typeof deferred.resolveData === \"function\";\n}\nfunction isResponse(value) {\n return value != null && typeof value.status === \"number\" && typeof value.statusText === \"string\" && typeof value.headers === \"object\" && typeof value.body !== \"undefined\";\n}\nfunction isRedirectResponse(result) {\n if (!isResponse(result)) {\n return false;\n }\n let status = result.status;\n let location = result.headers.get(\"Location\");\n return status >= 300 && status <= 399 && location != null;\n}\nfunction isQueryRouteResponse(obj) {\n return obj && isResponse(obj.response) && (obj.type === ResultType.data || ResultType.error);\n}\nfunction isValidMethod(method) {\n return validRequestMethods.has(method.toLowerCase());\n}\nfunction isMutationMethod(method) {\n return validMutationMethods.has(method.toLowerCase());\n}\nasync function resolveDeferredResults(currentMatches, matchesToLoad, results, signals, isFetcher, currentLoaderData) {\n for (let index = 0; index < results.length; index++) {\n let result = results[index];\n let match = matchesToLoad[index];\n // If we don't have a match, then we can have a deferred result to do\n // anything with. This is for revalidating fetchers where the route was\n // removed during HMR\n if (!match) {\n continue;\n }\n let currentMatch = currentMatches.find(m => m.route.id === match.route.id);\n let isRevalidatingLoader = currentMatch != null && !isNewRouteInstance(currentMatch, match) && (currentLoaderData && currentLoaderData[match.route.id]) !== undefined;\n if (isDeferredResult(result) && (isFetcher || isRevalidatingLoader)) {\n // Note: we do not have to touch activeDeferreds here since we race them\n // against the signal in resolveDeferredData and they'll get aborted\n // there if needed\n let signal = signals[index];\n invariant(signal, \"Expected an AbortSignal for revalidating fetcher deferred result\");\n await resolveDeferredData(result, signal, isFetcher).then(result => {\n if (result) {\n results[index] = result || results[index];\n }\n });\n }\n }\n}\nasync function resolveDeferredData(result, signal, unwrap) {\n if (unwrap === void 0) {\n unwrap = false;\n }\n let aborted = await result.deferredData.resolveData(signal);\n if (aborted) {\n return;\n }\n if (unwrap) {\n try {\n return {\n type: ResultType.data,\n data: result.deferredData.unwrappedData\n };\n } catch (e) {\n // Handle any TrackedPromise._error values encountered while unwrapping\n return {\n type: ResultType.error,\n error: e\n };\n }\n }\n return {\n type: ResultType.data,\n data: result.deferredData.data\n };\n}\nfunction hasNakedIndexQuery(search) {\n return new URLSearchParams(search).getAll(\"index\").some(v => v === \"\");\n}\n// Note: This should match the format exported by useMatches, so if you change\n// this please also change that :) Eventually we'll DRY this up\nfunction createUseMatchesMatch(match, loaderData) {\n let {\n route,\n pathname,\n params\n } = match;\n return {\n id: route.id,\n pathname,\n params,\n data: loaderData[route.id],\n handle: route.handle\n };\n}\nfunction getTargetMatch(matches, location) {\n let search = typeof location === \"string\" ? parsePath(location).search : location.search;\n if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || \"\")) {\n // Return the leaf index route when index is present\n return matches[matches.length - 1];\n }\n // Otherwise grab the deepest \"path contributing\" match (ignoring index and\n // pathless layout routes)\n let pathMatches = getPathContributingMatches(matches);\n return pathMatches[pathMatches.length - 1];\n}\nfunction getSubmissionFromNavigation(navigation) {\n let {\n formMethod,\n formAction,\n formEncType,\n text,\n formData,\n json\n } = navigation;\n if (!formMethod || !formAction || !formEncType) {\n return;\n }\n if (text != null) {\n return {\n formMethod,\n formAction,\n formEncType,\n formData: undefined,\n json: undefined,\n text\n };\n } else if (formData != null) {\n return {\n formMethod,\n formAction,\n formEncType,\n formData,\n json: undefined,\n text: undefined\n };\n } else if (json !== undefined) {\n return {\n formMethod,\n formAction,\n formEncType,\n formData: undefined,\n json,\n text: undefined\n };\n }\n}\nfunction getLoadingNavigation(location, submission) {\n if (submission) {\n let navigation = {\n state: \"loading\",\n location,\n formMethod: submission.formMethod,\n formAction: submission.formAction,\n formEncType: submission.formEncType,\n formData: submission.formData,\n json: submission.json,\n text: submission.text\n };\n return navigation;\n } else {\n let navigation = {\n state: \"loading\",\n location,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n json: undefined,\n text: undefined\n };\n return navigation;\n }\n}\nfunction getSubmittingNavigation(location, submission) {\n let navigation = {\n state: \"submitting\",\n location,\n formMethod: submission.formMethod,\n formAction: submission.formAction,\n formEncType: submission.formEncType,\n formData: submission.formData,\n json: submission.json,\n text: submission.text\n };\n return navigation;\n}\nfunction getLoadingFetcher(submission, data) {\n if (submission) {\n let fetcher = {\n state: \"loading\",\n formMethod: submission.formMethod,\n formAction: submission.formAction,\n formEncType: submission.formEncType,\n formData: submission.formData,\n json: submission.json,\n text: submission.text,\n data,\n \" _hasFetcherDoneAnything \": true\n };\n return fetcher;\n } else {\n let fetcher = {\n state: \"loading\",\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n json: undefined,\n text: undefined,\n data,\n \" _hasFetcherDoneAnything \": true\n };\n return fetcher;\n }\n}\nfunction getSubmittingFetcher(submission, existingFetcher) {\n let fetcher = {\n state: \"submitting\",\n formMethod: submission.formMethod,\n formAction: submission.formAction,\n formEncType: submission.formEncType,\n formData: submission.formData,\n json: submission.json,\n text: submission.text,\n data: existingFetcher ? existingFetcher.data : undefined,\n \" _hasFetcherDoneAnything \": true\n };\n return fetcher;\n}\nfunction getDoneFetcher(data) {\n let fetcher = {\n state: \"idle\",\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n json: undefined,\n text: undefined,\n data,\n \" _hasFetcherDoneAnything \": true\n };\n return fetcher;\n}\n//#endregion\n\n\n//# sourceMappingURL=router.js.map\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQHJlbWl4LXJ1bi9yb3V0ZXIvZGlzdC9yb3V0ZXIuanMiLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vbWFrZXJzLy4vbm9kZV9tb2R1bGVzL0ByZW1peC1ydW4vcm91dGVyL2Rpc3Qvcm91dGVyLmpzPzg2NGQiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAcmVtaXgtcnVuL3JvdXRlciB2MS43LjBcbiAqXG4gKiBDb3B5cmlnaHQgKGMpIFJlbWl4IFNvZnR3YXJlIEluYy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UubWQgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAbGljZW5zZSBNSVRcbiAqL1xuZnVuY3Rpb24gX2V4dGVuZHMoKSB7XG4gIF9leHRlbmRzID0gT2JqZWN0LmFzc2lnbiA/IE9iamVjdC5hc3NpZ24uYmluZCgpIDogZnVuY3Rpb24gKHRhcmdldCkge1xuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yICh2YXIga2V5IGluIHNvdXJjZSkge1xuICAgICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHNvdXJjZSwga2V5KSkge1xuICAgICAgICAgIHRhcmdldFtrZXldID0gc291cmNlW2tleV07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRhcmdldDtcbiAgfTtcbiAgcmV0dXJuIF9leHRlbmRzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG59XG5cbi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyNyZWdpb24gVHlwZXMgYW5kIENvbnN0YW50c1xuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbi8qKlxuICogQWN0aW9ucyByZXByZXNlbnQgdGhlIHR5cGUgb2YgY2hhbmdlIHRvIGEgbG9jYXRpb24gdmFsdWUuXG4gKi9cbnZhciBBY3Rpb247XG4oZnVuY3Rpb24gKEFjdGlvbikge1xuICAvKipcbiAgICogQSBQT1AgaW5kaWNhdGVzIGEgY2hhbmdlIHRvIGFuIGFyYml0cmFyeSBpbmRleCBpbiB0aGUgaGlzdG9yeSBzdGFjaywgc3VjaFxuICAgKiBhcyBhIGJhY2sgb3IgZm9yd2FyZCBuYXZpZ2F0aW9uLiBJdCBkb2VzIG5vdCBkZXNjcmliZSB0aGUgZGlyZWN0aW9uIG9mIHRoZVxuICAgKiBuYXZpZ2F0aW9uLCBvbmx5IHRoYXQgdGhlIGN1cnJlbnQgaW5kZXggY2hhbmdlZC5cbiAgICpcbiAgICogTm90ZTogVGhpcyBpcyB0aGUgZGVmYXVsdCBhY3Rpb24gZm9yIG5ld2x5IGNyZWF0ZWQgaGlzdG9yeSBvYmplY3RzLlxuICAgKi9cbiAgQWN0aW9uW1wiUG9wXCJdID0gXCJQT1BcIjtcbiAgLyoqXG4gICAqIEEgUFVTSCBpbmRpY2F0ZXMgYSBuZXcgZW50cnkgYmVpbmcgYWRkZWQgdG8gdGhlIGhpc3Rvcnkgc3RhY2ssIHN1Y2ggYXMgd2hlblxuICAgKiBhIGxpbmsgaXMgY2xpY2tlZCBhbmQgYSBuZXcgcGFnZSBsb2Fkcy4gV2hlbiB0aGlzIGhhcHBlbnMsIGFsbCBzdWJzZXF1ZW50XG4gICAqIGVudHJpZXMgaW4gdGhlIHN0YWNrIGFyZSBsb3N0LlxuICAgKi9cbiAgQWN0aW9uW1wiUHVzaFwiXSA9IFwiUFVTSFwiO1xuICAvKipcbiAgICogQSBSRVBMQUNFIGluZGljYXRlcyB0aGUgZW50cnkgYXQgdGhlIGN1cnJlbnQgaW5kZXggaW4gdGhlIGhpc3Rvcnkgc3RhY2tcbiAgICogYmVpbmcgcmVwbGFjZWQgYnkgYSBuZXcgb25lLlxuICAgKi9cbiAgQWN0aW9uW1wiUmVwbGFjZVwiXSA9IFwiUkVQTEFDRVwiO1xufSkoQWN0aW9uIHx8IChBY3Rpb24gPSB7fSkpO1xuY29uc3QgUG9wU3RhdGVFdmVudFR5cGUgPSBcInBvcHN0YXRlXCI7XG4vKipcbiAqIE1lbW9yeSBoaXN0b3J5IHN0b3JlcyB0aGUgY3VycmVudCBsb2NhdGlvbiBpbiBtZW1vcnkuIEl0IGlzIGRlc2lnbmVkIGZvciB1c2VcbiAqIGluIHN0YXRlZnVsIG5vbi1icm93c2VyIGVudmlyb25tZW50cyBsaWtlIHRlc3RzIGFuZCBSZWFjdCBOYXRpdmUuXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZU1lbW9yeUhpc3Rvcnkob3B0aW9ucykge1xuICBpZiAob3B0aW9ucyA9PT0gdm9pZCAwKSB7XG4gICAgb3B0aW9ucyA9IHt9O1xuICB9XG4gIGxldCB7XG4gICAgaW5pdGlhbEVudHJpZXMgPSBbXCIvXCJdLFxuICAgIGluaXRpYWxJbmRleCxcbiAgICB2NUNvbXBhdCA9IGZhbHNlXG4gIH0gPSBvcHRpb25zO1xuICBsZXQgZW50cmllczsgLy8gRGVjbGFyZSBzbyB3ZSBjYW4gYWNjZXNzIGZyb20gY3JlYXRlTWVtb3J5TG9jYXRpb25cbiAgZW50cmllcyA9IGluaXRpYWxFbnRyaWVzLm1hcCgoZW50cnksIGluZGV4KSA9PiBjcmVhdGVNZW1vcnlMb2NhdGlvbihlbnRyeSwgdHlwZW9mIGVudHJ5ID09PSBcInN0cmluZ1wiID8gbnVsbCA6IGVudHJ5LnN0YXRlLCBpbmRleCA9PT0gMCA/IFwiZGVmYXVsdFwiIDogdW5kZWZpbmVkKSk7XG4gIGxldCBpbmRleCA9IGNsYW1wSW5kZXgoaW5pdGlhbEluZGV4ID09IG51bGwgPyBlbnRyaWVzLmxlbmd0aCAtIDEgOiBpbml0aWFsSW5kZXgpO1xuICBsZXQgYWN0aW9uID0gQWN0aW9uLlBvcDtcbiAgbGV0IGxpc3RlbmVyID0gbnVsbDtcbiAgZnVuY3Rpb24gY2xhbXBJbmRleChuKSB7XG4gICAgcmV0dXJuIE1hdGgubWluKE1hdGgubWF4KG4sIDApLCBlbnRyaWVzLmxlbmd0aCAtIDEpO1xuICB9XG4gIGZ1bmN0aW9uIGdldEN1cnJlbnRMb2NhdGlvbigpIHtcbiAgICByZXR1cm4gZW50cmllc1tpbmRleF07XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlTWVtb3J5TG9jYXRpb24odG8sIHN0YXRlLCBrZXkpIHtcbiAgICBpZiAoc3RhdGUgPT09IHZvaWQgMCkge1xuICAgICAgc3RhdGUgPSBudWxsO1xuICAgIH1cbiAgICBsZXQgbG9jYXRpb24gPSBjcmVhdGVMb2NhdGlvbihlbnRyaWVzID8gZ2V0Q3VycmVudExvY2F0aW9uKCkucGF0aG5hbWUgOiBcIi9cIiwgdG8sIHN0YXRlLCBrZXkpO1xuICAgIHdhcm5pbmcobG9jYXRpb24ucGF0aG5hbWUuY2hhckF0KDApID09PSBcIi9cIiwgXCJyZWxhdGl2ZSBwYXRobmFtZXMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4gbWVtb3J5IGhpc3Rvcnk6IFwiICsgSlNPTi5zdHJpbmdpZnkodG8pKTtcbiAgICByZXR1cm4gbG9jYXRpb247XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlSHJlZih0bykge1xuICAgIHJldHVybiB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyB0byA6IGNyZWF0ZVBhdGgodG8pO1xuICB9XG4gIGxldCBoaXN0b3J5ID0ge1xuICAgIGdldCBpbmRleCgpIHtcbiAgICAgIHJldHVybiBpbmRleDtcbiAgICB9LFxuICAgIGdldCBhY3Rpb24oKSB7XG4gICAgICByZXR1cm4gYWN0aW9uO1xuICAgIH0sXG4gICAgZ2V0IGxvY2F0aW9uKCkge1xuICAgICAgcmV0dXJuIGdldEN1cnJlbnRMb2NhdGlvbigpO1xuICAgIH0sXG4gICAgY3JlYXRlSHJlZixcbiAgICBjcmVhdGVVUkwodG8pIHtcbiAgICAgIHJldHVybiBuZXcgVVJMKGNyZWF0ZUhyZWYodG8pLCBcImh0dHA6Ly9sb2NhbGhvc3RcIik7XG4gICAgfSxcbiAgICBlbmNvZGVMb2NhdGlvbih0bykge1xuICAgICAgbGV0IHBhdGggPSB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyBwYXJzZVBhdGgodG8pIDogdG87XG4gICAgICByZXR1cm4ge1xuICAgICAgICBwYXRobmFtZTogcGF0aC5wYXRobmFtZSB8fCBcIlwiLFxuICAgICAgICBzZWFyY2g6IHBhdGguc2VhcmNoIHx8IFwiXCIsXG4gICAgICAgIGhhc2g6IHBhdGguaGFzaCB8fCBcIlwiXG4gICAgICB9O1xuICAgIH0sXG4gICAgcHVzaCh0bywgc3RhdGUpIHtcbiAgICAgIGFjdGlvbiA9IEFjdGlvbi5QdXNoO1xuICAgICAgbGV0IG5leHRMb2NhdGlvbiA9IGNyZWF0ZU1lbW9yeUxvY2F0aW9uKHRvLCBzdGF0ZSk7XG4gICAgICBpbmRleCArPSAxO1xuICAgICAgZW50cmllcy5zcGxpY2UoaW5kZXgsIGVudHJpZXMubGVuZ3RoLCBuZXh0TG9jYXRpb24pO1xuICAgICAgaWYgKHY1Q29tcGF0ICYmIGxpc3RlbmVyKSB7XG4gICAgICAgIGxpc3RlbmVyKHtcbiAgICAgICAgICBhY3Rpb24sXG4gICAgICAgICAgbG9jYXRpb246IG5leHRMb2NhdGlvbixcbiAgICAgICAgICBkZWx0YTogMVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9LFxuICAgIHJlcGxhY2UodG8sIHN0YXRlKSB7XG4gICAgICBhY3Rpb24gPSBBY3Rpb24uUmVwbGFjZTtcbiAgICAgIGxldCBuZXh0TG9jYXRpb24gPSBjcmVhdGVNZW1vcnlMb2NhdGlvbih0bywgc3RhdGUpO1xuICAgICAgZW50cmllc1tpbmRleF0gPSBuZXh0TG9jYXRpb247XG4gICAgICBpZiAodjVDb21wYXQgJiYgbGlzdGVuZXIpIHtcbiAgICAgICAgbGlzdGVuZXIoe1xuICAgICAgICAgIGFjdGlvbixcbiAgICAgICAgICBsb2NhdGlvbjogbmV4dExvY2F0aW9uLFxuICAgICAgICAgIGRlbHRhOiAwXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0sXG4gICAgZ28oZGVsdGEpIHtcbiAgICAgIGFjdGlvbiA9IEFjdGlvbi5Qb3A7XG4gICAgICBsZXQgbmV4dEluZGV4ID0gY2xhbXBJbmRleChpbmRleCArIGRlbHRhKTtcbiAgICAgIGxldCBuZXh0TG9jYXRpb24gPSBlbnRyaWVzW25leHRJbmRleF07XG4gICAgICBpbmRleCA9IG5leHRJbmRleDtcbiAgICAgIGlmIChsaXN0ZW5lcikge1xuICAgICAgICBsaXN0ZW5lcih7XG4gICAgICAgICAgYWN0aW9uLFxuICAgICAgICAgIGxvY2F0aW9uOiBuZXh0TG9jYXRpb24sXG4gICAgICAgICAgZGVsdGFcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSxcbiAgICBsaXN0ZW4oZm4pIHtcbiAgICAgIGxpc3RlbmVyID0gZm47XG4gICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBsaXN0ZW5lciA9IG51bGw7XG4gICAgICB9O1xuICAgIH1cbiAgfTtcbiAgcmV0dXJuIGhpc3Rvcnk7XG59XG4vKipcbiAqIEJyb3dzZXIgaGlzdG9yeSBzdG9yZXMgdGhlIGxvY2F0aW9uIGluIHJlZ3VsYXIgVVJMcy4gVGhpcyBpcyB0aGUgc3RhbmRhcmQgZm9yXG4gKiBtb3N0IHdlYiBhcHBzLCBidXQgaXQgcmVxdWlyZXMgc29tZSBjb25maWd1cmF0aW9uIG9uIHRoZSBzZXJ2ZXIgdG8gZW5zdXJlIHlvdVxuICogc2VydmUgdGhlIHNhbWUgYXBwIGF0IG11bHRpcGxlIFVSTHMuXG4gKlxuICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vcmVtaXgtcnVuL2hpc3RvcnkvdHJlZS9tYWluL2RvY3MvYXBpLXJlZmVyZW5jZS5tZCNjcmVhdGVicm93c2VyaGlzdG9yeVxuICovXG5mdW5jdGlvbiBjcmVhdGVCcm93c2VySGlzdG9yeShvcHRpb25zKSB7XG4gIGlmIChvcHRpb25zID09PSB2b2lkIDApIHtcbiAgICBvcHRpb25zID0ge307XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlQnJvd3NlckxvY2F0aW9uKHdpbmRvdywgZ2xvYmFsSGlzdG9yeSkge1xuICAgIGxldCB7XG4gICAgICBwYXRobmFtZSxcbiAgICAgIHNlYXJjaCxcbiAgICAgIGhhc2hcbiAgICB9ID0gd2luZG93LmxvY2F0aW9uO1xuICAgIHJldHVybiBjcmVhdGVMb2NhdGlvbihcIlwiLCB7XG4gICAgICBwYXRobmFtZSxcbiAgICAgIHNlYXJjaCxcbiAgICAgIGhhc2hcbiAgICB9LFxuICAgIC8vIHN0YXRlIGRlZmF1bHRzIHRvIGBudWxsYCBiZWNhdXNlIGB3aW5kb3cuaGlzdG9yeS5zdGF0ZWAgZG9lc1xuICAgIGdsb2JhbEhpc3Rvcnkuc3RhdGUgJiYgZ2xvYmFsSGlzdG9yeS5zdGF0ZS51c3IgfHwgbnVsbCwgZ2xvYmFsSGlzdG9yeS5zdGF0ZSAmJiBnbG9iYWxIaXN0b3J5LnN0YXRlLmtleSB8fCBcImRlZmF1bHRcIik7XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlQnJvd3NlckhyZWYod2luZG93LCB0bykge1xuICAgIHJldHVybiB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyB0byA6IGNyZWF0ZVBhdGgodG8pO1xuICB9XG4gIHJldHVybiBnZXRVcmxCYXNlZEhpc3RvcnkoY3JlYXRlQnJvd3NlckxvY2F0aW9uLCBjcmVhdGVCcm93c2VySHJlZiwgbnVsbCwgb3B0aW9ucyk7XG59XG4vKipcbiAqIEhhc2ggaGlzdG9yeSBzdG9yZXMgdGhlIGxvY2F0aW9uIGluIHdpbmRvdy5sb2NhdGlvbi5oYXNoLiBUaGlzIG1ha2VzIGl0IGlkZWFsXG4gKiBmb3Igc2l0dWF0aW9ucyB3aGVyZSB5b3UgZG9uJ3Qgd2FudCB0byBzZW5kIHRoZSBsb2NhdGlvbiB0byB0aGUgc2VydmVyIGZvclxuICogc29tZSByZWFzb24sIGVpdGhlciBiZWNhdXNlIHlvdSBkbyBjYW5ub3QgY29uZmlndXJlIGl0IG9yIHRoZSBVUkwgc3BhY2UgaXNcbiAqIHJlc2VydmVkIGZvciBzb21ldGhpbmcgZWxzZS5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9yZW1peC1ydW4vaGlzdG9yeS90cmVlL21haW4vZG9jcy9hcGktcmVmZXJlbmNlLm1kI2NyZWF0ZWhhc2hoaXN0b3J5XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUhhc2hIaXN0b3J5KG9wdGlvbnMpIHtcbiAgaWYgKG9wdGlvbnMgPT09IHZvaWQgMCkge1xuICAgIG9wdGlvbnMgPSB7fTtcbiAgfVxuICBmdW5jdGlvbiBjcmVhdGVIYXNoTG9jYXRpb24od2luZG93LCBnbG9iYWxIaXN0b3J5KSB7XG4gICAgbGV0IHtcbiAgICAgIHBhdGhuYW1lID0gXCIvXCIsXG4gICAgICBzZWFyY2ggPSBcIlwiLFxuICAgICAgaGFzaCA9IFwiXCJcbiAgICB9ID0gcGFyc2VQYXRoKHdpbmRvdy5sb2NhdGlvbi5oYXNoLnN1YnN0cigxKSk7XG4gICAgcmV0dXJuIGNyZWF0ZUxvY2F0aW9uKFwiXCIsIHtcbiAgICAgIHBhdGhuYW1lLFxuICAgICAgc2VhcmNoLFxuICAgICAgaGFzaFxuICAgIH0sXG4gICAgLy8gc3RhdGUgZGVmYXVsdHMgdG8gYG51bGxgIGJlY2F1c2UgYHdpbmRvdy5oaXN0b3J5LnN0YXRlYCBkb2VzXG4gICAgZ2xvYmFsSGlzdG9yeS5zdGF0ZSAmJiBnbG9iYWxIaXN0b3J5LnN0YXRlLnVzciB8fCBudWxsLCBnbG9iYWxIaXN0b3J5LnN0YXRlICYmIGdsb2JhbEhpc3Rvcnkuc3RhdGUua2V5IHx8IFwiZGVmYXVsdFwiKTtcbiAgfVxuICBmdW5jdGlvbiBjcmVhdGVIYXNoSHJlZih3aW5kb3csIHRvKSB7XG4gICAgbGV0IGJhc2UgPSB3aW5kb3cuZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcImJhc2VcIik7XG4gICAgbGV0IGhyZWYgPSBcIlwiO1xuICAgIGlmIChiYXNlICYmIGJhc2UuZ2V0QXR0cmlidXRlKFwiaHJlZlwiKSkge1xuICAgICAgbGV0IHVybCA9IHdpbmRvdy5sb2NhdGlvbi5ocmVmO1xuICAgICAgbGV0IGhhc2hJbmRleCA9IHVybC5pbmRleE9mKFwiI1wiKTtcbiAgICAgIGhyZWYgPSBoYXNoSW5kZXggPT09IC0xID8gdXJsIDogdXJsLnNsaWNlKDAsIGhhc2hJbmRleCk7XG4gICAgfVxuICAgIHJldHVybiBocmVmICsgXCIjXCIgKyAodHlwZW9mIHRvID09PSBcInN0cmluZ1wiID8gdG8gOiBjcmVhdGVQYXRoKHRvKSk7XG4gIH1cbiAgZnVuY3Rpb24gdmFsaWRhdGVIYXNoTG9jYXRpb24obG9jYXRpb24sIHRvKSB7XG4gICAgd2FybmluZyhsb2NhdGlvbi5wYXRobmFtZS5jaGFyQXQoMCkgPT09IFwiL1wiLCBcInJlbGF0aXZlIHBhdGhuYW1lcyBhcmUgbm90IHN1cHBvcnRlZCBpbiBoYXNoIGhpc3RvcnkucHVzaChcIiArIEpTT04uc3RyaW5naWZ5KHRvKSArIFwiKVwiKTtcbiAgfVxuICByZXR1cm4gZ2V0VXJsQmFzZWRIaXN0b3J5KGNyZWF0ZUhhc2hMb2NhdGlvbiwgY3JlYXRlSGFzaEhyZWYsIHZhbGlkYXRlSGFzaExvY2F0aW9uLCBvcHRpb25zKTtcbn1cbmZ1bmN0aW9uIGludmFyaWFudCh2YWx1ZSwgbWVzc2FnZSkge1xuICBpZiAodmFsdWUgPT09IGZhbHNlIHx8IHZhbHVlID09PSBudWxsIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJ1bmRlZmluZWRcIikge1xuICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgfVxufVxuZnVuY3Rpb24gd2FybmluZyhjb25kLCBtZXNzYWdlKSB7XG4gIGlmICghY29uZCkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgaWYgKHR5cGVvZiBjb25zb2xlICE9PSBcInVuZGVmaW5lZFwiKSBjb25zb2xlLndhcm4obWVzc2FnZSk7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFdlbGNvbWUgdG8gZGVidWdnaW5nIGhpc3RvcnkhXG4gICAgICAvL1xuICAgICAgLy8gVGhpcyBlcnJvciBpcyB0aHJvd24gYXMgYSBjb252ZW5pZW5jZSBzbyB5b3UgY2FuIG1vcmUgZWFzaWx5XG4gICAgICAvLyBmaW5kIHRoZSBzb3VyY2UgZm9yIGEgd2FybmluZyB0aGF0IGFwcGVhcnMgaW4gdGhlIGNvbnNvbGUgYnlcbiAgICAgIC8vIGVuYWJsaW5nIFwicGF1c2Ugb24gZXhjZXB0aW9uc1wiIGluIHlvdXIgSmF2YVNjcmlwdCBkZWJ1Z2dlci5cbiAgICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1lbXB0eVxuICAgIH0gY2F0Y2ggKGUpIHt9XG4gIH1cbn1cbmZ1bmN0aW9uIGNyZWF0ZUtleSgpIHtcbiAgcmV0dXJuIE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCA4KTtcbn1cbi8qKlxuICogRm9yIGJyb3dzZXItYmFzZWQgaGlzdG9yaWVzLCB3ZSBjb21iaW5lIHRoZSBzdGF0ZSBhbmQga2V5IGludG8gYW4gb2JqZWN0XG4gKi9cbmZ1bmN0aW9uIGdldEhpc3RvcnlTdGF0ZShsb2NhdGlvbiwgaW5kZXgpIHtcbiAgcmV0dXJuIHtcbiAgICB1c3I6IGxvY2F0aW9uLnN0YXRlLFxuICAgIGtleTogbG9jYXRpb24ua2V5LFxuICAgIGlkeDogaW5kZXhcbiAgfTtcbn1cbi8qKlxuICogQ3JlYXRlcyBhIExvY2F0aW9uIG9iamVjdCB3aXRoIGEgdW5pcXVlIGtleSBmcm9tIHRoZSBnaXZlbiBQYXRoXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUxvY2F0aW9uKGN1cnJlbnQsIHRvLCBzdGF0ZSwga2V5KSB7XG4gIGlmIChzdGF0ZSA9PT0gdm9pZCAwKSB7XG4gICAgc3RhdGUgPSBudWxsO1xuICB9XG4gIGxldCBsb2NhdGlvbiA9IF9leHRlbmRzKHtcbiAgICBwYXRobmFtZTogdHlwZW9mIGN1cnJlbnQgPT09IFwic3RyaW5nXCIgPyBjdXJyZW50IDogY3VycmVudC5wYXRobmFtZSxcbiAgICBzZWFyY2g6IFwiXCIsXG4gICAgaGFzaDogXCJcIlxuICB9LCB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyBwYXJzZVBhdGgodG8pIDogdG8sIHtcbiAgICBzdGF0ZSxcbiAgICAvLyBUT0RPOiBUaGlzIGNvdWxkIGJlIGNsZWFuZWQgdXAuICBwdXNoL3JlcGxhY2Ugc2hvdWxkIHByb2JhYmx5IGp1c3QgdGFrZVxuICAgIC8vIGZ1bGwgTG9jYXRpb25zIG5vdyBhbmQgYXZvaWQgdGhlIG5lZWQgdG8gcnVuIHRocm91Z2ggdGhpcyBmbG93IGF0IGFsbFxuICAgIC8vIEJ1dCB0aGF0J3MgYSBwcmV0dHkgYmlnIHJlZmFjdG9yIHRvIHRoZSBjdXJyZW50IHRlc3Qgc3VpdGUgc28gZ29pbmcgdG9cbiAgICAvLyBrZWVwIGFzIGlzIGZvciB0aGUgdGltZSBiZWluZyBhbmQganVzdCBsZXQgYW55IGluY29taW5nIGtleXMgdGFrZSBwcmVjZWRlbmNlXG4gICAga2V5OiB0byAmJiB0by5rZXkgfHwga2V5IHx8IGNyZWF0ZUtleSgpXG4gIH0pO1xuICByZXR1cm4gbG9jYXRpb247XG59XG4vKipcbiAqIENyZWF0ZXMgYSBzdHJpbmcgVVJMIHBhdGggZnJvbSB0aGUgZ2l2ZW4gcGF0aG5hbWUsIHNlYXJjaCwgYW5kIGhhc2ggY29tcG9uZW50cy5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlUGF0aChfcmVmKSB7XG4gIGxldCB7XG4gICAgcGF0aG5hbWUgPSBcIi9cIixcbiAgICBzZWFyY2ggPSBcIlwiLFxuICAgIGhhc2ggPSBcIlwiXG4gIH0gPSBfcmVmO1xuICBpZiAoc2VhcmNoICYmIHNlYXJjaCAhPT0gXCI/XCIpIHBhdGhuYW1lICs9IHNlYXJjaC5jaGFyQXQoMCkgPT09IFwiP1wiID8gc2VhcmNoIDogXCI/XCIgKyBzZWFyY2g7XG4gIGlmIChoYXNoICYmIGhhc2ggIT09IFwiI1wiKSBwYXRobmFtZSArPSBoYXNoLmNoYXJBdCgwKSA9PT0gXCIjXCIgPyBoYXNoIDogXCIjXCIgKyBoYXNoO1xuICByZXR1cm4gcGF0aG5hbWU7XG59XG4vKipcbiAqIFBhcnNlcyBhIHN0cmluZyBVUkwgcGF0aCBpbnRvIGl0cyBzZXBhcmF0ZSBwYXRobmFtZSwgc2VhcmNoLCBhbmQgaGFzaCBjb21wb25lbnRzLlxuICovXG5mdW5jdGlvbiBwYXJzZVBhdGgocGF0aCkge1xuICBsZXQgcGFyc2VkUGF0aCA9IHt9O1xuICBpZiAocGF0aCkge1xuICAgIGxldCBoYXNoSW5kZXggPSBwYXRoLmluZGV4T2YoXCIjXCIpO1xuICAgIGlmIChoYXNoSW5kZXggPj0gMCkge1xuICAgICAgcGFyc2VkUGF0aC5oYXNoID0gcGF0aC5zdWJzdHIoaGFzaEluZGV4KTtcbiAgICAgIHBhdGggPSBwYXRoLnN1YnN0cigwLCBoYXNoSW5kZXgpO1xuICAgIH1cbiAgICBsZXQgc2VhcmNoSW5kZXggPSBwYXRoLmluZGV4T2YoXCI/XCIpO1xuICAgIGlmIChzZWFyY2hJbmRleCA+PSAwKSB7XG4gICAgICBwYXJzZWRQYXRoLnNlYXJjaCA9IHBhdGguc3Vic3RyKHNlYXJjaEluZGV4KTtcbiAgICAgIHBhdGggPSBwYXRoLnN1YnN0cigwLCBzZWFyY2hJbmRleCk7XG4gICAgfVxuICAgIGlmIChwYXRoKSB7XG4gICAgICBwYXJzZWRQYXRoLnBhdGhuYW1lID0gcGF0aDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHBhcnNlZFBhdGg7XG59XG5mdW5jdGlvbiBnZXRVcmxCYXNlZEhpc3RvcnkoZ2V0TG9jYXRpb24sIGNyZWF0ZUhyZWYsIHZhbGlkYXRlTG9jYXRpb24sIG9wdGlvbnMpIHtcbiAgaWYgKG9wdGlvbnMgPT09IHZvaWQgMCkge1xuICAgIG9wdGlvbnMgPSB7fTtcbiAgfVxuICBsZXQge1xuICAgIHdpbmRvdyA9IGRvY3VtZW50LmRlZmF1bHRWaWV3LFxuICAgIHY1Q29tcGF0ID0gZmFsc2VcbiAgfSA9IG9wdGlvbnM7XG4gIGxldCBnbG9iYWxIaXN0b3J5ID0gd2luZG93Lmhpc3Rvcnk7XG4gIGxldCBhY3Rpb24gPSBBY3Rpb24uUG9wO1xuICBsZXQgbGlzdGVuZXIgPSBudWxsO1xuICBsZXQgaW5kZXggPSBnZXRJbmRleCgpO1xuICAvLyBJbmRleCBzaG91bGQgb25seSBiZSBudWxsIHdoZW4gd2UgaW5pdGlhbGl6ZS4gSWYgbm90LCBpdCdzIGJlY2F1c2UgdGhlXG4gIC8vIHVzZXIgY2FsbGVkIGhpc3RvcnkucHVzaFN0YXRlIG9yIGhpc3RvcnkucmVwbGFjZVN0YXRlIGRpcmVjdGx5LCBpbiB3aGljaFxuICAvLyBjYXNlIHdlIHNob3VsZCBsb2cgYSB3YXJuaW5nIGFzIGl0IHdpbGwgcmVzdWx0IGluIGJ1Z3MuXG4gIGlmIChpbmRleCA9PSBudWxsKSB7XG4gICAgaW5kZXggPSAwO1xuICAgIGdsb2JhbEhpc3RvcnkucmVwbGFjZVN0YXRlKF9leHRlbmRzKHt9LCBnbG9iYWxIaXN0b3J5LnN0YXRlLCB7XG4gICAgICBpZHg6IGluZGV4XG4gICAgfSksIFwiXCIpO1xuICB9XG4gIGZ1bmN0aW9uIGdldEluZGV4KCkge1xuICAgIGxldCBzdGF0ZSA9IGdsb2JhbEhpc3Rvcnkuc3RhdGUgfHwge1xuICAgICAgaWR4OiBudWxsXG4gICAgfTtcbiAgICByZXR1cm4gc3RhdGUuaWR4O1xuICB9XG4gIGZ1bmN0aW9uIGhhbmRsZVBvcCgpIHtcbiAgICBhY3Rpb24gPSBBY3Rpb24uUG9wO1xuICAgIGxldCBuZXh0SW5kZXggPSBnZXRJbmRleCgpO1xuICAgIGxldCBkZWx0YSA9IG5leHRJbmRleCA9PSBudWxsID8gbnVsbCA6IG5leHRJbmRleCAtIGluZGV4O1xuICAgIGluZGV4ID0gbmV4dEluZGV4O1xuICAgIGlmIChsaXN0ZW5lcikge1xuICAgICAgbGlzdGVuZXIoe1xuICAgICAgICBhY3Rpb24sXG4gICAgICAgIGxvY2F0aW9uOiBoaXN0b3J5LmxvY2F0aW9uLFxuICAgICAgICBkZWx0YVxuICAgICAgfSk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIHB1c2godG8sIHN0YXRlKSB7XG4gICAgYWN0aW9uID0gQWN0aW9uLlB1c2g7XG4gICAgbGV0IGxvY2F0aW9uID0gY3JlYXRlTG9jYXRpb24oaGlzdG9yeS5sb2NhdGlvbiwgdG8sIHN0YXRlKTtcbiAgICBpZiAodmFsaWRhdGVMb2NhdGlvbikgdmFsaWRhdGVMb2NhdGlvbihsb2NhdGlvbiwgdG8pO1xuICAgIGluZGV4ID0gZ2V0SW5kZXgoKSArIDE7XG4gICAgbGV0IGhpc3RvcnlTdGF0ZSA9IGdldEhpc3RvcnlTdGF0ZShsb2NhdGlvbiwgaW5kZXgpO1xuICAgIGxldCB1cmwgPSBoaXN0b3J5LmNyZWF0ZUhyZWYobG9jYXRpb24pO1xuICAgIC8vIHRyeS4uLmNhdGNoIGJlY2F1c2UgaU9TIGxpbWl0cyB1cyB0byAxMDAgcHVzaFN0YXRlIGNhbGxzIDovXG4gICAgdHJ5IHtcbiAgICAgIGdsb2JhbEhpc3RvcnkucHVzaFN0YXRlKGhpc3RvcnlTdGF0ZSwgXCJcIiwgdXJsKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gSWYgdGhlIGV4Y2VwdGlvbiBpcyBiZWNhdXNlIGBzdGF0ZWAgY2FuJ3QgYmUgc2VyaWFsaXplZCwgbGV0IHRoYXQgdGhyb3dcbiAgICAgIC8vIG91dHdhcmRzIGp1c3QgbGlrZSBhIHJlcGxhY2UgY2FsbCB3b3VsZCBzbyB0aGUgZGV2IGtub3dzIHRoZSBjYXVzZVxuICAgICAgLy8gaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvbmF2LWhpc3RvcnktYXBpcy5odG1sI3NoYXJlZC1oaXN0b3J5LXB1c2gvcmVwbGFjZS1zdGF0ZS1zdGVwc1xuICAgICAgLy8gaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2Uvc3RydWN0dXJlZC1kYXRhLmh0bWwjc3RydWN0dXJlZHNlcmlhbGl6ZWludGVybmFsXG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBET01FeGNlcHRpb24gJiYgZXJyb3IubmFtZSA9PT0gXCJEYXRhQ2xvbmVFcnJvclwiKSB7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgICAgLy8gVGhleSBhcmUgZ29pbmcgdG8gbG9zZSBzdGF0ZSBoZXJlLCBidXQgdGhlcmUgaXMgbm8gcmVhbFxuICAgICAgLy8gd2F5IHRvIHdhcm4gdGhlbSBhYm91dCBpdCBzaW5jZSB0aGUgcGFnZSB3aWxsIHJlZnJlc2guLi5cbiAgICAgIHdpbmRvdy5sb2NhdGlvbi5hc3NpZ24odXJsKTtcbiAgICB9XG4gICAgaWYgKHY1Q29tcGF0ICYmIGxpc3RlbmVyKSB7XG4gICAgICBsaXN0ZW5lcih7XG4gICAgICAgIGFjdGlvbixcbiAgICAgICAgbG9jYXRpb246IGhpc3RvcnkubG9jYXRpb24sXG4gICAgICAgIGRlbHRhOiAxXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gcmVwbGFjZSh0bywgc3RhdGUpIHtcbiAgICBhY3Rpb24gPSBBY3Rpb24uUmVwbGFjZTtcbiAgICBsZXQgbG9jYXRpb24gPSBjcmVhdGVMb2NhdGlvbihoaXN0b3J5LmxvY2F0aW9uLCB0bywgc3RhdGUpO1xuICAgIGlmICh2YWxpZGF0ZUxvY2F0aW9uKSB2YWxpZGF0ZUxvY2F0aW9uKGxvY2F0aW9uLCB0byk7XG4gICAgaW5kZXggPSBnZXRJbmRleCgpO1xuICAgIGxldCBoaXN0b3J5U3RhdGUgPSBnZXRIaXN0b3J5U3RhdGUobG9jYXRpb24sIGluZGV4KTtcbiAgICBsZXQgdXJsID0gaGlzdG9yeS5jcmVhdGVIcmVmKGxvY2F0aW9uKTtcbiAgICBnbG9iYWxIaXN0b3J5LnJlcGxhY2VTdGF0ZShoaXN0b3J5U3RhdGUsIFwiXCIsIHVybCk7XG4gICAgaWYgKHY1Q29tcGF0ICYmIGxpc3RlbmVyKSB7XG4gICAgICBsaXN0ZW5lcih7XG4gICAgICAgIGFjdGlvbixcbiAgICAgICAgbG9jYXRpb246IGhpc3RvcnkubG9jYXRpb24sXG4gICAgICAgIGRlbHRhOiAwXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gY3JlYXRlVVJMKHRvKSB7XG4gICAgLy8gd2luZG93LmxvY2F0aW9uLm9yaWdpbiBpcyBcIm51bGxcIiAodGhlIGxpdGVyYWwgc3RyaW5nIHZhbHVlKSBpbiBGaXJlZm94XG4gICAgLy8gdW5kZXIgY2VydGFpbiBjb25kaXRpb25zLCBub3RhYmx5IHdoZW4gc2VydmluZyBmcm9tIGEgbG9jYWwgSFRNTCBmaWxlXG4gICAgLy8gU2VlIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTg3ODI5N1xuICAgIGxldCBiYXNlID0gd2luZG93LmxvY2F0aW9uLm9yaWdpbiAhPT0gXCJudWxsXCIgPyB3aW5kb3cubG9jYXRpb24ub3JpZ2luIDogd2luZG93LmxvY2F0aW9uLmhyZWY7XG4gICAgbGV0IGhyZWYgPSB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyB0byA6IGNyZWF0ZVBhdGgodG8pO1xuICAgIGludmFyaWFudChiYXNlLCBcIk5vIHdpbmRvdy5sb2NhdGlvbi4ob3JpZ2lufGhyZWYpIGF2YWlsYWJsZSB0byBjcmVhdGUgVVJMIGZvciBocmVmOiBcIiArIGhyZWYpO1xuICAgIHJldHVybiBuZXcgVVJMKGhyZWYsIGJhc2UpO1xuICB9XG4gIGxldCBoaXN0b3J5ID0ge1xuICAgIGdldCBhY3Rpb24oKSB7XG4gICAgICByZXR1cm4gYWN0aW9uO1xuICAgIH0sXG4gICAgZ2V0IGxvY2F0aW9uKCkge1xuICAgICAgcmV0dXJuIGdldExvY2F0aW9uKHdpbmRvdywgZ2xvYmFsSGlzdG9yeSk7XG4gICAgfSxcbiAgICBsaXN0ZW4oZm4pIHtcbiAgICAgIGlmIChsaXN0ZW5lcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJBIGhpc3Rvcnkgb25seSBhY2NlcHRzIG9uZSBhY3RpdmUgbGlzdGVuZXJcIik7XG4gICAgICB9XG4gICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihQb3BTdGF0ZUV2ZW50VHlwZSwgaGFuZGxlUG9wKTtcbiAgICAgIGxpc3RlbmVyID0gZm47XG4gICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICB3aW5kb3cucmVtb3ZlRXZlbnRMaXN0ZW5lcihQb3BTdGF0ZUV2ZW50VHlwZSwgaGFuZGxlUG9wKTtcbiAgICAgICAgbGlzdGVuZXIgPSBudWxsO1xuICAgICAgfTtcbiAgICB9LFxuICAgIGNyZWF0ZUhyZWYodG8pIHtcbiAgICAgIHJldHVybiBjcmVhdGVIcmVmKHdpbmRvdywgdG8pO1xuICAgIH0sXG4gICAgY3JlYXRlVVJMLFxuICAgIGVuY29kZUxvY2F0aW9uKHRvKSB7XG4gICAgICAvLyBFbmNvZGUgYSBMb2NhdGlvbiB0aGUgc2FtZSB3YXkgd2luZG93LmxvY2F0aW9uIHdvdWxkXG4gICAgICBsZXQgdXJsID0gY3JlYXRlVVJMKHRvKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHBhdGhuYW1lOiB1cmwucGF0aG5hbWUsXG4gICAgICAgIHNlYXJjaDogdXJsLnNlYXJjaCxcbiAgICAgICAgaGFzaDogdXJsLmhhc2hcbiAgICAgIH07XG4gICAgfSxcbiAgICBwdXNoLFxuICAgIHJlcGxhY2UsXG4gICAgZ28obikge1xuICAgICAgcmV0dXJuIGdsb2JhbEhpc3RvcnkuZ28obik7XG4gICAgfVxuICB9O1xuICByZXR1cm4gaGlzdG9yeTtcbn1cbi8vI2VuZHJlZ2lvblxuXG52YXIgUmVzdWx0VHlwZTtcbihmdW5jdGlvbiAoUmVzdWx0VHlwZSkge1xuICBSZXN1bHRUeXBlW1wiZGF0YVwiXSA9IFwiZGF0YVwiO1xuICBSZXN1bHRUeXBlW1wiZGVmZXJyZWRcIl0gPSBcImRlZmVycmVkXCI7XG4gIFJlc3VsdFR5cGVbXCJyZWRpcmVjdFwiXSA9IFwicmVkaXJlY3RcIjtcbiAgUmVzdWx0VHlwZVtcImVycm9yXCJdID0gXCJlcnJvclwiO1xufSkoUmVzdWx0VHlwZSB8fCAoUmVzdWx0VHlwZSA9IHt9KSk7XG5jb25zdCBpbW11dGFibGVSb3V0ZUtleXMgPSBuZXcgU2V0KFtcImxhenlcIiwgXCJjYXNlU2Vuc2l0aXZlXCIsIFwicGF0aFwiLCBcImlkXCIsIFwiaW5kZXhcIiwgXCJjaGlsZHJlblwiXSk7XG5mdW5jdGlvbiBpc0luZGV4Um91dGUocm91dGUpIHtcbiAgcmV0dXJuIHJvdXRlLmluZGV4ID09PSB0cnVlO1xufVxuLy8gV2FsayB0aGUgcm91dGUgdHJlZSBnZW5lcmF0aW5nIHVuaXF1ZSBJRHMgd2hlcmUgbmVjZXNzYXJ5IHNvIHdlIGFyZSB3b3JraW5nXG4vLyBzb2xlbHkgd2l0aCBBZ25vc3RpY0RhdGFSb3V0ZU9iamVjdCdzIHdpdGhpbiB0aGUgUm91dGVyXG5mdW5jdGlvbiBjb252ZXJ0Um91dGVzVG9EYXRhUm91dGVzKHJvdXRlcywgbWFwUm91dGVQcm9wZXJ0aWVzLCBwYXJlbnRQYXRoLCBtYW5pZmVzdCkge1xuICBpZiAocGFyZW50UGF0aCA9PT0gdm9pZCAwKSB7XG4gICAgcGFyZW50UGF0aCA9IFtdO1xuICB9XG4gIGlmIChtYW5pZmVzdCA9PT0gdm9pZCAwKSB7XG4gICAgbWFuaWZlc3QgPSB7fTtcbiAgfVxuICByZXR1cm4gcm91dGVzLm1hcCgocm91dGUsIGluZGV4KSA9PiB7XG4gICAgbGV0IHRyZWVQYXRoID0gWy4uLnBhcmVudFBhdGgsIGluZGV4XTtcbiAgICBsZXQgaWQgPSB0eXBlb2Ygcm91dGUuaWQgPT09IFwic3RyaW5nXCIgPyByb3V0ZS5pZCA6IHRyZWVQYXRoLmpvaW4oXCItXCIpO1xuICAgIGludmFyaWFudChyb3V0ZS5pbmRleCAhPT0gdHJ1ZSB8fCAhcm91dGUuY2hpbGRyZW4sIFwiQ2Fubm90IHNwZWNpZnkgY2hpbGRyZW4gb24gYW4gaW5kZXggcm91dGVcIik7XG4gICAgaW52YXJpYW50KCFtYW5pZmVzdFtpZF0sIFwiRm91bmQgYSByb3V0ZSBpZCBjb2xsaXNpb24gb24gaWQgXFxcIlwiICsgaWQgKyBcIlxcXCIuICBSb3V0ZSBcIiArIFwiaWQncyBtdXN0IGJlIGdsb2JhbGx5IHVuaXF1ZSB3aXRoaW4gRGF0YSBSb3V0ZXIgdXNhZ2VzXCIpO1xuICAgIGlmIChpc0luZGV4Um91dGUocm91dGUpKSB7XG4gICAgICBsZXQgaW5kZXhSb3V0ZSA9IF9leHRlbmRzKHt9LCByb3V0ZSwgbWFwUm91dGVQcm9wZXJ0aWVzKHJvdXRlKSwge1xuICAgICAgICBpZFxuICAgICAgfSk7XG4gICAgICBtYW5pZmVzdFtpZF0gPSBpbmRleFJvdXRlO1xuICAgICAgcmV0dXJuIGluZGV4Um91dGU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCBwYXRoT3JMYXlvdXRSb3V0ZSA9IF9leHRlbmRzKHt9LCByb3V0ZSwgbWFwUm91dGVQcm9wZXJ0aWVzKHJvdXRlKSwge1xuICAgICAgICBpZCxcbiAgICAgICAgY2hpbGRyZW46IHVuZGVmaW5lZFxuICAgICAgfSk7XG4gICAgICBtYW5pZmVzdFtpZF0gPSBwYXRoT3JMYXlvdXRSb3V0ZTtcbiAgICAgIGlmIChyb3V0ZS5jaGlsZHJlbikge1xuICAgICAgICBwYXRoT3JMYXlvdXRSb3V0ZS5jaGlsZHJlbiA9IGNvbnZlcnRSb3V0ZXNUb0RhdGFSb3V0ZXMocm91dGUuY2hpbGRyZW4sIG1hcFJvdXRlUHJvcGVydGllcywgdHJlZVBhdGgsIG1hbmlmZXN0KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBwYXRoT3JMYXlvdXRSb3V0ZTtcbiAgICB9XG4gIH0pO1xufVxuLyoqXG4gKiBNYXRjaGVzIHRoZSBnaXZlbiByb3V0ZXMgdG8gYSBsb2NhdGlvbiBhbmQgcmV0dXJucyB0aGUgbWF0Y2ggZGF0YS5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL3V0aWxzL21hdGNoLXJvdXRlc1xuICovXG5mdW5jdGlvbiBtYXRjaFJvdXRlcyhyb3V0ZXMsIGxvY2F0aW9uQXJnLCBiYXNlbmFtZSkge1xuICBpZiAoYmFzZW5hbWUgPT09IHZvaWQgMCkge1xuICAgIGJhc2VuYW1lID0gXCIvXCI7XG4gIH1cbiAgbGV0IGxvY2F0aW9uID0gdHlwZW9mIGxvY2F0aW9uQXJnID09PSBcInN0cmluZ1wiID8gcGFyc2VQYXRoKGxvY2F0aW9uQXJnKSA6IGxvY2F0aW9uQXJnO1xuICBsZXQgcGF0aG5hbWUgPSBzdHJpcEJhc2VuYW1lKGxvY2F0aW9uLnBhdGhuYW1lIHx8IFwiL1wiLCBiYXNlbmFtZSk7XG4gIGlmIChwYXRobmFtZSA9PSBudWxsKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgbGV0IGJyYW5jaGVzID0gZmxhdHRlblJvdXRlcyhyb3V0ZXMpO1xuICByYW5rUm91dGVCcmFuY2hlcyhicmFuY2hlcyk7XG4gIGxldCBtYXRjaGVzID0gbnVsbDtcbiAgZm9yIChsZXQgaSA9IDA7IG1hdGNoZXMgPT0gbnVsbCAmJiBpIDwgYnJhbmNoZXMubGVuZ3RoOyArK2kpIHtcbiAgICBtYXRjaGVzID0gbWF0Y2hSb3V0ZUJyYW5jaChicmFuY2hlc1tpXSxcbiAgICAvLyBJbmNvbWluZyBwYXRobmFtZXMgYXJlIGdlbmVyYWxseSBlbmNvZGVkIGZyb20gZWl0aGVyIHdpbmRvdy5sb2NhdGlvblxuICAgIC8vIG9yIGZyb20gcm91dGVyLm5hdmlnYXRlLCBidXQgd2Ugd2FudCB0byBtYXRjaCBhZ2FpbnN0IHRoZSB1bmVuY29kZWRcbiAgICAvLyBwYXRocyBpbiB0aGUgcm91dGUgZGVmaW5pdGlvbnMuICBNZW1vcnkgcm91dGVyIGxvY2F0aW9ucyB3b24ndCBiZVxuICAgIC8vIGVuY29kZWQgaGVyZSBidXQgdGhlcmUgYWxzbyBzaG91bGRuJ3QgYmUgYW55dGhpbmcgdG8gZGVjb2RlIHNvIHRoaXNcbiAgICAvLyBzaG91bGQgYmUgYSBzYWZlIG9wZXJhdGlvbi4gIFRoaXMgYXZvaWRzIG5lZWRpbmcgbWF0Y2hSb3V0ZXMgdG8gYmVcbiAgICAvLyBoaXN0b3J5LWF3YXJlLlxuICAgIHNhZmVseURlY29kZVVSSShwYXRobmFtZSkpO1xuICB9XG4gIHJldHVybiBtYXRjaGVzO1xufVxuZnVuY3Rpb24gZmxhdHRlblJvdXRlcyhyb3V0ZXMsIGJyYW5jaGVzLCBwYXJlbnRzTWV0YSwgcGFyZW50UGF0aCkge1xuICBpZiAoYnJhbmNoZXMgPT09IHZvaWQgMCkge1xuICAgIGJyYW5jaGVzID0gW107XG4gIH1cbiAgaWYgKHBhcmVudHNNZXRhID09PSB2b2lkIDApIHtcbiAgICBwYXJlbnRzTWV0YSA9IFtdO1xuICB9XG4gIGlmIChwYXJlbnRQYXRoID09PSB2b2lkIDApIHtcbiAgICBwYXJlbnRQYXRoID0gXCJcIjtcbiAgfVxuICBsZXQgZmxhdHRlblJvdXRlID0gKHJvdXRlLCBpbmRleCwgcmVsYXRpdmVQYXRoKSA9PiB7XG4gICAgbGV0IG1ldGEgPSB7XG4gICAgICByZWxhdGl2ZVBhdGg6IHJlbGF0aXZlUGF0aCA9PT0gdW5kZWZpbmVkID8gcm91dGUucGF0aCB8fCBcIlwiIDogcmVsYXRpdmVQYXRoLFxuICAgICAgY2FzZVNlbnNpdGl2ZTogcm91dGUuY2FzZVNlbnNpdGl2ZSA9PT0gdHJ1ZSxcbiAgICAgIGNoaWxkcmVuSW5kZXg6IGluZGV4LFxuICAgICAgcm91dGVcbiAgICB9O1xuICAgIGlmIChtZXRhLnJlbGF0aXZlUGF0aC5zdGFydHNXaXRoKFwiL1wiKSkge1xuICAgICAgaW52YXJpYW50KG1ldGEucmVsYXRpdmVQYXRoLnN0YXJ0c1dpdGgocGFyZW50UGF0aCksIFwiQWJzb2x1dGUgcm91dGUgcGF0aCBcXFwiXCIgKyBtZXRhLnJlbGF0aXZlUGF0aCArIFwiXFxcIiBuZXN0ZWQgdW5kZXIgcGF0aCBcIiArIChcIlxcXCJcIiArIHBhcmVudFBhdGggKyBcIlxcXCIgaXMgbm90IHZhbGlkLiBBbiBhYnNvbHV0ZSBjaGlsZCByb3V0ZSBwYXRoIFwiKSArIFwibXVzdCBzdGFydCB3aXRoIHRoZSBjb21iaW5lZCBwYXRoIG9mIGFsbCBpdHMgcGFyZW50IHJvdXRlcy5cIik7XG4gICAgICBtZXRhLnJlbGF0aXZlUGF0aCA9IG1ldGEucmVsYXRpdmVQYXRoLnNsaWNlKHBhcmVudFBhdGgubGVuZ3RoKTtcbiAgICB9XG4gICAgbGV0IHBhdGggPSBqb2luUGF0aHMoW3BhcmVudFBhdGgsIG1ldGEucmVsYXRpdmVQYXRoXSk7XG4gICAgbGV0IHJvdXRlc01ldGEgPSBwYXJlbnRzTWV0YS5jb25jYXQobWV0YSk7XG4gICAgLy8gQWRkIHRoZSBjaGlsZHJlbiBiZWZvcmUgYWRkaW5nIHRoaXMgcm91dGUgdG8gdGhlIGFycmF5IHNvIHdlIHRyYXZlcnNlIHRoZVxuICAgIC8vIHJvdXRlIHRyZWUgZGVwdGgtZmlyc3QgYW5kIGNoaWxkIHJvdXRlcyBhcHBlYXIgYmVmb3JlIHRoZWlyIHBhcmVudHMgaW5cbiAgICAvLyB0aGUgXCJmbGF0dGVuZWRcIiB2ZXJzaW9uLlxuICAgIGlmIChyb3V0ZS5jaGlsZHJlbiAmJiByb3V0ZS5jaGlsZHJlbi5sZW5ndGggPiAwKSB7XG4gICAgICBpbnZhcmlhbnQoXG4gICAgICAvLyBPdXIgdHlwZXMga25vdyBiZXR0ZXIsIGJ1dCBydW50aW1lIEpTIG1heSBub3QhXG4gICAgICAvLyBAdHMtZXhwZWN0LWVycm9yXG4gICAgICByb3V0ZS5pbmRleCAhPT0gdHJ1ZSwgXCJJbmRleCByb3V0ZXMgbXVzdCBub3QgaGF2ZSBjaGlsZCByb3V0ZXMuIFBsZWFzZSByZW1vdmUgXCIgKyAoXCJhbGwgY2hpbGQgcm91dGVzIGZyb20gcm91dGUgcGF0aCBcXFwiXCIgKyBwYXRoICsgXCJcXFwiLlwiKSk7XG4gICAgICBmbGF0dGVuUm91dGVzKHJvdXRlLmNoaWxkcmVuLCBicmFuY2hlcywgcm91dGVzTWV0YSwgcGF0aCk7XG4gICAgfVxuICAgIC8vIFJvdXRlcyB3aXRob3V0IGEgcGF0aCBzaG91bGRuJ3QgZXZlciBtYXRjaCBieSB0aGVtc2VsdmVzIHVubGVzcyB0aGV5IGFyZVxuICAgIC8vIGluZGV4IHJvdXRlcywgc28gZG9uJ3QgYWRkIHRoZW0gdG8gdGhlIGxpc3Qgb2YgcG9zc2libGUgYnJhbmNoZXMuXG4gICAgaWYgKHJvdXRlLnBhdGggPT0gbnVsbCAmJiAhcm91dGUuaW5kZXgpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgYnJhbmNoZXMucHVzaCh7XG4gICAgICBwYXRoLFxuICAgICAgc2NvcmU6IGNvbXB1dGVTY29yZShwYXRoLCByb3V0ZS5pbmRleCksXG4gICAgICByb3V0ZXNNZXRhXG4gICAgfSk7XG4gIH07XG4gIHJvdXRlcy5mb3JFYWNoKChyb3V0ZSwgaW5kZXgpID0+IHtcbiAgICB2YXIgX3JvdXRlJHBhdGg7XG4gICAgLy8gY29hcnNlLWdyYWluIGNoZWNrIGZvciBvcHRpb25hbCBwYXJhbXNcbiAgICBpZiAocm91dGUucGF0aCA9PT0gXCJcIiB8fCAhKChfcm91dGUkcGF0aCA9IHJvdXRlLnBhdGgpICE9IG51bGwgJiYgX3JvdXRlJHBhdGguaW5jbHVkZXMoXCI/XCIpKSkge1xuICAgICAgZmxhdHRlblJvdXRlKHJvdXRlLCBpbmRleCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZvciAobGV0IGV4cGxvZGVkIG9mIGV4cGxvZGVPcHRpb25hbFNlZ21lbnRzKHJvdXRlLnBhdGgpKSB7XG4gICAgICAgIGZsYXR0ZW5Sb3V0ZShyb3V0ZSwgaW5kZXgsIGV4cGxvZGVkKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICByZXR1cm4gYnJhbmNoZXM7XG59XG4vKipcbiAqIENvbXB1dGVzIGFsbCBjb21iaW5hdGlvbnMgb2Ygb3B0aW9uYWwgcGF0aCBzZWdtZW50cyBmb3IgYSBnaXZlbiBwYXRoLFxuICogZXhjbHVkaW5nIGNvbWJpbmF0aW9ucyB0aGF0IGFyZSBhbWJpZ3VvdXMgYW5kIG9mIGxvd2VyIHByaW9yaXR5LlxuICpcbiAqIEZvciBleGFtcGxlLCBgL29uZS86dHdvPy90aHJlZS86Zm91cj8vOmZpdmU/YCBleHBsb2RlcyB0bzpcbiAqIC0gYC9vbmUvdGhyZWVgXG4gKiAtIGAvb25lLzp0d28vdGhyZWVgXG4gKiAtIGAvb25lL3RocmVlLzpmb3VyYFxuICogLSBgL29uZS90aHJlZS86Zml2ZWBcbiAqIC0gYC9vbmUvOnR3by90aHJlZS86Zm91cmBcbiAqIC0gYC9vbmUvOnR3by90aHJlZS86Zml2ZWBcbiAqIC0gYC9vbmUvdGhyZWUvOmZvdXIvOmZpdmVgXG4gKiAtIGAvb25lLzp0d28vdGhyZWUvOmZvdXIvOmZpdmVgXG4gKi9cbmZ1bmN0aW9uIGV4cGxvZGVPcHRpb25hbFNlZ21lbnRzKHBhdGgpIHtcbiAgbGV0IHNlZ21lbnRzID0gcGF0aC5zcGxpdChcIi9cIik7XG4gIGlmIChzZWdtZW50cy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcbiAgbGV0IFtmaXJzdCwgLi4ucmVzdF0gPSBzZWdtZW50cztcbiAgLy8gT3B0aW9uYWwgcGF0aCBzZWdtZW50cyBhcmUgZGVub3RlZCBieSBhIHRyYWlsaW5nIGA/YFxuICBsZXQgaXNPcHRpb25hbCA9IGZpcnN0LmVuZHNXaXRoKFwiP1wiKTtcbiAgLy8gQ29tcHV0ZSB0aGUgY29ycmVzcG9uZGluZyByZXF1aXJlZCBzZWdtZW50OiBgZm9vP2AgLT4gYGZvb2BcbiAgbGV0IHJlcXVpcmVkID0gZmlyc3QucmVwbGFjZSgvXFw/JC8sIFwiXCIpO1xuICBpZiAocmVzdC5sZW5ndGggPT09IDApIHtcbiAgICAvLyBJbnRlcHJldCBlbXB0eSBzdHJpbmcgYXMgb21pdHRpbmcgYW4gb3B0aW9uYWwgc2VnbWVudFxuICAgIC8vIGBbXCJvbmVcIiwgXCJcIiwgXCJ0aHJlZVwiXWAgY29ycmVzcG9uZHMgdG8gb21pdHRpbmcgYDp0d29gIGZyb20gYC9vbmUvOnR3bz8vdGhyZWVgIC0+IGAvb25lL3RocmVlYFxuICAgIHJldHVybiBpc09wdGlvbmFsID8gW3JlcXVpcmVkLCBcIlwiXSA6IFtyZXF1aXJlZF07XG4gIH1cbiAgbGV0IHJlc3RFeHBsb2RlZCA9IGV4cGxvZGVPcHRpb25hbFNlZ21lbnRzKHJlc3Quam9pbihcIi9cIikpO1xuICBsZXQgcmVzdWx0ID0gW107XG4gIC8vIEFsbCBjaGlsZCBwYXRocyB3aXRoIHRoZSBwcmVmaXguICBEbyB0aGlzIGZvciBhbGwgY2hpbGRyZW4gYmVmb3JlIHRoZVxuICAvLyBvcHRpb25hbCB2ZXJzaW9uIGZvciBhbGwgY2hpbGRyZW4gc28gd2UgZ2V0IGNvbnNpc3RlbnQgb3JkZXJpbmcgd2hlcmUgdGhlXG4gIC8vIHBhcmVudCBvcHRpb25hbCBhc3BlY3QgaXMgcHJlZmVycmVkIGFzIHJlcXVpcmVkLiAgT3RoZXJ3aXNlLCB3ZSBjYW4gZ2V0XG4gIC8vIGNoaWxkIHNlY3Rpb25zIGludGVyc3BlcnNlZCB3aGVyZSBkZWVwZXIgb3B0aW9uYWwgc2VnbWVudHMgYXJlIGhpZ2hlciB0aGFuXG4gIC8vIHBhcmVudCBvcHRpb25hbCBzZWdtZW50cywgd2hlcmUgZm9yIGV4YW1wbGUsIC86dHdvIHdvdWxkIGV4cGxvZGVzIF9lYXJsaWVyX1xuICAvLyB0aGVuIC86b25lLiAgQnkgYWx3YXlzIGluY2x1ZGluZyB0aGUgcGFyZW50IGFzIHJlcXVpcmVkIF9mb3IgYWxsIGNoaWxkcmVuX1xuICAvLyBmaXJzdCwgd2UgYXZvaWQgdGhpcyBpc3N1ZVxuICByZXN1bHQucHVzaCguLi5yZXN0RXhwbG9kZWQubWFwKHN1YnBhdGggPT4gc3VicGF0aCA9PT0gXCJcIiA/IHJlcXVpcmVkIDogW3JlcXVpcmVkLCBzdWJwYXRoXS5qb2luKFwiL1wiKSkpO1xuICAvLyBUaGVuIGlmIHRoaXMgaXMgYW4gb3B0aW9uYWwgdmFsdWUsIGFkZCBhbGwgY2hpbGQgdmVyc2lvbnMgd2l0aG91dFxuICBpZiAoaXNPcHRpb25hbCkge1xuICAgIHJlc3VsdC5wdXNoKC4uLnJlc3RFeHBsb2RlZCk7XG4gIH1cbiAgLy8gZm9yIGFic29sdXRlIHBhdGhzLCBlbnN1cmUgYC9gIGluc3RlYWQgb2YgZW1wdHkgc2VnbWVudFxuICByZXR1cm4gcmVzdWx0Lm1hcChleHBsb2RlZCA9PiBwYXRoLnN0YXJ0c1dpdGgoXCIvXCIpICYmIGV4cGxvZGVkID09PSBcIlwiID8gXCIvXCIgOiBleHBsb2RlZCk7XG59XG5mdW5jdGlvbiByYW5rUm91dGVCcmFuY2hlcyhicmFuY2hlcykge1xuICBicmFuY2hlcy5zb3J0KChhLCBiKSA9PiBhLnNjb3JlICE9PSBiLnNjb3JlID8gYi5zY29yZSAtIGEuc2NvcmUgLy8gSGlnaGVyIHNjb3JlIGZpcnN0XG4gIDogY29tcGFyZUluZGV4ZXMoYS5yb3V0ZXNNZXRhLm1hcChtZXRhID0+IG1ldGEuY2hpbGRyZW5JbmRleCksIGIucm91dGVzTWV0YS5tYXAobWV0YSA9PiBtZXRhLmNoaWxkcmVuSW5kZXgpKSk7XG59XG5jb25zdCBwYXJhbVJlID0gL146XFx3KyQvO1xuY29uc3QgZHluYW1pY1NlZ21lbnRWYWx1ZSA9IDM7XG5jb25zdCBpbmRleFJvdXRlVmFsdWUgPSAyO1xuY29uc3QgZW1wdHlTZWdtZW50VmFsdWUgPSAxO1xuY29uc3Qgc3RhdGljU2VnbWVudFZhbHVlID0gMTA7XG5jb25zdCBzcGxhdFBlbmFsdHkgPSAtMjtcbmNvbnN0IGlzU3BsYXQgPSBzID0+IHMgPT09IFwiKlwiO1xuZnVuY3Rpb24gY29tcHV0ZVNjb3JlKHBhdGgsIGluZGV4KSB7XG4gIGxldCBzZWdtZW50cyA9IHBhdGguc3BsaXQoXCIvXCIpO1xuICBsZXQgaW5pdGlhbFNjb3JlID0gc2VnbWVudHMubGVuZ3RoO1xuICBpZiAoc2VnbWVudHMuc29tZShpc1NwbGF0KSkge1xuICAgIGluaXRpYWxTY29yZSArPSBzcGxhdFBlbmFsdHk7XG4gIH1cbiAgaWYgKGluZGV4KSB7XG4gICAgaW5pdGlhbFNjb3JlICs9IGluZGV4Um91dGVWYWx1ZTtcbiAgfVxuICByZXR1cm4gc2VnbWVudHMuZmlsdGVyKHMgPT4gIWlzU3BsYXQocykpLnJlZHVjZSgoc2NvcmUsIHNlZ21lbnQpID0+IHNjb3JlICsgKHBhcmFtUmUudGVzdChzZWdtZW50KSA/IGR5bmFtaWNTZWdtZW50VmFsdWUgOiBzZWdtZW50ID09PSBcIlwiID8gZW1wdHlTZWdtZW50VmFsdWUgOiBzdGF0aWNTZWdtZW50VmFsdWUpLCBpbml0aWFsU2NvcmUpO1xufVxuZnVuY3Rpb24gY29tcGFyZUluZGV4ZXMoYSwgYikge1xuICBsZXQgc2libGluZ3MgPSBhLmxlbmd0aCA9PT0gYi5sZW5ndGggJiYgYS5zbGljZSgwLCAtMSkuZXZlcnkoKG4sIGkpID0+IG4gPT09IGJbaV0pO1xuICByZXR1cm4gc2libGluZ3MgP1xuICAvLyBJZiB0d28gcm91dGVzIGFyZSBzaWJsaW5ncywgd2Ugc2hvdWxkIHRyeSB0byBtYXRjaCB0aGUgZWFybGllciBzaWJsaW5nXG4gIC8vIGZpcnN0LiBUaGlzIGFsbG93cyBwZW9wbGUgdG8gaGF2ZSBmaW5lLWdyYWluZWQgY29udHJvbCBvdmVyIHRoZSBtYXRjaGluZ1xuICAvLyBiZWhhdmlvciBieSBzaW1wbHkgcHV0dGluZyByb3V0ZXMgd2l0aCBpZGVudGljYWwgcGF0aHMgaW4gdGhlIG9yZGVyIHRoZXlcbiAgLy8gd2FudCB0aGVtIHRyaWVkLlxuICBhW2EubGVuZ3RoIC0gMV0gLSBiW2IubGVuZ3RoIC0gMV0gOlxuICAvLyBPdGhlcndpc2UsIGl0IGRvZXNuJ3QgcmVhbGx5IG1ha2Ugc2Vuc2UgdG8gcmFuayBub24tc2libGluZ3MgYnkgaW5kZXgsXG4gIC8vIHNvIHRoZXkgc29ydCBlcXVhbGx5LlxuICAwO1xufVxuZnVuY3Rpb24gbWF0Y2hSb3V0ZUJyYW5jaChicmFuY2gsIHBhdGhuYW1lKSB7XG4gIGxldCB7XG4gICAgcm91dGVzTWV0YVxuICB9ID0gYnJhbmNoO1xuICBsZXQgbWF0Y2hlZFBhcmFtcyA9IHt9O1xuICBsZXQgbWF0Y2hlZFBhdGhuYW1lID0gXCIvXCI7XG4gIGxldCBtYXRjaGVzID0gW107XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgcm91dGVzTWV0YS5sZW5ndGg7ICsraSkge1xuICAgIGxldCBtZXRhID0gcm91dGVzTWV0YVtpXTtcbiAgICBsZXQgZW5kID0gaSA9PT0gcm91dGVzTWV0YS5sZW5ndGggLSAxO1xuICAgIGxldCByZW1haW5pbmdQYXRobmFtZSA9IG1hdGNoZWRQYXRobmFtZSA9PT0gXCIvXCIgPyBwYXRobmFtZSA6IHBhdGhuYW1lLnNsaWNlKG1hdGNoZWRQYXRobmFtZS5sZW5ndGgpIHx8IFwiL1wiO1xuICAgIGxldCBtYXRjaCA9IG1hdGNoUGF0aCh7XG4gICAgICBwYXRoOiBtZXRhLnJlbGF0aXZlUGF0aCxcbiAgICAgIGNhc2VTZW5zaXRpdmU6IG1ldGEuY2FzZVNlbnNpdGl2ZSxcbiAgICAgIGVuZFxuICAgIH0sIHJlbWFpbmluZ1BhdGhuYW1lKTtcbiAgICBpZiAoIW1hdGNoKSByZXR1cm4gbnVsbDtcbiAgICBPYmplY3QuYXNzaWduKG1hdGNoZWRQYXJhbXMsIG1hdGNoLnBhcmFtcyk7XG4gICAgbGV0IHJvdXRlID0gbWV0YS5yb3V0ZTtcbiAgICBtYXRjaGVzLnB1c2goe1xuICAgICAgLy8gVE9ETzogQ2FuIHRoaXMgYXMgYmUgYXZvaWRlZD9cbiAgICAgIHBhcmFtczogbWF0Y2hlZFBhcmFtcyxcbiAgICAgIHBhdGhuYW1lOiBqb2luUGF0aHMoW21hdGNoZWRQYXRobmFtZSwgbWF0Y2gucGF0aG5hbWVdKSxcbiAgICAgIHBhdGhuYW1lQmFzZTogbm9ybWFsaXplUGF0aG5hbWUoam9pblBhdGhzKFttYXRjaGVkUGF0aG5hbWUsIG1hdGNoLnBhdGhuYW1lQmFzZV0pKSxcbiAgICAgIHJvdXRlXG4gICAgfSk7XG4gICAgaWYgKG1hdGNoLnBhdGhuYW1lQmFzZSAhPT0gXCIvXCIpIHtcbiAgICAgIG1hdGNoZWRQYXRobmFtZSA9IGpvaW5QYXRocyhbbWF0Y2hlZFBhdGhuYW1lLCBtYXRjaC5wYXRobmFtZUJhc2VdKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG1hdGNoZXM7XG59XG4vKipcbiAqIFJldHVybnMgYSBwYXRoIHdpdGggcGFyYW1zIGludGVycG9sYXRlZC5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vcmVhY3Ryb3V0ZXIuY29tL3V0aWxzL2dlbmVyYXRlLXBhdGhcbiAqL1xuZnVuY3Rpb24gZ2VuZXJhdGVQYXRoKG9yaWdpbmFsUGF0aCwgcGFyYW1zKSB7XG4gIGlmIChwYXJhbXMgPT09IHZvaWQgMCkge1xuICAgIHBhcmFtcyA9IHt9O1xuICB9XG4gIGxldCBwYXRoID0gb3JpZ2luYWxQYXRoO1xuICBpZiAocGF0aC5lbmRzV2l0aChcIipcIikgJiYgcGF0aCAhPT0gXCIqXCIgJiYgIXBhdGguZW5kc1dpdGgoXCIvKlwiKSkge1xuICAgIHdhcm5pbmcoZmFsc2UsIFwiUm91dGUgcGF0aCBcXFwiXCIgKyBwYXRoICsgXCJcXFwiIHdpbGwgYmUgdHJlYXRlZCBhcyBpZiBpdCB3ZXJlIFwiICsgKFwiXFxcIlwiICsgcGF0aC5yZXBsYWNlKC9cXCokLywgXCIvKlwiKSArIFwiXFxcIiBiZWNhdXNlIHRoZSBgKmAgY2hhcmFjdGVyIG11c3QgXCIpICsgXCJhbHdheXMgZm9sbG93IGEgYC9gIGluIHRoZSBwYXR0ZXJuLiBUbyBnZXQgcmlkIG9mIHRoaXMgd2FybmluZywgXCIgKyAoXCJwbGVhc2UgY2hhbmdlIHRoZSByb3V0ZSBwYXRoIHRvIFxcXCJcIiArIHBhdGgucmVwbGFjZSgvXFwqJC8sIFwiLypcIikgKyBcIlxcXCIuXCIpKTtcbiAgICBwYXRoID0gcGF0aC5yZXBsYWNlKC9cXCokLywgXCIvKlwiKTtcbiAgfVxuICAvLyBlbnN1cmUgYC9gIGlzIGFkZGVkIGF0IHRoZSBiZWdpbm5pbmcgaWYgdGhlIHBhdGggaXMgYWJzb2x1dGVcbiAgY29uc3QgcHJlZml4ID0gcGF0aC5zdGFydHNXaXRoKFwiL1wiKSA/IFwiL1wiIDogXCJcIjtcbiAgY29uc3Qgc3RyaW5naWZ5ID0gcCA9PiBwID09IG51bGwgPyBcIlwiIDogdHlwZW9mIHAgPT09IFwic3RyaW5nXCIgPyBwIDogU3RyaW5nKHApO1xuICBjb25zdCBzZWdtZW50cyA9IHBhdGguc3BsaXQoL1xcLysvKS5tYXAoKHNlZ21lbnQsIGluZGV4LCBhcnJheSkgPT4ge1xuICAgIGNvbnN0IGlzTGFzdFNlZ21lbnQgPSBpbmRleCA9PT0gYXJyYXkubGVuZ3RoIC0gMTtcbiAgICAvLyBvbmx5IGFwcGx5IHRoZSBzcGxhdCBpZiBpdCdzIHRoZSBsYXN0IHNlZ21lbnRcbiAgICBpZiAoaXNMYXN0U2VnbWVudCAmJiBzZWdtZW50ID09PSBcIipcIikge1xuICAgICAgY29uc3Qgc3RhciA9IFwiKlwiO1xuICAgICAgLy8gQXBwbHkgdGhlIHNwbGF0XG4gICAgICByZXR1cm4gc3RyaW5naWZ5KHBhcmFtc1tzdGFyXSk7XG4gICAgfVxuICAgIGNvbnN0IGtleU1hdGNoID0gc2VnbWVudC5tYXRjaCgvXjooXFx3KykoXFw/PykkLyk7XG4gICAgaWYgKGtleU1hdGNoKSB7XG4gICAgICBjb25zdCBbLCBrZXksIG9wdGlvbmFsXSA9IGtleU1hdGNoO1xuICAgICAgbGV0IHBhcmFtID0gcGFyYW1zW2tleV07XG4gICAgICBpbnZhcmlhbnQob3B0aW9uYWwgPT09IFwiP1wiIHx8IHBhcmFtICE9IG51bGwsIFwiTWlzc2luZyBcXFwiOlwiICsga2V5ICsgXCJcXFwiIHBhcmFtXCIpO1xuICAgICAgcmV0dXJuIHN0cmluZ2lmeShwYXJhbSk7XG4gICAgfVxuICAgIC8vIFJlbW92ZSBhbnkgb3B0aW9uYWwgbWFya2VycyBmcm9tIG9wdGlvbmFsIHN0YXRpYyBzZWdtZW50c1xuICAgIHJldHVybiBzZWdtZW50LnJlcGxhY2UoL1xcPyQvZywgXCJcIik7XG4gIH0pXG4gIC8vIFJlbW92ZSBlbXB0eSBzZWdtZW50c1xuICAuZmlsdGVyKHNlZ21lbnQgPT4gISFzZWdtZW50KTtcbiAgcmV0dXJuIHByZWZpeCArIHNlZ21lbnRzLmpvaW4oXCIvXCIpO1xufVxuLyoqXG4gKiBQZXJmb3JtcyBwYXR0ZXJuIG1hdGNoaW5nIG9uIGEgVVJMIHBhdGhuYW1lIGFuZCByZXR1cm5zIGluZm9ybWF0aW9uIGFib3V0XG4gKiB0aGUgbWF0Y2guXG4gKlxuICogQHNlZSBodHRwczovL3JlYWN0cm91dGVyLmNvbS91dGlscy9tYXRjaC1wYXRoXG4gKi9cbmZ1bmN0aW9uIG1hdGNoUGF0aChwYXR0ZXJuLCBwYXRobmFtZSkge1xuICBpZiAodHlwZW9mIHBhdHRlcm4gPT09IFwic3RyaW5nXCIpIHtcbiAgICBwYXR0ZXJuID0ge1xuICAgICAgcGF0aDogcGF0dGVybixcbiAgICAgIGNhc2VTZW5zaXRpdmU6IGZhbHNlLFxuICAgICAgZW5kOiB0cnVlXG4gICAgfTtcbiAgfVxuICBsZXQgW21hdGNoZXIsIHBhcmFtTmFtZXNdID0gY29tcGlsZVBhdGgocGF0dGVybi5wYXRoLCBwYXR0ZXJuLmNhc2VTZW5zaXRpdmUsIHBhdHRlcm4uZW5kKTtcbiAgbGV0IG1hdGNoID0gcGF0aG5hbWUubWF0Y2gobWF0Y2hlcik7XG4gIGlmICghbWF0Y2gpIHJldHVybiBudWxsO1xuICBsZXQgbWF0Y2hlZFBhdGhuYW1lID0gbWF0Y2hbMF07XG4gIGxldCBwYXRobmFtZUJhc2UgPSBtYXRjaGVkUGF0aG5hbWUucmVwbGFjZSgvKC4pXFwvKyQvLCBcIiQxXCIpO1xuICBsZXQgY2FwdHVyZUdyb3VwcyA9IG1hdGNoLnNsaWNlKDEpO1xuICBsZXQgcGFyYW1zID0gcGFyYW1OYW1lcy5yZWR1Y2UoKG1lbW8sIHBhcmFtTmFtZSwgaW5kZXgpID0+IHtcbiAgICAvLyBXZSBuZWVkIHRvIGNvbXB1dGUgdGhlIHBhdGhuYW1lQmFzZSBoZXJlIHVzaW5nIHRoZSByYXcgc3BsYXQgdmFsdWVcbiAgICAvLyBpbnN0ZWFkIG9mIHVzaW5nIHBhcmFtc1tcIipcIl0gbGF0ZXIgYmVjYXVzZSBpdCB3aWxsIGJlIGRlY29kZWQgdGhlblxuICAgIGlmIChwYXJhbU5hbWUgPT09IFwiKlwiKSB7XG4gICAgICBsZXQgc3BsYXRWYWx1ZSA9IGNhcHR1cmVHcm91cHNbaW5kZXhdIHx8IFwiXCI7XG4gICAgICBwYXRobmFtZUJhc2UgPSBtYXRjaGVkUGF0aG5hbWUuc2xpY2UoMCwgbWF0Y2hlZFBhdGhuYW1lLmxlbmd0aCAtIHNwbGF0VmFsdWUubGVuZ3RoKS5yZXBsYWNlKC8oLilcXC8rJC8sIFwiJDFcIik7XG4gICAgfVxuICAgIG1lbW9bcGFyYW1OYW1lXSA9IHNhZmVseURlY29kZVVSSUNvbXBvbmVudChjYXB0dXJlR3JvdXBzW2luZGV4XSB8fCBcIlwiLCBwYXJhbU5hbWUpO1xuICAgIHJldHVybiBtZW1vO1xuICB9LCB7fSk7XG4gIHJldHVybiB7XG4gICAgcGFyYW1zLFxuICAgIHBhdGhuYW1lOiBtYXRjaGVkUGF0aG5hbWUsXG4gICAgcGF0aG5hbWVCYXNlLFxuICAgIHBhdHRlcm5cbiAgfTtcbn1cbmZ1bmN0aW9uIGNvbXBpbGVQYXRoKHBhdGgsIGNhc2VTZW5zaXRpdmUsIGVuZCkge1xuICBpZiAoY2FzZVNlbnNpdGl2ZSA9PT0gdm9pZCAwKSB7XG4gICAgY2FzZVNlbnNpdGl2ZSA9IGZhbHNlO1xuICB9XG4gIGlmIChlbmQgPT09IHZvaWQgMCkge1xuICAgIGVuZCA9IHRydWU7XG4gIH1cbiAgd2FybmluZyhwYXRoID09PSBcIipcIiB8fCAhcGF0aC5lbmRzV2l0aChcIipcIikgfHwgcGF0aC5lbmRzV2l0aChcIi8qXCIpLCBcIlJvdXRlIHBhdGggXFxcIlwiICsgcGF0aCArIFwiXFxcIiB3aWxsIGJlIHRyZWF0ZWQgYXMgaWYgaXQgd2VyZSBcIiArIChcIlxcXCJcIiArIHBhdGgucmVwbGFjZSgvXFwqJC8sIFwiLypcIikgKyBcIlxcXCIgYmVjYXVzZSB0aGUgYCpgIGNoYXJhY3RlciBtdXN0IFwiKSArIFwiYWx3YXlzIGZvbGxvdyBhIGAvYCBpbiB0aGUgcGF0dGVybi4gVG8gZ2V0IHJpZCBvZiB0aGlzIHdhcm5pbmcsIFwiICsgKFwicGxlYXNlIGNoYW5nZSB0aGUgcm91dGUgcGF0aCB0byBcXFwiXCIgKyBwYXRoLnJlcGxhY2UoL1xcKiQvLCBcIi8qXCIpICsgXCJcXFwiLlwiKSk7XG4gIGxldCBwYXJhbU5hbWVzID0gW107XG4gIGxldCByZWdleHBTb3VyY2UgPSBcIl5cIiArIHBhdGgucmVwbGFjZSgvXFwvKlxcKj8kLywgXCJcIikgLy8gSWdub3JlIHRyYWlsaW5nIC8gYW5kIC8qLCB3ZSdsbCBoYW5kbGUgaXQgYmVsb3dcbiAgLnJlcGxhY2UoL15cXC8qLywgXCIvXCIpIC8vIE1ha2Ugc3VyZSBpdCBoYXMgYSBsZWFkaW5nIC9cbiAgLnJlcGxhY2UoL1tcXFxcLiorXiQ/e318KClbXFxdXS9nLCBcIlxcXFwkJlwiKSAvLyBFc2NhcGUgc3BlY2lhbCByZWdleCBjaGFyc1xuICAucmVwbGFjZSgvXFwvOihcXHcrKS9nLCAoXywgcGFyYW1OYW1lKSA9PiB7XG4gICAgcGFyYW1OYW1lcy5wdXNoKHBhcmFtTmFtZSk7XG4gICAgcmV0dXJuIFwiLyhbXlxcXFwvXSspXCI7XG4gIH0pO1xuICBpZiAocGF0aC5lbmRzV2l0aChcIipcIikpIHtcbiAgICBwYXJhbU5hbWVzLnB1c2goXCIqXCIpO1xuICAgIHJlZ2V4cFNvdXJjZSArPSBwYXRoID09PSBcIipcIiB8fCBwYXRoID09PSBcIi8qXCIgPyBcIiguKikkXCIgLy8gQWxyZWFkeSBtYXRjaGVkIHRoZSBpbml0aWFsIC8sIGp1c3QgbWF0Y2ggdGhlIHJlc3RcbiAgICA6IFwiKD86XFxcXC8oLispfFxcXFwvKikkXCI7IC8vIERvbid0IGluY2x1ZGUgdGhlIC8gaW4gcGFyYW1zW1wiKlwiXVxuICB9IGVsc2UgaWYgKGVuZCkge1xuICAgIC8vIFdoZW4gbWF0Y2hpbmcgdG8gdGhlIGVuZCwgaWdub3JlIHRyYWlsaW5nIHNsYXNoZXNcbiAgICByZWdleHBTb3VyY2UgKz0gXCJcXFxcLyokXCI7XG4gIH0gZWxzZSBpZiAocGF0aCAhPT0gXCJcIiAmJiBwYXRoICE9PSBcIi9cIikge1xuICAgIC8vIElmIG91ciBwYXRoIGlzIG5vbi1lbXB0eSBhbmQgY29udGFpbnMgYW55dGhpbmcgYmV5b25kIGFuIGluaXRpYWwgc2xhc2gsXG4gICAgLy8gdGhlbiB3ZSBoYXZlIF9zb21lXyBmb3JtIG9mIHBhdGggaW4gb3VyIHJlZ2V4IHNvIHdlIHNob3VsZCBleHBlY3QgdG9cbiAgICAvLyBtYXRjaCBvbmx5IGlmIHdlIGZpbmQgdGhlIGVuZCBvZiB0aGlzIHBhdGggc2VnbWVudC4gIExvb2sgZm9yIGFuIG9wdGlvbmFsXG4gICAgLy8gbm9uLWNhcHR1cmVkIHRyYWlsaW5nIHNsYXNoICh0byBtYXRjaCBhIHBvcnRpb24gb2YgdGhlIFVSTCkgb3IgdGhlIGVuZFxuICAgIC8vIG9mIHRoZSBwYXRoIChpZiB3ZSd2ZSBtYXRjaGVkIHRvIHRoZSBlbmQpLiAgV2UgdXNlZCB0byBkbyB0aGlzIHdpdGggYVxuICAgIC8vIHdvcmQgYm91bmRhcnkgYnV0IHRoYXQgZ2l2ZXMgZmFsc2UgcG9zaXRpdmVzIG9uIHJvdXRlcyBsaWtlXG4gICAgLy8gL3VzZXItcHJlZmVyZW5jZXMgc2luY2UgYC1gIGNvdW50cyBhcyBhIHdvcmQgYm91bmRhcnkuXG4gICAgcmVnZXhwU291cmNlICs9IFwiKD86KD89XFxcXC98JCkpXCI7XG4gIH0gZWxzZSA7XG4gIGxldCBtYXRjaGVyID0gbmV3IFJlZ0V4cChyZWdleHBTb3VyY2UsIGNhc2VTZW5zaXRpdmUgPyB1bmRlZmluZWQgOiBcImlcIik7XG4gIHJldHVybiBbbWF0Y2hlciwgcGFyYW1OYW1lc107XG59XG5mdW5jdGlvbiBzYWZlbHlEZWNvZGVVUkkodmFsdWUpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gZGVjb2RlVVJJKHZhbHVlKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB3YXJuaW5nKGZhbHNlLCBcIlRoZSBVUkwgcGF0aCBcXFwiXCIgKyB2YWx1ZSArIFwiXFxcIiBjb3VsZCBub3QgYmUgZGVjb2RlZCBiZWNhdXNlIGl0IGlzIGlzIGEgXCIgKyBcIm1hbGZvcm1lZCBVUkwgc2VnbWVudC4gVGhpcyBpcyBwcm9iYWJseSBkdWUgdG8gYSBiYWQgcGVyY2VudCBcIiArIChcImVuY29kaW5nIChcIiArIGVycm9yICsgXCIpLlwiKSk7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG59XG5mdW5jdGlvbiBzYWZlbHlEZWNvZGVVUklDb21wb25lbnQodmFsdWUsIHBhcmFtTmFtZSkge1xuICB0cnkge1xuICAgIHJldHVybiBkZWNvZGVVUklDb21wb25lbnQodmFsdWUpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHdhcm5pbmcoZmFsc2UsIFwiVGhlIHZhbHVlIGZvciB0aGUgVVJMIHBhcmFtIFxcXCJcIiArIHBhcmFtTmFtZSArIFwiXFxcIiB3aWxsIG5vdCBiZSBkZWNvZGVkIGJlY2F1c2VcIiArIChcIiB0aGUgc3RyaW5nIFxcXCJcIiArIHZhbHVlICsgXCJcXFwiIGlzIGEgbWFsZm9ybWVkIFVSTCBzZWdtZW50LiBUaGlzIGlzIHByb2JhYmx5XCIpICsgKFwiIGR1ZSB0byBhIGJhZCBwZXJjZW50IGVuY29kaW5nIChcIiArIGVycm9yICsgXCIpLlwiKSk7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG59XG4vKipcbiAqIEBwcml2YXRlXG4gKi9cbmZ1bmN0aW9uIHN0cmlwQmFzZW5hbWUocGF0aG5hbWUsIGJhc2VuYW1lKSB7XG4gIGlmIChiYXNlbmFtZSA9PT0gXCIvXCIpIHJldHVybiBwYXRobmFtZTtcbiAgaWYgKCFwYXRobmFtZS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoYmFzZW5hbWUudG9Mb3dlckNhc2UoKSkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICAvLyBXZSB3YW50IHRvIGxlYXZlIHRyYWlsaW5nIHNsYXNoIGJlaGF2aW9yIGluIHRoZSB1c2VyJ3MgY29udHJvbCwgc28gaWYgdGhleVxuICAvLyBzcGVjaWZ5IGEgYmFzZW5hbWUgd2l0aCBhIHRyYWlsaW5nIHNsYXNoLCB3ZSBzaG91bGQgc3VwcG9ydCBpdFxuICBsZXQgc3RhcnRJbmRleCA9IGJhc2VuYW1lLmVuZHNXaXRoKFwiL1wiKSA/IGJhc2VuYW1lLmxlbmd0aCAtIDEgOiBiYXNlbmFtZS5sZW5ndGg7XG4gIGxldCBuZXh0Q2hhciA9IHBhdGhuYW1lLmNoYXJBdChzdGFydEluZGV4KTtcbiAgaWYgKG5leHRDaGFyICYmIG5leHRDaGFyICE9PSBcIi9cIikge1xuICAgIC8vIHBhdGhuYW1lIGRvZXMgbm90IHN0YXJ0IHdpdGggYmFzZW5hbWUvXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgcmV0dXJuIHBhdGhuYW1lLnNsaWNlKHN0YXJ0SW5kZXgpIHx8IFwiL1wiO1xufVxuLyoqXG4gKiBSZXR1cm5zIGEgcmVzb2x2ZWQgcGF0aCBvYmplY3QgcmVsYXRpdmUgdG8gdGhlIGdpdmVuIHBhdGhuYW1lLlxuICpcbiAqIEBzZWUgaHR0cHM6Ly9yZWFjdHJvdXRlci5jb20vdXRpbHMvcmVzb2x2ZS1wYXRoXG4gKi9cbmZ1bmN0aW9uIHJlc29sdmVQYXRoKHRvLCBmcm9tUGF0aG5hbWUpIHtcbiAgaWYgKGZyb21QYXRobmFtZSA9PT0gdm9pZCAwKSB7XG4gICAgZnJvbVBhdGhuYW1lID0gXCIvXCI7XG4gIH1cbiAgbGV0IHtcbiAgICBwYXRobmFtZTogdG9QYXRobmFtZSxcbiAgICBzZWFyY2ggPSBcIlwiLFxuICAgIGhhc2ggPSBcIlwiXG4gIH0gPSB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyBwYXJzZVBhdGgodG8pIDogdG87XG4gIGxldCBwYXRobmFtZSA9IHRvUGF0aG5hbWUgPyB0b1BhdGhuYW1lLnN0YXJ0c1dpdGgoXCIvXCIpID8gdG9QYXRobmFtZSA6IHJlc29sdmVQYXRobmFtZSh0b1BhdGhuYW1lLCBmcm9tUGF0aG5hbWUpIDogZnJvbVBhdGhuYW1lO1xuICByZXR1cm4ge1xuICAgIHBhdGhuYW1lLFxuICAgIHNlYXJjaDogbm9ybWFsaXplU2VhcmNoKHNlYXJjaCksXG4gICAgaGFzaDogbm9ybWFsaXplSGFzaChoYXNoKVxuICB9O1xufVxuZnVuY3Rpb24gcmVzb2x2ZVBhdGhuYW1lKHJlbGF0aXZlUGF0aCwgZnJvbVBhdGhuYW1lKSB7XG4gIGxldCBzZWdtZW50cyA9IGZyb21QYXRobmFtZS5yZXBsYWNlKC9cXC8rJC8sIFwiXCIpLnNwbGl0KFwiL1wiKTtcbiAgbGV0IHJlbGF0aXZlU2VnbWVudHMgPSByZWxhdGl2ZVBhdGguc3BsaXQoXCIvXCIpO1xuICByZWxhdGl2ZVNlZ21lbnRzLmZvckVhY2goc2VnbWVudCA9PiB7XG4gICAgaWYgKHNlZ21lbnQgPT09IFwiLi5cIikge1xuICAgICAgLy8gS2VlcCB0aGUgcm9vdCBcIlwiIHNlZ21lbnQgc28gdGhlIHBhdGhuYW1lIHN0YXJ0cyBhdCAvXG4gICAgICBpZiAoc2VnbWVudHMubGVuZ3RoID4gMSkgc2VnbWVudHMucG9wKCk7XG4gICAgfSBlbHNlIGlmIChzZWdtZW50ICE9PSBcIi5cIikge1xuICAgICAgc2VnbWVudHMucHVzaChzZWdtZW50KTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gc2VnbWVudHMubGVuZ3RoID4gMSA/IHNlZ21lbnRzLmpvaW4oXCIvXCIpIDogXCIvXCI7XG59XG5mdW5jdGlvbiBnZXRJbnZhbGlkUGF0aEVycm9yKGNoYXIsIGZpZWxkLCBkZXN0LCBwYXRoKSB7XG4gIHJldHVybiBcIkNhbm5vdCBpbmNsdWRlIGEgJ1wiICsgY2hhciArIFwiJyBjaGFyYWN0ZXIgaW4gYSBtYW51YWxseSBzcGVjaWZpZWQgXCIgKyAoXCJgdG8uXCIgKyBmaWVsZCArIFwiYCBmaWVsZCBbXCIgKyBKU09OLnN0cmluZ2lmeShwYXRoKSArIFwiXS4gIFBsZWFzZSBzZXBhcmF0ZSBpdCBvdXQgdG8gdGhlIFwiKSArIChcImB0by5cIiArIGRlc3QgKyBcImAgZmllbGQuIEFsdGVybmF0aXZlbHkgeW91IG1heSBwcm92aWRlIHRoZSBmdWxsIHBhdGggYXMgXCIpICsgXCJhIHN0cmluZyBpbiA8TGluayB0bz1cXFwiLi4uXFxcIj4gYW5kIHRoZSByb3V0ZXIgd2lsbCBwYXJzZSBpdCBmb3IgeW91LlwiO1xufVxuLyoqXG4gKiBAcHJpdmF0ZVxuICpcbiAqIFdoZW4gcHJvY2Vzc2luZyByZWxhdGl2ZSBuYXZpZ2F0aW9uIHdlIHdhbnQgdG8gaWdub3JlIGFuY2VzdG9yIHJvdXRlcyB0aGF0XG4gKiBkbyBub3QgY29udHJpYnV0ZSB0byB0aGUgcGF0aCwgc3VjaCB0aGF0IGluZGV4L3BhdGhsZXNzIGxheW91dCByb3V0ZXMgZG9uJ3RcbiAqIGludGVyZmVyZS5cbiAqXG4gKiBGb3IgZXhhbXBsZSwgd2hlbiBtb3ZpbmcgYSByb3V0ZSBlbGVtZW50IGludG8gYW4gaW5kZXggcm91dGUgYW5kL29yIGFcbiAqIHBhdGhsZXNzIGxheW91dCByb3V0ZSwgcmVsYXRpdmUgbGluayBiZWhhdmlvciBjb250YWluZWQgd2l0aGluIHNob3VsZCBzdGF5XG4gKiB0aGUgc2FtZS4gIEJvdGggb2YgdGhlIGZvbGxvd2luZyBleGFtcGxlcyBzaG91bGQgbGluayBiYWNrIHRvIHRoZSByb290OlxuICpcbiAqICAgPFJvdXRlIHBhdGg9XCIvXCI+XG4gKiAgICAgPFJvdXRlIHBhdGg9XCJhY2NvdW50c1wiIGVsZW1lbnQ9ezxMaW5rIHRvPVwiLi5cIn0+XG4gKiAgIDwvUm91dGU+XG4gKlxuICogICA8Um91dGUgcGF0aD1cIi9cIj5cbiAqICAgICA8Um91dGUgcGF0aD1cImFjY291bnRzXCI+XG4gKiAgICAgICA8Um91dGUgZWxlbWVudD17PEFjY291bnRzTGF5b3V0IC8+fT4gICAgICAgLy8gPC0tIERvZXMgbm90IGNvbnRyaWJ1dGVcbiAqICAgICAgICAgPFJvdXRlIGluZGV4IGVsZW1lbnQ9ezxMaW5rIHRvPVwiLi5cIn0gLz4gIC8vIDwtLSBEb2VzIG5vdCBjb250cmlidXRlXG4gKiAgICAgICA8L1JvdXRlXG4gKiAgICAgPC9Sb3V0ZT5cbiAqICAgPC9Sb3V0ZT5cbiAqL1xuZnVuY3Rpb24gZ2V0UGF0aENvbnRyaWJ1dGluZ01hdGNoZXMobWF0Y2hlcykge1xuICByZXR1cm4gbWF0Y2hlcy5maWx0ZXIoKG1hdGNoLCBpbmRleCkgPT4gaW5kZXggPT09IDAgfHwgbWF0Y2gucm91dGUucGF0aCAmJiBtYXRjaC5yb3V0ZS5wYXRoLmxlbmd0aCA+IDApO1xufVxuLyoqXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiByZXNvbHZlVG8odG9BcmcsIHJvdXRlUGF0aG5hbWVzLCBsb2NhdGlvblBhdGhuYW1lLCBpc1BhdGhSZWxhdGl2ZSkge1xuICBpZiAoaXNQYXRoUmVsYXRpdmUgPT09IHZvaWQgMCkge1xuICAgIGlzUGF0aFJlbGF0aXZlID0gZmFsc2U7XG4gIH1cbiAgbGV0IHRvO1xuICBpZiAodHlwZW9mIHRvQXJnID09PSBcInN0cmluZ1wiKSB7XG4gICAgdG8gPSBwYXJzZVBhdGgodG9BcmcpO1xuICB9IGVsc2Uge1xuICAgIHRvID0gX2V4dGVuZHMoe30sIHRvQXJnKTtcbiAgICBpbnZhcmlhbnQoIXRvLnBhdGhuYW1lIHx8ICF0by5wYXRobmFtZS5pbmNsdWRlcyhcIj9cIiksIGdldEludmFsaWRQYXRoRXJyb3IoXCI/XCIsIFwicGF0aG5hbWVcIiwgXCJzZWFyY2hcIiwgdG8pKTtcbiAgICBpbnZhcmlhbnQoIXRvLnBhdGhuYW1lIHx8ICF0by5wYXRobmFtZS5pbmNsdWRlcyhcIiNcIiksIGdldEludmFsaWRQYXRoRXJyb3IoXCIjXCIsIFwicGF0aG5hbWVcIiwgXCJoYXNoXCIsIHRvKSk7XG4gICAgaW52YXJpYW50KCF0by5zZWFyY2ggfHwgIXRvLnNlYXJjaC5pbmNsdWRlcyhcIiNcIiksIGdldEludmFsaWRQYXRoRXJyb3IoXCIjXCIsIFwic2VhcmNoXCIsIFwiaGFzaFwiLCB0bykpO1xuICB9XG4gIGxldCBpc0VtcHR5UGF0aCA9IHRvQXJnID09PSBcIlwiIHx8IHRvLnBhdGhuYW1lID09PSBcIlwiO1xuICBsZXQgdG9QYXRobmFtZSA9IGlzRW1wdHlQYXRoID8gXCIvXCIgOiB0by5wYXRobmFtZTtcbiAgbGV0IGZyb207XG4gIC8vIFJvdXRpbmcgaXMgcmVsYXRpdmUgdG8gdGhlIGN1cnJlbnQgcGF0aG5hbWUgaWYgZXhwbGljaXRseSByZXF1ZXN0ZWQuXG4gIC8vXG4gIC8vIElmIGEgcGF0aG5hbWUgaXMgZXhwbGljaXRseSBwcm92aWRlZCBpbiBgdG9gLCBpdCBzaG91bGQgYmUgcmVsYXRpdmUgdG8gdGhlXG4gIC8vIHJvdXRlIGNvbnRleHQuIFRoaXMgaXMgZXhwbGFpbmVkIGluIGBOb3RlIG9uIGA8TGluayB0bz5gIHZhbHVlc2AgaW4gb3VyXG4gIC8vIG1pZ3JhdGlvbiBndWlkZSBmcm9tIHY1IGFzIGEgbWVhbnMgb2YgZGlzYW1iaWd1YXRpb24gYmV0d2VlbiBgdG9gIHZhbHVlc1xuICAvLyB0aGF0IGJlZ2luIHdpdGggYC9gIGFuZCB0aG9zZSB0aGF0IGRvIG5vdC4gSG93ZXZlciwgdGhpcyBpcyBwcm9ibGVtYXRpYyBmb3JcbiAgLy8gYHRvYCB2YWx1ZXMgdGhhdCBkbyBub3QgcHJvdmlkZSBhIHBhdGhuYW1lLiBgdG9gIGNhbiBzaW1wbHkgYmUgYSBzZWFyY2ggb3JcbiAgLy8gaGFzaCBzdHJpbmcsIGluIHdoaWNoIGNhc2Ugd2Ugc2hvdWxkIGFzc3VtZSB0aGF0IHRoZSBuYXZpZ2F0aW9uIGlzIHJlbGF0aXZlXG4gIC8vIHRvIHRoZSBjdXJyZW50IGxvY2F0aW9uJ3MgcGF0aG5hbWUgYW5kICpub3QqIHRoZSByb3V0ZSBwYXRobmFtZS5cbiAgaWYgKGlzUGF0aFJlbGF0aXZlIHx8IHRvUGF0aG5hbWUgPT0gbnVsbCkge1xuICAgIGZyb20gPSBsb2NhdGlvblBhdGhuYW1lO1xuICB9IGVsc2Uge1xuICAgIGxldCByb3V0ZVBhdGhuYW1lSW5kZXggPSByb3V0ZVBhdGhuYW1lcy5sZW5ndGggLSAxO1xuICAgIGlmICh0b1BhdGhuYW1lLnN0YXJ0c1dpdGgoXCIuLlwiKSkge1xuICAgICAgbGV0IHRvU2VnbWVudHMgPSB0b1BhdGhuYW1lLnNwbGl0KFwiL1wiKTtcbiAgICAgIC8vIEVhY2ggbGVhZGluZyAuLiBzZWdtZW50IG1lYW5zIFwiZ28gdXAgb25lIHJvdXRlXCIgaW5zdGVhZCBvZiBcImdvIHVwIG9uZVxuICAgICAgLy8gVVJMIHNlZ21lbnRcIi4gIFRoaXMgaXMgYSBrZXkgZGlmZmVyZW5jZSBmcm9tIGhvdyA8YSBocmVmPiB3b3JrcyBhbmQgYVxuICAgICAgLy8gbWFqb3IgcmVhc29uIHdlIGNhbGwgdGhpcyBhIFwidG9cIiB2YWx1ZSBpbnN0ZWFkIG9mIGEgXCJocmVmXCIuXG4gICAgICB3aGlsZSAodG9TZWdtZW50c1swXSA9PT0gXCIuLlwiKSB7XG4gICAgICAgIHRvU2VnbWVudHMuc2hpZnQoKTtcbiAgICAgICAgcm91dGVQYXRobmFtZUluZGV4IC09IDE7XG4gICAgICB9XG4gICAgICB0by5wYXRobmFtZSA9IHRvU2VnbWVudHMuam9pbihcIi9cIik7XG4gICAgfVxuICAgIC8vIElmIHRoZXJlIGFyZSBtb3JlIFwiLi5cIiBzZWdtZW50cyB0aGFuIHBhcmVudCByb3V0ZXMsIHJlc29sdmUgcmVsYXRpdmUgdG9cbiAgICAvLyB0aGUgcm9vdCAvIFVSTC5cbiAgICBmcm9tID0gcm91dGVQYXRobmFtZUluZGV4ID49IDAgPyByb3V0ZVBhdGhuYW1lc1tyb3V0ZVBhdGhuYW1lSW5kZXhdIDogXCIvXCI7XG4gIH1cbiAgbGV0IHBhdGggPSByZXNvbHZlUGF0aCh0bywgZnJvbSk7XG4gIC8vIEVuc3VyZSB0aGUgcGF0aG5hbWUgaGFzIGEgdHJhaWxpbmcgc2xhc2ggaWYgdGhlIG9yaWdpbmFsIFwidG9cIiBoYWQgb25lXG4gIGxldCBoYXNFeHBsaWNpdFRyYWlsaW5nU2xhc2ggPSB0b1BhdGhuYW1lICYmIHRvUGF0aG5hbWUgIT09IFwiL1wiICYmIHRvUGF0aG5hbWUuZW5kc1dpdGgoXCIvXCIpO1xuICAvLyBPciBpZiB0aGlzIHdhcyBhIGxpbmsgdG8gdGhlIGN1cnJlbnQgcGF0aCB3aGljaCBoYXMgYSB0cmFpbGluZyBzbGFzaFxuICBsZXQgaGFzQ3VycmVudFRyYWlsaW5nU2xhc2ggPSAoaXNFbXB0eVBhdGggfHwgdG9QYXRobmFtZSA9PT0gXCIuXCIpICYmIGxvY2F0aW9uUGF0aG5hbWUuZW5kc1dpdGgoXCIvXCIpO1xuICBpZiAoIXBhdGgucGF0aG5hbWUuZW5kc1dpdGgoXCIvXCIpICYmIChoYXNFeHBsaWNpdFRyYWlsaW5nU2xhc2ggfHwgaGFzQ3VycmVudFRyYWlsaW5nU2xhc2gpKSB7XG4gICAgcGF0aC5wYXRobmFtZSArPSBcIi9cIjtcbiAgfVxuICByZXR1cm4gcGF0aDtcbn1cbi8qKlxuICogQHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gZ2V0VG9QYXRobmFtZSh0bykge1xuICAvLyBFbXB0eSBzdHJpbmdzIHNob3VsZCBiZSB0cmVhdGVkIHRoZSBzYW1lIGFzIC8gcGF0aHNcbiAgcmV0dXJuIHRvID09PSBcIlwiIHx8IHRvLnBhdGhuYW1lID09PSBcIlwiID8gXCIvXCIgOiB0eXBlb2YgdG8gPT09IFwic3RyaW5nXCIgPyBwYXJzZVBhdGgodG8pLnBhdGhuYW1lIDogdG8ucGF0aG5hbWU7XG59XG4vKipcbiAqIEBwcml2YXRlXG4gKi9cbmNvbnN0IGpvaW5QYXRocyA9IHBhdGhzID0+IHBhdGhzLmpvaW4oXCIvXCIpLnJlcGxhY2UoL1xcL1xcLysvZywgXCIvXCIpO1xuLyoqXG4gKiBAcHJpdmF0ZVxuICovXG5jb25zdCBub3JtYWxpemVQYXRobmFtZSA9IHBhdGhuYW1lID0+IHBhdGhuYW1lLnJlcGxhY2UoL1xcLyskLywgXCJcIikucmVwbGFjZSgvXlxcLyovLCBcIi9cIik7XG4vKipcbiAqIEBwcml2YXRlXG4gKi9cbmNvbnN0IG5vcm1hbGl6ZVNlYXJjaCA9IHNlYXJjaCA9PiAhc2VhcmNoIHx8IHNlYXJjaCA9PT0gXCI/XCIgPyBcIlwiIDogc2VhcmNoLnN0YXJ0c1dpdGgoXCI/XCIpID8gc2VhcmNoIDogXCI/XCIgKyBzZWFyY2g7XG4vKipcbiAqIEBwcml2YXRlXG4gKi9cbmNvbnN0IG5vcm1hbGl6ZUhhc2ggPSBoYXNoID0+ICFoYXNoIHx8IGhhc2ggPT09IFwiI1wiID8gXCJcIiA6IGhhc2guc3RhcnRzV2l0aChcIiNcIikgPyBoYXNoIDogXCIjXCIgKyBoYXNoO1xuLyoqXG4gKiBUaGlzIGlzIGEgc2hvcnRjdXQgZm9yIGNyZWF0aW5nIGBhcHBsaWNhdGlvbi9qc29uYCByZXNwb25zZXMuIENvbnZlcnRzIGBkYXRhYFxuICogdG8gSlNPTiBhbmQgc2V0cyB0aGUgYENvbnRlbnQtVHlwZWAgaGVhZGVyLlxuICovXG5jb25zdCBqc29uID0gZnVuY3Rpb24ganNvbihkYXRhLCBpbml0KSB7XG4gIGlmIChpbml0ID09PSB2b2lkIDApIHtcbiAgICBpbml0ID0ge307XG4gIH1cbiAgbGV0IHJlc3BvbnNlSW5pdCA9IHR5cGVvZiBpbml0ID09PSBcIm51bWJlclwiID8ge1xuICAgIHN0YXR1czogaW5pdFxuICB9IDogaW5pdDtcbiAgbGV0IGhlYWRlcnMgPSBuZXcgSGVhZGVycyhyZXNwb25zZUluaXQuaGVhZGVycyk7XG4gIGlmICghaGVhZGVycy5oYXMoXCJDb250ZW50LVR5cGVcIikpIHtcbiAgICBoZWFkZXJzLnNldChcIkNvbnRlbnQtVHlwZVwiLCBcImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLThcIik7XG4gIH1cbiAgcmV0dXJuIG5ldyBSZXNwb25zZShKU09OLnN0cmluZ2lmeShkYXRhKSwgX2V4dGVuZHMoe30sIHJlc3BvbnNlSW5pdCwge1xuICAgIGhlYWRlcnNcbiAgfSkpO1xufTtcbmNsYXNzIEFib3J0ZWREZWZlcnJlZEVycm9yIGV4dGVuZHMgRXJyb3Ige31cbmNsYXNzIERlZmVycmVkRGF0YSB7XG4gIGNvbnN0cnVjdG9yKGRhdGEsIHJlc3BvbnNlSW5pdCkge1xuICAgIHRoaXMucGVuZGluZ0tleXNTZXQgPSBuZXcgU2V0KCk7XG4gICAgdGhpcy5zdWJzY3JpYmVycyA9IG5ldyBTZXQoKTtcbiAgICB0aGlzLmRlZmVycmVkS2V5cyA9IFtdO1xuICAgIGludmFyaWFudChkYXRhICYmIHR5cGVvZiBkYXRhID09PSBcIm9iamVjdFwiICYmICFBcnJheS5pc0FycmF5KGRhdGEpLCBcImRlZmVyKCkgb25seSBhY2NlcHRzIHBsYWluIG9iamVjdHNcIik7XG4gICAgLy8gU2V0IHVwIGFuIEFib3J0Q29udHJvbGxlciArIFByb21pc2Ugd2UgY2FuIHJhY2UgYWdhaW5zdCB0byBleGl0IGVhcmx5XG4gICAgLy8gY2FuY2VsbGF0aW9uXG4gICAgbGV0IHJlamVjdDtcbiAgICB0aGlzLmFib3J0UHJvbWlzZSA9IG5ldyBQcm9taXNlKChfLCByKSA9PiByZWplY3QgPSByKTtcbiAgICB0aGlzLmNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgbGV0IG9uQWJvcnQgPSAoKSA9PiByZWplY3QobmV3IEFib3J0ZWREZWZlcnJlZEVycm9yKFwiRGVmZXJyZWQgZGF0YSBhYm9ydGVkXCIpKTtcbiAgICB0aGlzLnVubGlzdGVuQWJvcnRTaWduYWwgPSAoKSA9PiB0aGlzLmNvbnRyb2xsZXIuc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICB0aGlzLmNvbnRyb2xsZXIuc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICB0aGlzLmRhdGEgPSBPYmplY3QuZW50cmllcyhkYXRhKS5yZWR1Y2UoKGFjYywgX3JlZikgPT4ge1xuICAgICAgbGV0IFtrZXksIHZhbHVlXSA9IF9yZWY7XG4gICAgICByZXR1cm4gT2JqZWN0LmFzc2lnbihhY2MsIHtcbiAgICAgICAgW2tleV06IHRoaXMudHJhY2tQcm9taXNlKGtleSwgdmFsdWUpXG4gICAgICB9KTtcbiAgICB9LCB7fSk7XG4gICAgaWYgKHRoaXMuZG9uZSkge1xuICAgICAgLy8gQWxsIGluY29taW5nIHZhbHVlcyB3ZXJlIHJlc29sdmVkXG4gICAgICB0aGlzLnVubGlzdGVuQWJvcnRTaWduYWwoKTtcbiAgICB9XG4gICAgdGhpcy5pbml0ID0gcmVzcG9uc2VJbml0O1xuICB9XG4gIHRyYWNrUHJvbWlzZShrZXksIHZhbHVlKSB7XG4gICAgaWYgKCEodmFsdWUgaW5zdGFuY2VvZiBQcm9taXNlKSkge1xuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cbiAgICB0aGlzLmRlZmVycmVkS2V5cy5wdXNoKGtleSk7XG4gICAgdGhpcy5wZW5kaW5nS2V5c1NldC5hZGQoa2V5KTtcbiAgICAvLyBXZSBzdG9yZSBhIGxpdHRsZSB3cmFwcGVyIHByb21pc2UgdGhhdCB3aWxsIGJlIGV4dGVuZGVkIHdpdGhcbiAgICAvLyBfZGF0YS9fZXJyb3IgcHJvcHMgdXBvbiByZXNvbHZlL3JlamVjdFxuICAgIGxldCBwcm9taXNlID0gUHJvbWlzZS5yYWNlKFt2YWx1ZSwgdGhpcy5hYm9ydFByb21pc2VdKS50aGVuKGRhdGEgPT4gdGhpcy5vblNldHRsZShwcm9taXNlLCBrZXksIG51bGwsIGRhdGEpLCBlcnJvciA9PiB0aGlzLm9uU2V0dGxlKHByb21pc2UsIGtleSwgZXJyb3IpKTtcbiAgICAvLyBSZWdpc3RlciByZWplY3Rpb24gbGlzdGVuZXJzIHRvIGF2b2lkIHVuY2F1Z2h0IHByb21pc2UgcmVqZWN0aW9ucyBvblxuICAgIC8vIGVycm9ycyBvciBhYm9ydGVkIGRlZmVycmVkIHZhbHVlc1xuICAgIHByb21pc2UuY2F0Y2goKCkgPT4ge30pO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShwcm9taXNlLCBcIl90cmFja2VkXCIsIHtcbiAgICAgIGdldDogKCkgPT4gdHJ1ZVxuICAgIH0pO1xuICAgIHJldHVybiBwcm9taXNlO1xuICB9XG4gIG9uU2V0dGxlKHByb21pc2UsIGtleSwgZXJyb3IsIGRhdGEpIHtcbiAgICBpZiAodGhpcy5jb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkICYmIGVycm9yIGluc3RhbmNlb2YgQWJvcnRlZERlZmVycmVkRXJyb3IpIHtcbiAgICAgIHRoaXMudW5saXN0ZW5BYm9ydFNpZ25hbCgpO1xuICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsIFwiX2Vycm9yXCIsIHtcbiAgICAgICAgZ2V0OiAoKSA9PiBlcnJvclxuICAgICAgfSk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpO1xuICAgIH1cbiAgICB0aGlzLnBlbmRpbmdLZXlzU2V0LmRlbGV0ZShrZXkpO1xuICAgIGlmICh0aGlzLmRvbmUpIHtcbiAgICAgIC8vIE5vdGhpbmcgbGVmdCB0byBhYm9ydCFcbiAgICAgIHRoaXMudW5saXN0ZW5BYm9ydFNpZ25hbCgpO1xuICAgIH1cbiAgICBpZiAoZXJyb3IpIHtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShwcm9taXNlLCBcIl9lcnJvclwiLCB7XG4gICAgICAgIGdldDogKCkgPT4gZXJyb3JcbiAgICAgIH0pO1xuICAgICAgdGhpcy5lbWl0KGZhbHNlLCBrZXkpO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKTtcbiAgICB9XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb21pc2UsIFwiX2RhdGFcIiwge1xuICAgICAgZ2V0OiAoKSA9PiBkYXRhXG4gICAgfSk7XG4gICAgdGhpcy5lbWl0KGZhbHNlLCBrZXkpO1xuICAgIHJldHVybiBkYXRhO1xuICB9XG4gIGVtaXQoYWJvcnRlZCwgc2V0dGxlZEtleSkge1xuICAgIHRoaXMuc3Vic2NyaWJlcnMuZm9yRWFjaChzdWJzY3JpYmVyID0+IHN1YnNjcmliZXIoYWJvcnRlZCwgc2V0dGxlZEtleSkpO1xuICB9XG4gIHN1YnNjcmliZShmbikge1xuICAgIHRoaXMuc3Vic2NyaWJlcnMuYWRkKGZuKTtcbiAgICByZXR1cm4gKCkgPT4gdGhpcy5zdWJzY3JpYmVycy5kZWxldGUoZm4pO1xuICB9XG4gIGNhbmNlbCgpIHtcbiAgICB0aGlzLmNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICB0aGlzLnBlbmRpbmdLZXlzU2V0LmZvckVhY2goKHYsIGspID0+IHRoaXMucGVuZGluZ0tleXNTZXQuZGVsZXRlKGspKTtcbiAgICB0aGlzLmVtaXQodHJ1ZSk7XG4gIH1cbiAgYXN5bmMgcmVzb2x2ZURhdGEoc2lnbmFsKSB7XG4gICAgbGV0IGFib3J0ZWQgPSBmYWxzZTtcbiAgICBpZiAoIXRoaXMuZG9uZSkge1xuICAgICAgbGV0IG9uQWJvcnQgPSAoKSA9PiB0aGlzLmNhbmNlbCgpO1xuICAgICAgc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICAgIGFib3J0ZWQgPSBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHtcbiAgICAgICAgdGhpcy5zdWJzY3JpYmUoYWJvcnRlZCA9PiB7XG4gICAgICAgICAgc2lnbmFsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBvbkFib3J0KTtcbiAgICAgICAgICBpZiAoYWJvcnRlZCB8fCB0aGlzLmRvbmUpIHtcbiAgICAgICAgICAgIHJlc29sdmUoYWJvcnRlZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gYWJvcnRlZDtcbiAgfVxuICBnZXQgZG9uZSgpIHtcbiAgICByZXR1cm4gdGhpcy5wZW5kaW5nS2V5c1NldC5zaXplID09PSAwO1xuICB9XG4gIGdldCB1bndyYXBwZWREYXRhKCkge1xuICAgIGludmFyaWFudCh0aGlzLmRhdGEgIT09IG51bGwgJiYgdGhpcy5kb25lLCBcIkNhbiBvbmx5IHVud3JhcCBkYXRhIG9uIGluaXRpYWxpemVkIGFuZCBzZXR0bGVkIGRlZmVycmVkc1wiKTtcbiAgICByZXR1cm4gT2JqZWN0LmVudHJpZXModGhpcy5kYXRhKS5yZWR1Y2UoKGFjYywgX3JlZjIpID0+IHtcbiAgICAgIGxldCBba2V5LCB2YWx1ZV0gPSBfcmVmMjtcbiAgICAgIHJldHVybiBPYmplY3QuYXNzaWduKGFjYywge1xuICAgICAgICBba2V5XTogdW53cmFwVHJhY2tlZFByb21pc2UodmFsdWUpXG4gICAgICB9KTtcbiAgICB9LCB7fSk7XG4gIH1cbiAgZ2V0IHBlbmRpbmdLZXlzKCkge1xuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMucGVuZGluZ0tleXNTZXQpO1xuICB9XG59XG5mdW5jdGlvbiBpc1RyYWNrZWRQcm9taXNlKHZhbHVlKSB7XG4gIHJldHVybiB2YWx1ZSBpbnN0YW5jZW9mIFByb21pc2UgJiYgdmFsdWUuX3RyYWNrZWQgPT09IHRydWU7XG59XG5mdW5jdGlvbiB1bndyYXBUcmFja2VkUHJvbWlzZSh2YWx1ZSkge1xuICBpZiAoIWlzVHJhY2tlZFByb21pc2UodmFsdWUpKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG4gIGlmICh2YWx1ZS5fZXJyb3IpIHtcbiAgICB0aHJvdyB2YWx1ZS5fZXJyb3I7XG4gIH1cbiAgcmV0dXJuIHZhbHVlLl9kYXRhO1xufVxuY29uc3QgZGVmZXIgPSBmdW5jdGlvbiBkZWZlcihkYXRhLCBpbml0KSB7XG4gIGlmIChpbml0ID09PSB2b2lkIDApIHtcbiAgICBpbml0ID0ge307XG4gIH1cbiAgbGV0IHJlc3BvbnNlSW5pdCA9IHR5cGVvZiBpbml0ID09PSBcIm51bWJlclwiID8ge1xuICAgIHN0YXR1czogaW5pdFxuICB9IDogaW5pdDtcbiAgcmV0dXJuIG5ldyBEZWZlcnJlZERhdGEoZGF0YSwgcmVzcG9uc2VJbml0KTtcbn07XG4vKipcbiAqIEEgcmVkaXJlY3QgcmVzcG9uc2UuIFNldHMgdGhlIHN0YXR1cyBjb2RlIGFuZCB0aGUgYExvY2F0aW9uYCBoZWFkZXIuXG4gKiBEZWZhdWx0cyB0byBcIjMwMiBGb3VuZFwiLlxuICovXG5jb25zdCByZWRpcmVjdCA9IGZ1bmN0aW9uIHJlZGlyZWN0KHVybCwgaW5pdCkge1xuICBpZiAoaW5pdCA9PT0gdm9pZCAwKSB7XG4gICAgaW5pdCA9IDMwMjtcbiAgfVxuICBsZXQgcmVzcG9uc2VJbml0ID0gaW5pdDtcbiAgaWYgKHR5cGVvZiByZXNwb25zZUluaXQgPT09IFwibnVtYmVyXCIpIHtcbiAgICByZXNwb25zZUluaXQgPSB7XG4gICAgICBzdGF0dXM6IHJlc3BvbnNlSW5pdFxuICAgIH07XG4gIH0gZWxzZSBpZiAodHlwZW9mIHJlc3BvbnNlSW5pdC5zdGF0dXMgPT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICByZXNwb25zZUluaXQuc3RhdHVzID0gMzAyO1xuICB9XG4gIGxldCBoZWFkZXJzID0gbmV3IEhlYWRlcnMocmVzcG9uc2VJbml0LmhlYWRlcnMpO1xuICBoZWFkZXJzLnNldChcIkxvY2F0aW9uXCIsIHVybCk7XG4gIHJldHVybiBuZXcgUmVzcG9uc2UobnVsbCwgX2V4dGVuZHMoe30sIHJlc3BvbnNlSW5pdCwge1xuICAgIGhlYWRlcnNcbiAgfSkpO1xufTtcbi8qKlxuICogQHByaXZhdGVcbiAqIFV0aWxpdHkgY2xhc3Mgd2UgdXNlIHRvIGhvbGQgYXV0by11bndyYXBwZWQgNHh4LzV4eCBSZXNwb25zZSBib2RpZXNcbiAqL1xuY2xhc3MgRXJyb3JSZXNwb25zZSB7XG4gIGNvbnN0cnVjdG9yKHN0YXR1cywgc3RhdHVzVGV4dCwgZGF0YSwgaW50ZXJuYWwpIHtcbiAgICBpZiAoaW50ZXJuYWwgPT09IHZvaWQgMCkge1xuICAgICAgaW50ZXJuYWwgPSBmYWxzZTtcbiAgICB9XG4gICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XG4gICAgdGhpcy5zdGF0dXNUZXh0ID0gc3RhdHVzVGV4dCB8fCBcIlwiO1xuICAgIHRoaXMuaW50ZXJuYWwgPSBpbnRlcm5hbDtcbiAgICBpZiAoZGF0YSBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICB0aGlzLmRhdGEgPSBkYXRhLnRvU3RyaW5nKCk7XG4gICAgICB0aGlzLmVycm9yID0gZGF0YTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICB9XG4gIH1cbn1cbi8qKlxuICogQ2hlY2sgaWYgdGhlIGdpdmVuIGVycm9yIGlzIGFuIEVycm9yUmVzcG9uc2UgZ2VuZXJhdGVkIGZyb20gYSA0eHgvNXh4XG4gKiBSZXNwb25zZSB0aHJvd24gZnJvbSBhbiBhY3Rpb24vbG9hZGVyXG4gKi9cbmZ1bmN0aW9uIGlzUm91dGVFcnJvclJlc3BvbnNlKGVycm9yKSB7XG4gIHJldHVybiBlcnJvciAhPSBudWxsICYmIHR5cGVvZiBlcnJvci5zdGF0dXMgPT09IFwibnVtYmVyXCIgJiYgdHlwZW9mIGVycm9yLnN0YXR1c1RleHQgPT09IFwic3RyaW5nXCIgJiYgdHlwZW9mIGVycm9yLmludGVybmFsID09PSBcImJvb2xlYW5cIiAmJiBcImRhdGFcIiBpbiBlcnJvcjtcbn1cblxuY29uc3QgdmFsaWRNdXRhdGlvbk1ldGhvZHNBcnIgPSBbXCJwb3N0XCIsIFwicHV0XCIsIFwicGF0Y2hcIiwgXCJkZWxldGVcIl07XG5jb25zdCB2YWxpZE11dGF0aW9uTWV0aG9kcyA9IG5ldyBTZXQodmFsaWRNdXRhdGlvbk1ldGhvZHNBcnIpO1xuY29uc3QgdmFsaWRSZXF1ZXN0TWV0aG9kc0FyciA9IFtcImdldFwiLCAuLi52YWxpZE11dGF0aW9uTWV0aG9kc0Fycl07XG5jb25zdCB2YWxpZFJlcXVlc3RNZXRob2RzID0gbmV3IFNldCh2YWxpZFJlcXVlc3RNZXRob2RzQXJyKTtcbmNvbnN0IHJlZGlyZWN0U3RhdHVzQ29kZXMgPSBuZXcgU2V0KFszMDEsIDMwMiwgMzAzLCAzMDcsIDMwOF0pO1xuY29uc3QgcmVkaXJlY3RQcmVzZXJ2ZU1ldGhvZFN0YXR1c0NvZGVzID0gbmV3IFNldChbMzA3LCAzMDhdKTtcbmNvbnN0IElETEVfTkFWSUdBVElPTiA9IHtcbiAgc3RhdGU6IFwiaWRsZVwiLFxuICBsb2NhdGlvbjogdW5kZWZpbmVkLFxuICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsXG4gIGZvcm1BY3Rpb246IHVuZGVmaW5lZCxcbiAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCxcbiAgZm9ybURhdGE6IHVuZGVmaW5lZCxcbiAganNvbjogdW5kZWZpbmVkLFxuICB0ZXh0OiB1bmRlZmluZWRcbn07XG5jb25zdCBJRExFX0ZFVENIRVIgPSB7XG4gIHN0YXRlOiBcImlkbGVcIixcbiAgZGF0YTogdW5kZWZpbmVkLFxuICBmb3JtTWV0aG9kOiB1bmRlZmluZWQsXG4gIGZvcm1BY3Rpb246IHVuZGVmaW5lZCxcbiAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCxcbiAgZm9ybURhdGE6IHVuZGVmaW5lZCxcbiAganNvbjogdW5kZWZpbmVkLFxuICB0ZXh0OiB1bmRlZmluZWRcbn07XG5jb25zdCBJRExFX0JMT0NLRVIgPSB7XG4gIHN0YXRlOiBcInVuYmxvY2tlZFwiLFxuICBwcm9jZWVkOiB1bmRlZmluZWQsXG4gIHJlc2V0OiB1bmRlZmluZWQsXG4gIGxvY2F0aW9uOiB1bmRlZmluZWRcbn07XG5jb25zdCBBQlNPTFVURV9VUkxfUkVHRVggPSAvXig/OlthLXpdW2EtejAtOSsuLV0qOnxcXC9cXC8pL2k7XG5jb25zdCBkZWZhdWx0TWFwUm91dGVQcm9wZXJ0aWVzID0gcm91dGUgPT4gKHtcbiAgaGFzRXJyb3JCb3VuZGFyeTogQm9vbGVhbihyb3V0ZS5oYXNFcnJvckJvdW5kYXJ5KVxufSk7XG4vLyNlbmRyZWdpb25cbi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyNyZWdpb24gY3JlYXRlUm91dGVyXG4vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuLyoqXG4gKiBDcmVhdGUgYSByb3V0ZXIgYW5kIGxpc3RlbiB0byBoaXN0b3J5IFBPUCBuYXZpZ2F0aW9uc1xuICovXG5mdW5jdGlvbiBjcmVhdGVSb3V0ZXIoaW5pdCkge1xuICBjb25zdCByb3V0ZXJXaW5kb3cgPSBpbml0LndpbmRvdyA/IGluaXQud2luZG93IDogdHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiA/IHdpbmRvdyA6IHVuZGVmaW5lZDtcbiAgY29uc3QgaXNCcm93c2VyID0gdHlwZW9mIHJvdXRlcldpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiAmJiB0eXBlb2Ygcm91dGVyV2luZG93LmRvY3VtZW50ICE9PSBcInVuZGVmaW5lZFwiICYmIHR5cGVvZiByb3V0ZXJXaW5kb3cuZG9jdW1lbnQuY3JlYXRlRWxlbWVudCAhPT0gXCJ1bmRlZmluZWRcIjtcbiAgY29uc3QgaXNTZXJ2ZXIgPSAhaXNCcm93c2VyO1xuICBpbnZhcmlhbnQoaW5pdC5yb3V0ZXMubGVuZ3RoID4gMCwgXCJZb3UgbXVzdCBwcm92aWRlIGEgbm9uLWVtcHR5IHJvdXRlcyBhcnJheSB0byBjcmVhdGVSb3V0ZXJcIik7XG4gIGxldCBtYXBSb3V0ZVByb3BlcnRpZXM7XG4gIGlmIChpbml0Lm1hcFJvdXRlUHJvcGVydGllcykge1xuICAgIG1hcFJvdXRlUHJvcGVydGllcyA9IGluaXQubWFwUm91dGVQcm9wZXJ0aWVzO1xuICB9IGVsc2UgaWYgKGluaXQuZGV0ZWN0RXJyb3JCb3VuZGFyeSkge1xuICAgIC8vIElmIHRoZXkgYXJlIHN0aWxsIHVzaW5nIHRoZSBkZXByZWNhdGVkIHZlcnNpb24sIHdyYXAgaXQgd2l0aCB0aGUgbmV3IEFQSVxuICAgIGxldCBkZXRlY3RFcnJvckJvdW5kYXJ5ID0gaW5pdC5kZXRlY3RFcnJvckJvdW5kYXJ5O1xuICAgIG1hcFJvdXRlUHJvcGVydGllcyA9IHJvdXRlID0+ICh7XG4gICAgICBoYXNFcnJvckJvdW5kYXJ5OiBkZXRlY3RFcnJvckJvdW5kYXJ5KHJvdXRlKVxuICAgIH0pO1xuICB9IGVsc2Uge1xuICAgIG1hcFJvdXRlUHJvcGVydGllcyA9IGRlZmF1bHRNYXBSb3V0ZVByb3BlcnRpZXM7XG4gIH1cbiAgLy8gUm91dGVzIGtleWVkIGJ5IElEXG4gIGxldCBtYW5pZmVzdCA9IHt9O1xuICAvLyBSb3V0ZXMgaW4gdHJlZSBmb3JtYXQgZm9yIG1hdGNoaW5nXG4gIGxldCBkYXRhUm91dGVzID0gY29udmVydFJvdXRlc1RvRGF0YVJvdXRlcyhpbml0LnJvdXRlcywgbWFwUm91dGVQcm9wZXJ0aWVzLCB1bmRlZmluZWQsIG1hbmlmZXN0KTtcbiAgbGV0IGluRmxpZ2h0RGF0YVJvdXRlcztcbiAgbGV0IGJhc2VuYW1lID0gaW5pdC5iYXNlbmFtZSB8fCBcIi9cIjtcbiAgLy8gQ29uZmlnIGRyaXZlbiBiZWhhdmlvciBmbGFnc1xuICBsZXQgZnV0dXJlID0gX2V4dGVuZHMoe1xuICAgIHY3X25vcm1hbGl6ZUZvcm1NZXRob2Q6IGZhbHNlLFxuICAgIHY3X3ByZXBlbmRCYXNlbmFtZTogZmFsc2VcbiAgfSwgaW5pdC5mdXR1cmUpO1xuICAvLyBDbGVhbnVwIGZ1bmN0aW9uIGZvciBoaXN0b3J5XG4gIGxldCB1bmxpc3Rlbkhpc3RvcnkgPSBudWxsO1xuICAvLyBFeHRlcm5hbGx5LXByb3ZpZGVkIGZ1bmN0aW9ucyB0byBjYWxsIG9uIGFsbCBzdGF0ZSBjaGFuZ2VzXG4gIGxldCBzdWJzY3JpYmVycyA9IG5ldyBTZXQoKTtcbiAgLy8gRXh0ZXJuYWxseS1wcm92aWRlZCBvYmplY3QgdG8gaG9sZCBzY3JvbGwgcmVzdG9yYXRpb24gbG9jYXRpb25zIGR1cmluZyByb3V0aW5nXG4gIGxldCBzYXZlZFNjcm9sbFBvc2l0aW9ucyA9IG51bGw7XG4gIC8vIEV4dGVybmFsbHktcHJvdmlkZWQgZnVuY3Rpb24gdG8gZ2V0IHNjcm9sbCByZXN0b3JhdGlvbiBrZXlzXG4gIGxldCBnZXRTY3JvbGxSZXN0b3JhdGlvbktleSA9IG51bGw7XG4gIC8vIEV4dGVybmFsbHktcHJvdmlkZWQgZnVuY3Rpb24gdG8gZ2V0IGN1cnJlbnQgc2Nyb2xsIHBvc2l0aW9uXG4gIGxldCBnZXRTY3JvbGxQb3NpdGlvbiA9IG51bGw7XG4gIC8vIE9uZS10aW1lIGZsYWcgdG8gY29udHJvbCB0aGUgaW5pdGlhbCBoeWRyYXRpb24gc2Nyb2xsIHJlc3RvcmF0aW9uLiAgQmVjYXVzZVxuICAvLyB3ZSBkb24ndCBnZXQgdGhlIHNhdmVkIHBvc2l0aW9ucyBmcm9tIDxTY3JvbGxSZXN0b3JhdGlvbiAvPiB1bnRpbCBfYWZ0ZXJfXG4gIC8vIHRoZSBpbml0aWFsIHJlbmRlciwgd2UgbmVlZCB0byBtYW51YWxseSB0cmlnZ2VyIGEgc2VwYXJhdGUgdXBkYXRlU3RhdGUgdG9cbiAgLy8gc2VuZCBhbG9uZyB0aGUgcmVzdG9yZVNjcm9sbFBvc2l0aW9uXG4gIC8vIFNldCB0byB0cnVlIGlmIHdlIGhhdmUgYGh5ZHJhdGlvbkRhdGFgIHNpbmNlIHdlIGFzc3VtZSB3ZSB3ZXJlIFNTUidkIGFuZCB0aGF0XG4gIC8vIFNTUiBkaWQgdGhlIGluaXRpYWwgc2Nyb2xsIHJlc3RvcmF0aW9uLlxuICBsZXQgaW5pdGlhbFNjcm9sbFJlc3RvcmVkID0gaW5pdC5oeWRyYXRpb25EYXRhICE9IG51bGw7XG4gIGxldCBpbml0aWFsTWF0Y2hlcyA9IG1hdGNoUm91dGVzKGRhdGFSb3V0ZXMsIGluaXQuaGlzdG9yeS5sb2NhdGlvbiwgYmFzZW5hbWUpO1xuICBsZXQgaW5pdGlhbEVycm9ycyA9IG51bGw7XG4gIGlmIChpbml0aWFsTWF0Y2hlcyA9PSBudWxsKSB7XG4gICAgLy8gSWYgd2UgZG8gbm90IG1hdGNoIGEgdXNlci1wcm92aWRlZC1yb3V0ZSwgZmFsbCBiYWNrIHRvIHRoZSByb290XG4gICAgLy8gdG8gYWxsb3cgdGhlIGVycm9yIGJvdW5kYXJ5IHRvIHRha2Ugb3ZlclxuICAgIGxldCBlcnJvciA9IGdldEludGVybmFsUm91dGVyRXJyb3IoNDA0LCB7XG4gICAgICBwYXRobmFtZTogaW5pdC5oaXN0b3J5LmxvY2F0aW9uLnBhdGhuYW1lXG4gICAgfSk7XG4gICAgbGV0IHtcbiAgICAgIG1hdGNoZXMsXG4gICAgICByb3V0ZVxuICAgIH0gPSBnZXRTaG9ydENpcmN1aXRNYXRjaGVzKGRhdGFSb3V0ZXMpO1xuICAgIGluaXRpYWxNYXRjaGVzID0gbWF0Y2hlcztcbiAgICBpbml0aWFsRXJyb3JzID0ge1xuICAgICAgW3JvdXRlLmlkXTogZXJyb3JcbiAgICB9O1xuICB9XG4gIGxldCBpbml0aWFsaXplZCA9XG4gIC8vIEFsbCBpbml0aWFsTWF0Y2hlcyBuZWVkIHRvIGJlIGxvYWRlZCBiZWZvcmUgd2UncmUgcmVhZHkuICBJZiB3ZSBoYXZlIGxhenlcbiAgLy8gZnVuY3Rpb25zIGFyb3VuZCBzdGlsbCB0aGVuIHdlJ2xsIG5lZWQgdG8gcnVuIHRoZW0gaW4gaW5pdGlhbGl6ZSgpXG4gICFpbml0aWFsTWF0Y2hlcy5zb21lKG0gPT4gbS5yb3V0ZS5sYXp5KSAmJiAoXG4gIC8vIEFuZCB3ZSBoYXZlIHRvIGVpdGhlciBoYXZlIG5vIGxvYWRlcnMgb3IgaGF2ZSBiZWVuIHByb3ZpZGVkIGh5ZHJhdGlvbkRhdGFcbiAgIWluaXRpYWxNYXRjaGVzLnNvbWUobSA9PiBtLnJvdXRlLmxvYWRlcikgfHwgaW5pdC5oeWRyYXRpb25EYXRhICE9IG51bGwpO1xuICBsZXQgcm91dGVyO1xuICBsZXQgc3RhdGUgPSB7XG4gICAgaGlzdG9yeUFjdGlvbjogaW5pdC5oaXN0b3J5LmFjdGlvbixcbiAgICBsb2NhdGlvbjogaW5pdC5oaXN0b3J5LmxvY2F0aW9uLFxuICAgIG1hdGNoZXM6IGluaXRpYWxNYXRjaGVzLFxuICAgIGluaXRpYWxpemVkLFxuICAgIG5hdmlnYXRpb246IElETEVfTkFWSUdBVElPTixcbiAgICAvLyBEb24ndCByZXN0b3JlIG9uIGluaXRpYWwgdXBkYXRlU3RhdGUoKSBpZiB3ZSB3ZXJlIFNTUidkXG4gICAgcmVzdG9yZVNjcm9sbFBvc2l0aW9uOiBpbml0Lmh5ZHJhdGlvbkRhdGEgIT0gbnVsbCA/IGZhbHNlIDogbnVsbCxcbiAgICBwcmV2ZW50U2Nyb2xsUmVzZXQ6IGZhbHNlLFxuICAgIHJldmFsaWRhdGlvbjogXCJpZGxlXCIsXG4gICAgbG9hZGVyRGF0YTogaW5pdC5oeWRyYXRpb25EYXRhICYmIGluaXQuaHlkcmF0aW9uRGF0YS5sb2FkZXJEYXRhIHx8IHt9LFxuICAgIGFjdGlvbkRhdGE6IGluaXQuaHlkcmF0aW9uRGF0YSAmJiBpbml0Lmh5ZHJhdGlvbkRhdGEuYWN0aW9uRGF0YSB8fCBudWxsLFxuICAgIGVycm9yczogaW5pdC5oeWRyYXRpb25EYXRhICYmIGluaXQuaHlkcmF0aW9uRGF0YS5lcnJvcnMgfHwgaW5pdGlhbEVycm9ycyxcbiAgICBmZXRjaGVyczogbmV3IE1hcCgpLFxuICAgIGJsb2NrZXJzOiBuZXcgTWFwKClcbiAgfTtcbiAgLy8gLS0gU3RhdGVmdWwgaW50ZXJuYWwgdmFyaWFibGVzIHRvIG1hbmFnZSBuYXZpZ2F0aW9ucyAtLVxuICAvLyBDdXJyZW50IG5hdmlnYXRpb24gaW4gcHJvZ3Jlc3MgKHRvIGJlIGNvbW1pdHRlZCBpbiBjb21wbGV0ZU5hdmlnYXRpb24pXG4gIGxldCBwZW5kaW5nQWN0aW9uID0gQWN0aW9uLlBvcDtcbiAgLy8gU2hvdWxkIHRoZSBjdXJyZW50IG5hdmlnYXRpb24gcHJldmVudCB0aGUgc2Nyb2xsIHJlc2V0IGlmIHNjcm9sbCBjYW5ub3RcbiAgLy8gYmUgcmVzdG9yZWQ/XG4gIGxldCBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gZmFsc2U7XG4gIC8vIEFib3J0Q29udHJvbGxlciBmb3IgdGhlIGFjdGl2ZSBuYXZpZ2F0aW9uXG4gIGxldCBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXI7XG4gIC8vIFdlIHVzZSB0aGlzIHRvIGF2b2lkIHRvdWNoaW5nIGhpc3RvcnkgaW4gY29tcGxldGVOYXZpZ2F0aW9uIGlmIGFcbiAgLy8gcmV2YWxpZGF0aW9uIGlzIGVudGlyZWx5IHVuaW50ZXJydXB0ZWRcbiAgbGV0IGlzVW5pbnRlcnJ1cHRlZFJldmFsaWRhdGlvbiA9IGZhbHNlO1xuICAvLyBVc2UgdGhpcyBpbnRlcm5hbCBmbGFnIHRvIGZvcmNlIHJldmFsaWRhdGlvbiBvZiBhbGwgbG9hZGVyczpcbiAgLy8gIC0gc3VibWlzc2lvbnMgKGNvbXBsZXRlZCBvciBpbnRlcnJ1cHRlZClcbiAgLy8gIC0gdXNlUmV2YWxpZGF0b3IoKVxuICAvLyAgLSBYLVJlbWl4LVJldmFsaWRhdGUgKGZyb20gcmVkaXJlY3QpXG4gIGxldCBpc1JldmFsaWRhdGlvblJlcXVpcmVkID0gZmFsc2U7XG4gIC8vIFVzZSB0aGlzIGludGVybmFsIGFycmF5IHRvIGNhcHR1cmUgcm91dGVzIHRoYXQgcmVxdWlyZSByZXZhbGlkYXRpb24gZHVlXG4gIC8vIHRvIGEgY2FuY2VsbGVkIGRlZmVycmVkIG9uIGFjdGlvbiBzdWJtaXNzaW9uXG4gIGxldCBjYW5jZWxsZWREZWZlcnJlZFJvdXRlcyA9IFtdO1xuICAvLyBVc2UgdGhpcyBpbnRlcm5hbCBhcnJheSB0byBjYXB0dXJlIGZldGNoZXIgbG9hZHMgdGhhdCB3ZXJlIGNhbmNlbGxlZCBieSBhblxuICAvLyBhY3Rpb24gbmF2aWdhdGlvbiBhbmQgcmVxdWlyZSByZXZhbGlkYXRpb25cbiAgbGV0IGNhbmNlbGxlZEZldGNoZXJMb2FkcyA9IFtdO1xuICAvLyBBYm9ydENvbnRyb2xsZXJzIGZvciBhbnkgaW4tZmxpZ2h0IGZldGNoZXJzXG4gIGxldCBmZXRjaENvbnRyb2xsZXJzID0gbmV3IE1hcCgpO1xuICAvLyBUcmFjayBsb2FkcyBiYXNlZCBvbiB0aGUgb3JkZXIgaW4gd2hpY2ggdGhleSBzdGFydGVkXG4gIGxldCBpbmNyZW1lbnRpbmdMb2FkSWQgPSAwO1xuICAvLyBUcmFjayB0aGUgb3V0c3RhbmRpbmcgcGVuZGluZyBuYXZpZ2F0aW9uIGRhdGEgbG9hZCB0byBiZSBjb21wYXJlZCBhZ2FpbnN0XG4gIC8vIHRoZSBnbG9iYWxseSBpbmNyZW1lbnRpbmcgbG9hZCB3aGVuIGEgZmV0Y2hlciBsb2FkIGxhbmRzIGFmdGVyIGEgY29tcGxldGVkXG4gIC8vIG5hdmlnYXRpb25cbiAgbGV0IHBlbmRpbmdOYXZpZ2F0aW9uTG9hZElkID0gLTE7XG4gIC8vIEZldGNoZXJzIHRoYXQgdHJpZ2dlcmVkIGRhdGEgcmVsb2FkcyBhcyBhIHJlc3VsdCBvZiB0aGVpciBhY3Rpb25zXG4gIGxldCBmZXRjaFJlbG9hZElkcyA9IG5ldyBNYXAoKTtcbiAgLy8gRmV0Y2hlcnMgdGhhdCB0cmlnZ2VyZWQgcmVkaXJlY3QgbmF2aWdhdGlvbnNcbiAgbGV0IGZldGNoUmVkaXJlY3RJZHMgPSBuZXcgU2V0KCk7XG4gIC8vIE1vc3QgcmVjZW50IGhyZWYvbWF0Y2ggZm9yIGZldGNoZXIubG9hZCBjYWxscyBmb3IgZmV0Y2hlcnNcbiAgbGV0IGZldGNoTG9hZE1hdGNoZXMgPSBuZXcgTWFwKCk7XG4gIC8vIFN0b3JlIERlZmVycmVkRGF0YSBpbnN0YW5jZXMgZm9yIGFjdGl2ZSByb3V0ZSBtYXRjaGVzLiAgV2hlbiBhXG4gIC8vIHJvdXRlIGxvYWRlciByZXR1cm5zIGRlZmVyKCkgd2Ugc3RpY2sgb25lIGluIGhlcmUuICBUaGVuLCB3aGVuIGEgbmVzdGVkXG4gIC8vIHByb21pc2UgcmVzb2x2ZXMgd2UgdXBkYXRlIGxvYWRlckRhdGEuICBJZiBhIG5ldyBuYXZpZ2F0aW9uIHN0YXJ0cyB3ZVxuICAvLyBjYW5jZWwgYWN0aXZlIGRlZmVycmVkcyBmb3IgZWxpbWluYXRlZCByb3V0ZXMuXG4gIGxldCBhY3RpdmVEZWZlcnJlZHMgPSBuZXcgTWFwKCk7XG4gIC8vIFN0b3JlIGJsb2NrZXIgZnVuY3Rpb25zIGluIGEgc2VwYXJhdGUgTWFwIG91dHNpZGUgb2Ygcm91dGVyIHN0YXRlIHNpbmNlXG4gIC8vIHdlIGRvbid0IG5lZWQgdG8gdXBkYXRlIFVJIHN0YXRlIGlmIHRoZXkgY2hhbmdlXG4gIGxldCBibG9ja2VyRnVuY3Rpb25zID0gbmV3IE1hcCgpO1xuICAvLyBGbGFnIHRvIGlnbm9yZSB0aGUgbmV4dCBoaXN0b3J5IHVwZGF0ZSwgc28gd2UgY2FuIHJldmVydCB0aGUgVVJMIGNoYW5nZSBvblxuICAvLyBhIFBPUCBuYXZpZ2F0aW9uIHRoYXQgd2FzIGJsb2NrZWQgYnkgdGhlIHVzZXIgd2l0aG91dCB0b3VjaGluZyByb3V0ZXIgc3RhdGVcbiAgbGV0IGlnbm9yZU5leHRIaXN0b3J5VXBkYXRlID0gZmFsc2U7XG4gIC8vIEluaXRpYWxpemUgdGhlIHJvdXRlciwgYWxsIHNpZGUgZWZmZWN0cyBzaG91bGQgYmUga2lja2VkIG9mZiBmcm9tIGhlcmUuXG4gIC8vIEltcGxlbWVudGVkIGFzIGEgRmx1ZW50IEFQSSBmb3IgZWFzZSBvZjpcbiAgLy8gICBsZXQgcm91dGVyID0gY3JlYXRlUm91dGVyKGluaXQpLmluaXRpYWxpemUoKTtcbiAgZnVuY3Rpb24gaW5pdGlhbGl6ZSgpIHtcbiAgICAvLyBJZiBoaXN0b3J5IGluZm9ybXMgdXMgb2YgYSBQT1AgbmF2aWdhdGlvbiwgc3RhcnQgdGhlIG5hdmlnYXRpb24gYnV0IGRvIG5vdCB1cGRhdGVcbiAgICAvLyBzdGF0ZS4gIFdlJ2xsIHVwZGF0ZSBvdXIgb3duIHN0YXRlIG9uY2UgdGhlIG5hdmlnYXRpb24gY29tcGxldGVzXG4gICAgdW5saXN0ZW5IaXN0b3J5ID0gaW5pdC5oaXN0b3J5Lmxpc3RlbihfcmVmID0+IHtcbiAgICAgIGxldCB7XG4gICAgICAgIGFjdGlvbjogaGlzdG9yeUFjdGlvbixcbiAgICAgICAgbG9jYXRpb24sXG4gICAgICAgIGRlbHRhXG4gICAgICB9ID0gX3JlZjtcbiAgICAgIC8vIElnbm9yZSB0aGlzIGV2ZW50IGlmIGl0IHdhcyBqdXN0IHVzIHJlc2V0dGluZyB0aGUgVVJMIGZyb20gYVxuICAgICAgLy8gYmxvY2tlZCBQT1AgbmF2aWdhdGlvblxuICAgICAgaWYgKGlnbm9yZU5leHRIaXN0b3J5VXBkYXRlKSB7XG4gICAgICAgIGlnbm9yZU5leHRIaXN0b3J5VXBkYXRlID0gZmFsc2U7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHdhcm5pbmcoYmxvY2tlckZ1bmN0aW9ucy5zaXplID09PSAwIHx8IGRlbHRhICE9IG51bGwsIFwiWW91IGFyZSB0cnlpbmcgdG8gdXNlIGEgYmxvY2tlciBvbiBhIFBPUCBuYXZpZ2F0aW9uIHRvIGEgbG9jYXRpb24gXCIgKyBcInRoYXQgd2FzIG5vdCBjcmVhdGVkIGJ5IEByZW1peC1ydW4vcm91dGVyLiBUaGlzIHdpbGwgZmFpbCBzaWxlbnRseSBpbiBcIiArIFwicHJvZHVjdGlvbi4gVGhpcyBjYW4gaGFwcGVuIGlmIHlvdSBhcmUgbmF2aWdhdGluZyBvdXRzaWRlIHRoZSByb3V0ZXIgXCIgKyBcInZpYSBgd2luZG93Lmhpc3RvcnkucHVzaFN0YXRlYC9gd2luZG93LmxvY2F0aW9uLmhhc2hgIGluc3RlYWQgb2YgdXNpbmcgXCIgKyBcInJvdXRlciBuYXZpZ2F0aW9uIEFQSXMuICBUaGlzIGNhbiBhbHNvIGhhcHBlbiBpZiB5b3UgYXJlIHVzaW5nIFwiICsgXCJjcmVhdGVIYXNoUm91dGVyIGFuZCB0aGUgdXNlciBtYW51YWxseSBjaGFuZ2VzIHRoZSBVUkwuXCIpO1xuICAgICAgbGV0IGJsb2NrZXJLZXkgPSBzaG91bGRCbG9ja05hdmlnYXRpb24oe1xuICAgICAgICBjdXJyZW50TG9jYXRpb246IHN0YXRlLmxvY2F0aW9uLFxuICAgICAgICBuZXh0TG9jYXRpb246IGxvY2F0aW9uLFxuICAgICAgICBoaXN0b3J5QWN0aW9uXG4gICAgICB9KTtcbiAgICAgIGlmIChibG9ja2VyS2V5ICYmIGRlbHRhICE9IG51bGwpIHtcbiAgICAgICAgLy8gUmVzdG9yZSB0aGUgVVJMIHRvIG1hdGNoIHRoZSBjdXJyZW50IFVJLCBidXQgZG9uJ3QgdXBkYXRlIHJvdXRlciBzdGF0ZVxuICAgICAgICBpZ25vcmVOZXh0SGlzdG9yeVVwZGF0ZSA9IHRydWU7XG4gICAgICAgIGluaXQuaGlzdG9yeS5nbyhkZWx0YSAqIC0xKTtcbiAgICAgICAgLy8gUHV0IHRoZSBibG9ja2VyIGludG8gYSBibG9ja2VkIHN0YXRlXG4gICAgICAgIHVwZGF0ZUJsb2NrZXIoYmxvY2tlcktleSwge1xuICAgICAgICAgIHN0YXRlOiBcImJsb2NrZWRcIixcbiAgICAgICAgICBsb2NhdGlvbixcbiAgICAgICAgICBwcm9jZWVkKCkge1xuICAgICAgICAgICAgdXBkYXRlQmxvY2tlcihibG9ja2VyS2V5LCB7XG4gICAgICAgICAgICAgIHN0YXRlOiBcInByb2NlZWRpbmdcIixcbiAgICAgICAgICAgICAgcHJvY2VlZDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICByZXNldDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICBsb2NhdGlvblxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBSZS1kbyB0aGUgc2FtZSBQT1AgbmF2aWdhdGlvbiB3ZSBqdXN0IGJsb2NrZWRcbiAgICAgICAgICAgIGluaXQuaGlzdG9yeS5nbyhkZWx0YSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXNldCgpIHtcbiAgICAgICAgICAgIGxldCBibG9ja2VycyA9IG5ldyBNYXAoc3RhdGUuYmxvY2tlcnMpO1xuICAgICAgICAgICAgYmxvY2tlcnMuc2V0KGJsb2NrZXJLZXksIElETEVfQkxPQ0tFUik7XG4gICAgICAgICAgICB1cGRhdGVTdGF0ZSh7XG4gICAgICAgICAgICAgIGJsb2NrZXJzXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICByZXR1cm4gc3RhcnROYXZpZ2F0aW9uKGhpc3RvcnlBY3Rpb24sIGxvY2F0aW9uKTtcbiAgICB9KTtcbiAgICAvLyBLaWNrIG9mZiBpbml0aWFsIGRhdGEgbG9hZCBpZiBuZWVkZWQuICBVc2UgUG9wIHRvIGF2b2lkIG1vZGlmeWluZyBoaXN0b3J5XG4gICAgLy8gTm90ZSB3ZSBkb24ndCBkbyBhbnkgaGFuZGxpbmcgb2YgbGF6eSBoZXJlLiAgRm9yIFNQQSdzIGl0J2xsIGdldCBoYW5kbGVkXG4gICAgLy8gaW4gdGhlIG5vcm1hbCBuYXZpZ2F0aW9uIGZsb3cuICBGb3IgU1NSIGl0J3MgZXhwZWN0ZWQgdGhhdCBsYXp5IG1vZHVsZXMgYXJlXG4gICAgLy8gcmVzb2x2ZWQgcHJpb3IgdG8gcm91dGVyIGNyZWF0aW9uIHNpbmNlIHdlIGNhbid0IGdvIGludG8gYSBmYWxsYmFja0VsZW1lbnRcbiAgICAvLyBVSSBmb3IgU1NSJ2QgYXBwc1xuICAgIGlmICghc3RhdGUuaW5pdGlhbGl6ZWQpIHtcbiAgICAgIHN0YXJ0TmF2aWdhdGlvbihBY3Rpb24uUG9wLCBzdGF0ZS5sb2NhdGlvbik7XG4gICAgfVxuICAgIHJldHVybiByb3V0ZXI7XG4gIH1cbiAgLy8gQ2xlYW4gdXAgYSByb3V0ZXIgYW5kIGl0J3Mgc2lkZSBlZmZlY3RzXG4gIGZ1bmN0aW9uIGRpc3Bvc2UoKSB7XG4gICAgaWYgKHVubGlzdGVuSGlzdG9yeSkge1xuICAgICAgdW5saXN0ZW5IaXN0b3J5KCk7XG4gICAgfVxuICAgIHN1YnNjcmliZXJzLmNsZWFyKCk7XG4gICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyICYmIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlci5hYm9ydCgpO1xuICAgIHN0YXRlLmZldGNoZXJzLmZvckVhY2goKF8sIGtleSkgPT4gZGVsZXRlRmV0Y2hlcihrZXkpKTtcbiAgICBzdGF0ZS5ibG9ja2Vycy5mb3JFYWNoKChfLCBrZXkpID0+IGRlbGV0ZUJsb2NrZXIoa2V5KSk7XG4gIH1cbiAgLy8gU3Vic2NyaWJlIHRvIHN0YXRlIHVwZGF0ZXMgZm9yIHRoZSByb3V0ZXJcbiAgZnVuY3Rpb24gc3Vic2NyaWJlKGZuKSB7XG4gICAgc3Vic2NyaWJlcnMuYWRkKGZuKTtcbiAgICByZXR1cm4gKCkgPT4gc3Vic2NyaWJlcnMuZGVsZXRlKGZuKTtcbiAgfVxuICAvLyBVcGRhdGUgb3VyIHN0YXRlIGFuZCBub3RpZnkgdGhlIGNhbGxpbmcgY29udGV4dCBvZiB0aGUgY2hhbmdlXG4gIGZ1bmN0aW9uIHVwZGF0ZVN0YXRlKG5ld1N0YXRlKSB7XG4gICAgc3RhdGUgPSBfZXh0ZW5kcyh7fSwgc3RhdGUsIG5ld1N0YXRlKTtcbiAgICBzdWJzY3JpYmVycy5mb3JFYWNoKHN1YnNjcmliZXIgPT4gc3Vic2NyaWJlcihzdGF0ZSkpO1xuICB9XG4gIC8vIENvbXBsZXRlIGEgbmF2aWdhdGlvbiByZXR1cm5pbmcgdGhlIHN0YXRlLm5hdmlnYXRpb24gYmFjayB0byB0aGUgSURMRV9OQVZJR0FUSU9OXG4gIC8vIGFuZCBzZXR0aW5nIHN0YXRlLltoaXN0b3J5QWN0aW9uL2xvY2F0aW9uL21hdGNoZXNdIHRvIHRoZSBuZXcgcm91dGUuXG4gIC8vIC0gTG9jYXRpb24gaXMgYSByZXF1aXJlZCBwYXJhbVxuICAvLyAtIE5hdmlnYXRpb24gd2lsbCBhbHdheXMgYmUgc2V0IHRvIElETEVfTkFWSUdBVElPTlxuICAvLyAtIENhbiBwYXNzIGFueSBvdGhlciBzdGF0ZSBpbiBuZXdTdGF0ZVxuICBmdW5jdGlvbiBjb21wbGV0ZU5hdmlnYXRpb24obG9jYXRpb24sIG5ld1N0YXRlKSB7XG4gICAgdmFyIF9sb2NhdGlvbiRzdGF0ZSwgX2xvY2F0aW9uJHN0YXRlMjtcbiAgICAvLyBEZWR1Y2UgaWYgd2UncmUgaW4gYSBsb2FkaW5nL2FjdGlvblJlbG9hZCBzdGF0ZTpcbiAgICAvLyAtIFdlIGhhdmUgY29tbWl0dGVkIGFjdGlvbkRhdGEgaW4gdGhlIHN0b3JlXG4gICAgLy8gLSBUaGUgY3VycmVudCBuYXZpZ2F0aW9uIHdhcyBhIG11dGF0aW9uIHN1Ym1pc3Npb25cbiAgICAvLyAtIFdlJ3JlIHBhc3QgdGhlIHN1Ym1pdHRpbmcgc3RhdGUgYW5kIGludG8gdGhlIGxvYWRpbmcgc3RhdGVcbiAgICAvLyAtIFRoZSBsb2NhdGlvbiBiZWluZyBsb2FkZWQgaXMgbm90IHRoZSByZXN1bHQgb2YgYSByZWRpcmVjdFxuICAgIGxldCBpc0FjdGlvblJlbG9hZCA9IHN0YXRlLmFjdGlvbkRhdGEgIT0gbnVsbCAmJiBzdGF0ZS5uYXZpZ2F0aW9uLmZvcm1NZXRob2QgIT0gbnVsbCAmJiBpc011dGF0aW9uTWV0aG9kKHN0YXRlLm5hdmlnYXRpb24uZm9ybU1ldGhvZCkgJiYgc3RhdGUubmF2aWdhdGlvbi5zdGF0ZSA9PT0gXCJsb2FkaW5nXCIgJiYgKChfbG9jYXRpb24kc3RhdGUgPSBsb2NhdGlvbi5zdGF0ZSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9sb2NhdGlvbiRzdGF0ZS5faXNSZWRpcmVjdCkgIT09IHRydWU7XG4gICAgbGV0IGFjdGlvbkRhdGE7XG4gICAgaWYgKG5ld1N0YXRlLmFjdGlvbkRhdGEpIHtcbiAgICAgIGlmIChPYmplY3Qua2V5cyhuZXdTdGF0ZS5hY3Rpb25EYXRhKS5sZW5ndGggPiAwKSB7XG4gICAgICAgIGFjdGlvbkRhdGEgPSBuZXdTdGF0ZS5hY3Rpb25EYXRhO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gRW1wdHkgYWN0aW9uRGF0YSAtPiBjbGVhciBwcmlvciBhY3Rpb25EYXRhIGR1ZSB0byBhbiBhY3Rpb24gZXJyb3JcbiAgICAgICAgYWN0aW9uRGF0YSA9IG51bGw7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChpc0FjdGlvblJlbG9hZCkge1xuICAgICAgLy8gS2VlcCB0aGUgY3VycmVudCBkYXRhIGlmIHdlJ3JlIHdyYXBwaW5nIHVwIHRoZSBhY3Rpb24gcmVsb2FkXG4gICAgICBhY3Rpb25EYXRhID0gc3RhdGUuYWN0aW9uRGF0YTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQ2xlYXIgYWN0aW9uRGF0YSBvbiBhbnkgb3RoZXIgY29tcGxldGVkIG5hdmlnYXRpb25zXG4gICAgICBhY3Rpb25EYXRhID0gbnVsbDtcbiAgICB9XG4gICAgLy8gQWx3YXlzIHByZXNlcnZlIGFueSBleGlzdGluZyBsb2FkZXJEYXRhIGZyb20gcmUtdXNlZCByb3V0ZXNcbiAgICBsZXQgbG9hZGVyRGF0YSA9IG5ld1N0YXRlLmxvYWRlckRhdGEgPyBtZXJnZUxvYWRlckRhdGEoc3RhdGUubG9hZGVyRGF0YSwgbmV3U3RhdGUubG9hZGVyRGF0YSwgbmV3U3RhdGUubWF0Y2hlcyB8fCBbXSwgbmV3U3RhdGUuZXJyb3JzKSA6IHN0YXRlLmxvYWRlckRhdGE7XG4gICAgLy8gT24gYSBzdWNjZXNzZnVsIG5hdmlnYXRpb24gd2UgY2FuIGFzc3VtZSB3ZSBnb3QgdGhyb3VnaCBhbGwgYmxvY2tlcnNcbiAgICAvLyBzbyB3ZSBjYW4gc3RhcnQgZnJlc2hcbiAgICBsZXQgYmxvY2tlcnMgPSBuZXcgTWFwKCk7XG4gICAgYmxvY2tlckZ1bmN0aW9ucy5jbGVhcigpO1xuICAgIC8vIEFsd2F5cyByZXNwZWN0IHRoZSB1c2VyIGZsYWcuICBPdGhlcndpc2UgZG9uJ3QgcmVzZXQgb24gbXV0YXRpb25cbiAgICAvLyBzdWJtaXNzaW9uIG5hdmlnYXRpb25zIHVubGVzcyB0aGV5IHJlZGlyZWN0XG4gICAgbGV0IHByZXZlbnRTY3JvbGxSZXNldCA9IHBlbmRpbmdQcmV2ZW50U2Nyb2xsUmVzZXQgPT09IHRydWUgfHwgc3RhdGUubmF2aWdhdGlvbi5mb3JtTWV0aG9kICE9IG51bGwgJiYgaXNNdXRhdGlvbk1ldGhvZChzdGF0ZS5uYXZpZ2F0aW9uLmZvcm1NZXRob2QpICYmICgoX2xvY2F0aW9uJHN0YXRlMiA9IGxvY2F0aW9uLnN0YXRlKSA9PSBudWxsID8gdm9pZCAwIDogX2xvY2F0aW9uJHN0YXRlMi5faXNSZWRpcmVjdCkgIT09IHRydWU7XG4gICAgaWYgKGluRmxpZ2h0RGF0YVJvdXRlcykge1xuICAgICAgZGF0YVJvdXRlcyA9IGluRmxpZ2h0RGF0YVJvdXRlcztcbiAgICAgIGluRmxpZ2h0RGF0YVJvdXRlcyA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgaWYgKGlzVW5pbnRlcnJ1cHRlZFJldmFsaWRhdGlvbikgOyBlbHNlIGlmIChwZW5kaW5nQWN0aW9uID09PSBBY3Rpb24uUG9wKSA7IGVsc2UgaWYgKHBlbmRpbmdBY3Rpb24gPT09IEFjdGlvbi5QdXNoKSB7XG4gICAgICBpbml0Lmhpc3RvcnkucHVzaChsb2NhdGlvbiwgbG9jYXRpb24uc3RhdGUpO1xuICAgIH0gZWxzZSBpZiAocGVuZGluZ0FjdGlvbiA9PT0gQWN0aW9uLlJlcGxhY2UpIHtcbiAgICAgIGluaXQuaGlzdG9yeS5yZXBsYWNlKGxvY2F0aW9uLCBsb2NhdGlvbi5zdGF0ZSk7XG4gICAgfVxuICAgIHVwZGF0ZVN0YXRlKF9leHRlbmRzKHt9LCBuZXdTdGF0ZSwge1xuICAgICAgYWN0aW9uRGF0YSxcbiAgICAgIGxvYWRlckRhdGEsXG4gICAgICBoaXN0b3J5QWN0aW9uOiBwZW5kaW5nQWN0aW9uLFxuICAgICAgbG9jYXRpb24sXG4gICAgICBpbml0aWFsaXplZDogdHJ1ZSxcbiAgICAgIG5hdmlnYXRpb246IElETEVfTkFWSUdBVElPTixcbiAgICAgIHJldmFsaWRhdGlvbjogXCJpZGxlXCIsXG4gICAgICByZXN0b3JlU2Nyb2xsUG9zaXRpb246IGdldFNhdmVkU2Nyb2xsUG9zaXRpb24obG9jYXRpb24sIG5ld1N0YXRlLm1hdGNoZXMgfHwgc3RhdGUubWF0Y2hlcyksXG4gICAgICBwcmV2ZW50U2Nyb2xsUmVzZXQsXG4gICAgICBibG9ja2Vyc1xuICAgIH0pKTtcbiAgICAvLyBSZXNldCBzdGF0ZWZ1bCBuYXZpZ2F0aW9uIHZhcnNcbiAgICBwZW5kaW5nQWN0aW9uID0gQWN0aW9uLlBvcDtcbiAgICBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gZmFsc2U7XG4gICAgaXNVbmludGVycnVwdGVkUmV2YWxpZGF0aW9uID0gZmFsc2U7XG4gICAgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCA9IGZhbHNlO1xuICAgIGNhbmNlbGxlZERlZmVycmVkUm91dGVzID0gW107XG4gICAgY2FuY2VsbGVkRmV0Y2hlckxvYWRzID0gW107XG4gIH1cbiAgLy8gVHJpZ2dlciBhIG5hdmlnYXRpb24gZXZlbnQsIHdoaWNoIGNhbiBlaXRoZXIgYmUgYSBudW1lcmljYWwgUE9QIG9yIGEgUFVTSFxuICAvLyByZXBsYWNlIHdpdGggYW4gb3B0aW9uYWwgc3VibWlzc2lvblxuICBhc3luYyBmdW5jdGlvbiBuYXZpZ2F0ZSh0bywgb3B0cykge1xuICAgIGlmICh0eXBlb2YgdG8gPT09IFwibnVtYmVyXCIpIHtcbiAgICAgIGluaXQuaGlzdG9yeS5nbyh0byk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCBub3JtYWxpemVkUGF0aCA9IG5vcm1hbGl6ZVRvKHN0YXRlLmxvY2F0aW9uLCBzdGF0ZS5tYXRjaGVzLCBiYXNlbmFtZSwgZnV0dXJlLnY3X3ByZXBlbmRCYXNlbmFtZSwgdG8sIG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMuZnJvbVJvdXRlSWQsIG9wdHMgPT0gbnVsbCA/IHZvaWQgMCA6IG9wdHMucmVsYXRpdmUpO1xuICAgIGxldCB7XG4gICAgICBwYXRoLFxuICAgICAgc3VibWlzc2lvbixcbiAgICAgIGVycm9yXG4gICAgfSA9IG5vcm1hbGl6ZU5hdmlnYXRlT3B0aW9ucyhmdXR1cmUudjdfbm9ybWFsaXplRm9ybU1ldGhvZCwgZmFsc2UsIG5vcm1hbGl6ZWRQYXRoLCBvcHRzKTtcbiAgICBsZXQgY3VycmVudExvY2F0aW9uID0gc3RhdGUubG9jYXRpb247XG4gICAgbGV0IG5leHRMb2NhdGlvbiA9IGNyZWF0ZUxvY2F0aW9uKHN0YXRlLmxvY2F0aW9uLCBwYXRoLCBvcHRzICYmIG9wdHMuc3RhdGUpO1xuICAgIC8vIFdoZW4gdXNpbmcgbmF2aWdhdGUgYXMgYSBQVVNIL1JFUExBQ0Ugd2UgYXJlbid0IHJlYWRpbmcgYW4gYWxyZWFkeS1lbmNvZGVkXG4gICAgLy8gVVJMIGZyb20gd2luZG93LmxvY2F0aW9uLCBzbyB3ZSBuZWVkIHRvIGVuY29kZSBpdCBoZXJlIHNvIHRoZSBiZWhhdmlvclxuICAgIC8vIHJlbWFpbnMgdGhlIHNhbWUgYXMgUE9QIGFuZCBub24tZGF0YS1yb3V0ZXIgdXNhZ2VzLiAgbmV3IFVSTCgpIGRvZXMgYWxsXG4gICAgLy8gdGhlIHNhbWUgZW5jb2Rpbmcgd2UnZCBnZXQgZnJvbSBhIGhpc3RvcnkucHVzaFN0YXRlL3dpbmRvdy5sb2NhdGlvbiByZWFkXG4gICAgLy8gd2l0aG91dCBoYXZpbmcgdG8gdG91Y2ggaGlzdG9yeVxuICAgIG5leHRMb2NhdGlvbiA9IF9leHRlbmRzKHt9LCBuZXh0TG9jYXRpb24sIGluaXQuaGlzdG9yeS5lbmNvZGVMb2NhdGlvbihuZXh0TG9jYXRpb24pKTtcbiAgICBsZXQgdXNlclJlcGxhY2UgPSBvcHRzICYmIG9wdHMucmVwbGFjZSAhPSBudWxsID8gb3B0cy5yZXBsYWNlIDogdW5kZWZpbmVkO1xuICAgIGxldCBoaXN0b3J5QWN0aW9uID0gQWN0aW9uLlB1c2g7XG4gICAgaWYgKHVzZXJSZXBsYWNlID09PSB0cnVlKSB7XG4gICAgICBoaXN0b3J5QWN0aW9uID0gQWN0aW9uLlJlcGxhY2U7XG4gICAgfSBlbHNlIGlmICh1c2VyUmVwbGFjZSA9PT0gZmFsc2UpIDsgZWxzZSBpZiAoc3VibWlzc2lvbiAhPSBudWxsICYmIGlzTXV0YXRpb25NZXRob2Qoc3VibWlzc2lvbi5mb3JtTWV0aG9kKSAmJiBzdWJtaXNzaW9uLmZvcm1BY3Rpb24gPT09IHN0YXRlLmxvY2F0aW9uLnBhdGhuYW1lICsgc3RhdGUubG9jYXRpb24uc2VhcmNoKSB7XG4gICAgICAvLyBCeSBkZWZhdWx0IG9uIHN1Ym1pc3Npb25zIHRvIHRoZSBjdXJyZW50IGxvY2F0aW9uIHdlIFJFUExBQ0Ugc28gdGhhdFxuICAgICAgLy8gdXNlcnMgZG9uJ3QgaGF2ZSB0byBkb3VibGUtY2xpY2sgdGhlIGJhY2sgYnV0dG9uIHRvIGdldCB0byB0aGUgcHJpb3JcbiAgICAgIC8vIGxvY2F0aW9uLiAgSWYgdGhlIHVzZXIgcmVkaXJlY3RzIHRvIGEgZGlmZmVyZW50IGxvY2F0aW9uIGZyb20gdGhlXG4gICAgICAvLyBhY3Rpb24vbG9hZGVyIHRoaXMgd2lsbCBiZSBpZ25vcmVkIGFuZCB0aGUgcmVkaXJlY3Qgd2lsbCBiZSBhIFBVU0hcbiAgICAgIGhpc3RvcnlBY3Rpb24gPSBBY3Rpb24uUmVwbGFjZTtcbiAgICB9XG4gICAgbGV0IHByZXZlbnRTY3JvbGxSZXNldCA9IG9wdHMgJiYgXCJwcmV2ZW50U2Nyb2xsUmVzZXRcIiBpbiBvcHRzID8gb3B0cy5wcmV2ZW50U2Nyb2xsUmVzZXQgPT09IHRydWUgOiB1bmRlZmluZWQ7XG4gICAgbGV0IGJsb2NrZXJLZXkgPSBzaG91bGRCbG9ja05hdmlnYXRpb24oe1xuICAgICAgY3VycmVudExvY2F0aW9uLFxuICAgICAgbmV4dExvY2F0aW9uLFxuICAgICAgaGlzdG9yeUFjdGlvblxuICAgIH0pO1xuICAgIGlmIChibG9ja2VyS2V5KSB7XG4gICAgICAvLyBQdXQgdGhlIGJsb2NrZXIgaW50byBhIGJsb2NrZWQgc3RhdGVcbiAgICAgIHVwZGF0ZUJsb2NrZXIoYmxvY2tlcktleSwge1xuICAgICAgICBzdGF0ZTogXCJibG9ja2VkXCIsXG4gICAgICAgIGxvY2F0aW9uOiBuZXh0TG9jYXRpb24sXG4gICAgICAgIHByb2NlZWQoKSB7XG4gICAgICAgICAgdXBkYXRlQmxvY2tlcihibG9ja2VyS2V5LCB7XG4gICAgICAgICAgICBzdGF0ZTogXCJwcm9jZWVkaW5nXCIsXG4gICAgICAgICAgICBwcm9jZWVkOiB1bmRlZmluZWQsXG4gICAgICAgICAgICByZXNldDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgbG9jYXRpb246IG5leHRMb2NhdGlvblxuICAgICAgICAgIH0pO1xuICAgICAgICAgIC8vIFNlbmQgdGhlIHNhbWUgbmF2aWdhdGlvbiB0aHJvdWdoXG4gICAgICAgICAgbmF2aWdhdGUodG8sIG9wdHMpO1xuICAgICAgICB9LFxuICAgICAgICByZXNldCgpIHtcbiAgICAgICAgICBsZXQgYmxvY2tlcnMgPSBuZXcgTWFwKHN0YXRlLmJsb2NrZXJzKTtcbiAgICAgICAgICBibG9ja2Vycy5zZXQoYmxvY2tlcktleSwgSURMRV9CTE9DS0VSKTtcbiAgICAgICAgICB1cGRhdGVTdGF0ZSh7XG4gICAgICAgICAgICBibG9ja2Vyc1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmV0dXJuIGF3YWl0IHN0YXJ0TmF2aWdhdGlvbihoaXN0b3J5QWN0aW9uLCBuZXh0TG9jYXRpb24sIHtcbiAgICAgIHN1Ym1pc3Npb24sXG4gICAgICAvLyBTZW5kIHRocm91Z2ggdGhlIGZvcm1EYXRhIHNlcmlhbGl6YXRpb24gZXJyb3IgaWYgd2UgaGF2ZSBvbmUgc28gd2UgY2FuXG4gICAgICAvLyByZW5kZXIgYXQgdGhlIHJpZ2h0IGVycm9yIGJvdW5kYXJ5IGFmdGVyIHdlIG1hdGNoIHJvdXRlc1xuICAgICAgcGVuZGluZ0Vycm9yOiBlcnJvcixcbiAgICAgIHByZXZlbnRTY3JvbGxSZXNldCxcbiAgICAgIHJlcGxhY2U6IG9wdHMgJiYgb3B0cy5yZXBsYWNlXG4gICAgfSk7XG4gIH1cbiAgLy8gUmV2YWxpZGF0ZSBhbGwgY3VycmVudCBsb2FkZXJzLiAgSWYgYSBuYXZpZ2F0aW9uIGlzIGluIHByb2dyZXNzIG9yIGlmIHRoaXNcbiAgLy8gaXMgaW50ZXJydXB0ZWQgYnkgYSBuYXZpZ2F0aW9uLCBhbGxvdyB0aGlzIHRvIFwic3VjY2VlZFwiIGJ5IGNhbGxpbmcgYWxsXG4gIC8vIGxvYWRlcnMgZHVyaW5nIHRoZSBuZXh0IGxvYWRlciByb3VuZFxuICBmdW5jdGlvbiByZXZhbGlkYXRlKCkge1xuICAgIGludGVycnVwdEFjdGl2ZUxvYWRzKCk7XG4gICAgdXBkYXRlU3RhdGUoe1xuICAgICAgcmV2YWxpZGF0aW9uOiBcImxvYWRpbmdcIlxuICAgIH0pO1xuICAgIC8vIElmIHdlJ3JlIGN1cnJlbnRseSBzdWJtaXR0aW5nIGFuIGFjdGlvbiwgd2UgZG9uJ3QgbmVlZCB0byBzdGFydCBhIG5ld1xuICAgIC8vIG5hdmlnYXRpb24sIHdlJ2xsIGp1c3QgbGV0IHRoZSBmb2xsb3cgdXAgbG9hZGVyIGV4ZWN1dGlvbiBjYWxsIGFsbCBsb2FkZXJzXG4gICAgaWYgKHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgPT09IFwic3VibWl0dGluZ1wiKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIElmIHdlJ3JlIGN1cnJlbnRseSBpbiBhbiBpZGxlIHN0YXRlLCBzdGFydCBhIG5ldyBuYXZpZ2F0aW9uIGZvciB0aGUgY3VycmVudFxuICAgIC8vIGFjdGlvbi9sb2NhdGlvbiBhbmQgbWFyayBpdCBhcyB1bmludGVycnVwdGVkLCB3aGljaCB3aWxsIHNraXAgdGhlIGhpc3RvcnlcbiAgICAvLyB1cGRhdGUgaW4gY29tcGxldGVOYXZpZ2F0aW9uXG4gICAgaWYgKHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgPT09IFwiaWRsZVwiKSB7XG4gICAgICBzdGFydE5hdmlnYXRpb24oc3RhdGUuaGlzdG9yeUFjdGlvbiwgc3RhdGUubG9jYXRpb24sIHtcbiAgICAgICAgc3RhcnRVbmludGVycnVwdGVkUmV2YWxpZGF0aW9uOiB0cnVlXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gT3RoZXJ3aXNlLCBpZiB3ZSdyZSBjdXJyZW50bHkgaW4gYSBsb2FkaW5nIHN0YXRlLCBqdXN0IHN0YXJ0IGEgbmV3XG4gICAgLy8gbmF2aWdhdGlvbiB0byB0aGUgbmF2aWdhdGlvbi5sb2NhdGlvbiBidXQgZG8gbm90IHRyaWdnZXIgYW4gdW5pbnRlcnJ1cHRlZFxuICAgIC8vIHJldmFsaWRhdGlvbiBzbyB0aGF0IGhpc3RvcnkgY29ycmVjdGx5IHVwZGF0ZXMgb25jZSB0aGUgbmF2aWdhdGlvbiBjb21wbGV0ZXNcbiAgICBzdGFydE5hdmlnYXRpb24ocGVuZGluZ0FjdGlvbiB8fCBzdGF0ZS5oaXN0b3J5QWN0aW9uLCBzdGF0ZS5uYXZpZ2F0aW9uLmxvY2F0aW9uLCB7XG4gICAgICBvdmVycmlkZU5hdmlnYXRpb246IHN0YXRlLm5hdmlnYXRpb25cbiAgICB9KTtcbiAgfVxuICAvLyBTdGFydCBhIG5hdmlnYXRpb24gdG8gdGhlIGdpdmVuIGFjdGlvbi9sb2NhdGlvbi4gIENhbiBvcHRpb25hbGx5IHByb3ZpZGUgYVxuICAvLyBvdmVycmlkZU5hdmlnYXRpb24gd2hpY2ggd2lsbCBvdmVycmlkZSB0aGUgbm9ybWFsTG9hZCBpbiB0aGUgY2FzZSBvZiBhIHJlZGlyZWN0XG4gIC8vIG5hdmlnYXRpb25cbiAgYXN5bmMgZnVuY3Rpb24gc3RhcnROYXZpZ2F0aW9uKGhpc3RvcnlBY3Rpb24sIGxvY2F0aW9uLCBvcHRzKSB7XG4gICAgLy8gQWJvcnQgYW55IGluLXByb2dyZXNzIG5hdmlnYXRpb25zIGFuZCBzdGFydCBhIG5ldyBvbmUuIFVuc2V0IGFueSBvbmdvaW5nXG4gICAgLy8gdW5pbnRlcnJ1cHRlZCByZXZhbGlkYXRpb25zIHVubGVzcyB0b2xkIG90aGVyd2lzZSwgc2luY2Ugd2Ugd2FudCB0aGlzXG4gICAgLy8gbmV3IG5hdmlnYXRpb24gdG8gdXBkYXRlIGhpc3Rvcnkgbm9ybWFsbHlcbiAgICBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIgJiYgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyLmFib3J0KCk7XG4gICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyID0gbnVsbDtcbiAgICBwZW5kaW5nQWN0aW9uID0gaGlzdG9yeUFjdGlvbjtcbiAgICBpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24gPSAob3B0cyAmJiBvcHRzLnN0YXJ0VW5pbnRlcnJ1cHRlZFJldmFsaWRhdGlvbikgPT09IHRydWU7XG4gICAgLy8gU2F2ZSB0aGUgY3VycmVudCBzY3JvbGwgcG9zaXRpb24gZXZlcnkgdGltZSB3ZSBzdGFydCBhIG5ldyBuYXZpZ2F0aW9uLFxuICAgIC8vIGFuZCB0cmFjayB3aGV0aGVyIHdlIHNob3VsZCByZXNldCBzY3JvbGwgb24gY29tcGxldGlvblxuICAgIHNhdmVTY3JvbGxQb3NpdGlvbihzdGF0ZS5sb2NhdGlvbiwgc3RhdGUubWF0Y2hlcyk7XG4gICAgcGVuZGluZ1ByZXZlbnRTY3JvbGxSZXNldCA9IChvcHRzICYmIG9wdHMucHJldmVudFNjcm9sbFJlc2V0KSA9PT0gdHJ1ZTtcbiAgICBsZXQgcm91dGVzVG9Vc2UgPSBpbkZsaWdodERhdGFSb3V0ZXMgfHwgZGF0YVJvdXRlcztcbiAgICBsZXQgbG9hZGluZ05hdmlnYXRpb24gPSBvcHRzICYmIG9wdHMub3ZlcnJpZGVOYXZpZ2F0aW9uO1xuICAgIGxldCBtYXRjaGVzID0gbWF0Y2hSb3V0ZXMocm91dGVzVG9Vc2UsIGxvY2F0aW9uLCBiYXNlbmFtZSk7XG4gICAgLy8gU2hvcnQgY2lyY3VpdCB3aXRoIGEgNDA0IG9uIHRoZSByb290IGVycm9yIGJvdW5kYXJ5IGlmIHdlIG1hdGNoIG5vdGhpbmdcbiAgICBpZiAoIW1hdGNoZXMpIHtcbiAgICAgIGxldCBlcnJvciA9IGdldEludGVybmFsUm91dGVyRXJyb3IoNDA0LCB7XG4gICAgICAgIHBhdGhuYW1lOiBsb2NhdGlvbi5wYXRobmFtZVxuICAgICAgfSk7XG4gICAgICBsZXQge1xuICAgICAgICBtYXRjaGVzOiBub3RGb3VuZE1hdGNoZXMsXG4gICAgICAgIHJvdXRlXG4gICAgICB9ID0gZ2V0U2hvcnRDaXJjdWl0TWF0Y2hlcyhyb3V0ZXNUb1VzZSk7XG4gICAgICAvLyBDYW5jZWwgYWxsIHBlbmRpbmcgZGVmZXJyZWQgb24gNDA0cyBzaW5jZSB3ZSBkb24ndCBrZWVwIGFueSByb3V0ZXNcbiAgICAgIGNhbmNlbEFjdGl2ZURlZmVycmVkcygpO1xuICAgICAgY29tcGxldGVOYXZpZ2F0aW9uKGxvY2F0aW9uLCB7XG4gICAgICAgIG1hdGNoZXM6IG5vdEZvdW5kTWF0Y2hlcyxcbiAgICAgICAgbG9hZGVyRGF0YToge30sXG4gICAgICAgIGVycm9yczoge1xuICAgICAgICAgIFtyb3V0ZS5pZF06IGVycm9yXG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBTaG9ydCBjaXJjdWl0IGlmIGl0J3Mgb25seSBhIGhhc2ggY2hhbmdlIGFuZCBub3QgYSByZXZhbGlkYXRpb24gb3JcbiAgICAvLyBtdXRhdGlvbiBzdWJtaXNzaW9uLlxuICAgIC8vXG4gICAgLy8gSWdub3JlIG9uIGluaXRpYWwgcGFnZSBsb2FkcyBiZWNhdXNlIHNpbmNlIHRoZSBpbml0aWFsIGxvYWQgd2lsbCBhbHdheXNcbiAgICAvLyBiZSBcInNhbWUgaGFzaFwiLiAgRm9yIGV4YW1wbGUsIG9uIC9wYWdlI2hhc2ggYW5kIHN1Ym1pdCBhIDxGb3JtIG1ldGhvZD1cInBvc3RcIj5cbiAgICAvLyB3aGljaCB3aWxsIGRlZmF1bHQgdG8gYSBuYXZpZ2F0aW9uIHRvIC9wYWdlXG4gICAgaWYgKHN0YXRlLmluaXRpYWxpemVkICYmICFpc1JldmFsaWRhdGlvblJlcXVpcmVkICYmIGlzSGFzaENoYW5nZU9ubHkoc3RhdGUubG9jYXRpb24sIGxvY2F0aW9uKSAmJiAhKG9wdHMgJiYgb3B0cy5zdWJtaXNzaW9uICYmIGlzTXV0YXRpb25NZXRob2Qob3B0cy5zdWJtaXNzaW9uLmZvcm1NZXRob2QpKSkge1xuICAgICAgY29tcGxldGVOYXZpZ2F0aW9uKGxvY2F0aW9uLCB7XG4gICAgICAgIG1hdGNoZXNcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBDcmVhdGUgYSBjb250cm9sbGVyL1JlcXVlc3QgZm9yIHRoaXMgbmF2aWdhdGlvblxuICAgIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBsZXQgcmVxdWVzdCA9IGNyZWF0ZUNsaWVudFNpZGVSZXF1ZXN0KGluaXQuaGlzdG9yeSwgbG9jYXRpb24sIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlci5zaWduYWwsIG9wdHMgJiYgb3B0cy5zdWJtaXNzaW9uKTtcbiAgICBsZXQgcGVuZGluZ0FjdGlvbkRhdGE7XG4gICAgbGV0IHBlbmRpbmdFcnJvcjtcbiAgICBpZiAob3B0cyAmJiBvcHRzLnBlbmRpbmdFcnJvcikge1xuICAgICAgLy8gSWYgd2UgaGF2ZSBhIHBlbmRpbmdFcnJvciwgaXQgbWVhbnMgdGhlIHVzZXIgYXR0ZW1wdGVkIGEgR0VUIHN1Ym1pc3Npb25cbiAgICAgIC8vIHdpdGggYmluYXJ5IEZvcm1EYXRhIHNvIGFzc2lnbiBoZXJlIGFuZCBza2lwIHRvIGhhbmRsZUxvYWRlcnMuICBUaGF0XG4gICAgICAvLyB3YXkgd2UgaGFuZGxlIGNhbGxpbmcgbG9hZGVycyBhYm92ZSB0aGUgYm91bmRhcnkgZXRjLiAgSXQncyBub3QgcmVhbGx5XG4gICAgICAvLyBkaWZmZXJlbnQgZnJvbSBhbiBhY3Rpb25FcnJvciBpbiB0aGF0IHNlbnNlLlxuICAgICAgcGVuZGluZ0Vycm9yID0ge1xuICAgICAgICBbZmluZE5lYXJlc3RCb3VuZGFyeShtYXRjaGVzKS5yb3V0ZS5pZF06IG9wdHMucGVuZGluZ0Vycm9yXG4gICAgICB9O1xuICAgIH0gZWxzZSBpZiAob3B0cyAmJiBvcHRzLnN1Ym1pc3Npb24gJiYgaXNNdXRhdGlvbk1ldGhvZChvcHRzLnN1Ym1pc3Npb24uZm9ybU1ldGhvZCkpIHtcbiAgICAgIC8vIENhbGwgYWN0aW9uIGlmIHdlIHJlY2VpdmVkIGFuIGFjdGlvbiBzdWJtaXNzaW9uXG4gICAgICBsZXQgYWN0aW9uT3V0cHV0ID0gYXdhaXQgaGFuZGxlQWN0aW9uKHJlcXVlc3QsIGxvY2F0aW9uLCBvcHRzLnN1Ym1pc3Npb24sIG1hdGNoZXMsIHtcbiAgICAgICAgcmVwbGFjZTogb3B0cy5yZXBsYWNlXG4gICAgICB9KTtcbiAgICAgIGlmIChhY3Rpb25PdXRwdXQuc2hvcnRDaXJjdWl0ZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgcGVuZGluZ0FjdGlvbkRhdGEgPSBhY3Rpb25PdXRwdXQucGVuZGluZ0FjdGlvbkRhdGE7XG4gICAgICBwZW5kaW5nRXJyb3IgPSBhY3Rpb25PdXRwdXQucGVuZGluZ0FjdGlvbkVycm9yO1xuICAgICAgbG9hZGluZ05hdmlnYXRpb24gPSBnZXRMb2FkaW5nTmF2aWdhdGlvbihsb2NhdGlvbiwgb3B0cy5zdWJtaXNzaW9uKTtcbiAgICAgIC8vIENyZWF0ZSBhIEdFVCByZXF1ZXN0IGZvciB0aGUgbG9hZGVyc1xuICAgICAgcmVxdWVzdCA9IG5ldyBSZXF1ZXN0KHJlcXVlc3QudXJsLCB7XG4gICAgICAgIHNpZ25hbDogcmVxdWVzdC5zaWduYWxcbiAgICAgIH0pO1xuICAgIH1cbiAgICAvLyBDYWxsIGxvYWRlcnNcbiAgICBsZXQge1xuICAgICAgc2hvcnRDaXJjdWl0ZWQsXG4gICAgICBsb2FkZXJEYXRhLFxuICAgICAgZXJyb3JzXG4gICAgfSA9IGF3YWl0IGhhbmRsZUxvYWRlcnMocmVxdWVzdCwgbG9jYXRpb24sIG1hdGNoZXMsIGxvYWRpbmdOYXZpZ2F0aW9uLCBvcHRzICYmIG9wdHMuc3VibWlzc2lvbiwgb3B0cyAmJiBvcHRzLmZldGNoZXJTdWJtaXNzaW9uLCBvcHRzICYmIG9wdHMucmVwbGFjZSwgcGVuZGluZ0FjdGlvbkRhdGEsIHBlbmRpbmdFcnJvcik7XG4gICAgaWYgKHNob3J0Q2lyY3VpdGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIENsZWFuIHVwIG5vdyB0aGF0IHRoZSBhY3Rpb24vbG9hZGVycyBoYXZlIGNvbXBsZXRlZC4gIERvbid0IGNsZWFuIHVwIGlmXG4gICAgLy8gd2Ugc2hvcnQgY2lyY3VpdGVkIGJlY2F1c2UgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyIHdpbGwgaGF2ZSBhbHJlYWR5XG4gICAgLy8gYmVlbiBhc3NpZ25lZCB0byBhIG5ldyBjb250cm9sbGVyIGZvciB0aGUgbmV4dCBuYXZpZ2F0aW9uXG4gICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyID0gbnVsbDtcbiAgICBjb21wbGV0ZU5hdmlnYXRpb24obG9jYXRpb24sIF9leHRlbmRzKHtcbiAgICAgIG1hdGNoZXNcbiAgICB9LCBwZW5kaW5nQWN0aW9uRGF0YSA/IHtcbiAgICAgIGFjdGlvbkRhdGE6IHBlbmRpbmdBY3Rpb25EYXRhXG4gICAgfSA6IHt9LCB7XG4gICAgICBsb2FkZXJEYXRhLFxuICAgICAgZXJyb3JzXG4gICAgfSkpO1xuICB9XG4gIC8vIENhbGwgdGhlIGFjdGlvbiBtYXRjaGVkIGJ5IHRoZSBsZWFmIHJvdXRlIGZvciB0aGlzIG5hdmlnYXRpb24gYW5kIGhhbmRsZVxuICAvLyByZWRpcmVjdHMvZXJyb3JzXG4gIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUFjdGlvbihyZXF1ZXN0LCBsb2NhdGlvbiwgc3VibWlzc2lvbiwgbWF0Y2hlcywgb3B0cykge1xuICAgIGlmIChvcHRzID09PSB2b2lkIDApIHtcbiAgICAgIG9wdHMgPSB7fTtcbiAgICB9XG4gICAgaW50ZXJydXB0QWN0aXZlTG9hZHMoKTtcbiAgICAvLyBQdXQgdXMgaW4gYSBzdWJtaXR0aW5nIHN0YXRlXG4gICAgbGV0IG5hdmlnYXRpb24gPSBnZXRTdWJtaXR0aW5nTmF2aWdhdGlvbihsb2NhdGlvbiwgc3VibWlzc2lvbik7XG4gICAgdXBkYXRlU3RhdGUoe1xuICAgICAgbmF2aWdhdGlvblxuICAgIH0pO1xuICAgIC8vIENhbGwgb3VyIGFjdGlvbiBhbmQgZ2V0IHRoZSByZXN1bHRcbiAgICBsZXQgcmVzdWx0O1xuICAgIGxldCBhY3Rpb25NYXRjaCA9IGdldFRhcmdldE1hdGNoKG1hdGNoZXMsIGxvY2F0aW9uKTtcbiAgICBpZiAoIWFjdGlvbk1hdGNoLnJvdXRlLmFjdGlvbiAmJiAhYWN0aW9uTWF0Y2gucm91dGUubGF6eSkge1xuICAgICAgcmVzdWx0ID0ge1xuICAgICAgICB0eXBlOiBSZXN1bHRUeXBlLmVycm9yLFxuICAgICAgICBlcnJvcjogZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDUsIHtcbiAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICAgIHBhdGhuYW1lOiBsb2NhdGlvbi5wYXRobmFtZSxcbiAgICAgICAgICByb3V0ZUlkOiBhY3Rpb25NYXRjaC5yb3V0ZS5pZFxuICAgICAgICB9KVxuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbExvYWRlck9yQWN0aW9uKFwiYWN0aW9uXCIsIHJlcXVlc3QsIGFjdGlvbk1hdGNoLCBtYXRjaGVzLCBtYW5pZmVzdCwgbWFwUm91dGVQcm9wZXJ0aWVzLCBiYXNlbmFtZSk7XG4gICAgICBpZiAocmVxdWVzdC5zaWduYWwuYWJvcnRlZCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHNob3J0Q2lyY3VpdGVkOiB0cnVlXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIGxldCByZXBsYWNlO1xuICAgICAgaWYgKG9wdHMgJiYgb3B0cy5yZXBsYWNlICE9IG51bGwpIHtcbiAgICAgICAgcmVwbGFjZSA9IG9wdHMucmVwbGFjZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIElmIHRoZSB1c2VyIGRpZG4ndCBleHBsaWNpdHkgaW5kaWNhdGUgcmVwbGFjZSBiZWhhdmlvciwgcmVwbGFjZSBpZlxuICAgICAgICAvLyB3ZSByZWRpcmVjdGVkIHRvIHRoZSBleGFjdCBzYW1lIGxvY2F0aW9uIHdlJ3JlIGN1cnJlbnRseSBhdCB0byBhdm9pZFxuICAgICAgICAvLyBkb3VibGUgYmFjay1idXR0b25zXG4gICAgICAgIHJlcGxhY2UgPSByZXN1bHQubG9jYXRpb24gPT09IHN0YXRlLmxvY2F0aW9uLnBhdGhuYW1lICsgc3RhdGUubG9jYXRpb24uc2VhcmNoO1xuICAgICAgfVxuICAgICAgYXdhaXQgc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24oc3RhdGUsIHJlc3VsdCwge1xuICAgICAgICBzdWJtaXNzaW9uLFxuICAgICAgICByZXBsYWNlXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHNob3J0Q2lyY3VpdGVkOiB0cnVlXG4gICAgICB9O1xuICAgIH1cbiAgICBpZiAoaXNFcnJvclJlc3VsdChyZXN1bHQpKSB7XG4gICAgICAvLyBTdG9yZSBvZmYgdGhlIHBlbmRpbmcgZXJyb3IgLSB3ZSB1c2UgaXQgdG8gZGV0ZXJtaW5lIHdoaWNoIGxvYWRlcnNcbiAgICAgIC8vIHRvIGNhbGwgYW5kIHdpbGwgY29tbWl0IGl0IHdoZW4gd2UgY29tcGxldGUgdGhlIG5hdmlnYXRpb25cbiAgICAgIGxldCBib3VuZGFyeU1hdGNoID0gZmluZE5lYXJlc3RCb3VuZGFyeShtYXRjaGVzLCBhY3Rpb25NYXRjaC5yb3V0ZS5pZCk7XG4gICAgICAvLyBCeSBkZWZhdWx0LCBhbGwgc3VibWlzc2lvbnMgYXJlIFJFUExBQ0UgbmF2aWdhdGlvbnMsIGJ1dCBpZiB0aGVcbiAgICAgIC8vIGFjdGlvbiB0aHJldyBhbiBlcnJvciB0aGF0J2xsIGJlIHJlbmRlcmVkIGluIGFuIGVycm9yRWxlbWVudCwgd2UgZmFsbFxuICAgICAgLy8gYmFjayB0byBQVVNIIHNvIHRoYXQgdGhlIHVzZXIgY2FuIHVzZSB0aGUgYmFjayBidXR0b24gdG8gZ2V0IGJhY2sgdG9cbiAgICAgIC8vIHRoZSBwcmUtc3VibWlzc2lvbiBmb3JtIGxvY2F0aW9uIHRvIHRyeSBhZ2FpblxuICAgICAgaWYgKChvcHRzICYmIG9wdHMucmVwbGFjZSkgIT09IHRydWUpIHtcbiAgICAgICAgcGVuZGluZ0FjdGlvbiA9IEFjdGlvbi5QdXNoO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLy8gU2VuZCBiYWNrIGFuIGVtcHR5IG9iamVjdCB3ZSBjYW4gdXNlIHRvIGNsZWFyIG91dCBhbnkgcHJpb3IgYWN0aW9uRGF0YVxuICAgICAgICBwZW5kaW5nQWN0aW9uRGF0YToge30sXG4gICAgICAgIHBlbmRpbmdBY3Rpb25FcnJvcjoge1xuICAgICAgICAgIFtib3VuZGFyeU1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmVycm9yXG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgfVxuICAgIGlmIChpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIHRocm93IGdldEludGVybmFsUm91dGVyRXJyb3IoNDAwLCB7XG4gICAgICAgIHR5cGU6IFwiZGVmZXItYWN0aW9uXCJcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgcGVuZGluZ0FjdGlvbkRhdGE6IHtcbiAgICAgICAgW2FjdGlvbk1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmRhdGFcbiAgICAgIH1cbiAgICB9O1xuICB9XG4gIC8vIENhbGwgYWxsIGFwcGxpY2FibGUgbG9hZGVycyBmb3IgdGhlIGdpdmVuIG1hdGNoZXMsIGhhbmRsaW5nIHJlZGlyZWN0cyxcbiAgLy8gZXJyb3JzLCBldGMuXG4gIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUxvYWRlcnMocmVxdWVzdCwgbG9jYXRpb24sIG1hdGNoZXMsIG92ZXJyaWRlTmF2aWdhdGlvbiwgc3VibWlzc2lvbiwgZmV0Y2hlclN1Ym1pc3Npb24sIHJlcGxhY2UsIHBlbmRpbmdBY3Rpb25EYXRhLCBwZW5kaW5nRXJyb3IpIHtcbiAgICAvLyBGaWd1cmUgb3V0IHRoZSByaWdodCBuYXZpZ2F0aW9uIHdlIHdhbnQgdG8gdXNlIGZvciBkYXRhIGxvYWRpbmdcbiAgICBsZXQgbG9hZGluZ05hdmlnYXRpb24gPSBvdmVycmlkZU5hdmlnYXRpb24gfHwgZ2V0TG9hZGluZ05hdmlnYXRpb24obG9jYXRpb24sIHN1Ym1pc3Npb24pO1xuICAgIC8vIElmIHRoaXMgd2FzIGEgcmVkaXJlY3QgZnJvbSBhbiBhY3Rpb24gd2UgZG9uJ3QgaGF2ZSBhIFwic3VibWlzc2lvblwiIGJ1dFxuICAgIC8vIHdlIGhhdmUgaXQgb24gdGhlIGxvYWRpbmcgbmF2aWdhdGlvbiBzbyB1c2UgdGhhdCBpZiBhdmFpbGFibGVcbiAgICBsZXQgYWN0aXZlU3VibWlzc2lvbiA9IHN1Ym1pc3Npb24gfHwgZmV0Y2hlclN1Ym1pc3Npb24gfHwgZ2V0U3VibWlzc2lvbkZyb21OYXZpZ2F0aW9uKGxvYWRpbmdOYXZpZ2F0aW9uKTtcbiAgICBsZXQgcm91dGVzVG9Vc2UgPSBpbkZsaWdodERhdGFSb3V0ZXMgfHwgZGF0YVJvdXRlcztcbiAgICBsZXQgW21hdGNoZXNUb0xvYWQsIHJldmFsaWRhdGluZ0ZldGNoZXJzXSA9IGdldE1hdGNoZXNUb0xvYWQoaW5pdC5oaXN0b3J5LCBzdGF0ZSwgbWF0Y2hlcywgYWN0aXZlU3VibWlzc2lvbiwgbG9jYXRpb24sIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQsIGNhbmNlbGxlZERlZmVycmVkUm91dGVzLCBjYW5jZWxsZWRGZXRjaGVyTG9hZHMsIGZldGNoTG9hZE1hdGNoZXMsIGZldGNoUmVkaXJlY3RJZHMsIHJvdXRlc1RvVXNlLCBiYXNlbmFtZSwgcGVuZGluZ0FjdGlvbkRhdGEsIHBlbmRpbmdFcnJvcik7XG4gICAgLy8gQ2FuY2VsIHBlbmRpbmcgZGVmZXJyZWRzIGZvciBuby1sb25nZXItbWF0Y2hlZCByb3V0ZXMgb3Igcm91dGVzIHdlJ3JlXG4gICAgLy8gYWJvdXQgdG8gcmVsb2FkLiAgTm90ZSB0aGF0IGlmIHRoaXMgaXMgYW4gYWN0aW9uIHJlbG9hZCB3ZSB3b3VsZCBoYXZlXG4gICAgLy8gYWxyZWFkeSBjYW5jZWxsZWQgYWxsIHBlbmRpbmcgZGVmZXJyZWRzIHNvIHRoaXMgd291bGQgYmUgYSBuby1vcFxuICAgIGNhbmNlbEFjdGl2ZURlZmVycmVkcyhyb3V0ZUlkID0+ICEobWF0Y2hlcyAmJiBtYXRjaGVzLnNvbWUobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSkgfHwgbWF0Y2hlc1RvTG9hZCAmJiBtYXRjaGVzVG9Mb2FkLnNvbWUobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSk7XG4gICAgLy8gU2hvcnQgY2lyY3VpdCBpZiB3ZSBoYXZlIG5vIGxvYWRlcnMgdG8gcnVuXG4gICAgaWYgKG1hdGNoZXNUb0xvYWQubGVuZ3RoID09PSAwICYmIHJldmFsaWRhdGluZ0ZldGNoZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgbGV0IHVwZGF0ZWRGZXRjaGVycyA9IG1hcmtGZXRjaFJlZGlyZWN0c0RvbmUoKTtcbiAgICAgIGNvbXBsZXRlTmF2aWdhdGlvbihsb2NhdGlvbiwgX2V4dGVuZHMoe1xuICAgICAgICBtYXRjaGVzLFxuICAgICAgICBsb2FkZXJEYXRhOiB7fSxcbiAgICAgICAgLy8gQ29tbWl0IHBlbmRpbmcgZXJyb3IgaWYgd2UncmUgc2hvcnQgY2lyY3VpdGluZ1xuICAgICAgICBlcnJvcnM6IHBlbmRpbmdFcnJvciB8fCBudWxsXG4gICAgICB9LCBwZW5kaW5nQWN0aW9uRGF0YSA/IHtcbiAgICAgICAgYWN0aW9uRGF0YTogcGVuZGluZ0FjdGlvbkRhdGFcbiAgICAgIH0gOiB7fSwgdXBkYXRlZEZldGNoZXJzID8ge1xuICAgICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycylcbiAgICAgIH0gOiB7fSkpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc2hvcnRDaXJjdWl0ZWQ6IHRydWVcbiAgICAgIH07XG4gICAgfVxuICAgIC8vIElmIHRoaXMgaXMgYW4gdW5pbnRlcnJ1cHRlZCByZXZhbGlkYXRpb24sIHdlIHJlbWFpbiBpbiBvdXIgY3VycmVudCBpZGxlXG4gICAgLy8gc3RhdGUuICBJZiBub3QsIHdlIG5lZWQgdG8gc3dpdGNoIHRvIG91ciBsb2FkaW5nIHN0YXRlIGFuZCBsb2FkIGRhdGEsXG4gICAgLy8gcHJlc2VydmluZyBhbnkgbmV3IGFjdGlvbiBkYXRhIG9yIGV4aXN0aW5nIGFjdGlvbiBkYXRhIChpbiB0aGUgY2FzZSBvZlxuICAgIC8vIGEgcmV2YWxpZGF0aW9uIGludGVycnVwdGluZyBhbiBhY3Rpb25SZWxvYWQpXG4gICAgaWYgKCFpc1VuaW50ZXJydXB0ZWRSZXZhbGlkYXRpb24pIHtcbiAgICAgIHJldmFsaWRhdGluZ0ZldGNoZXJzLmZvckVhY2gocmYgPT4ge1xuICAgICAgICBsZXQgZmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChyZi5rZXkpO1xuICAgICAgICBsZXQgcmV2YWxpZGF0aW5nRmV0Y2hlciA9IGdldExvYWRpbmdGZXRjaGVyKHVuZGVmaW5lZCwgZmV0Y2hlciA/IGZldGNoZXIuZGF0YSA6IHVuZGVmaW5lZCk7XG4gICAgICAgIHN0YXRlLmZldGNoZXJzLnNldChyZi5rZXksIHJldmFsaWRhdGluZ0ZldGNoZXIpO1xuICAgICAgfSk7XG4gICAgICBsZXQgYWN0aW9uRGF0YSA9IHBlbmRpbmdBY3Rpb25EYXRhIHx8IHN0YXRlLmFjdGlvbkRhdGE7XG4gICAgICB1cGRhdGVTdGF0ZShfZXh0ZW5kcyh7XG4gICAgICAgIG5hdmlnYXRpb246IGxvYWRpbmdOYXZpZ2F0aW9uXG4gICAgICB9LCBhY3Rpb25EYXRhID8gT2JqZWN0LmtleXMoYWN0aW9uRGF0YSkubGVuZ3RoID09PSAwID8ge1xuICAgICAgICBhY3Rpb25EYXRhOiBudWxsXG4gICAgICB9IDoge1xuICAgICAgICBhY3Rpb25EYXRhXG4gICAgICB9IDoge30sIHJldmFsaWRhdGluZ0ZldGNoZXJzLmxlbmd0aCA+IDAgPyB7XG4gICAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKVxuICAgICAgfSA6IHt9KSk7XG4gICAgfVxuICAgIHBlbmRpbmdOYXZpZ2F0aW9uTG9hZElkID0gKytpbmNyZW1lbnRpbmdMb2FkSWQ7XG4gICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZm9yRWFjaChyZiA9PiB7XG4gICAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMocmYua2V5KSkge1xuICAgICAgICBhYm9ydEZldGNoZXIocmYua2V5KTtcbiAgICAgIH1cbiAgICAgIGlmIChyZi5jb250cm9sbGVyKSB7XG4gICAgICAgIC8vIEZldGNoZXJzIHVzZSBhbiBpbmRlcGVuZGVudCBBYm9ydENvbnRyb2xsZXIgc28gdGhhdCBhYm9ydGluZyBhIGZldGNoZXJcbiAgICAgICAgLy8gKHZpYSBkZWxldGVGZXRjaGVyKSBkb2VzIG5vdCBhYm9ydCB0aGUgdHJpZ2dlcmluZyBuYXZpZ2F0aW9uIHRoYXRcbiAgICAgICAgLy8gdHJpZ2dlcmVkIHRoZSByZXZhbGlkYXRpb25cbiAgICAgICAgZmV0Y2hDb250cm9sbGVycy5zZXQocmYua2V5LCByZi5jb250cm9sbGVyKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICAvLyBQcm94eSBuYXZpZ2F0aW9uIGFib3J0IHRocm91Z2ggdG8gcmV2YWxpZGF0aW9uIGZldGNoZXJzXG4gICAgbGV0IGFib3J0UGVuZGluZ0ZldGNoUmV2YWxpZGF0aW9ucyA9ICgpID0+IHJldmFsaWRhdGluZ0ZldGNoZXJzLmZvckVhY2goZiA9PiBhYm9ydEZldGNoZXIoZi5rZXkpKTtcbiAgICBpZiAocGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyKSB7XG4gICAgICBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIuc2lnbmFsLmFkZEV2ZW50TGlzdGVuZXIoXCJhYm9ydFwiLCBhYm9ydFBlbmRpbmdGZXRjaFJldmFsaWRhdGlvbnMpO1xuICAgIH1cbiAgICBsZXQge1xuICAgICAgcmVzdWx0cyxcbiAgICAgIGxvYWRlclJlc3VsdHMsXG4gICAgICBmZXRjaGVyUmVzdWx0c1xuICAgIH0gPSBhd2FpdCBjYWxsTG9hZGVyc0FuZE1heWJlUmVzb2x2ZURhdGEoc3RhdGUubWF0Y2hlcywgbWF0Y2hlcywgbWF0Y2hlc1RvTG9hZCwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMsIHJlcXVlc3QpO1xuICAgIGlmIChyZXF1ZXN0LnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzaG9ydENpcmN1aXRlZDogdHJ1ZVxuICAgICAgfTtcbiAgICB9XG4gICAgLy8gQ2xlYW4gdXAgX2FmdGVyXyBsb2FkZXJzIGhhdmUgY29tcGxldGVkLiAgRG9uJ3QgY2xlYW4gdXAgaWYgd2Ugc2hvcnRcbiAgICAvLyBjaXJjdWl0ZWQgYmVjYXVzZSBmZXRjaENvbnRyb2xsZXJzIHdvdWxkIGhhdmUgYmVlbiBhYm9ydGVkIGFuZFxuICAgIC8vIHJlYXNzaWduZWQgdG8gbmV3IGNvbnRyb2xsZXJzIGZvciB0aGUgbmV4dCBuYXZpZ2F0aW9uXG4gICAgaWYgKHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlcikge1xuICAgICAgcGVuZGluZ05hdmlnYXRpb25Db250cm9sbGVyLnNpZ25hbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgYWJvcnRQZW5kaW5nRmV0Y2hSZXZhbGlkYXRpb25zKTtcbiAgICB9XG4gICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZm9yRWFjaChyZiA9PiBmZXRjaENvbnRyb2xsZXJzLmRlbGV0ZShyZi5rZXkpKTtcbiAgICAvLyBJZiBhbnkgbG9hZGVycyByZXR1cm5lZCBhIHJlZGlyZWN0IFJlc3BvbnNlLCBzdGFydCBhIG5ldyBSRVBMQUNFIG5hdmlnYXRpb25cbiAgICBsZXQgcmVkaXJlY3QgPSBmaW5kUmVkaXJlY3QocmVzdWx0cyk7XG4gICAgaWYgKHJlZGlyZWN0KSB7XG4gICAgICBhd2FpdCBzdGFydFJlZGlyZWN0TmF2aWdhdGlvbihzdGF0ZSwgcmVkaXJlY3QsIHtcbiAgICAgICAgcmVwbGFjZVxuICAgICAgfSk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzaG9ydENpcmN1aXRlZDogdHJ1ZVxuICAgICAgfTtcbiAgICB9XG4gICAgLy8gUHJvY2VzcyBhbmQgY29tbWl0IG91dHB1dCBmcm9tIGxvYWRlcnNcbiAgICBsZXQge1xuICAgICAgbG9hZGVyRGF0YSxcbiAgICAgIGVycm9yc1xuICAgIH0gPSBwcm9jZXNzTG9hZGVyRGF0YShzdGF0ZSwgbWF0Y2hlcywgbWF0Y2hlc1RvTG9hZCwgbG9hZGVyUmVzdWx0cywgcGVuZGluZ0Vycm9yLCByZXZhbGlkYXRpbmdGZXRjaGVycywgZmV0Y2hlclJlc3VsdHMsIGFjdGl2ZURlZmVycmVkcyk7XG4gICAgLy8gV2lyZSB1cCBzdWJzY3JpYmVycyB0byB1cGRhdGUgbG9hZGVyRGF0YSBhcyBwcm9taXNlcyBzZXR0bGVcbiAgICBhY3RpdmVEZWZlcnJlZHMuZm9yRWFjaCgoZGVmZXJyZWREYXRhLCByb3V0ZUlkKSA9PiB7XG4gICAgICBkZWZlcnJlZERhdGEuc3Vic2NyaWJlKGFib3J0ZWQgPT4ge1xuICAgICAgICAvLyBOb3RlOiBObyBuZWVkIHRvIHVwZGF0ZVN0YXRlIGhlcmUgc2luY2UgdGhlIFRyYWNrZWRQcm9taXNlIG9uXG4gICAgICAgIC8vIGxvYWRlckRhdGEgaXMgc3RhYmxlIGFjcm9zcyByZXNvbHZlL3JlamVjdFxuICAgICAgICAvLyBSZW1vdmUgdGhpcyBpbnN0YW5jZSBpZiB3ZSB3ZXJlIGFib3J0ZWQgb3IgaWYgcHJvbWlzZXMgaGF2ZSBzZXR0bGVkXG4gICAgICAgIGlmIChhYm9ydGVkIHx8IGRlZmVycmVkRGF0YS5kb25lKSB7XG4gICAgICAgICAgYWN0aXZlRGVmZXJyZWRzLmRlbGV0ZShyb3V0ZUlkKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gICAgbGV0IHVwZGF0ZWRGZXRjaGVycyA9IG1hcmtGZXRjaFJlZGlyZWN0c0RvbmUoKTtcbiAgICBsZXQgZGlkQWJvcnRGZXRjaExvYWRzID0gYWJvcnRTdGFsZUZldGNoTG9hZHMocGVuZGluZ05hdmlnYXRpb25Mb2FkSWQpO1xuICAgIGxldCBzaG91bGRVcGRhdGVGZXRjaGVycyA9IHVwZGF0ZWRGZXRjaGVycyB8fCBkaWRBYm9ydEZldGNoTG9hZHMgfHwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMubGVuZ3RoID4gMDtcbiAgICByZXR1cm4gX2V4dGVuZHMoe1xuICAgICAgbG9hZGVyRGF0YSxcbiAgICAgIGVycm9yc1xuICAgIH0sIHNob3VsZFVwZGF0ZUZldGNoZXJzID8ge1xuICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpXG4gICAgfSA6IHt9KTtcbiAgfVxuICBmdW5jdGlvbiBnZXRGZXRjaGVyKGtleSkge1xuICAgIHJldHVybiBzdGF0ZS5mZXRjaGVycy5nZXQoa2V5KSB8fCBJRExFX0ZFVENIRVI7XG4gIH1cbiAgLy8gVHJpZ2dlciBhIGZldGNoZXIgbG9hZC9zdWJtaXQgZm9yIHRoZSBnaXZlbiBmZXRjaGVyIGtleVxuICBmdW5jdGlvbiBmZXRjaChrZXksIHJvdXRlSWQsIGhyZWYsIG9wdHMpIHtcbiAgICBpZiAoaXNTZXJ2ZXIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcInJvdXRlci5mZXRjaCgpIHdhcyBjYWxsZWQgZHVyaW5nIHRoZSBzZXJ2ZXIgcmVuZGVyLCBidXQgaXQgc2hvdWxkbid0IGJlLiBcIiArIFwiWW91IGFyZSBsaWtlbHkgY2FsbGluZyBhIHVzZUZldGNoZXIoKSBtZXRob2QgaW4gdGhlIGJvZHkgb2YgeW91ciBjb21wb25lbnQuIFwiICsgXCJUcnkgbW92aW5nIGl0IHRvIGEgdXNlRWZmZWN0IG9yIGEgY2FsbGJhY2suXCIpO1xuICAgIH1cbiAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMoa2V5KSkgYWJvcnRGZXRjaGVyKGtleSk7XG4gICAgbGV0IHJvdXRlc1RvVXNlID0gaW5GbGlnaHREYXRhUm91dGVzIHx8IGRhdGFSb3V0ZXM7XG4gICAgbGV0IG5vcm1hbGl6ZWRQYXRoID0gbm9ybWFsaXplVG8oc3RhdGUubG9jYXRpb24sIHN0YXRlLm1hdGNoZXMsIGJhc2VuYW1lLCBmdXR1cmUudjdfcHJlcGVuZEJhc2VuYW1lLCBocmVmLCByb3V0ZUlkLCBvcHRzID09IG51bGwgPyB2b2lkIDAgOiBvcHRzLnJlbGF0aXZlKTtcbiAgICBsZXQgbWF0Y2hlcyA9IG1hdGNoUm91dGVzKHJvdXRlc1RvVXNlLCBub3JtYWxpemVkUGF0aCwgYmFzZW5hbWUpO1xuICAgIGlmICghbWF0Y2hlcykge1xuICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDQsIHtcbiAgICAgICAgcGF0aG5hbWU6IG5vcm1hbGl6ZWRQYXRoXG4gICAgICB9KSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGxldCB7XG4gICAgICBwYXRoLFxuICAgICAgc3VibWlzc2lvbixcbiAgICAgIGVycm9yXG4gICAgfSA9IG5vcm1hbGl6ZU5hdmlnYXRlT3B0aW9ucyhmdXR1cmUudjdfbm9ybWFsaXplRm9ybU1ldGhvZCwgdHJ1ZSwgbm9ybWFsaXplZFBhdGgsIG9wdHMpO1xuICAgIGlmIChlcnJvcikge1xuICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgZXJyb3IpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgbWF0Y2ggPSBnZXRUYXJnZXRNYXRjaChtYXRjaGVzLCBwYXRoKTtcbiAgICBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0ID0gKG9wdHMgJiYgb3B0cy5wcmV2ZW50U2Nyb2xsUmVzZXQpID09PSB0cnVlO1xuICAgIGlmIChzdWJtaXNzaW9uICYmIGlzTXV0YXRpb25NZXRob2Qoc3VibWlzc2lvbi5mb3JtTWV0aG9kKSkge1xuICAgICAgaGFuZGxlRmV0Y2hlckFjdGlvbihrZXksIHJvdXRlSWQsIHBhdGgsIG1hdGNoLCBtYXRjaGVzLCBzdWJtaXNzaW9uKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gU3RvcmUgb2ZmIHRoZSBtYXRjaCBzbyB3ZSBjYW4gY2FsbCBpdCdzIHNob3VsZFJldmFsaWRhdGUgb24gc3Vic2VxdWVudFxuICAgIC8vIHJldmFsaWRhdGlvbnNcbiAgICBmZXRjaExvYWRNYXRjaGVzLnNldChrZXksIHtcbiAgICAgIHJvdXRlSWQsXG4gICAgICBwYXRoXG4gICAgfSk7XG4gICAgaGFuZGxlRmV0Y2hlckxvYWRlcihrZXksIHJvdXRlSWQsIHBhdGgsIG1hdGNoLCBtYXRjaGVzLCBzdWJtaXNzaW9uKTtcbiAgfVxuICAvLyBDYWxsIHRoZSBhY3Rpb24gZm9yIHRoZSBtYXRjaGVkIGZldGNoZXIuc3VibWl0KCksIGFuZCB0aGVuIGhhbmRsZSByZWRpcmVjdHMsXG4gIC8vIGVycm9ycywgYW5kIHJldmFsaWRhdGlvblxuICBhc3luYyBmdW5jdGlvbiBoYW5kbGVGZXRjaGVyQWN0aW9uKGtleSwgcm91dGVJZCwgcGF0aCwgbWF0Y2gsIHJlcXVlc3RNYXRjaGVzLCBzdWJtaXNzaW9uKSB7XG4gICAgaW50ZXJydXB0QWN0aXZlTG9hZHMoKTtcbiAgICBmZXRjaExvYWRNYXRjaGVzLmRlbGV0ZShrZXkpO1xuICAgIGlmICghbWF0Y2gucm91dGUuYWN0aW9uICYmICFtYXRjaC5yb3V0ZS5sYXp5KSB7XG4gICAgICBsZXQgZXJyb3IgPSBnZXRJbnRlcm5hbFJvdXRlckVycm9yKDQwNSwge1xuICAgICAgICBtZXRob2Q6IHN1Ym1pc3Npb24uZm9ybU1ldGhvZCxcbiAgICAgICAgcGF0aG5hbWU6IHBhdGgsXG4gICAgICAgIHJvdXRlSWQ6IHJvdXRlSWRcbiAgICAgIH0pO1xuICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgZXJyb3IpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBQdXQgdGhpcyBmZXRjaGVyIGludG8gaXQncyBzdWJtaXR0aW5nIHN0YXRlXG4gICAgbGV0IGV4aXN0aW5nRmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpO1xuICAgIGxldCBmZXRjaGVyID0gZ2V0U3VibWl0dGluZ0ZldGNoZXIoc3VibWlzc2lvbiwgZXhpc3RpbmdGZXRjaGVyKTtcbiAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBmZXRjaGVyKTtcbiAgICB1cGRhdGVTdGF0ZSh7XG4gICAgICBmZXRjaGVyczogbmV3IE1hcChzdGF0ZS5mZXRjaGVycylcbiAgICB9KTtcbiAgICAvLyBDYWxsIHRoZSBhY3Rpb24gZm9yIHRoZSBmZXRjaGVyXG4gICAgbGV0IGFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBsZXQgZmV0Y2hSZXF1ZXN0ID0gY3JlYXRlQ2xpZW50U2lkZVJlcXVlc3QoaW5pdC5oaXN0b3J5LCBwYXRoLCBhYm9ydENvbnRyb2xsZXIuc2lnbmFsLCBzdWJtaXNzaW9uKTtcbiAgICBmZXRjaENvbnRyb2xsZXJzLnNldChrZXksIGFib3J0Q29udHJvbGxlcik7XG4gICAgbGV0IGFjdGlvblJlc3VsdCA9IGF3YWl0IGNhbGxMb2FkZXJPckFjdGlvbihcImFjdGlvblwiLCBmZXRjaFJlcXVlc3QsIG1hdGNoLCByZXF1ZXN0TWF0Y2hlcywgbWFuaWZlc3QsIG1hcFJvdXRlUHJvcGVydGllcywgYmFzZW5hbWUpO1xuICAgIGlmIChmZXRjaFJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgIC8vIFdlIGNhbiBkZWxldGUgdGhpcyBzbyBsb25nIGFzIHdlIHdlcmVuJ3QgYWJvcnRlZCBieSBvdSBvdXIgb3duIGZldGNoZXJcbiAgICAgIC8vIHJlLXN1Ym1pdCB3aGljaCB3b3VsZCBoYXZlIHB1dCBfbmV3XyBjb250cm9sbGVyIGlzIGluIGZldGNoQ29udHJvbGxlcnNcbiAgICAgIGlmIChmZXRjaENvbnRyb2xsZXJzLmdldChrZXkpID09PSBhYm9ydENvbnRyb2xsZXIpIHtcbiAgICAgICAgZmV0Y2hDb250cm9sbGVycy5kZWxldGUoa2V5KTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGlzUmVkaXJlY3RSZXN1bHQoYWN0aW9uUmVzdWx0KSkge1xuICAgICAgZmV0Y2hDb250cm9sbGVycy5kZWxldGUoa2V5KTtcbiAgICAgIGZldGNoUmVkaXJlY3RJZHMuYWRkKGtleSk7XG4gICAgICBsZXQgbG9hZGluZ0ZldGNoZXIgPSBnZXRMb2FkaW5nRmV0Y2hlcihzdWJtaXNzaW9uKTtcbiAgICAgIHN0YXRlLmZldGNoZXJzLnNldChrZXksIGxvYWRpbmdGZXRjaGVyKTtcbiAgICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpXG4gICAgICB9KTtcbiAgICAgIHJldHVybiBzdGFydFJlZGlyZWN0TmF2aWdhdGlvbihzdGF0ZSwgYWN0aW9uUmVzdWx0LCB7XG4gICAgICAgIHN1Ym1pc3Npb24sXG4gICAgICAgIGlzRmV0Y2hBY3Rpb25SZWRpcmVjdDogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIFByb2Nlc3MgYW55IG5vbi1yZWRpcmVjdCBlcnJvcnMgdGhyb3duXG4gICAgaWYgKGlzRXJyb3JSZXN1bHQoYWN0aW9uUmVzdWx0KSkge1xuICAgICAgc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgYWN0aW9uUmVzdWx0LmVycm9yKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQoYWN0aW9uUmVzdWx0KSkge1xuICAgICAgdGhyb3cgZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDAsIHtcbiAgICAgICAgdHlwZTogXCJkZWZlci1hY3Rpb25cIlxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIFN0YXJ0IHRoZSBkYXRhIGxvYWQgZm9yIGN1cnJlbnQgbWF0Y2hlcywgb3IgdGhlIG5leHQgbG9jYXRpb24gaWYgd2UncmVcbiAgICAvLyBpbiB0aGUgbWlkZGxlIG9mIGEgbmF2aWdhdGlvblxuICAgIGxldCBuZXh0TG9jYXRpb24gPSBzdGF0ZS5uYXZpZ2F0aW9uLmxvY2F0aW9uIHx8IHN0YXRlLmxvY2F0aW9uO1xuICAgIGxldCByZXZhbGlkYXRpb25SZXF1ZXN0ID0gY3JlYXRlQ2xpZW50U2lkZVJlcXVlc3QoaW5pdC5oaXN0b3J5LCBuZXh0TG9jYXRpb24sIGFib3J0Q29udHJvbGxlci5zaWduYWwpO1xuICAgIGxldCByb3V0ZXNUb1VzZSA9IGluRmxpZ2h0RGF0YVJvdXRlcyB8fCBkYXRhUm91dGVzO1xuICAgIGxldCBtYXRjaGVzID0gc3RhdGUubmF2aWdhdGlvbi5zdGF0ZSAhPT0gXCJpZGxlXCIgPyBtYXRjaFJvdXRlcyhyb3V0ZXNUb1VzZSwgc3RhdGUubmF2aWdhdGlvbi5sb2NhdGlvbiwgYmFzZW5hbWUpIDogc3RhdGUubWF0Y2hlcztcbiAgICBpbnZhcmlhbnQobWF0Y2hlcywgXCJEaWRuJ3QgZmluZCBhbnkgbWF0Y2hlcyBhZnRlciBmZXRjaGVyIGFjdGlvblwiKTtcbiAgICBsZXQgbG9hZElkID0gKytpbmNyZW1lbnRpbmdMb2FkSWQ7XG4gICAgZmV0Y2hSZWxvYWRJZHMuc2V0KGtleSwgbG9hZElkKTtcbiAgICBsZXQgbG9hZEZldGNoZXIgPSBnZXRMb2FkaW5nRmV0Y2hlcihzdWJtaXNzaW9uLCBhY3Rpb25SZXN1bHQuZGF0YSk7XG4gICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgbG9hZEZldGNoZXIpO1xuICAgIGxldCBbbWF0Y2hlc1RvTG9hZCwgcmV2YWxpZGF0aW5nRmV0Y2hlcnNdID0gZ2V0TWF0Y2hlc1RvTG9hZChpbml0Lmhpc3RvcnksIHN0YXRlLCBtYXRjaGVzLCBzdWJtaXNzaW9uLCBuZXh0TG9jYXRpb24sIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQsIGNhbmNlbGxlZERlZmVycmVkUm91dGVzLCBjYW5jZWxsZWRGZXRjaGVyTG9hZHMsIGZldGNoTG9hZE1hdGNoZXMsIGZldGNoUmVkaXJlY3RJZHMsIHJvdXRlc1RvVXNlLCBiYXNlbmFtZSwge1xuICAgICAgW21hdGNoLnJvdXRlLmlkXTogYWN0aW9uUmVzdWx0LmRhdGFcbiAgICB9LCB1bmRlZmluZWQgLy8gTm8gbmVlZCB0byBzZW5kIHRocm91Z2ggZXJyb3JzIHNpbmNlIHdlIHNob3J0IGNpcmN1aXQgYWJvdmVcbiAgICApO1xuICAgIC8vIFB1dCBhbGwgcmV2YWxpZGF0aW5nIGZldGNoZXJzIGludG8gdGhlIGxvYWRpbmcgc3RhdGUsIGV4Y2VwdCBmb3IgdGhlXG4gICAgLy8gY3VycmVudCBmZXRjaGVyIHdoaWNoIHdlIHdhbnQgdG8ga2VlcCBpbiBpdCdzIGN1cnJlbnQgbG9hZGluZyBzdGF0ZSB3aGljaFxuICAgIC8vIGNvbnRhaW5zIGl0J3MgYWN0aW9uIHN1Ym1pc3Npb24gaW5mbyArIGFjdGlvbiBkYXRhXG4gICAgcmV2YWxpZGF0aW5nRmV0Y2hlcnMuZmlsdGVyKHJmID0+IHJmLmtleSAhPT0ga2V5KS5mb3JFYWNoKHJmID0+IHtcbiAgICAgIGxldCBzdGFsZUtleSA9IHJmLmtleTtcbiAgICAgIGxldCBleGlzdGluZ0ZldGNoZXIgPSBzdGF0ZS5mZXRjaGVycy5nZXQoc3RhbGVLZXkpO1xuICAgICAgbGV0IHJldmFsaWRhdGluZ0ZldGNoZXIgPSBnZXRMb2FkaW5nRmV0Y2hlcih1bmRlZmluZWQsIGV4aXN0aW5nRmV0Y2hlciA/IGV4aXN0aW5nRmV0Y2hlci5kYXRhIDogdW5kZWZpbmVkKTtcbiAgICAgIHN0YXRlLmZldGNoZXJzLnNldChzdGFsZUtleSwgcmV2YWxpZGF0aW5nRmV0Y2hlcik7XG4gICAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMoc3RhbGVLZXkpKSB7XG4gICAgICAgIGFib3J0RmV0Y2hlcihzdGFsZUtleSk7XG4gICAgICB9XG4gICAgICBpZiAocmYuY29udHJvbGxlcikge1xuICAgICAgICBmZXRjaENvbnRyb2xsZXJzLnNldChzdGFsZUtleSwgcmYuY29udHJvbGxlcik7XG4gICAgICB9XG4gICAgfSk7XG4gICAgdXBkYXRlU3RhdGUoe1xuICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpXG4gICAgfSk7XG4gICAgbGV0IGFib3J0UGVuZGluZ0ZldGNoUmV2YWxpZGF0aW9ucyA9ICgpID0+IHJldmFsaWRhdGluZ0ZldGNoZXJzLmZvckVhY2gocmYgPT4gYWJvcnRGZXRjaGVyKHJmLmtleSkpO1xuICAgIGFib3J0Q29udHJvbGxlci5zaWduYWwuYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0UGVuZGluZ0ZldGNoUmV2YWxpZGF0aW9ucyk7XG4gICAgbGV0IHtcbiAgICAgIHJlc3VsdHMsXG4gICAgICBsb2FkZXJSZXN1bHRzLFxuICAgICAgZmV0Y2hlclJlc3VsdHNcbiAgICB9ID0gYXdhaXQgY2FsbExvYWRlcnNBbmRNYXliZVJlc29sdmVEYXRhKHN0YXRlLm1hdGNoZXMsIG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJldmFsaWRhdGluZ0ZldGNoZXJzLCByZXZhbGlkYXRpb25SZXF1ZXN0KTtcbiAgICBpZiAoYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGFib3J0Q29udHJvbGxlci5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIGFib3J0UGVuZGluZ0ZldGNoUmV2YWxpZGF0aW9ucyk7XG4gICAgZmV0Y2hSZWxvYWRJZHMuZGVsZXRlKGtleSk7XG4gICAgZmV0Y2hDb250cm9sbGVycy5kZWxldGUoa2V5KTtcbiAgICByZXZhbGlkYXRpbmdGZXRjaGVycy5mb3JFYWNoKHIgPT4gZmV0Y2hDb250cm9sbGVycy5kZWxldGUoci5rZXkpKTtcbiAgICBsZXQgcmVkaXJlY3QgPSBmaW5kUmVkaXJlY3QocmVzdWx0cyk7XG4gICAgaWYgKHJlZGlyZWN0KSB7XG4gICAgICByZXR1cm4gc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24oc3RhdGUsIHJlZGlyZWN0KTtcbiAgICB9XG4gICAgLy8gUHJvY2VzcyBhbmQgY29tbWl0IG91dHB1dCBmcm9tIGxvYWRlcnNcbiAgICBsZXQge1xuICAgICAgbG9hZGVyRGF0YSxcbiAgICAgIGVycm9yc1xuICAgIH0gPSBwcm9jZXNzTG9hZGVyRGF0YShzdGF0ZSwgc3RhdGUubWF0Y2hlcywgbWF0Y2hlc1RvTG9hZCwgbG9hZGVyUmVzdWx0cywgdW5kZWZpbmVkLCByZXZhbGlkYXRpbmdGZXRjaGVycywgZmV0Y2hlclJlc3VsdHMsIGFjdGl2ZURlZmVycmVkcyk7XG4gICAgLy8gU2luY2Ugd2UgbGV0IHJldmFsaWRhdGlvbnMgY29tcGxldGUgZXZlbiBpZiB0aGUgc3VibWl0dGluZyBmZXRjaGVyIHdhc1xuICAgIC8vIGRlbGV0ZWQsIG9ubHkgcHV0IGl0IGJhY2sgdG8gaWRsZSBpZiBpdCBoYXNuJ3QgYmVlbiBkZWxldGVkXG4gICAgaWYgKHN0YXRlLmZldGNoZXJzLmhhcyhrZXkpKSB7XG4gICAgICBsZXQgZG9uZUZldGNoZXIgPSBnZXREb25lRmV0Y2hlcihhY3Rpb25SZXN1bHQuZGF0YSk7XG4gICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBkb25lRmV0Y2hlcik7XG4gICAgfVxuICAgIGxldCBkaWRBYm9ydEZldGNoTG9hZHMgPSBhYm9ydFN0YWxlRmV0Y2hMb2Fkcyhsb2FkSWQpO1xuICAgIC8vIElmIHdlIGFyZSBjdXJyZW50bHkgaW4gYSBuYXZpZ2F0aW9uIGxvYWRpbmcgc3RhdGUgYW5kIHRoaXMgZmV0Y2hlciBpc1xuICAgIC8vIG1vcmUgcmVjZW50IHRoYW4gdGhlIG5hdmlnYXRpb24sIHdlIHdhbnQgdGhlIG5ld2VyIGRhdGEgc28gYWJvcnQgdGhlXG4gICAgLy8gbmF2aWdhdGlvbiBhbmQgY29tcGxldGUgaXQgd2l0aCB0aGUgZmV0Y2hlciBkYXRhXG4gICAgaWYgKHN0YXRlLm5hdmlnYXRpb24uc3RhdGUgPT09IFwibG9hZGluZ1wiICYmIGxvYWRJZCA+IHBlbmRpbmdOYXZpZ2F0aW9uTG9hZElkKSB7XG4gICAgICBpbnZhcmlhbnQocGVuZGluZ0FjdGlvbiwgXCJFeHBlY3RlZCBwZW5kaW5nIGFjdGlvblwiKTtcbiAgICAgIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciAmJiBwZW5kaW5nTmF2aWdhdGlvbkNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICAgIGNvbXBsZXRlTmF2aWdhdGlvbihzdGF0ZS5uYXZpZ2F0aW9uLmxvY2F0aW9uLCB7XG4gICAgICAgIG1hdGNoZXMsXG4gICAgICAgIGxvYWRlckRhdGEsXG4gICAgICAgIGVycm9ycyxcbiAgICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gb3RoZXJ3aXNlIGp1c3QgdXBkYXRlIHdpdGggdGhlIGZldGNoZXIgZGF0YSwgcHJlc2VydmluZyBhbnkgZXhpc3RpbmdcbiAgICAgIC8vIGxvYWRlckRhdGEgZm9yIGxvYWRlcnMgdGhhdCBkaWQgbm90IG5lZWQgdG8gcmVsb2FkLiAgV2UgaGF2ZSB0b1xuICAgICAgLy8gbWFudWFsbHkgbWVyZ2UgaGVyZSBzaW5jZSB3ZSBhcmVuJ3QgZ29pbmcgdGhyb3VnaCBjb21wbGV0ZU5hdmlnYXRpb25cbiAgICAgIHVwZGF0ZVN0YXRlKF9leHRlbmRzKHtcbiAgICAgICAgZXJyb3JzLFxuICAgICAgICBsb2FkZXJEYXRhOiBtZXJnZUxvYWRlckRhdGEoc3RhdGUubG9hZGVyRGF0YSwgbG9hZGVyRGF0YSwgbWF0Y2hlcywgZXJyb3JzKVxuICAgICAgfSwgZGlkQWJvcnRGZXRjaExvYWRzIHx8IHJldmFsaWRhdGluZ0ZldGNoZXJzLmxlbmd0aCA+IDAgPyB7XG4gICAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKVxuICAgICAgfSA6IHt9KSk7XG4gICAgICBpc1JldmFsaWRhdGlvblJlcXVpcmVkID0gZmFsc2U7XG4gICAgfVxuICB9XG4gIC8vIENhbGwgdGhlIG1hdGNoZWQgbG9hZGVyIGZvciBmZXRjaGVyLmxvYWQoKSwgaGFuZGxpbmcgcmVkaXJlY3RzLCBlcnJvcnMsIGV0Yy5cbiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlRmV0Y2hlckxvYWRlcihrZXksIHJvdXRlSWQsIHBhdGgsIG1hdGNoLCBtYXRjaGVzLCBzdWJtaXNzaW9uKSB7XG4gICAgbGV0IGV4aXN0aW5nRmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpO1xuICAgIC8vIFB1dCB0aGlzIGZldGNoZXIgaW50byBpdCdzIGxvYWRpbmcgc3RhdGVcbiAgICBsZXQgbG9hZGluZ0ZldGNoZXIgPSBnZXRMb2FkaW5nRmV0Y2hlcihzdWJtaXNzaW9uLCBleGlzdGluZ0ZldGNoZXIgPyBleGlzdGluZ0ZldGNoZXIuZGF0YSA6IHVuZGVmaW5lZCk7XG4gICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgbG9hZGluZ0ZldGNoZXIpO1xuICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKVxuICAgIH0pO1xuICAgIC8vIENhbGwgdGhlIGxvYWRlciBmb3IgdGhpcyBmZXRjaGVyIHJvdXRlIG1hdGNoXG4gICAgbGV0IGFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBsZXQgZmV0Y2hSZXF1ZXN0ID0gY3JlYXRlQ2xpZW50U2lkZVJlcXVlc3QoaW5pdC5oaXN0b3J5LCBwYXRoLCBhYm9ydENvbnRyb2xsZXIuc2lnbmFsKTtcbiAgICBmZXRjaENvbnRyb2xsZXJzLnNldChrZXksIGFib3J0Q29udHJvbGxlcik7XG4gICAgbGV0IHJlc3VsdCA9IGF3YWl0IGNhbGxMb2FkZXJPckFjdGlvbihcImxvYWRlclwiLCBmZXRjaFJlcXVlc3QsIG1hdGNoLCBtYXRjaGVzLCBtYW5pZmVzdCwgbWFwUm91dGVQcm9wZXJ0aWVzLCBiYXNlbmFtZSk7XG4gICAgLy8gRGVmZXJyZWQgaXNuJ3Qgc3VwcG9ydGVkIGZvciBmZXRjaGVyIGxvYWRzLCBhd2FpdCBldmVyeXRoaW5nIGFuZCB0cmVhdCBpdFxuICAgIC8vIGFzIGEgbm9ybWFsIGxvYWQuICByZXNvbHZlRGVmZXJyZWREYXRhIHdpbGwgcmV0dXJuIHVuZGVmaW5lZCBpZiB0aGlzXG4gICAgLy8gZmV0Y2hlciBnZXRzIGFib3J0ZWQsIHNvIHdlIGp1c3QgbGVhdmUgcmVzdWx0IHVudG91Y2hlZCBhbmQgc2hvcnQgY2lyY3VpdFxuICAgIC8vIGJlbG93IGlmIHRoYXQgaGFwcGVuc1xuICAgIGlmIChpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIHJlc3VsdCA9IChhd2FpdCByZXNvbHZlRGVmZXJyZWREYXRhKHJlc3VsdCwgZmV0Y2hSZXF1ZXN0LnNpZ25hbCwgdHJ1ZSkpIHx8IHJlc3VsdDtcbiAgICB9XG4gICAgLy8gV2UgY2FuIGRlbGV0ZSB0aGlzIHNvIGxvbmcgYXMgd2Ugd2VyZW4ndCBhYm9ydGVkIGJ5IG91ciBvdXIgb3duIGZldGNoZXJcbiAgICAvLyByZS1sb2FkIHdoaWNoIHdvdWxkIGhhdmUgcHV0IF9uZXdfIGNvbnRyb2xsZXIgaXMgaW4gZmV0Y2hDb250cm9sbGVyc1xuICAgIGlmIChmZXRjaENvbnRyb2xsZXJzLmdldChrZXkpID09PSBhYm9ydENvbnRyb2xsZXIpIHtcbiAgICAgIGZldGNoQ29udHJvbGxlcnMuZGVsZXRlKGtleSk7XG4gICAgfVxuICAgIGlmIChmZXRjaFJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gSWYgdGhlIGxvYWRlciB0aHJldyBhIHJlZGlyZWN0IFJlc3BvbnNlLCBzdGFydCBhIG5ldyBSRVBMQUNFIG5hdmlnYXRpb25cbiAgICBpZiAoaXNSZWRpcmVjdFJlc3VsdChyZXN1bHQpKSB7XG4gICAgICBmZXRjaFJlZGlyZWN0SWRzLmFkZChrZXkpO1xuICAgICAgYXdhaXQgc3RhcnRSZWRpcmVjdE5hdmlnYXRpb24oc3RhdGUsIHJlc3VsdCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIFByb2Nlc3MgYW55IG5vbi1yZWRpcmVjdCBlcnJvcnMgdGhyb3duXG4gICAgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkge1xuICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KHN0YXRlLm1hdGNoZXMsIHJvdXRlSWQpO1xuICAgICAgc3RhdGUuZmV0Y2hlcnMuZGVsZXRlKGtleSk7XG4gICAgICAvLyBUT0RPOiBJbiByZW1peCwgdGhpcyB3b3VsZCByZXNldCB0byBJRExFX05BVklHQVRJT04gaWYgaXQgd2FzIGEgY2F0Y2ggLVxuICAgICAgLy8gZG8gd2UgbmVlZCB0byBiZWhhdmUgYW55IGRpZmZlcmVudGx5IHdpdGggb3VyIG5vbi1yZWRpcmVjdCBlcnJvcnM/XG4gICAgICAvLyBXaGF0IGlmIGl0IHdhcyBhIG5vbi1yZWRpcmVjdCBSZXNwb25zZT9cbiAgICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpLFxuICAgICAgICBlcnJvcnM6IHtcbiAgICAgICAgICBbYm91bmRhcnlNYXRjaC5yb3V0ZS5pZF06IHJlc3VsdC5lcnJvclxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaW52YXJpYW50KCFpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCksIFwiVW5oYW5kbGVkIGZldGNoZXIgZGVmZXJyZWQgZGF0YVwiKTtcbiAgICAvLyBQdXQgdGhlIGZldGNoZXIgYmFjayBpbnRvIGFuIGlkbGUgc3RhdGVcbiAgICBsZXQgZG9uZUZldGNoZXIgPSBnZXREb25lRmV0Y2hlcihyZXN1bHQuZGF0YSk7XG4gICAgc3RhdGUuZmV0Y2hlcnMuc2V0KGtleSwgZG9uZUZldGNoZXIpO1xuICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgIGZldGNoZXJzOiBuZXcgTWFwKHN0YXRlLmZldGNoZXJzKVxuICAgIH0pO1xuICB9XG4gIC8qKlxuICAgKiBVdGlsaXR5IGZ1bmN0aW9uIHRvIGhhbmRsZSByZWRpcmVjdHMgcmV0dXJuZWQgZnJvbSBhbiBhY3Rpb24gb3IgbG9hZGVyLlxuICAgKiBOb3JtYWxseSwgYSByZWRpcmVjdCBcInJlcGxhY2VzXCIgdGhlIG5hdmlnYXRpb24gdGhhdCB0cmlnZ2VyZWQgaXQuICBTbywgZm9yXG4gICAqIGV4YW1wbGU6XG4gICAqXG4gICAqICAtIHVzZXIgaXMgb24gL2FcbiAgICogIC0gdXNlciBjbGlja3MgYSBsaW5rIHRvIC9iXG4gICAqICAtIGxvYWRlciBmb3IgL2IgcmVkaXJlY3RzIHRvIC9jXG4gICAqXG4gICAqIEluIGEgbm9uLUpTIGFwcCB0aGUgYnJvd3NlciB3b3VsZCB0cmFjayB0aGUgaW4tZmxpZ2h0IG5hdmlnYXRpb24gdG8gL2IgYW5kXG4gICAqIHRoZW4gcmVwbGFjZSBpdCB3aXRoIC9jIHdoZW4gaXQgZW5jb3VudGVyZWQgdGhlIHJlZGlyZWN0IHJlc3BvbnNlLiAgSW5cbiAgICogdGhlIGVuZCBpdCB3b3VsZCBvbmx5IGV2ZXIgdXBkYXRlIHRoZSBVUkwgYmFyIHdpdGggL2MuXG4gICAqXG4gICAqIEluIGNsaWVudC1zaWRlIHJvdXRpbmcgdXNpbmcgcHVzaFN0YXRlL3JlcGxhY2VTdGF0ZSwgd2UgYWltIHRvIGVtdWxhdGVcbiAgICogdGhpcyBiZWhhdmlvciBhbmQgd2UgYWxzbyBkbyBub3QgdXBkYXRlIGhpc3RvcnkgdW50aWwgdGhlIGVuZCBvZiB0aGVcbiAgICogbmF2aWdhdGlvbiAoaW5jbHVkaW5nIHByb2Nlc3NlZCByZWRpcmVjdHMpLiAgVGhpcyBtZWFucyB0aGF0IHdlIG5ldmVyXG4gICAqIGFjdHVhbGx5IHRvdWNoIGhpc3RvcnkgdW50aWwgd2UndmUgcHJvY2Vzc2VkIHJlZGlyZWN0cywgc28gd2UganVzdCB1c2VcbiAgICogdGhlIGhpc3RvcnkgYWN0aW9uIGZyb20gdGhlIG9yaWdpbmFsIG5hdmlnYXRpb24gKFBVU0ggb3IgUkVQTEFDRSkuXG4gICAqL1xuICBhc3luYyBmdW5jdGlvbiBzdGFydFJlZGlyZWN0TmF2aWdhdGlvbihzdGF0ZSwgcmVkaXJlY3QsIF90ZW1wKSB7XG4gICAgbGV0IHtcbiAgICAgIHN1Ym1pc3Npb24sXG4gICAgICByZXBsYWNlLFxuICAgICAgaXNGZXRjaEFjdGlvblJlZGlyZWN0XG4gICAgfSA9IF90ZW1wID09PSB2b2lkIDAgPyB7fSA6IF90ZW1wO1xuICAgIGlmIChyZWRpcmVjdC5yZXZhbGlkYXRlKSB7XG4gICAgICBpc1JldmFsaWRhdGlvblJlcXVpcmVkID0gdHJ1ZTtcbiAgICB9XG4gICAgbGV0IHJlZGlyZWN0TG9jYXRpb24gPSBjcmVhdGVMb2NhdGlvbihzdGF0ZS5sb2NhdGlvbiwgcmVkaXJlY3QubG9jYXRpb24sIC8vIFRPRE86IFRoaXMgY2FuIGJlIHJlbW92ZWQgb25jZSB3ZSBnZXQgcmlkIG9mIHVzZVRyYW5zaXRpb24gaW4gUmVtaXggdjJcbiAgICBfZXh0ZW5kcyh7XG4gICAgICBfaXNSZWRpcmVjdDogdHJ1ZVxuICAgIH0sIGlzRmV0Y2hBY3Rpb25SZWRpcmVjdCA/IHtcbiAgICAgIF9pc0ZldGNoQWN0aW9uUmVkaXJlY3Q6IHRydWVcbiAgICB9IDoge30pKTtcbiAgICBpbnZhcmlhbnQocmVkaXJlY3RMb2NhdGlvbiwgXCJFeHBlY3RlZCBhIGxvY2F0aW9uIG9uIHRoZSByZWRpcmVjdCBuYXZpZ2F0aW9uXCIpO1xuICAgIC8vIENoZWNrIGlmIHRoaXMgYW4gYWJzb2x1dGUgZXh0ZXJuYWwgcmVkaXJlY3QgdGhhdCBnb2VzIHRvIGEgbmV3IG9yaWdpblxuICAgIGlmIChBQlNPTFVURV9VUkxfUkVHRVgudGVzdChyZWRpcmVjdC5sb2NhdGlvbikgJiYgaXNCcm93c2VyKSB7XG4gICAgICBsZXQgdXJsID0gaW5pdC5oaXN0b3J5LmNyZWF0ZVVSTChyZWRpcmVjdC5sb2NhdGlvbik7XG4gICAgICBsZXQgaXNEaWZmZXJlbnRCYXNlbmFtZSA9IHN0cmlwQmFzZW5hbWUodXJsLnBhdGhuYW1lLCBiYXNlbmFtZSkgPT0gbnVsbDtcbiAgICAgIGlmIChyb3V0ZXJXaW5kb3cubG9jYXRpb24ub3JpZ2luICE9PSB1cmwub3JpZ2luIHx8IGlzRGlmZmVyZW50QmFzZW5hbWUpIHtcbiAgICAgICAgaWYgKHJlcGxhY2UpIHtcbiAgICAgICAgICByb3V0ZXJXaW5kb3cubG9jYXRpb24ucmVwbGFjZShyZWRpcmVjdC5sb2NhdGlvbik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcm91dGVyV2luZG93LmxvY2F0aW9uLmFzc2lnbihyZWRpcmVjdC5sb2NhdGlvbik7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBUaGVyZSdzIG5vIG5lZWQgdG8gYWJvcnQgb24gcmVkaXJlY3RzLCBzaW5jZSB3ZSBkb24ndCBkZXRlY3QgdGhlXG4gICAgLy8gcmVkaXJlY3QgdW50aWwgdGhlIGFjdGlvbi9sb2FkZXJzIGhhdmUgc2V0dGxlZFxuICAgIHBlbmRpbmdOYXZpZ2F0aW9uQ29udHJvbGxlciA9IG51bGw7XG4gICAgbGV0IHJlZGlyZWN0SGlzdG9yeUFjdGlvbiA9IHJlcGxhY2UgPT09IHRydWUgPyBBY3Rpb24uUmVwbGFjZSA6IEFjdGlvbi5QdXNoO1xuICAgIC8vIFVzZSB0aGUgaW5jb21pbmcgc3VibWlzc2lvbiBpZiBwcm92aWRlZCwgZmFsbGJhY2sgb24gdGhlIGFjdGl2ZSBvbmUgaW5cbiAgICAvLyBzdGF0ZS5uYXZpZ2F0aW9uXG4gICAgbGV0IGFjdGl2ZVN1Ym1pc3Npb24gPSBzdWJtaXNzaW9uIHx8IGdldFN1Ym1pc3Npb25Gcm9tTmF2aWdhdGlvbihzdGF0ZS5uYXZpZ2F0aW9uKTtcbiAgICAvLyBJZiB0aGlzIHdhcyBhIDMwNy8zMDggc3VibWlzc2lvbiB3ZSB3YW50IHRvIHByZXNlcnZlIHRoZSBIVFRQIG1ldGhvZCBhbmRcbiAgICAvLyByZS1zdWJtaXQgdGhlIEdFVC9QT1NUL1BVVC9QQVRDSC9ERUxFVEUgYXMgYSBzdWJtaXNzaW9uIG5hdmlnYXRpb24gdG8gdGhlXG4gICAgLy8gcmVkaXJlY3RlZCBsb2NhdGlvblxuICAgIGlmIChyZWRpcmVjdFByZXNlcnZlTWV0aG9kU3RhdHVzQ29kZXMuaGFzKHJlZGlyZWN0LnN0YXR1cykgJiYgYWN0aXZlU3VibWlzc2lvbiAmJiBpc011dGF0aW9uTWV0aG9kKGFjdGl2ZVN1Ym1pc3Npb24uZm9ybU1ldGhvZCkpIHtcbiAgICAgIGF3YWl0IHN0YXJ0TmF2aWdhdGlvbihyZWRpcmVjdEhpc3RvcnlBY3Rpb24sIHJlZGlyZWN0TG9jYXRpb24sIHtcbiAgICAgICAgc3VibWlzc2lvbjogX2V4dGVuZHMoe30sIGFjdGl2ZVN1Ym1pc3Npb24sIHtcbiAgICAgICAgICBmb3JtQWN0aW9uOiByZWRpcmVjdC5sb2NhdGlvblxuICAgICAgICB9KSxcbiAgICAgICAgLy8gUHJlc2VydmUgdGhpcyBmbGFnIGFjcm9zcyByZWRpcmVjdHNcbiAgICAgICAgcHJldmVudFNjcm9sbFJlc2V0OiBwZW5kaW5nUHJldmVudFNjcm9sbFJlc2V0XG4gICAgICB9KTtcbiAgICB9IGVsc2UgaWYgKGlzRmV0Y2hBY3Rpb25SZWRpcmVjdCkge1xuICAgICAgLy8gRm9yIGEgZmV0Y2ggYWN0aW9uIHJlZGlyZWN0LCB3ZSBraWNrIG9mZiBhIG5ldyBsb2FkaW5nIG5hdmlnYXRpb25cbiAgICAgIC8vIHdpdGhvdXQgdGhlIGZldGNoZXIgc3VibWlzc2lvbiwgYnV0IHdlIHNlbmQgaXQgYWxvbmcgZm9yIHNob3VsZFJldmFsaWRhdGVcbiAgICAgIGF3YWl0IHN0YXJ0TmF2aWdhdGlvbihyZWRpcmVjdEhpc3RvcnlBY3Rpb24sIHJlZGlyZWN0TG9jYXRpb24sIHtcbiAgICAgICAgb3ZlcnJpZGVOYXZpZ2F0aW9uOiBnZXRMb2FkaW5nTmF2aWdhdGlvbihyZWRpcmVjdExvY2F0aW9uKSxcbiAgICAgICAgZmV0Y2hlclN1Ym1pc3Npb246IGFjdGl2ZVN1Ym1pc3Npb24sXG4gICAgICAgIC8vIFByZXNlcnZlIHRoaXMgZmxhZyBhY3Jvc3MgcmVkaXJlY3RzXG4gICAgICAgIHByZXZlbnRTY3JvbGxSZXNldDogcGVuZGluZ1ByZXZlbnRTY3JvbGxSZXNldFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIElmIHdlIGhhdmUgYSBzdWJtaXNzaW9uLCB3ZSB3aWxsIHByZXNlcnZlIGl0IHRocm91Z2ggdGhlIHJlZGlyZWN0IG5hdmlnYXRpb25cbiAgICAgIGxldCBvdmVycmlkZU5hdmlnYXRpb24gPSBnZXRMb2FkaW5nTmF2aWdhdGlvbihyZWRpcmVjdExvY2F0aW9uLCBhY3RpdmVTdWJtaXNzaW9uKTtcbiAgICAgIGF3YWl0IHN0YXJ0TmF2aWdhdGlvbihyZWRpcmVjdEhpc3RvcnlBY3Rpb24sIHJlZGlyZWN0TG9jYXRpb24sIHtcbiAgICAgICAgb3ZlcnJpZGVOYXZpZ2F0aW9uLFxuICAgICAgICAvLyBQcmVzZXJ2ZSB0aGlzIGZsYWcgYWNyb3NzIHJlZGlyZWN0c1xuICAgICAgICBwcmV2ZW50U2Nyb2xsUmVzZXQ6IHBlbmRpbmdQcmV2ZW50U2Nyb2xsUmVzZXRcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBhc3luYyBmdW5jdGlvbiBjYWxsTG9hZGVyc0FuZE1heWJlUmVzb2x2ZURhdGEoY3VycmVudE1hdGNoZXMsIG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIGZldGNoZXJzVG9Mb2FkLCByZXF1ZXN0KSB7XG4gICAgLy8gQ2FsbCBhbGwgbmF2aWdhdGlvbiBsb2FkZXJzIGFuZCByZXZhbGlkYXRpbmcgZmV0Y2hlciBsb2FkZXJzIGluIHBhcmFsbGVsLFxuICAgIC8vIHRoZW4gc2xpY2Ugb2ZmIHRoZSByZXN1bHRzIGludG8gc2VwYXJhdGUgYXJyYXlzIHNvIHdlIGNhbiBoYW5kbGUgdGhlbVxuICAgIC8vIGFjY29yZGluZ2x5XG4gICAgbGV0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChbLi4ubWF0Y2hlc1RvTG9hZC5tYXAobWF0Y2ggPT4gY2FsbExvYWRlck9yQWN0aW9uKFwibG9hZGVyXCIsIHJlcXVlc3QsIG1hdGNoLCBtYXRjaGVzLCBtYW5pZmVzdCwgbWFwUm91dGVQcm9wZXJ0aWVzLCBiYXNlbmFtZSkpLCAuLi5mZXRjaGVyc1RvTG9hZC5tYXAoZiA9PiB7XG4gICAgICBpZiAoZi5tYXRjaGVzICYmIGYubWF0Y2ggJiYgZi5jb250cm9sbGVyKSB7XG4gICAgICAgIHJldHVybiBjYWxsTG9hZGVyT3JBY3Rpb24oXCJsb2FkZXJcIiwgY3JlYXRlQ2xpZW50U2lkZVJlcXVlc3QoaW5pdC5oaXN0b3J5LCBmLnBhdGgsIGYuY29udHJvbGxlci5zaWduYWwpLCBmLm1hdGNoLCBmLm1hdGNoZXMsIG1hbmlmZXN0LCBtYXBSb3V0ZVByb3BlcnRpZXMsIGJhc2VuYW1lKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxldCBlcnJvciA9IHtcbiAgICAgICAgICB0eXBlOiBSZXN1bHRUeXBlLmVycm9yLFxuICAgICAgICAgIGVycm9yOiBnZXRJbnRlcm5hbFJvdXRlckVycm9yKDQwNCwge1xuICAgICAgICAgICAgcGF0aG5hbWU6IGYucGF0aFxuICAgICAgICAgIH0pXG4gICAgICAgIH07XG4gICAgICAgIHJldHVybiBlcnJvcjtcbiAgICAgIH1cbiAgICB9KV0pO1xuICAgIGxldCBsb2FkZXJSZXN1bHRzID0gcmVzdWx0cy5zbGljZSgwLCBtYXRjaGVzVG9Mb2FkLmxlbmd0aCk7XG4gICAgbGV0IGZldGNoZXJSZXN1bHRzID0gcmVzdWx0cy5zbGljZShtYXRjaGVzVG9Mb2FkLmxlbmd0aCk7XG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoW3Jlc29sdmVEZWZlcnJlZFJlc3VsdHMoY3VycmVudE1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIGxvYWRlclJlc3VsdHMsIGxvYWRlclJlc3VsdHMubWFwKCgpID0+IHJlcXVlc3Quc2lnbmFsKSwgZmFsc2UsIHN0YXRlLmxvYWRlckRhdGEpLCByZXNvbHZlRGVmZXJyZWRSZXN1bHRzKGN1cnJlbnRNYXRjaGVzLCBmZXRjaGVyc1RvTG9hZC5tYXAoZiA9PiBmLm1hdGNoKSwgZmV0Y2hlclJlc3VsdHMsIGZldGNoZXJzVG9Mb2FkLm1hcChmID0+IGYuY29udHJvbGxlciA/IGYuY29udHJvbGxlci5zaWduYWwgOiBudWxsKSwgdHJ1ZSldKTtcbiAgICByZXR1cm4ge1xuICAgICAgcmVzdWx0cyxcbiAgICAgIGxvYWRlclJlc3VsdHMsXG4gICAgICBmZXRjaGVyUmVzdWx0c1xuICAgIH07XG4gIH1cbiAgZnVuY3Rpb24gaW50ZXJydXB0QWN0aXZlTG9hZHMoKSB7XG4gICAgLy8gRXZlcnkgaW50ZXJydXB0aW9uIHRyaWdnZXJzIGEgcmV2YWxpZGF0aW9uXG4gICAgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCA9IHRydWU7XG4gICAgLy8gQ2FuY2VsIHBlbmRpbmcgcm91dGUtbGV2ZWwgZGVmZXJyZWRzIGFuZCBtYXJrIGNhbmNlbGxlZCByb3V0ZXMgZm9yXG4gICAgLy8gcmV2YWxpZGF0aW9uXG4gICAgY2FuY2VsbGVkRGVmZXJyZWRSb3V0ZXMucHVzaCguLi5jYW5jZWxBY3RpdmVEZWZlcnJlZHMoKSk7XG4gICAgLy8gQWJvcnQgaW4tZmxpZ2h0IGZldGNoZXIgbG9hZHNcbiAgICBmZXRjaExvYWRNYXRjaGVzLmZvckVhY2goKF8sIGtleSkgPT4ge1xuICAgICAgaWYgKGZldGNoQ29udHJvbGxlcnMuaGFzKGtleSkpIHtcbiAgICAgICAgY2FuY2VsbGVkRmV0Y2hlckxvYWRzLnB1c2goa2V5KTtcbiAgICAgICAgYWJvcnRGZXRjaGVyKGtleSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgZnVuY3Rpb24gc2V0RmV0Y2hlckVycm9yKGtleSwgcm91dGVJZCwgZXJyb3IpIHtcbiAgICBsZXQgYm91bmRhcnlNYXRjaCA9IGZpbmROZWFyZXN0Qm91bmRhcnkoc3RhdGUubWF0Y2hlcywgcm91dGVJZCk7XG4gICAgZGVsZXRlRmV0Y2hlcihrZXkpO1xuICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgIGVycm9yczoge1xuICAgICAgICBbYm91bmRhcnlNYXRjaC5yb3V0ZS5pZF06IGVycm9yXG4gICAgICB9LFxuICAgICAgZmV0Y2hlcnM6IG5ldyBNYXAoc3RhdGUuZmV0Y2hlcnMpXG4gICAgfSk7XG4gIH1cbiAgZnVuY3Rpb24gZGVsZXRlRmV0Y2hlcihrZXkpIHtcbiAgICBsZXQgZmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpO1xuICAgIC8vIERvbid0IGFib3J0IHRoZSBjb250cm9sbGVyIGlmIHRoaXMgaXMgYSBkZWxldGlvbiBvZiBhIGZldGNoZXIuc3VibWl0KClcbiAgICAvLyBpbiBpdCdzIGxvYWRpbmcgcGhhc2Ugc2luY2UgLSB3ZSBkb24ndCB3YW50IHRvIGFib3J0IHRoZSBjb3JyZXNwb25kaW5nXG4gICAgLy8gcmV2YWxpZGF0aW9uIGFuZCB3YW50IHRoZW0gdG8gY29tcGxldGUgYW5kIGxhbmRcbiAgICBpZiAoZmV0Y2hDb250cm9sbGVycy5oYXMoa2V5KSAmJiAhKGZldGNoZXIgJiYgZmV0Y2hlci5zdGF0ZSA9PT0gXCJsb2FkaW5nXCIgJiYgZmV0Y2hSZWxvYWRJZHMuaGFzKGtleSkpKSB7XG4gICAgICBhYm9ydEZldGNoZXIoa2V5KTtcbiAgICB9XG4gICAgZmV0Y2hMb2FkTWF0Y2hlcy5kZWxldGUoa2V5KTtcbiAgICBmZXRjaFJlbG9hZElkcy5kZWxldGUoa2V5KTtcbiAgICBmZXRjaFJlZGlyZWN0SWRzLmRlbGV0ZShrZXkpO1xuICAgIHN0YXRlLmZldGNoZXJzLmRlbGV0ZShrZXkpO1xuICB9XG4gIGZ1bmN0aW9uIGFib3J0RmV0Y2hlcihrZXkpIHtcbiAgICBsZXQgY29udHJvbGxlciA9IGZldGNoQ29udHJvbGxlcnMuZ2V0KGtleSk7XG4gICAgaW52YXJpYW50KGNvbnRyb2xsZXIsIFwiRXhwZWN0ZWQgZmV0Y2ggY29udHJvbGxlcjogXCIgKyBrZXkpO1xuICAgIGNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICBmZXRjaENvbnRyb2xsZXJzLmRlbGV0ZShrZXkpO1xuICB9XG4gIGZ1bmN0aW9uIG1hcmtGZXRjaGVyc0RvbmUoa2V5cykge1xuICAgIGZvciAobGV0IGtleSBvZiBrZXlzKSB7XG4gICAgICBsZXQgZmV0Y2hlciA9IGdldEZldGNoZXIoa2V5KTtcbiAgICAgIGxldCBkb25lRmV0Y2hlciA9IGdldERvbmVGZXRjaGVyKGZldGNoZXIuZGF0YSk7XG4gICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBkb25lRmV0Y2hlcik7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIG1hcmtGZXRjaFJlZGlyZWN0c0RvbmUoKSB7XG4gICAgbGV0IGRvbmVLZXlzID0gW107XG4gICAgbGV0IHVwZGF0ZWRGZXRjaGVycyA9IGZhbHNlO1xuICAgIGZvciAobGV0IGtleSBvZiBmZXRjaFJlZGlyZWN0SWRzKSB7XG4gICAgICBsZXQgZmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpO1xuICAgICAgaW52YXJpYW50KGZldGNoZXIsIFwiRXhwZWN0ZWQgZmV0Y2hlcjogXCIgKyBrZXkpO1xuICAgICAgaWYgKGZldGNoZXIuc3RhdGUgPT09IFwibG9hZGluZ1wiKSB7XG4gICAgICAgIGZldGNoUmVkaXJlY3RJZHMuZGVsZXRlKGtleSk7XG4gICAgICAgIGRvbmVLZXlzLnB1c2goa2V5KTtcbiAgICAgICAgdXBkYXRlZEZldGNoZXJzID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgbWFya0ZldGNoZXJzRG9uZShkb25lS2V5cyk7XG4gICAgcmV0dXJuIHVwZGF0ZWRGZXRjaGVycztcbiAgfVxuICBmdW5jdGlvbiBhYm9ydFN0YWxlRmV0Y2hMb2FkcyhsYW5kZWRJZCkge1xuICAgIGxldCB5ZWV0ZWRLZXlzID0gW107XG4gICAgZm9yIChsZXQgW2tleSwgaWRdIG9mIGZldGNoUmVsb2FkSWRzKSB7XG4gICAgICBpZiAoaWQgPCBsYW5kZWRJZCkge1xuICAgICAgICBsZXQgZmV0Y2hlciA9IHN0YXRlLmZldGNoZXJzLmdldChrZXkpO1xuICAgICAgICBpbnZhcmlhbnQoZmV0Y2hlciwgXCJFeHBlY3RlZCBmZXRjaGVyOiBcIiArIGtleSk7XG4gICAgICAgIGlmIChmZXRjaGVyLnN0YXRlID09PSBcImxvYWRpbmdcIikge1xuICAgICAgICAgIGFib3J0RmV0Y2hlcihrZXkpO1xuICAgICAgICAgIGZldGNoUmVsb2FkSWRzLmRlbGV0ZShrZXkpO1xuICAgICAgICAgIHllZXRlZEtleXMucHVzaChrZXkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIG1hcmtGZXRjaGVyc0RvbmUoeWVldGVkS2V5cyk7XG4gICAgcmV0dXJuIHllZXRlZEtleXMubGVuZ3RoID4gMDtcbiAgfVxuICBmdW5jdGlvbiBnZXRCbG9ja2VyKGtleSwgZm4pIHtcbiAgICBsZXQgYmxvY2tlciA9IHN0YXRlLmJsb2NrZXJzLmdldChrZXkpIHx8IElETEVfQkxPQ0tFUjtcbiAgICBpZiAoYmxvY2tlckZ1bmN0aW9ucy5nZXQoa2V5KSAhPT0gZm4pIHtcbiAgICAgIGJsb2NrZXJGdW5jdGlvbnMuc2V0KGtleSwgZm4pO1xuICAgIH1cbiAgICByZXR1cm4gYmxvY2tlcjtcbiAgfVxuICBmdW5jdGlvbiBkZWxldGVCbG9ja2VyKGtleSkge1xuICAgIHN0YXRlLmJsb2NrZXJzLmRlbGV0ZShrZXkpO1xuICAgIGJsb2NrZXJGdW5jdGlvbnMuZGVsZXRlKGtleSk7XG4gIH1cbiAgLy8gVXRpbGl0eSBmdW5jdGlvbiB0byB1cGRhdGUgYmxvY2tlcnMsIGVuc3VyaW5nIHZhbGlkIHN0YXRlIHRyYW5zaXRpb25zXG4gIGZ1bmN0aW9uIHVwZGF0ZUJsb2NrZXIoa2V5LCBuZXdCbG9ja2VyKSB7XG4gICAgbGV0IGJsb2NrZXIgPSBzdGF0ZS5ibG9ja2Vycy5nZXQoa2V5KSB8fCBJRExFX0JMT0NLRVI7XG4gICAgLy8gUG9vciBtYW5zIHN0YXRlIG1hY2hpbmUgOilcbiAgICAvLyBodHRwczovL21lcm1haWQubGl2ZS9lZGl0I3Bha286ZU5xVmtjOU93ekFNeGw4bDhubmpBWXJFdERJT0hFQklnd3ZLSlRSZUd5M19sRHBJcU8yN2s2YXdNRzBYY3JMbG56ODdud2RvbkVTb2dLWFhCdUU3OXJxNzVYWk8zLXlIZHMwUkpWdXY3MFlyUGxVckNFZTJIZnJPUlMzcnVicVpmdWh0cGc1Qzl3azV0WjRWS2NSVXE4OHE5WjhSUzAtNDhjRTFpSEprTDB1Z2JIdUZMdXM5TDZzcFp5OG5YOU1QMkNOZG9tVmFwb3NxdTNmR2F5VDhUOC1qSlF3aGVwb19VdHBnQlFhREVVb20wNGRaaEFOMWFKQkRsVUtKQnhFMWNlQjJTbWowTWxuLUlCVzVBRlUyZHdVaWt0dF8yUWFxMmRCZmFLZEV1cDg1VVY3WWQtZEtqbG5rYWJsMlB2cjBEVGtUcmVNXG4gICAgaW52YXJpYW50KGJsb2NrZXIuc3RhdGUgPT09IFwidW5ibG9ja2VkXCIgJiYgbmV3QmxvY2tlci5zdGF0ZSA9PT0gXCJibG9ja2VkXCIgfHwgYmxvY2tlci5zdGF0ZSA9PT0gXCJibG9ja2VkXCIgJiYgbmV3QmxvY2tlci5zdGF0ZSA9PT0gXCJibG9ja2VkXCIgfHwgYmxvY2tlci5zdGF0ZSA9PT0gXCJibG9ja2VkXCIgJiYgbmV3QmxvY2tlci5zdGF0ZSA9PT0gXCJwcm9jZWVkaW5nXCIgfHwgYmxvY2tlci5zdGF0ZSA9PT0gXCJibG9ja2VkXCIgJiYgbmV3QmxvY2tlci5zdGF0ZSA9PT0gXCJ1bmJsb2NrZWRcIiB8fCBibG9ja2VyLnN0YXRlID09PSBcInByb2NlZWRpbmdcIiAmJiBuZXdCbG9ja2VyLnN0YXRlID09PSBcInVuYmxvY2tlZFwiLCBcIkludmFsaWQgYmxvY2tlciBzdGF0ZSB0cmFuc2l0aW9uOiBcIiArIGJsb2NrZXIuc3RhdGUgKyBcIiAtPiBcIiArIG5ld0Jsb2NrZXIuc3RhdGUpO1xuICAgIGxldCBibG9ja2VycyA9IG5ldyBNYXAoc3RhdGUuYmxvY2tlcnMpO1xuICAgIGJsb2NrZXJzLnNldChrZXksIG5ld0Jsb2NrZXIpO1xuICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgIGJsb2NrZXJzXG4gICAgfSk7XG4gIH1cbiAgZnVuY3Rpb24gc2hvdWxkQmxvY2tOYXZpZ2F0aW9uKF9yZWYyKSB7XG4gICAgbGV0IHtcbiAgICAgIGN1cnJlbnRMb2NhdGlvbixcbiAgICAgIG5leHRMb2NhdGlvbixcbiAgICAgIGhpc3RvcnlBY3Rpb25cbiAgICB9ID0gX3JlZjI7XG4gICAgaWYgKGJsb2NrZXJGdW5jdGlvbnMuc2l6ZSA9PT0gMCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBXZSBvbnkgc3VwcG9ydCBhIHNpbmdsZSBhY3RpdmUgYmxvY2tlciBhdCB0aGUgbW9tZW50IHNpbmNlIHdlIGRvbid0IGhhdmVcbiAgICAvLyBhbnkgY29tcGVsbGluZyB1c2UgY2FzZXMgZm9yIG11bHRpLWJsb2NrZXIgeWV0XG4gICAgaWYgKGJsb2NrZXJGdW5jdGlvbnMuc2l6ZSA+IDEpIHtcbiAgICAgIHdhcm5pbmcoZmFsc2UsIFwiQSByb3V0ZXIgb25seSBzdXBwb3J0cyBvbmUgYmxvY2tlciBhdCBhIHRpbWVcIik7XG4gICAgfVxuICAgIGxldCBlbnRyaWVzID0gQXJyYXkuZnJvbShibG9ja2VyRnVuY3Rpb25zLmVudHJpZXMoKSk7XG4gICAgbGV0IFtibG9ja2VyS2V5LCBibG9ja2VyRnVuY3Rpb25dID0gZW50cmllc1tlbnRyaWVzLmxlbmd0aCAtIDFdO1xuICAgIGxldCBibG9ja2VyID0gc3RhdGUuYmxvY2tlcnMuZ2V0KGJsb2NrZXJLZXkpO1xuICAgIGlmIChibG9ja2VyICYmIGJsb2NrZXIuc3RhdGUgPT09IFwicHJvY2VlZGluZ1wiKSB7XG4gICAgICAvLyBJZiB0aGUgYmxvY2tlciBpcyBjdXJyZW50bHkgcHJvY2VlZGluZywgd2UgZG9uJ3QgbmVlZCB0byByZS1jaGVja1xuICAgICAgLy8gaXQgYW5kIGNhbiBsZXQgdGhpcyBuYXZpZ2F0aW9uIGNvbnRpbnVlXG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEF0IHRoaXMgcG9pbnQsIHdlIGtub3cgd2UncmUgdW5ibG9ja2VkL2Jsb2NrZWQgc28gd2UgbmVlZCB0byBjaGVjayB0aGVcbiAgICAvLyB1c2VyLXByb3ZpZGVkIGJsb2NrZXIgZnVuY3Rpb25cbiAgICBpZiAoYmxvY2tlckZ1bmN0aW9uKHtcbiAgICAgIGN1cnJlbnRMb2NhdGlvbixcbiAgICAgIG5leHRMb2NhdGlvbixcbiAgICAgIGhpc3RvcnlBY3Rpb25cbiAgICB9KSkge1xuICAgICAgcmV0dXJuIGJsb2NrZXJLZXk7XG4gICAgfVxuICB9XG4gIGZ1bmN0aW9uIGNhbmNlbEFjdGl2ZURlZmVycmVkcyhwcmVkaWNhdGUpIHtcbiAgICBsZXQgY2FuY2VsbGVkUm91dGVJZHMgPSBbXTtcbiAgICBhY3RpdmVEZWZlcnJlZHMuZm9yRWFjaCgoZGZkLCByb3V0ZUlkKSA9PiB7XG4gICAgICBpZiAoIXByZWRpY2F0ZSB8fCBwcmVkaWNhdGUocm91dGVJZCkpIHtcbiAgICAgICAgLy8gQ2FuY2VsIHRoZSBkZWZlcnJlZCAtIGJ1dCBkbyBub3QgcmVtb3ZlIGZyb20gYWN0aXZlRGVmZXJyZWRzIGhlcmUgLVxuICAgICAgICAvLyB3ZSByZWx5IG9uIHRoZSBzdWJzY3JpYmVycyB0byBkbyB0aGF0IHNvIG91ciB0ZXN0cyBjYW4gYXNzZXJ0IHByb3BlclxuICAgICAgICAvLyBjbGVhbnVwIHZpYSBfaW50ZXJuYWxBY3RpdmVEZWZlcnJlZHNcbiAgICAgICAgZGZkLmNhbmNlbCgpO1xuICAgICAgICBjYW5jZWxsZWRSb3V0ZUlkcy5wdXNoKHJvdXRlSWQpO1xuICAgICAgICBhY3RpdmVEZWZlcnJlZHMuZGVsZXRlKHJvdXRlSWQpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBjYW5jZWxsZWRSb3V0ZUlkcztcbiAgfVxuICAvLyBPcHQgaW4gdG8gY2FwdHVyaW5nIGFuZCByZXBvcnRpbmcgc2Nyb2xsIHBvc2l0aW9ucyBkdXJpbmcgbmF2aWdhdGlvbnMsXG4gIC8vIHVzZWQgYnkgdGhlIDxTY3JvbGxSZXN0b3JhdGlvbj4gY29tcG9uZW50XG4gIGZ1bmN0aW9uIGVuYWJsZVNjcm9sbFJlc3RvcmF0aW9uKHBvc2l0aW9ucywgZ2V0UG9zaXRpb24sIGdldEtleSkge1xuICAgIHNhdmVkU2Nyb2xsUG9zaXRpb25zID0gcG9zaXRpb25zO1xuICAgIGdldFNjcm9sbFBvc2l0aW9uID0gZ2V0UG9zaXRpb247XG4gICAgZ2V0U2Nyb2xsUmVzdG9yYXRpb25LZXkgPSBnZXRLZXkgfHwgbnVsbDtcbiAgICAvLyBQZXJmb3JtIGluaXRpYWwgaHlkcmF0aW9uIHNjcm9sbCByZXN0b3JhdGlvbiwgc2luY2Ugd2UgbWlzcyB0aGUgYm9hdCBvblxuICAgIC8vIHRoZSBpbml0aWFsIHVwZGF0ZVN0YXRlKCkgYmVjYXVzZSB3ZSd2ZSBub3QgeWV0IHJlbmRlcmVkIDxTY3JvbGxSZXN0b3JhdGlvbi8+XG4gICAgLy8gYW5kIHRoZXJlZm9yZSBoYXZlIG5vIHNhdmVkU2Nyb2xsUG9zaXRpb25zIGF2YWlsYWJsZVxuICAgIGlmICghaW5pdGlhbFNjcm9sbFJlc3RvcmVkICYmIHN0YXRlLm5hdmlnYXRpb24gPT09IElETEVfTkFWSUdBVElPTikge1xuICAgICAgaW5pdGlhbFNjcm9sbFJlc3RvcmVkID0gdHJ1ZTtcbiAgICAgIGxldCB5ID0gZ2V0U2F2ZWRTY3JvbGxQb3NpdGlvbihzdGF0ZS5sb2NhdGlvbiwgc3RhdGUubWF0Y2hlcyk7XG4gICAgICBpZiAoeSAhPSBudWxsKSB7XG4gICAgICAgIHVwZGF0ZVN0YXRlKHtcbiAgICAgICAgICByZXN0b3JlU2Nyb2xsUG9zaXRpb246IHlcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBzYXZlZFNjcm9sbFBvc2l0aW9ucyA9IG51bGw7XG4gICAgICBnZXRTY3JvbGxQb3NpdGlvbiA9IG51bGw7XG4gICAgICBnZXRTY3JvbGxSZXN0b3JhdGlvbktleSA9IG51bGw7XG4gICAgfTtcbiAgfVxuICBmdW5jdGlvbiBnZXRTY3JvbGxLZXkobG9jYXRpb24sIG1hdGNoZXMpIHtcbiAgICBpZiAoZ2V0U2Nyb2xsUmVzdG9yYXRpb25LZXkpIHtcbiAgICAgIGxldCBrZXkgPSBnZXRTY3JvbGxSZXN0b3JhdGlvbktleShsb2NhdGlvbiwgbWF0Y2hlcy5tYXAobSA9PiBjcmVhdGVVc2VNYXRjaGVzTWF0Y2gobSwgc3RhdGUubG9hZGVyRGF0YSkpKTtcbiAgICAgIHJldHVybiBrZXkgfHwgbG9jYXRpb24ua2V5O1xuICAgIH1cbiAgICByZXR1cm4gbG9jYXRpb24ua2V5O1xuICB9XG4gIGZ1bmN0aW9uIHNhdmVTY3JvbGxQb3NpdGlvbihsb2NhdGlvbiwgbWF0Y2hlcykge1xuICAgIGlmIChzYXZlZFNjcm9sbFBvc2l0aW9ucyAmJiBnZXRTY3JvbGxQb3NpdGlvbikge1xuICAgICAgbGV0IGtleSA9IGdldFNjcm9sbEtleShsb2NhdGlvbiwgbWF0Y2hlcyk7XG4gICAgICBzYXZlZFNjcm9sbFBvc2l0aW9uc1trZXldID0gZ2V0U2Nyb2xsUG9zaXRpb24oKTtcbiAgICB9XG4gIH1cbiAgZnVuY3Rpb24gZ2V0U2F2ZWRTY3JvbGxQb3NpdGlvbihsb2NhdGlvbiwgbWF0Y2hlcykge1xuICAgIGlmIChzYXZlZFNjcm9sbFBvc2l0aW9ucykge1xuICAgICAgbGV0IGtleSA9IGdldFNjcm9sbEtleShsb2NhdGlvbiwgbWF0Y2hlcyk7XG4gICAgICBsZXQgeSA9IHNhdmVkU2Nyb2xsUG9zaXRpb25zW2tleV07XG4gICAgICBpZiAodHlwZW9mIHkgPT09IFwibnVtYmVyXCIpIHtcbiAgICAgICAgcmV0dXJuIHk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGZ1bmN0aW9uIF9pbnRlcm5hbFNldFJvdXRlcyhuZXdSb3V0ZXMpIHtcbiAgICBtYW5pZmVzdCA9IHt9O1xuICAgIGluRmxpZ2h0RGF0YVJvdXRlcyA9IGNvbnZlcnRSb3V0ZXNUb0RhdGFSb3V0ZXMobmV3Um91dGVzLCBtYXBSb3V0ZVByb3BlcnRpZXMsIHVuZGVmaW5lZCwgbWFuaWZlc3QpO1xuICB9XG4gIHJvdXRlciA9IHtcbiAgICBnZXQgYmFzZW5hbWUoKSB7XG4gICAgICByZXR1cm4gYmFzZW5hbWU7XG4gICAgfSxcbiAgICBnZXQgc3RhdGUoKSB7XG4gICAgICByZXR1cm4gc3RhdGU7XG4gICAgfSxcbiAgICBnZXQgcm91dGVzKCkge1xuICAgICAgcmV0dXJuIGRhdGFSb3V0ZXM7XG4gICAgfSxcbiAgICBpbml0aWFsaXplLFxuICAgIHN1YnNjcmliZSxcbiAgICBlbmFibGVTY3JvbGxSZXN0b3JhdGlvbixcbiAgICBuYXZpZ2F0ZSxcbiAgICBmZXRjaCxcbiAgICByZXZhbGlkYXRlLFxuICAgIC8vIFBhc3N0aHJvdWdoIHRvIGhpc3RvcnktYXdhcmUgY3JlYXRlSHJlZiB1c2VkIGJ5IHVzZUhyZWYgc28gd2UgZ2V0IHByb3BlclxuICAgIC8vIGhhc2gtYXdhcmUgVVJMcyBpbiBET00gcGF0aHNcbiAgICBjcmVhdGVIcmVmOiB0byA9PiBpbml0Lmhpc3RvcnkuY3JlYXRlSHJlZih0byksXG4gICAgZW5jb2RlTG9jYXRpb246IHRvID0+IGluaXQuaGlzdG9yeS5lbmNvZGVMb2NhdGlvbih0byksXG4gICAgZ2V0RmV0Y2hlcixcbiAgICBkZWxldGVGZXRjaGVyLFxuICAgIGRpc3Bvc2UsXG4gICAgZ2V0QmxvY2tlcixcbiAgICBkZWxldGVCbG9ja2VyLFxuICAgIF9pbnRlcm5hbEZldGNoQ29udHJvbGxlcnM6IGZldGNoQ29udHJvbGxlcnMsXG4gICAgX2ludGVybmFsQWN0aXZlRGVmZXJyZWRzOiBhY3RpdmVEZWZlcnJlZHMsXG4gICAgLy8gVE9ETzogUmVtb3ZlIHNldFJvdXRlcywgaXQncyB0ZW1wb3JhcnkgdG8gYXZvaWQgZGVhbGluZyB3aXRoXG4gICAgLy8gdXBkYXRpbmcgdGhlIHRyZWUgd2hpbGUgdmFsaWRhdGluZyB0aGUgdXBkYXRlIGFsZ29yaXRobS5cbiAgICBfaW50ZXJuYWxTZXRSb3V0ZXNcbiAgfTtcbiAgcmV0dXJuIHJvdXRlcjtcbn1cbi8vI2VuZHJlZ2lvblxuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbi8vI3JlZ2lvbiBjcmVhdGVTdGF0aWNIYW5kbGVyXG4vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuY29uc3QgVU5TQUZFX0RFRkVSUkVEX1NZTUJPTCA9IFN5bWJvbChcImRlZmVycmVkXCIpO1xuZnVuY3Rpb24gY3JlYXRlU3RhdGljSGFuZGxlcihyb3V0ZXMsIG9wdHMpIHtcbiAgaW52YXJpYW50KHJvdXRlcy5sZW5ndGggPiAwLCBcIllvdSBtdXN0IHByb3ZpZGUgYSBub24tZW1wdHkgcm91dGVzIGFycmF5IHRvIGNyZWF0ZVN0YXRpY0hhbmRsZXJcIik7XG4gIGxldCBtYW5pZmVzdCA9IHt9O1xuICBsZXQgYmFzZW5hbWUgPSAob3B0cyA/IG9wdHMuYmFzZW5hbWUgOiBudWxsKSB8fCBcIi9cIjtcbiAgbGV0IG1hcFJvdXRlUHJvcGVydGllcztcbiAgaWYgKG9wdHMgIT0gbnVsbCAmJiBvcHRzLm1hcFJvdXRlUHJvcGVydGllcykge1xuICAgIG1hcFJvdXRlUHJvcGVydGllcyA9IG9wdHMubWFwUm91dGVQcm9wZXJ0aWVzO1xuICB9IGVsc2UgaWYgKG9wdHMgIT0gbnVsbCAmJiBvcHRzLmRldGVjdEVycm9yQm91bmRhcnkpIHtcbiAgICAvLyBJZiB0aGV5IGFyZSBzdGlsbCB1c2luZyB0aGUgZGVwcmVjYXRlZCB2ZXJzaW9uLCB3cmFwIGl0IHdpdGggdGhlIG5ldyBBUElcbiAgICBsZXQgZGV0ZWN0RXJyb3JCb3VuZGFyeSA9IG9wdHMuZGV0ZWN0RXJyb3JCb3VuZGFyeTtcbiAgICBtYXBSb3V0ZVByb3BlcnRpZXMgPSByb3V0ZSA9PiAoe1xuICAgICAgaGFzRXJyb3JCb3VuZGFyeTogZGV0ZWN0RXJyb3JCb3VuZGFyeShyb3V0ZSlcbiAgICB9KTtcbiAgfSBlbHNlIHtcbiAgICBtYXBSb3V0ZVByb3BlcnRpZXMgPSBkZWZhdWx0TWFwUm91dGVQcm9wZXJ0aWVzO1xuICB9XG4gIGxldCBkYXRhUm91dGVzID0gY29udmVydFJvdXRlc1RvRGF0YVJvdXRlcyhyb3V0ZXMsIG1hcFJvdXRlUHJvcGVydGllcywgdW5kZWZpbmVkLCBtYW5pZmVzdCk7XG4gIC8qKlxuICAgKiBUaGUgcXVlcnkoKSBtZXRob2QgaXMgaW50ZW5kZWQgZm9yIGRvY3VtZW50IHJlcXVlc3RzLCBpbiB3aGljaCB3ZSB3YW50IHRvXG4gICAqIGNhbGwgYW4gb3B0aW9uYWwgYWN0aW9uIGFuZCBwb3RlbnRpYWxseSBtdWx0aXBsZSBsb2FkZXJzIGZvciBhbGwgbmVzdGVkXG4gICAqIHJvdXRlcy4gIEl0IHJldHVybnMgYSBTdGF0aWNIYW5kbGVyQ29udGV4dCBvYmplY3QsIHdoaWNoIGlzIHZlcnkgc2ltaWxhclxuICAgKiB0byB0aGUgcm91dGVyIHN0YXRlIChsb2NhdGlvbiwgbG9hZGVyRGF0YSwgYWN0aW9uRGF0YSwgZXJyb3JzLCBldGMuKSBhbmRcbiAgICogYWxzbyBhZGRzIFNTUi1zcGVjaWZpYyBpbmZvcm1hdGlvbiBzdWNoIGFzIHRoZSBzdGF0dXNDb2RlIGFuZCBoZWFkZXJzXG4gICAqIGZyb20gYWN0aW9uL2xvYWRlcnMgUmVzcG9uc2VzLlxuICAgKlxuICAgKiBJdCBfc2hvdWxkXyBuZXZlciB0aHJvdyBhbmQgc2hvdWxkIHJlcG9ydCBhbGwgZXJyb3JzIHRocm91Z2ggdGhlXG4gICAqIHJldHVybmVkIGNvbnRleHQuZXJyb3JzIG9iamVjdCwgcHJvcGVybHkgYXNzb2NpYXRpbmcgZXJyb3JzIHRvIHRoZWlyIGVycm9yXG4gICAqIGJvdW5kYXJ5LiAgQWRkaXRpb25hbGx5LCBpdCB0cmFja3MgX2RlZXBlc3RSZW5kZXJlZEJvdW5kYXJ5SWQgd2hpY2ggY2FuIGJlXG4gICAqIHVzZWQgdG8gZW11bGF0ZSBSZWFjdCBlcnJvciBib3VuZGFyaWVzIGR1cmluZyBTU3IgYnkgcGVyZm9ybWluZyBhIHNlY29uZFxuICAgKiBwYXNzIG9ubHkgZG93biB0byB0aGUgYm91bmRhcnlJZC5cbiAgICpcbiAgICogVGhlIG9uZSBleGNlcHRpb24gd2hlcmUgd2UgZG8gbm90IHJldHVybiBhIFN0YXRpY0hhbmRsZXJDb250ZXh0IGlzIHdoZW4gYVxuICAgKiByZWRpcmVjdCByZXNwb25zZSBpcyByZXR1cm5lZCBvciB0aHJvd24gZnJvbSBhbnkgYWN0aW9uL2xvYWRlci4gIFdlXG4gICAqIHByb3BhZ2F0ZSB0aGF0IG91dCBhbmQgcmV0dXJuIHRoZSByYXcgUmVzcG9uc2Ugc28gdGhlIEhUVFAgc2VydmVyIGNhblxuICAgKiByZXR1cm4gaXQgZGlyZWN0bHkuXG4gICAqL1xuICBhc3luYyBmdW5jdGlvbiBxdWVyeShyZXF1ZXN0LCBfdGVtcDIpIHtcbiAgICBsZXQge1xuICAgICAgcmVxdWVzdENvbnRleHRcbiAgICB9ID0gX3RlbXAyID09PSB2b2lkIDAgPyB7fSA6IF90ZW1wMjtcbiAgICBsZXQgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCk7XG4gICAgbGV0IG1ldGhvZCA9IHJlcXVlc3QubWV0aG9kO1xuICAgIGxldCBsb2NhdGlvbiA9IGNyZWF0ZUxvY2F0aW9uKFwiXCIsIGNyZWF0ZVBhdGgodXJsKSwgbnVsbCwgXCJkZWZhdWx0XCIpO1xuICAgIGxldCBtYXRjaGVzID0gbWF0Y2hSb3V0ZXMoZGF0YVJvdXRlcywgbG9jYXRpb24sIGJhc2VuYW1lKTtcbiAgICAvLyBTU1Igc3VwcG9ydHMgSEVBRCByZXF1ZXN0cyB3aGlsZSBTUEEgZG9lc24ndFxuICAgIGlmICghaXNWYWxpZE1ldGhvZChtZXRob2QpICYmIG1ldGhvZCAhPT0gXCJIRUFEXCIpIHtcbiAgICAgIGxldCBlcnJvciA9IGdldEludGVybmFsUm91dGVyRXJyb3IoNDA1LCB7XG4gICAgICAgIG1ldGhvZFxuICAgICAgfSk7XG4gICAgICBsZXQge1xuICAgICAgICBtYXRjaGVzOiBtZXRob2ROb3RBbGxvd2VkTWF0Y2hlcyxcbiAgICAgICAgcm91dGVcbiAgICAgIH0gPSBnZXRTaG9ydENpcmN1aXRNYXRjaGVzKGRhdGFSb3V0ZXMpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYmFzZW5hbWUsXG4gICAgICAgIGxvY2F0aW9uLFxuICAgICAgICBtYXRjaGVzOiBtZXRob2ROb3RBbGxvd2VkTWF0Y2hlcyxcbiAgICAgICAgbG9hZGVyRGF0YToge30sXG4gICAgICAgIGFjdGlvbkRhdGE6IG51bGwsXG4gICAgICAgIGVycm9yczoge1xuICAgICAgICAgIFtyb3V0ZS5pZF06IGVycm9yXG4gICAgICAgIH0sXG4gICAgICAgIHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1cyxcbiAgICAgICAgbG9hZGVySGVhZGVyczoge30sXG4gICAgICAgIGFjdGlvbkhlYWRlcnM6IHt9LFxuICAgICAgICBhY3RpdmVEZWZlcnJlZHM6IG51bGxcbiAgICAgIH07XG4gICAgfSBlbHNlIGlmICghbWF0Y2hlcykge1xuICAgICAgbGV0IGVycm9yID0gZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDQsIHtcbiAgICAgICAgcGF0aG5hbWU6IGxvY2F0aW9uLnBhdGhuYW1lXG4gICAgICB9KTtcbiAgICAgIGxldCB7XG4gICAgICAgIG1hdGNoZXM6IG5vdEZvdW5kTWF0Y2hlcyxcbiAgICAgICAgcm91dGVcbiAgICAgIH0gPSBnZXRTaG9ydENpcmN1aXRNYXRjaGVzKGRhdGFSb3V0ZXMpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYmFzZW5hbWUsXG4gICAgICAgIGxvY2F0aW9uLFxuICAgICAgICBtYXRjaGVzOiBub3RGb3VuZE1hdGNoZXMsXG4gICAgICAgIGxvYWRlckRhdGE6IHt9LFxuICAgICAgICBhY3Rpb25EYXRhOiBudWxsLFxuICAgICAgICBlcnJvcnM6IHtcbiAgICAgICAgICBbcm91dGUuaWRdOiBlcnJvclxuICAgICAgICB9LFxuICAgICAgICBzdGF0dXNDb2RlOiBlcnJvci5zdGF0dXMsXG4gICAgICAgIGxvYWRlckhlYWRlcnM6IHt9LFxuICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fSxcbiAgICAgICAgYWN0aXZlRGVmZXJyZWRzOiBudWxsXG4gICAgICB9O1xuICAgIH1cbiAgICBsZXQgcmVzdWx0ID0gYXdhaXQgcXVlcnlJbXBsKHJlcXVlc3QsIGxvY2F0aW9uLCBtYXRjaGVzLCByZXF1ZXN0Q29udGV4dCk7XG4gICAgaWYgKGlzUmVzcG9uc2UocmVzdWx0KSkge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gICAgLy8gV2hlbiByZXR1cm5pbmcgU3RhdGljSGFuZGxlckNvbnRleHQsIHdlIHBhdGNoIGJhY2sgaW4gdGhlIGxvY2F0aW9uIGhlcmVcbiAgICAvLyBzaW5jZSB3ZSBuZWVkIGl0IGZvciBSZWFjdCBDb250ZXh0LiAgQnV0IHRoaXMgaGVscHMga2VlcCBvdXIgc3VibWl0IGFuZFxuICAgIC8vIGxvYWRSb3V0ZURhdGEgb3BlcmF0aW5nIG9uIGEgUmVxdWVzdCBpbnN0ZWFkIG9mIGEgTG9jYXRpb25cbiAgICByZXR1cm4gX2V4dGVuZHMoe1xuICAgICAgbG9jYXRpb24sXG4gICAgICBiYXNlbmFtZVxuICAgIH0sIHJlc3VsdCk7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBxdWVyeVJvdXRlKCkgbWV0aG9kIGlzIGludGVuZGVkIGZvciB0YXJnZXRlZCByb3V0ZSByZXF1ZXN0cywgZWl0aGVyXG4gICAqIGZvciBmZXRjaCA/X2RhdGEgcmVxdWVzdHMgb3IgcmVzb3VyY2Ugcm91dGUgcmVxdWVzdHMuICBJbiB0aGlzIGNhc2UsIHdlXG4gICAqIGFyZSBvbmx5IGV2ZXIgY2FsbGluZyBhIHNpbmdsZSBhY3Rpb24gb3IgbG9hZGVyLCBhbmQgd2UgYXJlIHJldHVybmluZyB0aGVcbiAgICogcmV0dXJuZWQgdmFsdWUgZGlyZWN0bHkuICBJbiBtb3N0IGNhc2VzLCB0aGlzIHdpbGwgYmUgYSBSZXNwb25zZSByZXR1cm5lZFxuICAgKiBmcm9tIHRoZSBhY3Rpb24vbG9hZGVyLCBidXQgaXQgbWF5IGJlIGEgcHJpbWl0aXZlIG9yIG90aGVyIHZhbHVlIGFzIHdlbGwgLVxuICAgKiBhbmQgaW4gc3VjaCBjYXNlcyB0aGUgY2FsbGluZyBjb250ZXh0IHNob3VsZCBoYW5kbGUgdGhhdCBhY2NvcmRpbmdseS5cbiAgICpcbiAgICogV2UgZG8gcmVzcGVjdCB0aGUgdGhyb3cvcmV0dXJuIGRpZmZlcmVudGlhdGlvbiwgc28gaWYgYW4gYWN0aW9uL2xvYWRlclxuICAgKiB0aHJvd3MsIHRoZW4gdGhpcyBtZXRob2Qgd2lsbCB0aHJvdyB0aGUgdmFsdWUuICBUaGlzIGlzIGltcG9ydGFudCBzbyB3ZVxuICAgKiBjYW4gZG8gcHJvcGVyIGJvdW5kYXJ5IGlkZW50aWZpY2F0aW9uIGluIFJlbWl4IHdoZXJlIGEgdGhyb3duIFJlc3BvbnNlXG4gICAqIG11c3QgZ28gdG8gdGhlIENhdGNoIEJvdW5kYXJ5IGJ1dCBhIHJldHVybmVkIFJlc3BvbnNlIGlzIGhhcHB5LXBhdGguXG4gICAqXG4gICAqIE9uZSB0aGluZyB0byBub3RlIGlzIHRoYXQgYW55IFJvdXRlci1pbml0aWF0ZWQgRXJyb3JzIHRoYXQgbWFrZSBzZW5zZVxuICAgKiB0byBhc3NvY2lhdGUgd2l0aCBhIHN0YXR1cyBjb2RlIHdpbGwgYmUgdGhyb3duIGFzIGFuIEVycm9yUmVzcG9uc2VcbiAgICogaW5zdGFuY2Ugd2hpY2ggaW5jbHVkZSB0aGUgcmF3IEVycm9yLCBzdWNoIHRoYXQgdGhlIGNhbGxpbmcgY29udGV4dCBjYW5cbiAgICogc2VyaWFsaXplIHRoZSBlcnJvciBhcyB0aGV5IHNlZSBmaXQgd2hpbGUgaW5jbHVkaW5nIHRoZSBwcm9wZXIgcmVzcG9uc2VcbiAgICogY29kZS4gIEV4YW1wbGVzIGhlcmUgYXJlIDQwNCBhbmQgNDA1IGVycm9ycyB0aGF0IG9jY3VyIHByaW9yIHRvIHJlYWNoaW5nXG4gICAqIGFueSB1c2VyLWRlZmluZWQgbG9hZGVycy5cbiAgICovXG4gIGFzeW5jIGZ1bmN0aW9uIHF1ZXJ5Um91dGUocmVxdWVzdCwgX3RlbXAzKSB7XG4gICAgbGV0IHtcbiAgICAgIHJvdXRlSWQsXG4gICAgICByZXF1ZXN0Q29udGV4dFxuICAgIH0gPSBfdGVtcDMgPT09IHZvaWQgMCA/IHt9IDogX3RlbXAzO1xuICAgIGxldCB1cmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsKTtcbiAgICBsZXQgbWV0aG9kID0gcmVxdWVzdC5tZXRob2Q7XG4gICAgbGV0IGxvY2F0aW9uID0gY3JlYXRlTG9jYXRpb24oXCJcIiwgY3JlYXRlUGF0aCh1cmwpLCBudWxsLCBcImRlZmF1bHRcIik7XG4gICAgbGV0IG1hdGNoZXMgPSBtYXRjaFJvdXRlcyhkYXRhUm91dGVzLCBsb2NhdGlvbiwgYmFzZW5hbWUpO1xuICAgIC8vIFNTUiBzdXBwb3J0cyBIRUFEIHJlcXVlc3RzIHdoaWxlIFNQQSBkb2Vzbid0XG4gICAgaWYgKCFpc1ZhbGlkTWV0aG9kKG1ldGhvZCkgJiYgbWV0aG9kICE9PSBcIkhFQURcIiAmJiBtZXRob2QgIT09IFwiT1BUSU9OU1wiKSB7XG4gICAgICB0aHJvdyBnZXRJbnRlcm5hbFJvdXRlckVycm9yKDQwNSwge1xuICAgICAgICBtZXRob2RcbiAgICAgIH0pO1xuICAgIH0gZWxzZSBpZiAoIW1hdGNoZXMpIHtcbiAgICAgIHRocm93IGdldEludGVybmFsUm91dGVyRXJyb3IoNDA0LCB7XG4gICAgICAgIHBhdGhuYW1lOiBsb2NhdGlvbi5wYXRobmFtZVxuICAgICAgfSk7XG4gICAgfVxuICAgIGxldCBtYXRjaCA9IHJvdXRlSWQgPyBtYXRjaGVzLmZpbmQobSA9PiBtLnJvdXRlLmlkID09PSByb3V0ZUlkKSA6IGdldFRhcmdldE1hdGNoKG1hdGNoZXMsIGxvY2F0aW9uKTtcbiAgICBpZiAocm91dGVJZCAmJiAhbWF0Y2gpIHtcbiAgICAgIHRocm93IGdldEludGVybmFsUm91dGVyRXJyb3IoNDAzLCB7XG4gICAgICAgIHBhdGhuYW1lOiBsb2NhdGlvbi5wYXRobmFtZSxcbiAgICAgICAgcm91dGVJZFxuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmICghbWF0Y2gpIHtcbiAgICAgIC8vIFRoaXMgc2hvdWxkIG5ldmVyIGhpdCBJIGRvbid0IHRoaW5rP1xuICAgICAgdGhyb3cgZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDQsIHtcbiAgICAgICAgcGF0aG5hbWU6IGxvY2F0aW9uLnBhdGhuYW1lXG4gICAgICB9KTtcbiAgICB9XG4gICAgbGV0IHJlc3VsdCA9IGF3YWl0IHF1ZXJ5SW1wbChyZXF1ZXN0LCBsb2NhdGlvbiwgbWF0Y2hlcywgcmVxdWVzdENvbnRleHQsIG1hdGNoKTtcbiAgICBpZiAoaXNSZXNwb25zZShyZXN1bHQpKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBsZXQgZXJyb3IgPSByZXN1bHQuZXJyb3JzID8gT2JqZWN0LnZhbHVlcyhyZXN1bHQuZXJyb3JzKVswXSA6IHVuZGVmaW5lZDtcbiAgICBpZiAoZXJyb3IgIT09IHVuZGVmaW5lZCkge1xuICAgICAgLy8gSWYgd2UgZ290IGJhY2sgcmVzdWx0LmVycm9ycywgdGhhdCBtZWFucyB0aGUgbG9hZGVyL2FjdGlvbiB0aHJld1xuICAgICAgLy8gX3NvbWV0aGluZ18gdGhhdCB3YXNuJ3QgYSBSZXNwb25zZSwgYnV0IGl0J3Mgbm90IGd1YXJhbnRlZWQvcmVxdWlyZWRcbiAgICAgIC8vIHRvIGJlIGFuIGBpbnN0YW5jZW9mIEVycm9yYCBlaXRoZXIsIHNvIHdlIGhhdmUgdG8gdXNlIHRocm93IGhlcmUgdG9cbiAgICAgIC8vIHByZXNlcnZlIHRoZSBcImVycm9yXCIgc3RhdGUgb3V0c2lkZSBvZiBxdWVyeUltcGwuXG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gICAgLy8gUGljayBvZmYgdGhlIHJpZ2h0IHN0YXRlIHZhbHVlIHRvIHJldHVyblxuICAgIGlmIChyZXN1bHQuYWN0aW9uRGF0YSkge1xuICAgICAgcmV0dXJuIE9iamVjdC52YWx1ZXMocmVzdWx0LmFjdGlvbkRhdGEpWzBdO1xuICAgIH1cbiAgICBpZiAocmVzdWx0LmxvYWRlckRhdGEpIHtcbiAgICAgIHZhciBfcmVzdWx0JGFjdGl2ZURlZmVycmU7XG4gICAgICBsZXQgZGF0YSA9IE9iamVjdC52YWx1ZXMocmVzdWx0LmxvYWRlckRhdGEpWzBdO1xuICAgICAgaWYgKChfcmVzdWx0JGFjdGl2ZURlZmVycmUgPSByZXN1bHQuYWN0aXZlRGVmZXJyZWRzKSAhPSBudWxsICYmIF9yZXN1bHQkYWN0aXZlRGVmZXJyZVttYXRjaC5yb3V0ZS5pZF0pIHtcbiAgICAgICAgZGF0YVtVTlNBRkVfREVGRVJSRURfU1lNQk9MXSA9IHJlc3VsdC5hY3RpdmVEZWZlcnJlZHNbbWF0Y2gucm91dGUuaWRdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfVxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgYXN5bmMgZnVuY3Rpb24gcXVlcnlJbXBsKHJlcXVlc3QsIGxvY2F0aW9uLCBtYXRjaGVzLCByZXF1ZXN0Q29udGV4dCwgcm91dGVNYXRjaCkge1xuICAgIGludmFyaWFudChyZXF1ZXN0LnNpZ25hbCwgXCJxdWVyeSgpL3F1ZXJ5Um91dGUoKSByZXF1ZXN0cyBtdXN0IGNvbnRhaW4gYW4gQWJvcnRDb250cm9sbGVyIHNpZ25hbFwiKTtcbiAgICB0cnkge1xuICAgICAgaWYgKGlzTXV0YXRpb25NZXRob2QocmVxdWVzdC5tZXRob2QudG9Mb3dlckNhc2UoKSkpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IGF3YWl0IHN1Ym1pdChyZXF1ZXN0LCBtYXRjaGVzLCByb3V0ZU1hdGNoIHx8IGdldFRhcmdldE1hdGNoKG1hdGNoZXMsIGxvY2F0aW9uKSwgcmVxdWVzdENvbnRleHQsIHJvdXRlTWF0Y2ggIT0gbnVsbCk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9XG4gICAgICBsZXQgcmVzdWx0ID0gYXdhaXQgbG9hZFJvdXRlRGF0YShyZXF1ZXN0LCBtYXRjaGVzLCByZXF1ZXN0Q29udGV4dCwgcm91dGVNYXRjaCk7XG4gICAgICByZXR1cm4gaXNSZXNwb25zZShyZXN1bHQpID8gcmVzdWx0IDogX2V4dGVuZHMoe30sIHJlc3VsdCwge1xuICAgICAgICBhY3Rpb25EYXRhOiBudWxsLFxuICAgICAgICBhY3Rpb25IZWFkZXJzOiB7fVxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gSWYgdGhlIHVzZXIgdGhyZXcvcmV0dXJuZWQgYSBSZXNwb25zZSBpbiBjYWxsTG9hZGVyT3JBY3Rpb24sIHdlIHRocm93XG4gICAgICAvLyBpdCB0byBiYWlsIG91dCBhbmQgdGhlbiByZXR1cm4gb3IgdGhyb3cgaGVyZSBiYXNlZCBvbiB3aGV0aGVyIHRoZSB1c2VyXG4gICAgICAvLyByZXR1cm5lZCBvciB0aHJld1xuICAgICAgaWYgKGlzUXVlcnlSb3V0ZVJlc3BvbnNlKGUpKSB7XG4gICAgICAgIGlmIChlLnR5cGUgPT09IFJlc3VsdFR5cGUuZXJyb3IgJiYgIWlzUmVkaXJlY3RSZXNwb25zZShlLnJlc3BvbnNlKSkge1xuICAgICAgICAgIHRocm93IGUucmVzcG9uc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGUucmVzcG9uc2U7XG4gICAgICB9XG4gICAgICAvLyBSZWRpcmVjdHMgYXJlIGFsd2F5cyByZXR1cm5lZCBzaW5jZSB0aGV5IGRvbid0IHByb3BhZ2F0ZSB0byBjYXRjaFxuICAgICAgLy8gYm91bmRhcmllc1xuICAgICAgaWYgKGlzUmVkaXJlY3RSZXNwb25zZShlKSkge1xuICAgICAgICByZXR1cm4gZTtcbiAgICAgIH1cbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIHN1Ym1pdChyZXF1ZXN0LCBtYXRjaGVzLCBhY3Rpb25NYXRjaCwgcmVxdWVzdENvbnRleHQsIGlzUm91dGVSZXF1ZXN0KSB7XG4gICAgbGV0IHJlc3VsdDtcbiAgICBpZiAoIWFjdGlvbk1hdGNoLnJvdXRlLmFjdGlvbiAmJiAhYWN0aW9uTWF0Y2gucm91dGUubGF6eSkge1xuICAgICAgbGV0IGVycm9yID0gZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDUsIHtcbiAgICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgICAgcGF0aG5hbWU6IG5ldyBVUkwocmVxdWVzdC51cmwpLnBhdGhuYW1lLFxuICAgICAgICByb3V0ZUlkOiBhY3Rpb25NYXRjaC5yb3V0ZS5pZFxuICAgICAgfSk7XG4gICAgICBpZiAoaXNSb3V0ZVJlcXVlc3QpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICByZXN1bHQgPSB7XG4gICAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZXJyb3IsXG4gICAgICAgIGVycm9yXG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXN1bHQgPSBhd2FpdCBjYWxsTG9hZGVyT3JBY3Rpb24oXCJhY3Rpb25cIiwgcmVxdWVzdCwgYWN0aW9uTWF0Y2gsIG1hdGNoZXMsIG1hbmlmZXN0LCBtYXBSb3V0ZVByb3BlcnRpZXMsIGJhc2VuYW1lLCB7XG4gICAgICAgIGlzU3RhdGljUmVxdWVzdDogdHJ1ZSxcbiAgICAgICAgaXNSb3V0ZVJlcXVlc3QsXG4gICAgICAgIHJlcXVlc3RDb250ZXh0XG4gICAgICB9KTtcbiAgICAgIGlmIChyZXF1ZXN0LnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgIGxldCBtZXRob2QgPSBpc1JvdXRlUmVxdWVzdCA/IFwicXVlcnlSb3V0ZVwiIDogXCJxdWVyeVwiO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IobWV0aG9kICsgXCIoKSBjYWxsIGFib3J0ZWRcIik7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIC8vIFVoaGhoIC0gdGhpcyBzaG91bGQgbmV2ZXIgaGFwcGVuLCB3ZSBzaG91bGQgYWx3YXlzIHRocm93IHRoZXNlIGZyb21cbiAgICAgIC8vIGNhbGxMb2FkZXJPckFjdGlvbiwgYnV0IHRoZSB0eXBlIG5hcnJvd2luZyBoZXJlIGtlZXBzIFRTIGhhcHB5IGFuZCB3ZVxuICAgICAgLy8gY2FuIGdldCBiYWNrIG9uIHRoZSBcInRocm93IGFsbCByZWRpcmVjdCByZXNwb25zZXNcIiB0cmFpbiBoZXJlIHNob3VsZFxuICAgICAgLy8gdGhpcyBldmVyIGhhcHBlbiA6L1xuICAgICAgdGhyb3cgbmV3IFJlc3BvbnNlKG51bGwsIHtcbiAgICAgICAgc3RhdHVzOiByZXN1bHQuc3RhdHVzLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgTG9jYXRpb246IHJlc3VsdC5sb2NhdGlvblxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSkge1xuICAgICAgbGV0IGVycm9yID0gZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDAsIHtcbiAgICAgICAgdHlwZTogXCJkZWZlci1hY3Rpb25cIlxuICAgICAgfSk7XG4gICAgICBpZiAoaXNSb3V0ZVJlcXVlc3QpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICByZXN1bHQgPSB7XG4gICAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZXJyb3IsXG4gICAgICAgIGVycm9yXG4gICAgICB9O1xuICAgIH1cbiAgICBpZiAoaXNSb3V0ZVJlcXVlc3QpIHtcbiAgICAgIC8vIE5vdGU6IFRoaXMgc2hvdWxkIG9ubHkgYmUgbm9uLVJlc3BvbnNlIHZhbHVlcyBpZiB3ZSBnZXQgaGVyZSwgc2luY2VcbiAgICAgIC8vIGlzUm91dGVSZXF1ZXN0IHNob3VsZCB0aHJvdyBhbnkgUmVzcG9uc2UgcmVjZWl2ZWQgaW4gY2FsbExvYWRlck9yQWN0aW9uXG4gICAgICBpZiAoaXNFcnJvclJlc3VsdChyZXN1bHQpKSB7XG4gICAgICAgIHRocm93IHJlc3VsdC5lcnJvcjtcbiAgICAgIH1cbiAgICAgIHJldHVybiB7XG4gICAgICAgIG1hdGNoZXM6IFthY3Rpb25NYXRjaF0sXG4gICAgICAgIGxvYWRlckRhdGE6IHt9LFxuICAgICAgICBhY3Rpb25EYXRhOiB7XG4gICAgICAgICAgW2FjdGlvbk1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmRhdGFcbiAgICAgICAgfSxcbiAgICAgICAgZXJyb3JzOiBudWxsLFxuICAgICAgICAvLyBOb3RlOiBzdGF0dXNDb2RlICsgaGVhZGVycyBhcmUgdW51c2VkIGhlcmUgc2luY2UgcXVlcnlSb3V0ZSB3aWxsXG4gICAgICAgIC8vIHJldHVybiB0aGUgcmF3IFJlc3BvbnNlIG9yIHZhbHVlXG4gICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcbiAgICAgICAgbG9hZGVySGVhZGVyczoge30sXG4gICAgICAgIGFjdGlvbkhlYWRlcnM6IHt9LFxuICAgICAgICBhY3RpdmVEZWZlcnJlZHM6IG51bGxcbiAgICAgIH07XG4gICAgfVxuICAgIGlmIChpc0Vycm9yUmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIC8vIFN0b3JlIG9mZiB0aGUgcGVuZGluZyBlcnJvciAtIHdlIHVzZSBpdCB0byBkZXRlcm1pbmUgd2hpY2ggbG9hZGVyc1xuICAgICAgLy8gdG8gY2FsbCBhbmQgd2lsbCBjb21taXQgaXQgd2hlbiB3ZSBjb21wbGV0ZSB0aGUgbmF2aWdhdGlvblxuICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIGFjdGlvbk1hdGNoLnJvdXRlLmlkKTtcbiAgICAgIGxldCBjb250ZXh0ID0gYXdhaXQgbG9hZFJvdXRlRGF0YShyZXF1ZXN0LCBtYXRjaGVzLCByZXF1ZXN0Q29udGV4dCwgdW5kZWZpbmVkLCB7XG4gICAgICAgIFtib3VuZGFyeU1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmVycm9yXG4gICAgICB9KTtcbiAgICAgIC8vIGFjdGlvbiBzdGF0dXMgY29kZXMgdGFrZSBwcmVjZWRlbmNlIG92ZXIgbG9hZGVyIHN0YXR1cyBjb2Rlc1xuICAgICAgcmV0dXJuIF9leHRlbmRzKHt9LCBjb250ZXh0LCB7XG4gICAgICAgIHN0YXR1c0NvZGU6IGlzUm91dGVFcnJvclJlc3BvbnNlKHJlc3VsdC5lcnJvcikgPyByZXN1bHQuZXJyb3Iuc3RhdHVzIDogNTAwLFxuICAgICAgICBhY3Rpb25EYXRhOiBudWxsLFxuICAgICAgICBhY3Rpb25IZWFkZXJzOiBfZXh0ZW5kcyh7fSwgcmVzdWx0LmhlYWRlcnMgPyB7XG4gICAgICAgICAgW2FjdGlvbk1hdGNoLnJvdXRlLmlkXTogcmVzdWx0LmhlYWRlcnNcbiAgICAgICAgfSA6IHt9KVxuICAgICAgfSk7XG4gICAgfVxuICAgIC8vIENyZWF0ZSBhIEdFVCByZXF1ZXN0IGZvciB0aGUgbG9hZGVyc1xuICAgIGxldCBsb2FkZXJSZXF1ZXN0ID0gbmV3IFJlcXVlc3QocmVxdWVzdC51cmwsIHtcbiAgICAgIGhlYWRlcnM6IHJlcXVlc3QuaGVhZGVycyxcbiAgICAgIHJlZGlyZWN0OiByZXF1ZXN0LnJlZGlyZWN0LFxuICAgICAgc2lnbmFsOiByZXF1ZXN0LnNpZ25hbFxuICAgIH0pO1xuICAgIGxldCBjb250ZXh0ID0gYXdhaXQgbG9hZFJvdXRlRGF0YShsb2FkZXJSZXF1ZXN0LCBtYXRjaGVzLCByZXF1ZXN0Q29udGV4dCk7XG4gICAgcmV0dXJuIF9leHRlbmRzKHt9LCBjb250ZXh0LCByZXN1bHQuc3RhdHVzQ29kZSA/IHtcbiAgICAgIHN0YXR1c0NvZGU6IHJlc3VsdC5zdGF0dXNDb2RlXG4gICAgfSA6IHt9LCB7XG4gICAgICBhY3Rpb25EYXRhOiB7XG4gICAgICAgIFthY3Rpb25NYXRjaC5yb3V0ZS5pZF06IHJlc3VsdC5kYXRhXG4gICAgICB9LFxuICAgICAgYWN0aW9uSGVhZGVyczogX2V4dGVuZHMoe30sIHJlc3VsdC5oZWFkZXJzID8ge1xuICAgICAgICBbYWN0aW9uTWF0Y2gucm91dGUuaWRdOiByZXN1bHQuaGVhZGVyc1xuICAgICAgfSA6IHt9KVxuICAgIH0pO1xuICB9XG4gIGFzeW5jIGZ1bmN0aW9uIGxvYWRSb3V0ZURhdGEocmVxdWVzdCwgbWF0Y2hlcywgcmVxdWVzdENvbnRleHQsIHJvdXRlTWF0Y2gsIHBlbmRpbmdBY3Rpb25FcnJvcikge1xuICAgIGxldCBpc1JvdXRlUmVxdWVzdCA9IHJvdXRlTWF0Y2ggIT0gbnVsbDtcbiAgICAvLyBTaG9ydCBjaXJjdWl0IGlmIHdlIGhhdmUgbm8gbG9hZGVycyB0byBydW4gKHF1ZXJ5Um91dGUoKSlcbiAgICBpZiAoaXNSb3V0ZVJlcXVlc3QgJiYgIShyb3V0ZU1hdGNoICE9IG51bGwgJiYgcm91dGVNYXRjaC5yb3V0ZS5sb2FkZXIpICYmICEocm91dGVNYXRjaCAhPSBudWxsICYmIHJvdXRlTWF0Y2gucm91dGUubGF6eSkpIHtcbiAgICAgIHRocm93IGdldEludGVybmFsUm91dGVyRXJyb3IoNDAwLCB7XG4gICAgICAgIG1ldGhvZDogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgIHBhdGhuYW1lOiBuZXcgVVJMKHJlcXVlc3QudXJsKS5wYXRobmFtZSxcbiAgICAgICAgcm91dGVJZDogcm91dGVNYXRjaCA9PSBudWxsID8gdm9pZCAwIDogcm91dGVNYXRjaC5yb3V0ZS5pZFxuICAgICAgfSk7XG4gICAgfVxuICAgIGxldCByZXF1ZXN0TWF0Y2hlcyA9IHJvdXRlTWF0Y2ggPyBbcm91dGVNYXRjaF0gOiBnZXRMb2FkZXJNYXRjaGVzVW50aWxCb3VuZGFyeShtYXRjaGVzLCBPYmplY3Qua2V5cyhwZW5kaW5nQWN0aW9uRXJyb3IgfHwge30pWzBdKTtcbiAgICBsZXQgbWF0Y2hlc1RvTG9hZCA9IHJlcXVlc3RNYXRjaGVzLmZpbHRlcihtID0+IG0ucm91dGUubG9hZGVyIHx8IG0ucm91dGUubGF6eSk7XG4gICAgLy8gU2hvcnQgY2lyY3VpdCBpZiB3ZSBoYXZlIG5vIGxvYWRlcnMgdG8gcnVuIChxdWVyeSgpKVxuICAgIGlmIChtYXRjaGVzVG9Mb2FkLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbWF0Y2hlcyxcbiAgICAgICAgLy8gQWRkIGEgbnVsbCBmb3IgYWxsIG1hdGNoZWQgcm91dGVzIGZvciBwcm9wZXIgcmV2YWxpZGF0aW9uIG9uIHRoZSBjbGllbnRcbiAgICAgICAgbG9hZGVyRGF0YTogbWF0Y2hlcy5yZWR1Y2UoKGFjYywgbSkgPT4gT2JqZWN0LmFzc2lnbihhY2MsIHtcbiAgICAgICAgICBbbS5yb3V0ZS5pZF06IG51bGxcbiAgICAgICAgfSksIHt9KSxcbiAgICAgICAgZXJyb3JzOiBwZW5kaW5nQWN0aW9uRXJyb3IgfHwgbnVsbCxcbiAgICAgICAgc3RhdHVzQ29kZTogMjAwLFxuICAgICAgICBsb2FkZXJIZWFkZXJzOiB7fSxcbiAgICAgICAgYWN0aXZlRGVmZXJyZWRzOiBudWxsXG4gICAgICB9O1xuICAgIH1cbiAgICBsZXQgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKFsuLi5tYXRjaGVzVG9Mb2FkLm1hcChtYXRjaCA9PiBjYWxsTG9hZGVyT3JBY3Rpb24oXCJsb2FkZXJcIiwgcmVxdWVzdCwgbWF0Y2gsIG1hdGNoZXMsIG1hbmlmZXN0LCBtYXBSb3V0ZVByb3BlcnRpZXMsIGJhc2VuYW1lLCB7XG4gICAgICBpc1N0YXRpY1JlcXVlc3Q6IHRydWUsXG4gICAgICBpc1JvdXRlUmVxdWVzdCxcbiAgICAgIHJlcXVlc3RDb250ZXh0XG4gICAgfSkpXSk7XG4gICAgaWYgKHJlcXVlc3Quc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgIGxldCBtZXRob2QgPSBpc1JvdXRlUmVxdWVzdCA/IFwicXVlcnlSb3V0ZVwiIDogXCJxdWVyeVwiO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKG1ldGhvZCArIFwiKCkgY2FsbCBhYm9ydGVkXCIpO1xuICAgIH1cbiAgICAvLyBQcm9jZXNzIGFuZCBjb21taXQgb3V0cHV0IGZyb20gbG9hZGVyc1xuICAgIGxldCBhY3RpdmVEZWZlcnJlZHMgPSBuZXcgTWFwKCk7XG4gICAgbGV0IGNvbnRleHQgPSBwcm9jZXNzUm91dGVMb2FkZXJEYXRhKG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdBY3Rpb25FcnJvciwgYWN0aXZlRGVmZXJyZWRzKTtcbiAgICAvLyBBZGQgYSBudWxsIGZvciBhbnkgbm9uLWxvYWRlciBtYXRjaGVzIGZvciBwcm9wZXIgcmV2YWxpZGF0aW9uIG9uIHRoZSBjbGllbnRcbiAgICBsZXQgZXhlY3V0ZWRMb2FkZXJzID0gbmV3IFNldChtYXRjaGVzVG9Mb2FkLm1hcChtYXRjaCA9PiBtYXRjaC5yb3V0ZS5pZCkpO1xuICAgIG1hdGNoZXMuZm9yRWFjaChtYXRjaCA9PiB7XG4gICAgICBpZiAoIWV4ZWN1dGVkTG9hZGVycy5oYXMobWF0Y2gucm91dGUuaWQpKSB7XG4gICAgICAgIGNvbnRleHQubG9hZGVyRGF0YVttYXRjaC5yb3V0ZS5pZF0gPSBudWxsO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBfZXh0ZW5kcyh7fSwgY29udGV4dCwge1xuICAgICAgbWF0Y2hlcyxcbiAgICAgIGFjdGl2ZURlZmVycmVkczogYWN0aXZlRGVmZXJyZWRzLnNpemUgPiAwID8gT2JqZWN0LmZyb21FbnRyaWVzKGFjdGl2ZURlZmVycmVkcy5lbnRyaWVzKCkpIDogbnVsbFxuICAgIH0pO1xuICB9XG4gIHJldHVybiB7XG4gICAgZGF0YVJvdXRlcyxcbiAgICBxdWVyeSxcbiAgICBxdWVyeVJvdXRlXG4gIH07XG59XG4vLyNlbmRyZWdpb25cbi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4vLyNyZWdpb24gSGVscGVyc1xuLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbi8qKlxuICogR2l2ZW4gYW4gZXhpc3RpbmcgU3RhdGljSGFuZGxlckNvbnRleHQgYW5kIGFuIGVycm9yIHRocm93biBhdCByZW5kZXIgdGltZSxcbiAqIHByb3ZpZGUgYW4gdXBkYXRlZCBTdGF0aWNIYW5kbGVyQ29udGV4dCBzdWl0YWJsZSBmb3IgYSBzZWNvbmQgU1NSIHJlbmRlclxuICovXG5mdW5jdGlvbiBnZXRTdGF0aWNDb250ZXh0RnJvbUVycm9yKHJvdXRlcywgY29udGV4dCwgZXJyb3IpIHtcbiAgbGV0IG5ld0NvbnRleHQgPSBfZXh0ZW5kcyh7fSwgY29udGV4dCwge1xuICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICBlcnJvcnM6IHtcbiAgICAgIFtjb250ZXh0Ll9kZWVwZXN0UmVuZGVyZWRCb3VuZGFyeUlkIHx8IHJvdXRlc1swXS5pZF06IGVycm9yXG4gICAgfVxuICB9KTtcbiAgcmV0dXJuIG5ld0NvbnRleHQ7XG59XG5mdW5jdGlvbiBpc1N1Ym1pc3Npb25OYXZpZ2F0aW9uKG9wdHMpIHtcbiAgcmV0dXJuIG9wdHMgIT0gbnVsbCAmJiAoXCJmb3JtRGF0YVwiIGluIG9wdHMgJiYgb3B0cy5mb3JtRGF0YSAhPSBudWxsIHx8IFwiYm9keVwiIGluIG9wdHMgJiYgb3B0cy5ib2R5ICE9PSB1bmRlZmluZWQpO1xufVxuZnVuY3Rpb24gbm9ybWFsaXplVG8obG9jYXRpb24sIG1hdGNoZXMsIGJhc2VuYW1lLCBwcmVwZW5kQmFzZW5hbWUsIHRvLCBmcm9tUm91dGVJZCwgcmVsYXRpdmUpIHtcbiAgbGV0IGNvbnRleHR1YWxNYXRjaGVzO1xuICBsZXQgYWN0aXZlUm91dGVNYXRjaDtcbiAgaWYgKGZyb21Sb3V0ZUlkICE9IG51bGwgJiYgcmVsYXRpdmUgIT09IFwicGF0aFwiKSB7XG4gICAgLy8gR3JhYiBtYXRjaGVzIHVwIHRvIHRoZSBjYWxsaW5nIHJvdXRlIHNvIG91ciByb3V0ZS1yZWxhdGl2ZSBsb2dpYyBpc1xuICAgIC8vIHJlbGF0aXZlIHRvIHRoZSBjb3JyZWN0IHNvdXJjZSByb3V0ZS4gIFdoZW4gdXNpbmcgcmVsYXRpdmU6cGF0aCxcbiAgICAvLyBmcm9tUm91dGVJZCBpcyBpZ25vcmVkIHNpbmNlIHRoYXQgaXMgYWx3YXlzIHJlbGF0aXZlIHRvIHRoZSBjdXJyZW50XG4gICAgLy8gbG9jYXRpb24gcGF0aFxuICAgIGNvbnRleHR1YWxNYXRjaGVzID0gW107XG4gICAgZm9yIChsZXQgbWF0Y2ggb2YgbWF0Y2hlcykge1xuICAgICAgY29udGV4dHVhbE1hdGNoZXMucHVzaChtYXRjaCk7XG4gICAgICBpZiAobWF0Y2gucm91dGUuaWQgPT09IGZyb21Sb3V0ZUlkKSB7XG4gICAgICAgIGFjdGl2ZVJvdXRlTWF0Y2ggPSBtYXRjaDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGNvbnRleHR1YWxNYXRjaGVzID0gbWF0Y2hlcztcbiAgICBhY3RpdmVSb3V0ZU1hdGNoID0gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdO1xuICB9XG4gIC8vIFJlc29sdmUgdGhlIHJlbGF0aXZlIHBhdGhcbiAgbGV0IHBhdGggPSByZXNvbHZlVG8odG8gPyB0byA6IFwiLlwiLCBnZXRQYXRoQ29udHJpYnV0aW5nTWF0Y2hlcyhjb250ZXh0dWFsTWF0Y2hlcykubWFwKG0gPT4gbS5wYXRobmFtZUJhc2UpLCBzdHJpcEJhc2VuYW1lKGxvY2F0aW9uLnBhdGhuYW1lLCBiYXNlbmFtZSkgfHwgbG9jYXRpb24ucGF0aG5hbWUsIHJlbGF0aXZlID09PSBcInBhdGhcIik7XG4gIC8vIFdoZW4gYHRvYCBpcyBub3Qgc3BlY2lmaWVkIHdlIGluaGVyaXQgc2VhcmNoL2hhc2ggZnJvbSB0aGUgY3VycmVudFxuICAvLyBsb2NhdGlvbiwgdW5saWtlIHdoZW4gdG89XCIuXCIgYW5kIHdlIGp1c3QgaW5oZXJpdCB0aGUgcGF0aC5cbiAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9yZW1peC1ydW4vcmVtaXgvaXNzdWVzLzkyN1xuICBpZiAodG8gPT0gbnVsbCkge1xuICAgIHBhdGguc2VhcmNoID0gbG9jYXRpb24uc2VhcmNoO1xuICAgIHBhdGguaGFzaCA9IGxvY2F0aW9uLmhhc2g7XG4gIH1cbiAgLy8gQWRkIGFuID9pbmRleCBwYXJhbSBmb3IgbWF0Y2hlZCBpbmRleCByb3V0ZXMgaWYgd2UgZG9uJ3QgYWxyZWFkeSBoYXZlIG9uZVxuICBpZiAoKHRvID09IG51bGwgfHwgdG8gPT09IFwiXCIgfHwgdG8gPT09IFwiLlwiKSAmJiBhY3RpdmVSb3V0ZU1hdGNoICYmIGFjdGl2ZVJvdXRlTWF0Y2gucm91dGUuaW5kZXggJiYgIWhhc05ha2VkSW5kZXhRdWVyeShwYXRoLnNlYXJjaCkpIHtcbiAgICBwYXRoLnNlYXJjaCA9IHBhdGguc2VhcmNoID8gcGF0aC5zZWFyY2gucmVwbGFjZSgvXlxcPy8sIFwiP2luZGV4JlwiKSA6IFwiP2luZGV4XCI7XG4gIH1cbiAgLy8gSWYgd2UncmUgb3BlcmF0aW5nIHdpdGhpbiBhIGJhc2VuYW1lLCBwcmVwZW5kIGl0IHRvIHRoZSBwYXRobmFtZS4gIElmXG4gIC8vIHRoaXMgaXMgYSByb290IG5hdmlnYXRpb24sIHRoZW4ganVzdCB1c2UgdGhlIHJhdyBiYXNlbmFtZSB3aGljaCBhbGxvd3NcbiAgLy8gdGhlIGJhc2VuYW1lIHRvIGhhdmUgZnVsbCBjb250cm9sIG92ZXIgdGhlIHByZXNlbmNlIG9mIGEgdHJhaWxpbmcgc2xhc2hcbiAgLy8gb24gcm9vdCBhY3Rpb25zXG4gIGlmIChwcmVwZW5kQmFzZW5hbWUgJiYgYmFzZW5hbWUgIT09IFwiL1wiKSB7XG4gICAgcGF0aC5wYXRobmFtZSA9IHBhdGgucGF0aG5hbWUgPT09IFwiL1wiID8gYmFzZW5hbWUgOiBqb2luUGF0aHMoW2Jhc2VuYW1lLCBwYXRoLnBhdGhuYW1lXSk7XG4gIH1cbiAgcmV0dXJuIGNyZWF0ZVBhdGgocGF0aCk7XG59XG4vLyBOb3JtYWxpemUgbmF2aWdhdGlvbiBvcHRpb25zIGJ5IGNvbnZlcnRpbmcgZm9ybU1ldGhvZD1HRVQgZm9ybURhdGEgb2JqZWN0cyB0b1xuLy8gVVJMU2VhcmNoUGFyYW1zIHNvIHRoZXkgYmVoYXZlIGlkZW50aWNhbGx5IHRvIGxpbmtzIHdpdGggcXVlcnkgcGFyYW1zXG5mdW5jdGlvbiBub3JtYWxpemVOYXZpZ2F0ZU9wdGlvbnMobm9ybWFsaXplRm9ybU1ldGhvZCwgaXNGZXRjaGVyLCBwYXRoLCBvcHRzKSB7XG4gIC8vIFJldHVybiBsb2NhdGlvbiB2ZXJiYXRpbSBvbiBub24tc3VibWlzc2lvbiBuYXZpZ2F0aW9uc1xuICBpZiAoIW9wdHMgfHwgIWlzU3VibWlzc2lvbk5hdmlnYXRpb24ob3B0cykpIHtcbiAgICByZXR1cm4ge1xuICAgICAgcGF0aFxuICAgIH07XG4gIH1cbiAgaWYgKG9wdHMuZm9ybU1ldGhvZCAmJiAhaXNWYWxpZE1ldGhvZChvcHRzLmZvcm1NZXRob2QpKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBhdGgsXG4gICAgICBlcnJvcjogZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDUsIHtcbiAgICAgICAgbWV0aG9kOiBvcHRzLmZvcm1NZXRob2RcbiAgICAgIH0pXG4gICAgfTtcbiAgfVxuICBsZXQgZ2V0SW52YWxpZEJvZHlFcnJvciA9ICgpID0+ICh7XG4gICAgcGF0aCxcbiAgICBlcnJvcjogZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDAsIHtcbiAgICAgIHR5cGU6IFwiaW52YWxpZC1ib2R5XCJcbiAgICB9KVxuICB9KTtcbiAgLy8gQ3JlYXRlIGEgU3VibWlzc2lvbiBvbiBub24tR0VUIG5hdmlnYXRpb25zXG4gIGxldCByYXdGb3JtTWV0aG9kID0gb3B0cy5mb3JtTWV0aG9kIHx8IFwiZ2V0XCI7XG4gIGxldCBmb3JtTWV0aG9kID0gbm9ybWFsaXplRm9ybU1ldGhvZCA/IHJhd0Zvcm1NZXRob2QudG9VcHBlckNhc2UoKSA6IHJhd0Zvcm1NZXRob2QudG9Mb3dlckNhc2UoKTtcbiAgbGV0IGZvcm1BY3Rpb24gPSBzdHJpcEhhc2hGcm9tUGF0aChwYXRoKTtcbiAgaWYgKG9wdHMuYm9keSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgaWYgKG9wdHMuZm9ybUVuY1R5cGUgPT09IFwidGV4dC9wbGFpblwiKSB7XG4gICAgICAvLyB0ZXh0IG9ubHkgc3VwcG9ydCBQT1NUL1BVVC9QQVRDSC9ERUxFVEUgc3VibWlzc2lvbnNcbiAgICAgIGlmICghaXNNdXRhdGlvbk1ldGhvZChmb3JtTWV0aG9kKSkge1xuICAgICAgICByZXR1cm4gZ2V0SW52YWxpZEJvZHlFcnJvcigpO1xuICAgICAgfVxuICAgICAgbGV0IHRleHQgPSB0eXBlb2Ygb3B0cy5ib2R5ID09PSBcInN0cmluZ1wiID8gb3B0cy5ib2R5IDogb3B0cy5ib2R5IGluc3RhbmNlb2YgRm9ybURhdGEgfHwgb3B0cy5ib2R5IGluc3RhbmNlb2YgVVJMU2VhcmNoUGFyYW1zID9cbiAgICAgIC8vIGh0dHBzOi8vaHRtbC5zcGVjLndoYXR3Zy5vcmcvbXVsdGlwYWdlL2Zvcm0tY29udHJvbC1pbmZyYXN0cnVjdHVyZS5odG1sI3BsYWluLXRleHQtZm9ybS1kYXRhXG4gICAgICBBcnJheS5mcm9tKG9wdHMuYm9keS5lbnRyaWVzKCkpLnJlZHVjZSgoYWNjLCBfcmVmMykgPT4ge1xuICAgICAgICBsZXQgW25hbWUsIHZhbHVlXSA9IF9yZWYzO1xuICAgICAgICByZXR1cm4gXCJcIiArIGFjYyArIG5hbWUgKyBcIj1cIiArIHZhbHVlICsgXCJcXG5cIjtcbiAgICAgIH0sIFwiXCIpIDogU3RyaW5nKG9wdHMuYm9keSk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBwYXRoLFxuICAgICAgICBzdWJtaXNzaW9uOiB7XG4gICAgICAgICAgZm9ybU1ldGhvZCxcbiAgICAgICAgICBmb3JtQWN0aW9uLFxuICAgICAgICAgIGZvcm1FbmNUeXBlOiBvcHRzLmZvcm1FbmNUeXBlLFxuICAgICAgICAgIGZvcm1EYXRhOiB1bmRlZmluZWQsXG4gICAgICAgICAganNvbjogdW5kZWZpbmVkLFxuICAgICAgICAgIHRleHRcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKG9wdHMuZm9ybUVuY1R5cGUgPT09IFwiYXBwbGljYXRpb24vanNvblwiKSB7XG4gICAgICAvLyBqc29uIG9ubHkgc3VwcG9ydHMgUE9TVC9QVVQvUEFUQ0gvREVMRVRFIHN1Ym1pc3Npb25zXG4gICAgICBpZiAoIWlzTXV0YXRpb25NZXRob2QoZm9ybU1ldGhvZCkpIHtcbiAgICAgICAgcmV0dXJuIGdldEludmFsaWRCb2R5RXJyb3IoKTtcbiAgICAgIH1cbiAgICAgIHRyeSB7XG4gICAgICAgIGxldCBqc29uID0gdHlwZW9mIG9wdHMuYm9keSA9PT0gXCJzdHJpbmdcIiA/IEpTT04ucGFyc2Uob3B0cy5ib2R5KSA6IG9wdHMuYm9keTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBwYXRoLFxuICAgICAgICAgIHN1Ym1pc3Npb246IHtcbiAgICAgICAgICAgIGZvcm1NZXRob2QsXG4gICAgICAgICAgICBmb3JtQWN0aW9uLFxuICAgICAgICAgICAgZm9ybUVuY1R5cGU6IG9wdHMuZm9ybUVuY1R5cGUsXG4gICAgICAgICAgICBmb3JtRGF0YTogdW5kZWZpbmVkLFxuICAgICAgICAgICAganNvbixcbiAgICAgICAgICAgIHRleHQ6IHVuZGVmaW5lZFxuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgcmV0dXJuIGdldEludmFsaWRCb2R5RXJyb3IoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgaW52YXJpYW50KHR5cGVvZiBGb3JtRGF0YSA9PT0gXCJmdW5jdGlvblwiLCBcIkZvcm1EYXRhIGlzIG5vdCBhdmFpbGFibGUgaW4gdGhpcyBlbnZpcm9ubWVudFwiKTtcbiAgbGV0IHNlYXJjaFBhcmFtcztcbiAgbGV0IGZvcm1EYXRhO1xuICBpZiAob3B0cy5mb3JtRGF0YSkge1xuICAgIHNlYXJjaFBhcmFtcyA9IGNvbnZlcnRGb3JtRGF0YVRvU2VhcmNoUGFyYW1zKG9wdHMuZm9ybURhdGEpO1xuICAgIGZvcm1EYXRhID0gb3B0cy5mb3JtRGF0YTtcbiAgfSBlbHNlIGlmIChvcHRzLmJvZHkgaW5zdGFuY2VvZiBGb3JtRGF0YSkge1xuICAgIHNlYXJjaFBhcmFtcyA9IGNvbnZlcnRGb3JtRGF0YVRvU2VhcmNoUGFyYW1zKG9wdHMuYm9keSk7XG4gICAgZm9ybURhdGEgPSBvcHRzLmJvZHk7XG4gIH0gZWxzZSBpZiAob3B0cy5ib2R5IGluc3RhbmNlb2YgVVJMU2VhcmNoUGFyYW1zKSB7XG4gICAgc2VhcmNoUGFyYW1zID0gb3B0cy5ib2R5O1xuICAgIGZvcm1EYXRhID0gY29udmVydFNlYXJjaFBhcmFtc1RvRm9ybURhdGEoc2VhcmNoUGFyYW1zKTtcbiAgfSBlbHNlIGlmIChvcHRzLmJvZHkgPT0gbnVsbCkge1xuICAgIHNlYXJjaFBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgICBmb3JtRGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICB9IGVsc2Uge1xuICAgIHRyeSB7XG4gICAgICBzZWFyY2hQYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKG9wdHMuYm9keSk7XG4gICAgICBmb3JtRGF0YSA9IGNvbnZlcnRTZWFyY2hQYXJhbXNUb0Zvcm1EYXRhKHNlYXJjaFBhcmFtcyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuIGdldEludmFsaWRCb2R5RXJyb3IoKTtcbiAgICB9XG4gIH1cbiAgbGV0IHN1Ym1pc3Npb24gPSB7XG4gICAgZm9ybU1ldGhvZCxcbiAgICBmb3JtQWN0aW9uLFxuICAgIGZvcm1FbmNUeXBlOiBvcHRzICYmIG9wdHMuZm9ybUVuY1R5cGUgfHwgXCJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWRcIixcbiAgICBmb3JtRGF0YSxcbiAgICBqc29uOiB1bmRlZmluZWQsXG4gICAgdGV4dDogdW5kZWZpbmVkXG4gIH07XG4gIGlmIChpc011dGF0aW9uTWV0aG9kKHN1Ym1pc3Npb24uZm9ybU1ldGhvZCkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgcGF0aCxcbiAgICAgIHN1Ym1pc3Npb25cbiAgICB9O1xuICB9XG4gIC8vIEZsYXR0ZW4gc3VibWlzc2lvbiBvbnRvIFVSTFNlYXJjaFBhcmFtcyBmb3IgR0VUIHN1Ym1pc3Npb25zXG4gIGxldCBwYXJzZWRQYXRoID0gcGFyc2VQYXRoKHBhdGgpO1xuICAvLyBPbiBHRVQgbmF2aWdhdGlvbiBzdWJtaXNzaW9ucyB3ZSBjYW4gZHJvcCB0aGUgP2luZGV4IHBhcmFtIGZyb20gdGhlXG4gIC8vIHJlc3VsdGluZyBsb2NhdGlvbiBzaW5jZSBhbGwgbG9hZGVycyB3aWxsIHJ1bi4gIEJ1dCBmZXRjaGVyIEdFVCBzdWJtaXNzaW9uc1xuICAvLyBvbmx5IHJ1biBhIHNpbmdsZSBsb2FkZXIgc28gd2UgbmVlZCB0byBwcmVzZXJ2ZSBhbnkgaW5jb21pbmcgP2luZGV4IHBhcmFtc1xuICBpZiAoaXNGZXRjaGVyICYmIHBhcnNlZFBhdGguc2VhcmNoICYmIGhhc05ha2VkSW5kZXhRdWVyeShwYXJzZWRQYXRoLnNlYXJjaCkpIHtcbiAgICBzZWFyY2hQYXJhbXMuYXBwZW5kKFwiaW5kZXhcIiwgXCJcIik7XG4gIH1cbiAgcGFyc2VkUGF0aC5zZWFyY2ggPSBcIj9cIiArIHNlYXJjaFBhcmFtcztcbiAgcmV0dXJuIHtcbiAgICBwYXRoOiBjcmVhdGVQYXRoKHBhcnNlZFBhdGgpLFxuICAgIHN1Ym1pc3Npb25cbiAgfTtcbn1cbi8vIEZpbHRlciBvdXQgYWxsIHJvdXRlcyBiZWxvdyBhbnkgY2F1Z2h0IGVycm9yIGFzIHRoZXkgYXJlbid0IGdvaW5nIHRvXG4vLyByZW5kZXIgc28gd2UgZG9uJ3QgbmVlZCB0byBsb2FkIHRoZW1cbmZ1bmN0aW9uIGdldExvYWRlck1hdGNoZXNVbnRpbEJvdW5kYXJ5KG1hdGNoZXMsIGJvdW5kYXJ5SWQpIHtcbiAgbGV0IGJvdW5kYXJ5TWF0Y2hlcyA9IG1hdGNoZXM7XG4gIGlmIChib3VuZGFyeUlkKSB7XG4gICAgbGV0IGluZGV4ID0gbWF0Y2hlcy5maW5kSW5kZXgobSA9PiBtLnJvdXRlLmlkID09PSBib3VuZGFyeUlkKTtcbiAgICBpZiAoaW5kZXggPj0gMCkge1xuICAgICAgYm91bmRhcnlNYXRjaGVzID0gbWF0Y2hlcy5zbGljZSgwLCBpbmRleCk7XG4gICAgfVxuICB9XG4gIHJldHVybiBib3VuZGFyeU1hdGNoZXM7XG59XG5mdW5jdGlvbiBnZXRNYXRjaGVzVG9Mb2FkKGhpc3RvcnksIHN0YXRlLCBtYXRjaGVzLCBzdWJtaXNzaW9uLCBsb2NhdGlvbiwgaXNSZXZhbGlkYXRpb25SZXF1aXJlZCwgY2FuY2VsbGVkRGVmZXJyZWRSb3V0ZXMsIGNhbmNlbGxlZEZldGNoZXJMb2FkcywgZmV0Y2hMb2FkTWF0Y2hlcywgZmV0Y2hSZWRpcmVjdElkcywgcm91dGVzVG9Vc2UsIGJhc2VuYW1lLCBwZW5kaW5nQWN0aW9uRGF0YSwgcGVuZGluZ0Vycm9yKSB7XG4gIGxldCBhY3Rpb25SZXN1bHQgPSBwZW5kaW5nRXJyb3IgPyBPYmplY3QudmFsdWVzKHBlbmRpbmdFcnJvcilbMF0gOiBwZW5kaW5nQWN0aW9uRGF0YSA/IE9iamVjdC52YWx1ZXMocGVuZGluZ0FjdGlvbkRhdGEpWzBdIDogdW5kZWZpbmVkO1xuICBsZXQgY3VycmVudFVybCA9IGhpc3RvcnkuY3JlYXRlVVJMKHN0YXRlLmxvY2F0aW9uKTtcbiAgbGV0IG5leHRVcmwgPSBoaXN0b3J5LmNyZWF0ZVVSTChsb2NhdGlvbik7XG4gIC8vIFBpY2sgbmF2aWdhdGlvbiBtYXRjaGVzIHRoYXQgYXJlIG5ldC1uZXcgb3IgcXVhbGlmeSBmb3IgcmV2YWxpZGF0aW9uXG4gIGxldCBib3VuZGFyeUlkID0gcGVuZGluZ0Vycm9yID8gT2JqZWN0LmtleXMocGVuZGluZ0Vycm9yKVswXSA6IHVuZGVmaW5lZDtcbiAgbGV0IGJvdW5kYXJ5TWF0Y2hlcyA9IGdldExvYWRlck1hdGNoZXNVbnRpbEJvdW5kYXJ5KG1hdGNoZXMsIGJvdW5kYXJ5SWQpO1xuICBsZXQgbmF2aWdhdGlvbk1hdGNoZXMgPSBib3VuZGFyeU1hdGNoZXMuZmlsdGVyKChtYXRjaCwgaW5kZXgpID0+IHtcbiAgICBpZiAobWF0Y2gucm91dGUubGF6eSkge1xuICAgICAgLy8gV2UgaGF2ZW4ndCBsb2FkZWQgdGhpcyByb3V0ZSB5ZXQgc28gd2UgZG9uJ3Qga25vdyBpZiBpdCdzIGdvdCBhIGxvYWRlciFcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBpZiAobWF0Y2gucm91dGUubG9hZGVyID09IG51bGwpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgLy8gQWx3YXlzIGNhbGwgdGhlIGxvYWRlciBvbiBuZXcgcm91dGUgaW5zdGFuY2VzIGFuZCBwZW5kaW5nIGRlZmVyIGNhbmNlbGxhdGlvbnNcbiAgICBpZiAoaXNOZXdMb2FkZXIoc3RhdGUubG9hZGVyRGF0YSwgc3RhdGUubWF0Y2hlc1tpbmRleF0sIG1hdGNoKSB8fCBjYW5jZWxsZWREZWZlcnJlZFJvdXRlcy5zb21lKGlkID0+IGlkID09PSBtYXRjaC5yb3V0ZS5pZCkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICAvLyBUaGlzIGlzIHRoZSBkZWZhdWx0IGltcGxlbWVudGF0aW9uIGZvciB3aGVuIHdlIHJldmFsaWRhdGUuICBJZiB0aGUgcm91dGVcbiAgICAvLyBwcm92aWRlcyBpdCdzIG93biBpbXBsZW1lbnRhdGlvbiwgdGhlbiB3ZSBnaXZlIHRoZW0gZnVsbCBjb250cm9sIGJ1dFxuICAgIC8vIHByb3ZpZGUgdGhpcyB2YWx1ZSBzbyB0aGV5IGNhbiBsZXZlcmFnZSBpdCBpZiBuZWVkZWQgYWZ0ZXIgdGhleSBjaGVja1xuICAgIC8vIHRoZWlyIG93biBzcGVjaWZpYyB1c2UgY2FzZXNcbiAgICBsZXQgY3VycmVudFJvdXRlTWF0Y2ggPSBzdGF0ZS5tYXRjaGVzW2luZGV4XTtcbiAgICBsZXQgbmV4dFJvdXRlTWF0Y2ggPSBtYXRjaDtcbiAgICByZXR1cm4gc2hvdWxkUmV2YWxpZGF0ZUxvYWRlcihtYXRjaCwgX2V4dGVuZHMoe1xuICAgICAgY3VycmVudFVybCxcbiAgICAgIGN1cnJlbnRQYXJhbXM6IGN1cnJlbnRSb3V0ZU1hdGNoLnBhcmFtcyxcbiAgICAgIG5leHRVcmwsXG4gICAgICBuZXh0UGFyYW1zOiBuZXh0Um91dGVNYXRjaC5wYXJhbXNcbiAgICB9LCBzdWJtaXNzaW9uLCB7XG4gICAgICBhY3Rpb25SZXN1bHQsXG4gICAgICBkZWZhdWx0U2hvdWxkUmV2YWxpZGF0ZTpcbiAgICAgIC8vIEZvcmNlZCByZXZhbGlkYXRpb24gZHVlIHRvIHN1Ym1pc3Npb24sIHVzZVJldmFsaWRhdG9yLCBvciBYLVJlbWl4LVJldmFsaWRhdGVcbiAgICAgIGlzUmV2YWxpZGF0aW9uUmVxdWlyZWQgfHxcbiAgICAgIC8vIENsaWNrZWQgdGhlIHNhbWUgbGluaywgcmVzdWJtaXR0ZWQgYSBHRVQgZm9ybVxuICAgICAgY3VycmVudFVybC5wYXRobmFtZSArIGN1cnJlbnRVcmwuc2VhcmNoID09PSBuZXh0VXJsLnBhdGhuYW1lICsgbmV4dFVybC5zZWFyY2ggfHxcbiAgICAgIC8vIFNlYXJjaCBwYXJhbXMgYWZmZWN0IGFsbCBsb2FkZXJzXG4gICAgICBjdXJyZW50VXJsLnNlYXJjaCAhPT0gbmV4dFVybC5zZWFyY2ggfHwgaXNOZXdSb3V0ZUluc3RhbmNlKGN1cnJlbnRSb3V0ZU1hdGNoLCBuZXh0Um91dGVNYXRjaClcbiAgICB9KSk7XG4gIH0pO1xuICAvLyBQaWNrIGZldGNoZXIubG9hZHMgdGhhdCBuZWVkIHRvIGJlIHJldmFsaWRhdGVkXG4gIGxldCByZXZhbGlkYXRpbmdGZXRjaGVycyA9IFtdO1xuICBmZXRjaExvYWRNYXRjaGVzLmZvckVhY2goKGYsIGtleSkgPT4ge1xuICAgIC8vIERvbid0IHJldmFsaWRhdGUgaWYgZmV0Y2hlciB3b24ndCBiZSBwcmVzZW50IGluIHRoZSBzdWJzZXF1ZW50IHJlbmRlclxuICAgIGlmICghbWF0Y2hlcy5zb21lKG0gPT4gbS5yb3V0ZS5pZCA9PT0gZi5yb3V0ZUlkKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgZmV0Y2hlck1hdGNoZXMgPSBtYXRjaFJvdXRlcyhyb3V0ZXNUb1VzZSwgZi5wYXRoLCBiYXNlbmFtZSk7XG4gICAgLy8gSWYgdGhlIGZldGNoZXIgcGF0aCBubyBsb25nZXIgbWF0Y2hlcywgcHVzaCBpdCBpbiB3aXRoIG51bGwgbWF0Y2hlcyBzb1xuICAgIC8vIHdlIGNhbiB0cmlnZ2VyIGEgNDA0IGluIGNhbGxMb2FkZXJzQW5kTWF5YmVSZXNvbHZlRGF0YVxuICAgIGlmICghZmV0Y2hlck1hdGNoZXMpIHtcbiAgICAgIHJldmFsaWRhdGluZ0ZldGNoZXJzLnB1c2goe1xuICAgICAgICBrZXksXG4gICAgICAgIHJvdXRlSWQ6IGYucm91dGVJZCxcbiAgICAgICAgcGF0aDogZi5wYXRoLFxuICAgICAgICBtYXRjaGVzOiBudWxsLFxuICAgICAgICBtYXRjaDogbnVsbCxcbiAgICAgICAgY29udHJvbGxlcjogbnVsbFxuICAgICAgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIFJldmFsaWRhdGluZyBmZXRjaGVycyBhcmUgZGVjb3VwbGVkIGZyb20gdGhlIHJvdXRlIG1hdGNoZXMgc2luY2UgdGhleVxuICAgIC8vIGxvYWQgZnJvbSBhIHN0YXRpYyBocmVmLiAgVGhleSBvbmx5IHNldCBgZGVmYXVsdFNob3VsZFJldmFsaWRhdGVgIG9uXG4gICAgLy8gZXhwbGljaXQgcmV2YWxpZGF0aW9uIGR1ZSB0byBzdWJtaXNzaW9uLCB1c2VSZXZhbGlkYXRvciwgb3IgWC1SZW1peC1SZXZhbGlkYXRlXG4gICAgLy9cbiAgICAvLyBUaGV5IGF1dG9tYXRpY2FsbHkgcmV2YWxpZGF0ZSB3aXRob3V0IGV2ZW4gY2FsbGluZyBzaG91bGRSZXZhbGlkYXRlIGlmOlxuICAgIC8vIC0gVGhleSB3ZXJlIGNhbmNlbGxlZFxuICAgIC8vIC0gVGhleSdyZSBpbiB0aGUgbWlkZGxlIG9mIHRoZWlyIGZpcnN0IGxvYWQgYW5kIHRoZXJlZm9yZSB0aGlzIGlzIHN0aWxsXG4gICAgLy8gICBhbiBpbml0aWFsIGxvYWQgYW5kIG5vdCBhIHJldmFsaWRhdGlvblxuICAgIC8vXG4gICAgLy8gSWYgbmVpdGhlciBvZiB0aG9zZSBpcyB0cnVlLCB0aGVuIHRoZXkgX2Fsd2F5c18gY2hlY2sgc2hvdWxkUmV2YWxpZGF0ZVxuICAgIGxldCBmZXRjaGVyID0gc3RhdGUuZmV0Y2hlcnMuZ2V0KGtleSk7XG4gICAgbGV0IGlzUGVyZm9ybWluZ0luaXRpYWxMb2FkID0gZmV0Y2hlciAmJiBmZXRjaGVyLnN0YXRlICE9PSBcImlkbGVcIiAmJiBmZXRjaGVyLmRhdGEgPT09IHVuZGVmaW5lZCAmJlxuICAgIC8vIElmIGEgZmV0Y2hlci5sb2FkIHJlZGlyZWN0ZWQgdGhlbiBpdCdsbCBiZSBcImxvYWRpbmdcIiB3aXRob3V0IGFueSBkYXRhXG4gICAgLy8gc28gZW5zdXJlIHdlJ3JlIG5vdCBwcm9jZXNzaW5nIHRoZSByZWRpcmVjdCBmcm9tIHRoaXMgZmV0Y2hlclxuICAgICFmZXRjaFJlZGlyZWN0SWRzLmhhcyhrZXkpO1xuICAgIGxldCBmZXRjaGVyTWF0Y2ggPSBnZXRUYXJnZXRNYXRjaChmZXRjaGVyTWF0Y2hlcywgZi5wYXRoKTtcbiAgICBsZXQgc2hvdWxkUmV2YWxpZGF0ZSA9IGNhbmNlbGxlZEZldGNoZXJMb2Fkcy5pbmNsdWRlcyhrZXkpIHx8IGlzUGVyZm9ybWluZ0luaXRpYWxMb2FkIHx8IHNob3VsZFJldmFsaWRhdGVMb2FkZXIoZmV0Y2hlck1hdGNoLCBfZXh0ZW5kcyh7XG4gICAgICBjdXJyZW50VXJsLFxuICAgICAgY3VycmVudFBhcmFtczogc3RhdGUubWF0Y2hlc1tzdGF0ZS5tYXRjaGVzLmxlbmd0aCAtIDFdLnBhcmFtcyxcbiAgICAgIG5leHRVcmwsXG4gICAgICBuZXh0UGFyYW1zOiBtYXRjaGVzW21hdGNoZXMubGVuZ3RoIC0gMV0ucGFyYW1zXG4gICAgfSwgc3VibWlzc2lvbiwge1xuICAgICAgYWN0aW9uUmVzdWx0LFxuICAgICAgZGVmYXVsdFNob3VsZFJldmFsaWRhdGU6IGlzUmV2YWxpZGF0aW9uUmVxdWlyZWRcbiAgICB9KSk7XG4gICAgaWYgKHNob3VsZFJldmFsaWRhdGUpIHtcbiAgICAgIHJldmFsaWRhdGluZ0ZldGNoZXJzLnB1c2goe1xuICAgICAgICBrZXksXG4gICAgICAgIHJvdXRlSWQ6IGYucm91dGVJZCxcbiAgICAgICAgcGF0aDogZi5wYXRoLFxuICAgICAgICBtYXRjaGVzOiBmZXRjaGVyTWF0Y2hlcyxcbiAgICAgICAgbWF0Y2g6IGZldGNoZXJNYXRjaCxcbiAgICAgICAgY29udHJvbGxlcjogbmV3IEFib3J0Q29udHJvbGxlcigpXG4gICAgICB9KTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gW25hdmlnYXRpb25NYXRjaGVzLCByZXZhbGlkYXRpbmdGZXRjaGVyc107XG59XG5mdW5jdGlvbiBpc05ld0xvYWRlcihjdXJyZW50TG9hZGVyRGF0YSwgY3VycmVudE1hdGNoLCBtYXRjaCkge1xuICBsZXQgaXNOZXcgPVxuICAvLyBbYV0gLT4gW2EsIGJdXG4gICFjdXJyZW50TWF0Y2ggfHxcbiAgLy8gW2EsIGJdIC0+IFthLCBjXVxuICBtYXRjaC5yb3V0ZS5pZCAhPT0gY3VycmVudE1hdGNoLnJvdXRlLmlkO1xuICAvLyBIYW5kbGUgdGhlIGNhc2UgdGhhdCB3ZSBkb24ndCBoYXZlIGRhdGEgZm9yIGEgcmUtdXNlZCByb3V0ZSwgcG90ZW50aWFsbHlcbiAgLy8gZnJvbSBhIHByaW9yIGVycm9yIG9yIGZyb20gYSBjYW5jZWxsZWQgcGVuZGluZyBkZWZlcnJlZFxuICBsZXQgaXNNaXNzaW5nRGF0YSA9IGN1cnJlbnRMb2FkZXJEYXRhW21hdGNoLnJvdXRlLmlkXSA9PT0gdW5kZWZpbmVkO1xuICAvLyBBbHdheXMgbG9hZCBpZiB0aGlzIGlzIGEgbmV0LW5ldyByb3V0ZSBvciB3ZSBkb24ndCB5ZXQgaGF2ZSBkYXRhXG4gIHJldHVybiBpc05ldyB8fCBpc01pc3NpbmdEYXRhO1xufVxuZnVuY3Rpb24gaXNOZXdSb3V0ZUluc3RhbmNlKGN1cnJlbnRNYXRjaCwgbWF0Y2gpIHtcbiAgbGV0IGN1cnJlbnRQYXRoID0gY3VycmVudE1hdGNoLnJvdXRlLnBhdGg7XG4gIHJldHVybiAoXG4gICAgLy8gcGFyYW0gY2hhbmdlIGZvciB0aGlzIG1hdGNoLCAvdXNlcnMvMTIzIC0+IC91c2Vycy80NTZcbiAgICBjdXJyZW50TWF0Y2gucGF0aG5hbWUgIT09IG1hdGNoLnBhdGhuYW1lIHx8XG4gICAgLy8gc3BsYXQgcGFyYW0gY2hhbmdlZCwgd2hpY2ggaXMgbm90IHByZXNlbnQgaW4gbWF0Y2gucGF0aFxuICAgIC8vIGUuZy4gL2ZpbGVzL2ltYWdlcy9hdmF0YXIuanBnIC0+IGZpbGVzL2ZpbmFuY2VzLnhsc1xuICAgIGN1cnJlbnRQYXRoICE9IG51bGwgJiYgY3VycmVudFBhdGguZW5kc1dpdGgoXCIqXCIpICYmIGN1cnJlbnRNYXRjaC5wYXJhbXNbXCIqXCJdICE9PSBtYXRjaC5wYXJhbXNbXCIqXCJdXG4gICk7XG59XG5mdW5jdGlvbiBzaG91bGRSZXZhbGlkYXRlTG9hZGVyKGxvYWRlck1hdGNoLCBhcmcpIHtcbiAgaWYgKGxvYWRlck1hdGNoLnJvdXRlLnNob3VsZFJldmFsaWRhdGUpIHtcbiAgICBsZXQgcm91dGVDaG9pY2UgPSBsb2FkZXJNYXRjaC5yb3V0ZS5zaG91bGRSZXZhbGlkYXRlKGFyZyk7XG4gICAgaWYgKHR5cGVvZiByb3V0ZUNob2ljZSA9PT0gXCJib29sZWFuXCIpIHtcbiAgICAgIHJldHVybiByb3V0ZUNob2ljZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGFyZy5kZWZhdWx0U2hvdWxkUmV2YWxpZGF0ZTtcbn1cbi8qKlxuICogRXhlY3V0ZSByb3V0ZS5sYXp5KCkgbWV0aG9kcyB0byBsYXppbHkgbG9hZCByb3V0ZSBtb2R1bGVzIChsb2FkZXIsIGFjdGlvbixcbiAqIHNob3VsZFJldmFsaWRhdGUpIGFuZCB1cGRhdGUgdGhlIHJvdXRlTWFuaWZlc3QgaW4gcGxhY2Ugd2hpY2ggc2hhcmVzIG9iamVjdHNcbiAqIHdpdGggZGF0YVJvdXRlcyBzbyB0aG9zZSBnZXQgdXBkYXRlZCBhcyB3ZWxsLlxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkTGF6eVJvdXRlTW9kdWxlKHJvdXRlLCBtYXBSb3V0ZVByb3BlcnRpZXMsIG1hbmlmZXN0KSB7XG4gIGlmICghcm91dGUubGF6eSkge1xuICAgIHJldHVybjtcbiAgfVxuICBsZXQgbGF6eVJvdXRlID0gYXdhaXQgcm91dGUubGF6eSgpO1xuICAvLyBJZiB0aGUgbGF6eSByb3V0ZSBmdW5jdGlvbiB3YXMgZXhlY3V0ZWQgYW5kIHJlbW92ZWQgYnkgYW5vdGhlciBwYXJhbGxlbFxuICAvLyBjYWxsIHRoZW4gd2UgY2FuIHJldHVybiAtIGZpcnN0IGxhenkoKSB0byBmaW5pc2ggd2lucyBiZWNhdXNlIHRoZSByZXR1cm5cbiAgLy8gdmFsdWUgb2YgbGF6eSBpcyBleHBlY3RlZCB0byBiZSBzdGF0aWNcbiAgaWYgKCFyb3V0ZS5sYXp5KSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGxldCByb3V0ZVRvVXBkYXRlID0gbWFuaWZlc3Rbcm91dGUuaWRdO1xuICBpbnZhcmlhbnQocm91dGVUb1VwZGF0ZSwgXCJObyByb3V0ZSBmb3VuZCBpbiBtYW5pZmVzdFwiKTtcbiAgLy8gVXBkYXRlIHRoZSByb3V0ZSBpbiBwbGFjZS4gIFRoaXMgc2hvdWxkIGJlIHNhZmUgYmVjYXVzZSB0aGVyZSdzIG5vIHdheVxuICAvLyB3ZSBjb3VsZCB5ZXQgYmUgc2l0dGluZyBvbiB0aGlzIHJvdXRlIGFzIHdlIGNhbid0IGdldCB0aGVyZSB3aXRob3V0XG4gIC8vIHJlc29sdmluZyBsYXp5KCkgZmlyc3QuXG4gIC8vXG4gIC8vIFRoaXMgaXMgZGlmZmVyZW50IHRoYW4gdGhlIEhNUiBcInVwZGF0ZVwiIHVzZS1jYXNlIHdoZXJlIHdlIG1heSBhY3RpdmVseSBiZVxuICAvLyBvbiB0aGUgcm91dGUgYmVpbmcgdXBkYXRlZC4gIFRoZSBtYWluIGNvbmNlcm4gYm9pbHMgZG93biB0byBcImRvZXMgdGhpc1xuICAvLyBtdXRhdGlvbiBhZmZlY3QgYW55IG9uZ29pbmcgbmF2aWdhdGlvbnMgb3IgYW55IGN1cnJlbnQgc3RhdGUubWF0Y2hlc1xuICAvLyB2YWx1ZXM/XCIuICBJZiBub3QsIGl0IHNob3VsZCBiZSBzYWZlIHRvIHVwZGF0ZSBpbiBwbGFjZS5cbiAgbGV0IHJvdXRlVXBkYXRlcyA9IHt9O1xuICBmb3IgKGxldCBsYXp5Um91dGVQcm9wZXJ0eSBpbiBsYXp5Um91dGUpIHtcbiAgICBsZXQgc3RhdGljUm91dGVWYWx1ZSA9IHJvdXRlVG9VcGRhdGVbbGF6eVJvdXRlUHJvcGVydHldO1xuICAgIGxldCBpc1Byb3BlcnR5U3RhdGljYWxseURlZmluZWQgPSBzdGF0aWNSb3V0ZVZhbHVlICE9PSB1bmRlZmluZWQgJiZcbiAgICAvLyBUaGlzIHByb3BlcnR5IGlzbid0IHN0YXRpYyBzaW5jZSBpdCBzaG91bGQgYWx3YXlzIGJlIHVwZGF0ZWQgYmFzZWRcbiAgICAvLyBvbiB0aGUgcm91dGUgdXBkYXRlc1xuICAgIGxhenlSb3V0ZVByb3BlcnR5ICE9PSBcImhhc0Vycm9yQm91bmRhcnlcIjtcbiAgICB3YXJuaW5nKCFpc1Byb3BlcnR5U3RhdGljYWxseURlZmluZWQsIFwiUm91dGUgXFxcIlwiICsgcm91dGVUb1VwZGF0ZS5pZCArIFwiXFxcIiBoYXMgYSBzdGF0aWMgcHJvcGVydHkgXFxcIlwiICsgbGF6eVJvdXRlUHJvcGVydHkgKyBcIlxcXCIgXCIgKyBcImRlZmluZWQgYnV0IGl0cyBsYXp5IGZ1bmN0aW9uIGlzIGFsc28gcmV0dXJuaW5nIGEgdmFsdWUgZm9yIHRoaXMgcHJvcGVydHkuIFwiICsgKFwiVGhlIGxhenkgcm91dGUgcHJvcGVydHkgXFxcIlwiICsgbGF6eVJvdXRlUHJvcGVydHkgKyBcIlxcXCIgd2lsbCBiZSBpZ25vcmVkLlwiKSk7XG4gICAgaWYgKCFpc1Byb3BlcnR5U3RhdGljYWxseURlZmluZWQgJiYgIWltbXV0YWJsZVJvdXRlS2V5cy5oYXMobGF6eVJvdXRlUHJvcGVydHkpKSB7XG4gICAgICByb3V0ZVVwZGF0ZXNbbGF6eVJvdXRlUHJvcGVydHldID0gbGF6eVJvdXRlW2xhenlSb3V0ZVByb3BlcnR5XTtcbiAgICB9XG4gIH1cbiAgLy8gTXV0YXRlIHRoZSByb3V0ZSB3aXRoIHRoZSBwcm92aWRlZCB1cGRhdGVzLiAgRG8gdGhpcyBmaXJzdCBzbyB3ZSBwYXNzXG4gIC8vIHRoZSB1cGRhdGVkIHZlcnNpb24gdG8gbWFwUm91dGVQcm9wZXJ0aWVzXG4gIE9iamVjdC5hc3NpZ24ocm91dGVUb1VwZGF0ZSwgcm91dGVVcGRhdGVzKTtcbiAgLy8gTXV0YXRlIHRoZSBgaGFzRXJyb3JCb3VuZGFyeWAgcHJvcGVydHkgb24gdGhlIHJvdXRlIGJhc2VkIG9uIHRoZSByb3V0ZVxuICAvLyB1cGRhdGVzIGFuZCByZW1vdmUgdGhlIGBsYXp5YCBmdW5jdGlvbiBzbyB3ZSBkb24ndCByZXNvbHZlIHRoZSBsYXp5XG4gIC8vIHJvdXRlIGFnYWluLlxuICBPYmplY3QuYXNzaWduKHJvdXRlVG9VcGRhdGUsIF9leHRlbmRzKHt9LCBtYXBSb3V0ZVByb3BlcnRpZXMocm91dGVUb1VwZGF0ZSksIHtcbiAgICBsYXp5OiB1bmRlZmluZWRcbiAgfSkpO1xufVxuYXN5bmMgZnVuY3Rpb24gY2FsbExvYWRlck9yQWN0aW9uKHR5cGUsIHJlcXVlc3QsIG1hdGNoLCBtYXRjaGVzLCBtYW5pZmVzdCwgbWFwUm91dGVQcm9wZXJ0aWVzLCBiYXNlbmFtZSwgb3B0cykge1xuICBpZiAob3B0cyA9PT0gdm9pZCAwKSB7XG4gICAgb3B0cyA9IHt9O1xuICB9XG4gIGxldCByZXN1bHRUeXBlO1xuICBsZXQgcmVzdWx0O1xuICBsZXQgb25SZWplY3Q7XG4gIGxldCBydW5IYW5kbGVyID0gaGFuZGxlciA9PiB7XG4gICAgLy8gU2V0dXAgYSBwcm9taXNlIHdlIGNhbiByYWNlIGFnYWluc3Qgc28gdGhhdCBhYm9ydCBzaWduYWxzIHNob3J0IGNpcmN1aXRcbiAgICBsZXQgcmVqZWN0O1xuICAgIGxldCBhYm9ydFByb21pc2UgPSBuZXcgUHJvbWlzZSgoXywgcikgPT4gcmVqZWN0ID0gcik7XG4gICAgb25SZWplY3QgPSAoKSA9PiByZWplY3QoKTtcbiAgICByZXF1ZXN0LnNpZ25hbC5hZGRFdmVudExpc3RlbmVyKFwiYWJvcnRcIiwgb25SZWplY3QpO1xuICAgIHJldHVybiBQcm9taXNlLnJhY2UoW2hhbmRsZXIoe1xuICAgICAgcmVxdWVzdCxcbiAgICAgIHBhcmFtczogbWF0Y2gucGFyYW1zLFxuICAgICAgY29udGV4dDogb3B0cy5yZXF1ZXN0Q29udGV4dFxuICAgIH0pLCBhYm9ydFByb21pc2VdKTtcbiAgfTtcbiAgdHJ5IHtcbiAgICBsZXQgaGFuZGxlciA9IG1hdGNoLnJvdXRlW3R5cGVdO1xuICAgIGlmIChtYXRjaC5yb3V0ZS5sYXp5KSB7XG4gICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAvLyBSdW4gc3RhdGljYWxseSBkZWZpbmVkIGhhbmRsZXIgaW4gcGFyYWxsZWwgd2l0aCBsYXp5KClcbiAgICAgICAgbGV0IHZhbHVlcyA9IGF3YWl0IFByb21pc2UuYWxsKFtydW5IYW5kbGVyKGhhbmRsZXIpLCBsb2FkTGF6eVJvdXRlTW9kdWxlKG1hdGNoLnJvdXRlLCBtYXBSb3V0ZVByb3BlcnRpZXMsIG1hbmlmZXN0KV0pO1xuICAgICAgICByZXN1bHQgPSB2YWx1ZXNbMF07XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBMb2FkIGxhenkgcm91dGUgbW9kdWxlLCB0aGVuIHJ1biBhbnkgcmV0dXJuZWQgaGFuZGxlclxuICAgICAgICBhd2FpdCBsb2FkTGF6eVJvdXRlTW9kdWxlKG1hdGNoLnJvdXRlLCBtYXBSb3V0ZVByb3BlcnRpZXMsIG1hbmlmZXN0KTtcbiAgICAgICAgaGFuZGxlciA9IG1hdGNoLnJvdXRlW3R5cGVdO1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgIC8vIEhhbmRsZXIgc3RpbGwgcnVuIGV2ZW4gaWYgd2UgZ290IGludGVycnVwdGVkIHRvIG1haW50YWluIGNvbnNpc3RlbmN5XG4gICAgICAgICAgLy8gd2l0aCB1bi1hYm9ydGFibGUgYmVoYXZpb3Igb2YgaGFuZGxlciBleGVjdXRpb24gb24gbm9uLWxhenkgb3JcbiAgICAgICAgICAvLyBwcmV2aW91c2x5LWxhenktbG9hZGVkIHJvdXRlc1xuICAgICAgICAgIHJlc3VsdCA9IGF3YWl0IHJ1bkhhbmRsZXIoaGFuZGxlcik7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gXCJhY3Rpb25cIikge1xuICAgICAgICAgIGxldCB1cmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsKTtcbiAgICAgICAgICBsZXQgcGF0aG5hbWUgPSB1cmwucGF0aG5hbWUgKyB1cmwuc2VhcmNoO1xuICAgICAgICAgIHRocm93IGdldEludGVybmFsUm91dGVyRXJyb3IoNDA1LCB7XG4gICAgICAgICAgICBtZXRob2Q6IHJlcXVlc3QubWV0aG9kLFxuICAgICAgICAgICAgcGF0aG5hbWUsXG4gICAgICAgICAgICByb3V0ZUlkOiBtYXRjaC5yb3V0ZS5pZFxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGxhenkoKSByb3V0ZSBoYXMgbm8gbG9hZGVyIHRvIHJ1bi4gIFNob3J0IGNpcmN1aXQgaGVyZSBzbyB3ZSBkb24ndFxuICAgICAgICAgIC8vIGhpdCB0aGUgaW52YXJpYW50IGJlbG93IHRoYXQgZXJyb3JzIG9uIHJldHVybmluZyB1bmRlZmluZWQuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHR5cGU6IFJlc3VsdFR5cGUuZGF0YSxcbiAgICAgICAgICAgIGRhdGE6IHVuZGVmaW5lZFxuICAgICAgICAgIH07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCFoYW5kbGVyKSB7XG4gICAgICBsZXQgdXJsID0gbmV3IFVSTChyZXF1ZXN0LnVybCk7XG4gICAgICBsZXQgcGF0aG5hbWUgPSB1cmwucGF0aG5hbWUgKyB1cmwuc2VhcmNoO1xuICAgICAgdGhyb3cgZ2V0SW50ZXJuYWxSb3V0ZXJFcnJvcig0MDQsIHtcbiAgICAgICAgcGF0aG5hbWVcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXN1bHQgPSBhd2FpdCBydW5IYW5kbGVyKGhhbmRsZXIpO1xuICAgIH1cbiAgICBpbnZhcmlhbnQocmVzdWx0ICE9PSB1bmRlZmluZWQsIFwiWW91IGRlZmluZWQgXCIgKyAodHlwZSA9PT0gXCJhY3Rpb25cIiA/IFwiYW4gYWN0aW9uXCIgOiBcImEgbG9hZGVyXCIpICsgXCIgZm9yIHJvdXRlIFwiICsgKFwiXFxcIlwiICsgbWF0Y2gucm91dGUuaWQgKyBcIlxcXCIgYnV0IGRpZG4ndCByZXR1cm4gYW55dGhpbmcgZnJvbSB5b3VyIGBcIiArIHR5cGUgKyBcImAgXCIpICsgXCJmdW5jdGlvbi4gUGxlYXNlIHJldHVybiBhIHZhbHVlIG9yIGBudWxsYC5cIik7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXN1bHRUeXBlID0gUmVzdWx0VHlwZS5lcnJvcjtcbiAgICByZXN1bHQgPSBlO1xuICB9IGZpbmFsbHkge1xuICAgIGlmIChvblJlamVjdCkge1xuICAgICAgcmVxdWVzdC5zaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsIG9uUmVqZWN0KTtcbiAgICB9XG4gIH1cbiAgaWYgKGlzUmVzcG9uc2UocmVzdWx0KSkge1xuICAgIGxldCBzdGF0dXMgPSByZXN1bHQuc3RhdHVzO1xuICAgIC8vIFByb2Nlc3MgcmVkaXJlY3RzXG4gICAgaWYgKHJlZGlyZWN0U3RhdHVzQ29kZXMuaGFzKHN0YXR1cykpIHtcbiAgICAgIGxldCBsb2NhdGlvbiA9IHJlc3VsdC5oZWFkZXJzLmdldChcIkxvY2F0aW9uXCIpO1xuICAgICAgaW52YXJpYW50KGxvY2F0aW9uLCBcIlJlZGlyZWN0cyByZXR1cm5lZC90aHJvd24gZnJvbSBsb2FkZXJzL2FjdGlvbnMgbXVzdCBoYXZlIGEgTG9jYXRpb24gaGVhZGVyXCIpO1xuICAgICAgLy8gU3VwcG9ydCByZWxhdGl2ZSByb3V0aW5nIGluIGludGVybmFsIHJlZGlyZWN0c1xuICAgICAgaWYgKCFBQlNPTFVURV9VUkxfUkVHRVgudGVzdChsb2NhdGlvbikpIHtcbiAgICAgICAgbG9jYXRpb24gPSBub3JtYWxpemVUbyhuZXcgVVJMKHJlcXVlc3QudXJsKSwgbWF0Y2hlcy5zbGljZSgwLCBtYXRjaGVzLmluZGV4T2YobWF0Y2gpICsgMSksIGJhc2VuYW1lLCB0cnVlLCBsb2NhdGlvbik7XG4gICAgICB9IGVsc2UgaWYgKCFvcHRzLmlzU3RhdGljUmVxdWVzdCkge1xuICAgICAgICAvLyBTdHJpcCBvZmYgdGhlIHByb3RvY29sK29yaWdpbiBmb3Igc2FtZS1vcmlnaW4gKyBzYW1lLWJhc2VuYW1lIGFic29sdXRlXG4gICAgICAgIC8vIHJlZGlyZWN0cy4gSWYgdGhpcyBpcyBhIHN0YXRpYyByZXF1ZXN0LCB3ZSBjYW4gbGV0IGl0IGdvIGJhY2sgdG8gdGhlXG4gICAgICAgIC8vIGJyb3dzZXIgYXMtaXNcbiAgICAgICAgbGV0IGN1cnJlbnRVcmwgPSBuZXcgVVJMKHJlcXVlc3QudXJsKTtcbiAgICAgICAgbGV0IHVybCA9IGxvY2F0aW9uLnN0YXJ0c1dpdGgoXCIvL1wiKSA/IG5ldyBVUkwoY3VycmVudFVybC5wcm90b2NvbCArIGxvY2F0aW9uKSA6IG5ldyBVUkwobG9jYXRpb24pO1xuICAgICAgICBsZXQgaXNTYW1lQmFzZW5hbWUgPSBzdHJpcEJhc2VuYW1lKHVybC5wYXRobmFtZSwgYmFzZW5hbWUpICE9IG51bGw7XG4gICAgICAgIGlmICh1cmwub3JpZ2luID09PSBjdXJyZW50VXJsLm9yaWdpbiAmJiBpc1NhbWVCYXNlbmFtZSkge1xuICAgICAgICAgIGxvY2F0aW9uID0gdXJsLnBhdGhuYW1lICsgdXJsLnNlYXJjaCArIHVybC5oYXNoO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBEb24ndCBwcm9jZXNzIHJlZGlyZWN0cyBpbiB0aGUgcm91dGVyIGR1cmluZyBzdGF0aWMgcmVxdWVzdHMgcmVxdWVzdHMuXG4gICAgICAvLyBJbnN0ZWFkLCB0aHJvdyB0aGUgUmVzcG9uc2UgYW5kIGxldCB0aGUgc2VydmVyIGhhbmRsZSBpdCB3aXRoIGFuIEhUVFBcbiAgICAgIC8vIHJlZGlyZWN0LiAgV2UgYWxzbyB1cGRhdGUgdGhlIExvY2F0aW9uIGhlYWRlciBpbiBwbGFjZSBpbiB0aGlzIGZsb3cgc29cbiAgICAgIC8vIGJhc2VuYW1lIGFuZCByZWxhdGl2ZSByb3V0aW5nIGlzIHRha2VuIGludG8gYWNjb3VudFxuICAgICAgaWYgKG9wdHMuaXNTdGF0aWNSZXF1ZXN0KSB7XG4gICAgICAgIHJlc3VsdC5oZWFkZXJzLnNldChcIkxvY2F0aW9uXCIsIGxvY2F0aW9uKTtcbiAgICAgICAgdGhyb3cgcmVzdWx0O1xuICAgICAgfVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogUmVzdWx0VHlwZS5yZWRpcmVjdCxcbiAgICAgICAgc3RhdHVzLFxuICAgICAgICBsb2NhdGlvbixcbiAgICAgICAgcmV2YWxpZGF0ZTogcmVzdWx0LmhlYWRlcnMuZ2V0KFwiWC1SZW1peC1SZXZhbGlkYXRlXCIpICE9PSBudWxsXG4gICAgICB9O1xuICAgIH1cbiAgICAvLyBGb3IgU1NSIHNpbmdsZS1yb3V0ZSByZXF1ZXN0cywgd2Ugd2FudCB0byBoYW5kIFJlc3BvbnNlcyBiYWNrIGRpcmVjdGx5XG4gICAgLy8gd2l0aG91dCB1bndyYXBwaW5nLiAgV2UgZG8gdGhpcyB3aXRoIHRoZSBRdWVyeVJvdXRlUmVzcG9uc2Ugd3JhcHBlclxuICAgIC8vIGludGVyZmFjZSBzbyB3ZSBjYW4ga25vdyB3aGV0aGVyIGl0IHdhcyByZXR1cm5lZCBvciB0aHJvd25cbiAgICBpZiAob3B0cy5pc1JvdXRlUmVxdWVzdCkge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXRocm93LWxpdGVyYWxcbiAgICAgIHRocm93IHtcbiAgICAgICAgdHlwZTogcmVzdWx0VHlwZSB8fCBSZXN1bHRUeXBlLmRhdGEsXG4gICAgICAgIHJlc3BvbnNlOiByZXN1bHRcbiAgICAgIH07XG4gICAgfVxuICAgIGxldCBkYXRhO1xuICAgIGxldCBjb250ZW50VHlwZSA9IHJlc3VsdC5oZWFkZXJzLmdldChcIkNvbnRlbnQtVHlwZVwiKTtcbiAgICAvLyBDaGVjayBiZXR3ZWVuIHdvcmQgYm91bmRhcmllcyBpbnN0ZWFkIG9mIHN0YXJ0c1dpdGgoKSBkdWUgdG8gdGhlIGxhc3RcbiAgICAvLyBwYXJhZ3JhcGggb2YgaHR0cHM6Ly9odHRwd2cub3JnL3NwZWNzL3JmYzkxMTAuaHRtbCNmaWVsZC5jb250ZW50LXR5cGVcbiAgICBpZiAoY29udGVudFR5cGUgJiYgL1xcYmFwcGxpY2F0aW9uXFwvanNvblxcYi8udGVzdChjb250ZW50VHlwZSkpIHtcbiAgICAgIGRhdGEgPSBhd2FpdCByZXN1bHQuanNvbigpO1xuICAgIH0gZWxzZSB7XG4gICAgICBkYXRhID0gYXdhaXQgcmVzdWx0LnRleHQoKTtcbiAgICB9XG4gICAgaWYgKHJlc3VsdFR5cGUgPT09IFJlc3VsdFR5cGUuZXJyb3IpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHR5cGU6IHJlc3VsdFR5cGUsXG4gICAgICAgIGVycm9yOiBuZXcgRXJyb3JSZXNwb25zZShzdGF0dXMsIHJlc3VsdC5zdGF0dXNUZXh0LCBkYXRhKSxcbiAgICAgICAgaGVhZGVyczogcmVzdWx0LmhlYWRlcnNcbiAgICAgIH07XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICB0eXBlOiBSZXN1bHRUeXBlLmRhdGEsXG4gICAgICBkYXRhLFxuICAgICAgc3RhdHVzQ29kZTogcmVzdWx0LnN0YXR1cyxcbiAgICAgIGhlYWRlcnM6IHJlc3VsdC5oZWFkZXJzXG4gICAgfTtcbiAgfVxuICBpZiAocmVzdWx0VHlwZSA9PT0gUmVzdWx0VHlwZS5lcnJvcikge1xuICAgIHJldHVybiB7XG4gICAgICB0eXBlOiByZXN1bHRUeXBlLFxuICAgICAgZXJyb3I6IHJlc3VsdFxuICAgIH07XG4gIH1cbiAgaWYgKGlzRGVmZXJyZWREYXRhKHJlc3VsdCkpIHtcbiAgICB2YXIgX3Jlc3VsdCRpbml0LCBfcmVzdWx0JGluaXQyO1xuICAgIHJldHVybiB7XG4gICAgICB0eXBlOiBSZXN1bHRUeXBlLmRlZmVycmVkLFxuICAgICAgZGVmZXJyZWREYXRhOiByZXN1bHQsXG4gICAgICBzdGF0dXNDb2RlOiAoX3Jlc3VsdCRpbml0ID0gcmVzdWx0LmluaXQpID09IG51bGwgPyB2b2lkIDAgOiBfcmVzdWx0JGluaXQuc3RhdHVzLFxuICAgICAgaGVhZGVyczogKChfcmVzdWx0JGluaXQyID0gcmVzdWx0LmluaXQpID09IG51bGwgPyB2b2lkIDAgOiBfcmVzdWx0JGluaXQyLmhlYWRlcnMpICYmIG5ldyBIZWFkZXJzKHJlc3VsdC5pbml0LmhlYWRlcnMpXG4gICAgfTtcbiAgfVxuICByZXR1cm4ge1xuICAgIHR5cGU6IFJlc3VsdFR5cGUuZGF0YSxcbiAgICBkYXRhOiByZXN1bHRcbiAgfTtcbn1cbi8vIFV0aWxpdHkgbWV0aG9kIGZvciBjcmVhdGluZyB0aGUgUmVxdWVzdCBpbnN0YW5jZXMgZm9yIGxvYWRlcnMvYWN0aW9ucyBkdXJpbmdcbi8vIGNsaWVudC1zaWRlIG5hdmlnYXRpb25zIGFuZCBmZXRjaGVzLiAgRHVyaW5nIFNTUiB3ZSB3aWxsIGFsd2F5cyBoYXZlIGFcbi8vIFJlcXVlc3QgaW5zdGFuY2UgZnJvbSB0aGUgc3RhdGljIGhhbmRsZXIgKHF1ZXJ5L3F1ZXJ5Um91dGUpXG5mdW5jdGlvbiBjcmVhdGVDbGllbnRTaWRlUmVxdWVzdChoaXN0b3J5LCBsb2NhdGlvbiwgc2lnbmFsLCBzdWJtaXNzaW9uKSB7XG4gIGxldCB1cmwgPSBoaXN0b3J5LmNyZWF0ZVVSTChzdHJpcEhhc2hGcm9tUGF0aChsb2NhdGlvbikpLnRvU3RyaW5nKCk7XG4gIGxldCBpbml0ID0ge1xuICAgIHNpZ25hbFxuICB9O1xuICBpZiAoc3VibWlzc2lvbiAmJiBpc011dGF0aW9uTWV0aG9kKHN1Ym1pc3Npb24uZm9ybU1ldGhvZCkpIHtcbiAgICBsZXQge1xuICAgICAgZm9ybU1ldGhvZCxcbiAgICAgIGZvcm1FbmNUeXBlXG4gICAgfSA9IHN1Ym1pc3Npb247XG4gICAgLy8gRGlkbid0IHRoaW5rIHdlIG5lZWRlZCB0aGlzIGJ1dCBpdCB0dXJucyBvdXQgdW5saWtlIG90aGVyIG1ldGhvZHMsIHBhdGNoXG4gICAgLy8gd29uJ3QgYmUgcHJvcGVybHkgbm9ybWFsaXplZCB0byB1cHBlcmNhc2UgYW5kIHJlc3VsdHMgaW4gYSA0MDUgZXJyb3IuXG4gICAgLy8gU2VlOiBodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jY29uY2VwdC1tZXRob2RcbiAgICBpbml0Lm1ldGhvZCA9IGZvcm1NZXRob2QudG9VcHBlckNhc2UoKTtcbiAgICBpZiAoZm9ybUVuY1R5cGUgPT09IFwiYXBwbGljYXRpb24vanNvblwiKSB7XG4gICAgICBpbml0LmhlYWRlcnMgPSBuZXcgSGVhZGVycyh7XG4gICAgICAgIFwiQ29udGVudC1UeXBlXCI6IGZvcm1FbmNUeXBlXG4gICAgICB9KTtcbiAgICAgIGluaXQuYm9keSA9IEpTT04uc3RyaW5naWZ5KHN1Ym1pc3Npb24uanNvbik7XG4gICAgfSBlbHNlIGlmIChmb3JtRW5jVHlwZSA9PT0gXCJ0ZXh0L3BsYWluXCIpIHtcbiAgICAgIC8vIENvbnRlbnQtVHlwZSBpcyBpbmZlcnJlZCAoaHR0cHM6Ly9mZXRjaC5zcGVjLndoYXR3Zy5vcmcvI2RvbS1yZXF1ZXN0KVxuICAgICAgaW5pdC5ib2R5ID0gc3VibWlzc2lvbi50ZXh0O1xuICAgIH0gZWxzZSBpZiAoZm9ybUVuY1R5cGUgPT09IFwiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkXCIgJiYgc3VibWlzc2lvbi5mb3JtRGF0YSkge1xuICAgICAgLy8gQ29udGVudC1UeXBlIGlzIGluZmVycmVkIChodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jZG9tLXJlcXVlc3QpXG4gICAgICBpbml0LmJvZHkgPSBjb252ZXJ0Rm9ybURhdGFUb1NlYXJjaFBhcmFtcyhzdWJtaXNzaW9uLmZvcm1EYXRhKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQ29udGVudC1UeXBlIGlzIGluZmVycmVkIChodHRwczovL2ZldGNoLnNwZWMud2hhdHdnLm9yZy8jZG9tLXJlcXVlc3QpXG4gICAgICBpbml0LmJvZHkgPSBzdWJtaXNzaW9uLmZvcm1EYXRhO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbmV3IFJlcXVlc3QodXJsLCBpbml0KTtcbn1cbmZ1bmN0aW9uIGNvbnZlcnRGb3JtRGF0YVRvU2VhcmNoUGFyYW1zKGZvcm1EYXRhKSB7XG4gIGxldCBzZWFyY2hQYXJhbXMgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKCk7XG4gIGZvciAobGV0IFtrZXksIHZhbHVlXSBvZiBmb3JtRGF0YS5lbnRyaWVzKCkpIHtcbiAgICAvLyBodHRwczovL2h0bWwuc3BlYy53aGF0d2cub3JnL211bHRpcGFnZS9mb3JtLWNvbnRyb2wtaW5mcmFzdHJ1Y3R1cmUuaHRtbCNjb252ZXJ0aW5nLWFuLWVudHJ5LWxpc3QtdG8tYS1saXN0LW9mLW5hbWUtdmFsdWUtcGFpcnNcbiAgICBzZWFyY2hQYXJhbXMuYXBwZW5kKGtleSwgdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiID8gdmFsdWUgOiB2YWx1ZS5uYW1lKTtcbiAgfVxuICByZXR1cm4gc2VhcmNoUGFyYW1zO1xufVxuZnVuY3Rpb24gY29udmVydFNlYXJjaFBhcmFtc1RvRm9ybURhdGEoc2VhcmNoUGFyYW1zKSB7XG4gIGxldCBmb3JtRGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICBmb3IgKGxldCBba2V5LCB2YWx1ZV0gb2Ygc2VhcmNoUGFyYW1zLmVudHJpZXMoKSkge1xuICAgIGZvcm1EYXRhLmFwcGVuZChrZXksIHZhbHVlKTtcbiAgfVxuICByZXR1cm4gZm9ybURhdGE7XG59XG5mdW5jdGlvbiBwcm9jZXNzUm91dGVMb2FkZXJEYXRhKG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdFcnJvciwgYWN0aXZlRGVmZXJyZWRzKSB7XG4gIC8vIEZpbGwgaW4gbG9hZGVyRGF0YS9lcnJvcnMgZnJvbSBvdXIgbG9hZGVyc1xuICBsZXQgbG9hZGVyRGF0YSA9IHt9O1xuICBsZXQgZXJyb3JzID0gbnVsbDtcbiAgbGV0IHN0YXR1c0NvZGU7XG4gIGxldCBmb3VuZEVycm9yID0gZmFsc2U7XG4gIGxldCBsb2FkZXJIZWFkZXJzID0ge307XG4gIC8vIFByb2Nlc3MgbG9hZGVyIHJlc3VsdHMgaW50byBzdGF0ZS5sb2FkZXJEYXRhL3N0YXRlLmVycm9yc1xuICByZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaW5kZXgpID0+IHtcbiAgICBsZXQgaWQgPSBtYXRjaGVzVG9Mb2FkW2luZGV4XS5yb3V0ZS5pZDtcbiAgICBpbnZhcmlhbnQoIWlzUmVkaXJlY3RSZXN1bHQocmVzdWx0KSwgXCJDYW5ub3QgaGFuZGxlIHJlZGlyZWN0IHJlc3VsdHMgaW4gcHJvY2Vzc0xvYWRlckRhdGFcIik7XG4gICAgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkge1xuICAgICAgLy8gTG9vayB1cHdhcmRzIGZyb20gdGhlIG1hdGNoZWQgcm91dGUgZm9yIHRoZSBjbG9zZXN0IGFuY2VzdG9yXG4gICAgICAvLyBlcnJvciBib3VuZGFyeSwgZGVmYXVsdGluZyB0byB0aGUgcm9vdCBtYXRjaFxuICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIGlkKTtcbiAgICAgIGxldCBlcnJvciA9IHJlc3VsdC5lcnJvcjtcbiAgICAgIC8vIElmIHdlIGhhdmUgYSBwZW5kaW5nIGFjdGlvbiBlcnJvciwgd2UgcmVwb3J0IGl0IGF0IHRoZSBoaWdoZXN0LXJvdXRlXG4gICAgICAvLyB0aGF0IHRocm93cyBhIGxvYWRlciBlcnJvciwgYW5kIHRoZW4gY2xlYXIgaXQgb3V0IHRvIGluZGljYXRlIHRoYXRcbiAgICAgIC8vIGl0IHdhcyBjb25zdW1lZFxuICAgICAgaWYgKHBlbmRpbmdFcnJvcikge1xuICAgICAgICBlcnJvciA9IE9iamVjdC52YWx1ZXMocGVuZGluZ0Vycm9yKVswXTtcbiAgICAgICAgcGVuZGluZ0Vycm9yID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgZXJyb3JzID0gZXJyb3JzIHx8IHt9O1xuICAgICAgLy8gUHJlZmVyIGhpZ2hlciBlcnJvciB2YWx1ZXMgaWYgbG93ZXIgZXJyb3JzIGJ1YmJsZSB0byB0aGUgc2FtZSBib3VuZGFyeVxuICAgICAgaWYgKGVycm9yc1tib3VuZGFyeU1hdGNoLnJvdXRlLmlkXSA9PSBudWxsKSB7XG4gICAgICAgIGVycm9yc1tib3VuZGFyeU1hdGNoLnJvdXRlLmlkXSA9IGVycm9yO1xuICAgICAgfVxuICAgICAgLy8gQ2xlYXIgb3VyIGFueSBwcmlvciBsb2FkZXJEYXRhIGZvciB0aGUgdGhyb3dpbmcgcm91dGVcbiAgICAgIGxvYWRlckRhdGFbaWRdID0gdW5kZWZpbmVkO1xuICAgICAgLy8gT25jZSB3ZSBmaW5kIG91ciBmaXJzdCAoaGlnaGVzdCkgZXJyb3IsIHdlIHNldCB0aGUgc3RhdHVzIGNvZGUgYW5kXG4gICAgICAvLyBwcmV2ZW50IGRlZXBlciBzdGF0dXMgY29kZXMgZnJvbSBvdmVycmlkaW5nXG4gICAgICBpZiAoIWZvdW5kRXJyb3IpIHtcbiAgICAgICAgZm91bmRFcnJvciA9IHRydWU7XG4gICAgICAgIHN0YXR1c0NvZGUgPSBpc1JvdXRlRXJyb3JSZXNwb25zZShyZXN1bHQuZXJyb3IpID8gcmVzdWx0LmVycm9yLnN0YXR1cyA6IDUwMDtcbiAgICAgIH1cbiAgICAgIGlmIChyZXN1bHQuaGVhZGVycykge1xuICAgICAgICBsb2FkZXJIZWFkZXJzW2lkXSA9IHJlc3VsdC5oZWFkZXJzO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoaXNEZWZlcnJlZFJlc3VsdChyZXN1bHQpKSB7XG4gICAgICAgIGFjdGl2ZURlZmVycmVkcy5zZXQoaWQsIHJlc3VsdC5kZWZlcnJlZERhdGEpO1xuICAgICAgICBsb2FkZXJEYXRhW2lkXSA9IHJlc3VsdC5kZWZlcnJlZERhdGEuZGF0YTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvYWRlckRhdGFbaWRdID0gcmVzdWx0LmRhdGE7XG4gICAgICB9XG4gICAgICAvLyBFcnJvciBzdGF0dXMgY29kZXMgYWx3YXlzIG92ZXJyaWRlIHN1Y2Nlc3Mgc3RhdHVzIGNvZGVzLCBidXQgaWYgYWxsXG4gICAgICAvLyBsb2FkZXJzIGFyZSBzdWNjZXNzZnVsIHdlIHRha2UgdGhlIGRlZXBlc3Qgc3RhdHVzIGNvZGUuXG4gICAgICBpZiAocmVzdWx0LnN0YXR1c0NvZGUgIT0gbnVsbCAmJiByZXN1bHQuc3RhdHVzQ29kZSAhPT0gMjAwICYmICFmb3VuZEVycm9yKSB7XG4gICAgICAgIHN0YXR1c0NvZGUgPSByZXN1bHQuc3RhdHVzQ29kZTtcbiAgICAgIH1cbiAgICAgIGlmIChyZXN1bHQuaGVhZGVycykge1xuICAgICAgICBsb2FkZXJIZWFkZXJzW2lkXSA9IHJlc3VsdC5oZWFkZXJzO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIC8vIElmIHdlIGRpZG4ndCBjb25zdW1lIHRoZSBwZW5kaW5nIGFjdGlvbiBlcnJvciAoaS5lLiwgYWxsIGxvYWRlcnNcbiAgLy8gcmVzb2x2ZWQpLCB0aGVuIGNvbnN1bWUgaXQgaGVyZS4gIEFsc28gY2xlYXIgb3V0IGFueSBsb2FkZXJEYXRhIGZvciB0aGVcbiAgLy8gdGhyb3dpbmcgcm91dGVcbiAgaWYgKHBlbmRpbmdFcnJvcikge1xuICAgIGVycm9ycyA9IHBlbmRpbmdFcnJvcjtcbiAgICBsb2FkZXJEYXRhW09iamVjdC5rZXlzKHBlbmRpbmdFcnJvcilbMF1dID0gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiB7XG4gICAgbG9hZGVyRGF0YSxcbiAgICBlcnJvcnMsXG4gICAgc3RhdHVzQ29kZTogc3RhdHVzQ29kZSB8fCAyMDAsXG4gICAgbG9hZGVySGVhZGVyc1xuICB9O1xufVxuZnVuY3Rpb24gcHJvY2Vzc0xvYWRlckRhdGEoc3RhdGUsIG1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHBlbmRpbmdFcnJvciwgcmV2YWxpZGF0aW5nRmV0Y2hlcnMsIGZldGNoZXJSZXN1bHRzLCBhY3RpdmVEZWZlcnJlZHMpIHtcbiAgbGV0IHtcbiAgICBsb2FkZXJEYXRhLFxuICAgIGVycm9yc1xuICB9ID0gcHJvY2Vzc1JvdXRlTG9hZGVyRGF0YShtYXRjaGVzLCBtYXRjaGVzVG9Mb2FkLCByZXN1bHRzLCBwZW5kaW5nRXJyb3IsIGFjdGl2ZURlZmVycmVkcyk7XG4gIC8vIFByb2Nlc3MgcmVzdWx0cyBmcm9tIG91ciByZXZhbGlkYXRpbmcgZmV0Y2hlcnNcbiAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHJldmFsaWRhdGluZ0ZldGNoZXJzLmxlbmd0aDsgaW5kZXgrKykge1xuICAgIGxldCB7XG4gICAgICBrZXksXG4gICAgICBtYXRjaCxcbiAgICAgIGNvbnRyb2xsZXJcbiAgICB9ID0gcmV2YWxpZGF0aW5nRmV0Y2hlcnNbaW5kZXhdO1xuICAgIGludmFyaWFudChmZXRjaGVyUmVzdWx0cyAhPT0gdW5kZWZpbmVkICYmIGZldGNoZXJSZXN1bHRzW2luZGV4XSAhPT0gdW5kZWZpbmVkLCBcIkRpZCBub3QgZmluZCBjb3JyZXNwb25kaW5nIGZldGNoZXIgcmVzdWx0XCIpO1xuICAgIGxldCByZXN1bHQgPSBmZXRjaGVyUmVzdWx0c1tpbmRleF07XG4gICAgLy8gUHJvY2VzcyBmZXRjaGVyIG5vbi1yZWRpcmVjdCBlcnJvcnNcbiAgICBpZiAoY29udHJvbGxlciAmJiBjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAvLyBOb3RoaW5nIHRvIGRvIGZvciBhYm9ydGVkIGZldGNoZXJzXG4gICAgICBjb250aW51ZTtcbiAgICB9IGVsc2UgaWYgKGlzRXJyb3JSZXN1bHQocmVzdWx0KSkge1xuICAgICAgbGV0IGJvdW5kYXJ5TWF0Y2ggPSBmaW5kTmVhcmVzdEJvdW5kYXJ5KHN0YXRlLm1hdGNoZXMsIG1hdGNoID09IG51bGwgPyB2b2lkIDAgOiBtYXRjaC5yb3V0ZS5pZCk7XG4gICAgICBpZiAoIShlcnJvcnMgJiYgZXJyb3JzW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdKSkge1xuICAgICAgICBlcnJvcnMgPSBfZXh0ZW5kcyh7fSwgZXJyb3JzLCB7XG4gICAgICAgICAgW2JvdW5kYXJ5TWF0Y2gucm91dGUuaWRdOiByZXN1bHQuZXJyb3JcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBzdGF0ZS5mZXRjaGVycy5kZWxldGUoa2V5KTtcbiAgICB9IGVsc2UgaWYgKGlzUmVkaXJlY3RSZXN1bHQocmVzdWx0KSkge1xuICAgICAgLy8gU2hvdWxkIG5ldmVyIGdldCBoZXJlLCByZWRpcmVjdHMgc2hvdWxkIGdldCBwcm9jZXNzZWQgYWJvdmUsIGJ1dCB3ZVxuICAgICAgLy8ga2VlcCB0aGlzIHRvIHR5cGUgbmFycm93IHRvIGEgc3VjY2VzcyByZXN1bHQgaW4gdGhlIGVsc2VcbiAgICAgIGludmFyaWFudChmYWxzZSwgXCJVbmhhbmRsZWQgZmV0Y2hlciByZXZhbGlkYXRpb24gcmVkaXJlY3RcIik7XG4gICAgfSBlbHNlIGlmIChpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIC8vIFNob3VsZCBuZXZlciBnZXQgaGVyZSwgZGVmZXJyZWQgZGF0YSBzaG91bGQgYmUgYXdhaXRlZCBmb3IgZmV0Y2hlcnNcbiAgICAgIC8vIGluIHJlc29sdmVEZWZlcnJlZFJlc3VsdHNcbiAgICAgIGludmFyaWFudChmYWxzZSwgXCJVbmhhbmRsZWQgZmV0Y2hlciBkZWZlcnJlZCBkYXRhXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgZG9uZUZldGNoZXIgPSBnZXREb25lRmV0Y2hlcihyZXN1bHQuZGF0YSk7XG4gICAgICBzdGF0ZS5mZXRjaGVycy5zZXQoa2V5LCBkb25lRmV0Y2hlcik7XG4gICAgfVxuICB9XG4gIHJldHVybiB7XG4gICAgbG9hZGVyRGF0YSxcbiAgICBlcnJvcnNcbiAgfTtcbn1cbmZ1bmN0aW9uIG1lcmdlTG9hZGVyRGF0YShsb2FkZXJEYXRhLCBuZXdMb2FkZXJEYXRhLCBtYXRjaGVzLCBlcnJvcnMpIHtcbiAgbGV0IG1lcmdlZExvYWRlckRhdGEgPSBfZXh0ZW5kcyh7fSwgbmV3TG9hZGVyRGF0YSk7XG4gIGZvciAobGV0IG1hdGNoIG9mIG1hdGNoZXMpIHtcbiAgICBsZXQgaWQgPSBtYXRjaC5yb3V0ZS5pZDtcbiAgICBpZiAobmV3TG9hZGVyRGF0YS5oYXNPd25Qcm9wZXJ0eShpZCkpIHtcbiAgICAgIGlmIChuZXdMb2FkZXJEYXRhW2lkXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIG1lcmdlZExvYWRlckRhdGFbaWRdID0gbmV3TG9hZGVyRGF0YVtpZF07XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChsb2FkZXJEYXRhW2lkXSAhPT0gdW5kZWZpbmVkICYmIG1hdGNoLnJvdXRlLmxvYWRlcikge1xuICAgICAgLy8gUHJlc2VydmUgZXhpc3Rpbmcga2V5cyBub3QgaW5jbHVkZWQgaW4gbmV3TG9hZGVyRGF0YSBhbmQgd2hlcmUgYSBsb2FkZXJcbiAgICAgIC8vIHdhc24ndCByZW1vdmVkIGJ5IEhNUlxuICAgICAgbWVyZ2VkTG9hZGVyRGF0YVtpZF0gPSBsb2FkZXJEYXRhW2lkXTtcbiAgICB9XG4gICAgaWYgKGVycm9ycyAmJiBlcnJvcnMuaGFzT3duUHJvcGVydHkoaWQpKSB7XG4gICAgICAvLyBEb24ndCBrZWVwIGFueSBsb2FkZXIgZGF0YSBiZWxvdyB0aGUgYm91bmRhcnlcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbWVyZ2VkTG9hZGVyRGF0YTtcbn1cbi8vIEZpbmQgdGhlIG5lYXJlc3QgZXJyb3IgYm91bmRhcnksIGxvb2tpbmcgdXB3YXJkcyBmcm9tIHRoZSBsZWFmIHJvdXRlIChvciB0aGVcbi8vIHJvdXRlIHNwZWNpZmllZCBieSByb3V0ZUlkKSBmb3IgdGhlIGNsb3Nlc3QgYW5jZXN0b3IgZXJyb3IgYm91bmRhcnksXG4vLyBkZWZhdWx0aW5nIHRvIHRoZSByb290IG1hdGNoXG5mdW5jdGlvbiBmaW5kTmVhcmVzdEJvdW5kYXJ5KG1hdGNoZXMsIHJvdXRlSWQpIHtcbiAgbGV0IGVsaWdpYmxlTWF0Y2hlcyA9IHJvdXRlSWQgPyBtYXRjaGVzLnNsaWNlKDAsIG1hdGNoZXMuZmluZEluZGV4KG0gPT4gbS5yb3V0ZS5pZCA9PT0gcm91dGVJZCkgKyAxKSA6IFsuLi5tYXRjaGVzXTtcbiAgcmV0dXJuIGVsaWdpYmxlTWF0Y2hlcy5yZXZlcnNlKCkuZmluZChtID0+IG0ucm91dGUuaGFzRXJyb3JCb3VuZGFyeSA9PT0gdHJ1ZSkgfHwgbWF0Y2hlc1swXTtcbn1cbmZ1bmN0aW9uIGdldFNob3J0Q2lyY3VpdE1hdGNoZXMocm91dGVzKSB7XG4gIC8vIFByZWZlciBhIHJvb3QgbGF5b3V0IHJvdXRlIGlmIHByZXNlbnQsIG90aGVyd2lzZSBzaGltIGluIGEgcm91dGUgb2JqZWN0XG4gIGxldCByb3V0ZSA9IHJvdXRlcy5maW5kKHIgPT4gci5pbmRleCB8fCAhci5wYXRoIHx8IHIucGF0aCA9PT0gXCIvXCIpIHx8IHtcbiAgICBpZDogXCJfX3NoaW0tZXJyb3Itcm91dGVfX1wiXG4gIH07XG4gIHJldHVybiB7XG4gICAgbWF0Y2hlczogW3tcbiAgICAgIHBhcmFtczoge30sXG4gICAgICBwYXRobmFtZTogXCJcIixcbiAgICAgIHBhdGhuYW1lQmFzZTogXCJcIixcbiAgICAgIHJvdXRlXG4gICAgfV0sXG4gICAgcm91dGVcbiAgfTtcbn1cbmZ1bmN0aW9uIGdldEludGVybmFsUm91dGVyRXJyb3Ioc3RhdHVzLCBfdGVtcDQpIHtcbiAgbGV0IHtcbiAgICBwYXRobmFtZSxcbiAgICByb3V0ZUlkLFxuICAgIG1ldGhvZCxcbiAgICB0eXBlXG4gIH0gPSBfdGVtcDQgPT09IHZvaWQgMCA/IHt9IDogX3RlbXA0O1xuICBsZXQgc3RhdHVzVGV4dCA9IFwiVW5rbm93biBTZXJ2ZXIgRXJyb3JcIjtcbiAgbGV0IGVycm9yTWVzc2FnZSA9IFwiVW5rbm93biBAcmVtaXgtcnVuL3JvdXRlciBlcnJvclwiO1xuICBpZiAoc3RhdHVzID09PSA0MDApIHtcbiAgICBzdGF0dXNUZXh0ID0gXCJCYWQgUmVxdWVzdFwiO1xuICAgIGlmIChtZXRob2QgJiYgcGF0aG5hbWUgJiYgcm91dGVJZCkge1xuICAgICAgZXJyb3JNZXNzYWdlID0gXCJZb3UgbWFkZSBhIFwiICsgbWV0aG9kICsgXCIgcmVxdWVzdCB0byBcXFwiXCIgKyBwYXRobmFtZSArIFwiXFxcIiBidXQgXCIgKyAoXCJkaWQgbm90IHByb3ZpZGUgYSBgbG9hZGVyYCBmb3Igcm91dGUgXFxcIlwiICsgcm91dGVJZCArIFwiXFxcIiwgXCIpICsgXCJzbyB0aGVyZSBpcyBubyB3YXkgdG8gaGFuZGxlIHRoZSByZXF1ZXN0LlwiO1xuICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gXCJkZWZlci1hY3Rpb25cIikge1xuICAgICAgZXJyb3JNZXNzYWdlID0gXCJkZWZlcigpIGlzIG5vdCBzdXBwb3J0ZWQgaW4gYWN0aW9uc1wiO1xuICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gXCJpbnZhbGlkLWJvZHlcIikge1xuICAgICAgZXJyb3JNZXNzYWdlID0gXCJVbmFibGUgdG8gZW5jb2RlIHN1Ym1pc3Npb24gYm9keVwiO1xuICAgIH1cbiAgfSBlbHNlIGlmIChzdGF0dXMgPT09IDQwMykge1xuICAgIHN0YXR1c1RleHQgPSBcIkZvcmJpZGRlblwiO1xuICAgIGVycm9yTWVzc2FnZSA9IFwiUm91dGUgXFxcIlwiICsgcm91dGVJZCArIFwiXFxcIiBkb2VzIG5vdCBtYXRjaCBVUkwgXFxcIlwiICsgcGF0aG5hbWUgKyBcIlxcXCJcIjtcbiAgfSBlbHNlIGlmIChzdGF0dXMgPT09IDQwNCkge1xuICAgIHN0YXR1c1RleHQgPSBcIk5vdCBGb3VuZFwiO1xuICAgIGVycm9yTWVzc2FnZSA9IFwiTm8gcm91dGUgbWF0Y2hlcyBVUkwgXFxcIlwiICsgcGF0aG5hbWUgKyBcIlxcXCJcIjtcbiAgfSBlbHNlIGlmIChzdGF0dXMgPT09IDQwNSkge1xuICAgIHN0YXR1c1RleHQgPSBcIk1ldGhvZCBOb3QgQWxsb3dlZFwiO1xuICAgIGlmIChtZXRob2QgJiYgcGF0aG5hbWUgJiYgcm91dGVJZCkge1xuICAgICAgZXJyb3JNZXNzYWdlID0gXCJZb3UgbWFkZSBhIFwiICsgbWV0aG9kLnRvVXBwZXJDYXNlKCkgKyBcIiByZXF1ZXN0IHRvIFxcXCJcIiArIHBhdGhuYW1lICsgXCJcXFwiIGJ1dCBcIiArIChcImRpZCBub3QgcHJvdmlkZSBhbiBgYWN0aW9uYCBmb3Igcm91dGUgXFxcIlwiICsgcm91dGVJZCArIFwiXFxcIiwgXCIpICsgXCJzbyB0aGVyZSBpcyBubyB3YXkgdG8gaGFuZGxlIHRoZSByZXF1ZXN0LlwiO1xuICAgIH0gZWxzZSBpZiAobWV0aG9kKSB7XG4gICAgICBlcnJvck1lc3NhZ2UgPSBcIkludmFsaWQgcmVxdWVzdCBtZXRob2QgXFxcIlwiICsgbWV0aG9kLnRvVXBwZXJDYXNlKCkgKyBcIlxcXCJcIjtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG5ldyBFcnJvclJlc3BvbnNlKHN0YXR1cyB8fCA1MDAsIHN0YXR1c1RleHQsIG5ldyBFcnJvcihlcnJvck1lc3NhZ2UpLCB0cnVlKTtcbn1cbi8vIEZpbmQgYW55IHJldHVybmVkIHJlZGlyZWN0IGVycm9ycywgc3RhcnRpbmcgZnJvbSB0aGUgbG93ZXN0IG1hdGNoXG5mdW5jdGlvbiBmaW5kUmVkaXJlY3QocmVzdWx0cykge1xuICBmb3IgKGxldCBpID0gcmVzdWx0cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgIGxldCByZXN1bHQgPSByZXN1bHRzW2ldO1xuICAgIGlmIChpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkpIHtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICB9XG59XG5mdW5jdGlvbiBzdHJpcEhhc2hGcm9tUGF0aChwYXRoKSB7XG4gIGxldCBwYXJzZWRQYXRoID0gdHlwZW9mIHBhdGggPT09IFwic3RyaW5nXCIgPyBwYXJzZVBhdGgocGF0aCkgOiBwYXRoO1xuICByZXR1cm4gY3JlYXRlUGF0aChfZXh0ZW5kcyh7fSwgcGFyc2VkUGF0aCwge1xuICAgIGhhc2g6IFwiXCJcbiAgfSkpO1xufVxuZnVuY3Rpb24gaXNIYXNoQ2hhbmdlT25seShhLCBiKSB7XG4gIGlmIChhLnBhdGhuYW1lICE9PSBiLnBhdGhuYW1lIHx8IGEuc2VhcmNoICE9PSBiLnNlYXJjaCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBpZiAoYS5oYXNoID09PSBcIlwiKSB7XG4gICAgLy8gL3BhZ2UgLT4gL3BhZ2UjaGFzaFxuICAgIHJldHVybiBiLmhhc2ggIT09IFwiXCI7XG4gIH0gZWxzZSBpZiAoYS5oYXNoID09PSBiLmhhc2gpIHtcbiAgICAvLyAvcGFnZSNoYXNoIC0+IC9wYWdlI2hhc2hcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBlbHNlIGlmIChiLmhhc2ggIT09IFwiXCIpIHtcbiAgICAvLyAvcGFnZSNoYXNoIC0+IC9wYWdlI290aGVyXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgLy8gSWYgdGhlIGhhc2ggaXMgcmVtb3ZlZCB0aGUgYnJvd3NlciB3aWxsIHJlLXBlcmZvcm0gYSByZXF1ZXN0IHRvIHRoZSBzZXJ2ZXJcbiAgLy8gL3BhZ2UjaGFzaCAtPiAvcGFnZVxuICByZXR1cm4gZmFsc2U7XG59XG5mdW5jdGlvbiBpc0RlZmVycmVkUmVzdWx0KHJlc3VsdCkge1xuICByZXR1cm4gcmVzdWx0LnR5cGUgPT09IFJlc3VsdFR5cGUuZGVmZXJyZWQ7XG59XG5mdW5jdGlvbiBpc0Vycm9yUmVzdWx0KHJlc3VsdCkge1xuICByZXR1cm4gcmVzdWx0LnR5cGUgPT09IFJlc3VsdFR5cGUuZXJyb3I7XG59XG5mdW5jdGlvbiBpc1JlZGlyZWN0UmVzdWx0KHJlc3VsdCkge1xuICByZXR1cm4gKHJlc3VsdCAmJiByZXN1bHQudHlwZSkgPT09IFJlc3VsdFR5cGUucmVkaXJlY3Q7XG59XG5mdW5jdGlvbiBpc0RlZmVycmVkRGF0YSh2YWx1ZSkge1xuICBsZXQgZGVmZXJyZWQgPSB2YWx1ZTtcbiAgcmV0dXJuIGRlZmVycmVkICYmIHR5cGVvZiBkZWZlcnJlZCA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgZGVmZXJyZWQuZGF0YSA9PT0gXCJvYmplY3RcIiAmJiB0eXBlb2YgZGVmZXJyZWQuc3Vic2NyaWJlID09PSBcImZ1bmN0aW9uXCIgJiYgdHlwZW9mIGRlZmVycmVkLmNhbmNlbCA9PT0gXCJmdW5jdGlvblwiICYmIHR5cGVvZiBkZWZlcnJlZC5yZXNvbHZlRGF0YSA9PT0gXCJmdW5jdGlvblwiO1xufVxuZnVuY3Rpb24gaXNSZXNwb25zZSh2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUgIT0gbnVsbCAmJiB0eXBlb2YgdmFsdWUuc3RhdHVzID09PSBcIm51bWJlclwiICYmIHR5cGVvZiB2YWx1ZS5zdGF0dXNUZXh0ID09PSBcInN0cmluZ1wiICYmIHR5cGVvZiB2YWx1ZS5oZWFkZXJzID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiB2YWx1ZS5ib2R5ICE9PSBcInVuZGVmaW5lZFwiO1xufVxuZnVuY3Rpb24gaXNSZWRpcmVjdFJlc3BvbnNlKHJlc3VsdCkge1xuICBpZiAoIWlzUmVzcG9uc2UocmVzdWx0KSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBsZXQgc3RhdHVzID0gcmVzdWx0LnN0YXR1cztcbiAgbGV0IGxvY2F0aW9uID0gcmVzdWx0LmhlYWRlcnMuZ2V0KFwiTG9jYXRpb25cIik7XG4gIHJldHVybiBzdGF0dXMgPj0gMzAwICYmIHN0YXR1cyA8PSAzOTkgJiYgbG9jYXRpb24gIT0gbnVsbDtcbn1cbmZ1bmN0aW9uIGlzUXVlcnlSb3V0ZVJlc3BvbnNlKG9iaikge1xuICByZXR1cm4gb2JqICYmIGlzUmVzcG9uc2Uob2JqLnJlc3BvbnNlKSAmJiAob2JqLnR5cGUgPT09IFJlc3VsdFR5cGUuZGF0YSB8fCBSZXN1bHRUeXBlLmVycm9yKTtcbn1cbmZ1bmN0aW9uIGlzVmFsaWRNZXRob2QobWV0aG9kKSB7XG4gIHJldHVybiB2YWxpZFJlcXVlc3RNZXRob2RzLmhhcyhtZXRob2QudG9Mb3dlckNhc2UoKSk7XG59XG5mdW5jdGlvbiBpc011dGF0aW9uTWV0aG9kKG1ldGhvZCkge1xuICByZXR1cm4gdmFsaWRNdXRhdGlvbk1ldGhvZHMuaGFzKG1ldGhvZC50b0xvd2VyQ2FzZSgpKTtcbn1cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVEZWZlcnJlZFJlc3VsdHMoY3VycmVudE1hdGNoZXMsIG1hdGNoZXNUb0xvYWQsIHJlc3VsdHMsIHNpZ25hbHMsIGlzRmV0Y2hlciwgY3VycmVudExvYWRlckRhdGEpIHtcbiAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IHJlc3VsdHMubGVuZ3RoOyBpbmRleCsrKSB7XG4gICAgbGV0IHJlc3VsdCA9IHJlc3VsdHNbaW5kZXhdO1xuICAgIGxldCBtYXRjaCA9IG1hdGNoZXNUb0xvYWRbaW5kZXhdO1xuICAgIC8vIElmIHdlIGRvbid0IGhhdmUgYSBtYXRjaCwgdGhlbiB3ZSBjYW4gaGF2ZSBhIGRlZmVycmVkIHJlc3VsdCB0byBkb1xuICAgIC8vIGFueXRoaW5nIHdpdGguICBUaGlzIGlzIGZvciByZXZhbGlkYXRpbmcgZmV0Y2hlcnMgd2hlcmUgdGhlIHJvdXRlIHdhc1xuICAgIC8vIHJlbW92ZWQgZHVyaW5nIEhNUlxuICAgIGlmICghbWF0Y2gpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBsZXQgY3VycmVudE1hdGNoID0gY3VycmVudE1hdGNoZXMuZmluZChtID0+IG0ucm91dGUuaWQgPT09IG1hdGNoLnJvdXRlLmlkKTtcbiAgICBsZXQgaXNSZXZhbGlkYXRpbmdMb2FkZXIgPSBjdXJyZW50TWF0Y2ggIT0gbnVsbCAmJiAhaXNOZXdSb3V0ZUluc3RhbmNlKGN1cnJlbnRNYXRjaCwgbWF0Y2gpICYmIChjdXJyZW50TG9hZGVyRGF0YSAmJiBjdXJyZW50TG9hZGVyRGF0YVttYXRjaC5yb3V0ZS5pZF0pICE9PSB1bmRlZmluZWQ7XG4gICAgaWYgKGlzRGVmZXJyZWRSZXN1bHQocmVzdWx0KSAmJiAoaXNGZXRjaGVyIHx8IGlzUmV2YWxpZGF0aW5nTG9hZGVyKSkge1xuICAgICAgLy8gTm90ZTogd2UgZG8gbm90IGhhdmUgdG8gdG91Y2ggYWN0aXZlRGVmZXJyZWRzIGhlcmUgc2luY2Ugd2UgcmFjZSB0aGVtXG4gICAgICAvLyBhZ2FpbnN0IHRoZSBzaWduYWwgaW4gcmVzb2x2ZURlZmVycmVkRGF0YSBhbmQgdGhleSdsbCBnZXQgYWJvcnRlZFxuICAgICAgLy8gdGhlcmUgaWYgbmVlZGVkXG4gICAgICBsZXQgc2lnbmFsID0gc2lnbmFsc1tpbmRleF07XG4gICAgICBpbnZhcmlhbnQoc2lnbmFsLCBcIkV4cGVjdGVkIGFuIEFib3J0U2lnbmFsIGZvciByZXZhbGlkYXRpbmcgZmV0Y2hlciBkZWZlcnJlZCByZXN1bHRcIik7XG4gICAgICBhd2FpdCByZXNvbHZlRGVmZXJyZWREYXRhKHJlc3VsdCwgc2lnbmFsLCBpc0ZldGNoZXIpLnRoZW4ocmVzdWx0ID0+IHtcbiAgICAgICAgaWYgKHJlc3VsdCkge1xuICAgICAgICAgIHJlc3VsdHNbaW5kZXhdID0gcmVzdWx0IHx8IHJlc3VsdHNbaW5kZXhdO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVEZWZlcnJlZERhdGEocmVzdWx0LCBzaWduYWwsIHVud3JhcCkge1xuICBpZiAodW53cmFwID09PSB2b2lkIDApIHtcbiAgICB1bndyYXAgPSBmYWxzZTtcbiAgfVxuICBsZXQgYWJvcnRlZCA9IGF3YWl0IHJlc3VsdC5kZWZlcnJlZERhdGEucmVzb2x2ZURhdGEoc2lnbmFsKTtcbiAgaWYgKGFib3J0ZWQpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKHVud3JhcCkge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0eXBlOiBSZXN1bHRUeXBlLmRhdGEsXG4gICAgICAgIGRhdGE6IHJlc3VsdC5kZWZlcnJlZERhdGEudW53cmFwcGVkRGF0YVxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBIYW5kbGUgYW55IFRyYWNrZWRQcm9taXNlLl9lcnJvciB2YWx1ZXMgZW5jb3VudGVyZWQgd2hpbGUgdW53cmFwcGluZ1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdHlwZTogUmVzdWx0VHlwZS5lcnJvcixcbiAgICAgICAgZXJyb3I6IGVcbiAgICAgIH07XG4gICAgfVxuICB9XG4gIHJldHVybiB7XG4gICAgdHlwZTogUmVzdWx0VHlwZS5kYXRhLFxuICAgIGRhdGE6IHJlc3VsdC5kZWZlcnJlZERhdGEuZGF0YVxuICB9O1xufVxuZnVuY3Rpb24gaGFzTmFrZWRJbmRleFF1ZXJ5KHNlYXJjaCkge1xuICByZXR1cm4gbmV3IFVSTFNlYXJjaFBhcmFtcyhzZWFyY2gpLmdldEFsbChcImluZGV4XCIpLnNvbWUodiA9PiB2ID09PSBcIlwiKTtcbn1cbi8vIE5vdGU6IFRoaXMgc2hvdWxkIG1hdGNoIHRoZSBmb3JtYXQgZXhwb3J0ZWQgYnkgdXNlTWF0Y2hlcywgc28gaWYgeW91IGNoYW5nZVxuLy8gdGhpcyBwbGVhc2UgYWxzbyBjaGFuZ2UgdGhhdCA6KSAgRXZlbnR1YWxseSB3ZSdsbCBEUlkgdGhpcyB1cFxuZnVuY3Rpb24gY3JlYXRlVXNlTWF0Y2hlc01hdGNoKG1hdGNoLCBsb2FkZXJEYXRhKSB7XG4gIGxldCB7XG4gICAgcm91dGUsXG4gICAgcGF0aG5hbWUsXG4gICAgcGFyYW1zXG4gIH0gPSBtYXRjaDtcbiAgcmV0dXJuIHtcbiAgICBpZDogcm91dGUuaWQsXG4gICAgcGF0aG5hbWUsXG4gICAgcGFyYW1zLFxuICAgIGRhdGE6IGxvYWRlckRhdGFbcm91dGUuaWRdLFxuICAgIGhhbmRsZTogcm91dGUuaGFuZGxlXG4gIH07XG59XG5mdW5jdGlvbiBnZXRUYXJnZXRNYXRjaChtYXRjaGVzLCBsb2NhdGlvbikge1xuICBsZXQgc2VhcmNoID0gdHlwZW9mIGxvY2F0aW9uID09PSBcInN0cmluZ1wiID8gcGFyc2VQYXRoKGxvY2F0aW9uKS5zZWFyY2ggOiBsb2NhdGlvbi5zZWFyY2g7XG4gIGlmIChtYXRjaGVzW21hdGNoZXMubGVuZ3RoIC0gMV0ucm91dGUuaW5kZXggJiYgaGFzTmFrZWRJbmRleFF1ZXJ5KHNlYXJjaCB8fCBcIlwiKSkge1xuICAgIC8vIFJldHVybiB0aGUgbGVhZiBpbmRleCByb3V0ZSB3aGVuIGluZGV4IGlzIHByZXNlbnRcbiAgICByZXR1cm4gbWF0Y2hlc1ttYXRjaGVzLmxlbmd0aCAtIDFdO1xuICB9XG4gIC8vIE90aGVyd2lzZSBncmFiIHRoZSBkZWVwZXN0IFwicGF0aCBjb250cmlidXRpbmdcIiBtYXRjaCAoaWdub3JpbmcgaW5kZXggYW5kXG4gIC8vIHBhdGhsZXNzIGxheW91dCByb3V0ZXMpXG4gIGxldCBwYXRoTWF0Y2hlcyA9IGdldFBhdGhDb250cmlidXRpbmdNYXRjaGVzKG1hdGNoZXMpO1xuICByZXR1cm4gcGF0aE1hdGNoZXNbcGF0aE1hdGNoZXMubGVuZ3RoIC0gMV07XG59XG5mdW5jdGlvbiBnZXRTdWJtaXNzaW9uRnJvbU5hdmlnYXRpb24obmF2aWdhdGlvbikge1xuICBsZXQge1xuICAgIGZvcm1NZXRob2QsXG4gICAgZm9ybUFjdGlvbixcbiAgICBmb3JtRW5jVHlwZSxcbiAgICB0ZXh0LFxuICAgIGZvcm1EYXRhLFxuICAgIGpzb25cbiAgfSA9IG5hdmlnYXRpb247XG4gIGlmICghZm9ybU1ldGhvZCB8fCAhZm9ybUFjdGlvbiB8fCAhZm9ybUVuY1R5cGUpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKHRleHQgIT0gbnVsbCkge1xuICAgIHJldHVybiB7XG4gICAgICBmb3JtTWV0aG9kLFxuICAgICAgZm9ybUFjdGlvbixcbiAgICAgIGZvcm1FbmNUeXBlLFxuICAgICAgZm9ybURhdGE6IHVuZGVmaW5lZCxcbiAgICAgIGpzb246IHVuZGVmaW5lZCxcbiAgICAgIHRleHRcbiAgICB9O1xuICB9IGVsc2UgaWYgKGZvcm1EYXRhICE9IG51bGwpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybU1ldGhvZCxcbiAgICAgIGZvcm1BY3Rpb24sXG4gICAgICBmb3JtRW5jVHlwZSxcbiAgICAgIGZvcm1EYXRhLFxuICAgICAganNvbjogdW5kZWZpbmVkLFxuICAgICAgdGV4dDogdW5kZWZpbmVkXG4gICAgfTtcbiAgfSBlbHNlIGlmIChqc29uICE9PSB1bmRlZmluZWQpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZm9ybU1ldGhvZCxcbiAgICAgIGZvcm1BY3Rpb24sXG4gICAgICBmb3JtRW5jVHlwZSxcbiAgICAgIGZvcm1EYXRhOiB1bmRlZmluZWQsXG4gICAgICBqc29uLFxuICAgICAgdGV4dDogdW5kZWZpbmVkXG4gICAgfTtcbiAgfVxufVxuZnVuY3Rpb24gZ2V0TG9hZGluZ05hdmlnYXRpb24obG9jYXRpb24sIHN1Ym1pc3Npb24pIHtcbiAgaWYgKHN1Ym1pc3Npb24pIHtcbiAgICBsZXQgbmF2aWdhdGlvbiA9IHtcbiAgICAgIHN0YXRlOiBcImxvYWRpbmdcIixcbiAgICAgIGxvY2F0aW9uLFxuICAgICAgZm9ybU1ldGhvZDogc3VibWlzc2lvbi5mb3JtTWV0aG9kLFxuICAgICAgZm9ybUFjdGlvbjogc3VibWlzc2lvbi5mb3JtQWN0aW9uLFxuICAgICAgZm9ybUVuY1R5cGU6IHN1Ym1pc3Npb24uZm9ybUVuY1R5cGUsXG4gICAgICBmb3JtRGF0YTogc3VibWlzc2lvbi5mb3JtRGF0YSxcbiAgICAgIGpzb246IHN1Ym1pc3Npb24uanNvbixcbiAgICAgIHRleHQ6IHN1Ym1pc3Npb24udGV4dFxuICAgIH07XG4gICAgcmV0dXJuIG5hdmlnYXRpb247XG4gIH0gZWxzZSB7XG4gICAgbGV0IG5hdmlnYXRpb24gPSB7XG4gICAgICBzdGF0ZTogXCJsb2FkaW5nXCIsXG4gICAgICBsb2NhdGlvbixcbiAgICAgIGZvcm1NZXRob2Q6IHVuZGVmaW5lZCxcbiAgICAgIGZvcm1BY3Rpb246IHVuZGVmaW5lZCxcbiAgICAgIGZvcm1FbmNUeXBlOiB1bmRlZmluZWQsXG4gICAgICBmb3JtRGF0YTogdW5kZWZpbmVkLFxuICAgICAganNvbjogdW5kZWZpbmVkLFxuICAgICAgdGV4dDogdW5kZWZpbmVkXG4gICAgfTtcbiAgICByZXR1cm4gbmF2aWdhdGlvbjtcbiAgfVxufVxuZnVuY3Rpb24gZ2V0U3VibWl0dGluZ05hdmlnYXRpb24obG9jYXRpb24sIHN1Ym1pc3Npb24pIHtcbiAgbGV0IG5hdmlnYXRpb24gPSB7XG4gICAgc3RhdGU6IFwic3VibWl0dGluZ1wiLFxuICAgIGxvY2F0aW9uLFxuICAgIGZvcm1NZXRob2Q6IHN1Ym1pc3Npb24uZm9ybU1ldGhvZCxcbiAgICBmb3JtQWN0aW9uOiBzdWJtaXNzaW9uLmZvcm1BY3Rpb24sXG4gICAgZm9ybUVuY1R5cGU6IHN1Ym1pc3Npb24uZm9ybUVuY1R5cGUsXG4gICAgZm9ybURhdGE6IHN1Ym1pc3Npb24uZm9ybURhdGEsXG4gICAganNvbjogc3VibWlzc2lvbi5qc29uLFxuICAgIHRleHQ6IHN1Ym1pc3Npb24udGV4dFxuICB9O1xuICByZXR1cm4gbmF2aWdhdGlvbjtcbn1cbmZ1bmN0aW9uIGdldExvYWRpbmdGZXRjaGVyKHN1Ym1pc3Npb24sIGRhdGEpIHtcbiAgaWYgKHN1Ym1pc3Npb24pIHtcbiAgICBsZXQgZmV0Y2hlciA9IHtcbiAgICAgIHN0YXRlOiBcImxvYWRpbmdcIixcbiAgICAgIGZvcm1NZXRob2Q6IHN1Ym1pc3Npb24uZm9ybU1ldGhvZCxcbiAgICAgIGZvcm1BY3Rpb246IHN1Ym1pc3Npb24uZm9ybUFjdGlvbixcbiAgICAgIGZvcm1FbmNUeXBlOiBzdWJtaXNzaW9uLmZvcm1FbmNUeXBlLFxuICAgICAgZm9ybURhdGE6IHN1Ym1pc3Npb24uZm9ybURhdGEsXG4gICAgICBqc29uOiBzdWJtaXNzaW9uLmpzb24sXG4gICAgICB0ZXh0OiBzdWJtaXNzaW9uLnRleHQsXG4gICAgICBkYXRhLFxuICAgICAgXCIgX2hhc0ZldGNoZXJEb25lQW55dGhpbmcgXCI6IHRydWVcbiAgICB9O1xuICAgIHJldHVybiBmZXRjaGVyO1xuICB9IGVsc2Uge1xuICAgIGxldCBmZXRjaGVyID0ge1xuICAgICAgc3RhdGU6IFwibG9hZGluZ1wiLFxuICAgICAgZm9ybU1ldGhvZDogdW5kZWZpbmVkLFxuICAgICAgZm9ybUFjdGlvbjogdW5kZWZpbmVkLFxuICAgICAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCxcbiAgICAgIGZvcm1EYXRhOiB1bmRlZmluZWQsXG4gICAgICBqc29uOiB1bmRlZmluZWQsXG4gICAgICB0ZXh0OiB1bmRlZmluZWQsXG4gICAgICBkYXRhLFxuICAgICAgXCIgX2hhc0ZldGNoZXJEb25lQW55dGhpbmcgXCI6IHRydWVcbiAgICB9O1xuICAgIHJldHVybiBmZXRjaGVyO1xuICB9XG59XG5mdW5jdGlvbiBnZXRTdWJtaXR0aW5nRmV0Y2hlcihzdWJtaXNzaW9uLCBleGlzdGluZ0ZldGNoZXIpIHtcbiAgbGV0IGZldGNoZXIgPSB7XG4gICAgc3RhdGU6IFwic3VibWl0dGluZ1wiLFxuICAgIGZvcm1NZXRob2Q6IHN1Ym1pc3Npb24uZm9ybU1ldGhvZCxcbiAgICBmb3JtQWN0aW9uOiBzdWJtaXNzaW9uLmZvcm1BY3Rpb24sXG4gICAgZm9ybUVuY1R5cGU6IHN1Ym1pc3Npb24uZm9ybUVuY1R5cGUsXG4gICAgZm9ybURhdGE6IHN1Ym1pc3Npb24uZm9ybURhdGEsXG4gICAganNvbjogc3VibWlzc2lvbi5qc29uLFxuICAgIHRleHQ6IHN1Ym1pc3Npb24udGV4dCxcbiAgICBkYXRhOiBleGlzdGluZ0ZldGNoZXIgPyBleGlzdGluZ0ZldGNoZXIuZGF0YSA6IHVuZGVmaW5lZCxcbiAgICBcIiBfaGFzRmV0Y2hlckRvbmVBbnl0aGluZyBcIjogdHJ1ZVxuICB9O1xuICByZXR1cm4gZmV0Y2hlcjtcbn1cbmZ1bmN0aW9uIGdldERvbmVGZXRjaGVyKGRhdGEpIHtcbiAgbGV0IGZldGNoZXIgPSB7XG4gICAgc3RhdGU6IFwiaWRsZVwiLFxuICAgIGZvcm1NZXRob2Q6IHVuZGVmaW5lZCxcbiAgICBmb3JtQWN0aW9uOiB1bmRlZmluZWQsXG4gICAgZm9ybUVuY1R5cGU6IHVuZGVmaW5lZCxcbiAgICBmb3JtRGF0YTogdW5kZWZpbmVkLFxuICAgIGpzb246IHVuZGVmaW5lZCxcbiAgICB0ZXh0OiB1bmRlZmluZWQsXG4gICAgZGF0YSxcbiAgICBcIiBfaGFzRmV0Y2hlckRvbmVBbnl0aGluZyBcIjogdHJ1ZVxuICB9O1xuICByZXR1cm4gZmV0Y2hlcjtcbn1cbi8vI2VuZHJlZ2lvblxuXG5leHBvcnQgeyBBYm9ydGVkRGVmZXJyZWRFcnJvciwgQWN0aW9uLCBFcnJvclJlc3BvbnNlLCBJRExFX0JMT0NLRVIsIElETEVfRkVUQ0hFUiwgSURMRV9OQVZJR0FUSU9OLCBVTlNBRkVfREVGRVJSRURfU1lNQk9MLCBEZWZlcnJlZERhdGEgYXMgVU5TQUZFX0RlZmVycmVkRGF0YSwgY29udmVydFJvdXRlc1RvRGF0YVJvdXRlcyBhcyBVTlNBRkVfY29udmVydFJvdXRlc1RvRGF0YVJvdXRlcywgZ2V0UGF0aENvbnRyaWJ1dGluZ01hdGNoZXMgYXMgVU5TQUZFX2dldFBhdGhDb250cmlidXRpbmdNYXRjaGVzLCBpbnZhcmlhbnQgYXMgVU5TQUZFX2ludmFyaWFudCwgd2FybmluZyBhcyBVTlNBRkVfd2FybmluZywgY3JlYXRlQnJvd3Nlckhpc3RvcnksIGNyZWF0ZUhhc2hIaXN0b3J5LCBjcmVhdGVNZW1vcnlIaXN0b3J5LCBjcmVhdGVQYXRoLCBjcmVhdGVSb3V0ZXIsIGNyZWF0ZVN0YXRpY0hhbmRsZXIsIGRlZmVyLCBnZW5lcmF0ZVBhdGgsIGdldFN0YXRpY0NvbnRleHRGcm9tRXJyb3IsIGdldFRvUGF0aG5hbWUsIGlzRGVmZXJyZWREYXRhLCBpc1JvdXRlRXJyb3JSZXNwb25zZSwgam9pblBhdGhzLCBqc29uLCBtYXRjaFBhdGgsIG1hdGNoUm91dGVzLCBub3JtYWxpemVQYXRobmFtZSwgcGFyc2VQYXRoLCByZWRpcmVjdCwgcmVzb2x2ZVBhdGgsIHJlc29sdmVUbywgc3RyaXBCYXNlbmFtZSB9O1xuLy8jIHNvdXJjZU1hcHBpbmdVUkw9cm91dGVyLmpzLm1hcFxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@remix-run/router/dist/router.js\n"); -// EXPORTS -__webpack_require__.d(__webpack_exports__, { - Z: () => (/* binding */ app) -}); - -// EXTERNAL MODULE: ./node_modules/react/index.js -var react = __webpack_require__(294); -var react_namespaceObject = /*#__PURE__*/__webpack_require__.t(react, 2); -;// CONCATENATED MODULE: ./node_modules/@remix-run/router/dist/router.js -/** - * @remix-run/router v1.7.0 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */ -function _extends() { - _extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - return target; - }; - return _extends.apply(this, arguments); -} - -//////////////////////////////////////////////////////////////////////////////// -//#region Types and Constants -//////////////////////////////////////////////////////////////////////////////// -/** - * Actions represent the type of change to a location value. - */ -var Action; -(function (Action) { - /** - * A POP indicates a change to an arbitrary index in the history stack, such - * as a back or forward navigation. It does not describe the direction of the - * navigation, only that the current index changed. - * - * Note: This is the default action for newly created history objects. - */ - Action["Pop"] = "POP"; - /** - * A PUSH indicates a new entry being added to the history stack, such as when - * a link is clicked and a new page loads. When this happens, all subsequent - * entries in the stack are lost. - */ - Action["Push"] = "PUSH"; - /** - * A REPLACE indicates the entry at the current index in the history stack - * being replaced by a new one. - */ - Action["Replace"] = "REPLACE"; -})(Action || (Action = {})); -const PopStateEventType = "popstate"; -/** - * Memory history stores the current location in memory. It is designed for use - * in stateful non-browser environments like tests and React Native. - */ -function router_createMemoryHistory(options) { - if (options === void 0) { - options = {}; - } - let { - initialEntries = ["/"], - initialIndex, - v5Compat = false - } = options; - let entries; // Declare so we can access from createMemoryLocation - entries = initialEntries.map((entry, index) => createMemoryLocation(entry, typeof entry === "string" ? null : entry.state, index === 0 ? "default" : undefined)); - let index = clampIndex(initialIndex == null ? entries.length - 1 : initialIndex); - let action = Action.Pop; - let listener = null; - function clampIndex(n) { - return Math.min(Math.max(n, 0), entries.length - 1); - } - function getCurrentLocation() { - return entries[index]; - } - function createMemoryLocation(to, state, key) { - if (state === void 0) { - state = null; - } - let location = createLocation(entries ? getCurrentLocation().pathname : "/", to, state, key); - warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in memory history: " + JSON.stringify(to)); - return location; - } - function createHref(to) { - return typeof to === "string" ? to : router_createPath(to); - } - let history = { - get index() { - return index; - }, - get action() { - return action; - }, - get location() { - return getCurrentLocation(); - }, - createHref, - createURL(to) { - return new URL(createHref(to), "http://localhost"); - }, - encodeLocation(to) { - let path = typeof to === "string" ? parsePath(to) : to; - return { - pathname: path.pathname || "", - search: path.search || "", - hash: path.hash || "" - }; - }, - push(to, state) { - action = Action.Push; - let nextLocation = createMemoryLocation(to, state); - index += 1; - entries.splice(index, entries.length, nextLocation); - if (v5Compat && listener) { - listener({ - action, - location: nextLocation, - delta: 1 - }); - } - }, - replace(to, state) { - action = Action.Replace; - let nextLocation = createMemoryLocation(to, state); - entries[index] = nextLocation; - if (v5Compat && listener) { - listener({ - action, - location: nextLocation, - delta: 0 - }); - } - }, - go(delta) { - action = Action.Pop; - let nextIndex = clampIndex(index + delta); - let nextLocation = entries[nextIndex]; - index = nextIndex; - if (listener) { - listener({ - action, - location: nextLocation, - delta - }); - } - }, - listen(fn) { - listener = fn; - return () => { - listener = null; - }; - } - }; - return history; -} -/** - * Browser history stores the location in regular URLs. This is the standard for - * most web apps, but it requires some configuration on the server to ensure you - * serve the same app at multiple URLs. - * - * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory - */ -function router_createBrowserHistory(options) { - if (options === void 0) { - options = {}; - } - function createBrowserLocation(window, globalHistory) { - let { - pathname, - search, - hash - } = window.location; - return createLocation("", { - pathname, - search, - hash - }, - // state defaults to `null` because `window.history.state` does - globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || "default"); - } - function createBrowserHref(window, to) { - return typeof to === "string" ? to : router_createPath(to); - } - return getUrlBasedHistory(createBrowserLocation, createBrowserHref, null, options); -} -/** - * Hash history stores the location in window.location.hash. This makes it ideal - * for situations where you don't want to send the location to the server for - * some reason, either because you do cannot configure it or the URL space is - * reserved for something else. - * - * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory - */ -function router_createHashHistory(options) { - if (options === void 0) { - options = {}; - } - function createHashLocation(window, globalHistory) { - let { - pathname = "/", - search = "", - hash = "" - } = parsePath(window.location.hash.substr(1)); - return createLocation("", { - pathname, - search, - hash - }, - // state defaults to `null` because `window.history.state` does - globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || "default"); - } - function createHashHref(window, to) { - let base = window.document.querySelector("base"); - let href = ""; - if (base && base.getAttribute("href")) { - let url = window.location.href; - let hashIndex = url.indexOf("#"); - href = hashIndex === -1 ? url : url.slice(0, hashIndex); - } - return href + "#" + (typeof to === "string" ? to : router_createPath(to)); - } - function validateHashLocation(location, to) { - warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")"); - } - return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options); -} -function invariant(value, message) { - if (value === false || value === null || typeof value === "undefined") { - throw new Error(message); - } -} -function warning(cond, message) { - if (!cond) { - // eslint-disable-next-line no-console - if (typeof console !== "undefined") console.warn(message); - try { - // Welcome to debugging history! - // - // This error is thrown as a convenience so you can more easily - // find the source for a warning that appears in the console by - // enabling "pause on exceptions" in your JavaScript debugger. - throw new Error(message); - // eslint-disable-next-line no-empty - } catch (e) {} - } -} -function createKey() { - return Math.random().toString(36).substr(2, 8); -} -/** - * For browser-based histories, we combine the state and key into an object - */ -function getHistoryState(location, index) { - return { - usr: location.state, - key: location.key, - idx: index - }; -} -/** - * Creates a Location object with a unique key from the given Path - */ -function createLocation(current, to, state, key) { - if (state === void 0) { - state = null; - } - let location = _extends({ - pathname: typeof current === "string" ? current : current.pathname, - search: "", - hash: "" - }, typeof to === "string" ? parsePath(to) : to, { - state, - // TODO: This could be cleaned up. push/replace should probably just take - // full Locations now and avoid the need to run through this flow at all - // But that's a pretty big refactor to the current test suite so going to - // keep as is for the time being and just let any incoming keys take precedence - key: to && to.key || key || createKey() - }); - return location; -} -/** - * Creates a string URL path from the given pathname, search, and hash components. - */ -function router_createPath(_ref) { - let { - pathname = "/", - search = "", - hash = "" - } = _ref; - if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search; - if (hash && hash !== "#") pathname += hash.charAt(0) === "#" ? hash : "#" + hash; - return pathname; -} -/** - * Parses a string URL path into its separate pathname, search, and hash components. - */ -function parsePath(path) { - let parsedPath = {}; - if (path) { - let hashIndex = path.indexOf("#"); - if (hashIndex >= 0) { - parsedPath.hash = path.substr(hashIndex); - path = path.substr(0, hashIndex); - } - let searchIndex = path.indexOf("?"); - if (searchIndex >= 0) { - parsedPath.search = path.substr(searchIndex); - path = path.substr(0, searchIndex); - } - if (path) { - parsedPath.pathname = path; - } - } - return parsedPath; -} -function getUrlBasedHistory(getLocation, createHref, validateLocation, options) { - if (options === void 0) { - options = {}; - } - let { - window = document.defaultView, - v5Compat = false - } = options; - let globalHistory = window.history; - let action = Action.Pop; - let listener = null; - let index = getIndex(); - // Index should only be null when we initialize. If not, it's because the - // user called history.pushState or history.replaceState directly, in which - // case we should log a warning as it will result in bugs. - if (index == null) { - index = 0; - globalHistory.replaceState(_extends({}, globalHistory.state, { - idx: index - }), ""); - } - function getIndex() { - let state = globalHistory.state || { - idx: null - }; - return state.idx; - } - function handlePop() { - action = Action.Pop; - let nextIndex = getIndex(); - let delta = nextIndex == null ? null : nextIndex - index; - index = nextIndex; - if (listener) { - listener({ - action, - location: history.location, - delta - }); - } - } - function push(to, state) { - action = Action.Push; - let location = createLocation(history.location, to, state); - if (validateLocation) validateLocation(location, to); - index = getIndex() + 1; - let historyState = getHistoryState(location, index); - let url = history.createHref(location); - // try...catch because iOS limits us to 100 pushState calls :/ - try { - globalHistory.pushState(historyState, "", url); - } catch (error) { - // If the exception is because `state` can't be serialized, let that throw - // outwards just like a replace call would so the dev knows the cause - // https://html.spec.whatwg.org/multipage/nav-history-apis.html#shared-history-push/replace-state-steps - // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal - if (error instanceof DOMException && error.name === "DataCloneError") { - throw error; - } - // They are going to lose state here, but there is no real - // way to warn them about it since the page will refresh... - window.location.assign(url); - } - if (v5Compat && listener) { - listener({ - action, - location: history.location, - delta: 1 - }); - } - } - function replace(to, state) { - action = Action.Replace; - let location = createLocation(history.location, to, state); - if (validateLocation) validateLocation(location, to); - index = getIndex(); - let historyState = getHistoryState(location, index); - let url = history.createHref(location); - globalHistory.replaceState(historyState, "", url); - if (v5Compat && listener) { - listener({ - action, - location: history.location, - delta: 0 - }); - } - } - function createURL(to) { - // window.location.origin is "null" (the literal string value) in Firefox - // under certain conditions, notably when serving from a local HTML file - // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297 - let base = window.location.origin !== "null" ? window.location.origin : window.location.href; - let href = typeof to === "string" ? to : router_createPath(to); - invariant(base, "No window.location.(origin|href) available to create URL for href: " + href); - return new URL(href, base); - } - let history = { - get action() { - return action; - }, - get location() { - return getLocation(window, globalHistory); - }, - listen(fn) { - if (listener) { - throw new Error("A history only accepts one active listener"); - } - window.addEventListener(PopStateEventType, handlePop); - listener = fn; - return () => { - window.removeEventListener(PopStateEventType, handlePop); - listener = null; - }; - }, - createHref(to) { - return createHref(window, to); - }, - createURL, - encodeLocation(to) { - // Encode a Location the same way window.location would - let url = createURL(to); - return { - pathname: url.pathname, - search: url.search, - hash: url.hash - }; - }, - push, - replace, - go(n) { - return globalHistory.go(n); - } - }; - return history; -} -//#endregion - -var ResultType; -(function (ResultType) { - ResultType["data"] = "data"; - ResultType["deferred"] = "deferred"; - ResultType["redirect"] = "redirect"; - ResultType["error"] = "error"; -})(ResultType || (ResultType = {})); -const immutableRouteKeys = new Set(["lazy", "caseSensitive", "path", "id", "index", "children"]); -function isIndexRoute(route) { - return route.index === true; -} -// Walk the route tree generating unique IDs where necessary so we are working -// solely with AgnosticDataRouteObject's within the Router -function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath, manifest) { - if (parentPath === void 0) { - parentPath = []; - } - if (manifest === void 0) { - manifest = {}; - } - return routes.map((route, index) => { - let treePath = [...parentPath, index]; - let id = typeof route.id === "string" ? route.id : treePath.join("-"); - invariant(route.index !== true || !route.children, "Cannot specify children on an index route"); - invariant(!manifest[id], "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages"); - if (isIndexRoute(route)) { - let indexRoute = _extends({}, route, mapRouteProperties(route), { - id - }); - manifest[id] = indexRoute; - return indexRoute; - } else { - let pathOrLayoutRoute = _extends({}, route, mapRouteProperties(route), { - id, - children: undefined - }); - manifest[id] = pathOrLayoutRoute; - if (route.children) { - pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest); - } - return pathOrLayoutRoute; - } - }); -} -/** - * Matches the given routes to a location and returns the match data. - * - * @see https://reactrouter.com/utils/match-routes - */ -function matchRoutes(routes, locationArg, basename) { - if (basename === void 0) { - basename = "/"; - } - let location = typeof locationArg === "string" ? parsePath(locationArg) : locationArg; - let pathname = router_stripBasename(location.pathname || "/", basename); - if (pathname == null) { - return null; - } - let branches = flattenRoutes(routes); - rankRouteBranches(branches); - let matches = null; - for (let i = 0; matches == null && i < branches.length; ++i) { - matches = matchRouteBranch(branches[i], - // Incoming pathnames are generally encoded from either window.location - // or from router.navigate, but we want to match against the unencoded - // paths in the route definitions. Memory router locations won't be - // encoded here but there also shouldn't be anything to decode so this - // should be a safe operation. This avoids needing matchRoutes to be - // history-aware. - safelyDecodeURI(pathname)); - } - return matches; -} -function flattenRoutes(routes, branches, parentsMeta, parentPath) { - if (branches === void 0) { - branches = []; - } - if (parentsMeta === void 0) { - parentsMeta = []; - } - if (parentPath === void 0) { - parentPath = ""; - } - let flattenRoute = (route, index, relativePath) => { - let meta = { - relativePath: relativePath === undefined ? route.path || "" : relativePath, - caseSensitive: route.caseSensitive === true, - childrenIndex: index, - route - }; - if (meta.relativePath.startsWith("/")) { - invariant(meta.relativePath.startsWith(parentPath), "Absolute route path \"" + meta.relativePath + "\" nested under path " + ("\"" + parentPath + "\" is not valid. An absolute child route path ") + "must start with the combined path of all its parent routes."); - meta.relativePath = meta.relativePath.slice(parentPath.length); - } - let path = router_joinPaths([parentPath, meta.relativePath]); - let routesMeta = parentsMeta.concat(meta); - // Add the children before adding this route to the array so we traverse the - // route tree depth-first and child routes appear before their parents in - // the "flattened" version. - if (route.children && route.children.length > 0) { - invariant( - // Our types know better, but runtime JS may not! - // @ts-expect-error - route.index !== true, "Index routes must not have child routes. Please remove " + ("all child routes from route path \"" + path + "\".")); - flattenRoutes(route.children, branches, routesMeta, path); - } - // Routes without a path shouldn't ever match by themselves unless they are - // index routes, so don't add them to the list of possible branches. - if (route.path == null && !route.index) { - return; - } - branches.push({ - path, - score: computeScore(path, route.index), - routesMeta - }); - }; - routes.forEach((route, index) => { - var _route$path; - // coarse-grain check for optional params - if (route.path === "" || !((_route$path = route.path) != null && _route$path.includes("?"))) { - flattenRoute(route, index); - } else { - for (let exploded of explodeOptionalSegments(route.path)) { - flattenRoute(route, index, exploded); - } - } - }); - return branches; -} -/** - * Computes all combinations of optional path segments for a given path, - * excluding combinations that are ambiguous and of lower priority. - * - * For example, `/one/:two?/three/:four?/:five?` explodes to: - * - `/one/three` - * - `/one/:two/three` - * - `/one/three/:four` - * - `/one/three/:five` - * - `/one/:two/three/:four` - * - `/one/:two/three/:five` - * - `/one/three/:four/:five` - * - `/one/:two/three/:four/:five` - */ -function explodeOptionalSegments(path) { - let segments = path.split("/"); - if (segments.length === 0) return []; - let [first, ...rest] = segments; - // Optional path segments are denoted by a trailing `?` - let isOptional = first.endsWith("?"); - // Compute the corresponding required segment: `foo?` -> `foo` - let required = first.replace(/\?$/, ""); - if (rest.length === 0) { - // Intepret empty string as omitting an optional segment - // `["one", "", "three"]` corresponds to omitting `:two` from `/one/:two?/three` -> `/one/three` - return isOptional ? [required, ""] : [required]; - } - let restExploded = explodeOptionalSegments(rest.join("/")); - let result = []; - // All child paths with the prefix. Do this for all children before the - // optional version for all children so we get consistent ordering where the - // parent optional aspect is preferred as required. Otherwise, we can get - // child sections interspersed where deeper optional segments are higher than - // parent optional segments, where for example, /:two would explodes _earlier_ - // then /:one. By always including the parent as required _for all children_ - // first, we avoid this issue - result.push(...restExploded.map(subpath => subpath === "" ? required : [required, subpath].join("/"))); - // Then if this is an optional value, add all child versions without - if (isOptional) { - result.push(...restExploded); - } - // for absolute paths, ensure `/` instead of empty segment - return result.map(exploded => path.startsWith("/") && exploded === "" ? "/" : exploded); -} -function rankRouteBranches(branches) { - branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first - : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex))); -} -const paramRe = /^:\w+$/; -const dynamicSegmentValue = 3; -const indexRouteValue = 2; -const emptySegmentValue = 1; -const staticSegmentValue = 10; -const splatPenalty = -2; -const isSplat = s => s === "*"; -function computeScore(path, index) { - let segments = path.split("/"); - let initialScore = segments.length; - if (segments.some(isSplat)) { - initialScore += splatPenalty; - } - if (index) { - initialScore += indexRouteValue; - } - return segments.filter(s => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore); -} -function compareIndexes(a, b) { - let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]); - return siblings ? - // If two routes are siblings, we should try to match the earlier sibling - // first. This allows people to have fine-grained control over the matching - // behavior by simply putting routes with identical paths in the order they - // want them tried. - a[a.length - 1] - b[b.length - 1] : - // Otherwise, it doesn't really make sense to rank non-siblings by index, - // so they sort equally. - 0; -} -function matchRouteBranch(branch, pathname) { - let { - routesMeta - } = branch; - let matchedParams = {}; - let matchedPathname = "/"; - let matches = []; - for (let i = 0; i < routesMeta.length; ++i) { - let meta = routesMeta[i]; - let end = i === routesMeta.length - 1; - let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/"; - let match = router_matchPath({ - path: meta.relativePath, - caseSensitive: meta.caseSensitive, - end - }, remainingPathname); - if (!match) return null; - Object.assign(matchedParams, match.params); - let route = meta.route; - matches.push({ - // TODO: Can this as be avoided? - params: matchedParams, - pathname: router_joinPaths([matchedPathname, match.pathname]), - pathnameBase: normalizePathname(router_joinPaths([matchedPathname, match.pathnameBase])), - route - }); - if (match.pathnameBase !== "/") { - matchedPathname = router_joinPaths([matchedPathname, match.pathnameBase]); - } - } - return matches; -} -/** - * Returns a path with params interpolated. - * - * @see https://reactrouter.com/utils/generate-path - */ -function generatePath(originalPath, params) { - if (params === void 0) { - params = {}; - } - let path = originalPath; - if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) { - warning(false, "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\".")); - path = path.replace(/\*$/, "/*"); - } - // ensure `/` is added at the beginning if the path is absolute - const prefix = path.startsWith("/") ? "/" : ""; - const stringify = p => p == null ? "" : typeof p === "string" ? p : String(p); - const segments = path.split(/\/+/).map((segment, index, array) => { - const isLastSegment = index === array.length - 1; - // only apply the splat if it's the last segment - if (isLastSegment && segment === "*") { - const star = "*"; - // Apply the splat - return stringify(params[star]); - } - const keyMatch = segment.match(/^:(\w+)(\??)$/); - if (keyMatch) { - const [, key, optional] = keyMatch; - let param = params[key]; - invariant(optional === "?" || param != null, "Missing \":" + key + "\" param"); - return stringify(param); - } - // Remove any optional markers from optional static segments - return segment.replace(/\?$/g, ""); - }) - // Remove empty segments - .filter(segment => !!segment); - return prefix + segments.join("/"); -} -/** - * Performs pattern matching on a URL pathname and returns information about - * the match. - * - * @see https://reactrouter.com/utils/match-path - */ -function router_matchPath(pattern, pathname) { - if (typeof pattern === "string") { - pattern = { - path: pattern, - caseSensitive: false, - end: true - }; - } - let [matcher, paramNames] = compilePath(pattern.path, pattern.caseSensitive, pattern.end); - let match = pathname.match(matcher); - if (!match) return null; - let matchedPathname = match[0]; - let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1"); - let captureGroups = match.slice(1); - let params = paramNames.reduce((memo, paramName, index) => { - // We need to compute the pathnameBase here using the raw splat value - // instead of using params["*"] later because it will be decoded then - if (paramName === "*") { - let splatValue = captureGroups[index] || ""; - pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1"); - } - memo[paramName] = safelyDecodeURIComponent(captureGroups[index] || "", paramName); - return memo; - }, {}); - return { - params, - pathname: matchedPathname, - pathnameBase, - pattern - }; -} -function compilePath(path, caseSensitive, end) { - if (caseSensitive === void 0) { - caseSensitive = false; - } - if (end === void 0) { - end = true; - } - warning(path === "*" || !path.endsWith("*") || path.endsWith("/*"), "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\".")); - let paramNames = []; - let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below - .replace(/^\/*/, "/") // Make sure it has a leading / - .replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars - .replace(/\/:(\w+)/g, (_, paramName) => { - paramNames.push(paramName); - return "/([^\\/]+)"; - }); - if (path.endsWith("*")) { - paramNames.push("*"); - regexpSource += path === "*" || path === "/*" ? "(.*)$" // Already matched the initial /, just match the rest - : "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"] - } else if (end) { - // When matching to the end, ignore trailing slashes - regexpSource += "\\/*$"; - } else if (path !== "" && path !== "/") { - // If our path is non-empty and contains anything beyond an initial slash, - // then we have _some_ form of path in our regex so we should expect to - // match only if we find the end of this path segment. Look for an optional - // non-captured trailing slash (to match a portion of the URL) or the end - // of the path (if we've matched to the end). We used to do this with a - // word boundary but that gives false positives on routes like - // /user-preferences since `-` counts as a word boundary. - regexpSource += "(?:(?=\\/|$))"; - } else ; - let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i"); - return [matcher, paramNames]; -} -function safelyDecodeURI(value) { - try { - return decodeURI(value); - } catch (error) { - warning(false, "The URL path \"" + value + "\" could not be decoded because it is is a " + "malformed URL segment. This is probably due to a bad percent " + ("encoding (" + error + ").")); - return value; - } -} -function safelyDecodeURIComponent(value, paramName) { - try { - return decodeURIComponent(value); - } catch (error) { - warning(false, "The value for the URL param \"" + paramName + "\" will not be decoded because" + (" the string \"" + value + "\" is a malformed URL segment. This is probably") + (" due to a bad percent encoding (" + error + ").")); - return value; - } -} -/** - * @private - */ -function router_stripBasename(pathname, basename) { - if (basename === "/") return pathname; - if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) { - return null; - } - // We want to leave trailing slash behavior in the user's control, so if they - // specify a basename with a trailing slash, we should support it - let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length; - let nextChar = pathname.charAt(startIndex); - if (nextChar && nextChar !== "/") { - // pathname does not start with basename/ - return null; - } - return pathname.slice(startIndex) || "/"; -} -/** - * Returns a resolved path object relative to the given pathname. - * - * @see https://reactrouter.com/utils/resolve-path - */ -function resolvePath(to, fromPathname) { - if (fromPathname === void 0) { - fromPathname = "/"; - } - let { - pathname: toPathname, - search = "", - hash = "" - } = typeof to === "string" ? parsePath(to) : to; - let pathname = toPathname ? toPathname.startsWith("/") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname; - return { - pathname, - search: normalizeSearch(search), - hash: normalizeHash(hash) - }; -} -function resolvePathname(relativePath, fromPathname) { - let segments = fromPathname.replace(/\/+$/, "").split("/"); - let relativeSegments = relativePath.split("/"); - relativeSegments.forEach(segment => { - if (segment === "..") { - // Keep the root "" segment so the pathname starts at / - if (segments.length > 1) segments.pop(); - } else if (segment !== ".") { - segments.push(segment); - } - }); - return segments.length > 1 ? segments.join("/") : "/"; -} -function getInvalidPathError(char, field, dest, path) { - return "Cannot include a '" + char + "' character in a manually specified " + ("`to." + field + "` field [" + JSON.stringify(path) + "]. Please separate it out to the ") + ("`to." + dest + "` field. Alternatively you may provide the full path as ") + "a string in and the router will parse it for you."; -} -/** - * @private - * - * When processing relative navigation we want to ignore ancestor routes that - * do not contribute to the path, such that index/pathless layout routes don't - * interfere. - * - * For example, when moving a route element into an index route and/or a - * pathless layout route, relative link behavior contained within should stay - * the same. Both of the following examples should link back to the root: - * - * - * - * - * - * - * - * }> // <-- Does not contribute - * // <-- Does not contribute - * - * - */ -function getPathContributingMatches(matches) { - return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0); -} -/** - * @private - */ -function router_resolveTo(toArg, routePathnames, locationPathname, isPathRelative) { - if (isPathRelative === void 0) { - isPathRelative = false; - } - let to; - if (typeof toArg === "string") { - to = parsePath(toArg); - } else { - to = _extends({}, toArg); - invariant(!to.pathname || !to.pathname.includes("?"), getInvalidPathError("?", "pathname", "search", to)); - invariant(!to.pathname || !to.pathname.includes("#"), getInvalidPathError("#", "pathname", "hash", to)); - invariant(!to.search || !to.search.includes("#"), getInvalidPathError("#", "search", "hash", to)); - } - let isEmptyPath = toArg === "" || to.pathname === ""; - let toPathname = isEmptyPath ? "/" : to.pathname; - let from; - // Routing is relative to the current pathname if explicitly requested. - // - // If a pathname is explicitly provided in `to`, it should be relative to the - // route context. This is explained in `Note on `` values` in our - // migration guide from v5 as a means of disambiguation between `to` values - // that begin with `/` and those that do not. However, this is problematic for - // `to` values that do not provide a pathname. `to` can simply be a search or - // hash string, in which case we should assume that the navigation is relative - // to the current location's pathname and *not* the route pathname. - if (isPathRelative || toPathname == null) { - from = locationPathname; - } else { - let routePathnameIndex = routePathnames.length - 1; - if (toPathname.startsWith("..")) { - let toSegments = toPathname.split("/"); - // Each leading .. segment means "go up one route" instead of "go up one - // URL segment". This is a key difference from how works and a - // major reason we call this a "to" value instead of a "href". - while (toSegments[0] === "..") { - toSegments.shift(); - routePathnameIndex -= 1; - } - to.pathname = toSegments.join("/"); - } - // If there are more ".." segments than parent routes, resolve relative to - // the root / URL. - from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/"; - } - let path = resolvePath(to, from); - // Ensure the pathname has a trailing slash if the original "to" had one - let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/"); - // Or if this was a link to the current path which has a trailing slash - let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/"); - if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) { - path.pathname += "/"; - } - return path; -} -/** - * @private - */ -function getToPathname(to) { - // Empty strings should be treated the same as / paths - return to === "" || to.pathname === "" ? "/" : typeof to === "string" ? parsePath(to).pathname : to.pathname; -} -/** - * @private - */ -const router_joinPaths = paths => paths.join("/").replace(/\/\/+/g, "/"); -/** - * @private - */ -const normalizePathname = pathname => pathname.replace(/\/+$/, "").replace(/^\/*/, "/"); -/** - * @private - */ -const normalizeSearch = search => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search; -/** - * @private - */ -const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash; -/** - * This is a shortcut for creating `application/json` responses. Converts `data` - * to JSON and sets the `Content-Type` header. - */ -const json = function json(data, init) { - if (init === void 0) { - init = {}; - } - let responseInit = typeof init === "number" ? { - status: init - } : init; - let headers = new Headers(responseInit.headers); - if (!headers.has("Content-Type")) { - headers.set("Content-Type", "application/json; charset=utf-8"); - } - return new Response(JSON.stringify(data), _extends({}, responseInit, { - headers - })); -}; -class AbortedDeferredError extends Error {} -class DeferredData { - constructor(data, responseInit) { - this.pendingKeysSet = new Set(); - this.subscribers = new Set(); - this.deferredKeys = []; - invariant(data && typeof data === "object" && !Array.isArray(data), "defer() only accepts plain objects"); - // Set up an AbortController + Promise we can race against to exit early - // cancellation - let reject; - this.abortPromise = new Promise((_, r) => reject = r); - this.controller = new AbortController(); - let onAbort = () => reject(new AbortedDeferredError("Deferred data aborted")); - this.unlistenAbortSignal = () => this.controller.signal.removeEventListener("abort", onAbort); - this.controller.signal.addEventListener("abort", onAbort); - this.data = Object.entries(data).reduce((acc, _ref) => { - let [key, value] = _ref; - return Object.assign(acc, { - [key]: this.trackPromise(key, value) - }); - }, {}); - if (this.done) { - // All incoming values were resolved - this.unlistenAbortSignal(); - } - this.init = responseInit; - } - trackPromise(key, value) { - if (!(value instanceof Promise)) { - return value; - } - this.deferredKeys.push(key); - this.pendingKeysSet.add(key); - // We store a little wrapper promise that will be extended with - // _data/_error props upon resolve/reject - let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error)); - // Register rejection listeners to avoid uncaught promise rejections on - // errors or aborted deferred values - promise.catch(() => {}); - Object.defineProperty(promise, "_tracked", { - get: () => true - }); - return promise; - } - onSettle(promise, key, error, data) { - if (this.controller.signal.aborted && error instanceof AbortedDeferredError) { - this.unlistenAbortSignal(); - Object.defineProperty(promise, "_error", { - get: () => error - }); - return Promise.reject(error); - } - this.pendingKeysSet.delete(key); - if (this.done) { - // Nothing left to abort! - this.unlistenAbortSignal(); - } - if (error) { - Object.defineProperty(promise, "_error", { - get: () => error - }); - this.emit(false, key); - return Promise.reject(error); - } - Object.defineProperty(promise, "_data", { - get: () => data - }); - this.emit(false, key); - return data; - } - emit(aborted, settledKey) { - this.subscribers.forEach(subscriber => subscriber(aborted, settledKey)); - } - subscribe(fn) { - this.subscribers.add(fn); - return () => this.subscribers.delete(fn); - } - cancel() { - this.controller.abort(); - this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k)); - this.emit(true); - } - async resolveData(signal) { - let aborted = false; - if (!this.done) { - let onAbort = () => this.cancel(); - signal.addEventListener("abort", onAbort); - aborted = await new Promise(resolve => { - this.subscribe(aborted => { - signal.removeEventListener("abort", onAbort); - if (aborted || this.done) { - resolve(aborted); - } - }); - }); - } - return aborted; - } - get done() { - return this.pendingKeysSet.size === 0; - } - get unwrappedData() { - invariant(this.data !== null && this.done, "Can only unwrap data on initialized and settled deferreds"); - return Object.entries(this.data).reduce((acc, _ref2) => { - let [key, value] = _ref2; - return Object.assign(acc, { - [key]: unwrapTrackedPromise(value) - }); - }, {}); - } - get pendingKeys() { - return Array.from(this.pendingKeysSet); - } -} -function isTrackedPromise(value) { - return value instanceof Promise && value._tracked === true; -} -function unwrapTrackedPromise(value) { - if (!isTrackedPromise(value)) { - return value; - } - if (value._error) { - throw value._error; - } - return value._data; -} -const defer = function defer(data, init) { - if (init === void 0) { - init = {}; - } - let responseInit = typeof init === "number" ? { - status: init - } : init; - return new DeferredData(data, responseInit); -}; -/** - * A redirect response. Sets the status code and the `Location` header. - * Defaults to "302 Found". - */ -const redirect = function redirect(url, init) { - if (init === void 0) { - init = 302; - } - let responseInit = init; - if (typeof responseInit === "number") { - responseInit = { - status: responseInit - }; - } else if (typeof responseInit.status === "undefined") { - responseInit.status = 302; - } - let headers = new Headers(responseInit.headers); - headers.set("Location", url); - return new Response(null, _extends({}, responseInit, { - headers - })); -}; -/** - * @private - * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies - */ -class ErrorResponse { - constructor(status, statusText, data, internal) { - if (internal === void 0) { - internal = false; - } - this.status = status; - this.statusText = statusText || ""; - this.internal = internal; - if (data instanceof Error) { - this.data = data.toString(); - this.error = data; - } else { - this.data = data; - } - } -} -/** - * Check if the given error is an ErrorResponse generated from a 4xx/5xx - * Response thrown from an action/loader - */ -function isRouteErrorResponse(error) { - return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error; -} - -const validMutationMethodsArr = ["post", "put", "patch", "delete"]; -const validMutationMethods = new Set(validMutationMethodsArr); -const validRequestMethodsArr = ["get", ...validMutationMethodsArr]; -const validRequestMethods = new Set(validRequestMethodsArr); -const redirectStatusCodes = new Set([301, 302, 303, 307, 308]); -const redirectPreserveMethodStatusCodes = new Set([307, 308]); -const IDLE_NAVIGATION = { - state: "idle", - location: undefined, - formMethod: undefined, - formAction: undefined, - formEncType: undefined, - formData: undefined, - json: undefined, - text: undefined -}; -const IDLE_FETCHER = { - state: "idle", - data: undefined, - formMethod: undefined, - formAction: undefined, - formEncType: undefined, - formData: undefined, - json: undefined, - text: undefined -}; -const router_IDLE_BLOCKER = { - state: "unblocked", - proceed: undefined, - reset: undefined, - location: undefined -}; -const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i; -const defaultMapRouteProperties = route => ({ - hasErrorBoundary: Boolean(route.hasErrorBoundary) -}); -//#endregion -//////////////////////////////////////////////////////////////////////////////// -//#region createRouter -//////////////////////////////////////////////////////////////////////////////// -/** - * Create a router and listen to history POP navigations - */ -function router_createRouter(init) { - const routerWindow = init.window ? init.window : typeof window !== "undefined" ? window : undefined; - const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined"; - const isServer = !isBrowser; - invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter"); - let mapRouteProperties; - if (init.mapRouteProperties) { - mapRouteProperties = init.mapRouteProperties; - } else if (init.detectErrorBoundary) { - // If they are still using the deprecated version, wrap it with the new API - let detectErrorBoundary = init.detectErrorBoundary; - mapRouteProperties = route => ({ - hasErrorBoundary: detectErrorBoundary(route) - }); - } else { - mapRouteProperties = defaultMapRouteProperties; - } - // Routes keyed by ID - let manifest = {}; - // Routes in tree format for matching - let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest); - let inFlightDataRoutes; - let basename = init.basename || "/"; - // Config driven behavior flags - let future = _extends({ - v7_normalizeFormMethod: false, - v7_prependBasename: false - }, init.future); - // Cleanup function for history - let unlistenHistory = null; - // Externally-provided functions to call on all state changes - let subscribers = new Set(); - // Externally-provided object to hold scroll restoration locations during routing - let savedScrollPositions = null; - // Externally-provided function to get scroll restoration keys - let getScrollRestorationKey = null; - // Externally-provided function to get current scroll position - let getScrollPosition = null; - // One-time flag to control the initial hydration scroll restoration. Because - // we don't get the saved positions from until _after_ - // the initial render, we need to manually trigger a separate updateState to - // send along the restoreScrollPosition - // Set to true if we have `hydrationData` since we assume we were SSR'd and that - // SSR did the initial scroll restoration. - let initialScrollRestored = init.hydrationData != null; - let initialMatches = matchRoutes(dataRoutes, init.history.location, basename); - let initialErrors = null; - if (initialMatches == null) { - // If we do not match a user-provided-route, fall back to the root - // to allow the error boundary to take over - let error = getInternalRouterError(404, { - pathname: init.history.location.pathname - }); - let { - matches, - route - } = getShortCircuitMatches(dataRoutes); - initialMatches = matches; - initialErrors = { - [route.id]: error - }; - } - let initialized = - // All initialMatches need to be loaded before we're ready. If we have lazy - // functions around still then we'll need to run them in initialize() - !initialMatches.some(m => m.route.lazy) && ( - // And we have to either have no loaders or have been provided hydrationData - !initialMatches.some(m => m.route.loader) || init.hydrationData != null); - let router; - let state = { - historyAction: init.history.action, - location: init.history.location, - matches: initialMatches, - initialized, - navigation: IDLE_NAVIGATION, - // Don't restore on initial updateState() if we were SSR'd - restoreScrollPosition: init.hydrationData != null ? false : null, - preventScrollReset: false, - revalidation: "idle", - loaderData: init.hydrationData && init.hydrationData.loaderData || {}, - actionData: init.hydrationData && init.hydrationData.actionData || null, - errors: init.hydrationData && init.hydrationData.errors || initialErrors, - fetchers: new Map(), - blockers: new Map() - }; - // -- Stateful internal variables to manage navigations -- - // Current navigation in progress (to be committed in completeNavigation) - let pendingAction = Action.Pop; - // Should the current navigation prevent the scroll reset if scroll cannot - // be restored? - let pendingPreventScrollReset = false; - // AbortController for the active navigation - let pendingNavigationController; - // We use this to avoid touching history in completeNavigation if a - // revalidation is entirely uninterrupted - let isUninterruptedRevalidation = false; - // Use this internal flag to force revalidation of all loaders: - // - submissions (completed or interrupted) - // - useRevalidator() - // - X-Remix-Revalidate (from redirect) - let isRevalidationRequired = false; - // Use this internal array to capture routes that require revalidation due - // to a cancelled deferred on action submission - let cancelledDeferredRoutes = []; - // Use this internal array to capture fetcher loads that were cancelled by an - // action navigation and require revalidation - let cancelledFetcherLoads = []; - // AbortControllers for any in-flight fetchers - let fetchControllers = new Map(); - // Track loads based on the order in which they started - let incrementingLoadId = 0; - // Track the outstanding pending navigation data load to be compared against - // the globally incrementing load when a fetcher load lands after a completed - // navigation - let pendingNavigationLoadId = -1; - // Fetchers that triggered data reloads as a result of their actions - let fetchReloadIds = new Map(); - // Fetchers that triggered redirect navigations - let fetchRedirectIds = new Set(); - // Most recent href/match for fetcher.load calls for fetchers - let fetchLoadMatches = new Map(); - // Store DeferredData instances for active route matches. When a - // route loader returns defer() we stick one in here. Then, when a nested - // promise resolves we update loaderData. If a new navigation starts we - // cancel active deferreds for eliminated routes. - let activeDeferreds = new Map(); - // Store blocker functions in a separate Map outside of router state since - // we don't need to update UI state if they change - let blockerFunctions = new Map(); - // Flag to ignore the next history update, so we can revert the URL change on - // a POP navigation that was blocked by the user without touching router state - let ignoreNextHistoryUpdate = false; - // Initialize the router, all side effects should be kicked off from here. - // Implemented as a Fluent API for ease of: - // let router = createRouter(init).initialize(); - function initialize() { - // If history informs us of a POP navigation, start the navigation but do not update - // state. We'll update our own state once the navigation completes - unlistenHistory = init.history.listen(_ref => { - let { - action: historyAction, - location, - delta - } = _ref; - // Ignore this event if it was just us resetting the URL from a - // blocked POP navigation - if (ignoreNextHistoryUpdate) { - ignoreNextHistoryUpdate = false; - return; - } - warning(blockerFunctions.size === 0 || delta != null, "You are trying to use a blocker on a POP navigation to a location " + "that was not created by @remix-run/router. This will fail silently in " + "production. This can happen if you are navigating outside the router " + "via `window.history.pushState`/`window.location.hash` instead of using " + "router navigation APIs. This can also happen if you are using " + "createHashRouter and the user manually changes the URL."); - let blockerKey = shouldBlockNavigation({ - currentLocation: state.location, - nextLocation: location, - historyAction - }); - if (blockerKey && delta != null) { - // Restore the URL to match the current UI, but don't update router state - ignoreNextHistoryUpdate = true; - init.history.go(delta * -1); - // Put the blocker into a blocked state - updateBlocker(blockerKey, { - state: "blocked", - location, - proceed() { - updateBlocker(blockerKey, { - state: "proceeding", - proceed: undefined, - reset: undefined, - location - }); - // Re-do the same POP navigation we just blocked - init.history.go(delta); - }, - reset() { - let blockers = new Map(state.blockers); - blockers.set(blockerKey, router_IDLE_BLOCKER); - updateState({ - blockers - }); - } - }); - return; - } - return startNavigation(historyAction, location); - }); - // Kick off initial data load if needed. Use Pop to avoid modifying history - // Note we don't do any handling of lazy here. For SPA's it'll get handled - // in the normal navigation flow. For SSR it's expected that lazy modules are - // resolved prior to router creation since we can't go into a fallbackElement - // UI for SSR'd apps - if (!state.initialized) { - startNavigation(Action.Pop, state.location); - } - return router; - } - // Clean up a router and it's side effects - function dispose() { - if (unlistenHistory) { - unlistenHistory(); - } - subscribers.clear(); - pendingNavigationController && pendingNavigationController.abort(); - state.fetchers.forEach((_, key) => deleteFetcher(key)); - state.blockers.forEach((_, key) => deleteBlocker(key)); - } - // Subscribe to state updates for the router - function subscribe(fn) { - subscribers.add(fn); - return () => subscribers.delete(fn); - } - // Update our state and notify the calling context of the change - function updateState(newState) { - state = _extends({}, state, newState); - subscribers.forEach(subscriber => subscriber(state)); - } - // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION - // and setting state.[historyAction/location/matches] to the new route. - // - Location is a required param - // - Navigation will always be set to IDLE_NAVIGATION - // - Can pass any other state in newState - function completeNavigation(location, newState) { - var _location$state, _location$state2; - // Deduce if we're in a loading/actionReload state: - // - We have committed actionData in the store - // - The current navigation was a mutation submission - // - We're past the submitting state and into the loading state - // - The location being loaded is not the result of a redirect - let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === "loading" && ((_location$state = location.state) == null ? void 0 : _location$state._isRedirect) !== true; - let actionData; - if (newState.actionData) { - if (Object.keys(newState.actionData).length > 0) { - actionData = newState.actionData; - } else { - // Empty actionData -> clear prior actionData due to an action error - actionData = null; - } - } else if (isActionReload) { - // Keep the current data if we're wrapping up the action reload - actionData = state.actionData; - } else { - // Clear actionData on any other completed navigations - actionData = null; - } - // Always preserve any existing loaderData from re-used routes - let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData; - // On a successful navigation we can assume we got through all blockers - // so we can start fresh - let blockers = new Map(); - blockerFunctions.clear(); - // Always respect the user flag. Otherwise don't reset on mutation - // submission navigations unless they redirect - let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true; - if (inFlightDataRoutes) { - dataRoutes = inFlightDataRoutes; - inFlightDataRoutes = undefined; - } - if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) { - init.history.push(location, location.state); - } else if (pendingAction === Action.Replace) { - init.history.replace(location, location.state); - } - updateState(_extends({}, newState, { - actionData, - loaderData, - historyAction: pendingAction, - location, - initialized: true, - navigation: IDLE_NAVIGATION, - revalidation: "idle", - restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches), - preventScrollReset, - blockers - })); - // Reset stateful navigation vars - pendingAction = Action.Pop; - pendingPreventScrollReset = false; - isUninterruptedRevalidation = false; - isRevalidationRequired = false; - cancelledDeferredRoutes = []; - cancelledFetcherLoads = []; - } - // Trigger a navigation event, which can either be a numerical POP or a PUSH - // replace with an optional submission - async function navigate(to, opts) { - if (typeof to === "number") { - init.history.go(to); - return; - } - let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, to, opts == null ? void 0 : opts.fromRouteId, opts == null ? void 0 : opts.relative); - let { - path, - submission, - error - } = normalizeNavigateOptions(future.v7_normalizeFormMethod, false, normalizedPath, opts); - let currentLocation = state.location; - let nextLocation = createLocation(state.location, path, opts && opts.state); - // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded - // URL from window.location, so we need to encode it here so the behavior - // remains the same as POP and non-data-router usages. new URL() does all - // the same encoding we'd get from a history.pushState/window.location read - // without having to touch history - nextLocation = _extends({}, nextLocation, init.history.encodeLocation(nextLocation)); - let userReplace = opts && opts.replace != null ? opts.replace : undefined; - let historyAction = Action.Push; - if (userReplace === true) { - historyAction = Action.Replace; - } else if (userReplace === false) ; else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) { - // By default on submissions to the current location we REPLACE so that - // users don't have to double-click the back button to get to the prior - // location. If the user redirects to a different location from the - // action/loader this will be ignored and the redirect will be a PUSH - historyAction = Action.Replace; - } - let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined; - let blockerKey = shouldBlockNavigation({ - currentLocation, - nextLocation, - historyAction - }); - if (blockerKey) { - // Put the blocker into a blocked state - updateBlocker(blockerKey, { - state: "blocked", - location: nextLocation, - proceed() { - updateBlocker(blockerKey, { - state: "proceeding", - proceed: undefined, - reset: undefined, - location: nextLocation - }); - // Send the same navigation through - navigate(to, opts); - }, - reset() { - let blockers = new Map(state.blockers); - blockers.set(blockerKey, router_IDLE_BLOCKER); - updateState({ - blockers - }); - } - }); - return; - } - return await startNavigation(historyAction, nextLocation, { - submission, - // Send through the formData serialization error if we have one so we can - // render at the right error boundary after we match routes - pendingError: error, - preventScrollReset, - replace: opts && opts.replace - }); - } - // Revalidate all current loaders. If a navigation is in progress or if this - // is interrupted by a navigation, allow this to "succeed" by calling all - // loaders during the next loader round - function revalidate() { - interruptActiveLoads(); - updateState({ - revalidation: "loading" - }); - // If we're currently submitting an action, we don't need to start a new - // navigation, we'll just let the follow up loader execution call all loaders - if (state.navigation.state === "submitting") { - return; - } - // If we're currently in an idle state, start a new navigation for the current - // action/location and mark it as uninterrupted, which will skip the history - // update in completeNavigation - if (state.navigation.state === "idle") { - startNavigation(state.historyAction, state.location, { - startUninterruptedRevalidation: true - }); - return; - } - // Otherwise, if we're currently in a loading state, just start a new - // navigation to the navigation.location but do not trigger an uninterrupted - // revalidation so that history correctly updates once the navigation completes - startNavigation(pendingAction || state.historyAction, state.navigation.location, { - overrideNavigation: state.navigation - }); - } - // Start a navigation to the given action/location. Can optionally provide a - // overrideNavigation which will override the normalLoad in the case of a redirect - // navigation - async function startNavigation(historyAction, location, opts) { - // Abort any in-progress navigations and start a new one. Unset any ongoing - // uninterrupted revalidations unless told otherwise, since we want this - // new navigation to update history normally - pendingNavigationController && pendingNavigationController.abort(); - pendingNavigationController = null; - pendingAction = historyAction; - isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true; - // Save the current scroll position every time we start a new navigation, - // and track whether we should reset scroll on completion - saveScrollPosition(state.location, state.matches); - pendingPreventScrollReset = (opts && opts.preventScrollReset) === true; - let routesToUse = inFlightDataRoutes || dataRoutes; - let loadingNavigation = opts && opts.overrideNavigation; - let matches = matchRoutes(routesToUse, location, basename); - // Short circuit with a 404 on the root error boundary if we match nothing - if (!matches) { - let error = getInternalRouterError(404, { - pathname: location.pathname - }); - let { - matches: notFoundMatches, - route - } = getShortCircuitMatches(routesToUse); - // Cancel all pending deferred on 404s since we don't keep any routes - cancelActiveDeferreds(); - completeNavigation(location, { - matches: notFoundMatches, - loaderData: {}, - errors: { - [route.id]: error - } - }); - return; - } - // Short circuit if it's only a hash change and not a revalidation or - // mutation submission. - // - // Ignore on initial page loads because since the initial load will always - // be "same hash". For example, on /page#hash and submit a - // which will default to a navigation to /page - if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) { - completeNavigation(location, { - matches - }); - return; - } - // Create a controller/Request for this navigation - pendingNavigationController = new AbortController(); - let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission); - let pendingActionData; - let pendingError; - if (opts && opts.pendingError) { - // If we have a pendingError, it means the user attempted a GET submission - // with binary FormData so assign here and skip to handleLoaders. That - // way we handle calling loaders above the boundary etc. It's not really - // different from an actionError in that sense. - pendingError = { - [findNearestBoundary(matches).route.id]: opts.pendingError - }; - } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) { - // Call action if we received an action submission - let actionOutput = await handleAction(request, location, opts.submission, matches, { - replace: opts.replace - }); - if (actionOutput.shortCircuited) { - return; - } - pendingActionData = actionOutput.pendingActionData; - pendingError = actionOutput.pendingActionError; - loadingNavigation = getLoadingNavigation(location, opts.submission); - // Create a GET request for the loaders - request = new Request(request.url, { - signal: request.signal - }); - } - // Call loaders - let { - shortCircuited, - loaderData, - errors - } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, pendingActionData, pendingError); - if (shortCircuited) { - return; - } - // Clean up now that the action/loaders have completed. Don't clean up if - // we short circuited because pendingNavigationController will have already - // been assigned to a new controller for the next navigation - pendingNavigationController = null; - completeNavigation(location, _extends({ - matches - }, pendingActionData ? { - actionData: pendingActionData - } : {}, { - loaderData, - errors - })); - } - // Call the action matched by the leaf route for this navigation and handle - // redirects/errors - async function handleAction(request, location, submission, matches, opts) { - if (opts === void 0) { - opts = {}; - } - interruptActiveLoads(); - // Put us in a submitting state - let navigation = getSubmittingNavigation(location, submission); - updateState({ - navigation - }); - // Call our action and get the result - let result; - let actionMatch = getTargetMatch(matches, location); - if (!actionMatch.route.action && !actionMatch.route.lazy) { - result = { - type: ResultType.error, - error: getInternalRouterError(405, { - method: request.method, - pathname: location.pathname, - routeId: actionMatch.route.id - }) - }; - } else { - result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename); - if (request.signal.aborted) { - return { - shortCircuited: true - }; - } - } - if (isRedirectResult(result)) { - let replace; - if (opts && opts.replace != null) { - replace = opts.replace; - } else { - // If the user didn't explicity indicate replace behavior, replace if - // we redirected to the exact same location we're currently at to avoid - // double back-buttons - replace = result.location === state.location.pathname + state.location.search; - } - await startRedirectNavigation(state, result, { - submission, - replace - }); - return { - shortCircuited: true - }; - } - if (isErrorResult(result)) { - // Store off the pending error - we use it to determine which loaders - // to call and will commit it when we complete the navigation - let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id); - // By default, all submissions are REPLACE navigations, but if the - // action threw an error that'll be rendered in an errorElement, we fall - // back to PUSH so that the user can use the back button to get back to - // the pre-submission form location to try again - if ((opts && opts.replace) !== true) { - pendingAction = Action.Push; - } - return { - // Send back an empty object we can use to clear out any prior actionData - pendingActionData: {}, - pendingActionError: { - [boundaryMatch.route.id]: result.error - } - }; - } - if (isDeferredResult(result)) { - throw getInternalRouterError(400, { - type: "defer-action" - }); - } - return { - pendingActionData: { - [actionMatch.route.id]: result.data - } - }; - } - // Call all applicable loaders for the given matches, handling redirects, - // errors, etc. - async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, pendingActionData, pendingError) { - // Figure out the right navigation we want to use for data loading - let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission); - // If this was a redirect from an action we don't have a "submission" but - // we have it on the loading navigation so use that if available - let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation); - let routesToUse = inFlightDataRoutes || dataRoutes; - let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError); - // Cancel pending deferreds for no-longer-matched routes or routes we're - // about to reload. Note that if this is an action reload we would have - // already cancelled all pending deferreds so this would be a no-op - cancelActiveDeferreds(routeId => !(matches && matches.some(m => m.route.id === routeId)) || matchesToLoad && matchesToLoad.some(m => m.route.id === routeId)); - // Short circuit if we have no loaders to run - if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) { - let updatedFetchers = markFetchRedirectsDone(); - completeNavigation(location, _extends({ - matches, - loaderData: {}, - // Commit pending error if we're short circuiting - errors: pendingError || null - }, pendingActionData ? { - actionData: pendingActionData - } : {}, updatedFetchers ? { - fetchers: new Map(state.fetchers) - } : {})); - return { - shortCircuited: true - }; - } - // If this is an uninterrupted revalidation, we remain in our current idle - // state. If not, we need to switch to our loading state and load data, - // preserving any new action data or existing action data (in the case of - // a revalidation interrupting an actionReload) - if (!isUninterruptedRevalidation) { - revalidatingFetchers.forEach(rf => { - let fetcher = state.fetchers.get(rf.key); - let revalidatingFetcher = getLoadingFetcher(undefined, fetcher ? fetcher.data : undefined); - state.fetchers.set(rf.key, revalidatingFetcher); - }); - let actionData = pendingActionData || state.actionData; - updateState(_extends({ - navigation: loadingNavigation - }, actionData ? Object.keys(actionData).length === 0 ? { - actionData: null - } : { - actionData - } : {}, revalidatingFetchers.length > 0 ? { - fetchers: new Map(state.fetchers) - } : {})); - } - pendingNavigationLoadId = ++incrementingLoadId; - revalidatingFetchers.forEach(rf => { - if (fetchControllers.has(rf.key)) { - abortFetcher(rf.key); - } - if (rf.controller) { - // Fetchers use an independent AbortController so that aborting a fetcher - // (via deleteFetcher) does not abort the triggering navigation that - // triggered the revalidation - fetchControllers.set(rf.key, rf.controller); - } - }); - // Proxy navigation abort through to revalidation fetchers - let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(f => abortFetcher(f.key)); - if (pendingNavigationController) { - pendingNavigationController.signal.addEventListener("abort", abortPendingFetchRevalidations); - } - let { - results, - loaderResults, - fetcherResults - } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request); - if (request.signal.aborted) { - return { - shortCircuited: true - }; - } - // Clean up _after_ loaders have completed. Don't clean up if we short - // circuited because fetchControllers would have been aborted and - // reassigned to new controllers for the next navigation - if (pendingNavigationController) { - pendingNavigationController.signal.removeEventListener("abort", abortPendingFetchRevalidations); - } - revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key)); - // If any loaders returned a redirect Response, start a new REPLACE navigation - let redirect = findRedirect(results); - if (redirect) { - await startRedirectNavigation(state, redirect, { - replace - }); - return { - shortCircuited: true - }; - } - // Process and commit output from loaders - let { - loaderData, - errors - } = processLoaderData(state, matches, matchesToLoad, loaderResults, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds); - // Wire up subscribers to update loaderData as promises settle - activeDeferreds.forEach((deferredData, routeId) => { - deferredData.subscribe(aborted => { - // Note: No need to updateState here since the TrackedPromise on - // loaderData is stable across resolve/reject - // Remove this instance if we were aborted or if promises have settled - if (aborted || deferredData.done) { - activeDeferreds.delete(routeId); - } - }); - }); - let updatedFetchers = markFetchRedirectsDone(); - let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId); - let shouldUpdateFetchers = updatedFetchers || didAbortFetchLoads || revalidatingFetchers.length > 0; - return _extends({ - loaderData, - errors - }, shouldUpdateFetchers ? { - fetchers: new Map(state.fetchers) - } : {}); - } - function getFetcher(key) { - return state.fetchers.get(key) || IDLE_FETCHER; - } - // Trigger a fetcher load/submit for the given fetcher key - function fetch(key, routeId, href, opts) { - if (isServer) { - throw new Error("router.fetch() was called during the server render, but it shouldn't be. " + "You are likely calling a useFetcher() method in the body of your component. " + "Try moving it to a useEffect or a callback."); - } - if (fetchControllers.has(key)) abortFetcher(key); - let routesToUse = inFlightDataRoutes || dataRoutes; - let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, routeId, opts == null ? void 0 : opts.relative); - let matches = matchRoutes(routesToUse, normalizedPath, basename); - if (!matches) { - setFetcherError(key, routeId, getInternalRouterError(404, { - pathname: normalizedPath - })); - return; - } - let { - path, - submission, - error - } = normalizeNavigateOptions(future.v7_normalizeFormMethod, true, normalizedPath, opts); - if (error) { - setFetcherError(key, routeId, error); - return; - } - let match = getTargetMatch(matches, path); - pendingPreventScrollReset = (opts && opts.preventScrollReset) === true; - if (submission && isMutationMethod(submission.formMethod)) { - handleFetcherAction(key, routeId, path, match, matches, submission); - return; - } - // Store off the match so we can call it's shouldRevalidate on subsequent - // revalidations - fetchLoadMatches.set(key, { - routeId, - path - }); - handleFetcherLoader(key, routeId, path, match, matches, submission); - } - // Call the action for the matched fetcher.submit(), and then handle redirects, - // errors, and revalidation - async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) { - interruptActiveLoads(); - fetchLoadMatches.delete(key); - if (!match.route.action && !match.route.lazy) { - let error = getInternalRouterError(405, { - method: submission.formMethod, - pathname: path, - routeId: routeId - }); - setFetcherError(key, routeId, error); - return; - } - // Put this fetcher into it's submitting state - let existingFetcher = state.fetchers.get(key); - let fetcher = getSubmittingFetcher(submission, existingFetcher); - state.fetchers.set(key, fetcher); - updateState({ - fetchers: new Map(state.fetchers) - }); - // Call the action for the fetcher - let abortController = new AbortController(); - let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission); - fetchControllers.set(key, abortController); - let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename); - if (fetchRequest.signal.aborted) { - // We can delete this so long as we weren't aborted by ou our own fetcher - // re-submit which would have put _new_ controller is in fetchControllers - if (fetchControllers.get(key) === abortController) { - fetchControllers.delete(key); - } - return; - } - if (isRedirectResult(actionResult)) { - fetchControllers.delete(key); - fetchRedirectIds.add(key); - let loadingFetcher = getLoadingFetcher(submission); - state.fetchers.set(key, loadingFetcher); - updateState({ - fetchers: new Map(state.fetchers) - }); - return startRedirectNavigation(state, actionResult, { - submission, - isFetchActionRedirect: true - }); - } - // Process any non-redirect errors thrown - if (isErrorResult(actionResult)) { - setFetcherError(key, routeId, actionResult.error); - return; - } - if (isDeferredResult(actionResult)) { - throw getInternalRouterError(400, { - type: "defer-action" - }); - } - // Start the data load for current matches, or the next location if we're - // in the middle of a navigation - let nextLocation = state.navigation.location || state.location; - let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal); - let routesToUse = inFlightDataRoutes || dataRoutes; - let matches = state.navigation.state !== "idle" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches; - invariant(matches, "Didn't find any matches after fetcher action"); - let loadId = ++incrementingLoadId; - fetchReloadIds.set(key, loadId); - let loadFetcher = getLoadingFetcher(submission, actionResult.data); - state.fetchers.set(key, loadFetcher); - let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, { - [match.route.id]: actionResult.data - }, undefined // No need to send through errors since we short circuit above - ); - // Put all revalidating fetchers into the loading state, except for the - // current fetcher which we want to keep in it's current loading state which - // contains it's action submission info + action data - revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => { - let staleKey = rf.key; - let existingFetcher = state.fetchers.get(staleKey); - let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined); - state.fetchers.set(staleKey, revalidatingFetcher); - if (fetchControllers.has(staleKey)) { - abortFetcher(staleKey); - } - if (rf.controller) { - fetchControllers.set(staleKey, rf.controller); - } - }); - updateState({ - fetchers: new Map(state.fetchers) - }); - let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(rf => abortFetcher(rf.key)); - abortController.signal.addEventListener("abort", abortPendingFetchRevalidations); - let { - results, - loaderResults, - fetcherResults - } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest); - if (abortController.signal.aborted) { - return; - } - abortController.signal.removeEventListener("abort", abortPendingFetchRevalidations); - fetchReloadIds.delete(key); - fetchControllers.delete(key); - revalidatingFetchers.forEach(r => fetchControllers.delete(r.key)); - let redirect = findRedirect(results); - if (redirect) { - return startRedirectNavigation(state, redirect); - } - // Process and commit output from loaders - let { - loaderData, - errors - } = processLoaderData(state, state.matches, matchesToLoad, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds); - // Since we let revalidations complete even if the submitting fetcher was - // deleted, only put it back to idle if it hasn't been deleted - if (state.fetchers.has(key)) { - let doneFetcher = getDoneFetcher(actionResult.data); - state.fetchers.set(key, doneFetcher); - } - let didAbortFetchLoads = abortStaleFetchLoads(loadId); - // If we are currently in a navigation loading state and this fetcher is - // more recent than the navigation, we want the newer data so abort the - // navigation and complete it with the fetcher data - if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) { - invariant(pendingAction, "Expected pending action"); - pendingNavigationController && pendingNavigationController.abort(); - completeNavigation(state.navigation.location, { - matches, - loaderData, - errors, - fetchers: new Map(state.fetchers) - }); - } else { - // otherwise just update with the fetcher data, preserving any existing - // loaderData for loaders that did not need to reload. We have to - // manually merge here since we aren't going through completeNavigation - updateState(_extends({ - errors, - loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors) - }, didAbortFetchLoads || revalidatingFetchers.length > 0 ? { - fetchers: new Map(state.fetchers) - } : {})); - isRevalidationRequired = false; - } - } - // Call the matched loader for fetcher.load(), handling redirects, errors, etc. - async function handleFetcherLoader(key, routeId, path, match, matches, submission) { - let existingFetcher = state.fetchers.get(key); - // Put this fetcher into it's loading state - let loadingFetcher = getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined); - state.fetchers.set(key, loadingFetcher); - updateState({ - fetchers: new Map(state.fetchers) - }); - // Call the loader for this fetcher route match - let abortController = new AbortController(); - let fetchRequest = createClientSideRequest(init.history, path, abortController.signal); - fetchControllers.set(key, abortController); - let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, mapRouteProperties, basename); - // Deferred isn't supported for fetcher loads, await everything and treat it - // as a normal load. resolveDeferredData will return undefined if this - // fetcher gets aborted, so we just leave result untouched and short circuit - // below if that happens - if (isDeferredResult(result)) { - result = (await resolveDeferredData(result, fetchRequest.signal, true)) || result; - } - // We can delete this so long as we weren't aborted by our our own fetcher - // re-load which would have put _new_ controller is in fetchControllers - if (fetchControllers.get(key) === abortController) { - fetchControllers.delete(key); - } - if (fetchRequest.signal.aborted) { - return; - } - // If the loader threw a redirect Response, start a new REPLACE navigation - if (isRedirectResult(result)) { - fetchRedirectIds.add(key); - await startRedirectNavigation(state, result); - return; - } - // Process any non-redirect errors thrown - if (isErrorResult(result)) { - let boundaryMatch = findNearestBoundary(state.matches, routeId); - state.fetchers.delete(key); - // TODO: In remix, this would reset to IDLE_NAVIGATION if it was a catch - - // do we need to behave any differently with our non-redirect errors? - // What if it was a non-redirect Response? - updateState({ - fetchers: new Map(state.fetchers), - errors: { - [boundaryMatch.route.id]: result.error - } - }); - return; - } - invariant(!isDeferredResult(result), "Unhandled fetcher deferred data"); - // Put the fetcher back into an idle state - let doneFetcher = getDoneFetcher(result.data); - state.fetchers.set(key, doneFetcher); - updateState({ - fetchers: new Map(state.fetchers) - }); - } - /** - * Utility function to handle redirects returned from an action or loader. - * Normally, a redirect "replaces" the navigation that triggered it. So, for - * example: - * - * - user is on /a - * - user clicks a link to /b - * - loader for /b redirects to /c - * - * In a non-JS app the browser would track the in-flight navigation to /b and - * then replace it with /c when it encountered the redirect response. In - * the end it would only ever update the URL bar with /c. - * - * In client-side routing using pushState/replaceState, we aim to emulate - * this behavior and we also do not update history until the end of the - * navigation (including processed redirects). This means that we never - * actually touch history until we've processed redirects, so we just use - * the history action from the original navigation (PUSH or REPLACE). - */ - async function startRedirectNavigation(state, redirect, _temp) { - let { - submission, - replace, - isFetchActionRedirect - } = _temp === void 0 ? {} : _temp; - if (redirect.revalidate) { - isRevalidationRequired = true; - } - let redirectLocation = createLocation(state.location, redirect.location, // TODO: This can be removed once we get rid of useTransition in Remix v2 - _extends({ - _isRedirect: true - }, isFetchActionRedirect ? { - _isFetchActionRedirect: true - } : {})); - invariant(redirectLocation, "Expected a location on the redirect navigation"); - // Check if this an absolute external redirect that goes to a new origin - if (ABSOLUTE_URL_REGEX.test(redirect.location) && isBrowser) { - let url = init.history.createURL(redirect.location); - let isDifferentBasename = router_stripBasename(url.pathname, basename) == null; - if (routerWindow.location.origin !== url.origin || isDifferentBasename) { - if (replace) { - routerWindow.location.replace(redirect.location); - } else { - routerWindow.location.assign(redirect.location); - } - return; - } - } - // There's no need to abort on redirects, since we don't detect the - // redirect until the action/loaders have settled - pendingNavigationController = null; - let redirectHistoryAction = replace === true ? Action.Replace : Action.Push; - // Use the incoming submission if provided, fallback on the active one in - // state.navigation - let activeSubmission = submission || getSubmissionFromNavigation(state.navigation); - // If this was a 307/308 submission we want to preserve the HTTP method and - // re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the - // redirected location - if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) { - await startNavigation(redirectHistoryAction, redirectLocation, { - submission: _extends({}, activeSubmission, { - formAction: redirect.location - }), - // Preserve this flag across redirects - preventScrollReset: pendingPreventScrollReset - }); - } else if (isFetchActionRedirect) { - // For a fetch action redirect, we kick off a new loading navigation - // without the fetcher submission, but we send it along for shouldRevalidate - await startNavigation(redirectHistoryAction, redirectLocation, { - overrideNavigation: getLoadingNavigation(redirectLocation), - fetcherSubmission: activeSubmission, - // Preserve this flag across redirects - preventScrollReset: pendingPreventScrollReset - }); - } else { - // If we have a submission, we will preserve it through the redirect navigation - let overrideNavigation = getLoadingNavigation(redirectLocation, activeSubmission); - await startNavigation(redirectHistoryAction, redirectLocation, { - overrideNavigation, - // Preserve this flag across redirects - preventScrollReset: pendingPreventScrollReset - }); - } - } - async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) { - // Call all navigation loaders and revalidating fetcher loaders in parallel, - // then slice off the results into separate arrays so we can handle them - // accordingly - let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, mapRouteProperties, basename)), ...fetchersToLoad.map(f => { - if (f.matches && f.match && f.controller) { - return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, f.controller.signal), f.match, f.matches, manifest, mapRouteProperties, basename); - } else { - let error = { - type: ResultType.error, - error: getInternalRouterError(404, { - pathname: f.path - }) - }; - return error; - } - })]); - let loaderResults = results.slice(0, matchesToLoad.length); - let fetcherResults = results.slice(matchesToLoad.length); - await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]); - return { - results, - loaderResults, - fetcherResults - }; - } - function interruptActiveLoads() { - // Every interruption triggers a revalidation - isRevalidationRequired = true; - // Cancel pending route-level deferreds and mark cancelled routes for - // revalidation - cancelledDeferredRoutes.push(...cancelActiveDeferreds()); - // Abort in-flight fetcher loads - fetchLoadMatches.forEach((_, key) => { - if (fetchControllers.has(key)) { - cancelledFetcherLoads.push(key); - abortFetcher(key); - } - }); - } - function setFetcherError(key, routeId, error) { - let boundaryMatch = findNearestBoundary(state.matches, routeId); - deleteFetcher(key); - updateState({ - errors: { - [boundaryMatch.route.id]: error - }, - fetchers: new Map(state.fetchers) - }); - } - function deleteFetcher(key) { - let fetcher = state.fetchers.get(key); - // Don't abort the controller if this is a deletion of a fetcher.submit() - // in it's loading phase since - we don't want to abort the corresponding - // revalidation and want them to complete and land - if (fetchControllers.has(key) && !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))) { - abortFetcher(key); - } - fetchLoadMatches.delete(key); - fetchReloadIds.delete(key); - fetchRedirectIds.delete(key); - state.fetchers.delete(key); - } - function abortFetcher(key) { - let controller = fetchControllers.get(key); - invariant(controller, "Expected fetch controller: " + key); - controller.abort(); - fetchControllers.delete(key); - } - function markFetchersDone(keys) { - for (let key of keys) { - let fetcher = getFetcher(key); - let doneFetcher = getDoneFetcher(fetcher.data); - state.fetchers.set(key, doneFetcher); - } - } - function markFetchRedirectsDone() { - let doneKeys = []; - let updatedFetchers = false; - for (let key of fetchRedirectIds) { - let fetcher = state.fetchers.get(key); - invariant(fetcher, "Expected fetcher: " + key); - if (fetcher.state === "loading") { - fetchRedirectIds.delete(key); - doneKeys.push(key); - updatedFetchers = true; - } - } - markFetchersDone(doneKeys); - return updatedFetchers; - } - function abortStaleFetchLoads(landedId) { - let yeetedKeys = []; - for (let [key, id] of fetchReloadIds) { - if (id < landedId) { - let fetcher = state.fetchers.get(key); - invariant(fetcher, "Expected fetcher: " + key); - if (fetcher.state === "loading") { - abortFetcher(key); - fetchReloadIds.delete(key); - yeetedKeys.push(key); - } - } - } - markFetchersDone(yeetedKeys); - return yeetedKeys.length > 0; - } - function getBlocker(key, fn) { - let blocker = state.blockers.get(key) || router_IDLE_BLOCKER; - if (blockerFunctions.get(key) !== fn) { - blockerFunctions.set(key, fn); - } - return blocker; - } - function deleteBlocker(key) { - state.blockers.delete(key); - blockerFunctions.delete(key); - } - // Utility function to update blockers, ensuring valid state transitions - function updateBlocker(key, newBlocker) { - let blocker = state.blockers.get(key) || router_IDLE_BLOCKER; - // Poor mans state machine :) - // https://mermaid.live/edit#pako:eNqVkc9OwzAMxl8l8nnjAYrEtDIOHEBIgwvKJTReGy3_lDpIqO27k6awMG0XcrLlnz87nwdonESogKXXBuE79rq75XZO3-yHds0RJVuv70YrPlUrCEe2HfrORS3rubqZfuhtpg5C9wk5tZ4VKcRUq88q9Z8RS0-48cE1iHJkL0ugbHuFLus9L6spZy8nX9MP2CNdomVaposqu3fGayT8T8-jJQwhepo_UtpgBQaDEUom04dZhAN1aJBDlUKJBxE1ceB2Smj0Mln-IBW5AFU2dwUiktt_2Qaq2dBfaKdEup85UV7Yd-dKjlnkabl2Pvr0DTkTreM - invariant(blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked", "Invalid blocker state transition: " + blocker.state + " -> " + newBlocker.state); - let blockers = new Map(state.blockers); - blockers.set(key, newBlocker); - updateState({ - blockers - }); - } - function shouldBlockNavigation(_ref2) { - let { - currentLocation, - nextLocation, - historyAction - } = _ref2; - if (blockerFunctions.size === 0) { - return; - } - // We ony support a single active blocker at the moment since we don't have - // any compelling use cases for multi-blocker yet - if (blockerFunctions.size > 1) { - warning(false, "A router only supports one blocker at a time"); - } - let entries = Array.from(blockerFunctions.entries()); - let [blockerKey, blockerFunction] = entries[entries.length - 1]; - let blocker = state.blockers.get(blockerKey); - if (blocker && blocker.state === "proceeding") { - // If the blocker is currently proceeding, we don't need to re-check - // it and can let this navigation continue - return; - } - // At this point, we know we're unblocked/blocked so we need to check the - // user-provided blocker function - if (blockerFunction({ - currentLocation, - nextLocation, - historyAction - })) { - return blockerKey; - } - } - function cancelActiveDeferreds(predicate) { - let cancelledRouteIds = []; - activeDeferreds.forEach((dfd, routeId) => { - if (!predicate || predicate(routeId)) { - // Cancel the deferred - but do not remove from activeDeferreds here - - // we rely on the subscribers to do that so our tests can assert proper - // cleanup via _internalActiveDeferreds - dfd.cancel(); - cancelledRouteIds.push(routeId); - activeDeferreds.delete(routeId); - } - }); - return cancelledRouteIds; - } - // Opt in to capturing and reporting scroll positions during navigations, - // used by the component - function enableScrollRestoration(positions, getPosition, getKey) { - savedScrollPositions = positions; - getScrollPosition = getPosition; - getScrollRestorationKey = getKey || null; - // Perform initial hydration scroll restoration, since we miss the boat on - // the initial updateState() because we've not yet rendered - // and therefore have no savedScrollPositions available - if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) { - initialScrollRestored = true; - let y = getSavedScrollPosition(state.location, state.matches); - if (y != null) { - updateState({ - restoreScrollPosition: y - }); - } - } - return () => { - savedScrollPositions = null; - getScrollPosition = null; - getScrollRestorationKey = null; - }; - } - function getScrollKey(location, matches) { - if (getScrollRestorationKey) { - let key = getScrollRestorationKey(location, matches.map(m => createUseMatchesMatch(m, state.loaderData))); - return key || location.key; - } - return location.key; - } - function saveScrollPosition(location, matches) { - if (savedScrollPositions && getScrollPosition) { - let key = getScrollKey(location, matches); - savedScrollPositions[key] = getScrollPosition(); - } - } - function getSavedScrollPosition(location, matches) { - if (savedScrollPositions) { - let key = getScrollKey(location, matches); - let y = savedScrollPositions[key]; - if (typeof y === "number") { - return y; - } - } - return null; - } - function _internalSetRoutes(newRoutes) { - manifest = {}; - inFlightDataRoutes = convertRoutesToDataRoutes(newRoutes, mapRouteProperties, undefined, manifest); - } - router = { - get basename() { - return basename; - }, - get state() { - return state; - }, - get routes() { - return dataRoutes; - }, - initialize, - subscribe, - enableScrollRestoration, - navigate, - fetch, - revalidate, - // Passthrough to history-aware createHref used by useHref so we get proper - // hash-aware URLs in DOM paths - createHref: to => init.history.createHref(to), - encodeLocation: to => init.history.encodeLocation(to), - getFetcher, - deleteFetcher, - dispose, - getBlocker, - deleteBlocker, - _internalFetchControllers: fetchControllers, - _internalActiveDeferreds: activeDeferreds, - // TODO: Remove setRoutes, it's temporary to avoid dealing with - // updating the tree while validating the update algorithm. - _internalSetRoutes - }; - return router; -} -//#endregion -//////////////////////////////////////////////////////////////////////////////// -//#region createStaticHandler -//////////////////////////////////////////////////////////////////////////////// -const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred"); -function createStaticHandler(routes, opts) { - invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler"); - let manifest = {}; - let basename = (opts ? opts.basename : null) || "/"; - let mapRouteProperties; - if (opts != null && opts.mapRouteProperties) { - mapRouteProperties = opts.mapRouteProperties; - } else if (opts != null && opts.detectErrorBoundary) { - // If they are still using the deprecated version, wrap it with the new API - let detectErrorBoundary = opts.detectErrorBoundary; - mapRouteProperties = route => ({ - hasErrorBoundary: detectErrorBoundary(route) - }); - } else { - mapRouteProperties = defaultMapRouteProperties; - } - let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest); - /** - * The query() method is intended for document requests, in which we want to - * call an optional action and potentially multiple loaders for all nested - * routes. It returns a StaticHandlerContext object, which is very similar - * to the router state (location, loaderData, actionData, errors, etc.) and - * also adds SSR-specific information such as the statusCode and headers - * from action/loaders Responses. - * - * It _should_ never throw and should report all errors through the - * returned context.errors object, properly associating errors to their error - * boundary. Additionally, it tracks _deepestRenderedBoundaryId which can be - * used to emulate React error boundaries during SSr by performing a second - * pass only down to the boundaryId. - * - * The one exception where we do not return a StaticHandlerContext is when a - * redirect response is returned or thrown from any action/loader. We - * propagate that out and return the raw Response so the HTTP server can - * return it directly. - */ - async function query(request, _temp2) { - let { - requestContext - } = _temp2 === void 0 ? {} : _temp2; - let url = new URL(request.url); - let method = request.method; - let location = createLocation("", router_createPath(url), null, "default"); - let matches = matchRoutes(dataRoutes, location, basename); - // SSR supports HEAD requests while SPA doesn't - if (!isValidMethod(method) && method !== "HEAD") { - let error = getInternalRouterError(405, { - method - }); - let { - matches: methodNotAllowedMatches, - route - } = getShortCircuitMatches(dataRoutes); - return { - basename, - location, - matches: methodNotAllowedMatches, - loaderData: {}, - actionData: null, - errors: { - [route.id]: error - }, - statusCode: error.status, - loaderHeaders: {}, - actionHeaders: {}, - activeDeferreds: null - }; - } else if (!matches) { - let error = getInternalRouterError(404, { - pathname: location.pathname - }); - let { - matches: notFoundMatches, - route - } = getShortCircuitMatches(dataRoutes); - return { - basename, - location, - matches: notFoundMatches, - loaderData: {}, - actionData: null, - errors: { - [route.id]: error - }, - statusCode: error.status, - loaderHeaders: {}, - actionHeaders: {}, - activeDeferreds: null - }; - } - let result = await queryImpl(request, location, matches, requestContext); - if (isResponse(result)) { - return result; - } - // When returning StaticHandlerContext, we patch back in the location here - // since we need it for React Context. But this helps keep our submit and - // loadRouteData operating on a Request instead of a Location - return _extends({ - location, - basename - }, result); - } - /** - * The queryRoute() method is intended for targeted route requests, either - * for fetch ?_data requests or resource route requests. In this case, we - * are only ever calling a single action or loader, and we are returning the - * returned value directly. In most cases, this will be a Response returned - * from the action/loader, but it may be a primitive or other value as well - - * and in such cases the calling context should handle that accordingly. - * - * We do respect the throw/return differentiation, so if an action/loader - * throws, then this method will throw the value. This is important so we - * can do proper boundary identification in Remix where a thrown Response - * must go to the Catch Boundary but a returned Response is happy-path. - * - * One thing to note is that any Router-initiated Errors that make sense - * to associate with a status code will be thrown as an ErrorResponse - * instance which include the raw Error, such that the calling context can - * serialize the error as they see fit while including the proper response - * code. Examples here are 404 and 405 errors that occur prior to reaching - * any user-defined loaders. - */ - async function queryRoute(request, _temp3) { - let { - routeId, - requestContext - } = _temp3 === void 0 ? {} : _temp3; - let url = new URL(request.url); - let method = request.method; - let location = createLocation("", router_createPath(url), null, "default"); - let matches = matchRoutes(dataRoutes, location, basename); - // SSR supports HEAD requests while SPA doesn't - if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") { - throw getInternalRouterError(405, { - method - }); - } else if (!matches) { - throw getInternalRouterError(404, { - pathname: location.pathname - }); - } - let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location); - if (routeId && !match) { - throw getInternalRouterError(403, { - pathname: location.pathname, - routeId - }); - } else if (!match) { - // This should never hit I don't think? - throw getInternalRouterError(404, { - pathname: location.pathname - }); - } - let result = await queryImpl(request, location, matches, requestContext, match); - if (isResponse(result)) { - return result; - } - let error = result.errors ? Object.values(result.errors)[0] : undefined; - if (error !== undefined) { - // If we got back result.errors, that means the loader/action threw - // _something_ that wasn't a Response, but it's not guaranteed/required - // to be an `instanceof Error` either, so we have to use throw here to - // preserve the "error" state outside of queryImpl. - throw error; - } - // Pick off the right state value to return - if (result.actionData) { - return Object.values(result.actionData)[0]; - } - if (result.loaderData) { - var _result$activeDeferre; - let data = Object.values(result.loaderData)[0]; - if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) { - data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id]; - } - return data; - } - return undefined; - } - async function queryImpl(request, location, matches, requestContext, routeMatch) { - invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal"); - try { - if (isMutationMethod(request.method.toLowerCase())) { - let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null); - return result; - } - let result = await loadRouteData(request, matches, requestContext, routeMatch); - return isResponse(result) ? result : _extends({}, result, { - actionData: null, - actionHeaders: {} - }); - } catch (e) { - // If the user threw/returned a Response in callLoaderOrAction, we throw - // it to bail out and then return or throw here based on whether the user - // returned or threw - if (isQueryRouteResponse(e)) { - if (e.type === ResultType.error && !isRedirectResponse(e.response)) { - throw e.response; - } - return e.response; - } - // Redirects are always returned since they don't propagate to catch - // boundaries - if (isRedirectResponse(e)) { - return e; - } - throw e; - } - } - async function submit(request, matches, actionMatch, requestContext, isRouteRequest) { - let result; - if (!actionMatch.route.action && !actionMatch.route.lazy) { - let error = getInternalRouterError(405, { - method: request.method, - pathname: new URL(request.url).pathname, - routeId: actionMatch.route.id - }); - if (isRouteRequest) { - throw error; - } - result = { - type: ResultType.error, - error - }; - } else { - result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, { - isStaticRequest: true, - isRouteRequest, - requestContext - }); - if (request.signal.aborted) { - let method = isRouteRequest ? "queryRoute" : "query"; - throw new Error(method + "() call aborted"); - } - } - if (isRedirectResult(result)) { - // Uhhhh - this should never happen, we should always throw these from - // callLoaderOrAction, but the type narrowing here keeps TS happy and we - // can get back on the "throw all redirect responses" train here should - // this ever happen :/ - throw new Response(null, { - status: result.status, - headers: { - Location: result.location - } - }); - } - if (isDeferredResult(result)) { - let error = getInternalRouterError(400, { - type: "defer-action" - }); - if (isRouteRequest) { - throw error; - } - result = { - type: ResultType.error, - error - }; - } - if (isRouteRequest) { - // Note: This should only be non-Response values if we get here, since - // isRouteRequest should throw any Response received in callLoaderOrAction - if (isErrorResult(result)) { - throw result.error; - } - return { - matches: [actionMatch], - loaderData: {}, - actionData: { - [actionMatch.route.id]: result.data - }, - errors: null, - // Note: statusCode + headers are unused here since queryRoute will - // return the raw Response or value - statusCode: 200, - loaderHeaders: {}, - actionHeaders: {}, - activeDeferreds: null - }; - } - if (isErrorResult(result)) { - // Store off the pending error - we use it to determine which loaders - // to call and will commit it when we complete the navigation - let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id); - let context = await loadRouteData(request, matches, requestContext, undefined, { - [boundaryMatch.route.id]: result.error - }); - // action status codes take precedence over loader status codes - return _extends({}, context, { - statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500, - actionData: null, - actionHeaders: _extends({}, result.headers ? { - [actionMatch.route.id]: result.headers - } : {}) - }); - } - // Create a GET request for the loaders - let loaderRequest = new Request(request.url, { - headers: request.headers, - redirect: request.redirect, - signal: request.signal - }); - let context = await loadRouteData(loaderRequest, matches, requestContext); - return _extends({}, context, result.statusCode ? { - statusCode: result.statusCode - } : {}, { - actionData: { - [actionMatch.route.id]: result.data - }, - actionHeaders: _extends({}, result.headers ? { - [actionMatch.route.id]: result.headers - } : {}) - }); - } - async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) { - let isRouteRequest = routeMatch != null; - // Short circuit if we have no loaders to run (queryRoute()) - if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) { - throw getInternalRouterError(400, { - method: request.method, - pathname: new URL(request.url).pathname, - routeId: routeMatch == null ? void 0 : routeMatch.route.id - }); - } - let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]); - let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); - // Short circuit if we have no loaders to run (query()) - if (matchesToLoad.length === 0) { - return { - matches, - // Add a null for all matched routes for proper revalidation on the client - loaderData: matches.reduce((acc, m) => Object.assign(acc, { - [m.route.id]: null - }), {}), - errors: pendingActionError || null, - statusCode: 200, - loaderHeaders: {}, - activeDeferreds: null - }; - } - let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, mapRouteProperties, basename, { - isStaticRequest: true, - isRouteRequest, - requestContext - }))]); - if (request.signal.aborted) { - let method = isRouteRequest ? "queryRoute" : "query"; - throw new Error(method + "() call aborted"); - } - // Process and commit output from loaders - let activeDeferreds = new Map(); - let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds); - // Add a null for any non-loader matches for proper revalidation on the client - let executedLoaders = new Set(matchesToLoad.map(match => match.route.id)); - matches.forEach(match => { - if (!executedLoaders.has(match.route.id)) { - context.loaderData[match.route.id] = null; - } - }); - return _extends({}, context, { - matches, - activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null - }); - } - return { - dataRoutes, - query, - queryRoute - }; -} -//#endregion -//////////////////////////////////////////////////////////////////////////////// -//#region Helpers -//////////////////////////////////////////////////////////////////////////////// -/** - * Given an existing StaticHandlerContext and an error thrown at render time, - * provide an updated StaticHandlerContext suitable for a second SSR render - */ -function getStaticContextFromError(routes, context, error) { - let newContext = _extends({}, context, { - statusCode: 500, - errors: { - [context._deepestRenderedBoundaryId || routes[0].id]: error - } - }); - return newContext; -} -function isSubmissionNavigation(opts) { - return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== undefined); -} -function normalizeTo(location, matches, basename, prependBasename, to, fromRouteId, relative) { - let contextualMatches; - let activeRouteMatch; - if (fromRouteId != null && relative !== "path") { - // Grab matches up to the calling route so our route-relative logic is - // relative to the correct source route. When using relative:path, - // fromRouteId is ignored since that is always relative to the current - // location path - contextualMatches = []; - for (let match of matches) { - contextualMatches.push(match); - if (match.route.id === fromRouteId) { - activeRouteMatch = match; - break; - } - } - } else { - contextualMatches = matches; - activeRouteMatch = matches[matches.length - 1]; - } - // Resolve the relative path - let path = router_resolveTo(to ? to : ".", getPathContributingMatches(contextualMatches).map(m => m.pathnameBase), router_stripBasename(location.pathname, basename) || location.pathname, relative === "path"); - // When `to` is not specified we inherit search/hash from the current - // location, unlike when to="." and we just inherit the path. - // See https://github.com/remix-run/remix/issues/927 - if (to == null) { - path.search = location.search; - path.hash = location.hash; - } - // Add an ?index param for matched index routes if we don't already have one - if ((to == null || to === "" || to === ".") && activeRouteMatch && activeRouteMatch.route.index && !hasNakedIndexQuery(path.search)) { - path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index"; - } - // If we're operating within a basename, prepend it to the pathname. If - // this is a root navigation, then just use the raw basename which allows - // the basename to have full control over the presence of a trailing slash - // on root actions - if (prependBasename && basename !== "/") { - path.pathname = path.pathname === "/" ? basename : router_joinPaths([basename, path.pathname]); - } - return router_createPath(path); -} -// Normalize navigation options by converting formMethod=GET formData objects to -// URLSearchParams so they behave identically to links with query params -function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) { - // Return location verbatim on non-submission navigations - if (!opts || !isSubmissionNavigation(opts)) { - return { - path - }; - } - if (opts.formMethod && !isValidMethod(opts.formMethod)) { - return { - path, - error: getInternalRouterError(405, { - method: opts.formMethod - }) - }; - } - let getInvalidBodyError = () => ({ - path, - error: getInternalRouterError(400, { - type: "invalid-body" - }) - }); - // Create a Submission on non-GET navigations - let rawFormMethod = opts.formMethod || "get"; - let formMethod = normalizeFormMethod ? rawFormMethod.toUpperCase() : rawFormMethod.toLowerCase(); - let formAction = stripHashFromPath(path); - if (opts.body !== undefined) { - if (opts.formEncType === "text/plain") { - // text only support POST/PUT/PATCH/DELETE submissions - if (!isMutationMethod(formMethod)) { - return getInvalidBodyError(); - } - let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ? - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data - Array.from(opts.body.entries()).reduce((acc, _ref3) => { - let [name, value] = _ref3; - return "" + acc + name + "=" + value + "\n"; - }, "") : String(opts.body); - return { - path, - submission: { - formMethod, - formAction, - formEncType: opts.formEncType, - formData: undefined, - json: undefined, - text - } - }; - } else if (opts.formEncType === "application/json") { - // json only supports POST/PUT/PATCH/DELETE submissions - if (!isMutationMethod(formMethod)) { - return getInvalidBodyError(); - } - try { - let json = typeof opts.body === "string" ? JSON.parse(opts.body) : opts.body; - return { - path, - submission: { - formMethod, - formAction, - formEncType: opts.formEncType, - formData: undefined, - json, - text: undefined - } - }; - } catch (e) { - return getInvalidBodyError(); - } - } - } - invariant(typeof FormData === "function", "FormData is not available in this environment"); - let searchParams; - let formData; - if (opts.formData) { - searchParams = convertFormDataToSearchParams(opts.formData); - formData = opts.formData; - } else if (opts.body instanceof FormData) { - searchParams = convertFormDataToSearchParams(opts.body); - formData = opts.body; - } else if (opts.body instanceof URLSearchParams) { - searchParams = opts.body; - formData = convertSearchParamsToFormData(searchParams); - } else if (opts.body == null) { - searchParams = new URLSearchParams(); - formData = new FormData(); - } else { - try { - searchParams = new URLSearchParams(opts.body); - formData = convertSearchParamsToFormData(searchParams); - } catch (e) { - return getInvalidBodyError(); - } - } - let submission = { - formMethod, - formAction, - formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded", - formData, - json: undefined, - text: undefined - }; - if (isMutationMethod(submission.formMethod)) { - return { - path, - submission - }; - } - // Flatten submission onto URLSearchParams for GET submissions - let parsedPath = parsePath(path); - // On GET navigation submissions we can drop the ?index param from the - // resulting location since all loaders will run. But fetcher GET submissions - // only run a single loader so we need to preserve any incoming ?index params - if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) { - searchParams.append("index", ""); - } - parsedPath.search = "?" + searchParams; - return { - path: router_createPath(parsedPath), - submission - }; -} -// Filter out all routes below any caught error as they aren't going to -// render so we don't need to load them -function getLoaderMatchesUntilBoundary(matches, boundaryId) { - let boundaryMatches = matches; - if (boundaryId) { - let index = matches.findIndex(m => m.route.id === boundaryId); - if (index >= 0) { - boundaryMatches = matches.slice(0, index); - } - } - return boundaryMatches; -} -function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) { - let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; - let currentUrl = history.createURL(state.location); - let nextUrl = history.createURL(location); - // Pick navigation matches that are net-new or qualify for revalidation - let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined; - let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId); - let navigationMatches = boundaryMatches.filter((match, index) => { - if (match.route.lazy) { - // We haven't loaded this route yet so we don't know if it's got a loader! - return true; - } - if (match.route.loader == null) { - return false; - } - // Always call the loader on new route instances and pending defer cancellations - if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) { - return true; - } - // This is the default implementation for when we revalidate. If the route - // provides it's own implementation, then we give them full control but - // provide this value so they can leverage it if needed after they check - // their own specific use cases - let currentRouteMatch = state.matches[index]; - let nextRouteMatch = match; - return shouldRevalidateLoader(match, _extends({ - currentUrl, - currentParams: currentRouteMatch.params, - nextUrl, - nextParams: nextRouteMatch.params - }, submission, { - actionResult, - defaultShouldRevalidate: - // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate - isRevalidationRequired || - // Clicked the same link, resubmitted a GET form - currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search || - // Search params affect all loaders - currentUrl.search !== nextUrl.search || isNewRouteInstance(currentRouteMatch, nextRouteMatch) - })); - }); - // Pick fetcher.loads that need to be revalidated - let revalidatingFetchers = []; - fetchLoadMatches.forEach((f, key) => { - // Don't revalidate if fetcher won't be present in the subsequent render - if (!matches.some(m => m.route.id === f.routeId)) { - return; - } - let fetcherMatches = matchRoutes(routesToUse, f.path, basename); - // If the fetcher path no longer matches, push it in with null matches so - // we can trigger a 404 in callLoadersAndMaybeResolveData - if (!fetcherMatches) { - revalidatingFetchers.push({ - key, - routeId: f.routeId, - path: f.path, - matches: null, - match: null, - controller: null - }); - return; - } - // Revalidating fetchers are decoupled from the route matches since they - // load from a static href. They only set `defaultShouldRevalidate` on - // explicit revalidation due to submission, useRevalidator, or X-Remix-Revalidate - // - // They automatically revalidate without even calling shouldRevalidate if: - // - They were cancelled - // - They're in the middle of their first load and therefore this is still - // an initial load and not a revalidation - // - // If neither of those is true, then they _always_ check shouldRevalidate - let fetcher = state.fetchers.get(key); - let isPerformingInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === undefined && - // If a fetcher.load redirected then it'll be "loading" without any data - // so ensure we're not processing the redirect from this fetcher - !fetchRedirectIds.has(key); - let fetcherMatch = getTargetMatch(fetcherMatches, f.path); - let shouldRevalidate = cancelledFetcherLoads.includes(key) || isPerformingInitialLoad || shouldRevalidateLoader(fetcherMatch, _extends({ - currentUrl, - currentParams: state.matches[state.matches.length - 1].params, - nextUrl, - nextParams: matches[matches.length - 1].params - }, submission, { - actionResult, - defaultShouldRevalidate: isRevalidationRequired - })); - if (shouldRevalidate) { - revalidatingFetchers.push({ - key, - routeId: f.routeId, - path: f.path, - matches: fetcherMatches, - match: fetcherMatch, - controller: new AbortController() - }); - } - }); - return [navigationMatches, revalidatingFetchers]; -} -function isNewLoader(currentLoaderData, currentMatch, match) { - let isNew = - // [a] -> [a, b] - !currentMatch || - // [a, b] -> [a, c] - match.route.id !== currentMatch.route.id; - // Handle the case that we don't have data for a re-used route, potentially - // from a prior error or from a cancelled pending deferred - let isMissingData = currentLoaderData[match.route.id] === undefined; - // Always load if this is a net-new route or we don't yet have data - return isNew || isMissingData; -} -function isNewRouteInstance(currentMatch, match) { - let currentPath = currentMatch.route.path; - return ( - // param change for this match, /users/123 -> /users/456 - currentMatch.pathname !== match.pathname || - // splat param changed, which is not present in match.path - // e.g. /files/images/avatar.jpg -> files/finances.xls - currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"] - ); -} -function shouldRevalidateLoader(loaderMatch, arg) { - if (loaderMatch.route.shouldRevalidate) { - let routeChoice = loaderMatch.route.shouldRevalidate(arg); - if (typeof routeChoice === "boolean") { - return routeChoice; - } - } - return arg.defaultShouldRevalidate; -} -/** - * Execute route.lazy() methods to lazily load route modules (loader, action, - * shouldRevalidate) and update the routeManifest in place which shares objects - * with dataRoutes so those get updated as well. - */ -async function loadLazyRouteModule(route, mapRouteProperties, manifest) { - if (!route.lazy) { - return; - } - let lazyRoute = await route.lazy(); - // If the lazy route function was executed and removed by another parallel - // call then we can return - first lazy() to finish wins because the return - // value of lazy is expected to be static - if (!route.lazy) { - return; - } - let routeToUpdate = manifest[route.id]; - invariant(routeToUpdate, "No route found in manifest"); - // Update the route in place. This should be safe because there's no way - // we could yet be sitting on this route as we can't get there without - // resolving lazy() first. - // - // This is different than the HMR "update" use-case where we may actively be - // on the route being updated. The main concern boils down to "does this - // mutation affect any ongoing navigations or any current state.matches - // values?". If not, it should be safe to update in place. - let routeUpdates = {}; - for (let lazyRouteProperty in lazyRoute) { - let staticRouteValue = routeToUpdate[lazyRouteProperty]; - let isPropertyStaticallyDefined = staticRouteValue !== undefined && - // This property isn't static since it should always be updated based - // on the route updates - lazyRouteProperty !== "hasErrorBoundary"; - warning(!isPropertyStaticallyDefined, "Route \"" + routeToUpdate.id + "\" has a static property \"" + lazyRouteProperty + "\" " + "defined but its lazy function is also returning a value for this property. " + ("The lazy route property \"" + lazyRouteProperty + "\" will be ignored.")); - if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) { - routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty]; - } - } - // Mutate the route with the provided updates. Do this first so we pass - // the updated version to mapRouteProperties - Object.assign(routeToUpdate, routeUpdates); - // Mutate the `hasErrorBoundary` property on the route based on the route - // updates and remove the `lazy` function so we don't resolve the lazy - // route again. - Object.assign(routeToUpdate, _extends({}, mapRouteProperties(routeToUpdate), { - lazy: undefined - })); -} -async function callLoaderOrAction(type, request, match, matches, manifest, mapRouteProperties, basename, opts) { - if (opts === void 0) { - opts = {}; - } - let resultType; - let result; - let onReject; - let runHandler = handler => { - // Setup a promise we can race against so that abort signals short circuit - let reject; - let abortPromise = new Promise((_, r) => reject = r); - onReject = () => reject(); - request.signal.addEventListener("abort", onReject); - return Promise.race([handler({ - request, - params: match.params, - context: opts.requestContext - }), abortPromise]); - }; - try { - let handler = match.route[type]; - if (match.route.lazy) { - if (handler) { - // Run statically defined handler in parallel with lazy() - let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]); - result = values[0]; - } else { - // Load lazy route module, then run any returned handler - await loadLazyRouteModule(match.route, mapRouteProperties, manifest); - handler = match.route[type]; - if (handler) { - // Handler still run even if we got interrupted to maintain consistency - // with un-abortable behavior of handler execution on non-lazy or - // previously-lazy-loaded routes - result = await runHandler(handler); - } else if (type === "action") { - let url = new URL(request.url); - let pathname = url.pathname + url.search; - throw getInternalRouterError(405, { - method: request.method, - pathname, - routeId: match.route.id - }); - } else { - // lazy() route has no loader to run. Short circuit here so we don't - // hit the invariant below that errors on returning undefined. - return { - type: ResultType.data, - data: undefined - }; - } - } - } else if (!handler) { - let url = new URL(request.url); - let pathname = url.pathname + url.search; - throw getInternalRouterError(404, { - pathname - }); - } else { - result = await runHandler(handler); - } - invariant(result !== undefined, "You defined " + (type === "action" ? "an action" : "a loader") + " for route " + ("\"" + match.route.id + "\" but didn't return anything from your `" + type + "` ") + "function. Please return a value or `null`."); - } catch (e) { - resultType = ResultType.error; - result = e; - } finally { - if (onReject) { - request.signal.removeEventListener("abort", onReject); - } - } - if (isResponse(result)) { - let status = result.status; - // Process redirects - if (redirectStatusCodes.has(status)) { - let location = result.headers.get("Location"); - invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); - // Support relative routing in internal redirects - if (!ABSOLUTE_URL_REGEX.test(location)) { - location = normalizeTo(new URL(request.url), matches.slice(0, matches.indexOf(match) + 1), basename, true, location); - } else if (!opts.isStaticRequest) { - // Strip off the protocol+origin for same-origin + same-basename absolute - // redirects. If this is a static request, we can let it go back to the - // browser as-is - let currentUrl = new URL(request.url); - let url = location.startsWith("//") ? new URL(currentUrl.protocol + location) : new URL(location); - let isSameBasename = router_stripBasename(url.pathname, basename) != null; - if (url.origin === currentUrl.origin && isSameBasename) { - location = url.pathname + url.search + url.hash; - } - } - // Don't process redirects in the router during static requests requests. - // Instead, throw the Response and let the server handle it with an HTTP - // redirect. We also update the Location header in place in this flow so - // basename and relative routing is taken into account - if (opts.isStaticRequest) { - result.headers.set("Location", location); - throw result; - } - return { - type: ResultType.redirect, - status, - location, - revalidate: result.headers.get("X-Remix-Revalidate") !== null - }; - } - // For SSR single-route requests, we want to hand Responses back directly - // without unwrapping. We do this with the QueryRouteResponse wrapper - // interface so we can know whether it was returned or thrown - if (opts.isRouteRequest) { - // eslint-disable-next-line no-throw-literal - throw { - type: resultType || ResultType.data, - response: result - }; - } - let data; - let contentType = result.headers.get("Content-Type"); - // Check between word boundaries instead of startsWith() due to the last - // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type - if (contentType && /\bapplication\/json\b/.test(contentType)) { - data = await result.json(); - } else { - data = await result.text(); - } - if (resultType === ResultType.error) { - return { - type: resultType, - error: new ErrorResponse(status, result.statusText, data), - headers: result.headers - }; - } - return { - type: ResultType.data, - data, - statusCode: result.status, - headers: result.headers - }; - } - if (resultType === ResultType.error) { - return { - type: resultType, - error: result - }; - } - if (isDeferredData(result)) { - var _result$init, _result$init2; - return { - type: ResultType.deferred, - deferredData: result, - statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status, - headers: ((_result$init2 = result.init) == null ? void 0 : _result$init2.headers) && new Headers(result.init.headers) - }; - } - return { - type: ResultType.data, - data: result - }; -} -// Utility method for creating the Request instances for loaders/actions during -// client-side navigations and fetches. During SSR we will always have a -// Request instance from the static handler (query/queryRoute) -function createClientSideRequest(history, location, signal, submission) { - let url = history.createURL(stripHashFromPath(location)).toString(); - let init = { - signal - }; - if (submission && isMutationMethod(submission.formMethod)) { - let { - formMethod, - formEncType - } = submission; - // Didn't think we needed this but it turns out unlike other methods, patch - // won't be properly normalized to uppercase and results in a 405 error. - // See: https://fetch.spec.whatwg.org/#concept-method - init.method = formMethod.toUpperCase(); - if (formEncType === "application/json") { - init.headers = new Headers({ - "Content-Type": formEncType - }); - init.body = JSON.stringify(submission.json); - } else if (formEncType === "text/plain") { - // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request) - init.body = submission.text; - } else if (formEncType === "application/x-www-form-urlencoded" && submission.formData) { - // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request) - init.body = convertFormDataToSearchParams(submission.formData); - } else { - // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request) - init.body = submission.formData; - } - } - return new Request(url, init); -} -function convertFormDataToSearchParams(formData) { - let searchParams = new URLSearchParams(); - for (let [key, value] of formData.entries()) { - // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs - searchParams.append(key, typeof value === "string" ? value : value.name); - } - return searchParams; -} -function convertSearchParamsToFormData(searchParams) { - let formData = new FormData(); - for (let [key, value] of searchParams.entries()) { - formData.append(key, value); - } - return formData; -} -function processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds) { - // Fill in loaderData/errors from our loaders - let loaderData = {}; - let errors = null; - let statusCode; - let foundError = false; - let loaderHeaders = {}; - // Process loader results into state.loaderData/state.errors - results.forEach((result, index) => { - let id = matchesToLoad[index].route.id; - invariant(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData"); - if (isErrorResult(result)) { - // Look upwards from the matched route for the closest ancestor - // error boundary, defaulting to the root match - let boundaryMatch = findNearestBoundary(matches, id); - let error = result.error; - // If we have a pending action error, we report it at the highest-route - // that throws a loader error, and then clear it out to indicate that - // it was consumed - if (pendingError) { - error = Object.values(pendingError)[0]; - pendingError = undefined; - } - errors = errors || {}; - // Prefer higher error values if lower errors bubble to the same boundary - if (errors[boundaryMatch.route.id] == null) { - errors[boundaryMatch.route.id] = error; - } - // Clear our any prior loaderData for the throwing route - loaderData[id] = undefined; - // Once we find our first (highest) error, we set the status code and - // prevent deeper status codes from overriding - if (!foundError) { - foundError = true; - statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500; - } - if (result.headers) { - loaderHeaders[id] = result.headers; - } - } else { - if (isDeferredResult(result)) { - activeDeferreds.set(id, result.deferredData); - loaderData[id] = result.deferredData.data; - } else { - loaderData[id] = result.data; - } - // Error status codes always override success status codes, but if all - // loaders are successful we take the deepest status code. - if (result.statusCode != null && result.statusCode !== 200 && !foundError) { - statusCode = result.statusCode; - } - if (result.headers) { - loaderHeaders[id] = result.headers; - } - } - }); - // If we didn't consume the pending action error (i.e., all loaders - // resolved), then consume it here. Also clear out any loaderData for the - // throwing route - if (pendingError) { - errors = pendingError; - loaderData[Object.keys(pendingError)[0]] = undefined; - } - return { - loaderData, - errors, - statusCode: statusCode || 200, - loaderHeaders - }; -} -function processLoaderData(state, matches, matchesToLoad, results, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds) { - let { - loaderData, - errors - } = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds); - // Process results from our revalidating fetchers - for (let index = 0; index < revalidatingFetchers.length; index++) { - let { - key, - match, - controller - } = revalidatingFetchers[index]; - invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, "Did not find corresponding fetcher result"); - let result = fetcherResults[index]; - // Process fetcher non-redirect errors - if (controller && controller.signal.aborted) { - // Nothing to do for aborted fetchers - continue; - } else if (isErrorResult(result)) { - let boundaryMatch = findNearestBoundary(state.matches, match == null ? void 0 : match.route.id); - if (!(errors && errors[boundaryMatch.route.id])) { - errors = _extends({}, errors, { - [boundaryMatch.route.id]: result.error - }); - } - state.fetchers.delete(key); - } else if (isRedirectResult(result)) { - // Should never get here, redirects should get processed above, but we - // keep this to type narrow to a success result in the else - invariant(false, "Unhandled fetcher revalidation redirect"); - } else if (isDeferredResult(result)) { - // Should never get here, deferred data should be awaited for fetchers - // in resolveDeferredResults - invariant(false, "Unhandled fetcher deferred data"); - } else { - let doneFetcher = getDoneFetcher(result.data); - state.fetchers.set(key, doneFetcher); - } - } - return { - loaderData, - errors - }; -} -function mergeLoaderData(loaderData, newLoaderData, matches, errors) { - let mergedLoaderData = _extends({}, newLoaderData); - for (let match of matches) { - let id = match.route.id; - if (newLoaderData.hasOwnProperty(id)) { - if (newLoaderData[id] !== undefined) { - mergedLoaderData[id] = newLoaderData[id]; - } - } else if (loaderData[id] !== undefined && match.route.loader) { - // Preserve existing keys not included in newLoaderData and where a loader - // wasn't removed by HMR - mergedLoaderData[id] = loaderData[id]; - } - if (errors && errors.hasOwnProperty(id)) { - // Don't keep any loader data below the boundary - break; - } - } - return mergedLoaderData; -} -// Find the nearest error boundary, looking upwards from the leaf route (or the -// route specified by routeId) for the closest ancestor error boundary, -// defaulting to the root match -function findNearestBoundary(matches, routeId) { - let eligibleMatches = routeId ? matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1) : [...matches]; - return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0]; -} -function getShortCircuitMatches(routes) { - // Prefer a root layout route if present, otherwise shim in a route object - let route = routes.find(r => r.index || !r.path || r.path === "/") || { - id: "__shim-error-route__" - }; - return { - matches: [{ - params: {}, - pathname: "", - pathnameBase: "", - route - }], - route - }; -} -function getInternalRouterError(status, _temp4) { - let { - pathname, - routeId, - method, - type - } = _temp4 === void 0 ? {} : _temp4; - let statusText = "Unknown Server Error"; - let errorMessage = "Unknown @remix-run/router error"; - if (status === 400) { - statusText = "Bad Request"; - if (method && pathname && routeId) { - errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request."; - } else if (type === "defer-action") { - errorMessage = "defer() is not supported in actions"; - } else if (type === "invalid-body") { - errorMessage = "Unable to encode submission body"; - } - } else if (status === 403) { - statusText = "Forbidden"; - errorMessage = "Route \"" + routeId + "\" does not match URL \"" + pathname + "\""; - } else if (status === 404) { - statusText = "Not Found"; - errorMessage = "No route matches URL \"" + pathname + "\""; - } else if (status === 405) { - statusText = "Method Not Allowed"; - if (method && pathname && routeId) { - errorMessage = "You made a " + method.toUpperCase() + " request to \"" + pathname + "\" but " + ("did not provide an `action` for route \"" + routeId + "\", ") + "so there is no way to handle the request."; - } else if (method) { - errorMessage = "Invalid request method \"" + method.toUpperCase() + "\""; - } - } - return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true); -} -// Find any returned redirect errors, starting from the lowest match -function findRedirect(results) { - for (let i = results.length - 1; i >= 0; i--) { - let result = results[i]; - if (isRedirectResult(result)) { - return result; - } - } -} -function stripHashFromPath(path) { - let parsedPath = typeof path === "string" ? parsePath(path) : path; - return router_createPath(_extends({}, parsedPath, { - hash: "" - })); -} -function isHashChangeOnly(a, b) { - if (a.pathname !== b.pathname || a.search !== b.search) { - return false; - } - if (a.hash === "") { - // /page -> /page#hash - return b.hash !== ""; - } else if (a.hash === b.hash) { - // /page#hash -> /page#hash - return true; - } else if (b.hash !== "") { - // /page#hash -> /page#other - return true; - } - // If the hash is removed the browser will re-perform a request to the server - // /page#hash -> /page - return false; -} -function isDeferredResult(result) { - return result.type === ResultType.deferred; -} -function isErrorResult(result) { - return result.type === ResultType.error; -} -function isRedirectResult(result) { - return (result && result.type) === ResultType.redirect; -} -function isDeferredData(value) { - let deferred = value; - return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function"; -} -function isResponse(value) { - return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined"; -} -function isRedirectResponse(result) { - if (!isResponse(result)) { - return false; - } - let status = result.status; - let location = result.headers.get("Location"); - return status >= 300 && status <= 399 && location != null; -} -function isQueryRouteResponse(obj) { - return obj && isResponse(obj.response) && (obj.type === ResultType.data || ResultType.error); -} -function isValidMethod(method) { - return validRequestMethods.has(method.toLowerCase()); -} -function isMutationMethod(method) { - return validMutationMethods.has(method.toLowerCase()); -} -async function resolveDeferredResults(currentMatches, matchesToLoad, results, signals, isFetcher, currentLoaderData) { - for (let index = 0; index < results.length; index++) { - let result = results[index]; - let match = matchesToLoad[index]; - // If we don't have a match, then we can have a deferred result to do - // anything with. This is for revalidating fetchers where the route was - // removed during HMR - if (!match) { - continue; - } - let currentMatch = currentMatches.find(m => m.route.id === match.route.id); - let isRevalidatingLoader = currentMatch != null && !isNewRouteInstance(currentMatch, match) && (currentLoaderData && currentLoaderData[match.route.id]) !== undefined; - if (isDeferredResult(result) && (isFetcher || isRevalidatingLoader)) { - // Note: we do not have to touch activeDeferreds here since we race them - // against the signal in resolveDeferredData and they'll get aborted - // there if needed - let signal = signals[index]; - invariant(signal, "Expected an AbortSignal for revalidating fetcher deferred result"); - await resolveDeferredData(result, signal, isFetcher).then(result => { - if (result) { - results[index] = result || results[index]; - } - }); - } - } -} -async function resolveDeferredData(result, signal, unwrap) { - if (unwrap === void 0) { - unwrap = false; - } - let aborted = await result.deferredData.resolveData(signal); - if (aborted) { - return; - } - if (unwrap) { - try { - return { - type: ResultType.data, - data: result.deferredData.unwrappedData - }; - } catch (e) { - // Handle any TrackedPromise._error values encountered while unwrapping - return { - type: ResultType.error, - error: e - }; - } - } - return { - type: ResultType.data, - data: result.deferredData.data - }; -} -function hasNakedIndexQuery(search) { - return new URLSearchParams(search).getAll("index").some(v => v === ""); -} -// Note: This should match the format exported by useMatches, so if you change -// this please also change that :) Eventually we'll DRY this up -function createUseMatchesMatch(match, loaderData) { - let { - route, - pathname, - params - } = match; - return { - id: route.id, - pathname, - params, - data: loaderData[route.id], - handle: route.handle - }; -} -function getTargetMatch(matches, location) { - let search = typeof location === "string" ? parsePath(location).search : location.search; - if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) { - // Return the leaf index route when index is present - return matches[matches.length - 1]; - } - // Otherwise grab the deepest "path contributing" match (ignoring index and - // pathless layout routes) - let pathMatches = getPathContributingMatches(matches); - return pathMatches[pathMatches.length - 1]; -} -function getSubmissionFromNavigation(navigation) { - let { - formMethod, - formAction, - formEncType, - text, - formData, - json - } = navigation; - if (!formMethod || !formAction || !formEncType) { - return; - } - if (text != null) { - return { - formMethod, - formAction, - formEncType, - formData: undefined, - json: undefined, - text - }; - } else if (formData != null) { - return { - formMethod, - formAction, - formEncType, - formData, - json: undefined, - text: undefined - }; - } else if (json !== undefined) { - return { - formMethod, - formAction, - formEncType, - formData: undefined, - json, - text: undefined - }; - } -} -function getLoadingNavigation(location, submission) { - if (submission) { - let navigation = { - state: "loading", - location, - formMethod: submission.formMethod, - formAction: submission.formAction, - formEncType: submission.formEncType, - formData: submission.formData, - json: submission.json, - text: submission.text - }; - return navigation; - } else { - let navigation = { - state: "loading", - location, - formMethod: undefined, - formAction: undefined, - formEncType: undefined, - formData: undefined, - json: undefined, - text: undefined - }; - return navigation; - } -} -function getSubmittingNavigation(location, submission) { - let navigation = { - state: "submitting", - location, - formMethod: submission.formMethod, - formAction: submission.formAction, - formEncType: submission.formEncType, - formData: submission.formData, - json: submission.json, - text: submission.text - }; - return navigation; -} -function getLoadingFetcher(submission, data) { - if (submission) { - let fetcher = { - state: "loading", - formMethod: submission.formMethod, - formAction: submission.formAction, - formEncType: submission.formEncType, - formData: submission.formData, - json: submission.json, - text: submission.text, - data, - " _hasFetcherDoneAnything ": true - }; - return fetcher; - } else { - let fetcher = { - state: "loading", - formMethod: undefined, - formAction: undefined, - formEncType: undefined, - formData: undefined, - json: undefined, - text: undefined, - data, - " _hasFetcherDoneAnything ": true - }; - return fetcher; - } -} -function getSubmittingFetcher(submission, existingFetcher) { - let fetcher = { - state: "submitting", - formMethod: submission.formMethod, - formAction: submission.formAction, - formEncType: submission.formEncType, - formData: submission.formData, - json: submission.json, - text: submission.text, - data: existingFetcher ? existingFetcher.data : undefined, - " _hasFetcherDoneAnything ": true - }; - return fetcher; -} -function getDoneFetcher(data) { - let fetcher = { - state: "idle", - formMethod: undefined, - formAction: undefined, - formEncType: undefined, - formData: undefined, - json: undefined, - text: undefined, - data, - " _hasFetcherDoneAnything ": true - }; - return fetcher; -} -//#endregion - - -//# sourceMappingURL=router.js.map - -;// CONCATENATED MODULE: ./node_modules/react-router/dist/index.js -/** - * React Router v6.14.0 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */ +/***/ }), +/***/ "./src/js/app/app.js": +/*!***************************!*\ + !*** ./src/js/app/app.js ***! + \***************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/slicedToArray */ \"./node_modules/@babel/runtime/helpers/esm/slicedToArray.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react-router-dom */ \"./node_modules/react-router/dist/index.js\");\n/* harmony import */ var _routes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./routes */ \"./src/js/app/routes/index.js\");\n/* harmony import */ var _components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components */ \"./src/js/app/components/index.js\");\n/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./config */ \"./src/js/app/config/index.js\");\n/* harmony import */ var _context__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./context */ \"./src/js/app/context/index.js\");\n\n\n\n\n\n\n\nvar App = function App() {\n var base_url = _config__WEBPACK_IMPORTED_MODULE_4__.AppConfig.SITE_URL;\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false),\n _useState2 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState, 2),\n loading = _useState2[0],\n setLoading = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([{\n code: \"en\",\n native_name: \"English\",\n is_default: true\n }]),\n _useState4 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState3, 2),\n activeLanguages = _useState4[0],\n setActiveLanguages = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]),\n _useState6 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState5, 2),\n menuData = _useState6[0],\n setMenuData = _useState6[1];\n var _useState7 = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]),\n _useState8 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState7, 2),\n lang = _useState8[0],\n setLang = _useState8[1];\n (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(function () {\n // console.log(\"app config\", AppConfig);\n getActiveLanguages();\n getMenuData();\n }, [lang]);\n var getActiveLanguages = function getActiveLanguages() {\n var SITE_ACTIVE_PLUGINS = _config__WEBPACK_IMPORTED_MODULE_4__.AppConfig.SITE_ACTIVE_PLUGINS;\n var wpml = \"sitepress-multilingual-cms/sitepress.php\";\n if (!SITE_ACTIVE_PLUGINS.includes(wpml)) {\n setActiveLanguages();\n } else {\n var url = \"\".concat(_config__WEBPACK_IMPORTED_MODULE_4__.ApiEndpoint.GET_ACTIVE_LANGUAGES);\n _config__WEBPACK_IMPORTED_MODULE_4__.HttpRequest.httpGet(url).then(function (result) {\n setActiveLanguages(result);\n })[\"catch\"](function (error) {\n console.log(\"Error when getting ActiveLanguages data\", error);\n })[\"finally\"](function () {});\n }\n };\n var getMenuData = function getMenuData() {\n setLoading(true);\n var url = \"/\".concat(lang).concat(_config__WEBPACK_IMPORTED_MODULE_4__.ApiEndpoint.GET_MENUS, \"?menus\");\n _config__WEBPACK_IMPORTED_MODULE_4__.HttpRequest.httpGet(url).then(function (result) {\n setMenuData(result);\n })[\"catch\"](function (error) {\n console.log(\"Error when getting menu data\", error);\n })[\"finally\"](function () {\n setLoading(false);\n });\n };\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement((react__WEBPACK_IMPORTED_MODULE_1___default().Fragment), null, loading ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_components__WEBPACK_IMPORTED_MODULE_3__.AppLoader, null) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(_context__WEBPACK_IMPORTED_MODULE_5__.DataContext.Provider, {\n value: {\n activeLanguages: activeLanguages,\n setActiveLanguages: setActiveLanguages,\n menuData: menuData,\n setMenuData: setMenuData,\n lang: lang,\n setLang: setLang\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_6__.RouterProvider, {\n router: _routes__WEBPACK_IMPORTED_MODULE_2__.routes\n })));\n};\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (App);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvanMvYXBwL2FwcC5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZXMiOlsid2VicGFjazovL21ha2Vycy8uL3NyYy9qcy9hcHAvYXBwLmpzPzk1N2EiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IF9zbGljZWRUb0FycmF5IGZyb20gXCJAYmFiZWwvcnVudGltZS9oZWxwZXJzL3NsaWNlZFRvQXJyYXlcIjtcbmltcG9ydCBSZWFjdCwgeyB1c2VTdGF0ZSwgdXNlRWZmZWN0IH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBSb3V0ZXJQcm92aWRlciB9IGZyb20gXCJyZWFjdC1yb3V0ZXItZG9tXCI7XG5pbXBvcnQgeyByb3V0ZXMgfSBmcm9tIFwiLi9yb3V0ZXNcIjtcbmltcG9ydCB7IEFwcExvYWRlciB9IGZyb20gXCIuL2NvbXBvbmVudHNcIjtcbmltcG9ydCB7IEFwaUVuZHBvaW50LCBBcHBDb25maWcsIEh0dHBSZXF1ZXN0IH0gZnJvbSBcIi4vY29uZmlnXCI7XG5pbXBvcnQgeyBEYXRhQ29udGV4dCB9IGZyb20gXCIuL2NvbnRleHRcIjtcbnZhciBBcHAgPSBmdW5jdGlvbiBBcHAoKSB7XG4gIHZhciBiYXNlX3VybCA9IEFwcENvbmZpZy5TSVRFX1VSTDtcbiAgdmFyIF91c2VTdGF0ZSA9IHVzZVN0YXRlKGZhbHNlKSxcbiAgICBfdXNlU3RhdGUyID0gX3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlLCAyKSxcbiAgICBsb2FkaW5nID0gX3VzZVN0YXRlMlswXSxcbiAgICBzZXRMb2FkaW5nID0gX3VzZVN0YXRlMlsxXTtcbiAgdmFyIF91c2VTdGF0ZTMgPSB1c2VTdGF0ZShbe1xuICAgICAgY29kZTogXCJlblwiLFxuICAgICAgbmF0aXZlX25hbWU6IFwiRW5nbGlzaFwiLFxuICAgICAgaXNfZGVmYXVsdDogdHJ1ZVxuICAgIH1dKSxcbiAgICBfdXNlU3RhdGU0ID0gX3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlMywgMiksXG4gICAgYWN0aXZlTGFuZ3VhZ2VzID0gX3VzZVN0YXRlNFswXSxcbiAgICBzZXRBY3RpdmVMYW5ndWFnZXMgPSBfdXNlU3RhdGU0WzFdO1xuICB2YXIgX3VzZVN0YXRlNSA9IHVzZVN0YXRlKFtdKSxcbiAgICBfdXNlU3RhdGU2ID0gX3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlNSwgMiksXG4gICAgbWVudURhdGEgPSBfdXNlU3RhdGU2WzBdLFxuICAgIHNldE1lbnVEYXRhID0gX3VzZVN0YXRlNlsxXTtcbiAgdmFyIF91c2VTdGF0ZTcgPSB1c2VTdGF0ZShbXSksXG4gICAgX3VzZVN0YXRlOCA9IF9zbGljZWRUb0FycmF5KF91c2VTdGF0ZTcsIDIpLFxuICAgIGxhbmcgPSBfdXNlU3RhdGU4WzBdLFxuICAgIHNldExhbmcgPSBfdXNlU3RhdGU4WzFdO1xuICB1c2VFZmZlY3QoZnVuY3Rpb24gKCkge1xuICAgIC8vIGNvbnNvbGUubG9nKFwiYXBwIGNvbmZpZ1wiLCBBcHBDb25maWcpO1xuICAgIGdldEFjdGl2ZUxhbmd1YWdlcygpO1xuICAgIGdldE1lbnVEYXRhKCk7XG4gIH0sIFtsYW5nXSk7XG4gIHZhciBnZXRBY3RpdmVMYW5ndWFnZXMgPSBmdW5jdGlvbiBnZXRBY3RpdmVMYW5ndWFnZXMoKSB7XG4gICAgdmFyIFNJVEVfQUNUSVZFX1BMVUdJTlMgPSBBcHBDb25maWcuU0lURV9BQ1RJVkVfUExVR0lOUztcbiAgICB2YXIgd3BtbCA9IFwic2l0ZXByZXNzLW11bHRpbGluZ3VhbC1jbXMvc2l0ZXByZXNzLnBocFwiO1xuICAgIGlmICghU0lURV9BQ1RJVkVfUExVR0lOUy5pbmNsdWRlcyh3cG1sKSkge1xuICAgICAgc2V0QWN0aXZlTGFuZ3VhZ2VzKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhciB1cmwgPSBcIlwiLmNvbmNhdChBcGlFbmRwb2ludC5HRVRfQUNUSVZFX0xBTkdVQUdFUyk7XG4gICAgICBIdHRwUmVxdWVzdC5odHRwR2V0KHVybCkudGhlbihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICAgIHNldEFjdGl2ZUxhbmd1YWdlcyhyZXN1bHQpO1xuICAgICAgfSlbXCJjYXRjaFwiXShmdW5jdGlvbiAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJFcnJvciB3aGVuIGdldHRpbmcgQWN0aXZlTGFuZ3VhZ2VzIGRhdGFcIiwgZXJyb3IpO1xuICAgICAgfSlbXCJmaW5hbGx5XCJdKGZ1bmN0aW9uICgpIHt9KTtcbiAgICB9XG4gIH07XG4gIHZhciBnZXRNZW51RGF0YSA9IGZ1bmN0aW9uIGdldE1lbnVEYXRhKCkge1xuICAgIHNldExvYWRpbmcodHJ1ZSk7XG4gICAgdmFyIHVybCA9IFwiL1wiLmNvbmNhdChsYW5nKS5jb25jYXQoQXBpRW5kcG9pbnQuR0VUX01FTlVTLCBcIj9tZW51c1wiKTtcbiAgICBIdHRwUmVxdWVzdC5odHRwR2V0KHVybCkudGhlbihmdW5jdGlvbiAocmVzdWx0KSB7XG4gICAgICBzZXRNZW51RGF0YShyZXN1bHQpO1xuICAgIH0pW1wiY2F0Y2hcIl0oZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmxvZyhcIkVycm9yIHdoZW4gZ2V0dGluZyBtZW51IGRhdGFcIiwgZXJyb3IpO1xuICAgIH0pW1wiZmluYWxseVwiXShmdW5jdGlvbiAoKSB7XG4gICAgICBzZXRMb2FkaW5nKGZhbHNlKTtcbiAgICB9KTtcbiAgfTtcbiAgcmV0dXJuIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFJlYWN0LkZyYWdtZW50LCBudWxsLCBsb2FkaW5nID8gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoQXBwTG9hZGVyLCBudWxsKSA6IC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KERhdGFDb250ZXh0LlByb3ZpZGVyLCB7XG4gICAgdmFsdWU6IHtcbiAgICAgIGFjdGl2ZUxhbmd1YWdlczogYWN0aXZlTGFuZ3VhZ2VzLFxuICAgICAgc2V0QWN0aXZlTGFuZ3VhZ2VzOiBzZXRBY3RpdmVMYW5ndWFnZXMsXG4gICAgICBtZW51RGF0YTogbWVudURhdGEsXG4gICAgICBzZXRNZW51RGF0YTogc2V0TWVudURhdGEsXG4gICAgICBsYW5nOiBsYW5nLFxuICAgICAgc2V0TGFuZzogc2V0TGFuZ1xuICAgIH1cbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoUm91dGVyUHJvdmlkZXIsIHtcbiAgICByb3V0ZXI6IHJvdXRlc1xuICB9KSkpO1xufTtcbmV4cG9ydCBkZWZhdWx0IEFwcDsiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./src/js/app/app.js\n"); +/***/ }), -function dist_extends() { - dist_extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - return target; - }; - return dist_extends.apply(this, arguments); -} - -// Create react-specific types from the agnostic types in @remix-run/router to -// export from react-router -const DataRouterContext = /*#__PURE__*/react.createContext(null); -if (false) {} -const DataRouterStateContext = /*#__PURE__*/react.createContext(null); -if (false) {} -const AwaitContext = /*#__PURE__*/react.createContext(null); -if (false) {} - -/** - * A Navigator is a "location changer"; it's how you get to different locations. - * - * Every history instance conforms to the Navigator interface, but the - * distinction is useful primarily when it comes to the low-level API - * where both the location and a navigator must be provided separately in order - * to avoid "tearing" that may occur in a suspense-enabled app if the action - * and/or location were to be read directly from the history instance. - */ +/***/ "./src/js/app/components/footer/Footer.js": +/*!************************************************!*\ + !*** ./src/js/app/components/footer/Footer.js ***! + \************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -const NavigationContext = /*#__PURE__*/react.createContext(null); -if (false) {} -const LocationContext = /*#__PURE__*/react.createContext(null); -if (false) {} -const RouteContext = /*#__PURE__*/react.createContext({ - outlet: null, - matches: [], - isDataRoute: false -}); -if (false) {} -const RouteErrorContext = /*#__PURE__*/react.createContext(null); -if (false) {} - -/** - * Returns the full href for the given "to" value. This is useful for building - * custom links that are also accessible and preserve right-click behavior. - * - * @see https://reactrouter.com/hooks/use-href - */ -function useHref(to, _temp) { - let { - relative - } = _temp === void 0 ? {} : _temp; - !useInRouterContext() ? false ? 0 : invariant(false) : void 0; - let { - basename, - navigator - } = react.useContext(NavigationContext); - let { - hash, - pathname, - search - } = dist_useResolvedPath(to, { - relative - }); - let joinedPathname = pathname; - - // If we're operating within a basename, prepend it to the pathname prior - // to creating the href. If this is a root navigation, then just use the raw - // basename which allows the basename to have full control over the presence - // of a trailing slash on root links - if (basename !== "/") { - joinedPathname = pathname === "/" ? basename : router_joinPaths([basename, pathname]); - } - return navigator.createHref({ - pathname: joinedPathname, - search, - hash - }); -} - -/** - * Returns true if this component is a descendant of a . - * - * @see https://reactrouter.com/hooks/use-in-router-context - */ -function useInRouterContext() { - return react.useContext(LocationContext) != null; -} - -/** - * Returns the current location object, which represents the current URL in web - * browsers. - * - * Note: If you're using this it may mean you're doing some of your own - * "routing" in your app, and we'd like to know what your use case is. We may - * be able to provide something higher-level to better suit your needs. - * - * @see https://reactrouter.com/hooks/use-location - */ -function dist_useLocation() { - !useInRouterContext() ? false ? 0 : invariant(false) : void 0; - return react.useContext(LocationContext).location; -} - -/** - * Returns the current navigation action which describes how the router came to - * the current location, either by a pop, push, or replace on the history stack. - * - * @see https://reactrouter.com/hooks/use-navigation-type - */ -function useNavigationType() { - return React.useContext(LocationContext).navigationType; -} - -/** - * Returns a PathMatch object if the given pattern matches the current URL. - * This is useful for components that need to know "active" state, e.g. - * . - * - * @see https://reactrouter.com/hooks/use-match - */ -function useMatch(pattern) { - !useInRouterContext() ? false ? 0 : UNSAFE_invariant(false) : void 0; - let { - pathname - } = dist_useLocation(); - return React.useMemo(() => matchPath(pattern, pathname), [pathname, pattern]); -} - -/** - * The interface for the navigate() function returned from useNavigate(). - */ +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Footer: () => (/* binding */ Footer)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! react-router-dom */ \"./node_modules/react-router-dom/dist/index.js\");\n/* harmony import */ var _scss_style_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../../scss/style.scss */ \"./src/scss/style.scss\");\n/* harmony import */ var _config_AppConfig__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/AppConfig */ \"./src/js/app/config/AppConfig.js\");\n\n\n\n\nvar Footer = function Footer() {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"footer\", null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"container\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: \"d-flex justify-content-between py-4 my-4 border-top\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"p\", null, \"\\xA9 2023\", _config_AppConfig__WEBPACK_IMPORTED_MODULE_2__.AppConfig.SITE_TITLE ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", null, \" \", _config_AppConfig__WEBPACK_IMPORTED_MODULE_2__.AppConfig.SITE_TITLE) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", null, \" Site Title \"), \". All rights reserved.\"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"ul\", {\n className: \"list-unstyled d-flex\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"li\", {\n className: \"ms-3\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_3__.Link, {\n className: \"link-light\",\n href: \"https://twitter.com/webxrawards\",\n rel: \"noreferrer\",\n target: \"_blank\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"dropdown__logo-img\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"fa-brands fa-twitter fa-xl\"\n })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"li\", {\n className: \"ms-3\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_3__.Link, {\n className: \"link-light\",\n href: \"https://www.instagram.com/webxrawards/\",\n rel: \"noreferrer\",\n target: \"_blank\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"dropdown__logo-img\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"fa-brands fa-instagram fa-xl\"\n })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"li\", {\n className: \"ms-3\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_3__.Link, {\n className: \"link-light\",\n href: \"https://www.facebook.com/groups/webxrawards\",\n rel: \"noreferrer\",\n target: \"_blank\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"dropdown__logo-img\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"fa-brands fa-facebook fa-xl\"\n })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"li\", {\n className: \"ms-3\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_3__.Link, {\n className: \"link-light\",\n href: \"https://www.linkedin.com/company/the-polys/\",\n rel: \"noreferrer\",\n target: \"_blank\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"dropdown__logo-img\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"fa-brands fa-linkedin fa-xl\"\n })))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"li\", {\n className: \"ms-3\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_3__.Link, {\n className: \"link-light\",\n href: \"https://discord.gg/T5vRuM5cDS\",\n rel: \"noreferrer\",\n target: \"_blank\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"span\", {\n className: \"dropdown__logo-img\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"i\", {\n className: \"fa-brands fa-discord fa-xl\"\n }))))))));\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvanMvYXBwL2NvbXBvbmVudHMvZm9vdGVyL0Zvb3Rlci5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vbWFrZXJzLy4vc3JjL2pzL2FwcC9jb21wb25lbnRzL2Zvb3Rlci9Gb290ZXIuanM/Y2UzMiJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBMaW5rIH0gZnJvbSBcInJlYWN0LXJvdXRlci1kb21cIjtcbmltcG9ydCBcIi4uLy4uLy4uLy4uL3Njc3Mvc3R5bGUuc2Nzc1wiO1xuaW1wb3J0IHsgQXBwQ29uZmlnIH0gZnJvbSBcIi4uLy4uL2NvbmZpZy9BcHBDb25maWdcIjtcbmV4cG9ydCB2YXIgRm9vdGVyID0gZnVuY3Rpb24gRm9vdGVyKCkge1xuICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJmb290ZXJcIiwgbnVsbCwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIiwge1xuICAgIGNsYXNzTmFtZTogXCJjb250YWluZXJcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImRpdlwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImQtZmxleCBqdXN0aWZ5LWNvbnRlbnQtYmV0d2VlbiBweS00IG15LTQgYm9yZGVyLXRvcFwiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwicFwiLCBudWxsLCBcIlxceEE5IDIwMjNcIiwgQXBwQ29uZmlnLlNJVEVfVElUTEUgPyAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInNwYW5cIiwgbnVsbCwgXCIgXCIsIEFwcENvbmZpZy5TSVRFX1RJVExFKSA6IC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwic3BhblwiLCBudWxsLCBcIiBTaXRlIFRpdGxlIFwiKSwgXCIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXCIpLCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInVsXCIsIHtcbiAgICBjbGFzc05hbWU6IFwibGlzdC11bnN0eWxlZCBkLWZsZXhcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImxpXCIsIHtcbiAgICBjbGFzc05hbWU6IFwibXMtM1wiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KExpbmssIHtcbiAgICBjbGFzc05hbWU6IFwibGluay1saWdodFwiLFxuICAgIGhyZWY6IFwiaHR0cHM6Ly90d2l0dGVyLmNvbS93ZWJ4cmF3YXJkc1wiLFxuICAgIHJlbDogXCJub3JlZmVycmVyXCIsXG4gICAgdGFyZ2V0OiBcIl9ibGFua1wiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwic3BhblwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImRyb3Bkb3duX19sb2dvLWltZ1wiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiaVwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImZhLWJyYW5kcyBmYS10d2l0dGVyIGZhLXhsXCJcbiAgfSkpKSksIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwibGlcIiwge1xuICAgIGNsYXNzTmFtZTogXCJtcy0zXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoTGluaywge1xuICAgIGNsYXNzTmFtZTogXCJsaW5rLWxpZ2h0XCIsXG4gICAgaHJlZjogXCJodHRwczovL3d3dy5pbnN0YWdyYW0uY29tL3dlYnhyYXdhcmRzL1wiLFxuICAgIHJlbDogXCJub3JlZmVycmVyXCIsXG4gICAgdGFyZ2V0OiBcIl9ibGFua1wiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwic3BhblwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImRyb3Bkb3duX19sb2dvLWltZ1wiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiaVwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImZhLWJyYW5kcyBmYS1pbnN0YWdyYW0gZmEteGxcIlxuICB9KSkpKSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgY2xhc3NOYW1lOiBcIm1zLTNcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgY2xhc3NOYW1lOiBcImxpbmstbGlnaHRcIixcbiAgICBocmVmOiBcImh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9ncm91cHMvd2VieHJhd2FyZHNcIixcbiAgICByZWw6IFwibm9yZWZlcnJlclwiLFxuICAgIHRhcmdldDogXCJfYmxhbmtcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInNwYW5cIiwge1xuICAgIGNsYXNzTmFtZTogXCJkcm9wZG93bl9fbG9nby1pbWdcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImlcIiwge1xuICAgIGNsYXNzTmFtZTogXCJmYS1icmFuZHMgZmEtZmFjZWJvb2sgZmEteGxcIlxuICB9KSkpKSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgY2xhc3NOYW1lOiBcIm1zLTNcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgY2xhc3NOYW1lOiBcImxpbmstbGlnaHRcIixcbiAgICBocmVmOiBcImh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9jb21wYW55L3RoZS1wb2x5cy9cIixcbiAgICByZWw6IFwibm9yZWZlcnJlclwiLFxuICAgIHRhcmdldDogXCJfYmxhbmtcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInNwYW5cIiwge1xuICAgIGNsYXNzTmFtZTogXCJkcm9wZG93bl9fbG9nby1pbWdcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImlcIiwge1xuICAgIGNsYXNzTmFtZTogXCJmYS1icmFuZHMgZmEtbGlua2VkaW4gZmEteGxcIlxuICB9KSkpKSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgY2xhc3NOYW1lOiBcIm1zLTNcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgY2xhc3NOYW1lOiBcImxpbmstbGlnaHRcIixcbiAgICBocmVmOiBcImh0dHBzOi8vZGlzY29yZC5nZy9UNXZSdU01Y0RTXCIsXG4gICAgcmVsOiBcIm5vcmVmZXJyZXJcIixcbiAgICB0YXJnZXQ6IFwiX2JsYW5rXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIsIHtcbiAgICBjbGFzc05hbWU6IFwiZHJvcGRvd25fX2xvZ28taW1nXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJpXCIsIHtcbiAgICBjbGFzc05hbWU6IFwiZmEtYnJhbmRzIGZhLWRpc2NvcmQgZmEteGxcIlxuICB9KSkpKSkpKSk7XG59OyJdLCJuYW1lcyI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/js/app/components/footer/Footer.js\n"); -const navigateEffectWarning = (/* unused pure expression or super */ null && ("You should call navigate() in a React.useEffect(), not when " + "your component is first rendered.")); - -// Mute warnings for calls to useNavigate in SSR environments -function useIsomorphicLayoutEffect(cb) { - let isStatic = react.useContext(NavigationContext).static; - if (!isStatic) { - // We should be able to get rid of this once react 18.3 is released - // See: https://github.com/facebook/react/pull/26395 - // eslint-disable-next-line react-hooks/rules-of-hooks - react.useLayoutEffect(cb); - } -} - -/** - * Returns an imperative method for changing the location. Used by s, but - * may also be used by other elements to change the location. - * - * @see https://reactrouter.com/hooks/use-navigate - */ -function dist_useNavigate() { - let { - isDataRoute - } = react.useContext(RouteContext); - // Conditional usage is OK here because the usage of a data router is static - // eslint-disable-next-line react-hooks/rules-of-hooks - return isDataRoute ? useNavigateStable() : useNavigateUnstable(); -} -function useNavigateUnstable() { - !useInRouterContext() ? false ? 0 : invariant(false) : void 0; - let dataRouterContext = react.useContext(DataRouterContext); - let { - basename, - navigator - } = react.useContext(NavigationContext); - let { - matches - } = react.useContext(RouteContext); - let { - pathname: locationPathname - } = dist_useLocation(); - let routePathnamesJson = JSON.stringify(getPathContributingMatches(matches).map(match => match.pathnameBase)); - let activeRef = react.useRef(false); - useIsomorphicLayoutEffect(() => { - activeRef.current = true; - }); - let navigate = react.useCallback(function (to, options) { - if (options === void 0) { - options = {}; - } - false ? 0 : void 0; - - // Short circuit here since if this happens on first render the navigate - // is useless because we haven't wired up our history listener yet - if (!activeRef.current) return; - if (typeof to === "number") { - navigator.go(to); - return; - } - let path = router_resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === "path"); - - // If we're operating within a basename, prepend it to the pathname prior - // to handing off to history (but only if we're not in a data router, - // otherwise it'll prepend the basename inside of the router). - // If this is a root navigation, then we navigate to the raw basename - // which allows the basename to have full control over the presence of a - // trailing slash on root links - if (dataRouterContext == null && basename !== "/") { - path.pathname = path.pathname === "/" ? basename : router_joinPaths([basename, path.pathname]); - } - (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options); - }, [basename, navigator, routePathnamesJson, locationPathname, dataRouterContext]); - return navigate; -} -const OutletContext = /*#__PURE__*/react.createContext(null); - -/** - * Returns the context (if provided) for the child route at this level of the route - * hierarchy. - * @see https://reactrouter.com/hooks/use-outlet-context - */ -function useOutletContext() { - return React.useContext(OutletContext); -} - -/** - * Returns the element for the child route at this level of the route - * hierarchy. Used internally by to render child routes. - * - * @see https://reactrouter.com/hooks/use-outlet - */ -function useOutlet(context) { - let outlet = react.useContext(RouteContext).outlet; - if (outlet) { - return /*#__PURE__*/react.createElement(OutletContext.Provider, { - value: context - }, outlet); - } - return outlet; -} - -/** - * Returns an object of key/value pairs of the dynamic params from the current - * URL that were matched by the route path. - * - * @see https://reactrouter.com/hooks/use-params - */ -function useParams() { - let { - matches - } = react.useContext(RouteContext); - let routeMatch = matches[matches.length - 1]; - return routeMatch ? routeMatch.params : {}; -} - -/** - * Resolves the pathname of the given `to` value against the current location. - * - * @see https://reactrouter.com/hooks/use-resolved-path - */ -function dist_useResolvedPath(to, _temp2) { - let { - relative - } = _temp2 === void 0 ? {} : _temp2; - let { - matches - } = react.useContext(RouteContext); - let { - pathname: locationPathname - } = dist_useLocation(); - let routePathnamesJson = JSON.stringify(getPathContributingMatches(matches).map(match => match.pathnameBase)); - return react.useMemo(() => router_resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === "path"), [to, routePathnamesJson, locationPathname, relative]); -} - -/** - * Returns the element of the route that matched the current location, prepared - * with the correct context to render the remainder of the route tree. Route - * elements in the tree must render an to render their child route's - * element. - * - * @see https://reactrouter.com/hooks/use-routes - */ -function useRoutes(routes, locationArg) { - return useRoutesImpl(routes, locationArg); -} - -// Internal implementation with accept optional param for RouterProvider usage -function useRoutesImpl(routes, locationArg, dataRouterState) { - !useInRouterContext() ? false ? 0 : invariant(false) : void 0; - let { - navigator - } = react.useContext(NavigationContext); - let { - matches: parentMatches - } = react.useContext(RouteContext); - let routeMatch = parentMatches[parentMatches.length - 1]; - let parentParams = routeMatch ? routeMatch.params : {}; - let parentPathname = routeMatch ? routeMatch.pathname : "/"; - let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/"; - let parentRoute = routeMatch && routeMatch.route; - if (false) {} - let locationFromContext = dist_useLocation(); - let location; - if (locationArg) { - var _parsedLocationArg$pa; - let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg; - !(parentPathnameBase === "/" || ((_parsedLocationArg$pa = parsedLocationArg.pathname) == null ? void 0 : _parsedLocationArg$pa.startsWith(parentPathnameBase))) ? false ? 0 : invariant(false) : void 0; - location = parsedLocationArg; - } else { - location = locationFromContext; - } - let pathname = location.pathname || "/"; - let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/"; - let matches = matchRoutes(routes, { - pathname: remainingPathname - }); - if (false) {} - let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, { - params: Object.assign({}, parentParams, match.params), - pathname: router_joinPaths([parentPathnameBase, - // Re-encode pathnames that were decoded inside matchRoutes - navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname]), - pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : router_joinPaths([parentPathnameBase, - // Re-encode pathnames that were decoded inside matchRoutes - navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase]) - })), parentMatches, dataRouterState); - - // When a user passes in a `locationArg`, the associated routes need to - // be wrapped in a new `LocationContext.Provider` in order for `useLocation` - // to use the scoped location instead of the global location. - if (locationArg && renderedMatches) { - return /*#__PURE__*/react.createElement(LocationContext.Provider, { - value: { - location: dist_extends({ - pathname: "/", - search: "", - hash: "", - state: null, - key: "default" - }, location), - navigationType: Action.Pop - } - }, renderedMatches); - } - return renderedMatches; -} -function DefaultErrorComponent() { - let error = useRouteError(); - let message = isRouteErrorResponse(error) ? error.status + " " + error.statusText : error instanceof Error ? error.message : JSON.stringify(error); - let stack = error instanceof Error ? error.stack : null; - let lightgrey = "rgba(200,200,200, 0.5)"; - let preStyles = { - padding: "0.5rem", - backgroundColor: lightgrey - }; - let codeStyles = { - padding: "2px 4px", - backgroundColor: lightgrey - }; - let devInfo = null; - if (false) {} - return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("h2", null, "Unexpected Application Error!"), /*#__PURE__*/react.createElement("h3", { - style: { - fontStyle: "italic" - } - }, message), stack ? /*#__PURE__*/react.createElement("pre", { - style: preStyles - }, stack) : null, devInfo); -} -const defaultErrorElement = /*#__PURE__*/react.createElement(DefaultErrorComponent, null); -class RenderErrorBoundary extends react.Component { - constructor(props) { - super(props); - this.state = { - location: props.location, - revalidation: props.revalidation, - error: props.error - }; - } - static getDerivedStateFromError(error) { - return { - error: error - }; - } - static getDerivedStateFromProps(props, state) { - // When we get into an error state, the user will likely click "back" to the - // previous page that didn't have an error. Because this wraps the entire - // application, that will have no effect--the error page continues to display. - // This gives us a mechanism to recover from the error when the location changes. - // - // Whether we're in an error state or not, we update the location in state - // so that when we are in an error state, it gets reset when a new location - // comes in and the user recovers from the error. - if (state.location !== props.location || state.revalidation !== "idle" && props.revalidation === "idle") { - return { - error: props.error, - location: props.location, - revalidation: props.revalidation - }; - } - - // If we're not changing locations, preserve the location but still surface - // any new errors that may come through. We retain the existing error, we do - // this because the error provided from the app state may be cleared without - // the location changing. - return { - error: props.error || state.error, - location: state.location, - revalidation: props.revalidation || state.revalidation - }; - } - componentDidCatch(error, errorInfo) { - console.error("React Router caught the following error during render", error, errorInfo); - } - render() { - return this.state.error ? /*#__PURE__*/react.createElement(RouteContext.Provider, { - value: this.props.routeContext - }, /*#__PURE__*/react.createElement(RouteErrorContext.Provider, { - value: this.state.error, - children: this.props.component - })) : this.props.children; - } -} -function RenderedRoute(_ref) { - let { - routeContext, - match, - children - } = _ref; - let dataRouterContext = react.useContext(DataRouterContext); - - // Track how deep we got in our render pass to emulate SSR componentDidCatch - // in a DataStaticRouter - if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) { - dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id; - } - return /*#__PURE__*/react.createElement(RouteContext.Provider, { - value: routeContext - }, children); -} -function _renderMatches(matches, parentMatches, dataRouterState) { - var _dataRouterState2; - if (parentMatches === void 0) { - parentMatches = []; - } - if (dataRouterState === void 0) { - dataRouterState = null; - } - if (matches == null) { - var _dataRouterState; - if ((_dataRouterState = dataRouterState) != null && _dataRouterState.errors) { - // Don't bail if we have data router errors so we can render them in the - // boundary. Use the pre-matched (or shimmed) matches - matches = dataRouterState.matches; - } else { - return null; - } - } - let renderedMatches = matches; - - // If we have data errors, trim matches to the highest error boundary - let errors = (_dataRouterState2 = dataRouterState) == null ? void 0 : _dataRouterState2.errors; - if (errors != null) { - let errorIndex = renderedMatches.findIndex(m => m.route.id && (errors == null ? void 0 : errors[m.route.id])); - !(errorIndex >= 0) ? false ? 0 : invariant(false) : void 0; - renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1)); - } - return renderedMatches.reduceRight((outlet, match, index) => { - let error = match.route.id ? errors == null ? void 0 : errors[match.route.id] : null; - // Only data routers handle errors - let errorElement = null; - if (dataRouterState) { - errorElement = match.route.errorElement || defaultErrorElement; - } - let matches = parentMatches.concat(renderedMatches.slice(0, index + 1)); - let getChildren = () => { - let children; - if (error) { - children = errorElement; - } else if (match.route.Component) { - // Note: This is a de-optimized path since React won't re-use the - // ReactElement since it's identity changes with each new - // React.createElement call. We keep this so folks can use - // `` in `` but generally `Component` - // usage is only advised in `RouterProvider` when we can convert it to - // `element` ahead of time. - children = /*#__PURE__*/react.createElement(match.route.Component, null); - } else if (match.route.element) { - children = match.route.element; - } else { - children = outlet; - } - return /*#__PURE__*/react.createElement(RenderedRoute, { - match: match, - routeContext: { - outlet, - matches, - isDataRoute: dataRouterState != null - }, - children: children - }); - }; - // Only wrap in an error boundary within data router usages when we have an - // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to - // an ancestor ErrorBoundary/errorElement - return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /*#__PURE__*/react.createElement(RenderErrorBoundary, { - location: dataRouterState.location, - revalidation: dataRouterState.revalidation, - component: errorElement, - error: error, - children: getChildren(), - routeContext: { - outlet: null, - matches, - isDataRoute: true - } - }) : getChildren(); - }, null); -} -var DataRouterHook; -(function (DataRouterHook) { - DataRouterHook["UseBlocker"] = "useBlocker"; - DataRouterHook["UseRevalidator"] = "useRevalidator"; - DataRouterHook["UseNavigateStable"] = "useNavigate"; -})(DataRouterHook || (DataRouterHook = {})); -var DataRouterStateHook; -(function (DataRouterStateHook) { - DataRouterStateHook["UseBlocker"] = "useBlocker"; - DataRouterStateHook["UseLoaderData"] = "useLoaderData"; - DataRouterStateHook["UseActionData"] = "useActionData"; - DataRouterStateHook["UseRouteError"] = "useRouteError"; - DataRouterStateHook["UseNavigation"] = "useNavigation"; - DataRouterStateHook["UseRouteLoaderData"] = "useRouteLoaderData"; - DataRouterStateHook["UseMatches"] = "useMatches"; - DataRouterStateHook["UseRevalidator"] = "useRevalidator"; - DataRouterStateHook["UseNavigateStable"] = "useNavigate"; - DataRouterStateHook["UseRouteId"] = "useRouteId"; -})(DataRouterStateHook || (DataRouterStateHook = {})); -function getDataRouterConsoleError(hookName) { - return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router."; -} -function useDataRouterContext(hookName) { - let ctx = react.useContext(DataRouterContext); - !ctx ? false ? 0 : invariant(false) : void 0; - return ctx; -} -function useDataRouterState(hookName) { - let state = react.useContext(DataRouterStateContext); - !state ? false ? 0 : invariant(false) : void 0; - return state; -} -function useRouteContext(hookName) { - let route = react.useContext(RouteContext); - !route ? false ? 0 : invariant(false) : void 0; - return route; -} - -// Internal version with hookName-aware debugging -function useCurrentRouteId(hookName) { - let route = useRouteContext(hookName); - let thisRoute = route.matches[route.matches.length - 1]; - !thisRoute.route.id ? false ? 0 : invariant(false) : void 0; - return thisRoute.route.id; -} - -/** - * Returns the ID for the nearest contextual route - */ -function useRouteId() { - return useCurrentRouteId(DataRouterStateHook.UseRouteId); -} +/***/ }), -/** - * Returns the current navigation, defaulting to an "idle" navigation when - * no navigation is in progress - */ -function dist_useNavigation() { - let state = useDataRouterState(DataRouterStateHook.UseNavigation); - return state.navigation; -} - -/** - * Returns a revalidate function for manually triggering revalidation, as well - * as the current state of any manual revalidations - */ -function useRevalidator() { - let dataRouterContext = useDataRouterContext(DataRouterHook.UseRevalidator); - let state = useDataRouterState(DataRouterStateHook.UseRevalidator); - return { - revalidate: dataRouterContext.router.revalidate, - state: state.revalidation - }; -} - -/** - * Returns the active route matches, useful for accessing loaderData for - * parent/child routes or the route "handle" property - */ -function dist_useMatches() { - let { - matches, - loaderData - } = useDataRouterState(DataRouterStateHook.UseMatches); - return React.useMemo(() => matches.map(match => { - let { - pathname, - params - } = match; - // Note: This structure matches that created by createUseMatchesMatch - // in the @remix-run/router , so if you change this please also change - // that :) Eventually we'll DRY this up - return { - id: match.route.id, - pathname, - params, - data: loaderData[match.route.id], - handle: match.route.handle - }; - }), [matches, loaderData]); -} - -/** - * Returns the loader data for the nearest ancestor Route loader - */ -function useLoaderData() { - let state = useDataRouterState(DataRouterStateHook.UseLoaderData); - let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData); - if (state.errors && state.errors[routeId] != null) { - console.error("You cannot `useLoaderData` in an errorElement (routeId: " + routeId + ")"); - return undefined; - } - return state.loaderData[routeId]; -} - -/** - * Returns the loaderData for the given routeId - */ -function useRouteLoaderData(routeId) { - let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData); - return state.loaderData[routeId]; -} +/***/ "./src/js/app/components/header/Header.js": +/*!************************************************!*\ + !*** ./src/js/app/components/header/Header.js ***! + \************************************************/ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -/** - * Returns the action data for the nearest ancestor Route action - */ -function useActionData() { - let state = useDataRouterState(DataRouterStateHook.UseActionData); - let route = React.useContext(RouteContext); - !route ? false ? 0 : UNSAFE_invariant(false) : void 0; - return Object.values((state == null ? void 0 : state.actionData) || {})[0]; -} - -/** - * Returns the nearest ancestor Route error, which could be a loader/action - * error or a render error. This is intended to be called from your - * ErrorBoundary/errorElement to display a proper error message. - */ -function useRouteError() { - var _state$errors; - let error = react.useContext(RouteErrorContext); - let state = useDataRouterState(DataRouterStateHook.UseRouteError); - let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError); - - // If this was a render error, we put it in a RouteError context inside - // of RenderErrorBoundary - if (error) { - return error; - } - - // Otherwise look for errors from our data router state - return (_state$errors = state.errors) == null ? void 0 : _state$errors[routeId]; -} - -/** - * Returns the happy-path data from the nearest ancestor value - */ -function useAsyncValue() { - let value = React.useContext(AwaitContext); - return value == null ? void 0 : value._data; -} +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Header: () => (/* binding */ Header)\n/* harmony export */ });\n/* harmony import */ var _babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/slicedToArray */ \"./node_modules/@babel/runtime/helpers/esm/slicedToArray.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! react-router-dom */ \"./node_modules/react-router-dom/dist/index.js\");\n/* harmony import */ var _scss_style_scss__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../../scss/style.scss */ \"./src/scss/style.scss\");\n/* harmony import */ var _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../config/AppConfig */ \"./src/js/app/config/AppConfig.js\");\n/* harmony import */ var _context__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../context */ \"./src/js/app/context/index.js\");\n\n\n\n\n\n\nvar Header = function Header() {\n var _useState = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]),\n _useState2 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState, 2),\n mainMenus = _useState2[0],\n setMainMenus = _useState2[1];\n var _useState3 = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]),\n _useState4 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState3, 2),\n parentMenus = _useState4[0],\n setParentMenus = _useState4[1];\n var _useState5 = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]),\n _useState6 = (0,_babel_runtime_helpers_slicedToArray__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_useState5, 2),\n childMenus = _useState6[0],\n setChildMenus = _useState6[1];\n var _useContext = (0,react__WEBPACK_IMPORTED_MODULE_1__.useContext)(_context__WEBPACK_IMPORTED_MODULE_4__.DataContext),\n activeLanguages = _useContext.activeLanguages,\n menuData = _useContext.menuData,\n lang = _useContext.lang,\n setLang = _useContext.setLang;\n (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(function () {\n menuData.filter(function (item) {\n if ((item === null || item === void 0 ? void 0 : item.slug) == \"main-menu\") {\n setMainMenus(item === null || item === void 0 ? void 0 : item.items);\n }\n });\n }, [menuData, lang]);\n var SubNestedMenus = function SubNestedMenus(_ref) {\n var items = _ref.items;\n console.log(\"SubNested Menus Item\", items);\n return items.length > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"ul\", {\n className: \"dropdown-menu dropdown__content subNested-Menus\"\n }, items.map(function (childItem) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"li\", {\n key: childItem.ID,\n className: \"nav-item\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_5__.Link, {\n className: \"dropdown-item\",\n to: childItem.url\n }, childItem.title));\n }));\n };\n var NestedMenu = function NestedMenu(_ref2) {\n var items = _ref2.items;\n // console.log(\"Nested Item\", items);\n return (items === null || items === void 0 ? void 0 : items.length) > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"ul\", {\n className: \"dropdown-menu dropdown__content\"\n }, items.map(function (childItem) {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"li\", {\n key: childItem === null || childItem === void 0 ? void 0 : childItem.ID,\n className: \"nav-item\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_5__.Link, {\n className: \"dropdown-item\",\n to: childItem === null || childItem === void 0 ? void 0 : childItem.url\n }, childItem === null || childItem === void 0 ? void 0 : childItem.title));\n }));\n };\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"header\", {\n className: \"App-header\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"App-header-left\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_5__.Link, {\n className: \"\",\n to: \"/\"\n }, (_config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig === null || _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig === void 0 ? void 0 : _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig.SITE_CUSTOM_LOGO) && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"App-logo\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"img\", {\n src: _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig === null || _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig === void 0 ? void 0 : _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig.SITE_CUSTOM_LOGO[0],\n alt: \"logo\"\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"App-title\"\n }, _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig.SITE_TITLE ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"h4\", {\n className: \"title-head\"\n }, \" \", _config_AppConfig__WEBPACK_IMPORTED_MODULE_3__.AppConfig.SITE_TITLE) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"h4\", {\n className: \"title-head\"\n }, \" WebXR By - PowerSimple \")))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"App-header-right d-flex flex-row-reverse \"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"nav\", {\n className: \"navbar navbar-expand-lg navbar-dark \"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"container-fluid navbar-container \"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"button\", {\n className: \"navbar-toggler\",\n type: \"button\",\n \"data-bs-toggle\": \"collapse\",\n \"data-bs-target\": \"#navbarToggleBtn\",\n \"aria-controls\": \"navbarToggleBtn\",\n \"aria-expanded\": \"false\",\n \"aria-label\": \"Toggle navigation\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"span\", {\n className: \"navbar-toggler-icon\"\n })), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"div\", {\n className: \"collapse navbar-collapse\",\n id: \"navbarToggleBtn\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"ul\", {\n className: \"navbar-nav me-auto mb-2 mb-lg-0 \"\n }, /* The main dropdown menu items of the Navbar */\n\n (mainMenus === null || mainMenus === void 0 ? void 0 : mainMenus.length) > 0 && (mainMenus === null || mainMenus === void 0 ? void 0 : mainMenus.map(function (menuItem) {\n if (menuItem.menu_item_parent === \"0\") {\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"li\", {\n className: \"nav-item dropdown\",\n key: menuItem.ID\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_5__.Link, {\n className: \"dropdownbtn nav-link active\",\n to: menuItem.url\n }, menuItem.title), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(NestedMenu, {\n items: mainMenus.filter(function (childItem) {\n return childItem.menu_item_parent == menuItem.ID;\n })\n }));\n }\n })), /* The activeLanguages items of the Navbar */\n (activeLanguages === null || activeLanguages === void 0 ? void 0 : activeLanguages.length) > 0 && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"li\", {\n className: \"nav-item dropdown\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(react_router_dom__WEBPACK_IMPORTED_MODULE_5__.Link, {\n className: \"dropdownbtn nav-link active\",\n to: \"#\"\n }, \"Languages\"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"ul\", {\n className: \"dropdown-menu dropdown__content\"\n }, activeLanguages === null || activeLanguages === void 0 ? void 0 : activeLanguages.map(function (item, index) {\n var code = item === null || item === void 0 ? void 0 : item.code;\n if (code == \"en\") {\n code = \"\";\n }\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"li\", {\n className: \"nav-item\",\n key: index\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement(\"span\", {\n className: \"dropdown-item\",\n onClick: function onClick() {\n return setLang(\"\".concat(code));\n }\n }, item === null || item === void 0 ? void 0 : item.native_name));\n })))))))));\n};//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvanMvYXBwL2NvbXBvbmVudHMvaGVhZGVyL0hlYWRlci5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9tYWtlcnMvLi9zcmMvanMvYXBwL2NvbXBvbmVudHMvaGVhZGVyL0hlYWRlci5qcz9jOTE0Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBfc2xpY2VkVG9BcnJheSBmcm9tIFwiQGJhYmVsL3J1bnRpbWUvaGVscGVycy9zbGljZWRUb0FycmF5XCI7XG5pbXBvcnQgUmVhY3QsIHsgdXNlU3RhdGUsIHVzZUVmZmVjdCwgdXNlQ29udGV4dCwgdXNlTWVtbyB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgTGluayB9IGZyb20gXCJyZWFjdC1yb3V0ZXItZG9tXCI7XG5pbXBvcnQgXCIuLi8uLi8uLi8uLi9zY3NzL3N0eWxlLnNjc3NcIjtcbmltcG9ydCB7IEFwcENvbmZpZyB9IGZyb20gXCIuLi8uLi9jb25maWcvQXBwQ29uZmlnXCI7XG5pbXBvcnQgeyBEYXRhQ29udGV4dCB9IGZyb20gXCIuLi8uLi9jb250ZXh0XCI7XG5leHBvcnQgdmFyIEhlYWRlciA9IGZ1bmN0aW9uIEhlYWRlcigpIHtcbiAgdmFyIF91c2VTdGF0ZSA9IHVzZVN0YXRlKFtdKSxcbiAgICBfdXNlU3RhdGUyID0gX3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlLCAyKSxcbiAgICBtYWluTWVudXMgPSBfdXNlU3RhdGUyWzBdLFxuICAgIHNldE1haW5NZW51cyA9IF91c2VTdGF0ZTJbMV07XG4gIHZhciBfdXNlU3RhdGUzID0gdXNlU3RhdGUoW10pLFxuICAgIF91c2VTdGF0ZTQgPSBfc2xpY2VkVG9BcnJheShfdXNlU3RhdGUzLCAyKSxcbiAgICBwYXJlbnRNZW51cyA9IF91c2VTdGF0ZTRbMF0sXG4gICAgc2V0UGFyZW50TWVudXMgPSBfdXNlU3RhdGU0WzFdO1xuICB2YXIgX3VzZVN0YXRlNSA9IHVzZVN0YXRlKFtdKSxcbiAgICBfdXNlU3RhdGU2ID0gX3NsaWNlZFRvQXJyYXkoX3VzZVN0YXRlNSwgMiksXG4gICAgY2hpbGRNZW51cyA9IF91c2VTdGF0ZTZbMF0sXG4gICAgc2V0Q2hpbGRNZW51cyA9IF91c2VTdGF0ZTZbMV07XG4gIHZhciBfdXNlQ29udGV4dCA9IHVzZUNvbnRleHQoRGF0YUNvbnRleHQpLFxuICAgIGFjdGl2ZUxhbmd1YWdlcyA9IF91c2VDb250ZXh0LmFjdGl2ZUxhbmd1YWdlcyxcbiAgICBtZW51RGF0YSA9IF91c2VDb250ZXh0Lm1lbnVEYXRhLFxuICAgIGxhbmcgPSBfdXNlQ29udGV4dC5sYW5nLFxuICAgIHNldExhbmcgPSBfdXNlQ29udGV4dC5zZXRMYW5nO1xuICB1c2VNZW1vKGZ1bmN0aW9uICgpIHtcbiAgICBtZW51RGF0YS5maWx0ZXIoZnVuY3Rpb24gKGl0ZW0pIHtcbiAgICAgIGlmICgoaXRlbSA9PT0gbnVsbCB8fCBpdGVtID09PSB2b2lkIDAgPyB2b2lkIDAgOiBpdGVtLnNsdWcpID09IFwibWFpbi1tZW51XCIpIHtcbiAgICAgICAgc2V0TWFpbk1lbnVzKGl0ZW0gPT09IG51bGwgfHwgaXRlbSA9PT0gdm9pZCAwID8gdm9pZCAwIDogaXRlbS5pdGVtcyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0sIFttZW51RGF0YSwgbGFuZ10pO1xuICB2YXIgU3ViTmVzdGVkTWVudXMgPSBmdW5jdGlvbiBTdWJOZXN0ZWRNZW51cyhfcmVmKSB7XG4gICAgdmFyIGl0ZW1zID0gX3JlZi5pdGVtcztcbiAgICBjb25zb2xlLmxvZyhcIlN1Yk5lc3RlZCBNZW51cyBJdGVtXCIsIGl0ZW1zKTtcbiAgICByZXR1cm4gaXRlbXMubGVuZ3RoID4gMCAmJiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInVsXCIsIHtcbiAgICAgIGNsYXNzTmFtZTogXCJkcm9wZG93bi1tZW51IGRyb3Bkb3duX19jb250ZW50IHN1Yk5lc3RlZC1NZW51c1wiXG4gICAgfSwgaXRlbXMubWFwKGZ1bmN0aW9uIChjaGlsZEl0ZW0pIHtcbiAgICAgIHJldHVybiAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImxpXCIsIHtcbiAgICAgICAga2V5OiBjaGlsZEl0ZW0uSUQsXG4gICAgICAgIGNsYXNzTmFtZTogXCJuYXYtaXRlbVwiXG4gICAgICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgICAgIGNsYXNzTmFtZTogXCJkcm9wZG93bi1pdGVtXCIsXG4gICAgICAgIHRvOiBjaGlsZEl0ZW0udXJsXG4gICAgICB9LCBjaGlsZEl0ZW0udGl0bGUpKTtcbiAgICB9KSk7XG4gIH07XG4gIHZhciBOZXN0ZWRNZW51ID0gZnVuY3Rpb24gTmVzdGVkTWVudShfcmVmMikge1xuICAgIHZhciBpdGVtcyA9IF9yZWYyLml0ZW1zO1xuICAgIC8vIGNvbnNvbGUubG9nKFwiTmVzdGVkIEl0ZW1cIiwgaXRlbXMpO1xuICAgIHJldHVybiAoaXRlbXMgPT09IG51bGwgfHwgaXRlbXMgPT09IHZvaWQgMCA/IHZvaWQgMCA6IGl0ZW1zLmxlbmd0aCkgPiAwICYmIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwidWxcIiwge1xuICAgICAgY2xhc3NOYW1lOiBcImRyb3Bkb3duLW1lbnUgZHJvcGRvd25fX2NvbnRlbnRcIlxuICAgIH0sIGl0ZW1zLm1hcChmdW5jdGlvbiAoY2hpbGRJdGVtKSB7XG4gICAgICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgICAgIGtleTogY2hpbGRJdGVtID09PSBudWxsIHx8IGNoaWxkSXRlbSA9PT0gdm9pZCAwID8gdm9pZCAwIDogY2hpbGRJdGVtLklELFxuICAgICAgICBjbGFzc05hbWU6IFwibmF2LWl0ZW1cIlxuICAgICAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoTGluaywge1xuICAgICAgICBjbGFzc05hbWU6IFwiZHJvcGRvd24taXRlbVwiLFxuICAgICAgICB0bzogY2hpbGRJdGVtID09PSBudWxsIHx8IGNoaWxkSXRlbSA9PT0gdm9pZCAwID8gdm9pZCAwIDogY2hpbGRJdGVtLnVybFxuICAgICAgfSwgY2hpbGRJdGVtID09PSBudWxsIHx8IGNoaWxkSXRlbSA9PT0gdm9pZCAwID8gdm9pZCAwIDogY2hpbGRJdGVtLnRpdGxlKSk7XG4gICAgfSkpO1xuICB9O1xuICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJoZWFkZXJcIiwge1xuICAgIGNsYXNzTmFtZTogXCJBcHAtaGVhZGVyXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIiwge1xuICAgIGNsYXNzTmFtZTogXCJBcHAtaGVhZGVyLWxlZnRcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgY2xhc3NOYW1lOiBcIlwiLFxuICAgIHRvOiBcIi9cIlxuICB9LCAoQXBwQ29uZmlnID09PSBudWxsIHx8IEFwcENvbmZpZyA9PT0gdm9pZCAwID8gdm9pZCAwIDogQXBwQ29uZmlnLlNJVEVfQ1VTVE9NX0xPR08pICYmIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiZGl2XCIsIHtcbiAgICBjbGFzc05hbWU6IFwiQXBwLWxvZ29cIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImltZ1wiLCB7XG4gICAgc3JjOiBBcHBDb25maWcgPT09IG51bGwgfHwgQXBwQ29uZmlnID09PSB2b2lkIDAgPyB2b2lkIDAgOiBBcHBDb25maWcuU0lURV9DVVNUT01fTE9HT1swXSxcbiAgICBhbHQ6IFwibG9nb1wiXG4gIH0pKSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIiwge1xuICAgIGNsYXNzTmFtZTogXCJBcHAtdGl0bGVcIlxuICB9LCBBcHBDb25maWcuU0lURV9USVRMRSA/IC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiaDRcIiwge1xuICAgIGNsYXNzTmFtZTogXCJ0aXRsZS1oZWFkXCJcbiAgfSwgXCIgXCIsIEFwcENvbmZpZy5TSVRFX1RJVExFKSA6IC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiaDRcIiwge1xuICAgIGNsYXNzTmFtZTogXCJ0aXRsZS1oZWFkXCJcbiAgfSwgXCIgV2ViWFIgQnkgLSBQb3dlclNpbXBsZSBcIikpKSksIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwiZGl2XCIsIHtcbiAgICBjbGFzc05hbWU6IFwiQXBwLWhlYWRlci1yaWdodCAgZC1mbGV4IGZsZXgtcm93LXJldmVyc2UgXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJuYXZcIiwge1xuICAgIGNsYXNzTmFtZTogXCJuYXZiYXIgbmF2YmFyLWV4cGFuZC1sZyBuYXZiYXItZGFyayAgXCJcbiAgfSwgLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIiwge1xuICAgIGNsYXNzTmFtZTogXCJjb250YWluZXItZmx1aWQgbmF2YmFyLWNvbnRhaW5lciBcIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImJ1dHRvblwiLCB7XG4gICAgY2xhc3NOYW1lOiBcIm5hdmJhci10b2dnbGVyXCIsXG4gICAgdHlwZTogXCJidXR0b25cIixcbiAgICBcImRhdGEtYnMtdG9nZ2xlXCI6IFwiY29sbGFwc2VcIixcbiAgICBcImRhdGEtYnMtdGFyZ2V0XCI6IFwiI25hdmJhclRvZ2dsZUJ0blwiLFxuICAgIFwiYXJpYS1jb250cm9sc1wiOiBcIm5hdmJhclRvZ2dsZUJ0blwiLFxuICAgIFwiYXJpYS1leHBhbmRlZFwiOiBcImZhbHNlXCIsXG4gICAgXCJhcmlhLWxhYmVsXCI6IFwiVG9nZ2xlIG5hdmlnYXRpb25cIlxuICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInNwYW5cIiwge1xuICAgIGNsYXNzTmFtZTogXCJuYXZiYXItdG9nZ2xlci1pY29uXCJcbiAgfSkpLCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcImRpdlwiLCB7XG4gICAgY2xhc3NOYW1lOiBcImNvbGxhcHNlIG5hdmJhci1jb2xsYXBzZVwiLFxuICAgIGlkOiBcIm5hdmJhclRvZ2dsZUJ0blwiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwidWxcIiwge1xuICAgIGNsYXNzTmFtZTogXCJuYXZiYXItbmF2IG1lLWF1dG8gbWItMiBtYi1sZy0wIFwiXG4gIH0sIC8qIFRoZSBtYWluIGRyb3Bkb3duIG1lbnUgaXRlbXMgb2YgdGhlIE5hdmJhciAqL1xuXG4gIChtYWluTWVudXMgPT09IG51bGwgfHwgbWFpbk1lbnVzID09PSB2b2lkIDAgPyB2b2lkIDAgOiBtYWluTWVudXMubGVuZ3RoKSA+IDAgJiYgKG1haW5NZW51cyA9PT0gbnVsbCB8fCBtYWluTWVudXMgPT09IHZvaWQgMCA/IHZvaWQgMCA6IG1haW5NZW51cy5tYXAoZnVuY3Rpb24gKG1lbnVJdGVtKSB7XG4gICAgaWYgKG1lbnVJdGVtLm1lbnVfaXRlbV9wYXJlbnQgPT09IFwiMFwiKSB7XG4gICAgICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgICAgIGNsYXNzTmFtZTogXCJuYXYtaXRlbSBkcm9wZG93blwiLFxuICAgICAgICBrZXk6IG1lbnVJdGVtLklEXG4gICAgICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChMaW5rLCB7XG4gICAgICAgIGNsYXNzTmFtZTogXCJkcm9wZG93bmJ0biBuYXYtbGluayBhY3RpdmVcIixcbiAgICAgICAgdG86IG1lbnVJdGVtLnVybFxuICAgICAgfSwgbWVudUl0ZW0udGl0bGUpLCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChOZXN0ZWRNZW51LCB7XG4gICAgICAgIGl0ZW1zOiBtYWluTWVudXMuZmlsdGVyKGZ1bmN0aW9uIChjaGlsZEl0ZW0pIHtcbiAgICAgICAgICByZXR1cm4gY2hpbGRJdGVtLm1lbnVfaXRlbV9wYXJlbnQgPT0gbWVudUl0ZW0uSUQ7XG4gICAgICAgIH0pXG4gICAgICB9KSk7XG4gICAgfVxuICB9KSksIC8qIFRoZSBhY3RpdmVMYW5ndWFnZXMgaXRlbXMgb2YgdGhlIE5hdmJhciAqL1xuICAoYWN0aXZlTGFuZ3VhZ2VzID09PSBudWxsIHx8IGFjdGl2ZUxhbmd1YWdlcyA9PT0gdm9pZCAwID8gdm9pZCAwIDogYWN0aXZlTGFuZ3VhZ2VzLmxlbmd0aCkgPiAwICYmIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KFwibGlcIiwge1xuICAgIGNsYXNzTmFtZTogXCJuYXYtaXRlbSBkcm9wZG93blwiXG4gIH0sIC8qI19fUFVSRV9fKi9SZWFjdC5jcmVhdGVFbGVtZW50KExpbmssIHtcbiAgICBjbGFzc05hbWU6IFwiZHJvcGRvd25idG4gbmF2LWxpbmsgYWN0aXZlXCIsXG4gICAgdG86IFwiI1wiXG4gIH0sIFwiTGFuZ3VhZ2VzXCIpLCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInVsXCIsIHtcbiAgICBjbGFzc05hbWU6IFwiZHJvcGRvd24tbWVudSBkcm9wZG93bl9fY29udGVudFwiXG4gIH0sIGFjdGl2ZUxhbmd1YWdlcyA9PT0gbnVsbCB8fCBhY3RpdmVMYW5ndWFnZXMgPT09IHZvaWQgMCA/IHZvaWQgMCA6IGFjdGl2ZUxhbmd1YWdlcy5tYXAoZnVuY3Rpb24gKGl0ZW0sIGluZGV4KSB7XG4gICAgdmFyIGNvZGUgPSBpdGVtID09PSBudWxsIHx8IGl0ZW0gPT09IHZvaWQgMCA/IHZvaWQgMCA6IGl0ZW0uY29kZTtcbiAgICBpZiAoY29kZSA9PSBcImVuXCIpIHtcbiAgICAgIGNvZGUgPSBcIlwiO1xuICAgIH1cbiAgICByZXR1cm4gLyojX19QVVJFX18qL1JlYWN0LmNyZWF0ZUVsZW1lbnQoXCJsaVwiLCB7XG4gICAgICBjbGFzc05hbWU6IFwibmF2LWl0ZW1cIixcbiAgICAgIGtleTogaW5kZXhcbiAgICB9LCAvKiNfX1BVUkVfXyovUmVhY3QuY3JlYXRlRWxlbWVudChcInNwYW5cIiwge1xuICAgICAgY2xhc3NOYW1lOiBcImRyb3Bkb3duLWl0ZW1cIixcbiAgICAgIG9uQ2xpY2s6IGZ1bmN0aW9uIG9uQ2xpY2soKSB7XG4gICAgICAgIHJldHVybiBzZXRMYW5nKFwiXCIuY29uY2F0KGNvZGUpKTtcbiAgICAgIH1cbiAgICB9LCBpdGVtID09PSBudWxsIHx8IGl0ZW0gPT09IHZvaWQgMCA/IHZvaWQgMCA6IGl0ZW0ubmF0aXZlX25hbWUpKTtcbiAgfSkpKSkpKSkpKTtcbn07Il0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/js/app/components/header/Header.js\n"); -/** - * Returns the error from the nearest ancestor value - */ -function useAsyncError() { - let value = React.useContext(AwaitContext); - return value == null ? void 0 : value._error; -} -let blockerId = 0; - -/** - * Allow the application to block navigations within the SPA and present the - * user a confirmation dialog to confirm the navigation. Mostly used to avoid - * using half-filled form data. This does not handle hard-reloads or - * cross-origin navigations. - */ -function useBlocker(shouldBlock) { - let { - router, - basename - } = useDataRouterContext(DataRouterHook.UseBlocker); - let state = useDataRouterState(DataRouterStateHook.UseBlocker); - let [blockerKey, setBlockerKey] = React.useState(""); - let [blocker, setBlocker] = React.useState(IDLE_BLOCKER); - let blockerFunction = React.useCallback(arg => { - if (typeof shouldBlock !== "function") { - return !!shouldBlock; - } - if (basename === "/") { - return shouldBlock(arg); - } - - // If they provided us a function and we've got an active basename, strip - // it from the locations we expose to the user to match the behavior of - // useLocation - let { - currentLocation, - nextLocation, - historyAction - } = arg; - return shouldBlock({ - currentLocation: dist_extends({}, currentLocation, { - pathname: stripBasename(currentLocation.pathname, basename) || currentLocation.pathname - }), - nextLocation: dist_extends({}, nextLocation, { - pathname: stripBasename(nextLocation.pathname, basename) || nextLocation.pathname - }), - historyAction - }); - }, [basename, shouldBlock]); - React.useEffect(() => { - let key = String(++blockerId); - setBlocker(router.getBlocker(key, blockerFunction)); - setBlockerKey(key); - return () => router.deleteBlocker(key); - }, [router, setBlocker, setBlockerKey, blockerFunction]); - - // Prefer the blocker from state since DataRouterContext is memoized so this - // ensures we update on blocker state updates - return blockerKey && state.blockers.has(blockerKey) ? state.blockers.get(blockerKey) : blocker; -} - -/** - * Stable version of useNavigate that is used when we are in the context of - * a RouterProvider. - */ -function useNavigateStable() { - let { - router - } = useDataRouterContext(DataRouterHook.UseNavigateStable); - let id = useCurrentRouteId(DataRouterStateHook.UseNavigateStable); - let activeRef = react.useRef(false); - useIsomorphicLayoutEffect(() => { - activeRef.current = true; - }); - let navigate = react.useCallback(function (to, options) { - if (options === void 0) { - options = {}; - } - false ? 0 : void 0; - - // Short circuit here since if this happens on first render the navigate - // is useless because we haven't wired up our router subscriber yet - if (!activeRef.current) return; - if (typeof to === "number") { - router.navigate(to); - } else { - router.navigate(to, dist_extends({ - fromRouteId: id - }, options)); - } - }, [router, id]); - return navigate; -} -const alreadyWarned = {}; -function warningOnce(key, cond, message) { - if (!cond && !alreadyWarned[key]) { - alreadyWarned[key] = true; - false ? 0 : void 0; - } -} - -/** - Webpack + React 17 fails to compile on any of the following because webpack - complains that `startTransition` doesn't exist in `React`: - * import { startTransition } from "react" - * import * as React from from "react"; - "startTransition" in React ? React.startTransition(() => setState()) : setState() - * import * as React from from "react"; - "startTransition" in React ? React["startTransition"](() => setState()) : setState() - - Moving it to a constant such as the following solves the Webpack/React 17 issue: - * import * as React from from "react"; - const START_TRANSITION = "startTransition"; - START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState() - - However, that introduces webpack/terser minification issues in production builds - in React 18 where minification/obfuscation ends up removing the call of - React.startTransition entirely from the first half of the ternary. Grabbing - this exported reference once up front resolves that issue. - - See https://github.com/remix-run/react-router/issues/10579 -*/ -const START_TRANSITION = "startTransition"; -const startTransitionImpl = react_namespaceObject[START_TRANSITION]; - -/** - * Given a Remix Router instance, render the appropriate UI - */ -function RouterProvider(_ref) { - let { - fallbackElement, - router, - future - } = _ref; - // Need to use a layout effect here so we are subscribed early enough to - // pick up on any render-driven redirects/navigations (useEffect/) - let [state, setStateImpl] = react.useState(router.state); - let { - v7_startTransition - } = future || {}; - let setState = react.useCallback(newState => { - v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState); - }, [setStateImpl, v7_startTransition]); - react.useLayoutEffect(() => router.subscribe(setState), [router, setState]); - let navigator = react.useMemo(() => { - return { - createHref: router.createHref, - encodeLocation: router.encodeLocation, - go: n => router.navigate(n), - push: (to, state, opts) => router.navigate(to, { - state, - preventScrollReset: opts == null ? void 0 : opts.preventScrollReset - }), - replace: (to, state, opts) => router.navigate(to, { - replace: true, - state, - preventScrollReset: opts == null ? void 0 : opts.preventScrollReset - }) - }; - }, [router]); - let basename = router.basename || "/"; - let dataRouterContext = react.useMemo(() => ({ - router, - navigator, - static: false, - basename - }), [router, navigator, basename]); - - // The fragment and {null} here are important! We need them to keep React 18's - // useId happy when we are server-rendering since we may have a