import { ReactiveObject } from "./reactive-object" ; import { Observable , Subject } from "rxjs/Rx" ; import { CollectionChangedEventArgs } from "./events/collection-changed-event-args" ; import { PropertyChangedEventArgs } from "./events/property-changed-event-args" ; if ( ! Array . prototype . find ) { Array . prototype . find = function ( predicate ) { if ( this === null ) { throw new TypeError ( 'Array.prototype.find called on null or undefined' ) ; } if ( typeof predicate !== 'function' ) { throw new TypeError ( 'predicate must be a function' ) ; } var list = Object ( this ) ; var length = list . length >>> 0 ; var thisArg = arguments [ 1 ] ; var value ; for ( var i = 0 ; i < length ; i ++ ) { value = list [ i ] ; if ( predicate . call ( thisArg , value , i , list ) ) { return value ; } } return undefined ; } ; } if ( ! Array . prototype . findIndex ) { Array . prototype . findIndex = function ( predicate ) { if ( this === null ) { throw new TypeError ( 'Array.prototype.findIndex called on null or undefined' ) ; } if ( typeof predicate !== 'function' ) { throw new TypeError ( 'predicate must be a function' ) ; } var list = Object ( this ) ; var length = list . length >>> 0 ; var thisArg = arguments [ 1 ] ; var value ; for ( var i = 0 ; i < length ; i ++ ) { value = list [ i ] ; if ( predicate . call ( thisArg , value , i , list ) ) { return i ; } } return - 1 ; } ; } function _bindFunction < TFunction extends Function > ( fn , thisArg ) { var bound = fn ; if ( thisArg ) { bound = fn . bind ( thisArg ) ; } return bound ; } /**
 * Defines a class that provides powerful observable functionality
 * around a traditional array.
 */ export class ReactiveArray < T > extends ReactiveObject { private _array : T [ ] ; private _changed : Subject < CollectionChangedEventArgs < T > > ; param arr constructor ( arr ? ) { super ( ) ; this . _changed = new Subject < CollectionChangedEventArgs < T > > ( ) ; var copied = arr ? arr . slice ( ) : [ ] ; if ( Array . isArray ( copied ) ) { this . _array = copied ; } else { this . _array = copied . _array ; } } private emitArrayChanges ( addStartIndex , addedItems , deleteStartIndex , deletedItems ) { if ( addedItems . length > 0 || deletedItems . length > 0 ) { var e = new CollectionChangedEventArgs < T > ( this ) ; e . addedItems = addedItems . slice ( ) ; e . addedItemsIndex = addStartIndex ; e . removedItems = deletedItems . slice ( ) ; e . removedItemsIndex = deleteStartIndex ; this . _changed . next ( e ) ; } } /**
     * Gets an observable that resolves whenever the array changes.
     * Note that changes are only observed for the ReactiveArray itself.
     * This means that only operations such as push(), pop(), splice(), shift(), and unshift()
     * emit changed() events.
     */ public get changed ( ) : Observable < CollectionChangedEventArgs < T > > { return this . _changed . asObservable ( ) ; } /**
     * Gets an observable that resolves whenever a new item is added to the array.
     */ public get itemsAdded ( ) : Observable < CollectionChangedEventArgs < T > > { return this . changed . filter ( e => e . addedItems . length > 0 ) ; } /**
     * Gets an observable that resolves whenever a item is removed from the array.
     */ public get itemsRemoved ( ) : Observable < CollectionChangedEventArgs < T > > { return this . changed . filter ( e => e . removedItems . length > 0 ) ; } /**
     * Gets the item at the given index in the array.
     */ public getItem ( index ) { return this . _array [ index ] ; } /**
     * Sets the value of the given index to the given item in the array.
     */ public setItem ( index , value ) { this . _array [ index ] = value ; } param values public unshift ( ... values ) { this . trackPropertyChanges ( "length" , ( ) => { this . _array . unshift ( ... values ) ; this . emitArrayChanges ( 0 , values , 0 , [ ] ) ; } ) ; } /**
     * Removes a single item from the beginning of this array and returns it.
     */ public shift ( ) { return this . trackPropertyChanges ( "length" , ( ) => { var removed = this . _array . shift ( ) ; if ( typeof removed !== "undefined" ) { this . emitArrayChanges ( 0 , [ ] , 0 , [ removed ] ) ; } return removed ; } ) ; } param values public push ( ... values ) { this . trackPropertyChanges ( "length" , ( ) => { this . _array . push ( ... values ) ; this . emitArrayChanges ( this . _array . length - values . length , values , 0 , [ ] ) ; } ) ; } /**
     * Removes a single item from the end of this array and returns it.
     */ public pop ( ) { return this . trackPropertyChanges ( "length" , ( ) => { var removed = this . _array . pop ( ) ; if ( typeof removed !== "undefined" ) { this . emitArrayChanges ( 0 , [ ] , this . _array . length , [ removed ] ) ; } return removed ; } ) ; } /**
     * Gets the number of items that are currently stored in the array.
     */ public get length ( ) : number { return this . _array . length ; } param start param end public slice ( start ? , end ? ) { return new ReactiveArray < T > ( this . _array . slice ( start , end ) ) ; } param start param deleteCount param items public splice ( start , deleteCount , ... items ) { return this . trackPropertyChanges ( "length" , ( ) => { var deleted = this . _array . splice ( start , deleteCount , ... items ) ; this . emitArrayChanges ( start , items , start , deleted ) ; return ReactiveArray . from ( deleted ) ; } ) ; } param compareFunction public sort ( compareFunction ? ) { var newArr = new ReactiveArray < T > ( ) ; newArr . _array = this . _array . sort ( compareFunction ) ; return newArr ; } param callback param thisArg public map < TNew > ( callback , thisArg ? ) { var newArr = new ReactiveArray < TNew > ( ) ; var bound = _bindFunction ( callback , thisArg ) ; newArr . _array = this . _array . map ( ( value , index , arr ) => bound ( value , index , this ) ) ; return newArr ; } param callback param thisArg public filter ( callback , thisArg ? ) { var newArr = new ReactiveArray < T > ( ) ; var bound = _bindFunction ( callback , thisArg ) ; newArr . _array = this . _array . filter ( ( value , index , arr ) => bound ( value , index , this ) ) ; return newArr ; } param value param fromIndex public indexOf ( value , fromIndex ? ) { return this . _array . indexOf ( value , fromIndex ) ; } param value param fromIndex public lastIndexOf ( value , fromIndex = this . length - 1 ) { return this . _array . lastIndexOf ( value , fromIndex ) ; } param callback param thisArg public forEach ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; this . _array . forEach ( ( value , index , arr ) => bound ( value , index , this ) ) ; } param callback param initialValue public reduce ( callback , initialValue ? ) { if ( typeof initialValue !== "undefined" ) { return this . _array . reduce ( ( prev , current , index ) => callback ( prev , current , index , this ) , initialValue ) ; } else { return this . _array . reduce ( ( prev , current , index ) => callback ( prev , current , index , this ) ) ; } } param callback param thisArg public every ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . _array . every ( ( value , index , arr ) => bound ( value , index , this ) ) ; } param callback param thisArg public some ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . _array . some ( ( value , index , arr ) => bound ( value , index , this ) ) ; } param callback param thisArg public find ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . _array . find ( ( value , index , arr ) => bound ( value , index , this ) ) ; } param callback param thisArg public findIndex ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . _array . findIndex ( < any > ( ( value , index , arr ) => bound ( value , index , this ) ) ) ; } param property public whenAnyItem < TProp > ( property ) { var derived = this . derived . filter ( i => i != null ) . map ( i => { var obj = ( < ReactiveObject > < any > i ) ; var when = obj . whenAny < TProp > ( < any > property ) ; return when . publish ( ) . refCount ( ) ; } ) . build ( ) ; return derived . toObservable ( ) . map ( o => Observable . merge ( ... o ) ) . switch ( ) . distinct ( ) ; } /**
     * Gets a cold observable that resolves when any property on any item in the array
     * changes.
     */ public whenAnyItemProperty ( ) { var derived = this . derived . filter ( i => i != null ) . map ( i => { var obj = ( < ReactiveObject > < any > i ) ; var when = obj . propertyChanged ; return when . publish ( ) . refCount ( ) ; } ) . build ( ) ; return derived . toObservable ( ) . map ( o => Observable . merge ( ... o ) ) . switch ( ) . distinct ( ) ; } param property public whenAnyItemValue < TProp > ( property ) { return this . whenAnyItem ( property ) . map ( e => e . newPropertyValue ) ; } param properth public whenAnyItemObservable < TProp > ( property ) { return this . whenAnyItemValue ( property ) . filter ( o => o != null ) . mergeAll ( ) ; } /**
     * Gets a new builder object that can be used to create a child array from this array that tracks the changes made to this array.
     */ public get derived ( ) : DerivedReactiveArrayBuilder < T > { return new DerivedReactiveArrayBuilder ( this ) ; } /**
     * Gets a new builder object that can be used to create an observable that calculates a single value
     * from this array.
     */ public get computed ( ) : ComputedReactiveArrayBuilder < T > { return new ComputedReactiveArrayBuilder ( this ) ; } param arr public static from < T > ( arr ) { return new ReactiveArray < T > ( arr ) ; } param values public static of < T > ( ... values ) { return ReactiveArray . from ( values ) ; } /**
     * Converts this ReactiveArray into a traditional JavaScript array object.
     */ public toArray ( ) { return this . _array . slice ( ) ; } /**
     * Converts this reactive array into an observable stream that contains
     * the snapshots of this array's values.
     */ public toObservable ( ) { return this . changed . map ( e => this . toArray ( ) ) . startWith ( this . toArray ( ) ) . publishReplay ( 1 ) . refCount ( ) ; } /**
     * Gets the JSON object that represents the values in this array.
     */ public toJSON ( ) { return this . map ( ( v ) => { if ( typeof v === "undefined" || v === null ) { return null ; } else if ( typeof v . toJSON === "function" ) { return v . toJSON ( ) ; } else { return v ; } } ) . toArray ( ) ; } /**
     * Gets the string representation of this ReactiveArray.
     */ public toString ( ) { var items = this . _array . map ( i => { var type = typeof i ; if ( type === "undefined" ) { return "undefined" ; } else if ( i === null ) { return "null" ; } else if ( type === "string" ) { return `'${ i }'` ; } else { return i . toString ( ) ; } } ) . join ( ", " ) ; return `[${ items }]` ; } } class DerivedReactiveArray < TIn , TOut > extends ReactiveArray < TOut > { private _trackedItems : TransformResult [ ] ; constructor ( private parent , private triggers , private eventSteps , private arraySteps ) { super ( ) ; this . _trackedItems = [ ] ; var e = new CollectionChangedEventArgs < TIn > ( this ) ; e . addedItems = parent . toArray ( ) ; e . addedItemsIndex = 0 ; e . movedItems = [ ] ; e . removedItems = [ ] ; e . removedItemsIndex = 0 ; this . _apply ( e ) ; Observable . merge ( parent . changed , ... triggers ) . subscribe ( e => { this . _apply ( e ) ; } ) ; } public splice ( start , deleteCount , ... items ) { return DerivedReactiveArray . _throwNotSupported ( ) ; } public push ( ... items ) { DerivedReactiveArray . _throwNotSupported ( ) ; } public pop ( ) { return DerivedReactiveArray . _throwNotSupported ( ) ; } public shift ( ) { return DerivedReactiveArray . _throwNotSupported ( ) ; } public unshift ( ... values ) { return DerivedReactiveArray . _throwNotSupported ( ) ; } public setItem ( index , value ) { DerivedReactiveArray . _throwNotSupported ( ) ; } private static _throwNotSupported ( ) { throw new Error ( "Derived arrays do not support modification. If you want support for two-way derived arrays, file an issue at https://github.com/KallynGowdy/RxUI/issues." ) ; } private _applyItem ( item , index , arr ) { var result = { keep : true , value : item } ; for ( var i = 0 ; i < this . eventSteps . length ; i ++ ) { result = this . eventSteps [ i ] . transform ( result . value , index , arr ) ; if ( result . keep === false ) { break ; } } return result ; } private _apply ( event ) { var addedItems = [ ] ; for ( var c = 0 ; c < event . addedItems . length ; c ++ ) { var item = event . addedItems [ c ] ; var result = this . _applyItem ( item , c , event . addedItems ) ; addedItems . push ( result ) ; } var currentArr = this . _trackedItems ; currentArr . splice ( event . removedItemsIndex , event . removedItems . length ) ; currentArr . splice ( event . addedItemsIndex , 0 , ... addedItems ) ; var finalArr = currentArr . filter ( t => t . keep ) . map ( t => t . value ) ; var final = DerivedReactiveArray . _transformArray ( finalArr , this . arraySteps ) ; super . splice . apply ( this , [ 0 , this . length , ... final ] ) ; } /**
     * Runs the given transform result through each of the defined steps in this object
     * and returns the result.
     */ private static _transformArray ( initial , steps ) { var current = initial ; for ( var i = 0 ; i < steps . length ; i ++ ) { current = steps [ i ] . transform ( current ) ; } return current ; } } interface TransformResult { value : any ; keep : boolean ; } /**
 * Transforms that run on individual items in the array.
 */ interface BuilderItemTransform { transform : ( value : any , index : number , arry : any [ ] ) => TransformResult ; } /**
 * Transforms that run on the entire compiled array.
 */ interface BuilderArrayTransform { transform : ( arr : any [ ] ) => any [ ] ; } class FilterTransform < T > implements BuilderItemTransform { constructor ( private predicate ) { } transform ( value , index , arr ) { return { value : value , keep : this . predicate ( value , index , arr ) } ; } } class MapTransform < TIn , TOut > implements BuilderItemTransform { constructor ( private map ) { } transform ( value , index , arr ) { return { value : this . map ( value , index , arr ) , keep : true } ; } } class SortTransform < T > implements BuilderArrayTransform { constructor ( private compareFunction ) { } transform ( current ) { return current . sort ( this . compareFunction ) ; } } /**
 * Defines a class that acts as a builder for derived reactive arrays.
 */ export class DerivedReactiveArrayBuilder < T > { private parent : ReactiveArray < T > ; private eventSteps : BuilderItemTransform [ ] ; private arraySteps : BuilderArrayTransform [ ] ; private triggers : Observable < CollectionChangedEventArgs < T > > [ ] ; constructor ( parent ) { this . parent = parent ; this . eventSteps = [ ] ; this . arraySteps = [ ] ; this . triggers = [ ] ; } private addEvent < T > ( transform ) { this . eventSteps . push ( transform ) ; return < DerivedReactiveArrayBuilder < T > > < any > this ; } private addArray < T > ( transform ) { this . arraySteps . push ( transform ) ; return < DerivedReactiveArrayBuilder < T > > < any > this ; } param properties public whenAnyItem < TProp > ( ... properties ) { var mapped = Observable . merge ( ... properties . map ( p => this . parent . whenAnyItem ( p ) . map ( e => ( { sender : e . sender , propertyName : e . propertyName , newPropertyValue : e . newPropertyValue , index : this . parent . indexOf ( e . sender ) } ) ) ) ) ; var triggers = mapped . map ( e => { var args = new CollectionChangedEventArgs < T > ( this ) ; args . addedItems = [ e . sender ] ; args . addedItemsIndex = e . index ; args . removedItems = [ e . sender ] ; args . removedItemsIndex = e . index ; return args ; } ) ; this . triggers . push ( triggers ) ; return this ; } /**
     * Instructs the child reactive array to trigger updates when any property on one of the items from the parent
     * array has changed.
     */ public whenAnyItemProperty ( ) { var mapped = this . parent . whenAnyItemProperty ( ) . map ( e => ( { sender : e . sender , propertyName : e . propertyName , newPropertyValue : e . newPropertyValue , index : this . parent . indexOf ( e . sender ) } ) ) ; var trigger = mapped . map ( e => { var args = new CollectionChangedEventArgs < T > ( this ) ; args . addedItems = [ e . sender ] ; args . addedItemsIndex = e . index ; args . removedItems = [ e . sender ] ; args . removedItemsIndex = e . index ; return args ; } ) ; this . triggers . push ( trigger ) ; return this ; } param predicate public filter ( predicate ) { return this . addEvent < T > ( new FilterTransform < T > ( predicate ) ) ; } param transform public map < TNew > ( transform ) { return this . addEvent < TNew > ( new MapTransform < T , TNew > ( transform ) ) ; } param compareFunction public sort ( compareFunction ? ) { return this . addArray < T > ( new SortTransform < T > ( compareFunction ) ) ; } /**
     * Creates a new child array according to the rules previously defined with this builder object and returns it.
     * Currently, derived reactive arrays do not support direct modification via push(), pop(), splice(), etc.
     */ public build ( ) { return new DerivedReactiveArray < any , T > ( this . parent , this . triggers , this . eventSteps , this . arraySteps ) ; } } /**
 * Defines a class that acts as a builder for computed observables that are based on an array.
 */ export class ComputedReactiveArrayBuilder < T > { private parent : ReactiveArray < T > ; constructor ( parent ) { this . parent = parent ; } param callback param initialValue public reduce ( callback , initialValue ? ) { return this . parent . toObservable ( ) . map ( arr => arr . reduce ( ( prev , current , index , arr ) => callback ( prev , current , index , this . parent ) , initialValue ) ) ; } param callback param thisArg public every ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . parent . toObservable ( ) . map ( arr => arr . every ( ( value , index , arr ) => bound ( value , index , this . parent ) ) ) ; } param callback param thisArg public some ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . parent . toObservable ( ) . map ( arr => arr . some ( ( value , index , arr ) => bound ( value , index , this . parent ) ) ) ; } param callback param thisArg public find ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . parent . toObservable ( ) . map ( arr => arr . find ( ( value , index , arr ) => bound ( value , index , this . parent ) ) ) ; } param callback param thisArg public findIndex ( callback , thisArg ? ) { var bound = _bindFunction ( callback , thisArg ) ; return this . parent . toObservable ( ) . map ( arr => arr . findIndex ( < any > ( ( value , index , arr ) => bound ( value , index , this . parent ) ) ) ) ; } }