import { Observable , EventData } from '../observable' ; import { ChangedData , ChangeType } from '../observable-array' ; /**
 * Event args for "itemsLoading" event.
 */ export interface ItemsLoading extends EventData { /**
     * Start index.
     */ index : number ; /**
     * Number of items to load.
     */ count : number ; } /**
 * Advanced array like class that helps loading items on demand.
 */ export class VirtualArray < T > extends Observable { /**
     * String value used when hooking to change event.
     */ public static changeEvent = ChangeType . Change ; /**
     * String value used when hooking to itemsLoading event.
     */ public static itemsLoadingEvent = 'itemsLoading' ; private _requestedIndexes : Array < number > ; private _loadedIndexes : Array < number > ; private _length : number ; private _cache : { } ; constructor ( length = 0 ) { super ( ) ; this . _length = length ; this . _cache = { } ; this . _requestedIndexes = [ ] ; this . _loadedIndexes = [ ] ; } /**
     * Gets or sets length for the virtual array.
     */ get length ( ) : number { return this . _length ; } set length ( value ) { if ( this . _length !== value ) { const index = this . _length ; const count = value - this . _length ; this . _length = value ; this . notify ( { eventName : ChangeType . Change , object : this , action : count > 0 ? ChangeType . Add : ChangeType . Delete , index : index , removed : new Array ( count < 0 ? Math . abs ( count ) : 0 ) , addedCount : count > 0 ? count : 0 , } ) ; } } /**
     * Gets or sets load size for the virtual array.
     */ private _loadSize : number ; get loadSize ( ) : number { return this . _loadSize ; } set loadSize ( value ) { this . _loadSize = value ; } getItem ( index ) { const item = this . _cache [ index ] ; if ( item === undefined ) { if ( index >= 0 && index < this . length && this . _requestedIndexes . indexOf ( index ) < 0 && this . _loadedIndexes . indexOf ( index ) < 0 ) { this . requestItems ( index ) ; } } return item ; } setItem ( index , value ) { if ( this . _cache [ index ] !== value ) { this . load ( index , [ value ] ) ; } } /**
     * Loads items from an array starting at index.
     */ load ( index , items ) { for ( let i = 0 ; i < items . length ; i ++ ) { const itemIndex = index + i ; this . _cache [ itemIndex ] = items [ i ] ; this . _requestedIndexes . splice ( this . _requestedIndexes . indexOf ( itemIndex ) , 1 ) ; if ( this . _loadedIndexes . indexOf ( itemIndex ) < 0 ) { this . _loadedIndexes . push ( itemIndex ) ; } } if ( this . _requestedIndexes . length > 0 ) { for ( let i = 0 ; i < this . loadSize - items . length ; i ++ ) { this . _requestedIndexes . splice ( this . _requestedIndexes . indexOf ( index + i ) , 1 ) ; } } this . notify ( { eventName : ChangeType . Change , object : this , action : ChangeType . Update , index : index , removed : new Array ( items . length ) , addedCount : items . length , } ) ; } private requestItems ( index ) { const indexesToLoad = [ ] ; const pageIndex = this . _loadSize > 0 ? this . _loadSize * Math . floor ( index / this . _loadSize ) : index ; let count = 0 ; let start = - 1 ; for ( let i = 0 ; i < this . loadSize ; i ++ ) { const itemIndex = pageIndex + i ; if ( itemIndex >= this . _length ) { break ; } if ( this . _loadedIndexes . indexOf ( itemIndex ) < 0 ) { if ( start < 0 ) { start = itemIndex ; } indexesToLoad . push ( itemIndex ) ; if ( this . _requestedIndexes . indexOf ( itemIndex ) < 0 ) { this . _requestedIndexes . push ( itemIndex ) ; } count ++ ; } else { if ( count > 0 ) { this . notify ( { eventName : VirtualArray . itemsLoadingEvent , object : this , index : start , count : count , } ) ; } start = - 1 ; count = 0 ; } } if ( start >= 0 && count > 0 ) { this . notify ( { eventName : VirtualArray . itemsLoadingEvent , object : this , index : start , count : count , } ) ; } } } export interface VirtualArray < T > { param eventNames param callback param thisArg on ( eventNames , callback , thisArg ? ) : void ; /**
     * Raised when still not loaded items are requested.
     */ on ( event , callback , thisArg ? ) : void ; /**
     * Raised when a change occurs.
     */ on ( event , callback , thisArg ? ) : void ; }