UNPKG

ng2-tree

Version:

angular2 component for visualizing data that can be naturally represented as a tree

562 lines 69.9 kB
import { defaultsDeep, get, has, includes, isEmpty, isFunction, isNil, omit, once, size, trim } from './utils/fn.utils'; import { FoldingType, TreeModelSettings, TreeStatus } from './tree.types'; import * as uuidv4 from 'uuid/v4'; import { Observable, of } from 'rxjs'; var ChildrenLoadingState; (function (ChildrenLoadingState) { ChildrenLoadingState[ChildrenLoadingState["NotStarted"] = 0] = "NotStarted"; ChildrenLoadingState[ChildrenLoadingState["Loading"] = 1] = "Loading"; ChildrenLoadingState[ChildrenLoadingState["Completed"] = 2] = "Completed"; })(ChildrenLoadingState || (ChildrenLoadingState = {})); export class Tree { _children; _loadChildren; _childrenLoadingState = ChildrenLoadingState.NotStarted; _childrenAsyncOnce = once(() => { return new Observable((observer) => { setTimeout(() => { this._childrenLoadingState = ChildrenLoadingState.Loading; this._loadChildren((children) => { this._children = (children || []).map((child) => new Tree(child, this)); this._childrenLoadingState = ChildrenLoadingState.Completed; observer.next(this.children); observer.complete(); }); }); }); }); node; parent; // STATIC METHODS ---------------------------------------------------------------------------------------------------- /** * Check that value passed is not empty (it doesn't consist of only whitespace symbols). * @param {string} value - A value that should be checked. * @returns {boolean} - A flag indicating that value is empty or not. * @static */ static isValueEmpty(value) { return isEmpty(trim(value)); } /** * Check whether a given value can be considered RenamableNode. * @param {any} value - A value to check. * @returns {boolean} - A flag indicating whether given value is Renamable node or not. * @static */ static isRenamable(value) { return (has(value, 'setName') && isFunction(value.setName) && (has(value, 'toString') && isFunction(value.toString) && value.toString !== Object.toString)); } static cloneTreeShallow(origin) { const tree = new Tree(Object.assign({}, origin.node)); tree._children = origin._children; return tree; } static applyNewValueToRenamable(value, newValue) { const renamableValue = Object.assign({}, value); renamableValue.setName(newValue); return renamableValue; } /** * Build an instance of Tree from an object implementing TreeModel interface. * @param {TreeModel} model - A model that is used to build a tree. * @param {Tree} [parent] - An optional parent if you want to build a tree from the model that should be a child of an existing Tree instance. * @param {boolean} [isBranch] - An option that makes a branch from created tree. Branch can have children. */ constructor(node, parent = null, isBranch = false) { this.buildTreeFromModel(node, parent, isBranch || Array.isArray(node.children)); } buildTreeFromModel(model, parent, isBranch) { this.parent = parent; this.node = Object.assign(omit(model, 'children'), { settings: TreeModelSettings.merge(model, get(parent, 'node')) }, { emitLoadNextLevel: model.emitLoadNextLevel === true }); if (isFunction(this.node.loadChildren)) { this._loadChildren = this.node.loadChildren; } else { get(model, 'children', []).forEach((child, index) => { this._addChild(new Tree(child, this), index); }); } if (!Array.isArray(this._children)) { this._children = this.node.loadChildren || isBranch ? [] : null; } } hasDeferredChildren() { return typeof this._loadChildren === 'function'; } /* Setting the children loading state to Loading since a request was dispatched to the client */ loadingChildrenRequested() { this._childrenLoadingState = ChildrenLoadingState.Loading; } /** * Check whether children of the node are being loaded. * Makes sense only for nodes that define `loadChildren` function. * @returns {boolean} A flag indicating that children are being loaded. */ childrenAreBeingLoaded() { return this._childrenLoadingState === ChildrenLoadingState.Loading; } /** * Check whether children of the node were loaded. * Makes sense only for nodes that define `loadChildren` function. * @returns {boolean} A flag indicating that children were loaded. */ childrenWereLoaded() { return this._childrenLoadingState === ChildrenLoadingState.Completed; } canLoadChildren() { return (this._childrenLoadingState === ChildrenLoadingState.NotStarted && this.foldingType === FoldingType.Expanded && !!this._loadChildren); } /** * Check whether children of the node should be loaded and not loaded yet. * Makes sense only for nodes that define `loadChildren` function. * @returns {boolean} A flag indicating that children should be loaded for the current node. */ childrenShouldBeLoaded() { return !this.childrenWereLoaded() && (!!this._loadChildren || this.node.emitLoadNextLevel === true); } /** * Get children of the current tree. * @returns {Tree[]} The children of the current tree. */ get children() { return this._children; } /** * By getting value from this property you start process of loading node's children using `loadChildren` function. * Once children are loaded `loadChildren` function won't be called anymore and loaded for the first time children are emitted in case of subsequent calls. * @returns {Observable<Tree[]>} An observable which emits children once they are loaded. */ get childrenAsync() { if (this.canLoadChildren()) { return this._childrenAsyncOnce(); } return of(this.children); } /** * By calling this method you start process of loading node's children using `loadChildren` function. */ reloadChildren() { if (this.childrenShouldBeLoaded()) { this._childrenLoadingState = ChildrenLoadingState.Loading; this._loadChildren((children) => { this._children = children && children.map((child) => new Tree(child, this)); this._childrenLoadingState = ChildrenLoadingState.Completed; }); } } /** * By calling this method you will remove all current children of a treee and create new. */ setChildren(children) { this._children = children && children.map((child) => new Tree(child, this)); if (this.childrenShouldBeLoaded()) { this._childrenLoadingState = ChildrenLoadingState.Completed; } } /** * Create a new node in the current tree. * @param {boolean} isBranch - A flag that indicates whether a new node should be a "Branch". "Leaf" node will be created by default * @param {TreeModel} model - Tree model of the new node which will be inserted. Empty node will be created by default and it will fire edit mode of this node * @returns {Tree} A newly created child node. */ createNode(isBranch, model = { value: '' }) { const tree = new Tree(model, this, isBranch); if (!model.id) { tree.markAsNew(); } tree.id = tree.id || uuidv4(); if (this.childrenShouldBeLoaded() && !(this.childrenAreBeingLoaded() || this.childrenWereLoaded())) { return null; } if (this.isLeaf()) { return this.addSibling(tree); } else { return this.addChild(tree); } } /** * Get the value of the current node * @returns {(string|RenamableNode)} The value of the node. */ get value() { return this.node.value; } set checked(checked) { this.node.settings = Object.assign({}, this.node.settings, { checked }); } get checked() { return !!get(this.node.settings, 'checked'); } get checkedChildren() { return this.hasLoadedChildern() ? this.children.filter(child => child.checked) : []; } set selectionAllowed(selectionAllowed) { this.node.settings = Object.assign({}, this.node.settings, { selectionAllowed }); } get selectionAllowed() { const value = get(this.node.settings, 'selectionAllowed'); return isNil(value) ? true : !!value; } hasLoadedChildern() { return !isEmpty(this.children); } loadedChildrenAmount() { return size(this.children); } checkedChildrenAmount() { return size(this.checkedChildren); } /** * Set the value of the current node * @param {(string|RenamableNode)} value - The new value of the node. */ set value(value) { if (typeof value !== 'string' && !Tree.isRenamable(value)) { return; } const stringifiedValue = '' + value; if (Tree.isRenamable(this.value)) { this.node.value = Tree.applyNewValueToRenamable(this.value, stringifiedValue); } else { this.node.value = Tree.isValueEmpty(stringifiedValue) ? this.node.value : stringifiedValue; } } /** * Add a sibling node for the current node. This won't work if the current node is a root. * @param {Tree} sibling - A node that should become a sibling. * @param [number] position - Position in which sibling will be inserted. By default it will be inserted at the last position in a parent. * @returns {Tree} A newly inserted sibling, or null if you are trying to make a sibling for the root. */ addSibling(sibling, position) { if (Array.isArray(get(this.parent, 'children'))) { return this.parent.addChild(sibling, position); } return null; } /** * Add a child node for the current node. * @param {Tree} child - A node that should become a child. * @param [number] position - Position in which child will be inserted. By default it will be inserted at the last position in a parent. * @returns {Tree} A newly inserted child. */ addChild(child, position) { const newborn = this._addChild(Tree.cloneTreeShallow(child), position); this._setFoldingType(); if (this.isNodeCollapsed()) { this.switchFoldingType(); } return newborn; } _addChild(child, position = size(this._children) || 0) { child.parent = this; if (Array.isArray(this._children)) { this._children.splice(position, 0, child); } else { this._children = [child]; } return child; } /** * Swap position of the current node with the given sibling. If node passed as a parameter is not a sibling - nothing happens. * @param {Tree} sibling - A sibling with which current node shold be swapped. */ swapWithSibling(sibling) { if (!this.hasSibling(sibling)) { return; } const siblingIndex = sibling.positionInParent; const thisTreeIndex = this.positionInParent; this.parent._children[siblingIndex] = this; this.parent._children[thisTreeIndex] = sibling; } /** * Get a node's position in its parent. * @returns {number} The position inside a parent. */ get positionInParent() { if (this.isRoot()) { return -1; } return this.parent.children ? this.parent.children.indexOf(this) : -1; } /** * Check whether or not this tree is static. * @returns {boolean} A flag indicating whether or not this tree is static. */ isStatic() { return get(this.node.settings, 'static', false); } /** * Check whether or not this tree has a left menu. * @returns {boolean} A flag indicating whether or not this tree has a left menu. */ hasLeftMenu() { return !get(this.node.settings, 'static', false) && get(this.node.settings, 'leftMenu', false); } /** * Check whether or not this tree has a right menu. * @returns {boolean} A flag indicating whether or not this tree has a right menu. */ hasRightMenu() { return !get(this.node.settings, 'static', false) && get(this.node.settings, 'rightMenu', false); } /** * Check whether this tree is "Leaf" or not. * @returns {boolean} A flag indicating whether or not this tree is a "Leaf". */ isLeaf() { return !this.isBranch(); } /** * Get menu items of the current tree. * @returns {NodeMenuItem[]} The menu items of the current tree. */ get menuItems() { return get(this.node.settings, 'menuItems'); } /** * Check whether or not this tree has a custom menu. * @returns {boolean} A flag indicating whether or not this tree has a custom menu. */ hasCustomMenu() { return !this.isStatic() && !!get(this.node.settings, 'menuItems', false); } /** * Check whether this tree is "Branch" or not. "Branch" is a node that has children. * @returns {boolean} A flag indicating whether or not this tree is a "Branch". */ isBranch() { return this.node.emitLoadNextLevel === true || Array.isArray(this._children); } /** * Check whether this tree has children. * @returns {boolean} A flag indicating whether or not this tree has children. */ hasChildren() { return !isEmpty(this._children) || this.childrenShouldBeLoaded(); } /** * Check whether this tree is a root or not. The root is the tree (node) that doesn't have parent (or technically its parent is null). * @returns {boolean} A flag indicating whether or not this tree is the root. */ isRoot() { return isNil(this.parent); } /** * Check whether provided tree is a sibling of the current tree. Sibling trees (nodes) are the trees that have the same parent. * @param {Tree} tree - A tree that should be tested on a siblingness. * @returns {boolean} A flag indicating whether or not provided tree is the sibling of the current one. */ hasSibling(tree) { return !this.isRoot() && includes(this.parent.children, tree); } /** * Check whether provided tree is a child of the current tree. * This method tests that provided tree is a <strong>direct</strong> child of the current tree. * @param {Tree} tree - A tree that should be tested (child candidate). * @returns {boolean} A flag indicating whether provided tree is a child or not. */ hasChild(tree) { return includes(this._children, tree); } /** * Remove given tree from the current tree. * The given tree will be removed only in case it is a direct child of the current tree (@see {@link hasChild}). * @param {Tree} tree - A tree that should be removed. */ removeChild(tree) { if (!this.hasChildren()) { return; } const childIndex = this._children.findIndex((child) => child === tree); if (childIndex >= 0) { this._children.splice(childIndex, 1); } this._setFoldingType(); } /** * Remove current tree from its parent. */ removeItselfFromParent() { if (!this.parent) { return; } this.parent.removeChild(this); } /** * Switch folding type of the current tree. "Leaf" node cannot switch its folding type cause it doesn't have children, hence nothing to fold. * If node is a "Branch" and it is expanded, then by invoking current method state of the tree should be switched to "collapsed" and vice versa. */ switchFoldingType() { if (this.isLeaf() || !this.hasChildren()) { return; } this.disableCollapseOnInit(); this.node._foldingType = this.isNodeExpanded() ? FoldingType.Collapsed : FoldingType.Expanded; } /** * Check that tree is expanded. * @returns {boolean} A flag indicating whether current tree is expanded. Always returns false for the "Leaf" tree and for an empty tree. */ isNodeExpanded() { return this.foldingType === FoldingType.Expanded; } /** * Check that tree is collapsed. * @returns {boolean} A flag indicating whether current tree is collapsed. Always returns false for the "Leaf" tree and for an empty tree. */ isNodeCollapsed() { return this.foldingType === FoldingType.Collapsed; } /** * Set a current folding type: expanded, collapsed or leaf. */ _setFoldingType() { if (this.childrenShouldBeLoaded()) { this.node._foldingType = FoldingType.Collapsed; } else if (this._children && !isEmpty(this._children)) { this.node._foldingType = this.isCollapsedOnInit() ? FoldingType.Collapsed : FoldingType.Expanded; } else if (Array.isArray(this._children)) { this.node._foldingType = FoldingType.Empty; } else { this.node._foldingType = FoldingType.Leaf; } } /** * Get a current folding type: expanded, collapsed or leaf. * @returns {FoldingType} A folding type of the current tree. */ get foldingType() { if (!this.node._foldingType) { this._setFoldingType(); } return this.node._foldingType; } /** * Get a css class for element which displayes folding state - expanded, collapsed or leaf * @returns {string} A string icontaining css class (classes) */ get foldingCssClass() { return this.getCssClassesFromSettings() || this.foldingType.cssClass; } getCssClassesFromSettings() { if (!this.node._foldingType) { this._setFoldingType(); } if (this.node._foldingType === FoldingType.Collapsed) { return get(this.node.settings, 'cssClasses.collapsed', null); } else if (this.node._foldingType === FoldingType.Expanded) { return get(this.node.settings, 'cssClasses.expanded', null); } else if (this.node._foldingType === FoldingType.Empty) { return get(this.node.settings, 'cssClasses.empty', null); } return get(this.node.settings, 'cssClasses.leaf', null); } /** * Get a html template to render before every node's name. * @returns {string} A string representing a html template. */ get nodeTemplate() { return this.getTemplateFromSettings(); } getTemplateFromSettings() { if (this.isLeaf()) { return get(this.node.settings, 'templates.leaf', ''); } else { return get(this.node.settings, 'templates.node', ''); } } /** * Get a html template to render for an element activatin left menu of a node. * @returns {string} A string representing a html template. */ get leftMenuTemplate() { if (this.hasLeftMenu()) { return get(this.node.settings, 'templates.leftMenu', '<span></span>'); } return ''; } disableCollapseOnInit() { if (this.node.settings) { this.node.settings.isCollapsedOnInit = false; } } isCollapsedOnInit() { return !!get(this.node.settings, 'isCollapsedOnInit'); } keepNodesInDOM() { return get(this.node.settings, 'keepNodesInDOM'); } /** * Check that current tree is newly created (added by user via menu for example). Tree that was built from the TreeModel is not marked as new. * @returns {boolean} A flag whether the tree is new. */ isNew() { return this.node._status === TreeStatus.New; } get id() { return get(this.node, 'id'); } set id(id) { this.node.id = id; } /** * Mark current tree as new (@see {@link isNew}). */ markAsNew() { this.node._status = TreeStatus.New; } /** * Check that current tree is being renamed (it is in the process of its value renaming initiated by a user). * @returns {boolean} A flag whether the tree is being renamed. */ isBeingRenamed() { return this.node._status === TreeStatus.IsBeingRenamed; } /** * Mark current tree as being renamed (@see {@link isBeingRenamed}). */ markAsBeingRenamed() { this.node._status = TreeStatus.IsBeingRenamed; } /** * Check that current tree is modified (for example it was renamed). * @returns {boolean} A flag whether the tree is modified. */ isModified() { return this.node._status === TreeStatus.Modified; } /** * Mark current tree as modified (@see {@link isModified}). */ markAsModified() { this.node._status = TreeStatus.Modified; } /** * Makes a clone of an underlying TreeModel instance * @returns {TreeModel} a clone of an underlying TreeModel instance */ toTreeModel() { const model = defaultsDeep(this.isLeaf() ? {} : { children: [] }, this.node); if (this.children) { this.children.forEach(child => { model.children.push(child.toTreeModel()); }); } return model; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJlZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy90cmVlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFeEgsT0FBTyxFQUVMLFdBQVcsRUFHWCxpQkFBaUIsRUFDakIsVUFBVSxFQUNYLE1BQU0sY0FBYyxDQUFDO0FBR3RCLE9BQU8sS0FBSyxNQUFNLE1BQU0sU0FBUyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxVQUFVLEVBQVksRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRWhELElBQUssb0JBSUo7QUFKRCxXQUFLLG9CQUFvQjtJQUN2QiwyRUFBVSxDQUFBO0lBQ1YscUVBQU8sQ0FBQTtJQUNQLHlFQUFTLENBQUE7QUFDWCxDQUFDLEVBSkksb0JBQW9CLEtBQXBCLG9CQUFvQixRQUl4QjtBQUVELE1BQU0sT0FBTyxJQUFJO0lBQ1AsU0FBUyxDQUFTO0lBQ2xCLGFBQWEsQ0FBMEI7SUFDdkMscUJBQXFCLEdBQXlCLG9CQUFvQixDQUFDLFVBQVUsQ0FBQztJQUM5RSxrQkFBa0IsR0FBNkIsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUMvRCxPQUFPLElBQUksVUFBVSxDQUFDLENBQUMsUUFBMEIsRUFBRSxFQUFFO1lBQ25ELFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQ2QsSUFBSSxDQUFDLHFCQUFxQixHQUFHLG9CQUFvQixDQUFDLE9BQU8sQ0FBQztnQkFDMUQsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLFFBQXFCLEVBQUUsRUFBRTtvQkFDM0MsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxLQUFn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QUFDO1NBQ0o7UUFFRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlZmF1bHRzRGVlcCwgZ2V0LCBoYXMsIGluY2x1ZGVzLCBpc0VtcHR5LCBpc0Z1bmN0aW9uLCBpc05pbCwgb21pdCwgb25jZSwgc2l6ZSwgdHJpbSB9IGZyb20gJy4vdXRpbHMvZm4udXRpbHMnO1xuXG5pbXBvcnQge1xuICBDaGlsZHJlbkxvYWRpbmdGdW5jdGlvbixcbiAgRm9sZGluZ1R5cGUsXG4gIFJlbmFtYWJsZU5vZGUsXG4gIFRyZWVNb2RlbCxcbiAgVHJlZU1vZGVsU2V0dGluZ3MsXG4gIFRyZWVTdGF0dXNcbn0gZnJvbSAnLi90cmVlLnR5cGVzJztcbmltcG9ydCB7IE5vZGVNZW51SXRlbSB9IGZyb20gJy4vbWVudS9ub2RlLW1lbnUuY29tcG9uZW50JztcblxuaW1wb3J0ICogYXMgdXVpZHY0IGZyb20gJ3V1aWQvdjQnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgT2JzZXJ2ZXIsIG9mIH0gZnJvbSAncnhqcyc7XG5cbmVudW0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUge1xuICBOb3RTdGFydGVkLFxuICBMb2FkaW5nLFxuICBDb21wbGV0ZWRcbn1cblxuZXhwb3J0IGNsYXNzIFRyZWUge1xuICBwcml2YXRlIF9jaGlsZHJlbjogVHJlZVtdO1xuICBwcml2YXRlIF9sb2FkQ2hpbGRyZW46IENoaWxkcmVuTG9hZGluZ0Z1bmN0aW9uO1xuICBwcml2YXRlIF9jaGlsZHJlbkxvYWRpbmdTdGF0ZTogQ2hpbGRyZW5Mb2FkaW5nU3RhdGUgPSBDaGlsZHJlbkxvYWRpbmdTdGF0ZS5Ob3RTdGFydGVkO1xuICBwcml2YXRlIF9jaGlsZHJlbkFzeW5jT25jZTogKCkgPT4gT2JzZXJ2YWJsZTxUcmVlW10+ID0gb25jZSgoKSA9PiB7XG4gICAgcmV0dXJuIG5ldyBPYnNlcnZhYmxlKChvYnNlcnZlcjogT2JzZXJ2ZXI8VHJlZVtdPikgPT4ge1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuX2NoaWxkcmVuTG9hZGluZ1N0YXRlID0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUuTG9hZGluZztcbiAgICAgICAgdGhpcy5fbG9hZENoaWxkcmVuKChjaGlsZHJlbjogVHJlZU1vZGVsW10pID0+IHtcbiAgICAgICAgICB0aGlzLl9jaGlsZHJlbiA9IChjaGlsZHJlbiB8fCBbXSkubWFwKChjaGlsZDogVHJlZU1vZGVsKSA9PiBuZXcgVHJlZShjaGlsZCwgdGhpcykpO1xuICAgICAgICAgIHRoaXMuX2NoaWxkcmVuTG9hZGluZ1N0YXRlID0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUuQ29tcGxldGVkO1xuICAgICAgICAgIG9ic2VydmVyLm5leHQodGhpcy5jaGlsZHJlbik7XG4gICAgICAgICAgb2JzZXJ2ZXIuY29tcGxldGUoKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgcHVibGljIG5vZGU6IFRyZWVNb2RlbDtcbiAgcHVibGljIHBhcmVudDogVHJlZTtcblxuICAvLyBTVEFUSUMgTUVUSE9EUyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgLyoqXG4gICAqIENoZWNrIHRoYXQgdmFsdWUgcGFzc2VkIGlzIG5vdCBlbXB0eSAoaXQgZG9lc24ndCBjb25zaXN0IG9mIG9ubHkgd2hpdGVzcGFjZSBzeW1ib2xzKS5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gQSB2YWx1ZSB0aGF0IHNob3VsZCBiZSBjaGVja2VkLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBBIGZsYWcgaW5kaWNhdGluZyB0aGF0IHZhbHVlIGlzIGVtcHR5IG9yIG5vdC5cbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcHVibGljIHN0YXRpYyBpc1ZhbHVlRW1wdHkodmFsdWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpc0VtcHR5KHRyaW0odmFsdWUpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayB3aGV0aGVyIGEgZ2l2ZW4gdmFsdWUgY2FuIGJlIGNvbnNpZGVyZWQgUmVuYW1hYmxlTm9kZS5cbiAgICogQHBhcmFtIHthbnl9IHZhbHVlIC0gQSB2YWx1ZSB0byBjaGVjay5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBnaXZlbiB2YWx1ZSBpcyBSZW5hbWFibGUgbm9kZSBvciBub3QuXG4gICAqIEBzdGF0aWNcbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgaXNSZW5hbWFibGUodmFsdWU6IGFueSk6IHZhbHVlIGlzIFJlbmFtYWJsZU5vZGUge1xuICAgIHJldHVybiAoXG4gICAgICBoYXModmFsdWUsICdzZXROYW1lJykgJiZcbiAgICAgIGlzRnVuY3Rpb24odmFsdWUuc2V0TmFtZSkgJiZcbiAgICAgIChoYXModmFsdWUsICd0b1N0cmluZycpICYmIGlzRnVuY3Rpb24odmFsdWUudG9TdHJpbmcpICYmIHZhbHVlLnRvU3RyaW5nICE9PSBPYmplY3QudG9TdHJpbmcpXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGNsb25lVHJlZVNoYWxsb3cob3JpZ2luOiBUcmVlKTogVHJlZSB7XG4gICAgY29uc3QgdHJlZSA9IG5ldyBUcmVlKE9iamVjdC5hc3NpZ24oe30sIG9yaWdpbi5ub2RlKSk7XG4gICAgdHJlZS5fY2hpbGRyZW4gPSBvcmlnaW4uX2NoaWxkcmVuO1xuICAgIHJldHVybiB0cmVlO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgYXBwbHlOZXdWYWx1ZVRvUmVuYW1hYmxlKHZhbHVlOiBSZW5hbWFibGVOb2RlLCBuZXdWYWx1ZTogc3RyaW5nKTogUmVuYW1hYmxlTm9kZSB7XG4gICAgY29uc3QgcmVuYW1hYmxlVmFsdWU6IFJlbmFtYWJsZU5vZGUgPSBPYmplY3QuYXNzaWduKHt9LCB2YWx1ZSBhcyBSZW5hbWFibGVOb2RlKTtcbiAgICByZW5hbWFibGVWYWx1ZS5zZXROYW1lKG5ld1ZhbHVlKTtcbiAgICByZXR1cm4gcmVuYW1hYmxlVmFsdWU7XG4gIH1cblxuICAvKipcbiAgICogQnVpbGQgYW4gaW5zdGFuY2Ugb2YgVHJlZSBmcm9tIGFuIG9iamVjdCBpbXBsZW1lbnRpbmcgVHJlZU1vZGVsIGludGVyZmFjZS5cbiAgICogQHBhcmFtIHtUcmVlTW9kZWx9IG1vZGVsIC0gQSBtb2RlbCB0aGF0IGlzIHVzZWQgdG8gYnVpbGQgYSB0cmVlLlxuICAgKiBAcGFyYW0ge1RyZWV9IFtwYXJlbnRdIC0gQW4gb3B0aW9uYWwgcGFyZW50IGlmIHlvdSB3YW50IHRvIGJ1aWxkIGEgdHJlZSBmcm9tIHRoZSBtb2RlbCB0aGF0IHNob3VsZCBiZSBhIGNoaWxkIG9mIGFuIGV4aXN0aW5nIFRyZWUgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzQnJhbmNoXSAtIEFuIG9wdGlvbiB0aGF0IG1ha2VzIGEgYnJhbmNoIGZyb20gY3JlYXRlZCB0cmVlLiBCcmFuY2ggY2FuIGhhdmUgY2hpbGRyZW4uXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3Iobm9kZTogVHJlZU1vZGVsLCBwYXJlbnQ6IFRyZWUgPSBudWxsLCBpc0JyYW5jaDogYm9vbGVhbiA9IGZhbHNlKSB7XG4gICAgdGhpcy5idWlsZFRyZWVGcm9tTW9kZWwobm9kZSwgcGFyZW50LCBpc0JyYW5jaCB8fCBBcnJheS5pc0FycmF5KG5vZGUuY2hpbGRyZW4pKTtcbiAgfVxuXG4gIHByaXZhdGUgYnVpbGRUcmVlRnJvbU1vZGVsKG1vZGVsOiBUcmVlTW9kZWwsIHBhcmVudDogVHJlZSwgaXNCcmFuY2g6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLnBhcmVudCA9IHBhcmVudDtcbiAgICB0aGlzLm5vZGUgPSBPYmplY3QuYXNzaWduKFxuICAgICAgb21pdChtb2RlbCwgJ2NoaWxkcmVuJykgYXMgVHJlZU1vZGVsLFxuICAgICAgeyBzZXR0aW5nczogVHJlZU1vZGVsU2V0dGluZ3MubWVyZ2UobW9kZWwsIGdldChwYXJlbnQsICdub2RlJykpIH0sXG4gICAgICB7IGVtaXRMb2FkTmV4dExldmVsOiBtb2RlbC5lbWl0TG9hZE5leHRMZXZlbCA9PT0gdHJ1ZSB9XG4gICAgKSBhcyBUcmVlTW9kZWw7XG5cbiAgICBpZiAoaXNGdW5jdGlvbih0aGlzLm5vZGUubG9hZENoaWxkcmVuKSkge1xuICAgICAgdGhpcy5fbG9hZENoaWxkcmVuID0gdGhpcy5ub2RlLmxvYWRDaGlsZHJlbjtcbiAgICB9IGVsc2Uge1xuICAgICAgZ2V0KG1vZGVsLCAnY2hpbGRyZW4nLCBbXSkuZm9yRWFjaCgoY2hpbGQ6IFRyZWVNb2RlbCwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgICB0aGlzLl9hZGRDaGlsZChuZXcgVHJlZShjaGlsZCwgdGhpcyksIGluZGV4KTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmICghQXJyYXkuaXNBcnJheSh0aGlzLl9jaGlsZHJlbikpIHtcbiAgICAgIHRoaXMuX2NoaWxkcmVuID0gdGhpcy5ub2RlLmxvYWRDaGlsZHJlbiB8fCBpc0JyYW5jaCA/IFtdIDogbnVsbDtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgaGFzRGVmZXJyZWRDaGlsZHJlbigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdHlwZW9mIHRoaXMuX2xvYWRDaGlsZHJlbiA9PT0gJ2Z1bmN0aW9uJztcbiAgfVxuICAvKiBTZXR0aW5nIHRoZSBjaGlsZHJlbiBsb2FkaW5nIHN0YXRlIHRvIExvYWRpbmcgc2luY2UgYSByZXF1ZXN0IHdhcyBkaXNwYXRjaGVkIHRvIHRoZSBjbGllbnQgKi9cbiAgcHVibGljIGxvYWRpbmdDaGlsZHJlblJlcXVlc3RlZCgpOiB2b2lkIHtcbiAgICB0aGlzLl9jaGlsZHJlbkxvYWRpbmdTdGF0ZSA9IENoaWxkcmVuTG9hZGluZ1N0YXRlLkxvYWRpbmc7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgd2hldGhlciBjaGlsZHJlbiBvZiB0aGUgbm9kZSBhcmUgYmVpbmcgbG9hZGVkLlxuICAgKiBNYWtlcyBzZW5zZSBvbmx5IGZvciBub2RlcyB0aGF0IGRlZmluZSBgbG9hZENoaWxkcmVuYCBmdW5jdGlvbi5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IEEgZmxhZyBpbmRpY2F0aW5nIHRoYXQgY2hpbGRyZW4gYXJlIGJlaW5nIGxvYWRlZC5cbiAgICovXG4gIHB1YmxpYyBjaGlsZHJlbkFyZUJlaW5nTG9hZGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9jaGlsZHJlbkxvYWRpbmdTdGF0ZSA9PT0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUuTG9hZGluZztcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayB3aGV0aGVyIGNoaWxkcmVuIG9mIHRoZSBub2RlIHdlcmUgbG9hZGVkLlxuICAgKiBNYWtlcyBzZW5zZSBvbmx5IGZvciBub2RlcyB0aGF0IGRlZmluZSBgbG9hZENoaWxkcmVuYCBmdW5jdGlvbi5cbiAgICogQHJldHVybnMge2Jvb2xlYW59IEEgZmxhZyBpbmRpY2F0aW5nIHRoYXQgY2hpbGRyZW4gd2VyZSBsb2FkZWQuXG4gICAqL1xuICBwdWJsaWMgY2hpbGRyZW5XZXJlTG9hZGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLl9jaGlsZHJlbkxvYWRpbmdTdGF0ZSA9PT0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUuQ29tcGxldGVkO1xuICB9XG5cbiAgcHJpdmF0ZSBjYW5Mb2FkQ2hpbGRyZW4oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRoaXMuX2NoaWxkcmVuTG9hZGluZ1N0YXRlID09PSBDaGlsZHJlbkxvYWRpbmdTdGF0ZS5Ob3RTdGFydGVkICYmXG4gICAgICB0aGlzLmZvbGRpbmdUeXBlID09PSBGb2xkaW5nVHlwZS5FeHBhbmRlZCAmJlxuICAgICAgISF0aGlzLl9sb2FkQ2hpbGRyZW5cbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIHdoZXRoZXIgY2hpbGRyZW4gb2YgdGhlIG5vZGUgc2hvdWxkIGJlIGxvYWRlZCBhbmQgbm90IGxvYWRlZCB5ZXQuXG4gICAqIE1ha2VzIHNlbnNlIG9ubHkgZm9yIG5vZGVzIHRoYXQgZGVmaW5lIGBsb2FkQ2hpbGRyZW5gIGZ1bmN0aW9uLlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gQSBmbGFnIGluZGljYXRpbmcgdGhhdCBjaGlsZHJlbiBzaG91bGQgYmUgbG9hZGVkIGZvciB0aGUgY3VycmVudCBub2RlLlxuICAgKi9cbiAgcHVibGljIGNoaWxkcmVuU2hvdWxkQmVMb2FkZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICF0aGlzLmNoaWxkcmVuV2VyZUxvYWRlZCgpICYmICghIXRoaXMuX2xvYWRDaGlsZHJlbiB8fCB0aGlzLm5vZGUuZW1pdExvYWROZXh0TGV2ZWwgPT09IHRydWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBjaGlsZHJlbiBvZiB0aGUgY3VycmVudCB0cmVlLlxuICAgKiBAcmV0dXJucyB7VHJlZVtdfSBUaGUgY2hpbGRyZW4gb2YgdGhlIGN1cnJlbnQgdHJlZS5cbiAgICovXG4gIHB1YmxpYyBnZXQgY2hpbGRyZW4oKTogVHJlZVtdIHtcbiAgICByZXR1cm4gdGhpcy5fY2hpbGRyZW47XG4gIH1cblxuICAvKipcbiAgICogQnkgZ2V0dGluZyB2YWx1ZSBmcm9tIHRoaXMgcHJvcGVydHkgeW91IHN0YXJ0IHByb2Nlc3Mgb2YgbG9hZGluZyBub2RlJ3MgY2hpbGRyZW4gdXNpbmcgYGxvYWRDaGlsZHJlbmAgZnVuY3Rpb24uXG4gICAqIE9uY2UgY2hpbGRyZW4gYXJlIGxvYWRlZCBgbG9hZENoaWxkcmVuYCBmdW5jdGlvbiB3b24ndCBiZSBjYWxsZWQgYW55bW9yZSBhbmQgbG9hZGVkIGZvciB0aGUgZmlyc3QgdGltZSBjaGlsZHJlbiBhcmUgZW1pdHRlZCBpbiBjYXNlIG9mIHN1YnNlcXVlbnQgY2FsbHMuXG4gICAqIEByZXR1cm5zIHtPYnNlcnZhYmxlPFRyZWVbXT59IEFuIG9ic2VydmFibGUgd2hpY2ggZW1pdHMgY2hpbGRyZW4gb25jZSB0aGV5IGFyZSBsb2FkZWQuXG4gICAqL1xuICBwdWJsaWMgZ2V0IGNoaWxkcmVuQXN5bmMoKTogT2JzZXJ2YWJsZTxUcmVlW10+IHtcbiAgICBpZiAodGhpcy5jYW5Mb2FkQ2hpbGRyZW4oKSkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NoaWxkcmVuQXN5bmNPbmNlKCk7XG4gICAgfVxuICAgIHJldHVybiBvZih0aGlzLmNoaWxkcmVuKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCeSBjYWxsaW5nIHRoaXMgbWV0aG9kIHlvdSBzdGFydCBwcm9jZXNzIG9mIGxvYWRpbmcgbm9kZSdzIGNoaWxkcmVuIHVzaW5nIGBsb2FkQ2hpbGRyZW5gIGZ1bmN0aW9uLlxuICAgKi9cbiAgcHVibGljIHJlbG9hZENoaWxkcmVuKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNoaWxkcmVuU2hvdWxkQmVMb2FkZWQoKSkge1xuICAgICAgdGhpcy5fY2hpbGRyZW5Mb2FkaW5nU3RhdGUgPSBDaGlsZHJlbkxvYWRpbmdTdGF0ZS5Mb2FkaW5nO1xuICAgICAgdGhpcy5fbG9hZENoaWxkcmVuKChjaGlsZHJlbjogVHJlZU1vZGVsW10pID0+IHtcbiAgICAgICAgdGhpcy5fY2hpbGRyZW4gPSBjaGlsZHJlbiAmJiBjaGlsZHJlbi5tYXAoKGNoaWxkOiBUcmVlTW9kZWwpID0+IG5ldyBUcmVlKGNoaWxkLCB0aGlzKSk7XG4gICAgICAgIHRoaXMuX2NoaWxkcmVuTG9hZGluZ1N0YXRlID0gQ2hpbGRyZW5Mb2FkaW5nU3RhdGUuQ29tcGxldGVkO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEJ5IGNhbGxpbmcgdGhpcyBtZXRob2QgeW91IHdpbGwgcmVtb3ZlIGFsbCBjdXJyZW50IGNoaWxkcmVuIG9mIGEgdHJlZWUgYW5kIGNyZWF0ZSBuZXcuXG4gICAqL1xuICBwdWJsaWMgc2V0Q2hpbGRyZW4oY2hpbGRyZW46IEFycmF5PFRyZWVNb2RlbD4pOiB2b2lkIHtcbiAgICB0aGlzLl9jaGlsZHJlbiA9IGNoaWxkcmVuICYmIGNoaWxkcmVuLm1hcCgoY2hpbGQ6IFRyZWVNb2RlbCkgPT4gbmV3IFRyZWUoY2hpbGQsIHRoaXMp