import { AnimationToolBar } from './AnimationToolBar' ; import { ActionClear } from './ActionClear' ; import { Drawing } from './Drawing' ; import { BoardManager } from './boardManager' ; import { ErrorMessage } from './ErrorMessage' ; import { ActionSerialized } from './ActionSerialized' ; import { ActionDeserializer } from './ActionDeserializer' ; import { UserManager } from './UserManager' ; import { Action } from './Action' ; import { ActionInit } from './ActionInit' ; /**
 * data structure for the linear history of actions (draw a line, eraser here, etc.)
 */ export class Timeline { /**
     * stack of actions
     * remark: the first action is of Type ActionInit and is like a sentinelle
     */ public actions : ( Action ) [ ] = [ ] ; private currentIndex = - 1 ; /**
     * empty the stack and set it with the current canvas as the initial state
     */ clear ( ) { const actionInit = new ActionInit ( UserManager . me . userID , undefined ) ; this . actions = [ actionInit ] ; this . currentIndex = 0 ; } /**
     * goto frame t
     */ async setCurrentIndex ( t ) { const previousIndex = this . currentIndex ; this . currentIndex = t ; await this . updateState ( previousIndex ) ; } returns get nbActions ( ) : number { return this . actions . length ; } param action return getTimeStepForActionCloseTo ( action , t ) { if ( this . actions [ t ] == action ) return t ; const actionSerialized = JSON . stringify ( action . serialize ( ) ) ; if ( this . actions [ t ] ) if ( JSON . stringify ( this . actions [ t ] . serialize ( ) ) == actionSerialized ) return t ; return this . actions . findIndex ( ( a ) => { if ( a . constructor == action . constructor ) return actionSerialized == JSON . stringify ( a . serialize ( ) ) ; else return false ; } ) ; } param previoust param nextt description async updateState ( previoust ) { const nextt = this . currentIndex ; if ( previoust <= nextt ) { for ( let i = previoust + 1 ; i <= nextt ; i ++ ) await this . actions [ i ] . redo ( ) ; } else if ( this . getNextClearAction ( ) > previoust ) { let bug113 = false ; let sthToDoFromStart = false ; for ( let t = previoust ; t >= nextt + 1 ; t -- ) if ( this . actions [ t ] == undefined ) { ErrorMessage . show ( "issue #113. error with t = " + t + " try to undo/redo again" ) ; bug113 = true ; } else if ( this . actions [ t ] . isDirectlyUndoable ) await this . actions [ t ] . undo ( ) ; else sthToDoFromStart = true ; if ( sthToDoFromStart ) { Drawing . clear ( ) ; await this . canvasRedraw ( ) ; } if ( bug113 ) this . repair ( ) ; } else { this . resetAndUpdate ( ) ; } } returns description getCurrentIndex ( ) { return this . currentIndex ; } clearAndReset ( canvasDataURL ) { const actionInit = new ActionInit ( UserManager . me . userID , canvasDataURL ) ; actionInit . redo ( ) ; this . actions = [ actionInit ] ; this . currentIndex = 0 ; } param A param t description public async load ( A , t ) { this . actions = A . map ( ActionDeserializer . deserialize ) ; this . currentIndex = t ; console . log ( "loaded stack with " + this . actions . length + " elements" ) ; this . resetAndUpdate ( ) ; } description private async resetAndUpdate ( ) { Drawing . clear ( ) ; if ( BoardManager . MAGNETCANCELLABLE ) document . getElementById ( "magnets" ) . innerHTML = "" ; await this . doAllActionsUntilCurrentIndex ( ) ; } description private async doAllActionsUntilCurrentIndex ( ) { let bug113 = false ; for ( let t = this . getLastClearAction ( ) + 1 ; t <= this . currentIndex ; t ++ ) { if ( this . actions [ t ] == undefined ) { ErrorMessage . show ( "issue #113. error with t = " + t + " try to undo/redo again" ) ; bug113 = true ; } else await this . actions [ t ] . redo ( ) ; } if ( bug113 ) this . repair ( ) ; } param elToInsert param indexesArray param insertIndex description moveAction ( indexToMove , insertIndex ) { if ( insertIndex == 0 || indexToMove == 0 ) return ; const eltToAdd = this . actions [ indexToMove ] ; this . actions . splice ( indexToMove , 1 ) ; this . actions . splice ( insertIndex , 0 , eltToAdd ) ; this . resetAndUpdate ( ) ; } param action param t description insertAction ( action , t , executeAgain = true ) { this . actions . splice ( t , 0 , action ) ; if ( t == this . currentIndex + 1 ) { this . currentIndex ++ ; if ( executeAgain ) this . actions [ t ] . redo ( ) ; } else { if ( t <= this . currentIndex ) this . currentIndex ++ ; this . resetAndUpdate ( ) ; } AnimationToolBar . updateAddAction ( t ) ; } param { * } action description insertActionNowAlreadyExecuted ( action , executeAgain = true ) { this . insertAction ( action , this . currentIndex + 1 , executeAgain ) ; } param t description deleteAction ( t ) { if ( t == 0 ) return ; if ( t == this . currentIndex ) { this . currentIndex -- ; this . updateState ( t ) ; } this . actions . splice ( t , 1 ) ; if ( t < this . currentIndex ) { this . currentIndex -- ; this . resetAndUpdate ( ) ; } AnimationToolBar . updateDeleteAction ( t ) ; } returns getLastClearAction ( ) { for ( let t = this . currentIndex ; t >= 0 ; t -- ) { if ( this . actions [ t ] instanceof ActionClear ) return t ; } return 0 ; } returns getNextClearAction ( ) { for ( let t = this . currentIndex ; t < this . actions . length ; t ++ ) { if ( this . actions [ t ] instanceof ActionClear ) return t ; } return Infinity ; } property description async canvasRedraw ( ) { let bug113 = false ; for ( let t = this . getLastClearAction ( ) + 1 ; t <= this . currentIndex ; t ++ ) { if ( this . actions [ t ] == undefined ) { ErrorMessage . show ( "issue #113. error with t = " + t + " try to undo/redo again" ) ; bug113 = true ; } else if ( ! this . actions [ t ] . isDirectlyUndoable ) await this . actions [ t ] . redo ( ) ; } if ( bug113 ) this . repair ( ) ; } returns getLastAction ( ) { return this . actions [ this . currentIndex ] ; } returns getPreviousPausedFrame ( ) { for ( let i = this . currentIndex - 1 ; i >= 0 ; i -- ) if ( this . actions [ i ] . pause ) return i ; return this . currentIndex ; } returns getNextPausedFrame ( ) { for ( let i = this . currentIndex + 1 ; i <= this . actions . length - 1 ; i ++ ) if ( this . actions [ i ] . pause ) return i ; return this . actions . length - 1 ; } /**
     * go to the presvious paused frame
     */ async previousPausedFrame ( ) { if ( this . isBegin ( ) ) return ; const newIndex = this . getPreviousPausedFrame ( ) ; this . setCurrentIndex ( newIndex ) ; } returns description async nextPausedFrame ( ) { if ( this . isEnd ( ) ) return ; const tGoal = this . getNextPausedFrame ( ) ; for ( let i = this . currentIndex + 1 ; i <= tGoal ; i ++ ) if ( this . actions [ i ] . isBlocking ) await this . actions [ i ] . redoAnimated ( ) ; else this . actions [ i ] . redoAnimated ( ) ; this . currentIndex = tGoal ; } returns isBegin ( ) { return this . currentIndex <= 0 ; } returns isEnd ( ) { return this . currentIndex >= this . actions . length - 1 ; } description __str__ ( ) { let s = "" ; for ( let i = 0 ; i < this . actions . length ; i ++ ) { s += ( i == 0 ) ? "i" : ( ( this . actions [ i ] == undefined ) ? "u" : "a" ) ; s += ( i == this . currentIndex ) ? ". " : "  " ; } return s ; } description repair ( ) { if ( this . actions . indexOf ( undefined ) >= 0 ) { ErrorMessage . show ( "#113 issue" ) ; this . actions = this . actions . filter ( ( a ) => a != undefined ) ; this . currentIndex = this . actions . length - 1 ; } } returns serialize ( ) { this . repair ( ) ; return this . actions . map ( ( a ) => a ? a . serialize ( ) : undefined ) ; } returns isSeveralSlides ( ) { for ( const action of this . actions ) { if ( action . pause ) return true ; } return false ; } }