[mod]
This commit is contained in:
parent
49b0da5f5b
commit
12697e2a7a
24 changed files with 5970 additions and 2171 deletions
5
conf/example.json
Normal file
5
conf/example.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"view_mode": "table",
|
||||
"calendar_id": 6,
|
||||
"timezone_shift": 0
|
||||
}
|
|
@ -3,93 +3,119 @@
|
|||
{
|
||||
"id": 1,
|
||||
"object": {
|
||||
"name": "Anton"
|
||||
"name": "christian.frass"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"object": {
|
||||
"name": "Berta"
|
||||
"name": "andre.weichert"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"object": {
|
||||
"name": "Caesar"
|
||||
"name": "steffen.doegnitz"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"object": {
|
||||
"name": "frank.dietrich"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"object": {
|
||||
"name": "michael.berger"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"object": {
|
||||
"name": "roland.schroeder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"object": {
|
||||
"name": "rene.hahn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"object": {
|
||||
"name": "max.meierhof"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"object": {
|
||||
"name": "klaus.kleba"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"object": {
|
||||
"name": "tim.detzner"
|
||||
}
|
||||
}
|
||||
],
|
||||
"calendars": [
|
||||
{
|
||||
"id": 1,
|
||||
"id": 5,
|
||||
"object": {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": "Garten",
|
||||
"name": "BV",
|
||||
"private": false,
|
||||
"hue": 0.0000000000000000,
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"role": "editor"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "Unkraut jähten",
|
||||
"name": "9. Bundesparteitag | 1. Sitzung | Tag 1",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 12},
|
||||
"date": {"year": 2024, "month": 10, "day": 18},
|
||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": null,
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 10, "day": 18},
|
||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "Halle",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Kartoffeln ernten",
|
||||
"name": "9. Bundesparteitag | 1. Sitzung | Tag 2",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 24},
|
||||
"time": {"hour": 17, "minute": 0, "second": 0}
|
||||
"date": {"year": 2024, "month": 10, "day": 19},
|
||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 24},
|
||||
"time": {"hour": 20, "minute": 0, "second": 0}
|
||||
"date": {"year": 2024, "month": 10, "day": 19},
|
||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "Halle",
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"object": {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": "Kochen",
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"role": "editor"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"role": "viewer"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "Krautnudeln",
|
||||
"name": "9. Bundesparteitag | 1. Sitzung | Tag 3",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 21},
|
||||
"time": {"hour": 17, "minute": 0, "second": 0}
|
||||
"date": {"year": 2024, "month": 10, "day": 20},
|
||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 21},
|
||||
"time": {"hour": 19, "minute": 0, "second": 0}
|
||||
"date": {"year": 2024, "month": 10, "day": 20},
|
||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "Halle",
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
|
@ -101,26 +127,70 @@
|
|||
"object": {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": "Band",
|
||||
"name": "LV Sachsen",
|
||||
"private": false,
|
||||
"hue": 0.6180339887498949,
|
||||
"users": [
|
||||
{
|
||||
"id": 2,
|
||||
"id": 9,
|
||||
"role": "editor"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "Auftritt im Park",
|
||||
"name": "Sören Pellmann zu den Landtagswahlen im Osten",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 21},
|
||||
"time": {"hour": 20, "minute": 0, "second": 0}
|
||||
"date": {"year": 2024, "month": 9, "day": 11},
|
||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": null,
|
||||
"location": "online: https://v2202002113208108062.supersrv.de/b/har-jbu-lxy-rx1",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Erinnern versammeln. Praktiken für die Zukünfte einer Gesellschaft der Vielen.",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 13},
|
||||
"time": {"hour": 17, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 21},
|
||||
"date": {"year": 2024, "month": 9, "day": 15},
|
||||
"time": null
|
||||
},
|
||||
"location": "Weltecho, Annaberger Straße 24, 09111 Chemnitz",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Parteikonvent zur Auswertung des Wahljahres",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 14},
|
||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 14},
|
||||
"time": {"hour": 16, "minute": 30, "second": 0}
|
||||
},
|
||||
"location": "Veranstaltungs- und Kulturforum STADTPARK | Hammertal 3 | 09669 Frankenberg/Sachsen",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Ist die extreme Rechte noch zu stoppen?",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 19},
|
||||
"time": {"hour": 19, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 19},
|
||||
"time": {"hour": 21, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "online: https://www.dielinke-sachsen.de/termine/?termin_ort=digital-internet-stream",
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
|
@ -130,18 +200,134 @@
|
|||
{
|
||||
"id": 4,
|
||||
"object": {
|
||||
"kind": "caldav",
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"source_url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36"
|
||||
"name": "KV Zwickau",
|
||||
"private": false,
|
||||
"hue": 0.4721359549995796,
|
||||
"events": [
|
||||
{
|
||||
"name": "Vorstands-Sitzung",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 5},
|
||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 5},
|
||||
"time": {"hour": 21, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "Zwickau, Innere Schneeberger Straße 17",
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"id": 1,
|
||||
"object": {
|
||||
"kind": "caldav",
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"source_url": "https://roydfalk:uywvui66svLHFMUp6LndxNO9ZASNrY9vmUDcAPPNIr7PWmjAMpEQce0JiA9AAeUH@vikunja.linke.sx/dav/projects/4/"
|
||||
"name": "OV Glauchau",
|
||||
"private": false,
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"role": "editor"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"role": "editor"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"role": "editor"
|
||||
}
|
||||
],
|
||||
"hue": 0.09016994374947451,
|
||||
"events": [
|
||||
{
|
||||
"name": "Kinderspieletag",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 8},
|
||||
"time": {"hour": 12, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 8},
|
||||
"time": {"hour": 16, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Mitglieder-Sitzung",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 19},
|
||||
"time": {"hour": 17, "minute": 30, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 19},
|
||||
"time": {"hour": 19, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": null,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"object": {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": "OV Zwickau",
|
||||
"private": false,
|
||||
"hue": 0.09016994374947451,
|
||||
"users": [
|
||||
{
|
||||
"id": 7,
|
||||
"role": "editor"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"role": "viewer"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "4ter Christopher Street Day",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 8, "day": 31},
|
||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 8, "day": 31},
|
||||
"time": {"hour": 17, "minute": 0, "second": 0}
|
||||
},
|
||||
"location": "Zwickau",
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"name": "Regionaltreffen Westsachsen: Schule ohne Rassismus – Schule mit Courage",
|
||||
"begin": {
|
||||
"timezone_shift": 2,
|
||||
"date": {"year": 2024, "month": 9, "day": 19},
|
||||
"time": {"hour": 9, "minute": 0, "second": 0}
|
||||
},
|
||||
"end": null,
|
||||
"location": null,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -154,10 +340,24 @@
|
|||
"sources": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"object": {
|
||||
"kind": "caldav",
|
||||
"data": {
|
||||
"name": "Lixer",
|
||||
"private": false,
|
||||
"read_only": true,
|
||||
"source_url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
21
doc/konzept.md
Normal file
21
doc/konzept.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
- Kalender sollen unabhängig von Nutzern bestehen können
|
||||
- einem Kalender können beliebig viele Nutzer zugeordnet werden, die jeweils bestimmte Berechtigungen haben (z.B. als Rollen "admin", "editor", "viewer", …)
|
||||
- Berechtigungen:
|
||||
- Kalender anlegen
|
||||
- Kalender-Stammdaten ändern
|
||||
- Kalender-Einträge lesen
|
||||
- Kalender-Einträge erstellen
|
||||
- Kalender-Einträge ändern
|
||||
- Kalender-Einträge entfernen
|
||||
- Kalender sind für gewöhnlichen öffentlich
|
||||
- es gibt verschiedene Arten von Kalendern:
|
||||
- konkret
|
||||
- enthält Veranstaltungen
|
||||
- Sammlung
|
||||
- enthält selbst keine Veranstaltungen
|
||||
- verweist auf beliebig viele andere Kalender (Kreise vermeiden)
|
||||
- extern
|
||||
- über CalDAV
|
||||
- sollte read-only- und read/write-Modus haben
|
||||
- nach dem Anmelden sieht man eine Liste mit öffentlichen Kalendern, gruppiert nach jewiliger Rolle
|
||||
- Entwurfsname: "zeitbild"
|
1199
lib/plankton/plankton.d.ts
vendored
1199
lib/plankton/plankton.d.ts
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,521 +0,0 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
function date_object_get_week_of_year(
|
||||
date : Date
|
||||
) : int
|
||||
{
|
||||
let date_ : Date = new Date(date.getTime());
|
||||
date_.setHours(0, 0, 0, 0);
|
||||
// Thursday in current week decides the year.
|
||||
date_.setDate(date_.getDate() + 3 - (date_.getDay() + 6) % 7);
|
||||
// January 4 is always in week 1.
|
||||
let week1 : Date = new Date(date_.getFullYear(), 0, 4);
|
||||
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||
return (
|
||||
1
|
||||
+
|
||||
Math.round(
|
||||
(
|
||||
((date_.getTime() - week1.getTime()) / 86400000)
|
||||
-
|
||||
3
|
||||
+
|
||||
(week1.getDay() + 6) % 7
|
||||
)
|
||||
/
|
||||
7
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo unite with type_datetimeobject?
|
||||
*/
|
||||
type type_datetime = {
|
||||
timezone_shift : int;
|
||||
date : type_date;
|
||||
time : (
|
||||
null
|
||||
|
|
||||
type_time
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone_shift
|
||||
*/
|
||||
function datetime_from_date_object(
|
||||
date : Date,
|
||||
options : {
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : type_datetime
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
const date_ : Date = lib_plankton.call.convey(
|
||||
date,
|
||||
[
|
||||
(x : Date) => x.getTime(),
|
||||
(x : int) => (x + (((options.timezone_shift as int) * (60 * 60)) * 1000)),
|
||||
(x : int) => new Date(x),
|
||||
]
|
||||
);
|
||||
const iso_string : string = date_.toISOString();
|
||||
return {
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
"date": {
|
||||
"year": parseInt(iso_string.slice(0, 4)),
|
||||
"month": parseInt(iso_string.slice(5, 7)),
|
||||
"day": parseInt(iso_string.slice(8, 10)),
|
||||
},
|
||||
"time": {
|
||||
"hour": parseInt(iso_string.slice(11, 13)),
|
||||
"minute": parseInt(iso_string.slice(14, 16)),
|
||||
"second": parseInt(iso_string.slice(17, 19)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo negative shift?
|
||||
*/
|
||||
function datetime_to_date_object(
|
||||
datetime : type_datetime
|
||||
) : Date
|
||||
{
|
||||
const iso_string : string = lib_plankton.string.coin(
|
||||
"{{year}}-{{month}}-{{day}}T{{hour}}:{{minute}}:{{second}}.000+{{shift}}",
|
||||
{
|
||||
"year": datetime.date.year.toFixed(0).padStart(4, "0"),
|
||||
"month": datetime.date.month.toFixed(0).padStart(2, "0"),
|
||||
"day": datetime.date.day.toFixed(0).padStart(2, "0"),
|
||||
"hour": ((datetime.time !== null) ? datetime.time.hour : 0).toFixed(0).padStart(2, "0"),
|
||||
"minute": ((datetime.time !== null) ? datetime.time.minute : 0).toFixed(0).padStart(2, "0"),
|
||||
"second": ((datetime.time !== null) ? datetime.time.second : 0).toFixed(0).padStart(2, "0"),
|
||||
"shift": (datetime.timezone_shift.toFixed(0).padStart(2, "0") + ":00"),
|
||||
}
|
||||
);
|
||||
return (new Date(iso_string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
|
||||
type type_pit = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_to_date_object(
|
||||
pit : type_pit
|
||||
) : Date
|
||||
{
|
||||
return (new Date(pit * 1000));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_from_date_object(
|
||||
date_object : Date
|
||||
) : type_pit
|
||||
{
|
||||
return Math.round(date_object.getTime() / 1000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_now(
|
||||
) : type_pit
|
||||
{
|
||||
return pit_from_date_object(new Date(Date.now()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
function pit_to_datetime(
|
||||
pit : type_pit,
|
||||
options : {
|
||||
timezone_shift ?: int
|
||||
} = {}
|
||||
) : type_datetime
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
const date_object : Date = pit_to_date_object(pit);
|
||||
return datetime_from_date_object(
|
||||
date_object,
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_from_datetime(
|
||||
datetime : type_datetime
|
||||
) : type_pit
|
||||
{
|
||||
return lib_plankton.call.convey(
|
||||
datetime,
|
||||
[
|
||||
datetime_to_date_object,
|
||||
pit_from_date_object,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_is_before(
|
||||
pit : type_pit,
|
||||
reference : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (pit < reference);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_is_after(
|
||||
pit : type_pit,
|
||||
reference : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (pit > reference);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_is_between(
|
||||
pit : type_pit,
|
||||
reference_left : type_pit,
|
||||
reference_right : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (
|
||||
pit_is_after(pit, reference_left)
|
||||
&&
|
||||
pit_is_before(pit, reference_right)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_hour(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_day(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_week(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * 7 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_year(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * 365 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_minute(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.hour
|
||||
),
|
||||
"minute": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.minute
|
||||
),
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_hour(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.hour
|
||||
),
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_day(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_week(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const date_object : Date = pit_to_date_object(pit);
|
||||
return lib_plankton.call.convey(
|
||||
date_object.getDay(),
|
||||
[
|
||||
(x : int) => ((x === 0) ? 7 : x),
|
||||
(x : int) => (x - 1),
|
||||
(x : int) => pit_shift_day(pit, (-x)),
|
||||
pit_trunc_day
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_month(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_year(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param year year according to specified timezone shift
|
||||
* @param week week according to specified timezone shift
|
||||
* @return the begin of the week (monday, 00:00)
|
||||
*/
|
||||
function pit_from_year_and_week(
|
||||
year : int,
|
||||
week : int,
|
||||
options : {
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : type_pit
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
return lib_plankton.call.convey(
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
"date": {
|
||||
"year": year,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0
|
||||
}
|
||||
},
|
||||
[
|
||||
pit_from_datetime,
|
||||
(x : type_pit) => pit_shift_week(x, (week - 1)),
|
||||
pit_trunc_week,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
function ical_datetime_to_own_datetime(
|
||||
ical_datetime : lib_plankton.ical.type_datetime
|
||||
) : type_datetime
|
||||
{
|
||||
return {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": ical_datetime.date.year,
|
||||
"month": ical_datetime.date.month,
|
||||
"day": ical_datetime.date.day,
|
||||
},
|
||||
"time": (
|
||||
(ical_datetime.time === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
{
|
||||
"hour": ical_datetime.time.hour,
|
||||
"minute": ical_datetime.time.minute,
|
||||
"second": ical_datetime.time.second,
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
function ical_dt_to_own_datetime(
|
||||
ical_dt: lib_plankton.ical.type_dt
|
||||
) : type_datetime
|
||||
{
|
||||
return {
|
||||
"timezone_shift": 0,
|
||||
"date": ical_dt.value.date,
|
||||
"time": (
|
||||
(ical_dt.value.time === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
{
|
||||
"hour": ical_dt.value.time.hour,
|
||||
"minute": ical_dt.value.time.minute,
|
||||
"second": ical_dt.value.time.second,
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
26
source/index.html
Normal file
26
source/index.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<script type="text/javascript" src="logic.js">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
_zeitbild.frontend.main()
|
||||
.then(
|
||||
() => {}
|
||||
)
|
||||
.catch(
|
||||
(error) => {console.error(error);}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
1002
source/logic.ts
1002
source/logic.ts
File diff suppressed because it is too large
Load diff
339
source/logic/backend.ts
Normal file
339
source/logic/backend.ts
Normal file
|
@ -0,0 +1,339 @@
|
|||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.resources.backend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
var _data : _zeitbild.frontend.type_datamodel;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function init(
|
||||
) : Promise<void>
|
||||
{
|
||||
const path : string = "data.json";
|
||||
if (_data === undefined) {
|
||||
_data = lib_plankton.call.convey(
|
||||
await lib_plankton.file.read(path),
|
||||
[
|
||||
lib_plankton.json.decode,
|
||||
(data_raw : any) => (
|
||||
({
|
||||
"users": data_raw["users"],
|
||||
"calendars": (
|
||||
data_raw["calendars"]
|
||||
.map(
|
||||
(calendar_entry_raw : any) => ({
|
||||
"id": calendar_entry_raw["id"],
|
||||
"object": (
|
||||
((calendar_object_raw) => {
|
||||
switch (calendar_object_raw["kind"]) {
|
||||
default: {
|
||||
return calendar_object_raw;
|
||||
break;
|
||||
}
|
||||
case "concrete": {
|
||||
return {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": calendar_object_raw["data"]["name"],
|
||||
"private": (
|
||||
calendar_object_raw["data"]["private"]
|
||||
??
|
||||
false
|
||||
),
|
||||
"users": calendar_object_raw["data"]["users"],
|
||||
"events": (
|
||||
calendar_object_raw["data"]["events"]
|
||||
.map(
|
||||
(event_raw : any) => ({
|
||||
"name": event_raw["name"],
|
||||
"begin": event_raw["begin"],
|
||||
"end": (
|
||||
(
|
||||
(
|
||||
event_raw["end"]
|
||||
??
|
||||
null
|
||||
)
|
||||
===
|
||||
null
|
||||
)
|
||||
?
|
||||
null
|
||||
:
|
||||
event_raw["end"]
|
||||
),
|
||||
"location": (
|
||||
event_raw["location"]
|
||||
??
|
||||
null
|
||||
),
|
||||
"description": (
|
||||
event_raw["description"]
|
||||
??
|
||||
null
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}) (calendar_entry_raw["object"])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
}) as type_datamodel
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_list(
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
key : type_calendar_id;
|
||||
preview : {
|
||||
name : string;
|
||||
}
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
await init();
|
||||
return Promise.resolve(
|
||||
_data.calendars
|
||||
.map(
|
||||
(calendar_entry) => {
|
||||
switch (calendar_entry.object.kind) {
|
||||
case "concrete": {
|
||||
return {
|
||||
"key": calendar_entry.id,
|
||||
"preview": {
|
||||
"name": calendar_entry.object.data.name,
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "collection": {
|
||||
return {
|
||||
"key": calendar_entry.id,
|
||||
"preview": {
|
||||
"name": calendar_entry.object.data.name,
|
||||
}
|
||||
};
|
||||
}
|
||||
case "caldav": {
|
||||
return {
|
||||
"key": calendar_entry.id,
|
||||
"preview": {
|
||||
"name": "(imported)",
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_read(
|
||||
calendar_id : type_calendar_id
|
||||
) : Promise<type_calendar_object>
|
||||
{
|
||||
await init();
|
||||
const hits = (
|
||||
_data.calendars
|
||||
.filter(
|
||||
(calendar_entry) => (calendar_entry.id === calendar_id)
|
||||
)
|
||||
);
|
||||
if (hits.length <= 0) {
|
||||
return Promise.reject<type_calendar_object>(new Error("not found"));
|
||||
}
|
||||
else {
|
||||
return Promise.resolve<type_calendar_object>(hits[0].object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo prevent loops
|
||||
*/
|
||||
export async function calendar_gather_events(
|
||||
calendar_id : type_calendar_id,
|
||||
from_pit : _zeitbild.frontend.helpers.type_pit,
|
||||
to_pit : _zeitbild.frontend.helpers.type_pit
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
calendar_name : string;
|
||||
event : type_event;
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
await init();
|
||||
const calendar_object : type_calendar_object = await calendar_read(
|
||||
calendar_id
|
||||
);
|
||||
lib_plankton.log.info(
|
||||
"calendar_gather_events",
|
||||
{
|
||||
"calendar_id": calendar_id,
|
||||
}
|
||||
);
|
||||
switch (calendar_object.kind) {
|
||||
case "concrete": {
|
||||
return Promise.resolve(
|
||||
calendar_object.data.events
|
||||
.filter(
|
||||
(event) => _zeitbild.frontend.helpers.pit_is_between(
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.data.name,
|
||||
"event": event
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "collection": {
|
||||
return (
|
||||
Promise.all(
|
||||
calendar_object.data.sources
|
||||
.map(
|
||||
(source_calendar_id) => calendar_gather_events(
|
||||
source_calendar_id,
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
(entries) => Promise.resolve(
|
||||
entries
|
||||
.reduce(
|
||||
(x, y) => x.concat(y),
|
||||
[]
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||
calendar_object.data.source_url
|
||||
);
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"version": "HTTP/2",
|
||||
"scheme": ((url.scheme === "https") ? "https" : "http"),
|
||||
"host": url.host,
|
||||
"path": (url.path ?? "/"),
|
||||
"query": url.query,
|
||||
"method": lib_plankton.http.enum_method.get,
|
||||
"headers": {},
|
||||
"body": null,
|
||||
};
|
||||
// TODO: cache?
|
||||
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
|
||||
http_request,
|
||||
{
|
||||
}
|
||||
);
|
||||
const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode(
|
||||
http_response.body.toString(),
|
||||
{
|
||||
}
|
||||
);
|
||||
return Promise.resolve(
|
||||
vcalendar.vevents
|
||||
.map(
|
||||
(vevent : lib_plankton.ical.type_vevent) => (
|
||||
(vevent.dtstart !== undefined)
|
||||
?
|
||||
{
|
||||
"name": (
|
||||
(vevent.summary !== undefined)
|
||||
?
|
||||
vevent.summary
|
||||
:
|
||||
"???"
|
||||
),
|
||||
"begin": _zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtstart),
|
||||
"end": (
|
||||
(vevent.dtend !== undefined)
|
||||
?
|
||||
_zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtend)
|
||||
:
|
||||
null
|
||||
),
|
||||
"location": (
|
||||
(vevent.location !== undefined)
|
||||
?
|
||||
vevent.location
|
||||
:
|
||||
null
|
||||
),
|
||||
"description": (
|
||||
(vevent.description !== undefined)
|
||||
?
|
||||
vevent.description
|
||||
:
|
||||
null
|
||||
),
|
||||
}
|
||||
:
|
||||
null
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
(event) => (event !== null)
|
||||
)
|
||||
.filter(
|
||||
(event) => _zeitbild.frontend.helpers.pit_is_between(
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.data.name,
|
||||
"event": event,
|
||||
})
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -58,7 +58,8 @@ async function main(
|
|||
// init
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}),
|
||||
// lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}),
|
||||
lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "info", "path": "/dev/stderr"}}),
|
||||
// lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}),
|
||||
]
|
||||
);
|
||||
|
@ -134,15 +135,15 @@ async function main(
|
|||
),
|
||||
]
|
||||
);
|
||||
let output : string;
|
||||
let content : string;
|
||||
switch (args["view_mode"]) {
|
||||
default: {
|
||||
output = "";
|
||||
content = "";
|
||||
throw (new Error("invalid view mode"));
|
||||
break;
|
||||
}
|
||||
case "table": {
|
||||
output = await calendar_view_table_html(
|
||||
content = await calendar_view_table_html(
|
||||
data,
|
||||
args.calendar_id,
|
||||
{
|
||||
|
@ -152,7 +153,7 @@ async function main(
|
|||
break;
|
||||
}
|
||||
case "list": {
|
||||
output = await calendar_view_list_html(
|
||||
content = await calendar_view_list_html(
|
||||
data,
|
||||
args.calendar_id,
|
||||
{
|
||||
|
@ -162,6 +163,12 @@ async function main(
|
|||
break;
|
||||
}
|
||||
}
|
||||
const output : string = template_coin(
|
||||
"main",
|
||||
{
|
||||
"content": content,
|
||||
}
|
||||
);
|
||||
process.stdout.write(output);
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
590
source/logic/helpers.ts
Normal file
590
source/logic/helpers.ts
Normal file
|
@ -0,0 +1,590 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.helpers
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
var _template_cache : Record<string, string> = {};
|
||||
|
||||
|
||||
/**
|
||||
* @todo caching
|
||||
*/
|
||||
export async function template_coin(
|
||||
name : string,
|
||||
data : Record<string, string>
|
||||
) : Promise<string>
|
||||
{
|
||||
let content : string;
|
||||
if (! (name in _template_cache)) {
|
||||
content = (
|
||||
(
|
||||
await lib_plankton.file.read(
|
||||
lib_plankton.string.coin(
|
||||
"templates/{{name}}.html.tpl",
|
||||
{
|
||||
"name": name,
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
.toString()
|
||||
);
|
||||
_template_cache[name] = content;
|
||||
}
|
||||
else {
|
||||
content = _template_cache[name];
|
||||
}
|
||||
return Promise.resolve<string>(
|
||||
lib_plankton.string.coin(
|
||||
content,
|
||||
data
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo outsource
|
||||
*/
|
||||
export async function promise_row<type_result>(
|
||||
members : Array<
|
||||
() => Promise<type_result>
|
||||
>
|
||||
) : Promise<
|
||||
Array<
|
||||
type_result
|
||||
>
|
||||
>
|
||||
{
|
||||
let results : Array<type_result> = [];
|
||||
for await (const member of members) {
|
||||
results.push(await member());
|
||||
}
|
||||
return Promise.resolve<Array<type_result>>(results);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function date_object_get_week_of_year(
|
||||
date : Date
|
||||
) : int
|
||||
{
|
||||
let date_ : Date = new Date(date.getTime());
|
||||
date_.setHours(0, 0, 0, 0);
|
||||
// Thursday in current week decides the year.
|
||||
date_.setDate(date_.getDate() + 3 - (date_.getDay() + 6) % 7);
|
||||
// January 4 is always in week 1.
|
||||
let week1 : Date = new Date(date_.getFullYear(), 0, 4);
|
||||
// Adjust to Thursday in week 1 and count number of weeks from date to week1.
|
||||
return (
|
||||
1
|
||||
+
|
||||
Math.round(
|
||||
(
|
||||
((date_.getTime() - week1.getTime()) / 86400000)
|
||||
-
|
||||
3
|
||||
+
|
||||
(week1.getDay() + 6) % 7
|
||||
)
|
||||
/
|
||||
7
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo unite with type_datetimeobject?
|
||||
*/
|
||||
export type type_datetime = {
|
||||
timezone_shift : int;
|
||||
date : type_date;
|
||||
time : (
|
||||
null
|
||||
|
|
||||
type_time
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone_shift
|
||||
*/
|
||||
function datetime_from_date_object(
|
||||
date : Date,
|
||||
options : {
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : type_datetime
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
const date_ : Date = lib_plankton.call.convey(
|
||||
date,
|
||||
[
|
||||
(x : Date) => x.getTime(),
|
||||
(x : int) => (x + (((options.timezone_shift as int) * (60 * 60)) * 1000)),
|
||||
(x : int) => new Date(x),
|
||||
]
|
||||
);
|
||||
const iso_string : string = date_.toISOString();
|
||||
return {
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
"date": {
|
||||
"year": parseInt(iso_string.slice(0, 4)),
|
||||
"month": parseInt(iso_string.slice(5, 7)),
|
||||
"day": parseInt(iso_string.slice(8, 10)),
|
||||
},
|
||||
"time": {
|
||||
"hour": parseInt(iso_string.slice(11, 13)),
|
||||
"minute": parseInt(iso_string.slice(14, 16)),
|
||||
"second": parseInt(iso_string.slice(17, 19)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo negative shift?
|
||||
*/
|
||||
function datetime_to_date_object(
|
||||
datetime : type_datetime
|
||||
) : Date
|
||||
{
|
||||
const iso_string : string = lib_plankton.string.coin(
|
||||
"{{year}}-{{month}}-{{day}}T{{hour}}:{{minute}}:{{second}}.000+{{shift}}",
|
||||
{
|
||||
"year": datetime.date.year.toFixed(0).padStart(4, "0"),
|
||||
"month": datetime.date.month.toFixed(0).padStart(2, "0"),
|
||||
"day": datetime.date.day.toFixed(0).padStart(2, "0"),
|
||||
"hour": ((datetime.time !== null) ? datetime.time.hour : 0).toFixed(0).padStart(2, "0"),
|
||||
"minute": ((datetime.time !== null) ? datetime.time.minute : 0).toFixed(0).padStart(2, "0"),
|
||||
"second": ((datetime.time !== null) ? datetime.time.second : 0).toFixed(0).padStart(2, "0"),
|
||||
"shift": (datetime.timezone_shift.toFixed(0).padStart(2, "0") + ":00"),
|
||||
}
|
||||
);
|
||||
return (new Date(iso_string));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_pit = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_to_date_object(
|
||||
pit : type_pit
|
||||
) : Date
|
||||
{
|
||||
return (new Date(pit * 1000));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_from_date_object(
|
||||
date_object : Date
|
||||
) : type_pit
|
||||
{
|
||||
return Math.round(date_object.getTime() / 1000);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_now(
|
||||
) : type_pit
|
||||
{
|
||||
return pit_from_date_object(new Date(Date.now()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
export function pit_to_datetime(
|
||||
pit : type_pit,
|
||||
options : {
|
||||
timezone_shift ?: int
|
||||
} = {}
|
||||
) : type_datetime
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
const date_object : Date = pit_to_date_object(pit);
|
||||
return datetime_from_date_object(
|
||||
date_object,
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_from_datetime(
|
||||
datetime : type_datetime
|
||||
) : type_pit
|
||||
{
|
||||
return lib_plankton.call.convey(
|
||||
datetime,
|
||||
[
|
||||
datetime_to_date_object,
|
||||
pit_from_date_object,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_is_before(
|
||||
pit : type_pit,
|
||||
reference : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (pit < reference);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_is_after(
|
||||
pit : type_pit,
|
||||
reference : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (pit > reference);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_is_between(
|
||||
pit : type_pit,
|
||||
reference_left : type_pit,
|
||||
reference_right : type_pit
|
||||
) : boolean
|
||||
{
|
||||
return (
|
||||
pit_is_after(pit, reference_left)
|
||||
&&
|
||||
pit_is_before(pit, reference_right)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_hour(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_shift_day(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_shift_week(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * 7 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_shift_year(
|
||||
pit : type_pit,
|
||||
increment : int
|
||||
) : type_pit
|
||||
{
|
||||
return (pit + (60 * 60 * 24 * 365 * increment));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_minute(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.hour
|
||||
),
|
||||
"minute": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.minute
|
||||
),
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_hour(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": (
|
||||
(datetime_input.time === null)
|
||||
?
|
||||
0
|
||||
:
|
||||
datetime_input.time.hour
|
||||
),
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_day(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": datetime_input.date.day,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function pit_trunc_week(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const date_object : Date = pit_to_date_object(pit);
|
||||
return lib_plankton.call.convey(
|
||||
date_object.getDay(),
|
||||
[
|
||||
(x : int) => ((x === 0) ? 7 : x),
|
||||
(x : int) => (x - 1),
|
||||
(x : int) => pit_shift_day(pit, (-x)),
|
||||
pit_trunc_day
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_month(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": datetime_input.date.month,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function pit_trunc_year(
|
||||
pit : type_pit
|
||||
) : type_pit
|
||||
{
|
||||
const datetime_input : type_datetime = pit_to_datetime(pit);
|
||||
const datetime_output : type_datetime = {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": datetime_input.date.year,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0,
|
||||
},
|
||||
};
|
||||
return pit_from_datetime(datetime_output);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param year year according to specified timezone shift
|
||||
* @param week week according to specified timezone shift
|
||||
* @return the begin of the week (monday, 00:00)
|
||||
*/
|
||||
export function pit_from_year_and_week(
|
||||
year : int,
|
||||
week : int,
|
||||
options : {
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : type_pit
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
return lib_plankton.call.convey(
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
"date": {
|
||||
"year": year,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
},
|
||||
"time": {
|
||||
"hour": 0,
|
||||
"minute": 0,
|
||||
"second": 0
|
||||
}
|
||||
},
|
||||
[
|
||||
pit_from_datetime,
|
||||
(x : type_pit) => pit_shift_week(x, (week - 1)),
|
||||
pit_trunc_week,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
function ical_datetime_to_own_datetime(
|
||||
ical_datetime : lib_plankton.ical.type_datetime
|
||||
) : type_datetime
|
||||
{
|
||||
return {
|
||||
"timezone_shift": 0,
|
||||
"date": {
|
||||
"year": ical_datetime.date.year,
|
||||
"month": ical_datetime.date.month,
|
||||
"day": ical_datetime.date.day,
|
||||
},
|
||||
"time": (
|
||||
(ical_datetime.time === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
{
|
||||
"hour": ical_datetime.time.hour,
|
||||
"minute": ical_datetime.time.minute,
|
||||
"second": ical_datetime.time.second,
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
export function ical_dt_to_own_datetime(
|
||||
ical_dt: lib_plankton.ical.type_dt
|
||||
) : type_datetime
|
||||
{
|
||||
return {
|
||||
"timezone_shift": 0,
|
||||
"date": ical_dt.value.date,
|
||||
"time": (
|
||||
(ical_dt.value.time === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
{
|
||||
"hour": ical_dt.value.time.hour,
|
||||
"minute": ical_dt.value.time.minute,
|
||||
"second": ical_dt.value.time.second,
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
70
source/logic/main.ts
Normal file
70
source/logic/main.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_conf = {
|
||||
view_mode : string;
|
||||
calendar_id : int;
|
||||
timezone_shift : int;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function main(
|
||||
) : Promise<void>
|
||||
{
|
||||
// init
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
lib_plankton.log.channel_make({"kind": "console", "data": {"threshold": "info"}}),
|
||||
]
|
||||
);
|
||||
|
||||
// conf
|
||||
const conf : type_conf = lib_plankton.json.decode(await lib_plankton.file.read("conf.json"));
|
||||
|
||||
// exec
|
||||
let content : string;
|
||||
switch (conf.view_mode) {
|
||||
default: {
|
||||
content = "";
|
||||
throw (new Error("invalid view mode"));
|
||||
break;
|
||||
}
|
||||
case "table": {
|
||||
content = await _zeitbild.frontend.view.calendar_view_table_html(
|
||||
conf.calendar_id,
|
||||
{
|
||||
"from": {
|
||||
"year": 2024,
|
||||
"week": 35
|
||||
},
|
||||
"to": {
|
||||
"year": 2024,
|
||||
"week": 43
|
||||
},
|
||||
"timezone_shift": conf.timezone_shift,
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "list": {
|
||||
content = await _zeitbild.frontend.view.calendar_view_list_html(
|
||||
conf.calendar_id,
|
||||
{
|
||||
"timezone_shift": conf.timezone_shift,
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
(document.querySelector("body") as HTMLBodyElement).innerHTML = content;
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
}
|
||||
|
115
source/logic/types.ts
Normal file
115
source/logic/types.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_role = (
|
||||
"editor"
|
||||
|
|
||||
"viewer"
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_user_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_user_object = {
|
||||
name : string;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_event = {
|
||||
name : string;
|
||||
begin : _zeitbild.frontend.helpers.type_datetime;
|
||||
end : (
|
||||
null
|
||||
|
|
||||
_zeitbild.frontend.helpers.type_datetime
|
||||
);
|
||||
location : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
description : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_calendar_id = int;
|
||||
|
||||
|
||||
/**
|
||||
* @todo bei "collection" Kreise vermeiden
|
||||
*/
|
||||
export type type_calendar_object = (
|
||||
{
|
||||
kind : "concrete";
|
||||
data : {
|
||||
name : string;
|
||||
private : boolean;
|
||||
users : Array<
|
||||
{
|
||||
id : type_user_id;
|
||||
role : type_role;
|
||||
}
|
||||
>;
|
||||
events : Array<type_event>;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "collection";
|
||||
data : {
|
||||
name : string;
|
||||
private : boolean;
|
||||
sources : Array<
|
||||
type_calendar_id
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
data : {
|
||||
name : string;
|
||||
private : boolean;
|
||||
read_only : boolean;
|
||||
source_url : string;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_datamodel = {
|
||||
users : Array<
|
||||
{
|
||||
id : type_user_id;
|
||||
object : type_user_object;
|
||||
}
|
||||
>;
|
||||
calendars : Array<
|
||||
{
|
||||
id : type_calendar_id;
|
||||
object : type_calendar_object;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
}
|
695
source/logic/view.ts
Normal file
695
source/logic/view.ts
Normal file
|
@ -0,0 +1,695 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.view
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function event_generate_tooltip(
|
||||
calendar_name : string,
|
||||
event : type_event
|
||||
) : string
|
||||
{
|
||||
return (
|
||||
lib_plankton.string.coin(
|
||||
"[{{calendar_name}}] {{event_name}}\n",
|
||||
{
|
||||
"calendar_name": calendar_name,
|
||||
"event_name": event.name,
|
||||
}
|
||||
)
|
||||
+
|
||||
"--\n"
|
||||
+
|
||||
(
|
||||
(event.begin.time !== null)
|
||||
?
|
||||
lib_plankton.string.coin(
|
||||
"{{label}}: {{value}}\n",
|
||||
{
|
||||
"label": "Anfang", // TODO: translate
|
||||
"value": lib_plankton.string.coin(
|
||||
"{{hour}}:{{minute}}",
|
||||
{
|
||||
"hour": event.begin.time.hour.toFixed(0).padStart(2, "0"),
|
||||
"minute": event.begin.time.minute.toFixed(0).padStart(2, "0"),
|
||||
}
|
||||
), // TODO: outsource
|
||||
}
|
||||
)
|
||||
:
|
||||
""
|
||||
)
|
||||
+
|
||||
(
|
||||
(event.end !== null)
|
||||
?
|
||||
lib_plankton.string.coin(
|
||||
"{{label}}: {{value}}\n",
|
||||
{
|
||||
"label": "Ende", // TODO: translate
|
||||
"value": (
|
||||
[
|
||||
(
|
||||
(
|
||||
(event.end.date.year !== event.begin.date.year)
|
||||
||
|
||||
(event.end.date.month !== event.begin.date.month)
|
||||
||
|
||||
(event.end.date.day !== event.begin.date.day)
|
||||
)
|
||||
?
|
||||
lib_plankton.string.coin(
|
||||
"{{year}}-{{month}}-{{day}}",
|
||||
{
|
||||
"year": event.end.date.year.toFixed(0).padStart(4, "0"),
|
||||
"month": event.end.date.month.toFixed(0).padStart(2, "0"),
|
||||
"day": event.end.date.month.toFixed(0).padStart(2, "0"),
|
||||
}
|
||||
)
|
||||
:
|
||||
null
|
||||
),
|
||||
(
|
||||
(event.end.time !== null)
|
||||
?
|
||||
lib_plankton.string.coin(
|
||||
"{{hour}}:{{minute}}",
|
||||
{
|
||||
"hour": event.end.time.hour.toFixed(0).padStart(2, "0"),
|
||||
"minute": event.end.time.minute.toFixed(0).padStart(2, "0"),
|
||||
}
|
||||
)
|
||||
:
|
||||
null
|
||||
),
|
||||
]
|
||||
.filter(x => (x !== null))
|
||||
.join(",")
|
||||
),
|
||||
}
|
||||
)
|
||||
:
|
||||
""
|
||||
)
|
||||
+
|
||||
(
|
||||
(event.location !== null)
|
||||
?
|
||||
(
|
||||
lib_plankton.string.coin(
|
||||
"{{label}}: {{value}}\n",
|
||||
{
|
||||
"label": "Ort", // TODO
|
||||
"value": event.location,
|
||||
}
|
||||
)
|
||||
)
|
||||
:
|
||||
""
|
||||
)
|
||||
+
|
||||
(
|
||||
(event.description !== null)
|
||||
?
|
||||
(
|
||||
"--\n"
|
||||
+
|
||||
lib_plankton.string.coin(
|
||||
"{{description}}\n",
|
||||
{
|
||||
"description": event.description,
|
||||
}
|
||||
)
|
||||
)
|
||||
:
|
||||
""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo kein "while"
|
||||
*/
|
||||
async function calendar_view_table_data(
|
||||
calendar_id : type_calendar_id,
|
||||
options : {
|
||||
from ?: {
|
||||
year : int;
|
||||
week : int;
|
||||
},
|
||||
to ?: {
|
||||
year : int;
|
||||
week : int;
|
||||
},
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : Promise<
|
||||
{
|
||||
sources : lib_plankton.structures.type_hashmap<
|
||||
type_calendar_id,
|
||||
{
|
||||
name : string;
|
||||
}
|
||||
>;
|
||||
rows : Array<
|
||||
{
|
||||
week : int;
|
||||
data : Array<
|
||||
{
|
||||
pit : _zeitbild.frontend.helpers.type_pit;
|
||||
entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
>;
|
||||
today : boolean;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>
|
||||
}
|
||||
>
|
||||
{
|
||||
const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now();
|
||||
options = Object.assign(
|
||||
{
|
||||
"from": lib_plankton.call.convey(
|
||||
now_pit,
|
||||
[
|
||||
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, -1),
|
||||
_zeitbild.frontend.helpers.pit_to_date_object,
|
||||
(x : Date) => ({
|
||||
"year": x.getFullYear(),
|
||||
"week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x),
|
||||
})
|
||||
]
|
||||
),
|
||||
"to": lib_plankton.call.convey(
|
||||
now_pit,
|
||||
[
|
||||
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4),
|
||||
_zeitbild.frontend.helpers.pit_to_date_object,
|
||||
(x : Date) => ({
|
||||
"year": x.getFullYear(),
|
||||
"week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x),
|
||||
})
|
||||
]
|
||||
),
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
/*
|
||||
const calendar_object : type_calendar_object = calendar_read(
|
||||
data,
|
||||
calendar_id
|
||||
);
|
||||
*/
|
||||
const from_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week(
|
||||
(options.from as {year : int; week : int}).year,
|
||||
(options.from as {year : int; week : int}).week,
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
}
|
||||
);
|
||||
const to_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week(
|
||||
(options.to as {year : int; week : int}).year,
|
||||
(options.to as {year : int; week : int}).week,
|
||||
{
|
||||
"timezone_shift": (options.timezone_shift as int),
|
||||
}
|
||||
);
|
||||
|
||||
// prepare
|
||||
const entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
calendar_name : string;
|
||||
event : type_event;
|
||||
}
|
||||
> = await _zeitbild.frontend.resources.backend.calendar_gather_events(
|
||||
calendar_id,
|
||||
from_pit,
|
||||
to_pit
|
||||
);
|
||||
let result : {
|
||||
sources : lib_plankton.structures.type_hashmap<
|
||||
type_calendar_id,
|
||||
{
|
||||
name : string;
|
||||
}
|
||||
>;
|
||||
rows : Array<
|
||||
{
|
||||
week : int;
|
||||
data : Array<
|
||||
{
|
||||
pit : _zeitbild.frontend.helpers.type_pit;
|
||||
entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
>;
|
||||
today : boolean;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
} = {
|
||||
"sources": lib_plankton.structures.hashmap_construct(
|
||||
x => x.toFixed(0),
|
||||
(
|
||||
entries
|
||||
.map(
|
||||
(entry) => (
|
||||
{
|
||||
"key": entry.calendar_id,
|
||||
"value": {
|
||||
"name": entry.calendar_name,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
"rows": [],
|
||||
};
|
||||
let row : Array<
|
||||
{
|
||||
pit : _zeitbild.frontend.helpers.type_pit;
|
||||
entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
>;
|
||||
today : boolean;
|
||||
}
|
||||
> = [];
|
||||
let day : int = 0;
|
||||
while (true) {
|
||||
const pit_current : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_shift_day(
|
||||
from_pit,
|
||||
day
|
||||
);
|
||||
if (
|
||||
_zeitbild.frontend.helpers.pit_is_before(
|
||||
pit_current,
|
||||
to_pit
|
||||
)
|
||||
) {
|
||||
day += 1;
|
||||
row.push(
|
||||
{
|
||||
"pit": pit_current,
|
||||
"entries": [],
|
||||
"today": false, // TODO
|
||||
}
|
||||
);
|
||||
if (day % 7 === 0) {
|
||||
result.rows.push(
|
||||
{
|
||||
"week": (
|
||||
(options.from as {year : int; week : int}).week
|
||||
+
|
||||
Math.floor(day / 7)
|
||||
-
|
||||
1 // TODO
|
||||
),
|
||||
"data": row
|
||||
}
|
||||
);
|
||||
row = [];
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fill
|
||||
{
|
||||
// events
|
||||
(
|
||||
entries
|
||||
.forEach(
|
||||
(entry) => {
|
||||
const distance_seconds : int = (
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(entry.event.begin)
|
||||
-
|
||||
from_pit
|
||||
);
|
||||
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);
|
||||
|
||||
if ((week >= 0) && (week < result.rows.length)) {
|
||||
result.rows[week].data[day].entries.push(entry);
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
// today
|
||||
{
|
||||
const distance_seconds : int = (
|
||||
now_pit
|
||||
-
|
||||
from_pit
|
||||
);
|
||||
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);
|
||||
|
||||
if ((week >= 0) && (week < result.rows.length)) {
|
||||
result.rows[week].data[day].today = true;
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_view_table_html(
|
||||
calendar_id : type_calendar_id,
|
||||
options : {
|
||||
from ?: {
|
||||
year : int;
|
||||
week : int;
|
||||
};
|
||||
to ?: {
|
||||
year : int;
|
||||
week : int;
|
||||
};
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : Promise<string>
|
||||
{
|
||||
const stuff : {
|
||||
sources : lib_plankton.structures.type_hashmap<
|
||||
type_calendar_id,
|
||||
{
|
||||
name : string;
|
||||
}
|
||||
>;
|
||||
rows : Array<
|
||||
{
|
||||
week : int;
|
||||
data : Array<
|
||||
{
|
||||
pit : _zeitbild.frontend.helpers.type_pit;
|
||||
entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
>;
|
||||
today : boolean;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
} = await calendar_view_table_data(
|
||||
calendar_id,
|
||||
options
|
||||
);
|
||||
const sources : lib_plankton.structures.type_hashmap<
|
||||
type_calendar_id,
|
||||
{
|
||||
name : string;
|
||||
color : lib_plankton.color.type_color;
|
||||
}
|
||||
> = lib_plankton.structures.hashmap_construct(
|
||||
(x => x.toFixed(0)),
|
||||
lib_plankton.structures.hashmap_dump(
|
||||
stuff.sources
|
||||
)
|
||||
.map(
|
||||
(pair) => ({
|
||||
"key": pair.key,
|
||||
"value": {
|
||||
"name": pair.value.name,
|
||||
"color": lib_plankton.color.give_generic(
|
||||
(pair.key + 0.2),
|
||||
{
|
||||
"saturation": 0.375,
|
||||
"value": 0.375,
|
||||
}
|
||||
),
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
return _zeitbild.frontend.helpers.template_coin(
|
||||
"tableview",
|
||||
{
|
||||
"sources": (
|
||||
await _zeitbild.frontend.helpers.promise_row<string>(
|
||||
lib_plankton.structures.hashmap_dump(sources)
|
||||
.map(
|
||||
({"key": calendar_id, "value": data}) => async () => _zeitbild.frontend.helpers.template_coin(
|
||||
"tableview-sources-entry",
|
||||
{
|
||||
"name": data.name,
|
||||
"color": lib_plankton.color.output_hex(data.color),
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
).join(""),
|
||||
"rows": (
|
||||
await _zeitbild.frontend.helpers.promise_row<string>(
|
||||
stuff.rows
|
||||
.map(
|
||||
(row) => async () => _zeitbild.frontend.helpers.template_coin(
|
||||
"tableview-row",
|
||||
{
|
||||
"week": row.week.toFixed(0).padStart(2, "0"),
|
||||
"cells": (
|
||||
await _zeitbild.frontend.helpers.promise_row<string>(
|
||||
row.data
|
||||
.map(
|
||||
(cell) => async () => _zeitbild.frontend.helpers.template_coin(
|
||||
"tableview-cell",
|
||||
{
|
||||
"extra_classes": (
|
||||
[""]
|
||||
.concat(cell.today ? ["calendar-cell-today"] : [])
|
||||
.join(" ")
|
||||
),
|
||||
"title": lib_plankton.call.convey(
|
||||
cell.pit,
|
||||
[
|
||||
_zeitbild.frontend.helpers.pit_to_datetime,
|
||||
(x : _zeitbild.frontend.helpers.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"),
|
||||
}
|
||||
),
|
||||
]
|
||||
),
|
||||
"day": lib_plankton.call.convey(
|
||||
cell.pit,
|
||||
[
|
||||
_zeitbild.frontend.helpers.pit_to_datetime,
|
||||
(x : _zeitbild.frontend.helpers.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"),
|
||||
}
|
||||
),
|
||||
]
|
||||
),
|
||||
"entries": (
|
||||
await _zeitbild.frontend.helpers.promise_row<string>(
|
||||
cell.entries
|
||||
.map(
|
||||
(entry) => () => _zeitbild.frontend.helpers.template_coin(
|
||||
"tableview-cell-entry",
|
||||
{
|
||||
"color": lib_plankton.color.output_hex(
|
||||
lib_plankton.structures.hashmap_get(
|
||||
sources,
|
||||
entry.calendar_id
|
||||
).color
|
||||
),
|
||||
"title": event_generate_tooltip(
|
||||
lib_plankton.structures.hashmap_get(
|
||||
sources,
|
||||
entry.calendar_id
|
||||
).name,
|
||||
entry.event
|
||||
),
|
||||
"name": entry.event.name,
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
).join(""),
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
).join(""),
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
).join(""),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function calendar_view_list_data(
|
||||
calendar_id : type_calendar_id,
|
||||
options : {
|
||||
from ?: _zeitbild.frontend.helpers.type_pit;
|
||||
to ?: _zeitbild.frontend.helpers.type_pit;
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now();
|
||||
options = Object.assign(
|
||||
{
|
||||
"from": lib_plankton.call.convey(
|
||||
now_pit,
|
||||
[
|
||||
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_day(x, -1),
|
||||
]
|
||||
),
|
||||
"to": lib_plankton.call.convey(
|
||||
now_pit,
|
||||
[
|
||||
(x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4),
|
||||
]
|
||||
),
|
||||
"timezone_shift": 0,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
const entries : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
> = await _zeitbild.frontend.resources.backend.calendar_gather_events(
|
||||
calendar_id,
|
||||
(options.from as _zeitbild.frontend.helpers.type_pit),
|
||||
(options.to as _zeitbild.frontend.helpers.type_pit)
|
||||
);
|
||||
// TODO: optimize
|
||||
entries.sort(
|
||||
(entry_1, entry_2) => (
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(entry_1.event.begin)
|
||||
-
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(entry_2.event.begin)
|
||||
)
|
||||
);
|
||||
|
||||
return Promise.resolve(entries);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_view_list_html(
|
||||
calendar_id : type_calendar_id,
|
||||
options : {
|
||||
from ?: _zeitbild.frontend.helpers.type_pit;
|
||||
to ?: _zeitbild.frontend.helpers.type_pit;
|
||||
timezone_shift ?: int;
|
||||
} = {}
|
||||
) : Promise<string>
|
||||
{
|
||||
const stuff : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
event : type_event;
|
||||
}
|
||||
> = await calendar_view_list_data(
|
||||
calendar_id,
|
||||
options
|
||||
);
|
||||
return Promise.resolve<string>(
|
||||
new lib_plankton.xml.class_node_complex(
|
||||
"div",
|
||||
{
|
||||
"class": "list",
|
||||
},
|
||||
[
|
||||
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"
|
||||
)
|
||||
]
|
||||
),
|
||||
new lib_plankton.xml.class_node_complex(
|
||||
"ul",
|
||||
{
|
||||
"class": "list-events",
|
||||
},
|
||||
(
|
||||
stuff
|
||||
.map(
|
||||
(entry) => (
|
||||
new lib_plankton.xml.class_node_complex(
|
||||
"li",
|
||||
{
|
||||
"class": "list-event_entry",
|
||||
},
|
||||
[
|
||||
new lib_plankton.xml.class_node_text(
|
||||
JSON.stringify(entry)
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
]
|
||||
).compile()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
78
source/style/main.css
Normal file
78
source/style/main.css
Normal file
|
@ -0,0 +1,78 @@
|
|||
html {
|
||||
background-color: #111;
|
||||
color: #FFF;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.calendar-pane-left {
|
||||
flex-basis: 12.5%;
|
||||
}
|
||||
|
||||
.calendar-pane-right {
|
||||
flex-basis: 87.5%;
|
||||
}
|
||||
|
||||
.tableview-sources {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.tableview-sources-entry {
|
||||
margin: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.calendar table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.calendar-cell {
|
||||
border: 1px solid #888;
|
||||
padding: 8px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.calendar-cell-day {
|
||||
width: 13.5%;
|
||||
}
|
||||
|
||||
.calendar-cell-week {
|
||||
width: 5.5%;
|
||||
}
|
||||
|
||||
.calendar-cell-regular {
|
||||
width: 13.5%;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.calendar-cell-today {
|
||||
outline: 4px solid #FFF;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
font-size: 0.75em;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.calendar-events {
|
||||
margin: 0; padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.calendar-event_entry {
|
||||
margin: 4px;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.75em;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
9
source/templates/main.html.tpl
Normal file
9
source/templates/main.html.tpl
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
3
source/templates/tableview-cell-entry.html.tpl
Normal file
3
source/templates/tableview-cell-entry.html.tpl
Normal file
|
@ -0,0 +1,3 @@
|
|||
<li class="calendar-event_entry" style="background-color: {{color}};" title="{{title}}">
|
||||
{{name}}
|
||||
</li>
|
8
source/templates/tableview-cell.html.tpl
Normal file
8
source/templates/tableview-cell.html.tpl
Normal file
|
@ -0,0 +1,8 @@
|
|||
<td class="calendar-cell calendar-cell-regular{{extra_classes}}">
|
||||
<span class="calendar-day" title="{{title}}">
|
||||
{{day}}
|
||||
</span>
|
||||
<ul class="calendar-events">
|
||||
{{entries}}
|
||||
</ul>
|
||||
</td>
|
6
source/templates/tableview-row.html.tpl
Normal file
6
source/templates/tableview-row.html.tpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
<tr>
|
||||
<th class="calendar-cell calendar-cell-week">
|
||||
{{week}}
|
||||
</th>
|
||||
{{cells}}
|
||||
</tr>
|
1
source/templates/tableview-sources-entry.html.tpl
Normal file
1
source/templates/tableview-sources-entry.html.tpl
Normal file
|
@ -0,0 +1 @@
|
|||
<li class="tableview-sources-entry" style="background-color: {{color}}">{{name}}</li>
|
27
source/templates/tableview.html.tpl
Normal file
27
source/templates/tableview.html.tpl
Normal file
|
@ -0,0 +1,27 @@
|
|||
<div class="calendar">
|
||||
<div class="calendar-pane calendar-pane-left">
|
||||
<ul class="tableview-sources">
|
||||
{{sources}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="calendar-pane calendar-pane-right">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="calendar-cell"></th>
|
||||
<th class="calendar-cell calendar-cell-day">Mo</th>
|
||||
<th class="calendar-cell calendar-cell-day">Di</th>
|
||||
<th class="calendar-cell calendar-cell-day">Mi</th>
|
||||
<th class="calendar-cell calendar-cell-day">Do</th>
|
||||
<th class="calendar-cell calendar-cell-day">Fr</th>
|
||||
<th class="calendar-cell calendar-cell-day">Sa</th>
|
||||
<th class="calendar-cell calendar-cell-day">So</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
38
tools/build
38
tools/build
|
@ -16,6 +16,22 @@ def main():
|
|||
metavar = "<output-directory>",
|
||||
help = "output directory",
|
||||
)
|
||||
argument_parser.add_argument(
|
||||
"-c",
|
||||
"--conf-path",
|
||||
type = str,
|
||||
default = None,
|
||||
metavar = "<conf-path>",
|
||||
help = "conf path",
|
||||
)
|
||||
argument_parser.add_argument(
|
||||
"-d",
|
||||
"--data-path",
|
||||
type = str,
|
||||
default = None,
|
||||
metavar = "<data-path>",
|
||||
help = "data path",
|
||||
)
|
||||
args = argument_parser.parse_args()
|
||||
|
||||
## exec
|
||||
|
@ -28,6 +44,28 @@ def main():
|
|||
" ".join(targets),
|
||||
)
|
||||
)
|
||||
if True:
|
||||
if (args.conf_path is None):
|
||||
pass
|
||||
else:
|
||||
_os.system(
|
||||
"cp %s %s/conf.json"
|
||||
% (
|
||||
args.conf_path,
|
||||
args.output_directory,
|
||||
)
|
||||
)
|
||||
if True:
|
||||
if (args.data_path is None):
|
||||
pass
|
||||
else:
|
||||
_os.system(
|
||||
"cp %s %s/data.json"
|
||||
% (
|
||||
args.data_path,
|
||||
args.output_directory,
|
||||
)
|
||||
)
|
||||
_sys.stdout.write("%s\n" % args.output_directory)
|
||||
|
||||
|
||||
|
|
|
@ -6,37 +6,58 @@ dir_temp := /tmp/kalender-temp
|
|||
dir_build := build
|
||||
dir_tools := tools
|
||||
|
||||
cmd_log := echo "--"
|
||||
cmd_cat := cat
|
||||
cmd_chmod := chmod
|
||||
cmd_mkdir := mkdir -p
|
||||
cmd_cp := cp
|
||||
cmd_log := echo "--"
|
||||
cmd_mkdir := mkdir -p
|
||||
cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
.PHONY: default
|
||||
default: ${dir_build}/kalender
|
||||
default: index templates style logic
|
||||
|
||||
${dir_temp}/kalender-unlinked.js: \
|
||||
.PHONY: index
|
||||
index: ${dir_build}/index.html
|
||||
|
||||
${dir_build}/index.html: \
|
||||
${dir_source}/index.html
|
||||
@ ${cmd_log} "index …"
|
||||
@ ${cmd_cp} -u -v $^ $@
|
||||
|
||||
.PHONY: templates
|
||||
templates: \
|
||||
$(wildcard ${dir_source}/templates/*)
|
||||
@ ${cmd_log} "templates …"
|
||||
@ ${cmd_mkdir} ${dir_build}/templates
|
||||
@ ${cmd_cp} -r -u -v ${dir_source}/templates/* ${dir_build}/templates/
|
||||
|
||||
.PHONY: style
|
||||
style: \
|
||||
$(wildcard ${dir_source}/style/*)
|
||||
@ ${cmd_log} "style …"
|
||||
@ ${cmd_mkdir} ${dir_build}
|
||||
@ ${cmd_cat} ${dir_source}/style/* > ${dir_build}/style.css
|
||||
|
||||
.PHONY: logic
|
||||
logic: ${dir_build}/logic.js
|
||||
|
||||
${dir_temp}/logic-unlinked.js: \
|
||||
${dir_lib}/plankton/plankton.d.ts \
|
||||
${dir_source}/helpers.ts \
|
||||
${dir_source}/logic.ts \
|
||||
${dir_source}/main.ts
|
||||
@ ${cmd_log} "compile …"
|
||||
${dir_source}/logic/helpers.ts \
|
||||
${dir_source}/logic/types.ts \
|
||||
${dir_source}/logic/backend.ts \
|
||||
${dir_source}/logic/view.ts \
|
||||
${dir_source}/logic/main.ts
|
||||
@ ${cmd_log} "logic | compile …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@
|
||||
@ ${cmd_tsc} --lib dom,es2020 --strict $^ --outFile $@
|
||||
|
||||
${dir_temp}/head.js:
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ echo "#!/usr/bin/env node" > $@
|
||||
|
||||
${dir_build}/kalender: \
|
||||
${dir_temp}/head.js \
|
||||
${dir_build}/logic.js: \
|
||||
${dir_lib}/plankton/plankton.js \
|
||||
${dir_temp}/kalender-unlinked.js
|
||||
@ ${cmd_log} "link …"
|
||||
${dir_temp}/logic-unlinked.js
|
||||
@ ${cmd_log} "logic | link …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_cat} $^ > $@
|
||||
@ ${cmd_chmod} +x $@
|
||||
|
|
|
@ -8,6 +8,7 @@ modules=""
|
|||
modules="${modules} base"
|
||||
modules="${modules} call"
|
||||
modules="${modules} file"
|
||||
modules="${modules} structures"
|
||||
modules="${modules} json"
|
||||
modules="${modules} args"
|
||||
modules="${modules} string"
|
||||
|
@ -23,5 +24,5 @@ modules="${modules} url"
|
|||
|
||||
mkdir -p ${dir}
|
||||
cd ${dir}
|
||||
ptk bundle node ${modules}
|
||||
ptk bundle web ${modules}
|
||||
cd - > /dev/null
|
||||
|
|
Loading…
Add table
Reference in a new issue