/** */ type type_role = ( "editor" | "viewer" ); /** */ type type_user_id = int; /** */ type type_user_object = { name : string; }; /** */ type type_event = { name : string; begin : type_datetime; end : ( null | type_datetime ); description : ( null | string ); }; /** */ type type_calendar_id = int; /** */ type type_calendar_object = ( { kind : "concrete"; data : { name : string; users : Array< { id : type_user_id; role : type_role; } >; events : Array; }; } | { kind : "collection"; data : { name : string; sources : Array< type_calendar_id >; } } ); /** */ type type_datamodel = { users : Array< { id : type_user_id; object : type_user_object; } >; calendars : Array< { id : type_calendar_id; object : type_calendar_object; } >; }; /** */ function calendar_list( data : type_datamodel ) : Array< { key : type_calendar_id; preview : { name : string; } } > { return ( data.calendars .map( (calendar_entry) => ({ "key": calendar_entry.id, "preview": { "name": calendar_entry.object.data.name, } }) ) ); } /** */ function calendar_read( data : type_datamodel, calendar_id : type_calendar_id ) : type_calendar_object { const hits = ( data.calendars .filter( (calendar_entry) => (calendar_entry.id === calendar_id) ) ); if (hits.length <= 0) { throw (new Error("not found")); } else { return hits[0].object; } } /** */ function calendar_gather_events( data : type_datamodel, calendar_id : type_calendar_id, from_pit : type_pit, to_pit : type_pit ) : Array< { calendar_id : type_calendar_id; event : type_event; } > { const calendar_object : type_calendar_object = calendar_read( data, calendar_id ); switch (calendar_object.kind) { case "concrete": { return ( calendar_object.data.events .filter( (event) => pit_is_between( pit_from_datetime(event.begin), from_pit, to_pit ) ) .map( (event) => ({"calendar_id": calendar_id, "event": event}) ) ); break; } case "collection": { return ( calendar_object.data.sources .map( (source_calendar_id) => calendar_gather_events( data, source_calendar_id, from_pit, to_pit ) ) .reduce( (x, y) => x.concat(y), [] ) ); break; } } } /** */ function calendar_view_table( data : type_datamodel, calendar_id : type_calendar_id, from : { year : int; week : int; }, to : { year : int; week : int; }, options : { timezone_shift ?: int; } = {} ) : Array< { week : int; data : Array< { pit : type_pit; entries : Array< { calendar_id : type_calendar_id; event : type_event; } >; today : boolean; } >; } > { options = Object.assign( { "timezone_shift": 0, }, options ); /* const calendar_object : type_calendar_object = calendar_read( data, calendar_id ); */ const from_pit : type_pit = pit_from_year_and_week( from.year, from.week, { "timezone_shift": (options.timezone_shift as int), } ); const to_pit : type_pit = pit_from_year_and_week( to.year, to.week, { "timezone_shift": (options.timezone_shift as int), } ); // prepare const entries : Array< { calendar_id : type_calendar_id; event : type_event; } > = calendar_gather_events( data, calendar_id, from_pit, to_pit ); let result : Array< { week : int; data : Array< { pit : type_pit; entries : Array< { calendar_id : type_calendar_id; event : type_event; } >; today : boolean; } >; } > = []; let row : Array< { pit : type_pit; entries : Array< { calendar_id : type_calendar_id; event : type_event; } >; today : boolean; } > = []; let day : int = 0; while (true) { const pit_current : type_pit = pit_shift_day(from_pit, day); if (pit_is_before(pit_current, to_pit)) { day += 1; row.push( { "pit": pit_current, "entries": [], "today": false, // TODO } ); if (day % 7 === 0) { result.push({"week": (from.week + Math.floor(day / 7)), "data": row}); row = []; } else { // do nothing } } else { break; } } // fill ( entries .forEach( (entry) => { const distance_seconds : int = (pit_from_datetime(entry.event.begin) - from_pit); // process.stderr.write(JSON.stringify({"begin": entry.event.begin, "begin_pit": pit_from_datetime(entry.event.begin), "from": from_pit, "diff": distance_seconds}) + "\n"); const distance_days : int = (distance_seconds / (60 * 60 * 24)); const week : int = Math.floor(Math.floor(distance_days) / 7); const day : int = (Math.floor(distance_days) % 7); // process.stderr.write(JSON.stringify({entry, distance_days, week, day}, undefined, "\t") + "\n"); result[week].data[day].entries.push(entry); } ) ); return result; } /** */ function calendar_view_table_html( data : type_datamodel, calendar_id : type_calendar_id, from : { year : int; week : int; }, to : { year : int; week : int; }, options : { timezone_shift ?: int; } = {} ) : string { options = Object.assign( { "timezone_shift": 0, }, options ); const rows : Array< { week : int; data : Array< { pit : type_pit; entries : Array< { calendar_id : type_calendar_id; event : type_event; } >; today : boolean; } >; } > = calendar_view_table( data, calendar_id, from, to, { "timezone_shift": options.timezone_shift, } ); return ( new lib_plankton.xml.class_node_complex( "div", { "class": "calendar", }, [ new lib_plankton.xml.class_node_complex( "style", {}, [ new lib_plankton.xml.class_node_text( "html {background-color: #111; color: #FFF; font-family: sans-serif;}\n" + "table {width: 100%; border-collapse: collapse;}\n" + ".calendar-cell {border: 1px solid #444; padding: 8px; vertical-align: top;}\n" + ".calendar-cell-day {width: 13.5%;}\n" + ".calendar-cell-week {width: 5.5%;}\n" + ".calendar-cell-regular {width: 13.5%;}\n" + ".calendar-day {font-size: 0.75em;}\n" + ".calendar-events {margin: 0; padding: 0; list-style-type: none;}\n" + ".calendar-event_entry {margin: 4px; padding: 4px; border-radius: 2px; font-size: 0.75em;}\n" ) ] ), new lib_plankton.xml.class_node_complex( "table", { }, [ new lib_plankton.xml.class_node_complex( "thead", { }, [ new lib_plankton.xml.class_node_complex( "tr", { }, ( [ new lib_plankton.xml.class_node_complex( "th", { "class": "calendar-cell", }, [ ] ), ] .concat( ["Mo","Di","Mi","Do","Fr","Sa","So"] .map( (day) => new lib_plankton.xml.class_node_complex( "th", { "class": "calendar-cell calendar-cell-day", }, [ new lib_plankton.xml.class_node_text(day) ] ) ) ) ) ) ] ), new lib_plankton.xml.class_node_complex( "tbody", { }, ( rows .map( (row) => ( new lib_plankton.xml.class_node_complex( "tr", { }, ( [ new lib_plankton.xml.class_node_complex( "th", { "class": "calendar-cell calendar-cell-week", }, [ new lib_plankton.xml.class_node_text( row.week.toFixed(0).padStart(2, "0") ) ] ), ] .concat( row.data .map( (cell) => ( new lib_plankton.xml.class_node_complex( "td", { "class": ( ( ["calendar-cell", "calendar-cell-regular"] .concat(cell.today ? ["today"] : []) ) .join(" ") ), "title": lib_plankton.call.convey( cell.pit, [ pit_to_datetime, (x : type_datetime) => lib_plankton.string.coin( "{{year}}-{{month}}-{{day}}", { "year": x.date.year.toFixed(0).padStart(4, "0"), "month": x.date.month.toFixed(0).padStart(2, "0"), "day": x.date.day.toFixed(0).padStart(2, "0"), } ), ] ) }, [ new lib_plankton.xml.class_node_complex( "span", { "class": "calendar-day", }, [ new lib_plankton.xml.class_node_text( lib_plankton.call.convey( cell.pit, [ pit_to_datetime, (x : type_datetime) => lib_plankton.string.coin( "{{day}}", { "year": x.date.year.toFixed(0).padStart(4, "0"), "month": x.date.month.toFixed(0).padStart(2, "0"), "day": x.date.day.toFixed(0).padStart(2, "0"), } ), ] ) ) ] ), new lib_plankton.xml.class_node_complex( "ul", { "class": "calendar-events", }, ( cell.entries .map( entry => ( new lib_plankton.xml.class_node_complex( "li", { "class": "calendar-event_entry", "style": lib_plankton.string.coin( "background-color: {{color}}", { "color": lib_plankton.call.convey( entry.calendar_id, [ (n : int) => ({"n": n, "saturation": 0.25, "value": 0.5}), lib_plankton.color.give_generic, lib_plankton.color.output_hex, ] ), } ), }, [ new lib_plankton.xml.class_node_text(entry.event.name), ] ) ) ) ) ), ] ) ) ) ) ) ) ) ) ) ) ] ) ] ).compile() ); }