text: /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  var completed = false;
  var testcases;
  var tc = 0;
  
  SECTION	= "";
  VERSION	= "";
  BUGNUMBER =	"";
  TITLE  = "";
  
  /*
   * constant strings
   */
  var	GLOBAL = "[object global]";
  var PASSED = " PASSED!"
  var FAILED = " FAILED! expected: ";
  var	DEBUG =	false;
  
  TZ_DIFF = -8;
  
  var TT = "";
  var TT_ = "";
  var BR = "";
  var NBSP = " ";
  var CR = "\n";
  var FONT = "";
  var FONT_ = "";
  var FONT_RED = "";
  var FONT_GREEN = "";
  var B = "";
  var B_ = ""
  var H2 = "";
  var H2_ = "";
  var HR = "";
  var DEBUG = false;
  
  var PASSED = " PASSED!"
  var FAILED = " FAILED! expected: ";
  
  function test() {
      for ( tc=0; tc < testcases.length; tc++ ) {
          testcases[tc].passed = writeTestCaseResult(
                              testcases[tc].expect,
                              testcases[tc].actual,
                              testcases[tc].description +" = "+
                              testcases[tc].actual );
  
          testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value ";
      }
      stopTest();
      return ( testcases );
  }
  /* wrapper for test cas constructor that doesn't require the SECTION
   * argument.
   */
  
  function AddTestCase( description, expect, actual ) {
      testcases[tc++] = new TestCase( SECTION, description, expect, actual );
  }
  
  function TestCase( n, d, e, a ) {
      this.name        = n;
      this.description = d;
      this.expect      = e;
      this.actual      = a;
      this.passed      = true;
      this.reason      = "";
      this.bugnumber   = BUGNUMBER;
  
      this.passed = getTestCaseResult( this.expect, this.actual );
      if ( DEBUG ) {
          print( "added " + this.description );
      }
  }
  
  /*
   * Set up test environment.
   *
   */
  function startTest() {
      if ( version ) {
      	//	JavaScript 1.3 is supposed to be compliant ecma	version	1.0
  	    if ( VERSION ==	"ECMA_1" ) {
  		    version	( "130"	);
      	}
  	    if ( VERSION ==	"JS_1.3" ) {
  		    version	( "130"	);
      	}
  	    if ( VERSION ==	"JS_1.2" ) {
  		    version	( "120"	);
      	}
  	    if ( VERSION  == "JS_1.1" )	{
  		    version	( "110"	);
      	}
  	    // for ecma	version	2.0, we	will leave the javascript version to
      	// the default ( for now ).
      }
  
      // print out bugnumber
  
      if ( BUGNUMBER ) {
              print ("BUGNUMBER: " + BUGNUMBER );
      }
  
      testcases = new Array();
      tc = 0;
  }
  
  
  function test() {
      for ( tc=0; tc < testcases.length; tc++ ) {
          testcases[tc].passed = writeTestCaseResult(
                              testcases[tc].expect,
                              testcases[tc].actual,
                              testcases[tc].description +" = "+ testcases[tc].actual );
          testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value ";
      }
      stopTest();
      return ( testcases );
  }
  
  
  function getTestCaseResult( expect, actual ) {
      //  because ( NaN == NaN ) always returns false, need to do
      //  a special compare to see if we got the right result.
          if ( actual != actual ) {
              if ( typeof actual == "object" ) {
                  actual = "NaN object";
              } else {
                  actual = "NaN number";
              }
          }
          if ( expect != expect ) {
              if ( typeof expect == "object" ) {
                  expect = "NaN object";
              } else {
                  expect = "NaN number";
              }
          }
  
          var passed = ( expect == actual ) ? true : false;
  
      //  if both objects are numbers
      // need to replace w/ IEEE standard for rounding
          if (    !passed
                  && typeof(actual) == "number"
                  && typeof(expect) == "number"
              ) {
                  if ( Math.abs(actual-expect) < 0.0000001 ) {
                      passed = true;
                  }
          }
  
      //  verify type is the same
          if ( typeof(expect) != typeof(actual) ) {
              passed = false;
          }
  
          return passed;
  }
  function writeTestCaseResult( expect, actual, string ) {
          var passed = getTestCaseResult( expect, actual );
          writeFormattedResult( expect, actual, string, passed );
          return passed;
  }
  function writeFormattedResult( expect, actual, string, passed ) {
          var s = TT + string ;
  
          for ( k = 0;
                k <  (60 - string.length >= 0 ? 60 - string.length : 5) ;
                k++ ) {
          }
  
          s += B ;
          s += ( passed ) ? FONT_GREEN + NBSP + PASSED : FONT_RED + NBSP + FAILED + expect + TT_ ;
  
          print( s + FONT_ + B_ + TT_ );
  
          return passed;
  }
  
  function writeHeaderToLog( string ) {
      print( H2 + string + H2_ );
  }
  function stopTest()
  {
      var sizeTag  = "<#TEST CASES SIZE>";
      var doneTag  = "<#TEST CASES DONE>";
      var beginTag = "<#TEST CASE ";
      var endTag   = ">";
  
      print(sizeTag);
      print(testcases.length);
      for (tc = 0; tc < testcases.length; tc++)
      {
          print(beginTag + 'PASSED'      + endTag);
          print(testcases[tc].passed);
          print(beginTag + 'NAME'        + endTag);
          print(testcases[tc].name);
          print(beginTag + 'EXPECTED'    + endTag);
          print(testcases[tc].expect);
          print(beginTag + 'ACTUAL'      + endTag);
          print(testcases[tc].actual);
          print(beginTag + 'DESCRIPTION' + endTag);
          print(testcases[tc].description);
          print(beginTag + 'REASON'      + endTag);
          print(( testcases[tc].passed ) ? "" : "wrong value ");
          print(beginTag + 'BUGNUMBER'   + endTag);
          print( BUGNUMBER );
      }
      print(doneTag);
      print( HR );
      gc();
  }
  function getFailedCases() {
    for ( var i = 0; i < testcases.length; i++ ) {
       if ( ! testcases[i].passed ) {
          print( testcases[i].description +" = " +testcases[i].actual +" expected: "+ testcases[i].expect );
       }
    }
  }
  function err( msg, page, line ) {
      testcases[tc].actual = "error";
      testcases[tc].reason = msg;
      writeTestCaseResult( testcases[tc].expect,
                           testcases[tc].actual,
                           testcases[tc].description +" = "+ testcases[tc].actual +
                           ": " + testcases[tc].reason );
      stopTest();
      return true;
  }
  
  /**
   *  Type Conversion functions used by Type Conversion
   *
   */
  
  
  
   /*
    * Date functions used by tests in Date suite
    *
    */
  var msPerDay =          86400000;
  var HoursPerDay =       24;
  var MinutesPerHour =    60;
  var SecondsPerMinute =  60;
  var msPerSecond =       1000;
  var msPerMinute =       60000;      //  msPerSecond * SecondsPerMinute
  var msPerHour =         3600000;    //  msPerMinute * MinutesPerHour
  
  var TIME_1970    = 0;
  var TIME_2000    = 946684800000;
  var TIME_1900    = -2208988800000;
  
  function Day( t ) {
      return ( Math.floor(t/msPerDay ) );
  }
  function DaysInYear( y ) {
      if ( y % 4 != 0 ) {
          return 365;
      }
      if ( (y % 4 == 0) && (y % 100 != 0) ) {
          return 366;
      }
      if ( (y % 100 == 0) &&  (y % 400 != 0) ) {
          return 365;
      }
      if ( (y % 400 == 0) ){
          return 366;
      } else {
          return "ERROR: DaysInYear(" + y + ") case not covered";
      }
  }
  function TimeInYear( y ) {
      return ( DaysInYear(y) * msPerDay );
  }
  function DayNumber( t ) {
      return ( Math.floor( t / msPerDay ) );
  }
  function TimeWithinDay( t ) {
      if ( t < 0 ) {
          return ( (t % msPerDay) + msPerDay );
      } else {
          return ( t % msPerDay );
      }
  }
  function YearNumber( t ) {
  }
  function TimeFromYear( y ) {
      return ( msPerDay * DayFromYear(y) );
  }
  function DayFromYear( y ) {
      return (    365*(y-1970) +
                  Math.floor((y-1969)/4) -
                  Math.floor((y-1901)/100) +
                  Math.floor((y-1601)/400) );
  }
  function InLeapYear( t ) {
      if ( DaysInYear(YearFromTime(t)) == 365 ) {
          return 0;
      }
      if ( DaysInYear(YearFromTime(t)) == 366 ) {
          return 1;
      } else {
          return "ERROR:  InLeapYear("+t+") case not covered";
      }
  }
  function YearFromTime( t ) {
      t = Number( t );
      var sign = ( t < 0 ) ? -1 : 1;
      var year = ( sign < 0 ) ? 1969 : 1970;
      for (   var timeToTimeZero = t; ;  ) {
      //  subtract the current year's time from the time that's left.
          timeToTimeZero -= sign * TimeInYear(year)
  
      //  if there's less than the current year's worth of time left, then break.
          if ( sign < 0 ) {
              if ( sign * timeToTimeZero <= 0 ) {
                  break;
              } else {
                  year += sign;
              }
          } else {
              if ( sign * timeToTimeZero < 0 ) {
                  break;
              } else {
                  year += sign;
              }
          }
      }
      return ( year );
  }
  function MonthFromTime( t ) {
      //  i know i could use switch but i'd rather not until it's part of ECMA
      var day = DayWithinYear( t );
      var leap = InLeapYear(t);
  
      if ( (0 <= day) && (day < 31) ) {
          return 0;
      }
      if ( (31 <= day) && (day < (59+leap)) ) {
          return 1;
      }
      if ( ((59+leap) <= day) && (day < (90+leap)) ) {
          return 2;
      }
      if ( ((90+leap) <= day) && (day < (120+leap)) ) {
          return 3;
      }
      if ( ((120+leap) <= day) && (day < (151+leap)) ) {
          return 4;
      }
      if ( ((151+leap) <= day) && (day < (181+leap)) ) {
          return 5;
      }
      if ( ((181+leap) <= day) && (day < (212+leap)) ) {
          return 6;
      }
      if ( ((212+leap) <= day) && (day < (243+leap)) ) {
          return 7;
      }
      if ( ((243+leap) <= day) && (day < (273+leap)) ) {
          return 8;
      }
      if ( ((273+leap) <= day) && (day < (304+leap)) ) {
          return 9;
      }
      if ( ((304+leap) <= day) && (day < (334+leap)) ) {
          return 10;
      }
      if ( ((334+leap) <= day) && (day < (365+leap)) ) {
          return 11;
      } else {
          return "ERROR:  MonthFromTime("+t+") not known";
      }
  }
  function DayWithinYear( t ) {
          return( Day(t) - DayFromYear(YearFromTime(t)));
  }
  function DateFromTime( t ) {
      var day = DayWithinYear(t);
      var month = MonthFromTime(t);
  
      if ( month == 0 ) {
          return ( day + 1 );
      }
      if ( month == 1 ) {
          return ( day - 30 );
      }
      if ( month == 2 ) {
          return ( day - 58 - InLeapYear(t) );
      }
      if ( month == 3 ) {
          return ( day - 89 - InLeapYear(t));
      }
      if ( month == 4 ) {
          return ( day - 119 - InLeapYear(t));
      }
      if ( month == 5 ) {
          return ( day - 150- InLeapYear(t));
      }
      if ( month == 6 ) {
          return ( day - 180- InLeapYear(t));
      }
      if ( month == 7 ) {
          return ( day - 211- InLeapYear(t));
      }
      if ( month == 8 ) {
          return ( day - 242- InLeapYear(t));
      }
      if ( month == 9 ) {
          return ( day - 272- InLeapYear(t));
      }
      if ( month == 10 ) {
          return ( day - 303- InLeapYear(t));
      }
      if ( month == 11 ) {
          return ( day - 333- InLeapYear(t));
      }
  
      return ("ERROR:  DateFromTime("+t+") not known" );
  }
  function WeekDay( t ) {
      var weekday = (Day(t)+4) % 7;
      return( weekday < 0 ? 7 + weekday : weekday );
  }
  
  // missing daylight savins time adjustment
  
  function HourFromTime( t ) {
      var h = Math.floor( t / msPerHour ) % HoursPerDay;
      return ( (h<0) ? HoursPerDay + h : h  );
  }
  function MinFromTime( t ) {
      var min = Math.floor( t / msPerMinute ) % MinutesPerHour;
      return( ( min < 0 ) ? MinutesPerHour + min : min  );
  }
  function SecFromTime( t ) {
      var sec = Math.floor( t / msPerSecond ) % SecondsPerMinute;
      return ( (sec < 0 ) ? SecondsPerMinute + sec : sec );
  }
  function msFromTime( t ) {
      var ms = t % msPerSecond;
      return ( (ms < 0 ) ? msPerSecond + ms : ms );
  }
  function LocalTZA() {
      return ( TZ_DIFF * msPerHour );
  }
  function UTC( t ) {
      return ( t - LocalTZA() - DaylightSavingTA(t - LocalTZA()) );
  }
  function DaylightSavingTA( t ) {
      t = t - LocalTZA();
  
      var dst_start = GetFirstSundayInApril(t) + 2*msPerHour;
      var dst_end   = GetLastSundayInOctober(t)+ 2*msPerHour;
  
      if ( t >= dst_start && t < dst_end ) {
          return msPerHour;
      } else {
          return 0;
      }
  
      // Daylight Savings Time starts on the first Sunday in April at 2:00AM in
      // PST.  Other time zones will need to override this function.
  
      print( new Date( UTC(dst_start + LocalTZA())) );
  
      return UTC(dst_start  + LocalTZA());
  }
  function GetFirstSundayInApril( t ) {
      var year = YearFromTime(t);
      var leap = InLeapYear(t);
  
      var april = TimeFromYear(year) + TimeInMonth(0, leap) + TimeInMonth(1,leap) +
      TimeInMonth(2,leap);
  
      for ( var first_sunday = april; WeekDay(first_sunday) > 0;
          first_sunday += msPerDay )
      {
          ;
      }
  
      return first_sunday;
  }
  function GetLastSundayInOctober( t ) {
      var year = YearFromTime(t);
      var leap = InLeapYear(t);
  
      for ( var oct = TimeFromYear(year), m = 0; m < 9; m++ ) {
          oct += TimeInMonth(m, leap);
      }
      for ( var last_sunday = oct + 30*msPerDay; WeekDay(last_sunday) > 0;
          last_sunday -= msPerDay )
      {
          ;
      }
      return last_sunday;
  }
  function LocalTime( t ) {
      return ( t + LocalTZA() + DaylightSavingTA(t) );
  }
  function MakeTime( hour, min, sec, ms ) {
      if ( isNaN( hour ) || isNaN( min ) || isNaN( sec ) || isNaN( ms ) ) {
          return Number.NaN;
      }
  
      hour = ToInteger(hour);
      min  = ToInteger( min);
      sec  = ToInteger( sec);
      ms   = ToInteger( ms );
  
      return( (hour*msPerHour) + (min*msPerMinute) +
              (sec*msPerSecond) + ms );
  }
  function MakeDay( year, month, date ) {
      if ( isNaN(year) || isNaN(month) || isNaN(date) ) {
          return Number.NaN;
      }
      year = ToInteger(year);
      month = ToInteger(month);
      date = ToInteger(date );
  
      var sign = ( year < 1970 ) ? -1 : 1;
      var t =    ( year < 1970 ) ? 1 :  0;
      var y =    ( year < 1970 ) ? 1969 : 1970;
  
      var result5 = year + Math.floor( month/12 );
      var result6 = month % 12;
  
      if ( year < 1970 ) {
         for ( y = 1969; y >= year; y += sign ) {
           t += sign * TimeInYear(y);
         }
      } else {
          for ( y = 1970 ; y < year; y += sign ) {
              t += sign * TimeInYear(y);
          }
      }
  
      var leap = InLeapYear( t );
  
      for ( var m = 0; m < month; m++ ) {
          t += TimeInMonth( m, leap );
      }
  
      if ( YearFromTime(t) != result5 ) {
          return Number.NaN;
      }
      if ( MonthFromTime(t) != result6 ) {
          return Number.NaN;
      }
      if ( DateFromTime(t) != 1 ) {
          return Number.NaN;
      }
  
      return ( (Day(t)) + date - 1 );
  }
  function TimeInMonth( month, leap ) {
      // september april june november
      // jan 0  feb 1  mar 2  apr 3   may 4  june 5  jul 6
      // aug 7  sep 8  oct 9  nov 10  dec 11
  
      if ( month == 3 || month == 5 || month == 8 || month == 10 ) {
          return ( 30*msPerDay );
      }
  
      // all the rest
      if ( month == 0 || month == 2 || month == 4 || month == 6 ||
           month == 7 || month == 9 || month == 11 ) {
          return ( 31*msPerDay );
       }
  
      // save february
      return ( (leap == 0) ? 28*msPerDay : 29*msPerDay );
  }
  function MakeDate( day, time ) {
      if (    day == Number.POSITIVE_INFINITY ||
              day == Number.NEGATIVE_INFINITY ||
              day == Number.NaN ) {
          return Number.NaN;
      }
      if (    time == Number.POSITIVE_INFINITY ||
              time == Number.POSITIVE_INFINITY ||
              day == Number.NaN) {
          return Number.NaN;
      }
      return ( day * msPerDay ) + time;
  }
  function TimeClip( t ) {
      if ( isNaN( t ) ) {
          return ( Number.NaN );
      }
      if ( Math.abs( t ) > 8.64e15 ) {
          return ( Number.NaN );
      }
  
      return ( ToInteger( t ) );
  }
  function ToInteger( t ) {
      t = Number( t );
  
      if ( isNaN( t ) ){
          return ( Number.NaN );
      }
      if ( t == 0 || t == -0 ||
           t == Number.POSITIVE_INFINITY || t == Number.NEGATIVE_INFINITY ) {
           return 0;
      }
  
      var sign = ( t < 0 ) ? -1 : 1;
  
      return ( sign * Math.floor( Math.abs( t ) ) );
  }
  function Enumerate ( o ) {
      var properties = new Array();
      for ( p in o ) {
         properties[ properties.length ] = new Array( p, o[p] );
      }
      return properties;
  }
  function AddTestCase( description, expect, actual ) {
      testcases[tc++] = new TestCase( SECTION, description, expect, actual );
  }
  
  function getFailedCases() {
    for ( var i = 0; i < testcases.length; i++ ) {
       if ( ! testcases[i].passed ) {
          print( testcases[i].description +" = " +testcases[i].actual +" expected: "+ testcases[i].expect );
       }
    }
  }
  