Jump to content

Module:CricketLeagueGroupStageSummary/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by SocietyBox (talk | contribs) at 04:05, 13 April 2014 (factorise). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
local _module = {}

-- The error buffer. It contains all the errors accumulated when the table was generated. addError() adds an error to the buffer
local errors
local errorIndex = 1
local addError = function(msg, matchID)
    errors = errors or {}
    if matchID then
        errors[errorIndex] = string.format('* <span class="error" style="font-weight: normal">Error (match %s): %s</span>\n', matchID, msg)
    else
        errors[errorIndex] = string.format('* <span class="error" style="font-weight: normal">Error: %s</span>\n', msg)
    end
    errorIndex = errorIndex + 1
end
    
local cricmatch = {}

cricmatch.__index = function(t, key)
    local ret = rawget(t, key)
    if ret then
        return ret
    end
    ret = cricmatch[key]
    if type(ret) == 'function' then
        return function(...) 
            return ret(t, ...) 
        end 
    else
        return ret
    end
end

cricmatch.getMargin = function(m)
    if not m.margin then
        addError("The result 'H' or 'A' requires a margin.", id)
    elseif m.margin == 'F' then return "Forfeited"
    elseif m.superover then return "Super Over"
    else
	    -- The text appended to the margin string if the match's 'dl' parameter is true
    	local dlString = m.dl and ' <span style="font-size: 85%">(D/L)</span>' or ""

        local n, t = tonumber(string.sub(m.margin, 1, -2)), string.upper(string.sub(m.margin, -1, -1))
        if not n then
            addError("Margin must be a valid number suffixed with R or W.", id)
        elseif t == 'R' then
            return string.format("%d runs%s", n, dlString)
        elseif t == 'W' then
            return string.format("%d wickets%s", n, dlString)
        elseif t == 'I' then
            if isLimitedOvers then
                addError("A margin suffixed with 'I' cannot be used in limited-overs formats.", m.id)
            else
                return string.format("Inns &amp; %d runs%s", n, dlString)
            end
        else
            addError("Margin must be 'F', or a valid number suffixed with 'R', 'W' or 'I'.", m.id)
        end
    end
    
    return nil   -- Return nil in case of an error
end

cricmatch.getResult = function(m, row)
	-- Result not provided
	if not m.result then
        addError("Missing parameter: 'result'.")
        addErrorCell(row, m.id)
        return
	end
	
    -- Match has not been played yet
    if m.result == 'X' then
    	addTableCell(row, notPlayedColour, string.format('[[#match%s|Match %s]]', m.id, m.id))
            .css('padding', '3px 5px')
    		.css('line-height', '300%')
	elseif m.result == 'D' then
	    -- Drawn match
		if isLimitedOvers then
        	-- No draws in limited-overs matches
            addError("The result 'D' cannot be used in limited-overs formats.", m.id)
            addErrorCell(row, m.id)
        else
    	addTableCell(row, drawColour, string.format('[[#match%s|Match drawn]]', m.id))
            .css('padding', '3px 5px')
    		.css('line-height', '300%')
        end
    elseif m.result == 'N' then
    	-- Abandoned match
    	addTableCell(row, noResultColour, string.format('[[#match%s|Match<br />abandoned]]', m.id))
            .css('padding', '3px 5px')
    elseif m.result == 'T' then
    	-- Tied match
    	addTableCell(row, tieColour, string.format('[[#match%s|Match tied]]', m.id))
            .css('padding', '3px 5px')
    elseif m.result == 'H' then
    	-- Home team wins
        local margin = m.getMargin()
        if margin then
            addTableCell(row, homeWinColour, string.format('%s<br />[[#match%s|%s]]', m.home.shortName, m.id, margin))
                .css('padding', '3px 5px')
        else addErrorCell(row, m.id) end
    elseif m.result == 'A' then
    	-- Away team wins
        local margin = m.getMargin()
        if margin then
            addTableCell(row, awayWinColour, string.format('%s<br />[[#match%s|%s]]', m.home.shortName, m.id, margin))
                .css('padding', '3px 5px')
        else addErrorCell(row, m.id) end
    else  -- Invalid result
        addError("Invalid result: '" .. m.result .. "'. Expected: H, A, X, N, D or T.", m.id)
        addErrorCell(row, m.id)
    end
end

function createMatch(data)
	local match = {}
	setmetatable(match, cricmatch)
	match.id = data.match
	match.home = data.home
	match.away = data.away
	match.result = data.result
	match.margin = data.margin
	match.dl = data.dl
	match.superover = data.superover
	return match
end

---------- Background colours for table cells ----------
local noMatchColour = "#C0C0C0"     -- No match defined
local homeWinColour = "#CCCCFF"     -- Home team wins
local awayWinColour = "#FFCCCC"     -- Away team wins
local noResultColour = "#FFDEAD"    -- Match abandoned
local drawColour = "#F0E68C"        -- Match drawn
local tieColour = "#DDFFDD"         -- Match tied
local notPlayedColour = "inherit"   -- Not played yet
local errorColour = "#FF7777"       -- Error
    
---------- Html Builder ----------
local htmlBuilder = require('Module:HtmlBuilder')
function addTableRow(tbl)
	return tbl.tag('tr')
end
function addTableCell(row, bg, text)
	return row.tag('td').css('background-color', bg).wikitext(text)
end
function addNoMatch(row)
	addTableCell(row, noMatchColour, '')
	return row
end
function addErrorCell(row, id)
	addTableCell(row, errorColour, string.format('[[#match%s|Match %s]]', id, id))
		.css('padding', '3px 5px')
		.css('line-height', '300%')
	return row
end
 
function buildLegend(container, isLimitedOvers)
    local key = container.tag('table')
    	.addClass('wikitable')
	    .css('float', 'right')
	    .css('text-align', 'center')
	    .css('font-size', '90%')

    if isLimitedOvers then
    	key.css('width', '30%')
	    local row
	    row = addTableRow(key)
	    addTableCell(row, homeWinColour, 'Home team won').css('width', '50%')
	    addTableCell(row, awayWinColour, 'Visitor team won').css('width', '50%')
	    row = addTableRow(key)
	    addTableCell(row, noResultColour, 'Match abandoned')
	    addTableCell(row, tieColour, 'Match tied')
    else
	    local row
	    row = addTableRow(key)
	    addTableCell(row, homeWinColour, 'Home team won').css('width', '33%')
	    addTableCell(row, awayWinColour, 'Visitor team won').css('width', '34%')
	    addTableCell(row, noResultColour, 'Match drawn')
	    	.css('width', '33%')
	    	.attr('rowspan', '2')
	    row = addTableRow(key)
	    addTableCell(row, tieColour, 'Match abandoned')
	    addTableCell(row, drawColour, 'Match tied')
    end

	local list = container.tag('ul')
		.css('font-size', '90%')
		.tag('li')
			.wikitext("'''Note''': Results listed are according to the home (horizontal) and visitor (vertical) teams.")
		.done()
		.tag('li')
			.wikitext("'''Note''': Click on a result to see a summary of the match.")
		.done()
	return container
end

_module.create = function(frame, title, teams, isLimitedOvers, tableStyle, useWebSlice)
 
    ---------- Functions ----------
    local strMatch = string.match
    local strFind = string.find
    local yesno = require("Module:Yesno")
    
    -- The table containing all the matches, looked up by home and away teams.
    local matches = {}
 
    -- Matches which have been defined have a 'true' value for their match number key in this table.
    local matchIDs = {}
    
    -- This is used to lookup team objects by name
    local teamLookup = {}
    
    -- The total number of teams
    local teamCount = #teams

    -- Sort the teams and create the lookup table
    local teamSorter = function(t1, t2)
        return t1.fullName < t2.fullName
    end
    table.sort(teams, teamSorter)
    for i = 1, teamCount do
        local teamObj = teams[i]
        teamLookup[teamObj.code] = teamObj
        teamLookup[teamObj.shortName] = teamObj
        teamLookup[teamObj.fullName] = teamObj
    end
 
    -- Parse the rows
    for i, row in ipairs(frame.args) do
        local matchData = {}
 
        -- Key/value pairs for each row are separated by semicolons, and a colon separates the key from the value.
        local semicolon = 0
        repeat
            local start = semicolon + 1
            semicolon = strFind(row, ';', start, true) or 0
            local colon = strFind(row, ':', start, true)
            if colon and (colon < semicolon or semicolon == 0) then
                matchData[strMatch(string.sub(row, start, colon - 1), '^%s*(.-)%s*$')] = strMatch(string.sub(row, colon + 1, semicolon - 1), '^%s*(.-)%s*$')
            end
        until semicolon == 0

        local hasError = false
        local id = matchData.match
        if not id then
            addError(strFormat("Parameter #%d does not have a match number.", i))
            hasError = true
        end
        
        -- Get the home and away teams for the match.
        local home, away, t
        t = matchData.home
        if t then
            home = teamLookup[t]
            if not home then
            	addError("The team '" .. t .. "' does not exist.", id)
            	hasError = true
            end
        else addError("Missing parameter: 'home'."); hasError = true end
        t = matchData.away
        if t then
            away = teamLookup[t]
            if not away then
            	addError("The team '" .. t .. "' does not exist.", id)
            	hasError = true
            end
        else addError("Missing parameter: 'away'."); hasError = true end
        
        if home == away then   -- Home and away teams cannot be the same.
            addError("Invalid match (home and away teams are equal).", id)
            hasError = true
        elseif matchIDs[id] then    -- A match with the given match number is already defined
            addError("A match with this match number has already been defined.", id); hasError = true
        end
        
        if not hasError then   -- If there is no error, store the match in the matches table.
            matchIDs[id] = true
            if not matches[home] then matches[home] = {} end
            matches[home][away] = createMatch(matchData)
        end
    end

    -- Construct the header
	local container = htmlBuilder.create('div')
		.css('float', 'left')
	local tbl = container.tag('table')
		.attr('class', 'wikitable')
		.cssText(tableStyle or "text-align: center; white-space: nowrap; width: 100%")
	local header = addTableRow(tbl)
		.tag('th')
			.attr('scope', 'row')
			.wikitext('Visitor team &#x2192;')
		.done()
	for i = 1, teamCount do
        local team = teams[i]
        header.tag('th')
        	.attr('rowspan', '2')
        	.attr('scope', 'col')
        	.css('padding', 'inherit 10px')
        	.wikitext(string.format('[[%s|%s]]', team.pageName, team.code))
        	.newline()
    end
    tbl.tag('tr')
    	.tag('th')
    		.attr('scope', 'col')
    		.wikitext('Home team &#x2193;')

    -- Output the main body of the table
    
    for i = 1, teamCount do
        -- Each row starts with a home team header
        local home = teams[i]
        local noHomeMatches = not matches[home]
        
        local row = addTableRow(tbl)
        local teamcell = row.tag('th')
        	.attr('scope', 'row')
        	.css('text-align', 'left')
        	.css('padding', '3px 5px')
        	.css('white-space', 'normal')
        	.wikitext(string.format('[[%s|%s]]', home.pageName, home.fullName))
        if noHomeMatches then
        	teamcell.css('line-height', '275%')
        	for j = 1, teamCount do
        		addNoMatch(row)
    		end
        else    
            for j = 1, teamCount do
                local away = teams[j]
                local match = matches[home][away]
                if match then
                	match.getResult(row)
                else
                    addNoMatch(row)
                end
            end
        end
    end

    -- Legend and notes
    buildLegend(container, isLimitedOvers)
    local output = tostring(container) .. '<div style="clear: both"></div>'
    if useWebSlice then
    	output = frame:expandTemplate({ title = "WebSlice-begin", args = { id = "427", title = name } })
    		.. output
    		.. frame:expandTemplate({ title = 'WebSlice-end', args = {} })
	end

 	-- If there are errors, output them and insert a tracking category
    if errorIndex ~= 1 then
    	output = string.format('<div class="plainlist">\n%s</div>[[Category:Pages with errors reported by Module:CricketLeagueGroupStageSummary]]\n', table.concat(errors)) .. output
    end

    return output
end

_module.test = function(frame, title, teams, isLimitedOvers, tableStyle, useWebSlice)
	return self.create(frame, title, teams, isLimitedOvers, tableStyle, useWebSlice)
end

return _module