/**
 * Utility functions that modify the content, visibility or behaviour
 * of DHTML elements - usually in or beside a <form>.
 *
 * The code in this script assumes all relevant elements on the HTML page
 * have an unique ID attribute.
 * And that the browser supports the getElementById() method.
 *
 * @Author: Harry du Mez
 */


  var Dynamic = { Version:    '1.0',
                  VersionNum:   1000000,
                  VersionHex: '01000000' }

  // Global variable holding the type of browser.
  var g_sBrowser = getBrowser();
  /* Can be either of:
   *                     'ie'      = Internet Explorer
   *                     'ff'      = FireFox
   *                     'moz'     = Mozilla : Netscape Navigator
   *                     'unknown' = no valid detection
   */

  // Global to remember which toolbox is visible
  var g_sToolbox = '';

  /**
   * Gets an HTML element by Id.
   * @param String sElementId : ID of the element
   * @return HTMLElement : The element or null if not found.
   */
  function getElement( sElementId )
  {
    return getChildElementById( document, sElementId );
  }

  /**
   * Gets a child HTML element by Id.
   * @param HTMLElement theParent  : the element to find a child element of.
   * @param String      sElementId : ID of the element
   * @return HTMLElement : The element or null if not found.
   */
  function getChildElementById( theParent, sElementId )
  {
    if ( theParent.getElementById ) // Is getElementById() supported?
    {
      return theParent.getElementById( sElementId );
    }
    // else try the IE variant
    if ( theParent.all )
    {
      return theParent.all[sElementId];
    }
    return null;
  }
  
  /**
   * Gets the parent element containing the element with a given Id.
   *   This function walks the DOM upwards 
   *   unitl a parent with the given tag name is encountered.    
   * @param String   sElementId : Id of the element to find a parent element of.
   * @param String   sTagName   : Tag name of the parent element;
   *                              '' = return direct parent.   
   * @return HTMLElement : The container element or null if not found.
   */
  function getParentElementById( sElementId, sTagName )
  {
    var theElement = getElement( sElementId );
    return getParentElement( theElement, sTagName );
  }

  /**
   * Gets the parent element containing the given element.
   *   This function walks the DOM upwards 
   *   unitl a parent with the given tag name is encountered.    
   * @param HTMLElement theElement : The element to find a parent element of.
   * @param String      sTagName   : Tag name of the parent element;
   *                                 '' = return direct parent.   
   * @return HTMLElement : The container element or null if not found.
   */
  function getParentElement( theElement, sTagName )
  {
    while ( theElement )
    {
      theElement = theElement.parentNode;
      if ( theElement.nodeType == 1 ) // Node.ELEMENT_NODE
      {
        if (   sTagName == ''
            || sTagName.toLowerCase() == theElement.tagName.toLowerCase()) return theElement;
      }
    }
    return null;
  }

  /**
   * Get the style class of an HTML element.
   * @param String sElementId : ID of the element
   * @return String : The className of the lement or '' if not found.
   */
  function getElementClass( sElementId )
  {
    var theElement = getElement( sElementId );
    return theElement ? theElement.className : '';
  }

  /**
   * Set the style class of an HTML element.
   * @param String sElementId : ID of the element
   * @param String sClass     : Class name
   */
  function setElementClass( sElementId, sClass )
  {
    var theElement = getElement( sElementId );

    if ( theElement) // Did we find it?
    {
      theElement.className = sClass;
    }
  }

  /**
   * Shows or hides an HTML element with a given ID.
   * When the element is hidden, it doesn't occupy any space.
   * @param String  sElementId  : ID of the element.
   * @param boolean fShow       : true  = show the element;
   *                              false = hide it (element doesn't occupy any space)
   */
  function setVisibility( sElementId, fShow )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      // Since we don't know whether the element is of in-line or block type,
      // we remove the display style, to reset it to the default.
      theElement.style.display = fShow ? '' : 'none';

      // Don't use the visibility style here,
      // because this renders 'hidden' elements by preserving their place!
    }
  }

  /**
   * Shows or hides an HTML element with a given ID.
   * When the element is hidden, the space it occupies is reserved.
   * @param String  sElementId  : ID of the element.
   * @param boolean fShow       : true  = show the element;
   *                              false = hide it (element keeps occupying space)
   */
  function setVisibility2( sElementId, fShow )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      theElement.style.visibility = fShow ? 'visible' : 'hidden';
    }
  }

  /**
   * Finds out whether an HTML element with a given ID
   * is visible or hidden.
   * @param String  sElementId  : ID of the element.
   * @return : 1 = element is visible
   *           0 = element is hidden
   *          -1 = element not found.
   */
  function isVisible( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      return theElement.style.display == 'none' ? 0 : 1;
    }
    return -1;
  }

  /**
   * Hides a visible HTML element with a given ID,
   * or shows it, when it is hidden.
   * @param String  sElementId  : ID of the element.
   */
  function toggleVisibility( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      var fShow = theElement.style.display == 'none';
      theElement.style.display = fShow ? '' : 'none';
    }
  }

  /**
   * Remove a class style from an element
   * and replaces it by another style.
   * @param theElement  element of which the class has to be changed.
   * @param sFromStyle  Style to remove
   *                     If == '' : sToStyle is appended
   * @param sToStyle    Style to replace it by.
   *                     If == '' : sFromStyle is removed
   * @param sMode       'down' = apply this style swap to any child elements as well
   * @return the number of elements whose style has been changed
   */
  function swapStyle( theElement, sFromStyle, sToStyle, sMode )
  {
    var iCount = 0;
    if ( theElement.nodeType == 1 ) // Do this for ELEMENT_NODEs only
    {
      var sClass = theElement.className;
      var sNewClass = replaceStyle( sClass, sFromStyle, sToStyle );
      if ( sNewClass != sClass )
      {
        theElement.className = sNewClass;
        iCount = 1;
      }

      if ( sMode == 'down' || sMode == 'both' )
      {
        var theChilds = theElement.childNodes;
        for ( var iChild = 0; iChild < theChilds.length; ++iChild )
        {
          iCount += swapStyle( theChilds[iChild], sFromStyle, sToStyle, sMode );
        }
      }
    }
    return iCount;
  }

  /**
   * Checks whether an element has a given class style.
   * @param theElement  element of which the class must be checked
   * @param sStyle      style (part) to search in the class property
   * @return boolean
   */
  function hasStyle( theElement, sStyle )
  {
    var sClass = theElement.className;
    if ( sClass )
    {
      var styles = sClass.split( ' ' );
      for ( var iIndex = 0; iIndex < styles.length; ++iIndex )
      {
        if ( styles[iIndex] == sStyle) return true;
      }
    }
    return false;
  }

  /**
   * Toggles an element between two styles.
   * @param theElement  element of which the class has to be changed.
   * @param sStyle1     If the element has this style it will be replaced by sStyle2
   * @param sStyle2     If the element has this style it will be replaced by sStyle1
   * @return the style being set.
   */
  function alternateStyles( theElement, sStyle1, sStyle2 )
  {
    if ( hasStyle( theElement, sStyle1 ))
    {
      swapStyle( theElement, sStyle1, sStyle2, '' );
      return sStyle2;
    }
    else if ( hasStyle( theElement, sStyle2 ))
    {
      swapStyle( theElement, sStyle2, sStyle1, '' );
      return sStyle1;
    }
  }


  /**
   * Set the disabled state of the element with a given ID.
   *
   * @param String    sElementId  : ID of the element to set.
   * @param boolean   fDisabled   : state to set
   */
  function setDisable( sElementId, fDisabled )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      theElement.disabled = fDisabled;
    }
  }
  /**
   * Finds out whether an HTML element with a given ID
   * is enabled.
   * @param String  sElementId  : ID of the element.
   * @return : 1 = element is enabled or has no disabled property
   *           0 = element is disabled
   *          -1 = element not found.
   */
  function isEnabled( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      return theElement.disabled ? 0 : 1;
    }
    return -1;
  }

  /**
   * Set the readOnly state of the element with a given ID.
   *
   * @param String    sElementId  : ID of the element to set.
   * @param boolean   fReadonly   : state to set
   */
  function setReadonly( sElementId, fReadonly )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      theElement.readOnly = fReadonly;
    }
  }
  /**
   * Finds out whether an HTML element with a given ID
   * is ReadOnly.
   * @param String  sElementId  : ID of the element.
   * @return : 1 = element is read only
   *           0 = element editable or has no readOnly property
   *          -1 = element not found.
   */
  function isReadOnly( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement) // Did we find it?
    {
      return theElement.readOnly ? 1 : 0;
    }
    return -1;
  }

  /**
   * Get the text inside an HTML element.
   * @param String sElementId : ID of the element
   * @return String : The text content of the element
   */
  function getElementText( sElementId )
  {
    var theElement = getElement( sElementId );
    return theElement ? hasElementInnerText( theElement ) ? theElement.innerText
                                                          : theElement.textContent
                      : '';
  }
  
  /**
   * Determines whether this DOM supports the innerText property
   * @return boolean : true  = innterText property supported
   *                   false = use textContent property instead
   */
  function hasInnerText()
  {
    return hasElementInnerText( document.getElementsByTagName( 'body' )[0] );
  }
  /**
   * Determines whether an element supports the innerText property
   * @param HTMLElement theElement : The element to test
   * @return boolean : true  = innterText property supported
   *                   false = use textContent property instead
   */
  function hasElementInnerText( theElement )
  {
    return theElement.innerText != undefined ? true : false;
  }

  /**
   * Set the text inside an HTML element.
   * @param String sElementId : ID of the element
   * @param String sText      : the text to set
   */
  function setElementText( sElementId, sText )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      if ( hasElementInnerText( theElement )) theElement.innerText   = sText;
      else                                    theElement.textContent = sText;
    }
  }

  /**
   * Set the HTML inside an HTML element.
   * @param String sElementId : ID of the element
   * @param String sText      : the HTML text to set
   */
  function setElementInnerHTML( sElementId, sText )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      switch ( theElement.tagName.toLowerCase())
      {
        case 'col'      :
        case 'colgroup' :
        case 'frameset' :
        case 'html'     :
        case 'style'    :
          alert( 'Cannot change the innerHTML of a ' + theElement.tagName + ' element!' );
          return;

        case 'title'    :
          document.title = sText;
          return;

        case 'table'    :
        case 'tbody'    :
        case 'tfoot'    :
        case 'thead'    :
          // remove all child elements (table rows).
          emptyElement( theElement );
          // insert child elements (table rows) from string.
          createChildsFromString( theElement, sText );
          break;

        case 'tr'       :
          // remove all child elements (table cells).
          emptyElement( theElement );
          // insert child elements (table cells) from string.
          createChildsFromString( theElement, sText );
          return;

        default:
          theElement.innerHTML = sText;
      }
    }
  }

  /**
   * Replaces the HTML of an HTML element.
   * @param String sElementId : ID of the element
   * @param String sText      : the HTML text to set
   */
  function setElementOuterHTML( sElementId, sText )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      var sTagName = theElement.tagName.toLowerCase();
      switch ( sTagName )
      {
        case 'caption'  :
        case 'col'      :
        case 'colgroup' :
        case 'frameset' :
        case 'html'     :
        case 'title'    :
          alert( 'Cannot change the outerHTML of a ' + theElement.tagName + ' element!' );
          return;

        case 'tbody'    :
        case 'tfoot'    :
        case 'thead'    :
        case 'tr'       :
          alert( 'Don\'t know how to set the outerHTML of a ' + theElement.tagName + ' element yet!' );
          // TODO :
          // remove this element (table rows).
          // build partial DOM from sText
          // insert partial DOM
          return;

        case 'td'       :
        case 'th'       :
          alert( 'Don\'t know how to set the outerHTML of a ' + theElement.tagName + ' table element yet!' );
          // TODO :
          // remove this element (table cell).
          // build partial DOM from sText
          // insert partial DOM
          return;

        default:
          theElement.outerHTML = sText;
      }
    }
  }

  /**
   * Appends childs to an existing element,
   * based on a HTML string.
   * @param Element  theParent  ELement to create childs for
   * @param String   sHtml      HTML string to parse
   */
  function createChildsFromString( theParent, sHtml )
  {
    if ( Sarissa )
    {
      var theDocument = theParent.ownerDocument;
      var sText = sHtml.replace( /&nbsp;/g,   "&#160;"  )  // Translate some expected entities,
                       .replace( /&copy;/g,   "&#169;"  )  // not predefined for XML
                       .replace( /&deg;/g,    "&#176;"  )
                       .replace( /&hellip;/g, "&#8230;" )
                       .replace( /&euro;/g,   "&#8364;" );
      var sXml = '<root>' + sText + '</root>'; // Insure there's a document root
      var theParser = new DOMParser();
      var theDom = theParser.parseFromString( sXml, 'text/xml' );
//      var theDom = theParser.parseFromString( sXml, 'application/xhtml+xml' );
      var theError = Sarissa.getParseErrorText( theDom );
      if ( theError == Sarissa.PARSED_OK )
      {
        var theRoot = theDom.documentElement;  // This is our dummy root element
        createChildsFromDom( theParent, theRoot );
      }
      else
      {
        if ( sText.length > 100 ) sHtml = sHtml.substring( 0, 100 ) + '...';
        alert( 'Error parsing HTML: ' + sHtml + '\n' + theError );
      }
    }
    else
    {
      alert( 'Sarissa missing!' );
    }
  }

  /**
   * Appends childs of an XML element
   *         to an HTML element,
   * @param Element  theTarget  Element to create childs for
   *                            Usually in an HTML DOM.
   * @param Element  theSource  Element to take childs from
   *                            Usually from an XML DOM.
   */
  function createChildsFromDom( theTarget, theSource )
  {
    var theDocument = theTarget.ownerDocument;
    var aNode = theSource.firstChild;
    while( aNode )
    {
      switch ( aNode.nodeType )
      {
        case 1 : // Node.ELEMENT_NODE :
          var theElement = theDocument.createElement( aNode.tagName );
          if ( aNode.attributes ) createAttributesFromDom( theElement, aNode.attributes );
          if ( aNode.hasChildNodes()) createChildsFromDom( theElement, aNode );
          theTarget.appendChild( theElement );
          break;

//        case 2 : // Node.ATTRIBUTE_NODE :
//          theTarget.setAttribute( aNode.name, aNode.value );
//          break;

        case 3 : // Node.TEXT_NODE :
          var theText = theDocument.createTextNode( aNode.nodeValue );
          theTarget.appendChild( theText );
          break;

        default :
          alert( 'Unhandled node type: ' + aNode.nodeType );
      }
      aNode = aNode.nextSibling;
    }
  }

  /**
   * Appends attributes to an HTML element,
   * @param Element      theElement    Element to append the attributes to.
   * @param NamedNodeMap theAttributes Element to take childs from
   *                                   Usually from an XML DOM.
   */
  function createAttributesFromDom( theElement, theAttributes )
  {
    if ( theAttributes )
    {
      for ( var iAtt = 0; iAtt < theAttributes.length; ++iAtt )
      {
        var aNode  = theAttributes.item( iAtt );
        var sName  = aNode.nodeName.toLowerCase();
        var sValue = aNode.nodeValue;
        if ( sName == "class" ) // In IE class cannot be set by attribute
        {
          theElement.className = sValue;
        }
//        else if ( sName == "style" ) // How about style ??
//        {
//          setStyles( theElement, sValue );
//        }
        else if ( sName == "colspan" ) // In IE colspan cannot be set by attribute
        {
          if ( 'td' == theElement.tagName.toLowerCase())
          {
            theElement.colSpan = sValue;
          }
        }
//        else if ( sName == "rowspan" ) // How about rowspan ??
//        {
//          if ( 'td' == theElement.tagName.toLowerCase())
//          {
//            theElement.rowSpan = sValue;
//          }
//        }
        else
        {
          theElement.setAttribute( sName, sValue );
        }
      }
    }
  }

  function setStyles( theElement, sStyles )
  {
    var settings = sStyles.split( ';' );
    for ( var iIndex = 0; iIndex < settings.length; ++iIndex )
    {
      var pair = settings[iIndex].split( ':' );
      if ( pair.length == 2 )
      {
        var sName  = trimString( pair[0].toLowerCase());
        var sValue = trimString( pair[1] );
        switch ( sName )
        {
          case 'background'       : theElement.style.background      = sValue; break;
          case 'background-color' : theElement.style.backgroundColor = sValue; break;
          // TODO : complete this for every expexted style setting
          default : alert( 'Don\'t know how to set style="' + sName + ': ' + sValue + ';" yet!' );
        }
      }
    }
  }

  /**
   * Removes all child nodes from an element
   * @param Element theElement : Element to be emptied.
   */
  function emptyElement( theElement )
  {
    while ( theElement.hasChildNodes())
    {
      var aNode = theElement.firstChild;
      theElement.removeChild( aNode );
    }
  }

  /**
   * Appends all child nodes from one element to another.
   * @param Element theSource : Element to copy the child nodes from.
   * @param Element theTarget : Element to append the nodes to.
   */
  function copyChildNodes( theSource, theTarget )
  {
    var aNode = theSource.firstChild;
    while( aNode )
    {
      theTarget.appendChild( aNode );
      aNode = aNode.nextSibling;
    }
  }

  /**
   * Get the value of an HTML element.
   * @param String sElementId : ID of the element
   */
  function getElementValue( sElementId )
  {
    var theElement = getElement( sElementId );
    return theElement && theElement.value ? theElement.value : '';
  }

  /**
   * Set the value of an HTML element.
   * @param String sElementId : ID of the element
   * @param String sValue     : the value to set
   */
  function setElementValue( sElementId, sValue )
  {
    var theElement = getElement( sElementId );
    if ( theElement ) theElement.value = sValue;
  }

  /**
   * Get the a field in a given form.
   * If the form contains multiple fields with the same name,
   * the first field with that name is returned.
   * @param Form   theForm    : Form object containing the field
   * @param String sFieldName : Name of the field
   */
  function getFormField( theForm, sFieldName )
  {
    var theItems = theForm.elements[ sFieldName ];
    if ( theItems )
    {
      // If it has a tagName, than it is a single element:
      if ( theItems.tagName ) return theItems;

      // Not an element, probably an array:
      var iNumItems = theItems.length;
      if ( iNumItems ) // is array, means: multiple items
      {
        if ( iNumItems > 0 ) return theItems.item( 0 );
      }
    }
    return null;
  }

  /**
   * Get the a field in a form.
   * If a field of the given name exists in multiple forms
   * the first occurence is returned.
   * @param Form   theForm    : Form object containing the field
   * @param String sFieldName : Name of the field
   */
  function getField( sFieldName )
  {
    var colForms = document.forms;
    if ( colForms )
    {
      var iNumForms = colForms.length;
      for ( var iForm = 0; iForm < iNumForms; ++iForm )
      {
        var aForm = colForms.item( iForm );
        var theField = getFormField( aForm, sFieldName );
        if ( theField ) return theField;
      }
    }
    return null;
  }

  /**
   * Get the value of a field in a form.
   * @param Form   theForm    : Form object containing the field
   * @param String sFieldName : Name of the field
   */
  function getFormFieldValue( theForm, sFieldName )
  {
    var theField = getFormField( theForm, sFieldName );
    var sResult = theField ? theField.value : '';
    if ( sResult == 'undefined' ) sResult = '';
    return  typeof sResult != 'undefined' ? sResult : '';
  }

  /**
   * Set the value of a field in a form.
   * @param Form   theForm    : Form object containing the field
   * @param String sFieldName : Name of the field
   * @param String sValue     : the value to set
   */
  function setFormFieldValue( theForm, sFieldName, sValue )
  {
    var theField = getFormField( theForm, sFieldName );
    if ( theField ) theField.value = sValue;
  }

  /**
   * Get the value of a field in a form.
   * @param Form   theForm    : Form object containing the field
   * @param String sFieldName : Name of the field
   */
  function getFieldValue( sFieldName )
  {
    var theField = getField( sFieldName );
    var sResult = theField ? theField.value : '';
    if ( sResult == 'undefined' ) sResult = '';
    return  typeof sResult != 'undefined' ? sResult : '';
  }

  /**
   * Set the value of a field in any form.
   * @param String sFieldName : Name of the field
   * @param String sValue     : the value to set
   */
  function setFieldValue( sFieldName, sValue )
  {
    var colForms = document.forms;
    if ( colForms )
    {
      var iNumForms = colForms.length;
      for ( var iForm = 0; iForm < iNumForms; ++iForm )
      {
        var aForm = colForms.item( iForm );
        var theField = getFormField( aForm, sFieldName );
        if ( theField ) theField.value = sValue;
      }
    }
  }

  /**
   * Get the <option> text inside a given <select> element.
   * @param Select  theList  : The <select> element
   * @param String  sValue   : Value of the option to get
   *                           or '' = get selected option.
   * @return String : Option text.
   */
  function getSelectOption( theList, sValue )
  {
    for ( var iOption = 0; iOption < theList.length; ++iOption )
    {
      var sOptValue = theList.options[iOption].value;
      if ( sOptValue != '' ) // Skip options without a value
      {
        var sOptText  = theList.options[iOption].text;
        if ( sValue == '' ) // Get selected option
        {
          var fSelected = theList.options[iOption].selected;
          if ( fSelected ) return sOptText;
        } 
        else if ( sOptValue == sValue ) // Get option with this value
        {
          return sOptText;
        }
      }
    }
    return '';
  }

  /**
   * Get the <option> text inside a given <select> element.
   * @param String sFieldName : Name of the <select> field
   * @param String sValue     : Value of the option to get
   *                            or '' = get selected option.
   * @return String : Option text.
   */
  function getOption( sFieldName, sValue )
  {
    var theField = getField( sFieldName );
    if ( theField )
    {
      return getSelectOption( theField, sValue );
    }
    return '';
  }
  
  /**
   * Set the selectes state of a <option> element
   *                    inside a <select> element.
   * @param Select  theList  : The <select> element
   * @param int     iSelect  : Index of the option to set;
   *                           -1 = none.
   * @param boolean fSelect  : True  = set selected,
   *                           False = set unselected;
   *                           Ignored is iSelect is out of range.
   * @param boolean fUnique  : True  = Unselect all other options.
   *                           To clear all selections, set iSelect to -1 
   *                                                    and fUnique to true.
   */
  function selectOption( theList, iSelect, fSelect, fUnique )
  {
    for ( var iOption = 0; iOption < theList.length; ++iOption )
    {
      var theOption = theList.options[iOption];
      if ( iOption == iSelect )
      {
        theOption.selected = fSelect;
      }
      else if ( fUnique )
      {
        theOption.selected = false;
      }
    }
  }

  /**
   * Sets the <option> element inside a given <select> element.
   * @param Select  theList  : The <select> element
   * @param String  sValue   : Value of the option to set
   * @param String  sText    : If null, the option is removed from the list;
   *                           Else it is appended if necessary and set to the given text.
   */
  function setSelectOption( theList, sValue, sText )
  {
    var fSelected = true;
    for ( var iOption = 0; iOption < theList.length; ++iOption )
    {
      if ( theList.options[iOption].value == sValue )
      {
        if ( sText ) // Change the text of an existing option
        {
          theList.options[iOption].text = sText;
        }
        else        // Remove the option
        {
          theList.options[iOption] = null;
        }
        return;
      }
      if ( theList.options[iOption].selected ) fSelected = false;
    }
    // If we end up here, the option doesn't exist (yet).
    if ( sText )
    {
      var theOption = new Option( sText, sValue, false, fSelected );
      theList.options[theList.length] = theOption;  // Append
    }
  }

  /**
   * moveOptionElement
   *                   Moves the selected elements in one listbox
   *                   to another listbox
   *
   * @param  Button  theButton  Form elememt being clicked
   * @param  String  sSource    Name of the select object to remove the selected element from
   * @param  String  sTarget    Name of the select object to add the removed element to
   * @param  String  sSort      How to insert into the target:
   *                            'T'           = sort on text
   *                            'V'           = sort on value
   *                            anything else = append to the end
   *
   *         The button object and both Select objects must be part of the same Form
   */
  function moveOptionElement( theButton, sSource, sTarget, sSort )
  {
    moveNonEmptyOptionElement( theButton, sSource, sTarget, '', null, sSort );
  }

  /**
   * moveNonEmptyOptionElement
   *                   Moves the selected elements in one listbox to another listbox,
   *                   with special consideration regarding items without a value (empty items).
   *
   * @param  Button  theButton    Form elememt being clicked.
   * @param  String  sSource      Name of the select object to remove the selected elements from.
   * @param  String  sTarget      Name of the select object to add the removed elements to.
   * @param  String  sEmpty       Side of the transaction that may contain empty elements.
   *                              'S'           = Source list may contain empty option elements
   *                                              don't copy them to the target;
   *                                              put an empty element in it, if it becomes empty.
   *                              'T'           = Target list may contain an empty option element
   *                                              remove that if non empty elements were added.
   *                              anything else = empty elements are not treated as described.
   * @param  String  sEmptyText   Text of the option to put in source list, if it become empty.
   *                              If null no empty option will be added.
   * @param  String  sSort        How to insert into the target:
   *                              'T'           = sort on text
   *                              'V'           = sort on value
   *                              anything else = append to the end
   *
   *         The button object and both Select objects must be part of the same Form
   */
  function moveNonEmptyOptionElement( theButton, sSource, sTarget, sEmpty, sEmptyText, sSort )
  {
    // TODO : check whether the item (double) clicked is an option in de source list,
    //        so we can move it directly.

    // Get the listbox objects:
    var theForm   = theButton.form;                   // Form
    var theSource = getFormField( theForm, sSource ); // Select
    var theTarget = getFormField( theForm, sTarget ); // Select

    // Get the selected entry in the source list
    var iSelected = theSource.selectedIndex;          // int
    if ( iSelected < 0 ) return;  // Nothing to do

    // Walk the list of options:
    var iOption = 0;
    var iAdded  = 0;
    while ( iOption < theSource.options.length )
    {
      var theOption = theSource.options[iOption];   // Option
      var sOption   = theOption.text;               // String

      // Is it selected?
      if ( theOption.selected && sOption != '' )
      {
        var sValue = theOption.value;
        if ( !( sEmpty == 'S' || sValue == '' ))
        {
          // Make a new copy of the element
          var theElement = new Option( sOption );
          theElement.value = sValue;

          var iPos = theTarget.options.length;
          if ( sSort == 'T' || sSort == 'V' )
          {
            // Find a place to insert the new item:
            for( iPos = 0; iPos < theTarget.options.length; ++iPos )
            {
              var anOption = theTarget.options[iPos];
              if ( sSort == 'T' )
              {
                if ( anOption.text > sOption ) break;
              }
              else // if ( sSort == 'V' )
              {
                if ( anOption.value > sValue ) break;
              }
            }
          }

          // Insert it:
          insertOption( theTarget, theElement, iPos );
          if ( sValue != '' ) iAdded++;

          // Select the new element (so it is scrolled into view).
          theElement.selected = true;
        }  

        // Remove the source element
        theSource.remove( iOption );
      }
      else // Only is the current otion is still in place,
      {    // we have to increment the index.
        ++iOption;
      }
    }
    
    if ( sEmptyText && sEmpty == 'S' && iOption == 0 ) // The source is now empty
    {
      var theElement = new Option( sEmptyText );
      theElement.value = '';
      insertOption( theSource, theElement, 0 );
    }

    if ( sEmpty == 'T' && iAdded > 0 )
    {
      var iOption = theTarget.options.length;
      while ( iOption > 0  )
      {
        var theOption = theTarget.options[--iOption];   // Option
        var sValue = theOption.value;
        if ( sValue == '' )
        {
          theTarget.remove( iOption );
        }
      }
    }  

    // The selection is now gone and position is at the top.
    // Select the item now on the position of the first previously selected item
    selectOption( theSource, iSelected, true, true ); // This doesn't work all the time.
    // Deselect immediately, we only wanted it to come into view:
    selectOption( theSource, -1, false, true );
 
    return;
  }

  /**
   * insertOption
   *             Adds a new option element to a select element
   *
   * @param  Select  theTarget  Select element to add the option to
   * @param  Option  theOption  Option element to add
   * @param  int     iPosition  Index of the element to insert before,
   *                            -1 = append at the end.
   */
  function insertOption( theTarget, theOption, iPosition )
  {
    var iCount = theTarget.options.length;
    if ( iPosition < 0 ) iPosition = iCount;
    if ( g_sBrowser == 'ie' )
    {
      if ( iPosition < iCount )
      {
        theTarget.add( theOption, iPosition );
      }
      else
      {
        theTarget.add( theOption );
      }
    }
    else
    {
      var elBefore = iPosition < iCount ? theTarget.options[iPosition]
                                        : null;
      theTarget.add( theOption, elBefore );
    }
  }

  /**
   * Moves de selected items inside a listbox one postion Up.
   *
   * @param String sListId     : The ID of the <select> element.
   * @param String sButtonUp   : ID of the button object which moves items up.
   *                             Will be disabled when the top is reached.
   *                             Null = don't bother.
   * @param String sButtonDown : ID of the button object which moves items down.
   *                             Will be enabled when the bottom is cleared.
   *                             Null = don't bother.
   */
  function moveSelectionUp( sListId, sButtonUp, sButtonDown )
  {
	  var theList = getElement( sListId );
  	if ( theList )
	  {
      for ( var iOption = 0; iOption < theList.length; ++iOption )
      {
        var theOption = theList.options[iOption];
        if ( theOption.selected )
        {
          if ( iOption < 1 ) return; // Item on top can't move up.
   	    	theList.remove( iOption );
          insertOption( theList, theOption, iOption - 1 );
        } 
      }
      if ( sButtonUp || sButtonDown ) enableListMoveButtons( theList, sButtonUp, sButtonDown )
  	}
  }

  /**
   * Moves de selected items inside a listbox one postion Down.
   *
   * @param String sListId     : The ID of the <select> element.
   * @param String sButtonUp   : ID of the button object which moves items up.
   *                             Will be enabled when the top is cleared.
   *                             Null = don't bother.
   * @param String sButtonDown : ID of the button object which moves items down.
   *                             Will be disabled when the bottom is reached.
   *                             Null = don't bother.
   */
  function moveSelectionDown( sListId, sButtonUp, sButtonDown )
  {
	  var theList = getElement( sListId );
  	if ( theList )
	  {
	    var iLast = theList.length - 1;
      for ( var iOption = iLast; iOption >= 0; --iOption )
      {
        var theOption = theList.options[iOption];
        if ( theOption.selected )
        {
          if ( iOption >= iLast ) return; // Item at bottom can't move down.
   	    	theList.remove( iOption );
          insertOption( theList, theOption, iOption + 1 );
        } 
      }
      if ( sButtonUp || sButtonDown ) enableListMoveButtons( theList, sButtonUp, sButtonDown )
  	}
  }
  
  /**
   * enableListMoveButtons
   *
   * Enables the buttons to move selected option elements
   *                             inside a select element
   *                             up or down.
   *
   * @param  Select  theList     Select element of which selected items may be moved.
   * @param  String  sButtonUp   ID of the button object which moves items up.
   *                             Enabled if one or more option elements are selected
   *                             and the first of them is NOT at the the top already.
   * @param  String  sButtonDown ID of the button object which moves items down.
   *                             Enabled if one or more option elements are selected
   *                             and the last of them is NOT at the the bottom already.
   */
  function enableListMoveButtons( theList, sButtonUp, sButtonDown )
  {
    var iCount = theList.options.length;
    var iFirst = -1;
    var iLast  = iCount;
    for ( var iOption = 0; iOption < iCount; ++iOption )
    {
      var theOption = theList.options[iOption];   // Option
      if ( theOption.selected )
      {
        if ( iFirst < 0 ) iFirst = iOption;
        iLast = iOption;
      }
    }
    if ( sButtonUp   ) setDisable( sButtonUp,   iFirst     <  1       );
    if ( sButtonDown ) setDisable( sButtonDown, iLast + 1  >= iCount  );
  }

  /**
   * finalizeSelection
   *                   Select all elements in one listbox
   *                   and deselects all in another.
   *
   *       This is needed when the user can select items by
   *       moving them from a list of posible option (source)
   *       to list of selected items (target).
   *       Just before the form is submitted selection must be
   *       removed from the source list and all items in
   *       the target list must be selected.
   *       So call finalizeSelection() from the onSubmit handler.
   *
   * @param  Form    theForm    Form object
   * @param  String  sSource    Name of the select object from which to remove the selection
   * @param  String  sTarget    Name of the select object of which all elements sould be selected
   *
   *         Both Select objects must be part of the given Form
   */
  function finalizeSelection( theForm, sSource, sTarget )
  {
    var theSource = getFormField( theForm, sSource ); // Select
    var theTarget = getFormField( theForm, sTarget ); // Select
    // Clear the selection in the Source list:
    theSource.selectedIndex = -1;

    // Make sure we can select all items:
    theTarget.multiple = true;
    for ( var iOption = 0; iOption < theTarget.options.length; ++iOption )
    {
      var theOption = theTarget.options[iOption];     // Option
      theOption.selected = true;
    }
  }

  /**
   * Returns the checked state of checkbox or radiobutton with a given ID.
   *
   * @param String    sCheckId    : ID of the chekbox or radiobutton
   */
  function getCheck( sCheckId )
  {
    var theElement = getElement( sCheckId );
    return  theElement ? theElement.checked : false;
  }

  /**
   * Checks or unchecks the checkbox with a given ID.
   *
   * @param String    sCheckId  : ID of the chekbox to set.
   * @param boolean   fChecked  : state to set
   */
  function setCheck( sCheckId, fChecked )
  {
    var theElement = getElement( sCheckId );
    if ( theElement ) // theElement is <input type="checkbox"> or <input type="radio"> element
    {
      theElement.checked = fChecked;
    }
  }

  /**
   * Checks or unchecks all checkboxes having the given name.
   *
   * @param Form    theForm  : Form object to which the buttons belong
   * @param String  sName    : value of the name attribute
   * @param boolean fChecked : true  = check all;
   *                           false = uncheck all.
   */
  function setAllChecks( theForm, sName, fChecked )
  {
    var theItems = theForm.elements[ sName ];
    if ( theItems )
    {
      var iNumItems = theItems.length;
      if ( iNumItems ) // is array, means: multiple items
      {
        for ( var iItem = 0; iItem < iNumItems; ++iItem )
        {
          var theItem = theItems.item( iItem );
          theItem.checked = fChecked;
        }
      }
      else
      {
        // is only 1 item
        theItems.checked = fChecked;
      }
    }
  }

  /**
   * Copies the Check status of a given checkbox,
   * to all checkboxes within the same form having the given name.
   *
   * @param CheckBox theElement : the checkbox being clicked
   * @param String   sName      : name of the boxes to check/uncheck
   */
  function toggleAllChecks( theElement, sName )
  {
    setAllChecks( theElement.form, sName, theElement.checked );
  }

  /**
   * Checks a 'CheckAll' checkbox if all of a series
   *                     of checkboxes are checked;
   * or unchecks it if either one of this series is unchecked.
   *
   * Called when one of a series of check boxes is clicked,
   * to synchonize the 'CheckAll' checkbox.
   *
   * @param CheckBox theElement : the checkbox being clicked
   * @param String   sName      : ID of the check/uncheck all box
   */
  function setCheckAll( theElement, sName )
  {
    var fCheck = true;
    var theForm  = theElement.form;
    var theItems = theForm.elements[ theElement.name ];
    var iNumItems = theItems.length;
    if ( iNumItems ) // is an array of multiple items
    {
      for ( var iItem = 0; iItem < iNumItems; ++iItem )
      {
        var theItem = theItems.item( iItem );
        if ( ! theItem.checked )
        {
          fCheck = false;
          break;
        }
      }
    }
    else // is only 1 item
    {
      fCheck = theItems.checked;
    }
    setCheck( sName, fCheck );
  }


  /**
   * Checks the checkbox with a given ID,
   * when all other checkboxes with the same name got cleared.
   *
   * This is used when in a series of checkboxes 1 or more (but never zero)
   * items must be checked. (Unlike radio buttons of which only one can be checked).
   *
   * @param Checkbox  theElement  : the current checkbox
   *                                Must have the same name and
   *                                be within the same form as the default checkbox.
   * @param String    sDefault    : ID of the default chekbox.
   *                                to set when all others have been cleared.
   * @param boolean   fUncheck    : true  = Uncheck the default if any other is checked;
   *                                false = Leave the default unchanged if any other is checked.
   *
   * This function is meant to be fired from the onclick event.
   * Note that the default may vary, depending which box get set/cleared
   * (always set another than the one being clicked).
   */
  function setDefaultCheck( theElement, sDefault, fUncheck )
  {
    var theForm    = theElement.form;
    var theDefault = null;
    var fCheck     = true;
    var theItems   = theForm.elements[ theElement.name ];
    var iNumItems  = theItems.length;
    if ( iNumItems ) // is an array, meaning there are indeed multiple items
    {
      for ( var iItem = 0; iItem < iNumItems; ++iItem )
      {
        var theItem = theItems.item( iItem );
        if ( theItem.checked ) fCheck = false; // return; // One of the boxes is checked, don't bother.
        if ( theItem.id == sDefault ) // We found the default item
        {
          theDefault = theItem;
          if ( !fCheck ) break; // We no what to do, don't bother the rest.
        }
        if ( !( fCheck || fUncheck )) return; // All conditions met, don't bother the rest.
      }
      // If we reach this, none of the boxes are currently checked.
      if ( theDefault && ( fCheck || fUncheck )) // We found the default checkbox.
      {
        theDefault.checked = fCheck;
      }
    }
  }

  /**
   * Return how many checkboxes with a given name
   *        are checked.
   *
   * @param Form    theForm  : Form object to which the checkboxes belong
   * @param String  sName    : value of the name attribute
   */
  function getCheckCount( theForm, sName )
  {
    var iCount = 0;
    var theItems = theForm.elements[ sName ];
    if ( theItems )
    {
      if ( theItems.length ) // Is array: multiple items
      {
        for ( var iItem = 0; iItem < theItems.length; ++iItem )
        {
          var theItem = theItems.item( iItem );
          if ( theItem.checked ) iCount++;
        }
      }
      else // Single item
      {
        iCount = theItem.checked ? 1 : 0;
      }
    }
    return iCount;
  }

  /**
   * Retrieves the value of the selected item
   * from the set of radio buttons with a given name attribute.
   * (This is the value for the attribute, to be submitted to the server).
   * @param Form   theForm    : Form object to which the buttons belong
   * @param String sFieldName : value of the name attribute
   */
  function getRadioSelection( theForm, sFieldName )
  {
    var theItems = theForm.elements[ sFieldName ];
    if ( theItems )
    {
      if ( theItems.length ) // Probably an array of more than one item.
      {
        var iNumItems = theItems.length;
       for ( var iItem = 0; iItem < iNumItems; ++iItem )
        {
          var theItem = theItems.item( iItem );
          if ( theItem.checked ) return theItem.value;
        }
      }
      else // Ony one item
      {
        if ( theItems.checked ) return theItems.value;
      }      
    }
    return '';
  }

  /**
   * Retrieves the value of the selected item
   * from the set of radio buttons with a given name attribute.
   * (This is the value for the attribute, to be submitted to the server).
   * @param String sName   : value of the name attribute
   */
  function getRadioValue( sFieldName )
  {
    var colForms = document.forms;
    if ( colForms )
    {
      var iNumForms = colForms.length;
      for ( var iForm = 0; iForm < iNumForms; ++iForm )
      {
        var aForm = colForms.item( iForm );
        var sValue = getRadioSelection( aForm, sFieldName );
        if ( sValue != '' ) return sValue;
      }
    }
    return '';
  }

  /**
   * Checks one of radio buttons
   * from the set of radio buttons with a given name attribute.
   * having the given value
   * @param Form   theForm : Form object to which the buttons belong
   * @param String sName   : value of the name attribute
   * @param String sValue  : value of the iten to check
   */
  function setRadioSelection( theForm, sName, sValue )
  {
    var theItems = theForm.elements[ sName ];
    if ( theItems )
    {
      if ( theItems.length ) // Probably an array of more than one item.
      {
        var iNumItems = theItems.length;
        for ( var iItem = 0; iItem < iNumItems; ++iItem )
        {
          var theItem = theItems.item( iItem );
          if ( theItem.value == sValue )
          {
            theItem.checked = true;
            return;
          }
        }
      }
      else // Ony one item
      {
        if ( theItems.value == sValue )
        {
          theItems.checked = true;
        }
      }      
    }
  }

  /**
   * Invokes the onclick handler - if any - of an element.
   * @param String sElementId : ID of the element
   */
  function clickElement( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement )
    {
      if ( theElement.click )
      {
         theElement.click();
      }
      else // the element doesn't have a click() method.
      {
        if ( theElement.href )
        {
          if ( theElement.href != '' && theElement.href != '#' )
          {
            location.href = theElement.href; // We are ignoring any target here !!!
            return;
          }
        }
        var theAtt = theElement.attributes['onclick'];
        if ( theAtt ) // There is an onclick attribute.
        {
          var sAtt = theAtt.nodeValue;
          var sFunction = sAtt.replace( /this/, 'getElement( \'' + sElementId + '\' )' );
          eval( sFunction );
        }
      }
    }
  }
  /**
   * Function to make (another) toolbox visible.
   * Called as a handler of a click or mouse over event
   * of a control to pop-up the toolbox.
   *
   * @param theElement   element to position the toolbox over
   * @param sToolboxId   Id of the toolbox
   *                     If '' : only remove the current, if any.
   * @param Positioning.
   *                     By default the upper-left corner of the toolbox
   *                     is placed upon the upper-left corner of the theElement.
   *                     This can be modified by putting one or more of the
   *                     following letters in the sPositioning argument:
   *                     'B' Position the toolbox Below the element;
   *                     'R' Position the toolbox to the Right of the element;
   *                     'W' Make the toolbox Width equal to that of the element.
   */
  function showToolbox( theElement, sToolboxId, sPositioning )
  {
    if ( g_sToolbox != '' ) setVisibility2( g_sToolbox, false );
    if ( sToolboxId != '' )
    {
      var theBox = getElement( sToolboxId );
      if ( theBox )
      {
        var iPosX   = 0;
        var iPosY   = 0;
        var iWidth  = 400;
        var iHeight = 0;
        if ( theElement )
        {
          iWidth  = theElement.offsetWidth;
          iHeight = theElement.offsetHeight;
          for ( var theParent = theElement; theParent; theParent = theParent.offsetParent )
          {
            if ( theParent == theBox.offsetParent ) break;
            iPosX += theParent.offsetLeft;
            iPosY += theParent.offsetTop;
            if ( theParent.tagName.toLowerCase() == 'body' ) break;
//            if ( theParent.tagName.toLowerCase() == 'html' ) break;
          }
        }
        if ( sPositioning.match( /R/ )) iPosX += iWidth;
        if ( sPositioning.match( /B/ )) iPosY += iHeight;
        theBox.style.left = iPosX + 'px';
        theBox.style.top  = iPosY + 'px';
        if ( sPositioning.match( /W/ )) theBox.style.width = iWidth;
        theBox.style.visibility = 'visible';
        

        // Is it necessary to scroll the current node
        // into view within theBox ?
        if ( theBox.scrollHeight > theBox.clientHeight )
        {
          var theNode = getChildElementById( theBox, sToolboxId + '_Scroll' );
          if ( theNode ) // The element to be scrolled into view
          {
            // We cannot use the method theNode.scrollIntoView() here,
            // because this will effect the scroll position of the whole page.
            // (Nothing visible above/below the targetted element).
            var iNodeOffset   = 0;
            var iCenterOffset = Math.floor(( theBox.clientHeight - theNode.offsetHeight ) / 2 );
            do
            {
              iNodeOffset += theNode.offsetTop;
              theNode = theNode.offsetParent;
              if ( theNode == theBox )
              {
                break;
              }
            }
            while( theNode.tagName.toLowerCase() != 'body' );
            theBox.scrollTop = iNodeOffset > iCenterOffset
                             ? iNodeOffset - iCenterOffset
                             : 0;
          }
        }
      }
    }
    g_sToolbox = sToolboxId;
  }

  /**
   * Jumps to another URL programmatically.
   * @param String theEvent : Event object.
   *                          (This is required, because FireFox has no global window.event object).
   * @param String sHref    : URL of the page to jump to
   * @param String sTarget  : (optional) name of the target window.
   *                          If omitted the target is '_self'.
   *                          If the shiftkey was held down, the target is always forced to '_blank'.
   */
  function jumpTo( theEvent, sHref, sTarget )
  {
    if ( theEvent.shiftKey ) sTarget = '_blank';
    if ( typeof sTarget != 'string' ) sTarget = '_self';
    window.open( sHref, sTarget );
  }

  /**
   * Scrolls the HTML element with the given Id into view.
   * @param String sElementId : ID of the element
   */
  function scrollTo( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement) theElement.scrollIntoView( true );
  }

  /**
   * Set the focus to the HTML element with the given Id.
   * @param String sElementId : ID of the element
   */
  function setFocus( sElementId )
  {
    var theElement = getElement( sElementId );
    if ( theElement ) theElement.focus();
  }

  /**
   * Set the focus to the first form field the given name.
   * @param String sFieldName : Name of the formfield element
   */
  function setFieldFocus( sFieldName )
  {
    var theField = getField( sFieldName );
    if ( theField ) theField.focus();
  }

  /**
   * Set the focus to a field in a form.
   * @param String sFormId    : Id of the form
   * @param String sFieldName : Name of the formfield element
   */
  function setFormFieldFocus( sFormId, sFieldName )
  {
    var theForm = getElement( sFormId );
    var theField = theForm ? getFormField( theForm, sFieldName ) : getField( sFieldName );
    if ( theField ) theField.focus();
  }

  /**
   * Scans the frameset from the top-most window and down
   * for a frame with e given name.
   * @param String sName  : name of the frame.
   * @return window object of the first frame found with the given name.
   */
  function findFrame( sName )
  {
    return findSubFrame( window.top, sName );
  }

  /**
   * Scans the frameset from a given widnow and down
   * for a frame with e given name.
   * @param String sName  : name of the frame.
   * @return window object of the first frame found with the given name.
   */
  function findSubFrame( theWindow, sName )
  {
    var theFrames = theWindow.frames;
    if ( theFrames )
    {
      for ( var iFrame = 0; iFrame < theFrames.length; ++iFrame )
      {
        var aFrame = theFrames[iFrame];
        if ( aFrame.name.toLowerCase() == sName.toLowerCase()) return aFrame;
        var theSubFrame = findSubFrame( aFrame, sName );
        if ( theSubFrame ) return theSubFrame;
      }
    }
    return null;
  }

  /**
   * Loads an URL into a named Frame.
   * @param String sFrameName : Name of the Frame.
   * @param String sHref      : URL of the page to load.
   *                            If omitted the current URL is reloaded.
   * @return int : 1 = frame loaded with a different URL
   *               0 = current URL refreshed
   *              -1 = frame not found.
   */
  function loadFrame( sFrameName, sHref )
  {
    var iResult = -1;
    var theFrame = findFrame( sFrameName );
    if ( theFrame )
    {
      if ( typeof sHref != 'string' ) sHref = '';
      var sUrl = theFrame.location.href;
      if ( sHref != '' &&  sHref != sUrl )
      {
        theFrame.location.replace( sHref );
        iResult = 1;
      }
      else
      {
        theFrame.location.reload( true );
        iResult = 0;
      }
    }
    return iResult;
  }


  /**
   * Calculates the space left in a textarea input field
   *
   * onKeyDown="textCounter(this.form.NaamVanTextArea,this.form.NaamVanCounter,0);"
   * onKeyUp="textCounter(this.form.NaamVanTextArea,this.form.NaamVanCounter,0);"
   *
   * extra field for free space indicator
   * <input readonly type=text name=NaamVanCounter size=5 maxlength=3 value="0" class="counter">
   *
   * Example of usage:
   * <textarea name="Comment1" rows="3" cols="153" READONLY onKeyDown="textCounter(this.form.Comment1,this.form.remLen1,0);" onKeyUp="textCounter(this.form.Comment1,this.form.remLen1,0);" class="editbox"></textarea><br>
   * <i>resterend aantal karakters:&nbsp;</i><input readonly type=text name=remLen1 size=5 maxlength=3 value="0" class="counter">
   *
   * @param TextArea     theTextArea   : the TextArea that has to be checked on its size
   * @param Textreadonly theCountField : the CountField that shows the free space left in the textarea
   * @param Numeric      iMaxLimit     : Max. size of the textarea
   * REMARK when the textarea is over its maxLimit it will be trimmed!
   */

  function textCounter( theTextArea, theCountField, iMaxLimit)
  {
    if (theTextArea.value.length > iMaxLimit) // if too long...trim it!
      theTextArea.value = theTextArea.value.substring(0, iMaxLimit);
  	else // otherwise, update 'characters left' counter
      theCountField.value = iMaxLimit - theTextArea.value.length;
  }

  /* Helper functions: */

  function getBrowser()
  {
    // For the purpose of this demo, this code is kept *very* simple.
    // For a better browser sniffer look at:
    // http://webreference.com/tools/browser/javascript.html

    var sResult = 'unknown';


    if ( navigator.userAgent.toLowerCase().indexOf( 'mozilla' ) >= 0 ) sResult = 'moz';  // Mozilla = Netscape Navigator
    if ( navigator.userAgent.toLowerCase().indexOf( 'firefox' ) >= 0 ) sResult = 'ff';   // FireFox
    if ( navigator.appVersion.toLowerCase().indexOf( 'msie' )   >= 0 ) sResult = 'ie';   // Internet Explorer
    if ( navigator.vendor &&
         navigator.vendor.toLowerCase() == 'firefox' )                 sResult = 'ff';   // FireFox

    return sResult;
  }

  function isMatchingElement ( theElement, sSearch )
  {
    var sPattern = sSearch.toUpperCase();          // String
    var iLen     = sPattern.length;                // int
    if ( iLen > 0 )
    {
      var sText = theElement.text.toUpperCase();   // String
      if ( sText.length >= iLen )
      {
        if ( sText.substr(0, iLen) == sPattern ) return true;
      }
    }
    return false;
  }

  /**
   * Replace one of the space-separated parts of a string
   * by another.
   * @param sClass      String existing of one ore more parts separated by spaces.
   * @param sFromStyle  Part te search for
   *                     If == '' : sToStyle is appended
   * @param sToStyle    String to replace it by.
   *                     If == '' : sFromStyle is removed
   *
   * This function is meant to change one of the (space separated) styles
   * in a class name by another.
   */
  function replaceStyle( sClass, sFromStyle, sToStyle )
  {
    var sResult = '';
    var fFound = false;
    if ( sClass )
    {
      var styles = sClass.split( ' ' );
      for ( var iIndex = 0; iIndex < styles.length; ++iIndex )
      {
        if ( styles[iIndex] == sFromStyle )
        {
          sResult = appendStyle( sResult, sToStyle );
          fFound = true;
        }
        else
        {
          if ( styles[iIndex] == sToStyle ) fFound = true;
          sResult = appendStyle( sResult, styles[iIndex] );
        }
      }
    }
    if ( !fFound && sFromStyle == '' ) sResult = appendStyle( sResult, sToStyle );
    return sResult;
  }

  function appendStyle( sClass, sStyle )
  {
    return sClass != '' ? sClass + ' ' + sStyle : sStyle;
  }

  /**
   * Shows the active tab and hides the others.
   * @param Element theTab : <li> element being clicked.
   *
   * This function assumes the following:
   * - the element being clicked is a <li> element;
   * - the other tabs (to be reset to the inactive state)
   *   are sibbling <li>s within the same <ul> element;
   * - these <li> elements have an id attribute in the form "clkXXX";
   * - corresponding tab sheet elements have an id attribute in the form "divXXX".
   * @return String : For convenience the ID of the item clicked.
   */
  function toggleTabContent( theTab )
  {
    var sActiveId = theTab.id; // Id of <li> element being clicked.
    var sActiveDivId = null;
    // Get list of <li>s corresponding to the tab contents
    var theList = theTab.parentNode.getElementsByTagName( 'li' );
    for ( var iTab = 0; iTab < theList.length; iTab++ )
    {
      var anElem = theList[iTab];                        // One of the <li> tab elements
      var sTabId = anElem.id;                            // Id of this tab
      var fActive = sTabId == sActiveId;                 // Is it the active one?
      swapStyle( anElem,
                 fActive ? 'tab' : 'tabCurrent',
                 fActive ? 'tabCurrent' : 'tab', '' );
      var sDivId = sTabId.replace( /^clk/, 'div' );      // Id of the corresponding div.
      if ( fActive ) sActiveDivId = sDivId;              // Remember it.
      else setVisibility( sDivId, false );               // Hide it.
    }
    if ( sActiveDivId ) setVisibility( sActiveDivId, true ); // Show it
    return sActiveId;
  }

  // Removes leading whitespaces
  function lTrimString( sValue )
  {
    return sValue.replace( /^\s+/, '' );
  }

  // Removes ending whitespaces
  function rTrimString( sValue )
  {
    return sValue.replace( /\s+$/, '' );
  }

  // Removes leading and ending whitespaces
  function trimString( sValue )
  {
    return lTrimString( rTrimString( sValue ));
  }

  // Pads a string to the left
  function lPadString( sValue, iSize, sPadding )
  {
    if ( typeof sPadding == 'undefined' ) sPadding = ' ';
    while( sValue.length < iSize )
    {
      sValue = sPadding + sValue;
    }
    return sValue;
  }
  
  // Pads a string to the right
  function rPadString( sValue, iSize, sPadding )
  {
    if ( typeof sPadding == 'undefined' ) sPadding = ' ';
    while( sValue.length < iSize )
    {
      sValue = sValue + sPadding;
    }
    return sValue;
  }

  if ( typeof String.prototype.trim == "undefined" )
  {
    String.prototype.trim = function()
                            {
                              return trimString( this );
                            }
  }
  
  if ( typeof String.prototype.lpad == "undefined" )
  {
    String.prototype.lpad = function( iSize, sPadding )
                            {
                              return lPadString( this, iSize, sPadding );
                            }
  }

  if ( typeof String.prototype.rpad == "undefined" )
  {
    String.prototype.rpad = function( iSize, sPadding )
                            {
                              return rPadString( this, iSize, sPadding );
                            }
  }
  
  /**
   * Formats a date suitable for the DatePicker
   *
   * @param  Date theDate : Date to be formatted.
   * @return String : Date as 8 decimal digits, in the form YYYYMMDD.
   */
  function makeDatePickerDate( theDate )
  {
    return  String( theDate.getFullYear())
          + String( theDate.getMonth() + 1 ).lpad( 2, '0' )
          + String( theDate.getDate()).lpad( 2, '0' );
  }

