if (typeof Overlay === "undefined") {
var Overlay = class {
workout = null;
workoutState = null;
workoutStatus = null;
display = false;
milestones = [];
program = null;
currentCpm = null;
bikes = [];
programs = [];
workouts = [];
bike = null;
setup = null;
colors = {
"-2": "#F66",
"-1": "#FBB",
"0": "#FFF",
"1": "#BFB",
"2": "#6F6",
};
/**
* @param {HTMLElement} mBody
* @param {HTMLElement} mTopLeft
* @param {HTMLElement} mCenter
* @param {HTMLElement} mBottomRight
*/
constructor(mBody, mTopLeft, mCenter, mBottomRight) {
this.mBody = mBody;
this.mTopLeft = mTopLeft;
this.mCenter = mCenter;
this.mBottomRight = mBottomRight;
this.initStyle();
this.hide();
}
initStyle() {
this.applyDefaultStyle(this.mTopLeft);
this.applyDefaultStyle(this.mCenter);
this.applyDefaultStyle(this.mBottomRight);
this.mTopLeft.id = "overlay-mTopLeft";
this.mTopLeft.style.left = 0;
this.mTopLeft.style.top = "25%";
this.mCenter.id = "overlay-mCenter";
this.mCenter.style.left = "50%";
this.mCenter.style.top = "50%";
this.mCenter.style.transform = "translate(-50%, -50%)";
this.mCenter.style.backgroundColor = "rgba(0, 0, 0, 0.75)";
this.mCenter.style.fontSize = "4vmax";
this.mBottomRight.id = "overlay-mBottomRight";
this.mBottomRight.style.right = 0;
this.mBottomRight.style.bottom = 0;
this.mBottomRight.style.fontSize = "2vmax";
}
applyDefaultStyle(element) {
this.mBody.append(element);
element.style.position = "fixed";
element.style.zIndex = 99999999;
element.style.color = "#FFF";
element.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
element.style.padding = "0.25em 0.5ch";
element.style.fontSize = "3vmax";
element.style.lineHeight = "1.33em";
}
hide() {
this.mTopLeft.style.display = "none";
this.mCenter.style.display = "none";
this.mBottomRight.style.display = "none";
this.display = false;
}
show() {
this.mTopLeft.style.display = "block";
this.mCenter.style.display = "block";
this.mBottomRight.style.display = "block";
this.display = true;
}
async setUp() {
this.writeCenter("Loading...");
this.bikes = await get("/bike");
this.programs = await get("/program");
this.workouts = await get("/workout?active=true");
this.writeCenter(null);
this.updateCenterState();
}
async updateCenterState() {
const state = this.getState();
switch (state) {
case "down":
this.writeCenter(`
(1) Ny økt
(2) Fortsett forrige økt
`);
break;
case "bike":
// TODO: Add this??
if (this.bikes.length === 1) {
this.setup = {state: "program", bike: this.bikes[0]};
this.updateCenterState();
} else {
this.writeCenter("Sykkelvalg ikke støttet");
}
break;
case "program":
const options = this.programs.map((p, i) => `(${i + 1}) ${p.name}`)
this.writeCenter("Velg programm:
" + options.join("
"));
break;
case "disconnected":
this.writeCenter("Kobler til...");
break;
case "connected":
this.writeCenter(`
(Enter) Start/Pause
(Esc) Stopp
`);
break;
case "started":
const calorieDiff = this.calorieDiff();
if (calorieDiff < 0) {
this.writeCenter(`
${calorieDiff} kcal!
`);
} else {
this.writeCenter(null);
}
break;
default:
this.writeCenter(`Ugyldig tilstand: ${state}`);
}
this.updateKeyBindings();
}
updateKeyBindings() {
const dis = this;
this.mBody.onkeyup = function (event) {
const state = dis.getState();
if (state === "program") {
const i = parseInt(event.key, 10);
if (!isNaN(i) && typeof dis.programs[i - 1] !== "undefined") {
dis.bike = {...dis.setup.bike};
dis.startWorkout(dis.bike, dis.programs[i - 1]);
}
}
let handled = false;
switch (event.key) {
case "1":
if (state === "down") {
dis.setup = {state: "bike"};
handled = true;
}
break;
case "2":
if (state === "down") {
const last = dis.workouts[dis.workouts.length - 1];
console.log(last);
if (last !== undefined) {
dis.resumeWorkout(last);
}
handled = true;
}
break;
case "Enter":
if (state === "connected") {
dis.start();
handled = true;
} else if (state === "started") {
dis.pause();
handled = true;
}
break;
case "Escape":
if (state === "connected") {
dis.stop();
handled = true;
}
break;
}
if (handled) {
dis.updateCenterState();
event.preventDefault();
}
}
}
async startWorkout(bike, program) {
this.bike = bike;
this.program = program;
this.milestones = [];
const bikeId = bike.id;
const programId = program.id;
this.workout = await post("/workout", {bikeId, programId});
this.updateCenterState();
this.workout = await post(`/workout/${this.workout.id}/connect`);
this.updateCenterState();
this.setup = null;
this.webSocket();
}
async resumeWorkout(workout) {
this.bike = workout.bike;
this.program = workout.program;
this.workout = workout;
this.setup = null;
this.webSocket();
}
async start() {
this.workout = await post(`/workout/${this.workout.id}/start`);
this.updateCenterState();
}
async pause() {
this.workout = await post(`/workout/${this.workout.id}/pause`);
this.updateCenterState();
}
async stop() {
await post(`/workout/${this.workout.id}/stop`);
this.workout = null;
this.workouts = await post(`/workout?active=true`);
this.updateCenterState();
}
async webSocket() {
let dis = this;
this.socket = new WebSocket(url(`/workout/${this.workout.id}/subscribe`, "ws"));
this.socket.onmessage = function ({data, timeStamp}) {
let body = JSON.parse(data);
if (typeof body.workout !== "undefined") {
dis.workout = {...body.workout, ...dis.workout}
}
if (typeof body.workoutStatusBackfill !== "undefined") {
body.workoutStatusBackfill.forEach(wsbf => dis.updateWorkoutStatus(wsbf));
}
if (typeof body.workoutStatus !== "undefined") {
dis.updateWorkoutStatus(body.workoutStatus)
}
}
}
updateWorkoutStatus(newState = null) {
if (newState !== null) {
this.workoutStatus = newState;
}
if (this.workoutStatus === null) {
this.writeTopLeft("");
return;
}
const {minutes, seconds, calories, distance, rpm} = this.workoutStatus;
const cpmState = this.checkCpm();
const color = this.colors[cpmState];
this.writeTopLeft(`
${pad(minutes)} : ${pad(seconds)}
${calories} kcal
${distance.toFixed(1)} km
${rpm} rpm
KPM: ${this.currentCpm.toFixed(1)}
`);
if (seconds === 0 && minutes > 0) {
this.updateMilestones(minutes, calories);
}
this.updateCenterState();
}
checkCpm() {
const {calories, minutes, seconds} = this.workoutStatus;
this.currentCpm = calories * 60 / ((minutes * 60) + seconds);
if (this.currentCpm === Infinity) {
this.currentCpm = 0;
}
const program = this.program;
if (program === null || this.currentCpm === 0) {
return 0;
}
if (this.calorieDiff() < 0) {
return -1;
} else {
return 1;
}
}
updateMilestones(minutes, calories) {
const expected = this.expectedCalories(minutes, 0);
const diff = calories - expected;
this.milestones.push({minutes, calories, diff});
if (minutes % 5 === 0) {
this.milestones = this.milestones.filter(m => m.minutes % 5 === 0)
}
let lines = "";
let lastDiff = 0;
this.milestones.sort((i, j) => j.minutes - i.minutes).forEach(({minutes, calories, diff}) => {
let color = this.colors["0"];
if (diff < 0) {
color = diff < lastDiff ? this.colors["-2"] : this.colors["-1"];
} else {
color = diff > lastDiff ? this.colors["2"] : this.colors["1"];
}
lines += `