161 lines
5.9 KiB
TypeScript
161 lines
5.9 KiB
TypeScript
// Leaflet map library (vendored)
|
|
import * as L from "../vendor/leaflet/leaflet.js"
|
|
window.L = L
|
|
|
|
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
|
import "phoenix_html"
|
|
// Establish Phoenix Socket and LiveView configuration.
|
|
import {Socket} from "phoenix"
|
|
import {LiveSocket} from "phoenix_live_view"
|
|
import {hooks as colocatedHooks} from "phoenix-colocated/microwaveprop"
|
|
import topbar from "../vendor/topbar"
|
|
|
|
import initLiveStash from "../../deps/live_stash/assets/js/live-stash.js"
|
|
import {PropagationMap} from "./propagation_map_hook"
|
|
import {ContactMap} from "./contact_map_hook"
|
|
import {ContactsMap} from "./contacts_map_hook"
|
|
import {ElevationProfile} from "./elevation_profile_hook"
|
|
import {WeatherMap} from "./weather_map_hook"
|
|
import {LocateMe, CopyLink} from "./locate_me_hook"
|
|
import {RoverMap} from "./rover_map_hook"
|
|
import {BeaconMap} from "./beacon_map_hook"
|
|
|
|
interface UtcClockHook extends ViewHook {
|
|
timer: ReturnType<typeof setInterval>
|
|
tick(): void
|
|
}
|
|
|
|
const UtcClock = {
|
|
mounted(this: UtcClockHook) {
|
|
this.tick()
|
|
this.timer = setInterval(() => this.tick(), 10_000)
|
|
},
|
|
tick(this: UtcClockHook) {
|
|
const now = new Date()
|
|
const h = now.getUTCHours().toString().padStart(2, "0")
|
|
const m = now.getUTCMinutes().toString().padStart(2, "0")
|
|
this.el.textContent = `${h}:${m} UTC`
|
|
},
|
|
destroyed(this: UtcClockHook) {
|
|
clearInterval(this.timer)
|
|
}
|
|
}
|
|
|
|
interface CommaNumberHook extends ViewHook {
|
|
format(): void
|
|
}
|
|
|
|
const CommaNumber = {
|
|
mounted(this: CommaNumberHook) {
|
|
this.el.addEventListener("input", () => this.format())
|
|
this.format()
|
|
},
|
|
updated(this: CommaNumberHook) { this.format() },
|
|
format(this: CommaNumberHook) {
|
|
const input = (this.el.querySelector("input") || this.el) as HTMLInputElement
|
|
const raw = input.value.replace(/,/g, "")
|
|
if (raw === "" || raw === "-") return
|
|
const cursor = input.selectionStart ?? 0
|
|
const commasBefore = (input.value.slice(0, cursor).match(/,/g) || []).length
|
|
const parts = raw.split(".")
|
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
|
const formatted = parts.join(".")
|
|
if (formatted !== input.value) {
|
|
input.value = formatted
|
|
const commasAfter = (formatted.slice(0, cursor).match(/,/g) || []).length
|
|
const newCursor = cursor + (commasAfter - commasBefore)
|
|
input.setSelectionRange(newCursor, newCursor)
|
|
}
|
|
}
|
|
}
|
|
|
|
const csrfToken = document.querySelector("meta[name='csrf-token']")!.getAttribute("content")
|
|
const liveSocket = new LiveSocket("/live", Socket, {
|
|
longPollFallbackMs: 2500,
|
|
params: initLiveStash({_csrf_token: csrfToken}),
|
|
hooks: {...colocatedHooks, PropagationMap, UtcClock, ContactMap, ContactsMap, ElevationProfile, WeatherMap, LocateMe, CopyLink, RoverMap, BeaconMap, CommaNumber},
|
|
})
|
|
|
|
// Show progress bar on live navigation and form submits
|
|
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
|
window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
|
|
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
|
|
|
|
// Delay disconnect alerts by 3 seconds so brief hiccups don't flash errors
|
|
let disconnectTimer: ReturnType<typeof setTimeout> | null = null
|
|
const DISCONNECT_DELAY_MS = 3000
|
|
|
|
function showDisconnectAlert(id: string) {
|
|
const el = document.getElementById(id)
|
|
if (el) el.removeAttribute("hidden")
|
|
}
|
|
|
|
function hideDisconnectAlert(id: string) {
|
|
const el = document.getElementById(id)
|
|
if (el) el.setAttribute("hidden", "")
|
|
}
|
|
|
|
new MutationObserver(() => {
|
|
const root = document.querySelector("[data-phx-main]")
|
|
if (!root) return
|
|
|
|
const hasError = root.classList.contains("phx-client-error") ||
|
|
root.classList.contains("phx-server-error")
|
|
|
|
if (hasError && !disconnectTimer) {
|
|
disconnectTimer = setTimeout(() => {
|
|
const r = document.querySelector("[data-phx-main]")
|
|
if (r?.classList.contains("phx-client-error")) showDisconnectAlert("client-error")
|
|
if (r?.classList.contains("phx-server-error")) showDisconnectAlert("server-error")
|
|
}, DISCONNECT_DELAY_MS)
|
|
} else if (!hasError && disconnectTimer) {
|
|
clearTimeout(disconnectTimer)
|
|
disconnectTimer = null
|
|
hideDisconnectAlert("client-error")
|
|
hideDisconnectAlert("server-error")
|
|
}
|
|
}).observe(document.body, {subtree: true, attributes: true, attributeFilter: ["class"]})
|
|
|
|
// connect if there are any LiveViews on the page
|
|
liveSocket.connect()
|
|
|
|
// expose liveSocket on window for web console debug logs and latency simulation:
|
|
// >> liveSocket.enableDebug()
|
|
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
|
// >> liveSocket.disableLatencySim()
|
|
window.liveSocket = liveSocket
|
|
|
|
// The lines below enable quality of life phoenix_live_reload
|
|
// development features:
|
|
//
|
|
// 1. stream server logs to the browser console
|
|
// 2. click on elements to jump to their definitions in your code editor
|
|
//
|
|
if (process.env.NODE_ENV === "development") {
|
|
window.addEventListener("phx:live_reload:attached", ({detail: reloader}: any) => {
|
|
// Enable server log streaming to client.
|
|
// Disable with reloader.disableServerLogs()
|
|
reloader.enableServerLogs()
|
|
|
|
// Open configured PLUG_EDITOR at file:line of the clicked element's HEEx component
|
|
//
|
|
// * click with "c" key pressed to open at caller location
|
|
// * click with "d" key pressed to open at function component definition location
|
|
let keyDown: string | null
|
|
window.addEventListener("keydown", (e: KeyboardEvent) => keyDown = e.key)
|
|
window.addEventListener("keyup", (_e: KeyboardEvent) => keyDown = null)
|
|
window.addEventListener("click", (e: MouseEvent) => {
|
|
if(keyDown === "c"){
|
|
e.preventDefault()
|
|
e.stopImmediatePropagation()
|
|
reloader.openEditorAtCaller(e.target)
|
|
} else if(keyDown === "d"){
|
|
e.preventDefault()
|
|
e.stopImmediatePropagation()
|
|
reloader.openEditorAtDef(e.target)
|
|
}
|
|
}, true)
|
|
|
|
window.liveReloader = reloader
|
|
})
|
|
}
|