// Javascript for refresh-free updates in gather screen.
// These initial declarations are processed when navigating to the page, refreshing, or selecting a new pagination link.

// =======================================================================================================================
// These variables shouldn't be renamed because they are also used in Flash code.
var pg_sections = new Array(0);     // array uses non-numeric index, so pg_sections.length is always 0
var a_price_schemes = new Array(0);
var b_sections = 0;                 // number of visible sections that are selected
var b_pages = 0;
var b_price = 0;
// =======================================================================================================================
var b_color_sections = 0;
var b_allcolor_price = 0;

var a_toggled_sects = new Array(0);

var book_sect_toggle_type;
var hidden_sect_id_list;
var last_chapter_unit;

// Variables for "child" custom book with sections associated with parent book sections (used only in PRS CulinaryArts program as of 7/2008)
var a_parent_sections = new Array(0); // unlike pg_sections, this array uses numeric array indices so array length can be > 0
var a_child_sections = new Array(0);
var a_child_sect_pages = new Array(0);
var a_child_sect_prices = new Array(0);
// Variables for "child" custom book, regardless of whether its sections are linked to parent book selections
var b_childbk_sections = 0;
var b_childbk_pages = 0;
var b_childbk_price = 0;

var related_section;
var related_sect_ID = null;
var bAlertForAddCoreWasShown;
var bPerformToggle;
var bUsingToggleFunc = false;   // will stay false if called from page that doesn't use the "toggle" functions
var vPrevPages = 0;
var vPrevChildPages = 0;

var bGatherJsDebug = false;
var bGatherJsDbg_AddPgSection = false;
var bGatherJsDbg_ToggleBook = false;
var bGatherJsDbg_UpdateContent = false;
var oNow, sDebugText, sFuncName;          // for debug alert messages


function toggle_book(book_id, chkbox_was_on, sect_id_list, form_name, aspx_name, list_form_name)
{
  if (bGatherJsDbg_ToggleBook)
  {
    sDebugText = '';    // Debugging begins here if called from a pam\gather page
    sFuncName = 'toggle_book';
    oNow = new Date();
    // Always append to sDebugText using +=
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += '  a_toggled_sects.length: ' + (a_toggled_sects == null ? 'null' : a_toggled_sects.length.toString()) + '\n';
  }

  var bSetChecked = !chkbox_was_on;
  var iToggledCount, j, k;
  var aSectIDs;
  var oHiddenSectIDList = '';
  var oSectID, oSection, oToggledSection;
  var oForm = document.forms[form_name];
  var oListForm = document.forms[list_form_name];
  if (oListForm && typeof(oListForm) != 'undefined')
    oListForm.disabled = true;    // prevent additional checkbox toggling during processing

  var sError = "";
  if (typeof(book_id) == 'undefined')
    sError = "toggle_book: parameter 'book_id' is undefined.";
  else if (typeof(form_name) == 'undefined')
    sError = "toggle_book: parameter 'form_name' is undefined.";
  if (sError != "")
  {
    alert(sError);
    return;
  }

  // Initialize module-level variables.
  book_sect_toggle_type = (chkbox_was_on ? '0' : '1');    // 0 = deselect, 1 = select
  hidden_sect_id_list = '';

  // Select or deselect all the sections in the book, depending on the book's previous selection state.
  init_checkbox_array('section', sect_id_list, bSetChecked, list_form_name)

  aSectIDs = sect_id_list.split(",");

  // Loop for each of the book's sections, not all of which may be visible on the current page. 
  for (j = 0; j < aSectIDs.length; j++)
  {
    if (aSectIDs[j] != null && aSectIDs[j] != '')
    {
      oSectID = aSectIDs[j];

      // Look for the section in the pg_sections array, which has the sections visible on the current page.
      oSection = pg_sections[oSectID];
      if (oSection && typeof(oSection) != 'undefined')
      {
        // To avoid array pointer problems (i.e. values getting overwritten) that were caused by placing oSection in
        // the a_toggled_sects array, create a copy of oSection which can be used instead.
        oToggledSection = new Array(oSection.length);
        for (oElem in oSection)
          oToggledSection[oElem] = oSection[oElem];
        oToggledSection['on'] = chkbox_was_on;
        oToggledSection['toggleType'] = book_sect_toggle_type;

        // Add the oSection array object copy as a new element at the end of the a_toggled_sects array,
        // whether or not this section was previously toggled (the array will be cleaned up later).
        iToggledCount = a_toggled_sects.length;
        a_toggled_sects[iToggledCount] = oToggledSection;
        
        if (bGatherJsDbg_ToggleBook)
        {
          sDebugText += '  j = ' + j.toString() + ': sectionID: ' + oSection['id'].toString()
            + ', iToggledCount: ' + (iToggledCount == null ? 'null' : iToggledCount.toString()) + '\n';
        }
      }
      else
      {
        // The section to be toggled is not visible on the current page.
        if (oHiddenSectIDList != '')
          oHiddenSectIDList += ',';
        oHiddenSectIDList += oSectID;
      }
    }   // end of if (aSectIDs[j] != null && aSectIDs[j] != '')
  }     // end of for (j = 0; j < aSectIDs.length; j++)

  if (oHiddenSectIDList != '')
    hidden_sect_id_list = oHiddenSectIDList;

  if (bGatherJsDbg_ToggleBook)
  {
    sDebugText += '  hidden_sect_id_list: ' + (hidden_sect_id_list == null ? 'null' : hidden_sect_id_list.toString()) + '\n';
  }

  update_content_selections();

  if (oListForm && typeof(oListForm) != 'undefined')
    oListForm.disabled = false;   // re-enable form with checkboxes

  if (bGatherJsDbg_ToggleBook)
  {
    sFuncName = 'toggle_book';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    alert(sDebugText);
  }
}


function toggle_section(sect_id, form_name, aspx_name, list_form_name, other_info)
{
  if (bGatherJsDebug)
  {
    sDebugText = '';    // Debugging begins here if called from a pam\gather page
    sFuncName = 'toggle_section';
    oNow = new Date();
    // Always append to sDebugText using +=
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += '  a_parent_sections.length: ' + (a_parent_sections == null ? 'null' : a_parent_sections.length.toString()) + '\n';
  }
  var bIsNewChapterUnit = false;
  var bIsCore, bSectNeedsCore;
  var iCount;
  var oElem, oSection, oToggledSection;
  var oForm = document.forms[form_name];
  var oListForm = document.forms[list_form_name];
  if (oListForm && typeof(oListForm) != 'undefined')
  {
    oListForm.disabled = true;    // prevent additional checkbox toggling during processing
  }

  var sError = "";
  if (typeof(sect_id) == 'undefined')
    sError = "toggle_section: parameter 'sect_id' is undefined.";
  else if (typeof(form_name) == 'undefined')
    sError = "toggle_section: parameter 'form_name' is undefined.";
  else if (typeof(pg_sections[sect_id]) == 'undefined')
    sError = "toggle_section: pg_sections[sect_id] is undefined.";
  else if (aspx_name == null)   // blank is OK
    sError = "toggle_section: parameter 'aspx_name' is not specified.";
  if (sError != "")
  {
    alert(sError);
    return;
  }

  bPerformToggle = true;
  bUsingToggleFunc = true;
  oSection = pg_sections[sect_id];
  if (oSection)
  {
    if (oSection['on'] == null)
      oSection['on'] = false;

    related_sect_ID = oSection['relatedSection'];       // pedagogy cannot remain selected by itself
    if (related_sect_ID != null && related_sect_ID != '')
    {
      related_section = pg_sections[related_sect_ID];   // will need to make sure the "related" parent is selected
      if (related_section['on'] == null)
        related_section['on'] = false;

      if (bGatherJsDebug)
      {
        sDebugText += '  related_section[id]: '
          + (related_section != null && typeof(related_section) != 'undefined' && related_section['id'] != null && typeof(related_section['id']) != 'undefined' ? related_section['id'].toString() : 'null')
          + ', related_section[on]: '
          + (related_section != null && typeof(related_section) != 'undefined' && related_section['on'] != null && typeof(related_section['on']) != 'undefined' ? related_section['on'].toString() : 'null') + '\n';
      }
    }
    else
    {
      related_sect_ID = null;   // do NOT set related_section to null
    }

    // See if the "related" section, if any, is selected.  (For a pedagogy, the related section is the "parent" section,
    // and for a section with an associated pedagogy, the related section is the pedagogy.)
    oSection['relatedSectState'] = (related_section && related_section['on'] == true ? '1' : '0');

    // Determine the number of "dependent" sections that are currently selected.
    bIsCore = (oSection['isCore'] != null && typeof(oSection['isCore']) != 'undefined' && oSection['isCore'] == true);
    bSectNeedsCore = (oSection['coreSections'] != null && typeof(oSection['coreSections']) != 'undefined' && oSection['coreSections'] != '');
    if (bIsCore || bSectNeedsCore)
    {
      // Don't test a numeric variable like an object because 0 is a legitimate value.  Test explicitly for null.
      // Do NOT change this code to "if (xyz == null || typeof(xyz) == 'undefined'))" because IT WILL FAIL.
      if (!(dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined'))   // if null or undefined
      {
        dependent_sect_count = (oSection['dependentSectCount'] == null ? 0 : oSection['dependentSectCount']);
        if (bGatherJsDebug)
        {
          sDebugText += '  dependent_sect_count set to: ' + dependent_sect_count.toString() + '\n';
        }
      }
      if (oSection['chapterUnitNum'] != null && typeof(oSection['chapterUnitNum']) != 'undefined')
      {
        bIsNewChapterUnit = (typeof(last_chapter_unit) != 'undefined' && last_chapter_unit > 0
          && oSection['chapterUnitNum'] != last_chapter_unit);
        if (bIsNewChapterUnit)
        {
          dependent_sect_count = oSection['dependentSectCount'];
          if (bGatherJsDebug)
          {
            sDebugText += '  This is a newly selected chapter unit; reset dependent_sect_count to: ' + dependent_sect_count.toString() + '\n';
          }
        }
      }
    }

    // Select or deselect the section, depending on its previous selection state.
    oSection['toggleType'] = (oSection['on'] ? '0' : '1');    // 0 = deselect, 1 = select
    if (oSection['on'])
    {
      // Unselect the previously selected item.
      // Note that remove_section calls remove_common, which calls update_basics_form.
      if (bGatherJsDebug)
      {
        sDebugText += '  Section was ON (remove section), related_sect_ID: ' + related_sect_ID + '\n';
      }
      remove_section(oSection, form_name, aspx_name, list_form_name, other_info);
    }
    else
    {
      // Select the previously deselected item.
      // Note that add_section calls add_common, which calls update_basics_form.
      if (bGatherJsDebug)
      {
        sDebugText += '  Section was OFF (add section), related_sect_ID: ' + related_sect_ID + '\n';
      }
      add_section(oSection, form_name, aspx_name, list_form_name, other_info);
    }
    
    if (bPerformToggle)   // if toggle was not aborted
    {
      // To avoid array pointer problems (i.e. values getting overwritten) that were caused by placing oSection in
      // the a_toggled_sects array, create a copy of oSection which can be used instead.
      oToggledSection = new Array(oSection.length);
      for (oElem in oSection)
        oToggledSection[oElem] = oSection[oElem];

      // Add the oSection array object copy as a new element at the end of the a_toggled_sects array,
      // whether or not this section was previously toggled (the array will be cleaned up later).
      // Don't use the "push" function to add to the array, because then you can't access array elements by index number.
      iCount = a_toggled_sects.length;
      a_toggled_sects[iCount] = oToggledSection;
      if (bGatherJsDebug)
      {
        var oTest = a_toggled_sects[iCount];
        sDebugText += '  iCount (old a_toggled_sects length): ' + iCount.toString() + ', new length: ' + a_toggled_sects.length.toString()
          + ', a_toggled_sects[' + iCount.toString() + '] ID: ' + oTest['id'].toString() + '\n';
      }
    }

    // The Pearson El-Hi program has sections grouped into "chapter units".  When the user has selected a section in a
    // different chapter unit than the previous selection, the toggled content in the previous chapter unit must be
    // updated in the database by setting pf_action to "update_content" and submitting the form.  This must be done
    // because the a_toggled_sects array is initialized for the new chapter unit in update_content_selections(), and this
    // code is currently not set up to track "core" and "dependent" selections in multiple chapter units simultaneously.
    if (oSection['chapterUnitNum'] != null && typeof(oSection['chapterUnitNum']) != 'undefined')
    {
      if (bIsNewChapterUnit)
      {
        update_content_selections();
        if (oForm && typeof(oForm) != 'undefined' && oForm.elements && typeof(oForm.elements) != 'undefined')
        {
          oForm.elements['pf_action'].value = 'update_content';
          oForm.submit();
        }
      }
      last_chapter_unit = oSection['chapterUnitNum'];
    }
  }

  if (oListForm && typeof(oListForm) != 'undefined')
  {
    oListForm.disabled = false;   // re-enable form with checkboxes
  }
  bUsingToggleFunc = false;   // reset flag

  if (bGatherJsDebug)
  {
    sFuncName = 'toggle_section';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    alert(sDebugText);
  }
}


function add_section(oSection, form_name, aspx_name, list_form_name, other_info)
{
  if (bGatherJsDebug)
  {
    sFuncName = 'add_section';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    if (oSection['category'])
      sDebugText += '  category: ' + oSection['category'] + ', oSection[on] (originally checked): ' + oSection['on'] + '\n';
    if (oSection['childSections'])
      sDebugText += '  childSections: ' + oSection['childSections'] + '\n';
    if (related_section && related_section['category'])
      sDebugText += '  related sect category: ' + related_section['category'] + ', checked: ' + related_section['on'] + '\n';
  }

  var aChildIDs, aChildPages, aChildPrices, aCoreSectIDs;
  var bExists;
  var bCoreAdded = false, bHiddenCoreAdded = false, bMultipleCoresAdded = false;
  var iAddPages = 0, iAddPrice = 0, iAddSects = 0;
  var i, j, k;
  var oChildID, oChildPages, oChildPrice, oCoreSection, oCoreSectID;
  var oCoreSectIDList = '';   // For saving comma-separated IDs of required (core) sections that aren't already selected or aren't visible on current page

  if (typeof(oSection['id']) == 'undefined')
  {
    alert("add_section: section id is undefined.");
    return;
  }
  var oListForm = document.forms[list_form_name];
  if (oListForm && typeof(oListForm) != 'undefined')
  {
    // If a child custom book (with associated sections) exists, add any child sections associated with the current
    // parent section that aren't already included in the child custom book.
    if (oSection['childSections'] != null && oSection['childSections'] != '')
    {
      // These three arrays will all be the same size.
      aChildIDs = oSection['childSections'].split(",");
      aChildPages = oSection['childSectPages'].split(",");
      aChildPrices = oSection['childSectPrices'].split(",");
      if (bGatherJsDebug)
      {
        sDebugText += '  section (CBI ID): ' + oSection['id'] + ' [title: ' + oSection['title']
          + '], b_sections: ' + b_sections + ', b_color_sections: ' + b_color_sections
          + ', oSection[pages]: ' + oSection['pages'] + '\n';
        sDebugText += '  oSection[childSections]: ' + oSection['childSections'] + '\n';
        if (aChildIDs)
          sDebugText += '  aChildIDs.length: ' + aChildIDs.length.toString() + '\n';
        if (oSection['childSectPages'])
          sDebugText += '  oSection[childSectPages]: ' + oSection['childSectPages'] + '\n';
        if (aChildPages)
          sDebugText += '  aChildPages.length: ' + aChildPages.length.toString() + '\n';
        if (oSection['childSectPrices'])
          sDebugText += '  oSection[childSectPrices]: ' + oSection['childSectPrices'] + '\n';
        if (aChildPrices)
          sDebugText += '  aChildPrices.length: ' + aChildPrices.length.toString() + '\n';
      }

      // Process each of the parent section's child IDs.
      for (j = 0; j < aChildIDs.length; j++)
      {
        if (bGatherJsDebug)
        {
          sDebugText += '  j: ' + j.toString() + ', aChildIDs[j]: ' + aChildIDs[j] + '\n';
        }
        if (aChildIDs[j] != null && aChildIDs[j] != '')
        {
          oChildID = aChildIDs[j];
          oChildPages = parseInt(aChildPages[j]);
          oChildPrice = Math.round(parseFloat(aChildPrices[j]) * 100);   // input value is in dollars but price is in cents
          if (bGatherJsDebug)
          {
            sDebugText += '  oChildPages: ' + (oChildPages == null ? 'null' : oChildPages.toString())
              + ', oChildPrice: ' + (oChildPrice == null ? 'null' : oChildPrice.toString()) + '\n';
          }

          bExists = false;
          if (a_child_sections.length > 0)
          {
            for (k = 0; k < a_child_sections.length; k++)
            {
              if (bGatherJsDebug)
              {
                sDebugText += '  k: ' + k.toString() + ', a_child_sections[k]: '
                  + (a_child_sections[k] == null ? 'null' : a_child_sections[k].toString())
                  + ', oChildID: ' + (oChildID == null ? 'null' : oChildID.toString()) + '\n';
              }
              if (a_child_sections[k] != null && a_child_sections[k] != '' && oChildID.toString() == a_child_sections[k].toString())
              {
                bExists = true;
                if (bGatherJsDebug)
                {
                  sDebugText += '  Sect ID ' + oChildID + ' EXISTS in a_child_sections array at element ' + k.toString() + '\n';
                }
                break;
              }
            }
          }
          if (!bExists)
          {
            iAddSects++;
            iAddPages += oChildPages;
            iAddPrice += oChildPrice;
            a_child_sections.push(oChildID);
            a_child_sect_pages.push(oChildPages);
            a_child_sect_prices.push(oChildPrice);
            if (bGatherJsDebug)
            {
              sDebugText += '  ADDED Sect ID ' + oChildID + ' to a_child_sections\n';
            }
          }
        }
      }
      // Add the parent section to the a_parent_sections array.
      bExists = false;
      for (i = 0; i < a_parent_sections.length; i++)
      {
        oTestSect = a_parent_sections[i];
        if (oTestSect && oTestSect['id'].toString() == oSection['id'].toString())
          bExists = true;
      }
      if (!bExists)
        a_parent_sections.push(oSection);

      // Update the values for the child custom book.
      if (iAddSects > 0)
      {
        b_childbk_sections += iAddSects;
        b_childbk_pages += iAddPages;
        b_childbk_price += iAddPrice;
      }
    }   // end of if (oSection['childSections'] != null ...)

    // Add any required (core) sections associated with the current section that aren't already selected.
    if (oSection['coreSections'] != null && oSection['coreSections'] != '')
    {
      aCoreSectIDs = oSection['coreSections'].split(",");
      if (bGatherJsDebug)
      {
        sDebugText += ' add_section: oSection[coreSections]: ' + oSection['coreSections']
          + ', aCoreSectIDs.length: ' + aCoreSectIDs.length.toString() + '\n';
      }
      for (j = 0; j < aCoreSectIDs.length; j++)
      {
        if (aCoreSectIDs[j] != null && aCoreSectIDs[j] != '')
        {
          oCoreSectID = aCoreSectIDs[j];
          oCoreSection = pg_sections[oCoreSectID];

          // The required (core) section could be on another page due to pagination, and thus wouldn't be visible
          // (it's not in pg_sections array), and oCoreSection would then be null.  If the core section is shown
          // on the current page, process it only if it isn't already selected.
          // Whether the core section is visible or not, it must be included in the database update, so add the
          // core section ID to "oCoreSectIDList".
          // Note: Setting window.location.href here won't do anything, because the form isn't resubmitted here.
          if (oCoreSection == null || (oCoreSection && oCoreSection['on'] != true))
          {
            if (oCoreSectIDList != '')
            {
              oCoreSectIDList += ',';
              bMultipleCoresAdded = true;
            }
            oCoreSectIDList += oCoreSectID;
            bCoreAdded = true;
            if (oCoreSection == null)
            {
              bHiddenCoreAdded = true;
            }
            else if (oCoreSection && oCoreSection['on'] != true)
            {
              add_common(oCoreSection);
              init_checkbox_array(oCoreSection['objName'], oCoreSection['id'], true, list_form_name);
            }
          }
        }
      }   // end of j (aCoreSectIDs) loop
      if (bGatherJsDebug)
      {
        sDebugText += '  oCoreSectIDList: ' + oCoreSectIDList + '\n';
      }
    }     // end of if (oSection['coreSections'] != null ...)

    add_common(oSection);

    init_checkbox_array(oSection['objName'], oSection['id'], true, list_form_name);

    // If a pedagogy is being added, and its related section had not already been selected, the "add_common" code would have
    // added both.  Show the related section as being selected.
    if (oSection['category'] == 'pedagogy')
    {
      if (related_section['on'] == true)
      {
        init_checkbox_array(related_section['objName'], related_section['id'], true, list_form_name);
      }
    }

    if (oSection['SectionB'] != null && oSection['SectionB'] != '')
    {
      enable_checkbox_array('section', oSection['SectionB'], false, list_form_name);   // false = disable
    }

    if (bCoreAdded && !bAlertForAddCoreWasShown)  // no need to show alert again until page is refreshed (add_pg_section is called)
    {
      alert("The selection you have chosen requires "
        + (bMultipleCoresAdded == true ? "" : "an ") + "additional selection"
        + (bMultipleCoresAdded == true ? "s.\nThese selections" : ".\nThis selection")
        + " will be included in your custom book."
        + (bHiddenCoreAdded == true ?
        "\n" + (bMultipleCoresAdded == true ? "One or more of these additional selections are" : "This additional selection is")
        + " not currently displayed."
        + " Consequently, you may need to refresh this page (or navigate to another page)"
        + " to see the updated selection/page count and book price." : ""));
      bAlertForAddCoreWasShown = true;
    }
  }
  if (bGatherJsDebug)
  {
    sFuncName = 'add_section';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }
}


function remove_section(oSection, form_name, aspx_name, list_form_name, other_info)
{
  if (bGatherJsDebug)
  {
    sFuncName = 'remove_section';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    if (oSection['category'])
      sDebugText += '  category: ' + oSection['category'] + ', oSection[on] (originally checked): ' + oSection['on'] + '\n';
    if (oSection['childSections'])
      sDebugText += '  childSections: ' + oSection['childSections'] + '\n';
    if (related_section && related_section['category'])
      sDebugText += '  related sect category: ' + related_section['category'] + ', checked: ' + related_section['on'] + '\n';
  }

  if (typeof(oSection['id']) == 'undefined')
  {
    alert("remove_section: section id is undefined.");
    return;
  }
  var oListForm = document.forms[list_form_name];
  if (oListForm && typeof(oListForm) != 'undefined')
  {
    remove_common(oSection, null);    // sets bPerformToggle false if removal is aborted
    if (bPerformToggle)
    {
      init_checkbox_array(oSection['objName'], oSection['id'], false, list_form_name);
      
      // If a section is being removed, and it had a related pedagogy that had also been selected, the "remove_common" code
      // would have removed both.  Show the related pedagogy as being unselected.
      if (oSection['category'] == 'section' && related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
      {
        if (related_section['on'] == false)
        {
          init_checkbox_array(related_section['objName'], related_section['id'], false, list_form_name);
        }
      }

      if (oSection['SectionB'] != null && oSection['SectionB'] != '')
      {
        enable_checkbox_array('section', oSection['SectionB'], true, list_form_name);
      }
    }
    else  // toggle was aborted; set checkbox back to checked and avoid refresh using no_response = 1.
    {
      window.location.href = './' + aspx_name + '?section_id=' + oSection['id'] + '&no_response=1';
      init_checkbox_array(oSection['objName'], oSection['id'], true, list_form_name);
    }
  }
  if (bGatherJsDebug)
  {
    sFuncName = 'remove_section';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }
}


function toggle_lm_section(sect_id, list_form_name)
{
  if (bGatherJsDebug)
  {
    sDebugText = '';    // Debugging begins here if called from a pam\gather page
    sFuncName = 'toggle_lm_section';
    oNow = new Date();
    // Always append to sDebugText using +=
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += 'sect_id: ' + sect_id + ', list_form_name: ' + list_form_name + '\n';
    sDebugText += 'b_sections: ' + b_sections + '\n';
    alert(sDebugText);
  }

  var oSection, oToggledSection;
  var oListForm = document.forms[list_form_name];
  if (oListForm && typeof(oListForm) != 'undefined')
  {
    oListForm.disabled = true;    // prevent additional checkbox toggling during processing
  }
  
  bPerformToggle = true;
  bUsingToggleFunc = true;
  oSection = pg_sections[sect_id];

  // Select or deselect the section, depending on its previous selection state.
  oSection['toggleType'] = (oSection['on'] ? '0' : '1');    // 0 = deselect, 1 = select
  if (oSection['on'])
  {
    // Unselect the previously selected item.
    // remove_lm_section calls remove_common, which calls update_basics_form.
    remove_lm_section(oSection, list_form_name);
  }
  else
  {
    // Select the previously deselected item.
    // Note that add_lm_section calls add_common, which calls update_basics_form.
    add_lm_section(oSection, list_form_name);
  }

  if (bPerformToggle)   // if toggle was not aborted
  {
    // To avoid array pointer problems (i.e. values getting overwritten) that were caused by placing oSection in
    // the a_toggled_sects array, create a copy of oSection which can be used instead.
    oToggledSection = new Array(oSection.length);
    for (oElem in oSection)
      oToggledSection[oElem] = oSection[oElem];
    oToggledSection['category'] = 'local'   // Mark this as local (original) material

    // Add the oSection array object copy as a new element at the end of the a_toggled_sects array,
    // whether or not this section was previously toggled (the array will be cleaned up later).
    // Don't use the "push" function to add to the array, because then you can't access array elements by index number.
    iCount = a_toggled_sects.length;
    a_toggled_sects[iCount] = oToggledSection;
  }

  if (oListForm && typeof(oListForm) != 'undefined')
  {
    oListForm.disabled = false;   // re-enable form with checkboxes
  }

  bUsingToggleFunc = false;   // reset flag

  if (bGatherJsDebug)
  {
    sFuncName = 'toggle_lm_section';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    alert(sDebugText);
  }
}


function add_lm_section(oSection, list_form_name)
{
  add_common(oSection);
  init_checkbox_array(oSection['objName'], oSection['id'], true, list_form_name);
}


function remove_lm_section(oSection, list_form_name)
{
  remove_common(oSection, null);    // sets bPerformToggle false if removal is aborted
  if (bPerformToggle)
  {
    init_checkbox_array(oSection['objName'], oSection['id'], false, list_form_name);
  }
}


function add_common(oSection)
{
  if (bGatherJsDebug)
  {
    sFuncName = 'add_common';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += 'Before sect addition: '
      + 'b_sections: ' + b_sections + ', b_color_sections: ' + b_color_sections + ', b_childbk_sections: ' + b_childbk_sections
      + ', b_pages: ' + b_pages.toString() + ', b_price: ' + b_price.toString() + '\n';
  }
  
  var bIsChild, bIsCore, bSectNeedsCore;

  if (oSection)
  {
    bIsChild = (oSection['isChildSect'] != null && typeof(oSection['isChildSect']) != 'undefined' && oSection['isChildSect'] == true);
    if (bGatherJsDebug)
    {
      sDebugText += '  bIsChild: ' + bIsChild.toString() + '\n';
    }
    bIsCore = (oSection['isCore'] != null && typeof(oSection['isCore']) != 'undefined' && oSection['isCore'] == true);
    bSectNeedsCore = (oSection['coreSections'] != null && typeof(oSection['coreSections']) != 'undefined' && oSection['coreSections'] != '');
    if (bGatherJsDebug)
    {
      if (bSectNeedsCore || bIsCore)
        sDebugText += '  bSectNeedsCore: ' + bSectNeedsCore.toString() + ', bIsCore: ' + bIsCore.toString()
          + ', dependent_sect_count: ' + (dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined' ? dependent_sect_count.toString() : "null or undefined")
          + ', oSection[dependentSectCount]: ' + (oSection['dependentSectCount'] != null && typeof(oSection['dependentSectCount']) != 'undefined' ? oSection['dependentSectCount'].toString() : "null or undefined") + '\n';
      else
        sDebugText += '  Not a core or dependent section' + '\n';
    }

    if ((bIsCore || bSectNeedsCore) && !(dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined'))
    {
      dependent_sect_count = (oSection['dependentSectCount'] == null ? 0 : oSection['dependentSectCount']);
      if (bGatherJsDebug)
      {
        sDebugText += '  dependent_sect_count set to: ' + dependent_sect_count.toString() + '\n';
      }
    }

    if (oSection['category'] != 'pedagogy')   // NOT pedagogy (i.e., normal sections, local material, etc.)
    {
      if (bIsChild)   // if child sections are being displayed
      {
        b_childbk_sections++;
      }
      else
      {
        b_sections++;
        if (oSection['isColorSection'])
          b_color_sections++;
        if (oSection['permNeeded'])
          perm_needed_count++;
      }

      if (bSectNeedsCore)
      {
        if (bGatherJsDebug)
        {
          sDebugText += '  add_common: orig dependent_sect_count: '
            + (dependent_sect_count == null ? 'null' : dependent_sect_count.toString())
            + '; orig oSection[dependentSectCount]: '
            + (oSection['dependentSectCount'] == null ? 'null' : oSection['dependentSectCount'].toString()) + '\n';
        }
        // Don't test a numeric variable like an object because 0 is a legitimate value.  Test explicitly for null.
        // Do NOT change this code to "if (xyz == null || typeof(xyz) == 'undefined'))" because IT WILL FAIL.
        if (!(dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined'))   // if null or undefined
          dependent_sect_count = oSection['dependentSectCount'];
        dependent_sect_count++;
        if (oSection['dependentSectCount'] != null && oSection['dependentSectCount'] != '')
          oSection['dependentSectCount'] = dependent_sect_count;
        if (bGatherJsDebug)
        {
          sDebugText += '  add_common: dependent_sect_count: '
            + (dependent_sect_count == null ? 'null' : "incremented to " + dependent_sect_count.toString())
            + '; oSection[dependentSectCount]: '
            + (oSection['dependentSectCount'] == null ? 'null' : oSection['dependentSectCount'].toString()) + '\n';
        }
      }

      if (bGatherJsDebug)
      {
        sDebugText += 'After sect addition: '
          + 'b_sections: ' + b_sections + ', b_color_sections: ' + b_color_sections
          + ', b_childbk_sections: ' + b_childbk_sections + '\n';
      }
    }
    else if (oSection['category'] == 'pedagogy')
    {
      // A pedagogy is not considered a section, but if the pedagogy's related section wasn't on before,
      // it must be selected.  Don't increment b_sections if only the pedagogy checkbox was selected.
      // Assume that pedagogies are not used in child custom books.
      if (bGatherJsDebug)
      {
        sDebugText += '  Pedagogy' + '\n';
      }
      if (!related_section['on'])   // if either null or false
      {
        b_sections++;
      }
    }

    if (bIsChild)   // if child sections are being displayed
    {
      vPrevChildPages = b_childbk_pages;
      //b_childbk_pages += oSection['pages'];
      b_childbk_pages += (oSection['allStartOnSameSide'] && (oSection['pages'] % 2 == 1) ?
        oSection['pages'] + 1 : oSection['pages']);
      if (bGatherJsDebug)
      {
        sDebugText += '  Added pages. Now b_childbk_pages: ' + b_childbk_pages.toString() + '\n';
      }
    }
    else
    {
      vPrevPages = b_pages;
      //b_pages += oSection['pages'];
      b_pages += (oSection['allStartOnSameSide'] && (oSection['pages'] % 2 == 1) ?
        oSection['pages'] + 1 : oSection['pages']);
      if (bGatherJsDebug)
      {
        sDebugText += '  Added pages. Now b_pages: ' + b_pages.toString() + '\n';
      }
    }
    if (oSection['category'] == 'pedagogy' && related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
    {
      if (!related_section['on'])   // if either null or false
      {
        // If a pedagogy is being added, and its related section had not already been selected, the section pages must also be added.
        // Assume that pedagogies are not used in child custom books.
        //b_pages += related_section['pages'];
        b_pages += (oSection['allStartOnSameSide'] && (related_section['pages'] % 2 == 1) ?
          related_section['pages'] + 1 : related_section['pages']);
        if (bGatherJsDebug)
        {
          sDebugText += '  Added related section pages. Now b_pages: ' + b_pages.toString() + '\n';
        }
      }
    }

    // Setting b_price here ignores changes to/from threshold price scheme.
    // In those cases, the page must be refreshed before the correct price is shown.
    // TO DO: If price calculation is visible AND if a threshold price scheme may apply, use
    // appropriate values from a_price_schemes, based on number of selected pages or sections.
    if (bIsChild)   // if child sections are being displayed
    {
      b_childbk_price += oSection['price'];
      b_allcolor_price += oSection['allColorPrice'];
      if (bGatherJsDebug)
      {
        sDebugText += '  Increased price. Now b_childbk_price: ' + b_childbk_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }
    else
    {
      b_price += oSection['price'];
      b_allcolor_price += oSection['allColorPrice'];
      if (bGatherJsDebug)
      {
        sDebugText += '  Increased price. Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }

    if (oSection['category'] == 'pedagogy' && related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
    {
      if (!related_section['on'])   // if either null or false
      {
        // If a pedagogy is being added, and its related section had not already been selected, the section price must also be added.
        b_price += related_section['price'];
        b_allcolor_price += related_section['allColorPrice'];
        if (bGatherJsDebug)
        {
          sDebugText += '  Added related section price. Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
        }
      }
    }

    oSection['on'] = true;
    if (oSection['category'] == 'pedagogy')
    {
      // Section gets turned on too.
      related_section['on'] = true;
    }

    // Update here rather than in toggle function.
    update_basics_form();

    if (typeof(thisMovie('ifactor_flash')) != 'undefined')
    {
      update_text('"' + oSection['title_trimmed'] + '" was added');
      playmovie('ifactor_flash', 5);
    }
  }
  if (bGatherJsDebug)
  {
    sFuncName = 'add_common';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }
}


function remove_common_from_flash(cbi_id)
{
  if (bGatherJsDebug)
  {
    sDebugText = '';    // Called from "sequence" folder; debugging begins here as in toggle_section()
    sFuncName = 'remove_common_from_flash';
    oNow = new Date();
    // Always append to sDebugText using +=
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += '  a_parent_sections.length: ' + (a_parent_sections == null ? 'null' : a_parent_sections.length.toString()) + '\n';
  }

  var oSection = pg_sections[cbi_id];
  bPerformToggle = true;
  remove_common(oSection, 'flash');     // sets bPerformToggle false if removal is aborted

  if (bGatherJsDebug)
  {
    sFuncName = 'remove_common_from_flash';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    alert(sDebugText);
  }
}


function remove_common(oSection, called_from)
{
  // This function is called when an item is deleted in "Edit Sequence" (via remove_common_from_flash)
  // as well as in a Gather page.

  if (bGatherJsDebug)
  {
    sFuncName = 'remove_common';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    if (oSection)
      sDebugText += '  section (CBI ID): ' + oSection['id'] + ' [title: ' + oSection['title']
        + '], b_sections: ' + b_sections + ', oSection[pages]: ' + oSection['pages'] + '\n';
    sDebugText += 'Before sect removal: '
      + 'b_sections: ' + b_sections + ', b_color_sections: ' + b_color_sections + ', b_childbk_sections: ' + b_childbk_sections
      + ', b_pages: ' + b_pages.toString() + ', b_price: ' + b_price.toString() + '\n';
  }

  var aChildIDs, aChildPages, aChildPrices;
  var bIsCore, bSectNeedsCore;
  var bIsChild, bRemoveChild;
  var iCurrIndex = -1;
  var iSubtractPages = 0, iSubtractPrice = 0, iSubtractSects = 0;
  var i, j, k;
  var oChildID, oChildPages, oChildPrice, oTestSect;
  
  if (oSection)  // Prevent script errors
  {
    bIsChild = (oSection['isChildSect'] != null && typeof(oSection['isChildSect']) != 'undefined' && oSection['isChildSect'] == true);
    if (bGatherJsDebug)
    {
      sDebugText += '  bIsChild: ' + bIsChild.toString() + '\n';
    }

    // If a child custom book (with sections linked to parent selections) exists, and a parent section is being removed:
    // First remove any associated child sections that aren't associated with any other sections in the parent custom book.
    // Don't execute this code if remove_common was called from a Sequence page because the a_parent_sections array will
    // likely not have valid data.  The necessary deletions will instead be handled on the server side.
    if (oSection['childSections'] != null && oSection['childSections'] != '')
    {
      // These three arrays will all be the same size.
      aChildIDs = oSection['childSections'].split(",");
      aChildPages = oSection['childSectPages'].split(",");
      aChildPrices = oSection['childSectPrices'].split(",");
      if (bGatherJsDebug)
      {
        sDebugText += '  Initial b_ch_sections: ' + b_childbk_sections
          + '. b_ch_pages: ' + b_childbk_pages + '. b_ch_price: ' + b_childbk_price + '\n';
        sDebugText += '  oSection[childSections]: ' + oSection['childSections'] + '\n';
        if (aChildIDs)
          sDebugText += '  aChildIDs.length: ' + aChildIDs.length.toString() + '\n';
        if (oSection['childSectPages'])
          sDebugText += '  oSection[childSectPages]: ' + oSection['childSectPages'] + '\n';
        if (aChildPages)
          sDebugText += '  aChildPages.length: ' + aChildPages.length.toString() + '\n';
        if (oSection['childSectPrices'])
          sDebugText += '  oSection[childSectPrices]: ' + oSection['childSectPrices'] + '\n';
        if (aChildPrices)
          sDebugText += '  aChildPrices.length: ' + aChildPrices.length.toString() + '\n';
      }

      // Process each of the parent section's child IDs.
      for (j = 0; j < aChildIDs.length; j++)
      {
        if (bGatherJsDebug)
        {
          sDebugText += '  j: ' + j.toString() + ', aChildIDs[j] = ' + aChildIDs[j] + '\n';
        }
        if (aChildIDs[j] != null && aChildIDs[j] != '')
        {
          oChildID = aChildIDs[j];
          oChildPages = parseInt(aChildPages[j]);
          oChildPrice = Math.round(parseFloat(aChildPrices[j]) * 100);   // input value is in dollars but price is in cents
          if (bGatherJsDebug)
          {
            sDebugText += '  oChildPages: ' + (oChildPages == null ? 'null' : oChildPages.toString())
              + ', oChildPrice: ' + (oChildPrice == null ? 'null' : oChildPrice.toString()) + '\n';
            sDebugText += '  a_parent_sections.length: ' + (a_parent_sections == null ? 'null' : a_parent_sections.length.toString()) + '\n';
          }

          // For this child ID, see if any other selected parent section has this child ID.  If so, don't remove it.
          // Note that we don't have data for all the parent sections in the entire custom book, just the ones shown
          // on the current page.  Consequently, the best we can do is to check among the parent sections that are
          // currently displayed.  If a child section shared by an "off-page" parent is inadvertently removed, it will
          // need to be restored when the user next refreshes or leaves the page.
          bRemoveChild = true;
          for (i = 0; i < a_parent_sections.length; i++)
          {
            oTestSect = a_parent_sections[i];
            if (bGatherJsDebug)
            {
              sDebugText += '  Testing section with ID ' + oTestSect['id'].toString() + '\n';
            }
            if (oTestSect && oTestSect['id'].toString() == oSection['id'].toString())  // this is the current parent section
            {
              iCurrIndex = i;   // location of the current parent section in the a_parent_sections array
              if (bGatherJsDebug)
              {
                sDebugText += '   iCurrIndex: ' + iCurrIndex.toString() + '\n';
              }
            }
            else if (oTestSect && oTestSect['id'].toString() != oSection['id'].toString()
              && oTestSect['childSections'] && oTestSect['childSections'].length > 0)
            {
              // See if this parent section is selected ('on' = true) and if it shares the child ID.  (Both 'on' tests are needed.)
              if (oTestSect['on'] && oTestSect['on'] == true && oTestSect['childSections'].toString().indexOf(oChildID.toString()) > -1)
              {
                bRemoveChild = false;
                if (bGatherJsDebug)
                {
                  sDebugText += '   Child sect ID ' + oChildID + ' has ANOTHER PARENT (ID ' + oTestSect['id'].toString()
                    + '); delete aborted\n';
                }
              }
            }
          }

          // Remove the child section if it has no other parents.
          if (bRemoveChild)
          {
            iSubtractSects++;
            iSubtractPages += oChildPages;
            iSubtractPrice += oChildPrice;
            if (bGatherJsDebug)
            {
              sDebugText += '  Preparing to delete; iSubtractSects: ' + iSubtractSects
                + ', a_child_sections.length: ' + a_child_sections.length.toString() + '\n';
            }
            if (a_child_sections.length > 0)
            {
              for (k = 0; k < a_child_sections.length; k++)
              {
                if (a_child_sections[k] != null && oChildID.toString() == a_child_sections[k].toString())
                {
                  // Note: The delete operator sets the array element to "undefined", but the array length is unchanged.
                  delete a_child_sections[k];
                  delete a_child_sect_pages[k];
                  delete a_child_sect_prices[k];
                  if (bGatherJsDebug)
                  {
                    sDebugText += '  DELETED child sect ID ' + oChildID + ' from child sections/pages/prices arrays\n';
                  }
                }
              }
            }
          }
        }
      }

      // Delete the removed parent section from a_parent_sections.
      if (iCurrIndex > -1)
      {
        oTestSect = a_parent_sections[iCurrIndex];
        if (oTestSect)
        {
          delete a_parent_sections[iCurrIndex];
          if (bGatherJsDebug)
          {
            sDebugText += '  DELETED parent sect ID ' + oTestSect['id'].toString()
              + ' at a_parent_sections index ' + iCurrIndex.toString() + '\n';
          }
        }
      }

      // Update the values for the child custom book.
      if (iSubtractSects > 0)
      {
        b_childbk_sections -= iSubtractSects;
        b_childbk_pages -= iSubtractPages;
        b_childbk_price -= iSubtractPrice;
      }
      if (bGatherJsDebug)
      {
        sDebugText += '  After update: b_ch_sections: ' + b_childbk_sections
          + '. b_ch_pages: ' + b_childbk_pages + '. b_ch_price: ' + b_childbk_price + '\n';
      }
    }   // end of if (oSection['childSections'] != null && oSection['childSections'] != '')

    bIsCore = (oSection['isCore'] != null && typeof(oSection['isCore']) != 'undefined' && oSection['isCore'] == true);
    bSectNeedsCore = (oSection['coreSections'] != null && typeof(oSection['coreSections']) != 'undefined' && oSection['coreSections'] != '');
    if (bGatherJsDebug)
    {
      sDebugText += '  oSection[category]: '
        + (oSection['category'] != null && typeof(oSection['category']) != 'undefined' ? oSection['category'] : "null or undefined") + '\n';
      if (bSectNeedsCore || bIsCore)
        sDebugText += '  bSectNeedsCore: ' + bSectNeedsCore.toString() + ', bIsCore: ' + bIsCore.toString()
          + ', dependent_sect_count: '
          + (dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined' ? dependent_sect_count.toString() : "null or undefined")
          + ', oSection[dependentSectCount]: '
          + (oSection['dependentSectCount'] != null && typeof(oSection['dependentSectCount']) != 'undefined' ?
              oSection['dependentSectCount'].toString() : "null or undefined") + '\n';
    }

    // Graph paper and unit titles are not considered sections; nor are pedagogy.
    // In BrowseArt, oSection['pages'] is always "0", so don't use oSection['pages'] != "0" as a test;
    // use b_sections > 0 instead.
    if (oSection['title'].indexOf(' sheet of ') < 0 && oSection['title'].indexOf(' sheets of ') < 0
        && ((!bIsChild && b_sections > 0) || (bIsChild && b_childbk_sections > 0)) && oSection['category'] != 'pedagogy')
    {
      if ((bIsCore || bSectNeedsCore) && !(dependent_sect_count != null && typeof(dependent_sect_count) != 'undefined'))
      {
        dependent_sect_count = (oSection['dependentSectCount'] == null ? 0 : oSection['dependentSectCount']);
        if (bGatherJsDebug)
        {
          sDebugText += '  dependent_sect_count set to: ' + dependent_sect_count.toString() + '\n';
        }
      }

      // Do not allow a core section to be removed if there are any dependent sections selected.
      if (bIsCore && dependent_sect_count > 0)
      {
        if (called_from == 'flash')
        {
          // If called from Flash, the item has already been removed from the displayed list.  Show a message, but
          // don't return; allow the page and selection counts to be decremented so that discrepancies are prevented.
          // This code can be removed if the pam\sequence code is fixed (i.e., to NOT remove the section).
          alert("This selection should be re-selected because it is required by at least one other selection.\n"
            + "You should not remove this selection without first removing the dependent selections.\n");
        }
        else
        {
          alert("This selection is required by at least one other selection.\n"
            + "You can not remove this selection without first removing the dependent selections.");
          bPerformToggle = false;
          return;
        }
      }

      if (bIsChild)   // if child sections are being displayed
      {
        b_childbk_sections--;
      }
      else
      {
        b_sections--;
        if (oSection['isColorSection'])
          b_color_sections--;
        if (oSection['permNeeded'])
          perm_needed_count--;
      }

      if (bSectNeedsCore && dependent_sect_count > 0)
      {
        dependent_sect_count--;
        if (oSection['dependentSectCount'] != null && oSection['dependentSectCount'] != '')
          oSection['dependentSectCount'] = dependent_sect_count;
        if (bGatherJsDebug)
        {
          sDebugText += '  remove common: dependent_sect_count decremented to: ' + dependent_sect_count.toString()
            + ', oSection[dependentSectCount]: '
            + (oSection['dependentSectCount'] == null ? 'null' : oSection['dependentSectCount'].toString()) + '\n';
        }
      }

      if (bGatherJsDebug)
      {
        sDebugText += 'After sect removal: '
          + 'b_sections: ' + b_sections + ', b_color_sections: ' + b_color_sections
          + ', b_childbk_sections: ' + b_childbk_sections + '\n';
      }
    }

    if (bIsChild)   // if child sections are being displayed
    {
      vPrevChildPages = b_childbk_pages;
      //b_childbk_pages -= oSection['pages'];
      b_childbk_pages -= (oSection['allStartOnSameSide'] && (oSection['pages'] % 2 == 1) ?
        oSection['pages'] + 1 : oSection['pages']);
      if (bGatherJsDebug)
      {
        sDebugText += '  Removed pages. Now b_childbk_pages: ' + b_childbk_pages.toString() + '\n';
      }
    }
    else
    {
      vPrevPages = b_pages;
      //b_pages -= oSection['pages'];
      b_pages -= (oSection['allStartOnSameSide'] && (oSection['pages'] % 2 == 1) ?
        oSection['pages'] + 1 : oSection['pages']);
      if (bGatherJsDebug)
      {
        sDebugText += '  Removed pages. Now b_pages: ' + b_pages.toString() + '\n';
      }
    }
    if (oSection['category'] == 'section' && related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
    {
      if (related_section['on'] == true)
      {
        // If a section is being removed, and the related pedagogy had also been selected, the pedagogy pages must also be removed.
        //b_pages -= related_section['pages'];
        b_pages -= (oSection['allStartOnSameSide'] && (related_section['pages'] % 2 == 1) ?
          related_section['pages'] + 1 : related_section['pages']);
        if (bGatherJsDebug)
        {
          sDebugText += '  Removing related pedagogy pages. Now b_pages: ' + b_pages.toString() + '\n';
        }
      }
    }

    // Setting b_price here ignores changes to/from threshold price scheme.
    // In those cases, the page must be refreshed before correct price is shown.
    // TO DO: If price calculation is visible AND if a threshold price scheme may apply, use
    // appropriate values from a_price_schemes, based on number of selected pages or sections.
    if (bIsChild)   // if child sections are being displayed
    {
      b_childbk_price -= oSection['price'];
      b_allcolor_price -= oSection['allColorPrice'];
      if (bGatherJsDebug)
      {
        sDebugText += '  Lowered price. Now b_childbk_price: ' + b_childbk_price.toString()  + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }
    else
    {
      b_price -= oSection['price'];
      b_allcolor_price -= oSection['allColorPrice'];
      if (bGatherJsDebug)
      {
        sDebugText += '  Lowered price. Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }

    if (oSection['category'] == 'section' && related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
    {
      if (related_section['on'] == true)
      {
        // If a section is being removed, and the related pedagogy had also been selected, the pedagogy price must also be subtracted.
        b_price -= related_section['price'];
        b_allcolor_price -= related_section['allColorPrice'];
        if (bGatherJsDebug)
        {
          sDebugText += '  Subtracted related pedagogy price. Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
        }
      }
    }

    oSection['on'] = false;
    if (oSection['category'] == 'section')
    {
      if (related_sect_ID != null && related_sect_ID != '' && typeof(related_section) != 'undefined')
      {
        // Pedagogy gets turned off too.
        related_section['on'] = false;
      }
    }
    // Update here rather than in toggle function so that flashseq updates too.
    update_basics_form();

    if (typeof(thisMovie('ifactor_flash')) != 'undefined')
    {
      update_text('"' + oSection['title_trimmed'] + '" was removed');
      playmovie('ifactor_flash', 29);
    }
  }
  if (bGatherJsDebug)
  {
    sFuncName = 'remove_common';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }
}


function update_basics_form()
{
  if (bGatherJsDebug)
  {
    sFuncName = 'update_basics_form';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }

  var vEvenBookPages = 0;
  var vEvenChildBookPages = 0;
  var oBasicsForm = document.forms['project_basics'];
  var oElem, oElemLbl, oElemChildPrice, oElemColorPrice;
  var sLbl, sPrice;
  var bUpdateChildData;

  // Some variables below (minimum_pages, minimum_price, perm_needed_count, permission_msg, pgm_page_cost, site_url)
  // are declared in pam\include\tabs.htm so that they are in the scope of all PAM pages.
  // Do NOT change this code to "if (xyz == null || typeof(xyz) == 'undefined'))" because IT WILL FAIL.
  if (!(pgm_page_cost != null && typeof(pgm_page_cost) != 'undefined'))
    pgm_page_cost = 0;
  if (!(pgm_color_page_cost != null && typeof(pgm_color_page_cost) != 'undefined'))
    pgm_color_page_cost = 0;
  if (!(pgm_monetary_symbol != null && typeof(pgm_monetary_symbol) != 'undefined'))
    pgm_monetary_symbol = "\u0024";
  if (!(pgm_currency_type != null && typeof(pgm_currency_type) != 'undefined'))
    pgm_currency_type = "";   // no default; leave blank so it can be tested in update_basics_form()
  if (!(use_British_spelling != null && typeof(use_British_spelling) != 'undefined'))
    use_British_spelling = "0";

  if (bGatherJsDebug)
  {
    if (oBasicsForm == null)
      sDebugText += '  oBasicsForm is null';
    else if (typeof(oBasicsForm) == 'undefined')
      sDebugText += '  oBasicsForm is undefined';
  }

  if (typeof(oBasicsForm) != 'undefined')
  {
    oElem = document.getElementById('eltHasChildBook');   // to work in Mozilla, use getElementById
    bUpdateChildData = (oElem != null && typeof(oElem) != 'undefined');   // update whether or not oElem.checked == true

    // Update the section count value.
    oElem = document.getElementById('eltBookSections');
    if (oElem != null  && typeof(oElem) != 'undefined')
    {
      oElem.value = '' + b_sections;  // use concatenation to convert to string
    }
    // If a child book exists, update its section count value.
    if (bUpdateChildData)
    {
      oElem = document.getElementById('eltChildBookSections');
      if (oElem != null && typeof(oElem) != 'undefined')
      {
        oElem.value = '' + b_childbk_sections;  // use concatenation to convert to string
      }
    }

    // Update the Pages value.
    oElem = document.getElementById('eltBookPages');  // Not used in LabArt
    if (oElem != null && typeof(oElem) != 'undefined')
    {
      // Show an even number of pages without changing b_pages value.
      vEvenBookPages = (b_pages % 2 == 0 ? b_pages : b_pages + 1);
      oElem.value = '' + vEvenBookPages;    // use concatenation to convert to string
    }
    if (bGatherJsDebug)
    {
      sDebugText += '  bUpdateChildData: ' + bUpdateChildData.toString() + '\n';
    }

    // If a child book exists, update its Pages value.
    if (bUpdateChildData)
    {
      oElem = document.getElementById('eltChildBookPages');
      if (oElem != null && typeof(oElem) != 'undefined')
      {
        // Show an even number of pages without changing b_childbk_pages value.
        vEvenChildBookPages = (b_childbk_pages % 2 == 0 ? b_childbk_pages : b_childbk_pages + 1);
        oElem.value = '' + vEvenChildBookPages;   // use concatenation to convert to string
      }
    }

    if (bGatherJsDebug)
    {
      sDebugText += '  b_pages: ' + b_pages.toString() + ', b_price: ' + b_price.toString()
        + ', vEvenBookPages: ' + vEvenBookPages.toString() + ', vPrevPages: ' + vPrevPages.toString()
        + ', pgm_page_cost: ' + pgm_page_cost.toString() + ', pgm_color_page_cost: ' + pgm_color_page_cost.toString() + '\n';
    }

    // Update the Price value(s), regardless of which "price" elements exist in the page.
    if (vEvenBookPages > b_pages)
    {
      // There is now an odd number of pages.
      if (vPrevPages % 2 == 0)
      {
        // If the previous number of pages was NOT odd, add the cost of a page.
        b_price += pgm_page_cost;           // price is in cents
        b_allcolor_price += pgm_color_page_cost;
      }
      if (bGatherJsDebug)
      {
        sDebugText += '  Branch 1 (vEvenBookPages > b_pages): Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }
    else if (vPrevPages % 2 == 1)
    {
      // There is now an even number of pages.
      // If previously had odd number of pages (with an incremented price), subtract the cost of a page.
      b_price -= pgm_page_cost;         //  price is in cents
      b_allcolor_price -= pgm_color_page_cost;
      if (bGatherJsDebug)
      {
        sDebugText += '  Branch 2 (vPrevPages % 2 == 1): Now b_price: ' + b_price.toString() + ', b_allcolor_price: ' + b_allcolor_price.toString() + '\n';
      }
    }
    
    // Set the "price" element to show the updated price.
    oElem = document.getElementById('eltBookPrice');  // Not used in LabArt
    if (oElem != null && typeof(oElem) != 'undefined')
    {
      oElem.value = pgm_monetary_symbol + (b_price / 100).toFixed(2);
      if (bGatherJsDebug)
      {
        sDebugText += '  b_pages: ' + b_pages + ', vEvenBookPages: ' + vEvenBookPages
          + ', b_price: ' + b_price + ', displayed price: ' + oBasicsForm.elements['eltBookPrice'].value + '\n';
      }
    }

    // Set the color price text in the same way the project_basics.htm code does when the form is refreshed.
    // If there is B&W content but no color content selected, show a message to that effect.
    // Otherwise, show the color net price normally.
    // There are two individual elements, one for the label before the price, and one for the actual price.
    oElemLbl = document.getElementById('eltBookAllColorPrcLabel');  // to work in Mozilla, use getElementById
    if (oElemLbl)
    {
      if (b_color_sections == 0 && b_sections > 0)
      {
        // If this text changes, make similar changes in project_basics.htm.
        sLbl = "No " + (use_British_spelling == "0" ? "color" : "colour") + " content";

      }
      else
      {
        // If this text changes, make similar changes in project_basics.htm.
        if (pgm_currency_type == "")
        {
          // Show price per color page (with cent sign) instead of currency type.
          sLbl = "Color Price (" + pgm_color_page_cost + "\u00A2/page): ";
        }
        else
        {
          sLbl = (use_British_spelling == "0" ? "Color" : "Colour") + " Price (" + pgm_currency_type + "): ";
        }
      }
      oElemLbl.value = sLbl;
    }

    oElemColorPrice = document.getElementById('eltBookAllColorPrice');     // to work in Mozilla, use getElementById
    if (oElemColorPrice)
    {
      if (b_color_sections == 0 && b_sections > 0)
      {
        sPrice = "";
      }
      else
      {
        sPrice = pgm_monetary_symbol + (b_allcolor_price / 100).toFixed(2);
      }
      oElemColorPrice.value = sPrice;
    }

    // If a child book exists, update its Price value.  Assume that all-color price doesn't apply.
    if (bUpdateChildData)
    {
      oElemChildPrice = document.getElementById('eltChildBookPrice');
      if (oElemChildPrice && typeof(oElemChildPrice) != 'undefined')
      {
        if (vEvenChildBookPages > b_childbk_pages)
        {
          // There is now an odd number of pages.
          if (vPrevChildPages % 2 == 0)
          {
            // If the previous number of pages was NOT odd, add the cost of a page.
            b_childbk_price += pgm_page_cost;   // price is in cents
          }
        }
        else if (vPrevChildPages % 2 == 1)
        {
          // There is now an even number of pages.
          // If previously had odd number of pages (with an incremented price), subtract the cost of a page.
          b_childbk_price -= pgm_page_cost;     //  price is in cents
        }
        oElemChildPrice.value = pgm_monetary_symbol + (b_childbk_price / 100).toFixed(2);
        if (bGatherJsDebug)
        {
          sDebugText += '  b_childbk_pages: ' + b_childbk_pages + ', vEvenChildBookPages: ' + vEvenChildBookPages
            + ', b_childbk_price: ' + b_childbk_price + ', displayed price: ' + oElemChildPrice.value + '\n';
        }
      }
    }

    // Show a permission message if necessary.
    oElem = document.getElementById('eltSpecMsgLoc2');
    if (oElem != null && typeof(oElem) != 'undefined')
    {
      if (bGatherJsDebug)
      {
        sDebugText += '  eltSpecMsgLoc2 exists; '
          + 'perm_needed_count: ' + perm_needed_count + ', permission_msg: ' + permission_msg + '\n';
      }
      oElem.value = (perm_needed_count != null && parseInt(perm_needed_count) > 0 ? permission_msg : '');
    }
  }   // end of if (typeof(oBasicsForm) != 'undefined')

  if (is_submittable())
  {
    set_submit_ok();
  }
  else
  {
    set_submit_no();
  }

  // If checklist is used, update its items.
  if (document.images['check_pages'] != null)
  {
    if (b_pages < minimum_pages)
    {
      document.images['check_pages'].src = '../shared/spacer.gif';
    }
    else
    {
      document.images['check_pages'].src = site_url + '/wizard/checked.gif';
    }
  }
  if (document.images['check_price'] != null)
  {
    if (b_price < minimum_price)
    {
      document.images['check_price'].src = '../shared/spacer.gif';
    }
    else
    {
      document.images['check_price'].src = site_url + '/wizard/checked.gif';
    }
  }
  if (bGatherJsDebug)
  {
    sFuncName = 'update_basics_form';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }
}


function update_special_message()
{
  // To work in Mozilla, use getElementById.
  // Some variables below are declared in pam\include\tabs.htm.
  var oElem;

  oElem = document.getElementById('eltSpecMsgLoc1');  // message above the content list
  if (oElem != null && typeof(oElem) != 'undefined')
    oElem.value = (show_special_msg_loc1 != null && show_special_msg_loc1 == true ? special_msg_loc1_text : '');

  oElem = document.getElementById('eltSpecMsgLoc2');  // message below the content list
  if (oElem != null && typeof(oElem) != 'undefined')
    oElem.value = (show_special_msg_loc2 != null && show_special_msg_loc2 == true ? special_msg_loc2_text : '');
}


function set_submit_ok()
{
  if (document.images['tab_submit'] != null)
  {
    document.images['tab_submit'].src = site_url + '/wizard/step5-off.gif';
    document.getElementById('check_txt').style.display = 'block';
    document.getElementById('nonck_txt').style.display = 'none';
  }
}


function set_submit_no()
{
  if (document.images['tab_submit'] != null)
  {
    document.images['tab_submit'].src = site_url + '/wizard/step5-gray.gif';
    document.getElementById('check_txt').style.display = 'none';
    document.getElementById('nonck_txt').style.display = 'block';
  }
}


function submitSelect(oSelect, defaultQID)
{
  if (oSelect.value == '0')
    return;

  if (oSelect.value == '')
    oSelect.form.elements['qid'].value = 'fetch';
  else
    oSelect.form.elements['qid'].value = defaultQID;

  oSelect.form.submit();
}


function update_text(message)
{
  document.forms['ifactor'].elements['ifactor_text'].value = message;
}


function add_pg_section(sect_id, obj_name, info_arr)
{
  // This function is called for each section when the section list is displayed in "Edit Sequence", as well as in
  // the Gather pages, for each section in the list while the page is being initialized.
  // For easier maintenance and compatibility with Flash code, set "constant" section values (that don't depend
  // on whether the section is selected) in this function rather than passing them into the toggle functions.

  if (bGatherJsDebug && bGatherJsDbg_AddPgSection)
  {
    // If called from "sequence" folder (while initiating the selections list), debugging begins here as in toggle_section().
    if (!sDebugText || typeof(sDebugText) == 'undefined')
      sDebugText = '';

    sFuncName = 'add_pg_section';
    oNow = new Date();
    // Always append to sDebugText using +=
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
  }

  bAlertForAddCoreWasShown = false;   // reset when page is loaded or reloaded
  var iIndex;
  var iLimit = 70;
  var sTrimmed = ((info_arr && info_arr['title']) ? info_arr['title'] : '');
  if (sTrimmed.length > iLimit)
  {
    iIndex = sTrimmed.substring(0, iLimit).lastIndexOf(" ");
    if (iIndex > -1)
      sTrimmed = sTrimmed.substring(0, iIndex) + " ...";
  }

  var oSection = new Array(23);   // Javascript arrays are zero-based; use 1 less than array element count
  oSection['id'] = sect_id;
  oSection['objName'] = (info_arr && info_arr['obj_name'] ? info_arr['obj_name'] : 'section');  // name shared by elements in array
  oSection['category'] = (info_arr && info_arr['category'] ? info_arr['category'] : 'section'); // section, pedagogy, etc. 
  oSection['on'] = (info_arr && info_arr['on'] ? info_arr['on'] : null);            // selected
  oSection['title'] = (info_arr && info_arr['title'] ? info_arr['title'] : null);
  oSection['title_trimmed'] = sTrimmed;
  oSection['pages'] = (info_arr && info_arr['pages'] ? info_arr['pages'] : 0);
  oSection['isColorSection'] = (info_arr && info_arr['isColorSection'] ? info_arr['isColorSection'] : false);
  oSection['price'] = (info_arr && info_arr['price'] ? info_arr['price'] : 0);                          // price in cents
  oSection['allColorPrice'] = (info_arr && info_arr['allColorPrice'] ? info_arr['allColorPrice'] : 0);  // all-color price in cents
  oSection['SectionB'] = (info_arr && info_arr['SectionB'] ? info_arr['SectionB'] : null);
  oSection['relatedSection'] = (info_arr && info_arr['relatedSection'] ? info_arr['relatedSection'] : null);
  oSection['childSections'] = (info_arr && info_arr['childSections'] ? info_arr['childSections'] : null);
  oSection['childSectPages'] = (info_arr && info_arr['childSectPages'] ? info_arr['childSectPages'] : null);
  oSection['childSectPrices'] = (info_arr && info_arr['childSectPrices'] ? info_arr['childSectPrices'] : null);
  oSection['isChildSect'] = (info_arr && info_arr['isChildSect'] ? info_arr['isChildSect'] : false);
  oSection['chapterUnitNum'] = (info_arr && info_arr['chapterUnitNum'] ? info_arr['chapterUnitNum'] : -1);
  oSection['isCore'] = (info_arr && info_arr['isCore'] ? info_arr['isCore'] : false);
  oSection['coreSections'] = (info_arr && info_arr['coreSections'] ? info_arr['coreSections'] : null);
  oSection['dependentSectCount'] = (info_arr && info_arr['dependentSectCount'] ? info_arr['dependentSectCount'] : 0);
  oSection['permNeeded'] = (info_arr && info_arr['permNeeded'] ? info_arr['permNeeded'] : false);
  oSection['allStartOnSameSide'] = (info_arr && info_arr['allStartOnSameSide'] ? info_arr['allStartOnSameSide'] : false);
  oSection['relatedSectState'] = null;  // this is used later
  oSection['toggleType'] = null;        // this is used later

  pg_sections[sect_id] = oSection;
  if (info_arr && info_arr['childSections'])
    a_parent_sections.push(oSection);

  // Unlike most other info_arr members, info_arr['specialMsgAtLoc1'] and info_arr['specialMsgAtLoc1']
  // aren't copied to oSection.  They're used to set "show_special_msg_loc1" and "show_special_msg_loc2"
  // (declared in pam\include\tabs.htm), which will be true if info_arr['specialMsgAtLoc1'] or
  // info_arr['specialMsgAtLoc2'] is true for any displayed section, whether the section is selected or not.
  // If variables were set by a previously loaded section, there's no need to set them again.
  if (info_arr && info_arr['specialMsgAtLoc1'] && info_arr['specialMsgAtLoc1'].toString() != '' && !show_special_msg_loc1)
  {
    show_special_msg_loc1 = true;
    special_msg_loc1_text = info_arr['specialMsgAtLoc1'];
  }
  if (info_arr && info_arr['specialMsgAtLoc2'] && info_arr['specialMsgAtLoc2'].toString() != '' && !show_special_msg_loc2)
  {
    show_special_msg_loc2 = true;
    special_msg_loc2_text = info_arr['specialMsgAtLoc2'];
  }
  else if (info_arr && info_arr['permNeeded'] && info_arr['permNeeded'] == true && !show_special_msg_loc2)
  {
    // The BSM "permission" message also uses the "eltSpecMsgLoc2" element.
    show_special_msg_loc2 = true;
    special_msg_loc2_text = clear_perm_msg;   // set in tabs.htm
  }

  if (bGatherJsDebug && bGatherJsDbg_AddPgSection)
  {
    sFuncName = 'add_pg_section';
    // Note: Adding to pg_sections using a non-numeric index doesn't change pg_sections.length, which is always 0.
    sDebugText += '  a_parent_sections.length: ' + (a_parent_sections == null ? 'null' : a_parent_sections.length.toString()) + '\n';
    alert(sDebugText);
  }
}


function add_price_scheme(ps_id, ps_arr)
{
  var oPriceScheme = new Array(6);
  oPriceScheme['id'] = ps_id;
  oPriceScheme['sequence'] = (ps_arr && ps_arr['sequence'] ? ps_arr['sequence'] : 0);
  oPriceScheme['costPerSection'] = (ps_arr && ps_arr['costPerSection'] ? ps_arr['costPerSection'] : 0);  // price in cents
  oPriceScheme['costPerPage'] = (ps_arr && ps_arr['costPerPage'] ? ps_arr['costPerPage'] : 0);           // price in cents
  oPriceScheme['sectionThreshhold'] = (ps_arr && ps_arr['sectionThreshhold'] ? ps_arr['sectionThreshhold'] : 0);
  oPriceScheme['pageThreshhold'] = (ps_arr && ps_arr['pageThreshhold'] ? ps_arr['pageThreshhold'] : 0);
  a_price_schemes[ps_id] = oPriceScheme;
  //if (bGatherJsDebug)
  //{
  //  alert('TEST - gather.js add_price_scheme: oPriceScheme[id]: ' + oPriceScheme['id'] + ', [sequence]: ' + oPriceScheme['sequence'] + ', [costPerSection]: ' + oPriceScheme['costPerSection']);
  //}
}


function update_content_selections()
{
  // Create "list" strings that identify all sections that have been toggled on or off, and store them in form elements
  // so that the database can be updated with the changed selections.
  
  var iCount, j, k, sCat, sDelim;
  var sCore;
  var oElem, oSection, oTestSect;
  var oCategoryList = '', oCoreSections = '';
  var oRelatedSectList = '', oRelatedStateList = '';
  var oSectList = '', oToggleTypeList = '';
  
  iCount = a_toggled_sects.length;
  if (bGatherJsDbg_UpdateContent)
  {
    sDebugText = '';    // Called from "update" button click; debugging begins here as in toggle_section()
    sFuncName = 'update_content_selections';
    oNow = new Date();
    sDebugText += sFuncName + ' start: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    sDebugText += 'a_toggled_sects.length: ' + iCount.toString() + '\n';
  }

  if (iCount > 0)
  {
    // Simplify later processing by removing any unneeded previous instances of each section object in the a_toggled_sects
    // array (if certain criteria are met), so that only the "latest" instance of each section addition/removal is kept.
    // This loop will be skipped if there is only one element in a_toggled_sects.
    // Note: Deleted array elements are not actually removed, but are set to "undefined".
    for (j = 1; j < iCount; j++)    // we're testing previous values, so start at the 2nd element
    {
      oSection = a_toggled_sects[j];
      if (oSection && typeof(oSection) != 'undefined' && oSection['id'] != null && typeof(oSection['id']) != 'undefined')
      {
        if (bGatherJsDbg_UpdateContent)
        {
          sDebugText += 'Prev elements test, j = ' + j.toString() + ': oSection[id]: ' + oSection['id'].toString() + '\n';
        }
        for (k = 0; k < j; k++)
        {
          oTestSect = a_toggled_sects[k];
          if (oTestSect && typeof(oTestSect) != 'undefined' && oTestSect['id'] != null && typeof(oTestSect['id']) != 'undefined'
              && oTestSect['id'] == oSection['id'] && oTestSect['toggleType'] == oSection['toggleType'])
          {
            delete a_toggled_sects[k];
            if (bGatherJsDbg_UpdateContent)
            {
              sDebugText += 'Section deleted for k = ' + k.toString() + ', section id: ' + oTestSect['id'].toString() + '\n';
            }
          }
        }
      }
    }
  
    // Any deletions from the previous step shouldn't change the array length, but get the length again to be safe.
    iCount = a_toggled_sects.length;    // array length will decrease with each loop, so the original length must be saved
  
    // Get data from each section object, and while doing so, empty out the a_toggled_sects array so that it
    // can be used to track future selection changes after this update.
    for (j = 0; j < iCount; j++)
    {
      sDelim = (j < iCount - 1 ? '|' : '');   // no delimiter after the final list item

      // Remove the section object from the beginning of the array (using shift function) and store it in oSection.
      // Append each relevant value from oSection to the output list strings.
      oSection = a_toggled_sects.shift();
      if (oSection && typeof(oSection) != 'undefined' && oSection['id'] != null && typeof(oSection['id']) != 'undefined')
      {
        oSectList += oSection['id'] + sDelim;
        oToggleTypeList += oSection['toggleType'] + sDelim;

        // Category: use only the first letter ("S" for section, "P" for pedagogy). If empty, use "S".
        sCat = (oSection['category'] == null || oSection['category'] == '' ?
          'S' : oSection['category'].substring(0, 1).toUpperCase());
        oCategoryList += sCat + sDelim;

        // If a pedagogy is being selected, the "related" parent section must also be selected if it wasn't already.
        oRelatedSectList += (oSection['relatedSection'] ? oSection['relatedSection'] : '') + sDelim;
        oRelatedStateList += (oSection['relatedSectState'] ? oSection['relatedSectState'] : '') + sDelim;
        {
          sDebugText += 'j = ' + j.toString() + ' (' + oSection['id']
            + '): category: ' + sCat + ', relatedSectState: ' + (oSection['relatedSectState'] ? oSection['relatedSectState'].toString() : '')
            + ', relatedSection: ' + (oSection['relatedSection'] ? oSection['relatedSection'] : '') + '\n';
        }
        
        if (oSection['toggleType'] == '1')
        {
          // If this section has any associated required (core) sections, append their IDs to "oCoreSections" if not already there.
          sCore = (oSection['coreSections'] ? oSection['coreSections'].toString().toLowerCase() : '');
          if (sCore != '' && oCoreSections.indexOf(sCore) == -1)
          {
            if (oCoreSections != '')
              oCoreSections += ',';
            oCoreSections += sCore;
          }
        }
      }
    }
    
    // Store the list strings in the form's hidden input elements.
    oElem = document.getElementById('contentSectList');   // to work in Mozilla, use getElementById
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oSectList;       // use concatenation to convert to string

    oElem = document.getElementById('contentToggleTypeList');
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oToggleTypeList;

    oElem = document.getElementById('contentCategoryList');
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oCategoryList;

    oElem = document.getElementById('contentRelatedSectList');
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oRelatedSectList;

    oElem = document.getElementById('contentRelatedStateList');
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oRelatedStateList;

    oElem = document.getElementById('contentCoreSections');
    if (oElem != null && typeof(oElem) != 'undefined')
      oElem.value = '' + oCoreSections;

    if (hidden_sect_id_list && typeof(hidden_sect_id_list) != 'undefined')
    {
      oElem = document.getElementById('hiddenSectList');
      if (oElem != null && typeof(oElem) != 'undefined')
        oElem.value = '' + hidden_sect_id_list;
    }

    if (book_sect_toggle_type && typeof(book_sect_toggle_type) != 'undefined')
    {
      oElem = document.getElementById('hiddenSectToggleType');
      if (oElem != null && typeof(oElem) != 'undefined')
        oElem.value = '' + book_sect_toggle_type;
    }

    a_toggled_sects = new Array(0);
  }   // end of if (iCount > 0)

  if (bGatherJsDbg_UpdateContent)
  {
    sFuncName = 'update_content_selections';
    sDebugText += 'a_toggled_sects was initialized\n';
    sDebugText += 'oSectList: ' + oSectList + '\n';
    sDebugText += 'oToggleTypeList: ' + oToggleTypeList + '\n';
    sDebugText += 'oCategoryList: ' + oCategoryList + '\n';
    sDebugText += 'oRelatedSectList: ' + oRelatedSectList + '\n';
    sDebugText += 'oRelatedStateList: ' + oRelatedStateList + '\n';
    sDebugText += 'oCoreSections: ' + oCoreSections + '\n';
    sDebugText += 'hidden_sect_id_list: ' + hidden_sect_id_list + '\n';
    sDebugText += 'book_sect_toggle_type: ' + book_sect_toggle_type + '\n';
    oNow = new Date();
    sDebugText += sFuncName + ' exit: ' + zeroPad(oNow.getHours(), 2) + ':' + zeroPad(oNow.getMinutes(), 2) + ':' + zeroPad(oNow.getSeconds(), 2) + '.' + zeroPad(oNow.getMilliseconds(), 3) + '\n';
    alert(sDebugText);
  }
}


function gatherUpdateRedirect(form_name, nav_to_page)
{
  // Similar to updateRedirect in pfactory.js, but with a call to update_content_selections if any selections have changed.
  // Update the current form (e.g. sFormName = 'pam/gather') before redirecting to a page in any folder that has the
  // same parent folder as the current folder (e.g. sNavToPageName = 'sequence/'; in this example, both the "gather"
  // and "sequence" folders are under "pam", and redirection will be to the default aspx file in "sequence").
  var bHasChildBook = false, bViewChildBook = false;
  var oElemHasChildBk, oElemCbookChoice;
  var oForm = document.forms[form_name];
  if (oForm && typeof(oForm) != 'undefined' && oForm.elements && typeof(oForm.elements) != 'undefined')
  {
    if (bGatherJsDbg_UpdateContent)
    {
      alert('gatherUpdateRedirect: Number of toggles: '
        + (a_toggled_sects && a_toggled_sects.length > 0 ? a_toggled_sects.length.toString() : '0'));
    }
    if (a_toggled_sects && a_toggled_sects.length > 0)
    {
      //var oConfirm = confirm("Update the project using your changed selections?");
      //if (!oConfirm)
      //  a_toggled_sects = new Array(0);   // discard the user's selection changes

      // To navigate to the other page, execute this code regardless of user confirmation.
      // If the user selected Cancel, no updating will occur because a_toggled_sects is empty.
      update_content_selections();

      oForm.elements['pf_action'].value = 'update_content';
    }

    // The existence of the "eltHasChildBook" checkbox indicates that a child custom book is offered,
    // regardless of whether the user has chosen to include it or view it.
    oElemHasChildBk = document.getElementById('eltHasChildBook'); // to work in Mozilla, use getElementById
    bHasChildBook = (oElemHasChildBk != null && typeof(oElemHasChildBk) != 'undefined'
      && oElemHasChildBk.checked == true);   // project_basics checkbox: test "checked", not "value" (which is always 1)
    if (bHasChildBook)
    {
      // See if the child custom book view is selected.
      oElemCbookChoice = document.getElementById('cbook_choice');
      bViewChildBook = (oElemCbookChoice != null && typeof(oElemCbookChoice) != 'undefined'
        && oElemCbookChoice.value == 'view_child_cbook');
    }
    // If the child custom book view is selected, and "cbook_select" is not in "nav_to_page", specify child book view.
    // Otherwise, if a child custom book exists and "nav_to_page" has no "cbook_select" param, assume parent book view.
    if (nav_to_page.indexOf('/start') > -1)    // never append "cbook_select" param if returning to start page
      oForm.elements['pf_redirect'].value = nav_to_page;
    else if (bViewChildBook && nav_to_page.indexOf("cbook_select") == -1)
      oForm.elements['pf_redirect'].value = nav_to_page
        + (nav_to_page.indexOf("?") > -1 ? "&" : "?") + "cbook_select=use_child_cbook";
    else if (bHasChildBook && !bViewChildBook && nav_to_page.indexOf("cbook_select") == -1)
      oForm.elements['pf_redirect'].value = nav_to_page
        + (nav_to_page.indexOf("?") > -1 ? "&" : "?") + "cbook_select=use_parent_cbook";
    else
      oForm.elements['pf_redirect'].value = nav_to_page;

    if (bGatherJsDbg_UpdateContent)
    {
      alert('End of gatherUpdateRedirect: form_name: ' + form_name + ', pf_redirect: ' + oForm.elements['pf_redirect'].value);
    }

    oForm.submit();
  }
  else  // in some cases (such as the "Terms" version of professor.htm), the form doesn't exist in the page
  {
    if (bGatherJsDbg_UpdateContent)
    {
      alert('gatherUpdateRedirect: navigating to ' + nav_to_page);
    }
    window.location = nav_to_page;
  }
  return false;
}


function thisMovie(movieName)
{
  // IE and Netscape refer to the movie object differently.
  // This function returns the appropriate syntax depending on the browser.
  if (window[movieName] != null)
  {
    return window[movieName];
  }
  else
  {
    return document[movieName];
  }
}


// Checks if movie is completely loaded.
// Returns true if yes, false if no.
function movieIsLoaded(theMovie)
{
  // First make sure the movie is defined.
  if (typeof(theMovie) != 'undefined')
  {
    // If it is, check how much of it is loaded.
    return theMovie.PercentLoaded() == 100;
  }
  else
  {
    // If the movie isn't defined, it's not loaded.
    return false;
  }
}


function playmovie(movieName, frame)
{
  if (movieIsLoaded(thisMovie(movieName)))
  {
    thisMovie(movieName).TGotoFrame("_level0/addPage",frame);
    thisMovie(movieName).TPlay("/addPage");
  }
}

