@transunion-ui/tablejs
Version:
Tablejs ========
1 lines • 519 kB
Source Map (JSON)
{"version":3,"file":"transunion-ui-tablejs.mjs","sources":["../../../projects/tablejs/src/lib/components/horiz-resize-grip/horiz-resize-grip.component.ts","../../../projects/tablejs/src/lib/components/horiz-resize-grip/horiz-resize-grip.component.html","../../../projects/tablejs/src/lib/components/reorder-grip/reorder-grip.component.ts","../../../projects/tablejs/src/lib/components/reorder-grip/reorder-grip.component.html","../../../projects/tablejs/src/lib/components/drag-and-drop-ghost/drag-and-drop-ghost.component.ts","../../../projects/tablejs/src/lib/components/drag-and-drop-ghost/drag-and-drop-ghost.component.html","../../../projects/tablejs/src/lib/shared/classes/tablejs-grid-proxy.ts","../../../projects/tablejs/src/lib/shared/classes/events/column-reorder-event.ts","../../../projects/tablejs/src/lib/shared/classes/events/column-resize-event.ts","../../../projects/tablejs/src/lib/shared/classes/events/grid-event.ts","../../../projects/tablejs/src/lib/services/grid/grid.service.ts","../../../projects/tablejs/src/lib/services/directive-registration/directive-registration.service.ts","../../../projects/tablejs/src/lib/directives/virtual-for/virtual-for.directive.ts","../../../projects/tablejs/src/lib/components/scroll-prev-spacer/scroll-prev-spacer.component.ts","../../../projects/tablejs/src/lib/components/scroll-prev-spacer/scroll-prev-spacer.component.html","../../../projects/tablejs/src/lib/shared/classes/events/scroll-viewport-event.ts","../../../projects/tablejs/src/lib/services/scroll-dispatcher/scroll-dispatcher.service.ts","../../../projects/tablejs/src/lib/services/operating-system/operating-system.service.ts","../../../projects/tablejs/src/lib/directives/scroll-viewport/scroll-viewport.directive.ts","../../../projects/tablejs/src/lib/directives/grid/grid.directive.ts","../../../projects/tablejs/src/lib/directives/grid-row/grid-row.directive.ts","../../../projects/tablejs/src/lib/directives/resizable-grip/resizable-grip.directive.ts","../../../projects/tablejs/src/lib/directives/infinite-scroll/infinite-scroll.directive.ts","../../../projects/tablejs/src/lib/components/grid/grid.component.ts","../../../projects/tablejs/src/lib/components/grid/grid.component.html","../../../projects/tablejs/src/lib/directives/editable-cell/editable-cell.directive.ts","../../../projects/tablejs/src/lib/directives/reorder-grip/reorder-grip.directive.ts","../../../projects/tablejs/src/lib/directives/reorder-col/reorder-col.directive.ts","../../../projects/tablejs/src/lib/directives/data-col-classes/data-col-classes.directive.ts","../../../projects/tablejs/src/lib/directives/data-col-class/data-col-class.directive.ts","../../../projects/tablejs/src/lib/directives/hide-column-if/hide-column-if.directive.ts","../../../projects/tablejs/src/lib/tablejs.module.ts","../../../projects/tablejs/src/lib/filterAndSort/comparators/comparator.ts","../../../projects/tablejs/src/lib/filterAndSort/options/sort-direction.ts","../../../projects/tablejs/src/lib/filterAndSort/services/filter-sort.service.ts","../../../projects/tablejs/src/lib/filterAndSort/options/sort-options.ts","../../../projects/tablejs/src/lib/filterAndSort/comparators/match-type.ts","../../../projects/tablejs/src/lib/filterAndSort/options/filter-options.ts","../../../projects/tablejs/src/lib/filterAndSort/comparators/sort-comparator.ts","../../../projects/tablejs/src/lib/filterAndSort/comparators/filter-comparator.ts","../../../projects/tablejs/src/lib/filterAndSort/filter-and-sort.module.ts","../../../projects/tablejs/src/lib/shared/classes/scrolling/range.ts","../../../projects/tablejs/src/public-api.ts","../../../projects/tablejs/src/transunion-ui-tablejs.ts"],"sourcesContent":["import { Component, OnInit, ViewEncapsulation } from '@angular/core';\n\n@Component({\n selector: 'tablejs-horiz-resize-grip',\n templateUrl: './horiz-resize-grip.component.html',\n styleUrls: ['./horiz-resize-grip.component.scss'],\n host: { class: 'resize-grip' },\n encapsulation: ViewEncapsulation.None\n})\nexport class HorizResizeGripComponent {\n\n constructor() { }\n\n}\n","<i class=\"fas fa-angle-left fa-xs\"></i><i class=\"fas fa-angle-right fa-xs\"></i>","import { Component, OnInit, ViewEncapsulation } from '@angular/core';\n\n@Component({\n selector: 'tablejs-reorder-grip',\n templateUrl: './reorder-grip.component.html',\n styleUrls: ['./reorder-grip.component.scss'],\n host: { class: 'col-dots-container' },\n encapsulation: ViewEncapsulation.None\n})\nexport class ReorderGripComponent {\n\n constructor() { }\n\n}\n","<span class=\"dots-3\"></span>\n<span class=\"dots-3\"></span>\n<span class=\"dots-3\"></span>\n<span class=\"dots-3\"></span>","import { AfterViewInit, ChangeDetectorRef, Component, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';\n\n@Component({\n selector: 'tablejs-drag-and-drop-ghost',\n templateUrl: './drag-and-drop-ghost.component.html',\n styleUrls: ['./drag-and-drop-ghost.component.scss'],\n encapsulation: ViewEncapsulation.None,\n host: { class: 'drag-and-drop-ghost' }\n})\nexport class DragAndDropGhostComponent implements AfterViewInit {\n\n @ViewChild('ref', {read: ViewContainerRef}) public ref: any;\n public left: number = 0;\n public top: number = 0;\n private _templateToLoad: TemplateRef<any>;\n private _contextToLoad: object | null = null;\n\n constructor(public viewContainerRef: ViewContainerRef, public cdr: ChangeDetectorRef) { }\n\n ngAfterViewInit(): void {\n this.ref.clear();\n if (this._templateToLoad) {\n this.ref.createEmbeddedView(this._templateToLoad, this._contextToLoad);\n this.cdr.detectChanges();\n }\n }\n\n public updateView(template: TemplateRef<any>, context: object | null = null): void {\n this._templateToLoad = template;\n this._contextToLoad = context;\n }\n\n getTransform(): string {\n return 'translate(' + this.left + 'px, ' + this.top + 'px';\n }\n\n}\n","<div class=\"drag-and-drop-ghost\" [ngStyle]=\"{ 'transform': getTransform() }\">\n <div #ref style=\"display: none;\"></div>\n</div>\n","export class TablejsGridProxy {\n static GRID_COUNT = 0;\n constructor() {\n }\n}\n","import { IColumnReorderEvent } from './../../interfaces/events/i-column-reorder-event';\n\nexport class ColumnReorderEvent implements IColumnReorderEvent {\n\n public static readonly ON_REORDER: string = 'onReorder';\n public static readonly ON_REORDER_START: string = 'onReorderStart';\n public static readonly ON_REORDER_END: string = 'onReorderEnd';\n\n public pointerEvent: any;\n public columnDragged: Element;\n public columnHovered: Element;\n public type: string;\n\n constructor() {}\n}\n","import { IColumnResizeEvent } from './../../interfaces/events/i-column-resize-event';\n\nexport class ColumnResizeEvent implements IColumnResizeEvent {\n\n public static readonly ON_RESIZE: string = 'onResize';\n public static readonly ON_RESIZE_START: string = 'onResizeStart';\n public static readonly ON_RESIZE_END: string = 'onResizeEnd';\n\n public pointerEvent: any;\n public columnWidth: number;\n public columnMinWidth: number;\n public classesBeingResized: string[];\n public type?: string;\n}\n","import { IGridEvent } from './../../interfaces/events/i-grid-event';\n\nexport class GridEvent implements IGridEvent {\n\n public static readonly ON_INITIALIZED: string = 'onInitialized';\n\n public gridComponent: any;\n public gridElement: HTMLElement;\n public type: string;\n}\n","import { Injectable } from '@angular/core';\nimport { IColumnData } from './../../shared/interfaces/i-column-data';\nimport { BehaviorSubject } from 'rxjs';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class GridService {\n\n public linkedDirectiveObjs: any = {};\n public containsInitialWidthSettings: BehaviorSubject<boolean | undefined> = new BehaviorSubject<boolean | undefined>(undefined);\n\n constructor() { }\n\n getParentTablejsGridDirective(el: HTMLElement | null): HTMLElement | null {\n while (el !== null && el.getAttribute('tablejsGrid') === null) {\n el = el.parentElement;\n }\n return el;\n }\n\n triggerHasInitialWidths(hasWidths: boolean): void {\n this.containsInitialWidthSettings.next(hasWidths);\n }\n}\n\ninterface ILinkedGrid {\n classWidths: number[];\n stylesByClass: any[];\n widthStyle: HTMLStyleElement;\n widthStyleFragment: DocumentFragment;\n reorderHighlightStyle: HTMLStyleElement;\n reorderHighlightStyleFragment: DocumentFragment;\n subGroupStyles: HTMLStyleElement[];\n subGroupFragments: DocumentFragment[];\n subGroupStyleObjs: any;\n gridOrder: number[];\n gridOrderStyles: HTMLStyleElement[];\n gridOrderFragments: DocumentFragment[];\n colDataGroups: IColumnData[][];\n}\n","import { Injectable } from '@angular/core';\nimport { GridService } from './../grid/grid.service';\nimport { ScrollViewportDirective } from './../../directives/scroll-viewport/scroll-viewport.directive';\nimport { VirtualForDirective } from './../../directives/virtual-for/virtual-for.directive';\nimport { IVirtualNexus } from './../../shared/interfaces/i-virtual-nexus';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class DirectiveRegistrationService {\n\n constructor(public gridService: GridService) { }\n\n private nexuses: IVirtualNexus[] = [];\n\n public setVirtualNexus(virtualForDirective: VirtualForDirective<any, any>, scrollViewportDirective: ScrollViewportDirective): IVirtualNexus {\n const nexus: IVirtualNexus = {\n scrollViewportDirective,\n virtualForDirective\n };\n this.nexuses.push(nexus);\n return nexus;\n }\n\n public clearVirtualNexus(nexus: IVirtualNexus): void {\n if (!nexus) {\n return;\n }\n nexus.scrollViewportDirective = null;\n nexus.virtualForDirective = null;\n const index: number = this.nexuses.indexOf(nexus);\n if (index === -1) {\n return;\n }\n this.nexuses.splice(index, 1);\n \n }\n\n public getVirtualNexusFromViewport(scrollViewportDirective: ScrollViewportDirective): IVirtualNexus {\n return this.nexuses.filter((nexus: IVirtualNexus) => nexus.scrollViewportDirective === scrollViewportDirective)[0];\n }\n\n public registerNodeAttributes(node: any) {\n if (node.getAttribute) {\n if (node.getAttribute('reordergrip') !== null) {\n this.registerReorderGripOnGridDirective(node, true);\n }\n if (node.getAttribute('resizablegrip') !== null) {\n this.registerResizableGripOnGridDirective(node, true);\n }\n if (node.getAttribute('tablejsDataColClasses') !== null) {\n this.registerDataColClassesOnGridDirective(node, true);\n }\n if (node.getAttribute('tablejsDataColClass') !== null) {\n this.registerDataColClassOnGridDirective(node, true);\n }\n if (node.getAttribute('tablejsGridRow') !== null) {\n this.registerRowsOnGridDirective(node, true);\n }\n }\n }\n\n public registerReorderGripOnGridDirective(node: HTMLElement, fromMutation: boolean = false) {\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n if (el !== null) {\n el['gridDirective'].addReorderGrip(node, fromMutation);\n }\n }\n\n public registerResizableGripOnGridDirective(node: HTMLElement, fromMutation: boolean = false) {\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n if (el !== null) {\n el['gridDirective'].addResizableGrip(node, fromMutation);\n }\n }\n\n public registerDataColClassesOnGridDirective(node: HTMLElement, fromMutation: boolean = false) {\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n (node as any).dataClasses = node.getAttribute('tablejsdatacolclasses')!.replace(new RegExp(' ', 'g'), '').split(',');\n el['gridDirective'].addColumnsWithDataClasses(node, fromMutation);\n }\n\n public registerDataColClassOnGridDirective(node: HTMLElement, fromMutation: boolean = false) {\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n if (!el) {\n return;\n }\n const cls: string | any | null = node.getAttribute('tablejsDataColClass');\n if (cls) {\n node.classList.add(cls);\n }\n const initialWidth = node.getAttribute('initialWidth');\n this.gridService.triggerHasInitialWidths(initialWidth ? true : false);\n el['gridDirective'].initialWidths[cls] = initialWidth;\n }\n\n public registerRowsOnGridDirective(node: HTMLElement, fromMutation: boolean = false) {\n node.classList.add('reorderable-table-row');\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n if (el !== null) {\n el['gridDirective'].addRow(node, fromMutation);\n }\n }\n\n public registerViewportOnGridDirective(node: HTMLElement): void {\n const el: HTMLElement | any | null = this.gridService.getParentTablejsGridDirective(node);\n if (el !== null) {\n el['gridDirective'].infiniteScrollViewports = [node];\n }\n }\n}\n","\nimport { Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, NgIterable, OnDestroy, TemplateRef, TrackByFunction, ViewContainerRef} from '@angular/core';\nimport { DirectiveRegistrationService } from './../../services/directive-registration/directive-registration.service';\nimport { IVirtualNexus } from './../../shared/interfaces/i-virtual-nexus';\nimport { Subject, Subscription } from 'rxjs';\nimport { ScrollViewportDirective } from './../scroll-viewport/scroll-viewport.directive';\nimport { Range } from './../../shared/classes/scrolling/range';\n\nexport class TablejsForOfContext<T, U extends NgIterable<T> = NgIterable<T>> {\n constructor(public $implicit: T, public tablejsVirtualForOf: U, public index: number, public count: number) {}\n\n get first(): boolean {\n return this.index === 0;\n }\n\n get last(): boolean {\n return this.index === this.count - 1;\n }\n\n get even(): boolean {\n return this.index % 2 === 0;\n }\n\n get odd(): boolean {\n return !this.even;\n }\n}\n\n@Directive({selector: '[tablejsVirtualFor][tablejsVirtualForOf]'})\nexport class VirtualForDirective<T, U extends NgIterable<T> = NgIterable<T>> implements DoCheck, OnDestroy {\n\n public virtualNexus: IVirtualNexus | null;\n public changes: Subject<any> = new Subject<any>();\n public rangeUpdatedSubscription$: Subscription;\n\n @Input()\n set tablejsVirtualForOf(tablejsVirtualForOf: U|undefined|null) {\n this._tablejsForOf = tablejsVirtualForOf;\n this._onRenderedDataChange();\n }\n\n public _tablejsForOf: U|undefined|null = null;\n private _lastTablejsForOf: U|undefined|null;\n private _differ: IterableDiffer<T>|null = null;\n private _tablejsVirtualForTrackBy: TrackByFunction<T> | undefined | null;\n private _scrollViewportDirective: ScrollViewportDirective | undefined | null;\n private _lastRange: Range;\n private _renderedItems: any[];\n private _parent: HTMLElement | undefined | null;\n /**\n * Asserts the correct type of the context for the template that `TablejsForOf` will render.\n *\n * The presence of this method is a signal to the Ivy template type-check compiler that the\n * `TablejsForOf` structural directive renders its template with a specific context type.\n */\n static ngTemplateContextGuard<T, U extends NgIterable<T>>(dir: VirtualForDirective<T, U>, ctx: any):\n ctx is TablejsForOfContext<T, U> {\n return true;\n }\n\n /**\n * A reference to the template that is stamped out for each item in the iterable.\n * @see [template reference variable](guide/template-reference-variables)\n */\n @Input()\n set tablejsVirtualForTemplate(value: TemplateRef<TablejsForOfContext<T, U>>) {\n if (value) {\n this._template = value;\n }\n }\n\n public get template(): TemplateRef<TablejsForOfContext<T, U>> {\n return this._template as TemplateRef<TablejsForOfContext<T, U>>;\n }\n\n @Input()\n get tablejsVirtualForTrackBy(): TrackByFunction<T> | undefined | null {\n return this._tablejsVirtualForTrackBy;\n }\n set tablejsVirtualForTrackBy(fn: TrackByFunction<T> | undefined | null) {\n this._tablejsVirtualForTrackBy = fn ?\n (index, item) => fn(index + (this._lastRange ? this._lastRange.extendedStartIndex! : 0), item) :\n undefined;\n\n this._onRenderedDataChange();\n }\n\n constructor(\n public _viewContainer: ViewContainerRef,\n public _template: TemplateRef<TablejsForOfContext<T, U>> | null,\n private _differs: IterableDiffers,\n private elementRef: ElementRef,\n private directiveRegistrationService: DirectiveRegistrationService) {\n \n this._parent = this._viewContainer.element.nativeElement.parentElement;\n\n while (this._parent !== null && this._parent !== undefined && (this._parent as any).scrollViewportDirective === undefined) {\n this._parent = this._parent.parentElement;\n }\n if (this._parent === null || this._parent === undefined) {\n throw Error('No scrollViewportDirective found for tablejsForOf. Declare a scrollViewport using the scrollViewportDirective.');\n } else {\n \n this._scrollViewportDirective = (this._parent as any).scrollViewportDirective;\n this.directiveRegistrationService.setVirtualNexus(this, this._scrollViewportDirective!);\n \n this._lastRange = this._scrollViewportDirective!.range;\n\n this.rangeUpdatedSubscription$ = this._scrollViewportDirective!.rangeUpdated.subscribe(rangeObj => {\n if (this.rangeIsDifferent(this._lastRange, rangeObj.range)) {\n this._lastRange = rangeObj.range;\n this._renderedItems = Array.from(this._tablejsForOf as Iterable<any>).slice(this._lastRange.extendedStartIndex!, this._lastRange.extendedEndIndex!);\n this._onRenderedDataChange(false);\n }\n });\n }\n }\n\n rangeIsDifferent(range1: Range, range2: Range): boolean {\n return range1.endIndex === range2.endIndex && range1.extendedEndIndex === range2.extendedEndIndex && range1.startIndex === range2.startIndex && range1.extendedStartIndex === range2.extendedStartIndex;\n }\n\n renderedItemsNeedUpdate(): boolean {\n return this._renderedItems.length !== this._lastRange.extendedEndIndex! - this._lastRange.extendedStartIndex!;\n }\n\n private _onRenderedDataChange(updateRenderedItems: boolean = true) {\n if (!this._renderedItems) {\n return;\n }\n if (updateRenderedItems) {\n this._renderedItems = Array.from(this._tablejsForOf as Iterable<any>).slice(this._lastRange.extendedStartIndex!, this._lastRange.extendedEndIndex!);\n }\n if (!this._differ) {\n this._differ = this._differs.find(this._renderedItems).create((index, item) => {\n return this.tablejsVirtualForTrackBy ? this.tablejsVirtualForTrackBy(index, item) : item;\n });\n }\n }\n\n ngDoCheck() {\n this.updateItems();\n }\n\n updateItems(): void {\n if (this._differ) {\n const scrollToOrigin = this._tablejsForOf !== this._lastTablejsForOf;\n let diffChanges: IterableChanges<any> | null = null;\n\n if (this.renderedItemsNeedUpdate()) {\n this._onRenderedDataChange();\n }\n\n try {\n diffChanges = this._differ.diff(this._renderedItems);\n } catch {\n this._differ = this._differs.find(this._renderedItems).create((index, item) => {\n return this.tablejsVirtualForTrackBy ? this.tablejsVirtualForTrackBy(index, item) : item;\n });\n }\n\n if (scrollToOrigin) {\n this._lastTablejsForOf = this._tablejsForOf;\n }\n if (diffChanges || scrollToOrigin) {\n this.changes.next({ tablejsForOf: this._tablejsForOf, scrollToOrigin });\n }\n }\n }\n\n ngOnDestroy(): void {\n this._lastTablejsForOf = null;\n this._tablejsForOf = null;\n this._differ = null;\n this._scrollViewportDirective = null;\n this._renderedItems = [];\n this._template = null;\n this._tablejsVirtualForTrackBy = null;\n if (this._parent) {\n (this._parent as any).scrollViewportDirective = null;\n this._parent = null;\n } \n\n if (this.rangeUpdatedSubscription$) {\n this.rangeUpdatedSubscription$.unsubscribe();\n }\n \n }\n}\n","import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';\n\n@Component({\n selector: 'tablejs-scroll-prev-spacer',\n templateUrl: './scroll-prev-spacer.component.html',\n styleUrls: ['./scroll-prev-spacer.component.scss']\n})\nexport class ScrollPrevSpacerComponent implements OnDestroy {\n\n @ViewChild('template', {static: true}) public template: any;\n constructor(public elementRef: ElementRef) { }\n\n ngOnDestroy(): void {\n this.template = null;\n }\n\n}\n","\n<ng-template #template>\n <tr tablejsPrevSpacer style=\"display: block; position: relative;\"></tr>\n</ng-template>\n","export class ScrollViewportEvent {\n public static readonly ON_ITEM_ADDED: string = 'onItemAdded';\n public static readonly ON_ITEM_REMOVED: string = 'onItemRemoved';\n public static readonly ON_ITEM_UPDATED: string = 'onItemUpdated';\n public static readonly ON_RANGE_UPDATED: string = 'onRangeUpdated';\n public static readonly ON_VIEWPORT_SCROLLED: string = 'onViewportScrolled';\n public static readonly ON_VIEWPORT_READY: string = 'onViewportReady';\n public static readonly ON_VIEWPORT_INITIALIZED: string = 'onViewportInitialized';\n}\n","import { EventEmitter, Injectable } from '@angular/core';\nimport { ScrollViewportEvent } from './../../shared/classes/events/scroll-viewport-event';\nimport { Range } from './../../shared/classes/scrolling/range';\nimport { ScrollViewportDirective } from './../../directives/scroll-viewport/scroll-viewport.directive';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ScrollDispatcherService {\n\n constructor() { }\n\n public dispatchAddItemEvents(eventEmitter: EventEmitter<any>, element: Node, i: number, viewport: ScrollViewportDirective, viewportElement: HTMLElement): void {\n eventEmitter.emit({\n element,\n index: i,\n viewport,\n viewportElement\n });\n const itemAddedEvent = new CustomEvent(ScrollViewportEvent.ON_ITEM_ADDED, {\n detail: {\n element,\n index: i,\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(itemAddedEvent);\n }\n\n public dispatchUpdateItemEvents(eventEmitter: EventEmitter<any>, element: Node, index: number, viewport: ScrollViewportDirective, viewportElement: HTMLElement): void {\n eventEmitter.emit({\n element,\n index,\n viewport,\n viewportElement\n });\n const itemUpdatedEvent = new CustomEvent(ScrollViewportEvent.ON_ITEM_UPDATED, {\n detail: {\n element,\n index,\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(itemUpdatedEvent);\n }\n\n public dispatchRemoveItemEvents(eventEmitter: EventEmitter<any>, element: Node, i: number, viewport: ScrollViewportDirective, viewportElement: HTMLElement): void {\n eventEmitter.emit({\n element,\n index: i,\n viewport,\n viewportElement\n });\n const itemRemovedEvent = new CustomEvent(ScrollViewportEvent.ON_ITEM_REMOVED, {\n detail: {\n element,\n index: i,\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(itemRemovedEvent);\n }\n\n public dispatchViewportReadyEvents(eventEmitter: EventEmitter<any>, viewport: ScrollViewportDirective, viewportElement: HTMLElement) {\n eventEmitter.emit({\n viewport,\n viewportElement\n });\n const viewportReadyEvent = new CustomEvent(ScrollViewportEvent.ON_VIEWPORT_READY, {\n detail: {\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(viewportReadyEvent);\n }\n\n public dispatchViewportInitializedEvents(eventEmitter: EventEmitter<any>, viewport: ScrollViewportDirective, viewportElement: HTMLElement) {\n eventEmitter.emit({\n viewport,\n viewportElement\n });\n const viewportInitializedEvent = new CustomEvent(ScrollViewportEvent.ON_VIEWPORT_INITIALIZED, {\n detail: {\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(viewportInitializedEvent);\n }\n\n public dispatchRangeUpdateEvents(eventEmitter: EventEmitter<any>, range: Range, viewport: ScrollViewportDirective, viewportElement: HTMLElement) {\n eventEmitter.emit({\n range,\n viewport,\n viewportElement\n });\n const rangeUpdatedEvent = new CustomEvent(ScrollViewportEvent.ON_ITEM_ADDED, {\n detail: {\n range,\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(rangeUpdatedEvent);\n }\n\n public dispatchViewportScrolledEvents(eventEmitter: EventEmitter<any>, scrollTop: number, overflow: number, viewport: ScrollViewportDirective, viewportElement: HTMLElement) {\n eventEmitter.emit({\n scrollTop,\n firstItemOverflow: overflow,\n viewport,\n viewportElement\n });\n const viewportScrolledEvent = new CustomEvent(ScrollViewportEvent.ON_ITEM_ADDED, {\n detail: {\n scrollTop,\n firstItemOverflow: overflow,\n viewport,\n viewportElement\n }\n });\n viewportElement.dispatchEvent(viewportScrolledEvent);\n }\n}\n","import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class OperatingSystemService {\n\n constructor() { }\n\n getOS() {\n const userAgent = window.navigator.userAgent;\n const platform = window.navigator.platform;\n const macosPlatforms: any[] = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'];\n const windowsPlatforms: any[] = ['Win32', 'Win64', 'Windows', 'WinCE'];\n const iosPlatforms: any[] = ['iPhone', 'iPad', 'iPod'];\n let os: string | null = null;\n\n if (macosPlatforms.indexOf(platform) !== -1) {\n os = 'Mac OS';\n } else if (iosPlatforms.indexOf(platform) !== -1) {\n os = 'iOS';\n } else if (windowsPlatforms.indexOf(platform) !== -1) {\n os = 'Windows';\n } else if (/Android/.test(userAgent)) {\n os = 'Android';\n } else if (!os && /Linux/.test(platform)) {\n os = 'Linux';\n }\n\n return os;\n }\n\n isMac(): boolean {\n return this.getOS() === 'Mac OS' || this.getOS() === 'iOS';\n }\n isPC(): boolean {\n return this.getOS() === 'Windows';\n }\n}\n","import { AfterViewInit, Directive, ChangeDetectorRef, ContentChild,\n ElementRef, EmbeddedViewRef, EventEmitter, Inject, Input, OnInit, Output, TemplateRef, ViewRef, OnDestroy, Renderer2, RendererFactory2} from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\nimport { GridService } from './../../services/grid/grid.service';\nimport { DirectiveRegistrationService } from './../../services/directive-registration/directive-registration.service';\nimport { Range } from './../../shared/classes/scrolling/range';\nimport { IScrollOptions } from './../../shared/interfaces/scrolling/i-scroll-options';\nimport { ScrollDispatcherService } from './../../services/scroll-dispatcher/scroll-dispatcher.service';\nimport { GridDirective } from './../../directives/grid/grid.directive';\nimport { TablejsForOfContext } from './../../directives/virtual-for/virtual-for.directive';\nimport { IVirtualNexus } from './../../shared/interfaces/i-virtual-nexus';\nimport { OperatingSystemService } from './../../services/operating-system/operating-system.service';\nimport { ScrollPrevSpacerComponent } from '../../components/scroll-prev-spacer/scroll-prev-spacer.component';\nimport { Subscription, take } from 'rxjs';\n\n@Directive({\n selector: '[tablejsScrollViewport], [tablejsscrollviewport], [tablejs-scroll-viewport]',\n host: { style: 'contain: content;'}\n})\nexport class ScrollViewportDirective implements AfterViewInit, OnDestroy, OnInit {\n\n @ContentChild('templateRef', { static: true }) public templateRef: TemplateRef<any> | null = null;\n\n @Input() templateID: string | null = '';\n @Input() generateCloneMethod: ((template: HTMLElement, items: any[], index: number) => Node) | null = null;\n private _arrowUpSpeed: string | number = 1;\n get arrowUpSpeed(): string | number {\n return Number(this._arrowUpSpeed);\n }\n @Input() set arrowUpSpeed(value: string | number) {\n this._arrowUpSpeed = Number(value);\n }\n\n private _arrowDownSpeed: string | number = 1;\n get arrowDownSpeed(): string | number {\n return Number(this._arrowDownSpeed);\n }\n @Input() set arrowDownSpeed(value: string | number) {\n this._arrowDownSpeed = Number(value);\n }\n\n private _preItemOverflow: string | number = 1;\n get preItemOverflow(): string | number {\n return Number(this._preItemOverflow);\n }\n @Input() set preItemOverflow(value: string | number) {\n this._preItemOverflow = Number(value);\n }\n\n private _postItemOverflow: string | number = 1;\n get postItemOverflow(): string | number {\n return Number(this._postItemOverflow);\n }\n @Input() set postItemOverflow(value: string | number) {\n this._postItemOverflow = Number(value);\n }\n\n private _itemLoadLimit: string | number = Infinity;\n get itemLoadLimit(): string | number {\n return Number(this._itemLoadLimit);\n }\n @Input() set itemLoadLimit(value: string | number) {\n this._itemLoadLimit = Number(value);\n }\n\n public timeoutID: any;\n\n items: any[] | null = null;\n\n // Custom Elements Inputs\n @Input() templateid: string | null = null;\n @Input() preitemoverflow: number = 1;\n @Input() postitemoverflow: number = 1;\n @Input() arrowupspeed: number = 1;\n @Input() arrowdownspeed: number = 1;\n @Input() itemloadlimit: number = Infinity;\n @Input() fillViewportScrolling: any;\n\n @Output() itemAdded: EventEmitter<any> = new EventEmitter<any>();\n @Output() itemRemoved: EventEmitter<any> = new EventEmitter<any>();\n @Output() itemUpdated: EventEmitter<any> = new EventEmitter<any>();\n @Output() rangeUpdated: EventEmitter<any> = new EventEmitter<any>();\n @Output() viewportScrolled: EventEmitter<any> = new EventEmitter<any>();\n @Output() viewportReady: EventEmitter<any> = new EventEmitter<any>();\n @Output() viewportInitialized: EventEmitter<any> = new EventEmitter<any>();\n\n private containerHeight: number | null = null;\n private heightLookup: any = {};\n private itemVisibilityLookup: any = {};\n public listElm: HTMLElement | null = null;\n public listContent: HTMLElement | null = null;\n public prevSpacer: HTMLElement | null = null;\n public postSpacer: HTMLElement | null = null;\n public gridDirective: GridDirective | null = null;\n public virtualForChangesSubscription$: Subscription;\n public preGridInitializeSubscription$: Subscription;\n public pauseViewportRenderUpdates: boolean = false;\n\n public range: Range = { startIndex: 0, endIndex: 1, extendedStartIndex: 0, extendedEndIndex: 1 };\n public lastRange: Range = { startIndex: this.range.startIndex, endIndex: this.range.endIndex, extendedStartIndex: this.range.extendedStartIndex, extendedEndIndex: this.range.extendedEndIndex };\n public lastScrollTop: number = 0;\n public currentScrollTop: number = 0;\n public currentScrollChange: number = 0;\n public template: HTMLElement | null = null;\n private estimatedFullContentHeight: number = 0;\n private estimatedPreListHeight: number = 0;\n private estimatedPostListHeight: number = 0;\n private totalItemsCounted: number = 0;\n private totalHeightCount: number = 0;\n private itemName: string = '';\n private avgItemHeight: number | undefined;\n private overflowHeightCount: number = 0;\n public scrollChangeByFirstIndexedItem: number = 0;\n private lastVisibleItemHeight: number = Infinity;\n private adjustedStartIndex: number | null = null;\n private forcedEndIndex: number | undefined = undefined;\n private placeholderObject: any = {};\n\n private postItemOverflowCount: number = -1;\n private preItemOverflowCount: number = -1;\n private lastVisibleItemOverflow: number = 0;\n private preOverflowHeight: number = 0;\n private mouseIsOverViewport: boolean = false;\n private lastHeight: number = 0;\n\n private observer: MutationObserver | null = null;\n private handleMouseOver: Function | null = null;\n private handleMouseOut: Function | null = null;\n private handleKeyDown: ((e: KeyboardEvent) => void) | null = null;\n private handleListContentScroll: ((this: HTMLElement, e: Event) => void) | undefined | null;\n private cloneFromTemplateRef: boolean = false;\n private viewportHasScrolled: boolean = false;\n private templateContext: TablejsForOfContext<any, any> | null = null;\n\n public virtualNexus: IVirtualNexus | null = null;\n\n private _cloneMethod: ((template: HTMLElement, items: any[], index: number) => Node) | null = null;\n private renderer: Renderer2;\n\n constructor(\n public elementRef: ElementRef,\n public gridService: GridService,\n @Inject(DOCUMENT) private document: any,\n private directiveRegistrationService: DirectiveRegistrationService,\n private scrollDispatcherService: ScrollDispatcherService,\n private operatingSystem: OperatingSystemService,\n private cdr: ChangeDetectorRef | null,\n private rendererFactory: RendererFactory2\n ) {\n this.renderer = this.rendererFactory.createRenderer(null, null);\n this.elementRef.nativeElement.scrollViewportDirective = this;\n }\n\n public handleScroll(e: Event) {\n\n e.preventDefault();\n\n this.currentScrollTop = this.listContent!.scrollTop;\n this.currentScrollChange = this.currentScrollTop - this.lastScrollTop;\n this.scrollChangeByFirstIndexedItem += this.currentScrollChange;\n this.lastVisibleItemOverflow -= this.currentScrollChange;\n\n const newRange = this.getRangeChange(this.scrollChangeByFirstIndexedItem);\n this.updateScrollFromRange(newRange);\n\n this.scrollDispatcherService.dispatchViewportScrolledEvents(this.viewportScrolled, this.lastScrollTop, this.scrollChangeByFirstIndexedItem, this, this.elementRef.nativeElement);\n\n }\n\n private registerViewportToElement() {\n this.elementRef.nativeElement.scrollViewport = this;\n }\n\n private attachMutationObserver(): void {\n const ths: any = this;\n this.observer = new MutationObserver((mutations: MutationRecord[]) => {\n mutations.forEach((mutation: MutationRecord) => {\n ths.updateMutations(mutation);\n });\n });\n\n this.observer.observe(this.listContent!, {\n // configure it to listen to attribute changes\n attributes: true,\n subtree: true,\n childList: true\n });\n }\n\n private updateMutations(mutation: MutationRecord): void {\n if (mutation.type === 'childList') {\n const addedNodes = Array.from(mutation.addedNodes);\n addedNodes.forEach(node => {\n this.directiveRegistrationService.registerNodeAttributes(node);\n this.getChildNodes(node);\n });\n }\n }\n\n private getChildNodes(node: Node) {\n node.childNodes.forEach(childNode => {\n this.directiveRegistrationService.registerNodeAttributes(childNode);\n if (childNode.childNodes) {\n this.getChildNodes(childNode);\n }\n });\n }\n\n public registerCustomElementsInputs(viewport: HTMLElement) {\n this.templateID = viewport.getAttribute('templateID');\n this.preItemOverflow = Number(viewport.getAttribute('preItemOverflow'));\n this.postItemOverflow = Number(viewport.getAttribute('postItemOverflow'));\n this.itemLoadLimit = Number(viewport.getAttribute('itemLoadLimit'));\n this.arrowUpSpeed = Number(viewport.getAttribute('arrowUpSpeed'));\n this.arrowDownSpeed = Number(viewport.getAttribute('arrowDownSpeed'));\n this.fillViewportScrolling = viewport.getAttribute('fillViewportScrolling');\n }\n\n private convertCustomElementsVariables() {\n if (this.templateid) {\n this.templateID = this.templateid;\n }\n if (this.preitemoverflow) {\n this.preItemOverflow = Number(this.preitemoverflow);\n }\n if (this.postitemoverflow) {\n this.postItemOverflow = Number(this.postitemoverflow);\n }\n if (this.arrowdownspeed) {\n this.arrowDownSpeed = Number(this.arrowdownspeed);\n }\n if (this.arrowupspeed) {\n this.arrowUpSpeed = Number(this.arrowupspeed);\n }\n if (this.itemloadlimit !== null) {\n this.itemLoadLimit = Number(this.itemloadlimit);\n }\n }\n\n private createTBodies() {\n this.listElm = this.elementRef.nativeElement;\n let body: HTMLElement | null = this.listElm!.getElementsByTagName('tbody')[0];\n if (body) {\n body = body.getAttribute('tablejsViewport') !== null ? body : null;\n }\n\n this.listContent = body ? body : document.createElement('tbody');\n this.listContent.setAttribute('tablejsListContent', '');\n this.listContent.setAttribute('tablejsViewport', '');\n this.listContent.style.display = 'block';\n this.listContent.style.position = 'relative';\n this.listContent.style.height = '350px';\n this.listContent.style.overflowY = 'auto';\n this.listElm!.appendChild(this.listContent);\n\n if (this.fillViewportScrolling !== undefined && this.fillViewportScrolling !== null) {\n const coverBody = document.createElement('tbody');\n coverBody.style.display = 'block';\n coverBody.style.position = 'absolute';\n coverBody.style.width = '100%';\n coverBody.style.height = '100%';\n coverBody.style.overflow = 'auto';\n coverBody.style.pointerEvents = 'none';\n coverBody.style.visibility = 'false';\n this.listElm!.appendChild(coverBody);\n }\n\n this.directiveRegistrationService.registerViewportOnGridDirective(this.listContent);\n\n const componentRef = this.virtualNexus!.virtualForDirective!._viewContainer.createComponent<ScrollPrevSpacerComponent>(ScrollPrevSpacerComponent);\n this.virtualNexus!.virtualForDirective!._viewContainer.detach(0);\n const ref: EmbeddedViewRef<any> = this.virtualNexus!.virtualForDirective!._viewContainer.createEmbeddedView(componentRef.instance.template, undefined, 0);\n componentRef.destroy();\n this.prevSpacer = ref.rootNodes[0];\n\n this.postSpacer = document.createElement('tr');\n this.postSpacer.setAttribute('tablejsPostSpacer', '');\n this.postSpacer.style.display = 'block';\n this.postSpacer.style.position = 'relative';\n this.listContent.appendChild(this.postSpacer);\n }\n\n private addScrollHandler(): void {\n this.listContent!.addEventListener('scroll', this.handleListContentScroll = (e: any) => {\n this.handleScroll(e);\n });\n }\n\n public rerenderRowAt(index: number, updateScrollPosition: boolean = false): void {\n if (!this.viewportHasScrolled) {\n return;\n }\n const ind = index - this.adjustedStartIndex!;\n const itemName: string = 'item' + index;\n\n if (ind > this.items!.length - 1 || this.itemVisibilityLookup[this.itemName] !== true) {\n return;\n }\n\n const indexMap: any = {};\n for (let i = 1; i < this.virtualNexus!.virtualForDirective!._viewContainer.length; i++) {\n indexMap[(this.virtualNexus!.virtualForDirective!._viewContainer.get(i) as EmbeddedViewRef<any>).rootNodes[0].index] = i;\n };\n const detachedRef: ViewRef | null = this.virtualNexus!.virtualForDirective!._viewContainer.detach(indexMap[index]);\n const child: HTMLElement = (detachedRef as EmbeddedViewRef<any>).rootNodes[0];\n detachedRef!.destroy();\n \n this.templateContext = new TablejsForOfContext<any, any>(this.items![index], this.virtualNexus!.virtualForDirective!._tablejsForOf, index, this.items!.length);\n const ref: EmbeddedViewRef<any> = this.virtualNexus!.virtualForDirective!._viewContainer.createEmbeddedView(this.virtualNexus!.virtualForDirective!._template!, this.templateContext, indexMap[index]);\n this.virtualNexus!.virtualForDirective!._viewContainer.move(ref, indexMap[index]);\n let clone: any = ref.rootNodes[0];\n clone.index = index;\n this.cdr!.detectChanges();\n\n this.scrollDispatcherService.dispatchRemoveItemEvents(this.itemRemoved, child, index, this, this.elementRef.nativeElement);\n\n const lookupHeight: number = clone.offsetHeight;\n const oldHeight: number = this.heightLookup[itemName];\n this.heightLookup[itemName] = lookupHeight;\n\n clone.lastHeight = lookupHeight;\n\n this.addResizeSensor(clone, index);\n\n if (oldHeight) {\n this.updateEstimatedHeightFromResize(oldHeight, lookupHeight);\n } else {\n this.updateEstimatedHeight(lookupHeight);\n }\n\n if (updateScrollPosition) {\n this.refreshViewport();\n }\n\n this.scrollDispatcherService.dispatchUpdateItemEvents(this.itemUpdated, clone, index, this, this.elementRef.nativeElement);\n this.scrollDispatcherService.dispatchAddItemEvents(this.itemAdded, clone, index, this, this.elementRef.nativeElement);\n }\n\n private viewportRendered() {\n this.virtualNexus = this.directiveRegistrationService.getVirtualNexusFromViewport(this);\n\n if (this.virtualNexus && this.virtualNexus.virtualForDirective) {\n this.items = this.virtualNexus.virtualForDirective._tablejsForOf;\n\n this.virtualForChangesSubscription$ = this.virtualNexus.virtualForDirective.changes.subscribe(item => {\n const isTheSameArray = this.items === item.tablejsForOf;\n this.items = item.tablejsForOf;\n\n const scrollToOptions = { index: 0, scrollAfterIndexedItem: 0 };\n if (isTheSameArray) {\n scrollToOptions.index = this.range.startIndex as number;\n scrollToOptions.scrollAfterIndexedItem = this.scrollChangeByFirstIndexedItem;\n\n // array has changed...rerender current elements\n const listChildren = Array.from(this.listContent!.childNodes);\n } else {\n this.updateItems(item.tablejsForOf, scrollToOptions);\n }\n });\n }\n\n this.createTBodies();\n this.addScrollHandler();\n\n if (this.items && (this.generateCloneMethod || this.virtualNexus.virtualForDirective!._template)) {\n this.initScroll({\n items: this.items,\n generateCloneMethod: this._cloneMethod!\n });\n }\n this.scrollDispatcherService.dispatchViewportReadyEvents(this.viewportReady, this, this.elementRef.nativeElement);\n }\n\n public scrollToBottom(): void {\n this.range.startIndex = this.items!.length;\n this.scrollToExact(this.range.startIndex, 0);\n }\n\n public scrollToTop(): void {\n this.scrollToExact(0, 0);\n }\n\n public pageUp(): void {\n let heightCount: number = this.scrollChangeByFirstIndexedItem;\n if (this.range.startIndex === 0) {\n this.scrollToExact(0, 0);\n return;\n }\n for (let i = this.range.startIndex! - 1; i >= 0; i--) {\n const lookupHeight: number = this.heightLookup['item' + i] ? this.heightLookup['item' + i] : this.avgItemHeight;\n heightCount += lookupHeight;\n if (heightCount >= this.containerHeight! || i === 0) {\n const overflowDifference: number = heightCount >= this.containerHeight! ? heightCount - this.containerHeight! : 0;\n this.scrollToExact(i, overflowDifference);\n break;\n }\n }\n }\n\n public pageDown(): void {\n this.range.startIndex = this.range.endIndex! - 1;\n const overflowDifference: number = this.heightLookup['item' + (this.range.endIndex! - 1).toString()] - this.lastVisibleItemOverflow;\n this.scrollToExact(this.range.startIndex, overflowDifference);\n }\n\n private addArrowListeners() {\n this.elementRef.nativeElement.addEventListener('mouseenter', this.handleMouseOver = (e: MouseEvent) => {\n this.mouseIsOverViewport = true;\n });\n \n this.elementRef.nativeElement.addEventListener('mouseleave', this.handleMouseOut = (e: MouseEvent) => {\n this.mouseIsOverViewport = false;\n });\n\n document.addEventListener('keydown', this.handleKeyDown = (e: KeyboardEvent) => {\n\n if (this.mouseIsOverViewport) {\n\n const isMac = this.operatingSystem.isMac();\n\n switch (e.code) {\n case 'ArrowDown':\n if (isMac && e.metaKey) {\n e.preventDefault();\n this.scrollToBottom();\n } else {\n e.preventDefault();\n this.range.startIndex! += Number(this.arrowDownSpeed);\n this.scrollToExact(this.range.startIndex!, 0);\n }\n break;\n case 'ArrowUp':\n if (isMac && e.metaKey) {\n e.preventDefault();\n this.scrollToTop();\n } else {\n if (this.scrollChangeByFirstIndexedItem === 0) {\n e.preventDefault();\n this.range.startIndex! -= Number(this.arrowUpSpeed);\n this.scrollToExact(this.range.startIndex!, 0);\n } else {\n e.preventDefault();\n this.scrollChangeByFirstIndexedItem = 0;\n this.scrollToExact(this.range.startIndex!, 0);\n }\n }\n break;\n case 'PageDown':\n e.preventDefault();\n this.pageDown();\n break;\n case 'PageUp':\n e.preventDefault();\n this.pageUp();\n break;\n case 'End':\n e.preventDefault();\n this.scrollToBottom();\n break;\n case 'Home':\n e.preventDefault();\n this.scrollToTop();\n break;\n }\n\n }\n });\n }\n\n\n public ngAfterViewInit() {\n this.gridDirective = (this.gridService.getParentTablejsGridDirective(this.elementRef.nativeElement)! as any)['gridDirective'];\n this.gridDirective!.scrollViewportDirective = this;\n\n this.preGridInitializeSubscription$ = this.gridDirective!.preGridInitialize.pipe(take(1)).subscribe(res => {\n this.cdr!.detectChanges();\n this.refreshContainerHeight();\n\n this.refreshViewport();\n // placeholder object is used only to initialize first grid render\n if (this.items![0] === this.placeholderObject) {\n this.items!.shift();\n }\n });\n\n this.viewportRendered();\n this.addArrowListeners();\n }\n\n public ngOnInit() {\n this.registerViewportToElement();\n this._cloneMethod = this.generateCloneMethod;\n }\n\n public ngOnDestroy() {\n this.listElm = null;\n if (this.virtualNexus && this.virtualNexus.virtualForDirective) {\n this.virtualNexus!.virtualForDirective!._viewContainer.detach(0);\n this.virtualNexus!.virtualForDirective!._viewContainer.clear();\n }\n this.items = [];\n this.elementRef.nativeElement.scrollViewport = null;\n this.templateRef = null;\n this._cloneMethod = null;\n this.generateCloneMethod = null;\n if (this.virtualNexus) {\n this.directiveRegistrationService.clearVirtualNexus(this.virtualNexus);\n this.virtualNexus.virtualForDirective = null;\n this.virtualNexus.scrollViewportDirective = null;\n this.virtualNexus = null;\n }\n \n clearTimeout(this.timeoutID);\n this.elementRef.nativeElement.removeEventListener('mouseenter', this.handleMouseOver);\n this.elementRef.nativeElement.removeEventListener('mouseleave', this.handleMouseOut);\n \n if (this.listContent) {\n this.listContent.removeEventListener('scroll', this.handleListContentScroll!);\n }\n this.handleListContentScroll = null;\n document.removeEventListener('keydown', this.handleKeyDown!);\n if (this.virtualForChangesSubscription$) {\n this.virtualForChangesSubscription$.unsubscribe();\n }\n if (this.preGridInitializeSubscription$) {\n this.preGridInitializeSubscription$.unsubscribe();\n }\n this.elementRef.nativeElement.scrollViewportDirective = null;\n }\n\n private setScrollSpacers(): void {\n\n const numItemsAfterShownList = this.items!.length - this.range.extendedEndIndex!;\n const numItemsBeforeShownList = this.adjustedStartIndex;\n\n const totalUnshownItems = numItemsBeforeShownList! + numItemsAfterShownList;\n\n const beforeItemHeightPercent = totalUnshownItems !== 0 ? numItemsBeforeShownList! / totalUnshownItems : 0;\n const afterItemHeightPercent = totalUnshownItems !== 0 ? numItemsAfterShownList / totalUnshownItems : 0;\n const remainingHeight = this.estimatedFullContentHeight - this.lastHeight;\n\n this.estimatedPreListHeight = Math.round(beforeItemHeightPercent * remainingHeight);\n this.estimatedPostListHeight = Math.round(afterItemHeightPercent * remainingHeight);\n\n // account for rounding both up\n this.estimatedPostListHeight = this.estimatedPostListHeight - (afterItemHeightPercent * remainingHeight) === 0.5 ? this.estimatedPostListHeight - 1 : this.estimatedPostListHeight;\n\n if (this.forcedEndIndex) {\n this.estimatedPreListHeight = 0;\n this.estimatedPostListHeight = 0;\n }\n\n this.prevSpacer!.style.height = this.estimatedPreListHeight.toString() + 'px';\n this.postSpacer!.style.height = this.estimatedPostListHeight.toString() + 'px';\n\n }\n\n private setHeightByListHeightDifference(liHeight: number, listHeight: number) {\n return liHeight - listHeight;\n }\n\n private removePreScrollItems(lastIndex: number, index: number) {\n if (lastIndex < index) {\n for (let i = lastIndex; i < index; i++) {\n const firstRef: ViewRef | null = this.virtualNexus!.virtualForDirective!._viewContainer.get(1);\n if (firstRef) {\n const firstChild = (firstRef as EmbeddedViewRef<any>).rootNodes[0];\n const itemName = 'item' + i;\n this.item