ngx-bootstrap-treeview
Version:
Ngx Bootstrap Treeview - Simple library to visualize, search and interact with tree data
1,034 lines (1,016 loc) • 110 kB
JavaScript
import { Injectable, Inject, NgZone, Component, Input, ElementRef, Renderer2, ViewChild, Output, EventEmitter, NgModule, ViewChildren, ChangeDetectorRef, defineInjectable, inject } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { faSquare, faCheckSquare, faFolder, faFolderOpen, faMinus, faCheck } from '@fortawesome/free-solid-svg-icons';
import { trigger, style, transition, animate, keyframes } from '@angular/animations';
import { EVENT_MANAGER_PLUGINS, EventManager } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class Tree {
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class Leaf {
/**
* @param {?} tree
*/
constructor(tree) {
this.value = tree.value;
this.label = tree.label;
this.data = tree.data;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class LeafClickedEvent {
/**
* @param {?} leaf
* @param {?} selectedLeaves
*/
constructor(leaf, selectedLeaves) {
this.leaf = leaf;
this.selectedLeaves = selectedLeaves;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template TreeSourceType, LeafSourceType
*/
class NgxBootstrapTreeviewMapper {
/**
* @param {?} treeMap
* @param {?} leafMap
*/
constructor(treeMap, leafMap) {
this.treeMap = treeMap;
this.leafMap = leafMap;
}
/**
* @param {?} item
* @return {?}
*/
mapTree(item) {
const { value, label, children, leaves, data } = {
value: item[this.treeMap.value],
label: item[this.treeMap.label],
children: item[this.treeMap.children],
leaves: item[this.treeMap.leavesKey],
data: item
};
/** @type {?} */
const result = {
value,
label,
data,
children: [...children.map(child => this.mapTree(child)), ...leaves.map(leaf => this.mapLeaf(leaf))]
};
return result;
}
/**
* @param {?} item
* @return {?}
*/
mapLeaf(item) {
return {
value: item[this.leafMap.value],
label: item[this.leafMap.label],
data: item
};
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class ContextMenuService {
constructor() {
this.lastContextMenuEvent = new BehaviorSubject(null);
}
/**
* @param {?} event
* @return {?}
*/
fire(event) {
this.lastContextMenuEvent.next(event);
}
}
ContextMenuService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
ContextMenuService.ctorParameters = () => [];
/** @nocollapse */ ContextMenuService.ngInjectableDef = defineInjectable({ factory: function ContextMenuService_Factory() { return new ContextMenuService(); }, token: ContextMenuService, providedIn: "root" });
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class NgxBootstrapTreeviewComponent {
/**
* @param {?} _host
* @param {?} _renderer
* @param {?} _zone
* @param {?} _changeDetector
* @param {?} _contextMenuService
*/
constructor(_host, _renderer, _zone, _changeDetector, _contextMenuService) {
this._host = _host;
this._renderer = _renderer;
this._zone = _zone;
this._changeDetector = _changeDetector;
this._contextMenuService = _contextMenuService;
this.contextMenus = {
leafMenu: {},
branchMenu: {},
rootMenu: {}
};
this.disableSelectedElements = false;
this.emptyFolderLabel = 'This folder is empty';
this.isAnimationDisabled = false;
this.isFirstLevel = true;
// This one is true IF AND ONLY IF we're at the top level, be it a root or a branch.
// This is because a branch where isFirstLevel = true is not always our first treeview instance
this.isFirstInstance = true;
this.preselectedItems = [];
// Icons inputs
this.openedFolderIcon = faFolderOpen;
this.closedFolderIcon = faFolder;
this.unselectedLeafIcon = faSquare;
this.selectedLeafIcon = faCheckSquare;
this.anyChildrenSelectedIcon = faMinus;
this.allChildrenSelectedIcon = faCheck;
this.filterString = '';
this.matchBranches = true;
this.branchClicked = new EventEmitter();
this.leafClicked = new EventEmitter();
this.selectedLeaves = [];
}
/**
* @return {?}
*/
ngOnInit() {
// We throw an exception if we have item or items but no mapper indicating how to handle them
if (!this.mapper && (this.item || this.items)) {
throw new Error('"item" or "items" are invalid parameters if you don\'t provide a mapper');
}
// If we have a mapper, it will handle "item" and "items"
if (this.mapper) {
if (this.items) {
this.trees = this.items.map(item => this.mapper.mapTree(item));
}
if (this.item) {
this.tree = this.mapper.mapTree(this.item);
}
}
// If we want to display one or more trees
if (this.trees && this.trees.length > 1) {
this.isRoot = true;
this.isBranch = this.isLeaf = false;
// return;
}
else if (this.trees) {
// Otherwise, we have trees but with less than 2 elements, we assign it to tree
// So ngOnInit() can keep going normaly
this.tree = this.trees[0];
}
if (this.tree && (this.tree.children || this.tree.loadChildren)) {
this.isBranch = true;
}
else {
this.isBranch = false;
}
this._resetDisplayedData();
if (!this.isRoot) {
this.isLeaf = !this.isBranch;
}
this.leavesCount = this.countLeaves(this.tree);
if (this.preselectedItems) {
this.preselectedItems.forEach(value => {
this.select(value);
});
}
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
// The this.tree || this.trees is used to avoid anerror since ngOnChanges is called before ngOnInit()
if ('filterString' in changes && (this.displayedTree || this.displayedTrees)) {
this._zone.runOutsideAngular(() => {
if (this._filterTrigger) {
clearTimeout(this._filterTrigger);
this._filterTrigger = null;
}
this._filterTrigger = setTimeout(() => {
this._zone.run(() => {
if (this.displayedTrees) {
this.displayedTrees = this._filterTrees();
}
else if (this.displayedTree) {
this.displayedTree = this._filterTree(this.displayedTree);
}
this._changeDetector.detectChanges();
if (this.filterString) {
this.unfoldAll();
}
else {
this._resetDisplayedData();
this.foldAll();
}
});
}, 250);
});
}
}
/**
* @return {?}
*/
onClick() {
if (this.isLeaf) {
this.onLeafClicked();
}
if (this.isBranch) {
if (this.loggingService) {
this.loggingService.log(`🌴 Branche cliquée: ${this.tree.label}`);
}
this.onBranchClicked();
}
}
/**
* @param {?} leafClickedEvent
* @return {?}
*/
onChildLeafClicked(leafClickedEvent) {
if (this.loggingService && this.isBranch) {
this.loggingService.log(`➡ Event entrant dans le parent ${this.tree.label}:`, leafClickedEvent);
}
else if (this.loggingService && this.isRoot) {
this.loggingService.log(`➡ Event entrant dans la racine:`, leafClickedEvent);
}
// When a child leaf is clicked, we check our selectedLeaves to select or unselect the clicked one
if (!this._leafExistsIn(this.selectedLeaves, leafClickedEvent.leaf)) {
this._selectLeaf(leafClickedEvent.leaf);
}
else {
this._unselectLeaf(leafClickedEvent.leaf);
}
// Now that the leaf is selected/unselected, we merge our selectedLeaves with the ones of the event
leafClickedEvent.selectedLeaves = this.selectedLeaves;
if (this.isBranch && this.loggingService) {
this.loggingService.log(`⬅ Event sortant de ${this.tree.label} vers un parent:`, leafClickedEvent);
}
else if (this.loggingService && this.isRoot) {
this.loggingService.log(`⬅ Event sortant de la racine:`, leafClickedEvent);
}
this.leafClicked.emit(leafClickedEvent);
}
/**
* @return {?}
*/
onLeafClicked() {
if (this.loggingService) {
this.loggingService.log('🍂 Feuille cliqué:', this.tree.label);
}
if (!this.disableSelectedElements || !this.isOpened) {
this._leafToggle();
}
}
/**
* @param {?} tree
* @return {?}
*/
countLeaves(tree) {
/** @type {?} */
let leavesCount = 0;
if (tree && (!tree.children || tree.loadChildren)) {
leavesCount = 1;
}
else if (tree) {
tree.children.forEach(child => {
leavesCount += this.countLeaves(child);
});
}
return leavesCount;
}
/**
* @param {?} value
* @return {?}
*/
select(value) {
if (this.isLeaf && this.tree.value === value && !this.isOpened) {
this._leafToggle();
}
else if (this.isRoot || this.isBranch) {
this.children.forEach((child) => {
child.select(value);
});
}
}
/**
* @param {?} value
* @return {?}
*/
unselect(value) {
if (this.isLeaf && this.tree.value === value && this.isOpened) {
this._leafToggle();
}
else if (this.isRoot || this.isBranch) {
this.children.forEach((child) => {
child.unselect(value);
});
}
}
/**
* @return {?}
*/
onBranchClicked() {
this._branchToggle();
this.branchClicked.emit(this.tree);
}
/**
* @param {?} branch
* @return {?}
*/
onChildBranchClicked(branch) {
this.branchClicked.emit(branch);
if (this.rootsContainer) {
requestAnimationFrame(() => {
// We use requestAnimationFrame because we want this to be processed once rerendering is complete
this.rootsContainer.nativeElement.style.height = this.computeHeight() + 'px';
});
}
}
/**
* @param {?} event
* @return {?}
*/
onContextMenu(event) {
event.stopPropagation();
event.preventDefault();
this._contextMenuService.fire({
target: this.tree,
event: event
});
}
/**
* @return {?}
*/
onElementAdded() {
this.leavesCount = this.countLeaves(this.tree);
return true;
}
/**
* @param {?} event
* @return {?}
*/
onRootContextMenu(event) {
event.stopPropagation();
event.preventDefault();
this._contextMenuService.fire({
event,
target: null
});
}
/**
* @param {?=} id
* @return {?}
*/
fold(id) {
if (!id) {
this._fold();
}
else {
this._foldId(id);
}
}
/**
* @param {?=} id
* @return {?}
*/
unfold(id) {
if (!id) {
this._unfold();
}
else {
this._unfoldId(id);
}
}
/**
* @return {?}
*/
show() {
/** @type {?} */
const domElement = this._host.nativeElement;
this._renderer.removeClass(domElement, 'd-none');
}
/**
* @return {?}
*/
hide() {
/** @type {?} */
const domElement = this._host.nativeElement;
this._renderer.addClass(domElement, 'd-none');
}
/**
* @param {?} filterString
* @param {?=} item
* @return {?}
*/
filter(filterString, item) {
if (this.isRoot) {
this.children.forEach((child) => {
child.filter(this.filterString);
});
}
else {
if (!item) {
item = this.tree;
}
/** @type {?} */
const matchingElementsCount = this.countFilteredItems(filterString, item);
if (matchingElementsCount === 0) {
this.hide();
}
else {
this.show();
if (filterString !== '') {
this._unfold();
}
else {
this._fold();
}
}
}
}
/**
* @param {?} filterString
* @param {?} item
* @return {?}
*/
countFilteredItems(filterString, item) {
/** @type {?} */
let matchingElementsCount = 0;
if (item.children) {
if (!filterString.length) {
return 1;
}
matchingElementsCount = item.children.reduce((acc, child) => acc + this.countFilteredItems(filterString, child), 0);
}
else {
/** @type {?} */
const regex = new RegExp(filterString, 'i');
if (regex.test(item.label)) {
matchingElementsCount++;
}
}
return matchingElementsCount;
}
/**
* @return {?}
*/
unfoldAll() {
// A branch will unfold itself
if (this.isBranch && !this.isOpened) {
this._branchToggle();
this._changeDetector.detectChanges();
}
// If we're not a leaf, we unfold all of our children
if (!this.isLeaf) {
this.children.forEach((child) => {
child.unfoldAll();
});
}
}
/**
* @return {?}
*/
foldAll() {
// A branch will fold itself
if (this.isBranch && this.isOpened) {
this._branchToggle();
}
// If we're not a leaf, we unfold all of our children
if (!this.isLeaf) {
this.children.forEach((child) => {
child.foldAll();
});
}
}
/**
* @return {?}
*/
computeHeight() {
if (!this.isRoot) {
return this.treeview.nativeElement.scrollHeight;
}
return this.children.reduce((prevValue, currentValue) => {
return prevValue + currentValue.computeHeight();
}, 0);
}
/**
* @return {?}
*/
_unfold() {
if (this.isBranch && !this.isOpened) {
this._branchToggle();
}
}
/**
* @return {?}
*/
_fold() {
if (this.isBranch && this.isOpened) {
this._branchToggle();
}
}
/**
* @param {?} id
* @return {?}
*/
_unfoldId(id) {
if (this.isBranch && this.tree.value === id && !this.isOpened) {
this._branchToggle();
}
else if (this.isBranch && this.children.length) {
this.children.forEach((child) => {
child.unfold(id);
});
}
}
/**
* @param {?} id
* @return {?}
*/
_foldId(id) {
if (this.isBranch && this.tree.value === id && this.isOpened) {
this._branchToggle();
}
else if (this.isBranch && this.children.length) {
this.children.forEach((child) => {
child.fold(id);
});
}
}
/**
* @return {?}
*/
_leafToggle() {
this.isOpened = !this.isOpened;
/** @type {?} */
const leaf = new Leaf(this.tree);
if (this.isOpened) {
this._selectLeaf(leaf);
}
else {
this._unselectLeaf(leaf);
}
/** @type {?} */
const event = new LeafClickedEvent(leaf, this.selectedLeaves);
this.leafClicked.emit(event);
}
/**
* @return {?}
*/
_branchToggle() {
this.isOpened = !this.isOpened;
}
/**
* @param {?} leaf
* @return {?}
*/
_selectLeaf(leaf) {
if (!this.isRoot && this.loggingService) {
this.loggingService.log(`✔️ Feuille sélectionnée dans ${this.tree.label}:`, leaf);
}
else if (this.loggingService) {
this.loggingService.log(`✔️ Feuille sélectionnée dans la racine:`, leaf);
}
if (!this._leafExistsIn(this.selectedLeaves, leaf)) {
this.selectedLeaves = [...this.selectedLeaves, leaf];
}
}
/**
* @param {?} leaf
* @return {?}
*/
_unselectLeaf(leaf) {
if (!this.isRoot && this.loggingService) {
this.loggingService.log(`❌ Feuille désélectionnée dans ${this.tree.label}:`, leaf);
}
else if (this.loggingService) {
this.loggingService.log(`❌ Feuille désélectionnée dans la racine:`, leaf);
}
/** @type {?} */
const index = this._leafIndex(this.selectedLeaves, leaf);
if (index !== -1) {
this.selectedLeaves.splice(index, 1);
}
}
// Function used to check if a given leaf does exist in sleectedLeaves array
// We use this because of the new Leaf(), which causes reference comparison of Array.indexOf() not to work
/**
* @param {?} leaves
* @param {?} leaf
* @return {?}
*/
_leafIndex(leaves, leaf) {
/** @type {?} */
let result = -1;
leaves.forEach((selectedLeaf, index) => {
if (selectedLeaf.value === leaf.value && selectedLeaf.label === leaf.label) {
result = index;
}
});
return result;
}
/**
* @param {?} leaves
* @param {?} leaf
* @return {?}
*/
_leafExistsIn(leaves, leaf) {
return this._leafIndex(leaves, leaf) !== -1;
}
/**
* @param {?} tree
* @return {?}
*/
_copyTree(tree) {
/** @type {?} */
const isTree = !!tree.children;
/** @type {?} */
const result = {
value: tree.value,
label: tree.label,
data: tree.data
};
if (isTree) {
/** @type {?} */
const children = tree.children.map(child => this._copyTree(child));
return Object.assign({}, result, { children });
}
else {
return result;
}
}
/**
* @param {?} trees
* @return {?}
*/
_copyTrees(trees) {
return trees.map((tree) => this._copyTree(tree));
}
/**
* @param {?=} trees
* @param {?=} filterString
* @return {?}
*/
_filterTrees(trees = this.trees, filterString = this.filterString) {
/** @type {?} */
const copies = this._copyTrees(trees);
if (filterString !== '') {
/** @type {?} */
const displayedTrees = copies
.map(copy => {
return this._filterTree(copy, filterString);
})
.filter(filteredCopy => !!filteredCopy);
return displayedTrees;
}
else {
return copies;
}
}
// This method alters tree.children and returns true if any elements matched the filter string
/**
* @param {?} tree
* @param {?=} filterString
* @return {?}
*/
_filterTree(tree, filterString = this.filterString) {
/** @type {?} */
const regex = new RegExp(filterString, 'i');
if (!tree.children) {
// Leaf handling
return regex.test(tree.label) ? tree : null;
}
else if (tree.children && tree.children.length) {
if (this.matchBranches && regex.test(tree.label)) {
return tree;
}
// Non empty branches handling
tree.children = tree.children.map((child) => this._filterTree(child)).filter(child => !!child);
return tree.children.length ? tree : null;
}
return null;
}
/**
* @return {?}
*/
_resetDisplayedData() {
if (this.tree) {
this.displayedTree = this._copyTree(this.tree);
}
else if (this.trees) {
this.displayedTrees = this._copyTrees(this.trees);
}
}
}
NgxBootstrapTreeviewComponent.decorators = [
{ type: Component, args: [{
// tslint:disable-next-line:component-selector
selector: 'ngx-bootstrap-treeview',
template: "<!-- This part is used if we have a single tree inputed to our component -->\r\n<ul\r\n [ngClass]=\"{\r\n branch: displayedTree.children,\r\n leaf: !displayedTree.children,\r\n opened: isOpened,\r\n 'pl-0': isFirstLevel,\r\n disabled: isLeaf && isOpened && disableSelectedElements\r\n }\"\r\n *ngIf=\"displayedTree\"\r\n class=\"mb-0\"\r\n #treeview\r\n>\r\n <li>\r\n <a [routerLink]=\"[]\" (click)=\"onClick()\" (contextmenu)=\"onContextMenu($event)\">\r\n <!-- Icons if we're reprensenting a branch -->\r\n <span class=\"icon\">\r\n <!-- Folder closed, no children selected -->\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"closedFolderIcon\"\r\n *ngIf=\"displayedTree.children && !isOpened && !selectedLeaves.length\"\r\n ></fa-icon>\r\n\r\n <!-- Folder closed, children selected but not all -->\r\n <fa-layers\r\n [fixedWidth]=\"true\"\r\n *ngIf=\"\r\n displayedTree.children &&\r\n !isOpened &&\r\n selectedLeaves.length > 0 &&\r\n selectedLeaves.length < this.leavesCount\r\n \"\r\n >\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"closedFolderIcon\"></fa-icon>\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"anyChildrenSelectedIcon\" transform=\"shrink-6 down-1\"></fa-icon>\r\n </fa-layers>\r\n\r\n <!-- Folder closed, all children selected -->\r\n <fa-layers\r\n [fixedWidth]=\"true\"\r\n *ngIf=\"\r\n displayedTree.children && !isOpened && selectedLeaves.length === leavesCount && leavesCount > 0\r\n \"\r\n >\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"closedFolderIcon\"></fa-icon>\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"allChildrenSelectedIcon\" transform=\"shrink-8 down-1\"></fa-icon>\r\n </fa-layers>\r\n\r\n <!-- Folder opened, no children selected -->\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"openedFolderIcon\"\r\n *ngIf=\"displayedTree.children && isOpened && !selectedLeaves.length\"\r\n ></fa-icon>\r\n\r\n <!-- Folder opened, children selected but not all -->\r\n <fa-layers\r\n [fixedWidth]=\"true\"\r\n *ngIf=\"\r\n displayedTree.children &&\r\n isOpened &&\r\n selectedLeaves.length > 0 &&\r\n selectedLeaves.length < this.leavesCount\r\n \"\r\n >\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"openedFolderIcon\"></fa-icon>\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"anyChildrenSelectedIcon\"\r\n transform=\"shrink-8 down-3 right-2\"\r\n class=\"opened-folder-mask\"\r\n ></fa-icon>\r\n </fa-layers>\r\n <!-- Folder opened, all children selected -->\r\n <fa-layers\r\n [fixedWidth]=\"true\"\r\n *ngIf=\"\r\n displayedTree.children && isOpened && selectedLeaves.length === leavesCount && leavesCount > 0\r\n \"\r\n >\r\n <fa-icon [fixedWidth]=\"true\" [icon]=\"openedFolderIcon\"></fa-icon>\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"allChildrenSelectedIcon\"\r\n transform=\"shrink-12 down-3 right-1\"\r\n class=\"opened-folder-mask\"\r\n ></fa-icon>\r\n </fa-layers>\r\n <!-- /branch icons -->\r\n\r\n <!-- Icons if we're reprensenting a leaf -->\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"unselectedLeafIcon\"\r\n *ngIf=\"!displayedTree.children && !isOpened\"\r\n ></fa-icon>\r\n <fa-icon\r\n [fixedWidth]=\"true\"\r\n [icon]=\"selectedLeafIcon\"\r\n *ngIf=\"!displayedTree.children && isOpened\"\r\n ></fa-icon>\r\n </span>\r\n <span class=\"label\">\r\n {{ displayedTree.label }}\r\n </span>\r\n </a>\r\n\r\n <div\r\n *ngIf=\"displayedTree.children && isOpened\"\r\n class=\"children\"\r\n [@.disabled]=\"isAnimationDisabled\"\r\n [@childrenAnimationTrigger]\r\n >\r\n <ul *ngIf=\"displayedTree.children.length === 0\">\r\n <li>\r\n <i>{{ emptyFolderLabel }}</i>\r\n </li>\r\n </ul>\r\n <ng-container *ngFor=\"let branch of displayedTree.children\">\r\n <ngx-bootstrap-treeview\r\n *ngIf=\"onElementAdded()\"\r\n [tree]=\"branch\"\r\n (branchClicked)=\"onChildBranchClicked($event)\"\r\n (leafClicked)=\"onChildLeafClicked($event)\"\r\n [selectedLeafIcon]=\"selectedLeafIcon\"\r\n [unselectedLeafIcon]=\"unselectedLeafIcon\"\r\n [openedFolderIcon]=\"openedFolderIcon\"\r\n [closedFolderIcon]=\"closedFolderIcon\"\r\n [canSelectBranch]=\"canSelectBranch\"\r\n [anyChildrenSelectedIcon]=\"anyChildrenSelectedIcon\"\r\n [allChildrenSelectedIcon]=\"allChildrenSelectedIcon\"\r\n [loggingService]=\"loggingService\"\r\n [isFirstInstance]=\"false\"\r\n [isFirstLevel]=\"false\"\r\n [disableSelectedElements]=\"disableSelectedElements\"\r\n [emptyFolderLabel]=\"emptyFolderLabel\"\r\n >\r\n </ngx-bootstrap-treeview>\r\n </ng-container>\r\n </div>\r\n </li>\r\n</ul>\r\n<!-- END Single tree provided -->\r\n\r\n<!-- Context menu, only shown once, so either on root or on branch if first level -->\r\n<ngx-bootstrap-treeview-context-menu\r\n *ngIf=\"contextMenus && isFirstInstance\"\r\n [config]=\"contextMenus\"\r\n [rootContextMenu]=\"contextMenus.rootMenu\"\r\n [branchContextMenu]=\"contextMenus.branchMenu\"\r\n [leafContextMenu]=\"contextMenus.leafMenu\"\r\n>\r\n</ngx-bootstrap-treeview-context-menu>\r\n\r\n<!-- If we provided an array of trees as an input, this part will be called -->\r\n<div *ngIf=\"displayedTrees\" (contextmenu)=\"onRootContextMenu($event)\" #rootsContainer>\r\n <ngx-bootstrap-treeview\r\n *ngFor=\"let displayedTree of displayedTrees\"\r\n [tree]=\"displayedTree\"\r\n (branchClicked)=\"onChildBranchClicked($event)\"\r\n (leafClicked)=\"onChildLeafClicked($event)\"\r\n [selectedLeafIcon]=\"selectedLeafIcon\"\r\n [unselectedLeafIcon]=\"unselectedLeafIcon\"\r\n [openedFolderIcon]=\"openedFolderIcon\"\r\n [closedFolderIcon]=\"closedFolderIcon\"\r\n [canSelectBranch]=\"canSelectBranch\"\r\n [anyChildrenSelectedIcon]=\"anyChildrenSelectedIcon\"\r\n [allChildrenSelectedIcon]=\"allChildrenSelectedIcon\"\r\n [loggingService]=\"loggingService\"\r\n [disableSelectedElements]=\"disableSelectedElements\"\r\n [isAnimationDisabled]=\"isAnimationDisabled\"\r\n [isFirstInstance]=\"false\"\r\n [emptyFolderLabel]=\"emptyFolderLabel\"\r\n ></ngx-bootstrap-treeview>\r\n</div>\r\n<!-- End multiple trees provided -->\r\n",
animations: [
trigger('childrenAnimationTrigger', [
transition(':leave', [animate('0.25s', style({ transform: 'translateX(-100%)', display: 'none' }))]),
transition(':enter', [
animate('0.25s', keyframes([
style({ transform: 'translateX(-100%)', display: 'block', offset: 0 }),
style({ transform: 'translateX(0%)', offset: 1 })
]))
])
])
],
styles: ["ul{list-style-type:none;overflow:visible;float:left;clear:left}ul li{position:relative;display:inline-block}ul fa-icon,ul fa-layers fa-icon{color:#000}ul fa-layers fa-icon:not(:first-child) ::ng-deep svg[data-prefix=fas]{color:#fff}ul fa-layers fa-icon.opened-folder-mask ::ng-deep svg{-webkit-transform:skew(-45deg,0);transform:skew(-45deg,0)}:host{display:block}:host>div{position:relative}"]
}] }
];
/** @nocollapse */
NgxBootstrapTreeviewComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: Renderer2 },
{ type: NgZone },
{ type: ChangeDetectorRef },
{ type: ContextMenuService }
];
NgxBootstrapTreeviewComponent.propDecorators = {
canSelectBranch: [{ type: Input }],
contextMenus: [{ type: Input }],
disableSelectedElements: [{ type: Input }],
emptyFolderLabel: [{ type: Input }],
isAnimationDisabled: [{ type: Input }],
isFirstLevel: [{ type: Input }],
isFirstInstance: [{ type: Input }],
isOpened: [{ type: Input }],
item: [{ type: Input }],
items: [{ type: Input }],
loggingService: [{ type: Input }],
mapper: [{ type: Input }],
preselectedItems: [{ type: Input }],
tree: [{ type: Input }],
trees: [{ type: Input }],
openedFolderIcon: [{ type: Input }],
closedFolderIcon: [{ type: Input }],
unselectedLeafIcon: [{ type: Input }],
selectedLeafIcon: [{ type: Input }],
anyChildrenSelectedIcon: [{ type: Input }],
allChildrenSelectedIcon: [{ type: Input }],
filterString: [{ type: Input }],
matchBranches: [{ type: Input }],
branchClicked: [{ type: Output }],
leafClicked: [{ type: Output }],
children: [{ type: ViewChildren, args: [NgxBootstrapTreeviewComponent,] }],
treeview: [{ type: ViewChild, args: ['treeview',] }],
rootsContainer: [{ type: ViewChild, args: ['rootsContainer',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class NgxBootstrapTreeviewContextMenuComponent {
/**
* @param {?} _renderer
* @param {?} _zone
* @param {?} _contextMenuService
*/
constructor(_renderer, _zone, _contextMenuService) {
this._renderer = _renderer;
this._zone = _zone;
this._contextMenuService = _contextMenuService;
this._defaultConfig = {
containerClass: '',
hoveredItemClass: '',
itemsClass: ''
};
this.config = {};
this.rootContextMenu = null;
this.branchContextMenu = null;
this.leafContextMenu = null;
this.hidden = new EventEmitter();
this.shown = new EventEmitter();
this._activeMenu = null;
}
/**
* @return {?}
*/
ngOnInit() {
this.config = Object.assign({}, this._defaultConfig, this.config);
this._contextMenuService.lastContextMenuEvent.subscribe((lastContextMenuEvent) => {
if (lastContextMenuEvent) {
this.show(lastContextMenuEvent);
}
else {
this.hide();
}
});
this._renderer.listen(document, 'click.out-zone', this.onDocumentClicked.bind(this));
this._renderer.listen(document, 'keyup.out-zone', this.onKeyPressed.bind(this));
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if ('config' in changes) {
this.config = Object.assign({}, this._defaultConfig, changes.config.currentValue);
}
}
/**
* @param {?} event
* @return {?}
*/
onDocumentClicked(event) {
if (this.lastContextMenuEvent) {
event.preventDefault();
event.stopPropagation();
this.hide();
}
}
/**
* @param {?} event
* @return {?}
*/
onKeyPressed(event) {
if (this.lastContextMenuEvent && event.key.toLowerCase() === 'escape') {
this.hide();
}
}
/**
* @return {?}
*/
getLabels() {
return this._activeMenu ? Object.keys(this._activeMenu) : [];
}
/**
* @param {?} label
* @return {?}
*/
onItemClicked(label) {
this._activeMenu[label](this.lastContextMenuEvent.target);
}
/**
* @return {?}
*/
hide() {
this._zone.run(() => {
this._activeMenu = null;
this.lastContextMenuEvent = null;
this.hidden.emit();
});
}
/**
* @param {?} contextMenuEvent
* @return {?}
*/
show(contextMenuEvent) {
this.lastContextMenuEvent = contextMenuEvent;
if (!contextMenuEvent.target) {
this._activeMenu = this.rootContextMenu;
}
else if (contextMenuEvent.target.children) {
this._activeMenu = this.branchContextMenu;
}
else {
this._activeMenu = this.leafContextMenu;
}
/** @type {?} */
const nativeElement = this.container.nativeElement;
/** @type {?} */
const x = this.lastContextMenuEvent.event.pageX.toString();
/** @type {?} */
const y = this.lastContextMenuEvent.event.pageY.toString();
this._renderer.setStyle(nativeElement, 'top', y + 'px');
this._renderer.setStyle(nativeElement, 'left', x + 'px');
this.shown.emit();
}
}
NgxBootstrapTreeviewContextMenuComponent.decorators = [
{ type: Component, args: [{
// tslint:disable-next-line: component-selector
selector: 'ngx-bootstrap-treeview-context-menu',
template: "<div\r\n class=\"context-menu list-group\"\r\n [ngClass]=\"{ 'd-none': !lastContextMenuEvent, 'd-block': lastContextMenuEvent }\"\r\n #container\r\n>\r\n <button\r\n class=\"context-menu-item {{ config.itemsClass }}\"\r\n *ngFor=\"let label of getLabels()\"\r\n (click)=\"onItemClicked(label)\"\r\n [ngClass]=\"{ 'list-group-item list-group-item-action px-3': !config.itemsClass }\"\r\n >\r\n {{ label }}\r\n </button>\r\n</div>\r\n",
styles: [".context-menu{background-color:#fff;border-radius:.25rem;left:0;position:fixed;top:0;z-index:100}.context-menu .context-menu-item{border-top-width:0;border-bottom-width:0;padding-top:.0625rem;padding-bottom:.0625rem;transition:background-color .3s}.context-menu .context-menu-item:first-of-type{padding-top:.125rem;border-top-width:1px}.context-menu .context-menu-item:last-of-type{padding-bottom:.125rem;border-bottom-width:1px}.context-menu .context-menu-item:hover{background:rgba(0,0,0,.8);color:#fff}"]
}] }
];
/** @nocollapse */
NgxBootstrapTreeviewContextMenuComponent.ctorParameters = () => [
{ type: Renderer2 },
{ type: NgZone },
{ type: ContextMenuService }
];
NgxBootstrapTreeviewContextMenuComponent.propDecorators = {
config: [{ type: Input }],
rootContextMenu: [{ type: Input }],
branchContextMenu: [{ type: Input }],
leafContextMenu: [{ type: Input }],
hidden: [{ type: Output }],
shown: [{ type: Output }],
container: [{ type: ViewChild, args: ['container',] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class NgxBootstrapTreeviewEventManagerService extends EventManager {
/**
* @param {?} plugins
* @param {?} zone
*/
constructor(plugins, zone) {
super(plugins, zone);
this.zone = zone;
}
/**
* @param {?} element
* @param {?} eventName
* @param {?} handler
* @return {?}
*/
addEventListener(element, eventName, handler) {
if (eventName.endsWith('out-zone')) {
eventName = eventName.split('.')[0];
return this.zone.runOutsideAngular(() => super.addEventListener(element, eventName, handler));
}
return super.addEventListener(element, eventName, handler);
}
}
NgxBootstrapTreeviewEventManagerService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxBootstrapTreeviewEventManagerService.ctorParameters = () => [
{ type: Array, decorators: [{ type: Inject, args: [EVENT_MANAGER_PLUGINS,] }] },
{ type: NgZone }
];
/** @nocollapse */ NgxBootstrapTreeviewEventManagerService.ngInjectableDef = defineInjectable({ factory: function NgxBootstrapTreeviewEventManagerService_Factory() { return new NgxBootstrapTreeviewEventManagerService(inject(EVENT_MANAGER_PLUGINS), inject(NgZone)); }, token: NgxBootstrapTreeviewEventManagerService, providedIn: "root" });
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class NgxBootstrapTreeviewModule {
}
NgxBootstrapTreeviewModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule, FontAwesomeModule, RouterModule.forChild([])],
declarations: [NgxBootstrapTreeviewComponent, NgxBootstrapTreeviewContextMenuComponent],
exports: [NgxBootstrapTreeviewComponent],
providers: [ContextMenuService, { provide: EventManager, useClass: NgxBootstrapTreeviewEventManagerService }]
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
class TreeMap {
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
export { NgxBootstrapTreeviewModule, LeafClickedEvent, Leaf, TreeMap, Tree, NgxBootstrapTreeviewMapper, NgxBootstrapTreeviewComponent, NgxBootstrapTreeviewContextMenuComponent as ɵb, ContextMenuService as ɵa, NgxBootstrapTreeviewEventManagerService as ɵc };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWJvb3RzdHJhcC10cmVldmlldy5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9tb2RlbHMvdHJlZS5tb2RlbC50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9tb2RlbHMvbGVhZi5tb2RlbC50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9tb2RlbHMvbGVhZi1jbGlja2VkLWV2ZW50Lm1vZGVsLnRzIiwibmc6Ly9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3L3V0aWxzL25neC1ib290c3RyYXAtdHJlZXZpZXctbWFwcGVyLnRzIiwibmc6Ly9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3L3NlcnZpY2VzL2NvbnRleHQtbWVudS5zZXJ2aWNlLnRzIiwibmc6Ly9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3L2NvbXBvbmVudHMvbmd4LWJvb3RzdHJhcC10cmVldmlldy9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3LmNvbXBvbmVudC50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9jb21wb25lbnRzL25neC1ib290c3RyYXAtdHJlZXZpZXctY29udGV4dC1tZW51L25neC1ib290c3RyYXAtdHJlZXZpZXctY29udGV4dC1tZW51LmNvbXBvbmVudC50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9zZXJ2aWNlcy9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3LWV2ZW50LW1hbmFnZXIuc2VydmljZS50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9uZ3gtYm9vdHN0cmFwLXRyZWV2aWV3Lm1vZHVsZS50cyIsIm5nOi8vbmd4LWJvb3RzdHJhcC10cmVldmlldy9tb2RlbHMvdHJlZS1tYXAubW9kZWwudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNsYXNzIFRyZWUge1xyXG4gICAgY2hpbGRyZW4/OiBUcmVlW107XHJcbiAgICBsb2FkQ2hpbGRyZW4/OiBGdW5jdGlvbjtcclxuICAgIGxhYmVsOiBzdHJpbmc7XHJcbiAgICB2YWx1ZTogbnVtYmVyIHwgc3RyaW5nO1xyXG4gICAgZGF0YT86IGFueTtcclxufVxyXG4iLCJpbXBvcnQgeyBUcmVlIH0gZnJvbSAnLi90cmVlLm1vZGVsJztcclxuXHJcbmV4cG9ydCBjbGFzcyBMZWFmIHtcclxuICAgIHB1YmxpYyB2YWx1ZTogc3RyaW5nIHwgbnVtYmVyO1xyXG4gICAgcHVibGljIGxhYmVsOiBzdHJpbmc7XHJcbiAgICBwdWJsaWMgZGF0YT86IGFueTtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcih0cmVlOiBUcmVlKSB7XHJcbiAgICAgICAgdGhpcy52YWx1ZSA9IHRyZWUudmFsdWU7XHJcbiAgICAgICAgdGhpcy5sYWJlbCA9IHRyZWUubGFiZWw7XHJcbiAgICAgICAgdGhpcy5kYXRhID0gdHJlZS5kYXRhO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IExlYWYgfSBmcm9tICcuL2xlYWYubW9kZWwnO1xyXG5cclxuZXhwb3J0IGNsYXNzIExlYWZDbGlja2VkRXZlbnQge1xyXG4gICAgcHVibGljIGxlYWY6IExlYWY7XHJcbiAgICBwdWJsaWMgc2VsZWN0ZWRMZWF2ZXM/OiBMZWFmW107XHJcblxyXG4gICAgY29uc3RydWN0b3IobGVhZjogTGVhZiwgc2VsZWN0ZWRMZWF2ZXM6IExlYWZbXSkge1xyXG4gICAgICAgIHRoaXMubGVhZiA9IGxlYWY7XHJcbiAgICAgICAgdGhpcy5zZWxlY3RlZExlYXZlcyA9IHNlbGVjdGVkTGVhdmVzO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IFRyZWUgfSBmcm9tICcuLi9tb2RlbHMvdHJlZS5tb2RlbCc7XHJcbmltcG9ydCB7IExlYWYgfSBmcm9tICcuLi9tb2RlbHMvbGVhZi5tb2RlbCc7XHJcbmltcG9ydCB7IFRyZWVNYXAgfSBmcm9tICcuLi9tb2RlbHMvdHJlZS1tYXAubW9kZWwnO1xyXG5pbXBvcnQgeyBMZWFmTWFwIH0gZnJvbSAnLi4vbW9kZWxzL2xlYWYtbWFwLm1vZGVsJztcclxuXHJcbmV4cG9ydCBjbGFzcyBOZ3hCb290c3RyYXBUcmVldmlld01hcHBlcjxUcmVlU291cmNlVHlwZSBleHRlbmRzIE9iamVjdCwgTGVhZlNvdXJjZVR5cGUgZXh0ZW5kcyBPYmplY3Q+IHtcclxuICAgIHRyZWVNYXA6IFRyZWVNYXA7XHJcbiAgICBsZWFmTWFwOiBMZWFmTWFwO1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKHRyZWVNYXA6IFRyZWVNYXAsIGxlYWZNYXA6IExlYWZNYXApIHtcclxuICAgICAgICB0aGlzLnRyZWVNYXAgPSB0cmVlTWFwO1xyXG4gICAgICAgIHRoaXMubGVhZk1hcCA9IGxlYWZNYXA7XHJcbiAgICB9XHJcblxyXG4gICAgbWFwVHJlZShpdGVtOiBUcmVlU291cmNlVHlwZSk6IFRyZWUge1xyXG4gICAgICAgIGNvbnN0IHsgdmFsdWUsIGxhYmVsLCBjaGlsZHJlbiwgbGVhdmVzLCBkYXRhIH0gPSB7XHJcbiAgICAgICAgICAgIHZhbHVlOiBpdGVtW3RoaXMudHJlZU1hcC52YWx1ZV0sXHJcbiAgICAgICAgICAgIGxhYmVsOiBpdGVtW3RoaXMudHJlZU1hcC5sYWJlbF0sXHJcbiAgICAgICAgICAgIGNoaWxkcmVuOiBpdGVtW3RoaXMudHJlZU1hcC5jaGlsZHJlbl0sXHJcbiAgICAgICAgICAgIGxlYXZlczogaXRlbVt0aGlzLnRyZWVNYXAubGVhdmVzS2V5XSxcclxuICAgICAgICAgICAgZGF0YTogaXRlbVxyXG4gICAgICAgIH07XHJcblxyXG4gICAgICAgIGNvbnN0IHJlc3VsdDogVHJlZSA9IHtcclxuICAgICAgICAgICAgdmFsdWUsXHJcbiAgICAgICAgICAgIGxhYmVsLFxyXG4gICAgICAgICAgICBkYXRhLFxyXG4gICAgICAgICAgICBjaGlsZHJlbjogWy4uLmNoaWxkcmVuLm1hcChjaGlsZCA9PiB0aGlzLm1hcFRyZWUoY2hpbGQpKSwgLi4ubGVhdmVzLm1hcChsZWFmID0+IHRoaXMubWFwTGVhZihsZWFmKSldXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgIH1cclxuXHJcbiAgICBtYXBMZWFmKGl0ZW06IExlYWZTb3VyY2VUeXBlKTogTGVhZiB7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgdmFsdWU6IGl0ZW1bdGhpcy5sZWFmTWFwLnZhbHVlXSxcclxuICAgICAgICAgICAgbGFiZWw6IGl0ZW1bdGhpcy5sZWFmTWFwLmxhYmVsXSxcclxuICAgICAgICAgICAgZGF0YTogaXRlbVxyXG4gICAgICAgIH07XHJcbiAgICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgTGVhZiB9IGZyb20gJy4uL21vZGVscy9sZWFmLm1vZGVsJztcclxuaW1wb3J0IHsgVHJlZSB9IGZyb20gJy4uL21vZGVscy90cmVlLm1vZGVsJztcclxuaW1wb3J0IHsgQ29udGV4dE1lbnVFdmVudCB9IGZyb20gJy4uL21vZGVscy9jb250ZXh0LW1lbnUtZXZlbnQubW9kZWwnO1xyXG5cclxuQEluamVjdGFibGUoe1xyXG4gICAgcHJvdmlkZWRJbjogJ3Jvb3QnXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBDb250ZXh0TWVudVNlcnZpY2Uge1xyXG4gICAgcHVibGljIGxhc3RDb250ZXh0TWVudUV2ZW50ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxDb250ZXh0TWVudUV2ZW50PihudWxsKTtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcigpIHt9XHJcblxyXG4gICAgZmlyZShldmVudDogQ29udGV4dE1lbnVFdmVudCkge1xyXG4gICAgICAgIHRoaXMubGFzdENvbnRleHRNZW51RXZlbnQubmV4dChldmVudCk7XHJcbiAgICB9XHJcbn1cclxuIiwiaW1wb3J0IHtcclxuICAgIENvbXBvbmVudCxcclxuICAgIE9uSW5pdCxcclxuICAgIElucHV0LFxyXG4gICAgT3V0cHV0LFxyXG4gICAgRXZlbnRFbWl0dGVyLFxyXG4gICAgVmlld0NoaWxkcmVuLFxyXG4gICAgUXVlcnlMaXN0LFxyXG4gICAgRWxlbWVudFJlZixcclxuICAgIFJlbmRlcmVyMixcclxuICAgIFNpbXBsZUNoYW5nZXMsXHJcbiAgICBPbkNoYW5nZXMsXHJcbiAgICBOZ1pvbmUsXHJcbiAgICBDaGFuZ2VEZXRlY3RvclJlZixcclxuICAgIFZpZXdDaGlsZFxyXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBUcmVlIH0gZnJvbSAnLi4vLi4vbW9kZWxzL3RyZWUubW9kZWwnO1xyXG5pbXBvcnQge1xyXG4gICAgZmFTcXVhcmUsXHJcbiAgICBmYUNoZWNrU3F1YXJlLFxyXG4gICAgZmFGb2xkZXIsXHJcbiAgICBmYUZvbGRlck9wZW4sXHJcbiAgICBmYU1pbnVzLFxyXG4gICAgZmFDaGVjayxcclxuICAgIEljb25EZWZpbml0aW9uXHJcbn0gZnJvbSAnQGZvcnRhd2Vzb21lL2ZyZWUtc29saWQtc3ZnLWljb25zJztcclxuaW1wb3J0IHsgdHJpZ2dlciwgc3RhdGUsIHN0eWxlLCB0cmFuc2l0aW9uLCBhbmltYXRlLCBrZXlmcmFtZXMgfSBmcm9tICdAYW5ndWxhci9hbmltYXRpb25zJztcclxuaW1wb3J0IHsgTGVhZiB9IGZyb20gJy4uLy4uL21vZGVscy9sZWFmLm1vZGVsJztcclxuaW1wb3J0IHsgTGVhZkNsaWNrZWRFdmVudCB9IGZyb20gJy4uLy4uL21vZGVscy9sZWFmLWNsaWNrZWQtZXZlbnQubW9kZWwnO1xyXG5pbXBvcnQgeyBJTG9nZ2luZ1NlcnZpY2UgfSBmcm9tICcuLi8uLi9pbnRlcmZhY2VzL0lMb2dnaW5nU2VydmljZS5pbnRlcmZhY2UnO1xyXG5pbXBvcnQgeyBOZ3hCb290c3RyYXBUcmVldmlld01hcHBlciB9IGZyb20gJy4uLy4uL3V0aWxzL25neC1ib290c3RyYXAtdHJlZXZpZXctbWFwcGVyJztcclxuaW1wb3J0IHsgTmd4Qm9vdHN0cmFwVHJlZXZpZXdDb250ZXh0TWVudXMgfSBmcm9tICcuLi8uLi9tb2RlbHMvbmd4LWJvb3RzdHJhcC10cmVldmlldy1jb250ZXh0LW1lbnVzLm1vZGVsJztcclxuaW1wb3J0IHsgQ29udGV4dE1lbnVTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvY29udGV4dC1tZW51LnNlcnZpY2UnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6Y29tcG9uZW50LXNlbGVjdG9yXHJcbiAgICBzZWxlY3RvcjogJ25neC1ib290c3RyYXAtdHJlZXZpZXcnLFxyXG4gICAgdGVtcGxhdGVVcmw6ICcuL25neC1ib290c3RyYXAtdHJlZXZpZXcuY29tcG9uZW50Lmh0bWwnLFxyXG4gICAgc3R5bGVVcmxzOiBbJy4vbmd4LWJvb3RzdHJhcC10cmVldmlldy5jb21wb25lbnQuc2NzcyddLFxyXG4gICAgYW5pbWF0aW9uczogW1xyXG4gICAgICAgIHRyaWdnZXIoJ2NoaWxkcmVuQW5pbWF0aW9uVHJpZ2dlcicsIFtcclxuICAgICAgICAgICAgdHJhbnNpdGlvbignOmxlYXZlJywgW2FuaW1hdGUoJzAuMjVzJywgc3R5bGUoeyB0cmFuc2Zvcm06ICd0cmFuc2xhdGVYKC0xMDAlKScsIGRpc3BsYXk6ICdub25lJyB9KSldKSxcclxuICAgICAgICAgICAgdHJhbnNpdGlvbignOmVudGVyJywgW1xyXG4gICAgICAgICAgICAgICAgYW5pbWF0ZShcclxuICAgICAgICAgICAgICAgICAgICAnMC4yNXMnLFxyXG4gICAgICAgICAgICAgICAgICAgIGtleWZyYW1lcyhbXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlKHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgtMTAwJSknLCBkaXNwbGF5OiAnYmxvY2snLCBvZmZzZXQ6IDAgfSksXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlKHsgdHJhbnNmb3JtOiAndHJhbnNsYXRlWCgwJSknLCBvZmZzZXQ6IDEgfSlcclxuICAgICAgICAgICAgICAgICAgICBdKVxyXG4gICAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICBdKVxyXG4gICAgICAgIF0pXHJcbiAgICBdXHJcbn0pXHJcbmV4cG9ydCBjbGFzcyBOZ3hCb290c3RyYXBUcmVldmlld0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzIHtcclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgY2FuU2VsZWN0QnJhbmNoOiBib29sZWFuO1xyXG5cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgY29udGV4dE1lbnVzOiBOZ3hCb290c3RyYXBUcmVldmlld0NvbnRleHRNZW51cyA9IHtcclxuICAgICAgICBsZWFmTWVudToge30sXHJcbiAgICAgICAgYnJhbmNoTWVudToge30sXHJcbiAgICAgICAgcm9vdE1lbnU6IHt9XHJcbiAgICB9O1xyXG5cclxuICAgIEBJbnB1dCgpXHJcbiAgICBwdWJsaWMgZGlzYWJsZVNlbGVjdGVkRWxlbWVudHMgPSBmYWxzZTtcclxuXHJcbiAgICBASW5wdXQoKVxyXG4gICAgcHVibGljIGVtcHR5