235 lines
6.3 KiB
Markdown
235 lines
6.3 KiB
Markdown
```space-lua
|
|
|
|
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)
|
|
editor.flashNotification(wantedMonth)
|
|
-- 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"))-1)
|
|
end
|
|
year = (wantedMonth - (wantedMonth % 12)) / 12
|
|
month = wantedMonth % 12 + 1
|
|
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
|
|
```
|
|
|
|
```space-style
|
|
.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;
|
|
}
|
|
}
|
|
```
|