UNPKG

figma-page-clone

Version:
399 lines (394 loc) 44.4 kB
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/code.ts"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./src/code.ts": /*!*********************!*\ !*** ./src/code.ts ***! \*********************/ /*! no static exports found */ /***/ (function(module, exports) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const showUIOptions = { visible: true, width: 350, height: 510, }; const childInstanceNodeRegex = /I[0-9]+:[0-9]+;/; const masterComponentsIds = []; const clonedMasterComponentsIds = []; const READ_ONLY_OR_DEPRECATED_KEYS = [ "mainComponent", "masterComponent", "verticalPadding", "horizontalPadding", "hasMissingFont", "type", "id", "parent", "removed", "children", "absoluteTransform", "constrainProportions", "reactions", "overlayPositionType", "overlayBackground", "overlayBackgroundInteraction", "overflowDirection", ]; const FUNCTION_BASED_KEYS = ["width", "height"]; const initiateUI = () => { const pages = []; const frames = []; figma.root.children.forEach((child) => { pages.push({ id: child.id, name: child.name }); }); figma.currentPage.children.forEach((child) => { frames.push({ id: child.id, name: child.name, selected: figma.currentPage.selection.indexOf(child) !== -1, }); }); figma.ui.postMessage({ pages: pages, id: figma.currentPage.id, name: figma.currentPage.name, frames: frames }); }; let deepCloneMasterComponents = true; let deepCloneMasterComponentsIds = []; figma.showUI(__html__, showUIOptions); initiateUI(); figma.ui.onmessage = (msg) => { if (msg.type === "focus") { initiateUI(); } if (msg.type === "cloned") { /* NOTE: PageClone actually clones EVERYTHING except DOCUMENT. However, I have not found a way to improve type casting */ let clone; // go to destination page or create new page if (msg.destination) { figma.currentPage = figma.getNodeById(msg.destination); clone = figma.currentPage; } else { clone = figma.createPage(); figma.currentPage = figma.getNodeById(clone.id); clone.name = msg.name; } // clone logic based on overwrite or not let pageChildrenNodes = []; if (msg.overwrite) { // get all the FRAME names of current page const existingFrames = []; figma.currentPage.children.forEach(node => { existingFrames.push({ id: node.id, name: node.name }); }); msg.frames.forEach(frame => { const frameToClone = figma.getNodeById(frame); // check if a frame with same name exists in currentPage const existingFrameIndex = existingFrames.findIndex(frame => frame.name === frameToClone.name); // if there is an existing node of the same name if (existingFrameIndex > -1) { // get the node const existingFrameNode = figma.getNodeById(existingFrames[existingFrameIndex].id); // clone the frame as per the msg.frames const newFrameNode = frameToClone.clone(); // reposition cloned frame to old frame newFrameNode.x = existingFrameNode.x; newFrameNode.y = existingFrameNode.y; // delete existing or old frame existingFrameNode.remove(); // for safety, splice the replace entry from existingFrameIndex in case there are 2 frames with same name; existingFrames.splice(existingFrameIndex, 1); // add newFrameNode to cloneFrameNodes for detach instance logic pageChildrenNodes.push(newFrameNode); } else { // if there is none of the same name const newFrameNode = frameToClone.clone(); // add newFrameNode to cloneFrameNodes for detach instance logic pageChildrenNodes.push(newFrameNode); } }); } else { msg.frames.forEach(frame => { const newFrameNode = figma.getNodeById(frame).clone(); // add newFrameNode to cloneFrameNodes for detach instance logic pageChildrenNodes.push(newFrameNode); }); } if (msg["detach-instances"]) { let currentLayerNodes = [figma.currentPage]; while (currentLayerNodes.length > 0) { let nextLayerNodes = []; currentLayerNodes.forEach(node => { if (node.children) { const instances = node.findChildren(child => child.type === "INSTANCE" || child.type === "COMPONENT"); instances.length > 0 ? instances.forEach(instance => detachInstance(instance)) : null; nextLayerNodes = nextLayerNodes.concat(node.findChildren(child => child.type === "GROUP" || child.type === "FRAME")); } }); currentLayerNodes = [...nextLayerNodes]; } } if (msg.sanitize) { const hiddenNodes = clone.findAll(node => node.visible === false && node.type !== "COMPONENT"); hiddenNodes.forEach(node => { !childInstanceNodeRegex.test(node.id) ? (figma.getNodeById(node.id) ? node.remove() : null) : null; }); } if (msg["clone-master"]) { // get all INSTANCE type nodes const instanceNodes = clone.findAll(node => node.type === "INSTANCE" && !childInstanceNodeRegex.test(node.id)); instanceNodes.forEach(node => { masterComponentsIds.indexOf(node.mainComponent.id) === -1 ? masterComponentsIds.push(node.mainComponent.id) : null; }); // get and store the unique master COMPONENT nodes, if they are not in cloned page masterComponentsIds.forEach(id => { const parentId = figma.getNodeById(id).parent ? figma.getNodeById(id).parent.id : ""; if (parentId !== figma.currentPage.id) { const masterClone = figma.getNodeById(id).clone(); masterClone.visible = false; clonedMasterComponentsIds.push(masterClone.id); } }); // remap individual INSTANCE nodes to their new master COMPONENT nodes instanceNodes.forEach(node => { // TODO findAll nodes within this instance that is not another instance // TODO remember the properties editable based on each type node.mainComponent = figma.getNodeById(clonedMasterComponentsIds[masterComponentsIds.indexOf(node.mainComponent.id)]); // TODO re-apply the properties here (if works, need to optimize for text char replacement) }); // deep clone INSTANCE nodes of new master COMPONENT nodes deepCloneMasterComponentsIds = [...clonedMasterComponentsIds]; while (deepCloneMasterComponents) { deepCloneMasterComponents = false; // reset deepCloneMasterComponents const nextDeepCloneMasterComponentsIds = []; deepCloneMasterComponentsIds.forEach(id => { // if it has children if (figma.getNodeById(id).children) { figma.getNodeById(id).children.forEach(child => { // if any of them are INSTANCE components if (child.type === "INSTANCE") { // get the id of the location of the child's master COMPONENT node const childMasterComponentParent = child.mainComponent.parent ? child.mainComponent.parent.id : ""; // if child's master COMPONENT not in currentPage if (childMasterComponentParent !== figma.currentPage.id) { // if child's master COMPONENT is not cloned yet if (masterComponentsIds.indexOf(child.mainComponent.id) === -1) { masterComponentsIds.push(child.mainComponent.id); const masterClone = child.mainComponent.clone(); masterClone.visible = false; clonedMasterComponentsIds.push(masterClone.id); nextDeepCloneMasterComponentsIds.push(masterClone.id); deepCloneMasterComponents = true; child.mainComponent = masterClone; } else { child.mainComponent = figma.getNodeById(clonedMasterComponentsIds[masterComponentsIds.indexOf(child.mainComponent.id)]); } } } }); } }); deepCloneMasterComponentsIds = [...nextDeepCloneMasterComponentsIds]; } // ungroup and existing Master Components const masterComponentGroup = figma.currentPage.findOne(node => node.name === "Master Components" && node.type === "GROUP"); if (masterComponentGroup) { masterComponentGroup.children.forEach(child => figma.currentPage.appendChild(child)); } // then group them into a components group const componentNodes = figma.currentPage.findAll(node => node.type === "COMPONENT"); if (componentNodes.length > 0) { const componentGroup = figma.group(componentNodes, figma.currentPage); componentGroup.name = "Master Components"; } } if (msg.locked) { clone.children.forEach((child) => (child.locked = true)); } figma.notify(`👍 Successfully cloned selected frames into ${clone.name}`); figma.closePlugin(); } }; const detachInstance = (instance) => { const parent = instance.parent; const newFrame = figma.createFrame(); for (const i in newFrame) { if (!READ_ONLY_OR_DEPRECATED_KEYS.includes(i)) { if (FUNCTION_BASED_KEYS.includes(i)) { if (i === "width" && instance[i] >= 0.01) { const resizeHeight = Math.max(newFrame.height, 0.01); newFrame.resize(instance[i], resizeHeight); } if (i === "height" && instance[i] >= 0.01) { const resizeWidth = Math.max(newFrame.width, 0.01); newFrame.resize(resizeWidth, instance[i]); } } else { // if cornerRadius is figma.mixed, rely on the other individual keys if (!(i === "cornerRadius" && instance[i] === figma.mixed)) { newFrame[i] = clone(instance[i]); } } } } instance.children.forEach(child => { const childClone = child.clone(); if (child.type !== "TEXT") { for (const j in childClone) { if (!READ_ONLY_OR_DEPRECATED_KEYS.includes(j)) { if (FUNCTION_BASED_KEYS.includes(j)) { if (j === "width" && child[j] >= 0.01) { const resizeHeight = childClone.type === "LINE" ? 0 : Math.max(childClone.height, 0.01); childClone.resize(child[j], resizeHeight); } if (j === "height" && child[j] >= 0.01) { const resizeWidth = Math.max(childClone.width, 0.01); childClone.resize(resizeWidth, child[j]); } } else { // if cornerRadius is figma.mixed, rely on the other individual keys if (!(j === "cornerRadius" && child[j] === figma.mixed)) { childClone[j] = clone(child[j]); } } } } } newFrame.appendChild(childClone); }); parent.insertChild(parent.children.indexOf(instance), newFrame); instance.remove(); }; function loadNodeFont(fontName) { return __awaiter(this, void 0, void 0, function* () { yield figma.loadFontAsync(fontName).catch(error => console.error(error)); }); } function clone(val) { const type = typeof val; if (val === null) { return null; } else if (type === "undefined" || type === "number" || type === "string" || type === "boolean") { return val; } else if (type === "object") { if (val instanceof Array) { return val.map(x => clone(x)); } else if (val instanceof Uint8Array) { return new Uint8Array(val); } else { let o = {}; for (const key in val) { o[key] = clone(val[key]); } return o; } } throw "unknown"; } /***/ }) /******/ }); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL2NvZGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtRQUFBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBOzs7UUFHQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMENBQTBDLGdDQUFnQztRQUMxRTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLHdEQUF3RCxrQkFBa0I7UUFDMUU7UUFDQSxpREFBaUQsY0FBYztRQUMvRDs7UUFFQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0EseUNBQXlDLGlDQUFpQztRQUMxRSxnSEFBZ0gsbUJBQW1CLEVBQUU7UUFDckk7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSwyQkFBMkIsMEJBQTBCLEVBQUU7UUFDdkQsaUNBQWlDLGVBQWU7UUFDaEQ7UUFDQTtRQUNBOztRQUVBO1FBQ0Esc0RBQXNELCtEQUErRDs7UUFFckg7UUFDQTs7O1FBR0E7UUFDQTs7Ozs7Ozs7Ozs7O0FDbEZBO0FBQ0EsMkJBQTJCLCtEQUErRCxnQkFBZ0IsRUFBRSxFQUFFO0FBQzlHO0FBQ0EsbUNBQW1DLE1BQU0sNkJBQTZCLEVBQUUsWUFBWSxXQUFXLEVBQUU7QUFDakcsa0NBQWtDLE1BQU0saUNBQWlDLEVBQUUsWUFBWSxXQUFXLEVBQUU7QUFDcEcsK0JBQStCLHFGQUFxRjtBQUNwSDtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0M7QUFDL0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixpQ0FBaUM7QUFDckQsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1QsS0FBSztBQUNMLDBCQUEwQix1RkFBdUY7QUFDakg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQ0FBcUMsK0JBQStCO0FBQ3BFLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCO0FBQ2pCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0Esa0RBQWtEO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUJBQXlCO0FBQ3pCO0FBQ0EsaUJBQWlCO0FBQ2pCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvRUFBb0UsV0FBVztBQUMvRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiY29kZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGdldHRlciB9KTtcbiBcdFx0fVxuIFx0fTtcblxuIFx0Ly8gZGVmaW5lIF9fZXNNb2R1bGUgb24gZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yID0gZnVuY3Rpb24oZXhwb3J0cykge1xuIFx0XHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcbiBcdFx0fVxuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xuIFx0fTtcblxuIFx0Ly8gY3JlYXRlIGEgZmFrZSBuYW1lc3BhY2Ugb2JqZWN0XG4gXHQvLyBtb2RlICYgMTogdmFsdWUgaXMgYSBtb2R1bGUgaWQsIHJlcXVpcmUgaXRcbiBcdC8vIG1vZGUgJiAyOiBtZXJnZSBhbGwgcHJvcGVydGllcyBvZiB2YWx1ZSBpbnRvIHRoZSBuc1xuIFx0Ly8gbW9kZSAmIDQ6IHJldHVybiB2YWx1ZSB3aGVuIGFscmVhZHkgbnMgb2JqZWN0XG4gXHQvLyBtb2RlICYgOHwxOiBiZWhhdmUgbGlrZSByZXF1aXJlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnQgPSBmdW5jdGlvbih2YWx1ZSwgbW9kZSkge1xuIFx0XHRpZihtb2RlICYgMSkgdmFsdWUgPSBfX3dlYnBhY2tfcmVxdWlyZV9fKHZhbHVlKTtcbiBcdFx0aWYobW9kZSAmIDgpIHJldHVybiB2YWx1ZTtcbiBcdFx0aWYoKG1vZGUgJiA0KSAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlICYmIHZhbHVlLl9fZXNNb2R1bGUpIHJldHVybiB2YWx1ZTtcbiBcdFx0dmFyIG5zID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5yKG5zKTtcbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KG5zLCAnZGVmYXVsdCcsIHsgZW51bWVyYWJsZTogdHJ1ZSwgdmFsdWU6IHZhbHVlIH0pO1xuIFx0XHRpZihtb2RlICYgMiAmJiB0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIGZvcih2YXIga2V5IGluIHZhbHVlKSBfX3dlYnBhY2tfcmVxdWlyZV9fLmQobnMsIGtleSwgZnVuY3Rpb24oa2V5KSB7IHJldHVybiB2YWx1ZVtrZXldOyB9LmJpbmQobnVsbCwga2V5KSk7XG4gXHRcdHJldHVybiBucztcbiBcdH07XG5cbiBcdC8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSBmdW5jdGlvbihtb2R1bGUpIHtcbiBcdFx0dmFyIGdldHRlciA9IG1vZHVsZSAmJiBtb2R1bGUuX19lc01vZHVsZSA/XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0RGVmYXVsdCgpIHsgcmV0dXJuIG1vZHVsZVsnZGVmYXVsdCddOyB9IDpcbiBcdFx0XHRmdW5jdGlvbiBnZXRNb2R1bGVFeHBvcnRzKCkgeyByZXR1cm4gbW9kdWxlOyB9O1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCAnYScsIGdldHRlcik7XG4gXHRcdHJldHVybiBnZXR0ZXI7XG4gXHR9O1xuXG4gXHQvLyBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGxcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubyA9IGZ1bmN0aW9uKG9iamVjdCwgcHJvcGVydHkpIHsgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIHByb3BlcnR5KTsgfTtcblxuIFx0Ly8gX193ZWJwYWNrX3B1YmxpY19wYXRoX19cbiBcdF9fd2VicGFja19yZXF1aXJlX18ucCA9IFwiXCI7XG5cblxuIFx0Ly8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4gXHRyZXR1cm4gX193ZWJwYWNrX3JlcXVpcmVfXyhfX3dlYnBhY2tfcmVxdWlyZV9fLnMgPSBcIi4vc3JjL2NvZGUudHNcIik7XG4iLCJ2YXIgX19hd2FpdGVyID0gKHRoaXMgJiYgdGhpcy5fX2F3YWl0ZXIpIHx8IGZ1bmN0aW9uICh0aGlzQXJnLCBfYXJndW1lbnRzLCBQLCBnZW5lcmF0b3IpIHtcbiAgICBmdW5jdGlvbiBhZG9wdCh2YWx1ZSkgeyByZXR1cm4gdmFsdWUgaW5zdGFuY2VvZiBQID8gdmFsdWUgOiBuZXcgUChmdW5jdGlvbiAocmVzb2x2ZSkgeyByZXNvbHZlKHZhbHVlKTsgfSk7IH1cbiAgICByZXR1cm4gbmV3IChQIHx8IChQID0gUHJvbWlzZSkpKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcbiAgICAgICAgZnVuY3Rpb24gZnVsZmlsbGVkKHZhbHVlKSB7IHRyeSB7IHN0ZXAoZ2VuZXJhdG9yLm5leHQodmFsdWUpKTsgfSBjYXRjaCAoZSkgeyByZWplY3QoZSk7IH0gfVxuICAgICAgICBmdW5jdGlvbiByZWplY3RlZCh2YWx1ZSkgeyB0cnkgeyBzdGVwKGdlbmVyYXRvcltcInRocm93XCJdKHZhbHVlKSk7IH0gY2F0Y2ggKGUpIHsgcmVqZWN0KGUpOyB9IH1cbiAgICAgICAgZnVuY3Rpb24gc3RlcChyZXN1bHQpIHsgcmVzdWx0LmRvbmUgPyByZXNvbHZlKHJlc3VsdC52YWx1ZSkgOiBhZG9wdChyZXN1bHQudmFsdWUpLnRoZW4oZnVsZmlsbGVkLCByZWplY3RlZCk7IH1cbiAgICAgICAgc3RlcCgoZ2VuZXJhdG9yID0gZ2VuZXJhdG9yLmFwcGx5KHRoaXNBcmcsIF9hcmd1bWVudHMgfHwgW10pKS5uZXh0KCkpO1xuICAgIH0pO1xufTtcbmNvbnN0IHNob3dVSU9wdGlvbnMgPSB7XG4gICAgdmlzaWJsZTogdHJ1ZSxcbiAgICB3aWR0aDogMzUwLFxuICAgIGhlaWdodDogNTEwLFxufTtcbmNvbnN0IGNoaWxkSW5zdGFuY2VOb2RlUmVnZXggPSAvSVswLTldKzpbMC05XSs7LztcbmNvbnN0IG1hc3RlckNvbXBvbmVudHNJZHMgPSBbXTtcbmNvbnN0IGNsb25lZE1hc3RlckNvbXBvbmVudHNJZHMgPSBbXTtcbmNvbnN0IFJFQURfT05MWV9PUl9ERVBSRUNBVEVEX0tFWVMgPSBbXG4gICAgXCJtYWluQ29tcG9uZW50XCIsXG4gICAgXCJtYXN0ZXJDb21wb25lbnRcIixcbiAgICBcInZlcnRpY2FsUGFkZGluZ1wiLFxuICAgIFwiaG9yaXpvbnRhbFBhZGRpbmdcIixcbiAgICBcImhhc01pc3NpbmdGb250XCIsXG4gICAgXCJ0eXBlXCIsXG4gICAgXCJpZFwiLFxuICAgIFwicGFyZW50XCIsXG4gICAgXCJyZW1vdmVkXCIsXG4gICAgXCJjaGlsZHJlblwiLFxuICAgIFwiYWJzb2x1dGVUcmFuc2Zvcm1cIixcbiAgICBcImNvbnN0cmFpblByb3BvcnRpb25zXCIsXG4gICAgXCJyZWFjdGlvbnNcIixcbiAgICBcIm92ZXJsYXlQb3NpdGlvblR5cGVcIixcbiAgICBcIm92ZXJsYXlCYWNrZ3JvdW5kXCIsXG4gICAgXCJvdmVybGF5QmFja2dyb3VuZEludGVyYWN0aW9uXCIsXG4gICAgXCJvdmVyZmxvd0RpcmVjdGlvblwiLFxuXTtcbmNvbnN0IEZVTkNUSU9OX0JBU0VEX0tFWVMgPSBbXCJ3aWR0aFwiLCBcImhlaWdodFwiXTtcbmNvbnN0IGluaXRpYXRlVUkgPSAoKSA9PiB7XG4gICAgY29uc3QgcGFnZXMgPSBbXTtcbiAgICBjb25zdCBmcmFtZXMgPSBbXTtcbiAgICBmaWdtYS5yb290LmNoaWxkcmVuLmZvckVhY2goKGNoaWxkKSA9PiB7XG4gICAgICAgIHBhZ2VzLnB1c2goeyBpZDogY2hpbGQuaWQsIG5hbWU6IGNoaWxkLm5hbWUgfSk7XG4gICAgfSk7XG4gICAgZmlnbWEuY3VycmVudFBhZ2UuY2hpbGRyZW4uZm9yRWFjaCgoY2hpbGQpID0+IHtcbiAgICAgICAgZnJhbWVzLnB1c2goe1xuICAgICAgICAgICAgaWQ6IGNoaWxkLmlkLFxuICAgICAgICAgICAgbmFtZTogY2hpbGQubmFtZSxcbiAgICAgICAgICAgIHNlbGVjdGVkOiBmaWdtYS5jdXJyZW50UGFnZS5zZWxlY3Rpb24uaW5kZXhPZihjaGlsZCkgIT09IC0xLFxuICAgICAgICB9KTtcbiAgICB9KTtcbiAgICBmaWdtYS51aS5wb3N0TWVzc2FnZSh7IHBhZ2VzOiBwYWdlcywgaWQ6IGZpZ21hLmN1cnJlbnRQYWdlLmlkLCBuYW1lOiBmaWdtYS5jdXJyZW50UGFnZS5uYW1lLCBmcmFtZXM6IGZyYW1lcyB9KTtcbn07XG5sZXQgZGVlcENsb25lTWFzdGVyQ29tcG9uZW50cyA9IHRydWU7XG5sZXQgZGVlcENsb25lTWFzdGVyQ29tcG9uZW50c0lkcyA9IFtdO1xuZmlnbWEuc2hvd1VJKF9faHRtbF9fLCBzaG93VUlPcHRpb25zKTtcbmluaXRpYXRlVUkoKTtcbmZpZ21hLnVpLm9ubWVzc2FnZSA9IChtc2cpID0+IHtcbiAgICBpZiAobXNnLnR5cGUgPT09IFwiZm9jdXNcIikge1xuICAgICAgICBpbml0aWF0ZVVJKCk7XG4gICAgfVxuICAgIGlmIChtc2cudHlwZSA9PT0gXCJjbG9uZWRcIikge1xuICAgICAgICAvKiBOT1RFOiBQYWdlQ2xvbmUgYWN0dWFsbHkgY2xvbmVzIEVWRVJZVEhJTkcgZXhjZXB0IERPQ1VNRU5ULiBIb3dldmVyLCBJIGhhdmUgbm90IGZvdW5kIGEgd2F5IHRvIGltcHJvdmUgdHlwZSBjYXN0aW5nICAqL1xuICAgICAgICBsZXQgY2xvbmU7XG4gICAgICAgIC8vIGdvIHRvIGRlc3RpbmF0aW9uIHBhZ2Ugb3IgY3JlYXRlIG5ldyBwYWdlXG4gICAgICAgIGlmIChtc2cuZGVzdGluYXRpb24pIHtcbiAgICAgICAgICAgIGZpZ21hLmN1cnJlbnRQYWdlID0gZmlnbWEuZ2V0Tm9kZUJ5SWQobXNnLmRlc3RpbmF0aW9uKTtcbiAgICAgICAgICAgIGNsb25lID0gZmlnbWEuY3VycmVudFBhZ2U7XG4gICAgICAgIH1cbiAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICBjbG9uZSA9IGZpZ21hLmNyZWF0ZVBhZ2UoKTtcbiAgICAgICAgICAgIGZpZ21hLmN1cnJlbnRQYWdlID0gZmlnbWEuZ2V0Tm9kZUJ5SWQoY2xvbmUuaWQpO1xuICAgICAgICAgICAgY2xvbmUubmFtZSA9IG1zZy5uYW1lO1xuICAgICAgICB9XG4gICAgICAgIC8vIGNsb25lIGxvZ2ljIGJhc2VkIG9uIG92ZXJ3cml0ZSBvciBub3RcbiAgICAgICAgbGV0IHBhZ2VDaGlsZHJlbk5vZGVzID0gW107XG4gICAgICAgIGlmIChtc2cub3ZlcndyaXRlKSB7XG4gICAgICAgICAgICAvLyBnZXQgYWxsIHRoZSBGUkFNRSBuYW1lcyBvZiBjdXJyZW50IHBhZ2VcbiAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nRnJhbWVzID0gW107XG4gICAgICAgICAgICBmaWdtYS5jdXJyZW50UGFnZS5jaGlsZHJlbi5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICAgICAgICAgIGV4aXN0aW5nRnJhbWVzLnB1c2goeyBpZDogbm9kZS5pZCwgbmFtZTogbm9kZS5uYW1lIH0pO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBtc2cuZnJhbWVzLmZvckVhY2goZnJhbWUgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGZyYW1lVG9DbG9uZSA9IGZpZ21hLmdldE5vZGVCeUlkKGZyYW1lKTtcbiAgICAgICAgICAgICAgICAvLyBjaGVjayBpZiBhIGZyYW1lIHdpdGggc2FtZSBuYW1lIGV4aXN0cyBpbiBjdXJyZW50UGFnZVxuICAgICAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nRnJhbWVJbmRleCA9IGV4aXN0aW5nRnJhbWVzLmZpbmRJbmRleChmcmFtZSA9PiBmcmFtZS5uYW1lID09PSBmcmFtZVRvQ2xvbmUubmFtZSk7XG4gICAgICAgICAgICAgICAgLy8gaWYgdGhlcmUgaXMgYW4gZXhpc3Rpbmcgbm9kZSBvZiB0aGUgc2FtZSBuYW1lXG4gICAgICAgICAgICAgICAgaWYgKGV4aXN0aW5nRnJhbWVJbmRleCA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGdldCB0aGUgbm9kZVxuICAgICAgICAgICAgICAgICAgICBjb25zdCBleGlzdGluZ0ZyYW1lTm9kZSA9IGZpZ21hLmdldE5vZGVCeUlkKGV4aXN0aW5nRnJhbWVzW2V4aXN0aW5nRnJhbWVJbmRleF0uaWQpO1xuICAgICAgICAgICAgICAgICAgICAvLyBjbG9uZSB0aGUgZnJhbWUgYXMgcGVyIHRoZSBtc2cuZnJhbWVzXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG5ld0ZyYW1lTm9kZSA9IGZyYW1lVG9DbG9uZS5jbG9uZSgpO1xuICAgICAgICAgICAgICAgICAgICAvLyByZXBvc2l0aW9uIGNsb25lZCBmcmFtZSB0byBvbGQgZnJhbWVcbiAgICAgICAgICAgICAgICAgICAgbmV3RnJhbWVOb2RlLnggPSBleGlzdGluZ0ZyYW1lTm9kZS54O1xuICAgICAgICAgICAgICAgICAgICBuZXdGcmFtZU5vZGUueSA9IGV4aXN0aW5nRnJhbWVOb2RlLnk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGRlbGV0ZSBleGlzdGluZyBvciBvbGQgZnJhbWVcbiAgICAgICAgICAgICAgICAgICAgZXhpc3RpbmdGcmFtZU5vZGUucmVtb3ZlKCk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGZvciBzYWZldHksIHNwbGljZSB0aGUgcmVwbGFjZSBlbnRyeSBmcm9tIGV4aXN0aW5nRnJhbWVJbmRleCBpbiBjYXNlIHRoZXJlIGFyZSAyIGZyYW1lcyB3aXRoIHNhbWUgbmFtZTtcbiAgICAgICAgICAgICAgICAgICAgZXhpc3RpbmdGcmFtZXMuc3BsaWNlKGV4aXN0aW5nRnJhbWVJbmRleCwgMSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIGFkZCBuZXdGcmFtZU5vZGUgdG8gY2xvbmVGcmFtZU5vZGVzIGZvciBkZXRhY2ggaW5zdGFuY2UgbG9naWNcbiAgICAgICAgICAgICAgICAgICAgcGFnZUNoaWxkcmVuTm9kZXMucHVzaChuZXdGcmFtZU5vZGUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gaWYgdGhlcmUgaXMgbm9uZSBvZiB0aGUgc2FtZSBuYW1lXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG5ld0ZyYW1lTm9kZSA9IGZyYW1lVG9DbG9uZS5jbG9uZSgpO1xuICAgICAgICAgICAgICAgICAgICAvLyBhZGQgbmV3RnJhbWVOb2RlIHRvIGNsb25lRnJhbWVOb2RlcyBmb3IgZGV0YWNoIGluc3RhbmNlIGxvZ2ljXG4gICAgICAgICAgICAgICAgICAgIHBhZ2VDaGlsZHJlbk5vZGVzLnB1c2gobmV3RnJhbWVOb2RlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIG1zZy5mcmFtZXMuZm9yRWFjaChmcmFtZSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgbmV3RnJhbWVOb2RlID0gZmlnbWEuZ2V0Tm9kZUJ5SWQoZnJhbWUpLmNsb25lKCk7XG4gICAgICAgICAgICAgICAgLy8gYWRkIG5ld0ZyYW1lTm9kZSB0byBjbG9uZUZyYW1lTm9kZXMgZm9yIGRldGFjaCBpbnN0YW5jZSBsb2dpY1xuICAgICAgICAgICAgICAgIHBhZ2VDaGlsZHJlbk5vZGVzLnB1c2gobmV3RnJhbWVOb2RlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIGlmIChtc2dbXCJkZXRhY2gtaW5zdGFuY2VzXCJdKSB7XG4gICAgICAgICAgICBsZXQgY3VycmVudExheWVyTm9kZXMgPSBbZmlnbWEuY3VycmVudFBhZ2VdO1xuICAgICAgICAgICAgd2hpbGUgKGN1cnJlbnRMYXllck5vZGVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICBsZXQgbmV4dExheWVyTm9kZXMgPSBbXTtcbiAgICAgICAgICAgICAgICBjdXJyZW50TGF5ZXJOb2Rlcy5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAobm9kZS5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5zdGFuY2VzID0gbm9kZS5maW5kQ2hpbGRyZW4oY2hpbGQgPT4gY2hpbGQudHlwZSA9PT0gXCJJTlNUQU5DRVwiIHx8IGNoaWxkLnR5cGUgPT09IFwiQ09NUE9ORU5UXCIpO1xuICAgICAgICAgICAgICAgICAgICAgICAgaW5zdGFuY2VzLmxlbmd0aCA+IDAgPyBpbnN0YW5jZXMuZm9yRWFjaChpbnN0YW5jZSA9PiBkZXRhY2hJbnN0YW5jZShpbnN0YW5jZSkpIDogbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5leHRMYXllck5vZGVzID0gbmV4dExheWVyTm9kZXMuY29uY2F0KG5vZGUuZmluZENoaWxkcmVuKGNoaWxkID0+IGNoaWxkLnR5cGUgPT09IFwiR1JPVVBcIiB8fCBjaGlsZC50eXBlID09PSBcIkZSQU1FXCIpKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGN1cnJlbnRMYXllck5vZGVzID0gWy4uLm5leHRMYXllck5vZGVzXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAobXNnLnNhbml0aXplKSB7XG4gICAgICAgICAgICBjb25zdCBoaWRkZW5Ob2RlcyA9IGNsb25lLmZpbmRBbGwobm9kZSA9PiBub2RlLnZpc2libGUgPT09IGZhbHNlICYmIG5vZGUudHlwZSAhPT0gXCJDT01QT05FTlRcIik7XG4gICAgICAgICAgICBoaWRkZW5Ob2Rlcy5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICAgICAgICAgICFjaGlsZEluc3RhbmNlTm9kZVJlZ2V4LnRlc3Qobm9kZS5pZCkgPyAoZmlnbWEuZ2V0Tm9kZUJ5SWQobm9kZS5pZCkgPyBub2RlLnJlbW92ZSgpIDogbnVsbCkgOiBudWxsO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1zZ1tcImNsb25lLW1hc3RlclwiXSkge1xuICAgICAgICAgICAgLy8gZ2V0IGFsbCBJTlNUQU5DRSB0eXBlIG5vZGVzXG4gICAgICAgICAgICBjb25zdCBpbnN0YW5jZU5vZGVzID0gY2xvbmUuZmluZEFsbChub2RlID0+IG5vZGUudHlwZSA9PT0gXCJJTlNUQU5DRVwiICYmICFjaGlsZEluc3RhbmNlTm9kZVJlZ2V4LnRlc3Qobm9kZS5pZCkpO1xuICAgICAgICAgICAgaW5zdGFuY2VOb2Rlcy5mb3JFYWNoKG5vZGUgPT4ge1xuICAgICAgICAgICAgICAgIG1hc3RlckNvbXBvbmVudHNJZHMuaW5kZXhPZihub2RlLm1haW5Db21wb25lbnQuaWQpID09PSAtMVxuICAgICAgICAgICAgICAgICAgICA/IG1hc3RlckNvbXBvbmVudHNJZHMucHVzaChub2RlLm1haW5Db21wb25lbnQuaWQpXG4gICAgICAgICAgICAgICAgICAgIDogbnVsbDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgLy8gZ2V0IGFuZCBzdG9yZSB0aGUgdW5pcXVlIG1hc3RlciBDT01QT05FTlQgbm9kZXMsIGlmIHRoZXkgYXJlIG5vdCBpbiBjbG9uZWQgcGFnZVxuICAgICAgICAgICAgbWFzdGVyQ29tcG9uZW50c0lkcy5mb3JFYWNoKGlkID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXJlbnRJZCA9IGZpZ21hLmdldE5vZGVCeUlkKGlkKS5wYXJlbnQgPyBmaWdtYS5nZXROb2RlQnlJZChpZCkucGFyZW50LmlkIDogXCJcIjtcbiAgICAgICAgICAgICAgICBpZiAocGFyZW50SWQgIT09IGZpZ21hLmN1cnJlbnRQYWdlLmlkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG1hc3RlckNsb25lID0gZmlnbWEuZ2V0Tm9kZUJ5SWQoaWQpLmNsb25lKCk7XG4gICAgICAgICAgICAgICAgICAgIG1hc3RlckNsb25lLnZpc2libGUgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgY2xvbmVkTWFzdGVyQ29tcG9uZW50c0lkcy5wdXNoKG1hc3RlckNsb25lLmlkKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIHJlbWFwIGluZGl2aWR1YWwgSU5TVEFOQ0Ugbm9kZXMgdG8gdGhlaXIgbmV3IG1hc3RlciBDT01QT05FTlQgbm9kZXNcbiAgICAgICAgICAgIGluc3RhbmNlTm9kZXMuZm9yRWFjaChub2RlID0+IHtcbiAgICAgICAgICAgICAgICAvLyBUT0RPIGZpbmRBbGwgbm9kZXMgd2l0aGluIHRoaXMgaW5zdGFuY2UgdGhhdCBpcyBub3QgYW5vdGhlciBpbnN0YW5jZVxuICAgICAgICAgICAgICAgIC8vIFRPRE8gcmVtZW1iZXIgdGhlIHByb3BlcnRpZXMgZWRpdGFibGUgYmFzZWQgb24gZWFjaCB0eXBlXG4gICAgICAgICAgICAgICAgbm9kZS5tYWluQ29tcG9uZW50ID0gZmlnbWEuZ2V0Tm9kZUJ5SWQoY2xvbmVkTWFzdGVyQ29tcG9uZW50c0lkc1ttYXN0ZXJDb21wb25lbnRzSWRzLmluZGV4T2Yobm9kZS5tYWluQ29tcG9uZW50LmlkKV0pO1xuICAgICAgICAgICAgICAgIC8vIFRPRE8gcmUtYXBwbHkgdGhlIHByb3BlcnRpZXMgaGVyZSAoaWYgd29ya3MsIG5lZWQgdG8gb3B0aW1pemUgZm9yIHRleHQgY2hhciByZXBsYWNlbWVudClcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgLy8gZGVlcCBjbG9uZSBJTlNUQU5DRSBub2RlcyBvZiBuZXcgbWFzdGVyIENPTVBPTkVOVCBub2Rlc1xuICAgICAgICAgICAgZGVlcENsb25lTWFzdGVyQ29tcG9uZW50c0lkcyA9IFsuLi5jbG9uZWRNYXN0ZXJDb21wb25lbnRzSWRzXTtcbiAgICAgICAgICAgIHdoaWxlIChkZWVwQ2xvbmVNYXN0ZXJDb21wb25lbnRzKSB7XG4gICAgICAgICAgICAgICAgZGVlcENsb25lTWFzdGVyQ29tcG9uZW50cyA9IGZhbHNlOyAvLyByZXNldCBkZWVwQ2xvbmVNYXN0ZXJDb21wb25lbnRzXG4gICAgICAgICAgICAgICAgY29uc3QgbmV4dERlZXBDbG9uZU1hc3RlckNvbXBvbmVudHNJZHMgPSBbXTtcbiAgICAgICAgICAgICAgICBkZWVwQ2xvbmVNYXN0ZXJDb21wb25lbnRzSWRzLmZvckVhY2goaWQgPT4ge1xuICAgICAgICAgICAgICAgICAgICAvLyBpZiBpdCBoYXMgY2hpbGRyZW5cbiAgICAgICAgICAgICAgICAgICAgaWYgKGZpZ21hLmdldE5vZGVCeUlkKGlkKS5jaGlsZHJlbikge1xuICAgICAgICAgICAgICAgICAgICAgICAgZmlnbWEuZ2V0Tm9kZUJ5SWQoaWQpLmNoaWxkcmVuLmZvckVhY2goY2hpbGQgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlmIGFueSBvZiB0aGVtIGFyZSBJTlNUQU5DRSBjb21wb25lbnRzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNoaWxkLnR5cGUgPT09IFwiSU5TVEFOQ0VcIikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBnZXQgdGhlIGlkIG9mIHRoZSBsb2NhdGlvbiBvZiB0aGUgY2hpbGQncyBtYXN0ZXIgQ09NUE9ORU5UIG5vZGVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgY2hpbGRNYXN0ZXJDb21wb25lbnRQYXJlbnQgPSBjaGlsZC5tYWluQ29tcG9uZW50LnBhcmVudFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPyBjaGlsZC5tYWluQ29tcG9uZW50LnBhcmVudC5pZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOiBcIlwiO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBpZiBjaGlsZCdzIG1hc3RlciBDT01QT05FTlQgbm90IGluIGN1cnJlbnRQYWdlXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjaGlsZE1hc3RlckNvbXBvbmVudFBhcmVudCAhPT0gZmlnbWEuY3VycmVudFBhZ2UuaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlmIGNoaWxkJ3MgbWFzdGVyIENPTVBPTkVOVCBpcyBub3QgY2xvbmVkIHlldFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKG1hc3RlckNvbXBvbmVudHNJZHMuaW5kZXhPZihjaGlsZC5tYWluQ29tcG9uZW50LmlkKSA9PT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXN0ZXJDb21wb25lbnRzSWRzLnB1c2goY2hpbGQubWFpbkNvbXBvbmVudC5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgbWFzdGVyQ2xvbmUgPSBjaGlsZC5tYWluQ29tcG9uZW50LmNsb25lKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFzdGVyQ2xvbmUudmlzaWJsZSA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsb25lZE1hc3RlckNvbXBvbmVudHNJZHMucHVzaChtYXN0ZXJDbG9uZS5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV4dERlZXBDbG9uZU1hc3RlckNvbXBvbmVudHNJZHMucHVzaChtYXN0ZXJDbG9uZS5pZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcENsb25lTWFzdGVyQ29tcG9uZW50cyA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGQubWFpbkNvbXBvbmVudCA9IG1hc3RlckNsb25lO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGQubWFpbkNvbXBvbmVudCA9IGZpZ21hLmdldE5vZGVCeUlkKGNsb25lZE1hc3RlckNvbXBvbmVudHNJZHNbbWFzdGVyQ29tcG9uZW50c0lkcy5pbmRleE9mKGNoaWxkLm1haW5Db21wb25lbnQuaWQpXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGRlZXBDbG9uZU1hc3RlckNvbXBvbmVudHNJZHMgPSBbLi4ubmV4dERlZXBDbG9uZU1hc3RlckNvbXBvbmVudHNJZHNdO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gdW5ncm91cCBhbmQgZXhpc3RpbmcgTWFzdGVyIENvbXBvbmVudHNcbiAgICAgICAgICAgIGNvbnN0IG1hc3RlckNvbXBvbmVudEdyb3VwID0gZmlnbWEuY3VycmVudFBhZ2UuZmluZE9uZShub2RlID0+IG5vZGUubmFtZSA9PT0gXCJNYXN0ZXIgQ29tcG9uZW50c1wiICYmIG5vZGUudHlwZSA9PT0gXCJHUk9VUFwiKTtcbiAgICAgICAgICAgIGlmIChtYXN0ZXJDb21wb25lbnRHcm91cCkge1xuICAgICAgICAgICAgICAgIG1hc3RlckNvbXBvbmVudEdyb3VwLmNoaWxkcmVuLmZvckVhY2goY2hpbGQgPT4gZmlnbWEuY3VycmVudFBhZ2UuYXBwZW5kQ2hpbGQoY2hpbGQpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIHRoZW4gZ3JvdXAgdGhlbSBpbnRvIGEgY29tcG9uZW50cyBncm91cFxuICAgICAgICAgICAgY29uc3QgY29tcG9uZW50Tm9kZXMgPSBmaWdtYS5jdXJyZW50UGFnZS5maW5kQWxsKG5vZGUgPT4gbm9kZS50eXBlID09PSBcIkNPTVBPTkVOVFwiKTtcbiAgICAgICAgICAgIGlmIChjb21wb25lbnROb2Rlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgY29tcG9uZW50R3JvdXAgPSBmaWdtYS5ncm91cChjb21wb25lbnROb2RlcywgZmlnbWEuY3VycmVudFBhZ2UpO1xuICAgICAgICAgICAgICAgIGNvbXBvbmVudEdyb3VwLm5hbWUgPSBcIk1hc3RlciBDb21wb25lbnRzXCI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG1zZy5sb2NrZWQpIHtcbiAgICAgICAgICAgIGNsb25lLmNoaWxkcmVuLmZvckVhY2goKGNoaWxkKSA9PiAoY2hpbGQubG9ja2VkID0gdHJ1ZSkpO1xuICAgICAgICB9XG4gICAgICAgIGZpZ21hLm5vdGlmeShg8J+RjSBTdWNjZXNzZnVsbHkgY2xvbmVkIHNlbGVjdGVkIGZyYW1lcyBpbnRvICR7Y2xvbmUubmFtZX1gKTtcbiAgICAgICAgZmlnbWEuY2xvc2VQbHVnaW4oKTtcbiAgICB9XG59O1xuY29uc3QgZGV0YWNoSW5zdGFuY2UgPSAoaW5zdGFuY2UpID0+IHtcbiAgICBjb25zdCBwYXJlbnQgPSBpbnN0YW5jZS5wYXJlbnQ7XG4gICAgY29uc3QgbmV3RnJhbWUgPSBmaWdtYS5jcmVhdGVGcmFtZSgpO1xuICAgIGZvciAoY29uc3QgaSBpbiBuZXdGcmFtZSkge1xuICAgICAgICBpZiAoIVJFQURfT05MWV9PUl9ERVBSRUNBVEVEX0tFWVMuaW5jbHVkZXMoaSkpIHtcbiAgICAgICAgICAgIGlmIChGVU5DVElPTl9CQVNFRF9LRVlTLmluY2x1ZGVzKGkpKSB7XG4gICAgICAgICAgICAgICAgaWYgKGkgPT09IFwid2lkdGhcIiAmJiBpbnN0YW5jZVtpXSA+PSAwLjAxKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc2l6ZUhlaWdodCA9IE1hdGgubWF4KG5ld0ZyYW1lLmhlaWdodCwgMC4wMSk7XG4gICAgICAgICAgICAgICAgICAgIG5ld0ZyYW1lLnJlc2l6ZShpbnN0YW5jZVtpXSwgcmVzaXplSGVpZ2h0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGkgPT09IFwiaGVpZ2h0XCIgJiYgaW5zdGFuY2VbaV0gPj0gMC4wMSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNpemVXaWR0aCA9IE1hdGgubWF4KG5ld0ZyYW1lLndpZHRoLCAwLjAxKTtcbiAgICAgICAgICAgICAgICAgICAgbmV3RnJhbWUucmVzaXplKHJlc2l6ZVdpZHRoLCBpbnN0YW5jZVtpXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gaWYgY29ybmVyUmFkaXVzIGlzIGZpZ21hLm1peGVkLCByZWx5IG9uIHRoZSBvdGhlciBpbmRpdmlkdWFsIGtleXNcbiAgICAgICAgICAgICAgICBpZiAoIShpID09PSBcImNvcm5lclJhZGl1c1wiICYmIGluc3RhbmNlW2ldID09PSBmaWdtYS5taXhlZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgbmV3RnJhbWVbaV0gPSBjbG9uZShpbnN0YW5jZVtpXSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIGluc3RhbmNlLmNoaWxkcmVuLmZvckVhY2goY2hpbGQgPT4ge1xuICAgICAgICBjb25zdCBjaGlsZENsb25lID0gY2hpbGQuY2xvbmUoKTtcbiAgICAgICAgaWYgKGNoaWxkLnR5cGUgIT09IFwiVEVYVFwiKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGogaW4gY2hpbGRDbG9uZSkge1xuICAgICAgICAgICAgICAgIGlmICghUkVBRF9PTkxZX09SX0RFUFJFQ0FURURfS0VZUy5pbmNsdWRlcyhqKSkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoRlVOQ1RJT05fQkFTRURfS0VZUy5pbmNsdWRlcyhqKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGogPT09IFwid2lkdGhcIiAmJiBjaGlsZFtqXSA+PSAwLjAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzaXplSGVpZ2h0ID0gY2hpbGRDbG9uZS50eXBlID09PSBcIkxJTkVcIiA/IDAgOiBNYXRoLm1heChjaGlsZENsb25lLmhlaWdodCwgMC4wMSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hpbGRDbG9uZS5yZXNpemUoY2hpbGRbal0sIHJlc2l6ZUhlaWdodCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoaiA9PT0gXCJoZWlnaHRcIiAmJiBjaGlsZFtqXSA+PSAwLjAxKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzaXplV2lkdGggPSBNYXRoLm1heChjaGlsZENsb25lLndpZHRoLCAwLjAxKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGlsZENsb25lLnJlc2l6ZShyZXNpemVXaWR0aCwgY2hpbGRbal0pO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gaWYgY29ybmVyUmFkaXVzIGlzIGZpZ21hLm1peGVkLCByZWx5IG9uIHRoZSBvdGhlciBpbmRpdmlkdWFsIGtleXNcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghKGogPT09IFwiY29ybmVyUmFkaXVzXCIgJiYgY2hpbGRbal0gPT09IGZpZ21hLm1peGVkKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoaWxkQ2xvbmVbal0gPSBjbG9uZShjaGlsZFtqXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgbmV3RnJhbWUuYXBwZW5kQ2hpbGQoY2hpbGRDbG9uZSk7XG4gICAgfSk7XG4gICAgcGFyZW50Lmluc2VydENoaWxkKHBhcmVudC5jaGlsZHJlbi5pbmRleE9mKGluc3RhbmNlKSwgbmV3RnJhbWUpO1xuICAgIGluc3RhbmNlLnJlbW92ZSgpO1xufTtcbmZ1bmN0aW9uIGxvYWROb2RlRm9udChmb250TmFtZSkge1xuICAgIHJldHVybiBfX2F3YWl0ZXIodGhpcywgdm9pZCAwLCB2b2lkIDAsIGZ1bmN0aW9uKiAoKSB7XG4gICAgICAgIHlpZWxkIGZpZ21hLmxvYWRGb250QXN5bmMoZm9udE5hbWUpLmNhdGNoKGVycm9yID0+IGNvbnNvbGUuZXJyb3IoZXJyb3IpKTtcbiAgICB9KTtcbn1cbmZ1bmN0aW9uIGNsb25lKHZhbCkge1xuICAgIGNvbnN0IHR5cGUgPSB0eXBlb2YgdmFsO1xuICAgIGlmICh2YWwgPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGVsc2UgaWYgKHR5cGUgPT09IFwidW5kZWZpbmVkXCIgfHwgdHlwZSA9PT0gXCJudW1iZXJcIiB8fCB0eXBlID09PSBcInN0cmluZ1wiIHx8IHR5cGUgPT09IFwiYm9vbGVhblwiKSB7XG4gICAgICAgIHJldHVybiB2YWw7XG4gICAgfVxuICAgIGVsc2UgaWYgKHR5cGUgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgaWYgKHZhbCBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgICByZXR1cm4gdmFsLm1hcCh4ID0+IGNsb25lKHgpKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmICh2YWwgaW5zdGFuY2VvZiBVaW50OEFycmF5KSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkodmFsKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxldCBvID0ge307XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGtleSBpbiB2YWwpIHtcbiAgICAgICAgICAgICAgICBvW2tleV0gPSBjbG9uZSh2YWxba2V5XSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbztcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBcInVua25vd25cIjtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0=