

/*===============================================================
MANUAL
-----------------------------------------------------------------
NAME
@(#) dateutils.js - utility functions for manipulating dates

SYNOPSIS
     Place  this code at  the top of the Body  section of the web
     page  on   which  you want to   make  use  of these  utility
     functions.

          <SCRIPT type="text/javascript"
                  src="[path/]dateutils.js">
          </SCRIPT>

     Then call the desired function as follows:

          <SCRIPT>function(args)</SCRIPT>

     Any output from   the  function will replace    the function
     invocation on the html page.

DESCRIPTION
     EACH function in this file has  its own Documentation below.
     The functions  are listed in this  Manual under the headings
     PUBLIC FUNCTIONS and  PRIVATE FUNCTIONS.    This  is to   be
     interpreted  as   describing   the   intended roll   of  the
     respective functions.   However, JavaScript itself  dones not
     support private functions, so all the functions in this file
     can be used publicly.

A WORD OF CAUTION
     Before editing this manual,  be sure that you understand the
     use of quotations and comments. So as to avoid having string
     messages containing  data  which also occurs in  the manual,
     thereby risking that the two  instances get out of sync with
     each other,  any data in the manual  which is required  in a
     message is turned into a string in situ. For an example, see
     PUBLIC FUNCTIONS section below.   The string 'g_pubfuncs' is
     used by the function 'listPubFuncs()'.

PUBLIC FUNCTIONS                            */var g_pubfuncs = "\
     hiliteUntil() - highlight following text until date arg   $\
     hiliteEnd() - end highlighting of text                    $\
     showUnitl() - show text arg until date arg only           $\
     insertMyDayGreeting() - insert greetings on special days  $\
     insertDateLastMod() - insert last-mod date on web page    $\
     insertDateutilsVers() - insert curr vers no on web page   $\
     listPubFuncs() - insert list of public funcs on web page";/*

PRIVATE FUNCTIONS
     doubleDigit() - add zero on left of single digits
     correctMillenium() - work-around for JS millenium Date bug
     getDate() - make and return Date object from date arg
     getMidnight() - get midnight at END of day in Date object
     getVers() - return current verion no and its date 
     makeList() - make list from str arg, formatted for web page
     isInt() - check that argument is an integer
     printErr() - print error message

DATES
     Dates  supplied as arguments to  these functions  must be in
     the format YYYYMMDD, e.g. 20000101 for 1st January 2000. The
     functions perform some tests on the date, and if they decide
     that the date  is not well-formed,  they will simply  ignore
     the function call. However, mind-reading  is not carried out
     to  ascertain    if the date   supplied   is what you really
     intended.  For  example, "00000000"  and 12345678 will  both
     pass the tests.

     Note, however, that  00000000 (without the  quotes) will NOT
     pass the   tests. This is not   due to the clever  design of
     these functions, but to the fact that an argument consisting
     of  any number  of zeros gets  passed to  the  function as a
     single zero, which is not a well-formed date.

     OUT-OF-RANGE DATES.  The argument '20090243' would end up as
     the  date 15th March  2009.  Again, this  is  not due to any
     clever programming  in these utilities, but  to JavaScript's
     laid-back attitude towards dates -- which is generally nicer
     than hitting  us over the head with  a hammer and telling us
     that we've made an error.

AUTHOR
     Donald M MacLean                         donald@macleans.net

HISTORY
     Vers 1.0    2002 11 02
        Amalgamation of  vers 1.2  of 'hiliteChanges.js' and vers
        1.2 of 'insertDateLastMod.js'

     Vers 1.0.1  2002 11 08
        Bug fix.  Vers 1.0  works in  IE  but  not  in  Netscape.
        Added   call    to   'correctMillenium()'   in   function
        'getMidnight()', which fixed bug. Corrections to Manual.
        Renamed some of private functions so as to conform to the 
        medialCapitalization pattern.

     Vers 1.1.0  2002 11 04
        Corrected   a few bugs  in  the Manual.  Added the public
        functions 'insertDateutilsVers()',  'listPubFuncs()'  and
        the private functions 'getVers()' and 'makeList()'.

     Vers 1.1.1  2002 11 08
        Vers 1.0 works in IE but not  in Netscape. Basically same
        bug fixes and corrections as in vers 1.0.1 (see above).
                                                */var g_vers = "\
@(#) Vers 1.2.0  2002 11 08                                  ";/*
        Added   the public  function 'insertMyDayGreeting()'  and
        created the "databse" 'myDays.js'   for the use   of this
        function.

End MANUAL
===============================================================*/


/*===============================================================
GLOBAL VARIABLES
---------------------------------------------------------------*/
var g_col      = "purple";      // Default colour
var g_hilitLim = new Date();    // Hilite-until date
var g_showLim  = new Date();    // Display-until date
var g_today    = new Date();
var g_level    = 0;      // Track nested calls to 'hiliteUnitl()'
/*---------------------------------------------------------------
These   global   variables   have  already  been  declared  above
in the Manual:
.................................................................
g_vers        // version no (and date) of 'dateutils.js', 
g_pubfuncs    // list of the public functions in this file
-----------------------------------------------------------------
This global variable is used as an array to  hold the database of
special days  for the function  'insertMyDayGreeting()'. See  the
documentation of that function for  the esoteric details of using
Objects as arrays, etc:
...............................................................*/
var myDays = new Object();
/*=============================================================*/


/*=============================================================*/
function hiliteUntil(date, colour)
/*---------------------------------------------------------------
NAME
     hiliteUntil() - highlight following text until date arg

DESCRIPTION
     This function  will  highlight  the text  which  follows its
     invocation in colour (default purple) until the current date
     is greater than date.  Highlighting is terminated by calling
     'hiliteEnd()'.

     Bracket any text which is to be hightlighted until date
     as follows:

          <SCRIPT>hiliteUntil(date[,colour])</SCRIPT>
             New or recently edited text 
             which you want to be highlighted
             until date
          <SCRIPT>hiliteEnd()</SCRIPT>

     The default highlighting colour   is purple. If  you  prefer
     some other  colour  as default,  edit  this  line (in
     GLOBAL VARIABLES):

          var g_col = "purple";     // Default colour

     If you  want  to highlight  some text  in a different colour
     from the default colour,  you  can supply  that colour as  a
     second argument to 'hiliteUntil()'. Colours should be string
     literals.  Since JavaScript is  very forgiving, no check  is
     made by this   program that the  colour supplied  is a valid
     colour.  The effect of  supplying  an invalid  colour  would
     simply be to  show  the text which  follows in  the  current
     colour.

EXAMPLES
     Highlight paragraphs  in the default  colour until 1st April
     2003:

          <SCRIPT>hiliteUntil(20030401)</SCRIPT>
             Microsoft has announced that they will be putting 
             the source source code 
             of their Windows Operating System
             in the public domain.
             <P>
             "It's time that our users got a chance to see
             what's going on", 
             said Microsoft chairman Bill Gates, and added
             "There are a lot of people out there 
             with good ideas.
             We want them to be able to contribute them
             to making Windows a better operating system."
          <SCRIPT>hiliteEnd()</SCRIPT>

     Hilite text in seagreen until 1st April 2003:

          <SCRIPT>hiliteUntil(20030332, "seagreen")</SCRIPT>
             Microsoft lawyers are suing this website for
             defamation of character. They claim that
             Mr Gates would never express the noble sentiments
             attributed to him on this page.
          <SCRIPT>hiliteEnd()</SCRIPT>

     The effect of the folowing  invocations would be to show the
     text which follows in the  current colour (since the colours
     specified are invalid):

          <SCRIPT>hiliteUntil(30000101, "sqwischy")</SCRIPT>
          <SCRIPT>hiliteUntil(30000101, "#FF002H")</SCRIPT>
          <SCRIPT>hiliteUntil(30000101, "#FF000")</SCRIPT>

     Date arguments are explained in the main Manual above.
---------------------------------------------------------------*/
{
  var argc = hiliteUntil.arguments.length;

  if ( argc == 0 ) {
    printErr("hiliteUntil()", "need at least one argument");
    return 0;
  } 

  var d = getDate(date);

  if ( d == 0 ) {
    return 0;
  } else {
    g_hilitLim = getMidnight(d);
  }
 
  var col = g_col;
  if ( argc > 1 )  col = colour; 

  if ( g_today < g_hilitLim) {
    g_level++;
    document.write('<FONT COLOR="' + col + '">');
  }
} /* End function 'hiliteUntil()' */
/*=============================================================*/


/*=============================================================*/
function hiliteEnd()
/*---------------------------------------------------------------
NAME
     hiliteEnd() - end highlighting of text

DESCRIPTION
     This  function marks the end of the text to  be  highlighted
     by 'hiliteUnitl();
---------------------------------------------------------------*/
{
  if ( g_today < g_hilitLim && g_level > 0 ) {

    g_level--;
    document.write('</FONT>');
  }
}  /* End function 'hiliteEnd()' */
/*=============================================================*/


/*=============================================================*/
function showUntil(date, text, colour)
/*---------------------------------------------------------------
NAME
     showUnitl() - show text arg until date arg only

DESCRIPTION
     This function will display  text (2nd arg) until the current
     date is greater than date (1st arg).  If colour (3rd arg) is
     missing, the   text   will  be  displayed  in    the default
     highlighting colour.
     
     How  to change the default  highlighting colour is explained
     in   the  documentation  of   'hiliteChanges()'.  Note  that
     changing the default  colour will effect ALL functions which
     use it.

     To display text until date only:

          <SCRIPT>showUntil(date, text [,colour])</SCRIPT>

EXAMPLE
     Inform readers of significance of text colour:
     
          <SCRIPT>showUntil(20030415,
             "Text in this colour"
             + "Was added in the revision"
             + "of 2003 03 20"
             , "seagreen")
          </SCRIPT>

     Note how the string  in the second argument to 'showUntil()'
     above has been spread over several lines.
---------------------------------------------------------------*/
{
  var argc = showUntil.arguments.length;

  if ( argc < 2 ) {
    printErr("showUntil()", "need at least two arguments");
    return 0;
  } 

  var d = getDate(date);

  if ( d == 0 ) {
    return 0;
  } else {
    g_showLim = getMidnight(d);
  }

  var showcol = g_col;

  if ( argc > 2 ) showcol = colour; 

  if ( g_today < g_showLim ) {
    document.write('<FONT COLOR="' + showcol + '">' 
      + '<P>' + text + '<P>'
      + '</FONT>');
  }
} /* End function 'showUntil()' */
/*=============================================================*/


/*=============================================================*/
function insertDateLastMod()
/*---------------------------------------------------------------
NAME
     insertDateLastMod() - insert last-modified date on web page

DESCRIPTION
     This function, when called from  any html page, inserts  the
     date of last modification of that page onto  the page at the
     point from which  the call was  made, provided that  the web
     server  makes   this  date   available  (otherwise   nothing
     happens).

     The  function  code   shows  how the  highly  illogical  and
     confusing  American date representation,  mm/dd/yyyy, can be
     converted into a more  sensible format, for example  yyyy mm
     dd.

     The function also shows how to deal with an inconsistency in
     JavaScript   Date  objects.    According to   the JavaScript
     specification,  years are integers  after the year 1900.  In
     other   words, if you  use  the getYear()  method for a Date
     object that holds a 1999  date, the returned value should be
     99.

     This  is how   JavaScript works  for  dates  prior to  2000.
     However, JavaScript treats  all years beginning with 2000 as
     the actual  year  value: 2001 is   2001.  At any  rate  some
     versions  do.  The versions of  JavaScript used in Netscape,
     up to and including version 6 (and possibly later), however,
     do not.
     
     To work around this inconsistency,  this script adds 1900 to
     all years less than 2000,  which should work until about the
     year 3900, (when this programmer will no longer be around to
     take the flak. :-)

     Thanks  to Danny Goodman, who documented  this bug.  You can
     read more about it here:

     http://developer.netscape.com/
           /viewsource/goodman_dateobject.html
---------------------------------------------------------------*/
{
  if (Date.parse(document.lastModified) == 0) {
    /*----------------------------------
    If the web server does not make this 
    date available,  0 will be returned.
    ----------------------------------*/
    document.write("Sorry. "
      + "The Web server does not make this date available.");
    return 0;
  } else {
    d = new Date(document.lastModified);
    document.write('<FONT COLOR="' + g_col + '"><SMALL><I>'
      + 'Senast &auml;ndrad: '
      +  correctMillenium(d.getYear()) + ' '
      +  doubleDigit(d.getMonth()+ 1) + ' '
      // JavaScript months are numbered 0-11
      +  doubleDigit(d.getDate()) + ' '
      +  doubleDigit(d.getHours()) + ':'
      +  doubleDigit(d.getMinutes())
      + '</FONT></SMALL></I>'
    ); /* End 'document.write(' */
  } /* End else */

  return 1;

} /* End function 'insertDateLastMod()' */
/*=============================================================*/


/*=============================================================*/
function insertMyDayGreeting()
/*---------------------------------------------------------------
NAME
     insertMyDayGreeting() - insert greetings on special days

DESCRIPTION
     This function compares today's date (month and day) with the
     special days which  have  been  defined in  the   "database"
     'myDays.js'.

     Since JavaScript  is  (quite  rightly) not  allowed  to open
     files   and read  from   them,  we  have to use   subterfuge
     (otherwise know as a kludge) here.  What we do is define our
     special days   as  a  JavaScript  associative   array, whose
     indices have the form  "mm,dd". For example "12,25" would be
     the index for Christmas Day. The  file 'myDates.js' has this
     format:

          myDays["02,20"] = "A good day for testing";
          myDays["04,01"] = "Dilbert's Birthday";
          myDays["12,25"] = "Christmas Day";

     Although this  works fairly well  for  the limited number of
     special days that most people observe, it is NOT designed to
     handle   the birthdays of thousands of   employees  in a big
     office.

     In fact, trying  to do so  would quickly  reveal one of  the
     shortcomings of this  kludge:  entries  with the  same  date
     over-write  each other,  so that  you end  up  with the LAST
     element in the array which has that date as index.

     The  "database" must, of course,  be read into the HTML file
     BEFORE calling this function  for the first time. (It's  not
     an error if you don't do so.  But if you don't, your special
     days will be ignored. :-)

     Thus the  HTML file  will   contain (at least)  these  three
     SCRIPT invocations, in this order:

        1 Read in the Date utilities in this file:
        ------------------------------------------
          <SCRIPT type="text/javascript"
                   src="../JavaScript/dateutils.js">
          </SCRIPT>
          
        2 Read in the database containting the special days:
        ----------------------------------------------------
          <SCRIPT type="text/javascript"
                   src="../JavaScript/myDays.js">
          </SCRIPT>
          
        3 Call this function:
        ---------------------
          <SCRIPT>insertMyDayGreeting();</SCRIPT> 

     If you  are  feeling uneasy about  using something  that has
     been declared as an Object  ('var myDays = new Object();) as
     if it were  an array ('myDays["12,25"] = "Christmas Day";'),
     you are in good company.   It is unlikely that anything less
     that reading about this in a good  textbook (e.g.  FLANAGAN:
     "JavaScript:  The    Definitive   Guide",  O'Reily)      and
     experimenting for  yourself   will relieve  your  feeling of
     unease.
---------------------------------------------------------------*/
{
  var today = new Date();

  var index = doubleDigit(today.getMonth()+ 1)
            + ','
            + doubleDigit(today.getDate());

  var myDay = myDays[index];

  /*----------------------------------------------
  The    value   of    an    undefined    variable
  (including an undefined  element  in  an  array)
  is   <undefined>.   However,  we  cannot compare
  this value with another by saying:

     if ( myDay != "undefined" );

  This will always  return  true.  To get  hold of
  the STRING "undefined" (for use in comparisons),
  we can use the 'typeof' operator:
  ----------------------------------------------*/
  var type  = typeof myDay;

  if ( type != "undefined" ) {
    document.write('<FONT COLOR="' + g_col + '"><SMALL><I>'
      + "<P>Today is " + myDay + "!<P>"
      + '</FONT></SMALL></I>');
  } else {
    /*----------------------------------------
    If you want to  have  a  default  greeting
    for non-special days, e.g.

         document.write('<FONT COLOR="' + g_col + '"><SMALL><I>'
           + '<P>The margin is too small, '
           + 'otherwise ...<P>'
           + '</FONT></SMALL></I>');

    this is where to put it.
    ----------------------------------------*/
  } /* End else */

  return;

} /* End function 'insertMyDayGreeting()' */
/*=============================================================*/


/*=============================================================*/
function insertDateutilsVers()
/*---------------------------------------------------------------
NAME
     insertDateutilsVers() - insert current vers no on web page

DESCRIPTION
     This function, when called from  any html page, inserts  the
     current version   number    of   'dateutils.js'  (and    its
     associated  date)  onto the page  at the point from which it
     was invoked.
---------------------------------------------------------------*/
{
  document.write('<FONT COLOR="' + g_col + '"><SMALL><I>'
    + "Current version of 'dateutils.js': "
    + getVers()
    + '</FONT></SMALL></I>');

} /* End function 'insertDateutilsVers()' */
/*=============================================================*/


/*=============================================================*/
function listPubFuncs()
/*---------------------------------------------------------------
NAME
     listPubFuncs() - insert list of public functions on web page

DESCRIPTION
     This  function,  when called from  any  html page, inserts a
     list of  the currently-supported public  functions available
     in 'dateutils.js'. (See the remarks on PUBLIC and PRIVATE in
     the MANUAL.)
---------------------------------------------------------------*/
{
  document.write(makeList(g_pubfuncs, '$'));

} /* End function 'listPubFuncs()' */
/*=============================================================*/


/*=============================================================*/
function doubleDigit(num)
/*---------------------------------------------------------------
NAME
     doubleDigit() - add zero on left of single digits

DESCRIPTION
     JavaScript months, days, hours and minutes can all be single
     digit integrals.  If  this function's  argument is a  single
     digit, it is padded on the left with a single zero.
---------------------------------------------------------------*/
{
  var i = num;

  if (i < 10) i = "0" + i;
  return i;

} /* End function 'doubleDigit()' */
/*=============================================================*/


/*=============================================================*/
function correctMillenium(year)
/*---------------------------------------------------------------
NAME
     correctMillenium() - work-around for JS millenium Date bug

DESCRIPTION
     Some  versions of  JavaScript   treat Years as the   integer
     obtained by subtracting 1900 from the actual year.  (Eg 1999
     would be 99, 2001 would be 101.)  Other versions treat years
     after 2000 as the value of Year. (I.e. 2001 would be 2001.)

     This function adds 1900  to any arguments received which are
     less than 2000.
---------------------------------------------------------------*/
{
  var i = year;

  if (i < 2000) i = i + 1900;
  return i;

} /* End function 'correctMillenium()' */
/*=============================================================*/


/*=============================================================*/
function getDate(yyyymmdd)
/*---------------------------------------------------------------
NAME
     getDate() - make and return Date object from date arg

DESCRIPTION
     This  auxilliary  function creates a  JavaScript Date Object
     (milliseconds since  midnight at end  of 31st Dec 1969) from
     an integer in the format yyyymmdd.
---------------------------------------------------------------*/
{
  if ( getDate.arguments.length == 0 ) {
    printErr("getDate()", "usage: getDate(yyyymmdd)");
    return 0;
  } 

  var d = yyyymmdd.toString(10);

  if ( d.length != 8 || ! isInt(d) ){
    printErr("getDate()", d + ": invalid date");
    return 0;
  }
  // JavaScript numbers months from 0 to 11!
  var year  = d.substring(0,4);
  var month = d.substring(4,6) - 1;
  var day   = d.substring(6,8);

  return new Date(year, month, day);

} /* End function 'getDate()' */
/*=============================================================*/


/*=============================================================*/
function getMidnight(date)
/*---------------------------------------------------------------
NAME
     getMidnight() - get midnight at END of day in Date object
---------------------------------------------------------------*/
{
  var midnightLastnight = new Date( 
    correctMillenium(date.getYear()),
    date.getMonth(),
    date.getDate(),
    0, 0, 0
  );
  var midnightTonight = new Date(
    midnightLastnight.getTime() +  24 * 60 * 60 * 1000
  );

  return midnightTonight;

} /* End function 'getMidnight()' */
/*=============================================================*/


/*=============================================================*/
function getVers()
/*---------------------------------------------------------------
NAME
     getVers() - get version number and its date

DESCRIPTION
     This  function returns a string in the format

          Vers 1.0    2000 01 01

     containing the current  version number of  this file and the
     date on which that version was checked in. It trims away the
     'whatis' string at  the   beginning of  the line,  and   the
     trailing blanks.
---------------------------------------------------------------*/
{
  /*-------------------------------
  Remove 'whatis' string ( "@(#)" )
  at beginning of line:
  ------------------------------ */
  var from = g_vers.indexOf("Ver");
  if ( from == -1 ) { from = g_vers.indexOf("ver"); }
  if ( from == -1 ) { return -1; }

  /*---------------------
  Remove trailing blanks:
  -------------------- */
  var to = g_vers.length -1;
  while ( g_vers.charAt(to) == " ") { to--; }
  /*-----------------------------
  The substring operator requires
  'to' to be ONE GREATER THAN the
  index of  the  last  character!
  ---------------------------- */
  to++;

  s = g_vers.substring(from, to);

  return s;

} /* End function 'getVers()' */
/*=============================================================*/


/*=============================================================*/
function makeList(str, delimiter)
/*---------------------------------------------------------------
NAME
     makeList() - make a list from string, formatted for web page

DESCRIPTION
     This  function expects str to be a string in the format

        "blah blah * blah blah blah* blah blah blah blah *blah"

     where in this case the delimiter argument is "*".

     Note that there is no sense in having any JavaScript newline
     characters ('\n') in the  list,  since these will simply  be
     ignored by  the HTML browser.   This  function replaces each
     occurrence of the delimiter with an HTML '<BR>' tag and adds
     a '<BR>'  tag to  the  beginning and end  of the  list.  The
     resulting list is returned.
---------------------------------------------------------------*/
{
  var arr = str.split(delimiter);

  var list = "<TT><BR>";

  for ( i = 0; i < arr.length; i++ ) {
    list = list 
         + "<SPACER TYPE=horizontal SIZE=50>" 
         + arr[i] 
         + "<BR>";
  }
  list = list + "</TT>";

  return list;

} /* End function 'makeList()' */
/*=============================================================*/


/*=============================================================*/
function isInt(n)
/*---------------------------------------------------------------
NAME
     isInt() - check that argument is an integer

DESCRIPTION
     This  function returns 1 if n is an integer, 0 otherwise.
---------------------------------------------------------------*/
{
  var s = n.toString();

  for ( i = 0; i < s.length ; i++) {
    if ( isNaN(parseInt(s.substring(i, i + 1))) ) return 0;
    //to-value must be one more than index of last char!
  }

  return 1;

} /* End function 'isInt()' */
/*=============================================================*/


/*=============================================================*/
function printErr(caller, msg)
/*---------------------------------------------------------------
NAME
     printErr() - print error message
---------------------------------------------------------------*/
{
  document.write('<FONT COLOR="#ff0000"><P>JAVASCRIPT ERROR: '
  + "'" + caller + "': " + msg + '<P></FONT>');

  // Successful return from THIS function:
  return 1;
}
/*=============================================================*/


/*==================
Local Variables:
indent-tabs-mode:nil
fill-column:65
fill-prefix:"     "
End:
==================*/


