/*========================================================================
 * FILE:        scriptures.js
 * AUTHOR:      Stephen W. Liddle and the scriptures.byu.edu team
 *
 * DESCRIPTION: JavaScript code to support all aspects of the site.
 */
/*------------------------------------------------------------------------
 *                      CONSTANTS
 */
// Codes associated with OnReadyStateChange.
var STATE_UNINITIALIZED = 0;
var STATE_LOADING = 1;
var STATE_LOADED = 2;
var STATE_INTERACTIVE = 3;
var STATE_COMPLETE = 4;

// Messages for various conditions
var IMG_LOADING = '<img src="searching.gif"/>';
var MSG_LOADING = IMG_LOADING + 'LOADING' + IMG_LOADING;
var MSG_UNSUPPORTED = 'Your browser does not support one of our key '
                    + 'site features.  Please upgrade to the latest '
                    + 'version of Firefox, Safari, or Internet Explorer.';

// Layout constants
var MENU_BAR_HEIGHT = 16;   // Menu bar height in scripture/talk panels
var MIN_BOTTOM_HEIGHT = 150;// Minimum bottom panel height
var MIN_LEFT_WIDTH = 320;   // Minimum left panel width
var MIN_RIGHT_WIDTH = 235;  // Minimum right panel width
var MIN_TOP_HEIGHT = 150;   // Minimum top panel height
var SPLITTER_SIZE = 3;      // Thickness of the splitter bars

// Book/volume abbreviations
var BOOKS = ['ot', 'nt', 'bm', 'dc', 'pgp'];
var VBOOKS = ['vot', 'vnt', 'vbm', 'vdc', 'vpgp'];

/*------------------------------------------------------------------------
 *                      GLOBAL VARIABLES
 */
// Cookies to track various options
var CookGroupBy;        // Group by citation or individual verse
var CookSortBy;         // Sort by scripture/date or citation frequency
var CookSpeaker;        // Currently selected speaker
var CookYearEnd;        // Current filter's ending year
var CookYearStart;      // Current filter's starting year
var CookCorpus;         // Display JoD, GC, era or Both

// Keep track of the speakers configuration.
var CurrentSpeakerParameters;

// Keep track of the current talk ID.
var TalkId;

// HTML divs for various index components
var DivCitations;
var DivFilter;
var DivInnerFilter;
var DivOptions;
var DivPrint;
var DivFeedback;
var DivResults;
var DivSpeakers;
var DivTopics;
var DivCitationsTab;
var DivTopicsTab;
var DivSpeakersTab;
var DivResultsTab;
var DivOptionsTab;
var DivPrintTab;
var DivFeedbackTab;

// Popup window handle.
var PopupWindow = null;

// Current horizontal splitter height
var SplitterHeight = 0;

// Current search offset
var SearchOffset = 0;
var ListOffset = 0;

// Global object to hold drag information.
var dragObj = new Object();
var dragInnerWidth = 0;
var dragOffsetHeight;
var dragMovePx;

// Initialize drag variables
var browser = new Browser();
dragObj.zIndex = 0;

/*========================================================================
 *                      FUNCTIONS
 */
/*------------------------------------------------------------------------
 * FUNCTION adjustHeights -- set panel heights and offsets
 */
function adjustHeights(splitY, clientHeight, hideId, eventType)
{
    if (hideId != null) {
        var mainDiv = document.getElementById('main');
        var localClientHeight = mainDiv.offsetHeight;

        if (hideId == 'talks') {
            toggleTalkDisplay(localClientHeight, eventType);
        } else if (hideId == 'scriptures') {
            toggleScripDisplay(localClientHeight, eventType);
        }
    } else if (splitY != null && splitY != '') {
        var talksDiv = document.getElementById('talks');
        var scripsDiv = document.getElementById('scriptures');
        var scripHolder = document.getElementById('scrip-holder');
        var talkHolder = document.getElementById('talk-holder');

        talkHolder.style.height = splitY + 'px';
        talksDiv.style.height = (splitY - MENU_BAR_HEIGHT) + 'px';

        scripHolder.style.height = (clientHeight - splitY) + 'px';
        scripHolder.style.top = splitY + 'px';
        scripsDiv.style.height = ( clientHeight - splitY - MENU_BAR_HEIGHT -
                                   SPLITTER_SIZE ) + 'px';
    }

} /* adjustHeights */

/*------------------------------------------------------------------------
 * FUNCTION adjustWidths -- set panel widths and offsets
 */
function adjustWidths(splitter, splitX, clientWidth)
{
    var sidebarDiv = document.getElementById('sidebar');
    var middleDiv = document.getElementById('middle');
    var mainDiv = document.getElementById('main');

    splitter.style.left  = splitX + 'px';
    sidebarDiv.style.width = splitX + 'px';
    middleDiv.style.width = splitX + 'px';

    with (mainDiv.style) {
        width = (clientWidth - splitX - SPLITTER_SIZE) + 'px';
        left = (parseInt(splitX) + SPLITTER_SIZE) + 'px';
    }

} /* adjustWidths */

/*------------------------------------------------------------------------
 * FUNCTION advancedQuery -- perform an advanced talk search
 *
 * Elements:
 *      frmAdvSearch: advanced search form
 *      advancedTerms: search terms string
 *      advancedField: the field to which to apply search terms,
 *                     one of {talks, titles, both} (default talks)
 *      speakerAdvSearch: speaker to which the search is limited
 *                     (default any; format "ID--given last (abbr)")
 *      startYearAdvSearch: starting year for the search
 *      endYearAdvSearch: ending year for the search
 *      genConf: checkbox indicating search General Conference 1971+
 *      impEra: checkbox indicating search Gen. Conf. 1942-1970
 *      jod: checkbox indicating search Journal of Discourses
 *      stpjs: checkbox indicating search Scriptures Teachings
 */
function advancedQuery(offset)
{
    if (offset == null || offset == '') {
        offset = 0;
    }

    // Get the search terms.
    var inputField = document.getElementById('advancedTerms');
    var inputText = inputField.value;
    var trimmed = inputText.replace(/^\s+|\s+$/g, '') ;

    if (trimmed != inputText) {
        inputField.value = trimmed;
    }
    if (trimmed == '') {
        return;
    }

    // Determine which field (talks, titles, or both) the user wants to search.
    var fieldSelect = document.getElementById('advancedField');
    var chosenField = fieldSelect.options[fieldSelect.selectedIndex].value;

    if (chosenField == '') {
        chosenField = 'talks';
    }

    // Determine which corpora the user wants to search.
    var chkGenConf = document.getElementById('genConf');
    var chkImpEra = document.getElementById('impEra');
    var chkJoD = document.getElementById('jod');
    var chkStpjs = document.getElementById('stpjs');
    var chosenCorpus = '';

    if (chkGenConf.checked) {
        chosenCorpus += 'G';
    }
    if (chkImpEra.checked) {
        chosenCorpus += 'E';
    }
    if (chkJoD.checked) {
        chosenCorpus += 'J';
    }
    if (chkStpjs.checked) {
        chosenCorpus += 'T';
    }
    if (chosenCorpus == '') {
        // Default to the most recent general conference talks.
        chosenCorpus = 'G';
    }

    // Determine whether this search is limited by speaker.
    var speaker = '';

    var speakerSelect = document.getElementById('speakerAdvSearch');
    if (speakerSelect.selectedIndex > 0) {
        speaker = '&speakerId=' + speakerSelect.options[speakerSelect.selectedIndex].id;
    }

    // Determine whether this search is limited by year.
    var startYear = '';
    var startYearSelect = document.getElementById('startYearAdvSearch');

    if (startYearSelect.selectedIndex > 0) {
        startYear = '&startYear=' +
                    startYearSelect.options[startYearSelect.selectedIndex].value;
    }

    var endYear = '';
    var endYearSelect = document.getElementById('endYearAdvSearch');

    if ((endYearSelect.selectedIndex + 1) < endYearSelect.length) {
        endYear = '&endYear=' +
                    endYearSelect.options[endYearSelect.selectedIndex].value;
    }

    showResults();
    SearchOffset = offset;
    ListOffset = 0;
    startRequest( handleGetSearchRequest,
                  'search.php?corpus=' + chosenCorpus +
                      '&field=' + chosenField + speaker +
                      startYear + endYear +
                      '&query=' + encodeURIComponent(inputField.value) +
                      '&offset=' + offset +
                      '&maxResults=' + 20,
                  'results', 'advanced' );

} /* advancedQuery */

/*------------------------------------------------------------------------
 * FUNCTION applyFilter -- Set/unset cookies for current filter options
 */
function applyFilter()
{
    var start = document.getElementById('startDate');
    var end = document.getElementById('endDate');
    var spkrOptions = document.getElementById('speakerOptions');
    var sortby = document.getElementById('sortby');
    var groupby = document.getElementById('groupby');
    var corpus = document.getElementById('corpus');

    if (start == null || start.selectedIndex <= 0) {
        deleteCookie('start');
    } else {
        setCookie('start', start.options[start.selectedIndex].value);
    }
    if (end == null || end.selectedIndex >= end.length - 1) {
        deleteCookie('end');
    } else {
        setCookie('end', end.options[end.selectedIndex].value);
    }
    if (spkrOptions == null || spkrOptions.selectedIndex <= 0) {
        deleteCookie('spkrFilter');
    } else {
        setCookie('spkrFilter', spkrOptions.options[spkrOptions.selectedIndex].value);
    }
    if (sortby == null || sortby.selectedIndex <= 0) {
        deleteCookie('sortby');
    } else {
        setCookie('sortby', sortby.options[sortby.selectedIndex].value);
    }
    if (groupby == null || groupby.selectedIndex > 0) {
        deleteCookie('groupby');
    } else {
        setCookie('groupby', groupby.options[groupby.selectedIndex].value);
    }

    if (corpus == null || corpus.selectedIndex <= 0) {
        deleteCookie('corpus');
    } else {
        setCookie('corpus', corpus.options[corpus.selectedIndex].value);
    }

    // Display filter settings info div.
    displayFilterInfo();

    // Set citations to default top-level tree.
    getCitations('', '');

    // Hide speakers and options divs.
    showCitations();

} /* applyFilter */

/*------------------------------------------------------------------------
 * FUNCTION applySpeakerFilter -- filter index to just one speaker
 */
function applySpeakerFilter(spkrID, name)
{
    setCookie('spkrFilter', spkrID + '--' + name);

    getCitations('', '');
    displayFilterInfo();

    DivSpeakers.className = 'hidden';
    document.getElementById(spkrID).selected = true;

} /* applySpeakerFilter */

/*------------------------------------------------------------------------
 * FUNCTION changeDiv1 -- show content in given element, hide load message
 */
function changeDiv1(content, id, el)
{
    el.innerHTML = content;
    el.className = 'visible';
    loadingMessage = document.getElementById(id + 'loading');
    p = el.parentNode;
    p.removeChild(loadingMessage);

} /* changeDiv1 */

/*------------------------------------------------------------------------
 * FUNCTION changeDiv2 -- show content and toggle plus/minus symbol
 */
function changeDiv2(content, id, d2)
{
    elem = document.getElementById(id);
    loadingMessage = document.getElementById(id + 'loading');
    p = elem.parentNode;
    toggleOpen(elem);
    var ds = document.getElementById(id + '-span');
    ds.className = 'visiblespan';
    ds.innerHTML = content;
    p.removeChild(loadingMessage);
    elem.className = d2;

} /* changeDiv2 */

/*------------------------------------------------------------------------
 * FUNCTION checkForFilter -- display filter information
 */
function checkForFilter(volume, book)
{
    if (volume != '' && book != '') {
        // Since a particular book is being requested, delete all current
        // cookies (which capture last index configuration) and set a new
        // current location to load.
        var cookies = ['spkrFilter', 'start', 'end', 'sortby', 'groupby', 'corpus'];
        for (i in cookies) {
            deleteCookie(cookies[i]);
        }
        setCookie('volume', 'v' + volume + '|b' + book + '|');
    }

    findDivs();
    loadCookies();
    var agt = navigator.userAgent.toLowerCase();
    if (agt.indexOf('safari') != -1) {
        document.getElementById('options').style.height = '210px';
    }

    if (CookYearStart != null) {
        document.getElementById('s' + CookYearStart).selected = true;
    } else {
        document.getElementById('startDate').selectedIndex = 0;
    }
    if (CookYearEnd != null) {
        document.getElementById('e' + CookYearEnd).selected = true;
    } else {
        document.getElementById('endDate').selectedIndex =
                                document.getElementById('endDate').length - 1;
    }

    if (CookSortBy != null && CookSortBy != '') {
        // Default is 0 for 'scd'
        var ix = 0;
        if (CookSortBy == 'scda') {
            ix = 1;
        } else if (CookSortBy == 'cfq') {
            ix = 2;
        }
        document.getElementById('sortby').selectedIndex = ix;
    }

    if (CookGroupBy != null && CookGroupBy != '') {
        document.getElementById('groupby').selectedIndex = 0;
    }

    if (CookCorpus != null && CookCorpus != '') {
        // Default is 0 for 'all'
        var ix = 0;
        if (CookCorpus == 'gc') {
            ix = 1;
        } else if (CookCorpus == 'era') {
            ix = 2;
        } else {
            ix = 3;
        }
        document.getElementById('corpus').selectedIndex = ix;
    }

    displayFilterInfo();
    getCitations('', '');

} /* checkForFilter */

/*------------------------------------------------------------------------
 * FUNCTION clearFilter -- Clears all filter cookies and hides filter div
 */
function clearFilter()
{
    collapseAll();

    var cookies = ['spkrFilter', 'start', 'end', 'sortby', 'groupby', 'corpus'];
    DivFilter.className = 'hidden';
    document.getElementById('filterDivInner').innerHTML = '';
    for (i in cookies) {
        deleteCookie(cookies[i]);
    }
    document.getElementById('startDate').selectedIndex = 0;
    var endEl = document.getElementById('endDate');
    endEl.selectedIndex = endEl.length - 1;
    document.getElementById('speakerOptions').selectedIndex = 0;
    document.getElementById('corpus').selectedIndex = 0;
    document.getElementById('sortby').selectedIndex = 0;
    document.getElementById('groupby').selectedIndex = 1;
    getCitations('', '');

} /* clearFilter */

/*------------------------------------------------------------------------
 * FUNCTION closeAdvancedSearch -- hide the advanced search window
 */
function closeAdvancedSearch()
{
   var advancedDiv = document.getElementById('advanced-search');

   advancedDiv.className = 'hidden';

} /* closeAdvancedSearch */

/*------------------------------------------------------------------------
 * FUNCTION collapse -- collapse a particular node
 */
function collapse(el, changeCookie)
{
    if (typeof el == 'object') {
        document.getElementById(el.id).className = 'hiddenspan';
        window.status += el.id + 'class=' + el.className + ' ';
        toggleClosed(el.parentNode);
    } else {
        toggleClosed(document.getElementById(el));
        document.getElementById(el + '-span').className = 'hiddenspan';
    }

    if (changeCookie) {
        var re = new RegExp(el + '\\|', 'i');
        x = getCookie('volume');
        if (x != null) {
            x = x.replace(re, '');
            setCookie('volume', x);
        }
    }

} /* collapse */

/*------------------------------------------------------------------------
 * FUNCTION collapseAll -- collapse all nodes
 */
function collapseAll()
{
    if (DivTopics.className == 'visible') {
        for (i = 2; i <= 28; i++) {
            collapseTopicDiv('tid' + i);
        }
    } else {
        for (vol in BOOKS) {
            var vbooksEl = document.getElementById(VBOOKS[vol]);

            if (vbooksEl) {
                // Find all open nodes within this book
                var openNodes = getElementsByClassName(vbooksEl, 'span', 'visiblespan');

                /*
                openNodes.each(function(item, index) { syncCollapse(item.id); });
                */
                for (node in openNodes) {
                    syncCollapse(openNodes[node].id);
                }
                if (vbooksEl.className == 'hidden') {
                    collapse(VBOOKS[vol], false);
                }
            }
        }

        // Clear the cookie that remembers what had been open
        setCookie('volume', '');
    }

} /* collapseAll */

/*------------------------------------------------------------------------
 * FUNCTION collapseAllExcept -- collapse all nodes except the given one
 */
function collapseAllExcept(excludeVolume, excludeBook, excludeChapter)
{
    for (vol in BOOKS) {
        var vbooksEl = document.getElementById(VBOOKS[vol]);

        if (vbooksEl) {
            // Find all open nodes within this book
            var openNodes = getElementsByClassName(vbooksEl, 'span', 'visiblespan');

            if (VBOOKS[vol] != excludeVolume) {
                for (node in openNodes) {
                    syncCollapse(openNodes[node].id);
                }
                if (vbooksEl.className == 'hidden') {
                    collapse(VBOOKS[vol], false);
                }
            } else {
                // Close all open chapters except the one we want
                /*
                openNodes.each(function(el, index) {
                    if ( !el.id.match(excludeVolume) &&
                         !el.id.match(excludeBook) &&
                         !el.id.match(excludeChapter) ) {
                        syncCollapse(el.id);
                    }
                });
                */

                for (node in openNodes) {
                    var el = openNodes[node];
                    if ( !el.id.match(excludeVolume) &&
                         !el.id.match(excludeBook) &&
                         !el.id.match(excludeChapter) ) {
                        syncCollapse(el.id);
                    }
                }
            }
        }
    }

} /* collapseAllExcept */

/*------------------------------------------------------------------------
 * FUNCTION collapseTopicDiv -- collapse the given topic div, recursively
 */
function collapseTopicDiv(divId)
{
    var div = document.getElementById(divId);
    if (div) {
        var span = document.getElementById(divId + '-span');
        if (span && span.className == 'visiblespan') {
            // Close any children that are open
            var childDivList = new Array();
            var childIx = 0;
            for (var ix = 0; ix < span.childNodes.length; ix++) {
                var childDiv = span.childNodes[ix];
                if (childDiv && childDiv.id && childDiv.id.substr(0, 3) == "tid") {
                    childDivList[childIx] = childDiv.id;
                    ++childIx;
                }
            }
            for (var ix2 = 0; ix2 < childIx; ix2++) {
                var theChildDivId = childDivList[ix2];
                collapseTopicDiv(theChildDivId);
            }

            span.className = 'hiddenspan';
            toggleClosed(div);
        }
    }

} /* collapseTopicDiv */

/*------------------------------------------------------------------------
 * FUNCTION computeNavigation -- compute the results navigation string
 *          (e.g., 1 2 3 4 Next Last) with hyperlinks to given offsets
 */
function computeNavigation(resultObject, advanced)
{
    var navString = '';
    var methodName = 'query';

    if (advanced == 'advanced') {
        methodName = 'advancedQuery';
    }

    if (resultObject.resultCount <= 20) {
        return navString;
    }

    var startIx = (SearchOffset / 20) - 5;
    var lastIx = Math.floor(resultObject.resultCount / 20);
    if (startIx + 11 > lastIx) {
        startIx = lastIx - 10;
    }
    if (startIx < 0) {
        startIx = 0;
    }

    for (var i = startIx; i < startIx + 11; i++) {
        if (i * 20 < resultObject.resultCount) {
            if (i * 20 == SearchOffset) {
                navString += '<span class="currentResultSet">' + (i + 1) + '</span> ';
            } else {
                navString += '<a href="javascript:' + methodName + '(' +
                             (i * 20) + ')">' +
                             (i + 1) + '</a> ';
            }
        } else {
            break;
        }
    }

    if (navString != '') {
        navString = '<div class="resultsNav">' +
                    ( (SearchOffset >= 20)
                                    ? '<a href="javascript:' + methodName + '(' +
                                      (SearchOffset - 20) +
                                      ')" class="prevNext">Previous</a> &nbsp;'
                                    : ''
                    ) +
                    navString +
                    ( (SearchOffset + 20 < resultObject.resultCount)
                                    ? '&nbsp;<a href="javascript:' + methodName + '(' +
                                      (SearchOffset + 20) +
                                      ')" class="prevNext">Next</a>'
                                    : ''
                    ) +
                    '</div>';
    }

    return navString;

} /* computeNavigation */

/*------------------------------------------------------------------------
 * FUNCTION computeNavigationSimilar -- compute the results navigation
 *     string (e.g., 1 2 3 4 Next Last) with hyperlinks to given offsets
 */
function computeNavigationSimilar(resultObject)
{
    var navString = '';
    var methodName = 'similar';

    if (resultObject.resultCount <= 20) {
        return navString;
    }

    var startIx = (SearchOffset / 20) - 5;
    var lastIx = Math.floor(resultObject.resultCount / 20);
    if (startIx + 11 > lastIx) {
        startIx = lastIx - 10;
    }
    if (startIx < 0) {
        startIx = 0;
    }

    for (var i = startIx; i < startIx + 11; i++) {
        if (i * 20 < resultObject.resultCount) {
            if (i * 20 == SearchOffset) {
                navString += '<span class="currentResultSet">' + (i + 1) + '</span> ';
            } else {
                navString += '<a href="javascript:' + methodName + '(' +
                             (i * 20) + ')">' +
                             (i + 1) + '</a> ';
            }
        } else {
            break;
        }
    }

    if (navString != '') {
        navString = '<div class="resultsNav">' +
                    ( (SearchOffset >= 20)
                                    ? '<a href="javascript:' + methodName + '(' +
                                      (SearchOffset - 20) +
                                      ')" class="prevNext">Previous</a> &nbsp;'
                                    : ''
                    ) +
                    navString +
                    ( (SearchOffset + 20 < resultObject.resultCount)
                                    ? '&nbsp;<a href="javascript:' + methodName + '(' +
                                      (SearchOffset + 20) +
                                      ')" class="prevNext">Next</a>'
                                    : ''
                    ) +
                    '</div>';
    }

    return navString;

} /* computeNavigationSimilar */

/*------------------------------------------------------------------------
 * FUNCTION deleteCookie -- delete the given cookie from the browser
 */
function deleteCookie(name)
{
    document.cookie = name + '=' + escape('') + '; path=/'
                    + '; expires=Thu, 01-Jan-1970 00:00:01 GMT';

} /* deleteCookie */

/*------------------------------------------------------------------------
 * FUNCTION displayFilterInfo -- display div describing current filter
 */
function displayFilterInfo()
{
    loadCookies();

    var filterDivHTML = '<div class="filterLine">'
                      + '<div class="boldInline">name</div>'
                      + '<div style="display:inline;">value</div></div>\n';
    var buttonDivHTML = '<div id="clearButtonDiv">'
                      + '<input type="button" value="Clear" '
                      +        'onclick="clearFilter();" '
                      +        'class="filterButton"/></div>\n';
    var filterHTML = '';

    if (CookYearStart != null && CookYearEnd != null) {
        temp = filterDivHTML.replace(/name/, 'Years: ');
        temp = temp.replace(/value/, CookYearStart + ' to ' + CookYearEnd);
        filterHTML += temp;
    } else if (CookYearStart == null && CookYearEnd != null) {
        temp = filterDivHTML.replace(/name/, 'Years: ');
        temp = temp.replace(/value/, ('1830 to ' + CookYearEnd));
        filterHTML += temp;
    } else if (CookYearStart != null && CookYearEnd == null) {
        temp = filterDivHTML.replace(/name/, 'Years: ');
        temp = temp.replace(/value/, CookYearStart + ' to present');
        filterHTML += temp;
    }

    if (CookSpeaker != null) {
        temp1 = CookSpeaker.substr(CookSpeaker.indexOf('--') + 2);
        temp2 = filterDivHTML.replace(/name/, 'Speaker: ');
        temp2 = temp2.replace(/value/, temp1);
        filterHTML += temp2;
    }

    if (CookCorpus !=  null) {
        if (CookCorpus == 'gc') {
            temp1 = 'General Conference Talks';
        } else if (CookCorpus == 'era') {
            temp1 = 'Improvement Era';
        } else if (CookCorpus == 'tpjs') {
            temp1 = 'Teachings of the Prophet Joseph Smith';
        } else if (CookCorpus == 'jod') {
            temp1 = 'Journal of Discourses';
        } else {
            // CookCorpus == 'both'
            temp1 = 'All';
        }
        temp2 = filterDivHTML.replace(/name/, 'Corpus: ');
        temp2 = temp2.replace(/value/, temp1);
        filterHTML += temp2;
    }

    if (filterHTML != '') {
        DivInnerFilter.innerHTML = filterHTML + buttonDivHTML;
        DivFilter.className = 'visible';
    } else {
        DivFilter.className = 'hidden';
    }

} /* displayFilterInfo */

/*------------------------------------------------------------------------
 * FUNCTION findDivs -- look up all the major divs we need
 */
function findDivs()
{
    DivSpeakers = document.getElementById('speakers');
    DivFilter = document.getElementById('filterDiv');
    DivInnerFilter = document.getElementById('filterDivInner');
    DivCitations = document.getElementById('citations');
    DivOptions = document.getElementById('options');
    DivFeedback = document.getElementById('feedback');
    DivPrint = document.getElementById('print');
    DivResults = document.getElementById('results');
    DivTopics = document.getElementById('topics');
    DivCitationsTab = document.getElementById('indexTab');
    DivTopicsTab = document.getElementById('topicsTab');
    DivSpeakersTab = document.getElementById('speakersTab');
    DivResultsTab = document.getElementById('searchTab');
    DivOptionsTab = document.getElementById('optionsTab');
    DivPrintTab = document.getElementById('printTab');
    DivFeedbackTab = document.getElementById('feedbackTab');

} /* findDivs */

/*------------------------------------------------------------------------
 * FUNCTION fixHSplitter -- adjust the horizontal splitter on resize
 */
function fixHSplitter()
{
    var hsplitter = document.getElementById('h-splitter');
    var innerWidth;

    if (browser.isIE) {
        innerWidth = document.body.clientWidth;
        document.getElementById('queryInput').style.top = '1px';
    } else {
        innerWidth = window.innerWidth;
    }

    hsplitter.style.height = SplitterHeight + 'px';
    var cookie = getCookie('width');

    if (cookie != null && cookie != '') {
        var splitWidth = innerWidth * cookie;

        if (splitWidth > (innerWidth - MIN_RIGHT_WIDTH)) {
            splitWidth = (innerWidth - MIN_RIGHT_WIDTH);
        }
        if (splitWidth < MIN_LEFT_WIDTH) {
            splitWidth = MIN_LEFT_WIDTH;
        }

        adjustWidths(hsplitter, splitWidth.toFixed(0), innerWidth);
    } else {
        var splitWidth = innerWidth * 32 / 100;
        adjustWidths(hsplitter, splitWidth.toFixed(0), innerWidth);
    }

} /* fixHSplitter */

/*------------------------------------------------------------------------
 * FUNCTION fixVSplitter -- adjust the vertical splitter on resize
 */
function fixVSplitter(evar)
{
    var eventType;
    if (evar) {
        eventType = 'resize';
    }
    initSize();
    SplitterHeight = document.getElementById('main').offsetHeight;
    var talkHeight;
    var scripHeight;

    var heightCookie = getCookie('height');
    var hiddenCookie = getCookie('hidden');

    if (heightCookie != null && heightCookie != '') {
        var hiddenDiv;
        if (hiddenCookie != null && hiddenCookie != '') {
            hiddenDiv = hiddenCookie;
        }

        var splitHeight = SplitterHeight * heightCookie;

        if (splitHeight > (SplitterHeight - MIN_BOTTOM_HEIGHT)) {
            splitHeight = (SplitterHeight - MIN_BOTTOM_HEIGHT);
        }

        if (splitHeight < MIN_TOP_HEIGHT) {
            splitHeight = MIN_TOP_HEIGHT;
        }

        adjustHeights(splitHeight, SplitterHeight, hiddenDiv, eventType);
    } else {
        talkHeight = (SplitterHeight - SPLITTER_SIZE) / 2;
        var x = talkHeight / SplitterHeight;
        setCookie('height', x.toFixed(2));
        adjustHeights(talkHeight, SplitterHeight, null, eventType);
    }

    if (browser.isNS) {
        document.getElementById('tmenu').className = 'nsmenu';
        document.getElementById('talk-open').className = 'nsmenu';
        document.getElementById('talk-print').className = 'nsmenu';
        document.getElementById('talk-similar').className = 'nsmenu';
        document.getElementById('smenu').className = 'nsmenu';
        document.getElementById('scrip-open').className = 'nsmenu';
    }

} /* fixVSplitter */

/*------------------------------------------------------------------------
 * FUNCTION getAnchorPosition -- find page-relative coordinates of the
 *                               named anchor
 */
function getAnchorPosition(anchorname, byName)
{
    // This function will return an Object with x and y properties
    var useWindow = false;
    var coordinates = new Object();
    var elem;

    if (byName) {
        elem = document.getElementsByName(anchorname);
        elem = elem[0];
    } else {
        elem = document.getElementById(anchorname);
    }
    if (!elem) {
        // No object.
        coordinates.x = 0;
        coordinates.y = 0;
    } else {
        var x = elem.offsetLeft;
        var y = elem.offsetTop;
        while ((elem = elem.offsetParent) != null) {
            x += elem.offsetLeft;
            y += elem.offsetTop;
        }
        coordinates.x = x;
        coordinates.y = y;
    }
    return coordinates;

} /* getAnchorPosition */

/*------------------------------------------------------------------------
 * FUNCTION getAnchorPositionPageOffsetTop -- find the page-relative top
 *                      offset of the given element
 */
function getAnchorPositionPageOffsetTop(elem)
{
    var top = elem.offsetTop;
    while ((elem = elem.offsetParent) != null) {
        top += elem.offsetTop;
    }
    return top;

} /* getAnchorPositionPageOffsetTop */

/*------------------------------------------------------------------------
 * FUNCTION getCitations -- set up the citations tree
 */
function getCitations(parameters, id)
{
    var target = 'citedest';
    if (parameters == '') {
        target = '';
    }

    if (id == '') {
        id = 'citations';
    }

    var span = document.getElementById(id + '-span');
    var d = document.getElementById(id);

    var cookieName = 'volume';
    if (getCookie('sortby') == 'cfq') {
        cookieName = 'cfqexpand';
    }

    if (parameters.match('y=n') != null) {
        span.className = 'hiddenspan';
        toggleClosed(d);

        var re = new RegExp(id + '\\|', 'i');
        var cookie = getCookie(cookieName);

        if (cookie != null) {
            cookie = cookie.replace(re, '');
            setCookie(cookieName, cookie);
        }
    } else if (span != null && span.className == 'hiddenspan') {
        var cookie = getCookie(cookieName);
        cookie += id + '|';
        setCookie(cookieName, cookie);

        span.className = 'visiblespan';
        toggleOpen(d);
    } else {
        d = document.getElementById(id);
        d2 = d.className;
        d.className = 'hidden';
        n = document.createElement('div');
        n.innerHTML = MSG_LOADING;
        n.id = id + 'loading';

        if (d2 != 'hidden') {
            n.className = d2;
        }

        p = d.parentNode;
        p.insertBefore(n,d);

        url = 'citations.php' + parameters;

        startRequest(handleCitationsReady, url, null, id);
    }

} /* getCitations */

/*------------------------------------------------------------------------
 * FUNCTION getCitationsOverride -- open up a particular volume
 */
function getCitationsOverride(id, vid)
{
    d = document.getElementById(vid);
    temp = d.innerHTML;

    var notCollapsed = true;
    for (i in BOOKS) {
        t = document.getElementById(VBOOKS[i]).innerHTML;
        if ((t.match('y=n&a=' + BOOKS[i]) || t.match('y=n&amp;a=' + BOOKS[i]))
            && VBOOKS[i] != vid) {
            collapse(VBOOKS[i], true);
            notCollapsed = false;
        }
    }

    if ((temp.match('y=n&a=' + id) || temp.match('y=n&amp;a=' + id))
        && notCollapsed) {
        collapse(vid, true);
    } else if (temp.match('y=y&a=' + id) || temp.match('y=y&amp;a=' + id)) {
        if (document.getElementById(vid).className != 'hidden') {
            getCitations('?c=1&amp;y=y&a=' + id + '&divId=' + vid, vid);
        }
    }

    showCitations();

    if (DivInnerFilter.innerHTML != '') {
        DivFilter.className = 'visible';
    }

}/* getCitationsOverride */

function getCookie(c_name)
{
    if (document.cookie.length > 0) {
        c_start = document.cookie.indexOf(c_name + '=');
        if (c_start >= 0) {
            c_start = c_start + c_name.length + 1;
            c_end = document.cookie.indexOf(';', c_start);
            if (c_end < 0) {
                c_end = document.cookie.length;
            }
            return (unescape(document.cookie.substring(c_start, c_end)));
        }
    }

    return (null);

} /* getCookie */

/*------------------------------------------------------------------------
 * FUNCTION getElementsByClassName -- find all descendants of a given
 * element whose tag and class names correspond to the given values
 */
function getElementsByClassName(elem, tagName, className)
{
    var result = new Array();

    if (elem && tagName && className) {
        var candidates = elem.getElementsByTagName(tagName);
        var ix = 0;
        for (var i = 0; i < candidates.length; i++) {
            var candidate = candidates[i];
            if (candidate.className == className) {
                result[ix++] = candidate;
            }
        }
    }
    return result;

} /* getElementsByClassName */

/*------------------------------------------------------------------------
 * FUNCTION getFeedback -- display the feedback dialog box
 */
function getFeedback()
{
    if (DivFeedback.className == 'visible') {
        DivFeedback.className = 'hidden';
    } else {
        DivFeedback.className = 'visible';
        DivOptions.className = 'hidden';
        DivPrint.className = 'hidden';
    }

} /* getFeedback */

/*------------------------------------------------------------------------
 * FUNCTION getNav -- gets navigation for JOD or General Conference
 */
function getNav(parameters, id)
{
    var url = 'gettalk.php' + parameters;

    if (parameters.indexOf('jod=yes') >= 0) {
        // Get a JoD table of contents page.
        if (parameters.indexOf('sort=speaker') >= 0) {
            url = 'jodTocBySpeaker.html';
        } else if (parameters.indexOf('sort=date') >= 0) {
            url = 'jodTocByDate.html';
        } else if (parameters.indexOf('sort=title') >= 0) {
            url = 'jodTocByTitle.html';
        } else {
            url = 'jodTocByVolume.html';
        }
    } else {
        // Get a General Conference table of contents page.
        if (parameters.indexOf('sort=speaker') >= 0) {
            url = 'gcTocBySpeaker.html';
        } else {
            url = 'gcTocByDate.html';
        }
    }

    // Request the contents of the URL.
    navParameters = new Object();
    navParameters.navId = id;
    navParameters.parameters = parameters;
    startRequest(handleGetNavReady, url, 'tContent', navParameters);

} /* getNav */

/*------------------------------------------------------------------------
 * FUNCTION getOptions -- display the options/filter dialog box
 */
function getOptions()
{
    if (DivOptions.className == 'visible') {
        DivOptions.className = 'hidden';
    } else {
        DivOptions.className = 'visible';
        DivPrint.className = 'hidden';
        DivFeedback.className = 'hidden';
    }

} /* getOptions */

/*------------------------------------------------------------------------
 * FUNCTION getPrint -- display the print dialog box
 */
function getPrint()
{
    if (DivPrint.className == 'visible') {
        DivPrint.className = 'hidden';
    } else {
        DivPrint.className = 'visible';
        DivOptions.className = 'hidden';
        DivFeedback.className = 'hidden'
    }

} /* getPrint */

/*------------------------------------------------------------------------
 * FUNCTION getScripture -- download and display scripture from server
 */
function getScripture(parameters, target)
{
    // If the scriptures window isn't visible, display it.
    if (document.getElementById('scriptures').className == 'hidden') {
        adjustHeights('', '', 'scriptures');
    }

    // If the advanced search window is visible, hide it.
    hideAdvanced();

    var url = 'about.php';
    if (parameters != '') {
        url = 'getscrip.php' + parameters;
        if (target != '') {
            url += '&target=' + target;
        }
    }

    startRequest(handleGetScriptureReady, url, 'sContent', parameters);

} /* getScripture */

/*------------------------------------------------------------------------
 * FUNCTION getSpeakers -- display the list of speakers
 */
function getSpeakers(parameters)
{
    loadingID = 'speakersloading';

    if ( DivSpeakers.className == 'hidden' &&
         DivSpeakers.innerHTML != '' &&
         parameters == CurrentSpeakerParameters ) {
        DivSpeakers.className = 'visible';
        DivSpeakersTab.className = 'visible';
    } else {
        CurrentSpeakerParameters = parameters;
        if ( document.getElementById(loadingID) &&
             document.getElementById(loadingID).className == 'visible' ) {
            return;
        }
        DivSpeakers.className = 'hidden';
        DivSpeakersTab.className = 'hidden';

        if (document.getElementById(loadingID)) {
            document.getElementById(loadingID).className = 'visible';
        } else {
            n = document.createElement('div');
            n.innerHTML = MSG_LOADING;
            n.id = 'speakersloading';
            n.className = 'visible';
            p = DivSpeakers.parentNode;
            p.insertBefore(n,DivSpeakers);
        }

        url = 'speakers.php' + parameters;
        startRequest(handleGetSpeakersReady, url, null, null);
    }

} /* getSpeakers */

/*------------------------------------------------------------------------
 * FUNCTION getTalk -- download and display talk contents from server
 */
function getTalk(parameters, target)
{
    // If the advanced search window is visible, hide it.
    hideAdvanced();

    if (parameters == '') {
        url = 'about.php';
    } else {
        url = 'gettalk.php' + parameters;

        var pattern = /^\?vol=([0-9]*)&disc=([0-9]*)$/;
        if (parameters.match(pattern)) {
            vol = parameters.match(pattern)[1];
            disc = parameters.match(pattern)[2];
            TalkId = (vol * 10000) + (disc * 1);
        } else {
            TalkId = parameters.replace(/^\?ID=/, '');
            TalkId = TalkId.replace(/&.*$/, '');
        }
    }
    startRequest(handleGetTalkReady, url, 'tContent', parameters);

} /* getTalk */

/*------------------------------------------------------------------------
 * FUNCTION getTopics -- handle access to the topical index tree
 *
 * There are two types of nodes: index nodes and citation nodes.  An
 * index node has a +/- symbol next to it, letting the user expand or
 * contract it.  Citation nodes display lowest-level topics with
 * hyperlinks to related documents.
 */
function getTopics(parameters, id)
{
    var target = 'topicdest';
    if (parameters == '') {
        target = '';
    }

    if (id == '') {
        id = 'topics';
        if (document.getElementById('scriptures').className != 'hidden') {
            adjustHeights('', '', 'scriptures');
        }
    }

    var span = document.getElementById(id + '-span');
    var topicDiv = document.getElementById(id);

    if (parameters.match('y=n') != null) {
        span.className = 'hiddenspan';
        toggleClosed(topicDiv);
    } else if (span != null && span.className == 'hiddenspan') {
        span.className = 'visiblespan';
        toggleOpen(topicDiv);
    } else {
        var topicClass = topicDiv.className;
        topicDiv.className = 'hidden';
        n = document.createElement('div');
        n.innerHTML = MSG_LOADING;
        n.id = id + 'loading';

        if (topicClass != 'hidden') {
            n.className = topicClass;
        }

        p = topicDiv.parentNode;
        p.insertBefore(n, topicDiv);

        url = 'topics.php' + parameters;

        topicParameters = new Object();
        topicParameters.id = id;
        topicParameters.div = topicDiv;
        topicParameters.className = topicClass;
        startRequest(handleGetTopicsReady, url, null, topicParameters);
    }

} /* getTopics */

/*------------------------------------------------------------------------
 * FUNCTION goTalkHome -- load the talks home content into the talks panel
 */
function goTalkHome()
{
    getTalk('', 'dest');
    var crumbs = document.getElementById('talk-crumbs');
    if (crumbs) {
        crumbs.innerHTML = 'Table of Contents: &nbsp; ' +
        '<a href="javascript:void(0);" onclick="getNav(\'?jod=yes\',\'\');" ' +
           'class="topLink">Journal of Discourses</a> &nbsp;&middot;&nbsp; <a href="javascript:void(0);" onclick="getNav(\'?gc=yes\',\'\');"' +
           'class="topLink">General Conference</a> &nbsp;&middot;&nbsp; <a href="/stpjs.html" target="_blank" class="topLink" title="Scriptural Teachings of the Prophet Joseph Smith">STPJS (html,</a><a href="/tpjs/STPJS.pdf" target="_blank" class="topLink" title="Scriptural Teachings of the Prophet Joseph Smith"> pdf)</a>';
    }

} /* goTalkHome */

/*------------------------------------------------------------------------
 * FUNCTION gotoElement -- scroll page to show the identified element
 */
function gotoElement(element, container)
{
    var y = 0;
    if (element != '') {
        if (container == 'scriptures') {
            // Scroll to a scripture
            y = getAnchorPositionPageOffsetTop(document.getElementById(element));
        } else {
            if (element.substring(0, 8) == 'footnote') {
                // Scroll to a footnote
                var cs = getAnchorPosition(element, true);
                y = cs.y;
            } else {
                // Scroll to a citation in a talk
                var cs = getAnchorPosition(element, false);
                y = cs.y;
            }
        }

        var ap = getAnchorPosition(container, false);
        var offset = ap.y;
        var distance = y - offset;

        var con = document.getElementById(container);
        con.scrollTop = distance;
    }

} /* gotoElement */

/*------------------------------------------------------------------------
 * FUNCTION handleCitationsReady -- process citations result string
 */
function handleCitationsReady(result, citationDivId, failed)
{
    d = document.getElementById(citationDivId);

    if (citationDivId == 'citations') {
        changeDiv1(result, citationDivId, d);
    } else {
        changeDiv2(result, citationDivId, d2);
    }

    logResult('Citations Request', citationDivId, failed);

} /* handleCitationsReady */

/*------------------------------------------------------------------------
 * FUNCTION handleGetNavReady -- process table-of-contents result string
 */
function handleGetNavReady(result, navParameters, failed)
{
    var pattern = /<div id=\"ldsurl\" style=\"display:none\">(.*)<\/div>/;
    var openUrl = '';
    var printUrl = '';
    if (result.match(pattern)) {
        openUrl = '&nbsp;<a href="'
               + result.match(pattern)[1]
               + '" target="_blank" '
               + 'title="Open Talk from LDS.org">Open</a>&nbsp;';
        printUrl = '&nbsp;<a href="'
               + 'printPage.php?url='
               + escape(result.match(pattern)[1])
               + '" target="_blank" '
               + 'title="Print Talk">Print</a>&nbsp;';
        similarUrl = '&nbsp;<a href="javascript:void(0)" onclick="similar(0)" '
               + 'title="Find Similar Talks">Similar</a>&nbsp;';
    } else {
        openUrl = '&nbsp;Open&nbsp;';
        printUrl = '&nbsp;Print&nbsp;';
        similarUrl = '&nbsp;Similar&nbsp;';
    }
    result = result.replace(pattern, '');

    var pattern2 = /<div id=\"talkcrumb\" style=\"display:none\">(.*)<\/div>/;
    var talkcrumb = '';
    if (result.match(pattern2)) {
        talkcrumb = result.match(pattern2)[1];
        document.getElementById('talk-crumbs').innerHTML = talkcrumb;
    }
    result = result.replace(pattern2, '');

    var contentDiv = document.getElementById('tContent');
    if (contentDiv) {
        var topen = document.getElementById('talk-open');
        topen.innerHTML = openUrl;
        var tprint = document.getElementById('talk-print');
        tprint.innerHTML = printUrl;
        var tsimilar = document.getElementById('talk-similar');
        tsimilar.innerHTML = similarUrl;
        contentDiv.innerHTML = result;
    } else {
        alert('Unable to display result -- please reload page.');
    }

    if (navParameters && navParameters.navId != '') {
        gotoElement(navParameters.navId, 'talks');
    }

    logResult('Table of Contents',
              (navParameters && navParameters.parameters) ? navParameters.parameters : '',
              failed);

} /* handleGetNavReady */

/*------------------------------------------------------------------------
 * FUNCTION handleGetSpeakersReady -- process speakers result string
 */
function handleGetSpeakersReady(result, parameters, failed)
{
    if ( DivTopics.className == 'hidden' &&
         DivCitations.className == 'hidden' &&
         DivResults.className == 'hidden' ) {
        DivSpeakers.className = 'visible';
        DivSpeakersTab.className = 'visible';
    }
    DivSpeakers.innerHTML = result;
    document.getElementById(loadingID).className = 'hidden';

    logResult('Speakers Request', '', failed);

} /* handleGetSpeakersReady */

/*------------------------------------------------------------------------
 * FUNCTION handleGetScriptureReady -- process scripture result string
 */
function handleGetScriptureReady(result, id, failed)
{
    var sContent = document.getElementById('sContent');
    sContent.innerHTML = '';
    sContent.innerHTML = result;

    pattern = /<div id=\"scripcrumb\" style=\"display:none\">(.*)<\/div>/;
    var scripcrumb = '';
    if (result.match(pattern)) {
        scripcrumb = result.match(pattern)[1];
        document.getElementById('scrip-crumbs').innerHTML = scripcrumb;
    }

    pattern = /<div id=\"scripurl\" style=\"display:none;\">(.*?)<\/div>/;
    var ldsurl = '';
    if (result.match(pattern)) {
        ldsurl = result.match(pattern)[1];
    }

    var sOpen = document.getElementById('scrip-open');
    sOpen.innerHTML = '&nbsp;<a href="' + ldsurl + '" '
                    + 'target="_blank" '
                    + 'title="Open Scripture from LDS.org">Open</a>&nbsp;';

    if (result.match('id="match"')) {
        gotoElement('match', 'scriptures');
    }

    logResult('Scripture Request', id, failed);

} /* handleGetScriptureReady */

/*------------------------------------------------------------------------
 * FUNCTION handleGetSearchRequest -- process search results string
 */
function handleGetSearchRequest(result, parameters, failed)
{
    var resultString = '';

    resultObject = eval('(' + result + ')');
    resultList = resultObject.resultList;

    if (resultObject.resultCount <= 0) {
        DivResults.innerHTML = '<div class="qSummary">No documents match ' +
            resultObject.resultDescription + ' (<b>' +
            resultObject.queryDuration +
            '</b> seconds)</div>';
        return;
    }

    resultString = '<div class="qSummary">Results <b>' +
                   (SearchOffset + 1) + ' - ' +
                   (SearchOffset + resultList.length) + '</b> of <b>' +
                   resultObject.resultCount + '</b> for ' +
                   resultObject.resultDescription + ' (<b>' +
                   resultObject.queryDuration +
                   '</b> seconds)</div>';
    var regExp = new RegExp("<font[^>]*>([^<]*)<\/font>", "ig");
    var matches = resultObject.resultDescription.match(regExp);
    var highlightString = "";
    if (matches) {
        for (var i = 0; i < matches.length; i++) {
            if (i > 0) {
                highlightString += " ";
            }
            highlightString += matches[i].replace(/\s*["]*\s*<\/font>/ig, '').replace(/<font [^>]*>\s*["]*\s*/ig, '').replace(/ /g, ':');
        }
    }
    // highlight = resultObject.resultDescription.replace(/<\/font>/ig, '').replace(/<font [^>]*>/ig, '');

    for (var i = 0; i < resultList.length; i++) {
        resultString += '<div class="qResult">' + (SearchOffset + i + 1) + '. ';
        switch (resultList[i].corpus) {
        case 'S':
        case 'Q':
        case 'R':
            scripParams = resultList[i].params.split('|');
            resultString += '<a onclick="getScripture(\'?book=' + scripParams[1];
            if (scripParams[2] != "null") {
                resultString += '&chap=' + scripParams[2];
            }
            if (scripParams[3] != "null") {
                resultString += '&verses=' + scripParams[3];
            }
            if (scripParams[4] != "null") {
                resultString += '&jst=' + scripParams[4];
            }
            resultString += '&highlight=' + highlightString;
            resultString += '\',\'';
            if (scripParams[3] != "null") {
                resultString += scripParams[3];
            }
            resultString += '\')" href="javascript:void(0);">';
            break;
        case 'G':
        case 'E':
        case 'J':
            resultString += '<a onclick="getTalk(\'?ID=' + resultList[i].id +
                            '&highlight=' + highlightString +
                            '\',\'dest\')" href="javascript:void(0);">';
            break;
        case 'T':
            resultString += '<a title="Teachings of the Prophet Joseph Smith, p. ' +
                            resultList[i].params +
                            '" target="_blank" href="/stpjs.html#' +
                            resultList[i].params.replace(/^0+/, '') +
                            // '&search=' + "'" + highlight + "'" +
                            '">';
            break;
        }
        resultString += resultList[i].title +
                        '</a><blockquote class="qContext">' +
                        resultList[i].context +
                        '</blockquote></div>';
    }

    resultString += computeNavigation(resultObject, parameters);

    DivResults.innerHTML = resultString;

} /* handleGetSearchRequest */

/*------------------------------------------------------------------------
 * FUNCTION handleGetSimilarRequest -- process similar results string
 */
function handleGetSimilarRequest(result, parameters, failed)
{
    var resultString = '';

    resultObject = eval('(' + result + ')');
    resultList = resultObject.resultList;

    if (resultObject.resultCount <= 0) {
        DivResults.innerHTML = '<div class="qSummary">No documents match ' +
            resultObject.resultDescription + ' (<b>' +
            resultObject.queryDuration +
            '</b> seconds)</div>';
        return;
    }

    resultString = '<div class="qSummary">Similar talks <b>' +
                   (SearchOffset + 1) + ' - ' +
                   (SearchOffset + resultList.length) + '</b> of <b>' +
                   resultObject.resultCount + '</b> (<b>' +
                   resultObject.queryDuration +
                   '</b> seconds)</div>';

    for (var i = 0; i < resultList.length; i++) {
        resultString += '<div class="qResult">' + (SearchOffset + i + 1) + '. ';
        switch (resultList[i].corpus) {
        case 'S':
        case 'Q':
        case 'R':
            scripParams = resultList[i].params.split('|');
            resultString += '<a onclick="getScripture(\'?book=' + scripParams[1];
            if (scripParams[2] != "null") {
                resultString += '&chap=' + scripParams[2];
            }
            if (scripParams[3] != "null") {
                resultString += '&verses=' + scripParams[3];
            }
            if (scripParams[4] != "null") {
                resultString += '&jst=' + scripParams[4];
            }
            resultString += '\',\'';
            if (scripParams[3] != "null") {
                resultString += scripParams[3];
            }
            resultString += '\')" href="javascript:void(0);">';
            break;
        case 'G':
        case 'E':
        case 'J':
            resultString += '<a onclick="getTalk(\'?ID=' + resultList[i].id +
                            '\',\'dest\')" href="javascript:void(0);">';
            break;
        case 'T':
            resultString += '<a title="Teachings of the Prophet Joseph Smith, p. ' +
                            resultList[i].params +
                            '" target="_blank" href="/stpjs.html#' +
                            resultList[i].params.replace(/^0+/, '') +
                            '">';
            break;
        }
        resultString += resultList[i].title +
                        '</a><blockquote class="qContext">' +
                        resultList[i].context +
                        '</blockquote></div>';
    }

    resultString += computeNavigationSimilar(resultObject);

    DivResults.innerHTML = resultString;

} /* handleGetSimilarRequest */

/*------------------------------------------------------------------------
 * FUNCTION handleGetTalkReady -- process talk result string
 */
function handleGetTalkReady(result, parameters, failed)
{
    var pattern = /<div id=\"ldsurl\" style=\"display:none\">(.*)<\/div>/;
    var openUrl = '';
    var printUrl = '';
    if (result.match(pattern)) {
        openUrl = '&nbsp;<a href="'
               + result.match(pattern)[1]
               + '" target="_blank" '
               + 'title="Open Talk from LDS.org">Open</a>&nbsp;';
        printUrl = '&nbsp;<a href="'
               + 'printPage.php?url='
               + escape(result.match(pattern)[1])
               + '" target="_blank" '
               + 'title="Print Talk">Print</a>&nbsp;';
        similarUrl = '&nbsp;<a href="javascript:void(0)" onclick="similar(0)" '
               + 'title="Find Similar Talks">Similar</a>&nbsp;';
    } else {
        openUrl = '&nbsp;Open&nbsp;';
        printUrl = '&nbsp;Print&nbsp;';
        similarUrl = '&nbsp;Similar&nbsp;';
    }
    result = result.replace(pattern, '');

    var pattern2 = /<div id=\"talkcrumb\" style=\"display:none\">(.*)<\/div>/;
    var talkcrumb = '';
    if (result.match(pattern2)) {
        talkcrumb = result.match(pattern2)[1];
        document.getElementById('talk-crumbs').innerHTML = talkcrumb;
    }
    result = result.replace(pattern2, '');

    var d = document.getElementById('tContent');
    d.innerHTML = result;

    var topen = document.getElementById('talk-open');
    topen.innerHTML = openUrl;

    var tprint = document.getElementById('talk-print');
    tprint.innerHTML = printUrl;

    var tsimilar = document.getElementById('talk-similar');
    tsimilar.innerHTML = similarUrl;

    if (result.match('id="dest"')) {
        gotoElement('dest', 'talks');
    }

    var pattern3 = /^[?]ID=([0-9]+)([&]CID=([0-9]+))?.*/;
    if (parameters.match(pattern3)) {
        // Talk ID
        s.prop2 = parameters.match(pattern3)[1];
        // Citation ID
        s.prop3 = parameters.match(pattern3)[3];
    }
    logResult('Talk Request', parameters, failed);

} /* handleGetTalkReady */

/*------------------------------------------------------------------------
 * FUNCTION handleGetTopicsReady -- process topics result string
 */
function handleGetTopicsReady(result, topicParameters, failed)
{
    if (topicParameters.id == 'topics') {
        changeDiv1( result, topicParameters.id,
                    document.getElementById(topicParameters.id) );
    } else {
        changeDiv2(result, topicParameters.id, topicParameters.className);
    }

    logResult('Topics Request', topicParameters, failed);

} /* handleGetTopicsReady */

/*------------------------------------------------------------------------
 * FUNCTION handleSubmitFeedbackReady -- process feedback result string
 */
function handleSubmitFeedbackReady(result, parameters, failed)
{
    document.getElementById('fbMsg').innerHTML =
                    'Feedback submitted successfully. Thank you!</div>';
    document.getElementById('fbSubmit').disabled = true;

    logResult('Feedback Submitted', '', failed);

} /* handleSubmitFeedbackReady */

/*------------------------------------------------------------------------
 * FUNCTION handleSyncRequestReady -- process sync request result string
 */
function handleSyncRequestReady(result, syncParms, failed)
{
    changeDiv2(result, syncParms.cID.id, syncParms.d2);
    gotoElement(syncParms.chapID, 'middle');

    logResult('Sync Request', syncParms.chapID, failed);

} /* handleSyncRequestReady */

/*------------------------------------------------------------------------
 * FUNCTION hideAdvanced -- hide the advanced search window
 */
function hideAdvanced()
{
   var advancedDiv = document.getElementById('advanced-search');

   advancedDiv.className = 'hidden';

} /* hideAdvanced */

/*------------------------------------------------------------------------
 * FUNCTION initSize -- size main reference div's to their initial size
 */
function initSize()
{
    var height = document.body.clientHeight - 90;
    document.getElementById('mainContent').style.height = height + 'px';
    document.getElementById('sidebar').style.height = height + 'px';
    document.getElementById('middle').style.height = (height - 41) + 'px';
    document.getElementById('h-splitter').style.height = height + 'px';
    document.getElementById('main').style.height = height + 'px';

} /* initSize */

/*------------------------------------------------------------------------
 * FUNCTION loadCookies -- load up all the filter cookies
 */
function loadCookies()
{
    CookYearStart = getCookie('start');
    CookYearEnd = getCookie('end');
    CookSortBy = getCookie('sortby');
    CookGroupBy = getCookie('groupby');
    CookSpeaker = getCookie('spkrFilter');
    CookCorpus = getCookie('corpus');

} /* loadCookies */

/*------------------------------------------------------------------------
 * FUNCTION logResult -- log page navigation result to Omniture
 */
function logResult(pageName, parameters, failed)
{
    if (failed) {
        s.pageName = 'Failed ' + pageName;
    } else {
        s.pageName = pageName;
    }
    // Ajax request detail
    s.prop1 = s.pageName + ' ' + parameters;
    void(s.t());
    s.prop1 = '';
    s.prop2 = '';
    s.prop3 = '';
    s.pageName = '';

} /* logResult */

/*------------------------------------------------------------------------
 * FUNCTION popup -- display the requested URL in a pop-up window
 */
function popup(url)
{
   if (PopupWindow) {
      PopupWindow.close();
   }
   PopupWindow = window.open(url, '',
                             'toolbar=0,scrollbars=0,location=0,' +
                             'statusbar=0,menubar=0,resizable=1,' +
                             'width=350,height=450,left=475,top=275');

} /* popup */

/*------------------------------------------------------------------------
 * FUNCTION printToPDF -- print the given request as a PDF in a new window
 */
function printToPDF()
{
    if (document.getElementById('printCheck').checked == true) {
        deleteCookie('pfrontmatter');
    } else {
        setCookie('pfrontmatter', 'no');
    }
    if (document.getElementById('printRadio1').checked == true) {
        deleteCookie('printall');
    } else {
        setCookie('printall', 'yes');
    }

    getPrint();
    var printWindow = window.open('citationspdf.php');

} /* printToPDF */

/*------------------------------------------------------------------------
 * FUNCTION query -- perform a free-form text search
 */
function query(offset)
{
    if (offset == null || offset == '') {
        offset = 0;
    }

    var inputField = document.getElementById('query');
    var corpusSelect = document.getElementById('corpus');
    var inputText = inputField.value;
    var trimmed = inputText.replace(/^\s+|\s+$/g, '') ;
    if (trimmed != inputText) {
        inputField.value = trimmed;
    }
    if (trimmed == '') {
        return;
    }
    var chosenCorpus = corpusSelect.options[corpusSelect.selectedIndex].value;
    if (chosenCorpus == '') {
        chosenCorpus = 'talks';
    }
    showResults();
    SearchOffset = offset;
    ListOffset = 0;
    startRequest( handleGetSearchRequest,
                  'search.php?corpus=' + chosenCorpus +
                      '&query=' + encodeURIComponent(inputField.value) +
                      '&offset=' + offset +
                      '&maxResults=' + 20,
                  'results', '' );

} /* query */

/*------------------------------------------------------------------------
 * FUNCTION resetFeedback -- reset the feedback form to its cleared state
 */
function resetFeedback(clearMessage)
{
    document.getElementById('fbType').selectedIndex = 0;
    document.getElementById('fbSubject').value = '';
    document.getElementById('fbText').value = '';
    document.getElementById('fbEmail').value =
            'Enter your email address for a response on your feedback';
    document.getElementById('fbSubmit').disabled = false;
    if (clearMessage) {
        document.getElementById('fbMsg').innerHTML = '';
    }

} /* resetFeedback */

/*------------------------------------------------------------------------
 * FUNCTION selectAll -- select all corpora in advanced search
 */
function selectAll()
{
    var chkGenConf = document.getElementById('genConf');
    var chkImpEra = document.getElementById('impEra');
    var chkJoD = document.getElementById('jod');
    var chkStpjs = document.getElementById('stpjs');

    chkGenConf.checked = true;
    chkImpEra.checked = true;
    chkJoD.checked = true;
    chkStpjs.checked = true;

} /* selectAll */

/*------------------------------------------------------------------------
 * FUNCTION selectNone -- deselect all corpora in advanced search
 */
function selectNone()
{
    var chkGenConf = document.getElementById('genConf');
    var chkImpEra = document.getElementById('impEra');
    var chkJoD = document.getElementById('jod');
    var chkStpjs = document.getElementById('stpjs');

    chkGenConf.checked = false;
    chkImpEra.checked = false;
    chkJoD.checked = false;
    chkStpjs.checked = false;

} /* selectNone */

/*------------------------------------------------------------------------
 * FUNCTION setCookie -- set cookie in browser
 */
function setCookie(name, value)
{
    var date = new Date();
    date.setTime(date.getTime() + (7 * 24 * 60 * 60 * 1000));
    document.cookie = name + '=' + escape(value) + '; path=/' +
                      '; expires=' + date.toGMTString();

} /* setCookie */

/*------------------------------------------------------------------------
 * FUNCTION showAdvanced -- show the advanced search window
 */
function showAdvanced()
{
   var advancedDiv = document.getElementById('advanced-search');

   advancedDiv.className = 'visible';

} /* showAdvanced */

/*------------------------------------------------------------------------
 * FUNCTION similar -- perform a similar-talk search
 */
function similar(offset)
{
    if (offset == null || offset == '') {
        offset = 0;
    }

    showResults();
    SearchOffset = offset;
    ListOffset = 0;
    startRequest( handleGetSimilarRequest,
                  'similar.php?id=' + TalkId +
                      '&offset=' + offset +
                      '&maxResults=' + 20,
                  'results', '' );

} /* similar */

/*------------------------------------------------------------------------
 * FUNCTION sortGCNav -- request general conference table of contents
 */
function sortGCNav(targ, selObj)
{
    var value = selObj.options[selObj.selectedIndex].value;
    if (value != 'Select One') {
        getNav('?gc=yes&sort=' + value, '');
    }

} /* sortGCNav */

/*------------------------------------------------------------------------
 * FUNCTION SortJODNav -- request Journal of Discourses table of contents
 */
function sortJODNav(targ, selObj)
{
    var value = selObj.options[selObj.selectedIndex].value;
    if (value != 'Select One') {
        getNav('?jod=yes&sort=' + value, '');
    }

} /* sortJODNav */

/*------------------------------------------------------------------------
 *                      XML HTTP REQUEST FUNCTION
 *
 * Note that we now rely on the XMLHttpRequest wrapper from Sergey Ilinsky
 * to process async requests more robustly.
 *
 * See http://www.ilinsky.com/articles/XMLHttpRequest/ for details.
 */
function startRequest(eventHandler, url, messageDiv, requestParameters)
{
    if (messageDiv) {
        var msgDiv = document.getElementById(messageDiv);
        if (msgDiv) {
            msgDiv.innerHTML = MSG_LOADING;
        }
    }

    var handleSuccess = function(o) {
        if (o.getAllResponseHeaders == undefined) {
            var callback2 = {
                success: handleSuccess2,
                failure: handleFailure,
                timeout: 60000
            };

            transaction.abort(o, null, true);
            var transaction2 = YAHOO.util.Connect.asyncRequest('GET', url, callback2);
        } else {
            eventHandler(o.responseText, requestParameters, false);
        }
    }

    var handleSuccess2 = function(o) {
        if (o.status !== undefined && o.status != 200) {
            alert("success status: " + o.status);
        }
        if (o.getAllResponseHeaders == undefined) {
            alert('Unable to process server request (twice). Please reload the page and try again.');
        } else {
            eventHandler(o.responseText, requestParameters, false);
        }
    }

    var handleFailure = function(o) {
        alert('Unable to process server request. Please reload the page and try again.');
    }

    var callback = {
        success: handleSuccess,
        failure: handleFailure,
        timeout: 30000
    };

    var transaction = YAHOO.util.Connect.asyncRequest('GET', url, callback);

    return true;

} /* startRequest */

/*------------------------------------------------------------------------
 * FUNCTION showCitations -- ensure that the citations panel is visible
 * and the others are hidden in the left column
 */
function showCitations()
{
    DivSpeakers.className = 'hidden';
    DivSpeakersTab.className = 'hidden';
    DivOptions.className = 'hidden';
    DivOptionsTab.className = 'hidden';
    DivResults.className = 'hidden';
    DivResultsTab.className = 'hidden';
    DivTopics.className = 'hidden';
    DivTopicsTab.className = 'hidden';
    DivPrint.className = 'hidden';
    DivPrintTab.className = 'hidden';
    DivFeedback.className = 'hidden';
    DivFeedbackTab.className = 'hidden';

    DivCitations.className = 'visible';
    DivCitationsTab.className = 'visible';

} /* showCitations */

/*------------------------------------------------------------------------
 * FUNCTION showResults -- show the query results panel
 */
function showResults()
{
    DivPrint.className = 'hidden';
    DivPrintTab.className = 'hidden';
    DivCitations.className = 'hidden';
    DivCitationsTab.className = 'hidden';
    DivFilter.className = 'hidden';
    DivSpeakers.className = 'hidden';
    DivSpeakersTab.className = 'hidden';
    DivOptions.className = 'hidden';
    DivOptionsTab.className = 'hidden';
    DivTopics.className = 'hidden';
    DivTopicsTab.className = 'hidden';

    DivResults.className = 'visible';
    DivResultsTab.className = 'visible';

} /* showResults */

/*------------------------------------------------------------------------
 * FUNCTION showSpeakers -- ensure that the citations panel is visible
 * and the others are hidden in the left column
 */
function showSpeakers(parameters)
{
    DivCitations.className = 'hidden';
    DivCitationsTab.className = 'hidden';
    DivOptions.className = 'hidden';
    DivOptionsTab.className = 'hidden';
    DivResults.className = 'hidden';
    DivResultsTab.className = 'hidden';
    DivTopics.className = 'hidden';
    DivTopicsTab.className = 'hidden';
    DivPrint.className = 'hidden';
    DivPrintTab.className = 'hidden';
    DivFeedback.className = 'hidden';
    DivFeedbackTab.className = 'hidden';

    DivSpeakersTab.className = 'visible';

    if ( DivSpeakers.innerHTML == '' ||
         parameters != CurrentSpeakerParameters ) {
        getSpeakers(parameters);
    }

    DivSpeakers.className = 'visible';

} /* showSpeakers */

/*------------------------------------------------------------------------
 * FUNCTION submitFeedback -- record user feedback in the database
 */
function submitFeedback()
{
    var selObj = document.getElementById('fbType');
    var type = selObj.options[selObj.selectedIndex].value;
    var subject = document.getElementById('fbSubject').value;
    var text = document.getElementById('fbText').value;
    var email = document.getElementById('fbEmail').value;

    if (email.match(/Enter your Email address/)) {
        email = '';
    }

    var params = '?type=' + encodeURIComponent(type) +
                 '&subject=' + encodeURIComponent(subject) +
                 '&msg=' + encodeURIComponent(text) +
                 '&email=' + encodeURIComponent(email);
    params = params.replace(/'/, "''");

    url = 'feedback.php' + params;

    startRequest(handleSubmitFeedbackReady, url, null, null);

} /* submitFeedback */

/*------------------------------------------------------------------------
 * FUNCTION sync -- synchronize table of contents to given reference
 *
 * The chapter may be empty for special cases like BM Title Page.
 * The volume and book must both be non-empty.
 */
function sync(el, volume, book, chap)
{
    var volID = 'v' + volume;
    var bookID = 'b' + book.replace(/ /g, '_');
    var chapID = 'c' + chap + ',' + book.replace(/ /g, '_');

    // Ensure that the scripture citation panel is visible
    showCitations();
    displayFilterInfo();

    // Collapse all open nodes except those leading to the target
    collapseAllExcept(volID, bookID, chapID);

    var alreadyOpen = false;

    try {
        var volEl = document.getElementById(volID);
        var bookEl = document.getElementById('b' + book.replace(/ /g, '_'));
        var chapEl = document.getElementById(chapID);

        var bookSpanID = bookID + '-span';
        var chapSpanID = chapID + '-span';
        var volSpanID = volID + '-span';
        var cID = '';
        var container = volEl;
        var d2 = null;
        var level = '';
        var url = 'citationsSync.php?vol=' + volume + '&book=' + book +
                  '&chap=' + chap + '&level=';

        // Remember that we're opening a particular volume/book/chapter combination
        if (chap == '') {
            cookie = volID + '|' + bookID + '|';
        } else {
            cookie = volID + '|' + bookID + '|' + chapID + '|';
        }
        setCookie('volume', cookie);

        var volSpan = document.getElementById(volSpanID);
        if ( volSpan && volSpan.innerHTML != '' ) {
            // Found the volume -- need to display it
            if (volSpan.className == 'hiddenspan') {
                toggleOpen(volEl);
                volSpan = document.getElementById(volSpanID);
                volSpan.className = 'visiblespan';
            }

            // Now process chapter and book levels
            if (chap != '') {
                // See if we can locate the chapter's span in the DOM tree
                // (only applies to targets that include a chapter)
                var chapSpan = document.getElementById(chapSpanID);
                if ( chapSpan && chapSpan.innerHTML != '' ) {
                    // Found the chapter -- no need to fetch it again
                    if (chapSpan.className == 'hiddenspan') {
                        toggleOpen(chapEl);
                        chapSpan = document.getElementById(chapSpanID);
                        chapSpan.className = 'visiblespan';
                    }
                    alreadyOpen = true;
                } else {
                    // Didn't find the chapter -- need to fetch it
                    level = 'chap';
                    cID = chapEl;
                }

                if (chapEl != null) {
                    // Show the book-level node that already exists
                    var bookSpan = document.getElementById(bookSpanID);
                    if (bookSpan.className == 'hiddenspan') {
                        toggleOpen(bookEl);
                        bookSpan = document.getElementById(bookSpanID);
                        bookSpan.className = 'visiblespan';
                    }
                } else {
                    // Didn't find the book -- need to fetch it
                    // (this overrides the chapter-level request above)
                    level = 'book';
                    cID = bookEl;
                }
            } else {
                // We're not looking for a chapter -- just a book
                var bookSpan = document.getElementById(bookSpanID);
                if (bookSpan && bookSpan.innerHTML != '') {
                    // Found the book -- no need to fetch it again
                    if (bookSpan.className == 'hiddenspan') {
                        toggleOpen(bookEl);
                        bookSpan = document.getElementById(bookSpanID);
                        bookSpan.className = 'visiblespan';
                    }
                    alreadyOpen = true;
                } else {
                    // Didn't find the book -- need to fetch it
                    level = 'book';
                    cID = bookEl;
                }
            }
        } else {
            // Didn't find the volume span -- need to fetch it
            level = 'vol';
            cID = volEl;
        }

        if (alreadyOpen) {
            gotoElement(chapID, 'middle');
        } else {
            url += level;
            container = document.getElementById(cID.id);
            d2 = container.className;

            // Hide the container while we request its new contents
            container.className = 'hidden';

            // Temporarily show a loading message in its place
            n = document.createElement('div');
            n.innerHTML = MSG_LOADING;
            n.id = cID.id + 'loading';
            n.className = d2;
            p = container.parentNode;
            p.insertBefore(n, container);
            gotoElement(cID.id + 'loading', 'middle');

            syncParms = new Object();
            syncParms.d2 = d2;
            syncParms.cID = cID;
            syncParms.chapID = chapID;
            startRequest(handleSyncRequestReady, url, null, syncParms);
        }
    } catch (e) {
        // If there is an error, do nothing.
    }

} /* sync */

/*------------------------------------------------------------------------
 * FUNCTION syncCollapse -- collapse a node in the citation tree
 */
function syncCollapse(id)
{
    elSpan = document.getElementById(id);
    if (elSpan != null) {
        // Hide the given span
        elSpan.className = 'hiddenspan';

        // Mark its parent node as collapsed
        toggleClosed(elSpan.parentNode);
    }

} /* syncCollapse */

/*------------------------------------------------------------------------
 * FUNCTION toggleClosed -- change HTML string to closed status
 */
function toggleClosed(elem)
{
    elem.innerHTML =
            elem.innerHTML.replace(/y=n/, 'y=y').replace(/minus.gif/, 'plus.gif');

} /* toggleClosed */

/*------------------------------------------------------------------------
 * FUNCTION toggleOpen -- change HTML string to open status
 */
function toggleOpen(elem)
{
    // NEEDSWORK: would it be any faster to use DOM manipulation
    // operations directly here, or is innerHTML just as fast?
    elem.innerHTML =
            elem.innerHTML.replace(/y=y/, 'y=n').replace(/plus.gif/, 'minus.gif');

} /* toggleOpen */

/*------------------------------------------------------------------------
 * FUNCTION toggleScripDisplay -- if the scriptures window is visible,
 *                                hide it, and if it's hidden, show it
 */
function toggleScripDisplay(clientHeight, eventType)
{
    talksDiv = document.getElementById('talks');
    talkHolder = document.getElementById('talk-holder');
    scripsDiv = document.getElementById('scriptures');
    scripHolder = document.getElementById('scrip-holder');

    if (scripsDiv.className == 'hidden' && eventType == null) {
        var cookieHeight = getCookie('height');
        var splitY = clientHeight * cookieHeight;

        if (splitY > (clientHeight - MIN_BOTTOM_HEIGHT)) {
            splitY = (clientHeight - MIN_BOTTOM_HEIGHT);
        }

        if (splitY < MIN_TOP_HEIGHT) {
            splitY = MIN_TOP_HEIGHT;
        }

        talkHolder.style.height = splitY + 'px';
        talksDiv.style.height = (splitY - MENU_BAR_HEIGHT) + 'px';

        scripHolder.style.height = (clientHeight - splitY) + 'px';
        scripHolder.style.top = splitY + 'px';
        scripsDiv.style.height =
            (clientHeight - splitY - MENU_BAR_HEIGHT - SPLITTER_SIZE) + 'px';

        scripsDiv.className = 'visible';
        setCookie('hidden', '');

        document.getElementById('sMenu-toggle').innerHTML =
                    '<a href="javascript:void(0)" onClick=' +
                    '"adjustHeights(\'\', \'\', \'scriptures\');" ' +
                    'title="Hide Scripture Window">Hide</a>&nbsp;';

    } else {
        if (talksDiv.className == 'hidden') {
            var tempTop = clientHeight - MENU_BAR_HEIGHT - SPLITTER_SIZE;
            toggleTalkDisplay(clientHeight);

            scripsDiv.style.height =
                ( clientHeight - ((clientHeight - SPLITTER_SIZE) / 2) -
                  MENU_BAR_HEIGHT - SPLITTER_SIZE ) + 'px';
        }
        scripsDiv.className = 'hidden';
        setCookie('hidden', 'scriptures');
        with (scripHolder.style) {
            var splitterTop = clientHeight - MENU_BAR_HEIGHT - SPLITTER_SIZE;
            top = splitterTop + 'px';
            talkHolder.style.height = top;
            talksDiv.style.height = (splitterTop - MENU_BAR_HEIGHT) + 'px';
        }

        document.getElementById('sMenu-toggle').innerHTML =
                    '<a href="javascript:void(0)" onClick=' +
                    '"adjustHeights(\'\', \'\', \'scriptures\');" ' +
                    'title="Show Scripture Window">Show Scripture</a>&nbsp;';
    }

} /* toggleScripDisplay */

/*------------------------------------------------------------------------
 * FUNCTION toggleTalkDisplay -- if the talk is visible, hide it, and if
 *                               it's hidden, show it
 */
function toggleTalkDisplay(clientHeight, eventType)
{
    talksDiv = document.getElementById('talks');
    talkHolder = document.getElementById('talk-holder');
    scripsDiv = document.getElementById('scriptures');
    scripHolder = document.getElementById('scrip-holder');

    if (talksDiv.className == 'hidden' && eventType == null) {
        var cookieHeight = getCookie('height');
        var splitY = clientHeight * cookieHeight;

        if (splitY > (clientHeight - MIN_BOTTOM_HEIGHT)) {
            splitY = (clientHeight - MIN_BOTTOM_HEIGHT);
        }

        if (splitY < MIN_TOP_HEIGHT) {
            splitY = MIN_TOP_HEIGHT;
        }

        talkHolder.style.height = splitY + 'px';
        talksDiv.style.height = (splitY - MENU_BAR_HEIGHT) + 'px';

        if (!dragOffsetHeight) {
            dragOffsetHeight = document.getElementById('main').offsetHeight;
        }
        scripHolder.style.height = (dragOffsetHeight - splitY) + 'px';
        scripHolder.style.top = splitY + 'px';
        scripsDiv.style.height = ( clientHeight - splitY - MENU_BAR_HEIGHT -
                                   SPLITTER_SIZE ) + 'px';

        talksDiv.className = 'visible';
        setCookie('hidden', '');

        document.getElementById('tMenu-toggle').innerHTML =
                    '<a href="javascript:void(0)" onClick=' +
                    '"adjustHeights(\'\', \'\', \'talks\');" ' +
                    'title="Hide Talk Window">Hide</a>&nbsp;';
    } else {
        setCookie('hidden', 'talks');

        if (scripsDiv.className == 'hidden') {
            var tempTop = MENU_BAR_HEIGHT;
            toggleScripDisplay(clientHeight);

            scripsDiv.style.height = ( clientHeight - MENU_BAR_HEIGHT -
                                       MENU_BAR_HEIGHT - SPLITTER_SIZE ) + 'px';
        }

        talksDiv.className = 'hidden';
        setCookie('hidden', 'talks');
        with (scripHolder.style) {
            top = MENU_BAR_HEIGHT + 'px';
            height = (clientHeight - MENU_BAR_HEIGHT) + 'px';
        }

        scripsDiv.style.height = ( clientHeight - MENU_BAR_HEIGHT -
                                   MENU_BAR_HEIGHT - SPLITTER_SIZE ) + 'px';

        document.getElementById('tMenu-toggle').innerHTML =
                    '<a href="javascript:void(0)" ' +
                    'onClick="adjustHeights(\'\', \'\', \'talks\');" ' +
                    'title="Show Talk Window">Show Talk</a>&nbsp;';
    }

} /* toggleTalkDisplay */

/*------------------------------------------------------------------------
 * FUNCTION useScriptures -- show the scripture citation panel
 */
function useScriptures()
{
    showCitations();
    displayFilterInfo();

} /* useScriptures */

/*------------------------------------------------------------------------
 * FUNCTION useTopics -- show the topical index panel
 */
function useTopics()
{
    DivPrint.className = 'hidden';
    DivPrintTab.className = 'hidden';
    DivCitations.className = 'hidden';
    DivCitationsTab.className = 'hidden';
    DivFilter.className = 'hidden';
    DivSpeakers.className = 'hidden';
    DivSpeakersTab.className = 'hidden';
    DivOptions.className = 'hidden';
    DivOptionsTab.className = 'hidden';
    DivResults.className = 'hidden';
    DivResultsTab.className = 'hidden';

    DivTopics.className = 'visible';
    DivTopicsTab.className = 'visible';

    if (DivTopics.innerHTML == '') {
        getTopics('', '');
    }

} /* useTopics */

//*****************************************************************************
// Do not remove this notice.
//
// Portions of the following code Copyright 2001 by Mike Hall.
// See http://www.brainjar.com for terms of use.
//*****************************************************************************

// Determine browser and version.
function Browser()
{
    var ua, srch, i;

    this.isIE    = false;
    this.isNS    = false;
    this.version = null;

    ua = navigator.userAgent;

    srch = 'MSIE';
    if ((i = ua.indexOf(srch)) >= 0) {
        this.isIE = true;
        this.version = parseFloat(ua.substr(i + s.length));
        return;
    }

    srch = 'Netscape6/';
    if ((i = ua.indexOf(srch)) >= 0) {
        this.isNS = true;
        this.version = parseFloat(ua.substr(i + s.length));
        return;
    }

    // Treat any other 'Gecko' browser as NS 6.1.
    srch = 'Gecko';
    if ((i = ua.indexOf(srch)) >= 0) {
        this.isNS = true;
        this.version = 6.1;
        return;
    }

} /* Browser */

function hDragGo(event)
{
    var main = document.getElementById('main');
    var x, y;

    // Get cursor position with respect to the page.
    if (browser.isIE) {
        x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
    }
    if (browser.isNS) {
        x = event.clientX + window.scrollX;
    }

    // Move drag element by the same amount the cursor has moved.
    dragMovePx = (dragObj.elStartLeft + x - dragObj.cursorStartX);

    if (browser.isIE) {
        dragInnerWidth = document.body.clientWidth;
    } else {
        dragInnerWidth = window.innerWidth;
    }

    if (dragMovePx < MIN_LEFT_WIDTH) {
        adjustWidths( dragObj.elNode, MIN_LEFT_WIDTH, dragInnerWidth );
    } else if (dragMovePx > (dragInnerWidth - MIN_RIGHT_WIDTH)) {
        adjustWidths( dragObj.elNode, (dragInnerWidth - MIN_RIGHT_WIDTH),
                      dragInnerWidth );
    } else {
        adjustWidths( dragObj.elNode, dragMovePx, dragInnerWidth );
    }

    if (browser.isIE) {
        window.event.cancelBubble = true;
        window.event.returnValue = false;
    }
    if (browser.isNS) {
        event.preventDefault();
    }

} /* hDragGo */

function hDragStart(event, id)
{
    var el;
    var x, y;
    if (browser.isIE) {
        dragInnerWidth = (document.body.clientWidth) + 4;
    } else {
        dragInnerWidth = window.innerWidth;
    }

    // If an element id was given, find it. Otherwise use the element being
    // clicked on.

    if (id) {
        dragObj.elNode = document.getElementById(id);
    } else {
        if (browser.isIE) {
            dragObj.elNode = window.event.srcElement;
        }
        if (browser.isNS) {
            dragObj.elNode = event.target;
        }

        // If this is a text node, use its parent element.
        if (dragObj.elNode.nodeType == 3) {
            dragObj.elNode = dragObj.elNode.parentNode;
        }
    }

    // Get cursor position with respect to the page.
    if (browser.isIE) {
        x = window.event.clientX + document.documentElement.scrollLeft +
            document.body.scrollLeft;
        y = window.event.clientY + document.documentElement.scrollTop +
            document.body.scrollTop;
    }
    if (browser.isNS) {
        x = event.clientX + window.scrollX;
        y = event.clientY + window.scrollY;
    }

    // Save starting positions of cursor and element.
    dragObj.cursorStartX = x;
    dragObj.cursorStartY = y;
    if (!parseInt(dragObj.elNode.style.left,  10)) {
        dragObj.elStartLeft = dragInnerWidth * 0.3;
    } else {
        dragObj.elStartLeft = parseInt(dragObj.elNode.style.left,  10);
    }

    if (isNaN(dragObj.elStartLeft)) {
        dragObj.elStartLeft = 0;
    }
    if (isNaN(dragObj.elStartTop)) {
        dragObj.elStartTop  = 0;
    }

    // Update element's z-index.
    dragObj.elNode.style.zIndex = ++dragObj.zIndex;

    // Capture mousemove and mouseup events on the page.
    if (browser.isIE) {
        document.attachEvent('onmousemove', hDragGo);
        document.attachEvent('onmouseup', hDragStop);
        window.event.cancelBubble = true;
        window.event.returnValue = false;
    }
    if (browser.isNS) {
        document.addEventListener('mousemove', hDragGo, true);
        document.addEventListener('mouseup', hDragStop, true);
        event.preventDefault();
    }

} /* hDragStart */

function hDragStop(event)
{
    if (dragMovePx < MIN_LEFT_WIDTH) {
        dragMovePx = MIN_LEFT_WIDTH + 5;
    }

    if (dragMovePx > (dragInnerWidth - MIN_RIGHT_WIDTH)) {
        dragMovePx = dragInnerWidth - MIN_RIGHT_WIDTH - 5;
    }

    var x = dragMovePx / dragInnerWidth;
    setCookie('width', x.toFixed(2));

    // Stop capturing mousemove and mouseup events
    if (browser.isIE) {
        document.detachEvent('onmousemove', hDragGo);
        document.detachEvent('onmouseup',   hDragStop);
    }
    if (browser.isNS) {
        document.removeEventListener('mousemove', hDragGo,   true);
        document.removeEventListener('mouseup',   hDragStop, true);
    }

} /* hDragStop */

function vDragGo(event)
{
    var main = document.getElementById('main');

    var x, y;

    // Get cursor position with respect to the page.

    if (browser.isIE) {
        y = window.event.clientY + document.documentElement.scrollTop +
            document.body.scrollTop;
    }
    if (browser.isNS) {
        y = event.clientY + window.scrollY;
    }

    // Move drag element by the same amount the cursor has moved.
    dragMovePx = dragObj.elStartTop + y - dragObj.cursorStartY;
    var temp = dragObj.elNode.style.top.substring(0, dragObj.elNode.style.top.length - 2);
    dragOffsetHeight = document.getElementById('main').offsetHeight;
    if (dragMovePx < MIN_TOP_HEIGHT) {
        adjustHeights(MIN_TOP_HEIGHT, dragOffsetHeight);
    } else if (dragMovePx > (dragOffsetHeight - MIN_BOTTOM_HEIGHT)) {
        adjustHeights((dragOffsetHeight - MIN_BOTTOM_HEIGHT), dragOffsetHeight);
    } else {
        adjustHeights(dragMovePx, dragOffsetHeight);
    }

    if (browser.isIE) {
        window.event.cancelBubble = true;
        window.event.returnValue = false;
    }
    if (browser.isNS) {
        event.preventDefault();
    }

} /* vDragGo */

function vDragStart(event, id)
{
    if ( document.getElementById('talks').className == 'hidden' ||
         document.getElementById('scriptures').className == 'hidden' ) {
        return;
    }

    var el;
    var x, y;

    // If an element id was given, find it. Otherwise use the element being
    // clicked on.

    if (id) {
        dragObj.elNode = document.getElementById(id);
    } else {
        if (browser.isIE) {
            dragObj.elNode = window.event.srcElement;
        }
        if (browser.isNS) {
            dragObj.elNode = event.target;
        }

        // If this is a text node, use its parent element.
        if (dragObj.elNode.nodeType == 3) {
            dragObj.elNode = dragObj.elNode.parentNode;
        }
    }

    // Get cursor position with respect to the page.

    if (browser.isIE) {
        x = window.event.clientX + document.documentElement.scrollLeft +
            document.body.scrollLeft;
        y = window.event.clientY + document.documentElement.scrollTop +
            document.body.scrollTop;
    }
    if (browser.isNS) {
        x = event.clientX + window.scrollX;
        y = event.clientY + window.scrollY;
    }

    // Save starting positions of cursor and element.
    dragObj.cursorStartX = x;
    dragObj.cursorStartY = y;
    //dragObj.elStartLeft  = parseInt(dragObj.elNode.style.left, 10);
    if (!parseInt(dragObj.elNode.style.top, 10)) {
        dragObj.elStartTop = document.getElementById('main').offsetHeight / 2;
    } else if (parseInt(dragObj.elNode.style.top, 10) == 50) {
        dragObj.elStartTop = document.getElementById('main').offsetHeight / 2;
    } else {
        dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10);
    }

    if (isNaN(dragObj.elStartLeft)) {
        dragObj.elStartLeft = 0;
    }
    if (isNaN(dragObj.elStartTop)) {
        dragObj.elStartTop = 0;
    }

    // Update element's z-index.
    dragObj.elNode.style.zIndex = ++dragObj.zIndex;

    // Capture mousemove and mouseup events on the page.
    if (browser.isIE) {
        document.attachEvent('onmousemove', vDragGo);
        document.attachEvent('onmouseup', vDragStop);
        window.event.cancelBubble = true;
        window.event.returnValue = false;
    }
    if (browser.isNS) {
        document.addEventListener('mousemove', vDragGo, true);
        document.addEventListener('mouseup', vDragStop, true);
        event.preventDefault();
    }

} /* vDragStart */

function vDragStop(event)
{
    if (dragMovePx < MIN_TOP_HEIGHT) {
        dragMovePx = MIN_TOP_HEIGHT + 5;
    }

    if (dragMovePx > (dragOffsetHeight - MIN_BOTTOM_HEIGHT)) {
        dragMovePx = dragOffsetHeight - MIN_BOTTOM_HEIGHT - 5;
    }

    var x = dragMovePx / dragOffsetHeight;
    setCookie('height', x.toFixed(2));

    // Stop capturing mousemove and mouseup events.
    if (browser.isIE) {
        document.detachEvent('onmousemove', vDragGo);
        document.detachEvent('onmouseup',   vDragStop);
    }
    if (browser.isNS) {
        document.removeEventListener('mousemove', vDragGo,   true);
        document.removeEventListener('mouseup',   vDragStop, true);
    }

} /* vDragStop */

/*========================================================================
 *                      END OF FILE scriptures.js
 */

