Jump to content

Module:Calendar widget

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Trappist the monk (talk | contribs) at 11:57, 30 June 2019. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

--[[
Module to create Calendar widget

--]]
require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;

local lang_obj = mw.language.getContentLanguage();

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( lang_obj:formatDate ("Y") )
local thismonth = tonumber( lang_obj:formatDate ("n") )

local function isleap(year)
	return '1' == lang_obj:formatDate ('L', tostring(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)
	return lang_obj:formatDate ('w', year .. '-' .. month .. '-' .. day) + 1;
--	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(props, mnum)
	local year = props.year
	if isleap(year) then daysinmonth[2] = 29 end
	local firstday = monthstart(year, mnum)
	local monthcal = {}
	local hdr_year = props.show_year and year or '';
	table.insert(monthcal, '<table class="mcal">')
	table.insert(monthcal, '<tr class="mcalhdr">')
--	table.insert(monthcal, '<th class="mcal" colspan="7">' .. monthname[mnum] .. ' ' .. year .. '</th>')
	table.insert(monthcal, '<th class="mcal" colspan="7">' .. monthname[mnum] .. ' ' .. hdr_year .. '</th>')
	table.insert(monthcal, '</tr>')
	table.insert(monthcal, '<tr class="mcalhdr">')
	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 class="mcal">')
	end --rows
	table.insert(monthcal, '</table>')
	return table.concat(monthcal, '\n')
end

local function displayyear(props)
	local year = props.year
	local rows = props.rows
	local cols = props.cols or 4
	local mnum;
	
	if rows then
		cols = math.ceil(12 / rows)
	else
		rows = math.ceil(12 / cols)
	end
	local yearcal = {}
	table.insert(yearcal, '<table class="ycal">')
	table.insert(yearcal, '<tr class="ycalhdr">')
	table.insert(yearcal, '<th class="ycal" colspan="' .. cols .. '>' .. year .. '</th>')
	for r = 1, rows do
		table.insert(yearcal, '<tr class="ycal" 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(props, 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(props)
--	local year = args.year or thisyear
--	local year = args.year
	local month = props.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
			props.show_year = true;												-- show year in individual month calendars
			return displaymonth(props, mnum)
		else
			return displayyear(props)
		end
	else
--		args.year = year
		return displayyear(props)
	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
	local args=getArgs (frame);
	local cal_props = {};														-- separate calendar properties table to preserve arguments as originally provided
	
	cal_props.year = args.year and tonumber(args.year) or thisyear;

	if args.month then
		local mnum = tonumber(args.month)
		if not mnum then														-- month provided as some sort of text string
			if args.month == "current" then
--				mnum = thismonth
--				year = thisyear
				cal_props.month = thismonth
				cal_props.year = thisyear
			elseif args.month == "last" then
				mnum = thismonth - 1
				if mnum == 0 then
--					mnum = 12
--					year = thisyear - 1
					cal_props.month = 12										-- december last year
					cal_props.year = thisyear - 1								-- last year
				else
					cal_props.month = mnum;										-- previous month
				end
			elseif args.month == "next" then
				mnum = thismonth + 1
				if mnum == 13 then
--					mnum = 1
--					year = thisyear + 1
					cal_props.month = 1											-- january next year
					cal_props.year = thisyear + 1								-- next year
				else
					cal_props.month = mnum;										-- next month
				end
			else
--				mnum = monthnumber[month:sub(1,3)] or thismonth
				local good
				good, cal_props.month = pcall (lang_obj.formatDate, lang_obj, 'n', args.month);
				if not good then
					cal_props.month = thismonth
				end
			end
		else
			cal_props.month = mnum;												-- month provided as a number
		end
	end

	return calendar(cal_props)
end


return p