| 
									
										
										
										
											2021-01-27 03:08:46 +01:00
										 |  |  | import {Utils} from "../Utils"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  | export class UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     private static allSources: UIEventSource<any>[] = UIEventSource.PrepPerf(); | 
					
						
							| 
									
										
										
										
											2020-07-20 21:03:55 +02:00
										 |  |  |     public data: T; | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     public trace: boolean; | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |     private readonly tag: string; | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |     private _callbacks: ((t: T) => (boolean | void | any)) [] = []; | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     constructor(data: T, tag: string = "") { | 
					
						
							|  |  |  |         this.tag = tag; | 
					
						
							|  |  |  |         this.data = data; | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         if (tag === undefined || tag === "") { | 
					
						
							| 
									
										
										
										
											2021-11-30 18:10:46 +01:00
										 |  |  |             const callstack = new Error().stack.split("\n") | 
					
						
							|  |  |  |             this.tag = callstack[1] | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         UIEventSource.allSources.push(this); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     static PrepPerf(): UIEventSource<any>[] { | 
					
						
							|  |  |  |         if (Utils.runningFromConsole) { | 
					
						
							| 
									
										
										
										
											2021-01-27 03:08:46 +01:00
										 |  |  |             return []; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         // @ts-ignore
 | 
					
						
							| 
									
										
										
										
											2021-02-05 16:32:37 +01:00
										 |  |  |         window.mapcomplete_performance = () => { | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |             console.log(UIEventSource.allSources.length, "uieventsources created"); | 
					
						
							|  |  |  |             const copy = [...UIEventSource.allSources]; | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |             copy.sort((a, b) => b._callbacks.length - a._callbacks.length); | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |             console.log("Topten is:") | 
					
						
							|  |  |  |             for (let i = 0; i < 10; i++) { | 
					
						
							|  |  |  |                 console.log(copy[i].tag, copy[i]); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-04-17 23:36:46 +02:00
										 |  |  |             return UIEventSource.allSources; | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         return []; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |     public static flatten<X>(source: UIEventSource<UIEventSource<X>>, possibleSources?: UIEventSource<any>[]): UIEventSource<X> { | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         const sink = new UIEventSource<X>(source.data?.data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         source.addCallback((latestData) => { | 
					
						
							|  |  |  |             sink.setData(latestData?.data); | 
					
						
							| 
									
										
										
										
											2022-01-19 20:34:04 +01:00
										 |  |  |             latestData.addCallback(data => { | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |                 if (source.data !== latestData) { | 
					
						
							| 
									
										
										
										
											2022-01-19 20:34:04 +01:00
										 |  |  |                     return true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 sink.setData(data) | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |         for (const possibleSource of possibleSources ?? []) { | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |             possibleSource?.addCallback(() => { | 
					
						
							|  |  |  |                 sink.setData(source.data?.data); | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return sink; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static Chronic(millis: number, asLong: () => boolean = undefined): UIEventSource<Date> { | 
					
						
							|  |  |  |         const source = new UIEventSource<Date>(undefined); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         function run() { | 
					
						
							|  |  |  |             source.setData(new Date()); | 
					
						
							|  |  |  |             if (asLong === undefined || asLong()) { | 
					
						
							|  |  |  |                 window.setTimeout(run, millis); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         run(); | 
					
						
							|  |  |  |         return source; | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-02 17:57:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. | 
					
						
							|  |  |  |      * If the promise fails, the value will stay undefined | 
					
						
							|  |  |  |      * @param promise | 
					
						
							|  |  |  |      * @constructor | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |     public static FromPromise<T>(promise: Promise<T>): UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         const src = new UIEventSource<T>(undefined) | 
					
						
							| 
									
										
										
										
											2021-09-29 23:56:59 +02:00
										 |  |  |         promise?.then(d => src.setData(d)) | 
					
						
							| 
									
										
										
										
											2021-10-02 17:57:54 +02:00
										 |  |  |         promise?.catch(err => console.warn("Promise failed:", err)) | 
					
						
							|  |  |  |         return src | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Converts a promise into a UIVentsource, sets the UIEVentSource when the result is calculated. | 
					
						
							|  |  |  |      * If the promise fails, the value will stay undefined | 
					
						
							|  |  |  |      * @param promise | 
					
						
							|  |  |  |      * @constructor | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |     public static FromPromiseWithErr<T>(promise: Promise<T>): UIEventSource<{ success: T } | { error: any }> { | 
					
						
							|  |  |  |         const src = new UIEventSource<{ success: T } | { error: any }>(undefined) | 
					
						
							|  |  |  |         promise?.then(d => src.setData({success: d})) | 
					
						
							| 
									
										
										
										
											2021-10-02 17:57:54 +02:00
										 |  |  |         promise?.catch(err => src.setData({error: err})) | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  |         return src | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Given a UIEVentSource with a list, returns a new UIEventSource which is only updated if the _contents_ of the list are different. | 
					
						
							|  |  |  |      * E.g. | 
					
						
							|  |  |  |      * const src = new UIEventSource([1,2,3]) | 
					
						
							|  |  |  |      * const stable = UIEventSource.ListStabilized(src) | 
					
						
							|  |  |  |      * src.addCallback(_ => console.log("src pinged")) | 
					
						
							|  |  |  |      * stable.addCallback(_ => console.log("stable pinged)) | 
					
						
							|  |  |  |      * src.setDate([...src.data]) | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |      * This will only trigger 'src pinged' | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |      * @param src | 
					
						
							|  |  |  |      * @constructor | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |     public static ListStabilized<T>(src: UIEventSource<T[]>): UIEventSource<T[]> { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |         const stable = new UIEventSource<T[]>(src.data) | 
					
						
							|  |  |  |         src.addCallback(list => { | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |             if (list === undefined) { | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |                 stable.setData(undefined) | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const oldList = stable.data | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |             if (oldList === list) { | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-10-15 14:52:11 +02:00
										 |  |  |             if (oldList === undefined || oldList.length !== list.length) { | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |                 stable.setData(list); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (let i = 0; i < list.length; i++) { | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |                 if (oldList[i] !== list[i]) { | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |                     stable.setData(list); | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |             // No actual changes, so we don't do anything
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         return stable | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |     public static asFloat(source: UIEventSource<string>): UIEventSource<number> { | 
					
						
							|  |  |  |         return source.map( | 
					
						
							|  |  |  |             (str) => { | 
					
						
							|  |  |  |                 let parsed = parseFloat(str); | 
					
						
							|  |  |  |                 return isNaN(parsed) ? undefined : parsed; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             [], | 
					
						
							|  |  |  |             (fl) => { | 
					
						
							|  |  |  |                 if (fl === undefined || isNaN(fl)) { | 
					
						
							|  |  |  |                     return undefined; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return ("" + fl).substr(0, 8); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-23 19:48:06 +01:00
										 |  |  |     public AsPromise(condition?: ((t: T )=> boolean)): Promise<T> { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         const self = this; | 
					
						
							| 
									
										
										
										
											2022-03-23 19:48:06 +01:00
										 |  |  |         condition = condition ?? (t => t !== undefined) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         return new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2022-03-23 19:48:06 +01:00
										 |  |  |             if (condition(self.data)) { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |                 resolve(self.data) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 self.addCallbackD(data => { | 
					
						
							|  |  |  |                     resolve(data) | 
					
						
							|  |  |  |                     return true; // return true to unregister as we only need to be called once
 | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public WaitForPromise(promise: Promise<T>, onFail: ((any) => void)): UIEventSource<T> { | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         promise?.then(d => self.setData(d)) | 
					
						
							|  |  |  |         promise?.catch(err => onFail(err)) | 
					
						
							|  |  |  |         return this | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public withEqualityStabilized(comparator: (t: T | undefined, t1: T | undefined) => boolean): UIEventSource<T> { | 
					
						
							|  |  |  |         let oldValue = undefined; | 
					
						
							|  |  |  |         return this.map(v => { | 
					
						
							|  |  |  |             if (v == oldValue) { | 
					
						
							|  |  |  |                 return oldValue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (comparator(oldValue, v)) { | 
					
						
							|  |  |  |                 return oldValue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             oldValue = v; | 
					
						
							|  |  |  |             return v; | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Adds a callback | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If the result of the callback is 'true', the callback is considered finished and will be removed again | 
					
						
							|  |  |  |      * @param callback | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public addCallback(callback: ((latestData: T) => (boolean | void | any))): UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         if (callback === console.log) { | 
					
						
							| 
									
										
										
										
											2020-10-06 01:37:02 +02:00
										 |  |  |             // This ^^^ actually works!
 | 
					
						
							|  |  |  |             throw "Don't add console.log directly as a callback - you'll won't be able to find it afterwards. Wrap it in a lambda instead." | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         if (this.trace) { | 
					
						
							|  |  |  |             console.trace("Added a callback") | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         this._callbacks.push(callback); | 
					
						
							|  |  |  |         return this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |     public addCallbackAndRun(callback: ((latestData: T) => (boolean | void | any))): UIEventSource<T> { | 
					
						
							|  |  |  |         const doDeleteCallback = callback(this.data); | 
					
						
							| 
									
										
										
										
											2021-10-02 15:16:41 +02:00
										 |  |  |         if (doDeleteCallback !== true) { | 
					
						
							| 
									
										
										
										
											2021-09-21 02:10:42 +02:00
										 |  |  |             this.addCallback(callback); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return this; | 
					
						
							| 
									
										
										
										
											2020-09-03 03:16:43 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-31 02:59:47 +02:00
										 |  |  |     public setData(t: T): UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2021-02-14 19:45:02 +01:00
										 |  |  |         if (this.data == t) { // MUST COMPARE BY REFERENCE!
 | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this.data = t; | 
					
						
							|  |  |  |         this.ping(); | 
					
						
							| 
									
										
										
										
											2020-08-31 02:59:47 +02:00
										 |  |  |         return this; | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public ping(): void { | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |         let toDelete = undefined | 
					
						
							| 
									
										
										
										
											2021-12-05 02:06:14 +01:00
										 |  |  |         let startTime = new Date().getTime() / 1000; | 
					
						
							| 
									
										
										
										
											2020-07-01 17:38:48 +02:00
										 |  |  |         for (const callback of this._callbacks) { | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |             if (callback(this.data) === true) { | 
					
						
							|  |  |  |                 // This callback wants to be deleted
 | 
					
						
							| 
									
										
										
										
											2021-11-16 02:57:26 +01:00
										 |  |  |                 // Note: it has to return precisely true in order to avoid accidental deletions
 | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |                 if (toDelete === undefined) { | 
					
						
							|  |  |  |                     toDelete = [callback] | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     toDelete.push(callback) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-12-05 02:06:14 +01:00
										 |  |  |         let endTime = new Date().getTime() / 1000 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |         if ((endTime - startTime) > 500) { | 
					
						
							|  |  |  |             console.trace("Warning: a ping of ", this.tag, " took more then 500ms; this is probably a performance issue") | 
					
						
							| 
									
										
										
										
											2021-12-05 02:06:14 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-18 14:52:09 +02:00
										 |  |  |         if (toDelete !== undefined) { | 
					
						
							|  |  |  |             for (const toDeleteElement of toDelete) { | 
					
						
							|  |  |  |                 this._callbacks.splice(this._callbacks.indexOf(toDeleteElement), 1) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Monadic bind function | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |     public bind<X>(f: ((t: T) => UIEventSource<X>)): UIEventSource<X> { | 
					
						
							|  |  |  |         const mapped = this.map(f) | 
					
						
							|  |  |  |         const sink = new UIEventSource<X>(undefined) | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |         const seenEventSources = new Set<UIEventSource<X>>(); | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |         mapped.addCallbackAndRun(newEventSource => { | 
					
						
							| 
									
										
										
										
											2021-10-08 04:33:39 +02:00
										 |  |  |             if (newEventSource === null) { | 
					
						
							|  |  |  |                 sink.setData(null) | 
					
						
							|  |  |  |             } else if (newEventSource === undefined) { | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |                 sink.setData(undefined) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             } else if (!seenEventSources.has(newEventSource)) { | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |                 seenEventSources.add(newEventSource) | 
					
						
							|  |  |  |                 newEventSource.addCallbackAndRun(resultData => { | 
					
						
							|  |  |  |                     if (mapped.data === newEventSource) { | 
					
						
							|  |  |  |                         sink.setData(resultData); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }) | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-10-08 04:33:39 +02:00
										 |  |  |                 // Already seen, so we don't have to add a callback, just update the value
 | 
					
						
							|  |  |  |                 sink.setData(newEventSource.data) | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  |         return sink; | 
					
						
							| 
									
										
										
										
											2021-10-03 20:50:18 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-03 01:38:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-19 19:16:20 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Monoidal map: | 
					
						
							|  |  |  |      * Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)' | 
					
						
							|  |  |  |      * @param f: The transforming function | 
					
						
							|  |  |  |      * @param extraSources: also trigger the update if one of these sources change | 
					
						
							|  |  |  |      * @param g: a 'backfunction to let the sync run in two directions. (data of the new UIEVEntSource, currentData) => newData | 
					
						
							| 
									
										
										
										
											2022-01-05 18:08:42 +01:00
										 |  |  |      * @param allowUnregister: if set, the update will be halted if no listeners are registered | 
					
						
							| 
									
										
										
										
											2021-06-19 19:16:20 +02:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public map<J>(f: ((t: T) => J), | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |                   extraSources: UIEventSource<any>[] = [], | 
					
						
							| 
									
										
										
										
											2022-01-05 18:08:42 +01:00
										 |  |  |                   g: ((j: J, t: T) => T) = undefined, | 
					
						
							|  |  |  |                   allowUnregister = false): UIEventSource<J> { | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         const self = this; | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-30 18:10:46 +01:00
										 |  |  |         const stack = new Error().stack.split("\n"); | 
					
						
							|  |  |  |         const callee = stack[1] | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |         const newSource = new UIEventSource<J>( | 
					
						
							| 
									
										
										
										
											2021-04-23 20:09:27 +02:00
										 |  |  |             f(this.data), | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |             "map(" + this.tag + ")@" + callee | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 11:23:36 +02:00
										 |  |  |         const update = function () { | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             newSource.setData(f(self.data)); | 
					
						
							| 
									
										
										
										
											2022-01-05 18:08:42 +01:00
										 |  |  |             return allowUnregister && newSource._callbacks.length === 0 | 
					
						
							| 
									
										
										
										
											2020-07-08 11:23:36 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-20 13:28:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 02:05:34 +01:00
										 |  |  |         this.addCallback(update); | 
					
						
							| 
									
										
										
										
											2020-07-08 11:23:36 +02:00
										 |  |  |         for (const extraSource of extraSources) { | 
					
						
							| 
									
										
										
										
											2020-08-31 13:25:13 +02:00
										 |  |  |             extraSource?.addCallback(update); | 
					
						
							| 
									
										
										
										
											2020-07-08 11:23:36 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         if (g !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |             newSource.addCallback((latest) => { | 
					
						
							| 
									
										
										
										
											2021-06-19 19:16:20 +02:00
										 |  |  |                 self.setData(g(latest, self.data)); | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         return newSource; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-18 12:00:38 +02:00
										 |  |  |     public syncWith(otherSource: UIEventSource<T>, reverseOverride = false): UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2020-07-21 00:07:04 +02:00
										 |  |  |         this.addCallback((latest) => otherSource.setData(latest)); | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         otherSource.addCallback((latest) => self.setData(latest)); | 
					
						
							| 
									
										
										
										
											2022-02-14 18:18:05 +01:00
										 |  |  |         if (reverseOverride) { | 
					
						
							|  |  |  |             if(otherSource.data !== undefined){ | 
					
						
							|  |  |  |                 this.setData(otherSource.data); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-09-18 12:00:38 +02:00
										 |  |  |         } else if (this.data === undefined) { | 
					
						
							|  |  |  |             this.setData(otherSource.data); | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2020-07-21 00:07:04 +02:00
										 |  |  |             otherSource.setData(this.data); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-24 17:53:09 +02:00
										 |  |  |         return this; | 
					
						
							| 
									
										
										
										
											2020-07-21 00:07:04 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |     public stabilized(millisToStabilize): UIEventSource<T> { | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         if (Utils.runningFromConsole) { | 
					
						
							| 
									
										
										
										
											2021-10-20 00:09:40 +02:00
										 |  |  |             return this; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 11:37:34 +02:00
										 |  |  |         const newSource = new UIEventSource<T>(this.data); | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 11:37:34 +02:00
										 |  |  |         let currentCallback = 0; | 
					
						
							|  |  |  |         this.addCallback(latestData => { | 
					
						
							|  |  |  |             currentCallback++; | 
					
						
							|  |  |  |             const thisCallback = currentCallback; | 
					
						
							|  |  |  |             window.setTimeout(() => { | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |                 if (thisCallback === currentCallback) { | 
					
						
							| 
									
										
										
										
											2020-09-02 11:37:34 +02:00
										 |  |  |                     newSource.setData(latestData); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }, millisToStabilize) | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 11:37:34 +02:00
										 |  |  |         return newSource; | 
					
						
							| 
									
										
										
										
											2020-09-03 16:44:48 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |     addCallbackAndRunD(callback: (data: T) => void) { | 
					
						
							| 
									
										
										
										
											2021-06-30 15:37:30 +02:00
										 |  |  |         this.addCallbackAndRun(data => { | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |             if (data !== undefined && data !== null) { | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |                 return callback(data) | 
					
						
							| 
									
										
										
										
											2021-06-30 15:37:30 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-22 05:02:09 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     addCallbackD(callback: (data: T) => void) { | 
					
						
							|  |  |  |         this.addCallback(data => { | 
					
						
							|  |  |  |             if (data !== undefined && data !== null) { | 
					
						
							|  |  |  |                 return callback(data) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  | } |