export interface Gettable < V > { get ( ) : V ; } export interface Settable < V > extends Gettable < V > { set ( v ) : void ; } export class Dependency { listeners : Set < ( ) => void > = new Set ( ) ; add ( listener ) { this . listeners . add ( listener ) ; } delete ( listener ) { this . listeners . delete ( listener ) ; } emit ( ) { this . listeners . forEach ( listener => listener ( ) ) ; } } const computations = [ ] ; export class Value < V > implements Settable < V > { value : V ; dependency = new Dependency ( ) ; constructor ( value ) { this . value = value ; } get ( ) { const computation = computations [ 0 ] ; computation && computation . add ( this . dependency ) ; return this . value ; } set ( value ) { this . value = value ; this . dependency . emit ( ) ; } update ( fn ) { this . set ( fn ( this . get ( ) ) ) ; } } export class DerivedValue < V > implements Gettable < V > { value : V ; dependency = new Dependency ( ) ; computation = new Set < Dependency > ( ) ; fn : ( ) => V ; stale = true ; constructor ( fn ) { this . fn = fn ; } run ( ) { computations . push ( new Set ( ) ) ; this . value = this . fn ( ) ; const computation = computations . shift ( ) ; this . stop ( ) ; computation . forEach ( dep => dep . add ( this . onUpdate ) ) ; this . computation = computation ; } onUpdate = ( ) => { this . stale = true ; this . dependency . emit ( ) ; } ; flush ( ) { if ( this . stale ) { this . stale = false ; this . run ( ) ; } } get ( ) { this . flush ( ) ; const deps = computations [ 0 ] ; deps && deps . add ( this . dependency ) ; return this . value ; } stop ( ) { this . computation . forEach ( dep => dep . delete ( this . onUpdate ) ) ; } } export class Constraint < V > implements Settable < V > { value : DerivedValue < V > ; setter : ( v : V ) => void ; constructor ( { get , set } ) { this . value = new DerivedValue ( get ) ; this . setter = set ; } get ( ) { return this . value . get ( ) ; } set ( value ) { this . setter ( value ) ; } update ( fn ) { this . set ( fn ( this . get ( ) ) ) ; } } function equal < V > ( x , y ) { if ( x === y ) { return true ; } if ( x . size !== y . size ) { return false ; } for ( var value of Array . from ( x ) ) { if ( ! y . has ( value ) ) { return false ; } } return true ; }