export class UIEventSource{ public data: T; private _callbacks = []; constructor(data: T) { this.data = data; } public addCallback(callback: ((latestData: T) => void)): UIEventSource { this._callbacks.push(callback); return this; } public addCallbackAndRun(callback: ((latestData: T) => void)): UIEventSource { callback(this.data); return this.addCallback(callback); } public setData(t: T): UIEventSource { if (this.data === t) { return; } this.data = t; this.ping(); return this; } public ping(): void { for (const callback of this._callbacks) { callback(this.data); } } public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { const sink = new UIEventSource(source.data?.data); source.addCallback((latestData) => { sink.setData(latestData?.data); }); for (const possibleSource of possibleSources) { possibleSource?.addCallback(() => { sink.setData(source.data?.data); }) } return sink; } public map(f: ((T) => J), extraSources: UIEventSource[] = [], g: ((J) => T) = undefined ): UIEventSource { const self = this; const newSource = new UIEventSource( f(this.data) ); const update = function () { newSource.setData(f(self.data)); } this.addCallback(update); for (const extraSource of extraSources) { extraSource?.addCallback(update); } if(g !== undefined) { newSource.addCallback((latest) => { self.setData((g(latest))); }) } return newSource; } public syncWith(otherSource: UIEventSource) : UIEventSource{ this.addCallback((latest) => otherSource.setData(latest)); const self = this; otherSource.addCallback((latest) => self.setData(latest)); if(this.data === undefined){ this.setData(otherSource.data); }else{ otherSource.setData(this.data); } return this; } public stabilized(millisToStabilize) : UIEventSource{ const newSource = new UIEventSource(this.data); let currentCallback = 0; this.addCallback(latestData => { currentCallback++; const thisCallback = currentCallback; window.setTimeout(() => { if(thisCallback === currentCallback){ newSource.setData(latestData); } }, millisToStabilize) }); return newSource; } public static Chronic(millis: number):UIEventSource{ const source = new UIEventSource(undefined); function run() { source.setData(new Date()); window.setTimeout(run, millis); } run(); return source; } }