// basic.js
//
// BASIC DATA VALIDATION FUNCTIONS:
//
// isIntegerInRange (s, a, b [,eok])   True if string s is an integer
//					 between a and b, inclusive.
// isYear (s [,eok])                   True if string s is a valid
//					 Year number.
// isMonth (s [,eok])                  True if string s is a valid month
//					 between 1 and 12.
// isDay (s [,eok])                    True if string s is a valid day
//					 between 1 and 31.
// daysInFebruary (year)               Returns number of days in February
//					 of that year.
// isDate (year, month, day)           True if string arguments form a
//
// Performance hint: when you deploy this file on your website, strip out the
// comment lines from the source code as well as any of the functions which
// you don't need.  This will give you a smaller .js file and achieve faster
// downloads.
//
// 18 Feb 97 created Eric Krock
//
// (c) 1997 Netscape Communications Corporation
// 1998 Supplemented by Dawn Bushong


// Attempting to make this library run on Navigator 2.0,
// so I'm supplying this array creation routine as per
// JavaScript 1.0 documentation.  If you're using 
// Navigator 3.0 or later, you don't need to do this;
// you can use the Array constructor instead.

function makeArray(n) {
//*** BUG: If I put this line in, I get two error messages:
//(1) Window.length can't be set by assignment
//(2) days has no property indexed by 4
//If I leave it out, the code works fine.
//   this.length = n;
 for (var i = 1; i <= n; i++) {
   this[i] = 0
 } 
 return this
}

var days = makeArray(12);
days[1]  = 31;
days[2]  = 29;   // must programmatically check this
days[3]  = 31;
days[4]  = 30;
days[5]  = 31;
days[6]  = 30;
days[7]  = 31;
days[8]  = 31;
days[9]  = 30;
days[10] = 31;
days[11] = 30;
days[12] = 31;


// isDateInteger(STRING s [, BOOLEAN emptyOK])
// 
// Returns true if all characters in string s are numbers.
//
// Accepts non-signed integers only. Does not accept floating 
// point, exponential notation, etc.
//
// We don't use parseInt because that would accept a string
// with trailing non-numeric characters.
//
// By default, returns defaultEmptyOK if s is empty.
// There is an optional second argument called emptyOK.
// emptyOK is used to override for a single function call
//      the default behavior which is specified globally by
//      defaultEmptyOK.
// If emptyOK is false (or any value other than true), 
//      the function will return false if s is empty.
// If emptyOK is true, the function will return true if s is empty.
//
// EXAMPLE FUNCTION CALL:     RESULT:
// isDateInteger ("5")            true 
// isDateInteger ("")             defaultEmptyOK
// isDateInteger ("-5")           false
// isDateInteger ("", true)       true
// isDateInteger ("", false)      false
// isDateInteger ("5", false)     true

function isDateInteger(s) {
  if (isEmpty(s)) return emptyReturn(isDateInteger.arguments);

  // Search through string's characters one by one
  // until we find a non-numeric character.
  // When we do, return false; if we don't, return true.

  for (var i = 0; i < s.length; i++) {   
    // Check that current character is number.
    var c = s.charAt(i);

    if ( !((c >= "0") && (c <= "9")) ) return false;
  }

  // All characters are numbers.
  return true;
}


// isIntegerInRange(STRING s, INTEGER a, INTEGER b [, BOOLEAN emptyOK])
// 
// isIntegerInRange returns true if string s is an integer 
// within the range of integer arguments a and b, inclusive.
// 
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isIntegerInRange(s, a, b) {
  // Catch non-integer strings to avoid creating a NaN below,
  // which isn't available on JavaScript 1.0 for Windows.
  if (isEmpty(s)) return emptyReturn(isIntegerInRange.arguments);
  if (!isDateInteger(s, false)) return false;

  // Now, explicitly change the type to integer via parseInt
  // so that the comparison code below will work both on 
  // JavaScript 1.2 (which typechecks in equality comparisons)
  // and JavaScript 1.1 and before (which doesn't).
  var num = parseInt (s,10);
  return ((num >= a) && (num <= b));
}


// isYear(STRING s [, BOOLEAN emptyOK])
// 
// isYear returns true if string s is a valid 
// Year number.  Must be 2 or 4 digits only.
// 
// For Year 2000 compliance, you are advised
// to use 4-digit year numbers everywhere.
//
// And yes, this function is not Year 10000 compliant, but 
// because I am giving you 8003 years of advance notice,
// I don't feel very guilty about this ...
//
// For B.C. compliance, write your own function. ;->
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isYear(s) {
  if (isEmpty(s)) return emptyReturn(isYear.arguments);
  return ( (isDateInteger(s) && parseInt(s,10) >= 0) &&
	   (s.length == 2) || (s.length == 4));
}


// isMonth(STRING s [, BOOLEAN emptyOK])
// 
// isMonth returns true if string s is a valid 
// month number between 1 and 12.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isMonth(s) {
  if (isEmpty(s)) return emptyReturn(isMonth.arguments);
  return (isIntegerInRange(s, 1, 12));
}


// isDay(STRING s [, BOOLEAN emptyOK])
// 
// isDay returns true if string s is a valid 
// day number between 1 and 31.
// 
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function isDay(s) {
  if (isEmpty(s)) return emptyReturn(isDay.arguments);
  return isIntegerInRange(s, 1, 31);
}


// daysInFebruary(INTEGER year)
// 
// Given integer argument year,
// returns number of days in February of that year.

function daysInFebruary(year) {
  // February has 29 days in any year evenly divisible by four,
  // EXCEPT for centurial years which are not also divisible by 400.
  return ( ((year % 4 == 0) && ((!(year % 100 == 0)) || (year % 400 == 0)) )
	 ? 29 : 28 );
}


// isDate (STRING year, STRING month, STRING day)
//
// isDate returns true if string arguments year, month, and day 
// form a valid date.
// 

function isDate (year, month, day) {
  // catch invalid years (not 2- or 4-digit) and invalid months and days.
  if (! (isYear(year, false) && isMonth(month, false) && isDay(day, false)))
    return false;

  // Explicitly change type to integer to make code work in both
  // JavaScript 1.1 and JavaScript 1.2.
  var intDay = parseInt(day, 10);
  var intMonth = parseInt(month, 10);

  // catch invalid days, except for February
  if (intDay > days[intMonth]) return false; 

  if ((intMonth == 2) && (intDay > daysInFebruary(parseInt(year,10))))
     return false;

  return true;
}

