| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- window.addEventListener('error', ((event, source, lineno, colno, error) => {
- alert(source + ' ' + lineno + ',' + colno + ': ' + error.message)
- }))
- const MIN_SPEED = 3 // km/h
- function round(value, decimals = 0) {
- return Math.round(value * (10 ** decimals)) / (10 ** decimals)
- }
- function fmtHhMm(h, m) {
- return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m)
- }
- function fmtDuration(seconds, resolution = null) {
- const divmod = (x, y) => [Math.floor(x / y), x % y];
- let days = 0, hours = 0, mins = 0, secs = seconds;
- [mins, secs] = divmod(secs, 60);
- [hours, mins] = divmod(mins, 60);
- [days, hours] = divmod(hours, 24);
- switch (resolution) {
- case "d":
- hours = mins = secs = 0
- break
- case "h":
- mins = secs = 0
- break
- case "m":
- secs = 0
- break
- case "s":
- secs = Math.floor(secs)
- break
- }
- const fmtToken = (value, unit) => ((value > 0 ? value + unit + ' ' : ''))
- const result = fmtToken(days, 'd') + fmtToken(hours, 'h') + fmtToken(mins, 'm') + fmtToken(secs, 's')
- if (result.length > 0) {
- return result.trim()
- } else {
- return '0' + (resolution || 's')
- }
- }
- document.addEventListener('alpine:init', () => {
- Alpine.data('main', () => ({
- page: 'dashboard',
- error: '',
- speed: {
- minRide: MIN_SPEED, // km/h
- current: 0, // km/h
- max: 0, // km/h
- avg: 0, // km/h
- nowaitAvg: 0, // km/h
- timeseries: {
- cache: [],
- values: []
- }
- },
- time: {
- start: new Date(), // date
- update: null, // epoch number
- waitDuration: 0, // secs
- tripDuration: 0, // secs
- updateDelay: null, //secs
- },
- altitude: {
- current: null, // meter
- accuracy: null // meter
- },
- heading: null, // 0.0-360.0 degrees
- positionAccuracy: null, // meter
- noSleep: null,
- init() {
- if (navigator.geolocation) {
- setInterval(() => {
- const now = new Date()
- this.time.tripDuration = (now.getTime() - this.time.start.getTime()) / 1000
- if (this.time.update)
- this.time.updateDelay = Math.floor((now.getTime() - this.time.update) / 1000)
- if (!isNaN(this.speed.current)) {
- this.speed.timeseries.cache.push(this.speed.current)
- if (Math.floor(now.getTime() / 1000) % 5 === 0) {
- if (this.speed.timeseries.cache.length > 0) {
- const currentAvg = this.speed.timeseries.cache.reduce((sum, val) => sum + val, 0) / this.speed.timeseries.cache.length
- this.speed.timeseries.values.push(currentAvg)
- this.speed.timeseries.cache = []
- }
- if (this.speed.timeseries.values.length > 0) {
- let sumTotal = 0, sumNoWait = 0, countNoWait = 0
- for (const sp of this.speed.timeseries.values) {
- sumTotal += sp
- if (sp >= MIN_SPEED) {
- sumNoWait += sp
- countNoWait++
- }
- }
- this.speed.avg = sumTotal / this.speed.timeseries.values.length
- this.speed.nowaitAvg = (sumNoWait / countNoWait) || 0
- }
- }
- if (this.speed.current < MIN_SPEED) {
- this.time.waitDuration++
- }
- }
- }, 1000)
- navigator.geolocation.watchPosition(({timestamp, coords}) => {
- this.time.update = timestamp
- this.speed.current = coords.speed * 3.6 // m/s to km/h
- if (!this.speed.max || this.speed.current > this.speed.max) this.speed.max = this.speed.current
- this.altitude.current = coords.altitude
- this.altitude.accuracy = coords.altitudeAccuracy
- this.heading = coords.heading
- this.positionAccuracy = coords.accuracy
- }, err => {
- this.error = err.message
- }, {
- enableHighAccuracy: true,
- timeout: 15_000
- })
- } else {
- alert("Geolocation is not supported by this browser.")
- }
- },
- fmtStartTime() {
- return fmtHhMm(this.time.start.getHours(), this.time.start.getMinutes())
- },
- fmtTripTime() {
- return fmtDuration(this.time.tripDuration, 'm')
- },
- fmtWaitTime() {
- return fmtDuration(this.time.waitDuration)
- },
- fmtUpdateDelay() {
- return fmtDuration(this.time.updateDelay) + ' ago'
- },
- fmtHeading() {
- const directions = ['N', 'NE', 'NE', 'NE', 'E', 'SE', 'SE', 'SE', 'S', 'SW', 'SW', 'SW', 'W', 'NW', 'NW', 'NW', 'N']
- return directions[round(this.heading / 22.5)]
- },
- fmtCurrentSpeed() {
- return round(this.speed.current)
- },
- fmtMaxSpeed() {
- return round(this.speed.max, 1).toLocaleString(undefined, {minimumFractionDigits: 1})
- },
- fmtAvgSpeed() {
- return round(this.speed.avg || 0, 1).toLocaleString(undefined, {minimumFractionDigits: 1})
- },
- fmtAvgNoWaitSpeed() {
- return round(this.speed.nowaitAvg || 0, 1).toLocaleString(undefined, {minimumFractionDigits: 1})
- },
- clamp(value, min, max) {
- return Math.min(Math.max(value, min), max)
- },
- toggleNoSleep() {
- if (this.noSleep) {
- this.noSleep.disable()
- this.noSleep = null
- } else {
- this.noSleep = new NoSleep()
- this.noSleep.enable()
- }
- }
- }))
- })
- window.addEventListener('load', async () => {
- if ('serviceWorker' in navigator) {
- try {
- await navigator.serviceWorker.register('./sw.js');
- } catch (e) {
- console.error(e)
- }
- }
- })
|