import { Options , ParsedOptions , freqIsDailyOrGreater } from './types' ; import { includes , notEmpty , isPresent , isNumber , isArray , isWeekdayStr } from './helpers' ; import RRule , { defaultKeys , DEFAULT_OPTIONS } from './rrule' ; import dateutil from './dateutil' ; import { Weekday } from './weekday' ; import { Time } from './datetime' ; export function initializeOptions ( options ) { const invalid = [ ] ; const keys = Object . keys ( options ) as ( keyof Options ) [ ] ; for ( const key of keys ) { if ( ! includes ( defaultKeys , key ) ) invalid . push ( key ) ; if ( dateutil . isDate ( options [ key ] ) && ! dateutil . isValidDate ( options [ key ] ) ) invalid . push ( key ) ; } if ( invalid . length ) { throw new Error ( 'Invalid options: ' + invalid . join ( ', ' ) ) ; } return { ... options } ; } export function parseOptions ( options ) { const opts = { ... DEFAULT_OPTIONS , ... initializeOptions ( options ) } ; if ( isPresent ( opts . byeaster ) ) opts . freq = RRule . YEARLY ; if ( ! ( isPresent ( opts . freq ) && RRule . FREQUENCIES [ opts . freq ] ) ) { throw new Error ( `Invalid frequency: ${ opts . freq } ${ options . freq }` ) ; } if ( ! opts . dtstart ) opts . dtstart = new Date ( new Date ( ) . setMilliseconds ( 0 ) ) ; if ( ! isPresent ( opts . wkst ) ) { opts . wkst = RRule . MO . weekday ; } else if ( isNumber ( opts . wkst ) ) { } else { opts . wkst = opts . wkst . weekday ; } if ( isPresent ( opts . bysetpos ) ) { if ( isNumber ( opts . bysetpos ) ) opts . bysetpos = [ opts . bysetpos ] ; for ( let i = 0 ; i < opts . bysetpos . length ; i ++ ) { const v = opts . bysetpos [ i ] ; if ( v === 0 || ! ( v >= - 366 && v <= 366 ) ) { throw new Error ( 'bysetpos must be between 1 and 366,' + ' or between -366 and -1' ) ; } } } if ( ! ( Boolean ( opts . byweekno as number ) || notEmpty ( opts . byweekno as number [ ] ) || notEmpty ( opts . byyearday as number [ ] ) || Boolean ( opts . bymonthday ) || notEmpty ( opts . bymonthday as number [ ] ) || isPresent ( opts . byweekday ) || isPresent ( opts . byeaster ) ) ) { switch ( opts . freq ) { case RRule . YEARLY : if ( ! opts . bymonth ) opts . bymonth = opts . dtstart . getUTCMonth ( ) + 1 ; opts . bymonthday = opts . dtstart . getUTCDate ( ) ; break ; case RRule . MONTHLY : opts . bymonthday = opts . dtstart . getUTCDate ( ) ; break ; case RRule . WEEKLY : opts . byweekday = [ dateutil . getWeekday ( opts . dtstart ) ] ; break ; } } if ( isPresent ( opts . bymonth ) && ! isArray ( opts . bymonth ) ) { opts . bymonth = [ opts . bymonth ] ; } if ( isPresent ( opts . byyearday ) && ! isArray ( opts . byyearday ) && isNumber ( opts . byyearday ) ) { opts . byyearday = [ opts . byyearday ] ; } if ( ! isPresent ( opts . bymonthday ) ) { opts . bymonthday = [ ] ; opts . bynmonthday = [ ] ; } else if ( isArray ( opts . bymonthday ) ) { const bymonthday = [ ] ; const bynmonthday = [ ] ; for ( let i = 0 ; i < opts . bymonthday . length ; i ++ ) { const v = opts . bymonthday [ i ] ; if ( v > 0 ) { bymonthday . push ( v ) ; } else if ( v < 0 ) { bynmonthday . push ( v ) ; } } opts . bymonthday = bymonthday ; opts . bynmonthday = bynmonthday ; } else if ( opts . bymonthday < 0 ) { opts . bynmonthday = [ opts . bymonthday ] ; opts . bymonthday = [ ] ; } else { opts . bynmonthday = [ ] ; opts . bymonthday = [ opts . bymonthday ] ; } if ( isPresent ( opts . byweekno ) && ! isArray ( opts . byweekno ) ) { opts . byweekno = [ opts . byweekno ] ; } if ( ! isPresent ( opts . byweekday ) ) { opts . bynweekday = null ; } else if ( isNumber ( opts . byweekday ) ) { opts . byweekday = [ opts . byweekday ] ; opts . bynweekday = null ; } else if ( isWeekdayStr ( opts . byweekday ) ) { opts . byweekday = [ Weekday . fromStr ( opts . byweekday ) . weekday ] ; opts . bynweekday = null ; } else if ( opts . byweekday instanceof Weekday ) { if ( ! opts . byweekday . n || opts . freq > RRule . MONTHLY ) { opts . byweekday = [ opts . byweekday . weekday ] ; opts . bynweekday = null ; } else { opts . bynweekday = [ [ opts . byweekday . weekday , opts . byweekday . n ] ] ; opts . byweekday = null ; } } else { const byweekday = [ ] ; const bynweekday = [ ] ; for ( let i = 0 ; i < opts . byweekday . length ; i ++ ) { const wday = opts . byweekday [ i ] ; if ( isNumber ( wday ) ) { byweekday . push ( wday ) ; continue ; } else if ( isWeekdayStr ( wday ) ) { byweekday . push ( Weekday . fromStr ( wday ) . weekday ) ; continue ; } if ( ! wday . n || opts . freq > RRule . MONTHLY ) { byweekday . push ( wday . weekday ) ; } else { bynweekday . push ( [ wday . weekday , wday . n ] ) ; } } opts . byweekday = notEmpty ( byweekday ) ? byweekday : null ; opts . bynweekday = notEmpty ( bynweekday ) ? bynweekday : null ; } if ( ! isPresent ( opts . byhour ) ) { opts . byhour = opts . freq < RRule . HOURLY ? [ opts . dtstart . getUTCHours ( ) ] : null ; } else if ( isNumber ( opts . byhour ) ) { opts . byhour = [ opts . byhour ] ; } if ( ! isPresent ( opts . byminute ) ) { opts . byminute = opts . freq < RRule . MINUTELY ? [ opts . dtstart . getUTCMinutes ( ) ] : null ; } else if ( isNumber ( opts . byminute ) ) { opts . byminute = [ opts . byminute ] ; } if ( ! isPresent ( opts . bysecond ) ) { opts . bysecond = opts . freq < RRule . SECONDLY ? [ opts . dtstart . getUTCSeconds ( ) ] : null ; } else if ( isNumber ( opts . bysecond ) ) { opts . bysecond = [ opts . bysecond ] ; } return { parsedOptions : opts as ParsedOptions } ; } export function buildTimeset ( opts ) { const millisecondModulo = opts . dtstart . getTime ( ) % 1000 ; if ( ! freqIsDailyOrGreater ( opts . freq ) ) { return [ ] ; } const timeset = [ ] ; opts . byhour . forEach ( hour => { opts . byminute . forEach ( minute => { opts . bysecond . forEach ( second => { timeset . push ( new Time ( hour , minute , second , millisecondModulo ) ) ; } ) ; } ) ; } ) ; return timeset ; }