--[[ LoU - List of Untergangers lua module 
     to improve parsing speed of the List of Untergangers page
     by mfaizsyahmi, 2017
local libCtry = require("Dev:Country")
--local libFlag = require("Module:iconflags")
local lang ="en")
local p = {}
-- constants
local ytPlFmt = '[ pl]'
local ytUserFmt = '[ yt]'
local ytUcFmt = '[ yt]'
local statusTbl = {
    ['a'] = '<span class="active">Active</span>',
    ['h'] = '<span class="hiatus">Hiatus</span>',
    ['d'] = '<span class="dormant">Dormant</span>',
    ['r'] = '<span class="retired">Retired</span>',
    ['s'] = '<span class="suicide">Suicide</span>',
    ['t'] = '<span class="terminated">Terminated</span>',
    ['x'] = '<span class="resurrected">Resurrected</span>',
    ['na']= '<span class="doubtful">Not Unterganger?</span>',
    ['a?']= '<span class="active">Active (?)</span>',
    ['h?']= '<span class="hiatus">Hiatus (?)</span>',
    ['d?']= '<span class="dormant">Dormant (?)</span>',
    ['r?']= '<span class="retired">Retired (?)</span>',
    ['s?']= '<span class="suicide">Suicide (?)</span>',
    ['t?']= '<span class="terminated">Terminated (?)</span>',
    ['x?']= '<span class="resurrected">Resurrected (?)</span>',
-- column mapping
local colMap = {
    {head="#", class="index unsortable"},
    {head="Name", class="name", text="namenote"},
    {head="Reg", class="region", text="reg", title="regTitle"},
    {head="Ctry", class="country", text="ctryText", title="countryName"},
    {head="Vids", class="vids", text="vids"},
    {head="Views", class="views", text="views"},
    {head="Subs", class="subs", text="subs"},
    {head="Status", class="status", text="status"},
    {head="YT", class="yt", text="ytlink"}
-- implementing or assignments
-- basically go through given list, iterate until non-nil value found
local function eqOr(list)
    for _,v in pairs(list) do
        if v~=nil then return v end
function p.eqor(frame) return eqOr(frame) end
-- come on, lua doesn't have this built in?
local function trim(str)
    return mw.text.trim(str)
-- if string has parsable number, formats it, otherwise just return
----for vids, views and subs col, which sometimes has "N/A"
local function formatNumCol(str)
    local n = tonumber(str)
    if n~=nil then
        return lang:formatNum(n)
        return str
local function buildTableRow(el, map, tbl, header)
    -- create the row builder
    local row= mw.html.create('tr')
    if tbl.noajax~= nil then row:addClass('noAJAX') end
    local cellTag = 'td'
    if header ~= nil then cellTag = 'th' end
    -- go through every item in the column map
    for i, attr in pairs(map) do
        -- the cell text
        -- pulls from tbl variable as defined in attr.text
        local text = eqOr{tbl[attr.text], ""}
        -- if building header take the maptext instead
        if header ~= nil then text = attr.head end
        -- create cell builder; apply text, class, title
        local cell = mw.html.create(cellTag)
            :attr("class", eqOr{attr.class, ""})
            :attr("title", eqOr{tbl[attr.title], ""})
        -- append cell to row
    -- append row to el
local function readRecordPage(pgName, pgNS, RS, FS, shift, pop)
    local dataPage =,pgNS)
    local dataStr = dataPage:getContent()
    local recordTbl={}
    local i=1 
    for recordStr in mw.ustring.gmatch(dataStr, "([^"..RS.."]+)") do
        if not mw.ustring.find(recordStr, '=', 1) then
            recordTbl[i] = mw.text.split(recordStr,FS,1)
            local fieldTbl = {}
            local j=1
            for fieldStr in mw.ustring.gmatch(recordStr, "([^"..FS.."]+)") do
                -- try and match key=value pair
                local fn = mw.ustring.gmatch(fieldStr, '[^=]+')
                local s1, s2 = trim(fn()), trim(fn())
                if s2~=nil then -- k/v match found
                    fieldTbl[s1] = s2
                else --unnamed value
                    fieldTbl[j] = trim(fieldStr)
                j = j + 1
            recordTbl[i] = fieldTbl
        i = i + 1
    if shift == true then table.remove(recordTbl,1) end
    if pop == true then table.remove(recordTbl) end
    return recordTbl
function p.main(frame)
    -- retrieve arguments
    local fArgs = frame.args--.getParent()
    local LoUNS = eqOr{fArgs.ns, "User"}
    local LoUPage = eqOr{, "Mfaizsyahmi/testdata"}
    local RS = eqOr{, "§"} -- record separator
    local FS = eqOr{fArgs.fs, "‖"} -- field separator
    -- read the record file and extract record table
    local recordTbl = readRecordPage(LoUPage,LoUNS,RS,FS,true,true)
    -- create the table builder
    local tableEl = mw.html.create('table')
    tableEl:attr("class", "wikitable sortable fullwidth hilight untergangers")
    -- build the header
    buildTableRow(tableEl, colMap, {}, true)
    -- iterate each record to build the table entry rows
    for _, record in pairs(recordTbl) do
        -- extract record fields into table
        local t = {
            name = eqOr{, record[1], ""},
            reg  = eqOr{record.region, record[2], ""},
            ctry = eqOr{record.ctry, record[3], ""},
            vids = eqOr{record.vids, record[4], ""},
            views= eqOr{record.views, record[5], ""},
            subs = eqOr{record.subs, record[6], ""},
            status = eqOr{record.status, record[7], ""},
            yt = eqOr{, record[8], ""},
            pl = eqOr{, ""},
            noajax = eqOr{record.noAjax, ""},
            note = eqOr{record.note, record[9], ""}
        --deriving values from the variables
        ----name and note
        if t.note ~= "" then
            t.namenote = ..' <small>('.. t.note ..')</small>'
            t.namenote =
        ----country and region
        t.countryCode = mw.ustring.lower(t.ctry)
        ----HPW accepts uk as synonym for gb
        if t.countryCode == 'uk' then t.countryCode = 'gb' end
        t.countryName = ""
        t.region = ""
        t.subregion = ""
        t.flag = ""
        if t.countryCode ~= "" then
            t.countryName = libCtry.main{t.countryCode,'name'}
            t.region = libCtry.main{t.countryCode,'region'}
            t.subregion = libCtry.main{t.countryCode,'sub-region'}
            --t.flag = libFlag.flag{t.countryCode, useemoji='om'}
            if t.reg == "" then
                t.reg = ".."
        --mw.log(t.countryName, t.region, t.subregion, t.flag)
        ----the title for region cell
        t.regTitle = t.region .." – ".. t.subregion
        ----text for ctry cell
        t.ctryText = t.ctry .."&nbsp;".. t.flag
        -- numbers
        t.vids = formatNumCol(t.vids)
        t.views = formatNumCol(t.views)
        t.subs = formatNumCol(t.subs)
        -- status
        t.status = eqOr{statusTbl[t.status], t.status}
        -- yt link
        t.ytlink = ''
        -- tilde means take the name argument
        if == '~' then
            _, _, = mw.ustring.find(,'%[?%[?([^%]]*)%]?%]?')
        if ~= "" then -- playlist defined
            t.ytlink = mw.ustring.format(ytPlFmt,
        elseif mw.ustring.len( 
            and mw.ustring.sub(,1,2)=='UC' then
            -- yt matches channel ID (string of 24 char length begin w/ UC)
            t.ytlink = mw.ustring.format(ytUcFmt,
        else -- old username
            t.ytlink = mw.ustring.format(ytUserFmt,
        -- finally build the row given the table of variables
        buildTableRow(tableEl, colMap, t)
    -- return the text returned by the html builder
    return tableEl:done()
function avg(t)
    local sum = 0
    local count = #t
    for _, n in pairs(t) do
        sum = sum + n
    return sum/count
-- benchmarking
-- how to:
function p.test(frame)
    local fArgs = frame.args--.getParent()
    local LoUNS = fArgs.ns or "User"
    local LoUPage = or "Mfaizsyahmi/testdata"
    local RS = or "§" -- record separator
    local FS = fArgs.fs or "‖" -- field separator
    local dataPage =,LoUNS)
    local dataStr = dataPage:getContent()
    local benchmarkType = tonumber(fArgs[1])
    local iterations = tonumber(fArgs[2]) or 10
    -- timing the difference between two methods
    local t1 = os.time()
    if benchmarkType==1 then
        for i = 1, 20 do
            local recordTbl={}
            for recordStr in mw.ustring.gmatch(dataStr, "([^"..RS.."]+)") do
                table.insert(recordTbl, mw.text.split(recordStr,FS,true) )
    elseif benchmarkType==2 then
        for i = 1, 20 do
            local recordTbl={}
            for recordStr in mw.ustring.gmatch(dataStr, "([^"..RS.."]+)") do
                local fieldTbl = {}
                local j=1
                for fieldStr in mw.ustring.gmatch(recordStr, "([^"..FS.."]+)") do
                    -- try and match key=value pair
                    local fn = mw.ustring.gmatch(fieldStr, '[^=]+')
                    local s1, s2 = fn(), fn()
                    if s2~=nil then -- k/v match found
                        fieldTbl[trim(s1)] = trim(s2)
                    else --unnamed value
                        fieldTbl[j] = trim(fieldStr)
                    j = j + 1
                table.insert(recordTbl, fieldTbl)
    local t2 = os.time()
    return (t2-t1)/iterations
return p

Ad blocker interference detected!

Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.