| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-20 21:03:55 +02:00
										 |  |  |     public data: T; | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |     private readonly tag: string; | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     private _callbacks = []; | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |      | 
					
						
							|  |  |  |     private static allSources : UIEventSource<any>[] = UIEventSource.PrepPerf(); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2021-01-27 03:08:46 +01:00
										 |  |  |     static PrepPerf() : UIEventSource<any>[]{ | 
					
						
							|  |  |  |         if(Utils.runningFromConsole){ | 
					
						
							|  |  |  |             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]; | 
					
						
							|  |  |  |             copy.sort((a,b) => b._callbacks.length - a._callbacks.length); | 
					
						
							|  |  |  |             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-01-25 03:12:09 +01:00
										 |  |  |     constructor(data: T, tag: string = "") { | 
					
						
							|  |  |  |         this.tag = tag; | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         this.data = data; | 
					
						
							| 
									
										
										
										
											2021-01-25 03:12:09 +01:00
										 |  |  |         UIEventSource.allSources.push(this); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public static flatten<X>(source: UIEventSource<UIEventSource<X>>, possibleSources: UIEventSource<any>[]): UIEventSource<X> { | 
					
						
							|  |  |  |         const sink = new UIEventSource<X>(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 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-03 03:16:43 +02:00
										 |  |  |     public addCallback(callback: ((latestData: T) => void)): 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." | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         this._callbacks.push(callback); | 
					
						
							|  |  |  |         return this; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-03 03:16:43 +02:00
										 |  |  |     public addCallbackAndRun(callback: ((latestData: T) => void)): UIEventSource<T> { | 
					
						
							|  |  |  |         callback(this.data); | 
					
						
							|  |  |  |         return this.addCallback(callback); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2020-07-01 17:38:48 +02:00
										 |  |  |         for (const callback of this._callbacks) { | 
					
						
							|  |  |  |             callback(this.data); | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +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 | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public map<J>(f: ((t: T) => J), | 
					
						
							| 
									
										
										
										
											2020-08-08 02:16:42 +02:00
										 |  |  |                   extraSources: UIEventSource<any>[] = [], | 
					
						
							| 
									
										
										
										
											2021-06-19 19:16:20 +02:00
										 |  |  |                   g: ((j:J, t:T) => T) = undefined): UIEventSource<J> { | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         const self = this; | 
					
						
							| 
									
										
										
										
											2021-01-21 05:52:36 +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), | 
					
						
							|  |  |  |             "map("+this.tag+")" | 
					
						
							| 
									
										
										
										
											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)); | 
					
						
							| 
									
										
										
										
											2020-07-08 11:23:36 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-20 13:28:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 21:58:29 +02:00
										 |  |  |         this.addCallbackAndRun(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)); | 
					
						
							| 
									
										
										
										
											2020-09-18 12:00:38 +02:00
										 |  |  |         if (reverseOverride && otherSource.data !== undefined) { | 
					
						
							|  |  |  |             this.setData(otherSource.data); | 
					
						
							|  |  |  |         } 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-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-06-30 15:37:30 +02:00
										 |  |  |     addCallbackAndRunD(callback: (data :T ) => void) { | 
					
						
							|  |  |  |         this.addCallbackAndRun(data => { | 
					
						
							|  |  |  |             if(data !== undefined && data !== null){ | 
					
						
							|  |  |  |                 callback(data) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  | } |