6.3 KiB
6.3 KiB
function generate_calendar(calendarID, year, month)
local month_names = {
"Januari", "Februari", "Mars", "April",
"Maj", "Juni", "Juli", "Augusti",
"September", "Oktober", "November", "December"
}
local day_names = {"Mån", "Tis", "Ons", "Tor", "Fre", "Lör", "Sön"}
-- A specified year and month will always display that calendar.
if year == 0 and month == 0 then
local wantedMonth = clientStore.get("calendar:" .. calendarID)
-- Calculate year and month from the current date plus a month offset.
if wantedMonth == nil then
wantedMonth = tonumber(os.date("%Y")) * 12 + tonumber(os.date("%m"))
end
year = (wantedMonth - (wantedMonth % 12)) / 12
month = wantedMonth % 12
if month == 0 then
month = 12
end
end
local events = query[[
from index.tag "event"
where
_.at.match(string.format("^%d-%02d-", year, month))
order by _.at
]]
local days = get_days_in_month(year, month)
-- Get the first day of the month (1 = Sunday, 2 = Monday, ..., 7 = Saturday)
local first_day = tonumber(os.date("%w", os.time{year=year, month=month, day=1})) + 1
-- Adjust the first day based on the week start setting
first_day = first_day - 1
if first_day == 0 then
first_day = 7
end
local html = [[
<table border='1'>
<tr>
<th colspan='7'>
<span class="month-prev"><</span>
<span class="month-now">Idag</span>
<span class="month-next">></span>
]] .. month_names[month] .. " " .. year .. [[
</th>
</tr>
]]
html = html .. "<tr>"
for _, day_name in ipairs(day_names) do
html = html .. "<th>" .. day_name .. "</th>"
end
html = html .. "</tr>\n"
local day = 1
local started = false
for week = 1, 6 do
html = html .. "<tr>"
for weekday = 1, 7 do
if not started and weekday == first_day then
started = true
end
if started and day <= days then
local cellcontent = ""
cellcontent = day
for _, e in pairs(events) do
local datematch = e.at.match(
string.format("^%d-%02d-%02d( +[0-9]+:[0-9]+)?", year, month, day))
if datematch and #datematch >= 2 and not datematch[2] then
cellcontent = cellcontent .. string.format(
[[<a href="%s"><div class="event all-day">%s</div></a>]],
e.ref, e.name
)
elseif datematch and #datematch >= 2 and datematch[2] then
cellcontent = cellcontent .. string.format(
[[
<a href="%s">
<div class="event">
<div class="time">%s</div>
<div>%s</div>
</div>
</a>
]],
e.ref,
datematch[2],
e.name
)
end
end
-- Mark today's date
if day == tonumber(os.date("%d")) and month == tonumber(os.date("%m")) and year == tonumber(os.date("%Y")) then
html = html .. string.format([[<td class="today">%s</td>]], cellcontent)
else
html = html .. string.format([[<td>%s</td>]], cellcontent)
end
day = day + 1
else
html = html .. "<td></td>"
end
end
html = html .. "</tr>"
if day > days then
break
end
end
html = html .. "</table>"
local tmpl = js.window.document.createElement("template")
tmpl.innerHTML = html
tmpl.content.querySelector('.month-prev').addEventListener('click', function()
storeMonth(calendarID, year, month, -1)
codeWidget.refreshAll()
end)
tmpl.content.querySelector('.month-next').addEventListener('click', function()
storeMonth(calendarID, year, month, 1)
codeWidget.refreshAll()
end)
tmpl.content.querySelector('.month-now').addEventListener('click', function()
clientStore.delete("calendar:" .. calendarID)
storeMonth(calendarID, tonumber(os.date("%Y")), tonumber(os.date("%m")), 0)
codeWidget.refreshAll()
end)
return widget.new{
html = tmpl.content,
display = "block",
cssClasses = {"calendartable"}
}
end
function storeMonth(calendarID, year, month, offset)
local storedMonth = clientStore.get("calendar:" .. calendarID)
if storedMonth == nil then
storedMonth = year * 12 + month
end
clientStore.set("calendar:" .. calendarID, storedMonth + offset)
codeWidget.refreshAll()
end
-- Local function to get the correct number of days in a month (handles leap years)
local function get_days_in_month(year, month)
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-- Check for leap year and adjust February
if month == 2 and ((year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)) then
return 29
end
return days_in_month[month]
end
.calendartable {
border-collapse: collapse;
table {
border-color: var(--panel-border-color);
border: 2px solid #444;
border-collapse: collapse;
th {
width: 14%;
padding: 3px;
text-align: left;
background-color: var(--panel-background-color);
color: #852624;
border: 1px solid #aaa;
}
td {
width: 14%;
padding: 3px;
text-align: left;
vertical-align: top;
border: 1px solid #aaa;
&.today {
background-color: #f6f3c2;
}
}
}
a {
text-decoration-line: none;
color: var(--root-color);
&.mark {
text-decoration-line: none;
color: var(--ui-accent-text-color);
}
}
span.extramarker {
background-color: yellow;
}
.event {
background-color: #2ca05a;
color: #fff;
border-radius: 4px;
padding: 4px 8px;
margin-top: 8px;
max-width: 200px;
white-space: normal;
overflow-wrap: normal !important;
word-break: normal !important;
&.all-day {
background-color: #852624;
}
.time {
font-weight: bold;
}
}
.month-prev,
.month-next,
.month-now {
cursor: pointer;
}
}