import { Observable , Subject , Subscription } from "rxjs/Rx" ; import { Scheduler } from "rxjs/Scheduler" ; import { RxApp } from "./rx-app" ; /**
 * Defines a class that represents a command that can run operations in the background.
 */ export class ReactiveCommand < TArgs , TResult > { private _executionInfo : Subject < ExecutionInfo < TResult > > ; private _synchronizedExcecutionInfo : Subject < ExecutionInfo < TResult > > ; private _results : Observable < TResult > ; private _canExecute : Observable < boolean > ; private _isExecuting : Observable < boolean > ; private _canExecuteSubscription : Subscription ; private _exceptions : Subject < any > ; /**
     * Gets an observable that represents whether the command is currently executing.
     */ public get isExecuting ( ) : Observable < boolean > { return this . _isExecuting ; } /**
     * Gets an observable that represents whether this command can execute.
     */ public get canExecute ( ) : Observable < boolean > { return this . _canExecute ; } param canRun param task param scheduler constructor ( private task , private canRun , private scheduler ) { if ( ! task ) { throw new Error ( "The task parameter must be supplied" ) ; } if ( ! canRun ) { throw new Error ( "The canRun parameter must be supplied" ) ; } if ( ! scheduler ) { throw new Error ( "The scheduler parameter must be supplied" ) ; } this . _executionInfo = new Subject < ExecutionInfo < TResult > > ( ) ; this . _synchronizedExcecutionInfo = this . _executionInfo ; this . _exceptions = new Subject < any > ( ) ; this . _isExecuting = this . _synchronizedExcecutionInfo . observeOn ( scheduler ) . map ( info => info . demarcation === ExecutionDemarcation . Begin ) . startWith ( false ) . distinctUntilChanged ( ) . publishReplay ( 1 ) . refCount ( ) ; this . _canExecute = this . canRun . catch ( ex => { this . _exceptions . next ( ex ) ; return Observable . of ( false ) ; } ) . startWith ( false ) . combineLatest ( this . _isExecuting , ( canRun , isExecuting ) => { return canRun && ! isExecuting ; } ) . distinctUntilChanged ( ) . publishReplay ( 1 ) . refCount ( ) ; this . _results = this . _synchronizedExcecutionInfo . observeOn ( scheduler ) . filter ( info => info . demarcation === ExecutionDemarcation . EndWithResult ) . map ( info => info . result ) ; this . _canExecuteSubscription = this . _canExecute . subscribe ( ) ; } private static defaultScheduler ( scheduler ) { return scheduler || RxApp . mainThreadScheduler ; } private static defaultCanRun ( canRun ) { return canRun || Observable . of ( true ) ; } param task param canRun param scheduler public static create < TArgs , TResult > ( task , canRun ? , scheduler ? ) { return new ReactiveCommand < TArgs , TResult > ( ( args ) => { var result = task ( args ) ; if ( typeof result !== "undefined" ) { return Observable . of ( result ) ; } else { return Observable . of ( null ) ; } } , ReactiveCommand . defaultCanRun ( canRun ) , ReactiveCommand . defaultScheduler ( scheduler ) ) ; } param task param canRun param scheduler public static createFromTask < TArgs , TResult > ( task , canRun ? , scheduler ? ) { return new ReactiveCommand < TArgs , TResult > ( ( args ) => Observable . fromPromise ( task ( args ) ) , ReactiveCommand . defaultCanRun ( canRun ) , ReactiveCommand . defaultScheduler ( scheduler ) ) ; } param task param canRun param scheduler public static createFromObservable < TArgs , TResult > ( task , canRun ? , scheduler ? ) { return new ReactiveCommand < TArgs , TResult > ( task , ReactiveCommand . defaultCanRun ( canRun ) , ReactiveCommand . defaultScheduler ( scheduler ) ) ; } /**
     * Executes this command asynchronously.
     * Note that this method does not check whether the command is currently executable.
     */ public execute ( arg = null ) { try { return Observable . defer ( ( ) => { this . _synchronizedExcecutionInfo . next ( ExecutionInfo . createBegin < TResult > ( ) ) ; return Observable . empty < TResult > ( ) ; } ) . concat ( this . task ( arg ) ) . do ( result => this . _synchronizedExcecutionInfo . next ( ExecutionInfo . createResult ( result ) ) , null , ( ) => this . _synchronizedExcecutionInfo . next ( ExecutionInfo . createEnded < TResult > ( ) ) ) . catch ( ex => { this . _synchronizedExcecutionInfo . next ( ExecutionInfo . createFail < TResult > ( ) ) ; this . _exceptions . next ( ex ) ; return Observable . throw ( ex ) ; } ) . publishLast ( ) . refCount ( ) ; } catch ( ex ) { this . _exceptions . next ( ex ) ; return Observable . throw ( ex ) ; } } /**
     * Executes this command asynchronously if the latest observed value from canExecute is true.
     */ public invoke ( arg = null ) { return this . canExecuteNow ( ) . filter ( canExecute => canExecute ) . flatMap ( c => { return this . execute ( arg ) ; } ) ; } /**
     * Gets an observable that determines whether the command is able to execute at the moment it is subscribed to.
     */ public canExecuteNow ( ) { return this . canExecute . first ( ) ; } /**
     * Gets the observable that represents the results of this command's operations.
     */ public get results ( ) : Observable < TResult > { return this . _results ; } } class ExecutionInfo < TResult > { public demarcation : ExecutionDemarcation ; public result : TResult ; constructor ( demarcation , result ) { this . demarcation = demarcation ; this . result = result ; } public static createBegin < TResult > ( ) { return new ExecutionInfo ( ExecutionDemarcation . Begin , null ) ; } public static createResult < TResult > ( result ) { return new ExecutionInfo ( ExecutionDemarcation . EndWithResult , result ) ; } public static createFail < TResult > ( ) { return new ExecutionInfo ( ExecutionDemarcation . EndWithException , null ) ; } public static createEnded < TResult > ( ) { return new ExecutionInfo ( ExecutionDemarcation . Ended , null ) ; } } enum ExecutionDemarcation { Begin , EndWithResult , EndWithException , Ended }