// func(S) -> S
static new ThreadLocal htmlTable_cellEncoder;
// htmlEncode = true
static S htmlTable2(O data, O... _) {
bool htmlEncode = optPar htmlEncode(_, true);
bool useBr = boolPar useBr(_);
Map paramsByColName = cast optPar paramsByColName(_);
O[] tdParams = cast optPar tdParams(_);
// prepare table
new L> rows;
new L cols;
if (data instanceof L) {
for (O x : (L) data) pcall {
rows.add(dataToTable_makeRow(x, cols));
}
} else if (data instanceof Map) {
Map map = cast data;
for (O key : map.keySet()) {
O value = map.get(key);
rows.add(litlist(structureOrText(key), structureOrText(value)));
}
} else
print("Unknown data type: " + data);
// get table width
int w = 0;
for (L row : rows)
w = max(w, l(row));
// construct HTML for table
new StringBuilder buf;
buf.append("\n");
// title
buf.append("\n");
for (S cell : padList(cols, w, "?"))
buf.append(" " + htmlTable2_encodeCell(cell, htmlEncode, useBr) + " | \n");
buf.append("
\n");
// data
for (L row : rows) {
buf.append("\n");
int i = 0;
for (S cell : padList(row, w, "")) {
S col = get(cols, i++);
O[] params = paramsPlus(tdParams, mapGet(paramsByColName, col));
buf.append(" " + tag('td, htmlTable2_encodeCell(cell, htmlEncode, useBr), params) + "\n");
}
buf.append("
\n");
}
buf.append("
\n");
ret buf.toString();
}
static S htmlTable2_encodeCell(S cell, boolean useHtmlEncode, boolean useBr) {
if (htmlTable_cellEncoder! != null) ret (S) callF(htmlTable_cellEncoder!, cell);
if (useHtmlEncode) cell = htmlEncode2(cell);
if (useBr) cell = nlToBr(cell);
ret cell;
}