local yesno = require('Module:Yesno')
local DatatableBuilder = {}
DatatableBuilder.__index = DatatableBuilder
DatatableBuilder.__tostring = DatatableBuilder.tostring
function DatatableBuilder.new()
local obj = setmetatable({
caption = nil,
headers = {},
columns = {},
columnOrder = {},
tbl = nil,
data = {},
}, DatatableBuilder)
return obj
end
function DatatableBuilder:setCaption(caption)
if caption == nil or caption == '' then
error('Caption must not be nil or empty')
end
self.caption = caption
return self
end
function DatatableBuilder:setColumnVisibility(columnName, visibility)
if columnName == nil or columnName == '' then
error('Column name must not be nil or empty')
end
if self.columns[columnName] == nil then
error('Column must have an associated column in the table')
end
self.columns[columnName].visible = visibility
return self
end
function DatatableBuilder:setHeaders(headers)
for i,header in ipairs(headers) do
if header == nil or header == '' then
error('Header must not be nil or empty')
end
if self.columns[header] == nil then
error('Header must have an associated column')
end
table.insert(self.headers, header)
end
return self
end
function DatatableBuilder:setColumns(...)
for _, col in ipairs(...) do
local columnName = col.name
if columnName == nil or columnName == '' then
error('Column name must not be nil or empty.')
end
if self.columns[columnName] then
error('Columns cannot have duplicate names.')
end
local visible = yesno(col.visible, true)
if visible == nil then
visible = true
end
self.columns[columnName] = {
['type'] = type(col.func or col.fn),
header = col.header,
dataFields = col.dataFields,
fn = col.func or col.fn,
default = col.default,
css = col.css,
sortable = col.sortable,
visible = visible
}
table.insert(self.columnOrder, columnName)
end
return self
end
function DatatableBuilder:setData(data)
self.data = data
return self
end
function DatatableBuilder:getContent(columnName, rowValues)
if columnName == nil or columnName == '' then
error('Column must not be nil or empty')
end
local content = nil
local currentCol = self.columns[columnName]
if currentCol == nil then
error(string.format('No such column with name: %s', columnName))
end
local rawCellValues = {}
for _, field in ipairs(currentCol.dataFields) do
table.insert(rawCellValues, rowValues[field])
end
if currentCol['type'] == 'function' then
content = currentCol.fn(unpack(rawCellValues))
else
content = table.concat(rawCellValues, ' ')
end
if content == nil or content == '' then
return currentCol.default or '?'
end
return content
end
function DatatableBuilder:getCSS(columnName)
local currentCol = self.columns[columnName]
return currentCol.css or {}
end
function DatatableBuilder:renderHeader()
local header = mw.html.create('tr')
for _, name in ipairs(self.columnOrder) do
local currentColumn = self.columns[name]
if currentColumn.visible == true then
local cell = header:tag('th')
:wikitext(currentColumn.header)
if currentColumn.sortable == false then
cell:addClass('unsortable')
end
end
end
return header
end
function DatatableBuilder:renderRow(val)
local row = mw.html.create('tr')
for _, name in ipairs(self.columnOrder) do
if self.columns[name].visible == true then
row:tag('td')
:css(self:getCSS(name))
:wikitext(self:getContent(name, val))
:done()
end
end
return row
end
function DatatableBuilder:generateTable()
self.tbl = mw.html.create('table')
:addClass('wikitable')
:addClass('sortable')
if self.caption then
local caption = mw.html.create('caption')
:wikitext(self.caption)
self.tbl:node(caption)
end
local header = self:renderHeader()
self.tbl:node(header)
if #self.data > 0 then
for _, val in ipairs(self.data) do
local row = self:renderRow(val)
self.tbl:node(row)
end
else
self.tbl:tag('tr')
:tag('td')
:attr('colspan', #self.columnOrder)
:css({
['text-align'] = 'center'
})
:wikitext("''No data''")
:done()
:done()
:done()
end
end
function DatatableBuilder:tostring()
if self.tbl == nil then
self:generateTable()
end
return tostring(self.tbl)
end
return DatatableBuilder