/**
 * Table of contents
 *
 * Classes:
 * - CMSDataGrid
 * - CMSTabArea
 * - CMSTabPage
 *
 */
/**
 * Class CMSDataGrid
 *
 * Creates an Scrollable Datagrid based on a standard table with fixed thead and tfoot
 *
 * Parameter:
 *   divId:          id-attribute of the <div /> the table is nested in
 *   viewPortHeight: inner height of the scrollable viewport
 *
 * Example:
 * <body onload="var mygrid = new CMSDataGrid('mygridId', 200);">...</body>
 */
function CMSDataGrid(divId, viewportHeight) {

  this._divId = divId;
  this._viewportHeight = viewportHeight;

  this._divObj = null;
  this._tableObj = null;
  this._theadObj = null;
  this._tbodyObj = null;
  this._tfootObj = null;
  this._originalWidth = null;

  // auto initialize
  this.init();

}


CMSDataGrid.prototype.init = function() {
  try {
      this._divObj = document.getElementById(this._divId);
    if (!this._divObj) return;
    this._tableObj = this._divObj.getElementsByTagName('table')[0];
    if (!this._tableObj) return;
    this._theadObj = this._tableObj.getElementsByTagName('thead')[0];
    if (!this._theadObj) return;
    this._tbodyObj = this._tableObj.getElementsByTagName('tbody')[0];
    if (!this._tbodyObj) return;
    this._tfootObj = this._tableObj.getElementsByTagName('tfoot')[0];

    if (document.all) {
      this._initIE();
    } else {
      this._initW3C();
    }
  }
  catch (error) {
    CMSExceptionStacktrace(error, 'CMSDataGrid [ERROR]');
  }
}


CMSDataGrid.prototype._initIE = function() {
  var divHeight = this._viewportHeight;

  this._initLegend();
  this._initTitle();

  // calculate new div height of viewport, quit if its larger than original table (-> no scrollarea needed!)
  divHeight += this._theadObj.offsetHeight;
  if (this._tfootObj) {
    divHeight += this._tfootObj.offsetHeight;
  }
  if (divHeight > this._tableObj.offsetHeight) {
    this._divObj.style.overflowY = 'visible';
    return;
  }



  // [A.1] save old width
  if (this._originalWidth == null) {
    this._originalWidth = this._divObj.offsetWidth;
  }

  // make div scrollable ...
  this._divObj.style.height = divHeight;
  this._divObj.style.overflowY = 'scroll';
  // bugfix for MSIE7: CMS-3667
  this._divObj.style.overflowX = 'hidden';

  // ... but fix tr of thead
  var theadtr = this._theadObj.getElementsByTagName('tr');
  for (var i = 0; i < theadtr.length; i++) {
    theadtr[i].style.position = 'relative';
    theadtr[i].style.setExpression('top','this.offsetParent.top');
  }

  // ... and fix thead (if existing)
  if (this._tfootObj) {
    var tfoottr = this._tfootObj.getElementsByTagName('tr')[0];
    tfoottr.style.position = 'relative';
    var borderBottomWidth = 0;
    try {
      borderBottomWidth = parseInt(this._divObj.currentStyle.borderBottomWidth);
    }
    catch (error) {
    }
    tfoottr.style.top = -1 * (this._tableObj.offsetTop + this._tableObj.offsetHeight - this._divObj.offsetHeight + borderBottomWidth);
  }

  // [A.2] correct width of grid to original value, because scrollbars make the grid bigger
  window.setTimeout("document.getElementById('"+this._divId+"').gridObject.fixScrollbars();", 50);

  this._divObj.gridObject = this;
}



CMSDataGrid.prototype.fixScrollbars = function() {
  if (document.all) {
	if (typeof document.body.style.maxWidth == "undefined") {
      // <= IE6
      this._divObj.style.marginRight= (this._divObj.offsetWidth-this._divObj.clientWidth)+"px";
    } else {
      // >= IE 7
    }
  }
}

CMSDataGrid.prototype._initW3C = function() {

  this._initLegend();
  this._initTitle();

  // declaration for mozilla
  if (this._tableObj.offsetHeight > this._viewportHeight) {
    // making tbody scrollable
    this._tbodyObj.style.height = this._viewportHeight+ 'px';
    this._tbodyObj.style.overflow = '-moz-scrollbars-vertical';
  } else {
    this._tbodyObj.style.overflow = 'auto';
  }

  this._divObj.gridObject = this;
}

CMSDataGrid.prototype._initLegend = function() {
  // look for legend icon to initialize legend-div with overlib
  if (typeof overlib != 'undefined') {
    var theaddivs = this._theadObj.getElementsByTagName('span');
    for (var i = 0; i < theaddivs.length; i++) {
      if (hasClass(theaddivs[i], 'legendicon')) {
        // get id from icon and create id of legendtext-object -> id: "<legendiconId>-text"
        var legendTextObj = document.getElementById(theaddivs[i].id + '-text');
        if (legendTextObj && legendTextObj.innerHTML) {

          theaddivs[i].onmouseover = function () { return overlib('<div class="'+legendTextObj.className+'" style="display: block;">'+legendTextObj.innerHTML+'</div>', FULLHTML); };
          theaddivs[i].onmouseout = function () { return nd(); };

          legendTextObj.style.display = 'none';
        }
      }
    }
  }
}

CMSDataGrid.prototype._initTitle = function() {
  // look for td's and initialize the title with innerHTML
  if (typeof overlib != 'undefined') {
    var tbodytds = this._tbodyObj.getElementsByTagName('td');
    for (var i = 0; i < tbodytds.length; i++) {
      if (hasClass(tbodytds[i], 'cmsTitleData')) {
        tbodytds[i].title = tbodytds[i].innerHTML + '\n' + tbodytds[i].title;
        tbodytds[i].title = this._replaceAll(tbodytds[i].title, '&nbsp;', ' ');
        tbodytds[i].title = this._replaceAll(tbodytds[i].title, '&lt;', '<');
        tbodytds[i].title = this._replaceAll(tbodytds[i].title, '&gt;', '>');
        tbodytds[i].title = this._replaceAll(tbodytds[i].title, '&quot;', '"');
        tbodytds[i].title = this._replaceAll(tbodytds[i].title, '&amp;', '&');
      }
    }
  }
}

CMSDataGrid.prototype._replaceAll = function(s, s2, s3) {
    if (s==null||s2==null||s3==null) {
        return s;
    }
    while (s.indexOf(s2)!=-1) {
        s = s.replace(s2, s3);
    }
    return s;
}

CMSDataGrid.prototype.destruct = function() {
  try {
    if (document.all) {
      this._destructIE();
    } else {
      this._destructW3C();
    }
  }
  catch (error) {
    CMSExceptionStacktrace(error, 'CMSDataGrid [ERROR]');
  }
}

CMSDataGrid.prototype._destructIE = function() {
  // restore
  this._divObj.style.height = 'auto';
  this._divObj.style.overflowY = 'visible';
  var theadtr = this._theadObj.getElementsByTagName('tr');
  for (var i = 0; i < theadtr.length; i++) {
    theadtr[i].style.position = 'static';
    theadtr[i].style.top = 'auto';
  }
  if (this._tfootObj) {
    var tfoottr = this._tfootObj.getElementsByTagName('tr')[0];
    tfoottr.style.position = 'static';
    tfoottr.style.top = 'auto';
  }
  if (this._originalWidth != null) {
    this._divObj.style.pixelWidth = this._originalWidth;
  }
}

CMSDataGrid.prototype._destructW3C = function() {
  // making tbody scrollable
  this._tbodyObj.style.maxHeight = null;

  // declaration for mozilla
  this._tbodyObj.style.overflow = 'visible';
}

CMSDataGrid.prototype.redraw = function() {
  if (this._divObj) {
    // currently redraw is only needed for MSIE ...
    if (document.all) {
      // save scrollpos for redraw
      this.lastScrollTop = this._divObj.scrollTop;
      this.lastScrollLeft = this._divObj.scrollLeft;

      this.destruct();
      this.init();

      // restore scrollpos for redraw
      this._divObj.scrollTop = this.lastScrollTop;
      this._divObj.scrollLeft = this.lastScrollLeft;
    }
  }
}

CMSDataGrid.prototype.addRows = function(htmlsource) {
  if (this._tbodyObj) {
    if (document.all) {
      // MSIE: tbody.innerHTML is read-only
      var tempDiv = document.createElement('div');
      tempDiv.innerHTML = '<table>'+htmlsource+'</table>';

      var newRow = tempDiv.getElementsByTagName('tr');
      while (newRow && newRow.length > 0) {
        this._tbodyObj.appendChild(newRow[0]);
        newRow = tempDiv.getElementsByTagName('tr');
      }

      tempDiv = null;
    } else {
      this._tbodyObj.innerHTML += htmlsource;
    }
  }
}

CMSDataGrid.prototype.removeRows = function() {
  if (this._tbodyObj) {
    if (document.all) {
        var row = this._tbodyObj.getElementsByTagName('tr');
      while (row && row.length > 0) {
        this._tbodyObj.removeChild(row[0]);
        row = this._tbodyObj.getElementsByTagName('tr');
      }
    } else {
      this._tbodyObj.innerHTML = "";
    }
  }
}

CMSDataGrid.prototype.removeRow = function(index) {
  if (this._tbodyObj) {
      var row = this._tbodyObj.getElementsByTagName('tr');
    for (i = 0; row && row.length > 0; i++) {
        if (i==index) {
        this._tbodyObj.removeChild(row[i]);
        break;
      }
      row = this._tbodyObj.getElementsByTagName('tr');
    }
  }
}

CMSDataGrid.prototype.show = function() {
  this._divObj.style.visibility="visible";
}

CMSDataGrid.prototype.hide = function() {
  this._divObj.style.visibility="hidden";
}




/**
 * Class CMSTabArea
 *
 * Creates an tabbed area for more comfortable arrangement of page content
 *
 * Parameter:
 *   divId:            id-attribute of the <div /> the pages are nested in
 *   clientAutoSave:     should last active tab saved in client-cookie (default = true)
 *   tabOnloadFunction   after activating the tab this function is called if it is specified
 *
 * Example:
 * <body onload="var myarea = new CMSTabArea('myAreaId');">...</body>
 */
function CMSTabArea(divId, clientAutoSave) {
  if (divId == null)
    return;

  this._divId = divId;
  this._divObj = null;
  this._subPages = new Array();
  this._activePage = 0;
  this._pageScrollingX = false;
  this._controlObj = null;
  if (clientAutoSave == null) {
    this._autoSave = true;
  } else {
    this._autoSave = clientAutoSave;
  }
  this._tabOnloadFunction = new Array();

  this.init();
}



CMSTabArea.prototype.init = function() {
  this._divObj = document.getElementById(this._divId);
  if (!this._divObj) return;

  // creating control panel
  this._controlObj = document.createElement('div');
  this._controlObj.className = 'tabcontrol';
  this._divObj.insertBefore(this._controlObj, this._divObj.firstChild );

  // getting tab pages
  var innerdivs = this._divObj.getElementsByTagName('div');
  for (var i = 0; i < innerdivs.length; i++) {

    if (hasClass(innerdivs[i], 'tabpage') && innerdivs[i].parentNode == this._divObj) {
      this.addTabPage(innerdivs[i]);
    }
  }
  if (this._autoSave) {
    this.loadActiveTabPage();
  }

}

CMSTabArea.prototype.addTabPage = function (pageElement) {
  if (!pageElement) return;

  var newIndex = this._subPages.length;
  var newPage =  new CMSTabPage(pageElement, this, newIndex);
  newPage._areaObj = this;
  this._subPages[newIndex] = newPage;

  // move the tabtitle from tabpage into the tab-control
  this._controlObj.appendChild(newPage._tabTitleObj);

  if (this._activePage == newIndex) {
    newPage.show();
  } else {
    newPage.hide();
  }
  newPage.setScrollingX(this._pageScrollingX);

}

/**
 * Shows the control-element (clickable tab-title) at the top of tabarea
 *
 * @param pageId the id (HTML-DOM) of the tab-page element
 */
CMSTabArea.prototype.enableTabPage = function (pageId) {
	if (!pageId) return;
	var tIndex = this.getIndexByPageId(pageId);
	if (tIndex == null) return;

	this._subPages[tIndex]._tabTitleObj.style.display = '';
}

/**
 * Hides the control-element (clickable tab-title) at the top of tabarea
 * Use this, if you want to prevent a user from accessing a specific tab-page
 *
 * @param pageId the id (HTML-DOM) of the tab-page element
 */
CMSTabArea.prototype.disableTabPage = function (pageId) {
	if (!pageId) return;
	var tIndex = this.getIndexByPageId(pageId);
	if (tIndex == null) return;

	this._subPages[tIndex]._tabTitleObj.style.display = 'none';
}

CMSTabArea.prototype.setSelectedPageIndex = function(pageIndex, doSave) {
  if (doSave == null) {
    doSave = this._autoSave;
  }

  if (pageIndex >= 0 && pageIndex < this._subPages.length) {
    if (this._activePage != -1) {
      this._subPages[this._activePage].hide();
    }
    this._subPages[pageIndex].show();
    this._activePage = pageIndex;
    if (doSave && this._autoSave) {
      this.saveActiveTabPage(this._activePage);
    }

    var functionIndex = this._subPages[pageIndex]._divObj.id;
    if (this._tabOnloadFunction[functionIndex]) {
      var tabOnloadFunction = this._tabOnloadFunction[functionIndex];
      if (tabOnloadFunction.indexOf(";")==-1) {
        tabOnloadFunction += ";"
      }
      eval(tabOnloadFunction);
    }
  }
}

CMSTabArea.prototype.addTabOnloadFunction = function(tabName, functionName) {
  this._tabOnloadFunction[tabName] = functionName;
}

CMSTabArea.prototype.setSelectedPageById = function(pageId) {
  var tIndex = this.getIndexByPageId(pageId);
  if (tIndex != null) {
       this._subPages[tIndex].select();
  }
}
CMSTabArea.prototype.getIndexByPageId = function(pageId) {
	for (var i = 0; i < this._subPages.length; i++) {
			if (this._subPages[i]._divObj.id == pageId) {
				return i;
			}
	}
	return null;
}
CMSTabArea.prototype.getSelectedPageIndex = function() {
    return this._activePage;
}
CMSTabArea.prototype.getSelectedPageId = function() {
    return this._subPages[this._activePage]._divObj.id;
}
CMSTabArea.prototype.setPageScrollingX = function(enabled) {
  for (var i = 0; i < this._subPages.length; i++) {
    this._subPages[i].setScrollingX(enabled);
  }
}
CMSTabArea.prototype.resetActiveTabPage = function(pageIndex) {
  if (pageIndex >= 0 && pageIndex < this._subPages.length) {
    var activePageId = this._subPages[pageIndex]._divObj.id;

    document.cookie = 'CMSTab_'+this._divObj.id+'_pge='+pageIndex;
  }
}
CMSTabArea.prototype.saveActiveTabPage = function(pageIndex) {
  if (!pageIndex) {
    pageIndex = this._activePage;
  }
  if (pageIndex >= 0 && pageIndex < this._subPages.length) {
    var activePageId = this._subPages[pageIndex]._divObj.id;

    document.cookie = 'CMSTab_'+this._divObj.id+'_pge='+pageIndex;
  }
}
CMSTabArea.prototype.loadActiveTabPage = function() {
    var searchString = 'CMSTab_'+this._divObj.id+'_pge=';

    var oldCookie = document.cookie;
    if (oldCookie) {
      var oldCookieT = new StringTokenizer(oldCookie, ';');
      while (oldCookieT.hasMoreTokens()) {
        var cookieEntry = oldCookieT.nextToken();
        if (cookieEntry.indexOf(searchString) > -1) {
          var keyValT = new StringTokenizer(cookieEntry, '=');
          var valNum = Number(keyValT.getTokens()[1]);
          if (isNaN(valNum)) {
            valNum = 0;
          }
          this.setSelectedPageIndex(valNum, false)
          break;
        }
      }
    }
}




/**
 * Class CMSTabPage
 *
 * Creates an page inside a tabbed area
 * *** should not be constructed/used directly ***
 *
 * @see CMSTabArea
 */
function CMSTabPage(elem, tabareaElem, pageIndex) {
  if ((elem == null) || (elem == null))
    return;

  this._divObj = elem;
  this._pageIndex = pageIndex;
  this._areaObj = tabareaElem;
  this._tabTitleObj = null;

  this.init();

}

CMSTabPage.prototype.init = function() {
  // getting tabtitle
  for (var i = 0; i < this._divObj.childNodes.length; i++) {
    if ((this._divObj.childNodes[i].nodeType == 1) &&
      (hasClass(this._divObj.childNodes[i], "tabtitle"))) {
      this._tabTitleObj = this._divObj.childNodes[i];
      break;
    }
  }


  // search tabtitle for <a>-Tag children ...
  var tabTitleALink = null;
  for (var i = 0; i < this._tabTitleObj.childNodes.length; i++) {
    if ((this._tabTitleObj.childNodes[i].nodeType == 1) &&
      (this._tabTitleObj.childNodes[i].nodeName == "A")) {
      tabTitleALink = this._tabTitleObj.childNodes[i];
      break;
    }
  }

  // ... if tabtitle element contains a <a>-Tag, use this instead creating own one.
  if (tabTitleALink != null) {
    var oldFunction = tabTitleALink.onclick;
    this._tabTitleObj.appendChild( tabTitleALink );

    var thisObj = this;
    tabTitleALink.onclick = function () { thisObj.select(); return oldFunction(); };
  } else {
    // getting content of tabtitle elemente and surround it with an <a>-tag
    var tabA = document.createElement('a');
    tabA.href = "JavaScript:void(0);";
    tabA.onclick = function () { return false; };
    while ( this._tabTitleObj.hasChildNodes() ) {
      tabA.appendChild( this._tabTitleObj.firstChild );
    }
    this._tabTitleObj.appendChild( tabA );

    var thisObj = this;
    this._tabTitleObj.onclick = function () { thisObj.select(); };
  }

}

CMSTabPage.prototype.show = function() {
	addClass(this._tabTitleObj, 'selected');
	this._tabTitleObj.style.display = ''; // enable tab-title if it has been disabled before
	this._divObj.style.display = 'block';

  // MSIE has problems with CMSDataGrid implementation in tabpages, we have to redraw them
  this.redrawGrids();
}

CMSTabPage.prototype.hide = function() {
  removeClass(this._tabTitleObj, 'selected');
  this._divObj.style.display = 'none';
}

CMSTabPage.prototype.select = function() {
  this._areaObj.setSelectedPageIndex( this._pageIndex );
}

CMSTabPage.prototype.redrawGrids = function() {
  var innerdivs = this._divObj.getElementsByTagName('div');
  for (var i = 0; i < innerdivs.length; i++) {
    if (hasClass(innerdivs[i], 'datagrid') && innerdivs[i].gridObject) {
      innerdivs[i].gridObject.redraw();
    }
  }
}

CMSTabPage.prototype.setScrollingX = function(enabled) {
  if (document.all) {
    if (enabled) {
      this._divObj.style.overflowX = 'auto';
    } else {
      this._divObj.style.overflowX = 'hidden';
    }
  } else {
    if (enabled) {
      this._divObj.style.overflow = 'auto';
    } else {
      if (this._divObj.clientHeight < this._divObj.scrollHeight) {
        this._divObj.style.overflow = '-moz-scrollbars-vertical';
        if (this._divObj.style.overflow != '-moz-scrollbars-vertical') {
          this._divObj.style.overflow = 'auto';
        }
      }
    }
  }
}


/*
 * Constructor.
 * Split up a material string based upong the separator.
 * Param    -  material, the String to be split up.
 * Param    -  separator, the String to look for within material. Should be
 *             something like "," or ".", not a regular expression.
 *
 */
function StringTokenizer (material, separator) {
   // Attributes.
   this.material = material;
   this.separator = separator;
   // Operations.
   this.getTokens = getTokens;
   this.nextToken = nextToken;
   this.countTokens = countTokens;
   this.hasMoreTokens = hasMoreTokens;
   this.tokensReturned = tokensReturned;
   // Initialisation code.
   this.tokens = this.getTokens();
   this.tokensReturned = 0;
}


/*
 * Go through material, putting each token into a new array.
 * Return      - the array with all the tokens in it.
 */
function getTokens() {
   // Create array of tokens.
   var tokens = new Array();
   var nextToken;
   // If no separators found, single token is the material string itself.
   if (this.material.indexOf (this.separator) < 0) {
    tokens [0] = this.material;
    return tokens;
   }
   // Establish initial start and end positions of the first token.
   start = 0;
   end = this.material.indexOf (this.separator, start);
   // Counter for how many tokens were found.
   var counter = 0;
   // Go through material, token at a time.
   var trimmed;
   while (this.material.length - start >= 1) {
    nextToken = this.material.substring (start, end);
    start = end + 1;
    if (this.material.indexOf (this.separator, start + 1) < 0) {
      end = this.material.length;
    } else {
      end = this.material.indexOf (this.separator, start + 1);
    }
        trimmed = trim (nextToken);
        // Remove any extra separators at start.
        while (trimmed.substring(0, this.separator.length) == this.separator) {
           trimmed = trimmed.substring (this.separator.length);
        }
        trimmed = trim(trimmed);
        if (trimmed == "") {
           continue;
        }
        tokens [counter] = trimmed;
    counter ++;
  }
    // Return the initialised array.
    return tokens;
}


/*
 * Return a count of the number of tokens in the material.
 * Return      - int number of tokens in material.
 */
function countTokens() {
  return this.tokens.length;
}

/*
 * Get next token in material.
 * Return      - next token in material.
 */
function nextToken() {
   if (this.tokensReturned >= this.tokens.length) {
      return null;
   } else {
      var returnToken = this.tokens [this.tokensReturned];
      this.tokensReturned ++;
      return returnToken;
   }
}



/*
 * Tests if there are more tokens available from this tokenizer's string. If
 * this method returns true, then a subsequent call to nextToken
 * will successfully return a token.
 *
 * Return      true if more tokens, false otherwise.
 */
function hasMoreTokens() {
   if (this.tokensReturned < this.tokens.length) {
      return true;
   } else {
      return false;
   }
}

/*
 * Returns the current returned token
 */
function tokensReturned() {
   return this.tokensReturned;
}

/*
 * Trim blanks from a String
 */
function trim (strToTrim) {
   return(strToTrim.replace(/^\s+|\s+$/g, ''));
}
