import { TimeTravel } from './timeTravel' ; import { Storage } from './storage' ; import { Component } from './component' ; import { patch } from './dom' ; import { serialize , deserialize , plainToClass , classToPlain , ClassTransformOptions } from 'class-transformer' ; export type AppOptions = { rootComponent ? : Component ; isVdomRendered ? : boolean ; } ; export class App { private _rootComponent ! : Component ; private lock = false ; private rootElement : Element ; private _timeTravelOn = false ; private isVdomRendered = false ; /** Manages serialization of root component to local storage */ storage : Storage ; /** Manages time travel - type is 'any' because component snapshots are converted to plain json objects */ time : TimeTravel < any > ; /** internal use only - update tally to avoid unnecessary refereshes for nested updates */ activeUpdates = 0 ; param rootComponentConstructor param containerId param rootComponent param isVdomRendered constructor ( rootComponentConstructor , containerId , options = { } ) { this . isVdomRendered = options . isVdomRendered == true ; this . rootElement = document . getElementById ( containerId ) ! ; this . time = new TimeTravel < any > ( state => this . setRootComponent ( < Component > < any > plainToClass ( rootComponentConstructor , state , this . serializerOptions ) , true , false , false ) ) ; var newRoot = ( options . rootComponent || new rootComponentConstructor ( ) ) ; newRoot . initDecorators ( ) ; this . storage = new Storage ( containerId , ( ) => serialize ( this . rootComponent , this . serializerOptions ) , serialized => { this . setRootComponent ( < Component > deserialize ( rootComponentConstructor , serialized , this . serializerOptions ) , true , false ) ; } ) ; this . storage . load ( ) ; if ( ! this . rootComponent ) this . setRootComponent ( newRoot ) ; } /** Root component of updates, view and serialization */ get rootComponent ( ) { return this . _rootComponent ; } private setRootComponent ( rootComponent , deserialize = false , doSave ? , doTimeSnapshot ? ) { this . _rootComponent = rootComponent ; this . snapshot ( doSave , doTimeSnapshot ) ; this . refresh ( deserialize ) ; } /** Whether snapshots occur by default after each update  */ get timeTravelOn ( ) { return this . _timeTravelOn ; } set timeTravelOn ( value ) { this . _timeTravelOn = value ; if ( value ) this . snapshot ( false , true ) ; } param doSave param doTimeSnapshot snapshot ( doSave ? , doTimeSnapshot ? ) { if ( this . activeUpdates > 0 ) return ; var json ; if ( doTimeSnapshot || ( this . timeTravelOn && doTimeSnapshot !== false ) ) { json = classToPlain ( this . rootComponent , this . serializerOptions ) ; this . time . push ( json ) ; } this . storage . save ( doSave , ( ) => json != null ? serialize ( json , this . serializerOptions ) : serialize ( this . rootComponent , this . serializerOptions ) ) ; } /** internal use only */ refresh ( deserialize = false ) { if ( this . activeUpdates > 0 ) return ; this . rootComponent . attach ( this , undefined , deserialize ) ; if ( this . lock ) return ; this . lock = true ; setTimeout ( ( ) => { this . lock = false ; var rootVNode = this . rootComponent . view ( ) ; if ( this . lock ) return ; if ( this . isVdomRendered ) patch ( rootVNode , < Element > this . rootElement . lastElementChild ) ; else { this . rootElement . appendChild ( patch ( rootVNode ) ) ; this . isVdomRendered = true ; } this . rootComponent . runRefreshesInternal ( ) ; } ) ; } serializerOptions : ClassTransformOptions = { enableCircularCheck : true } ; }