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; };