// $Id: gui.js,v 1.4 2007/06/14 18:06:50 stephen Exp $

/******************************************************************************
 * Javascript Library
 * name : gui.js
 * author : jon parker, jonparker@icserv.net
 *****************************************************************************/

/********************************* SELECT ALL ********************************/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : select_all( [string] all_box_group_name,
 *                           [string] box_group_name,
 *                           [html-form-object] form,
 *                           [boolean] value )
 *
 * description : This function will select or deselect all the checkbox items
 *               with the box_group_name.  It will also keep the all_box_group
 *               in sync by select all checkboxes belonging to that group.
 *
 * parameters  : all_box_group_name  : the name of the all_box group
 *               box_group_name      : the name of the checkbox group
 *               form                : the form to which the group belongs
 *               value               : the value to set the seletion to
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function select_all( all_box_group_name, box_group_name, form, value ){
   var box_group = new CheckboxGroup( box_group_name, form );
   var all_group = new CheckboxGroup( all_box_group_name, form );

   // synchronize the all_group to the value of the all_box
   all_group.set_group( value );

   // here we set each box in the desired group to the value of the all_box
   box_group.set_group( value );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : sync_select_all( [form-input-checkbox-object] current_box,
 *                                [string] all_group_name,
 *                                [html-form-object] form )
 * 
 * description : This function will keep the select all checkboxes in
 *               synchronization with the state of the checkbox group.
 *               If the user selects every element in the current_group
 *               manually, the all_group will be selected.  Likewise, if the
 *               current_group was previously all_selected, and one element
 *               is deselected, the all_group will be deselected.
 *               
 * parameters  : current_box    : the current element that called this function
 *               all_group_name : the select all checkbox group name
 *               form           : the form to which the all_group belongs
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function sync_select_all( current_box, all_group_name, form ){
   var current_group = new CheckboxGroup( current_box.name, current_box.form );
   var all_group = new CheckboxGroup( all_group_name, form );

   // if they selected the current_box which made the current_group all
   // selected, then set the all_group to true
   if( current_box.checked && current_group.all_selected() ){
      all_group.set_group( true );
   }
   // if they just deselected the current_box and the all_group was selected,
   //    then set the all_group to false
   else if( !current_box.checked && all_group.all_selected() ){
      all_group.set_group( false );
   }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : are_any_selected( [string] group_name,
                                   [html-form-object] form )
 *
 * description : creates a CheckboxGroup and returns it's is_any_selected value
 *
 * parameters  : group_name : the name of the checkbox group
 *               form       : the form to which the group belongs
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function get_num_selected( group_name, form ){
   var group = new CheckboxGroup( group_name, form );
   return group.num_selected();
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : CheckboxGroup
 *
 * function    : CheckboxGroup( [string] group_name, [html-form-object] form )
 *
 * description : The constructor sets up the checkbox group array.
 *
 * parameters  : group_name : the name of the checkbox group
 *               form       : the form to which the group belongs
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function CheckboxGroup( group_name, form ){
   this.name  = group_name;
   this.form  = form;
   this.group = create_element_array( group_name, form );
}

function create_element_array( element_name, form ){
   var group = new Array();

   // This section overcomes a browser incompatibility that if only
   //   one element exists in the checkbox group, it cannot be accessed
   //   in an array.  To overcome this we create our own array by
   //   looping through every element in the form and getting the ones
   //   with the appropriate group_name.

   var all = form.elements;
   var j = 0;
   for( i = 0; i < all.length; ++i ){
      if( all[i].name == element_name ){
         group[j++] = all[i];
      }
   }   

   return group;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : CheckboxGroup
 *
 * function    : set_box_group( [boolean] value )
 * 
 * description : This function sets each element in this checkbox group to the
 *               appropriate value.
 * 
 * parameters  : value : a boolean [true|false] value
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
CheckboxGroup.prototype.set_group = function( value ){
   for( i = 0; i < this.group.length; ++i ){
      this.group[i].checked = value;
   }
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : CheckboxGroup
 *
 * function    : num_selected()
 * 
 * description : This function returns the number of checkboxes in this group
 *               that are selected.
 *
 * returns     : int : number of selected checkboxes
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
CheckboxGroup.prototype.num_selected = function(){
   var j = 0;
   for( i = 0; i < this.group.length; ++i ){
      if( this.group[i].checked ){
         ++j;
      }
   }

   return j;
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : CheckboxGroup
 *
 * function    : all_selected()
 * 
 * description : This function checks to see if every checkbox in the group is
 *               selected or not.  As soon as the process finds one checkbox
 *               that is not selected, it will return false.
 *
 * returns     : boolean
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
CheckboxGroup.prototype.all_selected = function(){
   if( this.num_selected() == this.group.length ){
      return true;
   }

   return false;
};

/******************************** END SELECT ALL *****************************/


/******************************* SWAP BOX SYSTEM *****************************/

/**
 * disableParents
 * IE does not implement disabled options, so our solution is to
 * gray the text and not let them select parent options. Since options
 * do not handled any events (at least in IE), we use is select.onchange
 * to deselect any parents selected.
 * 
 * @tparam select-html-element sel
 *    
 */
function disableParents (sel) {
   //if  < IE 6
   for (var i = 0; i < sel.length; ++i) {
      if (sel[i].disabled && sel[i].selected) {
         sel[i].selected=false;
      }
   }
}

/**
 * Creates the proper indenting for inserting new options
 *
 * @tparam HTMLOptionElement option this option is parsed to find the
 *        number of indenting spaces
 *
 * @tparam String returns the string of indenting spaces using charCode.
 */
function findIndent (option) {
   var text = option.text;
   var i = 0;
   var str = '';
   while ((i < text.length) && (text.charAt(i) == String.fromCharCode(160))) {
      str += String.fromCharCode(160);
      ++i;
   }
   return str;
}

/**
 * Finds the list of selected options, and uses the SwapSystem to find
 * the corresponding OptionObject. moveRight or moveLeft is then called.
 * (these functions contain the logic of removing and inserting options.
 *
 * @tparam form-select-object from
 *     options will swap from 'from' to 'to'
 *
 * @tparam form-select-object to
 *     options will swap from 'from' to 'to'
 */
function swap( from, to ){
   var SSRef = SwapSystem.SSCollection;
   var i;
   var selectList = new Array();
   for( i = from.options.length - 1; i >= 0; --i ){
      if( from.options[i].selected ){
         selectList.push(from.options[i]);
      }
   }
   //this way removing will not effect the iteration count
   for (i = selectList.length - 1; i >= 0; --i) {
      var id = selectList[i].id
      var baseId = id.substr(0, id.lastIndexOf('@'));
      var selOption = SSRef.getOptionById(baseId);
      if (to.id == 'rightBox') {
         selOption.moveRight();
      }
      else {
         selOption.moveLeft();
      }
   }
}

/**
 * SwapSystem
 * Creates two swapboxes
 * Uses OptionObjects to hold heiarchy
 * Currently only one SwapSystem can display on a page.
 *
 * @ctor creates a new SwapSystem
 *
 * @tparam int height
 *     height of the swapboxes
 *
 * @tparam string vals_title
 *     title of the right swapbox
 *
 * @tparam string pos_vals_title
 *     title of the left swapbox
 *
 * @tparam string vals_form_name 
 *     form name for the values
 *
 * @tparam OptionObject-Array allOptions
 *     contains the logic for moving subcategories
 *
 * @tparam string-array rightIds
 *     holds the options that start on the right.
 *
 * @tparam html-form-object form
 *     optional. if form is not provided, SwapSystem will use the first form
 *     of the page (document.form[0])
 *
 * @tparam int table_size
 *     size of the table wrapping the SwapSystem.
 */
function SwapSystem( height, pos_vals_title, vals_title, pos_vals_form_name,
                     vals_form_name, allOptions, rightIds, form, table_size  )
{
   this.table_size = table_size != null ? table_size : 400;
   this.options = allOptions;
   this.form = form;
   this.rightIds = rightIds;
   this.pos_swap_box  = new Swapbox( height, pos_vals_title,
                                    pos_vals_form_name, allOptions, 'leftBox' );
   this.vals_swap_box = new Swapbox( height, vals_title,
                                    vals_form_name, allOptions, 'rightBox' );
   SwapSystem.SSCollection = this;
}

/**
 * SwapSystem.getOptions()
 *
 * @tparam OptionObject
 *    returns the SwapSystem.options
 */
function SwapSystem_getOptions() {
   return this.options;
}
SwapSystem.prototype.getOptions = SwapSystem_getOptions;

/**
 * SwapSystem.print()
 * This function will print out the SwapSystem
 */
function SwapSystem_print(){
   document.write( this );
   eval( 'this.form.' + this.pos_swap_box.form_name + '.options' );
   eval( 'this.form.' + this.vals_swap_box.form_name + '.options' );
   for (var i = 0; i < this.rightIds.length; ++i) {
      var rightOption = this.getOptionById(this.rightIds[i])
      rightOption.moveRight();
   }
};
SwapSystem.prototype.print = SwapSystem_print;

/**
 * Searches for the the OptionObject with the given id
 *
 * @tparam string id
 *    searches for OptionObject with the same id.
 *
 * @tparam OptionObject
 *    return the option found
 */
function SwapSystem_getOptionById (id) {
   var options = this.options
   for (i = 0; i < options.length; ++i) {
      var option = options[i].getOptionById(id)
      if (typeof option == 'object') {
         return option;
      }
   }
   return;
}
SwapSystem.prototype.getOptionById = SwapSystem_getOptionById;


/**
 * SwapSystem.toString()
 * 
 * This function generates the html for the SwapSystem.  Here is
 * where the buttons and layout are defined.  Each Swapbox is
 * responsible for providing its own html.
 *
 * @tparam string html string to create the SwapSystem
 */
function SwapSystem_toString() {
   var html = '';
   html += '' +
      '<table width="' + this.table_size + '" align="center">\n' +
      '<tbody>\n' +
       '<tr>\n' +
        '<td id="leftData" align="center" width="33%" valign="top" nowrap>\n' +
         this.pos_swap_box +
        '</td>\n' +
        '<td align="center" width="34%">\n' +
         '<input type="button" value="->" onclick="swap(' +
          this.pos_swap_box.form_name + ',' +
          this.vals_swap_box.form_name +
         ')"><br>\n' +
	      '<input type="button" value="<-" onclick="swap(' +
          this.vals_swap_box.form_name + ',' +
          this.pos_swap_box.form_name +
         ')"><br>\n' +
        '</td>\n' +
        '<td id="rightData" align="center" width="33%" valign="top" nowrap>\n' +
         this.vals_swap_box +
        '</td>\n' +
       '</tr>\n' +
      '</tbody>' +
      '</table>';

   return html;
}
SwapSystem.prototype.toString = SwapSystem_toString;

/**
 * Swapbox constructor
 * @ctor constructs a swapbox. Swapboxes are multi-select
 *
 * @tparam int height
 *    the height of the select box
 *
 * @tparam string title
 *    title for this swapbox
 *
 * @tparam string form_name
 *    form name for this box
 *
 * @tparam OptionObject-array options
 *    reference to the master list of options
 *
 * @tparam string side
 *    'left' | 'right'
 */
function Swapbox( height, title, form_name, options, side ){
   this.height     = height;
   this.title      = title;
   this.form_name  = form_name;
   this.options    = options;
   this.multi_move = 'multiple';
   this.side       = side;
}

/**
 * Swapbox.getSide()
 *
 * @tparam string
 *    returns side ('left' | 'right')
 */
function Swapbox_getSide() {
   return this.side;
}
Swapbox.prototype.getSide = Swapbox_getSide;

/**
 * Swapbox.toString()
 * 
 * This function generates the html for the Swapbox.  An initial
 *    option is created to provide spacing in Netscape browsers.
 *
 * @tparam string
 *    a string of html
 */
function Swapbox_toString(){
   var html = this.title + '<br>\n' + '<select name="' + this.form_name +
              '" size="' + this.height + '" id="' + this.side + '" ' +
              this.multi_move +
              ' onchange="disableParents(' + this.side + ')">\n';
   
   for (i = 0; i < this.options.length; ++i) {
      html += this.options[i].toString(this.side);
   }
   html += '</select>';

   return html;
};
Swapbox.prototype.toString = Swapbox_toString;

/**
 * @ctor OptionObject
 *
 * @tparam string text
 *    text of the option
 *
 * @tparam string value
 *    value of the option
 *
 * @tparam int id
 *    id of the option. This is holds the sequential ordering.
 *
 * @tparam  value
 *    value of the option
 */
function OptionObject (text, value, id) {
   this.text = text;
   this.value = value;
   this.id = id;
   this.parent;
   this.level = 0;
   this.children = new Array();
   this.left = true;
   this.right = false;
}

/**
 * OptionObject.getText()
 *
 * @tparam string
 *    returns the OptionObject.text
 */
function OptionObject_getText() {
   return this.text;
}
OptionObject.prototype.getText = OptionObject_getText;

/**
 * OptionObject.getValue()
 *
 * @tparam string
 *    returns the OptionObject.value
 */
function OptionObject_getValue() {
   return this.value;
}
OptionObject.prototype.getValue = OptionObject_getValue;

/**
 * OptionObject.getId()
 *
 * @tparam string
 *    returns the OptionObject.id
 */
function OptionObject_getId() {
   return this.id;
}
OptionObject.prototype.getId = OptionObject_getId;

/**
 * OptionObject.getParent()
 *
 * @tparam OptionObject
 *    returns the OptionObject.parent
 */
function OptionObject_getParent() {
   return this.parent;
}
OptionObject.prototype.getParent = OptionObject_getParent;

/**
 * OptionObject.getLeft()
 *
 * @tparam boolean
 *    returns the OptionObject.left
 */
function OptionObject_getLeft() {
   return this.left;
}
OptionObject.prototype.getLeft = OptionObject_getLeft;

/**
 * OptionObject.getRight()
 *
 * @tparam boolean
 *    returns the OptionObject.right
 */
function OptionObject_getRight() {
   return this.right;
}
OptionObject.prototype.getRight = OptionObject_getRight;

/**
 * OptionObject.getChildren()
 *
 * @tparam OptionObject-Array
 *    returns the OptionObject.children
 */
function OptionObject_getChildren() {
   return this.children;
}
OptionObject.prototype.getChildren = OptionObject_getChildren;

/**
 * OptionObject.moveRight()
 *
 * Inserts option into the right Swapbox if not in options
 * Removes option from the left Swapbox if no children exist in the leftbox
 * Calls this.parent.moveRight() to update the parents.
 */
function OptionObject_moveRight () {
   if (!(this.right)) {
      this.right = true;
      var rightBox = document.getElementById('rightBox');
      var rightOptions = rightBox.options;
      //add this option to Right
      var i;
      var optBaseId;
      var duplicate = false;
      for (i = 0; i < rightOptions.length; ++i) {
         optBaseId = rightOptions[i].id.substr(0,
                  rightOptions[i].id.lastIndexOf('@'));
         if (optBaseId > this.id) {
            break; //this way 'i' will not increment to the end.
         }
         else if (optBaseId == this.id) {
            duplicate = true;
            break;
         }
      }
      if (!duplicate) { //don't insert the same option twice
         var indent = findIndent(document.getElementById(this.id + "@l"))
         //var newOption = new Option(indent + this.text, this.value);
         var newOption = document.createElement('option');
         newOption.text = indent + this.text;
         newOption.value = this.value;
         newOption.id = this.id + "@r";
         if (this.children.length != 0) {
            newOption.style.color='graytext';
            newOption.disabled = true;
         }
         rightBox.length++;
         var j;
         for (j = rightBox.length - 1; ((j > 0) &&
             (rightBox[j-1].id.substr(0, rightBox[j-1].id.lastIndexOf('@')) > this.id)); --j) {
            rightBox[j].text = rightBox[j-1].text
            rightBox[j].value = rightBox[j-1].value;
            rightBox[j].id = rightBox[j-1].id;
            rightBox[j].style.color = rightBox[j-1].style.color;
            rightBox[j].disabled = rightBox[j-1].disabled;
         }
         rightBox[j] = newOption;
      }
   }

   var children = this.children
   var removeLeft = true;
   var i
   for (i = 0; i < children.length; ++i) {
      if (children[i].getLeft()) {
         removeLeft = false;
         break;
      }
   }
   if (removeLeft) {
      this.left = false;
      //remove this option from the left.
      var leftBox = document.getElementById('leftBox');
      var leftNode = document.getElementById(this.id + "@l");
      leftBox.removeChild(leftNode);
   }
   if (this.parent) {
      this.parent.moveRight()
   }
}
OptionObject.prototype.moveRight = OptionObject_moveRight;

/**
 * OptionObject.moveLeft()
 *
 * Inserts option into the leftbox if not already there
 * Removes option from the rightbox if no children exist there
 * Calls this.parent.moveLeft() to update the parents.
 */
function OptionObject_moveLeft () {

   if (!(this.left)) {
      this.left = true;
      var leftBox = document.getElementById('leftBox');
      var leftOptions = leftBox.options;
      //add this option to Left
      var i;
      var optBaseId;
      var duplicate = false;
      for (i = 0; i < leftOptions.length; ++i) {
         optBaseId = leftOptions[i].id.substr(0,
                  leftOptions[i].id.lastIndexOf('@'));
         if (optBaseId > this.id) {
            break; //this way 'i' will not increment to the end.
         }
         else if (optBaseId == this.id) {
            duplicate = true;
            break;
         }
      }
      if (!duplicate) { //don't insert the same option twice
         var indent = findIndent(document.getElementById(this.id + "@r"))
         var newOption = new Option(indent + this.text, this.value);
         newOption.id = this.id + "@l";
         if (this.children.length != 0) {
            newOption.disabled = true;
            newOption.style.color='graytext';
         }
         leftBox.length++;
         var j;
         for (j = leftBox.length - 1; ((j > 0) &&
             (leftBox[j-1].id.substr(0, leftBox[j-1].id.lastIndexOf('@')) > this.id)); --j) {
            leftBox[j].text         = leftBox[j-1].text
            leftBox[j].value        = leftBox[j-1].value;
            leftBox[j].id           = leftBox[j-1].id;
            leftBox[j].style.color  = leftBox[j-1].style.color;
            leftBox[j].disabled     = leftBox[j-1].disabled;
         }
         leftBox[j] = newOption;
      }
   }


   var children = this.children
   var removeRight = true;
   var i;
   for (i = 0; i < children.length; ++i) {
      if (children[i].getRight()) {
         removeRight = false;
      }
   }
   if (removeRight) {
      this.right = false;
      //remove this option from the right.
      var rightBox = document.getElementById('rightBox');
      var rightNode = document.getElementById(this.id + "@r");
      rightBox.removeChild(rightNode);
   }
   if (this.parent) {
      this.parent.moveLeft()
   }
}
OptionObject.prototype.moveLeft = OptionObject_moveLeft;

/**
 * OptionObject.getOptionById()
 * Recursively searches for the matching id.
 *
 * @tparam string id
 *    searching for the OptionObject that corresponds to id
 */
function OptionObject_getOptionById(id) {
   //if this id matches, return this
   if (this.id == id) {
      return this
   }
   var children = this.children
   var i
   for (i = 0; i < children.length; ++i) {
      var matchedId = children[i].getOptionById(id);
      //if an object is returned, return it.
      if ( (typeof matchedId == 'object') && (matchedId.id == id) ) {
         return matchedId;
      }
   }
   //children id's don't match id, return empty handed.
   return;
}
OptionObject.prototype.getOptionById = OptionObject_getOptionById;

/**
 * OptionObject.toString()
 * Recursively generates and returns a string of html to display an option
 *
 * @tparam string side
 *    side helps determine whether to print the option or not
 *
 * @tparam string spacing
 *    spacing properly indents each option.text
 */
function OptionObject_toString(side, spacing) {
   if (!(spacing)){
      spacing = '';
   }
   // if the option is on that side, print it
   if (((side == 'leftBox' ) && (this.left )) ||
       ((side == 'rightBox') && (this.right))) {

      var htmlValue = this.value;
      var htmlText = this.text;

      //escape out any potential hazards.
      htmlValue = htmlValue.replace(/</g, "&lt;");
      htmlValue = htmlValue.replace(/"/g, "&quot;");
      htmlText = htmlText.replace(/</g, "&lt;");

      var idTail = (side == 'leftBox') ? '@l' : '@r';
      var html = ' <option value="' + htmlValue + '" id="' +
         this.id + idTail + '" ';
      if (this.children.length != 0) {
         html += 'disabled="disabled" ';
         html += "style='color: graytext' ";
      }
      html += '>' + spacing + htmlText + '</option>\n'

      //try to print the children
      var children = this.children
      var i
      for (i = 0; i < children.length; ++i) {
         html += children[i].toString(side, '&nbsp;&nbsp;' + spacing)
      }
      return html;
   }
   return '';
}
OptionObject.prototype.toString = OptionObject_toString;

/**
 * OptionObject.setText()
 *
 * @tparam string text
 *    Sets the OptionObject.text
 */
function OptionObject_setText(text) {
   this.text = text;
}
OptionObject.prototype.setText = OptionObject_setText;

/**
 * OptionObject.setValue()
 *
 * @tparam string value
 *    Sets the OptionObject.value
 */
function OptionObject_setValue(value) {
   this.value = value;
}
OptionObject.prototype.setValue = OptionObject_setValue;

/**
 * OptionObject.setId()
 *
 * @tparam int id
 *    Sets the OptionObject.id
 */
function OptionObject_setId() {
   this.id = id;
}
OptionObject.prototype.setId = OptionObject_setId;

/**
 * OptionObject.setParent()
 *
 * @tparam OptionObject parent
 *    Sets the OptionObject.parent
 */
function OptionObject_setParent(parent) {
   this.parent = parent;
}
OptionObject.prototype.setParent = OptionObject_setParent;

/**
 * OptionObject.setLeft()
 *
 * @tparam boolean isLeft
 *    Sets the OptionObject.left
 */
function OptionObject_setLeft(isLeft) {
   this.left = isLeft;
}
OptionObject.prototype.setLeft = OptionObject_setLeft;

/**
 * OptionObject.setRight()
 *
 * @tparam boolean isRight
 *    Sets the OptionObject.right
 */
function OptionObject_setRight(isRight) {
   this.right = isRight;
}
OptionObject.prototype.setRight = OptionObject_setRight;

/**
 * OptionObject.addChild(child)
 * sets the child's parent to this.
 *
 * @tparam OptionObject child
 *    Adds child to the OptionObject.children array
 */
function OptionObject_addChild(child) {
   this.children.push(child);
   child.setParent(this);
}
OptionObject.prototype.addChild = OptionObject_addChild;

/**
 * OptionObject.removeChild(id)
 * removes the child from the parent, and unassigns the parent from the child.
 * Does not update the Swapbox display.
 *
 * @tparam int id
 *    Removes child from the 
 *
 * @tparam boolean
 *    returns true if the child was removed, false if it was not.
 */
function OptionObject_removeChild(id) {
   var children = this.children;
   for (i = 0; i < children.length; ++i) {
      if (children[i].id == id) {
         children[i].setParent(null);
         children = children.splice(i, 1);
         return true;
      }
   }
   return false;
}
OptionObject.prototype.removeChild = OptionObject_removeChild;

/******************************* END SWAP BOX SYSTEM *************************/

/************************************* DIALOGER ******************************/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : present_dialog( [string] title, [array] js_headers,
 *                               [string] callback, [string] guts,
 *                               [int] width, [int] height )
 * 
 * description : This function will create a new window and pass it the
 *               necessary parameters to provide a dialog window that talks
 *               back to its parent window and passes it all the form elements
 *               when the user submits the dialog window.  The guts is a string
 *               that contains all of the html to display in the dialog window.
 *
 * synopsis    : present_dialog( 'Foo', ['/EZBrowse/ezbrowse.js',
 *                                       '/javascript/affiliate.js'],
 *                               'handle_return', create_dialog(), 530, 440 )
 *
 *               function handle_return( elements ){
 *                  for( i = 0; i < elements.length; ++i ){
 *                     if( element[i].name == 'element_i_want' ){
 *                        do_something
 *                     }
 *                  }
 *               }
 *
 *               function create_dialog(){
 *                  return '<script>' + 
 *                          'var foo = new SwapSystem( ... )' +
 *                          'foo.print()' +
 *                         '<\/script>'
 *               }
 *               
 * parameters  : title      : the title that will appear at the top of the page
 *               js_headers : an array of strings that represent the .js files
 *                            to be included in the dialog page
 *               callback   : a string representation of the function to call
 *                            in the parent window
 *               guts       : a string of html code to be placed in the dialog
 *                            window
 *               width      : optional - width of the pop up window
 *               height     : optional - height of the pop up window
 *               win_title  : optional - title to give the window
 *
 * dependant   : /cgi-bin/dialoger.cgi
 *               note : we had to pass this through a .cgi script because the
 *                      new window would not write properly if the 'guts' had a
 *                      document.write() in it
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function present_dialog( title, js_headers, callback, guts,
                         width, height, window_title ) {
   var h = screen.availHeight, w = screen.availWidth;
   var popW = width != null ? width : 530,
       popH = height != null ? height : 440;
   var topPos = ( h - popH ) / 2, leftPos = ( w - popW ) / 2;

   // default to 'dialoger'
   window_title = window_title != null ? window_title : 'dialoger';

   // create a form with the specified variables
   dForm = document.createElement("form");
   dForm.innerHTML = "<input type='hidden' name='title' value='"+title+"' \/>" +
               "<input type='hidden' name='callback' value='"+callback+"' \/>" + 
               "<text" + "area name='guts'>"+guts+"<\/text"+"area>";
   for( i = 0; i < js_headers.length; ++i ){
      dForm.innerHTML += "<input type='hidden' name='js_headers' value='" +
                                                         js_headers[i] + "' />";
   }
   dForm.action = "/cgi-bin/dialoger.cgi";
   dForm.method = "post";
   dForm.target = window_title;
   dForm.style.display = "none";

   // place the form at the end of the document
   document.body.appendChild(dForm);

   // open a new blank window
   var win = window.open( "about:blank", window_title,
     "toolbar=no,location=no,resizable"+
     "=yes,status=no,scrollbars=yes,width="+popW+
     ",height="+popH+",screenX="+leftPos+
     ",screenY="+topPos+",top="+topPos+",left="+leftPos ); 

   // submit the form (targetted into the window)
   dForm.submit();
}

/*********************************** END DIALOGER ****************************/

/************************** HOT SPOT RADIO GROUP *****************************/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : HSRadioGroup
 *
 * function    : constructor( [string] form_name, [string] group_name,
 *                            [int] cols, [array] data )
 * 
 * description : This constructor creates a HSRadioGroup object.
 *
 * parameters  : form_name  : name of the form to which this HSRadioGroup
 *                            belongs
 *               group_name : form name to be given this HSRadioGroup
 *               cols       : number of columns we wish to display for this
 *                            group
 *               data       : the data for the value/text pairs of this group
 *
 * synopsis    : var foo = new HSRadioGroup( 'test_form', 'test_radio_group',
 *                                           2,
 *                                          [['1','Radio 1'],['2','Radio 2'],
 *                                           ['3','Radio 3'],['4','Radio 4']] )
 *               document.write( foo )
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function HSRadioGroup( form_name, group_name, cols, data ){
   this.form_name  = form_name;
   this.group_name = group_name;
   this.cols       = cols;
   this.data       = data;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : HSRadioGroup
 *
 * function    : toString()
 * 
 * description : This function generates the html needed for this group
 *
 * returns     : a string of html
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
HSRadioGroup.prototype.toString = function(){
   var html = '';

   for( i = 0; i < this.data.length; ++i ){
      var value    = this.data[i][0];
      var text     = this.data[i][1];
      var selected = this.data[i][2] != null ? ' checked ' : '';

      if( selected == '' && i == 0 ){
         selected = ' checked ';
      }

      if( !( i % this.cols ) ){
	 html += '<br>\n';
      }

      html += '<input type="radio" name="' + this.group_name + '" ' +
                      'value="' + value  + '"' + selected + '>\n' +

              hot_spot( text, '_' + this.group_name + i, 
                        'select_radio(document.' + this.form_name + '.' +
                                      this.group_name + '['+i+'])' );
   }

   return html;
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : select_radio( [form-input-radio-object] radio_button )
 * 
 * description : This function will select the designated radio_button.
 *
 * parameters  : radio_button : the radio button to select
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function select_radio( radio_button ) {
   radio_button.checked = true;
}

/************************ END HOT SPOT RADIO GROUP ***************************/


/*************************** HOTSPOT CHECK GROUP *****************************/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : HSCheckGroup
 *
 * function    : constructor( [string] form_name, [string] group_name,
 *                            [int] cols, [array] data )
 * 
 * description : This constructor creates a HSCheckGroup object.
 *
 * parameters  : form_name  : name of the form to which this HSCheckGroup
 *                            belongs
 *               group_name : form name to be given this HSCheckGroup
 *               cols       : number of columns we wish to display for this grp
 *               data       : the data for the value/text pairs of this group
 *
 * synopsis    : var foo = new HSCheckGroup( 'test_form', 'test_radio_group',
 *                                           2,
 *                                           [['1','Radio 1'],['2','Radio 2'],
 *                                           ['3','Radio 3'],['4','Radio 4']] )
 *               document.write( foo )
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function HSCheckGroup( form_name, group_name, cols, data ){
   this.form_name  = form_name;
   this.group_name = group_name;
   this.cols       = cols;
   this.data       = data;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * object      : HSCheckGroup
 *
 * function    : toString()
 * 
 * description : This function generates the html needed for this group
 *
 * returns     : a string of html
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
HSCheckGroup.prototype.toString = function(){
   var html = '';

   for( i = 0; i < this.data.length; ++i ){
      var value    = this.data[i][0];
      var text     = this.data[i][1];
      var selected = this.data[i][2] != null ? ' checked ' : '';

      if( !( i % this.cols ) ){
	 html += '<br>\n';
      }

      html += '<input type="checkbox" name="' + this.group_name + '" ' +
                      'value="' + value  + '"' + selected + '>\n' +

              hot_spot( text, '_' + this.group_name + i, 
                        'select_checkbox(document.' + this.form_name + '.' +
                                      this.group_name + '['+i+'])' );
   }

   return html;
};

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : select_checkbox( [form-input-checkbox-object] checkbox )
 * 
 * description : This function will select the designated checkbox.
 *
 * parameters  : checkbox : the checkbox to select
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function select_checkbox( checkbox ) {
   checkbox.checked = true;
}

/************************** END HOT SPOT CHECK GROUP *************************/


/******************************** HOT SPOT GENERATOR *************************/

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * function    : hot_spot( [string] to_wrap, [string] id, [string] on_click )
 * 
 * description : This function will create a span around the 'to_wrap' string
 *               with the appropriate 'id'.  The 'on_click' will be the
 *               function the span should call when it is clicked.
 *
 * requires    : /css/hot_spot.css
 *
 * parameters  : to_wrap  : the text to wrap the span around, possibly an img
 *                          tag
 *               id       : the id to give to the span
 *               on_click : what to do when the span is clicked
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function hot_spot( to_wrap, id, on_click ){
   return '<span id="' + id + '" class="hot_spot" onclick="' + on_click +'">' +
          to_wrap + '</span>';
}

/****************************** END HOT SPOT GENERATOR ***********************/
