Module:Calendar
From Angelina Jordan Wiki
Documentation for this module may be created at Module:Calendar/doc
-- Float control:
-- Default: float:right; clear:right; margin-left:0.2em;
--- Add nofloat=1 → removes float entirely.
--- Add float=left or other value to choose side.
--- Example: {{#invoke:Calendar|month |year=2025 |month=9 |float=left }}
local p = {}
local function trim(s)
if not s then return nil end
return (tostring(s):gsub("^%s*(.-)%s*$", "%1"))
end
local function escAttr(s)
if not s then return "" end
return tostring(s):gsub("&","&"):gsub('"',"""):gsub("<","<"):gsub(">",">")
end
local function extractDay(dateStr)
if not dateStr then return nil end
local y, m, d = tostring(dateStr):match("^(%d%d%d%d)%-(%d%d)%-(%d%d)")
if d then return tonumber(d) end
return nil
end
local monthNames = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
}
function p.month(frame)
local args = frame.args or (frame.getParent and frame:getParent().args) or {}
local year = tonumber(trim(args.year))
local month = tonumber(trim(args.month))
if not year or not month then
return "Error: module requires numeric |year= and |month=."
end
if month < 1 or month > 12 then
return "Error: |month must be between 1 and 12."
end
-- defaults from Cargo table definition
local tableName = trim(args.table) or "Dates"
local dateField = trim(args.datefield) or "date"
local typeField = trim(args.typefield) or "type"
local linkField = trim(args.linkfield) or "link"
local pageField = trim(args.pagefield) or "page"
local filterType = trim(args.type) or "da"
local daysInMonth = tonumber(os.date("*t", os.time{year=year, month=month+1, day=0}).day)
local firstWday = tonumber(os.date("*t", os.time{year=year, month=month, day=1}).wday)
local nofloat = args.nofloat and trim(args.nofloat) ~= ""
local floatDir = trim(args.float) or "right"
local style
if nofloat then
style = "font-size:80%; text-align:center;"
else
style = string.format("font-size:80%%; text-align:center; float:%s; clear:right; margin-left:0.2em;", floatDir)
end
local out = {}
local events = {}
-- Cargo query
if mw and mw.ext and mw.ext.cargo and mw.ext.cargo.query then
local startDate = string.format("%04d-%02d-01", year, month)
local endDate = string.format("%04d-%02d-%02d", year, month, daysInMonth)
local where = string.format("%s >= '%s' AND %s <= '%s' AND %s = '%s'",
dateField, startDate, dateField, endDate, typeField, filterType)
local fields = string.format("%s,%s,%s,%s", dateField, typeField, linkField, pageField)
local ok, results = pcall(mw.ext.cargo.query, tableName, fields, { where = where, limit = 1000 })
if ok and results then
for _, row in ipairs(results) do
local dateVal = row[dateField]
local linkVal = row[linkField] or row[pageField]
local di = extractDay(dateVal)
if di and linkVal then
events[di] = events[di] or {}
table.insert(events[di], tostring(linkVal))
end
end
end
end
-- Build table
table.insert(out, string.format('{| class="wikitable" style="%s"', style))
table.insert(out, '|-')
table.insert(out, string.format('! colspan="7" | %s %d', monthNames[month], year))
table.insert(out, '|-')
table.insert(out, '!Su !!Mo !!Tu !!We !!Th !!Fr !!Sa')
local day = 1
while day <= daysInMonth do
local cells = {}
for col = 1, 7 do
local cellContent, cell
if day == 1 and col < firstWday then
cellContent = " "
elseif day <= daysInMonth then
if events[day] and #events[day] > 0 then
local firstLink = events[day][1]
cellContent = string.format('[[%s|\'\'\'%d\'\'\']]', firstLink, day)
else
cellContent = tostring(day)
end
day = day + 1
else
cellContent = " "
end
-- weekend background (Sunday=1, Saturday=7)
if col == 1 or col == 7 then
cell = string.format('style="background-color:#eee;" | %s', cellContent)
else
cell = cellContent
end
table.insert(cells, cell)
end
table.insert(out, "|-")
table.insert(out, "| " .. table.concat(cells, " || "))
end
table.insert(out, "|}")
return table.concat(out, "\n")
end
return p