Jump to content

Module:Calendar widget

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by RexxS (talk | contribs) at 23:28, 29 June 2019 (vertical-align:top for year calendar). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

--[[
Module to create Calendar widget

--]]

local p = {}

local daysinmonth = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
local dayname = {
	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
}
local dayabbr = {}
for i, v in ipairs(dayname) do
	dayabbr[i] = v:sub(1, 2)
end
local monthname = {
	"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"
}
local monthabbr = {}
for i, v in ipairs(monthname) do
	monthabbr[i] = v:sub(1, 3)
end
local monthnumber = {}
for i, v in ipairs(monthabbr) do
	monthnumber[v] = i
end
local thisyear = tonumber( mw.language.getContentLanguage():formatDate("Y") )
local thismonth = tonumber( mw.language.getContentLanguage():formatDate("n") )

local function isleap(year)
	year = tonumber(year) or 1
	return year % 4 == 0
	and year % 100 ~= 0
	or year % 1000 == 0
end

--[[
Sakamoto's method: 1 <= month <= 12; year is Gregorian
dayofweek returns 1 to 7 or nil if bad arguments
--]]
local function dayofweek(year, month, day)
	local y = tonumber(year)
	local m = tonumber(month)
	local d = tonumber(day)
	if not (y and m and d) then return nil end
	local t = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}
	if m < 3 then y = y - 1 end
	return (y + math.floor(y/4) - math.floor(y/100) + math.floor(y/400) + t[m] + d) % 7 + 1
end

-- year and month both numeric:
local function monthstart(year, month)
	return dayofweek(year, month, 1)
end

-- generate the html to display a month calendar
local function displaymonth(args, mnum)
	local year = args.year
	if isleap(year) then daysinmonth[2] = 29 end
	local firstday = monthstart(year, mnum)
	local monthcal = {}
	table.insert(monthcal, '<table class="mcal">')
	table.insert(monthcal, '<tr>')
	table.insert(monthcal, '<th class="mcal" colspan="7">' .. monthname[mnum] .. ' ' .. year .. '</th>')
	table.insert(monthcal, '</tr>')
	table.insert(monthcal, '<tr>')
	for c = 1, 7 do
		table.insert(monthcal, '<th class="mcal" scope="col">' .. dayabbr[c] .. '</th>')
	end
	table.insert(monthcal, '</tr>')
	local numrows = math.ceil( (firstday + daysinmonth[mnum] - 1) / 7 )
	for r = 1, numrows do
		table.insert(monthcal, '</tr>')
		for c = 1, 7 do
			local dom = 7 * (r-1) + c + 1 - firstday
			if dom < 1 or dom > daysinmonth[mnum] then dom = "&nbsp;" end
			table.insert(monthcal, '<td class="mcal">' .. dom .. '</td>')
		end --columns
		table.insert(monthcal, '<tr>')
	end --rows
	table.insert(monthcal, '</table>')
	return table.concat(monthcal, '\n')
end

local function displayyear(args)
	local year = args.year
	local rows = args.rows
	local cols = args.cols or 4
	if rows then
		cols = math.ceil(12 / rows)
	else
		rows = math.ceil(12 / cols)
	end
	local yearcal = {}
	table.insert(yearcal, '<table class="ycal">')
	for r = 1, rows do
		table.insert(yearcal, '<tr style="vertical-align:top;">')
		for c = 1, cols do
			mnum = cols * (r - 1) + c
			if mnum < 13 then
				table.insert(yearcal, '<td class="ycal">' .. displaymonth(args, mnum) .. '</td>')
			else
				table.insert(yearcal, '&nbsp;')
			end
		end --cols
		table.insert(yearcal, '<tr>')
	end
	table.insert(yearcal, '</table>')
	return table.concat(yearcal, '\n')
end

local function calendar(args)
	local year = args.year or thisyear
	local month = args.month
	if month then
		local mnum = tonumber(month)
		if not mnum then
			if month == "current" then
				mnum = thismonth
				year = thisyear
			elseif month == "last" then
				mnum = thismonth - 1
				if mnum == 0 then
					mnum = 12
					year = thisyear - 1
				end
			elseif month == "next" then
				mnum = thismonth + 1
				if mnum == 13 then
					mnum = 1
					year = thisyear + 1
				end
			else
				mnum = monthnumber[month:sub(1,3)] or thismonth
			end
		end
		args.year = year
		if mnum > 0 and mnum <13 then
			return displaymonth(args, mnum)
		else
			return displayyear(args)
		end
	else
		args.year = year
		return displayyear(args)
	end
end

--------------------------------------------------
--[[
Sakamoto's method: ISO date
returns day name or nil if bad arguments
--]]
function p.dayofweek(frame)
	local isodate = mw.text.trim(frame.args[1] or "")
	local y, m, d = isodate:match("(%d+)%p(%d+)%p(%d+)")
	local dow = dayofweek(y, m, d)
	if not dow then return "" end
	return dayname[dow]
end

--[[
isleap returns "leap" if passed a leap year
otherwise returns nothing
]]
function p.isleap(frame)
	if isleap(frame.args[1]) then return "leap" end
	return ""
end

--[[
Main entry point for widget
--]]
function p.calendar(frame)
	local args = {}
	for k, v in pairs(frame:getParent().args) do
		if v ~= "" then args[k] = v end
	end
	for k, v in pairs(frame.args) do
		if v ~= "" then args[k] = v end
	end
	return calendar(args)
end


return p