@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
226 lines • 35.5 kB
JavaScript
import { ChangeDetectorRef, Directive, inject, Input, IterableDiffers, Renderer2, TemplateRef, ViewContainerRef } from '@angular/core';
import { Subject } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { VirtualForService } from './virtual-for.service';
import * as i0 from "@angular/core";
/**
* This implementation is inspired by the CDK virtual for:
* https://github.com/angular/material2/blob/master/src/cdk/scrolling/virtual-for-of.ts
* However the CDK requires a container component which limits use in places such
* as fixed header tables, so this is a more generic implementation that does not
* require a parent element but instead uses an attribute on the parent container instead
*/
export class VirtualForDirective {
/** Store the list of items to display */
set uxVirtualForOf(dataset) {
// emit the latest dataset
this._virtualScroll.dataset.next(dataset);
// store a local version of the dataset
this._dataset = dataset;
// if this is an update and not the initial dataset then we should
// forcibly redraw the list of items. In cases where the length of
// the dataset change would trigger a re-renderer as the scroll position
// would change, however if we are performing sorting then it would not
// so we must ensure we update everytime the dataset changes.
if (this._renderedRange) {
this.onRangeChange();
}
}
constructor() {
/** A reference to the container element where we will insert elements. */
this._viewContainerRef = inject(ViewContainerRef);
/** The template for all items */
this._templateRef = inject(TemplateRef);
/** Gets the set of Angular differs for detecting changes. */
this._differs = inject(IterableDiffers);
/** Get the renderer to perform DOM manipulation */
this._renderer = inject(Renderer2);
this._changeDetector = inject(ChangeDetectorRef);
/** A service to share values between the container and child elements */
this._virtualScroll = inject(VirtualForService, { optional: true });
/** Provide a trackBy function to optimize rendering */
this.uxVirtualForTrackBy = this.defaultTrackBy;
/** Indicate whether we need to perform a view update */
this._isDirty = false;
/** Store a cache of recently disposed views for reuse */
this._templateCache = [];
/** Limit the size of the cache as it can use a lot of memory */
this._cacheSize = 20;
/** Unsubscribe from all observables */
this._onDestroy = new Subject();
// While marked as optional, it isn't. We do this so we can provide a more helpful error message
if (!this._virtualScroll) {
throw new Error('The "uxVirtualFor" directive requires the "uxVirtualForContainer" directive to be added to the parent element.');
}
}
ngOnInit() {
// update the UI whenever the range changes
this._virtualScroll.range.pipe(distinctUntilChanged(this.isRangeSame), takeUntil(this._onDestroy)).subscribe(range => {
this._renderedRange = range;
this.onRangeChange();
this._changeDetector.detectChanges();
});
}
ngDoCheck() {
if (this._isDirty && this._differ) {
// check if there area any changes
const changes = this.getChanges();
if (changes) {
this.applyChanges(changes);
}
else {
this.updateContexts();
}
// now that we have rendered any change we should store this so we don't perform unneeded updates
this._isDirty = false;
}
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
/** If an itemSize is not specified we need to calculate it */
getHeight(context, length) {
// create a temporary view
const view = this.createView(0);
// set the implicit value to the item value
view.context.$implicit = context;
view.context.count = length;
view.context.even = true;
view.context.odd = false;
view.context.first = true;
view.context.last = length === 1;
// run change detection
view.detectChanges();
// get the size of the view
const height = view.rootNodes[0].offsetHeight;
// destroy the view
this._viewContainerRef.remove(0);
view.destroy();
return height;
}
/** Determine if the range has changed (performance optimization) */
isRangeSame(previous, current) {
return previous.start === current.start && previous.end === current.end;
}
onRangeChange() {
// store the visible range
this._renderedItems = this._dataset.slice(this._renderedRange.start, this._renderedRange.end);
// create the Angular differ if we haven't previously done so
if (!this._differ) {
this._differ = this._differs.find(this._renderedItems).create(this.uxVirtualForTrackBy);
}
// mark the view for re-render
this._isDirty = true;
}
/** Determine which items have changed */
getChanges() {
return this._differ.diff(this._renderedItems);
}
/** Insert, move and remove any items within the view */
applyChanges(changes) {
// Go through each changes and either add or rearrange accordingly
changes.forEachOperation((record, previousIndex, currentIndex) => {
// check if a new item was added
if (previousIndex === null) {
// create the new embedded view
const view = this.createView(currentIndex);
// set the implicit value to the item value
view.context.$implicit = record.item;
}
else if (currentIndex === null) { // check if the item should be removed
const view = this._viewContainerRef.detach(currentIndex);
const index = this._viewContainerRef.indexOf(view);
// if there is space in the cache then store the detached view
if (this._templateCache.length < this._cacheSize) {
this._templateCache.push(view);
}
else {
index === -1 ? view.destroy() : this._viewContainerRef.remove(index);
}
}
else { // the position of the item has changed
// get the view from its current position
const view = this._viewContainerRef.get(previousIndex);
// move it to the new position
this._viewContainerRef.move(view, currentIndex);
// update the implicit value (the rest will stay the same)
view.context.$implicit = record.item;
}
});
// Ensure the implicit value is correct for any items whose identity changed
changes.forEachIdentityChange((record) => {
const view = this._viewContainerRef.get(record.currentIndex);
if (view) {
view.context.$implicit = record.item;
}
});
this.updateContexts();
}
updateContexts() {
// update all the other context properties
for (let idx = 0; idx < this._viewContainerRef.length; idx++) {
// get the view at a given position
const view = this._viewContainerRef.get(idx);
// update the properties
view.context.index = this._renderedRange.start + idx;
view.context.count = this._dataset.length;
view.context.first = view.context.index === 0;
view.context.last = view.context.index === view.context.count - 1;
view.context.even = view.context.index % 2 === 0;
view.context.odd = !view.context.even;
// update the position in the DOM
view.rootNodes.forEach((node) => {
this._renderer.setStyle(node, 'position', 'absolute');
this._renderer.setStyle(node, 'width', '100%');
this._renderer.setStyle(node, 'top', '0');
this._renderer.setStyle(node, 'transform', `translateY(${view.context.index * this._virtualScroll.itemSize}px`);
});
view.detectChanges();
}
}
createView(index) {
// get a checked EmbeddedViewRef is there is one
const cachedTemplate = this._templateCache.pop();
if (cachedTemplate) {
// replace existing context with the defaults
cachedTemplate.context.$implicit = null;
cachedTemplate.context.index = -1;
cachedTemplate.context.count = -1;
cachedTemplate.context.first = false;
cachedTemplate.context.last = false;
cachedTemplate.context.even = false;
cachedTemplate.context.odd = false;
// insert the view
this._viewContainerRef.insert(cachedTemplate, index);
// return the cached EmbeddedViewRef
return cachedTemplate;
}
// otherwise create a new view and insert it
return this._viewContainerRef.createEmbeddedView(this._templateRef, {
$implicit: null,
index: -1,
count: -1,
first: false,
last: false,
even: false,
odd: false,
}, index);
}
defaultTrackBy(index) {
return index;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: VirtualForDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: VirtualForDirective, selector: "[uxVirtualFor][uxVirtualForOf]", inputs: { uxVirtualForOf: "uxVirtualForOf", uxVirtualForTrackBy: "uxVirtualForTrackBy" }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: VirtualForDirective, decorators: [{
type: Directive,
args: [{
selector: '[uxVirtualFor][uxVirtualForOf]',
}]
}], ctorParameters: () => [], propDecorators: { uxVirtualForOf: [{
type: Input
}], uxVirtualForTrackBy: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlydHVhbC1mb3IuZGlyZWN0aXZlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdmlydHVhbC1zY3JvbGwvdmlydHVhbC1mb3IvdmlydHVhbC1mb3IuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxTQUFTLEVBQTRCLE1BQU0sRUFBRSxLQUFLLEVBQXlELGVBQWUsRUFBcUIsU0FBUyxFQUFFLFdBQVcsRUFBbUIsZ0JBQWdCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNVAsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvQixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDakUsT0FBTyxFQUFtQixpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDOztBQUUzRTs7Ozs7O0dBTUc7QUFLSCxNQUFNLE9BQU8sbUJBQW1CO0lBa0I1Qix5Q0FBeUM7SUFDekMsSUFBYSxjQUFjLENBQUMsT0FBWTtRQUNwQywwQkFBMEI7UUFDMUIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRTFDLHVDQUF1QztRQUN2QyxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQztRQUV4QixrRUFBa0U7UUFDbEUsa0VBQWtFO1FBQ2xFLHdFQUF3RTtRQUN4RSx1RUFBdUU7UUFDdkUsNkRBQTZEO1FBQzdELElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3RCLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN6QixDQUFDO0lBQ0wsQ0FBQztJQTZCRDtRQTlEQSwwRUFBMEU7UUFDekQsc0JBQWlCLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFOUQsaUNBQWlDO1FBQ2hCLGlCQUFZLEdBQUcsTUFBTSxD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IERvQ2hlY2ssIEVtYmVkZGVkVmlld1JlZiwgaW5qZWN0LCBJbnB1dCwgSXRlcmFibGVDaGFuZ2VSZWNvcmQsIEl0ZXJhYmxlQ2hhbmdlcywgSXRlcmFibGVEaWZmZXIsIEl0ZXJhYmxlRGlmZmVycywgT25EZXN0cm95LCBPbkluaXQsIFJlbmRlcmVyMiwgVGVtcGxhdGVSZWYsIFRyYWNrQnlGdW5jdGlvbiwgVmlld0NvbnRhaW5lclJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGlzdGluY3RVbnRpbENoYW5nZWQsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IFZpcnR1YWxGb3JSYW5nZSwgVmlydHVhbEZvclNlcnZpY2UgfSBmcm9tICcuL3ZpcnR1YWwtZm9yLnNlcnZpY2UnO1xuXG4vKipcbiAqIFRoaXMgaW1wbGVtZW50YXRpb24gaXMgaW5zcGlyZWQgYnkgdGhlIENESyB2aXJ0dWFsIGZvcjpcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9hbmd1bGFyL21hdGVyaWFsMi9ibG9iL21hc3Rlci9zcmMvY2RrL3Njcm9sbGluZy92aXJ0dWFsLWZvci1vZi50c1xuICogSG93ZXZlciB0aGUgQ0RLIHJlcXVpcmVzIGEgY29udGFpbmVyIGNvbXBvbmVudCB3aGljaCBsaW1pdHMgdXNlIGluIHBsYWNlcyBzdWNoXG4gKiBhcyBmaXhlZCBoZWFkZXIgdGFibGVzLCBzbyB0aGlzIGlzIGEgbW9yZSBnZW5lcmljIGltcGxlbWVudGF0aW9uIHRoYXQgZG9lcyBub3RcbiAqIHJlcXVpcmUgYSBwYXJlbnQgZWxlbWVudCBidXQgaW5zdGVhZCB1c2VzIGFuIGF0dHJpYnV0ZSBvbiB0aGUgcGFyZW50IGNvbnRhaW5lciBpbnN0ZWFkXG4gKi9cblxuQERpcmVjdGl2ZSh7XG4gICAgc2VsZWN0b3I6ICdbdXhWaXJ0dWFsRm9yXVt1eFZpcnR1YWxGb3JPZl0nLFxufSlcbmV4cG9ydCBjbGFzcyBWaXJ0dWFsRm9yRGlyZWN0aXZlPFQ+IGltcGxlbWVudHMgT25Jbml0LCBEb0NoZWNrLCBPbkRlc3Ryb3kge1xuICAgIC8qKiBBIHJlZmVyZW5jZSB0byB0aGUgY29udGFpbmVyIGVsZW1lbnQgd2hlcmUgd2Ugd2lsbCBpbnNlcnQgZWxlbWVudHMuICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBfdmlld0NvbnRhaW5lclJlZiA9IGluamVjdChWaWV3Q29udGFpbmVyUmVmKTtcblxuICAgIC8qKiBUaGUgdGVtcGxhdGUgZm9yIGFsbCBpdGVtcyAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3RlbXBsYXRlUmVmID0gaW5qZWN0PFRlbXBsYXRlUmVmPFZpcnR1YWxGb3JPZkNvbnRleHQ8VD4+PihUZW1wbGF0ZVJlZik7XG5cbiAgICAvKiogR2V0cyB0aGUgc2V0IG9mIEFuZ3VsYXIgZGlmZmVycyBmb3IgZGV0ZWN0aW5nIGNoYW5nZXMuICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZGlmZmVycyA9IGluamVjdChJdGVyYWJsZURpZmZlcnMpO1xuXG4gICAgLyoqIEdldCB0aGUgcmVuZGVyZXIgdG8gcGVyZm9ybSBET00gbWFuaXB1bGF0aW9uICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBfcmVuZGVyZXIgPSBpbmplY3QoUmVuZGVyZXIyKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2NoYW5nZURldGVjdG9yID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcblxuICAgIC8qKiBBIHNlcnZpY2UgdG8gc2hhcmUgdmFsdWVzIGJldHdlZW4gdGhlIGNvbnRhaW5lciBhbmQgY2hpbGQgZWxlbWVudHMgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF92aXJ0dWFsU2Nyb2xsID0gaW5qZWN0PFZpcnR1YWxGb3JTZXJ2aWNlPFQ+PihWaXJ0dWFsRm9yU2VydmljZSwgeyBvcHRpb25hbDogdHJ1ZSB9KTtcblxuICAgIC8qKiBTdG9yZSB0aGUgbGlzdCBvZiBpdGVtcyB0byBkaXNwbGF5ICovXG4gICAgQElucHV0KCkgc2V0IHV4VmlydHVhbEZvck9mKGRhdGFzZXQ6IFRbXSkge1xuICAgICAgICAvLyBlbWl0IHRoZSBsYXRlc3QgZGF0YXNldFxuICAgICAgICB0aGlzLl92aXJ0dWFsU2Nyb2xsLmRhdGFzZXQubmV4dChkYXRhc2V0KTtcblxuICAgICAgICAvLyBzdG9yZSBhIGxvY2FsIHZlcnNpb24gb2YgdGhlIGRhdGFzZXRcbiAgICAgICAgdGhpcy5fZGF0YXNldCA9IGRhdGFzZXQ7XG5cbiAgICAgICAgLy8gaWYgdGhpcyBpcyBhbiB1cGRhdGUgYW5kIG5vdCB0aGUgaW5pdGlhbCBkYXRhc2V0IHRoZW4gd2Ugc2hvdWxkXG4gICAgICAgIC8vIGZvcmNpYmx5IHJlZHJhdyB0aGUgbGlzdCBvZiBpdGVtcy4gSW4gY2FzZXMgd2hlcmUgdGhlIGxlbmd0aCBvZlxuICAgICAgICAvLyB0aGUgZGF0YXNldCBjaGFuZ2Ugd291bGQgdHJpZ2dlciBhIHJlLXJlbmRlcmVyIGFzIHRoZSBzY3JvbGwgcG9zaXRpb25cbiAgICAgICAgLy8gd291bGQgY2hhbmdlLCBob3dldmVyIGlmIHdlIGFyZSBwZXJmb3JtaW5nIHNvcnRpbmcgdGhlbiBpdCB3b3VsZCBub3RcbiAgICAgICAgLy8gc28gd2UgbXVzdCBlbnN1cmUgd2UgdXBkYXRlIGV2ZXJ5dGltZSB0aGUgZGF0YXNldCBjaGFuZ2VzLlxuICAgICAgICBpZiAodGhpcy5fcmVuZGVyZWRSYW5nZSkge1xuICAgICAgICAgICAgdGhpcy5vblJhbmdlQ2hhbmdlKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogUHJvdmlkZSBhIHRyYWNrQnkgZnVuY3Rpb24gdG8gb3B0aW1pemUgcmVuZGVyaW5nICovXG4gICAgQElucHV0KCkgdXhWaXJ0dWFsRm9yVHJhY2tCeTogVHJhY2tCeUZ1bmN0aW9uPFQ+IHwgdW5kZWZpbmVkID0gdGhpcy5kZWZhdWx0VHJhY2tCeTtcblxuICAgIC8qKiBUaGUgaW5zdGFuY2Ugb2YgdGhlIGRpZmZlciB3ZSBjcmVhdGUgKi9cbiAgICBwcml2YXRlIF9kaWZmZXI6IEl0ZXJhYmxlRGlmZmVyPFQ+O1xuXG4gICAgLyoqIEtlZXAgYSBsb2NhbCByZWZlcmVuY2UgdG8gdGhlIGRhdGFzZXQgKi9cbiAgICBwcml2YXRlIF9kYXRhc2V0OiBUW107XG5cbiAgICAvKiogU3RvcmUgdGhlIGN1cnJlbnRseSByZW5kZXJlZCByYW5nZSAqL1xuICAgIHByaXZhdGUgX3JlbmRlcmVkUmFuZ2U6IFZpcnR1YWxGb3JSYW5nZTtcblxuICAgIC8qKiBTdG9yZSBhIGxpc3Qgb2YgYWxsIHRoZSBjdXJyZW50bHkgcmVuZGVyZWQgaXRlbXMgKi9cbiAgICBwcml2YXRlIF9yZW5kZXJlZEl0ZW1zOiBUW107XG5cbiAgICAvKiogSW5kaWNhdGUgd2hldGhlciB3ZSBuZWVkIHRvIHBlcmZvcm0gYSB2aWV3IHVwZGF0ZSAqL1xuICAgIHByaXZhdGUgX2lzRGlydHk6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBTdG9yZSBhIGNhY2hlIG9mIHJlY2VudGx5IGRpc3Bvc2VkIHZpZXdzIGZvciByZXVzZSAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3RlbXBsYXRlQ2FjaGU6IEVtYmVkZGVkVmlld1JlZjxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+PltdID0gW107XG5cbiAgICAvKiogTGltaXQgdGhlIHNpemUgb2YgdGhlIGNhY2hlIGFzIGl0IGNhbiB1c2UgYSBsb3Qgb2YgbWVtb3J5ICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBfY2FjaGVTaXplOiBudW1iZXIgPSAyMDtcblxuICAgIC8qKiBVbnN1YnNjcmliZSBmcm9tIGFsbCBvYnNlcnZhYmxlcyAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX29uRGVzdHJveSA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgLy8gV2hpbGUgbWFya2VkIGFzIG9wdGlvbmFsLCBpdCBpc24ndC4gV2UgZG8gdGhpcyBzbyB3ZSBjYW4gcHJvdmlkZSBhIG1vcmUgaGVscGZ1bCBlcnJvciBtZXNzYWdlXG4gICAgICAgIGlmICghdGhpcy5fdmlydHVhbFNjcm9sbCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUaGUgXCJ1eFZpcnR1YWxGb3JcIiBkaXJlY3RpdmUgcmVxdWlyZXMgdGhlIFwidXhWaXJ0dWFsRm9yQ29udGFpbmVyXCIgZGlyZWN0aXZlIHRvIGJlIGFkZGVkIHRvIHRoZSBwYXJlbnQgZWxlbWVudC4nKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25Jbml0KCk6IHZvaWQge1xuICAgICAgICAvLyB1cGRhdGUgdGhlIFVJIHdoZW5ldmVyIHRoZSByYW5nZSBjaGFuZ2VzXG4gICAgICAgIHRoaXMuX3ZpcnR1YWxTY3JvbGwucmFuZ2UucGlwZShkaXN0aW5jdFVudGlsQ2hhbmdlZCh0aGlzLmlzUmFuZ2VTYW1lKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSkpLnN1YnNjcmliZShyYW5nZSA9PiB7XG4gICAgICAgICAgICB0aGlzLl9yZW5kZXJlZFJhbmdlID0gcmFuZ2U7XG4gICAgICAgICAgICB0aGlzLm9uUmFuZ2VDaGFuZ2UoKTtcbiAgICAgICAgICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgbmdEb0NoZWNrKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5faXNEaXJ0eSAmJiB0aGlzLl9kaWZmZXIpIHtcblxuICAgICAgICAgICAgLy8gY2hlY2sgaWYgdGhlcmUgYXJlYSBhbnkgY2hhbmdlc1xuICAgICAgICAgICAgY29uc3QgY2hhbmdlcyA9IHRoaXMuZ2V0Q2hhbmdlcygpO1xuXG4gICAgICAgICAgICBpZiAoY2hhbmdlcykge1xuICAgICAgICAgICAgICAgIHRoaXMuYXBwbHlDaGFuZ2VzKGNoYW5nZXMpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZUNvbnRleHRzKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG5vdyB0aGF0IHdlIGhhdmUgcmVuZGVyZWQgYW55IGNoYW5nZSB3ZSBzaG91bGQgc3RvcmUgdGhpcyBzbyB3ZSBkb24ndCBwZXJmb3JtIHVubmVlZGVkIHVwZGF0ZXNcbiAgICAgICAgICAgIHRoaXMuX2lzRGlydHkgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICAvKiogSWYgYW4gaXRlbVNpemUgaXMgbm90IHNwZWNpZmllZCB3ZSBuZWVkIHRvIGNhbGN1bGF0ZSBpdCAqL1xuICAgIGdldEhlaWdodChjb250ZXh0OiBULCBsZW5ndGg6IG51bWJlcik6IG51bWJlciB7XG5cbiAgICAgICAgLy8gY3JlYXRlIGEgdGVtcG9yYXJ5IHZpZXdcbiAgICAgICAgY29uc3QgdmlldyA9IHRoaXMuY3JlYXRlVmlldygwKTtcblxuICAgICAgICAvLyBzZXQgdGhlIGltcGxpY2l0IHZhbHVlIHRvIHRoZSBpdGVtIHZhbHVlXG4gICAgICAgIHZpZXcuY29udGV4dC4kaW1wbGljaXQgPSBjb250ZXh0O1xuICAgICAgICB2aWV3LmNvbnRleHQuY291bnQgPSBsZW5ndGg7XG4gICAgICAgIHZpZXcuY29udGV4dC5ldmVuID0gdHJ1ZTtcbiAgICAgICAgdmlldy5jb250ZXh0Lm9kZCA9IGZhbHNlO1xuICAgICAgICB2aWV3LmNvbnRleHQuZmlyc3QgPSB0cnVlO1xuICAgICAgICB2aWV3LmNvbnRleHQubGFzdCA9IGxlbmd0aCA9PT0gMTtcblxuICAgICAgICAvLyBydW4gY2hhbmdlIGRldGVjdGlvblxuICAgICAgICB2aWV3LmRldGVjdENoYW5nZXMoKTtcblxuICAgICAgICAvLyBnZXQgdGhlIHNpemUgb2YgdGhlIHZpZXdcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gdmlldy5yb290Tm9kZXNbMF0ub2Zmc2V0SGVpZ2h0O1xuXG4gICAgICAgIC8vIGRlc3Ryb3kgdGhlIHZpZXdcbiAgICAgICAgdGhpcy5fdmlld0NvbnRhaW5lclJlZi5yZW1vdmUoMCk7XG4gICAgICAgIHZpZXcuZGVzdHJveSgpO1xuXG4gICAgICAgIHJldHVybiBoZWlnaHQ7XG4gICAgfVxuXG4gICAgLyoqIERldGVybWluZSBpZiB0aGUgcmFuZ2UgaGFzIGNoYW5nZWQgKHBlcmZvcm1hbmNlIG9wdGltaXphdGlvbikgKi9cbiAgICBwcml2YXRlIGlzUmFuZ2VTYW1lKHByZXZpb3VzOiBWaXJ0dWFsRm9yUmFuZ2UsIGN1cnJlbnQ6IFZpcnR1YWxGb3JSYW5nZSk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gcHJldmlvdXMuc3RhcnQgPT09IGN1cnJlbnQuc3RhcnQgJiYgcHJldmlvdXMuZW5kID09PSBjdXJyZW50LmVuZDtcbiAgICB9XG5cbiAgICBwcml2YXRlIG9uUmFuZ2VDaGFuZ2UoKTogdm9pZCB7XG5cbiAgICAgICAgLy8gc3RvcmUgdGhlIHZpc2libGUgcmFuZ2VcbiAgICAgICAgdGhpcy5fcmVuZGVyZWRJdGVtcyA9IHRoaXMuX2RhdGFzZXQuc2xpY2UodGhpcy5fcmVuZGVyZWRSYW5nZS5zdGFydCwgdGhpcy5fcmVuZGVyZWRSYW5nZS5lbmQpO1xuXG4gICAgICAgIC8vIGNyZWF0ZSB0aGUgQW5ndWxhciBkaWZmZXIgaWYgd2UgaGF2ZW4ndCBwcmV2aW91c2x5IGRvbmUgc29cbiAgICAgICAgaWYgKCF0aGlzLl9kaWZmZXIpIHtcbiAgICAgICAgICAgIHRoaXMuX2RpZmZlciA9IHRoaXMuX2RpZmZlcnMuZmluZCh0aGlzLl9yZW5kZXJlZEl0ZW1zKS5jcmVhdGUodGhpcy51eFZpcnR1YWxGb3JUcmFja0J5KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG1hcmsgdGhlIHZpZXcgZm9yIHJlLXJlbmRlclxuICAgICAgICB0aGlzLl9pc0RpcnR5ID0gdHJ1ZTtcbiAgICB9XG5cbiAgICAvKiogRGV0ZXJtaW5lIHdoaWNoIGl0ZW1zIGhhdmUgY2hhbmdlZCAqL1xuICAgIHByaXZhdGUgZ2V0Q2hhbmdlcygpOiBJdGVyYWJsZUNoYW5nZXM8VD4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fZGlmZmVyLmRpZmYodGhpcy5fcmVuZGVyZWRJdGVtcyk7XG4gICAgfVxuXG4gICAgLyoqIEluc2VydCwgbW92ZSBhbmQgcmVtb3ZlIGFueSBpdGVtcyB3aXRoaW4gdGhlIHZpZXcgKi9cbiAgICBwcml2YXRlIGFwcGx5Q2hhbmdlcyhjaGFuZ2VzOiBJdGVyYWJsZUNoYW5nZXM8VD4pOiB2b2lkIHtcblxuICAgICAgICAvLyBHbyB0aHJvdWdoIGVhY2ggY2hhbmdlcyBhbmQgZWl0aGVyIGFkZCBvciByZWFycmFuZ2UgYWNjb3JkaW5nbHlcbiAgICAgICAgY2hhbmdlcy5mb3JFYWNoT3BlcmF0aW9uKChyZWNvcmQ6IEl0ZXJhYmxlQ2hhbmdlUmVjb3JkPFQ+LCBwcmV2aW91c0luZGV4OiBudW1iZXIsIGN1cnJlbnRJbmRleDogbnVtYmVyKSA9PiB7XG5cbiAgICAgICAgICAgIC8vIGNoZWNrIGlmIGEgbmV3IGl0ZW0gd2FzIGFkZGVkXG4gICAgICAgICAgICBpZiAocHJldmlvdXNJbmRleCA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIC8vIGNyZWF0ZSB0aGUgbmV3IGVtYmVkZGVkIHZpZXdcbiAgICAgICAgICAgICAgICBjb25zdCB2aWV3ID0gdGhpcy5jcmVhdGVWaWV3KGN1cnJlbnRJbmRleCk7XG5cbiAgICAgICAgICAgICAgICAvLyBzZXQgdGhlIGltcGxpY2l0IHZhbHVlIHRvIHRoZSBpdGVtIHZhbHVlXG4gICAgICAgICAgICAgICAgdmlldy5jb250ZXh0LiRpbXBsaWNpdCA9IHJlY29yZC5pdGVtO1xuXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGN1cnJlbnRJbmRleCA9PT0gbnVsbCkgeyAvLyBjaGVjayBpZiB0aGUgaXRlbSBzaG91bGQgYmUgcmVtb3ZlZFxuICAgICAgICAgICAgICAgIGNvbnN0IHZpZXcgPSB0aGlzLl92aWV3Q29udGFpbmVyUmVmLmRldGFjaChjdXJyZW50SW5kZXgpIGFzIEVtYmVkZGVkVmlld1JlZjxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+PjtcbiAgICAgICAgICAgICAgICBjb25zdCBpbmRleCA9IHRoaXMuX3ZpZXdDb250YWluZXJSZWYuaW5kZXhPZih2aWV3KTtcblxuICAgICAgICAgICAgICAgIC8vIGlmIHRoZXJlIGlzIHNwYWNlIGluIHRoZSBjYWNoZSB0aGVuIHN0b3JlIHRoZSBkZXRhY2hlZCB2aWV3XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuX3RlbXBsYXRlQ2FjaGUubGVuZ3RoIDwgdGhpcy5fY2FjaGVTaXplKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3RlbXBsYXRlQ2FjaGUucHVzaCh2aWV3KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBpbmRleCA9PT0gLTEgPyB2aWV3LmRlc3Ryb3koKSA6IHRoaXMuX3ZpZXdDb250YWluZXJSZWYucmVtb3ZlKGluZGV4KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgeyAvLyB0aGUgcG9zaXRpb24gb2YgdGhlIGl0ZW0gaGFzIGNoYW5nZWRcblxuICAgICAgICAgICAgICAgIC8vIGdldCB0aGUgdmlldyBmcm9tIGl0cyBjdXJyZW50IHBvc2l0aW9uXG4gICAgICAgICAgICAgICAgY29uc3QgdmlldyA9IHRoaXMuX3ZpZXdDb250YWluZXJSZWYuZ2V0KHByZXZpb3VzSW5kZXgpIGFzIEVtYmVkZGVkVmlld1JlZjxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+PjtcblxuICAgICAgICAgICAgICAgIC8vIG1vdmUgaXQgdG8gdGhlIG5ldyBwb3NpdGlvblxuICAgICAgICAgICAgICAgIHRoaXMuX3ZpZXdDb250YWluZXJSZWYubW92ZSh2aWV3LCBjdXJyZW50SW5kZXgpO1xuXG4gICAgICAgICAgICAgICAgLy8gdXBkYXRlIHRoZSBpbXBsaWNpdCB2YWx1ZSAodGhlIHJlc3Qgd2lsbCBzdGF5IHRoZSBzYW1lKVxuICAgICAgICAgICAgICAgIHZpZXcuY29udGV4dC4kaW1wbGljaXQgPSByZWNvcmQuaXRlbTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gRW5zdXJlIHRoZSBpbXBsaWNpdCB2YWx1ZSBpcyBjb3JyZWN0IGZvciBhbnkgaXRlbXMgd2hvc2UgaWRlbnRpdHkgY2hhbmdlZFxuICAgICAgICBjaGFuZ2VzLmZvckVhY2hJZGVudGl0eUNoYW5nZSgocmVjb3JkOiBJdGVyYWJsZUNoYW5nZVJlY29yZDxUPikgPT4ge1xuICAgICAgICAgICAgY29uc3QgdmlldyA9IHRoaXMuX3ZpZXdDb250YWluZXJSZWYuZ2V0KHJlY29yZC5jdXJyZW50SW5kZXgpIGFzIEVtYmVkZGVkVmlld1JlZjxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+PjtcblxuICAgICAgICAgICAgaWYgKHZpZXcpIHtcbiAgICAgICAgICAgICAgICB2aWV3LmNvbnRleHQuJGltcGxpY2l0ID0gcmVjb3JkLml0ZW07XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMudXBkYXRlQ29udGV4dHMoKTtcbiAgICB9XG5cbiAgICB1cGRhdGVDb250ZXh0cygpOiB2b2lkIHtcblxuICAgICAgICAvLyB1cGRhdGUgYWxsIHRoZSBvdGhlciBjb250ZXh0IHByb3BlcnRpZXNcbiAgICAgICAgZm9yIChsZXQgaWR4ID0gMDsgaWR4IDwgdGhpcy5fdmlld0NvbnRhaW5lclJlZi5sZW5ndGg7IGlkeCsrKSB7XG5cbiAgICAgICAgICAgIC8vIGdldCB0aGUgdmlldyBhdCBhIGdpdmVuIHBvc2l0aW9uXG4gICAgICAgICAgICBjb25zdCB2aWV3ID0gdGhpcy5fdmlld0NvbnRhaW5lclJlZi5nZXQoaWR4KSBhcyBFbWJlZGRlZFZpZXdSZWY8VmlydHVhbEZvck9mQ29udGV4dDxUPj47XG5cbiAgICAgICAgICAgIC8vIHVwZGF0ZSB0aGUgcHJvcGVydGllc1xuICAgICAgICAgICAgdmlldy5jb250ZXh0LmluZGV4ID0gdGhpcy5fcmVuZGVyZWRSYW5nZS5zdGFydCArIGlkeDtcbiAgICAgICAgICAgIHZpZXcuY29udGV4dC5jb3VudCA9IHRoaXMuX2RhdGFzZXQubGVuZ3RoO1xuICAgICAgICAgICAgdmlldy5jb250ZXh0LmZpcnN0ID0gdmlldy5jb250ZXh0LmluZGV4ID09PSAwO1xuICAgICAgICAgICAgdmlldy5jb250ZXh0Lmxhc3QgPSB2aWV3LmNvbnRleHQuaW5kZXggPT09IHZpZXcuY29udGV4dC5jb3VudCAtIDE7XG4gICAgICAgICAgICB2aWV3LmNvbnRleHQuZXZlbiA9IHZpZXcuY29udGV4dC5pbmRleCAlIDIgPT09IDA7XG4gICAgICAgICAgICB2aWV3LmNvbnRleHQub2RkID0gIXZpZXcuY29udGV4dC5ldmVuO1xuXG4gICAgICAgICAgICAvLyB1cGRhdGUgdGhlIHBvc2l0aW9uIGluIHRoZSBET01cbiAgICAgICAgICAgIHZpZXcucm9vdE5vZGVzLmZvckVhY2goKG5vZGU6IEhUTUxFbGVtZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5fcmVuZGVyZXIuc2V0U3R5bGUobm9kZSwgJ3Bvc2l0aW9uJywgJ2Fic29sdXRlJyk7XG4gICAgICAgICAgICAgICAgdGhpcy5fcmVuZGVyZXIuc2V0U3R5bGUobm9kZSwgJ3dpZHRoJywgJzEwMCUnKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9yZW5kZXJlci5zZXRTdHlsZShub2RlLCAndG9wJywgJzAnKTtcbiAgICAgICAgICAgICAgICB0aGlzLl9yZW5kZXJlci5zZXRTdHlsZShub2RlLCAndHJhbnNmb3JtJywgYHRyYW5zbGF0ZVkoJHt2aWV3LmNvbnRleHQuaW5kZXggKiB0aGlzLl92aXJ0dWFsU2Nyb2xsLml0ZW1TaXplfXB4YCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgdmlldy5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGNyZWF0ZVZpZXcoaW5kZXg6IG51bWJlcik6IEVtYmVkZGVkVmlld1JlZjxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+PiB7XG5cbiAgICAgICAgLy8gZ2V0IGEgY2hlY2tlZCBFbWJlZGRlZFZpZXdSZWYgaXMgdGhlcmUgaXMgb25lXG4gICAgICAgIGNvbnN0IGNhY2hlZFRlbXBsYXRlID0gdGhpcy5fdGVtcGxhdGVDYWNoZS5wb3AoKTtcblxuICAgICAgICBpZiAoY2FjaGVkVGVtcGxhdGUpIHtcblxuICAgICAgICAgICAgLy8gcmVwbGFjZSBleGlzdGluZyBjb250ZXh0IHdpdGggdGhlIGRlZmF1bHRzXG4gICAgICAgICAgICBjYWNoZWRUZW1wbGF0ZS5jb250ZXh0LiRpbXBsaWNpdCA9IG51bGw7XG4gICAgICAgICAgICBjYWNoZWRUZW1wbGF0ZS5jb250ZXh0LmluZGV4ID0gLTE7XG4gICAgICAgICAgICBjYWNoZWRUZW1wbGF0ZS5jb250ZXh0LmNvdW50ID0gLTE7XG4gICAgICAgICAgICBjYWNoZWRUZW1wbGF0ZS5jb250ZXh0LmZpcnN0ID0gZmFsc2U7XG4gICAgICAgICAgICBjYWNoZWRUZW1wbGF0ZS5jb250ZXh0Lmxhc3QgPSBmYWxzZTtcbiAgICAgICAgICAgIGNhY2hlZFRlbXBsYXRlLmNvbnRleHQuZXZlbiA9IGZhbHNlO1xuICAgICAgICAgICAgY2FjaGVkVGVtcGxhdGUuY29udGV4dC5vZGQgPSBmYWxzZTtcblxuICAgICAgICAgICAgLy8gaW5zZXJ0IHRoZSB2aWV3XG4gICAgICAgICAgICB0aGlzLl92aWV3Q29udGFpbmVyUmVmLmluc2VydChjYWNoZWRUZW1wbGF0ZSwgaW5kZXgpO1xuXG4gICAgICAgICAgICAvLyByZXR1cm4gdGhlIGNhY2hlZCBFbWJlZGRlZFZpZXdSZWZcbiAgICAgICAgICAgIHJldHVybiBjYWNoZWRUZW1wbGF0ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG90aGVyd2lzZSBjcmVhdGUgYSBuZXcgdmlldyBhbmQgaW5zZXJ0IGl0XG4gICAgICAgIHJldHVybiB0aGlzLl92aWV3Q29udGFpbmVyUmVmLmNyZWF0ZUVtYmVkZGVkVmlldzxWaXJ0dWFsRm9yT2ZDb250ZXh0PFQ+Pih0aGlzLl90ZW1wbGF0ZVJlZiwge1xuICAgICAgICAgICAgJGltcGxpY2l0OiBudWxsLFxuICAgICAgICAgICAgaW5kZXg6IC0xLFxuICAgICAgICAgICAgY291bnQ6IC0xLFxuICAgICAgICAgICAgZmlyc3Q6IGZhbHNlLFxuICAgICAgICAgICAgbGFzdDogZmFsc2UsXG4gICAgICAgICAgICBldmVuOiBmYWxzZSxcbiAgICAgICAgICAgIG9kZDogZmFsc2UsXG4gICAgICAgIH0sIGluZGV4KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGRlZmF1bHRUcmFja0J5KGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gaW5kZXg7XG4gICAgfVxuXG59XG5cbi8qKiBXZSB3YW50IHRvIHN1cHBseSB0aGUgc2FtZSBwcm9wZXJ0aWVzIGFzIGBuZ0ZvcmAgKi9cbmV4cG9ydCBpbnRlcmZhY2UgVmlydHVhbEZvck9mQ29udGV4dDxUPiB7XG4gICAgJGltcGxpY2l0OiBUO1xuICAgIGluZGV4OiBudW1iZXI7XG4gICAgY291bnQ6IG51bWJlcjtcbiAgICBmaXJzdDogYm9vbGVhbjtcbiAgICBsYXN0OiBib29sZWFuO1xuICAgIGV2ZW46IGJvb2xlYW47XG4gICAgb2RkOiBib29sZWFuO1xufVxuIl19