package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"syscall/js"
	"time"
)

func fetchOauthClients(oauthList js.Value, localStorage js.Value, body []byte) {
	// Fetch the OAuth clients
	requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/oauth/list")
	if err != nil {
		var statusText = js.Global().Get("document").Call("createElement", "p")
		statusText.Set("innerText", "Error joining URL: "+err.Error())
		oauthList.Call("appendChild", statusText)
		return
	}

	response, err := http.Post(requestUri, "application/json", bytes.NewReader(body))
	if err != nil {
		var statusText = js.Global().Get("document").Call("createElement", "p")
		statusText.Set("innerText", "Error contacting server: "+err.Error())
		oauthList.Call("appendChild", statusText)
		return
	}

	// Get all our ducks in a row
	var responseMap map[string]interface{}

	// Read the response
	decoder := json.NewDecoder(response.Body)
	err = decoder.Decode(&responseMap)
	if err != nil {
		var statusText = js.Global().Get("document").Call("createElement", "p")
		statusText.Set("innerText", "Error decoding server response: "+err.Error())
		oauthList.Call("appendChild", statusText)
		return
	}

	// Close the response body
	err = response.Body.Close()
	if err != nil {
		fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
	}

	if response.StatusCode == 200 {
		for i := 0; i < oauthList.Get("childNodes").Length(); i++ {
			this := oauthList.Get("childNodes").Index(i)
			if this.Get("tagName").String() != "H2" {
				this.Call("remove")
			}
		}

		clients, ok := responseMap["apps"].([]interface{})
		if !ok {
			var statusText = js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", "Hi there! You don't have any OAuth2 clients yet. Create one above!")
			oauthList.Call("appendChild", statusText)
		} else {
			for _, app := range clients {
				var oauthElement = js.Global().Get("document").Call("createElement", "div")
				var oauthText = js.Global().Get("document").Call("createElement", "p")
				var oauthName = js.Global().Get("document").Call("createElement", "p")
				var oauthUrl = js.Global().Get("document").Call("createElement", "p")
				var oauthRemoveButton = js.Global().Get("document").Call("createElement", "button")
				oauthText.Set("innerText", app.(map[string]interface{})["appId"].(string))
				oauthName.Set("innerText", app.(map[string]interface{})["name"].(string))
				oauthUrl.Set("innerText", app.(map[string]interface{})["redirectUri"].(string))
				oauthRemoveButton.Set("innerText", "Delete permanently")
				oauthRemoveButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
					go func() {
						if js.Global().Call("confirm", "Are you sure you want to delete this client? This action cannot be undone.").Bool() {
							// Create the request body
							requestBody := map[string]interface{}{
								"token": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
								"appId": app.(map[string]interface{})["appId"].(string),
							}

							// Marshal the body
							bodyBytes, err := json.Marshal(requestBody)
							if err != nil {
								js.Global().Call("alert", "Error marshaling body: "+err.Error())
								return
							}

							// Send the request
							requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/oauth/remove")
							if err != nil {
								js.Global().Call("alert", "Error joining URL: "+err.Error())
								return
							}

							response, err := http.Post(requestUri, "application/json", bytes.NewReader(bodyBytes))
							if err != nil {
								js.Global().Call("alert", "Error contacting server: "+err.Error())
								return
							}

							// Get all our ducks in a row
							var responseMap map[string]interface{}

							// Read the response
							decoder := json.NewDecoder(response.Body)
							err = decoder.Decode(&responseMap)
							if err != nil {
								js.Global().Call("alert", "Error decoding server response: "+err.Error())
								return
							}

							// Close the response body
							err = response.Body.Close()
							if err != nil {
								fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
							}

							if response.StatusCode == 200 {
								oauthElement.Call("remove")
								if oauthList.Get("childNodes").Length() == 1 {
									var statusText = js.Global().Get("document").Call("createElement", "p")
									statusText.Set("innerText", "Hi there! You don't have any OAuth2 clients yet. Create one above!")
									oauthList.Call("appendChild", statusText)
								}
							} else if response.StatusCode != 500 {
								js.Global().Call("alert", responseMap["error"].(string))
							} else {
								js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
							}
						}
					}()
					return nil
				}))

				oauthElement.Call("append", oauthName)
				oauthElement.Call("append", oauthText)
				oauthElement.Call("append", oauthUrl)

				openId := false
				clientKeyShare := false
				scopes, ok := app.(map[string]interface{})["scopes"].([]interface{})
				if ok {
					for _, scope := range scopes {
						if scope.(string) == "openid" {
							openId = true
						} else if scope.(string) == "clientKeyShare" {
							if app.(map[string]interface{})["keyShareUri"].(string) != "" {
								clientKeyShare = true
								keyShareUri := js.Global().Get("document").Call("createElement", "p")
								keyShareUri.Set("innerText", "Key Share URI: "+app.(map[string]interface{})["keyShareUri"].(string))
								oauthElement.Call("append", keyShareUri)
							}
						}
					}

					oauthScopes := js.Global().Get("document").Call("createElement", "p")
					var scopeText strings.Builder
					if openId {
						scopeText.WriteString("OpenID")
					}

					if clientKeyShare {
						if openId {
							scopeText.WriteString(", ")
						}
						scopeText.WriteString("clientKeyShare")
					}

					oauthScopes.Set("innerText", "Scopes: "+scopeText.String())
					oauthElement.Call("append", oauthScopes)
				}

				oauthElement.Call("append", oauthRemoveButton)
				oauthElement.Get("classList").Call("add", "oauthEntry")

				oauthList.Call("appendChild", oauthElement)
			}
		}
	} else if response.StatusCode != 500 {
		statusText := js.Global().Get("document").Call("createElement", "p")
		statusText.Set("innerText", responseMap["error"].(string))
		oauthList.Call("appendChild", statusText)
	} else {
		statusText := js.Global().Get("document").Call("createElement", "p")
		statusText.Set("innerText", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
		oauthList.Call("appendChild", statusText)
	}
}

func main() {
	// Transition in
	js.Global().Get("document").Get("body").Set("style", "display: initial")
	js.Global().Get("swipe-out").Get("classList").Call("add", "swipe-out-animate")

	var sleepTime = 200 * time.Millisecond
	if js.Global().Get("window").Call("matchMedia", "(prefers-reduced-motion: reduce)").Get("matches").Bool() {
		sleepTime = 500 * time.Millisecond
	}

	time.Sleep(sleepTime)

	// Redirect to log-in if not signed in
	localStorage := js.Global().Get("localStorage")
	if localStorage.Call("getItem", "DONOTSHARE-secretKey").IsNull() {
		js.Global().Get("swipe").Get("classList").Call("add", "swipe-animate")
		time.Sleep(sleepTime)
		js.Global().Get("window").Get("location").Call("replace", "/login"+js.Global().Get("window").Get("location").Get("search").String())
	}

	var submitButton = js.Global().Get("document").Call("getElementById", "submitButton")
	var nameBox = js.Global().Get("document").Call("getElementById", "nameBox")
	var usernameBox = js.Global().Get("document").Call("getElementById", "usernameBox")
	var dateBox = js.Global().Get("document").Call("getElementById", "dateBox")
	var clientKeyShareBox = js.Global().Get("document").Call("getElementById", "clientKeyShareBox")
	var redirectUriBox = js.Global().Get("document").Call("getElementById", "redirectUriBox")
	var openIdBox = js.Global().Get("document").Call("getElementById", "openIdBox")
	var statusBox = js.Global().Get("document").Call("getElementById", "statusBox")
	var oauthList = js.Global().Get("document").Call("getElementById", "oauthList")
	var sessionList = js.Global().Get("document").Call("getElementById", "sessionList")
	var deleteAccountButton = js.Global().Get("document").Call("getElementById", "deleteAccountButton")
	var logoutButton = js.Global().Get("document").Call("getElementById", "logoutButton")
	var devAccountSwitcher = js.Global().Get("document").Call("getElementById", "devAccountSwitcher")
	var developers = js.Global().Get("document").Call("getElementById", "developers")
	var account = js.Global().Get("document").Call("getElementById", "account")

	// Fetch the OAuth clients and sessions
	go func() {
		// Check if the token is valid
		requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/loggedIn")
		if err != nil {
			js.Global().Call("alert", "Error joining URL: "+err.Error())
			return
		}

		loggedInBody := map[string]interface{}{
			"token": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
		}

		// Marshal the body
		body, err := json.Marshal(loggedInBody)
		if err != nil {
			js.Global().Call("alert", "Error marshaling signup body: "+err.Error())
			return
		}

		response, err := http.Post(requestUri, "application/json", bytes.NewReader(body))
		if err != nil {
			js.Global().Call("alert", "Error contacting server: "+err.Error())
			return
		}

		// Check if the response is 200
		if response.StatusCode == 401 {
			// Close the response body
			err = response.Body.Close()
			if err != nil {
				fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
			}

			// Redirect to log-out if not signed in
			js.Global().Get("swipe").Get("classList").Call("add", "swipe-animate")
			time.Sleep(sleepTime)
			js.Global().Get("window").Get("location").Call("replace", "/logout")
			return
		} else if response.StatusCode == 500 {
			// Read the response
			var responseMap map[string]interface{}
			decoder := json.NewDecoder(response.Body)
			err = decoder.Decode(&responseMap)
			if err != nil {
				js.Global().Call("alert", "Error decoding server response: "+err.Error())
				return
			}

			// Close the response body
			err = response.Body.Close()
			if err != nil {
				fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
			}

			// Alert the user if the server is down
			js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
			return
		}

		// Close the response body
		err = response.Body.Close()
		if err != nil {
			fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
		}

		// Fetch the OAuth clients
		fetchOauthClients(oauthList, localStorage, body)

		// Fetch the sessions
		requestUri, err = url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/session/list")
		if err != nil {
			var statusText = js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", "Error joining URL: "+err.Error())
			sessionList.Call("appendChild", statusText)
			return
		}

		response, err = http.Post(requestUri, "application/json", bytes.NewReader(body))
		if err != nil {
			var statusText = js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", "Error contacting server: "+err.Error())
			sessionList.Call("appendChild", statusText)
			return
		}

		// Get all our ducks in a row
		var responseMap map[string]interface{}

		// Read the response
		decoder := json.NewDecoder(response.Body)
		err = decoder.Decode(&responseMap)
		if err != nil {
			var statusText = js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", "Error decoding server response: "+err.Error())
			sessionList.Call("appendChild", statusText)
			return
		}

		// Close the response body
		err = response.Body.Close()
		if err != nil {
			fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
		}

		if response.StatusCode == 200 {
			if len(responseMap["sessions"].([]interface{})) == 0 {
				var statusText = js.Global().Get("document").Call("createElement", "p")
				statusText.Set("innerText", "Hi there! You don't have any sessions logged in, somehow. Congrats on breaking the laws of physics!")
				sessionList.Call("appendChild", statusText)
			} else {
				for i := 0; i < sessionList.Get("childNodes").Length(); i++ {
					this := sessionList.Get("childNodes").Index(i)
					if this.Get("tagName").String() != "H2" {
						this.Call("remove")
					}
				}

				for _, session := range responseMap["sessions"].([]interface{}) {
					var sessionElement = js.Global().Get("document").Call("createElement", "div")
					var sessionDevice = js.Global().Get("document").Call("createElement", "p")
					var sessionImage = js.Global().Get("document").Call("createElement", "img")
					var sessionRemoveButton = js.Global().Get("document").Call("createElement", "button")
					if session.(map[string]interface{})["session"].(string) == localStorage.Call("getItem", "DONOTSHARE-secretKey").String() {
						sessionDevice.Set("innerText", "(current) "+session.(map[string]interface{})["device"].(string))
					} else {
						sessionDevice.Set("innerText", session.(map[string]interface{})["device"].(string))
					}

					if strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "nt") || strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "macintosh") {
						sessionImage.Set("src", "/static/svg/device_computer.svg")
					} else if strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "iphone") || strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "android") || strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "ipod") {
						sessionImage.Set("src", "/static/svg/device_smartphone.svg")
					} else if strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "curl") || strings.Contains(strings.ToLower(session.(map[string]interface{})["device"].(string)), "go-http-client") {
						sessionImage.Set("src", "/static/svg/device_terminal.svg")
					} else {
						sessionImage.Set("src", "/static/svg/device_other.svg")
					}

					sessionRemoveButton.Set("innerText", "Force log out")
					sessionRemoveButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
						go func() {
							// Create the request body
							body := map[string]interface{}{
								"token":   localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
								"session": session.(map[string]interface{})["session"].(string),
							}

							// Marshal the body
							bodyBytes, err := json.Marshal(body)
							if err != nil {
								js.Global().Call("alert", "Error marshaling body: "+err.Error())
								return
							}

							// Send the request
							requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/session/remove")
							if err != nil {
								js.Global().Call("alert", "Error joining URL: "+err.Error())
								return
							}

							response, err := http.Post(requestUri, "application/json", bytes.NewReader(bodyBytes))
							if err != nil {
								js.Global().Call("alert", "Error contacting server: "+err.Error())
								return
							}

							// Get all our ducks in a row
							var responseMap map[string]interface{}

							// Read the response
							decoder := json.NewDecoder(response.Body)
							err = decoder.Decode(&responseMap)
							if err != nil {
								js.Global().Call("alert", "Error decoding server response: "+err.Error())
								return
							}

							// Close the response body
							err = response.Body.Close()
							if err != nil {
								fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
							}

							if response.StatusCode == 200 {
								sessionElement.Call("remove")
								if session.(map[string]interface{})["session"].(string) == localStorage.Call("getItem", "DONOTSHARE-secretKey").String() {
									js.Global().Get("swipe").Get("classList").Call("add", "swipe-animate")
									time.Sleep(sleepTime)
									js.Global().Get("window").Get("location").Call("replace", "/logout")
								}
							} else if response.StatusCode != 500 {
								js.Global().Call("alert", responseMap["error"].(string))
							} else {
								js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
							}
						}()
						return nil
					}))

					sessionElement.Call("append", sessionImage)
					sessionElement.Call("append", sessionDevice)
					sessionElement.Call("append", sessionRemoveButton)
					sessionElement.Get("classList").Call("add", "sessionEntry")

					sessionList.Call("appendChild", sessionElement)
				}
			}
		} else if response.StatusCode != 500 {
			statusText := js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", responseMap["error"].(string))
			sessionList.Call("appendChild", statusText)
		} else {
			statusText := js.Global().Get("document").Call("createElement", "p")
			statusText.Set("innerText", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
			sessionList.Call("appendChild", statusText)
		}

		// Fetch user information
		requestUri, err = url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/userinfo")
		if err != nil {
			js.Global().Call("alert", "Error joining URL: "+err.Error())
			return
		}

		// Re-use the body variable for this request
		response, err = http.Post(requestUri, "application/json", bytes.NewReader(body))
		if err != nil {
			js.Global().Call("alert", "Error contacting server: "+err.Error())
			return
		}

		// Read the response
		decoder = json.NewDecoder(response.Body)
		err = decoder.Decode(&responseMap)
		if err != nil {
			js.Global().Call("alert", "Error decoding server response: "+err.Error())
			return
		}

		// Close the response body
		err = response.Body.Close()
		if err != nil {
			js.Global().Call("alert", "Could not close response body: "+err.Error()+", memory leaks may occur")
		}

		if response.StatusCode == 200 {
			usernameBox.Set("innerText", "Username: "+responseMap["username"].(string))
			dateBox.Set("innerText", "Account created: "+time.Unix(int64(responseMap["created"].(float64)), 0).Format("2006-01-02 15:04:05"))
		} else if response.StatusCode != 500 {
			js.Global().Call("alert", responseMap["error"].(string))
		} else {
			js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
		}
	}()

	submitButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			// Validate the input
			if nameBox.Get("value").String() == "" {
				statusBox.Set("innerText", "An App Name is required!")
				return
			} else if redirectUriBox.Get("value").String() == "" {
				statusBox.Set("innerText", "A Redirect URI is required!")
				return
			}

			// Check for scopes
			var scopes []string
			if openIdBox.Get("checked").Bool() {
				scopes = append(scopes, "openid")
			}
			if clientKeyShareBox.Get("value").String() != "" {
				scopes = append(scopes, "clientKeyShare")
			}

			// Create the request body
			body := map[string]interface{}{
				"name":        nameBox.Get("value").String(),
				"redirectUri": redirectUriBox.Get("value").String(),
				"token":       localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
				"scopes":      scopes,
				"keyShareUri": redirectUriBox.Get("value").String(),
			}

			// Marshal the body
			bodyBytes, err := json.Marshal(body)
			if err != nil {
				statusBox.Set("innerText", "Error marshaling body: "+err.Error())
				return
			}

			// Send the request
			requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/oauth/add")
			if err != nil {
				statusBox.Set("innerText", "Error joining URL: "+err.Error())
				return
			}

			response, err := http.Post(requestUri, "application/json", bytes.NewReader(bodyBytes))
			if err != nil {
				statusBox.Set("innerText", "Error contacting server: "+err.Error())
				return
			}

			// Get all our ducks in a row
			var responseMap map[string]interface{}

			// Read the response
			decoder := json.NewDecoder(response.Body)
			err = decoder.Decode(&responseMap)
			if err != nil {
				statusBox.Set("innerText", "Error decoding server response: "+err.Error())
				return
			}

			// Close the response body
			err = response.Body.Close()
			if err != nil {
				fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
			}

			if response.StatusCode == 200 {
				statusBox.Set("innerText", "Your secret key is: "+responseMap["key"].(string)+" and your client ID is: "+responseMap["appId"].(string)+". This will only ever be shown once!")

				// Update the OAuth clients
				bodyMap := map[string]interface{}{
					"token": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
				}

				// Marshal the body
				body, err := json.Marshal(bodyMap)
				if err != nil {
					fmt.Println("Error marshaling body: " + err.Error() + ", this is non-fatal.")
					return
				}

				// Perform the request
				fetchOauthClients(oauthList, localStorage, body)
			} else if response.StatusCode != 500 {
				statusBox.Set("innerText", responseMap["error"].(string))
			} else {
				statusBox.Set("innerText", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
			}
		}()
		return nil
	}))

	deleteAccountButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			if js.Global().Call("confirm", "Are you sure you would like to delete your account forever? This cannot be undone.").Bool() {
				// Create the request body
				body := map[string]interface{}{
					"token": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
				}

				// Marshal the body
				bodyBytes, err := json.Marshal(body)
				if err != nil {
					statusBox.Set("innerText", "Error marshaling body: "+err.Error())
					return
				}

				// Send the request
				requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/deleteAccount")
				if err != nil {
					statusBox.Set("innerText", "Error joining URL: "+err.Error())
					return
				}

				response, err := http.Post(requestUri, "application/json", bytes.NewReader(bodyBytes))
				if err != nil {
					statusBox.Set("innerText", "Error contacting server: "+err.Error())
					return
				}

				// Get all our ducks in a row
				var responseMap map[string]interface{}

				// Read the response
				decoder := json.NewDecoder(response.Body)
				err = decoder.Decode(&responseMap)
				if err != nil {
					statusBox.Set("innerText", "Error decoding server response: "+err.Error())
					return
				}

				// Close the response body
				err = response.Body.Close()
				if err != nil {
					fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
				}

				if response.StatusCode == 200 {
					js.Global().Get("swipe").Get("classList").Call("add", "swipe-animate")
					time.Sleep(sleepTime)
					js.Global().Get("window").Get("location").Call("replace", "/logout")
				} else if response.StatusCode != 500 {
					js.Global().Call("alert", responseMap["error"].(string))
				} else {
					js.Global().Call("alert", "Something went wrong! (error code: "+responseMap["code"].(string)+")")
				}
			}
		}()
		return nil
	}))

	devAccountSwitcher.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		developers.Get("classList").Call("toggle", "hidden")
		account.Get("classList").Call("toggle", "hidden")
		if devAccountSwitcher.Get("innerText").String() == "Switch to developer view" {
			devAccountSwitcher.Set("innerText", "Switch to account view")
		} else {
			devAccountSwitcher.Set("innerText", "Switch to developer view")
		}
		return nil
	}))

	logoutButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		go func() {
			// Try to remove the session
			requestUri, err := url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/session/remove")
			if err != nil {
				js.Global().Call("alert", "Error joining URL: "+err.Error())
				return
			}

			// Create the request body
			body := map[string]interface{}{
				"token":   localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
				"session": localStorage.Call("getItem", "DONOTSHARE-secretKey").String(),
			}

			// Marshal the body
			bodyBytes, err := json.Marshal(body)
			if err != nil {
				js.Global().Call("alert", "Error marshaling body: "+err.Error())
				return
			}

			// Send the request
			response, err := http.Post(requestUri, "application/json", bytes.NewReader(bodyBytes))
			if err != nil {
				js.Global().Call("alert", "Error contacting server: "+err.Error())
				return
			}

			// Get all our ducks in a row
			var responseMap map[string]interface{}

			// Read the response
			decoder := json.NewDecoder(response.Body)
			err = decoder.Decode(&responseMap)
			if err != nil {
				js.Global().Call("alert", "Error decoding server response: "+err.Error())
				return
			}

			// Close the response body
			err = response.Body.Close()
			if err != nil {
				fmt.Println("Could not close response body: " + err.Error() + ", memory leaks may occur")
			}

			// We don't care about the response, we're logging out anyway
			js.Global().Get("swipe").Get("classList").Call("add", "swipe-animate")
			time.Sleep(sleepTime)
			js.Global().Get("window").Get("location").Call("replace", "/logout")
		}()
		return nil
	}))

	// Wait for events
	select {}
}
