| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  | // @ts-ignore
 | 
					
						
							|  |  |  | import osmAuth from "osm-auth"; | 
					
						
							|  |  |  | import {UIEventSource} from "../UI/UIEventSource"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class UserDetails { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public loggedIn = false; | 
					
						
							|  |  |  |     public name = "Not logged in"; | 
					
						
							|  |  |  |     public csCount = 0; | 
					
						
							|  |  |  |     public img: string; | 
					
						
							|  |  |  |     public unreadMessages = 0; | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |     public totalMessages = 0; | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |     public osmConnection: OsmConnection; | 
					
						
							|  |  |  |     public dryRun: boolean; | 
					
						
							|  |  |  |     home: { lon: number; lat: number }; | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export class OsmConnection { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private auth = new osmAuth({ | 
					
						
							|  |  |  |         oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', | 
					
						
							|  |  |  |         oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', | 
					
						
							|  |  |  |         auto: true // show a login form if the user is not authenticated and
 | 
					
						
							|  |  |  |                    // you try to do a call
 | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     public userDetails: UIEventSource<UserDetails>; | 
					
						
							|  |  |  |     private _dryRun: boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(dryRun: boolean) { | 
					
						
							|  |  |  |         this.userDetails = new UIEventSource<UserDetails>(new UserDetails()); | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |         this.userDetails.data.osmConnection = this; | 
					
						
							|  |  |  |         this.userDetails.data.dryRun = dryRun; | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |         this._dryRun = dryRun; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this.auth.authenticated()) { | 
					
						
							|  |  |  |             this.AttemptLogin(); // Also updates the user badge
 | 
					
						
							|  |  |  |         }else{ | 
					
						
							|  |  |  |             console.log("Not authenticated"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if(dryRun){ | 
					
						
							|  |  |  |             console.log("DRYRUN ENABLED"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public LogOut() { | 
					
						
							|  |  |  |         this.auth.logout(); | 
					
						
							| 
									
										
										
										
											2020-07-01 17:51:55 +02:00
										 |  |  |         this.userDetails.data.loggedIn = false; | 
					
						
							|  |  |  |         this.userDetails.ping(); | 
					
						
							|  |  |  |         console.log("Logged out") | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public AttemptLogin() { | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'GET', | 
					
						
							|  |  |  |             path: '/api/0.6/user/details' | 
					
						
							|  |  |  |         }, function (err, details) { | 
					
						
							|  |  |  |             if(err != null){ | 
					
						
							|  |  |  |                 console.log(err); | 
					
						
							|  |  |  |                 self.auth.logout(); | 
					
						
							|  |  |  |                 self.userDetails.data.loggedIn = false; | 
					
						
							|  |  |  |                 self.userDetails.ping(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |             if (details == null) { | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |              | 
					
						
							|  |  |  |             self.UpdatePreferences(); | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             // details is an XML DOM of user details
 | 
					
						
							|  |  |  |             let userInfo = details.getElementsByTagName("user")[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |             // let moreDetails = new DOMParser().parseFromString(userInfo.innerHTML, "text/xml");
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             let data = self.userDetails.data; | 
					
						
							|  |  |  |             data.loggedIn = true; | 
					
						
							| 
									
										
										
										
											2020-07-01 21:21:29 +02:00
										 |  |  |             console.log("Login completed, userinfo is ", userInfo); | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             data.name = userInfo.getAttribute('display_name'); | 
					
						
							|  |  |  |             data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count"); | 
					
						
							| 
									
										
										
										
											2020-06-28 23:33:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             data.img = undefined; | 
					
						
							|  |  |  |             const imgEl = userInfo.getElementsByTagName("img"); | 
					
						
							|  |  |  |             if (imgEl !== undefined && imgEl[0] !== undefined) { | 
					
						
							|  |  |  |                 data.img = imgEl[0].getAttribute("href"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             data.img = data.img ?? "./assets/osm-logo.svg"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |             const homeEl = userInfo.getElementsByTagName("home"); | 
					
						
							|  |  |  |             if (homeEl !== undefined && homeEl[0] !== undefined) { | 
					
						
							|  |  |  |                 const lat = parseFloat(homeEl[0].getAttribute("lat")); | 
					
						
							|  |  |  |                 const lon = parseFloat(homeEl[0].getAttribute("lon")); | 
					
						
							|  |  |  |                 data.home = {lat: lat, lon: lon}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |             const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0]; | 
					
						
							|  |  |  |             data.unreadMessages = parseInt(messages.getAttribute("unread")); | 
					
						
							|  |  |  |             data.totalMessages = parseInt(messages.getAttribute("count")); | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             self.userDetails.ping(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * All elements with class 'activate-osm-authentication' are loaded and get an 'onclick' to authenticate | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     registerActivateOsmAUthenticationClass() { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-28 00:06:23 +02:00
										 |  |  |         const self = this; | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |         const authElements = document.getElementsByClassName("activate-osm-authentication"); | 
					
						
							|  |  |  |         for (let i = 0; i < authElements.length; i++) { | 
					
						
							|  |  |  |             let element = authElements.item(i); | 
					
						
							|  |  |  |             // @ts-ignore
 | 
					
						
							|  |  |  |             element.onclick = function () { | 
					
						
							| 
									
										
										
										
											2020-06-28 00:06:23 +02:00
										 |  |  |                 self.AttemptLogin(); | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |     public preferences = new UIEventSource<any>({}); | 
					
						
							|  |  |  |     private UpdatePreferences() { | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'GET', | 
					
						
							|  |  |  |             path: '/api/0.6/user/preferences' | 
					
						
							|  |  |  |         }, function (error, value: XMLDocument) { | 
					
						
							|  |  |  |             if(error){ | 
					
						
							|  |  |  |                 console.log("Could not load preferences", error); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const prefs = value.getElementsByTagName("preference"); | 
					
						
							|  |  |  |             for (let i = 0; i < prefs.length; i++) { | 
					
						
							|  |  |  |                 const pref = prefs[i]; | 
					
						
							|  |  |  |                 const k = pref.getAttribute("k"); | 
					
						
							|  |  |  |                 const v = pref.getAttribute("v"); | 
					
						
							|  |  |  |                 self.preferences.data[k] = v; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             self.preferences.ping(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2020-07-01 21:21:29 +02:00
										 |  |  |     public SetPreference(k:string, v:string) { | 
					
						
							| 
									
										
										
										
											2020-07-05 18:59:47 +02:00
										 |  |  |         if(!this.userDetails.data.loggedIn){ | 
					
						
							|  |  |  |             console.log("Not saving preference: user not logged in"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-01 21:21:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (this.preferences.data[k] === v) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         console.log("Updating preference", k, " to ", v); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |         this.preferences.data[k] = v; | 
					
						
							|  |  |  |         this.preferences.ping(); | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'PUT', | 
					
						
							| 
									
										
										
										
											2020-07-01 21:21:29 +02:00
										 |  |  |             path: '/api/0.6/user/preferences/' + k, | 
					
						
							|  |  |  |             options: {header: {'Content-Type': 'text/plain'}}, | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |             content: v | 
					
						
							| 
									
										
										
										
											2020-07-01 21:21:29 +02:00
										 |  |  |         }, function (error, result) { | 
					
						
							|  |  |  |             if (error) { | 
					
						
							| 
									
										
										
										
											2020-06-29 03:12:44 +02:00
										 |  |  |                 console.log("Could not set preference", error); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             console.log("Preference written!", result); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |     private static parseUploadChangesetResponse(response: XMLDocument) { | 
					
						
							|  |  |  |         const nodes = response.getElementsByTagName("node"); | 
					
						
							|  |  |  |         const mapping = {}; | 
					
						
							|  |  |  |         // @ts-ignore
 | 
					
						
							|  |  |  |         for (const node of nodes) { | 
					
						
							|  |  |  |             const oldId = parseInt(node.attributes.old_id.value); | 
					
						
							|  |  |  |             const newId = parseInt(node.attributes.new_id.value); | 
					
						
							|  |  |  |             if (oldId !== undefined && newId !== undefined && | 
					
						
							|  |  |  |                 !isNaN(oldId) && !isNaN(newId)) { | 
					
						
							|  |  |  |                 mapping["node/"+oldId] = "node/"+newId; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return mapping; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public UploadChangeset(comment: string, generateChangeXML: ((csid: string) => string), | 
					
						
							|  |  |  |                            handleMapping: ((idMapping: any) => void), | 
					
						
							|  |  |  |                            continuation: (() => void)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this._dryRun) { | 
					
						
							|  |  |  |             console.log("NOT UPLOADING as dryrun is true"); | 
					
						
							|  |  |  |             var changesetXML = generateChangeXML("123456"); | 
					
						
							|  |  |  |             console.log(changesetXML); | 
					
						
							| 
									
										
										
										
											2020-06-27 03:06:51 +02:00
										 |  |  |             continuation(); | 
					
						
							| 
									
										
										
										
											2020-06-24 00:35:19 +02:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         this.OpenChangeset(comment, | 
					
						
							|  |  |  |             function (csId) { | 
					
						
							|  |  |  |                 var changesetXML = generateChangeXML(csId); | 
					
						
							|  |  |  |                 self.AddChange(csId, changesetXML, | 
					
						
							|  |  |  |                     function (csId, mapping) { | 
					
						
							|  |  |  |                         self.CloseChangeset(csId, continuation); | 
					
						
							|  |  |  |                         handleMapping(mapping); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         this.userDetails.data.csCount++; | 
					
						
							|  |  |  |         this.userDetails.ping(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private OpenChangeset(comment: string, continuation: ((changesetId: string) => void)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'PUT', | 
					
						
							|  |  |  |             path: '/api/0.6/changeset/create', | 
					
						
							|  |  |  |             options: { header: { 'Content-Type': 'text/xml' } }, | 
					
						
							|  |  |  |             content: '<osm><changeset>' + | 
					
						
							|  |  |  |                 '<tag k="created_by" v="MapComplete 0.0.0" />' + | 
					
						
							|  |  |  |                 '<tag k="comment" v="' + comment + '"/>' + | 
					
						
							|  |  |  |                 '</changeset></osm>' | 
					
						
							|  |  |  |         }, function (err, response) { | 
					
						
							|  |  |  |             if (response === undefined) { | 
					
						
							|  |  |  |                 console.log("err", err); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 continuation(response); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private AddChange(changesetId: string, | 
					
						
							|  |  |  |                       changesetXML: string, | 
					
						
							|  |  |  |                       continuation: ((changesetId: string, idMapping: any) => void)){ | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'POST', | 
					
						
							|  |  |  |             options: { header: { 'Content-Type': 'text/xml' } }, | 
					
						
							|  |  |  |             path: '/api/0.6/changeset/'+changesetId+'/upload', | 
					
						
							|  |  |  |             content: changesetXML | 
					
						
							|  |  |  |         }, function (err, response) { | 
					
						
							|  |  |  |             if (response == null) { | 
					
						
							|  |  |  |                 console.log("err", err); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const mapping = OsmConnection.parseUploadChangesetResponse(response); | 
					
						
							|  |  |  |             console.log("Uplaoded changeset ", changesetId); | 
					
						
							|  |  |  |             continuation(changesetId, mapping); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private CloseChangeset(changesetId: string, continuation : (() => void)) { | 
					
						
							|  |  |  |         console.log("closing"); | 
					
						
							|  |  |  |         this.auth.xhr({ | 
					
						
							|  |  |  |             method: 'PUT', | 
					
						
							|  |  |  |             path: '/api/0.6/changeset/'+changesetId+'/close', | 
					
						
							|  |  |  |         }, function (err, response) { | 
					
						
							|  |  |  |             if (response == null) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 console.log("err", err); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             console.log("Closed changeset ", changesetId); | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             if(continuation !== undefined){ | 
					
						
							|  |  |  |                 continuation(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |