/*
This script generates a linear (horizontal) calendar for a specified year, with days flowing left to right and months as rows.

You can personalize the calendar's appearance by defining your own colors:
If no rectangles are selected, the default color schema will be used (white and light blue-gray for weekends).
```javascript
*/
// -------------------------------------
// Constants initiation
// -------------------------------------
const CELL_WIDTH = 176; // Width of each day cell
const CELL_HEIGHT = 288; // Height of each day cell
const START_X = 0; // X start position
const START_Y = 0; // Y start position
const ROW_SPACING = 32; // Space between month rows
const MONTH_LABEL_WIDTH = 240; // Space for month labels on sides
// Colors
let COLOR_WEEKEND = "#e8eaed";
let COLOR_WEEKDAY = "#ffffff";
let COLOR_TEXT = "#000000";
let COLOR_WEEKEND_TEXT = "#000000";
let COLOR_HEADER_TEXT = "#000000";
const COLOR_STROKE = "#d0d4db";
let STROKE_WIDTH = 1;
let FILLSTYLE = "solid";
const ROUGHNESS = 0; // 0 = Architect, 1 = Artist, 2 = Cartoonist
const FONT_FAMILY = 3; // 1 = Virgil, 2 = Helvetica, 3 = Cascadia (code), 4 = Little One
// Font sizes
const FONT_SIZE_DAY = 56;
const FONT_SIZE_HEADER = 48;
const FONT_SIZE_MONTH = 64;
const FONT_SIZE_YEAR = 112;
// Day constants
const SATURDAY = 6;
const SUNDAY = 0;
const JANUARY = 0;
const FIRST_DAY_OF_THE_MONTH = 1;
// Day names (short)
const DAY_NAMES = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
// Month names (short)
const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// Number of day columns needed (max 37 to cover all possible month alignments: 6 days offset + 31 days)
const NUM_COLUMNS = 37;
// -------------------------------------
// Ask for requested Year
let requestedYear = parseFloat(new Date().getFullYear());
requestedYear = parseFloat(await utils.inputPrompt("Year?", requestedYear, requestedYear));
if (isNaN(requestedYear)) {
new Notice("Invalid number");
return;
}
// -------------------------------------
// Use selected elements for calendar style
// -------------------------------------
let elements = ea.getViewSelectedElements();
if (elements.length >= 1) {
COLOR_WEEKDAY = elements[0].backgroundColor;
FILLSTYLE = elements[0].fillStyle;
STROKE_WIDTH = elements[0].strokeWidth;
}
if (elements.length >= 2) {
COLOR_WEEKEND = elements[1].backgroundColor;
}
// -------------------------------------
// Helper function to get days in a month
// -------------------------------------
function getDaysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
}
// -------------------------------------
// Helper function to get the day of week for first day of month
// -------------------------------------
function getFirstDayOfMonth(year, month) {
return new Date(year, month, 1).getDay();
}
// -------------------------------------
// Draw day-of-week headers
// -------------------------------------
function drawDayHeaders(yPosition) {
ea.style.fontSize = FONT_SIZE_HEADER;
ea.style.strokeColor = COLOR_TEXT;
ea.style.fontFamily = FONT_FAMILY;
for (let col = 0; col < NUM_COLUMNS; col++) {
const dayIndex = col % 7;
const x = START_X + MONTH_LABEL_WIDTH + (col * CELL_WIDTH);
// Center the text in the cell (approximate width of 2 chars)
const textX = x + (CELL_WIDTH / 2) - (FONT_SIZE_HEADER * 0.6);
ea.addText(textX, yPosition, DAY_NAMES[dayIndex]);
}
}
// -------------------------------------
// Draw month row
// -------------------------------------
function drawMonthRow(year, month, rowIndex) {
const daysInMonth = getDaysInMonth(year, month);
const firstDayOfWeek = getFirstDayOfMonth(year, month);
const y = START_Y + 160 + (rowIndex * (CELL_HEIGHT + ROW_SPACING));
// Draw month label on the left
ea.style.fontSize = FONT_SIZE_MONTH;
ea.style.strokeColor = COLOR_TEXT;
ea.style.fontFamily = FONT_FAMILY;
const monthLabelY = y + (CELL_HEIGHT / 2) - (FONT_SIZE_MONTH / 2);
ea.addText(START_X, monthLabelY, MONTH_NAMES[month]);
// Draw day cells
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(year, month, day);
const dayOfWeek = date.getDay();
const col = firstDayOfWeek + day - 1; // Column position based on day of week alignment
const x = START_X + MONTH_LABEL_WIDTH + (col * CELL_WIDTH);
const isWeekend = dayOfWeek === SATURDAY || dayOfWeek === SUNDAY;
// Set cell style
ea.style.backgroundColor = isWeekend ? COLOR_WEEKEND : COLOR_WEEKDAY;
ea.style.strokeColor = COLOR_STROKE;
ea.style.strokeWidth = STROKE_WIDTH;
ea.style.fillStyle = FILLSTYLE;
ea.style.roughness = ROUGHNESS;
// Draw cell rectangle
ea.addRect(x, y, CELL_WIDTH, CELL_HEIGHT);
// Draw day number
ea.style.fontSize = FONT_SIZE_DAY;
ea.style.strokeColor = COLOR_TEXT;
ea.style.fontFamily = FONT_FAMILY;
// Position the day number at top center of the cell
const dayStr = String(day).padStart(2, "0");
const charWidth = FONT_SIZE_DAY * 0.6; // Approximate character width
const textWidth = dayStr.length * charWidth;
const textX = x + (CELL_WIDTH - textWidth) / 2;
const textY = y + 32; // 32px padding from top
ea.addText(textX, textY, dayStr);
}
// Draw month label on the right
ea.style.fontSize = FONT_SIZE_MONTH;
ea.style.strokeColor = COLOR_TEXT;
const rightLabelX = START_X + MONTH_LABEL_WIDTH + (NUM_COLUMNS * CELL_WIDTH) + 60;
ea.addText(rightLabelX, monthLabelY, MONTH_NAMES[month]);
}
// -------------------------------------
// Main calendar generation
// -------------------------------------
// Draw year title (centered above calendar)
ea.style.fontSize = FONT_SIZE_YEAR;
ea.style.strokeColor = COLOR_TEXT;
ea.style.fontFamily = FONT_FAMILY;
const yearX = START_X + MONTH_LABEL_WIDTH + ((NUM_COLUMNS * CELL_WIDTH) / 2) - 120;
ea.addText(yearX, START_Y - 200, String(requestedYear));
// Draw top day-of-week headers
drawDayHeaders(START_Y);
// Draw all 12 months
for (let month = 0; month < 12; month++) {
drawMonthRow(requestedYear, month, month);
}
// Draw bottom day-of-week headers
const bottomHeaderY = START_Y + 160 + (12 * (CELL_HEIGHT + ROW_SPACING)) + 40;
drawDayHeaders(bottomHeaderY);
await ea.addElementsToView(false, false, true);