web-workout/Workout.js

455 lines
15 KiB
JavaScript

class Workout {
// ----------
constructor(cont, plan) {
const wo_cont = document.createElement("div");
wo_cont.classList.add("workout-wrapper");
cont.appendChild(wo_cont);
// Inputs to the class. Workout plan and container to display it in
this.arena = cont;
this.container = wo_cont;
this.plan = plan;
// Status tracking information
this.action = "";
this.set_idx = 0;
this.set_repeat_idx = 0;
this.exercise_idx = 0;
this.workout_done = 0;
this.show_header = 0;
this.show_debug_status = 0;
// Dimensions
this.vwidth = window.innerWidth;
this.vheight = window.innerHeight;
let qstring = window.location.search;
let url_params = new URLSearchParams(qstring);
// Enable debug
this.show_debug_status = url_params.has("dbg");
// Enable table header
this.show_header = url_params.has("hdr");
}
// ----------
text_element(typ, txt) {
const el = document.createElement(typ);
el.textContent = txt;
return el;
}
// ----------
progress_indicator(id) {
const indicator = this.text_element("div", " ");
indicator.setAttribute("id", id);
indicator.classList.add("progress-indicator");
indicator.classList.add("pending");
return indicator;
}
// ----------
render_workout_header() {
const hdr = this.text_element("h1", this.plan.name);
this.container.appendChild(hdr);
}
// ----------
render_debug_status() {
const dbg_div = document.createElement("div");
dbg_div.classList.add("app_debug");
const dt = document.createElement("p");
dt.setAttribute("id", "app_debug_div");
dbg_div.appendChild(dt);
this.container.appendChild(dbg_div);
}
// ----------
update_debug_info() {
if (this.show_debug_status) {
const dt = this.container.querySelector("#app_debug_div");
const curr_set_repeat_max_idx = this.plan.set[this.set_idx].count-1;
const done_str = (this.workout_done === 1) ? "Done " : "";
dt.textContent = `${done_str}Action: ${this.action} Current: Set-${this.set_idx} [${this.set_repeat_idx}/${curr_set_repeat_max_idx}]/Exercise-${this.exercise_idx}`;
dt.textContent += `\nView ${this.vwidth} x ${this.vheight}`;
}
}
// ----------
render_exercise(prnt, el, idx) {
const tblr = document.createElement("tr");
tblr.setAttribute("class", `exercise-row exercise-idx-${idx} exercise-pending`);
// Status
const sts_td = document.createElement("td");
const sts_cb = document.createElement("input");
sts_cb.setAttribute("type", "checkbox");
sts_cb.setAttribute("disabled", true);
sts_td.appendChild(sts_cb);
tblr.appendChild(sts_td);
// Name
const name_td = document.createElement("td");
let ex_name = this.text_element("div", el.name);
ex_name.classList.add("col-name");
let ex_patt = this.text_element("div", `${el.pattern} x ${el.count}`);
ex_patt.classList.add("col-patt");
name_td.appendChild(ex_name);
name_td.appendChild(ex_patt);
tblr.appendChild(name_td);
el.done = 0;
prnt.appendChild(tblr);
}
// ----------
render_set(el, idx) {
const wo = this;
// Table with one set
const tbl = document.createElement("table");
tbl.setAttribute("class", `set-table set-table-idx-${idx}`);
const tcap = this.text_element("caption", el.name);
for (var i = 0; i < el.count; i++) {
const set_indicator = this.progress_indicator(`set-${idx}-${i}-indicator`);
tcap.appendChild(set_indicator);
}
tbl.appendChild(tcap);
if (this.show_header) {
// Header of the table
const tblh = document.createElement("thead");
const tblhr = document.createElement("tr");
let col = this.text_element("th", "Status");
col.classList.add("col-status");
tblhr.appendChild(col);
col = this.text_element("th", "Exercise");
col.classList.add("col-name");
tblhr.appendChild(col);
tblh.appendChild(tblhr);
tbl.appendChild(tblh);
}
// Content of table - exercises in the set
const tblb = document.createElement("tbody");
el.exercise.forEach(function(ex_el,idx) {
wo.render_exercise(tblb, ex_el, idx);
});
tbl.appendChild(tblb);
this.container.appendChild(tbl);
}
// ----------
render_workout() {
const wo = this;
this.render_workout_header();
if (this.show_debug_status) {
this.render_debug_status();
}
this.plan.set.forEach(function(el,idx) {
wo.render_set(el, idx);
});
this.render_fab();
this.update_debug_info();
}
// ----------
update_active_item() {
let wo = this;
// Make current active exercise row inactive
const curr_active_tr = this.container.querySelector(".exercise-row.active");
let wo_set_idx = this.set_idx;
let wo_exercise_idx = this.exercise_idx;
let wo_set_repeat_idx = this.set_repeat_idx;
const max_set_idx = this.plan.set.length-1;
const max_set_repeat_idx = this.plan.set[max_set_idx].count-1;
const max_exercise_idx = this.plan.set[max_set_idx].exercise.length-1;
if (curr_active_tr != null) {
curr_active_tr.classList.remove("active");
}
// Set active exercise row
let qstr = `table.set-table-idx-${wo_set_idx} tr.exercise-idx-${wo_exercise_idx}`;
console.log(`update_active_item - Looking for ${qstr}`);
const next_active_tr = this.container.querySelector(qstr);
if (next_active_tr != null) {
next_active_tr.classList.add("active");
}
// Set set repeat indicators
let set_tables = this.container.querySelectorAll(".set-table");
set_tables.forEach(function(stbl, tidx) {
let indicators = stbl.querySelectorAll(".progress-indicator");
indicators.forEach(function(ids, iidx) {
let ind_selector = `#set-${tidx}-${iidx}-indicator`;
console.log(`update_active_item - Ind: ${ind_selector} for set_idx ${wo_set_idx} repeat_idx ${wo_set_repeat_idx}`);
// if (stbl.querySelector(ind_selector) == null) return;
console.log(`update_active_item - Ind: tidx ${tidx} is ?? set_idx ${wo_set_idx}`);
if (tidx < wo_set_idx) {
stbl.querySelector(ind_selector).classList.remove("pending");
stbl.querySelector(ind_selector).classList.remove("active");
stbl.querySelector(ind_selector).classList.add("done");
}
else if (tidx > wo_set_idx) {
stbl.querySelector(ind_selector).classList.remove("done");
stbl.querySelector(ind_selector).classList.remove("active");
stbl.querySelector(ind_selector).classList.add("pending");
}
else if (tidx == wo_set_idx) {
const ex_chkbox = stbl.querySelector(`tr.exercise-idx-${wo_exercise_idx} input`);
if ((iidx < wo_set_repeat_idx)
// || ((iidx == wo_set_repeat_idx) && ex_chkbox.checked)
|| ((iidx == wo_set_repeat_idx) && wo.plan.set[tidx].exercise[wo_exercise_idx].done)
){
console.log(`update_active_item - Ind: iidx ${iidx} is < repeat_idx ${wo_set_repeat_idx} DONE`);
stbl.querySelector(ind_selector).classList.remove("pending");
stbl.querySelector(ind_selector).classList.remove("active");
stbl.querySelector(ind_selector).classList.add("done");
}
else if (iidx > wo_set_repeat_idx) {
console.log(`update_active_item - Ind: iidx ${iidx} is > repeat_idx ${wo_set_repeat_idx} PENDING`);
stbl.querySelector(ind_selector).classList.remove("done");
stbl.querySelector(ind_selector).classList.remove("active");
stbl.querySelector(ind_selector).classList.add("pending");
} else {
console.log(`update_active_item - Ind: iidx ${iidx} is = repeat_idx ${wo_set_repeat_idx} ACTIVE`);
stbl.querySelector(ind_selector).classList.remove("pending");
stbl.querySelector(ind_selector).classList.remove("done");
stbl.querySelector(ind_selector).classList.add("active");
}
}
});
});
this.plan.set.forEach(function(el,idx) {
let sidx = idx;
el.exercise.forEach(function(eel, eidx) {
let qstr = `table.set-table-idx-${sidx} tr.exercise-idx-${eidx}`;
const active_tr = wo.container.querySelector(qstr);
active_tr.classList.remove("exercise-pending");
qstr = `table.set-table-idx-${sidx} tr.exercise-idx-${eidx} input`;
const active_chkbox = wo.container.querySelector(qstr);
active_chkbox.disabled = true;
active_chkbox.checked = (wo.plan.set[sidx].exercise[eidx].done == 1);
});
});
// Mark workout done
if (this.workout_done == 1) {
const wo_h1 = this.container.querySelector(".workout-wrapper h1");
wo_h1.classList.add("done");
this.container.classList.add("done");
let buttons = this.container.querySelectorAll("button");
buttons.forEach(function(bttn, bidx) {
bttn.disabled = true;
});
const wo_done = this.text_element("div", "");
wo_done.setAttribute("id", "workout-done");
const wo_done_txt = this.text_element("div", "Workout done!");
wo_done_txt.classList.add("workout-done-txt");
wo_done.appendChild(wo_done_txt);
this.arena.appendChild(wo_done);
}
}
// ----------
handle_exercise_advance() {
const curr_exercise_max_idx = this.plan.set[this.set_idx].exercise.length-1;
const curr_set_repeat_max_idx = this.plan.set[this.set_idx].count-1;
const curr_set_max_idx = this.plan.set.length-1;
console.log(`handle_exercise_advance - Workout done ${this.workout_done} curr_exercise_max_idx = ${curr_exercise_max_idx} curr_set_max_idx = ${curr_set_max_idx}`);
this.handle_exercise_done();
if (this.exercise_idx === curr_exercise_max_idx) {
if (this.set_repeat_idx == curr_set_repeat_max_idx) {
if (this.set_idx === curr_set_max_idx) {
if (this.workout_done === 1) {
console.log("handle_exercise_advance - Exercise done");
}
} else {
this.set_idx += 1;
this.set_repeat_idx = 0;
this.exercise_idx = 0;
this.handle_set_undone();
}
} else {
this.set_repeat_idx += 1;
this.exercise_idx = 0;
this.handle_set_undone();
}
} else {
this.exercise_idx += 1;
}
}
// ----------
handle_exercise_regress() {
const curr_set_max_idx = this.plan.set.length-1;
const curr_set_repeat_max_idx = this.plan.set[this.set_idx].count-1;
if (this.exercise_idx === 0) {
if (this.set_repeat_idx === 0) {
if (this.set_idx === 0) {
console.log("handle_exercise_regress - Exercise start");
} else {
this.set_idx -= 1;
this.set_repeat_idx = this.plan.set[this.set_idx].count-1;
this.exercise_idx = this.plan.set[this.set_idx].exercise.length-1;
this.handle_set_done();
}
} else {
this.set_repeat_idx -= 1;
this.exercise_idx = this.plan.set[this.set_idx].exercise.length-1;
this.handle_set_done();
}
} else {
this.exercise_idx -= 1;
}
this.handle_exercise_undone();
}
// ----------
handle_exercise_done() {
if (this.plan.set[this.set_idx].exercise[this.exercise_idx].done == 0) {
this.plan.set[this.set_idx].exercise[this.exercise_idx].done = 1;
}
console.log(this.plan.set[this.set_idx]);
this.handle_workout_done();
}
// ----------
handle_exercise_undone() {
if (this.plan.set[this.set_idx].exercise[this.exercise_idx].done == 1) {
this.plan.set[this.set_idx].exercise[this.exercise_idx].done = 0;
}
}
// ----------
handle_set_done() {
// Set active exercise row
let qstr = `table.set-table-idx-${this.set_idx} tr input`;
console.log(`handle_set_done - Looking for ${qstr}`);
this.plan.set[this.set_idx].exercise.forEach(function(ex, idx) {
ex.done = 1;
});
}
// ----------
handle_set_undone() {
// Set active exercise row
let qstr = `table.set-table-idx-${this.set_idx} tr input`;
console.log(`handle_set_undone - Looking for ${qstr}`);
this.plan.set[this.set_idx].exercise.forEach(function(ex, idx) {
ex.done = 0;
});
}
// ----------
handle_workout_done() {
let wo = this;
const max_set_idx = this.plan.set.length-1;
const max_set_repeat_idx = this.plan.set[max_set_idx].count-1;
const max_exercise_idx = this.plan.set[max_set_idx].exercise.length-1;
if ((this.set_idx === max_set_idx)
&& (this.set_repeat_idx === max_set_repeat_idx)
&& (this.exercise_idx === max_exercise_idx)
&& (this.plan.set[this.set_idx].exercise[this.exercise_idx].done == 1)) {
this.workout_done = 1;
}
console.log(`handle_workout_done - ${this.workout_done}`);
console.log(` compare: ${this.set_idx} === ${max_set_idx}`);
console.log(` compare: ${this.set_repeat_idx} === ${max_set_idx}`);
console.log(` compare: ${this.exercise_idx} === ${max_exercise_idx}`);
}
// ----------
handle_action(act) {
if (this.workout_done) {
return;
}
this.action = act;
if ((act == "Button-Advance") || (act == "Key-n")) {
this.handle_exercise_advance();
} else if ((act == "Button-Regress") || (act == "Key-N")) {
this.handle_exercise_regress();
} else {
this.action = `Unsupported action - ${act}`;
}
this.update_debug_info();
this.update_active_item();
}
// ----------
render_fab() {
const fab_wrapper = document.createElement("div");
fab_wrapper.setAttribute("id", "fab-wrapper");
const fab_regress = this.text_element("button", " ");
fab_regress.classList.add("fab-button");
fab_regress.setAttribute("id", "fab-regress");
const fab_advance = this.text_element("button", " ");
fab_advance.classList.add("fab-button");
fab_advance.setAttribute("id", "fab-advance");
fab_wrapper.appendChild(fab_regress);
fab_wrapper.appendChild(fab_advance);
this.container.appendChild(fab_wrapper);
}
// ----------
render() {
const app = this;
this.render_workout();
this.update_active_item();
document.addEventListener('keydown', function(ev) {
app.handle_action(`Key-${ev.key}`);
});
const fab_advance = this.container.querySelector("#fab-advance");
fab_advance.addEventListener('click', function() {
app.handle_action('Button-Advance');
});
const fab_regress = this.container.querySelector("#fab-regress");
fab_regress.addEventListener('click', function() {
app.handle_action('Button-Regress');
});
window.addEventListener('resize', function() {
// Dimensions
app.vwidth = window.innerWidth;
app.vheight = window.innerHeight;
app.update_debug_info();
});
}
}