- commit
- 9c4b3e5c86a95809470955a9632ba405aa716331
- parent
- 23b4192227f944b704dbcfe0e08248a9b9123722
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2022-02-04 17:01
integrate with ical
Diffstat
| A | dav.js | 174 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | index.html | 1 | + |
| M | main.js | 18 | ++++++++++++++++++ |
| M | package.json | 3 | ++- |
4 files changed, 195 insertions, 1 deletions
diff --git a/dav.js b/dav.js
@@ -0,0 +1,174 @@
-1 1 var uuid = function() {
-1 2 if (crypto.randomUUID) {
-1 3 return crypto.randomUUID();
-1 4 }
-1 5
-1 6 var rnds = new Uint8Array(16);
-1 7 crypto.getRandomValues(rnds);
-1 8 rnds[6] = (rnds[6] & 0x0f) | 0x40;
-1 9 rnds[8] = (rnds[8] & 0x3f) | 0x80;
-1 10
-1 11 var s = '';
-1 12 for (var i = 0; i < 16; i++) {
-1 13 s += (rnds[i] + 0x100).toString(16).substr(1);
-1 14 if (i == 3 || i == 5 || i == 7 || i == 9) {
-1 15 s += '-';
-1 16 }
-1 17 }
-1 18 return s;
-1 19 };
-1 20
-1 21 var date2idate = function(date, allDay, offset) {
-1 22 var odate = offset ? new Date(date - offset) : date;
-1 23 var idate = ICAL.Time.fromJSDate(odate);
-1 24 if (allDay) {
-1 25 idate.hour = 0;
-1 26 idate.minute = 0;
-1 27 idate.second = 0;
-1 28 idate.isDate = true;
-1 29 }
-1 30 return idate;
-1 31 };
-1 32
-1 33 var formatDate = function(date) {
-1 34 return date
-1 35 .toISOString()
-1 36 .replace(/[-:.]/g, '')
-1 37 .replace('000Z', 'Z');
-1 38 };
-1 39
-1 40 export var getCalendars = function(url) {
-1 41 return fetch(url, {
-1 42 method: 'PROPFIND',
-1 43 credentials: 'same-origin',
-1 44 body: '<?xml version="1.0" encoding="utf-8"?>\n'
-1 45 + '<D:propfind xmlns:D="DAV:">\n'
-1 46 + ' <D:prop>\n'
-1 47 + ' <D:resourcetype/>\n'
-1 48 + ' <D:displayname/>\n'
-1 49 + ' <A:calendar-color xmlns:A="http://apple.com/ns/ical/"/>\n'
-1 50 + ' </D:prop>\n'
-1 51 + '</D:propfind>',
-1 52 }).then(function(response) {
-1 53 if (response.ok) {
-1 54 return response.text();
-1 55 } else {
-1 56 throw response;
-1 57 }
-1 58 }).then(function(xml) {
-1 59 var parser = new DOMParser();
-1 60 var dom = parser.parseFromString(xml, 'text/xml');
-1 61 var calendars = [];
-1 62 dom.querySelectorAll('response').forEach(response => {
-1 63 if (response.querySelector('resourcetype calendar')) {
-1 64 calendars.push({
-1 65 href: response.querySelector('href').textContent,
-1 66 name: response.querySelector('displayname').textContent,
-1 67 color: response.querySelector('calendar-color').textContent,
-1 68 });
-1 69 }
-1 70 });
-1 71 return calendars;
-1 72 });
-1 73 };
-1 74
-1 75 export var getEvents = function(href, info) {
-1 76 return fetch(href, {
-1 77 method: 'REPORT',
-1 78 credentials: 'same-origin',
-1 79 headers: {depth: '1'},
-1 80 body: '<?xml version="1.0" encoding="utf-8"?>\n'
-1 81 + '<L:calendar-query xmlns:L="urn:ietf:params:xml:ns:caldav">\n'
-1 82 + ' <D:prop xmlns:D="DAV:">\n'
-1 83 + ' <D:getcontenttype/>\n'
-1 84 + ' <D:getetag/>\n'
-1 85 + ' <L:calendar-data/>\n'
-1 86 + ' </D:prop>\n'
-1 87 + ' <L:filter>\n'
-1 88 + ' <L:comp-filter name="VCALENDAR">\n'
-1 89 + ' <L:comp-filter name="VEVENT">\n'
-1 90 + ` <L:time-range start="${formatDate(info.start)}" end="${formatDate(info.end)}"/>\n`
-1 91 + ' </L:comp-filter>\n'
-1 92 + ' </L:comp-filter>\n'
-1 93 + ' </L:filter>\n'
-1 94 + '</L:calendar-query>',
-1 95 }).then(function(response) {
-1 96 if (response.ok) {
-1 97 return response.text();
-1 98 } else {
-1 99 throw response;
-1 100 }
-1 101 }).then(function(xml) {
-1 102 var parser = new DOMParser();
-1 103 var dom = parser.parseFromString(xml, 'text/xml');
-1 104 var events = [];
-1 105 dom.querySelectorAll('response').forEach(response => {
-1 106 // https://github.com/mozilla-comm/ical.js/wiki
-1 107 var ics = response.querySelector('calendar-data').textContent;
-1 108 var jcal = ICAL.parse(ics);
-1 109 var comp = new ICAL.Component(jcal);
-1 110 var vevent = new ICAL.Event(comp.getFirstSubcomponent('vevent'));
-1 111 var iter = vevent.iterator();
-1 112 var start = vevent.startDate.toJSDate();
-1 113 var end = vevent.endDate.toJSDate();
-1 114 var i;
-1 115 while (i = iter.next()) {
-1 116 var istart = i.toJSDate();
-1 117 if (istart < info.start) {
-1 118 continue;
-1 119 } else if (istart > info.end) {
-1 120 break;
-1 121 }
-1 122 events.push({
-1 123 groupId: response.querySelector('href').textContent,
-1 124 title: vevent.summary,
-1 125 offset: istart - start,
-1 126 start: istart,
-1 127 end: new Date(istart - (start - end)),
-1 128 allDay: vevent.startDate.isDate,
-1 129 comp: comp,
-1 130 });
-1 131 }
-1 132 });
-1 133 return events;
-1 134 });
-1 135 };
-1 136
-1 137 export var createEvent = function(info, source) {
-1 138 var comp = new ICAL.Component(['vcalendar', [], []]);
-1 139 var compEvent = new ICAL.Component('vevent');
-1 140 comp.updatePropertyWithValue('prodid', '-//iCal.js Wiki Example');
-1 141 comp.addSubcomponent(compEvent);
-1 142
-1 143 var vevent = new ICAL.Event(compEvent);
-1 144 vevent.uid = uuid();
-1 145
-1 146 return {
-1 147 groupId: source.id + vevent.uid + '.ics', // FIXME: assumptions about href structure
-1 148 title: 'new event',
-1 149 offset: 0,
-1 150 start: info.date,
-1 151 allDay: info.allDay,
-1 152 comp: comp,
-1 153 };
-1 154 };
-1 155
-1 156 export var commitEvent = function(data) {
-1 157 var comp = data.extendedProps.comp;
-1 158 var vevent = new ICAL.Event(comp.getFirstSubcomponent('vevent'));
-1 159 vevent.summary = data.title;
-1 160 vevent.startDate = date2idate(data.start, data.allDay, data.extendedProps.offset);
-1 161 vevent.endDate = date2idate(data.end || data.start, data.allDay, data.extendedProps.offset);
-1 162 return fetch(data.groupId, {
-1 163 method: 'PUT',
-1 164 credentials: 'same-origin',
-1 165 body: comp.toString(),
-1 166 });
-1 167 };
-1 168
-1 169 export var deleteEvent = function(data) {
-1 170 return fetch(data.groupId, {
-1 171 method: 'DELETE',
-1 172 credentials: 'same-origin',
-1 173 });
-1 174 };
diff --git a/index.html b/index.html
@@ -12,6 +12,7 @@ 12 12 13 13 <script src="node_modules/fullcalendar-scheduler/main.js"></script> 14 14 <script src="node_modules/fullcalendar-scheduler/locales/de.js"></script> -1 15 <script src="node_modules/ical.js/build/ical.js"></script> 15 16 <script src="main.js" type="module"></script> 16 17 </body> 17 18 </html>
diff --git a/main.js b/main.js
@@ -1,3 +1,6 @@ -1 1 import * as config from './config.js'; -1 2 import * as dav from './dav.js'; -1 3 1 4 var calendar = new FullCalendar.Calendar( 2 5 document.querySelector('.calendar'), 3 6 { @@ -6,6 +9,8 @@ var calendar = new FullCalendar.Calendar( 6 9 scrollTime: '07:00', 7 10 nowIndicator: true, 8 11 weekNumberCalculation: 'ISO', -1 12 eventDrop: info => dav.commitEvent(info.event), -1 13 eventResize: info => dav.commitEvent(info.event), 9 14 height: '100%', 10 15 headerToolbar: { 11 16 left: 'timeGridWeek,dayGridMonth', @@ -16,3 +21,16 @@ var calendar = new FullCalendar.Calendar( 16 21 } 17 22 ); 18 23 calendar.render(); -1 24 -1 25 dav.getCalendars(config.rootUrl).then(calendars => { -1 26 calendars.forEach(cal => { -1 27 calendar.addEventSource({ -1 28 id: cal.href, -1 29 color: cal.color, -1 30 editable: true, -1 31 events: function(info, success, error) { -1 32 dav.getEvents(cal.href, info).then(success, error); -1 33 }, -1 34 }); -1 35 }); -1 36 });
diff --git a/package.json b/package.json
@@ -5,6 +5,7 @@ 5 5 "author": "Tobias Bengfort <tobias.bengfort@posteo.de>", 6 6 "license": "MIT", 7 7 "dependencies": {8 -1 "fullcalendar-scheduler": "^5.10.1"-1 8 "fullcalendar-scheduler": "^5.10.1", -1 9 "ical.js": "^1.4.0" 9 10 } 10 11 }