UNPKG

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
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