/*
 * element-util
 *
 * functions for setting gui element attributes at runtime.
 *
 * (c) Businessmart AG 2006
 * @author Andreas Geissel
 */

// ----------------------------------------------------------------------
/*
 * global definitions.
 */

var _gui_elm =
{
  max_search_level:     10, // tree depth to search for element
  even_tr_lines:    'even',
  odd_tr_lines:      'odd'
};

// ----------------------------------------------------------------------
/**
 * attributes for table rows:
 * <pre>
 * modename :
 * {
 *   'backgroundColor': <background color to be set for this mode>,
 *   'color':           <text color to be set for this mode>,
 *   'action':          <a function to be called for each input field
 *                       within the table row>,
 *   'isDefault':       <only one mode could be default, used to set
 *                       when no mode is set>
 * };
 */
_gui_elm.mode_attibutes =
{
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  restore: {
             backgroundColor: null,
             color:           null,
             isDefault:       true,
             action:          function(elm)
                              {
                                elm.disabled = false;
                              }
           },
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  'delete':{
             backgroundColor: '#bbbbbb',
             color:           '#eeeeee',
             action:          function(elm)
                              {
                                if (elm.type
                                    && elm.type.match(/hidden/i))
                                {
                                  return;
                                }
                                elm.disabled = true;
                              }
           },
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  change:  {
             backgroundColor: '#ffdd99',
             color:           null,
             action:          function(elm) { }
           },
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  odd:     {
             backgroundColor: '#FFDA8C',
             color:           null,
             action:          function(elm) { }
           },
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  even:    {
             backgroundColor: '#FFE1A4',
             color:           null,
             action:          function(elm) { }
           }
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
};

// ----------------------------------------------------------------------
/**
 * set the maximum depth to search down the dom tree
 * for some element.
 * @param aLevel
 */
function setMaxSearchLevel(aLevel)
{
  _gui_elm.max_search_level = aLevel;
}

// ----------------------------------------------------------------------
/**
 * set the value of an attribute.
 * The function checks if the mode exists. If not,
 * nothing will be done, otherwise the attutes value
 * will be set.
 * @param aModeName the name of an existing mode to be modified
 * @param anAttributeName the name of an attribute to be set
 * @param aValue the new value of the attribute
 */
function setModeAttribute(aModeName, anAttributeName, aValue)
{
  if (_gui_elm.mode_attibutes[aModeName])
  {
    _gui_elm.mode_attibutes[aModeName][anAttributeName] = aValue;
  }
}

// ----------------------------------------------------------------------
/**
 * append a node or overwrite an existing one.
 * @param aModeName    name of the mode to add
 * @param theBgColor   the color for tr background
 * @param theTextColor the color for tr text
 * @param theElementAction the action to perform on input elements.
 */
function addAttributeMode(aModeName,
                          theBgColor,
                          theTextColor,
                          anElementAction)
{
  var mode =
  {
    'backgroundColor': theBgColor,
    'color':           theTextColor,
    'action':          anElementAction
  };

  _gui_elm.mode_attibutes[aModeName] = mode;
}

// ----------------------------------------------------------------------
/**
 * if param is a node id the node itself is returned.
 *
 * @param aNodeOrId
 * @return a node
 */
function _getNode(aNodeOrId)
{
  if (typeof(aNodeOrId) == 'string')
  {
    return document.getElementById(aNodeOrId);
  }

  return aNodeOrId;
}

// ----------------------------------------------------------------------
/**
 * find the next outer table row (<tr>) for an input field.
 * @param anElement
 * @param theMaxSearchLeven
 * @param theNestedTrs
 * @see #setMaxSearchLevel(aLevel)
 */
function _findOuterTrFor(anElement, theMaxSearchLevel, theNestedTrs)
{
  var nestedTrs = theNestedTrs && (theNestedTrs > 0) ? theNestedTrs : 1;
  var level     = theMaxSearchLevel ? theMaxSearchLevel
                                    : _gui_elm.max_search_level;

  var elm = anElement;
  while (nestedTrs-- > 0)
  {
    do
    {
      elm = elm.parentNode;
    }
    while ((--level >= 0) && (elm.nodeName.toLowerCase() != "tr"));
  }
  return elm;
}

// ----------------------------------------------------------------------
/**
 * return a mode descriptor for a mode name.
 * @param theModeName
 * @return a mode descriptor
 */
function _findAttributeMode(theModeName)
{
  if (theModeName != null)
  {
    return _gui_elm.mode_attibutes[theModeName];
  }

  var defaultModeName = _findDefaultModeName();
  if (defaultModeName != null)
  {
    return _gui_elm.mode_attibutes[defaultModeName];
  }

  return null;
}

// ----------------------------------------------------------------------
/**
 * return the name of the default attribut mode.
 * Default attribute mode has a property calles
 * isDefault which must be set to true.
 * @return the name of the default attribute mode.
 */
function _findDefaultModeName()
{
  for (var mode in _gui_elm.mode_attibutes)
  {
    if (_gui_elm.mode_attibutes[mode].isDefault)
    {
      return mode;
    }
  }

  return null;
}

// ----------------------------------------------------------------------
/**
 * perform an action on input element and its siblings.
 * @param anElement
 * @param anAction
 * @param theOriginator
 */
function _performAction(anElement, anAction, theOriginator)
{
  if (anAction != null)
  {
    if ((anElement != theOriginator) && (anElement.form != null))
    {
      anAction(anElement);
    }

    for (var elm = anElement.firstChild; elm != null; elm = elm.nextSibling)
    {
      _performAction(elm, anAction, theOriginator);
    }
  }
}

// ----------------------------------------------------------------------
/**
 * set text color for a node and all sub nodes recursivly.
 * @param aNode
 * @param aColor
 */
function _setTextColor(aNode, aColor)
{
  var bgColor = aColor;
  if (bgColor == null)
  {
    bgColor = aNode.oldColor;
  }

  if (aNode.style != null)
  {
    if (bgColor != null)
    {
      aNode.oldColor = (aColor != null ? aNode.style.color : null);
      aNode.style.color = bgColor
    }

    for (var elm = aNode.firstChild; elm != null; elm = elm.nextSibling)
    {
      _setTextColor(elm, aColor);
    }
  }
}

// ----------------------------------------------------------------------
/**
 * modify the background color of a tr element.
 * @param aTr a tr element to modify
 * @param aBackgroundColor the color or null
 */
function _setBackgroundColor(aTr, aBackgroundColor)
{
  var bgColor = aBackgroundColor;
  if ((bgColor == null) && (aTr._gui_elm_oldBgColor != null))
  {
    bgColor = aTr._gui_elm_oldBgColor.pop();
  }

  if (bgColor != null)
  {
    var oldbg = aTr.style.backgroundColor;
    aTr.style.backgroundColor = bgColor;

    if (aTr._gui_elm_oldBgColor == null)
    {
      aTr._gui_elm_oldBgColor = new Array();
    }
    if (aBackgroundColor != null) // not for 'restore'
    {
      aTr._gui_elm_oldBgColor.push(oldbg);
    }
  }
}

// ----------------------------------------------------------------------
/**
 * find the maximal rowspan of all td node within a tr node.
 * @param aTr
 * @return the maximal rowspan
 */
function _findRowspan(aTr)
{
  var rowspan = 1;
  for (var nd = aTr.firstChild; nd != null; nd = nd.nextSibling)
  {
    var ndNm = nd.nodeName.toLowerCase()
    if (nd.nodeName.match(/t[dh]/i))
    {
      if (nd.rowSpan && (rowspan < nd.rowSpan))
      {
        rowspan = nd.rowSpan;
      }
    }
  }

  return rowspan;
}

// ----------------------------------------------------------------------
/**
 * find the table row including an input field and mark that
 * row corresponding to the mode.
 * @param anElement an input field
 * @param aModeName
 * @param theNestedTrsOrNull
 * @return true if outer tr could be modified.
 * @see #_findOuterTrFor()
 * @see #_findAttributeMode()
 * @see #_setFgColor()
 * @see #_setBackgroundColor()
 * @see #_performAction()
 */
function markTableLine(anElement, aModeName, theNestedTrsOrNull)
{
  var outer = _findOuterTrFor(anElement, null, theNestedTrsOrNull);
  if (outer != null)
  {
    if ((aModeName != null) && (aModeName != outer._gui_elm_modeName))
    {
      var mode = _findAttributeMode(aModeName);
      if (mode != null)
      {
        _setBackgroundColor(outer, mode.backgroundColor);
        _setTextColor(outer, mode.color);
        _performAction(outer, mode.action, anElement);
        outer._gui_elm_modeName = aModeName;
      }
      return true;
    }
  }
  return false;
}


// ----------------------------------------------------------------------
/**
 * find number of header rows.
 * The number is found by the regexp /^AUTOTRCOLOR(\d+)\-.+$/.
 * @param aTable
 * @return number of header rows
 */
function _headCount(aTable)
{
  var headerCount = aTable.id.replace(/^AUTOTRCOLOR(\d+)\-.+$/, '$1');
  return headerCount ? parseInt(headerCount, 10) : 0;
}

// ----------------------------------------------------------------------
/**
 *
 */
function _findTableBody(anElementOrTabId)
{
  var tab = _getNode(anElementOrTabId);
  while (tab.nodeName.toLowerCase() != 'table')
  {
    tab = tab.parentNode;
  }

  for(var nd = tab.firstChild; nd != null; nd = nd.nextSibling)
  {
    if (nd.nodeName.toLowerCase() == 'tbody')
    {
      return nd;
    }
  }

  return null;
}

// ----------------------------------------------------------------------
/**
 * find the table column including an input field and mark that
 * column corresponding to the mode.
 * @param anElementOrTabId
 * @param aColumnIndex (0..n)
 * @param aModeName
 * @param theNestedTrsOrNull
 * @return true if outer tr could be modified.
 */
function markTableColumn(anElementOrTabId,
                         aColumnIndex,
                         aModeName,
                         theNestedTrsOrNull)
{
  if (!isNaN(aColumnIndex) && (aColumnIndex > -1))
  {
    var tbody       = _findTableBody(anElementOrTabId);
    var headerCount = _headCount(tbody.parentNode);
    var maxCol      = -1;

    for (var ln = 0, nd = tbody.firstChild; nd != null; nd = nd.nextSibling)
    {
      if (nd.nodeName.match(/^tr$/i) && (headerCount-- <= 0))
      {
        var mode = _findAttributeMode(aModeName);
        if (!mode)
        {
          mode = _findAttributeMode((ln++ & 1) == 0 ? 'even' : 'odd');
        }
        var col  = maxCol;
        for (var snd = nd.lastChild; snd != null; snd = snd.previousSibling)
        {
          if (snd.nodeName.match(/^t[dh]$/i))
          {
            if (col == -1) { col = maxCol = snd.cellIndex; }
            if ((aColumnIndex == col) && (snd.colSpan == 1))
            {
              _setBackgroundColor(snd, mode.backgroundColor);
              _setTextColor(snd, mode.color);
            }
            col -= snd.colSpan;
          }
        }
      }
    }
  }
}

// ----------------------------------------------------------------------
/**
 * find outer tr to given element and set as deleted.
 * If tr is allready deleted restore it.
 * @param anElement
 * @see #markTableLine()
 */
function deleteOrRestore(anElement)
{
  if (!markTableLine(anElement, 'delete'))
  {
    var defaultMode = _findDefaultModeName();
    if (defaultMode != null)
    {
      markTableLine(anElement, defaultMode);
      anElement._gui_elm_oldValue = null;
    }
  }
}

// ----------------------------------------------------------------------
/**
 * mark all lines of table, even lines get a style named 'even'
 * and odd lines get a style named 'odd'.
 * @param aTable
 * @theHeadlineCounter
 */
function markTableLines(aTable, theHeadlineCounter,
                        theEvenClassName, theOddClassName)
{
  var headCounter = theHeadlineCounter ? theHeadlineCounter : 0;
  var tblElm      = aTable.firstChild;

  if (!tblElm) { return; }

  while (tblElm.nodeName.toLowerCase() != 'tbody')
  {
    tblElm = tblElm.nextSibling;
  }

  var evenClass = theEvenClassName ? theEvenClassName : _gui_elm.even_tr_lines;
  var oddClass  = theOddClassName  ? theOddClassName  : _gui_elm.odd_tr_lines;
  var rowspan   = 0;
  var style     = evenClass;
  for (var nd = tblElm.firstChild; nd != null; nd = nd.nextSibling)
  {
    if (nd.nodeName.toLowerCase() == 'tr')
    {
      if (headCounter <= 0)
      {
        if (--rowspan <= 0)
        {
          rowspan = _findRowspan(nd);
          style   = (--headCounter & 1) ? oddClass : evenClass;
        }
        nd.setAttribute("className", style); // makes ie happy
        nd.setAttribute("class",     style); // makes mozilla happy
      }
      else
      {
        --headCounter;
      }
    }
  }
}

// ----------------------------------------------------------------------
/**
 * mark even an odd lines of alls table that ids begin with
 * AUTOTRCOLOR.
 * @param theEvenClassName the name of style class for even lines
 * @param theOddClassName  the name of style class for odd lines
 * @see #markTableLines()
 */
function markAllAutoTableLines(theEvenClassName, theOddClassName)
{
  var tabArr = document.getElementsByTagName('table');
  for (var ii = 0; ii < tabArr.length; ++ii)
  {
    if ((tabArr[ii].id != null) && tabArr[ii].id.match(/^AUTOTRCOLOR\d+\-/))
    {
      var headLines = _headCount(tabArr[ii]);
      markTableLines(tabArr[ii],
                     headLines,
                     theEvenClassName,
                     theOddClassName);
    }
  }
}

// ----------------------------------------------------------------------
/**
 * set background colors of each tr within the table.
 * @param aTableId
 * @param theHeadlineCounter
 * @see #markTableLines()
 */
function markTableLinesById(aTableId, theHeadlineCounter)
{
  var table = document.getElementById(aTableId);
  if (table != null)
  {
    markTableLines(table, theHeadlineCounter);
  }
}

// ----------------------------------------------------------------------
/**
 * restore all table lines used within a form.
 * All elements of the form are used to find their
 * outer tr node.
 * This function is usable as onreset handler.
 * @param aForm the form to restore
 */
function restoreAll(aForm)
{
  var defaultMode = _findAttrubeMode();
  for (var ii = 0; ii < aForm.elements.length; ++ii)
  {
    markTableLine(aForm.elments[ii], defaultMode);
  }
}

// ----------------------------------------------------------------------
/**
 * set a node's position to absolute and if the visibility is true
 * it is set to visible.
 * @param aNodeOrId
 * @param theX
 * @param theY
 * @param theVisibility
 */
function setElementPosition(aNodeOrId, theX, theY, theVisibility)
{
  var node  = _getNode(aNodeOrId);
  var style = node.style;
  style.position   = 'absolute';
  style.top        = theY ? theY : style.top;
  style.left       = theX ? theX : style.left;
  style.visibility = theVisibility ? 'visible' : 'hidden';
}

// ----------------------------------------------------------------------
/**
 * find the postion of a node.
 * @param aNode to find the position of.
 * @return position of a node (obj.x and obj.y)
 */
function getPositionOf(aNode)
{
  var pos = new Object();
  pos.x = 0;
  pos.y = 0;
  pos.node = aNode;
  if (aNode.offsetParent)
  {
    while(aNode.offsetParent)
    {
      pos.x += aNode.offsetLeft;
      pos.y += aNode.offsetTop;
      aNode = aNode.offsetParent;
    }
  }
  else if (aNode.x)
  {
    pos.x = aNode.x;
    pos.y = aNode.y;
  }

  return pos;
}

// ----------------------------------------------------------------------
/**
 * find the width and height of a node.
 * @param aNode to find the dimension of.
 * @return position of a node (obj.witdh and obj.height)
 */
function getDimensionOf(aNodeOrId)
{
  var node = _getNode(aNodeOrId);

  var dim = new Object();
  dim.width  = node.offsetWidth;
  dim.height = node.offsetHeight;

  return dim;
}

// ----------------------------------------------------------------------
function _getFrameWidth()
{
  return document.body.clientWidth  + document.body.scrollLeft;
}

// ----------------------------------------------------------------------
function _getFrameHeight()
{
  return document.body.clientHeight + document.body.scrollTop;
}

// ----------------------------------------------------------------------
/**
 * if the an element would overlap the browser window
 * the position will be reduced.
 * @param theMax
 * @param aPosPart (x or y)
 * @param aDimPart (width or height)
 */
function _normPosPart(theMax, aPosPart, aDimPart)
{
  if (theMax < (aPosPart + aDimPart))
  {
    aPosPart -= aDimPart;
  }

  return aPosPart;
}

// ----------------------------------------------------------------------
/**
 * set absolute position of a node defined by id.
 * @param aReferenceNodeOrId determines the coordinates to set
 * @param theNodeToSetId     defines the id of the node to set
 *                           the postion of.
 */
function setPositionNearBy(aReferenceNodeOrId, theNodeToSetId)
{
  var node = _getNode(aReferenceNodeOrId);
  var pos  = getPositionOf(node);
  var dim  = getDimensionOf(document.getElementById(theNodeToSetId));

  pos.x = _normPosPart(_getFrameWidth(), pos.x, dim.width);
  pos.y = _normPosPart(_getFrameHeight(), pos.y, dim.height);

  setElementPosition(theNodeToSetId, pos.x, pos.y, true);
}

// ----------------------------------------------------------------------
/**
 * Replaces whitespace at the beginning, and the end of an input field,
 * deletes whitespace before and after a dot,
 * and allows one whitespace between the letters
 */
function supertrim(theValue) 
{ 
  var endValue;
  
  endValue=theValue.replace(/\s+/g," ");
  endValue=endValue.replace(/\.\s+/g,".");
  endValue=endValue.replace(/\s+\./g,".");
  endValue=endValue.replace(/\s+$/,"");
  endValue=endValue.replace(/^\s+/,"");
  
  return endValue;
}