recurrences.js
Sharecare is an online application for behavioral healthcare programs and providers. It includes access, clinical, fiscal, and reporting subsystems.
The Scheduler component keeps track of todo items, availability, scheduled events, and clinical appointments. Its functionality is similar to the Outlook calendar, but with extended availability and overlap definitions, more complex recurrence patterns, and both clinical and fiscal validations.
This file contains much of the logic for rendering recurrences.
Some of the methods called aren't part of the default JavaScript
classes (such as "Date.after( Date )").
These were added to the base classes with function signatures
which exactly match those in Java.
The code in this file, after an automated transformation,
will be reused in a Java program which will extend the
rendering of recurrences further into the future.
// Common JavaScript routines that help with recurrences.
// Methods:
// RecurrencePattern( recur_unit, recur_freq, offset_unit, offset_count, start_date, end_date )
// RenderRecurrenceIntoDateList( recurrence_obj )
// BuildRecurrenceDescription( recurrence_obj )
// Recurrences come in four flavors (the 'recur_unit'):
// 'day':
// Recurs every 'recur_freq' days.
// 'weekday':
// Recurs every 'recur_freq' weekdays (skipping over weekends).
// 'week':
// Recurs every 'recur_freq' weeks on a particular day of the week
// (given by 'offset_unit').
// 'month':
// Recurs every 'recur_freq' months on a particular day.
// 'offset_freq' tells whether to count from the beginning or the end of the month.
// 'offset_unit' gives the number of units in that direction; it can be 'day' or 'weekday',
// or any named day of the week (as with weekly recurrences).
// C-style "#ifndef/#define" code to ensure this isn't loaded twice
if ( ! document.LOADED_RECURRENCES ) {
document.LOADED_RECURRENCES = true;
// Constructor for the RecurrencePattern object.
function RecurrencePattern( recur_unit, recur_freq, offset_unit, offset_count, start_date, end_date )
{
this.recur_unit = recur_unit;
this.recur_freq = recur_freq;
this.offset_unit = offset_unit;
this.offset_count = offset_count;
this.start_date = start_date;
this.end_date = end_date;
}
// Return the list of dates for the given recurrence pattern.
// Note: some of the syntax (if/else instead of switch, double quotes)
// looks quaint, but was chosen for maximal ease of portability to Java.
function RenderRecurrenceIntoDateList( recurrence_obj, today_date )
{
var next_date, next_month;
var move_by, moved_by;
var direction
var date_list = "";
var r = recurrence_obj;
// based on the recurrence unit...
if ( r.recur_unit.equals( "day" ) )
{
// start with the specified day
next_date = r.start_date;
// for all possible dates...
while ( ! next_date.after( r.end_date ) )
{
// add this date (if in the future)
if ( ! today_date.after( next_date ) )
date_list = ListAppend( date_list, next_date.toCommonString() );
// advance N days
next_date.add( "day", r.recur_freq );
}
}
else if ( r.recur_unit.equals( "weekday" ) )
{
// start with the first weekday on or after the start
next_date = r.start_date;
while ( ! next_date.isWeekday() )
next_date.add( "day", 1 );
// for all possible dates...
while ( ! next_date.after( r.end_date ) )
{
// add this date (if in the future)
if ( ! today_date.after( next_date ) )
date_list = ListAppend( date_list, next_date.toCommonString() );
// advance N weekdays
moved_by = 0;
while ( moved_by < r.recur_freq )
{
next_date.add( "day", 1 );
// skip over any weekend days
while ( ! next_date.isWeekday() )
next_date.add( "day", 1 );
moved_by++;
}
}
}
else if ( r.recur_unit.equals( "week" ) )
{
// start with the first XXXday on or after the start
next_date = r.start_date;
while ( ! r.offset_unit.equals( next_date.dayOfWeekAsString( ) ) )
next_date.add( "day", 1 );
// for all possible dates...
while ( ! next_date.after( r.end_date ) )
{
// add this date (if in the future)
if ( ! today_date.after( next_date ) )
date_list = ListAppend( date_list, next_date.toCommonString() );
// advance N weeks
next_date.add( "week", r.recur_freq );
}
}
else if ( r.recur_unit.equals( "month" ) )
{
// separate the direction from the units
direction = 1
if ( r.offset_count > 0 )
{
direction = 1
move_by = r.offset_count - 1;
}
else
{
direction = -1
move_by = -r.offset_count - 1;
}
// start with the first day of the starting month
next_month = new Date( r.start_date );
next_month.firstOfTheMonth();
// for all possible dates...
while ( ! next_month.after( r.end_date ) )
{
// start at the appropriate end of the month
next_date = new Date( next_month );
if ( direction > 0 )
; // beginning of the month is just fine
else
{
// start at the end of the month instead
next_date.lastOfTheMonth();
}
// apply the appropriate offset
if ( r.offset_unit.equals( "day" ) )
{
next_date.add( "day", direction * move_by );
}
else if ( r.offset_unit.equals( "weekday" ) )
{
// start at the first weekday
while ( ! next_date.isWeekday() )
next_date.add( "day", direction );
// move the appropriate number of weekdays
moved_by = 0;
while ( moved_by < move_by )
{
next_date.add( "day", direction );
// skip over any weekend days
while ( ! next_date.isWeekday() )
next_date.add( "day", direction );
moved_by++;
}
}
else // it's a day of the week
{
// move to the appropriate day
while ( ! r.offset_unit.equals( next_date.dayOfWeekAsString() ) )
next_date.add( "day", direction );
// then shift by N weeks
next_date.add( "week", direction * move_by );
}
// if this date is still in this month, and in range, and not in the past, add this date
if ( next_date.getMonth() == next_month.getMonth() )
if ( ! ( next_date.before( r.start_date ) || next_date.after( r.end_date ) ) )
if ( ! today_date.after( next_date ) )
date_list = ListAppend( date_list, next_date.toCommonString() );
// advance N months
next_month.add( "month", r.recur_freq );
}
}
return date_list;
}
// Return a description of the given recurrence pattern.
function BuildRecurrenceDescription( recurrence_obj )
{
var desc;
var r = recurrence_obj;
switch ( r.recur_unit )
{
case 'day':
desc = NumberToFrequency( r.recur_freq ) + ' day';
break;
case 'weekday':
desc = NumberToFrequency( r.recur_freq ) + ' weekday';
break;
case 'week':
desc = NumberToFrequency( r.recur_freq ) + ' ' + r.offset_unit;
break;
case 'month':
// number of units into the month
desc = 'the ' + NumberToOrdinal( Math.abs( r.offset_count ) ) + ' ' + r.offset_unit;
// offset sign
desc += ' from the ' + ( ( r.offset_count < 0 ) ? 'end' : 'beginning' ) + ' of';
// month frequency
desc += ' ' + NumberToFrequency( r.recur_freq ) + ' month';
break;
default:
alert( 'Coding error: unknown recurrence unit ' + r.recur_unit + '!' );
return;
}
// date range (possibly open-ended)
if ( r.end_date != '' )
desc += ', between ' + r.start_date.toCommonString() + ' and ' + r.end_date.toCommonString();
else
desc += ', from ' + r.start_date.toCommonString() + ' on';
return desc;
}
// Return a string which is the list with the new value appended.
// Compatibility method: signature is equivalent to Cold Fusion.
function ListAppend( list, value, delimiters )
{
// default delimiter is a comma
if ( ! delimiters )
delimiters = ',';
// the first delimiter specified will be used
if ( delimiters.length > 1 )
delimiters = delimiters.charAt( 0 );
// append the new value to the list
if ( ( ! list ) || ( ! list.length ) || ( list.length == 0 ) )
list = value;
else
list = list + delimiters + value;
return list;
}
// Return true if two strings are identical.
// Compatibility method: signature is equivalent to Java.
new String();
String.prototype.equals = function( other_string )
{
return ( this == other_string );
}
// Return the ordinal form of a number.
function NumberToOrdinal( num )
{
var small_ordinals = [ 'zeroth', 'first', 'second', 'third' ];
var ordinal, suffix;
// small numbers get the word form
if ( num < small_ordinals.length )
ordinal = small_ordinals[ num ];
else
{
// most numbers end with 'th'.
// numbers ending with 1, 2, or 3 get 'st', 'nd', or 'rd'...
// except if they're in the teens. then they're 'th'.
suffix = 'th';
if ( ( ( num % 100 ) > 10 ) && ( ( num % 100 ) < 14 ) )
suffix = 'th';
else if ( num % 10 == 1 )
suffix = 'st';
else if ( num % 10 == 2 )
suffix = 'nd';
else if ( num % 10 == 3 )
suffix = 'rd';
ordinal = num + suffix;
}
return ordinal;
}
// Return the frequency form of a number ('every', 'every other', 'every third', etc).
function NumberToFrequency( num )
{
var small_freqs = [ 'no', 'every', 'every other' ];
var freq;
if ( num < small_freqs.length )
freq = small_freqs[ num ];
else
freq = 'every ' + NumberToOrdinal( num );
return freq;
}
}// End of C-style "#ifndef/#define"
Source code is:
© Copyright 2002, The Echo Group. All rights reserved.