LGS/lib/Shredifier.js

446 lines
16 KiB
JavaScript

var Shredifier = Shredifier || function() {
// page data array
this.pages = [];
this.page = null;
// DOM view elements
this.menu = null;
this.graph = null;
this.graph_head = null;
this.tabular = null;
this.tabular_head = null;
this.entries = null;
this.entries_head = null;
//
this.pages_dom = null;
//
this.tabular_object = null;
this.entries_object = null;
//
this.nodes = [];
};
/* ======== Initialize and Close methods ======== */
Shredifier.prototype.doInit = function() {
var self = this;
// find our elements
this.menu = document.getElementById('menu');
if (this.menu == null) return 1;
this.graph_head = document.getElementById('graph_head');
if (this.graph_head == null) return 2;
this.graph = document.getElementById('graph');
if (this.graph == null) return 3;
this.tabular = document.getElementById('tabular');
if (this.tabular == null) return 4;
this.tabular_head = document.getElementById('tabular_head');
if (this.tabular_head == null) return 4;
this.entries = document.getElementById('entries');
if (this.entries == null) return 4;
this.entries_head = document.getElementById('entries_head');
if (this.entries_head == null) return 4;
this.pages_dom = document.getElementById('pages');
if (this.pages_dom == null) return 5;
// add synchronization for tabular_head and tabular
this.tabular_head.addEventListener('scroll', function(e) {
self.tabular.scrollTop = self.tabular_head.scrollTop;
});
this.tabular_head.addEventListener('head-input', function(e) {
// FIXME
if (e.set_index == 0) {
self.page.exercises[e.exercise_index].name = e.value;
}
});
this.graph_object = new Graph(this.graph);
// set up our Graph listeners
this.graph_object.element.addEventListener('nodemove', function(e) {
var weight = (self.graph_object.row_count-e.to-1) * self.page.step_weight;
self.graph_object.setNodeText(e.node.id, e.node.column, weight, e.node.color);
var cell_id = 'input_'+(new ShredId(e.node.id.parts[0], e.node.id.parts[1], e.node.column-1).toString());
var cell = document.getElementById(cell_id);
// Okay, we're doing something weird now. This is to keep any trailing non-numeric value attached to the cell's value. For example, if the original value was "85x6" and the new weight value is "90", then it would set it to "90x6".
cell.value = weight + cell.value.substring(parseFloat(cell.value).toString().length);
var event = document.createEvent('HTMLEvents');
event.initEvent('input', true, true);
event.target = cell;
cell.dispatchEvent(event);
});
// FIXME: entries b0rks when col_count reaches 1
this.graph_object.element.addEventListener('coladded', function(e) {
if (e.col_count > 1) { // skip first padding column
for (var i = 0; i < self.tabular_object.exercises.length; i++) {
var exercise = self.tabular_object.exercises[i];
for (var j = 0; j < exercise.sets.length; j++) {
if (exercise.sets[j].entries.length < self.page.entry_count+1) {
exercise.sets[j].addEntry();
}
}
}
self.entries_object.addEntry();
if (self.page.entries[self.page.entry_count]) self.entries_object.entries[self.page.entry_count].input.value = self.page.entries[self.page.entry_count];
self.page.entry_count++;
}
});
this.graph_object.element.addEventListener('colremoved', function(e) {
if (e.col_count > 1) {
for (var i = 0; i < self.tabular_object.exercises.length; i++) {
var exercise = self.tabular_object.exercises[i];
for (var j = 0; j < exercise.sets.length; j++) {
exercise.sets[j].removeEntry(exercise.sets[j].entries.length-1);
}
}
self.entries_object.removeEntry(self.entries_object.entries.length-1);
self.page.entry_count--;
}
});
this.graph_object.element.addEventListener('rowremove', function(e) {
});
this.graph_object.element.addEventListener('colsadd', function(e) {
});
this.graph_object.element.addEventListener('celladd', function(e) {
});
this.graph_object.element.addEventListener('cellremove', function(e) {
});
// set up our entries
this.entries_object = new Entries(this.entries_head, this.entries);
// set up our entries listeners
this.entries_object.element.addEventListener('headinput', function(e) {
self.page.entries[e.entry_index] = e.entry.input.value;
});
// set up our Tabular
this.tabular_object = new Tabular(this.tabular_head, this.tabular);
// set up our Tabular listeners
this.tabular_object.element.addEventListener('addexercise', function(e) {
var exercise = self.page.exercises[e.exercise.index];
for (var s = 0; s < exercise.sets.length; s++) {
e.exercise.addSet();
}
});
this.tabular_object.element.addEventListener('removeexercise', function(e) {
self.page.removeExercise(e.exercise);
});
this.tabular_object.element.addEventListener('addset', function(e) {
var exercise = self.page.exercises[e.set.parent.index];
// set the Set header name
if (e.set.index == 0) {
e.set.head_input.value = exercise.name;
// add settings button
var btn = document.createElement('div');
btn.className = 'button conf';
btn.addEventListener('click', function(evt) {
console.log('bip');
// open settings dialog
});
e.set.row_head.appendChild(btn);
// add set button
var btn = document.createElement('div');
var set = e.set;
btn.className = 'button add';
btn.addEventListener('click', function(evt) {
set.parent.addSet();
});
e.set.row_head.appendChild(btn);
} else {
e.set.head_input.value = 'Set ' + (e.set.index+1);
// add delete button
var btn = document.createElement('div');
var set = e.set;
btn.className = 'button rem';
btn.addEventListener('click', function(evt) {
set.parent.removeSet(set.index);
});
e.set.row_head.appendChild(btn);
}
// set the row color
var r, g, b;
r = Math.round(exercise.color[0] + exercise.color[0] / 6 * e.set.index);
g = Math.round(exercise.color[1] + exercise.color[1] / 6 * e.set.index);
b = Math.round(exercise.color[2] + exercise.color[2] / 6 * e.set.index);
e.set.row.style.backgroundColor = 'rgba('+r+','+g+','+b+',0.75)';
e.set.row_head.style.backgroundColor = 'rgb('+r+','+g+','+b+')';
// add the entries
if (e.set.parent.sets.length > exercise.sets.length) {
exercise.createSet({text: e.set.head_input.value});
}
if (e.set.entries.length < self.page.entry_count) {
for (var i = 0; i < self.page.entry_count; i++) e.set.addEntry();
}
});
this.tabular_object.element.addEventListener('removeset', function(e) {
var exercise = self.page.exercises[e.set.parent.index];
exercise.removeSet(e.set_index);
});
this.tabular_object.element.addEventListener('addentry', function(e) {
var exercise = self.page.exercises[e.entry.parent.parent.index];
if (exercise) {
var set = exercise.sets[e.entry.parent.index];
if (set) {
var entry = set.entries[e.entry.index];
}
}
if (typeof entry !== 'undefined') e.entry.cell_input.value = entry;
// connect the entry to the graph table
e.entry.cell_input.id = 'input_'+(new ShredId(e.entry.parent.parent.index, e.entry.parent.index, e.entry.index).toString());
e.entry.cell_input.addEventListener('input', function(e) {
var parts = e.target.id.split('_');
var exercise = parts[1];
var set = parts[2];
var entry = parts[3];
self.updateSet(exercise, set, entry, parseFloat(e.target.value));
});
});
this.tabular_object.element.addEventListener('removeentry', function(e) {
var node_id = new ShredId(e.entry.parent.parent.index, e.entry.parent.index);
self.graph_object.clearNode(node_id, e.entry.index);
});
// Set up additional controls
var btn_add = document.getElementById('page-add');
btn_add.addEventListener('click', function(e) {
self.loadPage({});
});
var btn_new = document.getElementById('new');
btn_new.addEventListener('click', function(e) {
self.clearPages();
});
var btn_save = document.getElementById('save');
btn_save.addEventListener('click', function(e) {
self.saveFile();
});
var file_load = document.createElement('input');
file_load.type = 'file';
file_load.addEventListener('change', function(e) {
var files = e.target.files;
if (files.length <= 0) return;
var file = files[0];
if (typeof FileReader !== 'undefined') {
var reader = new FileReader;
reader.onload = function(evt) {
self.loadPages(JSON.parse(evt.target.result));
};
reader.readAsText(file);
}
});
var btn_load = document.getElementById('load');
btn_load.addEventListener('click', function(e) {
file_load.click();
});
var btn_exercise_add = document.getElementById('exercise-add');
btn_exercise_add.addEventListener('click', function(e) {
self.addExercise();
});
return 0;
};
/* ======== ======== */
Shredifier.prototype.clearPages = function() {
for (var i = 0; i < this.pages.length; i++) {
this.pages[i].tab.parentNode.removeChild(this.pages[i].tab);
}
this.pages = [];
this.page = null;
this.clearGraph();
this.clearTable();
this.clearEntries();
};
Shredifier.prototype.loadPages = function(json) {
var self = this;
self.clearPages();
for (var i = 0; i < json.pages.length; i++) {
this.loadPage(json.pages[i]);
}
};
Shredifier.prototype.loadPage = function(json) {
var self = this;
var page = new Page();
page.tab = document.createElement('span');
page.tab.className = 'tab';
page.input = document.createElement('input');
page.input.type = 'button';
page.input.value = '';
page.input.addEventListener('click', function(e) {
if (self.page === page) return;
self.focusPage(page);
});
page.input.addEventListener('dblclick', function(e) {
page.input.type = 'text';
});
page.input.addEventListener('blur', function(e) {
if (page.input.type == 'text') {
page.input.type = 'button';
page.name = page.input.value;
}
});
page.tab.appendChild(page.input);
page.remove = document.createElement('div');
page.remove.className = 'button close';
page.tab.appendChild(page.remove);
this.pages_dom.insertBefore(page.tab, this.pages_dom.lastElementChild);
this.pages.push(page);
page.loadJSON(json);
if (!this.page) {
self.focusPage(page);
}
page.element.addEventListener('addexercise', function(e) {
if (e.exercise.sets.length <= 0) {
//e.exercise.createSet({});
for (var i = 0; i < self.page.entry_count; i++) e.exercise.sets[0].addEntry();
}
});
};
Shredifier.prototype.loadFile = function() {
};
Shredifier.prototype.saveFile = function() {
var json = {
pages: []
};
for (var i = 0; i < this.pages.length; i++) {
json.pages.push(this.pages[i].getJSON());
}
var str_data = JSON.stringify(json);
var filename = (json.name ? json.name : 'swole').replace(/[\'\"\,`\.\!\?]/gi, '').replace(/[^a-z0-9_\-]/gi, '-').toLowerCase()+'.json';
var file = null;
file = new Blob([str_data], {type: 'data:application/json'});
// NOTE: we are assuming the user did not cancel the save action
// Save file
if (window.navigator.msSaveOrOpenBlob) { // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
return;
}
var url = URL.createObjectURL(file);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
};
Shredifier.prototype.focusPage = function(page) {
this.clearGraph();
this.clearTable();
this.clearEntries();
if (this.page) this.page.tab.className = 'tab';
this.page = page;
this.page.tab.className = 'tab selected';
this.buildEntries();
this.buildTable();
this.buildGraph();
};
/* ======== ======== */
Shredifier.prototype.syncPage = function() {
if (this.page == null) return;
};
Shredifier.prototype.addExercise = function() {
this.page.createExercise({});
this.tabular_object.addExercise();
};
/* ======== Page to Graph synchronization methods ======== */
Shredifier.prototype.buildGraph = function() {
// TODO: this should be calculated from a 'newrow' event, as per the new design
// this.graph_object.populate(rows+1, this.page.entry_count+1);
var rows = (this.page.end_weight/this.page.step_weight);
var entries = this.page.entry_count+1;
this.page.entry_count = 0; // FIXME: this is dumb
this.graph_object.populate(rows+1, entries);
// set our first cell text
var max = Math.max(this.page.start_weight, this.page.end_weight);
var min = Math.min(this.page.start_weight, this.page.end_weight);
var rows = (min < 0 ? Math.abs(min) + Math.abs(max) : max-min) / this.page.step_weight;
for (var i = 0; i <= rows; i++) {
// TODO: round to two decimal places
this.graph_object.setCellText(i, 0, (rows-i)*this.page.step_weight);
}
for (var e = 0; e < this.page.exercises.length; e++) {
var exercise = this.page.exercises[e];
for (var s = 0; s < exercise.sets.length; s++) {
var set = exercise.sets[s];
for (var i = 0; i < set.entries.length; i++) {
this.updateSet(e, s, i, parseFloat(set.entries[i]));
}
}
}
};
Shredifier.prototype.clearGraph = function() {
this.graph_object.clear();
};
Shredifier.prototype.buildEntries = function() {
};
Shredifier.prototype.clearEntries = function() {
this.entries_object.clear();
};
Shredifier.prototype.buildTable = function() {
for (var e = 0; e < this.page.exercises.length; e++) {
this.tabular_object.addExercise();
}
};
Shredifier.prototype.clearTable = function() {
this.tabular_object.clear();
};
/* ======== Update Methods ======== */
Shredifier.prototype.updateSet = function(exercise_id, set_id, entry_id, value) {
if (typeof this.page.exercises !== 'undefined' && this.page.exercises.length <= exercise_id) {
alert('cannot updateSet, exercise '+exercise_id+' is OOR!');
return;
}
var exercise = this.page.exercises[exercise_id];
if (typeof exercise.sets !== 'undefined' && exercise.sets.length <= set_id) {
alert('cannot updateSet, set '+set_id+' is OOR!');
return;
}
var set = exercise.sets[set_id];
/*if (typeof set.entries !== 'undefined' && set.entries.length <= entry_id) {
alert('cannot updateSet, entry '+entry_id+' is OOR!');
return;
}*/
var entry = set.entries[entry_id];
set.entries[entry_id] = value;
var nvalue = set.entries[entry_id]/this.page.step_weight;
if (isNaN(nvalue)) nvalue = 0;
var max = Math.max(this.page.start_weight, this.page.end_weight);
var min = Math.min(this.page.start_weight, this.page.end_weight);
var rows = (min < 0 ? Math.abs(min) + Math.abs(max) : max-min) / this.page.step_weight;
entry_id = parseInt(entry_id);
var node_id = new ShredId(exercise_id, set_id);
if (value == '') {
this.graph_object.clearNode(node_id, entry_id+1);
} else {
this.graph_object.setNode(node_id, entry_id+1, (rows-nvalue), exercise.color_string);
this.graph_object.setNodeText(node_id, entry_id+1, value, exercise.color_string);
}
};
Shredifier.prototype.syncGraph = function(exercise_id, value) {
if (value < this.page.start_weight || value > this.page.end_weight) {
}
};
Shredifier.prototype.createId = function(exercise_id, set_id, entry_id) {
return (typeof exercise_id !== 'undefined' ? exercise_id+'_' : '')+(typeof set_id !== 'undefined' ? set_id+'_' : '')+(typeof entry_id !== 'undefined' ? entry_id : '');
};
var ShredId = ShredId || function(a, b, c) {
this.parts = [];
this.parts[0] = a;
this.parts[1] = b;
this.parts[2] = c;
};
ShredId.prototype.toString = function() {
var str = '';
for (i = 0, len = this.parts.length; i < len; i++) {
if (typeof this.parts[i] !== 'undefined') {
str += this.parts[i] + (typeof this.parts[i+1] !== 'undefined' ? '_' : '');
}
}
return str;
};