/*
 * JavaScript code for use with the Foam Rangers database
 *
 * $Id: foamrangers.js,v 1.8 2006/02/17 03:56:03 dcato Exp $
 */

var gFormName = null;

function initForm(formName)
{
    var obj = document[formName];

    if (obj)
    {
        gFormName = formName;

        if (formName == 'newmemberForm' || formName == 'moduserForm')
        {
            setMembershipInfoFormView();
            setAccountInfoFormView();
            setAccountOptionsFormView();
            setActivityInfoFormView();
        }
        else if (formName == 'newbrewsletterForm' || formName == 'modbrewsletterForm')
        {
            setCompTypeFormView();
        }

        setEmailFormLabel();
    }
}

function focusField(obj, initial)
{
    if (obj.className == 'formField' && initial)
    {
        obj.value = '';
    }
    obj.className = 'formFieldSelected';
}

function blurField(obj)
{
    obj.className = 'formFieldFilled';
}

function firstField(formName, formField)
{
    try
    {
        document[formName][formField].focus();
    }
    catch (e)
    {
    }
}

function nextField(obj, event, formName, offset)
{
    // Quick return if the user tabbed into the field
    if (event.keyCode == 9)
        return;

    if (obj.value.length == obj.maxLength)
    {
        var next = obj.tabIndex;
        // HACK: Apply the specified offset, which should be the form's
        // fieldset number, for browsers that include the fieldset in
        // the list of form elements.
        if (formName.elements[0].nodeName.toLowerCase() == 'fieldset')
        {
            next += offset;
        }
        if (next < formName.elements.length)
        {
            formName.elements[next].focus();
        }
    }
}

function setMembershipInfoFormView()
{
    var obj = document.forms[gFormName].elements['membership_type'];

    if (obj)
    {
        // There are three types of membership: "paid", "comp", and "suspended"
        var membership_type = obj[0].checked ? obj[0].value : (obj[1].checked ? obj[1].value : obj[2].value);

        if (membership_type == 'paid')
        {
            showPaidFormInfo();
        }
        else if (membership_type == 'comp')
        {
            showCompFormInfo();
        }
        else /*if (membership_type == 'suspended')*/
        {
            hideMembershipFormInfo();
        }
    }
}

function setFamilyInfoFormView()
{
    var obj = document.forms[gFormName].elements['family_membership'];

    if (obj)
    {
        // There are only two values: "yes" and "no"
        var familyform_value = obj[0].checked ? obj[0].value : obj[1].value;

        if (familyform_value == 'yes')
        {
            showFamilyFormInfo();
        }
        else
        {
            hideFamilyFormInfo();
        }
    }
}

function setEmailFormLabel()
{
    var obj = document.forms[gFormName].elements['email_brewsletter'];

    if (obj)
    {
        // There are only two values: "yes" and "no"
        var ebrewsletter_value = obj[0].checked ? obj[0].value : obj[1].value;

        if (ebrewsletter_value == 'yes')
        {
            setRequiredFlag('email_label');
        }
        else
        {
            unsetRequiredFlag('email_label');
        }
    }
}

function setAccountInfoFormView()
{
    var obj = document.forms[gFormName].elements['password_type'];

    if (obj)
    {
        // Default to masked password
        var pwform_value = 'mask';
        var maskview = 'block';
        var exposeview = 'none';

        // There are only two values: "mask" and "expose"
        pwform_value = obj[0].checked ? obj[0].value : obj[1].value;

        if (pwform_value == 'expose')
        {
            exposeview = 'block';
            maskview = 'none';
        }
        document.getElementById('maskedpassword').style.display  = maskview;
        document.getElementById('exposedpassword').style.display = exposeview;
    }
}

function setAccountOptionsFormView()
{
    var obj = document.getElementById('accountoptions');

    if (obj)
    {
        // Default to none
        var accountoptions_view = 'none';

        var form = document.forms[gFormName];
        if ((document.getElementById('exposedpassword').style.display != 'none' &&
             form.elements['pw'].value.length > 0) ||
            (document.getElementById('maskedpassword').style.display != 'none' &&
             (form.elements['pw1'].value.length > 0 ||
              form.elements['pw2'].value.length > 0)))
        {
            accountoptions_view = 'block';
        }
        document.getElementById('accountoptions').style.display = accountoptions_view;
    }
}

function showAccountOptions()
{
    document.getElementById('accountoptions').style.display = 'block';
}

function setActivityInfoFormView()
{
    var obj = document.forms[gFormName].elements['activity_type'];

    if (obj)
    {
        // Default to "current" activity
        var activity_value = 'current';
        var current_view = 'block';
        var historical_view = 'none';

        // There are only two values: "current" and "historical"
        activity_value = obj[0].checked ? obj[0].value : obj[1].value;

        if (activity_value == 'historical')
        {
            historical_view = 'block';
            current_view = 'none';
        }
        document.getElementById('currentActivity').style.display = current_view;
        document.getElementById('historicalActivity').style.display = historical_view;
    }
}

function setCompTypeFormView()
{
    document.getElementById('otherType').style.display = 'none';
}

function setOtherCompTypeField()
{
    var otherDisplay = 'none';
    var selectIndex = document.forms[gFormName].elements['comp_type'].selectedIndex;
    var selectValue = document.forms[gFormName].elements['comp_type'].options[selectIndex].value;

    if (selectValue == 0)
    {
        otherDisplay = '';
    }

    document.getElementById('otherType').style.display = otherDisplay;
    if (otherDisplay != 'none')
    {
        document.getElementById('other_comp_type').focus();
    }
}

function setRequiredFlag(id)
{
    var obj = document.getElementById(id);

    if (obj)
    {
        var label = obj.innerHTML;

        if (label.substr(label.length-2, 2) != ' *')
        {
            obj.innerHTML = label + ' *';
        }
    }
}

function unsetRequiredFlag(id)
{
    var obj = document.getElementById(id);

    if (obj)
    {
        var label = obj.innerHTML;

        if (label.substr(label.length-2, 2) == ' *')
        {
            obj.innerHTML = label.substring(0, label.length-2);
        }
    }
}

function showPaidFormInfo()
{
    document.getElementById('paidinfo').style.display = 'block';
    document.getElementById('compinfo').style.display = 'none';
    document.getElementById('etbu').style.display = 'block';

    setFamilyInfoFormView();
}

function showCompFormInfo()
{
    document.getElementById('compinfo').style.display = 'block';
    document.getElementById('paidinfo').style.display = 'none';
    document.getElementById('etbu').style.display = 'block';

    hideFamilyFormInfo();
}

function hideMembershipFormInfo()
{
    document.getElementById('compinfo').style.display = 'none';
    document.getElementById('paidinfo').style.display = 'none';
    document.getElementById('etbu').style.display = 'none';
    hideFamilyFormInfo();
}

function showFamilyFormInfo()
{
    setRequiredFlag('spouse_name_label');
    document.getElementById('familyinfo').style.display = 'block';
}

function hideFamilyFormInfo()
{
    unsetRequiredFlag('spouse_name_label');
    document.getElementById('familyinfo').style.display = 'none';
}

function showMaskedPasswordForm()
{
    document.getElementById('maskedpassword').style.display  = 'block';
    document.getElementById('exposedpassword').style.display = 'none';
    setAccountOptionsFormView('');
}

function showExposedPasswordForm()
{
    document.getElementById('exposedpassword').style.display = 'block';
    document.getElementById('maskedpassword').style.display  = 'none';
    setAccountOptionsFormView('');
}

function showCurrentActivityForm()
{
    document.getElementById('currentActivity').style.display = 'block';
    document.getElementById('historicalActivity').style.display = 'none';
}

function showHistoricalActivityForm()
{
    document.getElementById('historicalActivity').style.display = 'block';
    document.getElementById('currentActivity').style.display = 'none';
}

function updateMembershipRoll(id, year0, year1, year2)
{
    // Notes:
    //
    // act_c[id][year1] and act_h[id][year0 .. year1]
    // will always exist. act_c[id][year2] and act_h[id][year2]
    // may not exist so their existence must be checked before we attempt
    // to modify them.
    //
    // act_c[id][year0] and act_h[id][year0] should not be
    // modified.

    var selectIndex = document.moduserForm.elements['expdate'].selectedIndex;
    var chosen_year = document.moduserForm.elements['expdate'].options[selectIndex].value - 0;

    if (chosen_year == year0)
    {
        // Reverted to the original selection so unset all newer selections
        for (var i = chosen_year + 1; i <= year2; i++)
        {
            var act_c_name = 'act_c['+id+']['+i+']';
            var act_c_obj = document.moduserForm.elements[act_c_name];
            if (act_c_obj)
            {
                act_c_obj.checked = false;
            }

            var act_h_name = 'act_h['+id+']['+i+']';
            var act_h_obj = document.moduserForm.elements[act_h_name];
            if (act_h_obj)
            {
                act_h_obj.checked = false;
            }
        }
    }
    else
    {
        // Set the chosen year's selection and clear any subsequent year's selections
        for (var i = year1; i <= year2; i++)
        {
            var act_c_name = 'act_c['+id+']['+i+']';
            var act_c_obj = document.moduserForm.elements[act_c_name];
            if (act_c_obj)
            {
                act_c_obj.checked = (i <= chosen_year);
            }

            var act_h_name = 'act_h['+id+']['+i+']';
            var act_h_obj = document.moduserForm.elements[act_h_name];
            if (act_h_obj)
            {
                act_h_obj.checked = (i <= chosen_year);
            }
        }
    }
}

function toggleAccountInfo()
{
    var obj = document.getElementById('accountinfo');
    var inputFields = obj.getElementsByTagName('input');
    var labelFields = obj.getElementsByTagName('label');
    var spanFields  = obj.getElementsByTagName('span');

    for (var i = 0; i < inputFields.length; i++)
    {
        if (inputFields[i].disabled == true)
        {
            inputFields[i].disabled = false;
            if (inputFields[i].className == 'formFieldDisabled')
            {
                inputFields[i].className = 'formField';
            }
        }
        else
        {
            inputFields[i].disabled = true;
            if (inputFields[i].className == 'formField')
            {
                inputFields[i].className = 'formFieldDisabled';
            }
        }
    }

    for (var i = 0; i < spanFields.length; i++)
    {
        switch (spanFields[i].className)
        {
            case 'fieldSuppl':
                spanFields[i].className = 'fieldSupplDisabled';
                break;
            case 'fieldSupplDisabled':
                spanFields[i].className = 'fieldSuppl';
                break;
        }
    }

    for (var i = 0; i < labelFields.length; i++)
    {
        switch (labelFields[i].className)
        {
            case 'formLabel':
                labelFields[i].className = 'formLabelDisabled';
                break;
            case 'formLabelDisabled':
                labelFields[i].className = 'formLabel';
                break;
            case 'fieldLabel':
                labelFields[i].className = 'fieldLabelDisabled';
                break;
            case 'fieldLabelDisabled':
                labelFields[i].className = 'fieldLabel';
                break;
        }
    }
}


function toggleBrewsletterInfo()
{
    var obj = document.getElementById('compinfo');
    var inputFields  = obj.getElementsByTagName('input');
    var labelFields  = obj.getElementsByTagName('label');
    var selectFields = obj.getElementsByTagName('select');
    var spanFields   = obj.getElementsByTagName('span');

    for (var i = 0; i < inputFields.length; i++)
    {
        if (inputFields[i].disabled == true)
        {
            inputFields[i].disabled = false;
            if (inputFields[i].className == 'formFieldDisabled')
            {
                inputFields[i].className = 'formField';
            }
        }
        else
        {
            inputFields[i].disabled = true;
            if (inputFields[i].className == 'formField')
            {
                inputFields[i].className = 'formFieldDisabled';
            }
        }
    }

    for (var i = 0; i < selectFields.length; i++)
    {
        if (selectFields[i].disabled == true)
        {
            selectFields[i].disabled = false;
            if (selectFields[i].className == 'formSelectDisabled')
            {
                selectFields[i].className = 'formSelect';
            }
        }
        else
        {
            selectFields[i].disabled = true;
            if (selectFields[i].className == 'formSelect')
            {
                selectFields[i].className = 'formSelectDisabled';
            }
        }
    }

    for (var i = 0; i < spanFields.length; i++)
    {
        switch (spanFields[i].className)
        {
            case 'fieldSuppl':
                spanFields[i].className = 'fieldSupplDisabled';
                break;
            case 'fieldSupplDisabled':
                spanFields[i].className = 'fieldSuppl';
                break;
            case 'fieldLabel':
                spanFields[i].className = 'fieldLabelDisabled';
                break;
            case 'fieldLabelDisabled':
                spanFields[i].className = 'fieldLabel';
                break;
        }
    }

    for (var i = 0; i < labelFields.length; i++)
    {
        switch (labelFields[i].className)
        {
            case 'formLabel':
                labelFields[i].className = 'formLabelDisabled';
                break;
            case 'formLabelDisabled':
                labelFields[i].className = 'formLabel';
                break;
            case 'fieldLabel':
                labelFields[i].className = 'fieldLabelDisabled';
                break;
            case 'fieldLabelDisabled':
                labelFields[i].className = 'fieldLabel';
                break;
        }
    }
}

function printBrewsletterEmailList(src)
{
    var width = 480;
    var height = 360;
    var title = 'BrewsletterEmailAddressList';
    var attrs = 'width='+width+
               ',height='+height+
               ',scrollbars=yes'+
               ',resizable=yes'+
               ',statusbar=no'+
               ',menubar=no'+
               ',toolbar=no'+
               ',dependent=no';
    emailWindow = open(src.getAttribute('action'), title, attrs);
    emailWindow.focus();
    return emailWindow;
}

function initMemberData()
{
    // Skip if running under IE 5.5 or IE 6.0 over https because image
    // caching is broken and the page updates take too damn long.
    var agent = navigator.userAgent.toLowerCase();
    if (document.URL.substr(0, 6) == 'https:' &&
        ((agent.indexOf("msie 6.0") != -1 ||
          agent.indexOf("msie 5.5") != -1) &&
         agent.indexOf("opera") == -1))
    {
	return;
    }

    var pageobj = document.getElementById('memberdata');
    var tabobj = document.getElementById('navtabs');

    if (pageobj && tabobj)
    {
        var divs = pageobj.getElementsByTagName('div');
        var navtabs = '<ul>';
        var current_tab = '';
        var tab_title = '';
        for (var i = 0; i < divs.length; i++)
        {
            if (divs[i].id == 'navtabs')
            {
                continue;
            }

            if (current_tab.length == 0)
            {
                current_tab = divs[i].id;
            }

            switch (divs[i].id)
            {
                case 'Contact':
                    tab_title = 'Show contact information';
                    break;
                case 'Account':
                    tab_title = 'Show login account information';
                    break;
                case 'Privacy':
                    tab_title = 'Show privacy settings';
                    break;
                case 'Personal':
                    tab_title = 'Show personal information';
                    break;
                case 'History':
                    tab_title = 'Show membership history';
                    break;
            }

            navtabs += '<li id="Nav' + divs[i].id + '"><a title="' + tab_title + '" href="javascript:showContent(\'memberdata\', \'' + divs[i].id + '\')" target="_self">' + divs[i].id + '</a></li>';
        }
        navtabs += '</ul>';
        tabobj.innerHTML = navtabs;

        showContent('memberdata', current_tab);
    }
}

function showContent(page_id, div_id)
{
    var pageobj = document.getElementById(page_id);

    if (pageobj)
    {
        var divs = pageobj.getElementsByTagName('div');
        for (var i = 0; i < divs.length; i++)
        {
            if (divs[i].id != 'navtabs')
            {
                if (divs[i].id == div_id)
                {
                    divs[i].style.display = 'block';
                    document.getElementById('Nav' + divs[i].id).className = 'selected';
                }
                else
                {
                    divs[i].style.display = 'none';
                    document.getElementById('Nav' + divs[i].id).className = 'normal';
                }
            }
        }
    }
}

function initDirectory()
{
    // Skip if running under IE 5.5 or IE 6.0 over https because image
    // caching is broken and the page updates take too damn long.
    var agent = navigator.userAgent.toLowerCase();
    if (document.URL.substr(0, 6) == 'https:' &&
        ((agent.indexOf("msie 6.0") != -1 ||
          agent.indexOf("msie 5.5") != -1) &&
         agent.indexOf("opera") == -1))
    {
        return;
    }

    var dirobj = document.getElementById('directory');
    var indexobj = document.getElementById('index');
    var tabobj = document.getElementById('navtabs');

    if (dirobj && indexobj && tabobj)
    {
        var divs = dirobj.getElementsByTagName('div');
        var current_tab = document.URL.substr(document.URL.length-2, 1) == '#' ? 'sec' + document.URL.substr(document.URL.length-1, 1).toUpperCase() : '';
        for (var i = 0; i < divs.length; i++)
        {
            if (divs[i].id == 'index' || divs[i].id == 'navtabs')
            {
                continue;
            }

            if (current_tab.length == 0)
            {
                current_tab = divs[i].id;
            }
            var headers = divs[i].getElementsByTagName('h3');
            headers[0].style.display = 'none';  /* There's only one */
        }

        var re1 = /<li><a title="([^"]*)" href="[^"]*">(.)/ig;
        var re2 = /<li class="empty">.<\/li>\s*/ig;
        var navtabs = indexobj.innerHTML.replace(re1, "<li id=\"tab$2\" class=\"normal\"><a title=\"$1\" href=\"javascript:showDirectorySection('sec$2')\">$2").replace(re2, '').replace(/Jump to/g, 'Show');
        tabobj.innerHTML = navtabs;

        indexobj.style.display = 'none';
        showDirectorySection(current_tab);
    }
}

function showDirectorySection(id)
{
    var dirobj = document.getElementById('directory');

    if (dirobj)
    {
        var divs = dirobj.getElementsByTagName('div');
        for (var i = 0; i < divs.length; i++)
        {
            if (divs[i].id != 'navtabs' && divs[i].id != 'index')
            {
                if (divs[i].id == id)
                {
                    divs[i].style.display = 'block';
                    document.getElementById(divs[i].id.replace(/sec/, 'tab')).className = 'selected';
                }
                else
                {
                    divs[i].style.display = 'none';
                    document.getElementById(divs[i].id.replace(/sec/, 'tab')).className = 'normal';
                }
            }
        }
    }
}

/******************************************************************************/

/*
 * Random password generator, adapted from http://www.fourmilab.ch/onetime/otpjs.html
 */

loadTime = (new Date()).getTime();

/*

    L'Ecuyer's two-sequence generator with a Bays-Durham shuffle
    on the back-end.  Schrage's algorithm is used to perform
    64-bit modular arithmetic within the 32-bit constraints of
    JavaScript.

    Bays, C. and S. D. Durham.  ACM Trans. Math. Software: 2 (1976)
        59-64.

    L'Ecuyer, P.  Communications of the ACM: 31 (1968) 742-774.

    Schrage, L.  ACM Trans. Math. Software: 5 (1979) 132-138.

*/

function uGen(old, a, q, r, m) {      // Schrage's modular multiplication algorithm
    var t;

    t = Math.floor(old / q);
    t = a * (old - (t * q)) - (t * r);
    return Math.round((t < 0) ? (t + m) : t);
}

function LEnext() {                   // Return next raw value
    var i;

    this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
    this.gen2 = uGen(this.gen2, 40692, 52774, 3791, 2147483399);

    /* Extract shuffle table index from most significant part
       of the previous result. */

    i = Math.floor(this.state / 67108862);

    // New state is sum of generators modulo one of their moduli

    this.state = Math.round((this.shuffle[i] + this.gen2) % 2147483563);

    // Replace value in shuffle table with generator 1 result

    this.shuffle[i] = this.gen1;

    return this.state;
}

//  Return next random integer between 0 and n inclusive

function LEnint(n) {
    return Math.floor(this.next() / (1 + 2147483562 / (n + 1)));
}

//  Constructor.  Called with seed value

function LEcuyer(s) {
    var i;

    this.shuffle = new Array(32);
    this.gen1 = this.gen2 = (s & 0x7FFFFFFF);
    for (i = 0; i < 19; i++) {
        this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
    }

    // Fill the shuffle table with values

    for (i = 0; i < 32; i++) {
        this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563);
        this.shuffle[31 - i] = this.gen1;
    }
    this.state = this.shuffle[0];
    this.next = LEnext;
    this.nextInt = LEnint;
}

function sepchar() {
    if (rsep) {
        var seps = "!#$%&()*+,-./:;<=>?@[]^_{|}~";
        return seps.charAt(sepran.nextInt(seps.length - 1));
    }
    return "-";
}

function genPW(formName) {
    window.status = "Generating...";

    document.getElementById(formName).pw.value = "";

    upper = false;
    rsep = true;
    numeric = false;
    alphanumeric = false;
    english = true;
    gibberish = false;
    npass = 1;
    pw_length = 8;
    sep = "";
//               01234567890123456789012345678901
    charcodes = " " +
                "!\"#$%&'()*+,-./0123456789:;<=>?" +
                "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
                "`abcdefghijklmnopqrstuvwxyz{|}~";

    var n, j, ran0;

    /*  Obtain seed from the clock.  To reduce the likelihood
        of the seed being guessed, we create the seed by combining
        the time of the request with the time the page was loaded,
        then use that composite value to seed an auxiliary generator
        which is cycled between one and 32 times based on the time
        derived initial seed, with the output of the generator fed
        back into the seed we use to generate the pad.  */

    seed = Math.round((new Date()).getTime() % Math.pow(2, 31));
    ran0 = new LEcuyer((seed ^ Math.round(loadTime % Math.pow(2, 31))) & 0x7FFFFFFF);
    for (j = 0; j < (5 + ((seed >> 3) & 0xF)); j++) {
        n = ran0.nextInt(31);
    }
    while (n-- >= 0) {
        seed = ((seed << 11) | (seed >>> (32 - 11))) ^ ran0.next();
    }
    seed &= 0x7FFFFFFF;

    ran1 = new LEcuyer(seed);
    if (rsep) {
        /*  Use a separate random generator for separators
            so that results are the same for a given seed
            for both choices of separators.  */
        sepran = new LEcuyer(seed);
    }

    ndig = 1;
    j = 10;
    while (npass >= j) {
        ndig++;
        j *= 10;
    }
    pw_item = pw_length + (sep > 0 ? (pw_length / sep) : 0);
    pw_item += ndig + 5;
    j = pw_item * 3;
    if (j < 132) {
        j = 132;
    }
    if (!numeric) {
        letters = "abcdefghijklmnopqrstuvwxyz";
        if (upper) {
            letters = letters.toUpperCase();
        }
        if (english) {

            // Frequency of English digraphs (from D. Edwards 1/27/66)

            frequency = new Array( 
                new Array(4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62,
                          23, 167, 2, 14, 0, 83, 76, 127, 7, 25, 8, 1,
                          9, 1), /* aa - az */

                new Array(13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
                          11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0), /* ba - bz */

                new Array(32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1,
                          0, 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0), /* ca - cz */

                new Array(40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15,
                          6, 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0), /* da - dz */

                new Array(84, 20, 55, 125, 51, 40, 19, 16, 50, 1, 4,
                          55, 54, 146, 35, 37, 6, 191, 149, 65, 9, 26,
                          21, 12, 5, 0), /* ea - ez */

                new Array(19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1,
                          0, 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0), /* fa - fz */

                new Array(20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1,
                          4, 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0), /* ga - gz */

                new Array(101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3,
                          2, 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0), /* ha - hz */

                new Array(40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38,
                          25, 202, 56, 12, 1, 46, 79, 117, 1, 22, 0,
                          4, 0, 3), /* ia - iz */

                new Array(3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4,
                          0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0), /* ja - jz */

                new Array(1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
                          0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0), /* ka - kz */

                new Array(44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2,
                          2, 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0), /* la - lz */

                new Array(52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7,
                          1, 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0), /* ma - mz */

                new Array(42, 10, 47, 122, 63, 19, 106, 12, 30, 1, 6,
                          6, 9, 7, 54, 7, 1, 7, 44, 124, 6, 1, 15, 0,
                          12, 0), /* na - nz */

                new Array(7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19,
                          41, 134, 13, 23, 0, 91, 23, 42, 55, 16, 28,
                          0, 4, 1), /* oa - oz */

                new Array(19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
                          27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0), /* pa - pz */

                new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                          0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0), /* qa - qz */

                new Array(83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5,
                          26, 16, 60, 4, 0, 24, 37, 55, 6, 11, 4, 0,
                          28, 0), /* ra - rz */

                new Array(65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7,
                          11, 12, 56, 17, 6, 9, 48, 116, 35, 1, 28, 0,
                          4, 0), /* sa - sz */

                new Array(57, 22, 3, 1, 76, 5, 2, 330, 126, 1, 0, 14,
                          10, 6, 79, 7, 0, 49, 50, 56, 21, 2, 27, 0,
                          24, 0), /* ta - tz */

                new Array(11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
                          1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0), /* ua - uz */

                new Array(7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
                          5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0), /* va - vz */

                new Array(36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1,
                          8, 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0), /* wa - wz */

                new Array(1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 1,
                          5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0), /* xa - xz */

                new Array(14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7,
                          5, 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0), /* ya - yz */

                new Array(1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* za - zz */ );

            // This MUST be equal to the sum of the equivalent rows above.

            row_sums = new Array(
                796,   160,    284,    401,    1276,   262,    199,    539,    777,    
                16,    39,     351,    243,    751,    662,    181,    17,     683,    
                662,   968,    248,    115,    180,    17,     162,    5
            );

            // Frequencies of starting characters.

            start_freq = new Array(
                1299,  425,    725,    271,    375,    470,    93,     223,    1009,
                24,    20,     355,    379,    319,    823,    618,    21,     317,
                962,   1991,   271,    104,    516,    6,      16,     14
            );

            // This MUST be equal to the sum of all elements in the above array.

            total_sum = 11646;
        }
        if (gibberish) {
            gibber = "abcdefghijklmnopqrstuvwxyz" +
                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                     "0123456789" +
                     "!#$%&()*+,-./:;<=>?@[]^_{|}~";
            if (upper) {
                /*  Convert to upper case, leaving two copies of the
                    alphabet for two reasons: first, to favour letters
                    over gnarl, and second, to change only the letter case
                    when the mode is selected.  */
                gibber = gibber.toUpperCase();
            }
        }
        if (alphanumeric) {
            alnum = "abcdefghijklmnopqrstuvwxyz" +
                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                    "0123456789";
            if (upper) {
                /*  Convert to upper case, leaving two copies of the
                    alphabet for two reasons: first, to favour letters
                    over gnarl, and second, to change only the letter case
                    when the mode is selected.  */
                alnum = alnum.toUpperCase();
            }
        }
    }
    for (line = 1; line <= npass; line++) {
        password = "";
        if (numeric) {
            for (nchars = 0; nchars < pw_length; nchars++) {
                if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) {
                    password += sepchar();
                }
                password += ran1.nextInt(9);
            }
        } else if (!english) {
            for (nchars = 0; nchars < pw_length; nchars++) {
                if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) {
                    password += sepchar();
                }
                if (gibberish) {
                    password += gibber.charAt(ran1.nextInt(gibber.length - 1));
                } else if (alphanumeric) {
                    password += alnum.charAt(ran1.nextInt(alnum.length - 1));
                } else {
                    password += letters.charAt(ran1.nextInt(25));
                }
            }
        } else {
            position = ran1.nextInt(total_sum - 1);
            for (row_position = 0, j = 0; position >= row_position;
                 row_position += start_freq[j], j++) {
                continue;
            }

            password = letters.charAt(i = j - 1);
            nch = 1;
            for (nchars = pw_length - 1; nchars; --nchars) {

                // Now find random position within the row.

                position = ran1.nextInt(row_sums[i] - 1);
                for (row_position = 0, j = 0;
                     position >= row_position;
                     row_position += frequency[i][j], j++) {
                }

                if ((sep > 0) && ((nch % sep) == 0)) {
                    password += sepchar();
                }
                nch++;
                password += letters.charAt(i = j - 1);
            }
        }
    }

    document.getElementById(formName).pw.value = password;
    setAccountOptionsFormView('');
    window.status = "Done.";
}
