@svar-ui/calendar-ical 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +119 -0
- package/package.json +29 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type EventID = string | number;
|
|
3
|
+
interface CalendarEvent {
|
|
4
|
+
id: EventID;
|
|
5
|
+
start: Date;
|
|
6
|
+
end: Date;
|
|
7
|
+
allDay?: boolean;
|
|
8
|
+
text?: string;
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/parse.d.ts
|
|
13
|
+
declare function parseICal(ics: string): CalendarEvent[];
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/serialize.d.ts
|
|
16
|
+
declare function serializeICal(events: CalendarEvent[]): string;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { type CalendarEvent, type EventID, parseICal, serializeICal };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//#region src/parse.ts
|
|
2
|
+
function unfold(ics) {
|
|
3
|
+
return ics.replace(/\r?\n[ \t]/g, "");
|
|
4
|
+
}
|
|
5
|
+
function parseDate(value) {
|
|
6
|
+
if (/^\d{8}$/.test(value)) {
|
|
7
|
+
const y = +value.slice(0, 4);
|
|
8
|
+
const m = +value.slice(4, 6) - 1;
|
|
9
|
+
const d = +value.slice(6, 8);
|
|
10
|
+
return {
|
|
11
|
+
date: new Date(y, m, d),
|
|
12
|
+
allDay: true
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (value.endsWith("Z")) {
|
|
16
|
+
const y = +value.slice(0, 4);
|
|
17
|
+
const mo = +value.slice(4, 6) - 1;
|
|
18
|
+
const d = +value.slice(6, 8);
|
|
19
|
+
const h = +value.slice(9, 11);
|
|
20
|
+
const mi = +value.slice(11, 13);
|
|
21
|
+
const s = +value.slice(13, 15);
|
|
22
|
+
return {
|
|
23
|
+
date: new Date(Date.UTC(y, mo, d, h, mi, s)),
|
|
24
|
+
allDay: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const y = +value.slice(0, 4);
|
|
28
|
+
const mo = +value.slice(4, 6) - 1;
|
|
29
|
+
const d = +value.slice(6, 8);
|
|
30
|
+
const h = +value.slice(9, 11);
|
|
31
|
+
const mi = +value.slice(11, 13);
|
|
32
|
+
const s = +value.slice(13, 15);
|
|
33
|
+
return {
|
|
34
|
+
date: new Date(y, mo, d, h, mi, s),
|
|
35
|
+
allDay: false
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function unescapeText(value) {
|
|
39
|
+
return value.replace(/\\n/g, "\n").replace(/\\,/g, ",").replace(/\\;/g, ";").replace(/\\\\/g, "\\");
|
|
40
|
+
}
|
|
41
|
+
function parseICal(ics) {
|
|
42
|
+
const lines = unfold(ics).split(/\r?\n/);
|
|
43
|
+
const events = [];
|
|
44
|
+
let current = null;
|
|
45
|
+
for (const line of lines) if (line === "BEGIN:VEVENT") current = {};
|
|
46
|
+
else if (line === "END:VEVENT" && current) {
|
|
47
|
+
const uid = current["UID"];
|
|
48
|
+
const startRaw = current["DTSTART"] ?? "";
|
|
49
|
+
const endRaw = current["DTEND"] ?? "";
|
|
50
|
+
if (startRaw) {
|
|
51
|
+
const { date: start, allDay } = parseDate(startRaw);
|
|
52
|
+
const { date: end } = endRaw ? parseDate(endRaw) : { date: start };
|
|
53
|
+
const event = {
|
|
54
|
+
id: uid ? isNaN(Number(uid)) ? uid : Number(uid) : `${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
55
|
+
start,
|
|
56
|
+
end
|
|
57
|
+
};
|
|
58
|
+
if (allDay) event.allDay = true;
|
|
59
|
+
if (current["SUMMARY"]) event.text = unescapeText(current["SUMMARY"]);
|
|
60
|
+
if (current["DESCRIPTION"]) event.description = unescapeText(current["DESCRIPTION"]);
|
|
61
|
+
events.push(event);
|
|
62
|
+
}
|
|
63
|
+
current = null;
|
|
64
|
+
} else if (current !== null) {
|
|
65
|
+
const colonIdx = line.indexOf(":");
|
|
66
|
+
if (colonIdx === -1) continue;
|
|
67
|
+
const baseKey = line.slice(0, colonIdx).split(";")[0];
|
|
68
|
+
const value = line.slice(colonIdx + 1);
|
|
69
|
+
current[baseKey] = value;
|
|
70
|
+
}
|
|
71
|
+
return events;
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/serialize.ts
|
|
75
|
+
function formatDate(date, allDay) {
|
|
76
|
+
if (allDay) return `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, "0")}${String(date.getDate()).padStart(2, "0")}`;
|
|
77
|
+
return `${date.getUTCFullYear()}${String(date.getUTCMonth() + 1).padStart(2, "0")}${String(date.getUTCDate()).padStart(2, "0")}T${String(date.getUTCHours()).padStart(2, "0")}${String(date.getUTCMinutes()).padStart(2, "0")}${String(date.getUTCSeconds()).padStart(2, "0")}Z`;
|
|
78
|
+
}
|
|
79
|
+
function escapeText(value) {
|
|
80
|
+
return value.replace(/\\/g, "\\\\").replace(/,/g, "\\,").replace(/;/g, "\\;").replace(/\n/g, "\\n");
|
|
81
|
+
}
|
|
82
|
+
function foldLine(line) {
|
|
83
|
+
if (line.length <= 75) return line;
|
|
84
|
+
const chunks = [line.slice(0, 75)];
|
|
85
|
+
let i = 75;
|
|
86
|
+
while (i < line.length) {
|
|
87
|
+
chunks.push(" " + line.slice(i, i + 74));
|
|
88
|
+
i += 74;
|
|
89
|
+
}
|
|
90
|
+
return chunks.join("\r\n");
|
|
91
|
+
}
|
|
92
|
+
function serializeICal(events) {
|
|
93
|
+
const now = formatDate(/* @__PURE__ */ new Date(), false);
|
|
94
|
+
const lines = [
|
|
95
|
+
"BEGIN:VCALENDAR",
|
|
96
|
+
"VERSION:2.0",
|
|
97
|
+
"PRODID:-//wx//calendar-ical//EN"
|
|
98
|
+
];
|
|
99
|
+
for (const ev of events) {
|
|
100
|
+
const allDay = ev.allDay ?? false;
|
|
101
|
+
lines.push("BEGIN:VEVENT");
|
|
102
|
+
lines.push(`UID:${ev.id}`);
|
|
103
|
+
lines.push(`DTSTAMP:${now}`);
|
|
104
|
+
if (allDay) {
|
|
105
|
+
lines.push(`DTSTART;VALUE=DATE:${formatDate(ev.start, true)}`);
|
|
106
|
+
lines.push(`DTEND;VALUE=DATE:${formatDate(ev.end, true)}`);
|
|
107
|
+
} else {
|
|
108
|
+
lines.push(`DTSTART:${formatDate(ev.start, false)}`);
|
|
109
|
+
lines.push(`DTEND:${formatDate(ev.end, false)}`);
|
|
110
|
+
}
|
|
111
|
+
if (ev.text) lines.push(`SUMMARY:${escapeText(ev.text)}`);
|
|
112
|
+
if (ev.description) lines.push(`DESCRIPTION:${escapeText(String(ev.description))}`);
|
|
113
|
+
lines.push("END:VEVENT");
|
|
114
|
+
}
|
|
115
|
+
lines.push("END:VCALENDAR");
|
|
116
|
+
return lines.map(foldLine).join("\r\n") + "\r\n";
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { parseICal, serializeICal };
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@svar-ui/calendar-ical",
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "iCal import/export for SVAR Calendar",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"license.txt"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/index.mjs",
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@typescript/native-preview": "7.0.0-dev.20260316.1",
|
|
20
|
+
"typescript": "^5.9.3",
|
|
21
|
+
"vite-plus": "0.1.16"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "vp pack",
|
|
25
|
+
"watch": "vp pack --watch",
|
|
26
|
+
"test": "vp test",
|
|
27
|
+
"check": "vp check"
|
|
28
|
+
}
|
|
29
|
+
}
|