ng2-tree
Version:
angular2 component for visualizing data that can be naturally represented as a tree
562 lines • 69.9 kB
JavaScript
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