604 lines
11 KiB
TypeScript
604 lines
11 KiB
TypeScript
|
|
/**
|
|
*/
|
|
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<type_event>;
|
|
};
|
|
}
|
|
|
|
|
{
|
|
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()
|
|
);
|
|
}
|