11const Log = require ( "logger" ) ;
22const moment = require ( "moment-timezone" ) ;
3- const IcalExpander = require ( "ical-expander " ) ;
3+ const ICAL = require ( "ical.js " ) ;
44
55const NodeHelper = require ( "node_helper" ) ;
66
@@ -118,6 +118,101 @@ module.exports = NodeHelper.create({
118118 }
119119 } ,
120120
121+ /**
122+ * Parse iCal data and expand recurring events using ical.js
123+ * @param {string } iCalData - The iCal data string
124+ * @param {Date } startDate - Start date for event range
125+ * @param {Date } endDate - End date for event range
126+ * @param {number } maxIterations - Maximum iterations for recurring events
127+ * @returns {Object } Object containing events and occurrences arrays
128+ */
129+ parseAndExpandEvents ( iCalData , startDate , endDate , maxIterations = 1000 ) {
130+ let jcalData ;
131+ try {
132+ jcalData = ICAL . parse ( iCalData ) ;
133+ } catch ( error ) {
134+ throw new Error ( `Failed to parse iCal data: ${ error . message } ` ) ;
135+ }
136+
137+ const comp = new ICAL . Component ( jcalData ) ;
138+ const vevents = comp . getAllSubcomponents ( "vevent" ) ;
139+
140+ const events = [ ] ;
141+ const occurrences = [ ] ;
142+
143+ for ( const vevent of vevents ) {
144+ const event = new ICAL . Event ( vevent ) ;
145+
146+ if ( event . isRecurring ( ) ) {
147+ // Handle recurring events
148+ const iterator = event . iterator ( ) ;
149+ let count = 0 ;
150+ let occurrence ;
151+
152+ while ( ( occurrence = iterator . next ( ) ) && count < maxIterations ) {
153+ const occurrenceStartDate = occurrence . toJSDate ( ) ;
154+ const occurrenceEndDate = new Date ( occurrenceStartDate . getTime ( ) + event . duration . toSeconds ( ) * 1000 ) ;
155+
156+ // Check if this occurrence falls within our date range
157+ if ( occurrenceEndDate >= startDate && occurrenceStartDate <= endDate ) {
158+ occurrences . push ( {
159+ startDate : {
160+ toJSDate : ( ) => occurrenceStartDate
161+ } ,
162+ endDate : {
163+ toJSDate : ( ) => occurrenceEndDate
164+ } ,
165+ item : {
166+ summary : event . summary ,
167+ location : event . location ,
168+ description : event . description ,
169+ uid : event . uid ,
170+ isRecurring : ( ) => true ,
171+ duration : event . duration
172+ } ,
173+ component : vevent
174+ } ) ;
175+ }
176+
177+ count += 1 ;
178+
179+ // Stop if we've gone past our end date
180+ if ( occurrenceStartDate > endDate ) {
181+ break ;
182+ }
183+ }
184+ } else {
185+ // Handle single events
186+ const eventStartDate = event . startDate . toJSDate ( ) ;
187+ const eventEndDate = event . endDate . toJSDate ( ) ;
188+
189+ // Check if this event falls within our date range
190+ if ( eventEndDate >= startDate && eventStartDate <= endDate ) {
191+ events . push ( {
192+ startDate : {
193+ toJSDate : ( ) => eventStartDate
194+ } ,
195+ endDate : {
196+ toJSDate : ( ) => eventEndDate
197+ } ,
198+ summary : event . summary ,
199+ location : event . location ,
200+ description : event . description ,
201+ uid : event . uid ,
202+ isRecurring : ( ) => false ,
203+ duration : event . duration ,
204+ component : vevent
205+ } ) ;
206+ }
207+ }
208+ }
209+
210+ return {
211+ events,
212+ occurrences
213+ } ;
214+ } ,
215+
121216 parser ( calendar , iCalData = null , error = null ) {
122217 if ( error ) {
123218 Log . error ( `[CALEXT2] calendar:${ calendar . name } >> Error: ${ error . message || error } ` ) ;
@@ -127,22 +222,17 @@ module.exports = NodeHelper.create({
127222 Log . log ( `[CALEXT2] calendar:${ calendar . name } >> No data to fetch` ) ;
128223 return ;
129224 }
130- let icalExpander ;
131- try {
132- icalExpander = new IcalExpander ( {
133- ics : iCalData ,
134- maxIterations : calendar . maxIterations
135- } ) ;
136- } catch ( e ) {
137- Log . log ( `[CALEXT2] calendar:${ calendar . name } >> ${ e . message } ` ) ;
138- return ;
139- }
140225
141226 let events ;
142227 try {
143- events = icalExpander . between (
144- moment ( ) . subtract ( calendar . beforeDays , "days" ) . startOf ( "day" ) . toDate ( ) ,
145- moment ( ) . add ( calendar . afterDays , "days" ) . endOf ( "day" ) . toDate ( )
228+ const startDate = moment ( ) . subtract ( calendar . beforeDays , "days" ) . startOf ( "day" ) . toDate ( ) ;
229+ const endDate = moment ( ) . add ( calendar . afterDays , "days" ) . endOf ( "day" ) . toDate ( ) ;
230+
231+ events = this . parseAndExpandEvents (
232+ iCalData ,
233+ startDate ,
234+ endDate ,
235+ calendar . maxIterations
146236 ) ;
147237 } catch ( e ) {
148238 Log . log ( `[CALEXT2] calendar:${ calendar . name } >> ${ e . message } ` ) ;
0 commit comments