UNPKG

react-canvaskit

Version:

A React implementation of the Skia-CanvasKit drawing library.

317 lines 53.7 kB
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()); }); }; import * as CanvasKitInit from 'canvaskit-wasm'; import * as React from 'react'; import * as ReactReconciler from 'react-reconciler'; import { createCkElement, isContainerElement, } from './SkiaElementTypes'; const loadRobotoFontData = fetch('https://storage.googleapis.com/skia-cdn/misc/Roboto-Regular.ttf').then((response) => response.arrayBuffer()); // @ts-ignore const canvasKitPromise = CanvasKitInit({ locateFile: (file) => `https://unpkg.com/canvaskit-wasm@0.32.0/bin/${file}`, }); let canvasKit; let CanvasKitContext; export let useCanvasKit; export let CanvasKitProvider; let FontManagerContext; export let useFontManager; export let FontManagerProvider; export function init() { return __awaiter(this, void 0, void 0, function* () { canvasKit = yield canvasKitPromise; const robotoFontData = yield loadRobotoFontData; // const copy to make the TS compiler happy when we pass it down to a lambda const ck = canvasKit; CanvasKitContext = React.createContext(ck); useCanvasKit = () => React.useContext(CanvasKitContext); CanvasKitProvider = ({ children }) => React.createElement(CanvasKitContext.Provider, { value: ck }, "children"); const defaultFontManager = ck.FontMgr.FromData(robotoFontData); FontManagerContext = React.createContext(defaultFontManager); useFontManager = () => React.useContext(FontManagerContext); FontManagerProvider = (props) => { if (props.fontData) { const fontMgrFromData = ck.FontMgr.FromData(...props.fontData); if (fontMgrFromData === null) { throw new Error('Failed to create font manager from font data.'); } return React.createElement(FontManagerContext.Provider, { value: fontMgrFromData }, props.children); } else { return React.createElement(FontManagerContext.Provider, { value: defaultFontManager }, props.children); } }; }); } // @ts-ignore TODO implement missing functions const hostConfig = { /** * This function is used by the reconciler in order to calculate current time for prioritising work. */ now: Date.now, supportsMutation: false, supportsPersistence: true, supportsHydration: false, createContainerChildSet(container) { return []; }, /** * Attaches new children to the set returned by createContainerChildSet * @param childSet * @param child */ appendChildToContainerChildSet(childSet, child) { childSet.push(child); }, replaceContainerChildren(container, newChildren) { container.children.forEach((child) => child.delete()); container.children = newChildren; }, /** * This function lets you share some context with the other functions in this HostConfig. * * This method lets you return the initial host context from the root of the tree. See `getChildHostContext` for the explanation of host context. * * If you don't intend to use host context, you can return `null`. * * This method happens **in the render phase**. Do not mutate the tree from it. * * @param rootContainerInstance is basically the root dom node you specify while calling render. This is most commonly * <div id="root"></div> * @return A context object that you wish to pass to immediate child. */ getRootHostContext(rootContainerInstance) { return { ckElement: rootContainerInstance }; }, /** * This function provides a way to access context from the parent and also a way to pass some context to the immediate * children of the current node. Context is basically a regular object containing some information. * * Host context lets you track some information about where you are in the tree so that it's available inside `createInstance` as the `hostContext` parameter. For example, the DOM renderer uses it to track whether it's inside an HTML or an SVG tree, because `createInstance` implementation needs to be different for them. * * If the node of this `type` does not influence the context you want to pass down, you can return `parentHostContext`. Alternatively, you can return any custom object representing the information you want to pass down. * * If you don't want to do anything here, return `parentHostContext`. * * This method happens **in the render phase**. Do not mutate the tree from it. * * @param parentHostContext Context from parent. Example: This will contain rootContext for the immediate child of * roothost. * @param type This contains the type of fiber i.e, ‘div’, ‘span’, ‘p’, ‘input’ etc. * @param rootContainerInstance rootInstance is basically the root dom node you specify while calling render. This is * most commonly <div id="root"></div> * @return A context object that you wish to pass to immediate child. */ getChildHostContext(parentHostContext, type, rootContainerInstance) { return parentHostContext; }, /** * If the function returns true, the text would be created inside the host element and no new text element would be * created separately. * * If this returned true, the next call would be to createInstance for the current element and traversal would stop at * this node (children of this element wont be traversed). * * If it returns false, getChildHostContext and shouldSetTextContent will be called on the child elements and it will * continue till shouldSetTextContent returns true or if the recursion reaches the last tree endpoint which usually is * a text node. When it reaches the last leaf text node it will call createTextInstance * * Some target platforms support setting an instance's text content without manually creating a text node. For example, in the DOM, you can set `node.textContent` instead of creating a text node and appending it. * * If you return `true` from this method, React will assume that this node's children are text, and will not create nodes for them. It will instead rely on you to have filled that text during `createInstance`. This is a performance optimization. For example, the DOM renderer returns `true` only if `type` is a known text-only parent (like `'textarea'`) or if `props.children` has a `'string'` type. If you return `true`, you will need to implement `resetTextContent` too. * * If you don't want to do anything here, you should return `false`. * * This method happens **in the render phase**. Do not mutate the tree from it. * * @param type This contains the type of fiber i.e, ‘div’, ‘span’, ‘p’, ‘input’ etc. * @param props Contains the props passed to the host react element. * @return This should be a boolean value. */ shouldSetTextContent(type, props) { return type === 'ck-text' || type === 'ck-paragraph'; }, /** * Here we specify how should renderer handle the text content * * Same as `createInstance`, but for text nodes. If your renderer doesn't support text nodes, you can throw here. * * @param text contains the text string that needs to be rendered. * @param rootContainerInstance root dom node you specify while calling render. This is most commonly * <div id="root"></div> * @param hostContext contains the context from the host node enclosing this text node. For example, in the case of * <p>Hello</p>: currentHostContext for Hello text node will be host context of p. * @param internalInstanceHandle The fiber node for the text instance. This manages work for this instance. * @return This should be an actual text view element. In case of dom it would be a textNode. */ createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) { throw new Error(`The text '${text}' must be wrapped in a ck-text or ck-paragraph element.`); }, /** * Create instance is called on all host nodes except the leaf text nodes. So we should return the correct view * element for each host type here. We are also supposed to take care of the props sent to the host element. For * example: setting up onClickListeners or setting up styling etc. * * This method should return a newly created node. For example, the DOM renderer would call `document.createElement(type)` here and then set the properties from `props`. * * You can use `rootContainer` to access the root container associated with that tree. For example, in the DOM renderer, this is useful to get the correct `document` reference that the root belongs to. * * The `hostContext` parameter lets you keep track of some information about your current place in the tree. To learn more about it, see `getChildHostContext` below. * * The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields, be aware that it may change significantly between versions. You're taking on additional maintenance risk by reading from it, and giving up all guarantees if you write something to it. * * This method happens **in the render phase**. It can (and usually should) mutate the node it has just created before returning it, but it must not modify any other nodes. It must not register any event handlers on the parent tree. This is because an instance being created doesn't guarantee it would be placed in the tree — it could be left unused and later collected by GC. If you need to do something when an instance is definitely in the tree, look at `commitMount` instead. * * @param type This contains the type of fiber i.e, ‘div’, ‘span’, ‘p’, ‘input’ etc. * @param props Contains the props passed to the host react element. * @param rootContainerInstance Root dom node you specify while calling render. This is most commonly <div id="root"></div> * @param hostContext contains the context from the parent node enclosing this node. This is the return value from getChildHostContext of the parent node. * @param internalInstanceHandle The fiber node for the text instance. This manages work for this instance. */ createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) { return createCkElement(type, props, hostContext.ckElement.canvasKit); }, /** * Here we will attach the child dom node to the parent on the initial render phase. This method will be called for * each child of the current node. * * This method should mutate the `parentInstance` and add the child to its list of children. For example, in the DOM this would translate to a `parentInstance.appendChild(child)` call. * * This method happens **in the render phase**. It can mutate `parentInstance` and `child`, but it must not modify any other nodes. It's called while the tree is still being built up and not connected to the actual tree on the screen. * * @param parentInstance The current node in the traversal * @param child The child dom node of the current node. */ appendInitialChild(parentInstance, child) { if (isContainerElement(parentInstance)) { parentInstance.children.push(child); } else { throw new Error('Bug? Trying to append a child to a parent that is not a container.'); } }, /** * In case of react native renderer, this function does nothing but return false. * * In case of react-dom, this adds default dom properties such as event listeners, etc. * For implementing auto focus for certain input elements (autofocus can happen only * after render is done), react-dom sends return type as true. This results in commitMount * method for this element to be called. The commitMount will be called only if an element * returns true in finalizeInitialChildren and after the all elements of the tree has been * rendered (even after resetAfterCommit). * * In this method, you can perform some final mutations on the `instance`. Unlike with `createInstance`, by the time `finalizeInitialChildren` is called, all the initial children have already been added to the `instance`, but the instance itself has not yet been connected to the tree on the screen. * * This method happens **in the render phase**. It can mutate `instance`, but it must not modify any other nodes. It's called while the tree is still being built up and not connected to the actual tree on the screen. * * There is a second purpose to this method. It lets you specify whether there is some work that needs to happen when the node is connected to the tree on the screen. If you return `true`, the instance will receive a `commitMount` call later. See its documentation below. * * If you don't want to do anything here, you should return `false`. * * @param parentInstance The instance is the dom element after appendInitialChild. * @param type This contains the type of fiber i.e, ‘div’, ‘span’, ‘p’, ‘input’ etc. * @param props Contains the props passed to the host react element. * @param rootContainerInstance root dom node you specify while calling render. This is most commonly <div id="root"></div> * @param hostContext contains the context from the parent node enclosing this node. This is the return value from getChildHostContext of the parent node. */ finalizeInitialChildren(parentInstance, type, props, rootContainerInstance, hostContext) { return false; }, finalizeContainerChildren(container, newChildren) { }, /** * * This function is called when we have made a in-memory render tree of all the views (Remember we are yet to attach * it the the actual root dom node). Here we can do any preparation that needs to be done on the rootContainer before * attaching the in memory render tree. For example: In the case of react-dom, it keeps track of all the currently * focused elements, disabled events temporarily, etc. * * This method lets you store some information before React starts making changes to the tree on the screen. For example, the DOM renderer stores the current text selection so that it can later restore it. This method is mirrored by `resetAfterCommit`. * * Even if you don't want to do anything here, you need to return `null` from it. * * @param containerInfo root dom node you specify while calling render. This is most commonly <div id="root"></div> */ prepareForCommit(containerInfo) { return null; }, /** * This function gets executed after the inmemory tree has been attached to the root dom element. Here we can do any * post attach operations that needs to be done. For example: react-dom re-enabled events which were temporarily * disabled in prepareForCommit and refocuses elements, etc. * * This method is called right after React has performed the tree mutations. You can use it to restore something you've stored in `prepareForCommit` — for example, text selection. * * You can leave it empty. * * @param containerInfo root dom node you specify while calling render. This is most commonly <div id="root"></div> */ resetAfterCommit(containerInfo) { var _a, _b; // TODO instead of re-rendering everything, only rerender dirty nodes? containerInfo.children.forEach((child) => child.render(containerInfo)); (_b = (_a = containerInfo.props).renderCallback) === null || _b === void 0 ? void 0 : _b.call(_a); }, getPublicInstance(instance) { return instance.skObject; }, /** * React calls this method so that you can compare the previous and the next props, and decide whether you need to update the underlying instance or not. If you don't need to update it, return `null`. If you need to update it, you can return an arbitrary object representing the changes that need to happen. Then in `commitUpdate` you would need to apply those changes to the instance. * * This method happens **in the render phase**. It should only *calculate* the update — but not apply it! For example, the DOM renderer returns an array that looks like `[prop1, value1, prop2, value2, ...]` for all props that have actually changed. And only in `commitUpdate` it applies those changes. You should calculate as much as you can in `prepareUpdate` so that `commitUpdate` can be very fast and straightforward. * * See the meaning of `rootContainer` and `hostContext` in the `createInstance` documentation. */ prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, hostContext) { // TODO check & return if we can need to create an entire new object or we can reuse the underlying skobject and use it as the payload in cloneInstance. }, cloneInstance(instance, updatePayload, type, oldProps, newProps, internalInstanceHandle, keepChildren, recyclableInstance) { // TODO implement a way where we can create a new instance and reuse the underlying canvaskit objects where possible const ckElement = createCkElement(type, newProps, instance.canvasKit); if (keepChildren && isContainerElement(ckElement) && isContainerElement(instance)) { ckElement.children = instance.children; } // recyclableInstance.props = newProps // return recyclableInstance return ckElement; }, }; const canvaskitReconciler = ReactReconciler(hostConfig); canvaskitReconciler.injectIntoDevTools({ bundleType: 1, version: '0.0.1', rendererPackageName: 'react-canvaskit', // package name }); export function render(element, canvas, renderCallback) { if (canvasKit === undefined) { throw new Error('Not initialized'); } const rootTag = 0; const hydrate = false; const skSurface = canvasKit.MakeWebGLCanvasSurface(canvas, undefined); if (skSurface === null) { throw new Error('Failed to create surface from canvas.'); } const ckSurfaceElement = { canvasKit, type: 'ck-surface', // @ts-ignore props: { width: canvas.width, height: canvas.height, renderCallback }, skObjectType: 'SkSurface', skObject: skSurface, children: [], render() { this.children.forEach((child) => child.render(ckSurfaceElement)); }, }; const container = canvaskitReconciler.createContainer(ckSurfaceElement, rootTag, hydrate, null); return new Promise((resolve) => { canvaskitReconciler.updateContainer(element, container, null, () => resolve()); }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVhY3RDYW52YXNLaXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvUmVhY3RDYW52YXNLaXQudHN4Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUNBLE9BQU8sS0FBSyxhQUFhLE1BQU0sZ0JBQWdCLENBQUE7QUFFL0MsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUE7QUFFOUIsT0FBTyxLQUFLLGVBQWUsTUFBTSxrQkFBa0IsQ0FBQTtBQUNuRCxPQUFPLEVBS0wsZUFBZSxFQUNmLGtCQUFrQixHQUNuQixNQUFNLG9CQUFvQixDQUFBO0FBRTNCLE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxDQUFDLGlFQUFpRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FDcEgsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUN2QixDQUFBO0FBQ0QsYUFBYTtBQUNiLE1BQU0sZ0JBQWdCLEdBQXVCLGFBQWEsQ0FBQztJQUN6RCxVQUFVLEVBQUUsQ0FBQyxJQUFZLEVBQVUsRUFBRSxDQUFDLCtDQUErQyxJQUFJLEVBQUU7Q0FDNUYsQ0FBQyxDQUFBO0FBQ0YsSUFBSSxTQUFnQyxDQUFBO0FBRXBDLElBQUksZ0JBQTBDLENBQUE7QUFDOUMsTUFBTSxDQUFDLElBQUksWUFBNkIsQ0FBQTtBQUN4QyxNQUFNLENBQUMsSUFBSSxpQkFBb0MsQ0FBQTtBQUUvQyxJQUFJLGtCQUFnRCxDQUFBO0FBQ3BELE1BQU0sQ0FBQyxJQUFJLGNBQW1DLENBQUE7QUFDOUMsTUFBTSxDQUFDLElBQUksbUJBQXFHLENBQUE7QUFFaEgsTUFBTSxVQUFnQixJQUFJOztRQUN4QixTQUFTLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQTtRQUNsQyxNQUFNLGNBQWMsR0FBRyxNQUFNLGtCQUFrQixDQUFBO1FBQy9DLDRFQUE0RTtRQUM1RSxNQUFNLEVBQUUsR0FBRyxTQUFTLENBQUE7UUFFcEIsZ0JBQWdCLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FB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QyxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUE7SUFDckUsSUFBSSxTQUFTLEtBQUssSUFBSSxFQUFFO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQTtLQUN6RDtJQUVELE1BQU0sZ0JBQWdCLEdBQXFDO1FBQ3pELFNBQVM7UUFDVCxJQUFJLEVBQUUsWUFBWTtRQUNsQixhQUFhO1FBQ2IsS0FBSyxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNLEVBQUUsY0FBYyxFQUFFO1FBQ3JFLFlBQVksRUFBRSxXQUFXO1FBQ3pCLFFBQVEsRUFBRSxTQUFTO1FBQ25CLFFBQVEsRUFBRSxFQUFFO1FBQ1osTUFBTTtZQUNKLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQTtRQUNsRSxDQUFDO0tBQ0YsQ0FBQTtJQUNELE1BQU0sU0FBUyxHQUFHLG1CQUFtQixDQUFDLGVBQWUsQ0FBQyxnQkFBZ0IsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFBO0lBRS9GLE9BQU8sSUFBSSxPQUFPLENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUNuQyxtQkFBbUIsQ0FBQyxlQUFlLENBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtJQUNoRixDQUFDLENBQUMsQ0FBQTtBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENhbnZhc0tpdCwgRm9udE1nciBhcyBTa0ZvbnRNYW5hZ2VyIH0gZnJvbSAnY2FudmFza2l0LXdhc20nXG5pbXBvcnQgKiBhcyBDYW52YXNLaXRJbml0IGZyb20gJ2NhbnZhc2tpdC13YXNtJ1xuaW1wb3J0IHR5cGUgeyBGdW5jdGlvbkNvbXBvbmVudCwgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgSG9zdENvbmZpZyB9IGZyb20gJ3JlYWN0LXJlY29uY2lsZXInXG5pbXBvcnQgKiBhcyBSZWFjdFJlY29uY2lsZXIgZnJvbSAncmVhY3QtcmVjb25jaWxlcidcbmltcG9ydCB7XG4gIENrRWxlbWVudCxcbiAgQ2tFbGVtZW50Q29udGFpbmVyLFxuICBDa0VsZW1lbnRQcm9wcyxcbiAgQ2tFbGVtZW50VHlwZSxcbiAgY3JlYXRlQ2tFbGVtZW50LFxuICBpc0NvbnRhaW5lckVsZW1lbnQsXG59IGZyb20gJy4vU2tpYUVsZW1lbnRUeXBlcydcblxuY29uc3QgbG9hZFJvYm90b0ZvbnREYXRhID0gZmV0Y2goJ2h0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9za2lhLWNkbi9taXNjL1JvYm90by1SZWd1bGFyLnR0ZicpLnRoZW4oKHJlc3BvbnNlKSA9PlxuICByZXNwb25zZS5hcnJheUJ1ZmZlcigpLFxuKVxuLy8gQHRzLWlnbm9yZVxuY29uc3QgY2FudmFzS2l0UHJvbWlzZTogUHJvbWlzZTxDYW52YXNLaXQ+ID0gQ2FudmFzS2l0SW5pdCh7XG4gIGxvY2F0ZUZpbGU6IChmaWxlOiBzdHJpbmcpOiBzdHJpbmcgPT4gYGh0dHBzOi8vdW5wa2cuY29tL2NhbnZhc2tpdC13YXNtQDAuMzIuMC9iaW4vJHtmaWxlfWAsXG59KVxubGV0IGNhbnZhc0tpdDogQ2FudmFzS2l0IHwgdW5kZWZpbmVkXG5cbmxldCBDYW52YXNLaXRDb250ZXh0OiBSZWFjdC5Db250ZXh0PENhbnZhc0tpdD5cbmV4cG9ydCBsZXQgdXNlQ2FudmFzS2l0OiAoKSA9PiBDYW52YXNLaXRcbmV4cG9ydCBsZXQgQ2FudmFzS2l0UHJvdmlkZXI6IEZ1bmN0aW9uQ29tcG9uZW50XG5cbmxldCBGb250TWFuYWdlckNvbnRleHQ6IFJlYWN0LkNvbnRleHQ8U2tGb250TWFuYWdlcj5cbmV4cG9ydCBsZXQgdXNlRm9udE1hbmFnZXI6ICgpID0+IFNrRm9udE1hbmFnZXJcbmV4cG9ydCBsZXQgRm9udE1hbmFnZXJQcm92aWRlcjogRnVuY3Rpb25Db21wb25lbnQ8eyBmb250RGF0YTogQXJyYXlCdWZmZXJbXSB8IHVuZGVmaW5lZDsgY2hpbGRyZW4/OiBSZWFjdE5vZGUgfT5cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXQoKSB7XG4gIGNhbnZhc0tpdCA9IGF3YWl0IGNhbnZhc0tpdFByb21pc2VcbiAgY29uc3Qgcm9ib3RvRm9udERhdGEgPSBhd2FpdCBsb2FkUm9ib3RvRm9udERhdGFcbiAgLy8gY29uc3QgY29weSB0byBtYWtlIHRoZSBUUyBjb21waWxlciBoYXBweSB3aGVuIHdlIHBhc3MgaXQgZG93biB0byBhIGxhbWJkYVxuICBjb25zdCBjayA9IGNhbnZhc0tpdFxuXG4gIENhbnZhc0tpdENvbnRleHQgPSBSZWFjdC5jcmVhdGVDb250ZXh0KGNrKVxuICB1c2VDYW52YXNLaXQgPSAoKSA9PiBSZWFjdC51c2VDb250ZXh0KENhbnZhc0tpdENvbnRleHQpXG4gIENhbnZhc0tpdFByb3ZpZGVyID0gKHsgY2hpbGRyZW4gfSkgPT4gPENhbnZhc0tpdENvbnRleHQuUHJvdmlkZXIgdmFsdWU9e2NrfT5jaGlsZHJlbjwvQ2FudmFzS2l0Q29udGV4dC5Qcm92aWRlcj5cblxuICBjb25zdCBkZWZhdWx0Rm9udE1hbmFnZXIgPSBjay5Gb250TWdyLkZyb21EYXRhKHJvYm90b0ZvbnREYXRhKSBhcyBTa0ZvbnRNYW5hZ2VyXG4gIEZvbnRNYW5hZ2VyQ29udGV4dCA9IFJlYWN0LmNyZWF0ZUNvbnRleHQoZGVmYXVsdEZvbnRNYW5hZ2VyKVxuICB1c2VGb250TWFuYWdlciA9ICgpID0+IFJlYWN0LnVzZUNvbnRleHQoRm9udE1hbmFnZXJDb250ZXh0KVxuICBGb250TWFuYWdlclByb3ZpZGVyID0gKHByb3BzOiB7IGZvbnREYXRhOiBBcnJheUJ1ZmZlcltdIHwgdW5kZWZpbmVkOyBjaGlsZHJlbj86IFJlYWN0Tm9kZSB9KSA9PiB7XG4gICAgaWYgKHByb3BzLmZvbnREYXRhKSB7XG4gICAgICBjb25zdCBmb250TWdyRnJvbURhdGEgPSBjay5Gb250TWdyLkZyb21EYXRhKC4uLnByb3BzLmZvbnREYXRhKVxuICAgICAgaWYgKGZvbnRNZ3JGcm9tRGF0YSA9PT0gbnVsbCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBjcmVhdGUgZm9udCBtYW5hZ2VyIGZyb20gZm9udCBkYXRhLicpXG4gICAgICB9XG5cbiAgICAgIHJldHVybiA8Rm9udE1hbmFnZXJDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXtmb250TWdyRnJvbURhdGF9Pntwcm9wcy5jaGlsZHJlbn08L0ZvbnRNYW5hZ2VyQ29udGV4dC5Qcm92aWRlcj5cbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIDxGb250TWFuYWdlckNvbnRleHQuUHJvdmlkZXIgdmFsdWU9e2RlZmF1bHRGb250TWFuYWdlcn0+e3Byb3BzLmNoaWxkcmVufTwvRm9udE1hbmFnZXJDb250ZXh0LlByb3ZpZGVyPlxuICAgIH1cbiAgfVxufVxuXG50eXBlIENvbnRhaW5lckNvbnRleHQgPSB7XG4gIGNrRWxlbWVudDogQ2tFbGVtZW50PGFueT5cbn1cblxuZXhwb3J0IGludGVyZmFjZSBTa09iamVjdFJlZjxUPiB7XG4gIGN1cnJlbnQ6IFRcbn1cblxuaW50ZXJmYWNlIFJlYWN0Q2FudmFzS2l0SG9zdENvbmZpZ1xuICBleHRlbmRzIEhvc3RDb25maWc8XG4gICAgQ2tFbGVtZW50VHlwZSwgLy8gVHlwZVxuICAgIENrRWxlbWVudFByb3BzPGFueT4sIC8vIFByb3BzXG4gICAgQ2tFbGVtZW50Q29udGFpbmVyPGFueT4sIC8vIENvbnRhaW5lclxuICAgIENrRWxlbWVudDxhbnk+LCAvLyBJbnN0YW5jZVxuICAgIENrRWxlbWVudDwnY2stdGV4dCc+IHwgQ2tFbGVtZW50PCdjay1wYXJhZ3JhcGgnPiwgLy8gVGV4dEluc3RhbmNlXG4gICAgYW55LCAvLyBTdXNwZW5jZUluc3RhbmNlXG4gICAgYW55LCAvLyBIeWRyYXRhYmxlSW5zdGFuY2VcbiAgICBTa09iamVjdFJlZjxhbnk+LCAvLyBQdWJsaWNJbnN0YW5jZVxuICAgIENvbnRhaW5lckNvbnRleHQsIC8vIEhvc3RDb250ZXh0XG4gICAgYW55LCAvLyBVcGRhdGVQYXlsb2FkXG4gICAgQ2tFbGVtZW50PGFueT5bXSwgLy8gX0NoaWxkU2V0XG4gICAgYW55LCAvLyBUaW1lb3V0SGFuZGxlXG4gICAgYW55IC8vIE5vVGltb3V0XG4gID4ge31cblxuLy8gQHRzLWlnbm9yZSBUT0RPIGltcGxlbWVudCBtaXNzaW5nIGZ1bmN0aW9uc1xuY29uc3QgaG9zdENvbmZpZzogUmVhY3RDYW52YXNLaXRIb3N0Q29uZmlnID0ge1xuICAvKipcbiAgICogVGhpcyBmdW5jdGlvbiBpcyB1c2VkIGJ5IHRoZSByZWNvbmNpbGVyIGluIG9yZGVyIHRvIGNhbGN1bGF0ZSBjdXJyZW50IHRpbWUgZm9yIHByaW9yaXRpc2luZyB3b3JrLlxuICAgKi9cbiAgbm93OiBEYXRlLm5vdyxcbiAgc3VwcG9ydHNNdXRhdGlvbjogZmFsc2UsXG4gIHN1cHBvcnRzUGVyc2lzdGVuY2U6IHRydWUsXG4gIHN1cHBvcnRzSHlkcmF0aW9uOiBmYWxzZSxcblxuICBjcmVhdGVDb250YWluZXJDaGlsZFNldChjb250YWluZXI6IENrRWxlbWVudENvbnRhaW5lcjxhbnk+KTogQ2tFbGVtZW50PGFueT5bXSB7XG4gICAgcmV0dXJuIFtdXG4gIH0sXG4gIC8qKlxuICAgKiBBdHRhY2hlcyBuZXcgY2hpbGRyZW4gdG8gdGhlIHNldCByZXR1cm5lZCBieSBjcmVhdGVDb250YWluZXJDaGlsZFNldFxuICAgKiBAcGFyYW0gY2hpbGRTZXRcbiAgICogQHBhcmFtIGNoaWxkXG4gICAqL1xuICBhcHBlbmRDaGlsZFRvQ29udGFpbmVyQ2hpbGRTZXQoY2hpbGRTZXQ6IENrRWxlbWVudDxhbnk+W10sIGNoaWxkOiBDa0VsZW1lbnQ8YW55Pikge1xuICAgIGNoaWxkU2V0LnB1c2goY2hpbGQpXG4gIH0sXG4gIHJlcGxhY2VDb250YWluZXJDaGlsZHJlbihjb250YWluZXI6IENrRWxlbWVudENvbnRhaW5lcjxhbnk+LCBuZXdDaGlsZHJlbjogQ2tFbGVtZW50PGFueT5bXSkge1xuICAgIGNvbnRhaW5lci5jaGlsZHJlbi5mb3JFYWNoKChjaGlsZCkgPT4gY2hpbGQuZGVsZXRlKCkpXG4gICAgY29udGFpbmVyLmNoaWxkcmVuID0gbmV3Q2hpbGRyZW5cbiAgfSxcbiAgLyoqXG4gICAqIFRoaXMgZnVuY3Rpb24gbGV0cyB5b3Ugc2hhcmUgc29tZSBjb250ZXh0IHdpdGggdGhlIG90aGVyIGZ1bmN0aW9ucyBpbiB0aGlzIEhvc3RDb25maWcuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGxldHMgeW91IHJldHVybiB0aGUgaW5pdGlhbCBob3N0IGNvbnRleHQgZnJvbSB0aGUgcm9vdCBvZiB0aGUgdHJlZS4gU2VlIGBnZXRDaGlsZEhvc3RDb250ZXh0YCBmb3IgdGhlIGV4cGxhbmF0aW9uIG9mIGhvc3QgY29udGV4dC5cbiAgICpcbiAgICogSWYgeW91IGRvbid0IGludGVuZCB0byB1c2UgaG9zdCBjb250ZXh0LCB5b3UgY2FuIHJldHVybiBgbnVsbGAuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcHBlbnMgKippbiB0aGUgcmVuZGVyIHBoYXNlKiouIERvIG5vdCBtdXRhdGUgdGhlIHRyZWUgZnJvbSBpdC5cbiAgICpcbiAgICogQHBhcmFtIHJvb3RDb250YWluZXJJbnN0YW5jZSBpcyBiYXNpY2FsbHkgdGhlIHJvb3QgZG9tIG5vZGUgeW91IHNwZWNpZnkgd2hpbGUgY2FsbGluZyByZW5kZXIuIFRoaXMgaXMgbW9zdCBjb21tb25seVxuICAgKiA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICAgKiBAcmV0dXJuIEEgY29udGV4dCBvYmplY3QgdGhhdCB5b3Ugd2lzaCB0byBwYXNzIHRvIGltbWVkaWF0ZSBjaGlsZC5cbiAgICovXG4gIGdldFJvb3RIb3N0Q29udGV4dChyb290Q29udGFpbmVySW5zdGFuY2UpOiBDb250YWluZXJDb250ZXh0IHtcbiAgICByZXR1cm4geyBja0VsZW1lbnQ6IHJvb3RDb250YWluZXJJbnN0YW5jZSB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIFRoaXMgZnVuY3Rpb24gcHJvdmlkZXMgYSB3YXkgdG8gYWNjZXNzIGNvbnRleHQgZnJvbSB0aGUgcGFyZW50IGFuZCBhbHNvIGEgd2F5IHRvIHBhc3Mgc29tZSBjb250ZXh0IHRvIHRoZSBpbW1lZGlhdGVcbiAgICogY2hpbGRyZW4gb2YgdGhlIGN1cnJlbnQgbm9kZS4gQ29udGV4dCBpcyBiYXNpY2FsbHkgYSByZWd1bGFyIG9iamVjdCBjb250YWluaW5nIHNvbWUgaW5mb3JtYXRpb24uXG4gICAqXG4gICAqIEhvc3QgY29udGV4dCBsZXRzIHlvdSB0cmFjayBzb21lIGluZm9ybWF0aW9uIGFib3V0IHdoZXJlIHlvdSBhcmUgaW4gdGhlIHRyZWUgc28gdGhhdCBpdCdzIGF2YWlsYWJsZSBpbnNpZGUgYGNyZWF0ZUluc3RhbmNlYCBhcyB0aGUgYGhvc3RDb250ZXh0YCBwYXJhbWV0ZXIuIEZvciBleGFtcGxlLCB0aGUgRE9NIHJlbmRlcmVyIHVzZXMgaXQgdG8gdHJhY2sgd2hldGhlciBpdCdzIGluc2lkZSBhbiBIVE1MIG9yIGFuIFNWRyB0cmVlLCBiZWNhdXNlIGBjcmVhdGVJbnN0YW5jZWAgaW1wbGVtZW50YXRpb24gbmVlZHMgdG8gYmUgZGlmZmVyZW50IGZvciB0aGVtLlxuICAgKlxuICAgKiBJZiB0aGUgbm9kZSBvZiB0aGlzIGB0eXBlYCBkb2VzIG5vdCBpbmZsdWVuY2UgdGhlIGNvbnRleHQgeW91IHdhbnQgdG8gcGFzcyBkb3duLCB5b3UgY2FuIHJldHVybiBgcGFyZW50SG9zdENvbnRleHRgLiBBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIHJldHVybiBhbnkgY3VzdG9tIG9iamVjdCByZXByZXNlbnRpbmcgdGhlIGluZm9ybWF0aW9uIHlvdSB3YW50IHRvIHBhc3MgZG93bi5cbiAgICpcbiAgICogSWYgeW91IGRvbid0IHdhbnQgdG8gZG8gYW55dGhpbmcgaGVyZSwgcmV0dXJuIGBwYXJlbnRIb3N0Q29udGV4dGAuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcHBlbnMgKippbiB0aGUgcmVuZGVyIHBoYXNlKiouIERvIG5vdCBtdXRhdGUgdGhlIHRyZWUgZnJvbSBpdC5cbiAgICpcbiAgICogQHBhcmFtIHBhcmVudEhvc3RDb250ZXh0IENvbnRleHQgZnJvbSBwYXJlbnQuIEV4YW1wbGU6IFRoaXMgd2lsbCBjb250YWluIHJvb3RDb250ZXh0IGZvciB0aGUgaW1tZWRpYXRlIGNoaWxkIG9mXG4gICAqIHJvb3Rob3N0LlxuICAgKiBAcGFyYW0gdHlwZSBUaGlzIGNvbnRhaW5zIHRoZSB0eXBlIG9mIGZpYmVyIGkuZSwg4oCYZGl24oCZLCDigJhzcGFu4oCZLCDigJhw4oCZLCDigJhpbnB1dOKAmSBldGMuXG4gICAqIEBwYXJhbSByb290Q29udGFpbmVySW5zdGFuY2Ugcm9vdEluc3RhbmNlIGlzIGJhc2ljYWxseSB0aGUgcm9vdCBkb20gbm9kZSB5b3Ugc3BlY2lmeSB3aGlsZSBjYWxsaW5nIHJlbmRlci4gVGhpcyBpc1xuICAgKiBtb3N0IGNvbW1vbmx5IDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gICAqIEByZXR1cm4gQSBjb250ZXh0IG9iamVjdCB0aGF0IHlvdSB3aXNoIHRvIHBhc3MgdG8gaW1tZWRpYXRlIGNoaWxkLlxuICAgKi9cbiAgZ2V0Q2hpbGRIb3N0Q29udGV4dChwYXJlbnRIb3N0Q29udGV4dCwgdHlwZSwgcm9vdENvbnRhaW5lckluc3RhbmNlKTogQ29udGFpbmVyQ29udGV4dCB7XG4gICAgcmV0dXJuIHBhcmVudEhvc3RDb250ZXh0XG4gIH0sXG5cbiAgLyoqXG4gICAqIElmIHRoZSBmdW5jdGlvbiByZXR1cm5zIHRydWUsIHRoZSB0ZXh0IHdvdWxkIGJlIGNyZWF0ZWQgaW5zaWRlIHRoZSBob3N0IGVsZW1lbnQgYW5kIG5vIG5ldyB0ZXh0IGVsZW1lbnQgd291bGQgYmVcbiAgICogY3JlYXRlZCBzZXBhcmF0ZWx5LlxuICAgKlxuICAgKiBJZiB0aGlzIHJldHVybmVkIHRydWUsIHRoZSBuZXh0IGNhbGwgd291bGQgYmUgdG8gY3JlYXRlSW5zdGFuY2UgZm9yIHRoZSBjdXJyZW50IGVsZW1lbnQgYW5kIHRyYXZlcnNhbCB3b3VsZCBzdG9wIGF0XG4gICAqIHRoaXMgbm9kZSAoY2hpbGRyZW4gb2YgdGhpcyBlbGVtZW50IHdvbnQgYmUgdHJhdmVyc2VkKS5cbiAgICpcbiAgICogSWYgaXQgcmV0dXJucyBmYWxzZSwgZ2V0Q2hpbGRIb3N0Q29udGV4dCBhbmQgc2hvdWxkU2V0VGV4dENvbnRlbnQgd2lsbCBiZSBjYWxsZWQgb24gdGhlIGNoaWxkIGVsZW1lbnRzIGFuZCBpdCB3aWxsXG4gICAqIGNvbnRpbnVlIHRpbGwgc2hvdWxkU2V0VGV4dENvbnRlbnQgcmV0dXJucyB0cnVlIG9yIGlmIHRoZSByZWN1cnNpb24gcmVhY2hlcyB0aGUgbGFzdCB0cmVlIGVuZHBvaW50IHdoaWNoIHVzdWFsbHkgaXNcbiAgICogYSB0ZXh0IG5vZGUuIFdoZW4gaXQgcmVhY2hlcyB0aGUgbGFzdCBsZWFmIHRleHQgbm9kZSBpdCB3aWxsIGNhbGwgY3JlYXRlVGV4dEluc3RhbmNlXG4gICAqXG4gICAqIFNvbWUgdGFyZ2V0IHBsYXRmb3JtcyBzdXBwb3J0IHNldHRpbmcgYW4gaW5zdGFuY2UncyB0ZXh0IGNvbnRlbnQgd2l0aG91dCBtYW51YWxseSBjcmVhdGluZyBhIHRleHQgbm9kZS4gRm9yIGV4YW1wbGUsIGluIHRoZSBET00sIHlvdSBjYW4gc2V0IGBub2RlLnRleHRDb250ZW50YCBpbnN0ZWFkIG9mIGNyZWF0aW5nIGEgdGV4dCBub2RlIGFuZCBhcHBlbmRpbmcgaXQuXG4gICAqXG4gICAqIElmIHlvdSByZXR1cm4gYHRydWVgIGZyb20gdGhpcyBtZXRob2QsIFJlYWN0IHdpbGwgYXNzdW1lIHRoYXQgdGhpcyBub2RlJ3MgY2hpbGRyZW4gYXJlIHRleHQsIGFuZCB3aWxsIG5vdCBjcmVhdGUgbm9kZXMgZm9yIHRoZW0uIEl0IHdpbGwgaW5zdGVhZCByZWx5IG9uIHlvdSB0byBoYXZlIGZpbGxlZCB0aGF0IHRleHQgZHVyaW5nIGBjcmVhdGVJbnN0YW5jZWAuIFRoaXMgaXMgYSBwZXJmb3JtYW5jZSBvcHRpbWl6YXRpb24uIEZvciBleGFtcGxlLCB0aGUgRE9NIHJlbmRlcmVyIHJldHVybnMgYHRydWVgIG9ubHkgaWYgYHR5cGVgIGlzIGEga25vd24gdGV4dC1vbmx5IHBhcmVudCAobGlrZSBgJ3RleHRhcmVhJ2ApIG9yIGlmIGBwcm9wcy5jaGlsZHJlbmAgaGFzIGEgYCdzdHJpbmcnYCB0eXBlLiBJZiB5b3UgcmV0dXJuIGB0cnVlYCwgeW91IHdpbGwgbmVlZCB0byBpbXBsZW1lbnQgYHJlc2V0VGV4dENvbnRlbnRgIHRvby5cbiAgICpcbiAgICogSWYgeW91IGRvbid0IHdhbnQgdG8gZG8gYW55dGhpbmcgaGVyZSwgeW91IHNob3VsZCByZXR1cm4gYGZhbHNlYC5cbiAgICpcbiAgICogVGhpcyBtZXRob2QgaGFwcGVucyAqKmluIHRoZSByZW5kZXIgcGhhc2UqKi4gRG8gbm90IG11dGF0ZSB0aGUgdHJlZSBmcm9tIGl0LlxuICAgKlxuICAgKiBAcGFyYW0gdHlwZSBUaGlzIGNvbnRhaW5zIHRoZSB0eXBlIG9mIGZpYmVyIGkuZSwg4oCYZGl24oCZLCDigJhzcGFu4oCZLCDigJhw4oCZLCDigJhpbnB1dOKAmSBldGMuXG4gICAqIEBwYXJhbSBwcm9wcyBDb250YWlucyB0aGUgcHJvcHMgcGFzc2VkIHRvIHRoZSBob3N0IHJlYWN0IGVsZW1lbnQuXG4gICAqIEByZXR1cm4gVGhpcyBzaG91bGQgYmUgYSBib29sZWFuIHZhbHVlLlxuICAgKi9cbiAgc2hvdWxkU2V0VGV4dENvbnRlbnQodHlwZSwgcHJvcHMpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdHlwZSA9PT0gJ2NrLXRleHQnIHx8IHR5cGUgPT09ICdjay1wYXJhZ3JhcGgnXG4gIH0sXG5cbiAgLyoqXG4gICAqIEhlcmUgd2Ugc3BlY2lmeSBob3cgc2hvdWxkIHJlbmRlcmVyIGhhbmRsZSB0aGUgdGV4dCBjb250ZW50XG4gICAqXG4gICAqIFNhbWUgYXMgYGNyZWF0ZUluc3RhbmNlYCwgYnV0IGZvciB0ZXh0IG5vZGVzLiBJZiB5b3VyIHJlbmRlcmVyIGRvZXNuJ3Qgc3VwcG9ydCB0ZXh0IG5vZGVzLCB5b3UgY2FuIHRocm93IGhlcmUuXG4gICAqXG4gICAqIEBwYXJhbSB0ZXh0IGNvbnRhaW5zIHRoZSB0ZXh0IHN0cmluZyB0aGF0IG5lZWRzIHRvIGJlIHJlbmRlcmVkLlxuICAgKiBAcGFyYW0gcm9vdENvbnRhaW5lckluc3RhbmNlIHJvb3QgZG9tIG5vZGUgeW91IHNwZWNpZnkgd2hpbGUgY2FsbGluZyByZW5kZXIuIFRoaXMgaXMgbW9zdCBjb21tb25seVxuICAgKiA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICAgKiBAcGFyYW0gaG9zdENvbnRleHQgY29udGFpbnMgdGhlIGNvbnRleHQgZnJvbSB0aGUgaG9zdCBub2RlIGVuY2xvc2luZyB0aGlzIHRleHQgbm9kZS4gRm9yIGV4YW1wbGUsIGluIHRoZSBjYXNlIG9mXG4gICAqIDxwPkhlbGxvPC9wPjogY3VycmVudEhvc3RDb250ZXh0IGZvciBIZWxsbyB0ZXh0IG5vZGUgd2lsbCBiZSBob3N0IGNvbnRleHQgb2YgcC5cbiAgICogQHBhcmFtIGludGVybmFsSW5zdGFuY2VIYW5kbGUgVGhlIGZpYmVyIG5vZGUgZm9yIHRoZSB0ZXh0IGluc3RhbmNlLiBUaGlzIG1hbmFnZXMgd29yayBmb3IgdGhpcyBpbnN0YW5jZS5cbiAgICogQHJldHVybiBUaGlzIHNob3VsZCBiZSBhbiBhY3R1YWwgdGV4dCB2aWV3IGVsZW1lbnQuIEluIGNhc2Ugb2YgZG9tIGl0IHdvdWxkIGJlIGEgdGV4dE5vZGUuXG4gICAqL1xuICBjcmVhdGVUZXh0SW5zdGFuY2UoXG4gICAgdGV4dCxcbiAgICByb290Q29udGFpbmVySW5zdGFuY2UsXG4gICAgaG9zdENvbnRleHQsXG4gICAgaW50ZXJuYWxJbnN0YW5jZUhhbmRsZSxcbiAgKTogQ2tFbGVtZW50PCdjay10ZXh0Jz4gfCBDa0VsZW1lbnQ8J2NrLXBhcmFncmFwaCc+IHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZSB0ZXh0ICcke3RleHR9JyBtdXN0IGJlIHdyYXBwZWQgaW4gYSBjay10ZXh0IG9yIGNrLXBhcmFncmFwaCBlbGVtZW50LmApXG4gIH0sXG5cbiAgLyoqXG4gICAqIENyZWF0ZSBpbnN0YW5jZSBpcyBjYWxsZWQgb24gYWxsIGhvc3Qgbm9kZXMgZXhjZXB0IHRoZSBsZWFmIHRleHQgbm9kZXMuIFNvIHdlIHNob3VsZCByZXR1cm4gdGhlIGNvcnJlY3Qgdmlld1xuICAgKiBlbGVtZW50IGZvciBlYWNoIGhvc3QgdHlwZSBoZXJlLiBXZSBhcmUgYWxzbyBzdXBwb3NlZCB0byB0YWtlIGNhcmUgb2YgdGhlIHByb3BzIHNlbnQgdG8gdGhlIGhvc3QgZWxlbWVudC4gRm9yXG4gICAqIGV4YW1wbGU6IHNldHRpbmcgdXAgb25DbGlja0xpc3RlbmVycyBvciBzZXR0aW5nIHVwIHN0eWxpbmcgZXRjLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBzaG91bGQgcmV0dXJuIGEgbmV3bHkgY3JlYXRlZCBub2RlLiBGb3IgZXhhbXBsZSwgdGhlIERPTSByZW5kZXJlciB3b3VsZCBjYWxsIGBkb2N1bWVudC5jcmVhdGVFbGVtZW50KHR5cGUpYCBoZXJlIGFuZCB0aGVuIHNldCB0aGUgcHJvcGVydGllcyBmcm9tIGBwcm9wc2AuXG4gICAqXG4gICAqIFlvdSBjYW4gdXNlIGByb290Q29udGFpbmVyYCB0byBhY2Nlc3MgdGhlIHJvb3QgY29udGFpbmVyIGFzc29jaWF0ZWQgd2l0aCB0aGF0IHRyZWUuIEZvciBleGFtcGxlLCBpbiB0aGUgRE9NIHJlbmRlcmVyLCB0aGlzIGlzIHVzZWZ1bCB0byBnZXQgdGhlIGNvcnJlY3QgYGRvY3VtZW50YCByZWZlcmVuY2UgdGhhdCB0aGUgcm9vdCBiZWxvbmdzIHRvLlxuICAgKlxuICAgKiBUaGUgYGhvc3RDb250ZXh0YCBwYXJhbWV0ZXIgbGV0cyB5b3Uga2VlcCB0cmFjayBvZiBzb21lIGluZm9ybWF0aW9uIGFib3V0IHlvdXIgY3VycmVudCBwbGFjZSBpbiB0aGUgdHJlZS4gVG8gbGVhcm4gbW9yZSBhYm91dCBpdCwgc2VlIGBnZXRDaGlsZEhvc3RDb250ZXh0YCBiZWxvdy5cbiAgICpcbiAgICogVGhlIGBpbnRlcm5hbEhhbmRsZWAgZGF0YSBzdHJ1Y3R1cmUgaXMgbWVhbnQgdG8gYmUgb3BhcXVlLiBJZiB5b3UgYmVuZCB0aGUgcnVsZXMgYW5kIHJlbHkgb24gaXRzIGludGVybmFsIGZpZWxkcywgYmUgYXdhcmUgdGhhdCBpdCBtYXkgY2hhbmdlIHNpZ25pZmljYW50bHkgYmV0d2VlbiB2ZXJzaW9ucy4gWW91J3JlIHRha2luZyBvbiBhZGRpdGlvbmFsIG1haW50ZW5hbmNlIHJpc2sgYnkgcmVhZGluZyBmcm9tIGl0LCBhbmQgZ2l2aW5nIHVwIGFsbCBndWFyYW50ZWVzIGlmIHlvdSB3cml0ZSBzb21ldGhpbmcgdG8gaXQuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcHBlbnMgKippbiB0aGUgcmVuZGVyIHBoYXNlKiouIEl0IGNhbiAoYW5kIHVzdWFsbHkgc2hvdWxkKSBtdXRhdGUgdGhlIG5vZGUgaXQgaGFzIGp1c3QgY3JlYXRlZCBiZWZvcmUgcmV0dXJuaW5nIGl0LCBidXQgaXQgbXVzdCBub3QgbW9kaWZ5IGFueSBvdGhlciBub2Rlcy4gSXQgbXVzdCBub3QgcmVnaXN0ZXIgYW55IGV2ZW50IGhhbmRsZXJzIG9uIHRoZSBwYXJlbnQgdHJlZS4gVGhpcyBpcyBiZWNhdXNlIGFuIGluc3RhbmNlIGJlaW5nIGNyZWF0ZWQgZG9lc24ndCBndWFyYW50ZWUgaXQgd291bGQgYmUgcGxhY2VkIGluIHRoZSB0cmVlIOKAlCBpdCBjb3VsZCBiZSBsZWZ0IHVudXNlZCBhbmQgbGF0ZXIgY29sbGVjdGVkIGJ5IEdDLiBJZiB5b3UgbmVlZCB0byBkbyBzb21ldGhpbmcgd2hlbiBhbiBpbnN0YW5jZSBpcyBkZWZpbml0ZWx5IGluIHRoZSB0cmVlLCBsb29rIGF0IGBjb21taXRNb3VudGAgaW5zdGVhZC5cbiAgICpcbiAgICogQHBhcmFtIHR5cGUgVGhpcyBjb250YWlucyB0aGUgdHlwZSBvZiBmaWJlciBpLmUsIOKAmGRpduKAmSwg4oCYc3BhbuKAmSwg4oCYcOKAmSwg4oCYaW5wdXTigJkgZXRjLlxuICAgKiBAcGFyYW0gcHJvcHMgIENvbnRhaW5zIHRoZSBwcm9wcyBwYXNzZWQgdG8gdGhlIGhvc3QgcmVhY3QgZWxlbWVudC5cbiAgICogQHBhcmFtIHJvb3RDb250YWluZXJJbnN0YW5jZSBSb290IGRvbSBub2RlIHlvdSBzcGVjaWZ5IHdoaWxlIGNhbGxpbmcgcmVuZGVyLiBUaGlzIGlzIG1vc3QgY29tbW9ubHkgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgICogQHBhcmFtIGhvc3RDb250ZXh0IGNvbnRhaW5zIHRoZSBjb250ZXh0IGZyb20gdGhlIHBhcmVudCBub2RlIGVuY2xvc2luZyB0aGlzIG5vZGUuIFRoaXMgaXMgdGhlIHJldHVybiB2YWx1ZSBmcm9tIGdldENoaWxkSG9zdENvbnRleHQgb2YgdGhlIHBhcmVudCBub2RlLlxuICAgKiBAcGFyYW0gaW50ZXJuYWxJbnN0YW5jZUhhbmRsZSBUaGUgZmliZXIgbm9kZSBmb3IgdGhlIHRleHQgaW5zdGFuY2UuIFRoaXMgbWFuYWdlcyB3b3JrIGZvciB0aGlzIGluc3RhbmNlLlxuICAgKi9cbiAgY3JlYXRlSW5zdGFuY2UodHlwZSwgcHJvcHMsIHJvb3RDb250YWluZXJJbnN0YW5jZSwgaG9zdENvbnRleHQsIGludGVybmFsSW5zdGFuY2VIYW5kbGUpIHtcbiAgICByZXR1cm4gY3JlYXRlQ2tFbGVtZW50KHR5cGUsIHByb3BzLCBob3N0Q29udGV4dC5ja0VsZW1lbnQuY2FudmFzS2l0KVxuICB9LFxuXG4gIC8qKlxuICAgKiBIZXJlIHdlIHdpbGwgYXR0YWNoIHRoZSBjaGlsZCBkb20gbm9kZSB0byB0aGUgcGFyZW50IG9uIHRoZSBpbml0aWFsIHJlbmRlciBwaGFzZS4gVGhpcyBtZXRob2Qgd2lsbCBiZSBjYWxsZWQgZm9yXG4gICAqIGVhY2ggY2hpbGQgb2YgdGhlIGN1cnJlbnQgbm9kZS5cbiAgICpcbiAgICogVGhpcyBtZXRob2Qgc2hvdWxkIG11dGF0ZSB0aGUgYHBhcmVudEluc3RhbmNlYCBhbmQgYWRkIHRoZSBjaGlsZCB0byBpdHMgbGlzdCBvZiBjaGlsZHJlbi4gRm9yIGV4YW1wbGUsIGluIHRoZSBET00gdGhpcyB3b3VsZCB0cmFuc2xhdGUgdG8gYSBgcGFyZW50SW5zdGFuY2UuYXBwZW5kQ2hpbGQoY2hpbGQpYCBjYWxsLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBoYXBwZW5zICoqaW4gdGhlIHJlbmRlciBwaGFzZSoqLiBJdCBjYW4gbXV0YXRlIGBwYXJlbnRJbnN0YW5jZWAgYW5kIGBjaGlsZGAsIGJ1dCBpdCBtdXN0IG5vdCBtb2RpZnkgYW55IG90aGVyIG5vZGVzLiBJdCdzIGNhbGxlZCB3aGlsZSB0aGUgdHJlZSBpcyBzdGlsbCBiZWluZyBidWlsdCB1cCBhbmQgbm90IGNvbm5lY3RlZCB0byB0aGUgYWN0dWFsIHRyZWUgb24gdGhlIHNjcmVlbi5cbiAgICpcbiAgICogQHBhcmFtIHBhcmVudEluc3RhbmNlIFRoZSBjdXJyZW50IG5vZGUgaW4gdGhlIHRyYXZlcnNhbFxuICAgKiBAcGFyYW0gY2hpbGQgVGhlIGNoaWxkIGRvbSBub2RlIG9mIHRoZSBjdXJyZW50IG5vZGUuXG4gICAqL1xuICBhcHBlbmRJbml0aWFsQ2hpbGQocGFyZW50SW5zdGFuY2UsIGNoaWxkKSB7XG4gICAgaWYgKGlzQ29udGFpbmVyRWxlbWVudChwYXJlbnRJbnN0YW5jZSkpIHtcbiAgICAgIHBhcmVudEluc3RhbmNlLmNoaWxkcmVuLnB1c2goY2hpbGQpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQnVnPyBUcnlpbmcgdG8gYXBwZW5kIGEgY2hpbGQgdG8gYSBwYXJlbnQgdGhhdCBpcyBub3QgYSBjb250YWluZXIuJylcbiAgICB9XG4gIH0sXG5cbiAgLyoqXG4gICAqIEluIGNhc2Ugb2YgcmVhY3QgbmF0aXZlIHJlbmRlcmVyLCB0aGlzIGZ1bmN0aW9uIGRvZXMgbm90aGluZyBidXQgcmV0dXJuIGZhbHNlLlxuICAgKlxuICAgKiBJbiBjYXNlIG9mIHJlYWN0LWRvbSwgdGhpcyBhZGRzIGRlZmF1bHQgZG9tIHByb3BlcnRpZXMgc3VjaCBhcyBldmVudCBsaXN0ZW5lcnMsIGV0Yy5cbiAgICogRm9yIGltcGxlbWVudGluZyBhdXRvIGZvY3VzIGZvciBjZXJ0YWluIGlucHV0IGVsZW1lbnRzIChhdXRvZm9jdXMgY2FuIGhhcHBlbiBvbmx5XG4gICAqIGFmdGVyIHJlbmRlciBpcyBkb25lKSwgcmVhY3QtZG9tIHNlbmRzIHJldHVybiB0eXBlIGFzIHRydWUuIFRoaXMgcmVzdWx0cyBpbiBjb21taXRNb3VudFxuICAgKiBtZXRob2QgZm9yIHRoaXMgZWxlbWVudCB0byBiZSBjYWxsZWQuIFRoZSBjb21taXRNb3VudCB3aWxsIGJlIGNhbGxlZCBvbmx5IGlmIGFuIGVsZW1lbnRcbiAgICogcmV0dXJucyB0cnVlIGluIGZpbmFsaXplSW5pdGlhbENoaWxkcmVuIGFuZCBhZnRlciB0aGUgYWxsIGVsZW1lbnRzIG9mIHRoZSB0cmVlIGhhcyBiZWVuXG4gICAqIHJlbmRlcmVkIChldmVuIGFmdGVyIHJlc2V0QWZ0ZXJDb21taXQpLlxuICAgKlxuICAgKiBJbiB0aGlzIG1ldGhvZCwgeW91IGNhbiBwZXJmb3JtIHNvbWUgZmluYWwgbXV0YXRpb25zIG9uIHRoZSBgaW5zdGFuY2VgLiBVbmxpa2Ugd2l0aCBgY3JlYXRlSW5zdGFuY2VgLCBieSB0aGUgdGltZSBgZmluYWxpemVJbml0aWFsQ2hpbGRyZW5gIGlzIGNhbGxlZCwgYWxsIHRoZSBpbml0aWFsIGNoaWxkcmVuIGhhdmUgYWxyZWFkeSBiZWVuIGFkZGVkIHRvIHRoZSBgaW5zdGFuY2VgLCBidXQgdGhlIGluc3RhbmNlIGl0c2VsZiBoYXMgbm90IHlldCBiZWVuIGNvbm5lY3RlZCB0byB0aGUgdHJlZSBvbiB0aGUgc2NyZWVuLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBoYXBwZW5zICoqaW4gdGhlIHJlbmRlciBwaGFzZSoqLiBJdCBjYW4gbXV0YXRlIGBpbnN0YW5jZWAsIGJ1dCBpdCBtdXN0IG5vdCBtb2RpZnkgYW55IG90aGVyIG5vZGVzLiBJdCdzIGNhbGxlZCB3aGlsZSB0aGUgdHJlZSBpcyBzdGlsbCBiZWluZyBidWlsdCB1cCBhbmQgbm90IGNvbm5lY3RlZCB0byB0aGUgYWN0dWFsIHRyZWUgb24gdGhlIHNjcmVlbi5cbiAgICpcbiAgICogVGhlcmUgaXMgYSBzZWNvbmQgcHVycG9zZSB0byB0aGlzIG1ldGhvZC4gSXQgbGV0cyB5b3Ugc3BlY2lmeSB3aGV0aGVyIHRoZXJlIGlzIHNvbWUgd29yayB0aGF0IG5lZWRzIHRvIGhhcHBlbiB3aGVuIHRoZSBub2RlIGlzIGNvbm5lY3RlZCB0byB0aGUgdHJlZSBvbiB0aGUgc2NyZWVuLiBJZiB5b3UgcmV0dXJuIGB0cnVlYCwgdGhlIGluc3RhbmNlIHdpbGwgcmVjZWl2ZSBhIGBjb21taXRNb3VudGAgY2FsbCBsYXRlci4gU2VlIGl0cyBkb2N1bWVudGF0aW9uIGJlbG93LlxuICAgKlxuICAgKiBJZiB5b3UgZG9uJ3Qgd2FudCB0byBkbyBhbnl0aGluZyBoZXJlLCB5b3Ugc2hvdWxkIHJldHVybiBgZmFsc2VgLlxuICAgKlxuICAgKiBAcGFyYW0gcGFyZW50SW5zdGFuY2UgVGhlIGluc3RhbmNlIGlzIHRoZSBkb20gZWxlbWVudCBhZnRlciBhcHBlbmRJbml0aWFsQ2hpbGQuXG4gICAqIEBwYXJhbSB0eXBlIFRoaXMgY29udGFpbnMgdGhlIHR5cGUgb2YgZmliZXIgaS5lLCDigJhkaXbigJksIOKAmHNwYW7igJksIOKAmHDigJksIOKAmGlucHV04oCZIGV0Yy5cbiAgICogQHBhcmFtIHByb3BzIENvbnRhaW5zIHRoZSBwcm9wcyBwYXNzZWQgdG8gdGhlIGhvc3QgcmVhY3QgZWxlbWVudC5cbiAgICogQHBhcmFtIHJvb3RDb250YWluZXJJbnN0YW5jZSByb290IGRvbSBub2RlIHlvdSBzcGVjaWZ5IHdoaWxlIGNhbGxpbmcgcmVuZGVyLiBUaGlzIGlzIG1vc3QgY29tbW9ubHkgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgICogQHBhcmFtIGhvc3RDb250ZXh0IGNvbnRhaW5zIHRoZSBjb250ZXh0IGZyb20gdGhlIHBhcmVudCBub2RlIGVuY2xvc2luZyB0aGlzIG5vZGUuIFRoaXMgaXMgdGhlIHJldHVybiB2YWx1ZSBmcm9tIGdldENoaWxkSG9zdENvbnRleHQgb2YgdGhlIHBhcmVudCBub2RlLlxuICAgKi9cbiAgZmluYWxpemVJbml0aWFsQ2hpbGRyZW4ocGFyZW50SW5zdGFuY2UsIHR5cGUsIHByb3BzLCByb290Q29udGFpbmVySW5zdGFuY2UsIGhvc3RDb250ZXh0KSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH0sXG4gIGZpbmFsaXplQ29udGFpbmVyQ2hpbGRyZW4oY29udGFpbmVyOiBDa0VsZW1lbnRDb250YWluZXI8YW55PiwgbmV3Q2hpbGRyZW46IENrRWxlbWVudDxhbnk+W10pIHt9LFxuXG4gIC8qKlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCB3aGVuIHdlIGhhdmUgbWFkZSBhIGluLW1lbW9yeSByZW5kZXIgdHJlZSBvZiBhbGwgdGhlIHZpZXdzIChSZW1lbWJlciB3ZSBhcmUgeWV0IHRvIGF0dGFjaFxuICAgKiBpdCB0aGUgdGhlIGFjdHVhbCByb290IGRvbSBub2RlKS4gSGVyZSB3ZSBjYW4gZG8gYW55IHByZXBhcmF0aW9uIHRoYXQgbmVlZHMgdG8gYmUgZG9uZSBvbiB0aGUgcm9vdENvbnRhaW5lciBiZWZvcmVcbiAgICogYXR0YWNoaW5nIHRoZSBpbiBtZW1vcnkgcmVuZGVyIHRyZWUuIEZvciBleGFtcGxlOiBJbiB0aGUgY2FzZSBvZiByZWFjdC1kb20sIGl0IGtlZXBzIHRyYWNrIG9mIGFsbCB0aGUgY3VycmVudGx5XG4gICAqIGZvY3VzZWQgZWxlbWVudHMsIGRpc2FibGVkIGV2ZW50cyB0ZW1wb3JhcmlseSwgZXRjLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBsZXRzIHlvdSBzdG9yZSBzb21lIGluZm9ybWF0aW9uIGJlZm9yZSBSZWFjdCBzdGFydHMgbWFraW5nIGNoYW5nZXMgdG8gdGhlIHRyZWUgb24gdGhlIHNjcmVlbi4gRm9yIGV4YW1wbGUsIHRoZSBET00gcmVuZGVyZXIgc3RvcmVzIHRoZSBjdXJyZW50IHRleHQgc2VsZWN0aW9uIHNvIHRoYXQgaXQgY2FuIGxhdGVyIHJlc3RvcmUgaXQuIFRoaXMgbWV0aG9kIGlzIG1pcnJvcmVkIGJ5IGByZXNldEFmdGVyQ29tbWl0YC5cbiAgICpcbiAgICogRXZlbiBpZiB5b3UgZG9uJ3Qgd2FudCB0byBkbyBhbnl0aGluZyBoZXJlLCB5b3UgbmVlZCB0byByZXR1cm4gYG51bGxgIGZyb20gaXQuXG4gICAqXG4gICAqIEBwYXJhbSBjb250YWluZXJJbmZvIHJvb3QgZG9tIG5vZGUgeW91IHNwZWNpZnkgd2hpbGUgY2FsbGluZyByZW5kZXIuIFRoaXMgaXMgbW9zdCBjb21tb25seSA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICAgKi9cbiAgcHJlcGFyZUZvckNvbW1pdChjb250YWluZXJJbmZvKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfSxcblxuICAvKipcbiAgICogVGhpcyBmdW5jdGlvbiBnZXRzIGV4ZWN1dGVkIGFmdGVyIHRoZSBpbm1lbW9yeSB0cmVlIGhhcyBiZWVuIGF0dGFjaGVkIHRvIHRoZSByb290IGRvbSBlbGVtZW50LiBIZXJlIHdlIGNhbiBkbyBhbnlcbiAgICogcG9zdCBhdHRhY2ggb3BlcmF0aW9ucyB0aGF0IG5lZWRzIHRvIGJlIGRvbmUuIEZvciBleGFtcGxlOiByZWFjdC1kb20gcmUtZW5hYmxlZCBldmVudHMgd2hpY2ggd2VyZSB0ZW1wb3JhcmlseVxuICAgKiBkaXNhYmxlZCBpbiBwcmVwYXJlRm9yQ29tbWl0IGFuZCByZWZvY3VzZXMgZWxlbWVudHMsIGV0Yy5cbiAgICpcbiAgICogVGhpcyBtZXRob2QgaXMgY2FsbGVkIHJpZ2h0IGFmdGVyIFJlYWN0IGhhcyBwZXJmb3JtZWQgdGhlIHRyZWUgbXV0YXRpb25zLiBZb3UgY2FuIHVzZSBpdCB0byByZXN0b3JlIHNvbWV0aGluZyB5b3UndmUgc3RvcmVkIGluIGBwcmVwYXJlRm9yQ29tbWl0YCDigJQgZm9yIGV4YW1wbGUsIHRleHQgc2VsZWN0aW9uLlxuICAgKlxuICAgKiBZb3UgY2FuIGxlYXZlIGl0IGVtcHR5LlxuICAgKlxuICAgKiBAcGFyYW0gY29udGFpbmVySW5mbyByb290IGRvbSBub2RlIHlvdSBzcGVjaWZ5IHdoaWxlIGNhbGxpbmcgcmVuZGVyLiBUaGlzIGlzIG1vc3QgY29tbW9ubHkgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgICovXG4gIHJlc2V0QWZ0ZXJDb21taXQoY29udGFpbmVySW5mbykge1xuICAgIC8vIFRPRE8gaW5zdGVhZCBvZiByZS1yZW5kZXJpbmcgZXZlcnl0aGluZywgb25seSByZXJlbmRlciBkaXJ0eSBub2Rlcz9cbiAgICBjb250YWluZXJJbmZvLmNoaWxkcmVuLmZvckVhY2goKGNoaWxkKSA9PiBjaGlsZC5yZW5kZXIoY29udGFpbmVySW5mbykpXG4gICAgY29udGFpbmVySW5mby5wcm9wcy5yZW5kZXJDYWxsYmFjaz8uKClcbiAgfSxcblxuICBnZXRQdWJsaWNJbnN0YW5jZShpbnN0YW5jZTogQ2tFbGVtZW50PGFueT4gfCBDa0VsZW1lbnQ8J2NrLXRleHQnPik6IFNrT2JqZWN0UmVmPGFueT4ge1xuICAgIHJldHVybiBpbnN0YW5jZS5za09iamVjdFxuICB9LFxuXG4gIC8qKlxuICAgKiBSZWFjdCBjYWxscyB0aGlzIG1ldGhvZCBzbyB0aGF0IHlvdSBjYW4gY29tcGFyZSB0aGUgcHJldmlvdXMgYW5kIHRoZSBuZXh0IHByb3BzLCBhbmQgZGVjaWRlIHdoZXRoZXIgeW91IG5lZWQgdG8gdXBkYXRlIHRoZSB1bmRlcmx5aW5nIGluc3RhbmNlIG9yIG5vdC4gSWYgeW91IGRvbid0IG5lZWQgdG8gdXBkYXRlIGl0LCByZXR1cm4gYG51bGxgLiBJZiB5b3UgbmVlZCB0byB1cGRhdGUgaXQsIHlvdSBjYW4gcmV0dXJuIGFuIGFyYml0cmFyeSBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBjaGFuZ2VzIHRoYXQgbmVlZCB0byBoYXBwZW4uIFRoZW4gaW4gYGNvbW1pdFVwZGF0ZWAgeW91IHdvdWxkIG5lZWQgdG8gYXBwbHkgdGhvc2UgY2hhbmdlcyB0byB0aGUgaW5zdGFuY2UuXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGhhcHBlbnMgKippbiB0aGUgcmVuZGVyIHBoYXNlKiouIEl0IHNob3VsZCBvbmx5ICpjYWxjdWxhdGUqIHRoZSB1cGRhdGUg4