UNPKG

@swimlane/ngx-datatable

Version:

ngx-datatable is an Angular table grid component for presenting large and complex data.

1 lines 296 kB
{"version":3,"file":"swimlane-ngx-datatable.mjs","sources":["../../../../projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/dimensions-helper.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/column-changes.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/tree.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/camel-case.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/id.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/column-mode.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/selection.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/sort.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/contextmenu.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/prefixes.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/translate.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/selection.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/keys.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/sort-direction.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/sort.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/math.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html","../../../../projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/click.type.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/elm-from-point.ts","../../../../projects/swimlane/ngx-datatable/src/public-api.ts","../../../../projects/swimlane/ngx-datatable/src/swimlane-ngx-datatable.ts"],"sourcesContent":["import { Inject, Injectable } from '@angular/core';\nimport { DOCUMENT } from '@angular/common';\n\n/**\n * Gets the width of the scrollbar. Nesc for windows\n * http://stackoverflow.com/a/13382873/888165\n */\n@Injectable()\nexport class ScrollbarHelper {\n width: number = this.getWidth();\n\n constructor(@Inject(DOCUMENT) private document: any) {}\n\n getWidth(): number {\n const outer = this.document.createElement('div');\n outer.style.visibility = 'hidden';\n outer.style.width = '100px';\n outer.style.msOverflowStyle = 'scrollbar';\n this.document.body.appendChild(outer);\n\n const widthNoScroll = outer.offsetWidth;\n outer.style.overflow = 'scroll';\n\n const inner = this.document.createElement('div');\n inner.style.width = '100%';\n outer.appendChild(inner);\n\n const widthWithScroll = inner.offsetWidth;\n outer.parentNode.removeChild(outer);\n\n return widthNoScroll - widthWithScroll;\n }\n}\n","import { Inject, Injectable } from '@angular/core';\n\n/**\n * Gets the width of the scrollbar. Nesc for windows\n * http://stackoverflow.com/a/13382873/888165\n */\n@Injectable()\nexport class DimensionsHelper {\n getDimensions(element: Element): ClientRect {\n return element.getBoundingClientRect();\n }\n}\n","import { Injectable } from '@angular/core';\n\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * service to make DatatableComponent aware of changes to\n * input bindings of DataTableColumnDirective\n */\n@Injectable()\nexport class ColumnChangesService {\n private columnInputChanges = new Subject<void>();\n\n get columnInputChanges$(): Observable<void> {\n return this.columnInputChanges.asObservable();\n }\n\n onInputChange(): void {\n this.columnInputChanges.next();\n }\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({ selector: '[ngx-datatable-footer-template]' })\nexport class DataTableFooterTemplateDirective {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Directive, Output, EventEmitter, ElementRef, HostBinding, NgZone, OnInit, OnDestroy } from '@angular/core';\n\n/**\n * Visibility Observer Directive\n *\n * Usage:\n *\n * \t\t<div\n * \t\t\tvisibilityObserver\n * \t\t\t(visible)=\"onVisible($event)\">\n * \t\t</div>\n *\n */\n@Directive({ selector: '[visibilityObserver]' })\nexport class VisibilityDirective implements OnInit, OnDestroy {\n @HostBinding('class.visible')\n isVisible: boolean = false;\n\n @Output() visible: EventEmitter<any> = new EventEmitter();\n\n timeout: any;\n\n constructor(private element: ElementRef, private zone: NgZone) {}\n\n ngOnInit(): void {\n this.runCheck();\n }\n\n ngOnDestroy(): void {\n clearTimeout(this.timeout);\n }\n\n onVisibilityChange(): void {\n // trigger zone recalc for columns\n this.zone.run(() => {\n this.isVisible = true;\n this.visible.emit(true);\n });\n }\n\n runCheck(): void {\n const check = () => {\n // https://davidwalsh.name/offsetheight-visibility\n const { offsetHeight, offsetWidth } = this.element.nativeElement;\n\n if (offsetHeight && offsetWidth) {\n clearTimeout(this.timeout);\n this.onVisibilityChange();\n } else {\n clearTimeout(this.timeout);\n this.zone.runOutsideAngular(() => {\n this.timeout = setTimeout(() => check(), 50);\n });\n }\n };\n\n this.timeout = setTimeout(() => check());\n }\n}\n","import { Directive, ElementRef, Input, Output, EventEmitter, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';\nimport { Subscription, fromEvent } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n/**\n * Draggable Directive for Angular2\n *\n * Inspiration:\n * https://github.com/AngularClass/angular2-examples/blob/master/rx-draggable/directives/draggable.ts\n * http://stackoverflow.com/questions/35662530/how-to-implement-drag-and-drop-in-angular2\n *\n */\n@Directive({ selector: '[draggable]' })\nexport class DraggableDirective implements OnDestroy, OnChanges {\n @Input() dragEventTarget: any;\n @Input() dragModel: any;\n @Input() dragX: boolean = true;\n @Input() dragY: boolean = true;\n\n @Output() dragStart: EventEmitter<any> = new EventEmitter();\n @Output() dragging: EventEmitter<any> = new EventEmitter();\n @Output() dragEnd: EventEmitter<any> = new EventEmitter();\n\n element: HTMLElement;\n isDragging: boolean = false;\n subscription: Subscription;\n\n constructor(element: ElementRef) {\n this.element = element.nativeElement;\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['dragEventTarget'] && changes['dragEventTarget'].currentValue && this.dragModel.dragging) {\n this.onMousedown(changes['dragEventTarget'].currentValue);\n }\n }\n\n ngOnDestroy(): void {\n this._destroySubscription();\n }\n\n onMouseup(event: MouseEvent): void {\n if (!this.isDragging) return;\n\n this.isDragging = false;\n this.element.classList.remove('dragging');\n\n if (this.subscription) {\n this._destroySubscription();\n this.dragEnd.emit({\n event,\n element: this.element,\n model: this.dragModel\n });\n }\n }\n\n onMousedown(event: MouseEvent): void {\n // we only want to drag the inner header text\n const isDragElm = (<HTMLElement>event.target).classList.contains('draggable');\n\n if (isDragElm && (this.dragX || this.dragY)) {\n event.preventDefault();\n this.isDragging = true;\n\n const mouseDownPos = { x: event.clientX, y: event.clientY };\n\n const mouseup = fromEvent(document, 'mouseup');\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup(ev));\n\n const mouseMoveSub = fromEvent(document, 'mousemove')\n .pipe(takeUntil(mouseup))\n .subscribe((ev: MouseEvent) => this.move(ev, mouseDownPos));\n\n this.subscription.add(mouseMoveSub);\n\n this.dragStart.emit({\n event,\n element: this.element,\n model: this.dragModel\n });\n }\n }\n\n move(event: MouseEvent, mouseDownPos: { x: number; y: number }): void {\n if (!this.isDragging) return;\n\n const x = event.clientX - mouseDownPos.x;\n const y = event.clientY - mouseDownPos.y;\n\n if (this.dragX) this.element.style.left = `${x}px`;\n if (this.dragY) this.element.style.top = `${y}px`;\n\n this.element.classList.add('dragging');\n\n this.dragging.emit({\n event,\n element: this.element,\n model: this.dragModel\n });\n }\n\n private _destroySubscription(): void {\n if (this.subscription) {\n this.subscription.unsubscribe();\n this.subscription = undefined;\n }\n }\n}\n","import {\n Directive,\n ElementRef,\n HostListener,\n Input,\n Output,\n EventEmitter,\n OnDestroy,\n AfterViewInit,\n Renderer2\n} from '@angular/core';\nimport { Subscription, fromEvent } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Directive({\n selector: '[resizeable]',\n host: {\n '[class.resizeable]': 'resizeEnabled'\n }\n})\nexport class ResizeableDirective implements OnDestroy, AfterViewInit {\n @Input() resizeEnabled: boolean = true;\n @Input() minWidth: number;\n @Input() maxWidth: number;\n\n @Output() resize: EventEmitter<any> = new EventEmitter();\n\n element: HTMLElement;\n subscription: Subscription;\n resizing: boolean = false;\n private resizeHandle: HTMLElement;\n\n constructor(element: ElementRef, private renderer: Renderer2) {\n this.element = element.nativeElement;\n }\n\n ngAfterViewInit(): void {\n const renderer2 = this.renderer;\n this.resizeHandle = renderer2.createElement('span');\n if (this.resizeEnabled) {\n renderer2.addClass(this.resizeHandle, 'resize-handle');\n } else {\n renderer2.addClass(this.resizeHandle, 'resize-handle--not-resizable');\n }\n renderer2.appendChild(this.element, this.resizeHandle);\n }\n\n ngOnDestroy(): void {\n this._destroySubscription();\n if (this.renderer.destroyNode) {\n this.renderer.destroyNode(this.resizeHandle);\n } else if (this.resizeHandle) {\n this.renderer.removeChild(this.renderer.parentNode(this.resizeHandle), this.resizeHandle);\n }\n }\n\n onMouseup(): void {\n this.resizing = false;\n\n if (this.subscription && !this.subscription.closed) {\n this._destroySubscription();\n this.resize.emit(this.element.clientWidth);\n }\n }\n\n @HostListener('mousedown', ['$event'])\n onMousedown(event: MouseEvent): void {\n const isHandle = (<HTMLElement>event.target).classList.contains('resize-handle');\n const initialWidth = this.element.clientWidth;\n const mouseDownScreenX = event.screenX;\n\n if (isHandle) {\n event.stopPropagation();\n this.resizing = true;\n\n const mouseup = fromEvent(document, 'mouseup');\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup());\n\n const mouseMoveSub = fromEvent(document, 'mousemove')\n .pipe(takeUntil(mouseup))\n .subscribe((e: MouseEvent) => this.move(e, initialWidth, mouseDownScreenX));\n\n this.subscription.add(mouseMoveSub);\n }\n }\n\n move(event: MouseEvent, initialWidth: number, mouseDownScreenX: number): void {\n const movementX = event.screenX - mouseDownScreenX;\n const newWidth = initialWidth + movementX;\n\n const overMinWidth = !this.minWidth || newWidth >= this.minWidth;\n const underMaxWidth = !this.maxWidth || newWidth <= this.maxWidth;\n\n if (overMinWidth && underMaxWidth) {\n this.element.style.width = `${newWidth}px`;\n }\n }\n\n private _destroySubscription() {\n if (this.subscription) {\n this.subscription.unsubscribe();\n this.subscription = undefined;\n }\n }\n}\n","import {\n Directive,\n Output,\n EventEmitter,\n ContentChildren,\n QueryList,\n KeyValueDiffers,\n AfterContentInit,\n OnDestroy,\n Inject\n} from '@angular/core';\nimport { DraggableDirective } from './draggable.directive';\nimport { DOCUMENT } from '@angular/common';\n\n@Directive({ selector: '[orderable]' })\nexport class OrderableDirective implements AfterContentInit, OnDestroy {\n @Output() reorder: EventEmitter<any> = new EventEmitter();\n @Output() targetChanged: EventEmitter<any> = new EventEmitter();\n\n @ContentChildren(DraggableDirective, { descendants: true })\n draggables: QueryList<DraggableDirective>;\n\n positions: any;\n differ: any;\n lastDraggingIndex: number;\n\n constructor(differs: KeyValueDiffers, @Inject(DOCUMENT) private document: any) {\n this.differ = differs.find({}).create();\n }\n\n ngAfterContentInit(): void {\n // HACK: Investigate Better Way\n this.updateSubscriptions();\n this.draggables.changes.subscribe(this.updateSubscriptions.bind(this));\n }\n\n ngOnDestroy(): void {\n this.draggables.forEach(d => {\n d.dragStart.unsubscribe();\n d.dragging.unsubscribe();\n d.dragEnd.unsubscribe();\n });\n }\n\n updateSubscriptions(): void {\n const diffs = this.differ.diff(this.createMapDiffs());\n\n if (diffs) {\n const subscribe = ({ currentValue, previousValue }: any) => {\n unsubscribe({ previousValue });\n\n if (currentValue) {\n currentValue.dragStart.subscribe(this.onDragStart.bind(this));\n currentValue.dragging.subscribe(this.onDragging.bind(this));\n currentValue.dragEnd.subscribe(this.onDragEnd.bind(this));\n }\n };\n\n const unsubscribe = ({ previousValue }: any) => {\n if (previousValue) {\n previousValue.dragStart.unsubscribe();\n previousValue.dragging.unsubscribe();\n previousValue.dragEnd.unsubscribe();\n }\n };\n\n diffs.forEachAddedItem(subscribe);\n // diffs.forEachChangedItem(subscribe.bind(this));\n diffs.forEachRemovedItem(unsubscribe);\n }\n }\n\n onDragStart(): void {\n this.positions = {};\n\n let i = 0;\n for (const dragger of this.draggables.toArray()) {\n const elm = dragger.element;\n const left = parseInt(elm.offsetLeft.toString(), 0);\n this.positions[dragger.dragModel.prop] = {\n left,\n right: left + parseInt(elm.offsetWidth.toString(), 0),\n index: i++,\n element: elm\n };\n }\n }\n\n onDragging({ element, model, event }: any): void {\n const prevPos = this.positions[model.prop];\n const target = this.isTarget(model, event);\n\n if (target) {\n if (this.lastDraggingIndex !== target.i) {\n this.targetChanged.emit({\n prevIndex: this.lastDraggingIndex,\n newIndex: target.i,\n initialIndex: prevPos.index\n });\n this.lastDraggingIndex = target.i;\n }\n } else if (this.lastDraggingIndex !== prevPos.index) {\n this.targetChanged.emit({\n prevIndex: this.lastDraggingIndex,\n initialIndex: prevPos.index\n });\n this.lastDraggingIndex = prevPos.index;\n }\n }\n\n onDragEnd({ element, model, event }: any): void {\n const prevPos = this.positions[model.prop];\n\n const target = this.isTarget(model, event);\n if (target) {\n this.reorder.emit({\n prevIndex: prevPos.index,\n newIndex: target.i,\n model\n });\n }\n\n this.lastDraggingIndex = undefined;\n element.style.left = 'auto';\n }\n\n isTarget(model: any, event: any): any {\n let i = 0;\n const x = event.x || event.clientX;\n const y = event.y || event.clientY;\n const targets = this.document.elementsFromPoint(x, y);\n\n for (const prop in this.positions) {\n // current column position which throws event.\n const pos = this.positions[prop];\n\n // since we drag the inner span, we need to find it in the elements at the cursor\n if (model.prop !== prop && targets.find((el: any) => el === pos.element)) {\n return {\n pos,\n i\n };\n }\n\n i++;\n }\n }\n\n private createMapDiffs(): { [key: string]: DraggableDirective } {\n return this.draggables.toArray().reduce((acc, curr) => {\n acc[curr.dragModel.$$id] = curr;\n return acc;\n }, {});\n }\n}\n","import { Directive, Input, Output, EventEmitter, HostBinding, HostListener, OnDestroy } from '@angular/core';\nimport { Observable, Subscription, fromEvent } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { MouseEvent } from '../events';\n\n@Directive({ selector: '[long-press]' })\nexport class LongPressDirective implements OnDestroy {\n @Input() pressEnabled: boolean = true;\n @Input() pressModel: any;\n @Input() duration: number = 500;\n\n @Output() longPressStart: EventEmitter<any> = new EventEmitter();\n @Output() longPressing: EventEmitter<any> = new EventEmitter();\n @Output() longPressEnd: EventEmitter<any> = new EventEmitter();\n\n pressing: boolean;\n isLongPressing: boolean;\n timeout: any;\n mouseX: number = 0;\n mouseY: number = 0;\n\n subscription: Subscription;\n\n @HostBinding('class.press')\n get press(): boolean {\n return this.pressing;\n }\n\n @HostBinding('class.longpress')\n get isLongPress(): boolean {\n return this.isLongPressing;\n }\n\n @HostListener('mousedown', ['$event'])\n onMouseDown(event: MouseEvent): void {\n // don't do right/middle clicks\n if (event.which !== 1 || !this.pressEnabled) return;\n\n // don't start drag if its on resize handle\n const target = <HTMLElement>event.target;\n if (target.classList.contains('resize-handle')) return;\n\n this.mouseX = event.clientX;\n this.mouseY = event.clientY;\n\n this.pressing = true;\n this.isLongPressing = false;\n\n const mouseup = fromEvent(document, 'mouseup');\n this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup());\n\n this.timeout = setTimeout(() => {\n this.isLongPressing = true;\n this.longPressStart.emit({\n event,\n model: this.pressModel\n });\n\n this.subscription.add(\n fromEvent(document, 'mousemove')\n .pipe(takeUntil(mouseup))\n .subscribe((mouseEvent: MouseEvent) => this.onMouseMove(mouseEvent))\n );\n\n this.loop(event);\n }, this.duration);\n\n this.loop(event);\n }\n\n onMouseMove(event: MouseEvent): void {\n if (this.pressing && !this.isLongPressing) {\n const xThres = Math.abs(event.clientX - this.mouseX) > 10;\n const yThres = Math.abs(event.clientY - this.mouseY) > 10;\n\n if (xThres || yThres) {\n this.endPress();\n }\n }\n }\n\n loop(event: MouseEvent): void {\n if (this.isLongPressing) {\n this.timeout = setTimeout(() => {\n this.longPressing.emit({\n event,\n model: this.pressModel\n });\n this.loop(event);\n }, 50);\n }\n }\n\n endPress(): void {\n clearTimeout(this.timeout);\n this.isLongPressing = false;\n this.pressing = false;\n this._destroySubscription();\n\n this.longPressEnd.emit({\n model: this.pressModel\n });\n }\n\n onMouseup(): void {\n this.endPress();\n }\n\n ngOnDestroy(): void {\n this._destroySubscription();\n }\n\n private _destroySubscription(): void {\n if (this.subscription) {\n this.subscription.unsubscribe();\n this.subscription = undefined;\n }\n }\n}\n","import {\n Component,\n Input,\n ElementRef,\n Output,\n EventEmitter,\n Renderer2,\n NgZone,\n OnInit,\n OnDestroy,\n HostBinding,\n ChangeDetectionStrategy\n} from '@angular/core';\n\nimport { MouseEvent } from '../../events';\n\n@Component({\n selector: 'datatable-scroller',\n template: ` <ng-content></ng-content> `,\n host: {\n class: 'datatable-scroll'\n },\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class ScrollerComponent implements OnInit, OnDestroy {\n @Input() scrollbarV: boolean = false;\n @Input() scrollbarH: boolean = false;\n\n @HostBinding('style.height.px')\n @Input()\n scrollHeight: number;\n\n @HostBinding('style.width.px')\n @Input()\n scrollWidth: number;\n\n @Output() scroll: EventEmitter<any> = new EventEmitter();\n\n scrollYPos: number = 0;\n scrollXPos: number = 0;\n prevScrollYPos: number = 0;\n prevScrollXPos: number = 0;\n element: any;\n parentElement: any;\n onScrollListener: any;\n\n private _scrollEventListener: any = null;\n\n constructor(private ngZone: NgZone, element: ElementRef, private renderer: Renderer2) {\n this.element = element.nativeElement;\n }\n\n ngOnInit(): void {\n // manual bind so we don't always listen\n if (this.scrollbarV || this.scrollbarH) {\n const renderer = this.renderer;\n this.parentElement = renderer.parentNode(renderer.parentNode(this.element));\n this._scrollEventListener = this.onScrolled.bind(this);\n this.parentElement.addEventListener('scroll', this._scrollEventListener);\n }\n }\n\n ngOnDestroy(): void {\n if (this._scrollEventListener) {\n this.parentElement.removeEventListener('scroll', this._scrollEventListener);\n this._scrollEventListener = null;\n }\n }\n\n setOffset(offsetY: number): void {\n if (this.parentElement) {\n this.parentElement.scrollTop = offsetY;\n }\n }\n\n onScrolled(event: MouseEvent): void {\n const dom: Element = <Element>event.currentTarget;\n requestAnimationFrame(() => {\n this.scrollYPos = dom.scrollTop;\n this.scrollXPos = dom.scrollLeft;\n this.updateOffset();\n });\n }\n\n updateOffset(): void {\n let direction: string;\n if (this.scrollYPos < this.prevScrollYPos) {\n direction = 'down';\n } else if (this.scrollYPos > this.prevScrollYPos) {\n direction = 'up';\n }\n\n this.scroll.emit({\n direction,\n scrollYPos: this.scrollYPos,\n scrollXPos: this.scrollXPos\n });\n\n this.prevScrollYPos = this.scrollYPos;\n this.prevScrollXPos = this.scrollXPos;\n }\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[ngx-datatable-group-header-template]'\n})\nexport class DatatableGroupHeaderTemplateDirective {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Input, Output, EventEmitter, Directive, TemplateRef, ContentChild } from '@angular/core';\nimport { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive';\n\n@Directive({ selector: 'ngx-datatable-group-header' })\nexport class DatatableGroupHeaderDirective {\n /**\n * Row height is required when virtual scroll is enabled.\n */\n @Input() rowHeight: number | ((group?: any, index?: number) => number) = 0;\n\n @Input('template')\n _templateInput: TemplateRef<any>;\n\n @ContentChild(DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true })\n _templateQuery: TemplateRef<any>;\n\n get template(): TemplateRef<any> {\n return this._templateInput || this._templateQuery;\n }\n\n /**\n * Track toggling of group visibility\n */\n @Output() toggle: EventEmitter<any> = new EventEmitter();\n\n /**\n * Toggle the expansion of a group\n */\n toggleExpandGroup(group: any): void {\n this.toggle.emit({\n type: 'group',\n value: group\n });\n }\n\n /**\n * Expand all groups\n */\n expandAllGroups(): void {\n this.toggle.emit({\n type: 'all',\n value: true\n });\n }\n\n /**\n * Collapse all groups\n */\n collapseAllGroups(): void {\n this.toggle.emit({\n type: 'all',\n value: false\n });\n }\n}\n","import { TableColumnProp } from '../types/table-column.type';\n\n// maybe rename this file to prop-getters.ts\n\nexport type ValueGetter = (obj: any, prop: TableColumnProp) => any;\n\n/**\n * Always returns the empty string ''\n */\nexport function emptyStringGetter(): string {\n return '';\n}\n\n/**\n * Returns the appropriate getter function for this kind of prop.\n * If prop == null, returns the emptyStringGetter.\n */\nexport function getterForProp(prop: TableColumnProp): ValueGetter {\n if (prop == null) {\n return emptyStringGetter;\n }\n\n if (typeof prop === 'number') {\n return numericIndexGetter;\n } else {\n // deep or simple\n if (prop.indexOf('.') !== -1) {\n return deepValueGetter;\n } else {\n return shallowValueGetter;\n }\n }\n}\n\n/**\n * Returns the value at this numeric index.\n * @param row array of values\n * @param index numeric index\n * @returns any or '' if invalid index\n */\nexport function numericIndexGetter(row: any[], index: number): any {\n if (row == null) {\n return '';\n }\n // mimic behavior of deepValueGetter\n if (!row || index == null) {\n return row;\n }\n\n const value = row[index];\n if (value == null) {\n return '';\n }\n return value;\n}\n\n/**\n * Returns the value of a field.\n * (more efficient than deepValueGetter)\n * @param obj object containing the field\n * @param fieldName field name string\n */\nexport function shallowValueGetter(obj: any, fieldName: string): any {\n if (obj == null) {\n return '';\n }\n if (!obj || !fieldName) {\n return obj;\n }\n\n const value = obj[fieldName];\n if (value == null) {\n return '';\n }\n return value;\n}\n\n/**\n * Returns a deep object given a string. zoo['animal.type']\n */\nexport function deepValueGetter(obj: any, path: string): any {\n if (obj == null) {\n return '';\n }\n if (!obj || !path) {\n return obj;\n }\n\n // check if path matches a root-level field\n // { \"a.b.c\": 123 }\n let current = obj[path];\n if (current !== undefined) {\n return current;\n }\n\n current = obj;\n const split = path.split('.');\n\n if (split.length) {\n for (let i = 0; i < split.length; i++) {\n current = current[split[i]];\n\n // if found undefined, return empty string\n if (current === undefined || current === null) {\n return '';\n }\n }\n }\n\n return current;\n}\n","import { getterForProp } from './column-prop-getters';\nimport { TableColumnProp } from '../types/table-column.type';\n\nexport type OptionalValueGetter = (row: any) => any | undefined;\nexport function optionalGetterForProp(prop: TableColumnProp): OptionalValueGetter {\n return prop && (row => getterForProp(prop)(row, prop));\n}\n\n/**\n * This functions rearrange items by their parents\n * Also sets the level value to each of the items\n *\n * Note: Expecting each item has a property called parentId\n * Note: This algorithm will fail if a list has two or more items with same ID\n * NOTE: This algorithm will fail if there is a deadlock of relationship\n *\n * For example,\n *\n * Input\n *\n * id -> parent\n * 1 -> 0\n * 2 -> 0\n * 3 -> 1\n * 4 -> 1\n * 5 -> 2\n * 7 -> 8\n * 6 -> 3\n *\n *\n * Output\n * id -> level\n * 1 -> 0\n * --3 -> 1\n * ----6 -> 2\n * --4 -> 1\n * 2 -> 0\n * --5 -> 1\n * 7 -> 8\n *\n *\n * @param rows\n *\n */\nexport function groupRowsByParents(rows: any[], from?: OptionalValueGetter, to?: OptionalValueGetter): any[] {\n if (from && to) {\n const nodeById = {};\n const l = rows.length;\n let node: TreeNode | null = null;\n\n nodeById[0] = new TreeNode(); // that's the root node\n\n const uniqIDs = rows.reduce((arr, item) => {\n const toValue = to(item);\n if (arr.indexOf(toValue) === -1) {\n arr.push(toValue);\n }\n return arr;\n }, []);\n\n for (let i = 0; i < l; i++) {\n // make TreeNode objects for each item\n nodeById[to(rows[i])] = new TreeNode(rows[i]);\n }\n\n for (let i = 0; i < l; i++) {\n // link all TreeNode objects\n node = nodeById[to(rows[i])];\n let parent = 0;\n const fromValue = from(node.row);\n if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) {\n parent = fromValue;\n }\n node.parent = nodeById[parent];\n node.row['level'] = node.parent.row['level'] + 1;\n node.parent.children.push(node);\n }\n\n let resolvedRows: any[] = [];\n nodeById[0].flatten(function () {\n resolvedRows = [...resolvedRows, this.row];\n }, true);\n\n return resolvedRows;\n } else {\n return rows;\n }\n}\n\nclass TreeNode {\n public row: any;\n public parent: any;\n public children: any[];\n\n constructor(row: any | null = null) {\n if (!row) {\n row = {\n level: -1,\n treeStatus: 'expanded'\n };\n }\n this.row = row;\n this.parent = null;\n this.children = [];\n }\n\n flatten(f: any, recursive: boolean) {\n if (this.row['treeStatus'] === 'expanded') {\n for (let i = 0, l = this.children.length; i < l; i++) {\n const child = this.children[i];\n f.apply(child, Array.prototype.slice.call(arguments, 2));\n if (recursive) child.flatten.apply(child, arguments);\n }\n }\n }\n}\n","/**\n * Converts strings from something to camel case\n * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase\n */\nexport function camelCase(str: string): string {\n // Replace special characters with a space\n str = str.replace(/[^a-zA-Z0-9 ]/g, ' ');\n // put a space before an uppercase letter\n str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');\n\n // Lower case first character and some other stuff\n str = str\n .replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '')\n .trim()\n .toLowerCase();\n\n // uppercase characters preceded by a space or number\n str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function (a, b, c) {\n return b.trim() + c.toUpperCase();\n });\n\n return str;\n}\n\n/**\n * Converts strings from camel case to words\n * http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text\n */\nexport function deCamelCase(str: string): string {\n return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase());\n}\n","/**\n * Creates a unique object id.\n * http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js\n */\nexport function id() {\n return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4);\n}\n","import { camelCase, deCamelCase } from './camel-case';\nimport { id } from './id';\nimport { getterForProp } from './column-prop-getters';\nimport { TableColumn } from '../types/table-column.type';\nimport { DataTableColumnDirective } from '../components/columns/column.directive';\n\n/**\n * Sets the column defaults\n */\nexport function setColumnDefaults(columns: TableColumn[]) {\n if (!columns) return;\n\n // Only one column should hold the tree view\n // Thus if multiple columns are provided with\n // isTreeColumn as true we take only the first one\n let treeColumnFound: boolean = false;\n\n for (const column of columns) {\n if (!column.$$id) {\n column.$$id = id();\n }\n\n // prop can be numeric; zero is valid not a missing prop\n // translate name => prop\n if (isNullOrUndefined(column.prop) && column.name) {\n column.prop = camelCase(column.name);\n }\n\n if (!column.$$valueGetter) {\n column.$$valueGetter = getterForProp(column.prop);\n }\n\n // format props if no name passed\n if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\n column.name = deCamelCase(String(column.prop));\n }\n\n if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\n column.name = ''; // Fixes IE and Edge displaying `null`\n }\n\n if (!column.hasOwnProperty('resizeable')) {\n column.resizeable = true;\n }\n\n if (!column.hasOwnProperty('sortable')) {\n column.sortable = true;\n }\n\n if (!column.hasOwnProperty('draggable')) {\n column.draggable = true;\n }\n\n if (!column.hasOwnProperty('canAutoResize')) {\n column.canAutoResize = true;\n }\n\n if (!column.hasOwnProperty('width')) {\n column.width = 150;\n }\n\n if (!column.hasOwnProperty('isTreeColumn')) {\n column.isTreeColumn = false;\n } else {\n if (column.isTreeColumn && !treeColumnFound) {\n // If the first column with isTreeColumn is true found\n // we mark that treeCoulmn is found\n treeColumnFound = true;\n } else {\n // After that isTreeColumn property for any other column\n // will be set as false\n column.isTreeColumn = false;\n }\n }\n }\n}\n\nexport function isNullOrUndefined<T>(value: T | null | undefined): value is null | undefined {\n return value === null || value === undefined;\n}\n\n/**\n * Translates templates definitions to objects\n */\nexport function translateTemplates(templates: DataTableColumnDirective[]): any[] {\n const result: any[] = [];\n for (const temp of templates) {\n const col: any = {};\n\n const props = Object.getOwnPropertyNames(temp);\n for (const prop of props) {\n col[prop] = temp[prop];\n }\n\n if (temp.headerTemplate) {\n col.headerTemplate = temp.headerTemplate;\n }\n\n if (temp.cellTemplate) {\n col.cellTemplate = temp.cellTemplate;\n }\n\n if (temp.summaryFunc) {\n col.summaryFunc = temp.summaryFunc;\n }\n\n if (temp.summaryTemplate) {\n col.summaryTemplate = temp.summaryTemplate;\n }\n\n result.push(col);\n }\n\n return result;\n}\n","export enum ColumnMode {\n standard = 'standard',\n flex = 'flex',\n force = 'force'\n}\n","export enum SelectionType {\n single = 'single',\n multi = 'multi',\n multiClick = 'multiClick',\n cell = 'cell',\n checkbox = 'checkbox'\n}\n","export enum SortType {\n single = 'single',\n multi = 'multi'\n}\n","export enum ContextmenuType {\n header = 'header',\n body = 'body'\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({ selector: '[ngx-datatable-header-template]' })\nexport class DataTableColumnHeaderDirective {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({ selector: '[ngx-datatable-cell-template]' })\nexport class DataTableColumnCellDirective {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({ selector: '[ngx-datatable-tree-toggle]' })\nexport class DataTableColumnCellTreeToggle {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Directive, TemplateRef, ContentChild, Input, OnChanges, SimpleChanges } from '@angular/core';\nimport { DataTableColumnHeaderDirective } from './column-header.directive';\nimport { DataTableColumnCellDirective } from './column-cell.directive';\nimport { DataTableColumnCellTreeToggle } from './tree.directive';\nimport { ColumnChangesService } from '../../services/column-changes.service';\nimport { TableColumnProp } from '../../types/table-column.type';\n\n@Directive({ selector: 'ngx-datatable-column' })\nexport class DataTableColumnDirective implements OnChanges {\n @Input() name: string;\n @Input() prop: TableColumnProp;\n @Input() frozenLeft: any;\n @Input() frozenRight: any;\n @Input() flexGrow: number;\n @Input() resizeable: boolean;\n @Input() comparator: any;\n @Input() pipe: any;\n @Input() sortable: boolean;\n @Input() draggable: boolean;\n @Input() canAutoResize: boolean;\n @Input() minWidth: number;\n @Input() width: number;\n @Input() maxWidth: number;\n @Input() checkboxable: boolean;\n @Input() headerCheckboxable: boolean;\n @Input() headerClass: string | ((data: any) => string | any);\n @Input() cellClass: string | ((data: any) => string | any);\n @Input() isTreeColumn: boolean;\n @Input() treeLevelIndent: number;\n @Input() summaryFunc: (cells: any[]) => any;\n @Input() summaryTemplate: TemplateRef<any>;\n\n @Input('cellTemplate')\n _cellTemplateInput: TemplateRef<any>;\n\n @ContentChild(DataTableColumnCellDirective, { read: TemplateRef, static: true })\n _cellTemplateQuery: TemplateRef<any>;\n\n get cellTemplate(): TemplateRef<any> {\n return this._cellTemplateInput || this._cellTemplateQuery;\n }\n\n @Input('headerTemplate')\n _headerTemplateInput: TemplateRef<any>;\n\n @ContentChild(DataTableColumnHeaderDirective, { read: TemplateRef, static: true })\n _headerTemplateQuery: TemplateRef<any>;\n\n get headerTemplate(): TemplateRef<any> {\n return this._headerTemplateInput || this._headerTemplateQuery;\n }\n\n @Input('treeToggleTemplate')\n _treeToggleTemplateInput: TemplateRef<any>;\n\n @ContentChild(DataTableColumnCellTreeToggle, { read: TemplateRef, static: true })\n _treeToggleTemplateQuery: TemplateRef<any>;\n\n get treeToggleTemplate(): TemplateRef<any> {\n return this._treeToggleTemplateInput || this._treeToggleTemplateQuery;\n }\n\n private isFirstChange = true;\n\n constructor(private columnChangesService: ColumnChangesService) {}\n\n ngOnChanges() {\n if (this.isFirstChange) {\n this.isFirstChange = false;\n } else {\n this.columnChangesService.onInputChange();\n }\n }\n}\n","import { Directive, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[ngx-datatable-row-detail-template]'\n})\nexport class DatatableRowDetailTemplateDirective {\n constructor(public template: TemplateRef<any>) {}\n}\n","import { Input, Output, EventEmitter, Directive, TemplateRef, ContentChild } from '@angular/core';\nimport { DatatableRowDetailTemplateDirective } from './row-detail-template.directive';\n\n@Directive({ selector: 'ngx-datatable-row-detail' })\nexport class DatatableRowDetailDirective {\n /**\n * The detail row height is required especially\n * when virtual scroll is enabled.\n */\n @Input() rowHeight: number | ((row?: any, index?: number) => number) = 0;\n\n @Input('template')\n _templateInput: TemplateRef<any>;\n\n @ContentChild(DatatableRowDetailTemplateDirective, { read: TemplateRef, static: true })\n _templateQuery: TemplateRef<any>;\n\n get template(): TemplateRef<any> {\n return this._templateInput || this._templateQuery;\n }\n\n /**\n * Row detail row visbility was toggled.\n */\n @Output() toggle: EventEmitter<any> = new EventEmitter();\n\n /**\n * Toggle the expansion of the row\n */\n toggleExpandRow(row: any): void {\n this.toggle.emit({\n type: 'row',\n value: row\n });\n }\n\n /**\n * API method to expand all the rows.\n */\n expandAllRows(): void {\n this.toggle.emit({\n type: 'all',\n value: true\n });\n }\n\n /**\n * API method to collapse all the rows.\n */\n collapseAllRows(): void {\n this.toggle.emit({\n type: 'all',\n value: false\n });\n }\n}\n","import { Input, Directive, TemplateRef, ContentChild } from '@angular/core';\nimport { DataTableFooterTemplateDirective } from './footer-template.directive';\n\n@Directive({ selector: 'ngx-datatable-footer' })\nexport class DatatableFooterDirective {\n @Input() footerHeight: number;\n @Input() totalMessage: string;\n @Input() selectedMessage: string | boolean;\n @Input() pagerLeftArrowIcon: string;\n @Input() pagerRightArrowIcon: string;\n @Input() pagerPreviousIcon: string;\n @Input() pagerNextIcon: string;\n\n @Input('template')\n _templateInput: TemplateRef<any>;\n\n @ContentChild(DataTableFooterTemplateDirective, { read: TemplateRef })\n _templateQuery: TemplateRef<any>;\n\n get template(): TemplateRef<any> {\n return this._templateInput || this._templateQuery;\n }\n}\n","/**\n * Returns the columns by pin.\n */\nexport function columnsByPin(cols: any[]) {\n const ret: { left: any; center: any; right: any } = {\n left: [],\n center: [],\n right: []\n };\n\n if (cols) {\n for (const col of cols) {\n if (col.frozenLeft) {\n ret.left.push(col);\n } else if (col.frozenRight) {\n ret.right.push(col);\n } else {\n ret.center.push(col);\n }\n }\n }\n\n return ret;\n}\n\n/**\n * Returns the widths of all group sets of a column\n */\nexport function columnGroupWidths(groups: any, all: any) {\n return {\n left: columnTotalWidth(groups.left),\n center: columnTotalWidth(groups.center),\n right: columnTotalWidth(groups.right),\n total: Math.floor(columnTotalWidth(all))\n };\n}\n\n/**\n * Calculates the total width of all columns and their groups\n */\nexport function columnTotalWidth(columns: any[], prop?: string) {\n let totalWidth = 0;\n\n if (columns) {\n for (const c of columns) {\n const has = prop && c[prop];\n const width = has ? c[prop] : c.width;\n totalWidth = totalWidth + parseFloat(width);\n }\n }\n\n return totalWidth;\n}\n\n/**\n * Calculates the total width of all columns and their groups\n */\nexport function columnsTotalWidth(columns: any, prop?: any) {\n let totalWidth = 0;\n\n for (const column of columns) {\n const has = prop && column[prop];\n totalWidth = totalWidth + (has ? column[prop] : column.width);\n }\n\n return totalWidth;\n}\n\nexport function columnsByPinArr(val: any) {\n const colsByPinArr = [];\n const colsByPin = columnsByPin(val);\n\n colsByPinArr.push({ type: 'left', columns: colsByPin['left'] });\n colsByPinArr.push({ type: 'center', columns: colsByPin['center'] });\n colsByPinArr.push({ type: 'right', columns: colsByPin['right'] });\n\n return colsByPinArr;\n}\n","/**\n * This object contains the cache of the various row heights that are present inside\n * the data table. Its based on Fenwick tree data structure that helps with\n * querying sums that have time complexity of log n.\n *\n * Fenwick Tree Credits: http://petr-mitrichev.blogspot.com/2013/05/fenwick-tree-range-updates.html\n * https://github.com/mikolalysenko/fenwick-tree\n *\n */\nexport class RowHeightCache {\n /**\n * Tree Array stores the cumulative information of the row heights to perform efficient\n * range queries and updates. Currently the tree is initialized to the base row\n * height instead of the detail row height.\n */\n private treeArray: number[] = [];\n\n /**\n * Clear the Tree array.\n */\n clearCache(): void {\n this.treeArray = [];\n }\n\n /**\n * Initialize the Fenwick tree with row Heights.\n *\n * @param rows The array of rows which contain the expanded status.\n * @param rowHeight The row height.\n * @param detailRowHeight The detail row height.\n */\n initCache(details: any): void {\n const { rows, rowHeight, detailRowHeight, externalVirtual, rowCount, rowIndexes, rowExpansions } = details;\n const isFn = typeof rowHeight === 'function';\n const isDetailFn = typeof detailRowHeight === 'function';\n\n if (!isFn && isNaN(rowHeight)) {\n throw new Error(`Row Height cache initialization failed. Please ensure that 'rowHeight' is a\n valid number or function value: (${rowHeight}) when 'scrollbarV' is enabled.`);\n }\n\n // Add this additional guard in case detailRowHeight is set to 'auto' as it wont work.\n if (!isDetailFn && isNaN(detailRowHeight)) {\n throw new Error(`Row Height cache initialization failed. Please ensure that 'detailRowHeight' is a\n valid number or function value: (${detailRowHeight}) when 'scrollbarV' is enabled.`);\n }\n\n const n = externalVirtual ? rowCount : rows.length;\n this.treeArray = new Array(n);\n\n for (let i = 0; i < n; ++i) {\n this.treeArray[i] = 0;\n }\n\n for (let i = 0; i < n; ++i) {\n const row = rows[i];\n let currentRowHeight = rowHeight;\n if (isFn) {\n currentRowHeight = rowHeight(row);\n }\n\n // Add the detail row height to the already expanded rows.\n // This is useful for the table that goes through a filter or sort.\n const expanded = rowExpansions.has(row);\n if (row && expanded) {\n if (isDetailFn) {\n const index = rowIndexes.get(row);\n currentRowHeight += detailRowHeight(row, index);\n } else {\n currentRowHeight += detailRowHeight;\n }\n }\n\n this.update(i, currentRowHeight);\n }\n }\n\n /**\n * Given the ScrollY position i.e. sum, provide the rowIndex\n * that is present in the current view port. Below handles edge cases.\n */\n getRowIndex(scrollY: number): number {\n if (scrollY === 0) return 0;\n return this.calcRowIndex(scrollY);\n }\n\n /**\n * When a row is expanded or rowHeight is changed, update the height. This can\n * be utilized in future when Angular Data table supports dynamic row heights.\n */\n update(atRowIndex: number, byRowHeight: number): void {\n if (!this.treeArray.length) {\n throw new Error(`Update at index ${atRowIndex} with value ${byRowHeight} failed:\n Row Height cache not initialized.`);\n }\n\n const n = this.treeArray.length;\n atRowIndex |= 0;\n\n while (atRowIndex < n) {\n this.treeArray[atRowIndex] += byRowHeight;\n atRowIndex |= atRowIndex + 1;\n }\n }\n\n /**\n * Range Sum query from 1 to the rowIndex\n */\n query(atIndex: number): number {\n if (!this.treeArray.length) {\n throw new Error(`query at index ${atIndex} failed: Fenwick tree array not initialized.`);\n }\n\n let sum = 0;\n atIndex |= 0;\n\n while (atIndex >= 0) {\n sum += this.treeArray[atIndex];\n atIndex = (atIndex & (atIndex + 1)) - 1;\n }\n\n return sum;\n }\n\n /**\n * Find the total height between 2 row indexes\n */\n queryBetween(atIndexA: number, atIndexB: number): number {\n return this.query(atIndexB) - this.query(atIndexA - 1);\n }\n\n /**\n * Given the ScrollY position i.e. sum, provide the rowIndex\n * that is present in the current view port.\n */\n private calcRowIndex(sum: number): number {\n if (!this.treeArray.length) return 0;\n\n let pos = -1;\n const dataLength = this.treeArray.length;\n\n // Get the highest bit for the block size.\n const highestBit = Math.pow(2, dataLength.toString(2).length - 1);\n\n for (let blockSize = highestBit; blockSize !== 0; blockSize >>= 1) {\n const nextPos = pos + blockSize;\n if (nextPos < dataLength && sum >= this.treeArray[nextPos]) {\n sum -= this.treeArray[nextPos];\n pos = nextPos;\n }\n }\n\n return pos + 1;\n }\n}\n","import { camelCase } from './camel-case';\n\nconst cache = {};\nconst testStyle = typeof document !== 'undefined' ? document.createElement('div').style : undefined;\n\n// Get Prefix\n// http://davidwalsh.name/vendor-prefix\nconst prefix = (function () {\n const styles = typeof window !== 'undefined' ? window.getComputedStyle(document.documentElement, '') : undefined;\n const match =\n typeof styles !== 'undefined'\n ? Array.prototype.slice\n .call(styles)\n .join('')\n .match(/-(moz|webkit|ms)-/)\n : null;\n const pre = match !== null ? match[1] : undefined;\n // tslint:disable-next-line: tsr-detect-non-literal-regexp\n const dom = typeof pre !== 'undefined' ? 'WebKit|Moz|MS|O'.match(new RegExp('(' + pre + ')', 'i'))[1] : undefined;\n\n return dom\n ? {\n dom,\n lowercase: pre,\n css: `-$