import ENGLISH , { Language } from './i18n' ; import RRule from '../index' ; import { Options , ByWeekday } from '../types' ; import { Weekday } from '../weekday' ; import { isArray , isNumber , isPresent , padStart } from '../helpers' ; /**
 * Return true if a value is in an array
 */ const contains = function ( arr , val ) { return arr . indexOf ( val ) !== - 1 ; } ; export type GetText = ( id : string | number | Weekday ) => string ; const defaultGetText = id => id . toString ( ) ; export type DateFormatter = ( year : number , month : string , day : number ) => string ; const defaultDateFormatter = ( year , month , day ) => `${ month } ${ day }, ${ year }` ; param { RRule } rrule param { Function } gettext param { Object } language constructor export default class ToText { static IMPLEMENTED : string [ ] [ ] ; private rrule : RRule ; private text : string [ ] ; private gettext : GetText ; private dateFormatter : DateFormatter ; private language : Language ; private options : Partial < Options > ; private origOptions : Partial < Options > ; private bymonthday : Options [ 'bymonthday' ] | null ; private byweekday : { allWeeks : ByWeekday [ ] | null ; someWeeks : ByWeekday [ ] | null ; isWeekdays : boolean ; isEveryDay : boolean ; } | null ; constructor ( rrule , gettext = defaultGetText , language = ENGLISH , dateFormatter = defaultDateFormatter ) { this . text = [ ] ; this . language = language || ENGLISH ; this . gettext = gettext ; this . dateFormatter = dateFormatter ; this . rrule = rrule ; this . options = rrule . options ; this . origOptions = rrule . origOptions ; if ( this . origOptions . bymonthday ) { const bymonthday = ( [ ] as number [ ] ) . concat ( this . options . bymonthday ! ) ; const bynmonthday = ( [ ] as number [ ] ) . concat ( this . options . bynmonthday ! ) ; bymonthday . sort ( ( a , b ) => a - b ) ; bynmonthday . sort ( ( a , b ) => b - a ) ; this . bymonthday = bymonthday . concat ( bynmonthday ) ; if ( ! this . bymonthday . length ) this . bymonthday = null ; } if ( isPresent ( this . origOptions . byweekday ) ) { const byweekday = ! isArray ( this . origOptions . byweekday ) ? [ this . origOptions . byweekday ] : this . origOptions . byweekday ; const days = String ( byweekday ) ; this . byweekday = { allWeeks : byweekday . filter ( function ( weekday ) { return ! weekday . n ; } ) , someWeeks : byweekday . filter ( function ( weekday ) { return Boolean ( weekday . n ) ; } ) , isWeekdays : days . indexOf ( 'MO' ) !== - 1 && days . indexOf ( 'TU' ) !== - 1 && days . indexOf ( 'WE' ) !== - 1 && days . indexOf ( 'TH' ) !== - 1 && days . indexOf ( 'FR' ) !== - 1 && days . indexOf ( 'SA' ) === - 1 && days . indexOf ( 'SU' ) === - 1 , isEveryDay : days . indexOf ( 'MO' ) !== - 1 && days . indexOf ( 'TU' ) !== - 1 && days . indexOf ( 'WE' ) !== - 1 && days . indexOf ( 'TH' ) !== - 1 && days . indexOf ( 'FR' ) !== - 1 && days . indexOf ( 'SA' ) !== - 1 && days . indexOf ( 'SU' ) !== - 1 } ; const sortWeekDays = function ( a , b ) { return a . weekday - b . weekday ; } ; this . byweekday . allWeeks ! . sort ( sortWeekDays ) ; this . byweekday . someWeeks ! . sort ( sortWeekDays ) ; if ( ! this . byweekday . allWeeks ! . length ) this . byweekday . allWeeks = null ; if ( ! this . byweekday . someWeeks ! . length ) this . byweekday . someWeeks = null ; } else { this . byweekday = null ; } } param { RRule } rrule return { Boolean } static isFullyConvertible ( rrule ) { let canConvert = true ; if ( ! ( rrule . options . freq in ToText . IMPLEMENTED ) ) return false ; if ( rrule . origOptions . until && rrule . origOptions . count ) return false ; for ( let key in rrule . origOptions ) { if ( contains ( [ 'dtstart' , 'wkst' , 'freq' ] , key ) ) return true ; if ( ! contains ( ToText . IMPLEMENTED [ rrule . options . freq ] , key ) ) return false ; } return canConvert ; } isFullyConvertible ( ) { return ToText . isFullyConvertible ( this . rrule ) ; } return { * } toString ( ) { const gettext = this . gettext ; if ( ! ( this . options . freq ! in ToText . IMPLEMENTED ) ) { return gettext ( 'RRule error: Unable to fully convert this rrule to text' ) ; } this . text = [ gettext ( 'every' ) ] ; this [ RRule . FREQUENCIES [ this . options . freq ] ] ( ) ; if ( this . options . until ) { this . add ( gettext ( 'until' ) ) ; const until = this . options . until ; this . add ( this . dateFormatter ( until . getUTCFullYear ( ) , this . language . monthNames [ until . getUTCMonth ( ) ] , until . getUTCDate ( ) ) ) ; } else if ( this . options . count ) { this . add ( gettext ( 'for' ) ) . add ( this . options . count . toString ( ) ) . add ( this . plural ( this . options . count ) ? gettext ( 'times' ) : gettext ( 'time' ) ) ; } if ( ! this . isFullyConvertible ( ) ) this . add ( gettext ( '(~ approximate)' ) ) ; return this . text . join ( '' ) ; } HOURLY ( ) { const gettext = this . gettext ; if ( this . options . interval !== 1 ) this . add ( this . options . interval ! . toString ( ) ) ; this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'hours' ) : gettext ( 'hour' ) ) ; } MINUTELY ( ) { const gettext = this . gettext ; if ( this . options . interval !== 1 ) this . add ( this . options . interval ! . toString ( ) ) ; this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'minutes' ) : gettext ( 'minute' ) ) ; } DAILY ( ) { const gettext = this . gettext ; if ( this . options . interval !== 1 ) this . add ( this . options . interval ! . toString ( ) ) ; if ( this . byweekday && this . byweekday . isWeekdays ) { this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'weekdays' ) : gettext ( 'weekday' ) ) ; } else { this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'days' ) : gettext ( 'day' ) ) ; } if ( this . origOptions . bymonth ) { this . add ( gettext ( 'in' ) ) ; this . _bymonth ( ) ; } if ( this . bymonthday ) { this . _bymonthday ( ) ; } else if ( this . byweekday ) { this . _byweekday ( ) ; } else if ( this . origOptions . byhour ) { this . _byhour ( ) ; } } WEEKLY ( ) { const gettext = this . gettext ; if ( this . options . interval !== 1 ) { this . add ( this . options . interval ! . toString ( ) ) . add ( this . plural ( this . options . interval ! ) ? gettext ( 'weeks' ) : gettext ( 'week' ) ) ; } if ( this . byweekday && this . byweekday . isWeekdays ) { if ( this . options . interval === 1 ) { this . add ( this . plural ( this . options . interval ) ? gettext ( 'weekdays' ) : gettext ( 'weekday' ) ) ; } else { this . add ( gettext ( 'on' ) ) . add ( gettext ( 'weekdays' ) ) ; } } else if ( this . byweekday && this . byweekday . isEveryDay ) { this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'days' ) : gettext ( 'day' ) ) ; } else { if ( this . options . interval === 1 ) this . add ( gettext ( 'week' ) ) ; if ( this . origOptions . bymonth ) { this . add ( gettext ( 'in' ) ) ; this . _bymonth ( ) ; } if ( this . bymonthday ) { this . _bymonthday ( ) ; } else if ( this . byweekday ) { this . _byweekday ( ) ; } } } MONTHLY ( ) { const gettext = this . gettext ; if ( this . origOptions . bymonth ) { if ( this . options . interval !== 1 ) { this . add ( this . options . interval ! . toString ( ) ) . add ( gettext ( 'months' ) ) ; if ( this . plural ( this . options . interval ! ) ) this . add ( gettext ( 'in' ) ) ; } else { } this . _bymonth ( ) ; } else { if ( this . options . interval !== 1 ) this . add ( this . options . interval ! . toString ( ) ) ; this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'months' ) : gettext ( 'month' ) ) ; } if ( this . bymonthday ) { this . _bymonthday ( ) ; } else if ( this . byweekday && this . byweekday . isWeekdays ) { this . add ( gettext ( 'on' ) ) . add ( gettext ( 'weekdays' ) ) ; } else if ( this . byweekday ) { this . _byweekday ( ) ; } } YEARLY ( ) { const gettext = this . gettext ; if ( this . origOptions . bymonth ) { if ( this . options . interval !== 1 ) { this . add ( this . options . interval ! . toString ( ) ) ; this . add ( gettext ( 'years' ) ) ; } else { } this . _bymonth ( ) ; } else { if ( this . options . interval !== 1 ) this . add ( this . options . interval ! . toString ( ) ) ; this . add ( this . plural ( this . options . interval ! ) ? gettext ( 'years' ) : gettext ( 'year' ) ) ; } if ( this . bymonthday ) { this . _bymonthday ( ) ; } else if ( this . byweekday ) { this . _byweekday ( ) ; } if ( this . options . byyearday ) { this . add ( gettext ( 'on the' ) ) . add ( this . list ( this . options . byyearday , this . nth , gettext ( 'and' ) ) ) . add ( gettext ( 'day' ) ) ; } if ( this . options . byweekno ) { this . add ( gettext ( 'in' ) ) . add ( this . plural ( ( this . options . byweekno as number [ ] ) . length ) ? gettext ( 'weeks' ) : gettext ( 'week' ) ) . add ( this . list ( this . options . byweekno , undefined , gettext ( 'and' ) ) ) ; } } private _bymonthday ( ) { const gettext = this . gettext ; if ( this . byweekday && this . byweekday . allWeeks ) { this . add ( gettext ( 'on' ) ) . add ( this . list ( this . byweekday . allWeeks , this . weekdaytext , gettext ( 'or' ) ) ) . add ( gettext ( 'the' ) ) . add ( this . list ( this . bymonthday ! , this . nth , gettext ( 'or' ) ) ) ; } else { this . add ( gettext ( 'on the' ) ) . add ( this . list ( this . bymonthday ! , this . nth , gettext ( 'and' ) ) ) ; } } private _byweekday ( ) { const gettext = this . gettext ; if ( this . byweekday ! . allWeeks && ! this . byweekday ! . isWeekdays ) { this . add ( gettext ( 'on' ) ) . add ( this . list ( this . byweekday ! . allWeeks , this . weekdaytext ) ) ; } if ( this . byweekday ! . someWeeks ) { if ( this . byweekday ! . allWeeks ) this . add ( gettext ( 'and' ) ) ; this . add ( gettext ( 'on the' ) ) . add ( this . list ( this . byweekday ! . someWeeks , this . weekdaytext , gettext ( 'and' ) ) ) ; } } private _byhour ( ) { const gettext = this . gettext ; this . add ( gettext ( 'at' ) ) . add ( this . list ( this . origOptions . byhour ! , undefined , gettext ( 'and' ) ) ) ; } private _bymonth ( ) { this . add ( this . list ( this . options . bymonth ! , this . monthtext , this . gettext ( 'and' ) ) ) ; } nth ( n ) { n = parseInt ( n . toString ( ) , 10 ) ; let nth ; let npos ; const gettext = this . gettext ; if ( n === - 1 ) return gettext ( 'last' ) ; npos = Math . abs ( n ) ; switch ( npos ) { case 1 : case 21 : case 31 : nth = npos + gettext ( 'st' ) ; break ; case 2 : case 22 : nth = npos + gettext ( 'nd' ) ; break ; case 3 : case 23 : nth = npos + gettext ( 'rd' ) ; break ; default : nth = npos + gettext ( 'th' ) ; } return n < 0 ? nth + ' ' + gettext ( 'last' ) : nth ; } monthtext ( m ) { return this . language . monthNames [ m - 1 ] ; } weekdaytext ( wday ) { const weekday = isNumber ( wday ) ? ( wday + 1 ) % 7 : wday . getJsWeekday ( ) ; return ( ( ( wday as Weekday ) . n ? this . nth ( ( wday as Weekday ) . n ! ) + ' ' : '' ) + this . language . dayNames [ weekday ] ) ; } plural ( n ) { return n % 100 !== 1 ; } add ( s ) { this . text . push ( ' ' ) ; this . text . push ( s ) ; return this ; } list ( arr , callback ? , finalDelim ? , delim = ',' ) { if ( ! isArray ( arr ) ) { arr = [ arr ] ; } const delimJoin = function ( array , delimiter , finalDelimiter ) { let list = '' ; for ( let i = 0 ; i < array . length ; i ++ ) { if ( i !== 0 ) { if ( i === array . length - 1 ) { list += ' ' + finalDelimiter + ' ' ; } else { list += delimiter + ' ' ; } } list += array [ i ] ; } return list ; } ; callback = callback || function ( o ) { return o . toString ( ) ; } ; const self = this ; const realCallback = function ( arg ) { return callback && callback . call ( self , arg ) ; } ; if ( finalDelim ) { return delimJoin ( arr . map ( realCallback ) , delim , finalDelim ) ; } else { return arr . map ( realCallback ) . join ( delim + ' ' ) ; } } }