figma-page-clone
Version:
One-click tool to clone a page
399 lines (394 loc) • 44.4 kB
JavaScript
/******/ (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=