'use strict'; /*@ Ox.Spreadsheet Spreadsheet ([options[, self]]) -> Spreadsheet options Options self Shared private variable @*/ Ox.Spreadsheet = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ columnPlaceholder: '', columns: [], columnTitleType: 'str', columnWidth: 64, rowPlaceholder: '', rows: [], rowTitleType: 'str', rowTitleWidth: 128, title: '', value: {} }) .options(options || {}) .addClass('OxSpreadsheet'); if (Ox.isEmpty(self.options.value)) { self.options.value = { columns: [], rows: [], values: [] } Ox.loop(4, function(i) { self.options.value.columns.push(''); self.options.value.rows.push(''); self.options.value.values.push([0, 0, 0, 0]); }); } else if (Ox.isEmpty(self.options.value.values)) { self.options.value.rows.forEach(function(row, r) { self.options.value.values.push([]); self.options.value.columns.forEach(function(column, c) { self.options.value.values[r].push(0); }); }); } renderSpreadsheet(); function addColumn(index) { self.options.value.columns.splice(index, 0, ''); self.options.value.values.forEach(function(columns) { columns.splice(index, 0, 0); }); renderSpreadsheet(); } function addRow(index) { self.options.value.rows.splice(index, 0, ''); self.options.value.values.splice(index, 0, Ox.repeat([0], self.columns)); renderSpreadsheet(); } function getSums() { var sums = { column: Ox.repeat([0], self.columns), row: Ox.repeat([0], self.rows), sheet: 0 }; self.options.value.values.forEach(function(columns, r) { columns.forEach(function(value, c) { sums.column[c] += value; sums.row[r] += value; sums.sheet += value; }); }); return sums; } function removeColumn(index) { self.options.value.columns.splice(index, 1); self.options.value.values.forEach(function(columns) { columns.splice(index, 1); }); renderSpreadsheet(); } function removeRow(index) { self.options.value.rows.splice(index, 1); self.options.value.values.splice(index, 1); renderSpreadsheet(); } function renderSpreadsheet() { self.columns = self.options.value.columns.length; self.rows = self.options.value.rows.length; self.sums = getSums(); self.$input = {}; that.empty() .css({ width: self.options.rowTitleWidth + self.options.columnWidth * (self.columns + 1) + 'px', height: 16 * (self.rows + 2) + 'px' }); [self.options.title].concat(Ox.clone(self.options.value.rows), ['Total']).forEach(function(row, r) { r--; [''].concat(Ox.clone(self.options.value.columns), ['Total']).forEach(function(column, c) { c--; if (r == -1) { if (c == -1 || c == self.columns) { Ox.Label({ style: 'square', textAlign: c == -1 ? 'left' : 'right', title: c == -1 ? self.options.title : 'Total', width: c == -1 ? self.options.rowTitleWidth : self.options.columnWidth }) .appendTo(that); } else { Ox.MenuButton({ style: 'square', type: 'image', items: [ {id: 'before', title: 'Add column before'}, {id: 'after', title: 'Add column after'}, {id: 'remove', title: 'Remove this column', disabled: self.columns == 1} ] }) .bindEvent({ click: function(data) { if (data.id == 'remove') { removeColumn(c); } else { addColumn(c + (data.id == 'after')); } triggerChangeEvent(); } }) .appendTo(that); Ox.Input({ placeholder: self.options.columnPlaceholder, style: 'square', type: self.options.columnTitleType, value: column, width: self.options.columnWidth - 16 }) .bindEvent({ change: function(data) { self.options.value.columns[c] = data.value; triggerChangeEvent(); } }) .appendTo(that); } } else { if (c == -1) { if (r < self.rows) { Ox.MenuButton({ style: 'square', type: 'image', items: [ {id: 'before', title: 'Add row above'}, {id: 'after', title: 'Add row below'}, {id: 'remove', title: 'Remove this row', disabled: self.rows == 1} ] }) .bindEvent({ click: function(data) { if (data.id == 'remove') { removeRow(r); } else { addRow(r + (data.id == 'after')); } triggerChangeEvent(); } }) .appendTo(that); Ox.Input({ placeholder: self.options.rowPlaceholder, style: 'square', type: self.options.rowTitleType, value: row, width: self.options.rowTitleWidth - 16 }) .bindEvent({ change: function(data) { self.options.value.rows[r] = data.value; triggerChangeEvent(); } }) .appendTo(that); } else { Ox.Label({ style: 'square', textAlign: 'right', title: row, width: self.options.rowTitleWidth }) .appendTo(that); } } else { var id = c + ',' + r, isColumnSum = r == self.rows, isRowSum = c == self.columns, isSheetSum = isColumnSum && isRowSum, isSum = isColumnSum || isRowSum; self.$input[id] = Ox.Input({ //changeOnKeypress: true, disabled: isSum, style: 'square', type: 'int', value: isSheetSum ? self.sums.sheet : isColumnSum ? self.sums.column[c] : isRowSum ? self.sums.row[r] : self.options.value.values[r][c], width: self.options.columnWidth }) .appendTo(that); !isSum && self.$input[id].bindEvent({ change: function(data) { self.options.value.values[r][c] = parseInt(data.value, 10); self.sums = getSums(); self.$input[c + ',' + self.rows].value(self.sums.column[c]); self.$input[self.columns + ',' + r].value(self.sums.row[r]); self.$input[self.columns + ',' + self.rows].value(self.sums.sheet); triggerChangeEvent(); } }); } } }); }); } function triggerChangeEvent() { that.triggerEvent('change', { value: self.options.value }); } return that; };