/* -------------------------------------------------------------------------- */
/*			    T O P _ C O N T R O L . J S			      */
/*			    ===========================			      */
/*									      */
/*   This file contains the JavaScript sources which are included into the    */
/*   "<HEAD>" element of the HTML file "top_control.html". See the header     */
/*   of that file for more information.					      */
/*									      */
/*		Authors:  Mark Jansen					      */
/*			  Hans Blom					      */
/* -------------------------------------------------------------------------- */


/*
 * Define the constants which are used to denote the type of color scaling for
 * the ping and throughput values.
 */

var LINEAR = 0;    // Scale colors linear.
var LOG    = 1;    // Scale colors logarithmic.

/*
 * The following global variables are parameters for all tables.
 */

var max_load_lower_bound = 2.0;   // Min. boundary for max. load value.
var max_nr_date_rows	 = 24;    // Max. # value rows in most tables.
var cookie_expire_days	 = 30;	  // The # days before cookies do expire.
var nr_days		 = 7;     // The # days in the most recent data file.
var start_date_ind	 = -1;	  // Start (most recent) date index.
var date_ind_incr	 = 1;	  // Increment value date indices.

/*
 * The following global variables are locally used in the tables, but they are
 * inquired from the Help documentation pages.
 */

var nr_skip_hosts = 0;    // The # hosts skipped in the last table viewed.

/*
 * Do not use full colors when the pixel depth of the screen is defined and when
 * it is too low.
 */

var full_color;

if (window.screen && window.screen.pixelDepth && window.screen.pixelDepth <= 8)
    full_color = false;
else
    full_color = true;

/*
 * Set the flag which controls the use of gray values. Useful for printing or to
 * include this page in a document.
 */

var use_gray_values = false;

/*
 * The following global variables are used in all tables.
 */

var color_map	       = new ColorMap ();   // Intens. colormap obj.
var help_window	       = null;		    // Help window variable.
var help_width	       = 0;		    // Width help window.
var help_height	       = 0;		    // Height help window.
var import_data_window = null;		    // Window to import data from file.
var import_data_width  = 0;		    // Width import data window.
var import_data_height = 0;		    // Height import data window.
var select_date_window = null;		    // Window with date selector.
var select_date_width  = 0;		    // Width date selector window.
var select_date_height = 0;		    // Height date selector window.
var plot_width	       = 0;		    // Width Applet plot window.
var plot_height	       = 0;		    // Height Applet plot window.

/*
 * Set the flag which determines if the net data dates should also be read. This
 * is only NOT the case when the net data are NOT read by the Applet immediately
 * after the data files are loaded: this IS the case when a local net data file
 * is imported to the special, local version of this package.
 */

var read_dates = true;    // SET ALSO IN APPLET ("ReadDates").

/*
 * Import a local data file to the special, local version of this packages when
 * the dates are NOT read.
 */

var import_data = !read_dates;

/*
 * The following global variables define the net types used.
 */

var qos_key   = "QoS";     // Quality of Service key.
var inet_key  = "Inet";    // Default Internet key.

/*
 * Set the flag which determines if documentation is available about the hosts
 * used.
 */

var is_hosts_doc = true;

/*
 * Set the original hosts title. It will be used for the imported version to
 * check if the data file was loaded from the same hosts set as the imported
 * version resides.
 */

var orig_hosts_title = "DAS-3";

/*
 * Declare some variables which are dependent from the net data and will be
 * initialised by it.
 */

var hosts_title;   // The title of the set hosts involved in the net tests.
var date_step;     // The # minutes between two dates.
var one_direct;	   // One-, or two-directional net performance tests flag.
var nr_directs;    // The # directions, dependent from one- / tow-direct. tests.
var use_qos;	   // Flag to denote if also QoS tests are performed.
var qos_title;	   // The title of the Quality-of-Service tests.
var inet_title;	   // The title of the tests along the "usual" Internet route.
var use_udp_bw;	   // Flag to denote if also UDP bandwidth tests should be done.

/*
 * Set the initial date step. It is required for setting the date in the top
 * control frame.
 */

var init_date_step = 30; // [m]

/*
 * Set the variable which denotes if initially UDP bandwidth tests should be
 * performed. It is required for defining the buttons in the top control frame.
 */

var init_use_udp_bw = true;

/*
 * Set the maximum boundaries which are used to detect if data there are
 * available for the corresponding data type.
 */

var conn_skip_bound = -5.0; // Net test skipped for conn. src_host => dest_host.
var no_data_bnd     =  0.0; // No data available.

/*
 * Define the indices used in the scroll size select.
 */

var scroll_title_index	    = 0;   // Index of the scroll title option.
var scroll_line_index	    = 1;   // Scroll a line option index. Default index.
var scroll_half_table_index = 2;   // Scroll a half table option index.
var scroll_table_index	    = 3;   // Scroll a table option index.
var nr_scroll_indices	    = 4;   // The # scroll indices.

/*
 * Set the default selected index and the scroll size.
 */

var scroll_select_index = scroll_line_index;
var scroll_size		= 1;

/*
 * Set the basename of the package.
 */

var package_base = "RTPL";

/*
 * Set the various ZIP compressed data file names and the data base names.
 */

var recent_data_file = "data/net_data.zip";	     // Data file last # days.
var week_base	     = "data/week/week_";	     // Week data files base.
var week_mean_file   = "data/week/week_mean.zip";    // Week mean data file.
var day_mean_file    = "data/day/day_mean.zip";	     // Day mean data file.
var quarter_base     = "data/quarter/weekday_mean_"; // Quarterly time mean bse.
var month_base	     = "data/month/workday_mean_";   // Monthly time mean base.

/*
 * Define the various net data type indices.
 */

var recent_data_index	 = 0;   // Data of the most recent days.
var week_data_index	 = 1;   // Data of a specified week.
var week_mean_data_index = 2;   // Data of the week mean values.
var day_mean_data_index	 = 3;   // Data of the day mean values.
var quarter_data_index	 = 4;   // Data of the quarter time weekday mean values.
var month_data_index	 = 5;   // Data of the month time workday mean values.
var nr_data_types	 = 6;   // The available # data types.

/*
 * Allocate the array for the net data descriptors and initialise these
 * desriptors.
 */

var net_data_descrs = new Array (nr_data_types);

net_data_descrs [recent_data_index] = new NetDataDescriptor (recent_data_file,
							     "Time",
							     "",
							     1,
							     "");

net_data_descrs [week_data_index] = new NetDataDescriptor (week_base,
							   "Time",
							   "",
							   1,
							   "Week");

net_data_descrs [week_mean_data_index] = new NetDataDescriptor (week_mean_file,
								"Week",
								"Week ",
								0,
								"");

net_data_descrs [day_mean_data_index] = new NetDataDescriptor (day_mean_file,
							       "Day",
							       "Day ",
							       0,
							       "");

net_data_descrs [quarter_data_index] = new NetDataDescriptor (quarter_base,
							      "Time",
							      "",
							      1,
							      "Quarter");

net_data_descrs [month_data_index] = new NetDataDescriptor (month_base,
							    "Time",
							    "",
							    1,
							    "Month");

/*
 * Initialise the current net data index with the data of the most recent days.
 * Also set an accelerator for the current net data descriptor and the current
 * data file to the file with current data.
 */

var data_type_index = recent_data_index;
var current_data    = net_data_descrs [data_type_index];
var data_file	    = recent_data_file;

/*
 * Initialise the new net data index from the data type going to load.
 */

var new_data_type_index = -1;

/*
 * Initialise the index of the selected data from the data selector window.
 */

var select_data_index = -1;

/*
 * Define the various ping types.
 */

var ping_min_type  = 0;   // Show minimum ping values.
var ping_avg_type  = 1;   // Show average ping values.
var ping_max_type  = 2;   // Show maximum ping values.
var ping_all_type  = 3;   // Show all ping types as.
var ping_lost_type = 4;   // Show the percentage ping packages lost.
var nr_ping_types  = 5;   // The # available ping types. Used to alloc. storage.

/*
 * Define the default ping type.
 */

var ping_default_type = ping_min_type;

/*
 * Set the variable which contains the current ping type to the default type.
 */

var ping_type = ping_default_type;

/*
 * Initialise the array with the (long) ping type titles.
 */

var ping_type_titles = new Array (nr_ping_types);

ping_type_titles [ping_min_type ] = "Minimum";
ping_type_titles [ping_avg_type ] = "Average";
ping_type_titles [ping_max_type ] = "Maximum";
ping_type_titles [ping_all_type ] = "All";
ping_type_titles [ping_lost_type] = "Packages Lost";

/*
 * Initialise the array with the (short) ping type titles.
 */

var ping_type_short_titles = new Array (nr_ping_types);

ping_type_short_titles [ping_min_type ] = "Min";
ping_type_short_titles [ping_avg_type ] = "Avg";
ping_type_short_titles [ping_max_type ] = "Max";
ping_type_short_titles [ping_all_type ] = "All";
ping_type_short_titles [ping_lost_type] = "Lost";

/*
 * Initialise the array with the (short) ping type labels.
 */

var ping_type_labels = new Array (nr_ping_types);

ping_type_labels [ping_min_type ] = "min";
ping_type_labels [ping_avg_type ] = "avg";
ping_type_labels [ping_max_type ] = "max";
ping_type_labels [ping_all_type ] = "all";
ping_type_labels [ping_lost_type] = "lost";

/*
 * Initialise the array with the ping type units.
 */

var ping_type_units = new Array (nr_ping_types);

ping_type_units [ping_min_type ] = "[ms]";
ping_type_units [ping_avg_type ] = "[ms]";
ping_type_units [ping_max_type ] = "[ms]";
ping_type_units [ping_all_type ] = "[ms]";
ping_type_units [ping_lost_type] = "[%]";

/*
 * Define the various UDP bandwidth types.
 */

var udp_bw_rate_type = 0;    // Show UDP bandwidth receive data rate.
var udp_bw_lost_type = 1;    // Show UDP bandwidth percentage frames lost.
var nr_udp_bw_types  = 2;    // The # available UDP bandwidth types. Used to
			     // allocate storage.

/*
 * Define the default UDP bandwidth type.
 */

var udp_bw_default_type = udp_bw_rate_type;

/*
 * Set the variable which contains the current UDP bandwidth type to the default
 * type.
 */

var udp_bw_type = udp_bw_default_type;

/*
 * Initialise the array with the (long) UDP bandwidth type titles.
 */

var udp_bw_type_titles = new Array (nr_udp_bw_types);

udp_bw_type_titles [udp_bw_rate_type] = "Receive Data Rate";
udp_bw_type_titles [udp_bw_lost_type] = "Percentages Frames Lost";

/*
 * Initialise the array with the (short) UDP bandwidth type titles.
 */

var udp_bw_type_short_titles = new Array (nr_udp_bw_types);

udp_bw_type_short_titles [udp_bw_rate_type] = "Data Rate";
udp_bw_type_short_titles [udp_bw_lost_type] = "Frames Lost";

/*
 * Initialise the array with the (short) UDP bandwidth type labels.
 */

var udp_bw_type_labels = new Array (nr_udp_bw_types);

udp_bw_type_labels [udp_bw_rate_type] = "rate";
udp_bw_type_labels [udp_bw_lost_type] = "lost";

/*
 * Initialise the array with the UDP bandwidth type units.
 */

var udp_bw_type_units = new Array (nr_udp_bw_types);

udp_bw_type_units [udp_bw_rate_type] = "[Mbit/s]";
udp_bw_type_units [udp_bw_lost_type] = "[%]";

/*
 * Initialise the array which converts a digit to a hexadecimal string.
 */

var dig_to_hex_str = new Array (16);

dig_to_hex_str [ 0] = "0";
dig_to_hex_str [ 1] = "1";
dig_to_hex_str [ 2] = "2";
dig_to_hex_str [ 3] = "3";
dig_to_hex_str [ 4] = "4";
dig_to_hex_str [ 5] = "5";
dig_to_hex_str [ 6] = "6";
dig_to_hex_str [ 7] = "7";
dig_to_hex_str [ 8] = "8";
dig_to_hex_str [ 9] = "9";
dig_to_hex_str [10] = "A";
dig_to_hex_str [11] = "B";
dig_to_hex_str [12] = "C";
dig_to_hex_str [13] = "D";
dig_to_hex_str [14] = "E";
dig_to_hex_str [15] = "F";

/*
 * The following global variable is used in the ping, throughput and UDP
 * bandwidth (when used) tables to denote the fixed host from which the
 * connection data are shown. Default the first host is always shown.
 */

var index_fixed_host = 0;

/*
 * Start with a linear color scaling of the ping, throughput and UDP bandwidth
 * values.
 */

var ping_scale_type	  = LINEAR;
var throughput_scale_type = LINEAR;
var udp_bw_scale_type	  = LINEAR;

/*
 * Set the minimum ping, throughput and UDP bandwidth values when logarithmic
 * scaling is used when the minimum value is equal to zero.
 */

var min_log_ping       = 1.0;
var min_log_throughput = 1.0;
var min_log_udp_bw     = 1.0;

/*
 * An intensity is written in white when the green component is below
 * the specified boundary. If not the intensity is written in black. See
 * also the class "ColorScale".
 */

var green_white_fgnd_bnd = 160;

/*
 * The following global variables are instances of the classes which
 * are writing the table documents. All these classes do implement
 * "write (date_ind, need_plot)".
 */

var net_data_overview_document = new NetDataOverviewDocument ();
var load_document	       = new LoadDocument	     ();
var ping_document	       = new PingDocument	     ();
var throughput_document        = new ThroughputDocument      ();
var udp_bw_document	       = new UDPBWDocument	     ();

/*
 * Set the corresponding ID's, used for the cookies file.
 */

var overview_id   = 0;
var load_id       = 1;
var ping_id       = 2;
var throughput_id = 3;
var udp_bw_id	  = 4;

/*
 * Initialise the current document.
 */

var current_document = net_data_overview_document;

/*
 * Default do not allow cookies: the user can authorise us to do so when no
 * cookies are found yet.
 */

var allow_cookies = false;

/*
 * Set the names of the cookies used to store the global table settings.
 */

var document_type_cookie       = "DocumentType";
var index_fixed_host_cookie    = "IndexFixedHost";
var scroll_select_index_cookie = "ScrollSelectIndex";
var max_nr_date_rows_cookie    = "MaxNrDateRows";
var ping_type_cookie	       = "PingType";
var udp_bw_type_cookie	       = "UDPBWType";

/*
 * Set the names of the cookies used to store the various window sizes.
 */

var help_width_cookie	      = "HelpWidth";
var help_height_cookie	      = "HelpHeight";
var import_data_width_cookie  = "ImportDataWidth";
var import_data_height_cookie = "ImportDataHeight";
var select_date_width_cookie  = "SelectDateWidth";
var select_date_height_cookie = "SelectDateHeight";
var plot_width_cookie	      = "PlotWidth";
var plot_height_cookie	      = "PlotHeight";

/*
 * Set the name of the cookie of the height of the top control window. Please
 * note that this cookie will only be read from the HTML file containing this
 * frame set, so it will only be set here when the user allows it.
 */

var top_control_height_cookie = "TopControlHeight";

/*
 * Set the global instance of the "RepeatWrite" class. It is used to repeatedly
 * reload the HTML page with the net data.
 */

var repeat_write = new RepeatWrite ();

/*
 * Get the global values, stored in the cookies file.
 */

getCookies ();

/*
 * Reset the current cookies to update the expire time for all cookies.
 */

setCookies ();

/**
 * Define a function to get the global values, stored in the cookies file.
 *
*********************/
function getCookies ()
/********************/
{
    /*
     * Get cookie value of the document index to start.
     */
     
    var new_document_id = getCookie (document_type_cookie);

    if (new_document_id != null)    // Cookie was defined.
    {
	if (new_document_id == overview_id)
	    current_document = net_data_overview_document;
	else if (new_document_id == load_id)
	    current_document = load_document;
	else if (new_document_id == ping_id)
	    current_document = ping_document;
	else if (new_document_id == throughput_id)
	    current_document = throughput_document;
	else if (new_document_id == udp_bw_id)
	    current_document = udp_bw_document

	allow_cookies = true;    // Cookies were found: are allowed to write.
    }

    /*
     * Get the fixed host index cookie, if any.
     */

    index_fixed_host = getCookieOrValue (index_fixed_host_cookie,
					 index_fixed_host);

    /*
     * Get the scroll select index cookie, if any and wel defined.
     */

    var new_scroll_select_index = getCookie (scroll_select_index_cookie);

    if ( new_scroll_select_index != null  &&
	 new_scroll_select_index >  0	  &&
	 new_scroll_select_index <  nr_scroll_indices )
    {
	scroll_select_index = new_scroll_select_index;
	allow_cookies	    = true;
    }

    /*
     * Get the max # date rows cookie, if any and wel defined.
     */

    var new_max_nr_date_rows = getCookie (max_nr_date_rows_cookie);

    if (new_max_nr_date_rows != null  &&  new_max_nr_date_rows > 0)
    {
	max_nr_date_rows = new_max_nr_date_rows;
	allow_cookies	 = true;
    }

    /*
     * Get the ping type cookie, if any. Check the boundaries.
     */

    var new_ping_type = getCookie (ping_type_cookie);

    if ( new_ping_type != null  &&
	 new_ping_type >= 0	&&
	 new_ping_type <  nr_ping_types )
    {
	ping_type     = new_ping_type;
	allow_cookies = true;
    }

    /*
     * Get the UDP bandwidth type cookie, if any. Check the boundaries.
     */

    var new_udp_bw_type = getCookie (udp_bw_type_cookie);

    if ( new_udp_bw_type != null  &&
	 new_udp_bw_type >= 0	  &&
	 new_udp_bw_type <  nr_udp_bw_types )
    {
	udp_bw_type   = new_udp_bw_type;
	allow_cookies = true;
    }

    /*
     * Get the help window size cookies, when set.
     */

    help_width  = getCookieOrValue (help_width_cookie,  help_width);
    help_height = getCookieOrValue (help_height_cookie, help_height);

    /*
     * Get the import data window size cookies, when set.
     */

    import_data_width  = getCookieOrValue (import_data_width_cookie,
					   import_data_width);
    import_data_height = getCookieOrValue (import_data_height_cookie,
					   import_data_height);

    /*
     * Get the select date window size cookies, when set.
     */

    select_date_width  = getCookieOrValue (select_date_width_cookie,
					   select_date_width);
    select_date_height = getCookieOrValue (select_date_height_cookie,
					   select_date_height);

    /*
     * Get the Applet plot window size cookies, when set.
     */

    plot_width  = getCookieOrValue (plot_width_cookie,  plot_width);
    plot_height = getCookieOrValue (plot_height_cookie, plot_height);

} // getCookies ()

/**
 * Get a cookie or use the current value of the corresponding variable when
 * undefined. When a cookie is obtained, set "allow_cookies" to mark that
 * cookies were allowed.
 *
 *    @param  name    The name of the cookie.
 *
 *    @param  value   The current value of the corresponding variable.
 *
 *    @return  The value obtained from the cookie or the current value of the
 *	       corresponding variable.
 *
**************************************/
function getCookieOrValue (name, value)
/*************************************/
{
    var new_value = getCookie (name);

    if (new_value == null)    // Cookie not set.
    {
	return value;    // Use current value.
    }
    else    // Cookie set.
    {
	allow_cookies = true;    // Cookie found, also allowed.

	return new_value;    // Use new value.
    }

} // getCookieOrValue ()

/**
 * Set all cookies.
 *
*********************/
function setCookies ()
/********************/
{
    setDocumentTypeCookie ();

    setCookie (index_fixed_host_cookie,	   index_fixed_host);
    setCookie (scroll_select_index_cookie, scroll_select_index);
    setCookie (max_nr_date_rows_cookie,	   max_nr_date_rows);
    setCookie (ping_type_cookie,	   ping_type);
    setCookie (udp_bw_type_cookie,	   udp_bw_type);

    /*
     * Set the widow size cookies only when the sizes of the corresponding
     * window are larger than zero.
     */

    if (help_width > 0  &&  help_height > 0)
    {
	setCookie (help_width_cookie,  help_width);
	setCookie (help_height_cookie, help_height);
    }

    if (import_data_width > 0  &&  import_data_height > 0)
    {
	setCookie (import_data_width_cookie,  import_data_width);
	setCookie (import_data_height_cookie, import_data_height);
    }

    if (select_date_width > 0  &&  select_date_height > 0)
    {
	setCookie (select_date_width_cookie,  select_date_width);
	setCookie (select_date_height_cookie, select_date_height);
    }

    if (plot_width > 0  &&  plot_height > 0)
    {
	setCookie (plot_width_cookie,  plot_width);
	setCookie (plot_height_cookie, plot_height);
    }

    /*
     * Set the height of the top control window cookie.
     */

    setTopControlHeightCookie ();

} // setCookies ()

/**
 * Define a function to request for a confirmation to allow cookies. After
 * confirmation, update also the expire time my resetting the cookies and
 * rewrite the document without the authorisation link.
 *
 *    @param  date_ind   The index of the date at the Applet which net data are
 *			 displayed.
 *
*********************************/
function doAllowCookies (date_ind)
/********************************/
{
    if (confirm ("Do you authorise to store the current\n" +
		 "table settings in your cookies file?"))
    {
	allow_cookies = true;

	setCookies	       ();
	current_document.write (date_ind, false);
    }
}

/**
 * Define a function to set the document type cookie.
 *
********************************/
function setDocumentTypeCookie ()
/*******************************/
{
    if (current_document == net_data_overview_document)
	setCookie (document_type_cookie, overview_id);
    else if (current_document == load_document)
	setCookie (document_type_cookie, load_id);
    else if (current_document == ping_document)
	setCookie (document_type_cookie, ping_id);
    else if (current_document == throughput_document)
	setCookie (document_type_cookie, throughput_id);
    else if (current_document == udp_bw_document)
	setCookie (document_type_cookie, udp_bw_id);
}

/**
 * Define a function to set the height of the top control window cookie, but
 * only when:
 *
 *   1. The height of the control window is larger than zero.
 *
 *   2. The browser properties are defined.
 *
 *   3. The setting is allowed for the current browser. Disallowed are:
 *	  o Netscape 4 because the top control height is incorrect.
 *
************************************/
function setTopControlHeightCookie ()
/***********************************/
{
    if ( window.innerHeight  &&  window.innerHeight > 0
         &&
	 navigator
	 &&
	 navigator.appName
	 &&
	 ( navigator.appName != "Netscape"
	   ||
	   navigator.appVersion
	   &&
	   navigator.appVersion.substring (0, 1) != "4") )
    {
	setCookie (top_control_height_cookie, window.innerHeight);
    }

} // setControlWindowHeightCookie()

/**
 * This function defines the constructor for the net data descriptor object.
 * This object contains various variables depending from the net data type.
 * Besides the values described below the following variables are also set:
 *
 *    table_date_step	    The date time step to be used in the net data
 *			    tables. For some net data this will be set from the
 *			    net data read by the Applet.
 *
 *    table_date_ind_incr   The current time interval index for this net data
 *			    type. It is defaulted to 1.
 *
 * Below do follow the description of the constructor arguments.
 *
 *    @param  file_or_base	       The file name of file base when there is
 *				       a serie of file names.
 *
 *    @param  time_or_date_id_title    The title which characterises the used
 *                                     time or date ID.
 *
 *    @param  time_or_date_id_prefix   The prefix to be placed before the time
 *				       or date ID. May be an empty string.
 *
 *    @param  table_date_step	       The date time step to be used in the net
 *				       data tables.
 *
 *    @param  table_min_select_index   The index to be used in the time interval
 *				       selector for the net data table.
 *
 *    @param  select_title	       The title of the date selector, if any.
 *				       If unused an empty string is used.
 *
**************************************************************************/
function NetDataDescriptor (file_or_base, time_or_date_id_title,
			    time_or_date_id_prefix, table_min_select_index,
			    select_title)
/*************************************************************************/
{
    this.file_or_base		= file_or_base;
    this.time_or_date_id_title	= time_or_date_id_title;
    this.time_or_date_id_prefix = time_or_date_id_prefix;
    this.table_min_select_index	= table_min_select_index;
    this.table_select_index	= table_min_select_index;
    this.select_title		= select_title;
    this.table_date_step	= 0;
    this.table_date_ind_incr    = 1;

} // function NetDataDescriptor ()

/**
 * This function defines the constructor for the intensity colormap
 * object. It defines a color depending from the specified intensity.
 * To be able to do this the user is required to specify the intensity
 * extrema. The object "ColorScale" is used to determine the RGB values
 * from the intensities.
 *
*******************/
function ColorMap ()
/******************/
{
    /*
     * This variable defines the maximum allowed RGB color intesity.
     */

    var max_color = 0xFF;

    /*
     * Define the color table used to scale the specified intensity
     * values. Linear scaling between the marker colors defined below is
     * used to retrieve the colors from the intensties. It is possible to use
     * gray color values (useful for printing in a document), ranging from
     * black (minimum intensity) to white (maximum intensity). Default "real"
     * colors are used ranging from blue (minimum intensity) to red (maximum
     * intensity). The RGB objects, used to store the RGB values, are defined
     * below.
     */

    if (use_gray_values)    // Use gray values from zero to "max_color".
    {
	var nr_grays    = 5;
	var color_table = new Array (nr_grays);
	var i_gray;

	color_table [0] = new RGB (0, 0, 0);

	for (i_gray = 1;  i_gray < nr_grays - 1;  i_gray++)
	{
	    var intens = Math.round ((i_gray * max_color) / (nr_grays - 1));

	    color_table [i_gray] = new RGB (intens, intens, intens);
	}

	color_table [nr_grays - 1] = new RGB (max_color, max_color, max_color);
    }
    else    // Colors ranging from blue to red.
    {
	var color_table = new Array (5);

	color_table [0] = new RGB (        0,         0, max_color);    //blue
	color_table [1] = new RGB (        0, max_color, max_color);    //cyan
	color_table [2] = new RGB (        0, max_color,         0);    //green
	color_table [3] = new RGB (max_color, max_color,         0);    //yellow
	color_table [4] = new RGB (max_color,         0,         0);    //red
    }

    /*
     * Define the class instances and class functions.
     */

    this.max_color   = max_color;	      // Max RGB value.
    this.color_table = color_table;	      // Color table.
    this.writeTable  = writeColorMapTable;    // Method to write color table.
    this.openTable   = openColorMapTable;     // Method to open color table.
    this.writeRow    = writeColorMapRow;      // Method to write color row.
    this.closeTable  = closeColorMapTable;    // Method to close color table.

} // function ColorMap ()

/**
 * This function writes the color table using an HTML TABLE element. The
 * map is displayed with the marker colors and the intermediate colors.
 * This function is a member function of the "ColorMap" class. Its class
 * name is "writeTable".
 *
 *    @param  title            The title of the specified intensities.
 *                             It is printed in the first column which
 *                             spans over the # color rows.
 *
 *    @param  extrema          The extrema of the intensity values.
 *
 *    @param  nr_color_rows    The # rows the color columns should span.
 *
 *    @param  scale_factor     The scale factor used to round the
 *                             intensities. It may be constructed with:
 *                                10.0^<NrDecimals>.
 *
**********************************************************/
function writeColorMapTable (title, extrema, nr_color_rows,
			     scale_factor)
/*********************************************************/
{
    /*
     * Write the open colormap table HTML lines.
     */

    this.openTable ();

    /*
     * Write the colormap columns (with one or more color rows).
     */

    this.writeRow (title, extrema, nr_color_rows, scale_factor);

    /*
     * Write the close colomap table HTML lines.
     */

    this.closeTable ();

} // function writeColorMapTable ()

/**
 * This function writes the opening of a color map table. It is a member
 * function of the "ColorMap" class. Its class name is "openTable".
 *
****************************/
function openColorMapTable ()
/***************************/
{
    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n"
	);
}

/**
 * This function writes the used color scaling from the intensity
 * ranging defined by the specified intensity extrema. The colormap is
 * displayed with the marker colors and with the intermediate colors.
 * This function is a member function of the "ColorMap" class. Its class
 * name is "writeRow".
 *
 *    @param  title	       The title of the specified intensities.
 *			       It is printed in the first column which
 *			       spans over the # color rows.
 *
 *    @param  extrema	       The extrema of the intensity values.
 *
 *    @param  nr_color_rows    The # rows the color columns should span.
 *
 *    @param  scale_factor     The scale factor used to round the
 *			       intensities. It may be constructed with:
 *			          10.0^<NrDecimals>.
 *
**********************************************************************/
function writeColorMapRow (title, extrema, nr_color_rows, scale_factor)
/*********************************************************************/
{
    /*
     * Calculate the total # color colums and the # color columns / row.
     */

    var color_table	  = this.color_table;
    var nr_tot_color_cols = 2 * color_table.length - 1;
    var nr_color_cols	  = Math.floor ( ( nr_tot_color_cols +
     					   nr_color_rows - 1
					 ) / nr_color_rows
				       );

    /*
     * Calculate the total color columns in the table.
     */

    var nr_table_color_cols = nr_color_cols * nr_color_rows;

    /*
     * Set the (logarithmic / linear) scaled extrema. Use the minimum assumed
     * resolution value when there are no extrema > 0.0 found with logarithmic
     * scaling.
     */

    var scale_min;
    var scale_max;

    if (extrema.scale_type == LINEAR)
    {
	scale_min = extrema.min;
	scale_max = extrema.max;
    }
    else    // LOG.
    {
	if (extrema.min == 0.0)    // No minimum > 0.0. use minimum resolution.
	{
	    scale_min = extrema.min_log_bnd;
	}
	else    // Minimum > 0.0 found.
	{
	    scale_min = Math.log (extrema.min);
	}

	if (extrema.max == 0.0)    // No maximum > 0.0. use minimum resolution.
	{
	    scale_max = extrema.min_log_bnd;
	}
	else    // Maximum > 0.0 found.
	{
	    scale_max = Math.log (extrema.max);
	}

    } // Logarithmic scaling.

    /*
     * Set the scale step used between the color columns.
     */

    var scale_step;
    
    if (nr_tot_color_cols > 1)
	scale_step = (scale_max - scale_min) / (nr_tot_color_cols - 1);
    else
	scale_step = scale_max - scale_min;

    /*
     * Declare (and initialise) some help variables.
     */

    var rgb_inter   = new RGB (0, 0, 0);
    var color_table = this.color_table;
    var i_max	    = nr_tot_color_cols - 1;
    var rgb;
    var rgb_next;
    var value;
    var i;

    /*
     * Open the colormap row with a new row and write the title in the
     * first column.
     */

    parent.NetData.document.write (
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n",
	"\n",
	"<TD ALIGN=\"left\"\n",
	"    ROWSPAN=\"", nr_color_rows, "\">", title, "</TD>\n"
	);

    /*
     * Write the color columns.
     */

    for (i = 0;  i < nr_tot_color_cols;  i++)
    {
	if (i == nr_color_cols)    // Start a new color row.
	{
	    parent.NetData.document.write (
		"</TR>\n",
		"\n",
		"<TR ALIGN=\"center\"\n",
		"    VALIGN=\"middle\">\n"
		);
	}

	/*
	 * Set & round the column intensity value.
	 */

	if (i == 0)
	    value = extrema.min;
	else if (i == i_max)
	    value = extrema.max;
	else
	{
	    value = scale_min + scale_step * i;  // Interpolate between extrema.

	    if (extrema.scale_type == LOG)   // Correct for logarithmic scaling.
		value = Math.exp (value);

	    /* Round the value in the specified interval. */

	    value = Math.round (value * scale_factor) / scale_factor;
	}

	if (i % 2 == 0)    // Handle a marker color.
	{
	    /*
	     * Set the marke RGB value and the next RGB value which is
	     * used at the next intermediate color.
	     */

	    rgb = color_table [i / 2];

	    if (i < i_max)   // No intermediate color after last marker.
	    {
		rgb_next = color_table [i / 2 + 1];
	    }

	    /*
	     * Write the marker intensity with the color baclground.
	     */

	    writeColorColumn (rgb, value, "");
	}
	else    // Handle an intermediate color.
	{
	    /*
	     * Interpolate the intermediate RGB values bewteen the
	     * current and the next RGB values.
	     */

	    rgb_inter.red   = Math.floor ( (rgb.red   + rgb_next.red  )
					   / 2 );
	    rgb_inter.green = Math.floor ( (rgb.green + rgb_next.green)
					   / 2 );
	    rgb_inter.blue  = Math.floor ( (rgb.blue  + rgb_next.blue )
					   / 2 );

	    /*
	     * Write a column with an intermediate color
	     */

	    writeColorColumn (rgb_inter, value, "");

	} // Handle an intermediate color.

    } // Write the color columns.

    /*
     * Fill the last row with empty columns when the modulus is
     * non-zero.
     */

    for (i = nr_tot_color_cols;  i < nr_table_color_cols;  i++)
	parent.NetData.document.write ("<TD>&nbsp;</TD>\n");

    /*
     * Close the colormap row.
     */

    parent.NetData.document.write (
	"\n",
	"</TR>\n"
	);

} // function writeColorMapRow ()

/**
 * This function closes the colormap table. It is a member function of
 * the "ColorMap" class. The class name of the function is "closeTable".
 *
*****************************/
function closeColorMapTable ()
/****************************/
{
    parent.NetData.document.write (
	"\n",
	"</TABLE>\n"
	);
}

/**
 * This function is the constructor of the object which is used to
 * calculate the RGB values from the specified intensities.
 *
 *    @param   extrema    RGB instance which specifies the extrema of
 *			  the intensities.
 *
****************************/
function ColorScale (extrema)
/***************************/
{
    /*
     * Define the class functions (F.) and instances (I.).
     */

    this.scale_type = extrema.scale_type;    // I. Linear or log. scaling type.
    this.scaleRGB   = scaleRGB;		     // F. Scale a RGB value.

    /*
     * Set the (logarithmic / linear) scaled extrema. Use the minimum assumed
     * resolution value when there are no extrema > 0.0 found with logarithmic
     * scaling.
     */

    if (this.scale_type == LINEAR)
    {
	this.scale_min = extrema.min;    // I. Intensity minimum.
	this.scale_max = extrema.max;    // I. Intensity maximum.
    }
    else    // LOG.
    {
	if (extrema.min == 0.0)    // No minimum > 0.0. use minimum resolution.
	{
	    this.scale_min = extrema.min_log_bnd;
	}
	else    // Minimum > 0.0 found.
	{
	    this.scale_min = Math.log (extrema.min);
	}

	if (extrema.max == 0.0)    // No maximum > 0.0. use minimum resolution.
	{
	    this.scale_max = extrema.min_log_bnd;
	}
	else    // Maximum > 0.0 found.
	{
	    this.scale_max = Math.log (extrema.max);
	}

    } // Logarithmic scaling.

    /*
     * Set the # intervals between the color table markes.
     */

    var nr_intvs = color_map.color_table.length - 1;

    if (nr_intvs < 1)    // Force at least one interval.
	nr_intvs = 1;

    /*
     * Define the scaling step per interval between two markers.
     */

    this.scale_step = (this.scale_max - this.scale_min) / nr_intvs;

} // function ColorScale ()

/**
 * Scale an RGB value from a specified intensity value.
 *
 *    @param  rgb_scale   The RGB object instance which is set.
 *
 *    @param  val	  The specified intensity value.
 *
*********************************/
function scaleRGB (rgb_scale, val)
/********************************/
{
    /*
     * Default to white when the intensity value is undefined.
     */

    if (val < 0.0)
    {
	var max_color   = color_map.max_color;

	rgb_scale.red   = max_color;
	rgb_scale.green = max_color;
	rgb_scale.blue  = max_color;

	return;    // Nothing to do anymore.
    }

    /*
     * Calculate the logarithm when this scale type is used.
     */

    if (this.scale_type == LOG)
    {
	if (val > 0.0)
	    val = Math.log (val);
	else
	    val = this.scale_min;
    }

    /*
     * Set some help variables.
     */

    var color_table = color_map.color_table;
    var nr_intvs    = color_table.length - 1;
    var max_intv    = nr_intvs - 1;
    var scale_step = this.scale_step;
    var color_intv;

    /*
     * Determine the used interval between two markers in the color
     * table from the intensity.
     */

    if (this.scale_step == 0)
    {
	color_intv = 0;
    }
    else
    {
	color_intv = Math.floor ( (val - this.scale_min) / scale_step );

	/*
	 * Adjust possible rounding errors.
	 */

	if (color_intv < 0)
	    color_intv = 0;
	else if (color_intv > max_intv)
	    color_intv = max_intv;
    }

    /*
     * 1. Calculate the "relative" intensity in the selected color
     *    interval in the range [0, <scale_step>].
     *
     * 2. Select the RGB values at the interval (for rel. intens. zero).
     *
     * 3. Select the RGB values at the next (interval (for rel. intens.
     *    <scale_step>.
     */

    var intv_val = val - this.scale_min - scale_step*color_intv;    // 1.
    var rgb_intv = color_table [color_intv  ];			    // 2.
    var rgb_next = color_table [color_intv+1];		            // 3.

    /*
     * Calculate the RGB values with linear interpolations of the
     * relative intensity when full color can be used; use only one
     * intermediate color between two colors in the color table
     * otherwise.
     */

    if (full_color)    // Use all available colors.
    {
	rgb_scale.red   = interpolateInt (rgb_intv.red, rgb_next.red,
					  intv_val, scale_step);
	rgb_scale.green = interpolateInt (rgb_intv.green, rgb_next.green,
					  intv_val, scale_step);
	rgb_scale.blue  = interpolateInt (rgb_intv.blue, rgb_next.blue,
					  intv_val, scale_step);
    }
    else    // Extend the colormap only with intermediate colors.
    {
	rgb_scale.red   = intermediateInt (rgb_intv.red, rgb_next.red,
					   intv_val, scale_step);
	rgb_scale.green = intermediateInt (rgb_intv.green, rgb_next.green,
					   intv_val, scale_step);
	rgb_scale.blue  = intermediateInt (rgb_intv.blue, rgb_next.blue,
					   intv_val, scale_step);
    }

} // function scaleRGB ()

/**
 * Write an intensity value in a table column where the background is
 * scaled in correspondence with the intensity value. The value is
 * written in white when the green value is below a threshold. If not it
 * is written in black.
 *
 *    @param  rgb	 The color scale RGB value.
 *
 *    @param  text	 The text to print.
 *
 *    @param  td_flags   Flags for the TD HTML column element.
 *
**********************************************/
function writeColorColumn (rgb, text, td_flags)
/*********************************************/
{
    /*
    var style_str = "background-color: " + rgb.toColorString ();

    if (rgb.green < green_white_fgnd_bnd)    // White foreground.
	style_str += "; color: #FFFFFF";

    parent.NetData.document.write (
	"<TD STYLE=\"", style_str, "\" ", td_flags, ">", text, "</TD>\n")
    */

    parent.NetData.document.write (
	"<TD BGCOLOR=\"", rgb.toColorString (), "\" ", td_flags, ">"
	);

    if (rgb.green < green_white_fgnd_bnd)    // White foreground.
	parent.NetData.document.write (
	    "<FONT COLOR=\"#FFFFFF\">", text, "</FONT>"
	    );
    else
	parent.NetData.document.write (text);    // Black foreground.

    parent.NetData.document.write ("</TD>\n");

} // function writeColorColumn ()

/**
 * Define the constructor for the RGB class. It is initiated with a
 * RGB value.
 *
 *    @param  red     The red intensity.
 *
 *    @param  green   The green intensity.
 *
 *    @param  blue    The blue intensity.
 *
******************************/
function RGB (red, green, blue)
/*****************************/
{
    this.red	       = red;
    this.green	       = green;
    this.blue	       = blue;
    this.toColorString = RGBToColorString;    // Set member function.

} // function RGB ()

/**
 * Convert a RGB object into a HTML color string and return it. The
 * format of the HTML color string is:
 *
 *		#<RR><GG><BB>.
 *
 * <RR>, <GG> and <BB> are two digit hexadecimal numbers.
 *
 * This function is a member function of the "RGB" class. Its class name
 * is "toColorString()".
 *
 *    @return  The HTML color string.
 *
***************************/
function RGBToColorString ()
/**************************/
{
    /*
     * Set the hexadecimal RGB values. Each of the RGB is individual converted
     * to a hexadecimal string which is used to compose the required color
     * string.
     */

    var red   = getRGBHexString (this.red);
    var green = getRGBHexString (this.green);
    var blue  = getRGBHexString (this.blue);

    return "#" + red + green + blue;    // Return the color string.

} // function RGBToColorString ()

/**
 * This function converts a RGB value into a hexadecimal string.
 *
 *    @param  rgb   The RGB value to convert
 *
 *    @return  The converted hexadecimal string.
 *
*****************************/
function getRGBHexString (rgb)
/****************************/
{
    if (rgb <= 0)      // Return the zero intensity string when the RGB value is
    {		       // too smaill.
	return "00";
    }
    else if (rgb >= 0xFF)    // Return the maximum intensity string when the RGB
    {			     // value is too large.
	return "FF";
    }
    else    // Return the both hexadecimal digits as string of the RGB value.
    {
	return dig_to_hex_str [Math.floor (rgb / 16)] +
	       dig_to_hex_str [rgb % 16];
    }

} // function getRGBHexString (rgb)

/**
 * Interpolate the float value <d_val> which is in the range
 *
 *		  [ 0, <d_range> ]
 *
 * in the integer range:
 *
 *		[ <i_start>, <i_end> ]
 *
 * and return the result.
 *
 *    @param  i_start   First value of the integer domain range.
 *
 *    @param  i_end     Last value of the integer domain range.
 *
 *    @param  d_val	The double value to interpolate.
 *
 *    @param  d_range	The last value of the double source range. The
 *			first value is always zero.
 *
 *    @return  The interpolated integer value.
 *
*******************************************************/
function interpolateInt (i_start, i_end, d_val, d_range)
/******************************************************/
{
    var i_min;
    var i_max;
    var i_val;

    if ( i_start == i_end  || // Return range int. val. if size is zero.
         d_range == 0 )       // or range is zero
    {
	return i_start;
    }
    else if (i_start < i_end)    // Set domain min and max values when
    {				 // <i_start> < <i_end>
	i_min = i_start;
	i_max = i_end;
    }
    else			 // Set domain min and max values when
    {				 // <i_start> > <i_end>. Adjust here
	i_min  = i_end;		 // the double source value.
	i_max  = i_start;
	d_val  = d_range - d_val;
    }

    /*
     * Interpolate the double source value.
     */

    i_val = i_min + Math.floor ((d_val * (i_max + 1 - i_min)) / d_range);

    /*
     * Correct for truncation errors and return the result.
     */

    if (i_val < i_min)
	return i_min;
    else if (i_val > i_max)
	return i_max;
    else
	return i_val;

} // function interpolateInt ()

/**
 * Map the float value <d_val> which is in the domain
 *
 *		  [ 0, <d_range> ]
 *
 * to the range
 *
 *		[ <i_start>, <i_end> ]
 *
 * as follows (when <d_range> > 0):
 *
 *	<d_val> < <d_range>/4			->  i_start
 *
 *	<d_range>/4 <= <d_val> < 3*<d_range>/4  ->  (i_start+i_end)/2
 *	3*<d_range>/4 <= <d_val> < <d_range>	->  i_end
 *
 * and in a comparable way when <d_range> < 0. Return the result.
 *
 *    @param  i_start   First value of the integer domain range.
 *
 *    @param  i_end     Last value of the integer domain range.
 *
 *    @param  d_val	The double value to interpolate.
 *
 *    @param  d_range	The last value of the double source range. The
 *			first value is always zero.
 *
 *    @return  The interpolated integer value.
 *
********************************************************/
function intermediateInt (i_start, i_end, d_val, d_range)
/*******************************************************/
{
    var i_min;
    var i_max;

    if ( i_start == i_end  || // Return range int. val. if size is zero.
         d_range == 0 )       // or range is zero
    {
	return i_start;
    }
    else if (i_start < i_end)    // Set domain min and max values when
    {				 // <i_start> < <i_end>
	i_min = i_start;
	i_max = i_end;
    }
    else			 // Set domain min and max values when
    {				 // <i_start> > <i_end>. Adjust here
	i_min  = i_end;		 // the double source value.
	i_max  = i_start;
	d_val  = d_range - d_val;
    }

    /*
     * Return the intervals specified above.
     */

    if (d_val < d_range / 4.0)
	return i_min;
    else if (d_val < 3.0 * d_range / 4.0)
	return Math.floor ((i_min + i_max) / 2);
    else
	return i_max;

} // function intermediateInt ()

/**
 * This function defines the constructor of an "Extrema" object. It is
 * instantiated with empty extrema (null strings). This class also contains the
 * scaling type (linear / logarithmic) and the minimum boundary for logarithmic
 * scaling. The reason is that with the linear scaling the values are >= 0.0,
 * but with logarithmic scaling the scaling values MUST be > 0.0.
 *
******************/
function Extrema ()
/*****************/
{
    this.min	     = 0.0;
    this.max	     = 0.0;
    this.scale_type  = LINEAR;
    this.log_min_bnd = 0.0;
    this.defined     = false;
    this.set         = setExtrema;    // Member function to set extrema.

} // function Extrema ()

/**
 * This function determines the extrema in the (double) array "values"
 * up to a maximum of <nr_values>, but only when <nr_values> is non-
 * zero. Elements of "values" which are equal to the empty string are
 * considered to be undetermined and they are therefore skipped. This
 * function is the member function "set()" of an instance of the
 * "Extrema" object. When uninitialised, the "Extrema" instance is
 * initialised. Else the array elements of "values" are compared with
 * the current extrema which are already stored in the "Extrema"
 * instance.
 *
 *    @param  values	    The array from which the extrema should be
 *			    calculated.
 *
 *    @param  nr_values     The possible # values used in the calculation.
 *
 *    @param  scale_type    Use linear or logarithmic scale.
 *
 *    @param  min_log_bnd   The minimum bounndary when logarithmic scaling is
 *			    used. It is used at logarithmic scaling when the
 *			    minimum value is zero.
 *
***************************************************************/
function setExtrema (values, nr_values, scale_type, min_log_bnd)
/**************************************************************/
{
    this.scale_type  = scale_type;
    this.min_log_bnd = min_log_bnd;

    /*
     * Correct <nr_values> when it is undetermined or larger than the
     * array length.
     */

    if (nr_values == 0  ||  nr_values > values.length)
	nr_values = values.length;

    var val_i;
    var i = 0;

    /*
     * Set the extrema instance to the first defined array element when
     * it is not yet defined.
     */

    while (i < nr_values  &&  !this.defined)
    {
	val_i = values [i];

	/*
	 * Require for logarithmic scaling that the values are larger than
	 * zero.
	 */

	if ( scale_type == LINEAR && val_i >= 0.0  ||
	     scale_type == LOG    && val_i >  0.0 )
	{
	    this.min	 = val_i;
	    this.max	 = val_i;
	    this.defined = true;
	}

	i++;
    }       

    /*
     * Determine the extrema from the array.
     */

    while (i < nr_values)
    {
	val_i = values [i];

	if ( scale_type == LINEAR && val_i >= 0.0  ||
	     scale_type == LOG    && val_i >  0.0 )
	{
	    if (val_i < this.min)
		this.min = val_i;
	    else if (val_i > this.max)
		this.max = val_i;
	}

	i++;
    }

} // function setExtrema ()

/**
 * This function is the constructor of the "RepeatWrite" class.
 *
**********************/
function RepeatWrite ()
/*********************/
{
    /*
     * Set the time out class variable as 1/5 of the measurement period
     * (specified in minutes), but never less than two minutes.
     */

    var min_time_out = 1 * 60 * 1000;    // Time out is in min * sec * milli.

    this.time_out = Math.floor ((init_date_step * 60 * 1000) / 5);

    if (this.time_out < min_time_out)    // Time out too small.
	this.time_out = min_time_out;

    /*
     * Set the other class variables and functions.
     */

    this.timer_set = false;		  // Timer not yet set.
    this.timer_id  = -1;		  // Timer value not yet set.
    this.set	   = setRepeatWrite;	  // Function to set the repeat write.
    this.repeat	   = doRepeatWrite;	  // Function to repeat the write.
    this.clear	   = clearRepeatWrite;	  // Function to clear the repeat write.

} // function RepeatWrite ()

/**
 * This function switches the repeat write on/off. It is the "set ()"
 * member fucntion of the "RepeatWrite" class.
 *
 *    @param  set_on   Sets the repeat write on / off.
 *
*******************************/
function setRepeatWrite (set_on)
/******************************/
{
    if (set_on)    // Switch on when not running.
    {
	if (!repeat_write.timer_set)
	{
	    repeat_write.timer_set = true;
	    repeat_write.repeat ();
	}
    }
    else    // Switch off when running.
    {
	if (repeat_write.timer_set)
	{
	    repeat_write.clear ();
	    repeat_write.timer_set = false;
	}
    }

} // function setRepeatWrite ()

/**
 * This function does a repeat write of the current document. It is the
 * "repeat ()" member function of the "RepeatWrite" class.
 *
************************/
function doRepeatWrite ()
/***********************/
{
    loadMostRecentData ();
    repeat_write.timer_id = setTimeout ("repeat_write.repeat()",
    				        repeat_write.time_out);
}

/**
 * This function clears the repeat write. It is the "clear ()" member
 * function of the "RepeatWrite" class.
 *
***************************/
function clearRepeatWrite ()
/**************************/
{
    clearTimeout (repeat_write.timer_id);
}

/**
 * Constructor of the PingData class.
 *
 *    @param  nr_data   The # data to store.
 *
**************************/
function PingData (nr_data)
/*************************/
{
    this.values	     = new Array (nr_data); // Value  represent. of ping data.
    this.strings     = new Array (nr_data); // String represent. of ping data.
    this.parse	     = parsePing;	    // Func. to parse an applet ping v.
    this.writeColumn = writeColumnPing;     // Func. to write a ping column val.
    
} // function PingData ()

/**
 * Obtain a ping value with the specified type from the Applet. It is returned
 * by the Applet as a string. It is stored both as a string and as a float.
 *
 *    @param  i		  The index of the 1th host used to get the ping value.
 *
 *    @param  j		  The index of the 2th host used to get the ping value.
 *
 *    @param  value_ind   The index where the ping value and its string
 *			  representation must be stored.
 *
 *    @param  date_ind    The index of the date at the Applet which ping value
 *			  must be obtained.
 *
 *    @param  ping_type   The type of the ping value.
 *
 *    @param  net_type    The index of the net traffic type.
 *
******************************************************************/
function parsePing (i, j, value_ind, date_ind, ping_type, net_type)
/*****************************************************************/
{
    var applet = document.applets ["NetData"];

    /*
     * Obtain the specified ping value from the Applet. Use the default ping
     * type as value representation when all ping values must be displayed.
     * The value representation is mainly used for the color scaling.
     */

    if (ping_type == ping_all_type)   // Get all ping types: use dft val repres.
    {
	this.values  [value_ind] = parseFloat (
				       applet.getPingValue (
					   i, j, date_ind, ping_default_type,
					   net_type));

	this.strings [value_ind] = applet.getPingValue (i, j, date_ind,
							ping_type, net_type);
    }
    else   // Get the min/avg/max ping type. Use the val. repres. of that type.
    {
	this.strings [value_ind] = applet.getPingValue (i, j, date_ind,
							ping_type, net_type);

	this.values  [value_ind] = parseFloat (this.strings [value_ind]);
    }

} // function parsePing ()

/**
 * Write a ping column value.
 *
 *    @param  color_scale   The color scaling used for the ping values.
 *
 *    @param  rgb_scale     The RGB scale values.
 *
 *    @param  index	    The index of the ping value and string
 *			    representation.
 *
 *    @param  td_flags	    Flags used in the <TD> HTML element.
 *
 *    @param  value_type    The value type of the net traffic.
 *
*****************************************************************************/
function writeColumnPing (color_scale, rgb_scale, index, td_flags, value_type)
/****************************************************************************/
{
    var value	  = this.values  [index];    // Ping value  representation.
    var value_str = this.strings [index];    // Ping string representation.

    writeColumnValue (color_scale, rgb_scale, value, value_str, td_flags,
		      value_type);
		      
} // function writeColumnPing ()

/**
 * This function is the constructor of the "NetDataOverviewTable" class.
 *
*******************************/
function NetDataOverviewTable ()
/******************************/
{
    /*
     * Initialise some instance variables.
     */

    this.load_extrema	      = new Extrema ();		         // Create the
    this.ping_extrema	      = new Extrema ();		         // overview
    this.throughput_extrema   = new Extrema ();		         // data
    this.udp_bw_extrema	      = new Extrema ();			 // extrema.
    this.writeTable	      = writeNetDataOverviewTable;       // Meth. wr.t.
    this.writeHeader	      = writeOverviewHeader;		 // Overv. head.
    this.writeLoadTable	      = writeLoadOverviewTable;	         // Load o.v.t.
    this.writePingTable	      = writePingOverviewTable;	         // Ping o.v.t.
    this.writeThroughputTable = writeThroughputOverviewTable;    // Tput o.v.t.
    this.writeUDPBWTable      = writeUDPBWOverviewTable;	 // U. B. o.v.t.
    this.writeColorTable      = writeOverviewColorTable;         // Color scl t.

} // function NetDataOverviewTable ()

/**
 * This function writes a HTML TABLE into the data body frame which gives an
 * overview of the Net data at a certain time, specified by its index.
 *
 * The net data values are represented by the four tables for the load, ping,
 * throughput and UDP bandwidth (when selected during the installation of the
 * current host set) respectively.
 *
 * For each of the four net data series the background colors of the columns,
 * where the net data values are displayed, are scaled according to the global
 * "ColorMap" instance defined in this page.
 *
 * Before the table is displayed the required data are obtained from the
 * "NetData" Applet and stored in local JavaScript arrays. When no data could be
 * read an alert box is displayed. The reason for the failure might be that the
 * data file was refreshed at the moment from reading. This function is a member
 * function of the "NetDataOverviewTable" class. Its class name is "writeTable".
 *
 *    @param  date_ind   The index of the time for which the overview
 *			 data are displayed. When the index is less than
 *			 zero the last index is displayed.
 *
 *    @return  True when the table could be written; false otherwise.
 *
********************************************/
function writeNetDataOverviewTable (date_ind)
/*******************************************/
{
    var nr_dates;
    var day;
    var time_or_date_id;
    var hosts;
    var host_titles;
    var nr_hosts;
    var nr_conns;
    var load_values;
    var qos_ping_data;
    var inet_ping_data;
    var qos_throughput_values;
    var inet_throughput_values;
    var qos_udp_bw_values;
    var inet_udp_bw_values;
    var i;
    var j;
    var j_start;
    var val_ind;
	   
    var applet = document.applets ["NetData"];
    
    /*
     * Force a start of the Applet. May be required when the Applet is not in
     * focus.
     */

    // applet.start ();

    /*
     * Get the # available dates from the Applet.
     */

    nr_dates = parseInt (applet.getNrDates ());

    /*
     * Show an alert box when no data could be read. In the place of the table a
     * diagnostic message is written and we return from this function.
     */

    if (nr_dates < 1)
    {
	showNoDataAvailable ();
	return false;
    }

    /*
     * Correct the date index when it is undefined or when the index is too
     * large.
     */

    if (date_ind < 0  ||  date_ind >= nr_dates)
	date_ind = nr_dates - 1;

    /*
     * Set the start date index.
     */

    start_date_ind = date_ind;

    /*
     * Get the date and the time/date ID from the Applet which corresponds with
     * the given date index.
     */

    day		    = applet.getDate	     (date_ind);
    time_or_date_id = applet.getTimeOrDateID (date_ind);

    /*
     * Set the current time/ID in the time input text field.
     */

    document.ControlButtons.TimeOrDateID.value =
	current_data.time_or_date_id_prefix + time_or_date_id;

    /*
     * Get the # hosts from the Applet. Calculate the # connections (between two
     * hosts) from the # hosts. The # connections is dependent from the usage of
     * one-directional or two-directional net tests traffic.
     */

    nr_hosts = parseInt (applet.getNrHosts ());
    nr_conns = ( nr_directs * nr_hosts * (nr_hosts - 1) ) / 2;

    /*
     * Correct the index of the fixed host when it was not correctly obtained
     * from the cookie. Correct it also when required.
     */

    if (index_fixed_host < 0)
    {
	index_fixed_host = 0;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }
    else if (index_fixed_host >= nr_hosts)
    {
	index_fixed_host = nr_hosts - 1;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }

    /*
     * Allocate the JavaScript arrays. All data are stored in one dimensional
     * arrays. A conversion formula maps the two dim. connection indices into
     * this array.
     */

    hosts		   = new Array    (nr_hosts);
    host_titles		   = new Array    (nr_hosts);
    load_values		   = new Array    (nr_hosts);
    inet_ping_data	   = new PingData (nr_conns);
    inet_throughput_values = new Array    (nr_conns);

    if (use_udp_bw)
	inet_udp_bw_values = new Array (nr_conns);

    if (use_qos)
    {
	qos_ping_data	      = new PingData (nr_conns);
	qos_throughput_values = new Array    (nr_conns);

	if (use_udp_bw)
	    qos_udp_bw_values = new Array (nr_conns);
    }

    /*
     * Obtain the required overview net data values from the applet. The values
     * are returned as strings. This is partly done to be compatible with older
     * browsers (Netscape 3 at some platforms). An empty string stands for a
     * value that was not available.
     */

    val_ind = 0;

    for (i = 0;  i < nr_hosts;  i++)  // Get the data for all hosts.
    {
	hosts	    [i] = applet.getHost      (i);
	host_titles [i] = applet.getHostTitle (i);

	/*
	 * Obtain the (one dimensional) load data. Parse the float value.
	 */

	load_values [i] = parseFloat (applet.getLoadValue (i, date_ind));

	/*
	 * Get the connection dependent (two dimensional) data which are yet
	 * unknown: they can be obtained from the hosts with a larger index.
	 * Differentiate between one-directional and two-directional net tests.
	 */

	if (nr_directs == 1)
	    j_start = i + 1;
	else
	    j_start = 0;

	for (j = j_start;  j < nr_hosts;  j++)
	{
	    if (j != i)    // Skip the current host.
	    {
		/*
		 * Get the ping values from the specified type for QoS & Inet.
		 */

		if (use_qos)
		    qos_ping_data.parse (i, j, val_ind, date_ind,
					 ping_type, qos_key);

		inet_ping_data.parse (i, j, val_ind, date_ind,
				      ping_type, inet_key);

		/*
		 * Get the througput values for QoS & Inet. They are parsed to
		 * floats.
		 */

		if (use_qos)
		    qos_throughput_values [val_ind] =
			parseFloat (applet.getThroughputValue (i, j, date_ind,
							       qos_key));

		inet_throughput_values [val_ind] =
		    parseFloat (applet.getThroughputValue (i, j, date_ind,
							   inet_key));

		/*
		 * When used: get the UDP bandwidth values from the specified
		 * type for QoS & Inet. They are parsed to floats.
		 */

		if (use_udp_bw)    // UDP bandwidth tests are performed.
		{
		    if (use_qos)
			qos_udp_bw_values [val_ind] =
			    parseFloat (applet.getUDPBWValue (i, j, date_ind,
							      udp_bw_type,
							      qos_key));

		    inet_udp_bw_values [val_ind] =
			parseFloat (applet.getUDPBWValue (i, j, date_ind,
							  udp_bw_type,
							  inet_key));

		} // UDP bandwidth tests are performed.

		val_ind++;    // Next net data value.

	    } // Skip the current host.

	} // For the two dim. host indices.

    } // For the one dim. host indices.

    /*
     * Get local references to the extrema data types.
     */

    var load_extrema	   = this.load_extrema;
    var ping_extrema	   = this.ping_extrema;
    var throughput_extrema = this.throughput_extrema;
    var udp_bw_extrema	   = this.udp_bw_extrema;

    /*
     * Set the extrema for these four data types, both for QoS and Internet.
     * Note that they are always uninitialised, because a new class instance is
     * created for each table written.
     */

    load_extrema.set (load_values, 0, LINEAR, 0.0);

    if (use_qos)
	ping_extrema.set (qos_ping_data.values, 0, LINEAR, 0.0);

    ping_extrema.set (inet_ping_data.values, 0, LINEAR, 0.0);

    if (use_qos)
	throughput_extrema.set (qos_throughput_values, 0, LINEAR, 0.0);

    throughput_extrema.set (inet_throughput_values, 0, LINEAR, 0.0);

    if (use_udp_bw)
    {
	if (use_qos)
	    udp_bw_extrema.set (qos_udp_bw_values, 0, LINEAR, 0.0);

	udp_bw_extrema.set (inet_udp_bw_values, 0, LINEAR, 0.0);
    }

    /*
     * Set the lower boundary for the load maximum.
     */

    if (load_extrema.max < max_load_lower_bound)
	load_extrema.max = max_load_lower_bound;

    /*
     * Write the overview data with tables for all types, displaying the data
     * for the current date.
     *
     * Start with the header.
     */


    this.writeHeader (day, time_or_date_id);

    /*
     * Write the load data table.
     */


    this.writeLoadTable (host_titles,
			 nr_hosts,
			 load_values);

    parent.NetData.document.write ("<BR>\n");

    /*
     * Write the ping data table.
     */

    this.writePingTable (day, time_or_date_id,
			 host_titles,
			 nr_hosts, nr_conns,
			 qos_ping_data, inet_ping_data);

    parent.NetData.document.write ("<BR>\n");

    /*
     * Write the throughput data table.
     */

    this.writeThroughputTable (day, time_or_date_id,
			       host_titles,
			       nr_hosts, nr_conns,
			       qos_throughput_values, inet_throughput_values);

    /*
     * Write the UDP bandwidth data table, when the tests are performed.
     */

    if (use_udp_bw)
    {
	parent.NetData.document.write ("<BR>\n");

	this.writeUDPBWTable (day, time_or_date_id,
			      host_titles,
			      nr_hosts, nr_conns,
			      qos_udp_bw_values, inet_udp_bw_values);
    }

    return true;   // The table could be written.

} // function writeNetDataOverviewTable ()

/**
 * This function writes the overview header.
 *
 * This function is a member function of the "NetDataOverviewTable" class. Its
 * class name is "writeHeader".
 *
 *    @param  day		The day of the overview.
 *
 *    @param  time_or_date_id   The time or week/day ID of the overview.
 *
**************************************************/
function writeOverviewHeader (day, time_or_date_id)
/*************************************************/
{
    /*
     * Write the HTML overview header table.
     */

    parent.NetData.document.write (
	"<TABLE BORDER=0 CELLSPACING=6 CELLPADDING=0>\n",
	"<TR ALIGN=\"center\">\n",
	"<TD><B>", hosts_title, "&nbsp;Net&nbsp;Test&nbsp;Results</B></TD>\n",
	"</TR>\n",
	"<TR ALIGN=\"center\">\n",
	"<TD><TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0>\n",
	"    <TR>\n",
	"    <TD><EM>Date:&nbsp;</EM></TD>\n",
	"    <TD>", day, "</TD>\n",
	"    </TR>\n",
	"    <TR>\n",
	"    <TD><EM>",
		    current_data.time_or_date_id_title,
		    ":&nbsp;</EM></TD>\n",
	"    <TD>", time_or_date_id, "</TD>\n",
	"    </TR>\n",
	"</TABLE></TD>\n",
	"</TR>\n",
	"</TABLE>\n"
	);
}

/**
 * This function writes an overview of the load data at a certain date in a
 * table.
 *
 * This function is a member function of the "NetDataOverviewTable" class. Its
 * class name is "writeLoadTable".
 *
 *    @param  host_tiltes   The array with host titles.
 *
 *    @param  nr_hosts	    The # hosts in the overview.
 *
 *    @param  load_values   The array with load values. values.
 *
*****************************************************/
function writeLoadOverviewTable (host_titles,
				 nr_hosts,
				 load_values)
/****************************************************/
{
    /*
     * Get the local references to the load extrema, set the color scale range
     * and allocate a RGB class instance for the color scaling.
     */

    var load_extrema	 = this.load_extrema;
    var load_color_scale = new ColorScale (load_extrema);
    var rgb_scale	 = new RGB	  (0, 0, 0);

    var j;
    var value;

    /*
     * Write the HTML Table header and the load element title column.
     */

    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n",
	"\n",
	"<CAPTION><B><A HREF=",
			"\"javascript:parent.Control.load_document.write(",
			       start_date_ind, ",true)\"\n",
	"               onMouseOver=\"window.status='Load table'; ",
			    "return true\"\n",
	"               onMouseOut=\"window.status=''; return true\"\n",
	"              >Load</A></B></CAPTION>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n"
	);

    /*
     * Write the host names in the title row.
     */

    for (j = 0;  j < nr_hosts;  j++)
	parent.NetData.document.write (
	    "<TD><B>", host_titles [j],"</B></TD>\n"
	    );

    /*
     * Close the title row.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n"
	);

    /*
     * Write the load values.
     */

    for (j = 0;  j < nr_hosts;  j++)
    {
	value = load_values [j];

	writeColumnValue (load_color_scale, rgb_scale, value, value, "", "");
    }

    /*
     * Close the load row and the table.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n",
	"</TABLE>\n"
	);
}

/**
 * This function writes an overview of the ping data at a certain date in a
 * table.
 *
 * This function is a member function of the "NetDataOverviewTable" class. Its
 * class name is "writePingTable".
 *
 *    @param  day		The day of the overview.
 *
 *    @param  time_or_date_id   The time or week/day ID of the overview.
 *
 *    @param  host_titles	The array with host titles.
 *
 *    @param  nr_hosts		The # hosts in the overview.
 *
 *    @param  nr_conns		The # connections between the hosts.
 *
 *    @param  qos_ping_data	The array with QoS ping data.
 *
 *    @param  inet_ping_data    The array with Internet ping data values.
 *
**************************************************************/
function writePingOverviewTable (day, time_or_date_id,
				 host_titles,
				 nr_hosts, nr_conns,
				 qos_ping_data, inet_ping_data)
/*************************************************************/
{
    /*
     * Get the local references to the ping extrema, set the color scale range
     * and allocate a RGB class instance for the color scaling.
     */

    var ping_extrema	 = this.ping_extrema;
    var ping_color_scale = new ColorScale (ping_extrema);
    var rgb_scale	 = new RGB	  (0, 0, 0);

    var i;
    var j;
    var val_ind;

    /*
     * If QoS is used, some column and row cells have a span of two; one
     * otherwise.
     */

    var qos_span = (use_qos ? 2 : 1);

    /*
     * Write the HTML Table header and the ping element title column. When two-
     * directional net tests traffic are used: add a diagnostic message about
     * the test direction used in the table.
     */

    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n",
	"\n",
	"<CAPTION><B>Ping ", ping_type_short_titles [ping_type], " ",
		     ping_type_units [ping_type], "</B>"
	);
	
    if (nr_directs == 2)
	parent.NetData.document.write (
	    "<BR>\n",
	    "            <FONT SIZE=\"-1\"",
			     ">(row&nbsp;&gt;&gt;&nbsp;column)</FONT>\n"
	    );

    parent.NetData.document.write (
	"</CAPTION>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n",
	"<TD ROWSPAN=", qos_span, ">&nbsp;</TD>\n"
	);

    /*
     * Write the host names in the upper title row.
     */

    for (j = 0;  j < nr_hosts;  j++)
	parent.NetData.document.write (
	    "<TD COLSPAN=", qos_span, "\n",
	    "   ><A HREF=",
	    	"\"javascript:parent.Control.ping_document.writeNewHost(",
		       start_date_ind, ",", j, ")\"\n",
	    "       onMouseOver=\"window.status='Ping table ", host_titles [j],
		    	"'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [j], "</A></TD>\n"
	    );

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

    if (use_qos)
    {
	parent.NetData.document.write ("<TR>\n");

	for (j = 0;  j < nr_hosts;  j++)
	    parent.NetData.document.write (
		"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n",
		"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
		);

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );
    }

    /*
     * Write the table body and the title columns.
     */

    for (i = 0;  i < nr_hosts;  i++)    // Do for all hosts.
    {
	/*
	 * Initialise a new HTML TABLE row. Write also the host title column.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n",
	    "<TD ALIGN=\"left\"",
	       "><A HREF=",
	         "\"javascript:parent.Control.ping_document.writeNewHost(",
			start_date_ind, ",", i, ")\"\n",
	    "       onMouseOver=\"window.status='Ping table ", host_titles [i],
		     	"'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [i], "</A></TD>\n"
	    );

	/*
	 * Write the QoS & Internet ping column values. The trace is undefined.
	 * At one-directional net tests the south-west region is a mirror of the
	 * north-east region. The function "getValueIndex()" takes care of the
	 * mirroring. It translates matrix rows and columns into indices of the
	 * value array.
	 */

	for (j = 0;  j < nr_hosts;  j++)   // Do for all value columns.
	{
	    if (i != j)     // Write ping value.
	    {
		val_ind = getValueIndex (i, j, nr_hosts);

		if (use_qos)
		    qos_ping_data.writeColumn (ping_color_scale, rgb_scale,
					       val_ind, "", qos_key);

		inet_ping_data.writeColumn (ping_color_scale, rgb_scale,
					    val_ind, "", inet_key);
	    }
	    else    // Write the empty trace column(s).
	    {
		if (use_qos)
		    writeColumnValue (ping_color_scale, rgb_scale, -10.0, "",
				      "", "");

		writeColumnValue (ping_color_scale, rgb_scale, -10.0, "",
				  "", "");
	    }

	} // For all value columns.

	/*
	 * Close the row with values.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );

    } // For all hosts.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

} // function writePingOverviewTable ()

/**
 * This function writes an overview of the throughput data at a certain date in
 * a table.
 *
 * This function is a member function of the "NetDataOverviewTable" class. Its
 * class name is "writePingTable".
 *
 *    @param  day		       The day of the overview.
 *
 *    @param  time_or_date_id	       The time or week/day ID of the overview.
 *
 *    @param  host_titles	       The array with host titles.
 *
 *    @param  nr_hosts		       The # hosts in the overview.
 *
 *    @param  nr_conns		       The # connections between the hosts.
 *
 *    @param  qos_throughput_values    The array with QoS throughput values.
 *
 *    @param  inet_throughput_values   The array with Internet throughput
 *				       values.
 *
**********************************************************/
function writeThroughputOverviewTable (
	     day, time_or_date_id,
	     host_titles,
	     nr_hosts, nr_conns,
	     qos_throughput_values, inet_throughput_values)
/*********************************************************/
{
    /*
     * Get the local references to the throughput extrema, set the color scale
     * range and allocate a RGB class instance for the color scaling.
     */

    var throughput_extrema     = this.throughput_extrema;
    var throughput_color_scale = new ColorScale (throughput_extrema);
    var rgb_scale	       = new RGB	(0, 0, 0);

    var i;
    var j;
    var val_ind;
    var qos_value;
    var inet_value;

    /*
     * If QoS is used, some column and row cells have a span of two; one
     * otherwise.
     */

    var qos_span = (use_qos ? 2 : 1);

    /*
     * Write the HTML Table header and the throughput element title column. When
     * two-directional net tests traffic are used: add a diagnostic message
     * about the test direction used in the table.
     */

    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n",
	"\n",
	"<CAPTION><B>Throughput [Mbit/s]</B>"
	);

    if (nr_directs == 2)
	parent.NetData.document.write (
	    "<BR>\n",
	    "            <FONT SIZE=\"-1\"",
			     ">(row&nbsp;&gt;&gt;&nbsp;column)</FONT>\n"
	    );

    parent.NetData.document.write (
	"</CAPTION>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n",
	"<TD ROWSPAN=", qos_span, ">&nbsp;</TD>\n"
	);

    /*
     * Write the host names in the upper title row.
     */

    for (j = 0;  j < nr_hosts;  j++)
	parent.NetData.document.write (
	    "<TD COLSPAN=", qos_span, "\n",
	    "   ><A HREF=",
		"\"javascript:parent.Control.throughput_document.writeNewHost(",
		       start_date_ind, ",", j, ")\"\n",
	    "       onMouseOver=\"window.status='Throughput table ",
			host_titles [j], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [j], "</A></TD>\n"
	    );

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

     if (use_qos)
     {
	parent.NetData.document.write ("<TR>\n");

	 for (j = 0;  j < nr_hosts;  j++)
	    parent.NetData.document.write (
		"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n",
		"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
		);

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );
    }

    /*
     * Write the table body and the title columns.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	/*
	 * Initialise a new HTML TABLE row. Write also the host title column.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n",
	    "<TD ALIGN=\"left\"",
	       "><A HREF=",
		"\"javascript:parent.Control.throughput_document.writeNewHost(",
		       start_date_ind, ",", i, ")\"\n",
	    "       onMouseOver=\"window.status='Throughput table ",
			host_titles [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [i], "</A></TD>\n"
	    );

	/*
	 * Write the QoS & Internet throughput column values. The trace is
	 * undefined. At one-directional net tests the south-west region is a
	 * mirror of the north-east region. The function "getValueIndex()" takes
	 * care of the mirroring. It translates matrix rows and columns into
	 * indices of the value array.
	 */

	for (j = 0;  j < nr_hosts;  j++)    // Do for all value columns.
	{
	    if (i != j)    // Write throughput value.
	    {
		val_ind = getValueIndex (i, j, nr_hosts);

		if (use_qos)
		{
		    qos_value = qos_throughput_values [val_ind];
		    writeColumnValue (throughput_color_scale, rgb_scale,
				      qos_value, qos_value,
				      "", "");
		}

		inet_value = inet_throughput_values [val_ind];
		writeColumnValue (throughput_color_scale, rgb_scale,
				  inet_value, inet_value,
				  "", "");
	    }
	    else    // Write the empty trace column(s).
	    {
		if (use_qos)
		    writeColumnValue (throughput_color_scale, rgb_scale,
				      -10.0, "",
				      "", "");

		writeColumnValue (throughput_color_scale, rgb_scale,
				  -10.0, "",
				  "", "");
	    }

	} // For all value columns.

	/*
	 * Close the row with values.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );

    } // For all hosts.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

} // function writeThroughputOverviewTable ()

/**
 * This function writes an overview of the UDP bandwidth data at a certain date
 * in a table.
 *
 * This function is a member function of the "NetDataOverviewTable" class. Its
 * class name is "writeUDPBWTable".
 *
 *    @param  day		   The day of the overview.
 *
 *    @param  time_or_date_id      The time or week/day ID of the overview.
 *
 *    @param  host_titles	   The array with host titles.
 *
 *    @param  nr_hosts		   The # hosts in the overview.
 *
 *    @param  nr_conns		   The # connections between the hosts.
 *
 *    @param  qos_udp_bw_values    The array with QoS UDP bandwidth data values.
 *
 *    @param  inet_udp_bw_values   The array with Internet UDP bandwidth data
 *				   values
 *
***********************************************************************/
function writeUDPBWOverviewTable (day, time_or_date_id,
				  host_titles,
				  nr_hosts, nr_conns,
				  qos_udp_bw_values, inet_udp_bw_values)
/**********************************************************************/
{
    /*
     * Get the local references to the UDP bandwidth extrema, set the color
     * scale range and allocate a RGB class instance for the color scaling.
     */

    var udp_bw_extrema	   = this.udp_bw_extrema;
    var udp_bw_color_scale = new ColorScale (udp_bw_extrema);
    var rgb_scale	   = new RGB	    (0, 0, 0);

    var i;
    var j;
    var val_ind;
    var qos_value;
    var inet_value;

    /*
     * If QoS is used, some column and row cells have a span of two; one
     * otherwise.
     */

    var qos_span = (use_qos ? 2 : 1);

    /*
     * Write the HTML Table header and the UDP bandwidth element title column.
     * When two-directional net tests traffic are used: add a diagnostic message
     * about the test direction used in the table.
     */

    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n",
	"\n",
	"<CAPTION><B>UDP ", udp_bw_type_short_titles [udp_bw_type], " ",
		     udp_bw_type_units [udp_bw_type], "</B>"
	)

    if (nr_directs == 2)
	parent.NetData.document.write (
	    "<BR>\n",
	    "            <FONT SIZE=\"-1\"",
			     ">(row&nbsp;&gt;&gt;&nbsp;column)</FONT>\n"
	    );

    parent.NetData.document.write (
	"</CAPTION>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n",
	"<TD ROWSPAN=", qos_span, ">&nbsp;</TD>\n"
	);

    /*
     * Write the host names in the upper title row.
     */

    for (j = 0;  j < nr_hosts;  j++)
	parent.NetData.document.write (
	    "<TD COLSPAN=", qos_span, "\n",
	    "   ><A HREF=",
	    	"\"javascript:parent.Control.udp_bw_document.writeNewHost(",
		       start_date_ind, ",", j, ")\"\n",
	    "       onMouseOver=\"window.status='UDP table ", host_titles [j],
			"'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [j], "</A></TD>\n"
	    );

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

    if (use_qos)
    {
	parent.NetData.document.write ("<TR>\n");

	for (j = 0;  j < nr_hosts;  j++)
	    parent.NetData.document.write (
		"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n",
		"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
		);

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );
    }

    /*
     * Write the table body and the title columns.
     */

    for (i = 0;  i < nr_hosts;  i++)    // Do for all hosts.
    {
	/*
	 * Initialise a new HTML TABLE row. Write also the host title column.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n",
	    "<TD ALIGN=\"left\"",
	       "><A HREF=",
	         "\"javascript:parent.Control.udp_bw_document.writeNewHost(",
			start_date_ind, ",", i, ")\"\n",
	    "       onMouseOver=\"window.status='UDP table ",
			host_titles [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", host_titles [i], "</A></TD>\n"
	    );

	/*
	 * Write the QoS & Internet UDP bandwidth column values. The trace is
	 * undefined. At one-directional net tests the south-west region is a
	 * mirror of the north-east region. The function "getValueIndex()" takes
	 * care of the mirroring. It translates matrix rows and columns into
	 * indices of the value array.
	 */

	for (j = 0;  j < nr_hosts;  j++)   // Do for all value columns.
	{
	    if (i != j)     // Write UDP bandwidth value.
	    {
		val_ind = getValueIndex (i, j, nr_hosts);

		if (use_qos)
		{
		    qos_value = qos_udp_bw_values [val_ind];
		    writeColumnValue (udp_bw_color_scale, rgb_scale,
				      qos_value, qos_value,
				      "", "");
		}

		inet_value = inet_udp_bw_values [val_ind];
		writeColumnValue (udp_bw_color_scale, rgb_scale,
				  inet_value, inet_value,
				  "", "");
	    }
	    else    // Write the empty trace column(s).
	    {
		if (use_qos)
		    writeColumnValue (udp_bw_color_scale, rgb_scale, -10.0, "",
				      "", "");

		writeColumnValue (udp_bw_color_scale, rgb_scale, -10.0, "",
				  "", "");
	    }

	} // For all value columns.

	/*
	 * Close the row with values.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );

    } // For all hosts.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

} // function writeUDPBWOverviewTable ()

/**
 * This function writes the net data color overview table. It shows the color
 * scaling of the four data types. This function is a member function of the
 * "NetDataOverviewTable" class. Its class name is "writeColorTable".
 *
**********************************/
function writeOverviewColorTable ()
/*********************************/
{
    var nr_color_rows = 1;	   // The # rows with color columns.
    var scale_factor  = 1000.0;    // Scale factor for # decimals.

    color_map.openTable ();    // Write open colormap.

    color_map.writeRow ("load",		      // Load scaling.
			this.load_extrema,
			nr_color_rows,
			scale_factor);
			
    color_map.writeRow ("ping " +		         // Ping scaling.
			ping_type_labels [ping_type] +
			" " +
			ping_type_units [ping_type],
			this.ping_extrema,
			nr_color_rows,
			scale_factor);

    color_map.writeRow ("throughput [Mbit/s]",	    // Throughput scaling.
			this.throughput_extrema,
			nr_color_rows,
			scale_factor);

    if (use_udp_bw)    // UDP bandwidth tests are selected.
    {
	color_map.writeRow ("UDP " +				  // UDP BW
			    udp_bw_type_labels [udp_bw_type] +    // scaling.
			    " " +
			    udp_bw_type_units [udp_bw_type],
			    this.udp_bw_extrema,
			    nr_color_rows,
			    scale_factor);
    }

    color_map.closeTable ();    // Write close colormap.

} // function writeOverviewColorTable ()

/**
 * Convert a row and column index from the table into a value array index.
 * Differentiate between one-directional and two-directional net tests: the
 * first uses only the north-west region of the values matrix; the latter uses
 * the full matrix with the exception of the trace.
 *
 *    @param  i   The table row indx.
 *
 *    @param  j   The table column index.
 *
 *    @param  n  The row and column size.
 *
 *    @return  The value array index.
 *
*******************************/
function getValueIndex (i, j, n)
/******************************/
{
    var i_swap;
    
    if (nr_directs == 1)    // One-directional: north-east region.
    {
	if (j < i)    // South-west is mirrored in north-east.
	{
	    i_swap = i;
	    i	   = j;
	    j	   = i_swap;
	}

	return n * i - (i * (i + 1)) / 2 + j - i - 1;
    }
    else    // Two-directional: full matrix with exception of trace.
    {
	if (j < i)    // Column before trace column.
	{
	    return (n - 1) * i + j;
	}
	else    // Column after skipped trace column.
	{
	    return (n - 1) * i + j - 1;
	}
    }
}

/**
 * This function is the constructor of The LoadTable class.
 *
********************/
function LoadTable ()
/*******************/
{
    /*
     * Initialise some instance variables.
     */

    this.extrema	 = new Extrema ();	   // Extrema load values.
    this.writeTable	 = writeLoadTable;	   // Method to write load tbl.
    this.setExtrema	 = setLoadExtrema;	   // Method to set extrema.
    this.writeColorTable = writeLoadColorTable;    // Method to write color tbl.

} // function LoadTable ()

/**
 * This function is the constructor of The PingTable class.
 *
********************/
function PingTable ()
/*******************/
{
    /*
     * Initialise some instance variables.
     */

    this.extrema         = new Extrema ();	   // Extrema ping values.
    this.writeTable      = writePingTable;	   // Method to write ping tbl.
    this.setExtrema      = setPingExtrema;	   // Method to set extrema.
    this.writeColorTable = writePingColorTable;    // Method to write color tbl.

} // function PingTable ()

/**
 * This function is the constructor of the ThroughputTable class.
 *
**************************/
function ThroughputTable ()
/*************************/
{
    /*
     * Initialise some instance variables.
     */

    this.extrema         = new Extrema ();            // Extrema tput values.
    this.writeTable      = writeThroughputTable;      // Method to wr. tput tbl.
    this.setExtrema      = setThroughputExtrema;      // Method to set extrema.
    this.writeColorTable = writeThroughputColorTable; // Method to wr. col. tbl.

} // function ThroughputTable ()

/**
 * This function is the constructor of the UDPBWTable class.
 *
*********************/
function UDPBWTable ()
/********************/
{
    /*
     * Initialise some instance variables.
     */

    this.extrema	 = new Extrema ();	   // Extrema UDP BW values.
    this.writeTable	 = writeUDPBWTable;	   // Method to write UDP BW t.
    this.setExtrema	 = setUDPBWExtrema;	   // Mmethod to set extrema.
    this.writeColorTable = writeUDPBWColorTable;   // Method to write color tbl.

} // function UDPBWTable ()

/**
 * This function writes a HTML TABLE to the data body frame. The table contains
 * the load values where the hosts are specified in the columns and the date and
 * times / date IDs in the rows. No more than <max_nr_date_rows> are displayed.
 * This function is a member function of the "LoadTable" class. Its class name
 * is "writeTable".
 *
 *    @param  date_ind   The index of the time for which the first load data are
 *			 displayed. The other dates are displayed in reverse
 *			 order. When the index is less than zero the most
 *			 recent date is displayed.
 *
 *    @return  True when the table could be written; false otherwise.
 *
*********************************/
function writeLoadTable (date_ind)
/********************************/
{
    var nr_dates;
    var days;
    var times_or_date_ids;
    var hosts;
    var host_titles;
    var nr_hosts;
    var load_values;
    var nr_date_rows;
    var i;
    var j;
    var applet = document.applets ["NetData"];

    /*
     * Force a start of the Applet. May be required when the Applet is not in
     * focus.
     */

    // applet.start ();

    /*
     * Get the # available dates from the Applet.
     */

    nr_dates = parseInt (applet.getNrDates ());

    /*
     * Show an alert box when no data could be read. In the place of the table a
     * diagnstic message is written and we return from this function.
     */

    if (nr_dates < 1)
    {
	showNoDataAvailable ();
	return false;
    }

    /*
     * Correct the date index when it is undefined or when the index is too
     * large.
     */

    if (date_ind < 0  ||  date_ind >= nr_dates)
	date_ind = nr_dates - 1;

    /*
     * Set the start date index.
     */

    start_date_ind = date_ind;

    /*
     * Obtain the # hosts from the Applet.
     */

    nr_hosts = parseInt (applet.getNrHosts ());

    /*
     * Correct the index of the fixed host when it was not correctly obtained
     * from the cookie. Correct it also when required.
     */

    if (index_fixed_host < 0)
    {
	index_fixed_host = 0;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }
    else if (index_fixed_host >= nr_hosts)
    {
	index_fixed_host = nr_hosts - 1;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }

    /*
     * Allocate the JavaScript arrays. Note that the load values are stored in a
     * double array. See below.
     */

    days	      = new Array (max_nr_date_rows);
    times_or_date_ids = new Array (max_nr_date_rows);
    hosts	      = new Array (nr_hosts);
    host_titles       = new Array (nr_hosts);
    load_values       = new Array (nr_hosts);

    /*
     * Obtain the dates and times/date IDs where the load values are measured
     * from the Applet. They are stored in reverse order: most recent date
     * first.
     */

    date_ind = start_date_ind;
    i	     = 0;

    while (i < max_nr_date_rows  &&  date_ind >= 0)
    {
	days		  [i] = applet.getDate	       (date_ind);
	times_or_date_ids [i] = applet.getTimeOrDateID (date_ind);

	date_ind -= date_ind_incr;
	i++;
    }

    nr_date_rows = i;    // The # rows used in this table.

    /*
     * Set the most recent time/ID in the time/ID input text field.
     */

    document.ControlButtons.TimeOrDateID.value = times_or_date_ids [0];

    /*
     * Obtain the required load data from the Applet.
     */

    for (i = 0;  i < nr_hosts;  i++)    // Do for all hosts.
    {
	hosts	    [i] = applet.getHost      (i);
	host_titles [i] = applet.getHostTitle (i);

	/*
	 * Allocate the array which contains the load values for the current
	 * host.
	 */

	var host_load_values = new Array (nr_date_rows);

	/*
	 * Get the load values for this host from the Applet. The load values
	 * are also stored in reverse order.
	 */

	date_ind = start_date_ind;
	j	 = 0;

	while (j < nr_date_rows)
	{
	    host_load_values [j] = parseFloat (applet.getLoadValue (i,
								    date_ind));

	    date_ind -= date_ind_incr;
	    j++;
	}

	/*
	 * Store the address of load values array in the host array.
	 */

	load_values [i] = host_load_values;

    } // For all hosts.

    /*
     * Determine the extrema from the load values.
     */

    this.setExtrema (load_values, nr_hosts);

    /*
     * Initialise some local variables.
     */

    var load_extrema = this.extrema;
    var color_scale  = new ColorScale (load_extrema);
    var rgb_scale    = new RGB	      (0, 0, 0);
    var value;

    /*
     * Write the table header and the date and time/ID headers in the title row.
     */

    parent.NetData.document.write (
	"<TABLE BORDER\n",
	"       FRAME\n",
	"       CELLPADDING=5>\n",
	"\n",
	"<CAPTION><B>Load ", hosts_title, " Hosts</B></CAPTION>\n",
	"\n",
	"<TR ALIGN=\"center\"\n",
	"    VALIGN=\"middle\">\n",
	"<TD><EM>Date</EM></TD>\n",
	"<TD><EM>", current_data.time_or_date_id_title, "</EM></TD>\n"
	);

    /*
     * Write the host names in the title row.
     */

    for (j = 0;  j < nr_hosts;  j++)
	parent.NetData.document.write (
	    "<TD><B>", host_titles [j], "</B></TD>\n"
	    );

    /*
     * Close the title row.
     */

    parent.NetData.document.write (
	"</TR>\n",
	"\n"
	);

    /*
     * Write the dates, times/IDs and load values in the table.
     */

    date_ind = start_date_ind;
    i	     = 0;

    while (i < nr_date_rows)   // Do for selected dates.
    {
	/*
	 * Open a new table row.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n"
	    );

	/*
	 * Write the the date and time/ID columns.
	 */

	parent.NetData.document.write (
	    "<TD>", days [i], "</TD>\n",
	    "<TD><A HREF=",
		"\"javascript:parent.Control.net_data_overview_document.",
		      "write(", date_ind, ",true)\"\n",
	    "       onMouseOver=\"window.status='Overview table ",
			times_or_date_ids [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", times_or_date_ids [i], "</A></TD>\n"
	    );

	/*
	 * Write the load values for all hosts in the table row.
	 */

	for (j = 0;  j < nr_hosts;  j++)
	{
	    value = load_values [j] [i];

	    writeColumnValue (color_scale, rgb_scale, value, value, "", "");
	
	} // For all hosts.

	/*
	 * Close the table row.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n");

	date_ind -= date_ind_incr;
	i++;

    } // For all dates.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

    return true;    // The table could be written.

} // function writeLoadTable () 

/**
 * This function writes a HTML TABLE to the data body frame. The table contains
 * the ping values where the hosts are specified in the columns and the date and
 * times or week/day IDs in the rows. No more than <max_nr_date_rows> are
 * displayed. Columns with skipped connections are not printed. This function is
 * a member function of the "PingTable" class. Its class name is "writeTable".
 *
 *    @param  date_ind   The index of the time/date ID for which the first ping
 *                       data are displayed. The other dates are displayed in
 *			 reverse order. When the index is less than zero the
 *			 most recent date is displayed.
 *
 *    @return  True when the table could be written; false otherwise.
 *
*********************************/
function writePingTable (date_ind)
/********************************/
{
    var nr_dates;
    var days;
    var times_or_date_ids;
    var host;
    var hosts;
    var host_titles;
    var skip_host_inds;
    var nr_hosts;
    var is_skip_hosts;
    var qos_ping_data;
    var inet_ping_data;
    var is_skip_qos_cols;
    var is_skip_inet_cols;
    var host_qos_ping_data;
    var host_inet_ping_data;
    var nr_date_rows;
    var i;
    var j;
    var k;
    var applet = document.applets ["NetData"];

    /*
     * If QoS is used, some row cells have a span of two; one otherwise.
     */

    var row_qos_span = (use_qos ? 2 : 1);

    /*
     * The column spans depend also from the skipped connections and is
     * therefore column dependent. Hence an array is used which will be
     * initialised later.
     */

    var col_qos_spans;

    /*
     * Force a start of the Applet. May be required when the Applet is not in
     * focus.
     */

    // applet.start ();

    /*
     * Get the # available dates from the Applet.
     */

    nr_dates = parseInt (applet.getNrDates ());

    /*
     * Show an alert box when no data could be read. In the place of the table a
     * diagnstic message is written and we return from this function.
     */

    if (nr_dates < 1)
    {
	showNoDataAvailable ();
	return false;
    }

    /*
     * Correct the date index when it is undefined or when the index is too
     * large.
     */

    if (date_ind < 0  ||  date_ind >= nr_dates)
	date_ind = nr_dates - 1;

    /*
     * Set the start date index.
     */

    start_date_ind = date_ind;

    /*
     * Obtain the # hosts from the Applet.
     */

    nr_hosts = parseInt (applet.getNrHosts ());

    /*
     * Correct the index of the fixed host when it was not correctly obtained
     * from the cookie. Correct it also when required.
     */

    if (index_fixed_host < 0)
    {
	index_fixed_host = 0;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }
    else if (index_fixed_host >= nr_hosts)
    {
	index_fixed_host = nr_hosts - 1;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }

    /*
     * Allocate the JavaScript arrays. Note that the ping values are stored in a
     * double array. See below.
     */

    days		= new Array (max_nr_date_rows);
    times_or_date_ids	= new Array (max_nr_date_rows);
    hosts		= new Array (nr_hosts);
    host_titles		= new Array (nr_hosts);
    skip_host_inds	= new Array (nr_hosts);
    is_skip_hosts	= new Array (nr_hosts);
    inet_ping_data	= new Array (nr_hosts * nr_directs);
    is_skip_inet_cols   = new Array (nr_hosts * nr_directs);
    col_qos_spans	= new Array (nr_hosts * nr_directs);
    host_inet_ping_data = new Array (nr_directs);

    if (use_qos)
    {
	qos_ping_data	   = new Array (nr_hosts * nr_directs);
	is_skip_qos_cols   = new Array (nr_hosts * nr_directs);
	host_qos_ping_data = new Array (nr_directs);
    }

    /*
     * Obtain the dates and times/IDs where the ping values are measured from
     * the Applet. They are stored in reverse order: most recent date first.
     */

    date_ind = start_date_ind;
    i        = 0;

    while (i < max_nr_date_rows  &&  date_ind >= 0)
    {
	days		  [i] = applet.getDate	       (date_ind);
	times_or_date_ids [i] = applet.getTimeOrDateID (date_ind);

	date_ind -= date_ind_incr;
	i++;
    }

    nr_date_rows = i;    // The # rows used in this table.

    /*
     * Set the most recent time/ID in the time/ID input text field.
     */

    document.ControlButtons.TimeOrDateID.value = times_or_date_ids [0];

    /* 
     * Get the hosts (titles) from the applet.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	hosts	    [i] = applet.getHost      (i);
	host_titles [i] = applet.getHostTitle (i);
    }

    /*
     * Read the skipped host connections from the Applet for the one-directional
     * and two-directional case.
     */

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	if (i == index_fixed_host)    // Fixed host: not in table columns.
	{
	    if (use_qos)
	    {
		for (k = 0;  k < nr_directs;  k++)
		    is_skip_qos_cols [nr_directs * i + k] = true;
	    }

	    for (k = 0;  k < nr_directs;  k++)
		is_skip_inet_cols [nr_directs * i + k] = true;
	}
	else    // Not the fixed host.
	{
	    /*
	     * Set the QoS skipped connections when used.
	     */

	    if (use_qos)    // Use QoS.
	    {
		if (applet.isSkippedConnection (index_fixed_host, i,
						qos_key) == "true")
		{
		   is_skip_qos_cols [nr_directs * i] = true;
		}
		else
		{
		   is_skip_qos_cols [nr_directs * i] = false;
		}

		if (nr_directs == 2)
		{
		    if (applet.isSkippedConnection (i, index_fixed_host,
						    qos_key) == "true")
		    {
			is_skip_qos_cols [nr_directs * i + 1] = true;
		    }
		    else
		    {
			is_skip_qos_cols [nr_directs * i + 1] = false;
		    }
		}

	    } // Use QoS.

	    if (applet.isSkippedConnection (index_fixed_host, i,
					    inet_key) == "true")
	    {
		is_skip_inet_cols [nr_directs * i] = true;
	    }
	    else
	    {
		is_skip_inet_cols [nr_directs * i] = false;
	    }

	    if (nr_directs == 2)
	    {
		if (applet.isSkippedConnection (i, index_fixed_host,
						inet_key) == "true")
		{
		    is_skip_inet_cols [nr_directs * i + 1] = true;
		}
		else
		{
		    is_skip_inet_cols [nr_directs * i + 1] = false;
		}
	    }

	} // Not the fixed host.

    } // For all hosts.

    /*
     * Set the hosts which are completely skipped. The skipped host indices are
     * stored (but not for the fixed host), but also an array with flags is set.
     */

    nr_skip_hosts = 0;

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	var is_skip_host = true;

	if (use_qos)
	{
	    k = 0;

	    while (k < nr_directs  &&  is_skip_host)
	    {
		if (!is_skip_qos_cols [nr_directs * i + k])
		    is_skip_host = false;
		    
		k++;
	    }
	}

	k = 0;

	while (k < nr_directs  &&  is_skip_host)
	{
	    if (!is_skip_inet_cols [nr_directs * i + k])
		is_skip_host = false;

	    k++;
	}

	is_skip_hosts [i] = is_skip_host;

	if (is_skip_host  &&  i != index_fixed_host)
	    skip_host_inds [nr_skip_hosts++] = i;

    } // For all hosts.

    /*
     * Set the QoS column spans, depending if there are columns skipped.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (k = 0;  k < nr_directs;  k++)
	{
	    var col_ind = nr_directs * i + k;

	    col_qos_spans [col_ind] = 0;

	    if (use_qos)
		if (!is_skip_qos_cols [col_ind])
		    col_qos_spans [col_ind]++;

	    if (!is_skip_inet_cols [col_ind])
		col_qos_spans [col_ind]++;
	}
    }

    /*
     * Obtain the required ping data from the Applet for the one-directional and
     * two-directional case. Note that the index counts the host, not the data.
     */

    for (i = 0;  i < nr_hosts;  i++)    // Do for all hosts.
    {
	if (is_skip_hosts [i])    // No QoS & Inet values for the skipped hosts.
	{
	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_ping_data [nr_directs * i + k] = null;

		inet_ping_data [nr_directs * i + k] = null;
	    }
	}
	else    // Host is unequal to the fixed host and not skipped.
	{
	    /*
	     * Allocate (1-dim) storage for the QoS & Inet ping data for the
	     * current host when the corresponding column is not skipped. Do it
	     * for all directional connections.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		{
		    if (is_skip_qos_cols [nr_directs * i + k])
			host_qos_ping_data [k] = null;
		    else
			host_qos_ping_data [k] = new PingData (nr_date_rows);
		}

		if (is_skip_inet_cols [nr_directs * i + k])
		    host_inet_ping_data [k] = null;
		else
		    host_inet_ping_data [k] = new PingData (nr_date_rows);
	    }

	    /*
	     * Reset the date index to the start date index.
	     */

	    date_ind = start_date_ind;

	    /*
	     * Parse the QoS & Inet ping data. Also parse the data for the
	     * "back" direction when two-directional traffic is used.
	     */

	    for (j = 0;  j < nr_date_rows;  j++)
	    {
		if (use_qos)
		{
		    if (!is_skip_qos_cols [nr_directs * i])
			host_qos_ping_data [0].parse (index_fixed_host, i,
						      j, date_ind,
						      ping_type, qos_key);

		    if (nr_directs == 2)
			if (!is_skip_qos_cols [nr_directs * i + 1])
			    host_qos_ping_data [1].parse (i, index_fixed_host,
							  j, date_ind,
							  ping_type, qos_key);
		}

		if (!is_skip_inet_cols [nr_directs * i])
		    host_inet_ping_data [0].parse (index_fixed_host, i,
						   j, date_ind,
						   ping_type, inet_key);

		if (nr_directs == 2)
		    if (!is_skip_inet_cols [nr_directs * i + 1])
			host_inet_ping_data [1].parse (i, index_fixed_host,
						       j, date_ind,
						       ping_type, inet_key);

		date_ind -= date_ind_incr;

	    } // For all dates in the table.

	    /*
	     * Store the one dim. ping data arrays in the double array
	     * containing all ping data. Take care of the connection directions.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_ping_data [nr_directs * i + k] = host_qos_ping_data [k];

		inet_ping_data [nr_directs * i + k] =  host_inet_ping_data [k];
	    }

	} // Host is not skipped and is therefore also not the fixed host.

    } // For all hosts.

    /* 
     * Determine the extrema from the ping values. Take care of the test
     * directions.
     */

     if (use_qos)
	 this.setExtrema (qos_ping_data, nr_hosts);

     this.setExtrema (inet_ping_data, nr_hosts);
    
    /* 
     * Initialise some local variables
     */
    
    var ping_extrema = this.extrema;
    var color_scale  = new ColorScale (ping_extrema);
    var rgb_scale    = new RGB        (0, 0, 0);

    /*
     * Write the table header and the date and time/ID headers in the title row.
     */

    parent.NetData.document.write (
        "<TABLE BORDER\n",
        "       FRAME\n",
        "       CELLPADDING=5>\n",
        "\n",
        "<CAPTION><B>Ping ", ping_type_short_titles [ping_type], " ",
		     ping_type_units [ping_type], " from&nbsp;/&nbsp;to ",
		     hosts [index_fixed_host]
	);

    if (hosts [index_fixed_host] != host_titles [index_fixed_host])
	parent.NetData.document.write (
	    " (", host_titles [index_fixed_host], ")"
	    );

    parent.NetData.document.write ("</B>");

    /*
     * Write the hyper links to the ping data of the hosts to which no ping
     * tests are performed, when existing.
     */

    if (nr_skip_hosts > 0)    // There are skipped hosts.
    {
	parent.NetData.document.write (
	    "\n",
	    "</P>\n",
	    "\n",
	    "<P>\n",
	    "         Skipped tests:"
	    );

	j = 0;

	while (j < nr_skip_hosts)
	{
	    var skip_host_ind = skip_host_inds [j];

	    parent.NetData.document.write (
		" ",
		"<A HREF=",
		    "\"javascript:parent.Control.ping_document.",
			  "writeNewHost(", start_date_ind, ",", skip_host_ind,
				      ")\"\n",
		    "   onMouseOver=\"window.status='Ping table ",
			    host_titles [skip_host_ind], "'; return true\"\n",
		    "  >", host_titles [skip_host_ind],"</A></TD>");

	    if (++j < nr_skip_hosts)
		parent.NetData.document.write (",");
	    else
		parent.NetData.document.write (".");
	}

    } // There are skipped hosts.

    parent.NetData.document.write (
	"</CAPTION>\n",
        "\n",
        "<TR ALIGN=\"center\"\n",
        "    VALIGN=\"middle\">\n",
        "<TD ROWSPAN=", row_qos_span, "><EM>Date</EM></TD>\n",
        "<TD ROWSPAN=", row_qos_span,
	   "><EM>", current_data.time_or_date_id_title, "</EM></TD>\n"
        );

    /*
     * Write the host names in the upper title row. Write two title columns with
     * direction marks when two-directional tests are used. When a QoS column
     * span is zero, all tests to/from this host are skipped and the
     * corresponding host column(s) is (are) not written which is always the
     * case for the fixed host.
     */

    for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
    {
	for (k = 0;  k < nr_directs;  k++)    // For all directions.
	{
	    var col_qos_span = col_qos_spans [nr_directs * j + k];
	    
	    if (col_qos_span > 0)   // (Directional) host column(s) not skipped.
	    {
		parent.NetData.document.write (
		    "<TD COLSPAN=", col_qos_span, ">"
		    );

		if (nr_directs == 2) // Write direction marks at two-directions.
		{
		    if (k == 0)
			parent.NetData.document.write (">> ");
		    else
			parent.NetData.document.write ("<< ");
		}

		parent.NetData.document.write (
		    "<A HREF=",
			"\"javascript:parent.Control.ping_document.",
			      "writeNewHost(", start_date_ind, ",", j, ")\"\n",
		    "   onMouseOver=\"window.status='Ping table ",
			    host_titles [j], "'; return true\"\n",
		    "   onMouseOut=\"window.status=''; return true\"\n",
		    "  >", host_titles [j],"</A></TD>\n");

	    } // (Directional) host column(s) not skipped.
	
	} // For all directions.

    } // For all hosts.

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
        "</TR>\n",
        "\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

    if (use_qos)
    {
	/*
	 * Open the lower title row.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n"
	    );

	/*
	 * Write the net type title columns when the columns are not skipped.
	 */

	for (j = 0;  j < nr_hosts;  j++)
	{
	    for (k = 0;  k < nr_directs; k++)
	    {
		if (!is_skip_qos_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n"
			);

		if (!is_skip_inet_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
			);
	    }
	}

	/*
	 * Close the lower title row.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );
    
    } // Use QoS.

    /* 
     * Write the dates, times/IDs and ping values in the table
     */

    date_ind = start_date_ind;
    i	     = 0;

    while (i < nr_date_rows)    // Do for selected dates
    {
        
	/*
	 * Open a new table row.
	 */

        parent.NetData.document.write (
            "<TR ALIGN=\"center\"\n",
            "    VALIGN=\"middle\">\n"
            );

        /*
         * Write the the date and time/ID columns.
         */
           
        parent.NetData.document.write (
            "<TD>", days [i], "</TD>\n",
            "<TD><A HREF=",
		"\"javascript:parent.Control.net_data_overview_document.",
		      "write(", date_ind, ",true)\"\n",
	    "       onMouseOver=\"window.status='Overview table ",
			times_or_date_ids [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", times_or_date_ids [i], "</A></TD>\n"
            );
 
        /*
         * Write the ping values for all hosts in the table row. Do it for all
	 * connections which are not skipped. The fixed host is default skipped.
         */

        for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
        {
	    for (k = 0;  k < nr_directs;  k++)    // For all directions.
	    {
		if (use_qos)
		    if (!is_skip_qos_cols [nr_directs*j + k])
			qos_ping_data [nr_directs*j + k].writeColumn (
							     color_scale,
							     rgb_scale,
							     i,
							     "",
							     "");

		if (!is_skip_inet_cols [nr_directs*j + k])
		    inet_ping_data [nr_directs*j + k].writeColumn (color_scale,
								   rgb_scale,
								   i,
								   "",
								   "");

	    } // For all directions.

	} // For all hosts.

	/*
         * Close the table row.
         */

        parent.NetData.document.write (
            "</TR>\n",
            "\n");

	date_ind -= date_ind_incr;
	i++;
    
    } // For all dates.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

    return true;    // The table could be written.

} // function writePingTable ()

/**
 * This function writes a HTML TABLE to the data body frame. The table contains
 * the throughput values where the hosts are specified in the olumns and the
 * date and times or week/day IDs in the rows. No more than <max_nr_date_rows>
 * are displayed. This function is a member function of the "ThroughputTable"
 * class. Its class name is "writeTable".
 *
 *    @param  date_ind   The index of the time/date ID for which the first
 *			 throughput data are displayed. The other dates are
 *                       displayed in reverse order. When the index is less than
 *			 zero the most recent date is displayed.
 *
 *    @return  True when the table could be written; false otherwise.
 *
***************************************/
function writeThroughputTable (date_ind)
/**************************************/
{
    var nr_dates;
    var days;
    var times_or_date_ids;
    var host;
    var hosts;
    var host_titles;
    var nr_hosts;
    var is_skip_hosts;
    var qos_throughput_values;
    var inet_throughput_values;
    var is_skip_qos_cols;
    var is_skip_inet_cols;
    var host_qos_throughput_values;
    var host_inet_throughput_values; 
    var nr_date_rows;
    var i;
    var j;
    var k;
    var applet = document.applets ["NetData"];

    /*
     * If QoS is used, some row cells have a span of two; one otherwise.
     */

    var row_qos_span = (use_qos ? 2 : 1);

    /*
     * The column spans depend also from the skipped connections and is
     * therefore column dependent. Hence an array is used which will be
     * initialised later.
     */

    var col_qos_spans;

    /*
     * Force a start of the Applet. May be required when the Applet is not in
     * focus.
     */

    // applet.start ();

    /*
     * Get the # available dates from the Applet.
     */

    nr_dates = parseInt (applet.getNrDates ());

    /*
     * Show an alert box when no data could be read. In the place of the table a
     * diagnstic message is written and we return from this function.
     */

    if (nr_dates < 1)
    {
	showNoDataAvailable ();
	return false;
    }

    /*
     * Correct the date index when it is undefined or when the index is too
     * large.
     */

    if (date_ind < 0  ||  date_ind >= nr_dates)
	date_ind = nr_dates - 1;

    /*
     * Set the start date index.
     */

    start_date_ind = date_ind;

    /*
     * Obtain the # hosts from the Applet. Use it also to calculate the
     * # connections between the fixed host and the other hosts.
     */

    nr_hosts = parseInt (applet.getNrHosts ());
    nr_conns = nr_directs * (nr_hosts - 1);

    /*
     * Correct the index of the fixed host when it was not correctly obtained
     * from the cookie. Correct it also when required.
     */

    if (index_fixed_host < 0)
    {
	index_fixed_host = 0;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }
    else if (index_fixed_host >= nr_hosts)
    {
	index_fixed_host = nr_hosts - 1;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }

    /*
     * Allocate the JavaScript arrays. Note that the throughput values are
     * stored in a double array. See below.
     */

    days			= new Array (max_nr_date_rows);
    times_or_date_ids		= new Array (max_nr_date_rows);
    hosts			= new Array (nr_hosts);
    host_titles			= new Array (nr_hosts);
    skip_host_inds		= new Array (nr_hosts);
    is_skip_hosts		= new Array (nr_hosts);
    inet_throughput_values	= new Array (nr_hosts * nr_directs);
    is_skip_inet_cols		= new Array (nr_hosts * nr_directs);
    col_qos_spans		= new Array (nr_hosts * nr_directs);
    host_inet_throughput_values = new Array (nr_directs);

    if (use_qos)
    {
	qos_throughput_values	   = new Array (nr_hosts * nr_directs);
	is_skip_qos_cols	   = new Array (nr_hosts * nr_directs);
	host_qos_throughput_values = new Array (nr_directs);
    }

    /*
     * Obtain the dates and times/IDs where the throughput values are measured
     * from the Applet. They are stored in reverse order: most recent date
     * first.
     */

    date_ind = start_date_ind;
    i        = 0;

    while (i < max_nr_date_rows  &&  date_ind >= 0)
    {
	days		  [i] = applet.getDate	       (date_ind);
	times_or_date_ids [i] = applet.getTimeOrDateID (date_ind);

	date_ind -= date_ind_incr;
	i++;
    }

    nr_date_rows = i;    // The # rows used in this table.

    /*
     * Set the most recent time/ID in the time/ID input text field.
     */

    document.ControlButtons.TimeOrDateID.value = times_or_date_ids [0];

    /*
     *   Get the hosts from the applet
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	hosts	    [i] = applet.getHost      (i);
	host_titles [i] = applet.getHostTitle (i);
    }

    /*
     * Read the skipped host connections from the Applet for the one-directional
     * and two-directional case.
     */

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	if (i == index_fixed_host)    // Fixed host: not in table columns.
	{
	    if (use_qos)
	    {
		for (k = 0;  k < nr_directs;  k++)
		    is_skip_qos_cols [nr_directs * i + k] = true;
	    }

	    for (k = 0;  k < nr_directs;  k++)
		is_skip_inet_cols [nr_directs * i + k] = true;
	}
	else    // Not the fixed host.
	{
	    /*
	     * Set the QoS skipped connections when used.
	     */

	    if (use_qos)    // Use QoS.
	    {
		if (applet.isSkippedConnection (index_fixed_host, i,
						qos_key) == "true")
		{
		   is_skip_qos_cols [nr_directs * i] = true;
		}
		else
		{
		   is_skip_qos_cols [nr_directs * i] = false;
		}

		if (nr_directs == 2)
		{
		    if (applet.isSkippedConnection (i, index_fixed_host,
						    qos_key) == "true")
		    {
			is_skip_qos_cols [nr_directs * i + 1] = true;
		    }
		    else
		    {
			is_skip_qos_cols [nr_directs * i + 1] = false;
		    }
		}

	    } // Use QoS.

	    if (applet.isSkippedConnection (index_fixed_host, i,
					    inet_key) == "true")
	    {
		is_skip_inet_cols [nr_directs * i] = true;
	    }
	    else
	    {
		is_skip_inet_cols [nr_directs * i] = false;
	    }

	    if (nr_directs == 2)
	    {
		if (applet.isSkippedConnection (i, index_fixed_host,
						inet_key) == "true")
		{
		    is_skip_inet_cols [nr_directs * i + 1] = true;
		}
		else
		{
		    is_skip_inet_cols [nr_directs * i + 1] = false;
		}
	    }

	} // Not the fixed host.

    } // For all hosts.

    /*
     * Set the hosts which are completely skipped. The skipped host indices are
     * stored (but not for the fixed host), but also an array with flags is set.
     */

    nr_skip_hosts = 0;

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	var is_skip_host = true;

	if (use_qos)
	{
	    k = 0;

	    while (k < nr_directs  &&  is_skip_host)
	    {
		if (!is_skip_qos_cols [nr_directs * i + k])
		    is_skip_host = false;
		    
		k++;
	    }
	}

	k = 0;

	while (k < nr_directs  &&  is_skip_host)
	{
	    if (!is_skip_inet_cols [nr_directs * i + k])
		is_skip_host = false;

	    k++;
	}

	is_skip_hosts [i] = is_skip_host;

	if (is_skip_host  &&  i != index_fixed_host)
	    skip_host_inds [nr_skip_hosts++] = i;

    } // For all hosts.

    /*
     * Set the QoS column spans, depending if there are columns skipped.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (k = 0;  k < nr_directs;  k++)
	{
	    var col_ind = nr_directs * i + k;

	    col_qos_spans [col_ind] = 0;

	    if (use_qos)
		if (!is_skip_qos_cols [col_ind])
		    col_qos_spans [col_ind]++;

	    if (!is_skip_inet_cols [col_ind])
		col_qos_spans [col_ind]++;
	}
    }

    /*
     * Obtain the required throughput data from the Applet for the one-
     * directional and the two-directional case. Note that the index counts the
     * host, not the data.
     */

    for (i = 0; i  < nr_hosts;  i++)    // Do for all hosts.
    {
	if (is_skip_hosts [i])    // No QoS & Inet values for the skipped hosts.
	{
	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_throughput_values [nr_directs * i + k] = null;

		inet_throughput_values [nr_directs * i + k] = null;
	    }
	}
	else    // i != index_fixed_host
	{
	    /*
	     * Allocate (1-dim) storage for the QoS & Inet throughput data for
	     * the current host when the corresponding columns is not skipped.
	     * Do it for all directional connections.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		{
		    if (is_skip_qos_cols [nr_directs * i + k])
			host_qos_throughput_values [k] = null;
		    else
			host_qos_throughput_values [k] = new Array (
								 nr_date_rows);
		}

		if (is_skip_inet_cols [nr_directs * i + k])
		    host_inet_throughput_values [k] = null;
		else
		    host_inet_throughput_values [k] = new Array (nr_date_rows);
	    }

	    /*
	     * Reset the date index to the start date index.
	     */

	    date_ind = start_date_ind;

	    /*
	     * Parse the QoS & Inet throughput data. Also parse the data for the
	     * "back" direction when two-directional traffic is used.
	     */

	    for (j = 0;  j < nr_date_rows;  j++)
	    {
		if (use_qos)
		{
		    if (!is_skip_qos_cols [nr_directs * i])
			host_qos_throughput_values [0] [j] =
			    parseFloat (applet.getThroughputValue (
					    index_fixed_host, i,
					    date_ind, qos_key));

		    if (nr_directs == 2)
			if (!is_skip_qos_cols [nr_directs * i + 1])
			    host_qos_throughput_values [1] [j] =
				parseFloat (applet.getThroughputValue (
						i, index_fixed_host,
						date_ind, qos_key));
		}

		if (!is_skip_inet_cols [nr_directs * i])
		    host_inet_throughput_values [0] [j] =
			parseFloat (applet.getThroughputValue (
					index_fixed_host, i,
					date_ind, inet_key));

		if (nr_directs == 2)
		    if (!is_skip_inet_cols [nr_directs * i + 1])
			host_inet_throughput_values [1] [j] =
			    parseFloat (applet.getThroughputValue (
					    i, index_fixed_host,
					    date_ind, inet_key));

		date_ind -= date_ind_incr;

	    } // For all dates in the table.

	    /*
	     * Store the one dim. throughput data arrays in the double array
	     * containing all throughput data. Take care of the connection
	     * directions.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_throughput_values [nr_directs * i + k] =
			host_qos_throughput_values [k];

		inet_throughput_values [nr_directs * i + k] =
		    host_inet_throughput_values [k];
	    }

	} // Host is not skipped and is therefore also not the fixed host.

    } // For all hosts.

    /*
     * Determine the extrema from the throughput values. Take care of the test
     * directions.
     */

     if (use_qos)
	 this.setExtrema (qos_throughput_values, nr_hosts);

     this.setExtrema (inet_throughput_values, nr_hosts);

    /*
     * Initialise some local variables
     */

    var throughput_extrema = this.extrema;
    var color_scale	   = new ColorScale (throughput_extrema);
    var rgb_scale	   = new RGB        (0, 0, 0);
    var qos_value;
    var inet_value;

    /*
     * Write the table header and the date and time/ID headers in the title row.
     */

    parent.NetData.document.write (
        "<TABLE BORDER\n",
        "       FRAME\n",
        "       CELLPADDING=5>\n",
        "\n",
        "<CAPTION><B>Throughput [Mbit/s] from&nbsp;/&nbsp;to ",
		     hosts [index_fixed_host]
	);

    if (hosts [index_fixed_host] != host_titles [index_fixed_host])
	parent.NetData.document.write (
	    " (", host_titles [index_fixed_host], ")"
	    );

    parent.NetData.document.write ("</B>");

    /*
     * Write the hyper links to the ping data of the hosts to which no ping
     * tests are performed, when existing.
     */

    if (nr_skip_hosts > 0)    // There are skipped hosts.
    {
	parent.NetData.document.write (
	    "\n",
	    "</P>\n",
	    "\n",
	    "<P>\n",
	    "         Skipped tests:"
	    );

	j = 0;

	while (j < nr_skip_hosts)
	{
	    var skip_host_ind = skip_host_inds [j];

	    parent.NetData.document.write (
		" ",
		"<A HREF=",
		    "\"javascript:parent.Control.throughput_document.",
			  "writeNewHost(", start_date_ind, ",", skip_host_ind,
				      ")\"\n",
		    "   onMouseOver=\"window.status='Ping table ",
			    host_titles [skip_host_ind], "'; return true\"\n",
		    "  >", host_titles [skip_host_ind],"</A></TD>");

	    if (++j < nr_skip_hosts)
		parent.NetData.document.write (",");
	    else
		parent.NetData.document.write (".");
	}

    } // There are skipped hosts.

    parent.NetData.document.write (
	"</CAPTION>\n",
        "\n",
        "<TR ALIGN=\"center\"\n",
        "    VALIGN=\"middle\">\n",
        "<TD ROWSPAN=", row_qos_span, "><EM>Date</EM></TD>\n",
        "<TD ROWSPAN=", row_qos_span,
	   "><EM>", current_data.time_or_date_id_title, "</EM></TD>\n"
        );

    /*
     * Write the host names in the upper title row. Write two title columns with
     * direction marks when two-directional tests are used. When a QoS column
     * span is zero, all tests to/from this host are skipped and the
     * corresponding host column(s) is (are) not written which is always the
     * case for the fixed host.
     */

    for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
    {
	for (k = 0;  k < nr_directs;  k++)    // For all directions.
	{
	    var col_qos_span = col_qos_spans [nr_directs * j + k];

	    if (col_qos_span > 0)   // (Directional) host column(s) not skipped.
	    {
		parent.NetData.document.write (
		    "<TD COLSPAN=", col_qos_span, ">");

		if (nr_directs == 2) // Write direction marks at two-directions.
		{
		    if (k == 0)
			parent.NetData.document.write (">> ");
		    else
			parent.NetData.document.write ("<< ");
		}

		parent.NetData.document.write (
		    "<A HREF=",
			"\"javascript:parent.Control.throughput_document.",
			      "writeNewHost(", start_date_ind, ",", j, ")\"\n",
		    "   onMouseOver=\"window.status='Throughput table ",
			    host_titles [j], "'; return true\"\n",
		    "   onMouseOut=\"window.status=''; return true\"\n",
		    "  >", host_titles [j], "</A></TD>\n"
		    );
  
	    } // (Directional) host column(s) not skipped.

        } // For all directions.

    } // For all hosts.

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
        "</TR>\n",
        "\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

    if (use_qos)
    {
	/*
	 * Open the lower title row.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n"
	    );

	/*
	 * Write the net type title columns when the columns are not skipped.
	 */

	for (j = 0;  j < nr_hosts;  j++)
	{
	    for (k = 0;  k < nr_directs; k++)
	    {
		if (!is_skip_qos_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n"
			);

		if (!is_skip_inet_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
			);
	    }
	}

	/*
	 * Close the lower title row.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );

    } // Use QoS.

    /*
     * Write the dates, times/IDs and throughput values in the table
     */

    date_ind = start_date_ind;
    i        = 0;

    while (i < nr_date_rows)    // Do for selected dates
    {
       
        /*
         * Open a new table row.
         */

        parent.NetData.document.write (
            "<TR ALIGN=\"center\"\n",
            "    VALIGN=\"middle\">\n"
            );

        /*
         * Write the the date and time/ID columns.
         */

        parent.NetData.document.write (
            "<TD>", days [i], "</TD>\n",
            "<TD><A HREF=",
		"\"javascript:parent.Control.net_data_overview_document.",
		      "write(", date_ind, ",true)\"\n",
	    "       onMouseOver=\"window.status='Overview table ",
			times_or_date_ids [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", times_or_date_ids [i], "</A></TD>\n"
            );

        /*
         * Write the throughput values for all hosts in the table row. Do it for
	 * all connections which are not skipped. The fixed host is default
	 * skipped.
         */

        for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
        {
	    for (k = 0;  k < nr_directs;  k++)    // For all directions.
	    {
		if (use_qos)
		    if (!is_skip_qos_cols [nr_directs*j + k])
		    {
			qos_value =
			    qos_throughput_values [nr_directs*j + k] [i];
			writeColumnValue (color_scale, rgb_scale,
					  qos_value, qos_value,
					  "", "");
		    }

		if (!is_skip_inet_cols [nr_directs*j + k])
		{
		    inet_value = inet_throughput_values [nr_directs*j + k] [i];
		    writeColumnValue (color_scale, rgb_scale,
				      inet_value, inet_value,
				      "", "");
		}

	    } // For all directions.

        } // For all hosts.

        /*
         * Close the table row.
         */

        parent.NetData.document.write (
            "</TR>\n",
            "\n");

        date_ind -= date_ind_incr;
        i++;

    } // For all dates.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

    return true;    // The table could be written.

} // function writeThroughputTable ()


/**
 * This function writes a HTML TABLE to the data body frame. The table contains
 * the UDP bandwidth values where the hosts are specified in the columns and the
 * date and times or week/day IDs in the rows. No more than <max_nr_date_rows>
 * are displayed. Columns with skipped connections are not printed. This
 * function is a member function of the "UDPBWTable" class. Its class name is
 * "writeTable".
 *
 *    @param  date_ind   The index of the time/date ID for which the first UDP
 *                       bandwidth data are displayed. The other dates are
 *			 displayed in reverse order. When the index is less than
 *			 zero the most recent date is displayed.
 *
 *    @return  True when the table could be written; false otherwise.
 *
**********************************/
function writeUDPBWTable (date_ind)
/*********************************/
{
    var nr_dates;
    var days;
    var times_or_date_ids;
    var host;
    var hosts;
    var host_titles;
    var skip_host_inds;
    var nr_hosts;
    var is_skip_hosts;
    var qos_udp_bw_values;
    var inet_udp_bw_values;
    var is_skip_qos_cols;
    var is_skip_inet_cols;
    var host_qos_udp_bw_values;
    var host_inet_udp_bw_values;
    var nr_date_rows;
    var i;
    var j;
    var k;
    var applet = document.applets ["NetData"];

    /*
     * If QoS is used, some row cells have a span of two; one otherwise.
     */

    var row_qos_span = (use_qos ? 2 : 1);

    /*
     * The column spans depend also from the skipped connections and is
     * therefore column dependent. Hence an array is used which will be
     * initialised later.
     */

    var col_qos_spans;

    /*
     * Force a start of the Applet. May be required when the Applet is not in
     * focus.
     */

    // applet.start ();

    /*
     * Get the # available dates from the Applet.
     */

    nr_dates = parseInt (applet.getNrDates ());

    /*
     * Show an alert box when no data could be read. In the place of the table a
     * diagnstic message is written and we return from this function.
     */

    if (nr_dates < 1)
    {
	showNoDataAvailable ();
	return false;
    }

    /*
     * Correct the date index when it is undefined or when the index is too
     * large.
     */

    if (date_ind < 0  ||  date_ind >= nr_dates)
	date_ind = nr_dates - 1;

    /*
     * Set the start date index.
     */

    start_date_ind = date_ind;

    /*
     * Obtain the # hosts from the Applet.
     */

    nr_hosts = parseInt (applet.getNrHosts ());

    /*
     * Correct the index of the fixed host when it was not correctly obtained
     * from the cookie. Correct it also when required.
     */

    if (index_fixed_host < 0)
    {
	index_fixed_host = 0;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }
    else if (index_fixed_host >= nr_hosts)
    {
	index_fixed_host = nr_hosts - 1;
	setCookie (index_fixed_host_cookie, index_fixed_host);
    }

    /*
     * Allocate the JavaScript arrays. Note that the UDP bandwidth values are
     * stored in a double array. See below.
     */

    days		    = new Array (max_nr_date_rows);
    times_or_date_ids	    = new Array (max_nr_date_rows);
    hosts		    = new Array (nr_hosts);
    host_titles		    = new Array (nr_hosts);
    skip_host_inds	    = new Array (nr_hosts);
    is_skip_hosts	    = new Array (nr_hosts);
    inet_udp_bw_values	    = new Array (nr_hosts * nr_directs);
    is_skip_inet_cols	    = new Array (nr_hosts * nr_directs);
    col_qos_spans	    = new Array (nr_hosts * nr_directs);
    host_inet_udp_bw_values = new Array (nr_directs);

    if (use_qos)
    {
	qos_udp_bw_values      = new Array (nr_hosts * nr_directs);
	is_skip_qos_cols       = new Array (nr_hosts * nr_directs);
	host_qos_udp_bw_values = new Array (nr_directs);
    }

    /*
     * Obtain the dates and times/IDs where the UDP bandwidth values are
     * measured from the Applet. They are stored in reverse order: most recent
     * date first.
     */

    date_ind = start_date_ind;
    i        = 0;

    while (i < max_nr_date_rows  &&  date_ind >= 0)
    {
	days		  [i] = applet.getDate	       (date_ind);
	times_or_date_ids [i] = applet.getTimeOrDateID (date_ind);

	date_ind -= date_ind_incr;
	i++;
    }

    nr_date_rows = i;    // The # rows used in this table.

    /*
     * Set the most recent time/ID in the time/ID input text field.
     */

    document.ControlButtons.TimeOrDateID.value = times_or_date_ids [0];

    /* 
     * Get the hosts (titles) from the applet.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	hosts	    [i] = applet.getHost      (i);
	host_titles [i] = applet.getHostTitle (i);
    }

    /*
     * Read the skipped host connections from the Applet for the one-directional
     * and two-directional case.
     */

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	if (i == index_fixed_host)    // Fixed host: not in table columns.
	{
	    if (use_qos)
	    {
		for (k = 0;  k < nr_directs;  k++)
		    is_skip_qos_cols [nr_directs * i + k] = true;
	    }

	    for (k = 0;  k < nr_directs;  k++)
		is_skip_inet_cols [nr_directs * i + k] = true;
	}
	else    // Not the fixed host.
	{
	    /*
	     * Set the QoS skipped connections when used.
	     */

	    if (use_qos)    // Use QoS.
	    {
		if (applet.isSkippedConnection (index_fixed_host, i,
						qos_key) == "true")
		{
		   is_skip_qos_cols [nr_directs * i] = true;
		}
		else
		{
		   is_skip_qos_cols [nr_directs * i] = false;
		}

		if (nr_directs == 2)
		{
		    if (applet.isSkippedConnection (i, index_fixed_host,
						    qos_key) == "true")
		    {
			is_skip_qos_cols [nr_directs * i + 1] = true;
		    }
		    else
		    {
			is_skip_qos_cols [nr_directs * i + 1] = false;
		    }
		}

	    } // Use QoS.

	    if (applet.isSkippedConnection (index_fixed_host, i,
					    inet_key) == "true")
	    {
		is_skip_inet_cols [nr_directs * i] = true;
	    }
	    else
	    {
		is_skip_inet_cols [nr_directs * i] = false;
	    }

	    if (nr_directs == 2)
	    {
		if (applet.isSkippedConnection (i, index_fixed_host,
						inet_key) == "true")
		{
		    is_skip_inet_cols [nr_directs * i + 1] = true;
		}
		else
		{
		    is_skip_inet_cols [nr_directs * i + 1] = false;
		}
	    }

	} // Not the fixed host.

    } // For all hosts.

    /*
     * Set the hosts which are completely skipped. The skipped host indices are
     * stored (but not for the fixed host), but also an array with flags is set.
     */

    nr_skip_hosts = 0;

    for (i = 0;  i < nr_hosts;  i++)    // For all hosts.
    {
	var is_skip_host = true;

	if (use_qos)
	{
	    k = 0;

	    while (k < nr_directs  &&  is_skip_host)
	    {
		if (!is_skip_qos_cols [nr_directs * i + k])
		    is_skip_host = false;
		    
		k++;
	    }
	}

	k = 0;

	while (k < nr_directs  &&  is_skip_host)
	{
	    if (!is_skip_inet_cols [nr_directs * i + k])
		is_skip_host = false;

	    k++;
	}

	is_skip_hosts [i] = is_skip_host;

	if (is_skip_host  &&  i != index_fixed_host)
	    skip_host_inds [nr_skip_hosts++] = i;

    } // For all hosts.

    /*
     * Set the QoS column spans, depending if there are columns skipped.
     */

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (k = 0;  k < nr_directs;  k++)
	{
	    var col_ind = nr_directs * i + k;

	    col_qos_spans [col_ind] = 0;

	    if (use_qos)
		if (!is_skip_qos_cols [col_ind])
		    col_qos_spans [col_ind]++;

	    if (!is_skip_inet_cols [col_ind])
		col_qos_spans [col_ind]++;
	}
    }

    /*
     * Obtain the required UDP bandwidth data from the Applet for the one-
     * directional and two-directional case. Note that the index counts the
     * host, not the data.
     */

    for (i = 0;  i < nr_hosts;  i++)    // Do for all hosts.
    {
	if (is_skip_hosts [i])    // No QoS & Inet values for the skipped hosts.
	{
	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_udp_bw_values [nr_directs * i + k] = null;

		inet_udp_bw_values [nr_directs * i + k] = null;
	    }
	}
	else    // Host is unequal to the fixed host and not skipped.
	{
	    /*
	     * Allocate (1-dim) storage for the QoS & Inet UDP bandwidth data
	     * for the current host when the corresponding column is not
	     * skipped. Do it for all directional connections.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		{
		    if (is_skip_qos_cols [nr_directs * i + k])
			host_qos_udp_bw_values [k] = null;
		    else
			host_qos_udp_bw_values [k] = new Array (nr_date_rows);
		}

		if (is_skip_inet_cols [nr_directs * i + k])
		    host_inet_udp_bw_values [k] = null;
		else
		    host_inet_udp_bw_values [k] = new Array (nr_date_rows);
	    }

	    /*
	     * Reset the date index to the start date index.
	     */

	    date_ind = start_date_ind;

	    /*
	     * Parse the QoS & Inet UDP bandwidth data. Also parse the data for
	     * the "back" direction when two-directional traffic is used.
	     */

	    for (j = 0;  j < nr_date_rows;  j++)
	    {
		if (use_qos)
		{
		    if (!is_skip_qos_cols [nr_directs * i])
			host_qos_udp_bw_values [0] [j] =
			    parseFloat (applet.getUDPBWValue (
					    index_fixed_host, i, date_ind,
					    udp_bw_type, qos_key));

		    if (nr_directs == 2)
			if (!is_skip_qos_cols [nr_directs * i + 1])
			    host_qos_udp_bw_values [1] [j] =
				parseFloat (applet.getUDPBWValue (
						i, index_fixed_host, date_ind,
						udp_bw_type, qos_key));
		}

		if (!is_skip_inet_cols [nr_directs * i])
		    host_inet_udp_bw_values [0] [j] =
			parseFloat (applet.getUDPBWValue (
					index_fixed_host, i, date_ind,
					udp_bw_type, inet_key));

		if (nr_directs == 2)
		    if (!is_skip_inet_cols [nr_directs * i + 1])
			host_inet_udp_bw_values [1] [j] =
			    parseFloat (applet.getUDPBWValue (
					    i, index_fixed_host, date_ind,
					    udp_bw_type, inet_key));

		date_ind -= date_ind_incr;

	    } // For all dates in the table.

	    /*
	     * Store the one dim. UDP bandwidth data arrays in the double array
	     * containing all UDP bandwidth data. Take care of the connection
	     * directions.
	     */

	    for (k = 0;  k < nr_directs;  k++)
	    {
		if (use_qos)
		    qos_udp_bw_values [nr_directs * i + k] =
			host_qos_udp_bw_values [k];

		inet_udp_bw_values [nr_directs * i + k] =
		    host_inet_udp_bw_values [k];
	    }

	} // Host is not skipped and is therefore also not the fixed host.

    } // For all hosts.

    /* 
     * Determine the extrema from the UDP bandwidth values. Take care of the
     * test directions.
     */

     if (use_qos)
	 this.setExtrema (qos_udp_bw_values, nr_hosts);

     this.setExtrema (inet_udp_bw_values, nr_hosts);

     /* 
     * Initialise some local variables
     */
    
    var udp_bw_extrema = this.extrema;
    var color_scale    = new ColorScale (udp_bw_extrema);
    var rgb_scale      = new RGB        (0, 0, 0);
    var qos_value;
    var inet_value;

    /*
     * Write the table header and the date and time/ID headers in the title row.
     */

    parent.NetData.document.write (
        "<TABLE BORDER\n",
        "       FRAME\n",
        "       CELLPADDING=5>\n",
        "\n",
        "<CAPTION><B>UDP ", udp_bw_type_short_titles [udp_bw_type], " ",
		     udp_bw_type_units [udp_bw_type], " from&nbsp;/&nbsp;to ",
		     hosts [index_fixed_host]
	);

    if (hosts [index_fixed_host] != host_titles [index_fixed_host])
	parent.NetData.document.write (
	    " (", host_titles [index_fixed_host], ")"
	    );

    parent.NetData.document.write ("</B>");

    /*
     * Write the hyper links to the UDP bandwidth data of the hosts to which no
     * UDP bandwidth tests are performed, when existing.
     */

    if (nr_skip_hosts > 0)    // There are skipped hosts.
    {
	parent.NetData.document.write (
	    "\n",
	    "</P>\n",
	    "\n",
	    "<P>\n",
	    "         Skipped tests:"
	    );

	j = 0;

	while (j < nr_skip_hosts)
	{
	    var skip_host_ind = skip_host_inds [j];

	    parent.NetData.document.write (
		" ",
		"<A HREF=",
		    "\"javascript:parent.Control.udp_bw_document.",
			  "writeNewHost(", start_date_ind, ",", skip_host_ind,
				      ")\"\n",
		    "   onMouseOver=\"window.status='Ping table ",
			    host_titles [skip_host_ind], "'; return true\"\n",
		    "  >", host_titles [skip_host_ind],"</A></TD>");

	    if (++j < nr_skip_hosts)
		parent.NetData.document.write (",");
	    else
		parent.NetData.document.write (".");
	}

    } // There are skipped hosts.

    parent.NetData.document.write (
	"</CAPTION>\n",
        "\n",
        "<TR ALIGN=\"center\"\n",
        "    VALIGN=\"middle\">\n",
        "<TD ROWSPAN=", row_qos_span, "><EM>Date</EM></TD>\n",
        "<TD ROWSPAN=", row_qos_span,
	   "><EM>", current_data.time_or_date_id_title, "</EM></TD>\n"
        );

    /*
     * Write the host names in the upper title row. Write two title columns with
     * direction marks when two-directional tests are used. When a QoS column
     * span is zero, all tests to/from this host are skipped and the
     * corresponding host column(s) is (are) not written which is always the
     * case for the fixed host.
     */

    for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
    {
	for (k = 0;  k < nr_directs;  k++)    // For all directions.
	{
	    var col_qos_span = col_qos_spans [nr_directs * j + k];
	    
	    if (col_qos_span > 0)   // (Directional) host column(s) not skipped.
	    {
		parent.NetData.document.write (
		    "<TD COLSPAN=", col_qos_span, ">"
		    );

		if (nr_directs == 2) // Write direction marks at two-directions.
		{
		    if (k == 0)
			parent.NetData.document.write (">> ");
		    else
			parent.NetData.document.write ("<< ");
		}

		parent.NetData.document.write (
		    "<A HREF=",
			"\"javascript:parent.Control.udp_bw_document.",
			      "writeNewHost(", start_date_ind, ",", j, ")\"\n",
		    "   onMouseOver=\"window.status='Ping table ",
			    host_titles [j], "'; return true\"\n",
		    "   onMouseOut=\"window.status=''; return true\"\n",
		    "  >", host_titles [j],"</A></TD>\n");

	    } // (Directional) host column(s) not skipped.
	
	} // For all directions.

    } // For all hosts.

    /*
     * Close the upper title row.
     */

    parent.NetData.document.write (
        "</TR>\n",
        "\n"
	);

    /*
     * Write the net types in the lower title row when QoS is used.
     */

    if (use_qos)
    {
	/*
	 * Open the lower title row.
	 */

	parent.NetData.document.write (
	    "<TR ALIGN=\"center\"\n",
	    "    VALIGN=\"middle\">\n"
	    );

	/*
	 * Write the net type title columns when the columns are not skipped.
	 */

	for (j = 0;  j < nr_hosts;  j++)
	{
	    for (k = 0;  k < nr_directs; k++)
	    {
		if (!is_skip_qos_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", qos_title,  "</FONT></TD>\n"
			);

		if (!is_skip_inet_cols [nr_directs * j + k])
		    parent.NetData.document.write (
			"<TD><FONT SIZE=-1>", inet_title, "</FONT></TD>\n"
			);
	    }
	}

	/*
	 * Close the lower title row.
	 */

	parent.NetData.document.write (
	    "</TR>\n",
	    "\n"
	    );
    
    } // Use QoS.

    /* 
     * Write the dates, times/IDs and UDP bandwidth values in the table
     */

    date_ind = start_date_ind;
    i	     = 0;

    while (i < nr_date_rows)    // Do for selected dates
    {
        
	/*
	 * Open a new table row.
	 */

        parent.NetData.document.write (
            "<TR ALIGN=\"center\"\n",
            "    VALIGN=\"middle\">\n"
            );

        /*
         * Write the the date and time/ID columns.
         */
           
        parent.NetData.document.write (
            "<TD>", days [i], "</TD>\n",
            "<TD><A HREF=",
		"\"javascript:parent.Control.net_data_overview_document.",
		      "write(", date_ind, ",true)\"\n",
	    "       onMouseOver=\"window.status='Overview table ",
			times_or_date_ids [i], "'; return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >", times_or_date_ids [i], "</A></TD>\n"
            );
 
        /*
         * Write the UDP bandwidth values for all hosts in the table row. Do it
	 * for all connections which are not skipped. The fixed host is default
	 * skipped.
         */

        for (j = 0;  j < nr_hosts;  j++)    // For all hosts.
        {
	    for (k = 0;  k < nr_directs;  k++)    // For all directions.
	    {
		if (use_qos)
		    if (!is_skip_qos_cols [nr_directs*j + k])
		    {
			qos_value =
			    qos_udp_bw_values [nr_directs*j + k] [i];
			writeColumnValue (color_scale, rgb_scale,
					  qos_value, qos_value,
					  "", "");
		    }

		if (!is_skip_inet_cols [nr_directs*j + k])
		{
		    inet_value = inet_udp_bw_values [nr_directs*j + k] [i];
		    writeColumnValue (color_scale, rgb_scale,
				      inet_value, inet_value,
				      "", "");
		}

	    } // For all directions.

	} // For all hosts.

	/*
         * Close the table row.
         */

        parent.NetData.document.write (
            "</TR>\n",
            "\n");

	date_ind -= date_ind_incr;
	i++;
    
    } // For all dates.

    /*
     * Close the table.
     */

    parent.NetData.document.write ("</TABLE>\n");

    return true;    // The table could be written.

} // function writeUDPBWTable ()

/**
 * This function writes the load colormap table. It is a member function
 * of the "LoadTable" class. The class name of this function is
 * "writeColorTable"
 *
******************************/
function writeLoadColorTable ()
/*****************************/
{
    color_map.writeTable ("load", this.extrema, 1, 1000.0);
}

/**
 * This function writes the ping colormap table. It is a member function
 * of the "PingTable" class. The class name of this function is
 * "writeColorTable"
 *
******************************/
function writePingColorTable ()
/*****************************/
{
    color_map.writeTable ("roundtrip " + ping_type_units [ping_type],
			  this.extrema, 1, 1000.0);
}

/**
 * This function writes the throughput colormap table. It is a member function
 * of the "ThroughputTable" class. The class name of this function is
 * "writeColorTable"
 *
************************************/
function writeThroughputColorTable ()
/***********************************/
{
    color_map.writeTable ("throughput [Mbit/s]", this.extrema, 1, 1000.0);
}

/**
 * This function writes the UDP bandwidth colormap table. It is a member
 * function of the "UDPBWTable" class. The class name of this function is
 * "writeColorTable"
 *
*******************************/
function writeUDPBWColorTable ()
/******************************/
{
    color_map.writeTable ("roundtrip " + udp_bw_type_units [udp_bw_type],
			  this.extrema, 1, 1000.0);
}

/**
 * 1. Show an alert box when no data could be read. This is only done when no
 *    timer was set.
 *
 * 2. Write a diagnostic message in the data body that there are no data
 *    available.
 *
******************************/
function showNoDataAvailable ()
/*****************************/
{
    if (!repeat_write.timer_set) 
    {
	/*
	 * Select the data type index from the data type selector when no data
	 * file is upploaded. Use it to give a diagnostic message, depending
	 * from the data type. Note that the data from a specific selected date
	 * could not be selected when they are not available. Hence no error
	 * message is required here.
	 *
	 * When a data file is imported, give a special diagnostic message.
	 */

	var option_index;

	if (import_data)
	{
	    option_index = recent_data_index;
	}
	else
	{
	    var options = document.ControlButtons.SelectNetDataType.options;
	    
	    option_index = options [options.selectedIndex].value;
	}

	if (import_data)   // Give a special message when importing a data file.
	{
	    window.alert ("An error occurred during the reading of the " +
			  "net test data.\n" +
			  "Please check the Java Console. Reasons for the " +
			  "failure may be:\n" +
			  "   o The file path is incorrect.\n" +
			  "   o Security exception: the data file is not in " +
			  "the HTML directory tree.\n");
	}
	else if (option_index == recent_data_index)    // Most recent data.
	{
	    window.alert ("The net test data of the last " + nr_days +
			  " days could not be read,\n" +
			  "presumably because the data file was refreshed at " +
			  "the\n" +
			  "time of reading. Please try it some time later ...");
	}
	else if (option_index == week_mean_data_index)   // Week mean values.
	{
	    window.alert ("The net test week mean data could not be read,\n" +
			  "presumably because the data file was not yet\n" +
			  "created. Please try it the next week ...");
	}
	else if (option_index == day_mean_data_index)    // Day mean values.
	{
	    window.alert ("The net test day mean data could not be read,\n" +
			  "presumably because the data file was not yet\n" +
			  "created. Please try it tomorrow ...");
	}
	else    // Unknown data type. Give a generic diagnostic message.
	{
	    window.alert ("The net test data could not be read.");
	}

    } // Show an alert window for the varioue data types.

    /*
     * Write a diagnostic message in the data body that there are no data
     * available.
     */

    parent.NetData.document.write (
	"<FONT SIZE=+1>At the moment there are no net test data\n",
	"              available.</FONT>\n");

} // function showNoDataAvailable ()

/**
 * Calculate the load extrema from the double array with load values.
 *
 *    @param  load_values    The double array with load values.
 *			     The first index gives the load values for
 *			     a host.
 *
 *    @param  nr_hosts	     The # hosts.
 *
**********************************************/
function setLoadExtrema (load_values, nr_hosts)
/*********************************************/
{
    var load_extrema = this.extrema;
    var i;

    for (i = 0;  i < nr_hosts;  i++)
	load_extrema.set (load_values [i], 0, LINEAR, 0.0);

    if (load_extrema.max < max_load_lower_bound)
	load_extrema.max = max_load_lower_bound;

} // function setLoadExtrema ()

/**
 * Calculate the ping extrema from the double array with ping values. Take care
 * for one-directional and two-directional tests traffic.
 *
 *    @param  ping_values   The double array with ping values. The first index
 *			    gives the load values for a host.
 *
 *    @param  nr_hosts	    The # hosts.
 *
********************************************/
function setPingExtrema (ping_data, nr_hosts)
/*******************************************/
{
    var ping_extrema = this.extrema;
    var i;
    var j;

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (j = 0;  j < nr_directs;  j++)
	{
	    if (ping_data [nr_directs * i + j] != null)    // Ping data defined.
	    {
		ping_extrema.set (ping_data [nr_directs * i + j].values, 0,
				  ping_scale_type, min_log_ping);
	    }
	}
    }

} // function setPingExtrema ()

/**
 * Calculate the throughput extrema from the double array with throughput
 * values. Take care for one-directional and two-directional tests traffic.
 *
 *    @param  throughput_values   The double array with throughput values. The
 *				  first index gives the throughput values for a
 *				  host.
 *
 *    @param  nr_hosts		  The # hosts.
 *
**********************************************************/
function setThroughputExtrema (throughput_values, nr_hosts)
/*********************************************************/
{
    var throughput_extrema = this.extrema;
    var i;
    var j;

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (j = 0;  j < nr_directs;  j++)
	{
	    if (throughput_values [nr_directs * i + j] != null)
	    {
		throughput_extrema.set (throughput_values [nr_directs * i + j],
					0,
					throughput_scale_type,
					min_log_throughput);
	    }
        }
    }

} // function setThroughputExtrema ()

/**
 * Calculate the UDP bandwidth extrema from the double array with ping values.
 * Take care for one-directional and two-directional tests traffic.
 *
 *    @param  udp_bw_values   The double array with UDP bandwidth values. The
 *			      first index gives the load values for a host.
 *
 *    @param  nr_hosts	      The # hosts.
 *
*************************************************/
function setUDPBWExtrema (udp_bw_values, nr_hosts)
/************************************************/
{
    var udp_bw_extrema = this.extrema;
    var i;
    var j;

    for (i = 0;  i < nr_hosts;  i++)
    {
	for (j = 0;  j < nr_directs;  j++)
	{
	    if (udp_bw_values [nr_directs * i + j] != null)
	    {
		udp_bw_extrema.set (udp_bw_values [nr_directs * i + j], 0,
				    udp_bw_scale_type, min_log_udp_bw);
	    }
	}
    }

} // function setUDPBWExtrema ()

/**
 * Write a column value in the table. When the value is defined the
 * background of the column is scaled according to the intensity of the
 * value. An undefined value is displayed as a blank column with a gray
 * background. When this undefined value is from a skipped connection, it is
 * denoted with corresponding string.
 *
 *    @param  color_scale   The object used to scale the background
 *			    color.
 *
 *    @param  rgb_scale     The RGB object to which the scaling result
 *			    is returned.
 *
 *    @param  value	    The intensity value to be scaled.
 *
 *    @param  value_str	    The string representation of the intensity value to
 *			    be scaled.
 *
 *    @param  td_flags	    A String with flags of the <TD> element.
 *
 *    @param  value_type    The HTML type element in which the value
 *			    should be typesetted. When undefined the
 *			    value is typesetted with plain type.
 *
*****************************************************************************/
function writeColumnValue (color_scale, rgb_scale, value, value_str, td_flags,
			   value_type)
/****************************************************************************/
{
    if (value < conn_skip_bound)    // Undefined value and connection skipped.
    {
	/*
	parent.NetData.document.write (
	    "<TD ", td_flags, " STYLE=\"background-color: #C0C0C0\"",
	       ">---</TD>\n"
	    );
	*/

	parent.NetData.document.write (
	    "<TD ", td_flags, " BGCOLOR=\"#C0C0C0\">---</TD>\n"
	    );
    }
    else if (value < no_data_bnd)    // Undefined value.
    {
	/*
	parent.NetData.document.write (
	    "<TD ", td_flags, " STYLE=\"background-color: #C0C0C0\"",
	       ">&nbsp;</TD>\n"
	    );
	*/

	parent.NetData.document.write (
	    "<TD ", td_flags, " BGCOLOR=\"#C0C0C0\">&nbsp;</TD>\n"
	    );
    }
    else    // Defined value.
    {
	color_scale.scaleRGB (rgb_scale, value);    // Scale color.

	/*
	 * Adjust the value by adding the required HTML elements when
	 * defined.
	 */

	if (value_type != "")
	    value_str = "<"  + value_type + ">" + value_str +
			"</" + value_type + ">";

	writeColorColumn (rgb_scale, value_str, td_flags);
    
    } // Defined value.

} // function writeColumnValue ()

/**
 * Constructor of the "NetDataOverviewDocument" class.
 *
**********************************/
function NetDataOverviewDocument ()
/*********************************/
{
    this.write	      = writeNetDataOverviewDocument;	     // Wr. overv. doc.
    this.writeNewForm = writeNewFormNetDataOverviewDocument; // Comp./Long form.
    this.plot	      = plotLoadData;			     // Default plot.
}

/**
 * Write HTML overview page in the other form.
 *
 *    @param  date_ind   The index of the time for which the overview
 *			 data are displayed. When the index is less than
 *			 zero the last index is displayed.
 *
******************************************************/
function writeNewFormNetDataOverviewDocument (date_ind)
/*****************************************************/
{
    compact_overview_table = !compact_overview_table;

    setCookie (compact_overview_table_cookie, compact_overview_table);

    net_data_overview_document.write (date_ind, false);

} // fucntion writeNewFormNetDataOverviewDocument ()

/**
 * Write the HTML page into the data body frame which gives an overview
 * of the net data at a certain time specified by its index.
 *
 * If a plot of the data is shown the current data type is replotted.
 *
 * See also the comment header of "writeNetDataOverviewTable()".
 *
 *    @param  date_ind     The index of the time for which the overview
 *			   data are displayed. When the index is less than
 *			   zero the last index is displayed.
 *
 *    @param  need_plot    If "true" plot the data when the data window is
 *			   shown; otherwise do not plot.
 *
**********************************************************/
function writeNetDataOverviewDocument (date_ind, need_plot)
/*********************************************************/
{
    var plot = this.plot = current_document.plot;
    
    /*
     * Set the current_document to this class. Set also the cookie.
     */

    current_document = net_data_overview_document;

    setDocumentTypeCookie ();

    /*
     * Create an instance of the "NetDataOverviewTable" class.
     */

    var net_data_overview_table = new NetDataOverviewTable ();

    /*
     * Reopen the data body frame.
     */

    parent.NetData.document.open ();

    /*
     * Write the page header.
     */

    parent.NetData.document.write (
	"<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
	"\n",
	"<BODY>\n",
	"\n",
	"<H1>Overview Net Tests between ", hosts_title, " Hosts</H1>\n",
	"\n",
	"<P>\n",
	"<UL>\n",
	"\n"
	);

    /*
     * Write the authorisation to set cookies when no cookies are allowed.
     */

    if (!allow_cookies)
	parent.NetData.document.write (
	    "<LI><A HREF=\"javascript:parent.Control.doAllowCookies(",
			       date_ind, ")\"\n",
	    "       onMouseOver=\"window.status='Authorise alert window'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >Authorise here</A> to store the current\n",
	    "    table settings in your cookies file.\n",
	    "\n"
	    );

    /*
     * Write hyperlinks to the "getting started" introduction and to the user
     * guide.
     */
	    
    parent.NetData.document.write (
	"<LI>See the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/get_start.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Getting started introduction'; ",
		    "return true\"\n",
	"      >getting started</A> introduction or the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/overview.html')\"",
		"\n",
	"       onMouseOver=\"window.status='User guide overview table'; ",
		    "return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
	"      >user guide</A> for a description of the table below.</LI>\n",
	"\n"
	);

    /*
     * Write a hyperlink to the host documentation when it is available and when
     * the data file resides from the same hosts set as this version. This may
     * differ in the imported version.
     */

    if (is_hosts_doc  &&  hosts_title == orig_hosts_title)
	parent.NetData.document.write (
	    "<LI>See also the\n",
	    "    <A HREF=",
		   "\"javascript:parent.Control.showHelp('hosts/index.html')\"",
		   "\n",
	    "       onMouseOver=\"window.status='Hosts documentation'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >hosts documentation</A>.</LI>\n",
	    "\n"
	    );

    /*
     * Write a hyperlink to the observations about the package HTML file. Close
     * the page header and related lines. Open the ping and UDP bandwidth type
     * links.
     */

    parent.NetData.document.write (
	"<LI>Some\n",
	"    <A HREF=",
	        "\"javascript:parent.Control.showHelp('about/index.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Observations about the ",
		    "package'; return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
	"      >observations</A>\n",
	"    about the package and the required bandwidth.</LI>\n",
	"\n",
	"</UL>\n",
	"</P>\n",
	"\n",
	"<H4>\n"
	);

    /*
     * Write the links to the various ping types.
     */

    writePingTypeLinks (date_ind);

    /*
     * Write the links to the various UDP bandwidth types, when these tests are
     * performed.
     */

    if (use_udp_bw)
    {
	parent.NetData.document.write ("<BR>\n");

	writeUDPBWTypeLinks (date_ind);
    }

    parent.NetData.document.write (
	"</H4>\n",
	"<P>\n",
	"<CENTER>\n"
	);

    /*
     * Write the overview table. Close the document and stop when this is not
     * possible.
     */

    if (!net_data_overview_table.writeTable (date_ind))
    {
	parent.NetData.document.close ();
	return;
    }

    /*
     * If a plot is shown and plotting is selected: replot the current net data
     * type with the same start date index used in this table.
     */

    if (need_plot)
	plot (date_ind, false);

    /*
     * Write some information about the scaling.
     */

    parent.NetData.document.write (
	"</CENTER>\n",
	"</P>\n",
	"\n",
	"<P>\n",
	"The <EM>load</EM>, <EM>roundtrip</EM>"
	);

    if (use_udp_bw)
	parent.NetData.document.write (
	    ", <EM>throughput</EM> and <EM>UDP\n"
	    );
    else
	parent.NetData.document.write (" and <EM>throughput</EM>\n");

    parent.NetData.document.write (
	"data series are each scaled with their private color distributions\n",
	"as is displayed below:<BR>\n",
	"&nbsp;<BR>\n",
	"\n",
	"<CENTER>\n"
	);

    /*
     * Write the table with the used color scaling.
     */

    net_data_overview_table.writeColorTable ();

    parent.NetData.document.write ("</CENTER>\n");

    /*
     * Write the hyperlink to the net data file when this version is not the
     * import version.
     */

    if (!import_data)
    {
	parent.NetData.document.write (
	    "</P>\n",
	    "\n",
	    "<P>\n"
	    );

	writeNetDataHyperLink ();
    }

    /*
     * Write the page trailor.
     */

    parent.NetData.document.write (
	"</P>\n",
	"\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);

    /*
     * Close the data body document frame. If we do not close it the
     * browser will keep waiting for data.
     */

    parent.NetData.document.close ();

} // function writeNetDataOverviewDocument ()

/**
 * Constructor of the "LoadDocument" class.
 *
***********************/
function LoadDocument ()
/**********************/
{
    this.write = writeLoadDocument;    // Function to write the load document.
    this.plot  = plotLoadData;	       // Function to plot the load data.
}

/**
 * Constructor of the "PingDocument" class.
 *
***********************/
function PingDocument ()
/**********************/
{
    this.write		   = writePingDocument;
			     // Function to write new ping document.
    this.writeNewHost	   = writeNewHostPingDocument;
			     // Function to write ping document with a new host.
    this.writeNewPingType  = writeNewPingTypeDocument;
			     // Function to write document with a new ping type.
    this.writeNewScaleType = writeNewScaleTypePingDocument;
			     // Function to write ping document with a new
			     // scale type (linear / logarithmic).
    this.plot		   = plotPingData;
			     // Function to plot the ping data.
}

/**
 * Constructor of the "ThroughputDocument" class.
 *
*****************************/
function ThroughputDocument ()
/****************************/
{
    this.write		   = writeThroughputDocument;
			     // Function to write new throughput document.
    this.writeNewHost	   = writeNewHostThroughputDocument;
			     // Function to write tput document with a new host.
    this.writeNewScaleType = writeNewScaleTypeThroughputDocument;
			     // Function to write throughput document with a new
			     // scale type (linear / logarithmic).
    this.plot		   = plotThroughputData;
			     // Function to plot the throughput data.
}

/**
 * Constructor of the "UDPBWDocument" class.
 *
************************/
function UDPBWDocument ()
/***********************/
{
    this.write		   = writeUDPBWDocument;
			     // Function to write new UDP bandwidth document.
    this.writeNewHost	   = writeNewHostUDPBWDocument;
			     // Function to write UDP bandwidth document with a
			     // new host.
    this.writeNewPingType  = writeNewUDPBWTypeDocument;
			     // Function to write documwent with a new UDP
			     // bandwidth type.
    this.writeNewScaleType = writeNewScaleTypeUDPBWDocument;
			     // Function to write UDP bandwidth document with a
			     // new scale type (linear / logarithmic).
    this.plot		   = plotUDPBWData;
			     // Function to plot the UDP bandwidth data.
}

/**
 * Write the HTML page into the data body frame which shows the load for
 * all hosts for a number of dates and times or week/day IDs.
 *
 * Plot also the load data when a plot window is shown and when plotting is
 * selected.
 *
 * See also "LoadTable.writeTable ()" ("writeLoadTable ()").
 *
 *    @param  date_ind    The index of the time for which the first load
 *			  data are displayed. The other dates are
 *			  displayed in reverse order. When the index is
 *			  less than zero the most recent date is
 *			  displayed.
 *
 *    @param  need_plot   If "true" plot the data when the data window is shown;
 *			  otherwise do not plot.
 *
***********************************************/
function writeLoadDocument (date_ind, need_plot)
/**********************************************/
{
    /*
     * Set the current_document to this class. Set also the cookie.
     */

    current_document = load_document;

    setDocumentTypeCookie ();

    /*
     * Create an instance of the "LoadTable" class.
     */

    var load_table = new LoadTable ();

    /*
     * Reopen the data body.
     */

    parent.NetData.document.open ();

    /*
     * Write the page header and a hyperlink to the "getting started"
     * introduction and to the user guide.
     */

    parent.NetData.document.write (
        "<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
        "\n",
        "<BODY>\n",
        "\n",
        "<H1>Load ", hosts_title, " Hosts</H1>\n",
        "\n",
        "<P>\n",
        "<UL>\n",
        "\n",
        "<LI>See the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/get_start.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Getting started introduction'; ",
		    "return true\"\n",
	"      >getting started</A> introduction or the\n",
        "    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/load.html')\"\n",
	"       onMouseOver=\"window.status='User guide load table'; ",
		    "return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
        "      >user guide</A> for a description of the table below.</LI>\n",
        "\n"
	);

    /*
     * Write a hyperlink to the host documentation when it is available and when
     * the data file resides from the same hosts set as this version. This may
     * differ in the imported version.
     */

    if (is_hosts_doc  &&  hosts_title == orig_hosts_title)
	parent.NetData.document.write (
	    "<LI>See also the\n",
	    "    <A HREF=",
		    "\"javascript:parent.Control.showHelp('hosts/index.html')",
		    "\"\n",
	    "       onMouseOver=\"window.status='Hosts documentation'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >hosts documentation</A>.</LI>\n",
	    "\n"
	    );

    parent.NetData.document.write (
        "</UL>\n",
        "</P>\n",
        "\n",
        "<P>\n",
        "<CENTER>\n"
        );

    /*
     * Write the load table. Close the document and stop when this is not
     * possibble.
     */

    if (!load_table.writeTable (date_ind))
    {
	parent.NetData.document.close ();
	return;
    }

    /*
     * Plot the load data when plotting is selected, starting at the same date
     * index as in the table, when a plot window is shown.
     */

    if (need_plot)
	plotLoadData (date_ind, false);

    /*
     * Write the table with the used color scaling.
     */

    parent.NetData.document.write (
	"</CENTER>\n",
	"</P>\n",
	"\n",
	"<P>\n",
	"De <EM>load</EM> data serie is scaled with the color distribution\n",
	"shown below:<BR>\n",
	"&nbsp;<BR>\n",
	"\n",
	"<CENTER>\n"
	);

    load_table.writeColorTable ();

    parent.NetData.document.write (
	"</CENTER>\n",
	"</P>\n",
	"\n",
        "<P>\n",
        "<CENTER>\n"
        );

    /*
     * Write the form which allows the user to change the # date rows in the
     * table.
     */

    writeSetNrDateRowsForm ();

    parent.NetData.document.write ("</CENTER>\n");

    /*
     * Write the hyperlink to the net data file when this version is not the
     * import version.
     */

    if (!import_data)
    {
	parent.NetData.document.write (
	    "</P>\n",
	    "\n",
	    "<P>\n"
	    );

	writeNetDataHyperLink ();
    }

    /*
     * Write the page trailor.
     */

    parent.NetData.document.write (
        "</P>\n",
        "\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);

    /*
     * Close the data body frame.
     */

    parent.NetData.document.close ();

} // function writeLoadDocument ()

/**
 * Plot the load data in a plot window. Store first the current sizes of the
 * plot window when the sizes are larger than zero.
 *
 *    @param  date_ind   The index of the most recent date where the plot is
 *			 started.
 *
 *    @param  force      True: also plot when the plot window is not yet shown.
 *			 False: only plot when the plot window is yet shown.
 *
**************************************/
function plotLoadData (date_ind, force)
/*************************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    setPlotSizeCookies ();

    if (force || applet.plotShowed () == "true")
	applet.plotLoadData (date_ind);
}

/**
 * Write the ping document with a new host for which all ping values are
 * displayed in the table.
 *
 *    @param  date_ind		The index of the most recent date shown in the
 *				table.
 *
 *    @param  index_ping_host   The index of the new host.
 *
************************************************************/
function writeNewHostPingDocument (date_ind, index_ping_host)
/***********************************************************/
{
    index_fixed_host = index_ping_host;

    setCookie (index_fixed_host_cookie, index_fixed_host);

    ping_document.write (date_ind, true);
}

/**
 * Write the ping or overview document with a new specfied ping type.
 *
 *    @param  date_ind	      The index of the most recent date shown in the
 *			      table.
 *
 *    @param  new_ping_type   The new ping type.
 *
**********************************************************/
function writeNewPingTypeDocument (date_ind, new_ping_type)
/*********************************************************/
{
    ping_type = new_ping_type;

    setCookie (ping_type_cookie, ping_type);

    current_document.write (date_ind, true);
}

/**
 * Write the ping document with the new, toggled scaling type.
 *
 *    @param  date_ind   The index of the most recent date shown in the table.
 *
************************************************/
function writeNewScaleTypePingDocument (date_ind)
/***********************************************/
{
    /*
     * Toggle the current scaling type.
     */

    if (ping_scale_type == LINEAR)
	ping_scale_type = LOG;
    else
	ping_scale_type = LINEAR;

    /*
     * Write the current document with the new scaling type.
     */

    current_document.write (date_ind, false);

} // writeNewScaleTypePingDocument ()

/**
 * Write the HTML page into the data body frame which shows the ping for
 * all hosts for a number of dates and times or week/day IDs.
 *
 * Plot also the ping data when a plot window is shown at this moment and when
 * plotting is selected.
 *
 * See also "PingTable.writeTable ()" ("writePingTable ()").
 *
 *    @param  date_ind    The index of the time for which the first ping
 *                        data are displayed. The other dates are
 *                        displayed in reverse order. When the index is
 *                        less than zero the most recent date is
 *                        displayed.
 *
 *    @param  need_plot   If "true" plot the data when the data window is
 *			  shown; otherwise do not plot.
 *
***********************************************/
function writePingDocument (date_ind, need_plot)
/**********************************************/
{
    /*
     * Set the current_document to this class. Set also the cookie.
     */

    current_document = ping_document;

    setDocumentTypeCookie ();

    /*
     * Create an instance of the "PingTable" class.
     */

     var ping_table = new PingTable ();

    /*
     * Reopen the data body.
     */

    parent.NetData.document.open ();

    /*
     * Write the page header and a hyperlink to the "getting started"
     * introduction and to the user guide.
     */
   
    parent.NetData.document.write (
        "<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
        "\n",
        "<BODY>\n",
        "\n",
        "<H1>Ping ", ping_type_titles [ping_type], " between ", hosts_title,
	     " Hosts</H1>\n",
        "\n",
        "<P>\n",
        "<UL>\n",
        "\n",
        "<LI>See the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/get_start.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Getting started introduction'; ",
		    "return true\"\n",
	"      >getting started</A> introduction or the\n",
        "    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/ping.html')\"\n",
	"       onMouseOver=\"window.status='User guide ping table'; ",
		    "return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
        "      >user guide</A> for a description of the table below.</LI>\n",
        "\n"
	);

    /*
     * Write a hyperlink to the host documentation when it is available and when
     * the data file resides from the same hosts set as this version. This may
     * differ in the imported version.
     */

    if (is_hosts_doc  &&  hosts_title == orig_hosts_title)
	parent.NetData.document.write (
	    "<LI>See also the\n",
	    "    <A HREF=",
		    "\"javascript:parent.Control.showHelp('hosts/index.html')",
		    "\"\n",
	    "       onMouseOver=\"window.status='Hosts documentation'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >hosts documentation</A>.</LI>\n",
	    "\n"
	    );
   
    parent.NetData.document.write (
        "</UL>\n",
        "</P>\n",
        "\n",
	"<H4>\n"
        );

    /*
     * Write the ping type hyperlinks. They can be used to adjust the ping type.
     */

    writePingTypeLinks (date_ind);

    /*
     * Write the scale type hyperlink. It can be used to toggle the scaling.
     */

    writeScaleTypeLink (date_ind, ping_scale_type, "ping");

    parent.NetData.document.write (
	"</H4>\n",
	"<P>\n",
	"<CENTER>\n"
	);

    /*
     * Write the ping table. Close the document and stop when this is not
     * possible.
     */

    if (!ping_table.writeTable (date_ind))
    {
	parent.NetData.document.close ();
	return;
    }
 
    /*
     * Plot the ping data when plotting is selected, starting at the specified
     * date index, when the plot is shown yet.
     */

    if (need_plot)
	plotPingData (date_ind, false);

    /*
     * Write the table with the used color scaling.
     */

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
        "\n",
        "<P>\n",
        "The <EM>ping</EM> data serie is scaled with the color distribution\n",
	"shown below:<BR>\n",
        "&nbsp;<BR>\n",
        "\n",

        "<CENTER>\n"
        );

    ping_table.writeColorTable ();

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
	"\n",
	"<P>\n",
	"<CENTER>\n"
	);

    /*
     * Write the form which allows the user to adjust the # date rows in the
     * table form.
     */

    writeSetNrDateRowsForm ();

    parent.NetData.document.write ("</CENTER>\n");

    /*
     * Write the hyperlink to the net data file when this version is not the
     * import version.
     */

    if (!import_data)
    {
	parent.NetData.document.write (
	    "</P>\n",
	    "\n",
	    "<P>\n"
	    );

	writeNetDataHyperLink ();
    }

    /*
     * Write the page trailor.
     */

    parent.NetData.document.write (
        "</P>\n",
        "\n",
        "</BODY>\n",
        "\n",
        "</HTML>\n"
        );

    /*
     * Close the data body frame.
     */

    parent.NetData.document.close ();

} // function writePingDocument ()

/**
 * Plot the ping data in the plot window. Store first the current sizes of the
 * plot window when the sizes are larger than zero.
 *
 *    @param  date_ind   The index of the most recent date where the plot is
 *			 started.
 *
 *    @param  force	 True: plot always; false: plot only when the plot
 *			 window is already shown.
 *
**************************************/
function plotPingData (date_ind, force)
/*************************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    setPlotSizeCookies ();

    if (force  ||  applet.plotShowed () == "true")
	applet.plotPingData (date_ind, index_fixed_host, ping_type);
}

/**
 * Write the hyperlinks to the various ping types in the document. After a
 * click at the specified type, the current document is rewritten with the new
 * type. To be able to do this the current_document must implement
 * "writeNewPingType()".
 *
 * The current ping type is not hyperlinked, however it is written in the
 * emphasizes style.
 *
 *    @param  date_ind   The index of the date for which the first data are
 *			 displayed.
 *
*************************************/
function writePingTypeLinks (date_ind)
/************************************/
{
    /*
     * Write the header label before the hyperlinks.
     */

    parent.NetData.document.write ("Select ping value: ");

    /*
     * Write the ping type hyperlinks.
     */

    var max_ping_type = nr_ping_types - 1;
    var i;
    var separ;

    for (i = 0;  i <= max_ping_type;  i++)    // Do for all ping types.
    {
	separ = (i == max_ping_type ? "." : ",");    // Separator or close char.

	if (i == ping_type)    // Write the current ping type emphasized.
	{
	    parent.NetData.document.write (
		"<EM>", ping_type_labels [i], "</EM>", separ, "\n"
		);
	}
	else    // Write a new ping type as a hyperlink.
	{
	    parent.NetData.document.write (
		"<A HREF=",
                    "\"javascript:",
			  "parent.Control.ping_document.writeNewPingType(",
				  date_ind, ",", i, ")\"\n",
		"   onMouseOver=\"window.status='Ping type: ",
			ping_type_labels [i], "'; return true\"\n",
		"   onMouseOut=\"window.status=''; return true\"\n",
		"  >", ping_type_labels [i], "</A>",
		separ, "\n"
	        );

	} // A new ping type is written.

    } // For all ping types.

} // function writePingTypeLinks ()

/**
 * Write the throughput document with a new host for which all throughput data
 * are displayed in the table.
 *
 *    @param  date_ind		      The index of the most recent date,
 *				      displayed in the table.
 *
 *    @param  index_throughput_host   The index of the new host.
 *
************************************************************************/
function writeNewHostThroughputDocument (date_ind, index_throughput_host)
/***********************************************************************/
{
    index_fixed_host = index_throughput_host;

    setCookie (index_fixed_host_cookie, index_fixed_host);

    throughput_document.write (date_ind, true);
}

/**
 * Write the throughput document with the new, toggled scaling type.
 *
 *    @param  date_ind   The index of the most recent date shown in the table.
 *
******************************************************/
function writeNewScaleTypeThroughputDocument (date_ind)
/*****************************************************/
{
    /*
     * Toggle the current scaling type.
     */

    if (throughput_scale_type == LINEAR)
	throughput_scale_type = LOG;
    else
	throughput_scale_type = LINEAR;

    /*
     * Write the current document with the new scaling type.
     */

    current_document.write (date_ind, false);

} // writeNewScaleTypeThroughputDocument ()

/**
 * Write the HTML page into the data body frame which shows the throughput for
 * all hosts for a number of dates and times or week/day IDs.
 *
 * Plot also the troughput data when a plot window is already shown and when
 * plotting is selected.
 *
 * See also "ThroughputTable.writeTable ()" ("writeThroughputTable ()").
 *
 *    @param  date_ind    The index of the time for which the first throughput
 *                        data are displayed. The other dates are
 *                        displayed in reverse order. When the index is
 *                        less than zero the most recent date is
 *                        displayed.
 *
 *    @param  need_plot   If "true" plot the data when the data window is
 *			  shown; otherwise do not plot.
 *
*****************************************************/
function writeThroughputDocument (date_ind, need_plot)
/****************************************************/
{
    /*
     * Set the current_document to this class. Set also the document cookie
     */

    current_document = throughput_document;

    setDocumentTypeCookie ();

    /*
     * Create an instance of the "ThroughputTable" class.
     */

    var throughput_table = new ThroughputTable ();

    /*
     * Reopen the data body.
     */

    parent.NetData.document.open ();

    /*
     * Write the page header and a hyperlink to the "getting started"
     * introduction and to the user guide.
     */

    parent.NetData.document.write (
        "<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
        "\n",
        "<BODY>\n",
        "\n",
        "<H1>Throughput between ", hosts_title, " Hosts</H1>\n",
        "\n",
        "<P>\n",
        "<UL>\n",
        "\n",
        "<LI>See the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/get_start.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Getting started introduction'; ",
		    "return true\"\n",
	"      >getting started</A> introduction or the\n",
        "    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/throughput.html')",
		"\"\n",
	"       onMouseOver=\"window.status='User guide throughput table'; ",
		"return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
        "      >user guide</A> for a description of the table below.</LI>\n",
        "\n"
	);

    /*
     * Write a hyperlink to the host documentation when it is available and when
     * the data file resides from the same hosts set as this version. This may
     * differ in the imported version.
     */

    if (is_hosts_doc  &&  hosts_title == orig_hosts_title)
	parent.NetData.document.write (
	    "<LI>See also the\n",
	    "    <A HREF=",
		    "\"javascript:parent.Control.showHelp('hosts/index.html')",
		    "\"\n",
	    "       onMouseOver=\"window.status='Hosts documentation'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >hosts documentation</A>.</LI>\n",
	    "\n"
	    );

    parent.NetData.document.write (
        "</UL>\n",
        "</P>\n",
        "\n",
	"<H4>\n"
	);

    /*
     * Write the scale type hyperlink. It can be used to toggle the scaling.
     */

    writeScaleTypeLink (date_ind, throughput_scale_type, "throughput");

    parent.NetData.document.write (
	"</H4>\n",
        "<P>\n",
        "<CENTER>\n"
        );

    /*
     * Write the throughput table. Close the document and stop when this is not
     * possible.
     */

     if (!throughput_table.writeTable (date_ind))
     {
	parent.NetData.document.close ();
	return;
    }

    /*
     * Plot the throughput data when plotting is selected, starting at the same
     * most recent date index as in the table. Plot the data only when a plot
     * window is already shown.
     */

    if (need_plot)
	plotThroughputData (date_ind, false);

    /*
     * Write the table with the used color scaling.
     */

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
        "\n",
        "<P>\n",
        "The <EM>throughput</EM> data serie is scaled with the color\n",
	"distribution shown below:<BR>\n",
        "&nbsp;<BR>\n",
        "\n",

        "<CENTER>\n"
        );

    throughput_table.writeColorTable ();

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
        "\n",
        "<P>\n",
        "<CENTER>\n"
        );

    /*
     * Write the form which allows the user to change the # date rows in the
     * table.
     */

    writeSetNrDateRowsForm ();

    parent.NetData.document.write ("</CENTER>\n");

    /*
     * Write the hyperlink to the net data file when this version is not the
     * import version.
     */

    if (!import_data)
    {
	parent.NetData.document.write (
	    "</P>\n",
	    "\n",
	    "<P>\n"
	    );

	writeNetDataHyperLink ();
    }

    /*
     * Write the page trailor.
     */

    parent.NetData.document.write (
        "</P>\n",
        "\n",
        "</BODY>\n",
        "\n",
        "</HTML>\n"
        );

    /*
     * Close the data body frame.
     */

    parent.NetData.document.close ();

} // function writeThroughputDocument ()

/**
 * Plot the throughput data starting at the specified date index. Store first
 * the current sizes of the plot window when the sizes are larger than zero.
 *
 *    @param  date_ind   Index of the most recent date where the plot is
 *			 started.
 *
 *    @oaram  force	 True: always plot; false: only plot when the plot
 *			 window is already shown.
 *
********************************************/
function plotThroughputData (date_ind, force)
/*******************************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    setPlotSizeCookies ();

    if (force  ||  applet.plotShowed () == "true")
	applet.plotThroughputData (date_ind, index_fixed_host);
}

/**
 * Write the UDP bandwidth document with a new host for which all UDP bandwidth
 * values are displayed in the table.
 *
 *    @param  date_ind		  The index of the most recent date shown in the
 *				  table.
 *
 *    @param  index_udp_bw_host   The index of the new host.
 *
***************************************************************/
function writeNewHostUDPBWDocument (date_ind, index_udp_bw_host)
/**************************************************************/
{
    index_fixed_host = index_udp_bw_host;

    setCookie (index_fixed_host_cookie, index_fixed_host);

    udp_bw_document.write (date_ind, true);
}

/**
 * Write the UDP bandwidth or overview document with a new specfied UDP
 * bandwidth type.
 *
 *    @param  date_ind		The index of the most recent date shown in the
 *				table.
 *
 *    @param  new_udp_bw_type   The new UDP bandwidth type.
 *
*************************************************************/
function writeNewUDPBWTypeDocument (date_ind, new_udp_bw_type)
/************************************************************/
{
    udp_bw_type = new_udp_bw_type;

    setCookie (udp_bw_type_cookie, udp_bw_type);

    current_document.write (date_ind, true);
}

/**
 * Write the UDP bandwidth document with the new, toggled scaling type.
 *
 *    @param  date_ind   The index of the most recent date shown in the table.
 *
*************************************************/
function writeNewScaleTypeUDPBWDocument (date_ind)
/************************************************/
{
    /*
     * Toggle the current scaling type.
     */

    if (udp_bw_scale_type == LINEAR)
	udp_bw_scale_type = LOG;
    else
	udp_bw_scale_type = LINEAR;

    /*
     * Write the current document with the new scaling type.
     */

    current_document.write (date_ind, false);

} // writeNewScaleTypeUDPBWDocument ()

/**
 * Write the HTML page into the data body frame which shows the UDP bandwidth
 * for * all hosts for a number of dates and times or week/day IDs.
 *
 * Plot also the UDP bandwidth data when a plot window is shown at this moment
 * and when plotting is selected.
 *
 * See also "UDPBWTable.writeTable ()" ("writeUDPBWTable ()").
 *
 *    @param  date_ind    The index of the time for which the first UDP
 *			  bandwidth data are displayed. The other dates are
 *                        displayed in reverse order. When the index is less
 *			  than zero the most recent date is displayed.
 *
 *    @param  need_plot   If "true" plot the data when the data window is shown;
 *			  otherwise do not plot.
 *
************************************************/
function writeUDPBWDocument (date_ind, need_plot)
/***********************************************/
{
    /*
     * Set the current_document to this class. Set also the cookie.
     */

    current_document = udp_bw_document;

    setDocumentTypeCookie ();

    /*
     * Create an instance of the "UDPBWTable" class.
     */

     var udp_bw_table = new UDPBWTable ();

    /*
     * Reopen the data body.
     */

    parent.NetData.document.open ();

    /*
     * Write the page header and a hyperlink to the "getting started"
     * introduction and to the user guide.
     */
   
    parent.NetData.document.write (
        "<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
        "\n",
        "<BODY>\n",
        "\n",
        "<H1>UDP ", udp_bw_type_titles [udp_bw_type], " between ", hosts_title,
	     " Hosts</H1>\n",
        "\n",
        "<P>\n",
        "<UL>\n",
        "\n",
        "<LI>See the\n",
	"    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/get_start.html')\"",
		"\n",
	"       onMouseOver=\"window.status='Getting started introduction'; ",
		    "return true\"\n",
	"      >getting started</A> introduction or the\n",
        "    <A HREF=",
		"\"javascript:parent.Control.showHelp('help/udp_bw.html')\"\n",
	"       onMouseOver=\"window.status='User guide UDP table'; ",
		    "return true\"\n",
	"       onMouseOut=\"window.status=''; return true\"\n",
        "      >user guide</A> for a description of the table below.</LI>\n",
        "\n"
	);

    /*
     * Write a hyperlink to the host documentation when it is available and when
     * the data file resides from the same hosts set as this version. This may
     * differ in the imported version.
     */

    if (is_hosts_doc  &&  hosts_title == orig_hosts_title)
	parent.NetData.document.write (
	    "<LI>See also the\n",
	    "    <A HREF=",
		    "\"javascript:parent.Control.showHelp('hosts/index.html')",
		    "\"\n",
	    "       onMouseOver=\"window.status='Hosts documentation'; ",
			"return true\"\n",
	    "       onMouseOut=\"window.status=''; return true\"\n",
	    "      >hosts documentation</A>.</LI>\n",
	    "\n"
	    );
   
    parent.NetData.document.write (
        "</UL>\n",
        "</P>\n",
        "\n",
	"<H4>\n"
        );

    /*
     * Write the UDP bandwidth type hyperlinks. They can be used to adjust the
     * UDP bandwidth type.
     */

    writeUDPBWTypeLinks (date_ind);

    /*
     * Write the scale type hyperlink. It can be used to toggle the scaling.
     */

    writeScaleTypeLink (date_ind, udp_bw_scale_type, "udp_bw");

    parent.NetData.document.write (
	"</H4>\n",
	"<P>\n",
	"<CENTER>\n"
	);

    /*
     * Write the UDP bandwidth table. Close the document and stop when this is
     * not possible.
     */

    if (!udp_bw_table.writeTable (date_ind))
    {
	parent.NetData.document.close ();
	return;
    }
 
    /*
     * Plot the UDP bandwidth data when plotting is selected, starting at the
     * specified date index, when the plot is shown yet.
     */

    if (need_plot)
	plotUDPBWData (date_ind, false);

    /*
     * Write the table with the used color scaling.
     */

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
        "\n",
        "<P>\n",
        "The <EM>UDP</EM> data serie is scaled with the color distribution\n",
	"shown below:<BR>\n",
        "&nbsp;<BR>\n",
        "\n",
        "<CENTER>\n"
        );

    udp_bw_table.writeColorTable ();

    parent.NetData.document.write (
        "</CENTER>\n",
        "</P>\n",
	"\n",
	"<P>\n",
	"<CENTER>\n"
	);

    /*
     * Write the form which allows the user to adjust the # date rows in the
     * table form.
     */

    writeSetNrDateRowsForm ();

    parent.NetData.document.write ("</CENTER>\n");

    /*
     * Write the hyperlink to the net data file when this version is not the
     * import version.
     */

    if (!import_data)
    {
	parent.NetData.document.write (
	    "</P>\n",
	    "\n",
	    "<P>\n"
	    );

	writeNetDataHyperLink ();
    }

    /*
     * Write the page trailor.
     */

    parent.NetData.document.write (
        "</P>\n",
        "\n",
        "</BODY>\n",
        "\n",
        "</HTML>\n"
        );

    /*
     * Close the data body frame.
     */

    parent.NetData.document.close ();

} // function writePingDocument ()

/**
 * Plot the UDP bandwidth data in the plot window. Store first the current sizes
 * of the plot window when the sizes are larger than zero.
 *
 *    @param  date_ind   The index of the most recent date where the plot is
 *			 started.
 *
 *    @param  force	 True: plot always; false: plot only when the plot
 *			 window is already shown.
 *
***************************************/
function plotUDPBWData (date_ind, force)
/**************************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    setPlotSizeCookies ();

    if (force  ||  applet.plotShowed () == "true")
	applet.plotUDPBWData (date_ind, index_fixed_host, udp_bw_type);
}

/**
 * Write the hyperlinks to the various UDP bandwidth types in the document.
 * After a click at the specified type, the current document is rewritten with
 * the new type. To be able to do this the current_document must implement
 * "writeNewPingType()".
 *
 * The current UDP bandwidth type is not hyperlinked, however it is written in
 * the emphasizes style.
 *
 *    @param  date_ind   The index of the date for which the first data are
 *			 displayed.
 *
**************************************/
function writeUDPBWTypeLinks (date_ind)
/*************************************/
{
    /*
     * Write the header label before the hyperlinks.
     */

    parent.NetData.document.write ("Select UDP value: ");

    /*
     * Write the UDP bandwidth type hyperlinks.
     */

    var max_udp_bw_type = nr_udp_bw_types - 1;
    var i;
    var separ;

    for (i = 0;  i <= max_udp_bw_type;  i++)    // Do for all UDP BW types.
    {
	separ = (i == max_udp_bw_type ? "." : ",");  // Separator or close char.

	if (i == udp_bw_type)    // Write the current UDP BW type emphasized.
	{
	    parent.NetData.document.write (
		"<EM>", udp_bw_type_labels [i], "</EM>", separ, "\n"
		);
	}
	else    // Write a new UDP bandwidth type as a hyperlink.
	{
	    parent.NetData.document.write (
		"<A HREF=",
                    "\"javascript:",
			  "parent.Control.udp_bw_document.writeNewPingType(",
				  date_ind, ",", i, ")\"\n",
		"   onMouseOver=\"window.status='UDP type: ",
			udp_bw_type_labels [i], "'; return true\"\n",
		"   onMouseOut=\"window.status=''; return true\"\n",
		"  >", udp_bw_type_labels [i], "</A>",
		separ, "\n"
	        );

	} // A new UDP bandwidth type is written.

    } // For all UDP bandwidth types.

} // function writeUDPBWTypeLinks ()

/**
 * Set the size of the new plot size from the plot size cookies, when the sizes
 * are larger than zero.
 *
*************************/
function setNewPlotSize ()
/************************/
{
    var applet = document.applets ["NetData"];

    if (plot_width > 0  &&  plot_height > 0)
    {
	applet.setNewPlotSize (plot_width, plot_height);
    }
}

/**
 * Set the plot size cookies, but only when the plot window is opan and when
 * both sizes, obtained from the plot window are larger than zero.
 *
*****************************/
function setPlotSizeCookies ()
/****************************/
{
    var applet = document.applets ["NetData"];
 
    if (applet.plotShowed () == "true")    // Plot is showed.
    {
	var new_plot_width  = parseInt (applet.getPlotWidth  ());
	var new_plot_height = parseInt (applet.getPlotHeight ());

	if (new_plot_width > 0  &&  new_plot_height > 0)
	{
	    plot_width  = new_plot_width;
	    plot_height = new_plot_height;
	
	    setCookie (plot_width_cookie,  plot_width);
	    setCookie (plot_height_cookie, plot_height);
	}
    }

} // setPlotSizeCookies()

/**
 * Write the hyperlink to toggle between linear and logarithmic scaling. After a
 * click at the link, the current document is rewritten with the toggled scale
 * type. To be able to do this the current_document must implement
 * "writeNewScaleType()"
 *
 *    @param  date_ind     The index of the date for which the first data are
 *			   displayed.
 *
 *    @param  scale_type   The current value of the scale type.
 *
 *    @param  data_name    Name of the data type to which the link refers.
 *
************************************************************/
function writeScaleTypeLink (date_ind, scale_type, data_name)
/***********************************************************/
{
    /*
     * Write the scale type hyperlink / emphasized scale type.
     */

    parent.NetData.document.write ("Set color scale:\n");

    /*
     * Write the linear, emphasized scale or link to this scale.
     */

    writeScaleTypeItem (LINEAR, scale_type, data_name, "linear", ",", date_ind);

    /*
     * Write the logarithmic, emphasized scale or link to this scale.
     */

     writeScaleTypeItem (LOG, scale_type, data_name, "log", ".", date_ind);

} // writeScaleTypeLink ()

/**
 * Write a scale type emphasize text or link. See also the function
 * "writeScaleTypeLink()".
 *
 *    @param  link_scale   The scale to the link / emphasized text.
 *
 *    @param  scale_type   The current value of the scale type.
 *
 *    @param  data_name    The name of the data to be referred in the hyperlink.
 *
 *    @param  scale_text   The text of the scale.
 *
 *    @param  separ        The separator to be placed after the link /
 *			   emphasized text.
 *
 *    @param  date_ind     The index of the date for which the first data are
 *			   displayed.
 *
**************************************************************/
function writeScaleTypeItem (link_scale, scale_type, data_name,
			     scale_text, separ, date_ind)
/*************************************************************/
{
    if (link_scale == scale_type)    // Write the current scale emphasized.
    {
	parent.NetData.document.write (
	    "<EM>", scale_text, "</EM>", separ, "\n");
    }
    else    // Write the new scale as a hyperlink.
    {
	parent.NetData.document.write (
	    "<A HREF=",
		"\"javascript:parent.Control.", data_name,
		      "_document.writeNewScaleType(", date_ind, ")\"\n",
	    "   onMouseOver=\"window.status='Color scale: ", scale_text,
		    "'; return true\"\n",
	    "   onMouseOut=\"window.status=''; return true\"\n",
	    "  >", scale_text, "</A>", separ, "\n"
	    );
    }

} // writeScaleTypeItem ()

/**
 * Write the form which allows the user to change the # date rows in the current
 * document.
 *
 * The current document is rewritten:
 *
 *    1. After a click at the button, entitled "Set".
 *
 *    2. After a carriage return at the input window. If a form contains only
 *	 one text input field this generates a form ACTION event (?!) at
 *	 Netscape 4 and Internet Explorer 4. Internet Explorer hangs when also
 *	 another event is used at the text input field. Therefor the ACTION
 *	 event of the form is also implemented.
 *
 * After both events the current document is rewritten with the newly specified
 * # date rows, specified in the text input field.
 *
 * See also "writeNewNrRowsDoc()".
 *
*********************************/
function writeSetNrDateRowsForm ()
/********************************/
{
    parent.NetData.document.write (
	"<FORM NAME=\"NrRowsForm\"\n",
	"      METHOD=\"post\"\n",
	"      ACTION=\"javascript:parent.Control.writeNewNrRowsDoc()\">\n",
	"\n",
	"<TABLE BORDER=0\n",
	"       CELLSPACING=5\n",
	"       CELLPADDING=0>\n",
	"\n",
	"<TR ALIGN=\"left\" VALIGN=\"middle\">\n",
	"\n",
	"<TD>Set the #&nbsp;table rows:</TD>\n",
	"\n",
	"<TD>&nbsp;</TD>\n",
	"\n",
	"<TD><INPUT NAME=\"NrRowsInput\"\n",
	"           VALUE=", max_nr_date_rows, "\n",
	"           onMouseOver=\"window.status='Set # table rows'; ",
			"return true\"\n",
	"           onMouseOut=\"window.status=''; return true\"\n",
	"           SIZE=\"6\"></TD>\n",
	"\n",
	"<TD><INPUT TYPE=\"button\"\n",
	"           NAME=\"NrRowsButton\"\n",
	"           VALUE=\"Set\"\n",
	"           onMouseOver=\"window.status='Set # table rows'; ",
			"return true\"\n",
	"           onMouseOut=\"window.status=''; return true\"\n",
	"           onClick=\"parent.Control.writeNewNrRowsDoc()\"></TD>\n",
	"\n",
	"</TR>\n",
	"\n",
	"</TABLE>\n",
	"\n",
	"</FORM>\n"
	);

} // function writeSetNrDateRowsForm ()

/**
 * Rewrite the current document with a changed # table date rows which are
 * specified in the # rows text input field. First the values of the scroll
 * select options are also adjusted.
 *
 * See also "writeSetNrDateRowsForm()".
 *
****************************/
function writeNewNrRowsDoc ()
/***************************/
{
    max_nr_date_rows = parent.NetData.document.NrRowsForm.NrRowsInput.value;

    setCookie (max_nr_date_rows_cookie, max_nr_date_rows);

    adjustScrollSelectValues ();

    current_document.write (start_date_ind, false);
}

/**
 * Write the hyperlink to the net data file.
 *
********************************/
function writeNetDataHyperLink ()
/*******************************/
{
    parent.NetData.document.write (
	"<UL>\n",
	"\n",
	"<LI>Download the raw, zipped\n",
	"    <A HREF=\"", data_file, "\"",
	      ">data file</A>. Download this\n",
	"    <A HREF=\"../download/", package_base.toLowerCase (),
	"_import.zip\">version</A>\n",
	"    of the package to view it locally.</LI>\n",
	"\n",
	"</UL>\n"
	);
}

/**
 * Define a function which returns if there are skipped connection. It will be
 * called from the help window.
 *
 *    @return   True when there are skipped connections; false otherwise.
 *
********************************/
function hasSkippedConnections ()
/*******************************/
{
    var applet	= document.applets ["NetData"];

    if (applet != null)  // The Applet exists: ask it if there are skipped conn.
    {
	// applet.start ();

	return applet.hasSkippedConnections ();
    }
    else    // The Applet does not exist: default to false.
    {
	return false;
    }
}

/**
 * Define a function which shows the specified HTML help page is a new
 * window named "HelpWindow". The window is resized when it was not yet
 * open. When known the sizes of a previously open help window are used.
 *
 * To be able to do this the resize Handler function is set, but only when the
 * Event Handler object is defined (not in IE).
 *
 * Set the help window and the resize handler with the "setHelpWindow()" method.
 * This allows this file to be updated when new URL's are loaded into the help
 * window. In this way the resize events can also be handled by this method.
 *
 * The help window is set by most help windows, but not by all. For instance it
 * may not be set in user defined "hosts docuentation" windows. Therefore it is
 * required to set it also here.
 *
 *    @param  help_html   The HTML page to be displayed.
 *
****************************/
function showHelp (help_html)
/***************************/
{
    setHelpWindow (showWindow (help_html,
			       "HelpWindow",
			       help_window,
			       help_width,
			       help_height,
			       530,
			       470));
}

/**
 * This function sets the help window to the new specified. one. This allows
 * this file to handle the resize events of new help windows which are formed
 * when a new URL is loaded into the help window. At that situation this method
 * should be called immediately in the header. Otherwise it does not have any
 * effect.
 *
 *    @param  new_help_window   The new help window to set.
 *
***************************************/
function setHelpWindow (new_help_window)
/**************************************/
{
    help_window = new_help_window;

    if (this.Event != null  &&  Event.RESIZE != null)
    {
	help_window.captureEvents (Event.RESIZE);
	help_window.onResize = handleHelpWindowResize;
    }
}

/**
 * Handle a resize of the Help window. When available the (inner) sizes of the
 * help window are stored to be used for a reopen. The corresponding cookies are
 * also stored when both sizes are larger than zero.
 *
*********************************/
function handleHelpWindowResize ()
/********************************/
{
    if (help_window != null)
    {
	if (help_window.innerWidth != null)
	    help_width = help_window.innerWidth;

	if (help_window.innerHeight != null)
	    help_height = help_window.innerHeight;

	if (help_width > 0  &&  help_height > 0)
	{
	    setCookie (help_width_cookie,  help_width);
	    setCookie (help_height_cookie, help_height);
	}
    }
}

/**
 * Close the help window when it was open.
 *
**************************/
function closeHelpWindow ()
/*************************/
{
    if (help_window != null  &&  !help_window.closed)
	help_window.close ();
}

/**
 * Define a function to get the data type index to be used in the selector
 * window. It will be used by this window. It is the index of the data type
 * going to load.
 *
 *    @return  The select data type index.
 *
*********************************/
function getSelectDataTypeIndex ()
/********************************/
{
    return new_data_type_index;
}

/**
 * Define a function to get the select title of the data type being selected. It
 * will be used by the selector window.
 *
 *    @return  The title of the selected data type.
 *
*************************/
function getSelectTitle ()
/************************/
{
    return net_data_descrs [new_data_type_index].select_title;
}

/**
 * Define a function which opens a browser window with a file selector to select
 * a data file to import. Thw window is resized when it was not yet open. When
 * known the sizes of a previously known selector window are used.
 *
 * To be able to do this the resize Handler function is set, but only when the
 * Event Handler object is defined (not in IE).
 *
*******************************/
function showImportDataWindow ()
/******************************/
{
    import_data_window = showWindow ("import_data.html",
				     "ImportDataWindow",
				     import_data_window,
				     import_data_width,
				     import_data_height,
				     500,
				     170);

    if (this.Event != null  &&  Event.RESIZE != null)
    {
	import_data_window.captureEvents (Event.RESIZE);
	import_data_window.onResize = handleImportWindowResize;
    }
}

/**
 * Handle a resize of the import data window. Whe available the (inner) sizes of
 * the import data window are stored to be used for a reopen. The corresponding
 * cookies are also stored when both sizes are larger than zero.
 *
***********************************/
function handleImportWindowResize ()
/**********************************/
{
    if (import_data_window != null)
    {
	if (import_data_window.innerWidth != null)
	    import_data_width = import_data_window.innerWidth;

	if (import_data_window.innerHeight != null)
	    import_data_height = import_data_window.innerHeight;

	if (import_data_width > 0  &&  import_data_height > 0)
	{
	    setCookie (import_data_width_cookie,  import_data_width);
	    setCookie (import_data_height_cookie, import_data_height);
	}
    }
}

/**
 * Close the import data window when it was open.
 *
********************************/
function closeImportDataWindow ()
/*******************************/
{
    if (import_data_window != null  &&  !import_data_window.closed)
	import_data_window.close ();
}

/**
 * Define a function which opens a browser window with a selector to select a
 * date in a new window named "SelectDateWindow". The window is resized when it
 * was not yet open. When known the sizes of a previously known selector window
 * are used.
 *
 * To be able to do this the resize Handler function is set, but only when the
 * Event Handler object is defined (not in IE).
 *
*******************************/
function showSelectDateWindow ()
/******************************/
{
    select_date_window = showWindow ("select_date.html",
				     "SelectDateWindow",
				     select_date_window,
				     select_date_width,
				     select_date_height,
				     200,
				     300);

    if (this.Event != null  &&  Event.RESIZE != null)
    {
	select_date_window.captureEvents (Event.RESIZE);
	select_date_window.onResize = handleSelectDateWindowResize;
    }
}

/**
 * Handle a resize of the select date window. When a available the (inner) sizes
 * of the date selector window are stored to be used for a reopen. The
 * corresponding cookies are also set when both sizes are larger than zero.
 *
***************************************/
function handleSelectDateWindowResize ()
/**************************************/
{
    if (select_date_window != null)
    {
	if (select_date_window.innerWidth != null)
	    select_date_width = select_date_window.innerWidth;

	if (select_date_window.innerHeight != null)
	    select_date_height = select_date_window.innerHeight;

	if (select_date_width > 0  &&  select_date_height > 0)
	{
	    setCookie (select_date_width_cookie,  select_date_width);
	    setCookie (select_date_height_cookie, select_date_height);
	}
    }
}

/**
 * Close the select date window when it was open.
 *
********************************/
function closeSelectDateWindow ()
/*******************************/
{
    if (select_date_window != null  &&  !select_date_window.closed)
	select_date_window.close ();
}

/**
 * Define a window which opens a browser window. When the window was not yet
 * open it is resized to the initial sizes. When it was previously open, it is
 * resized to these sizes if known.
 *
 *    @param  url	    The URL of the window to open.
 *
 *    @param  name	    The name of the window.
 *
 *    @param  curr_window   The current window.
 *
 *    @param  prev_width    The previous width of the window.
 *
 *    @param  prev_height   The previous height of the window.
 *
 *    @param  init_width    The inital width of the window.
 *
 *    @param  init_heigh    The initial height of the window.
 *
 *    @return  The new window descriptor.
 *
********************************************************************/
function showWindow (url, name, curr_window, prev_width, prev_height,
		     init_width, init_height)
/*******************************************************************/
{
    var dim_str;

    /*
     * Resize the window when it was not yet open or closed.
     */

    if (curr_window == null  ||  curr_window.closed)
    {
	/*
	 * Use the window dimension from a previously open window when they are
	 * known; use the initial sizes otherwise.
	 */

	if (prev_width > 0  &&  prev_height > 0)
	{
	    /*
	     * Set the dimension string, responsible for the resizing of the
	     * window when it is opened. The previous sizes are used.
	     */

	    dim_str = "width=" + prev_width + ",height=" + prev_height + ",";
	}
	else    // Use the initial sizes.
	{
	    /*
	     * Resize this window no more than 70 % of the screen dimensions.
	     * Note that not all browsers have defined the screen dimensions
	     * (not in Netscape 3), so we check below if they are defined. If
	     * not, the code below is skipped.
	     */

	    if ( this.screen   != null  &&
		 screen.width  != null  &&
		 screen.height != null )
	    {
		var win_part = 70; // %

		/*
		 * Set the maximum window sizes.
		 */

		var max_width  = (win_part * screen.width ) / 100;
		var max_height = (win_part * screen.height) / 100;

		if (init_width > max_width)      // Correct the window width.
		    init_width = max_width;

		if (init_height > max_height)    // Correct the window height.
		    init_height = max_height;
	    }

	    /*
	     * Set the dimension string, responsible for the resizing of the
	     * window when it is opened. The initial sizes are used.
	     */

	    dim_str = "width=" + init_width + ",height=" + init_height + ",";

	} // Use the initial sizes.
    }
    else    // The help window is already open: no resize string.
    {
	dim_str = "";
    }

    /*
     * Open the window and return it without the usual browser menus and
     * buttons, but allow resizing and scrollbars. The window size is set when
     * required.
     */

    return window.open (url,
			name,
			dim_str +
			"resizable=yes," +
			"scrollbars=yes," +
			"toolbar=no");

} // function showWindow ()

/**
 * Load the date interval specified in the date select window.
 *
*****************************************/
function loadSelectDataFromSelectWindow ()
/****************************************/
{
    /*
     * Get the new data type index and the date ID options from the date ID
     * select window.
     */

    var options	= select_date_window.document.Control.SelectDate.options;

    /*
     * Do nothing when the title header is selected or when the date index is
     * deselected (value -1) in the place of selected.
     */

    if (options.selectedIndex <= 0)
	return;

    /*
     * Store the selected index in a global varriable to be used at a reopening
     * of the select date windowi of the new selected data.
     */

    select_data_index = options.selectedIndex;

    /*
     * Set the selected date ID and the corresponding data file name.
     */

    var select_id = options [options.selectedIndex].value;

    /*
     * Set the new net data type index and the accelerator.
     */

    data_type_index = new_data_type_index;
    current_data    = net_data_descrs [data_type_index];

    /*
     * Construct the data file from the file base of for these type of data
     * files and from the selected date (or file) ID.
     */

    data_file = current_data.file_or_base + select_id + ".zip";

    /*
     * Load the date from the selected date interval.
     */

    loadNetDataType ();

} // function loadSelectDataFromSelectWindow ()

/**
 * Return the selected index of the select date window. The value of -1 is
 * returned when there is no selector window available or when the data type to
 * load differs from the current data type. This function is intended for a
 * reopening of the selector window.
 *
 *    @return  The selected index of the select window
 *
*************************/
function getSelectIndex ()
/************************/
{
    if (new_data_type_index == data_type_index)
	return select_data_index;
    else
	return -1;
}

/**
 * Load the document (and plot) with the net overview data at the current date
 * index. * When the properties from the Applet are not yet defined, try to re-
 * initialise * the document with the overview data.
 *
*************************************/
function loadCurrentNetDataOverview ()
/************************************/
{
    if (hosts_title)    // Applet properties are defined.
    {
	net_data_overview_document.write (start_date_ind, true);
    }
    else    // Applet properties are not yet defined.
    {
	current_document = net_data_overview_document;   // Show init. overv. d.

	reInitDocument ();
    }
}

/**
 * Load the document (and plot) with the load data at the current date index.
 * When the properties from the Applet are not yet defined, try to re-initialise * the document with the load data.
 *
**************************/
function loadCurrentLoad ()
/*************************/
{
    if (hosts_title)    // Applet properties are defined.
    {
	load_document.write (start_date_ind, true);
    }
    else    // Applet properties are not yet defined.
    {
	current_document = load_document;    // Show initially load data.

	reInitDocument ();
    }
}

/**
 * Load the document (and plot) with the ping data at the current date index.
 * When the properties from the Applet are not yet defined, try to re-initialise * the document with the ping data.
 *
**************************/
function loadCurrentPing ()
/*************************/
{
    if (hosts_title)    // Applet properties are defined.
    {
	ping_document.write (start_date_ind, true);
    }
    else    // Applet properties are not yet defined.
    {
	current_document = ping_document;    // Show initially ping data.

	reInitDocument ();
    }
}

/**
 * Load the document (and plot) with the throughput data at the current date
 * index. When the properties from the Applet are not yet defined, try to
 * re-initialise the document with the throughput data.
 *
********************************/
function loadCurrentThroughput ()
/*******************************/
{
    if (hosts_title)    // Applet properties are defined.
    {
	throughput_document.write (start_date_ind, true);
    }
    else    // Applet properties are not yet defined.
    {
	current_document = throughput_document;   // Show init. throughput data.
	
	reInitDocument ();
    }
}


/**
 * Load the document (and plot) with the UDP bandwidth data at the current date
 * index. When the properties from the Applet are not yet defined, try to
 * re-initialise the document with the UDP bandwidth data.
 *
***************************/
function loadCurrentUDPBW ()
/**************************/
{
    if (hosts_title)    // Applet properties are defined.
    {
	udp_bw_document.write (start_date_ind, true);
    }
    else    // Applet properties are not yet defined.
    {
	current_document = udp_bw_document;    // Show initially UDP BW data.

	reInitDocument ();
    }
}

/**
 * Write the button to display the current UDP bandwidth data. The button is
 * written in a column.
 *
****************************************/
function writeDisplayUDPBWButtonColumn ()
/***************************************/
{
    document.write (
	"<TD><INPUT TYPE=\"button\"\n",
	"           VALUE=\"UDP\"\n",
	"           onMouseOver=\"window.status='UDP table'; ",
			"return true\"\n",
	"           onMouseOut=\"window.status=''; return true\"\n",
	"           onClick=\"loadCurrentUDPBW()\"></TD>\n"
	);
}

/**
 * Write the button to plot the data corresponding with the current table. Due
 * to outlining, button label (value property) is also dependent from yes/no UDP
 * bandwidth test. The button is written in a column.
 *
************************************/
function writeDataplotButtonColumn ()
/***********************************/
{
    /*
     * Set the button label. Use the initial value of the UDP bandwidth flag
     * because this function will be called from the top control row before
     * Applet initialisation.
     */

    var button_value = ( init_use_udp_bw ? "Plot" : "Dataplot" );

    document.write (
	"<TD><INPUT TYPE=\"button\"\n",
	"           VALUE=\"", button_value, "\"\n",
	"           onMouseOver=\"window.status='Plot current data'; ",
		    "return true\"\n",
	"           onMouseOut=\"window.status=''; return true\"\n",
	"           onClick=\"plotCurrentData()\"></TD>\n"
	);

} // writeDataplotButtonColumn ()

/**
 * Write the timestep select column into the top control panel. Set also the
 * data type dependent minimum table index and the corresponding date step.
 *
************************************/
function writeTimeStepSelectColumn ()
/***********************************/
{
    /*
     * Set some constants, depending from the minimum date step in minutes. Use
     * the initial date step as a guess
     */
     
    var hour_step = 60 / init_date_step;    // The # date steps in an hour.
    var day_step  = 24 * hour_step;	    // The # date steps in a day.
    var week_step =  7 * day_step;	    // The # date steps in a week.

    /*
     * Select the minimum index for the table of the current net data.
     */

    var select_index = current_data.table_min_select_index;

    /*
     * Set some variables.
     */
    
    var option_index =  0;    // The index of the current option.
    var min_step     =  1;    // The minute step index of the current option.

    /*
     * Write the SELECT element and the dummy header name element.
     */

    document.write (
	"<TD><SELECT NAME=\"SelectTimeStep\"\n",
	"            SIZE=\"1\"\n",
	"            onMouseOver=\"window.status='Set row time step'; ",
			 "return true\"\n",
	"            onMouseOut=\"window.status=''; return true\"\n",
	"            onChange=\"loadCurrentTimeStep()\">\n",
	"    <OPTION VALUE=\"-1\">Timestep:</OPTION>\n"
	);

    /*
     * Write the minute step options (# minutes below an hour). They are powers
     * of 2. Count the # minute options. Select the first option when the data
     * type is equals to the recent or to the week data.
     */

    var nr_min_options = 0;

    while (min_step < hour_step)
    {
	document.write ("    <OPTION VALUE=\"", min_step, "\"");

	if ( nr_min_options == 0
	     &&
	     ( data_type_index == recent_data_index  ||
	       data_type_index == week_data_index ) )
	{
	    document.write (" SELECTED");
	}

	document.write (">", min_step * init_date_step, " min.</OPTION>\n");

	min_step       *= 2;
	nr_min_options ++;
    }

    /*
     * Write the constant hour and day steps and write the appropriate
     * "SELECTED" tags. Close also the SELECT element. Set first the # hour and
     * the # day options
     */

    var nr_hour_options = 5;
    var nr_day_options  = 3;

    document.write ("    <OPTION VALUE=\"", 1 * hour_step, "\"");

    if ( nr_min_options == 0
	 &&
	 ( data_type_index == recent_data_index  ||
	   data_type_index == week_data_index ) )
    {
	document.write (" SELECTED");
    }

    document.write (">1 hour</OPTION>\n");

    document.write (
	"    <OPTION VALUE=\"",  2 * hour_step, "\">2 hours</OPTION>\n",
	"    <OPTION VALUE=\"",  3 * hour_step, "\">3 hours</OPTION>\n",
	"    <OPTION VALUE=\"",  6 * hour_step, "\">6 hours</OPTION>\n",
	"    <OPTION VALUE=\"", 12 * hour_step, "\">12 hours</OPTION>\n",
	"    <OPTION VALUE=\"",  1 * day_step,  "\""
	);

    if (data_type_index == day_mean_data_index)
	document.write (" SELECTED");

    document.write (
	">1 day</OPTION>\n",
	"    <OPTION VALUE=\"", 2 * day_step,  "\">2 days</OPTION>\n",
	"    <OPTION VALUE=\"", 4 * day_step,  "\">4 days</OPTION>\n",
	"    <OPTION VALUE=\"", 1 * week_step, "\""
	);

    if (data_type_index == week_mean_data_index)
	document.write (" SELECTED");

    document.write (
	">1 week</OPTION>\n",
	"    <OPTION VALUE=", 2 * week_step, ">2 weeks</OPTION>\n",
	"    <OPTION VALUE=", 4 * week_step, ">4 weeks</OPTION>\n",
	"    </SELECT></TD>\n"
	);

    /*
     * Use the # hour and the # day options to set the (minimum) selector index
     * for the corresponding data type. Set also first the date step for the
     * connected table.
     */

    var week_mean_data = net_data_descrs [week_mean_data_index];

    week_mean_data.table_date_step    = week_step * init_date_step;
    week_mean_data.table_select_index = week_mean_data.table_min_select_index
				      = nr_min_options + nr_hour_options +
					nr_day_options + 1;
    
    var day_mean_data = net_data_descrs [day_mean_data_index];

    day_mean_data.table_date_step    = day_step * init_date_step;
    day_mean_data.table_select_index = day_mean_data.table_min_select_index
				     = nr_min_options + nr_hour_options + 1;

} // writeTimeStepSelectColumn ()

/**
 * Load the current time step from the SELECT element. Calculate a time interval
 * from the selected index. Use the time interval to obtain a data step for this
 * table. Rewrite the current document with the new time step.
 *
******************************/
function loadCurrentTimeStep ()
/*****************************/
{
    /*
     * Get the select options.
     */

    var options = document.ControlButtons.SelectTimeStep.options;

    /*
     * Do nothing when the dummy header option was selected.
     */

    if (options.selectedIndex == 0)
	return;

    /*
     * Set the selected index to the minimum selected index for the current data
     * type when it is too small.
     */

    if (options.selectedIndex < current_data.table_min_select_index)
	options.selectedIndex = current_data.table_min_select_index;

    /*
     * Calculate the time interval from the date step multipliers, specified in
     * the option values.
     */

    var time_intv = options [options.selectedIndex].value * init_date_step;

    /*
     * Set the time interval index. Correct for too small values.
     */

    date_ind_incr = Math.floor (time_intv / current_data.table_date_step);

    if (date_ind_incr < 1)
	date_ind_incr = 1;

    /*
     * Set the selected index and the time interval index for the current data
     * type. They are used when the data type is reloaded.
     */

    current_data.table_select_index  = options.selectedIndex;
    current_data.table_date_ind_incr = date_ind_incr;

    /*
     * Rewrite the current document when it is not the overview document with
     * the new time interval.
     */

    if (current_document != net_data_overview_document)
	current_document.write (start_date_ind, false);

} // loadCurrentTimeStep ()

/**
 * Write the select input field to select the scroll size. The input field is
 * written in a column.
 *
**************************************/
function writeScrollSizeSelectColumn ()
/*************************************/
{
    /*
     * Set the half table scroll size.
     */

    var scroll_half_table_size = Math.floor (max_nr_date_rows / 2);

    if (scroll_half_table_size < 1)
	scroll_half_table_size = 1;

    /*
     * Set the table scroll size. Set the last field at the top.
     */

    var scroll_table_size = max_nr_date_rows - 1;

    if (scroll_table_size < 1)
	scroll_table_size = 1;

    /*
     * Write the scroll select input field. Set also the selected scroll select
     * input field.
     */

    document.write (
	"<TD><SELECT NAME=\"SelectScrollSize\"\n",
	"            SIZE=\"1\"\n",
	"            onMouseOver=\"window.status='Set table row scroll size'; ",
			 "return true\"\n",
	"            onMouseOut=\"window.status=''; return true\"\n",
	"            onChange=\"selectScrollSize()\">\n",
	"    <OPTION VALUE=\"-1\">Scroll size:</OPTION>\n",
	"    <OPTION VALUE=\"1\""
	);
	
    if (scroll_select_index == scroll_line_index)    // Scroll line selected.
    {
	document.write (" SELECTED");
    }
    
    document.write (
	">Scroll line</OPTION>\n",
	"    <OPTION VALUE=\"", scroll_half_table_size, "\""
	);

    if (scroll_select_index == scroll_half_table_index)    // Scroll 1/2 table
    {							   // selected.
	document.write (" SELECTED");
    }

    document.write (
       	">Scroll 1/2 table</OPTION>\n",
	"    <OPTION VALUE=\"", scroll_table_size, "\""
	);

    if (scroll_select_index == scroll_table_index)    // Scroll table selected.
    {
	document.write (" SELECTED");
    }

    document.write (
	">Scroll table</OPTION>\n",
	"    </SELECT></TD>\n"
	);

    /*
     * Set the scroll size to the one defined at startup.
     */

    if (scroll_select_index == scroll_half_table_index)    // Scroll 1/2 table
    {							   // selected.
	scroll_size = scroll_half_table_size;
    }
    else if (scroll_select_index == scroll_table_index)    // Scroll table
    {							   // selected.
	scroll_size = scroll_table_size;
    }
    else						   // Default to line
    {							   // scrolling.
	scroll_size = 1;
    }

} // function writeScrollSizeSelectColumn ()

/**
 * Adjust the scroll select option values after the maximum # date rows has been
 * changed.
 *
***********************************/
function adjustScrollSelectValues ()
/**********************************/
{
    /*
     * Set the half table scroll size.
     */

    var scroll_half_table_size = Math.floor (max_nr_date_rows / 2);

    if (scroll_half_table_size < 1)
	scroll_half_table_size = 1;

    /*
     * Set the table scroll size. Set the last field at the top.
     */

    var scroll_table_size = max_nr_date_rows - 1;

    if (scroll_table_size < 1)
	scroll_table_size = 1;

    /*
     * Adjust the option values.
     */

    var options = document.ControlButtons.SelectScrollSize.options;

    options [scroll_half_table_index].value = scroll_half_table_size;
    options [scroll_table_index	    ].value = scroll_table_size;

    /*
     * Make the current scroll size up-to-date.
     */

    scroll_size = options [scroll_select_index].value;

} // function adjustScrollSelectValues ()

/**
 * Select the scroll size from the corresponding select input field.
 *
***************************/
function selectScrollSize ()
/**************************/
{
    /*
     * Set the scroll options.
     */

    var options = document.ControlButtons.SelectScrollSize.options;

    /*
     * Do nothing when the tile header is selected.
     */

    if (options.selectedIndex == scroll_title_index)
	return;

    /*
     * Store selected index and selected scroll size.
     */

    scroll_select_index = options.selectedIndex;
    scroll_size		= options [scroll_select_index].value;

    /*
     * Set the sroll select index cookie.
     */

    setCookie (scroll_select_index_cookie, scroll_select_index);

} // function selectScrollSize ()

/**
 * Write the button to select the net data file to import the data from. The
 * button is written in a column.
 *
*****************************************/
function writeImportNetDataButtonColumn ()
/****************************************/
{
    document.write (
	"<TD><INPUT TYPE=\"button\"\n",
	"           NAME=\"ImportNetData\"\n",
	"           VALUE=\"Import data ...\"\n",
	"           onMouseOver=\"window.status='Import data file ...'; ",
			"return true\"\n",
	"           onMouseOut=\"window.status=''; return true\"\n",
	"	    onClick=\"showImportDataWindow()\"></TD>\n"
	);
}

/**
 * Write the select input field to select a net data type and to load the net
 * data. The input field is written in a column. Set the default net data type
 * as selected.
 *
***************************************/
function writeNetDataTypeSelectColumn ()
/**************************************/
{
    /*
     * Open the select input field, write the header option and open the
     * "recent data" option.
     */
     
    document.write (
	"<TD><SELECT NAME=\"SelectNetDataType\"\n",
	"            SIZE=\"1\"\n",
	"            onMouseOver=\"window.status='Select data type'; ",
			 "return true\"\n",
	"            onMouseOut=\"window.status=''; return true\"\n",
	"            onChange=\"selectNetDataType()\">\n",
	"    <OPTION VALUE=\"-1\">Data type:</OPTION>\n",
	"    <OPTION VALUE=\"", recent_data_index, "\""
	);

    if (data_type_index == recent_data_index)
	document.write (" SELECTED");

    /*
     * Close the "recent data" option and open the "week data" option.
     */

    document.write (
	">Last ", nr_days, " days</OPTION>\n",
	"    <OPTION VALUE=\"", week_data_index, "\""
	);

    if (data_type_index == week_data_index)
	document.write (" SELECTED");

    /*
     * Close the "week data" option and open the "week mean data" option.
     */

    document.write (
	">Select week ...</OPTION>\n",
	"    <OPTION VALUE=\"", week_mean_data_index, "\""
	);

    if (data_type_index == week_mean_data_index)
	document.write (" SELECTED");

    /*
     * Close the "week mean data" option and open the "day mean data" option.
     */

    document.write (
	">Week mean</OPTION>\n",
	"    <OPTION VALUE=\"", day_mean_data_index, "\""
	);

    if (data_type_index == day_mean_data_index)
	document.write (" SELECTED");

    /*
     * Close the "day mean data" option and open the "quarter weekday time mean"
     * option.
     */

    document.write (
	">Day mean</OPTION>\n",
	"    <OPTION VALUE=\"", quarter_data_index, "\""
	);

    if (data_type_index == quarter_data_index)
	document.write (" SELECTED");

    /*
     * Close the "quarter weekday time mean" option and open the "month workday
     * time mean option".
     */

    document.write (
	">Sel. weekdays at ...</OPTION>\n",
	"    <OPTION VALUE=\"", month_data_index, "\""
	);

    if (data_type_index == month_data_index)
	document.write (" SELECTED");

    /*
     * Close the "month workday time mean" option, the select input field and
     * the column.
     */

    document.write (
	">Sel. workday at ...</OPTION>\n",
	"    </SELECT></TD>\n"
	);

} // function writeNetDataTypeSelectColumn ()

/**
 * Set the selected net data type and reload the document with the new data type
 * file.
 *
****************************/
function selectNetDataType ()
/***************************/
{
    /*
     * Get the select options.
     */

    var options = document.ControlButtons.SelectNetDataType.options;

    /*
     * Stop when the title of the data type selector was selected or when
     * nothing was selected.
     */

    if (options.selectedIndex <= 0)
	return;

    /*
     * Set the data type index to load.
     */

    new_data_type_index = options [options.selectedIndex].value;

    /*
     * If the selected data type has a defined select title: show the
     * corresponding selector window and load the net data after the selection
     * of the date ID. In the other situations the new net data are set and
     * loaded immediately. An open selector window is closed.
     */

    if (net_data_descrs [new_data_type_index].select_title != "")
    {
	/*
	 * Open the selector window to select a date dependent data file.
	 */

	showSelectDateWindow ();
    }
    else
    {
	/*
	 * Set the new net data type index and the accelerator.
	 */

	data_type_index = new_data_type_index;
	current_data    = net_data_descrs [data_type_index];

	/*
	 * An unique data file for the current data type.
	 */

	data_file = current_data.file_or_base;
	
	closeSelectDateWindow ();
	loadNetDataType	      ();
    }

} // function selectNetDataType ()

/**
 * Import the specified data file. This function will be called from the import
 * data window. The import data window is closed before the importing.
 *
 *    @param  new_data_file   The specified new data file to import.
 *
**************************************/
function importDataFile (new_data_file)
/*************************************/
{
    data_file = new_data_file;    // Set the data file to import.

    /*
     * Close the import window before importing and reading the data.
     */
     
    closeImportDataWindow ();

    /*
     * Load the net data from the Applet and show the table, starting at the
     * most recent data.
     */

    loadMostRecentData ();
}

/**
 * Get the data from the specified net data type from the Applet. Reload the
 * table with the new data.
 *
**************************/
function loadNetDataType ()
/*************************/
{
    /*
     * Reset the previous values of the time step selected index and of the time
     * interval index stored for the current net data.
     */

    var options = document.ControlButtons.SelectTimeStep.options;

    options.selectedIndex = current_data.table_select_index;
    date_ind_incr	  = current_data.table_date_ind_incr;

    /*
     * Load the net data from the Applet and show the table, starting at the
     * most recent data.
     */

    loadMostRecentData ();

} // function loadNetDataType

/**
 * (Re)load the net data from the applet and show the most recent data
 * from the view displayed at this moment.
 *
*****************************/
function loadMostRecentData ()
/****************************/
{
    /*
     * Set the # tries to reload the data after failure. Let the value depend
     * from the fact if the HTML is written repeatedly.
     */

    var nr_tries;

    if (repeat_write.timer_set)    // Repeatedly write HTML page.
	nr_tries = 15;
    else			   // HTML page selected by the user.
	nr_tries = 1;

    /*
     * Give the user some feedback that the net data are loading now.
     */

    writeLoadingDataDocument ();
	
    /*
     * Retry to let the Applet set the data until they are read or until
     * the maximum # retries is reached.
     */

    // applet.start ();

    retrySetNetData (nr_tries);

} // function loadMostRecentData ()

/**
 * This function retries (with recursive calls from "waitRetrySetNetData()") to
 * let the Applet set the data until they are read or until the maximum
 * # retries has been reached.
 *
 *    @param  nr_tries   The maximum # retries until the data read are from the
 *			 correct format.
 *
**********************************/
function retrySetNetData (nr_tries)
/*********************************/
{
    var applet = document.applets ["NetData"];

    applet.setNetData (data_file);    // Set the net data in the Applet.

    /*
     * Wait until the net data are read. Do a recursive call to this method when
     * the reading of the data failed, but not more than the # tries.
     */

    waitRetrySetNetData (nr_tries);

} // retrySetNetData ()

/**
 * Wait until the net data are read. Do a recursive call to this method when the
 * reading of the data failed, but not more than the # tries.
 *
 *    @param  nr_tries   The maximum # retries until the data read are from the
 *			 correct format.
 *
**************************************/
function waitRetrySetNetData (nr_tries)
/*************************************/
{
    var applet		  = document.applets ["NetData"];
    var wait_data_timeout = 200; // ms

    /*
     * Reload this function after the corresponding data timeout when the net
     * data have not yet been read.
     */

    if (applet.doReadData () == "true")
    {
	setTimeout ("waitRetrySetNetData(" + nr_tries + ")", wait_data_timeout);

	return;
    }

    /*
     * Make the next call of "retrySetNetData()" when the loading of the data
     * from the Applet failed, when the max. # tries has not yet been reached.
     */

    if (parseInt (applet.getNrDates ()) <= 0)    // Failure.
    {
	if (nr_tries-- > 0)    // Next call when maximum is not yet reached.
	{
	    retrySetNetData (nr_tries);

	    return;
	}
    }

    /*
     * Set the data type parameters when a data file is imported.
     */

    if (import_data)
	setDataTypeParameters ();

    /*
     * Set some parameters which are dependent from the net data file.
     */

    setNetDataParameters ();

    /*
     * Write (from) the most recent date. The loading user feedback will now be
     * overwritten.
     */

    current_document.write (-1, true);

} // function waitRetrySetNetData ()

/**
 * Write a message in the data body that the user should select a data file.
 * This message will be overwritten by further action from the user.
 *
*************************************/
function writeImportDataFileMessage ()
/************************************/
{
    /*
     * Open net data frame for the loading message.
     */

    parent.NetData.document.open ();

    /*
     * Write the select data message.
     */

    parent.NetData.document.write (
	"<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
	"\n",
	"<BODY>\n",
	"\n",
	"<CENTER>\n",
	"\n",
	"<H2>Please select a data file to import.</H2>\n",
	"\n",
	"</CENTER>\n",
	"\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);

    /*
     * Close the document to flush the browser.
     */

    parent.NetData.document.close ();

} // function writeImportDataFileMessage ()

/**
 * Give the user some feedback that the net data are being loaded from the
 * "NetData" Applet. This is done by writing a blinking message in the net
 * data frame (the lowest frame). When the actual net data are written in this
 * frame this message will be overwritten automaticly.
 *
 * Note that the caching mecanism of Internet Explorer 4.0 prevends this message
 * to be shown. However it is shown in Netscape 4.
 *
***********************************/
function writeLoadingDataDocument ()
/**********************************/
{
    /*
     * Open net data frame for the loading message.
     */

    parent.NetData.document.open ();

    /*
     * Write blinking loading message.
     */

    parent.NetData.document.write (
	"<HTML>\n",
	"\n",
	"<HEAD>\n"
	);

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    parent.NetData.document.write (
	"</HEAD>\n",
	"\n",
	"<BODY>\n",
	"\n",
	"<CENTER>\n",
	"\n",
	"<H1><BLINK>Loading Data ...</BLINK></H1>\n",
	"\n",
	"</CENTER>\n",
	"\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);

    /*
     * Close the document to flush the browser.
     */

    parent.NetData.document.close ();

} // function writeLoadingDataDocument ()

/**
 * Set the global data type parameters which are dependent from the current net
 * data.
 *
********************************/
function setDataTypeParameters ()
/*******************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    /*
     * Get the data type index of the current file from the Applet.
     */

    var file_data_type_index = parseInt (applet.getDataTypeIndex ());

    /*
     * Adjust some data type dependent parameters when the new file data type is
     * different from the current data type.
     */

    if (file_data_type_index != data_type_index)    // Data type is changed.
    {
	data_type_index = file_data_type_index;
	current_data	= net_data_descrs [data_type_index];

	/*
	 * Reset the previous values of the time step selected index and of the
	 * time interval index stored for the current net data.
	 */

	var options = document.ControlButtons.SelectTimeStep.options;

	options.selectedIndex = current_data.table_select_index;
	date_ind_incr	      = current_data.table_date_ind_incr;

    } // Data type is changed.

} // function setDataTypeParameters ()

/**
 * Set some global parameters which are dependent from the net data file.
 *
*******************************/
function setNetDataParameters ()
/******************************/
{
    var applet = document.applets ["NetData"];

    // applet.start ();

    /*
     * Obtain the net data dependent parameters from the Applet.
     */

    hosts_title = applet.getSetTitle ();	  // Title hosts set.
    date_step	= parseInt (applet.getPeriod ()); // Step [m] between two dates.

    if (applet.getOneDirect () == "true")    // One- / two-directional tests.
	one_direct = true;
    else
	one_direct = false;

    if (applet.getQoSAndInet () == "true")    // Also QoS tests?
	use_qos = true;
    else
	use_qos = false;

    if (applet.getUseUDPBW () == "true")    // Do UDP bandwidth measurements?
	use_udp_bw = true;
    else
	use_udp_bw = false;

    qos_title  = applet.getQoSTitle  ();    // Title of QoS tests.
    inet_title = applet.getInetTitle ();    // Title of the Internet tests.

    /*
     * Set the # directions count corresponding with the direction flag set
     * above.
     */

    nr_directs = (one_direct ? 1 : 2);

    /*
     * Calculate the minimum selected minute index for the corresponding data
     * descriptors from the date step for this data type and from the initial
     * data type, used when the top control frame was defined.
     */

    var min_opt_ind = 1;    // The minute option index.
    var min_step    = 1;    // The minute step index of the current option.

    var min_date_step = min_step * init_date_step;   // The init. min date step.

    /*
     * Increment the minute option index until the net data date step is
     * reached.
     */
     
    while (min_date_step < date_step)    // Next minute date step.
    {
	min_step      *= 2;
	min_date_step  = min_step * init_date_step;

	/*
	 * Only increment the minute option when the new minute date step is not
	 * larger than the table date step.
	 */

	if (min_date_step == date_step)
	{
	    min_opt_ind++;
	}
    }

    /*
     * Set the minimum selected minute index for the corresponding data
     * descriptors.
     */

    net_data_descrs [recent_data_index ].table_min_select_index = min_opt_ind;
    net_data_descrs [week_data_index   ].table_min_select_index = min_opt_ind;
    net_data_descrs [quarter_data_index].table_min_select_index = min_opt_ind;
    net_data_descrs [month_data_index  ].table_min_select_index = min_opt_ind;

    /*
     * Set the date step for the corresponding data descriptors.
     */

    net_data_descrs [recent_data_index ].table_date_step = date_step;
    net_data_descrs [week_data_index   ].table_date_step = date_step;
    net_data_descrs [quarter_data_index].table_date_step = date_step;
    net_data_descrs [month_data_index  ].table_date_step = date_step;

    /*
     * Adjust the selected time step option to the minute option index when the
     * selected time step was too small. However, do nothing when the dummy
     * header option was selected.
     */

    var time_step_options = document.ControlButtons.SelectTimeStep.options;

    if (time_step_options.selectedIndex > 0)  // Dummy header opt. not selected.
    {
	if (time_step_options.selectedIndex < min_opt_ind)    // Too small.
	{
	    time_step_options.selectedIndex = min_opt_ind;
	}
    }

} // function setNetDataParameters ()

/**
 * Increment the time shown with the specified step and scroll size.
 *
 *    @param  incr_val   The increment wich is used to increment the
 *			 date.
 *
*******************************/
function loadNextDate (incr_val)
/******************************/
{
    if (start_date_ind < 0)    // Start date index not yet initialised
	return;

    /*
     * Set the scroll_size documen dependent: the overview document does not
     * display data as function of the time.
     */

    var document_scroll_size;

    if (current_document == net_data_overview_document)
	document_scroll_size = 1;
    else
	document_scroll_size = scroll_size;

    /*
     * Increment the start date index with the specified step and scroll size.
     */

    start_date_ind += (incr_val * date_ind_incr * document_scroll_size);

    if (start_date_ind < 0)    // Start date index became too small. A
	start_date_ind = 0;    // value < 0 defaults to most recent value.

    /*
     * Redisplay the current table with the new selected time
     */

    current_document.write (start_date_ind, true);

} // function loadNextDate ()

/**
 * Plot the current table data, starting at the current date. Plot also when the
 * plot window is not yet shown.
 *
**************************/
function plotCurrentData ()
/*************************/
{
    current_document.plot (start_date_ind, true);
}

/**
 * Write the META declarations for the Net Data document.
 *
***************************/
function writeNetDataMeta ()
/**************************/
{
    parent.NetData.document.write (
	"<META HTTP-EQUIV=\"Content-Style-Type\" ",
	      "CONTENT=\"text/css\">\n",
	"<META HTTP-EQUIV=\"Content-Script-Type\" ",
	      "CONTENT=\"text/javascript\">\n"
	);
}

/**
 * Write the document style for the Net Data document.
 *
************************************/
function writeNetDataDocumentStyle ()
/***********************************/
{
    parent.NetData.document.write (
	"<STYLE>\n",
	"<!--\n",
	"\n",
	"BODY { background-color: #FFFFFF }\n",
	"BODY { color:            #000000 }\n",
	"\n",
	"A:link    { color: #0000EE }\n",
	"A:visited { color: #551A8B }\n",
	"A:active  { color: #E00000 }\n",
	"\n",
	"  -->\n",
	"</STYLE>\n"
	);
}

/**
 * Define a function to set a cookie. The # cookie expire days constant is used.
 * Do nothing when no cookies are allowed by the user.
 *
 *    @param  name    The name of the cookie.
 *
 *    @param  value   The value of the cookie.
 *
*******************************/
function setCookie (name, value)
/******************************/
{
    if (!allow_cookies)    // Cookies are not allowed: RETURN and do nothing.
	return;

    /*
     * Set the expire date to the # days from this moment.
     */

    var today	    = new Date ();
    var expire_date = new Date ();

    expire_date.setTime (today.getTime () +
			 1000 * 60 * 60 * 24 * cookie_expire_days);

    /*
     * Set the cookie, using the expire date.
     */

    document.cookie = name + "=" + escape (value) + ";expires=" +
		      expire_date.toGMTString ();

} // setCookie ()

/**
 * Return the value of the cookie with the specified name. When no cookie is
 * found, "null" is returned.
 *
 *    @param  name   The name of the cookie.
 *
 *    @return   The value of the specified cookie, or "null" when none was
 *		found.
 *
************************/
function getCookie (name)
/***********************/
{
    if (document.cookie.length > 0)    // If there are any cookies.
    {
	var search = name + "=";    // The cookie name string to search.

	var offset = document.cookie.indexOf (search);

	if (offset != -1)    // If cookie exists.
	{
	    /*
	     * Set index of beginning of value.
	     */

	    offset += search.length;

	    /*
	     * Set index of end of cookie value.
	     */

	    var end = document.cookie.indexOf (";", offset);

	    if (end == -1)    // Last cookie: set end to last char. in string.
	    {
		end = document.cookie.length;
	    }

	    /*
	     * Return the cookie substring.
	     */

	    return unescape (document.cookie.substring (offset, end));

	} // Cookie exists.

    } // There are any cookies.

    return null;    // Return "null" when no cookie was found.

} // getCookie ()

/**
 * This function writes the default overview table in the frame body
 * when this page was just loaded.
 *
*********************/
function handleLoad ()
/********************/
{
    initDocument ();
}

/**
 * Set all cookies. Close also the help window, the select week window and / or
 * the import data window when the top control window is closing.
 *
***********************/
function handleUnload ()
/**********************/
{
    setCookies		  ();
    closeHelpWindow	  ();
    closeSelectDateWindow ();
    closeImportDataWindow ();
}

/**
 * This function is called by a re-initialiasition of this document when it was
 * not yet properly loaded because the Applet properties in JavaScript stay
 * undefined. First the "writeLoadingDataDocument()" has to be re-called.
 * Otherwise at some browsers (Netscape 6) the Applet properties will stay
 * undefind.
 *
*************************/
function reInitDocument ()
/************************/
{
    writeLoadingDataDocument ();
    initDocument	     ();
}

/**
 * This function initialises the document. This implies that there is waited
 * until the net data are being read by the Applet, at leas when no data file
 * should be imported. Also some variables are set which are defined by the net
 * data read by the Applet.
 *
 * A reload is retried after a timeout when the Applet in not yet loaded.
 * However, there is a maximum # tries.
 *
***********************/
function initDocument ()
/**********************/
{
    var nr_tries = 20;

    /*
     * Call the real function which does the work. At failure this function will
     * recursively recall itsself with the maximum specified # tries.
     */

    retryInitDocument (nr_tries);

} // initDocument ()

/**
 * This function initialises the document. This implies that there is waited
 * until the net data are being read by the Applet, at leas when no data file
 * should be imported. Also some variables are set which are defined by the net
 * data read by the Applet.
 *
 * A reload is retried (by making a recursive call to itsself) after a timeout
 * when the Applet in not yet loaded. However, there is a maximum # tries.
 *
 *    @param  nr_tries    The maximum # tries this method is called when the
 *			  Applet is not yet started.
 *
************************************/
function retryInitDocument (nr_tries)
/***********************************/
{
    var applet	       = document.applets ["NetData"];
    var reload_timeout = 500; // ms

    /*
     * Only write a diagnostic message in the body header when a data file must
     * be imported: do nothing further.
     */

    if (import_data)    // Import data file: only write diagnostic message.
    {
	writeImportDataFileMessage ();
	return;
    }

    /*
     * When the Applet is not yet loaded and/or not yet started: retry after a
     * timeout. However, stop after a fixed # tries.
     */

    if (!applet  ||  !applet.is_started  ||  applet.is_started != "true")
    {
	if (nr_tries-- > 0)				      // Max # tries not
	{						      // yet reached.
	    setTimeout ("retryInitDocument(" + nr_tries + ")",
			reload_timeout);
	}
	else  // Write in the body frame to select the data type by the user.
	{
	    writeSelectDataTypeByUser ();
	}

	return;
    }

    /*
     * Wait until the net data are read. Initialise some parameters after that.
     */

    waitInitNetData ();

} //function initDocument ()

/**
 * This function writes in the body frame a message that the user should select
 * a data type.
 *
 * The reason for this message is that the Applet properties still are unknown
 * after the document "OnLoad" event did occur. Sometimes that happens at NS
 * browsers > 4. However, it is very likely that they are known after evry
 * loading actions are stopped (kind of "chicken" and "egg" problem), such that
 * the user is able to select them now after loading.
 *
************************************/
function writeSelectDataTypeByUser ()
/***********************************/
{
    /*
     * Open the net data frame for the unknown properties message.
     */

    parent.NetData.document.open ();

    /*
     * Write the META declarations and the document styles in the header.
     */

    parent.NetData.document.write (
	"<HTML>\n",
	"\n",
	"<HEAD>\n");

    writeNetDataMeta	      ();
    writeNetDataDocumentStyle ();

    /*
     * Write the unknown properties message in the body.
     */

    parent.NetData.document.write (
	"</HEAD>\n",
	"\n",
	"<BODY>\n",
	"\n",
	"<H1>Select Data Type</H1>\n",
	"\n",
	"The monitor data are read when the signal light at the left of the\n",
	"top frame had become green. Then there can be continued by\n",
	"selecting the data type to display with the\n",
	"&quot;<B>Overview</B>&quot;, &quot;<B>Throughput</B>&quot;,\n",
	"&quot;<B>Load</B>&quot; or &quot;<B>Ping</B>&quot; button in the\n",
	"top frame.\n",
	"\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);

    /*
    parent.NetData.document.write (
	"</HEAD>\n",
	"\n",
	"<BODY>\n",
	"\n",
	"<H1>No Applet Properties during Initialisation</H1>\n",
	"\n",
	"It was not possible by JavaScript to obtain the Applet properties\n",
	"during the initialisation of your browser. However it is propable\n",
	"still possible to select the required table with the\n",
	"&quot;<B>Overview</B>&quot;, &quot;<B>Throughput&quot;</B>,\n",
	"&quot;<B>Load</B>&quot; or &quot;<B>Ping</B>&quot; buttons in the\n",
	"top frame. If that also fails a reload can be tried with the\n",
	"&quot;<B>|&lt;&lt;</B>&quot;button.\n",
	"\n",
	"</BODY>\n",
	"\n",
	"</HTML>\n"
	);
    */

    /*
     * Close the document to flush the browser.
     */

    parent.NetData.document.close ();

}

/**
 * This function waits until the net data are read by checking at the Applet i
 * the data are being read. After a timeout this function will be recalled when
 * this is not the case.
 *
**************************/
function waitInitNetData ()
/*************************/
{
    var applet		  = document.applets ["NetData"];
    var wait_data_timeout = 200; // ms

    /*
     * Reload this function after the corresponding data timeout when the net
     * data have not yet been read.
     */

    if (applet.doReadData () == "true")
    {
	setTimeout ("waitInitNetData()", wait_data_timeout);

	return;
    }

    setNewPlotSize	 ();	// Set the cookies new plot size if set.
    setNetDataParameters ();    // Set some data file dependent parameters.

    /*
     * Write the data just read in table form in the body frame.
     */

    current_document.write (start_date_ind, true);

} // function waitInitNetData ()
