관리-도구
편집 파일: layout-manager.js
var _ = require('lodash'); var Cell = require('./cell'); var RowSpanCell = Cell.RowSpanCell; var ColSpanCell = Cell.ColSpanCell; (function(){ function layoutTable(table){ _.forEach(table,function(row,rowIndex){ _.forEach(row,function(cell,columnIndex){ cell.y = rowIndex; cell.x = columnIndex; for(var y = rowIndex; y >= 0; y--){ var row2 = table[y]; var xMax = (y === rowIndex) ? columnIndex : row2.length; for(var x = 0; x < xMax; x++){ var cell2 = row2[x]; while(cellsConflict(cell,cell2)){ cell.x++; } } } }); }); } function maxWidth(table) { var mw = 0; _.forEach(table, function (row) { _.forEach(row, function (cell) { mw = Math.max(mw,cell.x + (cell.colSpan || 1)); }); }); return mw; } function maxHeight(table){ return table.length; } function cellsConflict(cell1,cell2){ var yMin1 = cell1.y; var yMax1 = cell1.y - 1 + (cell1.rowSpan || 1); var yMin2 = cell2.y; var yMax2 = cell2.y - 1 + (cell2.rowSpan || 1); var yConflict = !(yMin1 > yMax2 || yMin2 > yMax1); var xMin1= cell1.x; var xMax1 = cell1.x - 1 + (cell1.colSpan || 1); var xMin2= cell2.x; var xMax2 = cell2.x - 1 + (cell2.colSpan || 1); var xConflict = !(xMin1 > xMax2 || xMin2 > xMax1); return yConflict && xConflict; } function conflictExists(rows,x,y){ var i_max = Math.min(rows.length-1,y); var cell = {x:x,y:y}; for(var i = 0; i <= i_max; i++){ var row = rows[i]; for(var j = 0; j < row.length; j++){ if(cellsConflict(cell,row[j])){ return true; } } } return false; } function allBlank(rows,y,xMin,xMax){ for(var x = xMin; x < xMax; x++){ if(conflictExists(rows,x,y)){ return false; } } return true; } function addRowSpanCells(table){ _.forEach(table,function(row,rowIndex){ _.forEach(row,function(cell){ for(var i = 1; i < cell.rowSpan; i++){ var rowSpanCell = new RowSpanCell(cell); rowSpanCell.x = cell.x; rowSpanCell.y = cell.y + i; rowSpanCell.colSpan = cell.colSpan; insertCell(rowSpanCell,table[rowIndex+i]); } }); }); } function addColSpanCells(cellRows){ for(var rowIndex = cellRows.length-1; rowIndex >= 0; rowIndex--) { var cellColumns = cellRows[rowIndex]; for (var columnIndex = 0; columnIndex < cellColumns.length; columnIndex++) { var cell = cellColumns[columnIndex]; for (var k = 1; k < cell.colSpan; k++) { var colSpanCell = new ColSpanCell(); colSpanCell.x = cell.x + k; colSpanCell.y = cell.y; cellColumns.splice(columnIndex + 1, 0, colSpanCell); } } } } function insertCell(cell,row){ var x = 0; while(x < row.length && (row[x].x < cell.x)) { x++; } row.splice(x,0,cell); } function fillInTable(table){ var h_max = maxHeight(table); var w_max = maxWidth(table); for(var y = 0; y < h_max; y++){ for(var x = 0; x < w_max; x++){ if(!conflictExists(table,x,y)){ var opts = {x:x,y:y,colSpan:1,rowSpan:1}; x++; while(x < w_max && !conflictExists(table,x,y)){ opts.colSpan++; x++; } var y2 = y + 1; while(y2 < h_max && allBlank(table,y2,opts.x,opts.x+opts.colSpan)){ opts.rowSpan++; y2++; } var cell = new Cell(opts); cell.x = opts.x; cell.y = opts.y; insertCell(cell,table[y]); } } } } function generateCells(rows){ return _.map(rows,function(row){ if(!_.isArray(row)){ var key = Object.keys(row)[0]; row = row[key]; if(_.isArray(row)){ row = row.slice(); row.unshift(key); } else { row = [key,row]; } } return _.map(row,function(cell){ return new Cell(cell); }); }); } function makeTableLayout(rows){ var cellRows = generateCells(rows); layoutTable(cellRows); fillInTable(cellRows); addRowSpanCells(cellRows); addColSpanCells(cellRows); return cellRows; } module.exports = { makeTableLayout: makeTableLayout, layoutTable: layoutTable, addRowSpanCells: addRowSpanCells, maxWidth:maxWidth, fillInTable:fillInTable, computeWidths:makeComputeWidths('colSpan','desiredWidth','x',1), computeHeights:makeComputeWidths('rowSpan','desiredHeight','y',1) }; })(); function makeComputeWidths(colSpan,desiredWidth,x,forcedMin){ return function(vals,table){ var result = []; var spanners = []; _.forEach(table,function(row){ _.forEach(row,function(cell){ if((cell[colSpan] || 1) > 1){ spanners.push(cell); } else { result[cell[x]] = Math.max(result[cell[x]] || 0, cell[desiredWidth] || 0, forcedMin); } }); }); _.forEach(vals,function(val,index){ if(_.isNumber(val)){ result[index] = val; } }); //_.forEach(spanners,function(cell){ for(var k = spanners.length - 1; k >=0; k--){ var cell = spanners[k]; var span = cell[colSpan]; var col = cell[x]; var existingWidth = result[col]; var editableCols = _.isNumber(vals[col]) ? 0 : 1; for(var i = 1; i < span; i ++){ existingWidth += 1 + result[col + i]; if(!_.isNumber(vals[col + i])){ editableCols++; } } if(cell[desiredWidth] > existingWidth){ i = 0; while(editableCols > 0 && cell[desiredWidth] > existingWidth){ if(!_.isNumber(vals[col+i])){ var dif = Math.round( (cell[desiredWidth] - existingWidth) / editableCols ); existingWidth += dif; result[col + i] += dif; editableCols--; } i++; } } } _.extend(vals,result); for(var j = 0; j < vals.length; j++){ vals[j] = Math.max(forcedMin, vals[j] || 0); } }; }