Jump to content

Module:AutosortTable: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Jfd34 (talk | contribs)
No edit summary
Jfd34 (talk | contribs)
No edit summary
Line 1: Line 1:
--[[
--[[
AutosortTable: Creates a table which is automatically sorted
AutosortTable: Creates a table which is automatically sorted

Usage: (Remove the hidden comments before use)
Usage: (Remove the hidden comments before use)

{{#invoke: AutosortTable|create
{{#invoke: AutosortTable|create

| class = wikitable <!-- Class for the entire table -->
| class = wikitable <!-- Class for the entire table -->
| style = width: 50%; <!-- CSS for the entire table -->
| style = width: 50%; <!-- CSS for the entire table -->
Line 12: Line 12:
| nsort = 2 <!-- Columns which use numeric sorting. Takes a coma-separated list of column numbers -->
| nsort = 2 <!-- Columns which use numeric sorting. Takes a coma-separated list of column numbers -->
| header = -- Name -- Age <!-- Table header -->
| header = -- Name -- Age <!-- Table header -->

| -- Bob -- 20 <!-- Row 1 -->
| -- Bob -- 20 <!-- Row 1 -->
| -- Peter -- 35 <!-- Row 2 -->
| -- Peter -- 35 <!-- Row 2 -->
Line 18: Line 18:
| -- James -- 50 <!-- Row 4 -->
| -- James -- 50 <!-- Row 4 -->
| background-color: #FFDDDD -- Henry -- 45 <!-- Row 5, with CSS -->
| background-color: #FFDDDD -- Henry -- 45 <!-- Row 5, with CSS -->

}}
}}
]]
]]

local _module = {}
local _module = {}

local HtmlBuilder = require('Module:HtmlBuilder')
local HtmlBuilder = require('Module:HtmlBuilder')

_module.create = function(frame)
_module.create = function(frame)
local args = frame.args
local args = frame.args
-- Named parameters
-- Named parameters
local class = args.class
local class = args.class
local style = args.style
local style = args.style
Line 41: Line 41:
local header = args.header
local header = args.header
local footer = args.footer
local footer = args.footer
local colstyle = args.colstyle
-- Frequently-used functions
-- Frequently-used functions
local strIndexOf = mw.ustring.find
local strIndexOf = mw.ustring.find
local strSplit = mw.text.split
local strSplit = mw.text.split
local strSub = mw.ustring.sub
local strSub = mw.ustring.sub
local strTrim = mw.text.trim
local strTrim = mw.text.trim
local seplen = #sep
local seplen = #sep
local nsortLookup, descLookup, hiddenLookup = {}, {}, {}
local nsortLookup, descLookup, hiddenLookup = {}, {}, {}
-- Create the table
-- Create the table
local html = HtmlBuilder.create()
local html = HtmlBuilder.create()
local htable = html.tag('table')
local htable = html.tag('table')
if class then htable.attr('class', class) end
if class then htable.attr('class', class) end
if style then htable.attr('style', style) end
if style then htable.attr('style', style) end
-- Parses a row string. The key paremter is used to assign a unique key to the result so that equal rows do not cause sort errors.
-- Parses a row string. The key paremter is used to assign a unique key to the result so that equal rows do not cause sort errors.
local parse = function(s, key)
local parse = function(s, key)
Line 70: Line 71:
s = strSub(s, firstSep + seplen, -1)
s = strSub(s, firstSep + seplen, -1)
end
end

return { key = key, css = css, data = strSplit(s, sep, true) }
return { key = key, css = css, data = strSplit(s, sep, true) }
end
end
--[[
--[[
Writes a row to the table.
Writes a row to the table.
Line 83: Line 84:
local row = htable.tag('tr')
local row = htable.tag('tr')
if css then row.attr('style', strTrim(css)) end
if css then row.attr('style', strTrim(css)) end
for i, v in ipairs(data) do
for i, v in ipairs(data) do
if not hiddenLookup[i] then
if not hiddenLookup[i] then
Line 100: Line 101:
cell = row.tag('td')
cell = row.tag('td')
end
end
local cellCss = colstyle[i]
if cellCss then cell.attr('style', strTrim(cellCss)) end -- Apply the column styling, if necessary
cell.wikitext(strTrim(v))
cell.wikitext(strTrim(v))
end
end
end
end
return row
return row
end
end
-- Parse the column styles
colstyle = parse(colstyle, -1).data
-- Write the header first
-- Write the header first
if header then
if header then
Line 112: Line 118:
writeHtml(headerData.css, headerData.data, 'header')
writeHtml(headerData.css, headerData.data, 'header')
end
end
-- Parse the data
-- Parse the data
local data = {}
local data = {}
for i, v in ipairs(frame.args) do data[i] = parse(v, i) end
for i, v in ipairs(frame.args) do data[i] = parse(v, i) end
order = strSplit(order, '%s*,%s*')
order = strSplit(order, '%s*,%s*')
nsort = strSplit(nsort, '%s*,%s*')
nsort = strSplit(nsort, '%s*,%s*')
desc = strSplit(desc, '%s*,%s*')
desc = strSplit(desc, '%s*,%s*')
hidden = strSplit(hidden, '%s*,%s*')
hidden = strSplit(hidden, '%s*,%s*')
for i, v in ipairs(order) do order[i] = tonumber(v) end
for i, v in ipairs(order) do order[i] = tonumber(v) end
for i, v in ipairs(nsort) do nsortLookup[tonumber(v) or -1] = true end
for i, v in ipairs(nsort) do nsortLookup[tonumber(v) or -1] = true end
for i, v in ipairs(desc) do descLookup[tonumber(v) or -1] = true end
for i, v in ipairs(desc) do descLookup[tonumber(v) or -1] = true end
for i, v in ipairs(hidden) do hiddenLookup[tonumber(v) or -1] = true end
for i, v in ipairs(hidden) do hiddenLookup[tonumber(v) or -1] = true end
--Sorting comparator function.
--Sorting comparator function.
local sortFunc = function(a, b)
local sortFunc = function(a, b)
Line 145: Line 151:
end
end
table.sort(data, sortFunc)
table.sort(data, sortFunc)
-- Write the sorted data to the HTML output
-- Write the sorted data to the HTML output
for i, v in ipairs(data) do writeHtml(v.css, v.data, nil) end
for i, v in ipairs(data) do writeHtml(v.css, v.data, nil) end
-- Write the footer
-- Write the footer
if footer then
if footer then
Line 154: Line 160:
writeHtml(footerData.css, footerData.data, 'footer')
writeHtml(footerData.css, footerData.data, 'footer')
end
end
return tostring(html)
return tostring(html)
end
end

return _module
return _module

Revision as of 09:59, 17 February 2014

--[[
AutosortTable: Creates a table which is automatically sorted
 
Usage: (Remove the hidden comments before use)
 
{{#invoke: AutosortTable|create
 
| class = wikitable                            <!-- Class for the entire table -->
| style = width: 50%;                          <!-- CSS for the entire table -->
| sep = --                                     <!-- Separator string used to separate cells -->
| order = 2, 1                                 <!-- Order for sorting preference, takes a coma-separated list of column numbers -->
| nsort = 2                                    <!-- Columns which use numeric sorting. Takes a coma-separated list of column numbers -->
| header =  -- Name -- Age                     <!-- Table header -->
 
| -- Bob -- 20                                 <!-- Row 1 -->
| -- Peter -- 35                               <!-- Row 2 -->
| -- John -- 35                                <!-- Row 3 -->
| -- James -- 50                               <!-- Row 4 -->
| background-color: #FFDDDD -- Henry -- 45     <!-- Row 5, with CSS -->
 
}}
]]
 
local _module = {}
 
local HtmlBuilder = require('Module:HtmlBuilder')
 
_module.create = function(frame)
 
    local args = frame.args
 
    -- Named parameters
 
    local class = args.class
    local style = args.style
    local sep = args.separator
    local order = args.order
    local desc = args.descending or ""
    local nsort = args.numeric or ""
    local hidden = args.hidden or ""
    local header = args.header
    local footer = args.footer
    local colstyle = args.colstyle
 
    -- Frequently-used functions
 
    local strIndexOf = mw.ustring.find
    local strSplit = mw.text.split
    local strSub = mw.ustring.sub
    local strTrim = mw.text.trim
 
    local seplen = #sep
    local nsortLookup, descLookup, hiddenLookup = {}, {}, {}
 
    -- Create the table
 
    local html = HtmlBuilder.create()
    local htable = html.tag('table')
    if class then htable.attr('class', class) end
    if style then htable.attr('style', style) end
 
    -- Parses a row string. The key paremter is used to assign a unique key to the result so that equal rows do not cause sort errors.
    local parse = function(s, key)
        local css
        local firstSep = strIndexOf(s, sep, 1, true)
        if firstSep == 1 then  -- no CSS
            css = nil
            s = strSub(s, seplen + 1, -1)
        else   -- CSS before first separator
            css = strSub(s, 1, firstSep - 1)
            s = strSub(s, firstSep + seplen, -1)
        end 
 
        return { key = key, css = css, data = strSplit(s, sep, true) }
    end
 
    --[[
        Writes a row to the table.
        css: CSS to apply to the row.
        data: The data (cells) of the row
        _type: Can be 'header', 'footer' or nil.
    ]]
    local writeHtml = function(css, data, _type)
        local row = htable.tag('tr')
        if css then row.attr('style', strTrim(css)) end
 
        for i, v in ipairs(data) do
            if not hiddenLookup[i] then
                local cell
                if _type == 'header' then
                    -- Header: use the 'th' tag with scope="col"
                    cell = row.tag('th')
                    cell.attr('scope', 'col')
                elseif _type == 'footer' then
                    -- Footer: Mark as 'sortbottom' so that it does not sort whe the table is made user-sortable
                    -- with the 'wikitable sortable' class
                    cell = row.tag('td')
                    cell.class('sortbottom')
                else
                    -- Ordinary cell
                    cell = row.tag('td')
                end
                local cellCss = colstyle[i]
                if cellCss then cell.attr('style', strTrim(cellCss)) end   -- Apply the column styling, if necessary
                cell.wikitext(strTrim(v))
            end
        end
 
        return row
    end
 
    -- Parse the column styles
    colstyle = parse(colstyle, -1).data
 
    -- Write the header first
    if header then
        local headerData = parse(header)
        writeHtml(headerData.css, headerData.data, 'header')
    end
 
    -- Parse the data
    local data = {}
    for i, v in ipairs(frame.args) do data[i] = parse(v, i) end
 
    order = strSplit(order, '%s*,%s*')
    nsort = strSplit(nsort, '%s*,%s*')
    desc = strSplit(desc, '%s*,%s*')
    hidden = strSplit(hidden, '%s*,%s*')
 
    for i, v in ipairs(order) do order[i] = tonumber(v) end
    for i, v in ipairs(nsort) do nsortLookup[tonumber(v) or -1] = true end
    for i, v in ipairs(desc) do descLookup[tonumber(v) or -1] = true end
    for i, v in ipairs(hidden) do hiddenLookup[tonumber(v) or -1] = true end
 
    --Sorting comparator function.
    local sortFunc = function(a, b)
        local ad, bd = a.data, b.data
        for i = 1, #order do
            local index = order[i]
            local ai, bi = ad[index], bd[index]
            if nsortLookup[index] then
                -- Numeric sort. Find the first occurence of a number an use it. Decimal points are allowed. Scientific notation not supported.
                ai = tonumber( (ai:find('.', 1, true) and ai:match('[+-]?%d*%.%d+') or ai:match('[+-]?%d+')) or 0 )
                bi = tonumber( (bi:find('.', 1, true) and bi:match('[+-]?%d*%.%d+') or bi:match('[+-]?%d+')) or 0 )
            end
            if ai ~= bi then
                if descLookup[index] then return ai > bi else return ai < bi end
            end
        end
        return a.key < b.key
    end
    table.sort(data, sortFunc)
 
    -- Write the sorted data to the HTML output
    for i, v in ipairs(data) do writeHtml(v.css, v.data, nil) end
 
    -- Write the footer
    if footer then
        local footerData = parse(footer)
        writeHtml(footerData.css, footerData.data, 'footer')
    end
 
    return tostring(html)
 
end
 
return _module