var JS_PLX = "C:\\!\\dh\\com\\Src\\plx.js (3466 lines) 2012-01-21 17:21 Rachel Bartlett";
/**********************************************************************/
/*                                                                    */
/*               Programming Language eXtensions (PLX)                */
/*               -------------------------------------                */
/*                                                                    */
/*                    (c) 1999-2011 Rachel Barlett                    */
/*                                                                    */
/**********************************************************************/
var plx_log_is_enabled = true;        // calls to x_log() depend on this
x_log("\nJS_PLX: \"" + JS_PLX + "\"");
// var YES = new String("YES");
// var NO  = new String("NO" );

/*--------------------------------------------------------------------*/
/*                                                                    */
/*      The following global settings can be modified externally      */
/*        at any time after this library is loaded and before         */
/*                the affected plx function is called.                */
/*                                                                    */
/*              Since Javascript always exports globals,              */
/*          they need to be protected with a 'plx_' prefix.           */
/*                                                                    */
/*         It is better to access them via accessor functions         */
/*            since data flow through these can be traced.            */
/*           Also, by returning the previous setting, they            */
/*            make it easy to implement a threaded queue.             */
/*                                                                    */
/*--------------------------------------------------------------------*/
var plx_get_parm_debug  = false;
var plx_hover_style     = new String("color: black; " // see x_hover_*()
                        + "background-color: white; " // see x_hover_*()
                        + "border-color: black; "     // see x_hover_*()
                        + "border-width: 1; "         // see x_hover_*()
                        + "border-style: solid;");    // see x_hover_*()

// document.body.clientWidth doesn't exist yet, but we can say
// what fraction of clientWidth we want hover craft to take up:
var plx_hover_width_fraction = 0.3;

var plx_link_icon = new String('');                      // see x_link()
var plx_media_frame_color = 'white';        // see x_media_frame_color()
var plx_media_frame_caption_style   // see x_media_frame_caption_style()
    = 'font-size: 12; font-weight: bold;';
var plx_media_frame_width = 6;              // see x_media_frame_width()
var plx_media_icon;                                     // user settable
var plx_media_folder;                                   // user settable
var plx_media_next_align_flag                        // 1st-time default
     = new String('-right');
var plx_player_folder = "JW_Player_5.0.753";            // user settable
var plx_url_parms = new Object();               // see x_get_url_parms()

/*                                                                    */
/*     Global variables that are used internally by this library      */
/*                                                                    */
var plx_log;                                              // see x_log()
var plx_log_to_browser = false;                           // see x_log()
var plx_peek_window;                                     // see x_peek()
var plx_popup_number = 0;                               // see x_popup()
var plx_parse_source_caption;                    // see x_parse_source()
var plx_parse_source_width;                      // see x_parse_source()
var plx_parse_source_height;                     // see x_parse_source()
var plx_parse_source_ext;                        // see x_parse_source()
var plx_resources_used = new Array();                  // see x_*used*()

/*                                                                    */
/*                  Global variables that are set by                  */
/*             similarly named global accessor functions              */
/*                                                                    */
var plx_browser;
var plx_browser_version;
var plx_domain;
var plx_email;
var plx_html;
var plx_os;
var plx_site;
var plx_user_agent = navigator.userAgent.toUpperCase();

/*                                                                    */
/*                     Global Accessor Functions                      */
/*                                                                    */
function x_hover_style              (a_new_val) {var old_val = plx_hover_style              ; plx_hover_style                  = a_new_val; return old_val;}
function x_link_icon                (a_new_val) {var old_val = plx_link_icon                ; plx_link_icon                    = a_new_val; return old_val;}
function x_media_frame_color        (a_new_val) {var old_val = plx_media_frame_color        ; plx_media_frame_color         = a_new_val; return old_val;}
function x_media_frame_caption_style(a_new_val) {var old_val = plx_media_frame_caption_style; plx_media_frame_caption_style = a_new_val; return old_val;}
function x_media_frame_width        (a_new_val) {var old_val = plx_media_frame_width        ; plx_media_frame_width         = a_new_val; return old_val;}
function x_media_icon               (a_new_val) {var old_val = plx_media_icon               ; plx_media_icon                = a_new_val; return old_val;}
function x_media_folder             (a_new_val) {var old_val = plx_media_folder             ; plx_media_folder             = a_new_val; return old_val;}
function x_media_next_align_flag    (a_new_val) {var old_val = plx_media_next_align_flag    ; plx_media_next_align_flag     = a_new_val; return old_val;}
function x_player_folder            (a_new_val) {var old_val = plx_player_folder            ; plx_player_folder             = a_new_val; return old_val;}

// function x_get_parm_debug           (a_new_val) {var old_val = plx_get_parm_debug           ; plx_get_parm_debug               = a_new_val; return old_val;}
// function x_hover_width              (a_new_val) {var old_val = plx_hover_width              ; plx_hover_width                  = a_new_val; return old_val;}
// function x_media_border_out         (a_new_val) {var old_val = plx_media_border_out         ; plx_media_border_out          = a_new_val; return old_val;}
// function x_media_border_over        (a_new_val) {var old_val = plx_media_border_over        ; plx_media_border_over         = a_new_val; return old_val;}

/*--------------------------------------------------------------------*/
/*        x_parse_url() global variables and wrapper functions        */
/*--------------------------------------------------------------------*/
var plx_anchor = new String();
var plx_page   = new String();
var plx_parms  = new String();
function x_page()   {x_parse_url(); return plx_page  ;}
function x_anchor() {x_parse_url(); return plx_anchor;}
function x_parms()  {x_parse_url(); return plx_parms ;}

/*--------------------------------------------------------------------*/
/*         x_lookup_link() wrapper functions (see also x_lookup_links()         */
/*--------------------------------------------------------------------*/
function x_abebooks(a_show, a_sarg) {return x_lookup_link(a_show, a_sarg, "http://www.abebooks.com/servlet/SearchResults?tn=");}
function x_alibris (a_show, a_sarg) {return x_lookup_link(a_show, a_sarg, "http://www.alibris.com/booksearch?keyword="       );}
function x_amazon  (a_show, a_sarg) {return x_lookup_link(a_show, a_sarg, "http://www.amazon.com/s?field-keywords="          );}
function x_google  (a_show, a_sarg) {return x_lookup_link(a_show, a_sarg, "http://www.google.com/search?q="                  );}
function x_wiki    (a_show, a_sarg) {return x_lookup_link(a_show, a_sarg, "http://en.wikipedia.org/wiki/"                    );}

/*--------------------------------------------------------------------*/
/*                          Setup hovercraft                          */
/*--------------------------------------------------------------------*/
// Create a <div> for hovercraft:
document.write("<div id='plx_hover_div'");
if (x_browser() == 'IE')
{
    document.write
    (
        " style='filter: "
        + "progid:DXImageTransform.Microsoft.Blinds"
        + "(direction=down, bands=1, duration=0.5) "
        + "progid:DXImageTransform.Microsoft.Blinds"
        + "(direction=up, bands=1, duration=0.5) "
        + "'"
    );
//         + "progid:DXImageTransform.Microsoft.GradientWipe"
//         + "(GradientSize=1.0, wipeStyle=1, motion=forward) "
//         + "progid:DXImageTransform.Microsoft.GradientWipe"
//         + "(GradientSize=1.0, wipeStyle=1, motion=reverse) "
//     + "progid:DXImageTransform.Microsoft.RandomDissolve() "
//     + "progid:DXImageTransform.Microsoft.RandomDissolve()'"
}
document.write("></div>");

//    + "progid:DXImageTransform.Microsoft.Stretch(duration=0.5) "
//    + "progid:DXImageTransform.Microsoft.Stretch(duration=0.5) "
//    + "progid:DXImageTransform.Microsoft.RandomDissolve() "
//    + "progid:DXImageTransform.Microsoft.RandomDissolve() "
//    + "progid:DXImageTransform.Microsoft.Shadow(direction=135) "

// The following works only if the filetype is 'shtml':
//var plx_remote_addr = '<!--#echo var="REMOTE_ADDR"-->';

/*--------------------------------------------------------------------*/
/*                         Start doing stuff                          */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                            $() function                            */
/*                     (inspired by Prototype.js)                     */
/*--------------------------------------------------------------------*/
// function $(a_elem_id)
// {
//     return document.getElementById(a_elem_id);
// }

/*--------------------------------------------------------------------*/
/*                          x_age() function                          */
/*--------------------------------------------------------------------*/
function x_age(a_birthday)
{
x_log("\nx_age(\"" + a_birthday + "\");");
    var today      = x_yyyymmdd();
    var birth_yyyy = a_birthday.substr(0, 4);
    var birth_mmdd = a_birthday.substr(4, 4);
    var today_yyyy = today.substr(0, 4);
    var today_mmdd = today.substr(4, 4);
    var result     = Number(today_yyyy) - Number(birth_yyyy);
    if (Number(today_mmdd) < Number(birth_mmdd)) result--;
x_log("\n       today: \"" + today      + "\"");
x_log("\n  birth_yyyy: \"" + birth_yyyy + "\"");
x_log("\n  birth_mmdd: \"" + birth_mmdd + "\"");
x_log("\n  today_yyyy: \"" + today_yyyy + "\"");
x_log("\n  today_mmdd: \"" + today_mmdd + "\"");
x_log("\n => \"" + result   + "\"");
    return String(result);
}

/*--------------------------------------------------------------------*/
/*                   x_bad_email_address() function                   */
/*--------------------------------------------------------------------*/
function x_bad_email_address(a_object, a_label)
{
// alert("x_bad_email_address(" + a_object.name + "='" + a_object.value + "', '" + a_label + "');");
// alert("typeof " + a_object.name + " = '" + (typeof a_object) + "'.");
    var value = a_object.value;
    var label = new String('your email address');
    var question = "Do you want to use this email address even though there's ";
    if (x_is_not_empty(a_label)) label = a_label;
    if (value == "")
    {
        return x_error_in_form(a_object, "Please enter " + label + ".");
    }
    if (value.indexOf("@") == -1)
    {
        if (!confirm(question + "no at sign ('@') in it?"))
        {
            return x_error_in_form(a_object);
        }
    }
    if (value.indexOf(".") == -1)
    {
        if (!confirm(question + "no period ('.') in it?"))
        {
            return x_error_in_form(a_object);
        }
    }
    var bad_chars = /[^A-Za-z0-9@_\-\.]/;
    var bad_x = value.search(bad_chars);
    if (bad_x > -1)
    {
        var bad_char = value.substr(bad_x, 1);
        if (!confirm(question + "a '" + bad_char
            + "' in position " + (bad_x + 1) + "?"))
        {
            return x_error_in_form(a_object);
        }
    }
    return false;
}

/*--------------------------------------------------------------------*/
/*                       x_bad_hhmm() function                        */
/*--------------------------------------------------------------------*/
function x_bad_hhmm(a_object, a_label, a_value)
{
// alert("x_bad_value(" + a_object + ", '" + a_label + "', '" + a_value + "');");
    if (x_no_object(a_object, a_label)) return true;
    var value = a_object.value;
    if (typeof a_value != 'undefined') value = a_value;
    var value_len = value.length;
    var hh = Math.floor(value.substr( 0, 2));
    var mm = Math.floor(value.substr( 2, 2));
// alert("    value: '" + value     + "'.\n"
//     + "value_len: '" + value_len + "'.\n"
//     + "       hh: '" + hh       + "'.\n"
//     + "       mm: '" + mm       + "'.");
    var err_msg = new String('');
    if (value_len != 2 && value_len != 4)
    {
        err_msg = "HHMM ('" + value + "') must be 2 or 4 digits in length.";
    }
    else if (isNaN(hh) || hh < 0 || hh > 23)
    {
        err_msg = "Invalid hour ('" + value.substr(0, 2) + "') in '" + value + "'.";
    }
    else if (value_len == 4 && (isNaN(mm) || mm < 0 || mm > 59))
    {
        err_msg = "Invalid minute ('" + value.substr(2, 2) + "') in '" + value + "'.";
    }
    if (err_msg != '') return x_error_in_form(a_object, err_msg);
    return false;
}

/*--------------------------------------------------------------------*/
/*                   x_bad_phone_number() function                    */
/*--------------------------------------------------------------------*/
function x_bad_phone_number(a_object, a_label)
{
    var val = a_object.value;
    var len = val.length;
    var chr = '';
    var label = new String('your phone number');
    var question = "Do you want to use this phone number even though ";
    if (x_is_not_empty(a_label)) label = a_label;
// Letters and syntax characters are allowed because
// sometimes people add annotations like "ext." and "cell:".
    var bad_chars = /[^ A-Za-z0-9()\*\-\:\.\/]/;
    var vx = val.search(bad_chars);
    if (vx > -1)
    {
        var bad_char = val.substr(vx, 1);
        if (!confirm(question + "there's a '" + bad_char
            + "' in position " + (vx + 1) + "?"))
        {
            return x_error_in_form(a_object);
        }
    }
    var num_digits = 0;
    for (vx = 0; vx < len; vx++)
    {
        chr = val.substr(vx, 1);
        if (chr >= '0' && chr <= '9') num_digits++;
    }
    if (num_digits < 10)
    {
        var only = 'only ';
        if (num_digits == 0) only = '';
        if (!confirm(question + 'it contains ' + only + x_cardinal(num_digits)
            + ' digit' + x_sing_plur(num_digits, '', 's') + '?'))
        {
            return x_error_in_form(a_object);
        }
    }
    return false;
}

/*--------------------------------------------------------------------*/
/*                       x_bad_range() function                       */
/*--------------------------------------------------------------------*/
function x_bad_range(a_object, a_label, a_min, a_max)
{
    if (x_no_input(a_object, a_label)) return true;
    if (isNaN(a_object.value) || a_object.value < a_min || a_object.value > a_max)
    {
        return x_error_in_form(a_object, "Please enter " + a_label
            + " between " + a_min + " and " + a_max + ".");
    }
    return false;
}

/*--------------------------------------------------------------------*/
/*                     x_bad_timeslot() function                      */
/*--------------------------------------------------------------------*/
function x_bad_timeslot(a_object, a_label)
{
    if (x_no_object(a_object, "The " + a_label)) return true;
    var timeslot = a_object.value;
    var pieces = timeslot.split('-');
    var yyyymmdd  = pieces[0];
    var hhmm_from = pieces[1];
    var hhmm_to   = pieces[2];
    if (x_bad_yyyymmdd(a_object, "The " + a_label, pieces[0])) return true;
    if (typeof hhmm_from != 'undefined' &&
        x_bad_hhmm(a_object, "The 'from' component ('hhmm') of the " + a_label, hhmm_from)) return true;
    if (typeof hhmm_to != 'undefined' &&
        x_bad_hhmm(a_object, "The 'to' component ('hhmm') of the " + a_label, hhmm_to)) return true;
    return false;
}

/*--------------------------------------------------------------------*/
/*                     x_bad_yyyymmdd() function                      */
/*--------------------------------------------------------------------*/
function x_bad_yyyymmdd(a_object, a_label, a_value)
{
// alert("x_bad_yyyymmdd(" + a_object + ", '" + a_label + "');");
    if (x_no_object(a_object, a_label)) return true;
    var yyyymmdd = a_object.value;
    if (typeof a_value != 'undefined') yyyymmdd = a_value;
    var yyyymmdd_len = yyyymmdd.length;
    var yyyy = Math.floor(yyyymmdd.substr( 0, 4));
    var mm   = Math.floor(yyyymmdd.substr( 4, 2));
    var dd   = Math.floor(yyyymmdd.substr( 6, 2));
// alert(a_label + ":\n"
//     + "yyyymmdd: '" + yyyymmdd + "'.\n"
//     + "yyyymmdd_len: '" + yyyymmdd_len + "'.\n"
//     + "yyyy: '" + yyyy     + "'.\n"
//     + "  mm: '" + mm       + "'.\n"
//     + "  dd: '" + dd       + "'.");
    var err_msg = new String('');
    if (yyyymmdd_len != 4 && yyyymmdd_len != 6 && yyyymmdd_len != 8)
    {
        err_msg = a_label + " must be 4, 6 or 8 digits in length.";
    }
    else if (isNaN(yyyy))
    {
        err_msg = "Invalid year ('" + yyyymmdd.substr(0, 4) + "') in '" + yyyymmdd + "'.";
    }
    else if (yyyymmdd_len >= 6 && (isNaN(mm) || mm < 1 || mm > 12))
    {
        err_msg = "Invalid month ('" + yyyymmdd.substr(4, 2) + "') in '" + yyyymmdd + "'.";
    }
    else if (yyyymmdd_len == 8 && (isNaN(dd) || dd < 1 || dd > 31))
    {
        err_msg = "Invalid day ('" + yyyymmdd.substr(6, 2) + "') in '" + yyyymmdd + "'.";
    }
    if (err_msg != '')
    {
        return x_error_in_form(a_object, err_msg);
    }
    return false;
}

/*--------------------------------------------------------------------*/
/*                        x_browser() function                        */
/*--------------------------------------------------------------------*/
function x_browser()
{
    if (typeof plx_browser != 'undefined') return plx_browser;
    plx_browser = 'Other';
    if (x_html() >= 4.0)
    {
//x_log_exp('navigator.appName', navigator.appName);
        if  (navigator.appName == 'Microsoft Internet Explorer')
        {
            plx_browser = 'IE';                 // IE 4.0 or greater
        }
        else if (navigator.appName == 'Netscape')
        {
            if (plx_user_agent.indexOf("FIREFOX") != -1)
            {
                plx_browser = 'FF'; // Mozilla Firefox 4.0 or greater
            }
            else
            {
                plx_browser = 'NN'; // Netscape Navigator 4.0 or greater
            }
        }
    }
//  x_log("\n\nx_browser() => '" + plx_browser + "'.");
    return plx_browser;
}

/*--------------------------------------------------------------------*/
/*                    x_browser_version() function                    */
/*--------------------------------------------------------------------*/
function x_browser_version()
{
    if (typeof plx_browser_version != 'undefined') return plx_browser_version;
    var browser = x_browser();
//x_log_exp('navigator.userAgent', navigator.userAgent);
    if  (browser == 'IE')
    {
/*                                                                    */
/*                         Index past "MSIE"                          */
/*                                                                    */
        if ((vx = plx_user_agent.indexOf("MSIE")) != -1)
        {
            plx_browser_version = parseFloat
            (
                plx_user_agent.substring
                (
                    vx + 5
//                  , plx_user_agent.indexOf(";", vx)
                )
            );
        }
    }
    else if (browser == 'NN')
    {
/*                                                                    */
/*                       Point past "Mozilla/"                        */
/*                                                                    */
        plx_browser_version = parseFloat(plx_user_agent.substring(8));
        if (navigator.appVersion.toLowerCase().indexOf("netscape6") != -1)
        {
            plx_browser_version = 6.0;
        }
    }
    else if (browser == 'FF')
    {
/*                                                                    */
/*                       Index past "Firefox/"                        */
/*                                                                    */
        if ((vx = plx_user_agent.indexOf("FIREFOX/")) != -1)
        {
// alert("vx = '" + vx + "'.");
            plx_browser_version = parseFloat
            (
                plx_user_agent.substring
                (
                    vx + 8
//                  , plx_user_agent.indexOf(";", vx)
                )
            );
// alert("plx_browser_version = '" + plx_browser_version + "'.");
        }
    }
//  x_log("\n\nx_browser_version() => '" + plx_browser_version + "'.");
    return plx_browser_version;
}

/*--------------------------------------------------------------------*/
/*                       x_cardinal() function                        */
/*--------------------------------------------------------------------*/
function x_cardinal(a_integer)
{
    var result = a_integer;
    var cardinal = new Array
    (
        'no', 'one', 'two', 'three', 'four',
        'five', 'six', 'seven', 'eight', 'nine'
    );
    if (a_integer < cardinal.length) result = cardinal[a_integer];
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_code() function                          */
/*--------------------------------------------------------------------*/
function x_code()
{
    return "<table align=center style='color: black;'><tr><td><pre><b>";
}

/*--------------------------------------------------------------------*/
/*                       x_code_end() function                        */
/*--------------------------------------------------------------------*/
function x_code_end()
{
    return "</b></pre></td></tr></table>";
}

/*--------------------------------------------------------------------*/
/*                         x_deal() function                          */
/*                                                                    */
/*   Given a deck of a_deck cards, shuffle them and return the top    */
/*   a_deal cards. The cards are returned in the form of integers in  */
/*   the range 0 to a_deal - 1. If you want to repeat a particular    */
/*   pattern, supply a_seed.                                          */
/*                                                                    */
/*   Since "cards" are dealt, no duplicates are returned.             */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_deal(a_deal, a_deck, a_seed)
{
    var i_deal = a_deal;                            // num cards to deal
    var i_deck = a_deck;                            // num cards in deck
    var i_seed = a_seed;                                 // shuffle seed

x_log("\nx_deal(" + i_deal + ", " + i_deck + ", " + i_seed + ");");

    var result = new Array();
    var i_prior_prng, i_dx, i_dx2, i_rand, i_dup, i_num_dupes, i_card;

    if (typeof i_seed != 'undefined')
    {
x_log("\nSeed '" + i_seed + "' was supplied.");
        i_prior_prng = Math.random;         // Remember the current prng
// x_log("\ni_prior_prng = \"" + i_prior_prng + "\"");
        Math.seedrandom(i_seed);   // Create new prng with explicit seed
    }
    else
    {
x_log("\nSeed was NOT supplied.");
    }
    i_num_dupes = 0;
    for (i_dx = 0; i_dx < i_deal; i_dx++)
    {
        result[i_dx] = -1;
        while(result[i_dx] == -1)
        {
            i_rand = Math.random();
            i_card = Math.floor(i_rand * i_deck);
            i_dup = '';
            for (i_dx2 = 0; i_dx2 < i_dx; i_dx2++)
            {
                if (result[i_dx2] == i_card)
                {
                    i_dup = 1;
                }
            }
// x_log("\ni_rand " + i_rand + " => i_card " + i_card);
            if (i_dup)
            {
// x_log(" (DUPLICATE!)");
                i_num_dupes++;
            }
            else
            {
                result[i_dx] = i_card;
                break;
            }
        }
    }
    if (typeof i_seed != 'undefined')
    {
        Math.random = i_prior_prng;    // Resume the prior prng sequence
    }
x_log("\ni_num_dupes: " + i_num_dupes);
x_log("\n => (" + result.join(', ') + ")");
    return result;
}

/*--------------------------------------------------------------------*/
/*                  x_difference_in_days() function                   */
/*--------------------------------------------------------------------*/
// The value of any Date object is the time since 1/1/1970 in
// milliseconds. One day contains 1000 * 60 * 60 * 24, or 86,400,000
// milliseconds. 864E5 is equivalent to 86400000 in exponent notation.
// x = mydate and x = mydate.getTime() are equivalent.
function x_difference_in_days(a_date_1, a_date_2)
{
// alert("x_difference_in_days();");
    var result = Math.floor((a_date_2 - a_date_1) / 864e5);
// alert("x_difference_in_days(" + a_date_1.getTime() + ", " + a_date_2.getTime() + ");"
// + "\nThe difference in days between " + a_date_1 + " and " + a_date_2 + " is " + result + ".");
    return result;
}

/*--------------------------------------------------------------------*/
/*                        x_domain() function                         */
/*--------------------------------------------------------------------*/
function x_domain()
{
    if (typeof plx_domain == 'undefined')
    {
        plx_domain = location.host;
        var www = plx_domain.indexOf('www.');
        if (www == 0) plx_domain = plx_domain.substr(4);
x_log("\nx_domain() => '" + plx_domain + "'.");
    }
    return plx_domain;
}

/*--------------------------------------------------------------------*/
/*                         x_dots() function                          */
/*--------------------------------------------------------------------*/
function x_dots(a_count)
{
    var count = a_count;
    var result = new String('.');
    var cx;
    for (cx = 0; cx < count - 1; cx++) result += "&nbsp;.";
    return result;
}

/*--------------------------------------------------------------------*/
/*                        x_dropcap() function                        */
/*--------------------------------------------------------------------*/
function x_dropcap(a_letter)
{
    var result = "<table align='left'><tr><td style='"
//         + "border: 1px solid black; "
//         + "background-color: FFFFFF; "
        + "font-family: Arial Black; "
//         + "font-family: Rockwell Extra Bold; "
//         + "font-family: Franklin Gothic Heavy; "
        + "padding-top: 4; "
        + "padding-left: 4; "
        + "padding-right: 4; "
        + "padding-bottom: 0; "
        + "font-size: 300%; "
        + "line-height: .9; "
        + "'>" + a_letter + "</td></tr></table>";
// alert(result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_email() function                         */
/*--------------------------------------------------------------------*/
function x_email(a_name, a_mailbox, a_domain, a_parms)
{
// x_email() with no parms returns the default email for this site,
// otherwise it returns a "<a href='mailto:...'>...</a>" tag
    if (typeof a_name == 'undefined')
    {
        if (typeof plx_email == 'undefined')
        {
            if (x_site() == "DHD")
            {
                plx_email = new String(x_yyyy() + '@hannotte.com');
            }
            else if (x_site() == "PRC")
            {
                plx_email = new String(x_yyyy() + '@rosenfels.org');
            }
            else if (x_site() == "NSC")
            {
                plx_email = new String(x_yyyy() + '@rosenfels.org');
            }
            else
            {
                plx_email = new String('dh' + x_yyyy() + '@nyc.rr.com');
            }
x_log("\nx_email() => '" + plx_email + "'.");
        }
        return plx_email;
    }
// a_mailbox is required
    var result = new String();
    var domain = x_domain();
    if (x_is_not_empty(a_domain)) domain = a_domain;
    var e_addr = a_mailbox + "@" + domain;
    var name   = new String(e_addr);
    var parms  = new String('');
    if (x_is_not_empty(a_name))  name  = a_name;
    if (x_is_not_empty(a_parms)) parms = '?' + a_parms;
    var result = "<a href='mailto:" + e_addr + parms + "'>"
        + "<b>" + name + "</b></a>";
x_log("\nx_email('" + a_name
+ "', '" + a_mailbox
+ "', '" + a_domain
+ "', '" + a_parms
+ "')\n => \"" + result + "\"");
// x_log("\nx_email() => '" + result + "'.");
    return result;
}

/*--------------------------------------------------------------------*/
/*                      x_email_href() function                       */
/*--------------------------------------------------------------------*/
function x_email_href()
{
    return new String("mailto:" + x_email()
        + '?subject=(Sent from "' + x_page() + '")');
//         + '?subject=(Sent from "' + location + '")');
}

/*--------------------------------------------------------------------*/
/*                      x_email_link() function                       */
/*--------------------------------------------------------------------*/
function x_email_link(a_anchor)
{
    var anchor = a_anchor;
    if (typeof a_anchor == 'undefined') anchor = x_email();
// alert("anchor: '" + anchor + "'");
    return new String('<a href="' + x_email_href()
        + '">' + anchor + '</a>');
}

/*--------------------------------------------------------------------*/
/*                         x_error() function                         */
/*--------------------------------------------------------------------*/
function x_error(a_message)
{
x_log("\nx_error('" + a_message + "'");
    x_write("\n<br><big><b>" + a_message + "</b></big>\n");
    return a_message;
}

/*--------------------------------------------------------------------*/
/*                     x_error_in_form() function                     */
/*--------------------------------------------------------------------*/
function x_error_in_form(a_object, a_message)
{
    if (typeof a_object == 'object')
    {
        if ("focus"  in a_object) a_object.focus();
        if ("select" in a_object) a_object.select();
    }
    if (typeof a_message != 'undefined')
    {
        if (a_message != '')
        {
            x_log("\nx_error_in_form(): " + a_message);
            alert(a_message);
        }
    }
    return true;
}

/*--------------------------------------------------------------------*/
/*                 x_expand_caption_codes() function                  */
/*                                                                    */
/*     This function converts "caption codes" to HTML. All caption    */
/* codes are within paired brackets:                                  */
/*                                                                    */
/*                  This            Becomes That                      */
/*                  -------         ------------                      */
/*                  [b]             <b>                               */
/*                  [i]             <i>                               */
/*                  [-b]            </b>                              */
/*                  [-i]            </i>                              */
/*                  [br]            <br>                              */
/*                  [apost]         &#039;                            */
/*                  [quote]         &#034;                            */
/*                  [123]           &#123;                            */
/*                  [xffff]         &#xffff;                          */
/*                                                                    */
/*--------------------------------------------------------------------*/
// function x_expand_caption_codes(a_caption)
// {
//     var result = new String(a_caption);
//     result = result.replace(/_/g, ' ');
//     result = result.replace(/\[b\]/gi, "<b>");
//     result = result.replace(/\[i\]/gi, "<i>");
//     result = result.replace(/\[-b\]/gi, "</b>");
//     result = result.replace(/\[-i\]/gi, "</i>");
//     result = result.replace(/\[br\]/gi, "<br>");
//     result = result.replace(/\[apost\]/gi, "&#039;");
//     result = result.replace(/\[quote\]/gi, "&#034;");
//     result = result.replace(/\[(\d{2,3})\]/g, "&#$1;");
//     result = result.replace(/\[x([\dabcdef]{2,4})\]/gi, "&#x$1;");
//
//     if (result != a_caption)
//     {
// x_log("\nx_expand_caption_codes(\"" + a_caption + "\")"
//     + "\n                    => \"" + result    + "\"");
//     }
//     return result;
// }

/*--------------------------------------------------------------------*/
/*                                                                    */
/*                 x_expand_caption_codes() function                  */
/*                                                                    */
/*     This function converts "caption codes" to HTML. Caption codes  */
/* are defined as anything within paired brackets. They are designed  */
/* to exist peacefully in Windows filenames and so avoid characters   */
/* that are illegal in such a context.                                */
/*                                                                    */
/*     Codes ending with '-' are starting HTML tags. Thus "[b-]"      */
/* becomes "<b>".                                                     */
/*                                                                    */
/*     Codes starting with '-' are ending HTML tags. Thus "[-b]"      */
/* becomes "</b>".                                                    */
/*                                                                    */
/*     Codes that are 2 or 3 digits are byte values in decimal.       */
/* Thus "[039]" becomes "&#039;".                                     */
/*                                                                    */
/*     Codes that are 2 or 4 hex digits preceeded by a single         */
/* 'x' are byte values in hex. Thus "[xffff]" becomes "&#xffff;".     */
/*                                                                    */
/*     The following codes become entities:                           */
/*                                                                    */
/*         CODE                ENTITY                                 */
/*         ----                ------                                 */
/*         [quot]              &quot;                                 */
/*                                                                    */
/*     NOTE: Things in brackets should not automatically be converted */
/* into entities. "[this is a comment]", for example, should NOT      */
/* become "&this is a comment;"                                       */
/*                                                                    */
/*     We also replace all underscores with space characters.         */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_expand_caption_codes(a_caption)
{
    var result = new String(a_caption);
    result = result.replace(/\[([^\[]*?)-\]/gi, "<$1>");
    result = result.replace(/\[-([^\[]*?)\]/gi, "<\/$1>");
    result = result.replace(/\[(\d{2,3})\]/g, "&#$1;");
    result = result.replace(/\[x([\dabcdef]{2,4})\]/gi, "&#x$1;");
//     result = result.replace(/\[(.*?)\]/g, "&$1;"); <-- too broad!
    result = result.replace(/\[quot\]/gi, "&quot;");
    result = result.replace(/_/g, " ");
    if (result != a_caption)
    {
x_log("\nx_expand_caption_codes(\"" + a_caption + "\")"
    + "\n                    => \"" + result    + "\"");
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*              x_expand_caption_codes_tester() function              */
/*--------------------------------------------------------------------*/
function x_expand_caption_codes_tester()
{
    x_write("<center>"
    + x_expand_caption_codes
      (
            "[97][98][99]"
       + " - [big-][039][120][121][122][039][-big]"
       + " - [quot][x61][x62][x63][quot]"
       + " - [x0078][x0079][x007a]"
       + " - [copyright 2010]"
       )
    + "</center>");
}

/*--------------------------------------------------------------------*/
/*                        x_folder() function                         */
/*--------------------------------------------------------------------*/
function x_folder(a_date)
{
    var result = location.pathname;
    result = result.substr(1, result.lastIndexOf('/') - 1);
x_log("\nx_folder() => \"" + result + "\".");
    return result
}

/*--------------------------------------------------------------------*/
/*                      x_format_date() function                      */
/*--------------------------------------------------------------------*/
function x_format_date(a_date)
{
    var months = ["January", "February", "March",
        "April", "May", "June", "July", "August",
        "September", "October", "November", "December"];
    var i_date = new Date();
    if (typeof a_date != 'undefined') i_date = a_date;
    return months[i_date.getMonth()] + " "
        + i_date.getDate() + ", "
        + i_date.getFullYear();
}

/*--------------------------------------------------------------------*/
/*                     x_format_dates() function                      */
/*--------------------------------------------------------------------*/
function x_format_dates
(
    a_num_events, a_month_interval, a_ordinal, a_dow, a_month, a_year
)
{
    var result = new String();
    var this_date = new Date();
    for (nex = 0; nex < a_num_events; nex++)
    {
        this_date = x_get_rel_date(a_ordinal, a_dow, a_month, a_year);
        result += x_format_date(this_date);
        if (nex  < a_num_events - 2) result += ", ";
        if (nex == a_num_events - 2) result += " and ";
        a_month += a_month_interval;
        if (a_month > 12)
        {
            a_month -= 12;
            a_year++;
        }
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*                    x_format_yyyymmdd() function                    */
/*--------------------------------------------------------------------*/
function x_format_yyyymmdd(a_date)
{
    var result = new String('');
x_log("\nx_format_yyyymmdd(" + a_date + ")");
    var date = new String(a_date);
    while (date.length < 8) date += '0';
    var yyyy = date.substr(0, 4) * 1;                          // numify
    var mm   = date.substr(4, 2) * 1;                          // numify
    var dd   = date.substr(6, 2) * 1;                          // numify
x_log("\n => yyyy, mm, dd: '" + yyyy + "', '" + mm + "', '" + dd);
    var months = ['', 'January', 'February', 'March', 'April', 'May', 'June',
        'July', 'August', 'September', 'October', 'November', 'December'];
    if (mm > 0)           result += months[mm];
    if (mm > 0 && dd > 0) result += " " + dd;
    if (mm > 0)           result += ", ";
    result += yyyy;
x_log(" => '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                 x_format_yyyymmddhhmmss() function                 */
/*--------------------------------------------------------------------*/
function x_format_yyyymmddhhmmss(a_time)
{
    var result = new String('');
x_log("\nx_format_yyyymmddhhmmss(" + a_time + ")");
    var yyyy = a_time.substr(0, 4);
    var mm   = a_time.substr(4, 2);
    var dd   = a_time.substr(6, 2);
    var hh   = a_time.substr(8, 2);
    var mi   = a_time.substr(10, 2);
    var ss   = a_time.substr(12, 2);
// x_log("\nyyyy, mm, dd: " + yyyy + ", " + mm + ", " + dd);
    result = yyyy + "/" + mm + "/" + dd + " " + hh + ":" + mi + ":" + ss;
x_log(" => '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                       x_get_parm() function                        */
/*                                                                    */
/*     This function extracts one "parm" from the input string you    */
/* pass to it as the first parameter. The parm can be one of two      */
/* types: either it can be one of several flags, or it can follow a   */
/* keyword. The second parameter indicates how to find this parm      */
/* inside the string:                                                 */
/*                                                                    */
/*     --- An array such as ["-left", "-right", "-center"] is seen    */
/*         as the list of flags.                                      */
/*                                                                    */
/*     --- A bare string such as "length=" or "genre:" is seen as     */
/*         the keyword preceeding the parm and enables parms to       */
/*         have any value, including an empty string.                 */
/*                                                                    */
/*     Parms (and the optional keywords that may precede them) begin  */
/* either at the start of the input field or at the next non-blank    */
/* after the previous parm. They end with the next blank or the end   */
/* of the input field. To make it easier to isolate the parms in the  */
/* input string, we pad both sides of the input field with one        */
/* blank.                                                             */
/*                                                                    */
/*     If the value after a keyword begins with a quotation mark or   */
/* an apostrophe then it must end with the next occurrence of that    */
/* same character instead of the next blank. This allows parms to     */
/* contain blanks and either apostrophes or quotation marks.          */
/*                                                                    */
/*     If the parm you're looking for isn't found, the value returned */
/* is undefined. This makes it simple to test for the presence        */
/* of a parm by coding:                                               */
/*                                                                    */
/*         hidden_flag = x_get_parm(input, ['-hidden']);              */
/*         if (typeof hidden_flag == 'undefined') {display()}         */
/*                                                                    */
/*     You can overide the default value returned by using the third  */
/* parameter to the x_get_parm() function.                            */
/*                                                                    */
/*     You can indicate that diagnostic messages be logged by         */
/* specifying a fourth parameter of 1.                                */
/*                                                                    */
/*     The documentation and logic of the x_get_parm() Perl function  */
/* in "Plx.pm" is identical to those of the x_get_parm() Javascript   */
/* function in "plx.js".                                              */
/*                                                                    */
/*--------------------------------------------------------------------*/
/* WARNING: I learned a lot about strings from writing this function. */
/*          Observe the following experimental results:               */
/*                                                                    */
/*                     literal typeof   instanceof                    */
/*          ------------------ -------- ----------                    */
/*                           3 'number'                               */
/*                      'word' 'string'                               */
/*              String('word') 'string' Object                        */
/*          new String('word') 'object' String                        */
/*--------------------------------------------------------------------*/
// alert(
// "                 3: " + (typeof                  3) + ", instanceof Object: " + (                 3 instanceof Object) + "\n" +
// "            'word': " + (typeof             'word') + ", instanceof Object: " + (            'word' instanceof Object) + "\n" +
// "    String('word'): " + (typeof     String('word')) + ", instanceof Object: " + (    Object('word') instanceof Object) + "\n" +
// "new String('word'): " + (typeof new String('word')) + ", instanceof String: " + (new String('word') instanceof String)
// );
function x_get_parm(a_input, a_find, a_default)
{
    var db = plx_get_parm_debug;
if (db) x_log("\nx_get_parm(\"" + a_input + "\", \"" + a_find + "\", \"" + a_default + "\")");

    var i_input   = a_input;
    var i_find    = a_find;
    var i_default = null; // undefined
    var result    = "";

if (db) x_log("\n  i_input: \"" + i_input + "\"");
if (db) x_log("\n   i_find: \"" + i_find + "\"");
if (db) x_log("\ni_default: \"" + i_default + "\"");
if (db) x_log("\n   result: \"" + result + "\"");

    if (x_is_not_empty(a_default))
    {
        result = i_default = a_default;
if (db) x_log("\ni_default: '" + i_default + "'");
if (db) x_log("\n   result: '" + result + "'");
    }

    if (x_is_empty(a_input) || x_is_empty(a_find))
    {
if (db) x_log("\n => \"" + result + "\"");
        return result;
    }

// Pad the i_input string to make searching easier:
    i_input = new String(' ' + i_input + ' ');

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*         Is the parm one of a list of pre-defined literals?         */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    if (i_find instanceof Array)
    {
        var fx;
        for (fx = 0; fx < i_find.length; fx++)
        {
            var flag = i_find[fx];
            if (i_input.indexOf(' ' + flag + ' ') > -1)
            {
                result = flag;
                break;
            }
        }
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                  Does the parm follow a keyword?                   */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    else if (typeof i_find == 'string')
    {
// If the parm is enclosed in quotation marks, the next quotation mark
// (rather than the next blank) defines the end of the parameter.
        var fromx, tox, char, delim;
        fromx = i_input.indexOf(' ' + i_find);
        if (fromx > -1)
        {
            fromx += 1 + i_find.length;
            delim = ' ';
            char = i_input.charAt(fromx);
            if (char == '"' || char == "'")
            {
                fromx++;
                delim = char;
            }
            tox = i_input.indexOf(delim, fromx);
            result = i_input.substring(fromx, tox);
        }
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         a_find is invalid                          */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    else
    {
        var funcall = "x_get_parm(\"" + i_input + "\", \"" + a_find
            + "\", \"" + i_default + "\", \"" + db + "\");";
        result =  funcall + " - second parameter "
            + "is neither a String() nor an Array().";
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         Return the result                          */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    if (db)
    {
        x_log("\n => \"" + result + "\"");
    }
    else
    {
        x_log("\nx_get_parm(\"" + a_input + "\", \"" + a_find + "\", \"" + a_default + "\")");
        x_log(" => \"" + result + "\"");
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*                    x_get_parm_tester() function                    */
/*--------------------------------------------------------------------*/
// set x_get_parm_debug to 'true' before using this function:
// function x_get_parm_tester()
// {
//     var input = "a=b c-\"d\" efg:\"Dean Hannotte\" hij='Rachel Bartlett' -center";
//     var undef;
// x_log("\nx_get_parm_tester();");
// x_log("\ninput: \"" + input + "\"");
// x_log("\nundef: \"" + undef + "\"");
//     x_get_parm('', '', '');
//     x_get_parm(input, '');
//     x_get_parm(input, 'a=');
//     x_get_parm(input, 'c-');
//     x_get_parm(input, 'efg:');
//     x_get_parm(input, 'hij=');
//     x_get_parm(input, 'nodefault=');
//     x_get_parm(input, ['-left', '-right'], '-right');
//     x_get_parm(input, ['-left', '-right', '-center'], '-right');
// }
// x_get_parm_tester();

/*--------------------------------------------------------------------*/
/*                     x_get_rel_date() function                      */
/*--------------------------------------------------------------------*/
// If numeric, then ordinal, dow and month begin with '1', not '0':
function x_get_rel_date(a_ordinal, a_dow, a_month, a_year)
{
x_log("\nx_get_rel_date('" + a_ordinal + "', '" + a_dow + "', '" + a_month + "', '" + a_year + "');");
    var ordinals = ["First", "Second", "Third", "Fourth"];
    var dows = ["Sunday", "Monday", "Tuesday",
        "Wednesday", "Thursday", "Friday", "Saturday"];
    var months = ["January", "February", "March",
        "April", "May", "June", "July", "August",
        "September", "October", "November", "December"];
    var ordinal_x = a_ordinal;
    var dow_x     = a_dow;
    var month_x   = a_month;
    if (typeof ordinal_x == 'number')
    {
        ordinal_x--;
    }
    else
    {
        for (ordinal_x = 0; ordinal_x < ordinals.length; ordinal_x++)
        {
            if (a_ordinal == ordinals[ordinal_x]) break;
        }
    }
    if (typeof dow_x == 'number')
    {
        dow_x--;
    }
    else
    {
        for (dow_x = 0; dow_x < dows.length; dow_x++)
        {
            if (a_dow == dows[dow_x]) break;
        }
    }
    if (typeof month_x == 'number')
    {
        month_x--;
    }
    else
    {
        for (month_x = 0; month_x < months.length; month_x++)
        {
            if (a_month == months[month_x]) break;
        }
    }
x_log("\n" + "ordinal_x: " + ordinal_x + ", dow_x: " + dow_x + ", month_x: " + month_x + ", year: " + a_year);
    var result = new Date(a_year, month_x, 1);
x_log("\nFirst day of " + a_month + ", " + a_year + " is " + result);
    var days_till_first_dow_x = dow_x - result.getDay();
    if (days_till_first_dow_x < 0) days_till_first_dow_x += 7;
    result.setDate(result.getDate() + days_till_first_dow_x);
x_log("\nFirst " + a_dow + " of " + a_month + ", " + a_year + " is " + result);
    result.setDate(result.getDate() + (7 * ordinal_x));
x_log("\n" + a_ordinal + " " + a_dow + " of " + a_month + ", " + a_year + " is " + result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_get_url_parms() function                     */
/*                                                                    */
/*     The plx_url_parms object reflects positional and keyword       */
/* parms found in the URL. Positional parameters are associated with  */
/* numeric indexes, and keyword parameters are associated with the    */
/* indexes given in the URL itself. Element 0 will give the number    */
/* of positional parms (as in KEXX). Element 1 will give the first    */
/* positional parm, element 2 the second, etc.                        */
/*                                                                    */
/*     For example, a URL of "a.com?b&c&d&e=f&g=h&i=j" will give you  */
/* a plx_url_parms object of:                                         */
/*                                                                    */
/*                             KEY VALUE                              */
/*                             --- -----                              */
/*                              0    3                                */
/*                              1    b                                */
/*                              2    c                                */
/*                              3    d                                */
/*                              e    f                                */
/*                              g    h                                */
/*                              i    j                                */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_get_url_parms()
{
x_log("\nx_get_url_parms();");
    x_parse_url();
    if (plx_parms == '') return;
    plx_url_parms[0] = 0;
x_log("\nplx_parms: \"" + plx_parms + "\".");
    var parms = plx_parms.substr(1).split('&');
    for (var px = 0; px < parms.length; px++)
    {
// x_log("\nparms[" + px + "]: \"" + parms[px] + "\"");

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                          Positional parm                           */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
        if (parms[px].indexOf('=') == -1)
        {
            plx_url_parms[0]++;
            plx_url_parms[plx_url_parms[0]] = parms[px];
// x_log("\nplx_url_parms[\"" + plx_url_parms[0] + "\"]: \"" + parms[px] + "\".");
        }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                            Keyword parm                            */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
        else
        {
            elems = parms[px].split('=');
            plx_url_parms[elems[0]] = elems[1];
// x_log("\nplx_url_parms[\"" + elems[0] + "\"]: \"" + elems[1] + "\".");
        }
    }
// x_log("\nplx_url_parms[\"0\"]: \"" + plx_url_parms[0] + "\".");
var k; for (k in plx_url_parms) x_log("\nplx_url_parms[\"" + k + "\"]: \"" + plx_url_parms[k] + "\".");
}

/*--------------------------------------------------------------------*/
/*                      x_hover_hide() function                       */
/*--------------------------------------------------------------------*/
function x_hover_hide()
{
    var i_div = document.getElementById('plx_hover_div');
    if (x_browser() == 'IE') i_div.filters[1].Apply();
    i_div.style.visibility = "hidden";
    if (x_browser() == 'IE') i_div.filters[1].Play();
}

/*--------------------------------------------------------------------*/
/*                      x_hover_hint() function                       */
/*                                                                    */
/* When your mouse hovers over a_anchor, a box pops up showing        */
/*     a_hint.                                                        */
/*                                                                    */
/* When your mouse leaves a_anchor, the box disappears.               */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_hover_hint(a_anchor, a_hint, a_top, a_left)
{
x_log("\nx_hover_hint(\"" + a_anchor + "\","
+ "\n \"" + a_hint + "\","
+ "\n " + a_top + ", " + a_left + ");");

    var i_anchor = new String(a_anchor);
    var i_hint   = new String(a_hint);
    var i_top    = a_top;
    var i_left   = a_left;

// An embedded quote in i_hint is tricky. Since i_hint
// is a quoted string, it must be represented as \" to be accepted.
// But once stored in the mouseover event as a plain quote,
// it can then cause a syntax error when the event is triggered.
// It must be escaped two more times. Converting each received
// " and &quot; to "&amp;quot;" seems to work.
    i_hint = i_hint.replace(/"/g, "&quot;");
    i_hint = i_hint.replace(/&quot;/g, "&amp;quot;");

    var result = new String("<span "
        + "style='font-weight: bold; "
        + "border-bottom-width: 1; border-bottom-style: dotted;'"
        + "\nonmouseover='x_hover_show(0, this, "
        + "\"" + plx_hover_style + "\", \""
        + x_javascript_safe(i_hint) + "\", " + i_top + ", " + i_left + ");'"
        + "\nonmouseout='x_hover_hide();'>"
        + "\n" + i_anchor + "</span>");

x_log("\n => \"" + result + "\"");
    return result;
}

/*--------------------------------------------------------------------*/
/*                      x_hover_link() function                       */
/*                                                                    */
/* When your mouse hovers over a_anchor, a box pops up showing        */
/*     a_hint.                                                        */
/*                                                                    */
/* When your mouse leaves a_anchor, the box remains until you         */
/* click on '[close]' in the upper right hand corner.                 */
/*                                                                    */
/* If a_link is given, an extra line appears at the bottom of the     */
/* box containing a link to a_link and saying "Read more . . .".      */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_hover_link(a_anchor, a_link, a_hint, a_top, a_left)
{
x_log("\nx_hover_link(\"" + a_anchor + "\","
+ "\n \"" + a_link + "\","
+ "\n \"" + a_hint + "\","
+ "\n " + a_top + ", " + a_left + ");");

    var i_anchor = new String(a_anchor);
    var i_link   = new String(a_link);
    var i_hint   = new String(a_hint);
    var i_top    = a_top;
    var i_left   = a_left;
//     var a_tag = new Array();

// (See comment above about embedded quotes.)
    i_hint = i_hint.replace(/"/g, "&quot;");
    i_hint = i_hint.replace(/&quot;/g, "&amp;quot;");
    if (i_link != "")
    {
        i_link = new String("<br><a href='" + i_link + "'>"
            + "<b>Read&nbsp;more&nbsp;.&nbsp;.&nbsp;.</b></a>");
    }
    var result = new String("<span"
        + " style='font-weight: bold;"
        + " border-bottom-width: 1; border-bottom-style: dotted;'"
        + "\nonmouseover='x_hover_show(1, this, \""
        + plx_hover_style + "\", \""
        + x_javascript_safe(i_hint + i_link) + "\", "
        + "\n" + i_top + ", " + i_left + ");'>"
        + "\n" + i_anchor + "</span>");
    return result;
}

/*--------------------------------------------------------------------*/
/*                      x_hover_show() function                       */
/*--------------------------------------------------------------------*/
function x_hover_show(a_retain, a_this, a_style, a_hint, a_top, a_left)
{
x_log("\nx_hover_show(" + a_retain + ","
+ "\n" + a_this + ","
+ "\n\"" + a_style + "\","
+ "\n\"" + a_hint + "\","
+ "\n" + a_top + ", " + a_left + ");");
    i_retain = a_retain;
    i_this   = a_this;
    i_style  = new String(a_style);
    i_hint   = new String(a_hint);
    i_top    = a_top;
    i_left   = a_left;

    var i_div = document.getElementById('plx_hover_div');
    i_div.style.position = "absolute";
    i_div.style.visibility = "hidden";

x_log("\ni_this.offsetHeight: " + i_this.offsetHeight);
x_log("\n i_this.offsetWidth: " + i_this.offsetWidth);
x_log("\n   i_this.offsetTop: " + i_this.offsetTop);
x_log("\n  i_this.offsetLeft: " + i_this.offsetLeft);

    var i_hover_width = Math.floor(document.body.clientWidth * plx_hover_width_fraction);
x_log("\ndocument.body.clientWidth: " + document.body.clientWidth);
x_log("\n plx_hover_width_fraction: " + plx_hover_width_fraction);
x_log("\n            i_hover_width: " + i_hover_width);

    var i_div_style_top, i_div_style_left;

// Hovercraft pop up just under the "i_link" anchor, and try to
// align with its left edge. But don't let them get too close
// to the anchor or the left and right edges of the client window.
// (There's no way to determine the bottom edge.)

    var i_min_margin_top = 12;
    var i_min_margin_sides = 64;

    if (typeof i_top != 'undefined')
    {
        i_div_style_top = i_top;
    }
    else
    {
        i_div_style_top = x_top(i_this) + i_this.offsetHeight + i_min_margin_top;
    }

    if (typeof i_left != 'undefined')
    {
        i_div_style_left = i_left;
    }
    else
    {
        i_div_style_left = x_left(i_this);
    }

x_log("\ni_div_style_top: " + i_div_style_top);
x_log("\ni_div_style_left: " + i_div_style_left);

// Once document.body.clientWidth exists, we can figure out if the
// hover box is going past the right edge of the document window.

// The following method of correcting overflow insures that
// the left edge of the hovercraft aligns with the left edge
// of the anchor unless overflow would otherwise occur.

// Set the minimal margin of the hovercraft from
// both the left and right edges of the client window:

    if (i_div_style_left < i_min_margin_sides)
    {
x_log("\nIncreasinging i_div_style_left from " + i_div_style_left);
        i_div_style_left = i_min_margin_sides;
x_log(" to " + i_div_style_left);
    }

    if (i_div_style_left > document.body.clientWidth - (i_hover_width + i_min_margin_sides))
    {
x_log("\nReducing i_div_style_left from " + i_div_style_left);
        i_div_style_left = document.body.clientWidth - (i_hover_width + i_min_margin_sides);
x_log(" to " + i_div_style_left);
    }

    i_div.style.top  = i_div_style_top ;
    i_div.style.left = i_div_style_left;

    i_div.style.padding = 0;
    var i_close_button = '';
    if (i_retain == 1)
    {
        i_close_button = "\n<p style='text-align: right; margin: 0; padding: 0;'>"
            + "<a href='javascript:x_hover_hide();'><big><b>[close]</b></big></a></p><hr>";
    }
    var i_hint = "<table width=" + i_hover_width
        + " cellpadding=0 cellspacing=0>"
        + "<tr><td style='" + i_style + " padding: 4;'>"
        + i_close_button + i_hint + "</td></tr></table>";

x_log("\n       i_div.style.top = '" + i_div.style.top        + "'.");
x_log("\n      i_div.style.left = '" + i_div.style.left       + "'.");
x_log("\n  i_div.style.position = '" + i_div.style.position   + "'.");
x_log("\ni_div.style.visibility = '" + i_div.style.visibility + "'.");
x_log("\n       i_div.innerHTML = '" + i_div.innerHTML        + "'.");

    i_div.innerHTML = i_hint;
    if (x_browser() == 'IE') i_div.filters[0].Apply();
    i_div.style.visibility = "visible";
    if (x_browser() == 'IE') i_div.filters[0].Play();
}

/*--------------------------------------------------------------------*/
/*                         x_html() function                          */
/*--------------------------------------------------------------------*/
function x_html()
{
    if (typeof plx_html != 'undefined') return plx_html;
    plx_html = parseFloat(navigator.appVersion);
    return plx_html;
}

/*--------------------------------------------------------------------*/
/*                  x_is_a_used_resource() function                   */
/*--------------------------------------------------------------------*/
function x_is_a_used_resource(a_resource)
{
    var rx;
    for (rx = 0; rx < plx_resources_used.length; rx++)
    {
        if (a_resource == plx_resources_used[rx])
        {
x_log("\nx_is_a_used_resource('" + a_resource + "') => " + true);
            return true;
        }
    }
x_log("\nx_is_a_used_resource('" + a_resource + "') => " + false);
    return false;
}

/*--------------------------------------------------------------------*/
/*                       x_is_empty() function                        */
/*--------------------------------------------------------------------*/
function x_is_empty(a_text)
{
    var result = typeof a_text == 'undefined' || a_text == '';
// x_log("\nx_is_empty('" + a_text + "') => " + result + ";");
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_is_not_empty() function                      */
/*--------------------------------------------------------------------*/
function x_is_not_empty(a_text)
{
    var result = typeof a_text != 'undefined' && a_text != '';
// x_log("\nx_is_not_empty('" + a_text + "') => " + result + ";");
    return result;
}

/*--------------------------------------------------------------------*/
/*                    x_javascript_safe() function                    */
/*--------------------------------------------------------------------*/
function x_javascript_safe(a_text)
{
    var result = new String(a_text);
    result = result.replace(/"/g, '&#034;');
    result = result.replace(/'/g, '&#039;');
// alert(a_text + "\n\n----------------------------------------------\n\n" + result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_left() function                          */
/*--------------------------------------------------------------------*/
function x_left(a_object)
{
    var result = 0;
    while (a_object.offsetParent)
    {
        result += a_object.offsetLeft
        a_object = a_object.offsetParent;
    }
x_log("\nx_left(" + a_object + ") => " + result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_link() function                          */
/*--------------------------------------------------------------------*/
function x_link(a_anchor, a_href, a_target, a_title)
{
    var anchor = a_anchor;
    var href   = a_href;
    var target = a_target;
    var title  = a_title;

    if (typeof a_anchor == 'undefined' || a_anchor == '') anchor = href;
    if (typeof a_title  == 'undefined' || a_title  == '') title  = href;

    var result = new String('');

    result += "<a";
    if (x_is_not_empty(a_target))
    {
        result += " target=\"" + a_target + "\"";
    }
    if (x_is_not_empty(a_href))
    {
        result += " href=\"" + a_href + "\"";
    }
    result += " title=\"" + title + "\"";
    if (x_is_not_empty(a_title))
    {
        result += " style='border-bottom-width: 1; border-bottom-style: dotted; "
            + "text-decoration: none;'";
    }
    result += ">";
    result += "<b>" + anchor + "</b>";
    if (plx_link_icon != ''
    && (href.substr(0, 7) == 'http://' || href.substr(0, 8) == 'https://'))
    {
        result += " " + plx_link_icon;
    }
    result += "</a>";
x_log("\nx_link(\"" + anchor + "\", \"" + href + "\", \"" + target + "\", \"" + title + "\") => \"" + result + "\"");
    return result;
}

/*--------------------------------------------------------------------*/
/*                   x_link_has_filetype() function                   */
/*--------------------------------------------------------------------*/
function x_link_has_filetype(a_link)
{
    var result = false;
    var link = new String(a_link);
    if (link.indexOf('.') > -1) result = true;
x_log("\nx_link_has_filetype('" + a_link + "') => " + result + ".");
    return result;
}

/*--------------------------------------------------------------------*/
/*                    x_location_folder() function                    */
/*                                                                    */
/*    This function takes a location like                             */
/*                                                                    */
/*        "http://www.paul-rosenfels.org/Haiku.htm"                   */
/*        or "file:///C:/dh/web/PRC/1/HTM/index.htm"                  */
/*                                                                    */
/*    and returns everything but the filename and extension:          */
/*                                                                    */
/*        "http://www.paul-rosenfels.org"                             */
/*        or "file:///C:/dh/web/PRC/1/HTM"                            */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_location_folder()
{
    var result = new String(location);
    var last_slash = result.lastIndexOf('/');
    if (last_slash > -1)
    {
        result = result.substr(0, last_slash);
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*                          x_log() function                          */
/*--------------------------------------------------------------------*/
function x_log(a_message)
{
    if (plx_log_is_enabled)
    {
        if (typeof plx_log == 'undefined')
        {
            plx_log = new String('');
        }
        plx_log += a_message;
        if (plx_log_to_browser)
        {
            a_message.replace(/\n/g, '\n<br>');
            x_write(a_message);
        }
    }
}

/*--------------------------------------------------------------------*/
/*                        x_log_all() function                        */
/*--------------------------------------------------------------------*/
function x_log_all()
{
    x_log
    (
      + "\n"
      + "\n                  document: \"" + document                   + '" (' + typeof document                   + ')'
      + "\ndocument.body.clientHeight: \"" + document.body.clientHeight + '" (' + typeof document.body.clientHeight + ')'
      + "\n document.body.clientWidth: \"" + document.body.clientWidth  + '" (' + typeof document.body.clientWidth  + ')'
      + "\n         document.referrer: \"" + document.referrer          + '" (' + typeof document.referrer          + ')'
      + "\n            document.title: \"" + document.title             + '" (' + typeof document.title             + ')'
      + "\n  document.fileCreatedDate: \"" + document.fileCreatedDate   + '" (' + typeof document.fileCreatedDate   + ')'
      + "\n document.fileModifiedDate: \"" + document.fileModifiedDate  + '" (' + typeof document.fileModifiedDate  + ')'
      + "\n     document.lastModified: \"" + document.lastModified      + '" (' + typeof document.lastModified      + ')'
      + "\n         document.protocol: \"" + document.protocol          + '" (' + typeof document.protocol          + ')'
      + "\n"
      + "\n                  location: \"" + location                   + '" (' + typeof location                   + ')'
      + "\n             location.hash: \"" + location.hash              + '" (' + typeof location.host              + ')'
      + "\n             location.host: \"" + location.host              + '" (' + typeof location.host              + ')'
      + "\n         location.hostname: \"" + location.hostname          + '" (' + typeof location.hostname          + ')'
      + "\n             location.href: \"" + location.href              + '" (' + typeof location.href              + ')'
      + "\n         location.pathname: \"" + location.pathname          + '" (' + typeof location.pathname          + ')'
      + "\n             location.port: \"" + location.port              + '" (' + typeof location.pathname          + ')'
      + "\n         location.protocol: \"" + location.protocol          + '" (' + typeof location.protocol          + ')'
      + "\n           location.search: \"" + location.search            + '" (' + typeof location.search            + ')'
      + "\n"
      + "\n                 navigator: \"" + navigator                  + '" (' + typeof navigator                  + ')'
      + "\n     navigator.appCodeName: \"" + navigator.appCodeName      + '" (' + typeof navigator.appCodeName      + ')'
      + "\n         navigator.appName: \"" + navigator.appName          + '" (' + typeof navigator.appName          + ')'
      + "\n      navigator.appVersion: \"" + navigator.appVersion       + '" (' + typeof navigator.appVersion       + ')'
      + "\n navigator.appMinorVersion: \"" + navigator.appMinorVersion  + '" (' + typeof navigator.appMinorVersion  + ')'
      + "\n   navigator.cookieEnabled: \"" + navigator.cookieEnabled    + '" (' + typeof navigator.cookieEnabled    + ')'
      + "\n        navigator.cpuClass: \"" + navigator.cpuClass         + '" (' + typeof navigator.cpuClass         + ')'
      + "\n          navigator.onLine: \"" + navigator.onLine           + '" (' + typeof navigator.onLine           + ')'
      + "\n        navigator.platform: \"" + navigator.platform         + '" (' + typeof navigator.platform         + ')'
      + "\n       navigator.userAgent: \"" + navigator.userAgent        + '" (' + typeof navigator.userAgent        + ')'
      + "\n navigator.browserLanguage: \"" + navigator.browserLanguage  + '" (' + typeof navigator.browserLanguage  + ')'
      + "\n  navigator.systemLanguage: \"" + navigator.systemLanguage   + '" (' + typeof navigator.systemLanguage   + ')'
      + "\n    navigator.userLanguage: \"" + navigator.userLanguage     + '" (' + typeof navigator.userLanguage     + ')'
      + "\n"
      + "\n window.screen.availHeight: \"" + window.screen.availHeight  + '" (' + typeof window.screen.availHeight  + ')'
      + "\n  window.screen.availWidth: \"" + window.screen.availWidth   + '" (' + typeof window.screen.availWidth   + ')'
      + "\n      window.screen.height: \"" + window.screen.height       + '" (' + typeof window.screen.height       + ')'
      + "\n       window.screen.width: \"" + window.screen.width        + '" (' + typeof window.screen.width        + ')'
      + "\n"
      + "\n                x_anchor(): \"" + x_anchor()                 + '" (' + typeof x_anchor()                 + ')'
      + "\n               x_browser(): \"" + x_browser()                + '" (' + typeof x_browser()                + ')'
      + "\n       x_browser_version(): \"" + x_browser_version()        + '" (' + typeof x_browser_version()        + ')'
      + "\n                 x_email(): \"" + x_email()                  + '" (' + typeof x_email()                  + ')'
      + "\n            x_email_href(): \"" + x_email_href()             + '" (' + typeof x_email_href()             + ')'
      + "\n            x_email_link(): \"" + x_email_link()             + '" (' + typeof x_email_link()             + ')'
      + "\n                x_folder(): \"" + x_folder()                 + '" (' + typeof x_folder()                 + ')'
      + "\n                  x_html(): \"" + x_html()                   + '" (' + typeof x_html()                   + ')'
      + "\n                    x_os(): \"" + x_os()                     + '" (' + typeof x_os()                     + ')'
      + "\n                  x_page(): \"" + x_page()                   + '" (' + typeof x_page()                   + ')'
      + "\n                 x_parms(): \"" + x_parms()                  + '" (' + typeof x_parms()                  + ')'
      + "\n                  x_site(): \"" + x_site()                   + '" (' + typeof x_site()                   + ')'
      + "\n           x_wide_screen(): \"" + x_wide_screen()            + '" (' + typeof x_wide_screen()            + ')'
      + "\n         x_window_height(): \"" + x_window_height()          + '" (' + typeof x_window_height()          + ')'
      + "\n          x_window_width(): \"" + x_window_width()           + '" (' + typeof x_window_width()           + ')'
      + "\n              x_yyyymmdd(): \"" + x_yyyymmdd()               + '" (' + typeof x_yyyymmdd()               + ')'
    );
//    + "\n  plx_hover_width_fraction: \"" + plx_hover_width_fraction   + '" (' + typeof plx_hover_width_fraction   + ')'
//    + "\n           plx_remote_addr: \"" + plx_remote_addr            + '" (' + typeof plx_remote_addr            + ')'
//    + "\n   navigator.javaEnabled(): \"" + navigator.javaEnabled()    + '" (' + typeof navigator.javaEnabled()    + ')'
}

/*--------------------------------------------------------------------*/
/*                      x_log_disable() function                      */
/*--------------------------------------------------------------------*/
function x_log_disable()
{
x_log("\nDISABLING THE LOG ...");
   plx_log_is_enabled = false;
}

/*--------------------------------------------------------------------*/
/*                      x_log_enable() function                       */
/*--------------------------------------------------------------------*/
function x_log_enable()
{
   plx_log_is_enabled = true;
x_log("\nENABLING THE LOG ...");
}

/*--------------------------------------------------------------------*/
/*                        x_log_exp() function                        */
/*--------------------------------------------------------------------*/
function x_log_exp(a_name, a_value)
{
    value = new String(a_value);
    if (typeof a_value != 'number') value = "'" + value + "'";
    x_log("\n" + a_name + " = " + value + " (" + typeof a_value + ").");
}

/*--------------------------------------------------------------------*/
/*                  x_log_resources_used() function                   */
/*--------------------------------------------------------------------*/
function x_log_resources_used()
{
    x_log("\nResources used:");
    var rx;
    for (rx = 0; rx < plx_resources_used.length; rx++)
    {
        x_log("\n " + plx_resources_used[rx]);
    }
}

/*--------------------------------------------------------------------*/
/*                    x_log_to_browser() function                     */
/*--------------------------------------------------------------------*/
function x_log_to_browser(a_new_value)
{
x_log("\nChanging plx_log_to_browser from '" + plx_log_to_browser + "' to '" + a_new_value + "'.");
    var old_value = plx_log_to_browser;
    plx_log_to_browser = a_new_value;
    return old_value;
}

/*--------------------------------------------------------------------*/
/*                      x_lookup_link() function                      */
/*                                                                    */
/*    This function is called by other functions to lookup search     */
/* arguments on abebooks, alibris, amazon, google and wikipedia.      */
/*                                                                    */
/*    a_show is how the subject is to be displayed in HTML.           */
/*                                                                    */
/*    a_sarg is the search argument to pass to the external website   */
/*        in question (defaults to a_show).                           */
/*                                                                    */
/*    a_html is the HTML prefix to a_sarg that tickles the external   */
/*        website to lookup the search argument passed to it.         */
/*                                                                    */
/*    NOTE: a_show and a_sarg can have underscores instead of         */
/*          spaces to prevent invalid breaks in Javascript strings.   */
/*                                                                    */
/*    To surround the search argument in quotes, use "&quot;".        */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_lookup_link(a_show, a_sarg, a_html)
{
    var show = new String(a_show);
    var sarg = new String(a_sarg);
    var html = new String(a_html);
    if (typeof a_sarg == 'undefined') sarg = show;
    var result  = new String("");
    show = show.replace(/_/g, " ");
    sarg = sarg.replace(/_/g, " ");
// x_log("\n              show.substr(0, 6): '" + show.substr(0, 6) + "'");
// x_log("\nshow.substr(show.length - 6, 6): '" + show.substr(show.length - 1, 6) + "'");
    if (show.substr(0, 6)               == "&quot;"
    &&  show.substr(show.length - 6, 6) == "&quot;")
    {
x_log("\nStripping quotes from '" + show + "'");
        show = show.substr(6, show.length - 12);
    }
    result = "<a href=\"" + html + sarg + "\"><b><i>" + show + "</i></b></a>";
x_log("\nx_lookup_link(\"" + show + "\", \"" + sarg + "\", \"" + html + "\")\n => \"" + result + "\"");
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_lookup_links() function                      */
/*                                                                    */
/*    This function is called to lookup subjects on two or more       */
/* of Abebooks, Alibris, Amazon, Google and Wikipedia.                */
/*                                                                    */
/*    a_sources is an array of one or more of the following flags:    */
/*                                                                    */
/*        '-abebooks'                                                 */
/*        '-alibris'                                                  */
/*        '-amazon'                                                   */
/*        '-google'                                                   */
/*        '-wiki'                                                     */
/*                                                                    */
/*    a_show is how the subject is to be displayed in HTML.           */
/*                                                                    */
/*    a_sarg is the subject formatted as a search argument to         */
/*       the external website in question (defaults to a_show).       */
/*                                                                    */
/*    NOTE: a_show and a_sarg can have underscores instead of         */
/*          spaces to prevent invalid breaks in Javascript strings.   */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_lookup_links(a_sources, a_show, a_sarg)
{
x_log("\nx_lookup_links([" + a_sources + "], \"" + a_show + "\", \"" + a_sarg + "\");");
    var i_sarg = a_sarg;
    if (typeof a_sarg == 'undefined') i_sarg = a_sarg;
    var sx, source;
    var result = new String();
    result += "<b><i>" + a_show.replace(/_/g, " ") + "</i></b> (";
    for (sx = 0; sx < a_sources.length; sx++)
    {
        if (sx) result += ", ";
        source = a_sources[sx];
        if (source == '-abebooks') result += x_abebooks('Abebooks'     , a_show, i_sarg);
        if (source == '-alibris' ) result += x_alibris ('Alibris Books', a_show, i_sarg);
        if (source == '-amazon'  ) result += x_amazon  ('Amazon'       , a_show, i_sarg);
        if (source == '-google'  ) result += x_google  ('Google'       , a_show, i_sarg);
        if (source == '-wiki'    ) result += x_wiki    ('Wikipedia'    , a_show, i_sarg);
    }
    result += ")";
x_log("\n => \"" + result + "\"");
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_media() function                         */
/*--------------------------------------------------------------------*/
function x_media
(
    a_type, a_source, a_resize, a_parms, a_caption_override
)
{
x_log("\nx_media('"
+ a_type             + "', '"
+ a_source           + "', '"
+ a_resize           + "', '"
+ a_parms            + "', '"
+ a_caption_override + "');");

    var i_type             = a_type;
    var i_source           = a_source;
    var i_resize           = a_resize;
    var i_parms            = a_parms;
    var i_caption_override = a_caption_override;

    var i_result  = new String('');

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                            Parse i_type                            */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    if (i_type != 'snapshot' && i_type != 'soundbite' && i_type != 'videoclip')
    {
        return x_error("Type parameter, '" + i_type
            + "', illegal in call to x_media().");
    }
    var i_funcname = 'x_' + i_type + '()';

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                           Parse i_source                           */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    if (typeof i_source == 'undefined')
    {
        return x_error("Source parameter missing in call to " + i_funcname + ".");
    }
// Apostrophes break the 'onclick' handler specification when i_resize != 0:
    if (i_source.indexOf("'") > -1)
    {
        return x_error("Apostrophe in " + i_funcname + " source "
            + "parameter is illegal, use \"[039]\" instead:\n \"" + i_source + "\".");
    }

// If i_source begins with http:, it's explicitly external
// If i_source begins with '/', that points to the website root
// Anything else will be prefixed with the value of plx_media_folder
// (if given), followed by i_type ('snapshots', 'soundbites', or videoclips').
    if (i_source.substr(0, 7) != "http://")
    {
        if (i_source.substr(0, 1) == '/')
        {
            i_source = i_source.substr(1);
        }
        else
        {
x_log("\nPrefixing '" + i_type + "s/' to '" + i_source + "'.");
            i_source = i_type + "s/" + i_source;
            if (x_is_not_empty(plx_media_folder))
            {
x_log("\nPrefixing '" + plx_media_folder + "/' to '" + i_source + "'.");
                i_source = plx_media_folder + "/" + i_source;
            }
        }
    }
x_log("\ni_source = '" + i_source + "'.");

    x_parse_source(i_source);
    var i_caption = new String(plx_parse_source_caption);
    var i_width   = plx_parse_source_width;
    var i_height  = plx_parse_source_height;
    var i_ext     = new String(plx_parse_source_ext);

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                    Parse the i_resize parameter                    */
/*                                                                    */
/*          100       specifies the width in pixels                   */
/*          'x100'    specifies the height in pixels                  */
/*          '100x100' specifies the width and height in pixels        */
/*                      (only this format can distort an image)       */
/*          '20%'     specifies the width as a percentage of          */
/*                    document.body.clientWidth                       */
/*          .25       specifies the width as a fraction of            */
/*                      the actual width.                             */
/*                                                                    */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// The 'i_resize' parameter modifies the size of the table cell AND the media footprint.
// The '-noexpand' flag can suppress expansion of the media.
// The '-noshrink' flag can suppress shrinkage of the media.
    var i_resized_width = i_width, i_resized_height = i_height;
// x_log("\ni_resize: " + i_resize + ", i_resized_width: " + i_resized_width + ", i_resized_height: " + i_resized_height);

// x_log("\n    typeof '20%': '" + (typeof      '20%') + "'");
// x_log("\n   typeof 'x100': '" + (typeof     'x100') + "'");
// x_log("\ntypeof '100x100': '" + (typeof  '100x100') + "'");
// x_log("\n      typeof 100: '" + (typeof        100) + "'");
// x_log("\n      typeof .25: '" + (typeof        .25) + "'");

x_log("\ni_resize: '" + i_resize + "'");
x_log("\ntypeof i_resize: '" + (typeof i_resize) + "'");
    if (x_is_not_empty(i_resize))
    {
        if (typeof i_resize == 'string')
        {
            var pos_x = i_resize.indexOf('x');
x_log("\npos_x: " + pos_x);
            if (i_resize.substr(i_resize.length - 1, 1) == '%')
            {               // can be a percent of document.body.clientWidth
                i_resize         = i_resize.substr(0, i_resize.length - 1) / 100;
                i_resized_width  = Math.floor(document.body.clientWidth * i_resize);
                i_resized_height = Math.floor(i_resized_width * i_height / i_width);
x_log("\nResize as percentage of window width, '" + i_resize + "', makes i_resized i_width " + i_resized_width + ", and i_resized i_height " + i_resized_height + ".");
            }
            else if (pos_x == 0)
            {
                i_resized_height = i_resize.substr(1);
                i_resized_width = Math.floor(i_width * i_resized_height / i_height);
x_log("\nResized height, " + i_resized_height + ", makes resized width " + i_resized_width + ".");
            }
            else if (pos_x > 0)
            {
                i_resized_width = i_resize.substr(0, x);
                i_resized_height = i_resize.substr(x + 1);
x_log("\nResize value, '" + i_resize + "', makes resized width " + i_resized_width + " and resized height " + i_resized_height + ".");
            }
            else
            {
                return x_error("Invalid i_resize parm, '" + i_resize
                    + "', in call to " + i_funcname + ": 'When "
                    + "a string it must contain an 'x' or end in '%'.");
            }
        }
        else // if (typeof i_resize == 'number')
        {
            if (i_resize <= 1.0)
            {
                i_resized_width  = Math.floor(i_width  * i_resize);
                i_resized_height = Math.floor(i_height * i_resize);
x_log("\nResize as a fraction of image width, " + i_resize + ", makes resized width " + i_resized_width + " and resized height " + i_resized_height + ".");
            }
            if (i_resize > 1.0)               // or a i_width override in pixels
            {
                i_resized_width  = i_resize;
                i_resized_height = Math.floor(i_resize * i_height / i_width);
x_log("\nResized width, " + i_resized_width + ", makes resized height " + i_resized_height + ".");
            }
        }

// Suppress expansion or shrinkage if requested:
        if (i_expand_flag == '-noexpand')
        {
            if (i_resized_width > i_width || i_resized_height > i_height)
            {
x_log("\nExpansion of the image suppressed.");
                i_resized_width  = i_width;
                i_resized_height = i_height;
            }
        }
        if (i_shrink_flag == '-noshrink')
        {
            if (i_resized_width < i_width || i_resized_height < i_height)
            {
x_log("\nShrinking of the image suppressed.");
                i_resized_width  = i_width;
                i_resized_height = i_height;
            }
        }
    }
// x_log("\ni_resize: '"    + i_resize
// + "', i_resized_width: " + i_resized_width
// + ", i_resized_height: " + i_resized_height);

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                           Parse i_parms                            */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    var i_align_flag = x_get_parm
    (
        i_parms,
        ['-left', '-right', '-center', '-noalign', '-alternate'],
        '-alternate'
    );
    var i_caption_flag = x_get_parm(i_parms, ['-caption', '-nocaption'], '-caption');
    var i_help_flag    = x_get_parm(i_parms, ['-help'   , '-nohelp'   ], '-help'   );
    var i_win_flag     = x_get_parm(i_parms, ['-oldwin' , '-newwin'   ], '-oldwin' );
    var i_expand_flag  = x_get_parm(i_parms, ['-expand' , '-noexpand' ], '-expand' );
    var i_shrink_flag  = x_get_parm(i_parms, ['-shrink' , '-noshrink' ], '-shrink' );

x_log("\ni_align_flag: '" + i_align_flag
+  "', i_caption_flag: '" + i_caption_flag
+     "', i_help_flag: '" + i_help_flag
+      "', i_win_flag: '" + i_win_flag
+   "', i_expand_flag: '" + i_expand_flag
+   "', i_shrink_flag: '" + i_shrink_flag + "'");

// Using 'align=' in <table> is deprecated. In fact, Firefox will ignore
// 'align=center' in <table> if you also use 'style='. So to center
// tables using CSS, use 'margin-left: auto; margin-right: auto;' in
// 'style=' instead of 'align=center' in <table>.

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                          Parse i_caption_override                  */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// A non-empty i_caption_override replaces the
// plx_parse_source_caption and cancels the '-nocaption' flag:
    if (i_caption_flag == '-nocaption') i_caption = '';
    if (x_is_not_empty(i_caption_override)) i_caption = i_caption_override;
x_log("\ni_caption: '" + i_caption + "'");

// Convert "caption codes" to HTML:
    i_caption = x_expand_caption_codes(i_caption);

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                    Let's think about this a bit                    */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
// Convert i_align_flag to i_align_parm and i_float attribute.
// (The float attribute was supposed to prevent containers
// from growing in depth such as in the Kate Middleton picture
// in rosenfels.org/WithRachel.htm, but it didn't work!)
    var i_align_parm = '';
    var i_float      = '';
    if (i_align_flag == '-left' )
    {
        i_align_parm = 'align=left ';
        i_float      = 'float: left;';
    }
    else if (i_align_flag == '-right')
    {
        i_align_parm = 'align=right ';
        i_float      = 'float: right;';
    }
    else if (i_align_flag == '-center' && x_browser() == 'IE')
    {
        i_align_parm = 'align=center ';
    }
    else if (i_align_flag == '-alternate')
    {
x_log("\nplx_media_next_align_flag: '" + plx_media_next_align_flag + "'");
        if (plx_media_next_align_flag == '-right')
        {
            i_align_parm = 'align=right ';
            i_float      = 'float: right;';
        }
        else
        {
            i_align_parm = 'align=left ';
            i_float      = 'float: left;';
        }
    }
x_log("\ni_align_parm: '" + i_align_parm + "'");
x_log("\n   i_float: '" + i_float  + "'");

// Derive margins and next align flag from i_align_parm:
    var i_margin_top    = plx_media_frame_width;
    var i_margin_bottom = plx_media_frame_width;
    var i_margin_left   = plx_media_frame_width;
    var i_margin_right  = plx_media_frame_width;
    if (i_align_flag == '-center')
    {
        i_margin_left   = 'auto';
        i_margin_right  = 'auto';
    }
    var i_hspace = 12;
    if (i_align_parm == 'align=left ')
    {
        i_margin_right += i_hspace;
        x_media_next_align_flag('-right');
x_log("\nplx_media_next_align_flag: '" + plx_media_next_align_flag + "'")
    }
    if (i_align_parm == 'align=right ')
    {
        i_margin_left += i_hspace;
        x_media_next_align_flag('-left');
x_log("\nplx_media_next_align_flag: '" + plx_media_next_align_flag + "'")
    }

    var i_margin = new String
    (
          "margin-left: "   + i_margin_left   + "; "
        + "margin-right: "  + i_margin_right  + "; "
        + "margin-top: "    + i_margin_top    + "; "
        + "margin-bottom: " + i_margin_bottom + ";"
    );

    var i_border_width = plx_media_frame_width;

    var i_border = new String
    (
          "border-width: " + i_border_width + "; "
        + "border-style: solid; "
        + "border-color: " + plx_media_frame_color + ";");

    var i_style = new String(i_margin + " " + i_border + " " + i_float);

// Should the resized source link to its original-size version?
    var i_percent = Math.floor(100 * i_width / i_resized_width);
    var i_handlers = new String('');
    var i_tooltip = new String();
    if (i_type == 'snapshot')
    {
        if (i_percent != 100)
        {
            i_style += " cursor: pointer;";

            i_handlers += "\n onmouseover='";
            if (i_align_flag != '-center')
            {
                i_handlers += "this.style.marginLeft" + " = " + (i_margin_left  - plx_media_frame_width) + "; "
                          + "this.style.marginRight"  + " = " + (i_margin_right - plx_media_frame_width) + "; ";
            }
            i_handlers += "this.style.marginTop"  + " = " + (i_margin_top    - plx_media_frame_width) + "; "
                      + "this.style.marginBottom" + " = " + (i_margin_bottom - plx_media_frame_width) + "; "
                      + "this.style.borderWidth"  + " = " + (i_border_width  + plx_media_frame_width) + ";'";

            i_handlers += "\n onmouseout='";
            if (i_align_flag != '-center')
            {
                i_handlers += "this.style.marginLeft"  + " = " + i_margin_left  + "; "
                            + "this.style.marginRight" + " = " + i_margin_right + "; ";
            }
            i_handlers += "this.style.marginTop"  + " = " + i_margin_top    + "; "
                      + "this.style.marginBottom" + " = " + i_margin_bottom + "; "
                      + "this.style.borderWidth"  + " = " + i_border_width  + ";'";

            if (i_win_flag == '-newwin')
            {
//                 var title = new String(i_caption.replace(/<br>/gi, " "));
//
//                 var popup = new String("<html><head><title>" + title + "</title></head><body>"
//                     + "<table width=\\\"100%\\\" height=\\\"100%\\\">"
//                     + "<tr><td align=\\\"center\\\" valign=\\\"middle\\\">"
//                     + "<img src=\\\"" + i_source + "\\\">"
//                     + "<br><font face=\\\"Verdana\\\"><big><b>"
//                     + title + "</b></big></font></td></tr></table></body></html>");
//
//                 i_handlers += "\n onclick='x_popup(\"\", \"\", "
//                          + Math.floor(i_resized_width  + 50) + ", "
//                          + Math.floor(i_resized_height + 60) + ", \"\", \""
//                          + popup + "\");'";
            }
            else // if (i_win_flag == '-oldwin')
            {
                if (i_type == 'snapshot')
                {
                    i_handlers += "\n onclick='";
                    if (i_align_flag != '-center')
                    {
                        i_handlers += "this.style.marginLeft"  + " = " + i_margin_left  + "; "
                                    + "this.style.marginRight" + " = " + i_margin_right + "; ";
                    }
                    i_handlers += "this.style.marginTop"    + " = " + i_margin_top    + "; "
                                + "this.style.marginBottom" + " = " + i_margin_bottom + "; "
                                + "this.style.borderWidth"  + " = " + i_border_width  + "; ";
                    i_handlers += "location=\"" + i_source + "\";'";
                }
            }

            if (i_help_flag == '-help')
            {
                if (i_percent > 100)
                {
                    i_tooltip = new String("Click to enlarge " + i_percent + "%");
                }
                if (i_percent < 100)
                {
                    i_tooltip = new String("Click to shrink to original size");
                }
            }
        }
    }

    i_style = "\n style='" + i_style + "'";

    if (x_is_not_empty(i_caption))
    {
// How shall we align the i_caption? "center", "left", etc. ?
        var i_align = "center";
        i_caption = "\n<table width='100%' cellspacing=0 cellpadding=4>"
            + "<tr><td align=" + i_align + " style='"
            + plx_media_frame_caption_style + "'>"
            + i_caption;
        i_caption += "</td></tr></table>";
    }

// The image i_width is also used as the cell i_width,
// making the cell wrap tightly around the image.

// The alt= and title= keywords can be used to specify a "tool tip".
// Neither IE nor Firefox ever uses both keywords.
//
//                                     IE uses the    Firefox uses
//                                     value of       the value of
//                                     -----------    ------------
// If only the alt= keyword is used    alt=
// If only the title= keyword is used  title=         title=
// If both keywords are used           title=         title=
//
// Therefor, only the title= keyword need be specified.

    result = "<table border=00 cellpadding=0 cellspacing=0 "
        + "bgcolor='" + plx_media_frame_color + "' "
        + i_align_parm
        + "width=" + i_resized_width
        + i_style
        + i_handlers + ">"
        + "\n<tr><td align='center'>";

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         Format a snapshot                          */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    if (i_type == 'snapshot')
    {
        result += "\n<img"
            + " src=\""   + i_source         + "\""
            + " width="   + i_resized_width
            + " height="  + i_resized_height
            + " title=\"" + i_tooltip        + "\""
            + ">";
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         Format a soundbite                         */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    else if (i_type == 'soundbite')
    {
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         Format a videoclip                         */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    else if (i_type == 'videoclip')
    {
// replace the '.flv' with '.jpg':
        var i_jpg = i_source.substr(0, i_source.length -4) + '.jpg';
//         i_jpg = i_jpg.replace(/videoclips/, "videoclips/snapshots");
x_log("\ni_source: '" + i_source + "'");
x_log("\n   i_jpg: '" + i_jpg + "'");

// JW FLV Player was created by Jeroen "JW" Wijering and his
// company, LongTail Video. (http://www.longtailvideo.com/)
// WARNING: The file= locations are relative to the location of player.swf!

        result += "<table><tr><td>"
            + "\n<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' "
            + "\n id='player' name='player'"
            + "\n width='" + i_resized_width + "'"
            + "\n height='" + i_resized_height + "'> "
            + "\n <param name='movie' value='" + plx_media_folder + "/" + plx_player_folder + "/player.swf' /> "
            + "\n <param name='allowfullscreen' value='true' /> "
            + "\n <param name='allowscriptaccess' value='always' /> "
            + "\n <param name='flashvars' "
            + "\n value='file=../../" + i_source + "&image=" + i_jpg + "' "
            + "\n /> "
            + "\n<embed "
            + "\n type='application/x-shockwave-flash' "
            + "\n id='player2' "
            + "\n name='player2' "
            + "\n src='" + plx_media_folder + "/" + plx_player_folder + "/player.swf' "
            + "\n width='" + i_resized_width + "'"
            + "\n height='" + i_resized_height + "' "
            + "\n allowscriptaccess='always' "
            + "\n allowfullscreen='true' "
            + "\n flashvars='file=../../" + i_source + "&image=" + i_jpg + "' "
            + "\n /> "
            + "\n</object>"
            + "\n</td></tr></table>";
    }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                         Format the caption                         */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
    result += i_caption
        + "\n</td></tr>"
        + "</table>";

x_log("\nx_media() => \"" + result + "\"");

// Don't squeeze out the newlines because we
// need the newline(s) in the <img> title= parm:
//    result = result.replace(/\n/g, '');
    return result;
}

/*--------------------------------------------------------------------*/
/*                       x_no_choice() function                       */
/*                                                                    */
/* NOTE: This function works for radio buttons as well as checkboxes. */
/*       For radio buttons, each button has the same name. Passing    */
/*       that name to this function passes an array of buttons.       */
/*       For checkboxes, each button has a different name, so you     */
/*       must create an array of button names and then pass that      */
/*       array to this function to achieve the same effect.           */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_no_choice(a_objects, a_label)
{
    for (ox = 0; ox < a_objects.length; ox++)
    {
        if (a_objects[ox].checked) return false;
    }
    return x_error_in_form(a_objects[0], "Please choose " + a_label + ".");
}

/*--------------------------------------------------------------------*/
/*                       x_no_input() function                        */
/*--------------------------------------------------------------------*/
function x_no_input(a_object, a_label)
{
    if (a_object.value != '') return false;
    return x_error_in_form(a_object, "Please enter " + a_label + ".");
}

/*--------------------------------------------------------------------*/
/*                       x_no_object() function                       */
/*--------------------------------------------------------------------*/
function x_no_object(a_object, a_label)
{
    if (typeof a_object == 'object') return false;
    return x_error_in_form(a_object, a_label + " is not an Javascript object.");
}

/*--------------------------------------------------------------------*/
/*                     x_no_selection() function                      */
/*--------------------------------------------------------------------*/
function x_no_selection(a_object, a_label)
{
    if (a_object.selectedIndex == -1
    ||  a_object.options[a_object.selectedIndex].value == ''
    ||  a_object.options[a_object.selectedIndex].value == '[not set]') // PHP constant
    {
        return x_error_in_form(a_object, "Please select " + a_label + ".");
    }
    return false;
}

/*--------------------------------------------------------------------*/
/*                        x_ordinal() function                        */
/*--------------------------------------------------------------------*/
function x_ordinal(a_integer)
{
    var result = a_integer;
    var ordinal = new Array
    (
        'zeroeth', 'first', 'second', 'third', 'fourth',
        'fifth', 'sixth', 'seventh', 'eighth', 'ninth'
    );
    if (a_integer < ordinal.length) result = ordinal[a_integer];
    return result;
}

/*--------------------------------------------------------------------*/
/*                          x_os() function                           */
/*--------------------------------------------------------------------*/
function x_os()
{
    if (typeof plx_os != 'undefined') return plx_os;
    plx_os = new String('Windows');
//x_log_exp('plx_user_agent', plx_user_agent);
    if (plx_user_agent.indexOf('MACINTOSH') != -1) plx_os = "MacOS";
    if (plx_user_agent.indexOf('MAC_POWER') != -1) plx_os = "MacOS";
    if (plx_user_agent.indexOf('HP-UX'    ) != -1) plx_os = "HP-UX";
    if (plx_user_agent.indexOf('SUNOS'    ) != -1) plx_os = "SunOS";
    if (plx_user_agent.indexOf('WEBTV'    ) != -1) plx_os = "WebTV";
//  x_log("\nx_os() => '" + plx_os + "'.");
    return plx_os;
}

/*--------------------------------------------------------------------*/
/*                   x_page_is_external() function                    */
/*                                                                    */
/*                                                                    */
/*   This function returns true ONLY if                               */
/*                                                                    */
/*   1) the string you pass it begins with "http://" or "https://",   */
/*                                                                    */
/*   2) whatever is left (barring anything past a possible slash)     */
/*      is not the same as the current host (location.hostname).      */
/*                                                                    */
/*   Note that this will fail to detect that a link such as           */
/*   "google.com" is external, so if you want such links to be        */
/*   recognized as external, pass "http://google.com".                */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_page_is_external(a_new_link)
{
x_log("\nx_page_is_external(\"" + a_new_link + "\");");
    var result       = false;
    var new_link     = new String(a_new_link);
    var new_protocol = new String();
    var new_hostname = new String();
    var slash        = 0;
    if  (new_link.substr(0, 7) == "http://")
    {
        new_protocol = new_link.substr(0, 7);
        new_link     = new_link.substr(7);
    }
    if  (new_link.substr(0, 8) == "https://")
    {
        new_protocol = new_link.substr(0, 8);
        new_link     = new_link.substr(8);
    }
    if (new_protocol != "")
    {
x_log("\nnew_protocol: \"" + new_protocol + "\"");
x_log("\n    new_link: \"" + new_link     + "\"");
        slash = new_link.indexOf("/");
x_log("\n       slash: " + slash);
        if (slash > -1)
        {
            new_hostname = new_link.substr(0, slash);
                new_link = new_link.substr(slash + 1);
            if (new_hostname != location.hostname) result = true;
        }
        else
        {
            new_hostname = new_link;
                new_link = "";
        }
        if (new_hostname != location.hostname) result = true;
x_log("\nnew_hostname: \"" + new_hostname + "\"");
x_log("\n    new_link: \"" + new_link     + "\"");
    }
x_log("\nx_page_is_external(\"" + a_new_link + "\") => " + result + ".");
    return result;
}

/*--------------------------------------------------------------------*/
/*                      x_page_is_pdf() function                      */
/*--------------------------------------------------------------------*/
function x_page_is_pdf(a_link)
{
    var result = false;
    if  (a_link.length >= 4
    &&   a_link.substr(-4, 4).toLowerCase() == ".pdf")
    {
        result = true;
    }
x_log("\nx_page_is_pdf('" + a_link + "') => " + result + ".");
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_parse_source() function                      */
/*--------------------------------------------------------------------*/
// substr(from, [len])
// substring(from, to)
function x_parse_source(a_src)
{
x_log("\nx_parse_source(\"" + a_src + "\");");

    plx_parse_source_caption = new String('');
    plx_parse_source_width   = 0;
    plx_parse_source_height  = 0;
    plx_parse_source_ext    = new String('');

    var src = new String(a_src);
// x_log("\ntypeof src: '" + (typeof src) + "'");
// x_log("\nsrc: '" + src + "'");
    if (src == 'undefined' || src == '')
    {
        plx_parse_source_caption =
            "Source missing in call to x_parse_source('" + src + "');"
        return x_error(plx_parse_source_caption);
    }
    else
    {
//         src = src.replace(/_/g, ' ');
// x_log("\n                     src: '" + src + "'");
        var dot = src.lastIndexOf('.');
// x_log("\n                     dot: '" + dot + "'");
        if (dot <= -1)
        {
            plx_parse_source_caption =
                "Dot missing in filename in call to x_parse_source('" + src + "');"
            return x_error(plx_parse_source_caption);
        }
        else
        {
            plx_parse_source_ext = src.substr(dot);
// x_log("\n   plx_parse_source_ext: '" + plx_parse_source_ext + "'");
            var x = src.lastIndexOf('x', dot);
// x_log("\n                       x: '" + x + "'");
            if (x <= -1)
            {
                plx_parse_source_caption =
                    "'x' missing in filename in call to x_parse_source('" + src + "');"
                return x_error(plx_parse_source_caption);
            }
            else
            {
                plx_parse_source_height = parseInt(src.substring(x + 1, dot));
// x_log("\n plx_parse_source_height: '" + plx_parse_source_height + "'");
                var space = src.lastIndexOf(' ', x);
                if (space == -1) space = src.lastIndexOf('_', x);
// x_log("\n                   space: '" + space + "'");
                if (space <= -1)
                {
                    plx_parse_source_caption = "Neither space nor underscore found before "
                        + "'x' in filename in call to x_parse_source('" + src + "');";
                    return x_error(plx_parse_source_caption);
                }
                else
                {
                    plx_parse_source_width =
                        parseInt(src.substring(space + 1, x));
// x_log("\n  plx_parse_source_width: '" + plx_parse_source_width + "'");
                    plx_parse_source_caption = src.substring(0, space);
// x_log("\nplx_parse_source_caption: '" + plx_parse_source_caption + "'");
                    var slash = plx_parse_source_caption.lastIndexOf('/');
// x_log("\n                   slash: '" + slash + "'");
                    if (slash > -1)
                    {
                        plx_parse_source_caption =
                            plx_parse_source_caption.substr(slash + 1);
                    }
                }
            }
        }
    }
    if (isNaN(plx_parse_source_width))
    {
        plx_parse_source_caption =
            "Width not given in call to x_parse_source('" + a_src + "');"
        return x_error(plx_parse_source_caption);
    }
    if (isNaN(plx_parse_source_height))
    {
        plx_parse_source_caption =
            "Height not given in call to x_parse_source('" + a_src + "');"
        return x_error(plx_parse_source_caption);
    }
x_log("\n plx_parse_source_caption: \"" + plx_parse_source_caption + "\"");
x_log("\n   plx_parse_source_width: "   + plx_parse_source_width         );
x_log("\n  plx_parse_source_height: "   + plx_parse_source_height        );
x_log("\n     plx_parse_source_ext: '"  + plx_parse_source_ext     +  "'");
}

/*--------------------------------------------------------------------*/
/*                       x_parse_url() function                       */
/*                                                                    */
/* If location contains "http://www.a.com/b.htm#c?d", then:           */
/*                                                                    */
/*      plx_page   will contain  "b.htm"                              */
/*      plx_anchor will contain  "#c"                                 */
/*      plx_parms  will contain  "?d"                                 */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_parse_url()
{
x_log("\nx_parse_url();");
    if (plx_page == '' && plx_anchor == '' && plx_parms == '')
    {
x_log("\n         location: '" + location          + "'.");
x_log("\n    location.href: '" + location.href     + "'.");
x_log("\nlocation.protocol: '" + location.protocol + "'.");
x_log("\n    location.host: '" + location.host     + "'.");
x_log("\nlocation.hostname: '" + location.hostname + "'.");
x_log("\nlocation.pathname: '" + location.pathname + "'.");
x_log("\n    location.hash: '" + location.hash     + "'.");
x_log("\n    location.port: '" + location.port     + "'.");
x_log("\n  location.search: '" + location.search   + "'.");

        var pathname  = new String(location.href);
        var poundsign = location.href.lastIndexOf('#');
        var ques_mark = location.href.lastIndexOf('?');
x_log("\n        poundsign: '" + poundsign         + "'.");
x_log("\n        ques_mark: '" + ques_mark         + "'.");

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                    Get plx_anchor and plx_parms                    */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
        if (poundsign > -1 || ques_mark > -1)
        {
            if (poundsign > -1 && ques_mark > -1)
            {
                if (ques_mark > poundsign)
                {
                    pathname   = location.href.substring(0, poundsign);
                    plx_anchor = location.href.substring(poundsign, ques_mark);
                    plx_parms  = location.href.substr(ques_mark);
                }
                else
                {
                    pathname   = location.href.substring(0, ques_mark);
                    plx_parms  = location.href.substring(ques_mark, poundsign);
                    plx_anchor = location.href.substr(poundsign);
                }
            }
            else if (poundsign > -1)
            {
                pathname   = location.href.substring(0, poundsign);
                plx_anchor = location.href.substr(poundsign);
            }
            else
            {
                pathname   = location.href.substring(0, ques_mark);
                plx_parms  = location.href.substr(ques_mark);
            }
        }

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                            Get plx_page                            */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
        pathname      = pathname.replace(/\\/g, "/");
        var backslash = pathname.lastIndexOf('/');
        plx_page      = pathname.substr(backslash + 1);
        if (plx_page == "") plx_page = "index.htm";

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                          Log the results                           */
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
x_log("\n  plx_page: '" + plx_page   + "'.");
x_log("\nplx_anchor: '" + plx_anchor + "'.");
x_log("\n plx_parms: '" + plx_parms  + "'.");
    }
}

/*--------------------------------------------------------------------*/
/*                         x_peek() function                          */
/*--------------------------------------------------------------------*/
function x_peek()
{
    width           = window.screen.width  - 50;
    height          = window.screen.height - 50;
    window_url      = new String('');
    window_name     = new String('plx_peek_window');
    window_features = new String("scrollbars=yes,menubar=no,"
        + "resizable=yes,toolbar=no,"
        + "left=25, top=25, width=" + width + ", height=" + height);
    if (!plx_peek_window || plx_peek_window.closed)
    {
        plx_peek_window = window.open(window_url,
            window_name, window_features);
    }
    setTimeout('x_peek_fill()', 500);
}

/*--------------------------------------------------------------------*/
/*                       x_peek_fill() function                       */
/*--------------------------------------------------------------------*/
function x_peek_fill()
{
    x_log_all();
    var safe_log = new String(plx_log);
    safe_log = new String(safe_log.replace(/&/g, '&amp;'));
    safe_log = new String(safe_log.replace(/</g, '&lt;'));
    plx_peek_window.document.write("<pre>" + safe_log + "</pre>");
}

/*--------------------------------------------------------------------*/
/*                         x_popup() function                         */
/*                                                                    */
/*      Width and height are given in pixels or, if either is 1       */
/* or less, a percentage of the screen width or height.               */
/*                                                                    */
/*      If you want your link to look like a link (i. e. underlined   */
/* and in blue), code:                                                */
/*                                                                    */
/*      <a href="javascript:x_popup('www.google.com');">              */
/*      Popup Google!</a>                                             */
/*                                                                    */
/*      If you want your link to look like normal text where the only */
/* indication that it's a link is the fact that the pointer turns     */
/* into a hand, code:                                                 */
/*                                                                    */
/*      <span onClick="x_popup('www.google.com'">                     */
/*      Popup Google!</span>                                          */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_popup(a_url, a_name, a_width, a_height, a_features, a_content)
{
    var url      = a_url;
    var name     = a_name;
    var width    = a_width;
    var height   = a_height;
    var features = a_features;
    var content  = a_content;
    var left     = 0;
    var top      = 0;
    var result   = new String();

// alert("screen.availWidth  = '" + screen.availWidth  + "',"
//  + "\nscreen.availHeight = '" + screen.availHeight + "'.");
    if (typeof a_url == 'undefined') url = '';
    if (typeof a_name == 'undefined' || a_name == '')
    {
        name = 'plx_popup_' + plx_popup_number++;
//        name = 'plx_popup';
    }

    if (typeof a_width == 'undefined' || a_width == 0) width = .9;
    if (width <= 1.0)
    {
        width = Math.floor(screen.availWidth * width);
    }
    else if (width > screen.availWidth - 100)
    {
        width = screen.availWidth - 100;
    }
    left = Math.floor((screen.availWidth  - width) / 2);

    if (typeof a_height == 'undefined' || a_height == 0) height = .8;
    if (height <= 1.0)
    {
        height = Math.floor( screen.availHeight * height);
    }
    else if (height > screen.availHeight - 100)
    {
        height = screen.availHeight - 100;
    }
    top = Math.floor((screen.availHeight - height) / 2);

    if (typeof a_features == 'undefined' || a_features == '')
    {
        features = new String
        (
              "scrollbars=yes, menubar=no,"
            + "resizable=yes, toolbar=no, "
            + "left=" + left + ", top=" + top + ", "
            + "width=" + width + ", height=" + height
        );
    }
    if (typeof a_content == 'undefined') content = '';

// alert("x_popup('" + a_url + "', '" + a_name + "', "
//     + a_width + ", " + a_height + ", '" + a_features + "');"
//     + '\n\n... returning window.open("' + url
//     + '", "' + name + '", "' + features + '");');

    result = window.open(url, name, features);
    if (content)
    {
        result.document.write(content);
    }
    result.focus();
//  return result;                  // this sometimes caused the current
                                      // window in IE6 to say "[object]"
    return;
}

/*--------------------------------------------------------------------*/
/*                        x_random() function                         */
/*                                                                    */
/*       A random number generator that supports seeding, from        */
/*     http://station.woj.com/2010/02/javascript-random-seed.html     */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*                                                                    */
/*     Note that "seed" is actually a remembered global variable,     */
/*    simply because we don't localize it using the "var" keyword.    */
/*            After the first call, it can never be empty.            */
/*                                                                    */
/*--------------------------------------------------------------------*/

// "JavaScript doesn't have a random seed function, which means you've
// got to roll you own. This little fella works like a charm:
//
// function random(seed) {
//   if (!seed)
//     seed = new Date().getTime();
//   seed = (seed*9301+49297) % 233280;
//   return seed/(233280.0);
// }"

function x_random(seed)
{
    var result;
x_log("\nx_random(" + seed + ")");
    if (!seed) seed = new Date().getTime();
    seed = (seed * 9301 + 49297) % 233280;
    result = seed/(233280.0);
x_log(" => " + result);
    return result;
}
/*--------------------------------------------------------------------*/
/*                       x_raisecap() function                        */
/*--------------------------------------------------------------------*/
function x_raisecap(a_letter)
{
    var result = "<span style='font-size: 280%; line-height: .9;'>"
        + a_letter + "</span>";
// alert(result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                        x_reverse() function                        */
/*--------------------------------------------------------------------*/
function x_reverse(a_string)
{
    var result = String("");
    for (rx = 0; rx < a_string.length; rx++)
    {
        result = a_string.substr(rx, 1) + result;
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*                          x_rgb() function                          */
/*--------------------------------------------------------------------*/
function x_rgb(a_red, a_green, a_blue)
{
    var red   = a_red * 256 * 256;
    var green = a_green * 256;
    var blue  = a_blue;

    red   =   a_red.toString(16).toUpperCase();
    green = a_green.toString(16).toUpperCase();
    blue  =  a_blue.toString(16).toUpperCase();

    if (red.length   == 1) red   = '0' + red;
    if (green.length == 1) green = '0' + green;
    if (blue.length  == 1) blue  = '0' + blue;

    var result = '#' + red + green + blue;
x_log("\nx_rgb(a_red, a_green, a_blue) => '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                       x_scrapbook() function                       */
/*                                                                    */
/*     This function arranges images in rows of uniform height,       */
/* as does Google Images, thus eliminating wasted real estate.        */
/*                                                                    */
/*                                                                    */
/* GOAL:                                                              */
/* 1. All the cells in any row should almost fill the width of the    */
/*    client window.                                                  */
/* 2. Each cell in any row should be the same height to avoid wasted  */
/*    real estate.                                                    */
/*                                                                    */
/*     Thus, the height of each cell should be just that number of    */
/* pixels which will make the width of the row be almost the client   */
/* width.                                                             */
/*                                                                    */
/* EXAMPLE:                                                           */
/*     If a 100x200 cell is followed by a 200x100 cell, the height of */
/* the second cell could be double that of the first, giving a        */
/* uniform height of 200. Then this height could be adjusted so that  */
/* the total width would not be 500, but almost the client width.     */
/*                                                                    */
/* ALGORITHM:                                                         */
/*     Let the uniform height be 100 (an arbitrary number) and find   */
/* the total width of this row. Then, given the actual available      */
/* width, adjust the uniform height accordingly.                      */
/*                                                                    */
/* BUG:                                                               */
/*     This function only knows the dimensions of each photo. Thus,   */
/* if the captions differ in length, the rows can still be uneven in  */
/* height!                                                            */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_scrapbook(a_title, a_rows)
{
x_log("\nx_scrapbook(\"" + a_title + "\");");

    var title  = a_title;
    var rows   = a_rows;
    var result = new String("\n");

    if (typeof title == 'string') result += "\n" + title;

// I'm not sure why, but document.body.clientWidth can
// change a little during the formatting of a scrapbook.
// So we capture it one time and use the same value throughout:
    if (typeof plx_client_width_for_scrapbooks != 'number')
    {
        plx_client_width_for_scrapbooks = document.body.clientWidth;
x_log("\nplx_client_width_for_scrapbooks is being initialized to "
    + plx_client_width_for_scrapbooks + " pixels.");
    }
    else
    {
x_log("\nplx_client_width_for_scrapbooks was already set to "
    + plx_client_width_for_scrapbooks + " pixels.");
    }

// Heuristics (i. e. "guesswork"):
    var AVAILABLE_WINDOW_FRACTION = .9;
    var CELL_BORDER_WIDTH = 24;

    var rx, row, cellx;
    var num_cells;
    var total_width;
    var uniform_height;
    var source  = new String();
    var caption = new String();
    var available_window_width = Math.floor
    (
        plx_client_width_for_scrapbooks * AVAILABLE_WINDOW_FRACTION
    );
x_log("\nThe available window is " + available_window_width + " pixels wide.");

    for (rx = 0; rx < rows.length; rx++)
    {
        result += "\n<table align=center cellspacing=0 cellpadding=0><tr>";

        row = rows[rx];
x_log("\ntypeof row[" + rx + "]: " + typeof row);
        if (row instanceof Array)
        {
            num_cells = row.length / 2;
x_log("\nThere are " + num_cells + " cells in row " + rx + ".");
            var this_available_window_width
                = available_window_width
                - ((num_cells - 1) * CELL_BORDER_WIDTH);
x_log("\nThe available width for this window, given " + num_cells
    + " cells with snapshot frames, is " + this_available_window_width + " pixels.");
            total_width = 0;
            uniform_height = 100;
            for (cellx = 0; cellx < row.length; cellx += 2)
            {
                source  = row[cellx + 0];
                caption = row[cellx + 1];
                x_parse_source(source);
                total_width += Math.floor(plx_parse_source_width
                    * (uniform_height / plx_parse_source_height));
            }
x_log("\nIf the uniform height of this row's cells were 100,");
x_log("\n the total width of this row would be " + total_width + ".");
            resize = 'x' + Math.floor(uniform_height
                * (this_available_window_width / total_width));
x_log("\nTherefor the uniform height has to be resized to " + resize + ".");

            for (cellx = 0; cellx < row.length; cellx += 2)
            {
                source  = row[cellx + 0];
                caption = row[cellx + 1];
                result += "\n<td valign=top>";
                result += x_snapshot(source, resize, '-noalign', caption);
                result += "</td>";
            }
        }
        else
        {
x_log("\n*** Row[" + rx + "] of call to x_scrapbook() is not an Array but rather an '" + typeof row + "' and will be ignored ***");
        }

        result += "</tr></table>";
    }
// alert(result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                       x_sing_plur() function                       */
/*--------------------------------------------------------------------*/
function x_sing_plur(a_integer, a_singular, a_plural)
{
x_log("\nx_sing_plur(" + a_integer + ", \"" + a_singular + "\", \"" + a_plural + "\");");
    if (a_integer == 1) return a_singular;
    return a_plural;
}

/*--------------------------------------------------------------------*/
/*                         x_site() function                          */
/*--------------------------------------------------------------------*/
function x_site()
{
    if (typeof plx_site != 'undefined') return plx_site;
    plx_site = '[unknown]';
    loc = new String(location.href);
    if (location.protocol == 'file:')
    {
        if (loc.indexOf('/CRK/') > -1)
        {
            plx_site = new String("CRK");
        }
        else if (loc.indexOf('/DHD/') > -1)
        {
            plx_site = new String("DHD");
        }
        else if (loc.indexOf('/ESR/') > -1)
        {
            plx_site = new String("ESR");
        }
        else if (loc.indexOf('/NSC/') > -1)
        {
            plx_site = new String("NSC");
        }
        else if (loc.indexOf('/PRC/') > -1)
        {
            plx_site = new String("PRC");
        }
    }
    else if (location.protocol == 'http:')
    {
        if (location.href.indexOf('charlesrknight.com') > -1)
        {
            plx_site = new String("CRK");
        }
        else if ((location.href.indexOf('hannotte.com') > -1)
             ||  (location.href.indexOf('hannotte.org') > -1)
             ||  (location.href.indexOf('hannotte.net') > -1)
             ||  (location.href.indexOf('deanhannotte.com') > -1)
             ||  (location.href.indexOf('dean-hannotte.com') > -1))
        {
            plx_site = new String("DHD");
        }
        else if (location.href.indexOf('eserver.org') > -1)
        {
            plx_site = new String("PRC");
        }
        else if (location.href.indexOf('geocities.com') > -1)
        {
            if (location.pathname.substr(0, 10) == '/dhannotte'
            ||  location.pathname.substr(0, 23) == '/SiliconValley/Lab/1783')
            {
                plx_site = new String("DHD");
            }
            else if (location.pathname.substr(0,  9) == '/nscenter'
                 ||  location.pathname.substr(0, 19) == '/Athens/Delphi/5179')
            {
                plx_site = new String("NSC");
            }
        }
        else if (location.href.indexOf('rosenfels.org') > -1
             ||  location.href.indexOf('paulrosenfels.org') > -1
             ||  location.href.indexOf('paul-rosenfels.org') > -1
             ||  location.href.indexOf('ninthstreetcenter.org/4') > -1)
        {
            plx_site = new String("PRC");
        }
        else if (location.href.indexOf('ninthstreetcenter.org') > -1)
        {
            plx_site = new String("NSC");
        }
    }
x_log("\nx_site() => '" + plx_site + "'.");
    return plx_site;
}

/*--------------------------------------------------------------------*/
/*                       x_snapshot() function                        */
/*--------------------------------------------------------------------*/
// WARNING: Using '-center' in snapshots embedded in table cells
//          causes width instability, so use '-noalign' instead.
function x_snapshot
(
    a_source, a_resize, a_parms, a_caption_override
)
{
x_log("\nX _ S N A P S H O T ( ) -------------------------------------------------");
    var result = x_media("snapshot", a_source, a_resize, a_parms, a_caption_override);
x_log("\n------------------------------------------------- X _ S N A P S H O T ( )");
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_soften_breaks() function                     */
/*                                                                    */
/*    This function implements the "soften break" (<sbr>) tag.        */
/*                                                                    */
/*      1) Convert all blanks to non-breaking space characters.       */
/*      2) Convert all "<sbr>" tags to blanks.                        */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_soften_breaks(a_html)
{
    var result = a_html;
    var lower_case_html = a_html.toLowerCase();
    if (lower_case_html.indexOf('<sbr>') > -1)
    {
        result = result.replace(/ <sbr>/gi, "<sbr>");
        result = result.replace(/<sbr> /gi, "<sbr>");
        result = result.replace(/ /g, "&nbsp;");
        result = result.replace(/-/g, "&minus;");
        result = result.replace(/<sbr>/gi, " ");
x_log("\nx_soften_breaks(\"" + a_html + "\")\n => \"" + result + "\"");
    }
    return result;
}

/*--------------------------------------------------------------------*/
/*            x_sort_table_selection_by_column() function             */
/*                                                                    */
/*     a_table is an array (table) of arrays (rows), equivalent       */
/*             in structure to an SQL table.                          */
/*                                                                    */
/*      a_rows is an array of indexes into the table where            */
/*             the rows to be included in the sort start.             */
/*             Thus this function doesn't need to                     */
/*             know how many cells each row contains.                 */
/*             This is the equivalent of the results of a SQL         */
/*             SELECT statement.                                      */
/*                                                                    */
/*      a_col  is an index into each row in which the sort            */
/*             key starts. This is the equivalent of a SORT           */
/*             BY parameter.                                          */
/*                                                                    */
/*          This function returns array of indexes into the           */
/*           table, sorted according to the caller's wishes.          */
/*           This is the equivalent of the results of a SQL           */
/*                         SELECT statement.                          */
/*                                                                    */
/*--------------------------------------------------------------------*/
function x_sort_table_selection_by_column(a_table, a_rows, a_col)
{
x_log("\nx_sort_table_selection_by_column(table of " + a_table.length
 + " cells, " + a_rows.length + " rows, column: " + a_col + ");");
    var max_passes   = 10000;
    var max_flips    = 100000;
    var total_passes = 0;
    var total_flips  = 0;
    var finished = false;
    while (finished == false && total_passes < max_passes)
    {
        total_passes++;
// x_log("\n Pass " + total_passes);
        var val1, val2;
        var ax1 = 0;
        var ax2 = 1;
        var flipped_on_this_pass = false;
        while (ax2 < a_rows.length && total_flips < max_flips)
        {
            val1 = pagetree[a_rows[ax1] + a_col];
            val2 = pagetree[a_rows[ax2] + a_col];
            if (val1 > val2)
            {
                flipped_on_this_pass = true;
                total_flips++;
// x_log("\n  Putting '" + val1 + "'"
//     + "\n    after '" + val2 + "'.");
                var save_ax = a_rows[ax1];
                a_rows[ax1] = a_rows[ax2];
                a_rows[ax2] = save_ax;
            }
            ax1++;
            ax2++;
        }
        if (flipped_on_this_pass == false) finished = true;
    }
x_log("\n ... passes: " + total_passes + ", flips: " + total_flips + ".");
    return a_rows;
}

/*--------------------------------------------------------------------*/
/*                       x_soundbite() function                       */
/*--------------------------------------------------------------------*/
function x_soundbite
(
    a_source, a_resize, a_parms, a_caption_override
)
{
x_log("\nX _ S O U N D B I T E ( ) -----------------------------------------------");
    var result = x_media("soundbite", a_source, a_resize, a_parms, a_caption_override);
x_log("\n----------------------------------------------- X _ S O U N D B I T E ( )");
    return result;
}

/*--------------------------------------------------------------------*/
/*                        x_squeeze() function                        */
/*                                                                    */
/*                  Trim initial and trailing blanks                  */
/*                   and eliminate redundant blanks                   */
/*--------------------------------------------------------------------*/
function x_squeeze(a_input)
{
    var result = new String(a_input);
    while (result.search(/^ /) > -1) result = result.replace(/^ /, "");
    while (result.search(/ $/) > -1) result = result.replace(/ $/, "");
    result = result.replace(/  /g, " ");
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_stars() function                         */
/*--------------------------------------------------------------------*/
function x_stars(a_count)
{
    var count = a_count;
    var result = new String("<p class='center'>");
    var cx;
    for (cx = 0; cx < count; cx++) result += "* ";
    result = result.substring(0, result.length -1);
// alert("x_stars(" + a_count + ") => '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                          x_tab() function                          */
/*--------------------------------------------------------------------*/
function x_tab()
{
    return x_tabs(1);
}

/*--------------------------------------------------------------------*/
/*                         x_tabs() function                          */
/*--------------------------------------------------------------------*/
function x_tabs(a_count)
{
    var count = a_count;
    var result = new String('');
    var cx;
    for (cx = 0; cx < count; cx++) result += "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
// x_log("\nx_tabs(" + count + ") = '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                x_timeslot_as_date_object() function                */
/*--------------------------------------------------------------------*/
function x_timeslot_as_date_object(a_timeslot)
{
var msg = "x_timeslot_as_date_object('" + a_timeslot + "')";
    var pieces = a_timeslot.split('-');
    var yyyymmdd  = pieces[0];
    var hhmm_from = pieces[1];
msg += "\n => ('" + yyyymmdd + "', '" + hhmm_from + "')";
    var yyyy = yyyymmdd.substr(0, 4);
    var mm   = yyyymmdd.substr(4, 2);
    var dd   = yyyymmdd.substr(6, 2);
msg += "\n => " + mm + "/" + dd + "/" + yyyy;
    var hh   = 0;
    var mi   = 0;
    var ss   = 0;
    if (x_is_not_empty(hhmm_from))
    {
        hh = hhmm_from.substr(0, 2);
        if (hhmm_from.length > 2) mi = hhmm_from.substr(2, 2);
        if (hhmm_from.length > 4) ss = hhmm_from.substr(4, 2);
    }
msg += " " + hh + ":" + mi + ":" + ss;
    var result = new Date(yyyy, mm - 1, dd, hh, mi, ss);
msg += "\n => '" + result + "'.";
// alert(msg);
    return result;
}

/*--------------------------------------------------------------------*/
/*                          x_top() function                          */
/*--------------------------------------------------------------------*/
function x_top(a_object)
{
    var result = 0;
    while (a_object.offsetParent)
    {
        result += a_object.offsetTop
        a_object = a_object.offsetParent;
    }
x_log("\nx_top(" + a_object + ") => " + result);
    return result;
}

/*--------------------------------------------------------------------*/
/*                     x_use_resource() function                      */
/*--------------------------------------------------------------------*/
function x_use_resource(a_resource)
{
x_log("\nx_use_resource('" + a_resource + "')");
    plx_resources_used.push(a_resource);
// x_log_resources_used();
}

/*--------------------------------------------------------------------*/
/*                      x_wide_screen() function                      */
/*--------------------------------------------------------------------*/
function x_wide_screen()
{
    if (x_window_width() > 800) return true;
    return false;
}

/*--------------------------------------------------------------------*/
/*                     x_window_height() function                     */
/*--------------------------------------------------------------------*/
function x_window_height()
{
//     if (x_os() != 'Windows') return 600;
//     if (x_browser() != 'IE') return 600;
    return document.body.clientHeight;
}

/*--------------------------------------------------------------------*/
/*                     x_window_width() function                      */
/*--------------------------------------------------------------------*/
function x_window_width()
{
// x_log("\nx_window_width() => " + document.body.clientWidth);
    return document.body.clientWidth;
}

/*--------------------------------------------------------------------*/
/*                         x_write() function                         */
/*--------------------------------------------------------------------*/
function x_write(a_string)
{
// x_log("\nx_write(\"" + a_string + "\");");
    document.write(a_string);
}

/*--------------------------------------------------------------------*/
/*                       x_videoclip() function                       */
/*--------------------------------------------------------------------*/
function x_videoclip
(
    a_source, a_resize, a_parms, a_caption_override
)
{
x_log("\nX _ V I D E O C L I P ( ) -----------------------------------------------");
    var result = x_media("videoclip", a_source, a_resize, a_parms, a_caption_override);
x_log("\n----------------------------------------------- X _ V I D E O C L I P ( )");
    return result;
}

/*--------------------------------------------------------------------*/
/*                         x_yyyy() function                          */
/*--------------------------------------------------------------------*/
function x_yyyy()
{
    var now = new Date();
    return now.getFullYear();
}

/*--------------------------------------------------------------------*/
/*                       x_yyyymmdd() function                        */
/*--------------------------------------------------------------------*/
function x_yyyymmdd(a_plus_or_minus)
{
x_log("\nx_yyyymmdd(" + a_plus_or_minus + ");");
    var when = new Date();
    if (typeof a_plus_or_minus != 'undefined')
    {
        when.setDate(when.getDate() + a_plus_or_minus);
    }
    var yyyy = new String(when.getFullYear());
    var mm   = new String(when.getMonth() + 1);
    var dd   = new String(when.getDate());
// x_log("\nyyyy: '" + yyyy + "'");
// x_log("\n  mm: '" + mm + "'");
// x_log("\n  dd: '" + dd + "'");
// x_log("\n  mm.length: '" + mm.length + "'");
// x_log("\n  dd.length: '" + dd.length + "'");
    if (mm.length == 1) mm = '0' + mm;
    if (dd.length == 1) dd = '0' + dd;
    var result = '' + yyyy + mm + dd;
x_log("\n ... returning '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                 x_yyyymmdd_2_mm_dd_yyyy() function                 */
/*--------------------------------------------------------------------*/
function x_yyyymmdd_2_mm_dd_yyyy(a_yyyymmdd)
{
x_log("\nx_yyyymmdd_2_mm_dd_yyyy(" + a_yyyymmdd + ") ");
    var yyyymmdd      = a_yyyymmdd;
    var yyyy          = yyyymmdd.substr(0, 4);
    var mm            = yyyymmdd.substr(4, 2);
    var dd            = yyyymmdd.substr(6, 2);
    var result        = new String("");
// If an 8-digit date is given, you'll get "mm/dd/yyyy"
// If a  6-digit date is given, you'll get "mm/yyyy"
// If a  4-digit date is given, you'll get "yyyy"
// If a  0-digit date is given, you'll get ""
    if (mm)   result  = mm + "/";
    if (dd)   result += dd + "/";
    if (yyyy) result += yyyy;
x_log(" => '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                        x_hhmmss() function                         */
/*--------------------------------------------------------------------*/
function x_hhmmss()
{
x_log("\nx_hhmmss();");
    var now = new Date();
    var hh = "" + now.getHours()  ; if (hh.length == 1) hh = '0' + hh;
    var mm = "" + now.getMinutes(); if (mm.length == 1) mm = '0' + mm;
    var ss = "" + now.getSeconds(); if (ss.length == 1) ss = '0' + ss;
// x_log("\nhh: '" + hh + "'");
// x_log("\nmm: '" + mm + "'");
// x_log("\nss: '" + ss + "'");
    var result = new String(hh + mm + ss);
x_log("\n ... returning '" + result + "'");
    return result;
}

/*--------------------------------------------------------------------*/
/*                       x_hh_mm_ss() function                        */
/*--------------------------------------------------------------------*/
function x_hh_mm_ss(a_delim)
{
    var delim = ':';
    if (typeof a_delim != 'undefined') delim = a_delim;
    var now = new Date();
    var hh = now.getHours()  ; if (hh.length == 1) hh = '0' + hh;
    var mm = now.getMinutes(); if (mm.length == 1) mm = '0' + mm;
    var ss = now.getSeconds(); if (ss.length == 1) dd = '0' + dd;
    var result = new String(hh + delim + mm + delim + ss);
    return result;
}

/**********************************************************************/
/*                               E N D                                */
/**********************************************************************/

// Left quote:
// <td
// style="padding: 10px; color: rgb(178, 183, 242); font-size: 40px; font-family: serif; font-weight: bold; text-align: left;"
// valign="top" width="20">“</td>
//
// Right quote:
//<td
// style="padding: 10px; color: rgb(178, 183, 242); font-size: 40px; font-family: serif; font-weight: bold; text-align: right;"
// valign="bottom" width="20">”</td>


