+ template#set "yyyy" (string_of_int yyyy);
+ template#set "prev_yyyy" (string_of_int (yyyy - 1));
+ template#set "next_yyyy" (string_of_int (yyyy + 1));
+
+ (* Return true if there are any events on a particular day. *)
+ let has_events date = List.exists (fun (d, _) -> date = d) pages in
+
+ (* Generate each month template separately ...
+ * Wow, finally found a place I can use a for loop.
+ *)
+ for mm = 1 to 12 do
+ let str =
+ let template = year_1m_template in
+ template#set "yyyy" (string_of_int yyyy);
+ template#set "mm" (sprintf "%02d" mm);
+ template#set "month_name" (long_month mm);
+ let dow = GregorianDate.day_of_week (yyyy, mm, 1) in
+ let dow = if dow = 7 then 0 else dow in
+ let max_dd = GregorianDate.days_in_month yyyy mm in
+ let dd = ref (1-dow) in
+ let rows = ref [] in
+ for r = 0 to 5 do (* up to 5 rows ... *)
+ let cols = ref [] in
+ for c = 0 to 6 do (* 7 columns, Sunday - Saturday *)
+ let is_day = !dd >= 1 && !dd <= max_dd in
+ let clasz =
+ if is_day then (
+ let date = yyyy, mm, !dd in
+ let is_weekend = GregorianDate.day_of_week date >= 6 in
+ let events = has_events date in
+ (if is_weekend then "cal_year_1m_weekend " else "") ^
+ (if events then "cal_year_1m_events" else "")
+ ) else
+ "cal_year_1m_empty" in
+ let col =
+ [ "is_day", Template.VarConditional is_day;
+ "dd", Template.VarString (sprintf "%02d" !dd);
+ "class", Template.VarString clasz ] in
+ cols := col :: !cols;
+ incr dd
+ done;
+ rows := [ "cols", Template.VarTable (List.rev !cols) ] :: !rows;
+ cols := []
+ done;
+
+ template#table "rows" (List.rev !rows);
+
+ template#to_string in
+ template#set ("month" ^ string_of_int mm) str
+ done;
+
+ (* Annual events. *)
+ let events =
+ List.filter (function ((_, 0, 0), _) -> true | _ -> false) pages in