      1 xq.RichTable = Class.create({
      2 	initialize: function(rdom, table) {
      3 		this.rdom = rdom;
      4 		this.table = table;
      5 	},
      6 	insertNewRowAt: function(tr, where) {
      7 		var row = this.rdom.createElement("TR");
      8 		var cells = tr.cells;
      9 		for(var i = 0; i < cells.length; i++) {
     10 			var cell = this.rdom.createElement(cells[i].nodeName);
     11 			this.rdom.correctEmptyElement(cell);
     12 			row.appendChild(cell);
     13 		}
     14 		return this.rdom.insertNodeAt(row, tr, where);
     15 	},
     16 	insertNewCellAt: function(cell, where) {
     17 		// collect cells;
     18 		var cells = [];
     19 		var x = this.getXIndexOf(cell);
     20 		var y = 0;
     21 		while(true) {
     22 			var cur = this.getCellAt(x, y);
     23 			if(!cur) break;
     24 			cells.push(cur);
     25 			y++;
     26 		}
     27
     28 		// insert new cells
     29 		for(var i = 0; i < cells.length; i++) {
     30 			var cell = this.rdom.createElement(cells[i].nodeName);
     31 			this.rdom.correctEmptyElement(cell);
     32 			this.rdom.insertNodeAt(cell, cells[i], where);
     33 		}
     34 	},
     35 	deleteRow: function(tr) {
     36 		return this.rdom.removeBlock(tr);
     37 	},
     38 	deleteCell: function(cell) {
     39 		if(!cell.previousSibling && !cell.nextSibling) {
     40 			this.rdom.deleteNode(this.table);
     41 			return;
     42 		}
     43
     44 		// collect cells;
     45 		var cells = [];
     46 		var x = this.getXIndexOf(cell);
     47 		var y = 0;
     48 		while(true) {
     49 			var cur = this.getCellAt(x, y);
     50 			if(!cur) break;
     51 			cells.push(cur);
     52 			y++;
     53 		}
     54
     55 		for(var i = 0; i < cells.length; i++) {
     56 			this.rdom.deleteNode(cells[i]);
     57 		}
     58 	},
     59 	getPreviousCellOf: function(cell) {
     60 		if(cell.previousSibling) return cell.previousSibling;
     61 		var adjRow = this.getPreviousRowOf(cell.parentNode);
     62 		if(adjRow) return adjRow.lastChild;
     63 		return null;
     64 	},
     65 	getNextCellOf: function(cell) {
     66 		if(cell.nextSibling) return cell.nextSibling;
     67 		var adjRow = this.getNextRowOf(cell.parentNode);
     68 		if(adjRow) return adjRow.firstChild;
     69 		return null;
     70 	},
     71 	getPreviousRowOf: function(row) {
     72 		if(row.previousSibling) return row.previousSibling;
     73 		var rowContainer = row.parentNode;
     74 		if(rowContainer.previousSibling && rowContainer.previousSibling.lastChild) return rowContainer.previousSibling.lastChild;
     75 		return null;
     76 	},
     77 	getNextRowOf: function(row) {
     78 		if(row.nextSibling) return row.nextSibling;
     79 		var rowContainer = row.parentNode;
     80 		if(rowContainer.nextSibling && rowContainer.nextSibling.firstChild) return rowContainer.nextSibling.firstChild;
     81 		return null;
     82 	},
     83 	getAboveCellOf: function(cell) {
     84 		var row = this.getPreviousRowOf(cell.parentNode);
     85 		if(!row) return null;
     86
     87 		var x = this.getXIndexOf(cell);
     88 		return row.cells[x];
     89 	},
     90 	getBelowCellOf: function(cell) {
     91 		var row = this.getNextRowOf(cell.parentNode);
     92 		if(!row) return null;
     93
     94 		var x = this.getXIndexOf(cell);
     95 		return row.cells[x];
     96 	},
     97 	getXIndexOf: function(cell) {
     98 		var row = cell.parentNode;
     99 		for(var i = 0; i < row.cells.length; i++) {
    100 			if(row.cells[i] == cell) return i;
    101 		}
    102
    103 		return -1;
    104 	},
    105 	getYIndexOf: function(cell) {
    106 		var y = -1;
    107
    108 		// find y
    109 		var group = row.parentNode;
    110 		for(var i = 0; i <group.rows.length; i++) {
    111 			if(group.rows[i] == row) {
    112 				y = i;
    113 				break;
    114 			}
    115 		}
    116 		if(this.hasHeadingAtTop() && group.nodeName == "TBODY") y = y + 1;
    117
    118 		return y;
    119 	},
    120 	/**
    121 	 * TODO: Not used. Delete or not?
    122 	 */
    123 	getLocationOf: function(cell) {
    124 		var x = this.getXIndexOf(cell);
    125 		var y = this.getYIndexOf(cell);
    126 		return {x:x, y:y};
    127 	},
    128 	getCellAt: function(col, row) {
    129 		var row = this.getRowAt(row);
    130 		return (row && row.cells.length > col) ? row.cells[col] : null;
    131 	},
    132 	getRowAt: function(index) {
    133 		if(this.hasHeadingAtTop()) {
    134 			return index == 0 ? this.table.tHead.rows[0] : this.table.tBodies[0].rows[index - 1];
    135 		} else {
    136 			var rows = this.table.tBodies[0].rows;
    137 			return (rows.length > index) ? rows[index] : null;
    138 		}
    139 	},
    140 	getDom: function() {
    141 		return this.table;
    142 	},
    143 	hasHeadingAtTop: function() {
    144 		return !!(this.table.tHead && this.table.tHead.rows[0]);
    145 	},
    146 	hasHeadingAtLeft: function() {
    147 		return this.table.tBodies[0].rows[0].cells[0].nodeName == "TH";
    148 	},
    149 	correctEmptyCells: function() {
    150 		var cells = $A(this.table.getElementsByTagName("TH"));
    151 		cells.push($A(this.table.getElementsByTagName("TD")));
    152 		cells = cells.flatten();
    153
    154 		for(var i = 0; i < cells.length; i++) {
    155 			if(this.rdom.isEmptyBlock(cells[i])) this.rdom.correctEmptyElement(cells[i])
    156 		}
    157 	}
    158 });
    159
    160 xq.RichTable.create = function(rdom, cols, rows, headerPositions) {
    161 	if(["t", "tl", "lt"].include(headerPositions)) var headingAtTop = true
    162 	if(["l", "tl", "lt"].include(headerPositions)) var headingAtLeft = true
    163
    164 	var sb = []
    165 	sb.push('<table class="datatable">')
    166
    167 	// thead
    168 	if(headingAtTop) {
    169 		sb.push('<thead><tr>')
    170 		for(var i = 0; i < cols; i++) sb.push('<th></th>')
    171 		sb.push('</tr></thead>')
    172 		rows -= 1
    173 	}
    174
    175 	// tbody
    176 	sb.push('<tbody>')
    177 	for(var i = 0; i < rows; i++) {
    178 		sb.push('<tr>')
    179
    180 		for(var j = 0; j < cols; j++) {
    181 			if(headingAtLeft && j == 0) {
    182 				sb.push('<th></th>')
    183 			} else {
    184 				sb.push('<td></td>')
    185 			}
    186 		}
    187
    188 		sb.push('</tr>')
    189 	}
    190 	sb.push('</tbody>')
    191
    192 	sb.push('</table>')
    193
    194 	// create DOM element
    195 	var container = rdom.createElement("div");
    196 	container.innerHTML = sb.join("");
    197
    198 	// correct empty cells and return
    199 	var rtable = new xq.RichTable(rdom, container.firstChild);
    200 	rtable.correctEmptyCells();
    201 	return rtable;
    202 }