@fullcalendar/icalendar 5.11.3 → 6.0.0-beta.2

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.
File without changes
package/index.cjs ADDED
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var index_cjs = require('@fullcalendar/core/index.cjs');
6
+ var internal_cjs = require('@fullcalendar/core/internal.cjs');
7
+ var ICAL = require('ical.js');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n["default"] = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var ICAL__namespace = /*#__PURE__*/_interopNamespace(ICAL);
28
+
29
+ /* eslint-disable */
30
+ class IcalExpander {
31
+ constructor(opts) {
32
+ this.maxIterations = opts.maxIterations != null ? opts.maxIterations : 1000;
33
+ this.skipInvalidDates = opts.skipInvalidDates != null ? opts.skipInvalidDates : false;
34
+ this.jCalData = ICAL__namespace.parse(opts.ics);
35
+ this.component = new ICAL__namespace.Component(this.jCalData);
36
+ this.events = this.component.getAllSubcomponents('vevent').map(vevent => new ICAL__namespace.Event(vevent));
37
+ if (this.skipInvalidDates) {
38
+ this.events = this.events.filter((evt) => {
39
+ try {
40
+ evt.startDate.toJSDate();
41
+ evt.endDate.toJSDate();
42
+ return true;
43
+ }
44
+ catch (err) {
45
+ // skipping events with invalid time
46
+ return false;
47
+ }
48
+ });
49
+ }
50
+ }
51
+ between(after, before) {
52
+ function isEventWithinRange(startTime, endTime) {
53
+ return (!after || endTime >= after.getTime()) &&
54
+ (!before || startTime <= before.getTime());
55
+ }
56
+ function getTimes(eventOrOccurrence) {
57
+ const startTime = eventOrOccurrence.startDate.toJSDate().getTime();
58
+ let endTime = eventOrOccurrence.endDate.toJSDate().getTime();
59
+ // If it is an all day event, the end date is set to 00:00 of the next day
60
+ // So we need to make it be 23:59:59 to compare correctly with the given range
61
+ if (eventOrOccurrence.endDate.isDate && (endTime > startTime)) {
62
+ endTime -= 1;
63
+ }
64
+ return { startTime, endTime };
65
+ }
66
+ const exceptions = [];
67
+ this.events.forEach((event) => {
68
+ if (event.isRecurrenceException())
69
+ exceptions.push(event);
70
+ });
71
+ const ret = {
72
+ events: [],
73
+ occurrences: [],
74
+ };
75
+ this.events.filter(e => !e.isRecurrenceException()).forEach((event) => {
76
+ const exdates = [];
77
+ event.component.getAllProperties('exdate').forEach((exdateProp) => {
78
+ const exdate = exdateProp.getFirstValue();
79
+ exdates.push(exdate.toJSDate().getTime());
80
+ });
81
+ // Recurring event is handled differently
82
+ if (event.isRecurring()) {
83
+ const iterator = event.iterator();
84
+ let next;
85
+ let i = 0;
86
+ do {
87
+ i += 1;
88
+ next = iterator.next();
89
+ if (next) {
90
+ const occurrence = event.getOccurrenceDetails(next);
91
+ const { startTime, endTime } = getTimes(occurrence);
92
+ const isOccurrenceExcluded = exdates.indexOf(startTime) !== -1;
93
+ // TODO check that within same day?
94
+ const exception = exceptions.find(ex => ex.uid === event.uid && ex.recurrenceId.toJSDate().getTime() === occurrence.startDate.toJSDate().getTime());
95
+ // We have passed the max date, stop
96
+ if (before && startTime > before.getTime())
97
+ break;
98
+ // Check that we are within our range
99
+ if (isEventWithinRange(startTime, endTime)) {
100
+ if (exception) {
101
+ ret.events.push(exception);
102
+ }
103
+ else if (!isOccurrenceExcluded) {
104
+ ret.occurrences.push(occurrence);
105
+ }
106
+ }
107
+ }
108
+ } while (next && (!this.maxIterations || i < this.maxIterations));
109
+ return;
110
+ }
111
+ // Non-recurring event:
112
+ const { startTime, endTime } = getTimes(event);
113
+ if (isEventWithinRange(startTime, endTime))
114
+ ret.events.push(event);
115
+ });
116
+ return ret;
117
+ }
118
+ before(before) {
119
+ return this.between(undefined, before);
120
+ }
121
+ after(after) {
122
+ return this.between(after);
123
+ }
124
+ all() {
125
+ return this.between();
126
+ }
127
+ }
128
+
129
+ const eventSourceDef = {
130
+ parseMeta(refined) {
131
+ if (refined.url && refined.format === 'ics') {
132
+ return {
133
+ url: refined.url,
134
+ format: 'ics',
135
+ };
136
+ }
137
+ return null;
138
+ },
139
+ fetch(arg, successCallback, errorCallback) {
140
+ let meta = arg.eventSource.meta;
141
+ let { internalState } = meta;
142
+ /*
143
+ NOTE: isRefetch is a HACK. we would do the recurring-expanding in a separate plugin hook,
144
+ but we couldn't leverage built-in allDay-guessing, among other things.
145
+ */
146
+ if (!internalState || arg.isRefetch) {
147
+ internalState = meta.internalState = {
148
+ response: null,
149
+ iCalExpanderPromise: fetch(meta.url, { method: 'GET' }).then((response) => {
150
+ return response.text().then((icsText) => {
151
+ internalState.response = response;
152
+ return new IcalExpander({
153
+ ics: icsText,
154
+ skipInvalidDates: true,
155
+ });
156
+ });
157
+ }),
158
+ };
159
+ }
160
+ internalState.iCalExpanderPromise.then((iCalExpander) => {
161
+ successCallback({
162
+ rawEvents: expandICalEvents(iCalExpander, arg.range),
163
+ response: internalState.response,
164
+ });
165
+ }, errorCallback);
166
+ },
167
+ };
168
+ function expandICalEvents(iCalExpander, range) {
169
+ // expand the range. because our `range` is timeZone-agnostic UTC
170
+ // or maybe because ical.js always produces dates in local time? i forget
171
+ let rangeStart = internal_cjs.addDays(range.start, -1);
172
+ let rangeEnd = internal_cjs.addDays(range.end, 1);
173
+ let iCalRes = iCalExpander.between(rangeStart, rangeEnd); // end inclusive. will give extra results
174
+ let expanded = [];
175
+ // TODO: instead of using startDate/endDate.toString to communicate allDay,
176
+ // we can query startDate/endDate.isDate. More efficient to avoid formatting/reparsing.
177
+ // single events
178
+ for (let iCalEvent of iCalRes.events) {
179
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalEvent.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalEvent.endDate)
180
+ ? iCalEvent.endDate.toString()
181
+ : null }));
182
+ }
183
+ // recurring event instances
184
+ for (let iCalOccurence of iCalRes.occurrences) {
185
+ let iCalEvent = iCalOccurence.item;
186
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalOccurence.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalOccurence.endDate)
187
+ ? iCalOccurence.endDate.toString()
188
+ : null }));
189
+ }
190
+ return expanded;
191
+ }
192
+ function buildNonDateProps(iCalEvent) {
193
+ return {
194
+ title: iCalEvent.summary,
195
+ url: extractEventUrl(iCalEvent),
196
+ extendedProps: {
197
+ location: iCalEvent.location,
198
+ organizer: iCalEvent.organizer,
199
+ description: iCalEvent.description,
200
+ },
201
+ };
202
+ }
203
+ function extractEventUrl(iCalEvent) {
204
+ let urlProp = iCalEvent.component.getFirstProperty('url');
205
+ return urlProp ? urlProp.getFirstValue() : '';
206
+ }
207
+ function specifiesEnd(iCalEvent) {
208
+ return Boolean(iCalEvent.component.getFirstProperty('dtend')) ||
209
+ Boolean(iCalEvent.component.getFirstProperty('duration'));
210
+ }
211
+
212
+ var index = index_cjs.createPlugin({
213
+ name: '@fullcalendar/icalendar',
214
+ eventSourceDefs: [eventSourceDef],
215
+ });
216
+
217
+ exports["default"] = index;
package/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { PluginDef } from '@fullcalendar/core';
2
+
3
+ declare const _default: PluginDef;
4
+ //# sourceMappingURL=index.d.ts.map
5
+
6
+ export { _default as default };
@@ -0,0 +1,225 @@
1
+ /*!
2
+ FullCalendar iCalendar Plugin v6.0.0-beta.2
3
+ Docs & License: https://fullcalendar.io/
4
+ (c) 2022 Adam Shaw
5
+ */
6
+ FullCalendar.ICalendar = (function (exports, internal, core, ICAL) {
7
+ 'use strict';
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n["default"] = e;
24
+ return n;
25
+ }
26
+
27
+ var ICAL__namespace = /*#__PURE__*/_interopNamespace(ICAL);
28
+
29
+ /* eslint-disable */
30
+ class IcalExpander {
31
+ constructor(opts) {
32
+ this.maxIterations = opts.maxIterations != null ? opts.maxIterations : 1000;
33
+ this.skipInvalidDates = opts.skipInvalidDates != null ? opts.skipInvalidDates : false;
34
+ this.jCalData = ICAL__namespace.parse(opts.ics);
35
+ this.component = new ICAL__namespace.Component(this.jCalData);
36
+ this.events = this.component.getAllSubcomponents('vevent').map(vevent => new ICAL__namespace.Event(vevent));
37
+ if (this.skipInvalidDates) {
38
+ this.events = this.events.filter((evt) => {
39
+ try {
40
+ evt.startDate.toJSDate();
41
+ evt.endDate.toJSDate();
42
+ return true;
43
+ }
44
+ catch (err) {
45
+ // skipping events with invalid time
46
+ return false;
47
+ }
48
+ });
49
+ }
50
+ }
51
+ between(after, before) {
52
+ function isEventWithinRange(startTime, endTime) {
53
+ return (!after || endTime >= after.getTime()) &&
54
+ (!before || startTime <= before.getTime());
55
+ }
56
+ function getTimes(eventOrOccurrence) {
57
+ const startTime = eventOrOccurrence.startDate.toJSDate().getTime();
58
+ let endTime = eventOrOccurrence.endDate.toJSDate().getTime();
59
+ // If it is an all day event, the end date is set to 00:00 of the next day
60
+ // So we need to make it be 23:59:59 to compare correctly with the given range
61
+ if (eventOrOccurrence.endDate.isDate && (endTime > startTime)) {
62
+ endTime -= 1;
63
+ }
64
+ return { startTime, endTime };
65
+ }
66
+ const exceptions = [];
67
+ this.events.forEach((event) => {
68
+ if (event.isRecurrenceException())
69
+ exceptions.push(event);
70
+ });
71
+ const ret = {
72
+ events: [],
73
+ occurrences: [],
74
+ };
75
+ this.events.filter(e => !e.isRecurrenceException()).forEach((event) => {
76
+ const exdates = [];
77
+ event.component.getAllProperties('exdate').forEach((exdateProp) => {
78
+ const exdate = exdateProp.getFirstValue();
79
+ exdates.push(exdate.toJSDate().getTime());
80
+ });
81
+ // Recurring event is handled differently
82
+ if (event.isRecurring()) {
83
+ const iterator = event.iterator();
84
+ let next;
85
+ let i = 0;
86
+ do {
87
+ i += 1;
88
+ next = iterator.next();
89
+ if (next) {
90
+ const occurrence = event.getOccurrenceDetails(next);
91
+ const { startTime, endTime } = getTimes(occurrence);
92
+ const isOccurrenceExcluded = exdates.indexOf(startTime) !== -1;
93
+ // TODO check that within same day?
94
+ const exception = exceptions.find(ex => ex.uid === event.uid && ex.recurrenceId.toJSDate().getTime() === occurrence.startDate.toJSDate().getTime());
95
+ // We have passed the max date, stop
96
+ if (before && startTime > before.getTime())
97
+ break;
98
+ // Check that we are within our range
99
+ if (isEventWithinRange(startTime, endTime)) {
100
+ if (exception) {
101
+ ret.events.push(exception);
102
+ }
103
+ else if (!isOccurrenceExcluded) {
104
+ ret.occurrences.push(occurrence);
105
+ }
106
+ }
107
+ }
108
+ } while (next && (!this.maxIterations || i < this.maxIterations));
109
+ return;
110
+ }
111
+ // Non-recurring event:
112
+ const { startTime, endTime } = getTimes(event);
113
+ if (isEventWithinRange(startTime, endTime))
114
+ ret.events.push(event);
115
+ });
116
+ return ret;
117
+ }
118
+ before(before) {
119
+ return this.between(undefined, before);
120
+ }
121
+ after(after) {
122
+ return this.between(after);
123
+ }
124
+ all() {
125
+ return this.between();
126
+ }
127
+ }
128
+
129
+ const eventSourceDef = {
130
+ parseMeta(refined) {
131
+ if (refined.url && refined.format === 'ics') {
132
+ return {
133
+ url: refined.url,
134
+ format: 'ics',
135
+ };
136
+ }
137
+ return null;
138
+ },
139
+ fetch(arg, successCallback, errorCallback) {
140
+ let meta = arg.eventSource.meta;
141
+ let { internalState } = meta;
142
+ /*
143
+ NOTE: isRefetch is a HACK. we would do the recurring-expanding in a separate plugin hook,
144
+ but we couldn't leverage built-in allDay-guessing, among other things.
145
+ */
146
+ if (!internalState || arg.isRefetch) {
147
+ internalState = meta.internalState = {
148
+ response: null,
149
+ iCalExpanderPromise: fetch(meta.url, { method: 'GET' }).then((response) => {
150
+ return response.text().then((icsText) => {
151
+ internalState.response = response;
152
+ return new IcalExpander({
153
+ ics: icsText,
154
+ skipInvalidDates: true,
155
+ });
156
+ });
157
+ }),
158
+ };
159
+ }
160
+ internalState.iCalExpanderPromise.then((iCalExpander) => {
161
+ successCallback({
162
+ rawEvents: expandICalEvents(iCalExpander, arg.range),
163
+ response: internalState.response,
164
+ });
165
+ }, errorCallback);
166
+ },
167
+ };
168
+ function expandICalEvents(iCalExpander, range) {
169
+ // expand the range. because our `range` is timeZone-agnostic UTC
170
+ // or maybe because ical.js always produces dates in local time? i forget
171
+ let rangeStart = internal.addDays(range.start, -1);
172
+ let rangeEnd = internal.addDays(range.end, 1);
173
+ let iCalRes = iCalExpander.between(rangeStart, rangeEnd); // end inclusive. will give extra results
174
+ let expanded = [];
175
+ // TODO: instead of using startDate/endDate.toString to communicate allDay,
176
+ // we can query startDate/endDate.isDate. More efficient to avoid formatting/reparsing.
177
+ // single events
178
+ for (let iCalEvent of iCalRes.events) {
179
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalEvent.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalEvent.endDate)
180
+ ? iCalEvent.endDate.toString()
181
+ : null }));
182
+ }
183
+ // recurring event instances
184
+ for (let iCalOccurence of iCalRes.occurrences) {
185
+ let iCalEvent = iCalOccurence.item;
186
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalOccurence.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalOccurence.endDate)
187
+ ? iCalOccurence.endDate.toString()
188
+ : null }));
189
+ }
190
+ return expanded;
191
+ }
192
+ function buildNonDateProps(iCalEvent) {
193
+ return {
194
+ title: iCalEvent.summary,
195
+ url: extractEventUrl(iCalEvent),
196
+ extendedProps: {
197
+ location: iCalEvent.location,
198
+ organizer: iCalEvent.organizer,
199
+ description: iCalEvent.description,
200
+ },
201
+ };
202
+ }
203
+ function extractEventUrl(iCalEvent) {
204
+ let urlProp = iCalEvent.component.getFirstProperty('url');
205
+ return urlProp ? urlProp.getFirstValue() : '';
206
+ }
207
+ function specifiesEnd(iCalEvent) {
208
+ return Boolean(iCalEvent.component.getFirstProperty('dtend')) ||
209
+ Boolean(iCalEvent.component.getFirstProperty('duration'));
210
+ }
211
+
212
+ var plugin = core.createPlugin({
213
+ name: '@fullcalendar/icalendar',
214
+ eventSourceDefs: [eventSourceDef],
215
+ });
216
+
217
+ internal.globalPlugins.push(plugin);
218
+
219
+ exports["default"] = plugin;
220
+
221
+ Object.defineProperty(exports, '__esModule', { value: true });
222
+
223
+ return exports;
224
+
225
+ })({}, FullCalendar.Internal, FullCalendar, ICAL);
@@ -0,0 +1,6 @@
1
+ /*!
2
+ FullCalendar iCalendar Plugin v6.0.0-beta.2
3
+ Docs & License: https://fullcalendar.io/
4
+ (c) 2022 Adam Shaw
5
+ */
6
+ FullCalendar.ICalendar=function(e,t,n,r){"use strict";function a(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,t}var s=a(r);class i{constructor(e){this.maxIterations=null!=e.maxIterations?e.maxIterations:1e3,this.skipInvalidDates=null!=e.skipInvalidDates&&e.skipInvalidDates,this.jCalData=s.parse(e.ics),this.component=new s.Component(this.jCalData),this.events=this.component.getAllSubcomponents("vevent").map(e=>new s.Event(e)),this.skipInvalidDates&&(this.events=this.events.filter(e=>{try{return e.startDate.toJSDate(),e.endDate.toJSDate(),!0}catch(e){return!1}}))}between(e,t){function n(n,r){return(!e||r>=e.getTime())&&(!t||n<=t.getTime())}function r(e){const t=e.startDate.toJSDate().getTime();let n=e.endDate.toJSDate().getTime();return e.endDate.isDate&&n>t&&(n-=1),{startTime:t,endTime:n}}const a=[];this.events.forEach(e=>{e.isRecurrenceException()&&a.push(e)});const s={events:[],occurrences:[]};return this.events.filter(e=>!e.isRecurrenceException()).forEach(e=>{const i=[];if(e.component.getAllProperties("exdate").forEach(e=>{const t=e.getFirstValue();i.push(t.toJSDate().getTime())}),e.isRecurring()){const o=e.iterator();let c,l=0;do{if(l+=1,c=o.next(),c){const o=e.getOccurrenceDetails(c),{startTime:l,endTime:u}=r(o),d=-1!==i.indexOf(l),p=a.find(t=>t.uid===e.uid&&t.recurrenceId.toJSDate().getTime()===o.startDate.toJSDate().getTime());if(t&&l>t.getTime())break;n(l,u)&&(p?s.events.push(p):d||s.occurrences.push(o))}}while(c&&(!this.maxIterations||l<this.maxIterations));return}const{startTime:o,endTime:c}=r(e);n(o,c)&&s.events.push(e)}),s}before(e){return this.between(void 0,e)}after(e){return this.between(e)}all(){return this.between()}}const o={parseMeta:e=>e.url&&"ics"===e.format?{url:e.url,format:"ics"}:null,fetch(e,t,n){let r=e.eventSource.meta,{internalState:a}=r;a&&!e.isRefetch||(a=r.internalState={response:null,iCalExpanderPromise:fetch(r.url,{method:"GET"}).then(e=>e.text().then(t=>(a.response=e,new i({ics:t,skipInvalidDates:!0}))))}),a.iCalExpanderPromise.then(n=>{t({rawEvents:c(n,e.range),response:a.response})},n)}};function c(e,n){let r=t.addDays(n.start,-1),a=t.addDays(n.end,1),s=e.between(r,a),i=[];for(let e of s.events)i.push(Object.assign(Object.assign({},l(e)),{start:e.startDate.toString(),end:d(e)&&e.endDate?e.endDate.toString():null}));for(let e of s.occurrences){let t=e.item;i.push(Object.assign(Object.assign({},l(t)),{start:e.startDate.toString(),end:d(t)&&e.endDate?e.endDate.toString():null}))}return i}function l(e){return{title:e.summary,url:u(e),extendedProps:{location:e.location,organizer:e.organizer,description:e.description}}}function u(e){let t=e.component.getFirstProperty("url");return t?t.getFirstValue():""}function d(e){return Boolean(e.component.getFirstProperty("dtend"))||Boolean(e.component.getFirstProperty("duration"))}var p=n.createPlugin({name:"@fullcalendar/icalendar",eventSourceDefs:[o]});return t.globalPlugins.push(p),e.default=p,Object.defineProperty(e,"__esModule",{value:!0}),e}({},FullCalendar.Internal,FullCalendar,ICAL);
package/index.js ADDED
@@ -0,0 +1,193 @@
1
+ import { createPlugin } from '@fullcalendar/core/index.js';
2
+ import { addDays } from '@fullcalendar/core/internal.js';
3
+ import * as ICAL from 'ical.js';
4
+
5
+ /* eslint-disable */
6
+ class IcalExpander {
7
+ constructor(opts) {
8
+ this.maxIterations = opts.maxIterations != null ? opts.maxIterations : 1000;
9
+ this.skipInvalidDates = opts.skipInvalidDates != null ? opts.skipInvalidDates : false;
10
+ this.jCalData = ICAL.parse(opts.ics);
11
+ this.component = new ICAL.Component(this.jCalData);
12
+ this.events = this.component.getAllSubcomponents('vevent').map(vevent => new ICAL.Event(vevent));
13
+ if (this.skipInvalidDates) {
14
+ this.events = this.events.filter((evt) => {
15
+ try {
16
+ evt.startDate.toJSDate();
17
+ evt.endDate.toJSDate();
18
+ return true;
19
+ }
20
+ catch (err) {
21
+ // skipping events with invalid time
22
+ return false;
23
+ }
24
+ });
25
+ }
26
+ }
27
+ between(after, before) {
28
+ function isEventWithinRange(startTime, endTime) {
29
+ return (!after || endTime >= after.getTime()) &&
30
+ (!before || startTime <= before.getTime());
31
+ }
32
+ function getTimes(eventOrOccurrence) {
33
+ const startTime = eventOrOccurrence.startDate.toJSDate().getTime();
34
+ let endTime = eventOrOccurrence.endDate.toJSDate().getTime();
35
+ // If it is an all day event, the end date is set to 00:00 of the next day
36
+ // So we need to make it be 23:59:59 to compare correctly with the given range
37
+ if (eventOrOccurrence.endDate.isDate && (endTime > startTime)) {
38
+ endTime -= 1;
39
+ }
40
+ return { startTime, endTime };
41
+ }
42
+ const exceptions = [];
43
+ this.events.forEach((event) => {
44
+ if (event.isRecurrenceException())
45
+ exceptions.push(event);
46
+ });
47
+ const ret = {
48
+ events: [],
49
+ occurrences: [],
50
+ };
51
+ this.events.filter(e => !e.isRecurrenceException()).forEach((event) => {
52
+ const exdates = [];
53
+ event.component.getAllProperties('exdate').forEach((exdateProp) => {
54
+ const exdate = exdateProp.getFirstValue();
55
+ exdates.push(exdate.toJSDate().getTime());
56
+ });
57
+ // Recurring event is handled differently
58
+ if (event.isRecurring()) {
59
+ const iterator = event.iterator();
60
+ let next;
61
+ let i = 0;
62
+ do {
63
+ i += 1;
64
+ next = iterator.next();
65
+ if (next) {
66
+ const occurrence = event.getOccurrenceDetails(next);
67
+ const { startTime, endTime } = getTimes(occurrence);
68
+ const isOccurrenceExcluded = exdates.indexOf(startTime) !== -1;
69
+ // TODO check that within same day?
70
+ const exception = exceptions.find(ex => ex.uid === event.uid && ex.recurrenceId.toJSDate().getTime() === occurrence.startDate.toJSDate().getTime());
71
+ // We have passed the max date, stop
72
+ if (before && startTime > before.getTime())
73
+ break;
74
+ // Check that we are within our range
75
+ if (isEventWithinRange(startTime, endTime)) {
76
+ if (exception) {
77
+ ret.events.push(exception);
78
+ }
79
+ else if (!isOccurrenceExcluded) {
80
+ ret.occurrences.push(occurrence);
81
+ }
82
+ }
83
+ }
84
+ } while (next && (!this.maxIterations || i < this.maxIterations));
85
+ return;
86
+ }
87
+ // Non-recurring event:
88
+ const { startTime, endTime } = getTimes(event);
89
+ if (isEventWithinRange(startTime, endTime))
90
+ ret.events.push(event);
91
+ });
92
+ return ret;
93
+ }
94
+ before(before) {
95
+ return this.between(undefined, before);
96
+ }
97
+ after(after) {
98
+ return this.between(after);
99
+ }
100
+ all() {
101
+ return this.between();
102
+ }
103
+ }
104
+
105
+ const eventSourceDef = {
106
+ parseMeta(refined) {
107
+ if (refined.url && refined.format === 'ics') {
108
+ return {
109
+ url: refined.url,
110
+ format: 'ics',
111
+ };
112
+ }
113
+ return null;
114
+ },
115
+ fetch(arg, successCallback, errorCallback) {
116
+ let meta = arg.eventSource.meta;
117
+ let { internalState } = meta;
118
+ /*
119
+ NOTE: isRefetch is a HACK. we would do the recurring-expanding in a separate plugin hook,
120
+ but we couldn't leverage built-in allDay-guessing, among other things.
121
+ */
122
+ if (!internalState || arg.isRefetch) {
123
+ internalState = meta.internalState = {
124
+ response: null,
125
+ iCalExpanderPromise: fetch(meta.url, { method: 'GET' }).then((response) => {
126
+ return response.text().then((icsText) => {
127
+ internalState.response = response;
128
+ return new IcalExpander({
129
+ ics: icsText,
130
+ skipInvalidDates: true,
131
+ });
132
+ });
133
+ }),
134
+ };
135
+ }
136
+ internalState.iCalExpanderPromise.then((iCalExpander) => {
137
+ successCallback({
138
+ rawEvents: expandICalEvents(iCalExpander, arg.range),
139
+ response: internalState.response,
140
+ });
141
+ }, errorCallback);
142
+ },
143
+ };
144
+ function expandICalEvents(iCalExpander, range) {
145
+ // expand the range. because our `range` is timeZone-agnostic UTC
146
+ // or maybe because ical.js always produces dates in local time? i forget
147
+ let rangeStart = addDays(range.start, -1);
148
+ let rangeEnd = addDays(range.end, 1);
149
+ let iCalRes = iCalExpander.between(rangeStart, rangeEnd); // end inclusive. will give extra results
150
+ let expanded = [];
151
+ // TODO: instead of using startDate/endDate.toString to communicate allDay,
152
+ // we can query startDate/endDate.isDate. More efficient to avoid formatting/reparsing.
153
+ // single events
154
+ for (let iCalEvent of iCalRes.events) {
155
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalEvent.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalEvent.endDate)
156
+ ? iCalEvent.endDate.toString()
157
+ : null }));
158
+ }
159
+ // recurring event instances
160
+ for (let iCalOccurence of iCalRes.occurrences) {
161
+ let iCalEvent = iCalOccurence.item;
162
+ expanded.push(Object.assign(Object.assign({}, buildNonDateProps(iCalEvent)), { start: iCalOccurence.startDate.toString(), end: (specifiesEnd(iCalEvent) && iCalOccurence.endDate)
163
+ ? iCalOccurence.endDate.toString()
164
+ : null }));
165
+ }
166
+ return expanded;
167
+ }
168
+ function buildNonDateProps(iCalEvent) {
169
+ return {
170
+ title: iCalEvent.summary,
171
+ url: extractEventUrl(iCalEvent),
172
+ extendedProps: {
173
+ location: iCalEvent.location,
174
+ organizer: iCalEvent.organizer,
175
+ description: iCalEvent.description,
176
+ },
177
+ };
178
+ }
179
+ function extractEventUrl(iCalEvent) {
180
+ let urlProp = iCalEvent.component.getFirstProperty('url');
181
+ return urlProp ? urlProp.getFirstValue() : '';
182
+ }
183
+ function specifiesEnd(iCalEvent) {
184
+ return Boolean(iCalEvent.component.getFirstProperty('dtend')) ||
185
+ Boolean(iCalEvent.component.getFirstProperty('duration'));
186
+ }
187
+
188
+ var index = createPlugin({
189
+ name: '@fullcalendar/icalendar',
190
+ eventSourceDefs: [eventSourceDef],
191
+ });
192
+
193
+ export { index as default };