/*
 * Copyright (c) 1998-2009 TeamDev Ltd. All Rights Reserved.
 * Use is subject to license terms.
 */

// -------------------------- COMMON UTILITIES

function q__getUserStylePropertyValue(element, propertyName, defaultValue1, defaultValue2) {
  var value = q__calculateStyleProperty(element, propertyName);
  if (!value || value == defaultValue1 || value == defaultValue2)
    return null;
  return value;
}

function q__getUserClassPropertyValue(element, propertyName, defaultValue1, defaultValue2) {
  var value = q__evaluateStyleClassProperty(element.className, propertyName);
  if (!value || value == defaultValue1 || value == defaultValue2)
    return null;
  return value;
}

function q__getRowsInSection(table, tableSection) {
  var section = q__getChildNodesWithNames(table, [tableSection])[0];
  var result = (section != null) ? q__getChildNodesWithNames(section, ["tr"]) : [];
  return result;
}

function q__getRowCells(row) {
  var cells = row.cells;
  return cells
}

function q__getCellColSpan(cell) {
  var colSpan = cell.colSpan;
  if (!colSpan || colSpan == -1)
    colSpan = 1;
  return colSpan;
}

function q__handleUnsupportedRowStyleProperties(row) {
  var cells = row._cells;
  if (!q__isExplorer()) {
    var paddingLeft = q__getUserStylePropertyValue(row, "padding-left", "0px");
    var paddingRight = q__getUserStylePropertyValue(row, "padding-right", "0px");
    var paddingTop = q__getUserStylePropertyValue(row, "padding-top", "0px");
    var paddingBottom = q__getUserStylePropertyValue(row, "padding-bottom", "0px");
    var lineHeight = q__getUserClassPropertyValue(row, "line-height", "normal");

    if (paddingLeft || paddingRight || paddingTop || paddingBottom || lineHeight) {
      for (var cellIndex = 0, cellCount = cells.length; cellIndex < cellCount; cellIndex++) {
        var cell = cells[cellIndex];
        q__setCellStyleProperty(cell, "paddingLeft", paddingLeft);
        q__setCellStyleProperty(cell, "paddingRight", paddingRight);
        q__setCellStyleProperty(cell, "paddingTop", paddingTop);
        q__setCellStyleProperty(cell, "paddingBottom", paddingBottom);
        q__setCellStyleProperty(cell, "lineHeight", lineHeight);
      }
    }
  }

  var propertyValues = q__evaluateStyleClassProperties_cached(
          row.className,
          ["borderLeft", "borderRight", "borderTop", "borderBottom"],
          row._table);
  if (propertyValues.borderLeft || propertyValues.borderRight || propertyValues.borderTop || propertyValues.borderBottom) {
    for (var cellIndex = 0, cellCount = cells.length; cellIndex < cellCount; cellIndex++) {
      var cell = cells[cellIndex];
      q__setCellStyleProperty(cell, "borderLeft", propertyValues.borderLeft);
      q__setCellStyleProperty(cell, "borderRight", propertyValues.borderRight);
      q__setCellStyleProperty(cell, "borderTop", propertyValues.borderTop);
      q__setCellStyleProperty(cell, "borderBottom", propertyValues.borderBottom);
    }
  }
}

function q__evaluateStyleClassProperties_cached(className, propertyNames, cacheInElement) {
  if (!cacheInElement.__stylePropertyCache)
    cacheInElement.__stylePropertyCache = new Array();
  var stylePropertiesKey = className + "___" + propertyNames.join("__");
  var propertyValues = cacheInElement.__stylePropertyCache[stylePropertiesKey];
  if (!propertyValues) {
    propertyValues = q__evaluateStyleClassProperties(className, propertyNames);
    cacheInElement.__stylePropertyCache[stylePropertiesKey] = propertyValues;
  }
  return propertyValues;
}


// -------------------------- INITIALIZATION

/**
 *
 * @param columns defines a hierarchy of all table's columns with all their configuration settings. It is an array of
 *   column specification objects. Each column specification object contains the following properties:
 *   - header: {
 *     - pos: {row: int, cell: int}
 *     - className: String
 *     - onclick, ondblclick and all other events: String
 *   }
 *   - filter: {
 *     - pos: {row: int, cell: int}
 *     - className: String
 *     - onclick, ondblclick and all other events: String
 *   }
 *   - body: {
 *     - className: String
 *     - oddClassName: String
 *     - evenClassName: String
 *     - onclick, ondblclick and all other events: String
 *   }
 *   - footer: {
 *     - pos: {row: int, cell: int}
 *     - className: String
 *     - onclick, ondblclick and all other events: String
 *   - subColumns: [array of column specification objects]
 *
 * @param commonHeaderExists
 * @param filterRowExists
 * @param commonFooterExists
 * @param noDataRows
 * @param gridLines
 * @param rowStyles
 * @param rowStylesMap
 * @param cellStylesMap
 * @param forceUsingCellStyles
 * @param additionalCellWrapperStyle
 * @param invisibleRowsAllowed
 */

function q__initTableStyles(columns, commonHeaderExists, filterRowExists, commonFooterExists, noDataRows,
                            gridLines, rowStyles, rowStylesMap, cellStylesMap, forceUsingCellStyles,
                            additionalCellWrapperStyle, invisibleRowsAllowed) {
  var table = this;
  table._commonHeaderExists = commonHeaderExists;
  table._commonFooterExists = commonFooterExists;
  table._filterRowExists = filterRowExists;
  table._noDataRows = noDataRows;
  table._forceUsingCellStyles = forceUsingCellStyles;
  table._invisibleRowsAllowed = invisibleRowsAllowed;

  (function(bodyRowClass, bodyOddRowClass, rolloverRowClass, commonHeaderRowClass, headerRowClass, filterRowClass, commonFooterRowClass, footerRowClass) {
    table._commonHeaderRowClass = commonHeaderRowClass;
    table._commonFooterRowClass = commonFooterRowClass;
    table._headerRowClass = headerRowClass;
    table._filterRowClass = filterRowClass;
    table._footerRowClass = footerRowClass;
    table._bodyRowClass = bodyRowClass;
    table._bodyOddRowClass = bodyOddRowClass;
    table._rolloverRowClass = rolloverRowClass;
  }).apply(null, rowStyles);
  table._rowStylesMap = rowStylesMap;
  table._cellStylesMap = cellStylesMap;
  table._additionalCellWrapperStyle = additionalCellWrapperStyle;

  // initialize grid lines
  var tempIdx = 0;
  table._horizontalGridLines   = gridLines[tempIdx++];
  table._verticalGridLines     = gridLines[tempIdx++];
  table._commonHeaderSeparator = gridLines[tempIdx++];
  table._commonFooterSeparator = gridLines[tempIdx++];
  table._headerHorizSeparator  = gridLines[tempIdx++];
  table._headerVertSeparator   = gridLines[tempIdx++];
  table._filterRowSeparator    = gridLines[tempIdx++];
  table._multiHeaderSeparator  = gridLines[tempIdx++];
  table._multiFooterSeparator  = gridLines[tempIdx++];
  table._footerHorizSeparator  = gridLines[tempIdx++];
  table._footerVertSeparator   = gridLines[tempIdx++];

  table.style.emptyCells = "show";
  if (q__isExplorer()) {
    var bordersNeeded =
            table._horizontalGridLines   ||
            table._verticalGridLines     ||
            table._commonHeaderSeparator ||
            table._commonFooterSeparator ||
            table._headerHorizSeparator  ||
            table._headerVertSeparator   ||
            table._filterRowSeparator    ||
            table._footerHorizSeparator  ||
            table._footerVertSeparator;
    if (bordersNeeded)
      table.style.borderCollapse = "collapse";
  }

  table._rowInsertionCallbacks = new Array();
  table._addRowInsertionCallback = function(callback) {
    table._rowInsertionCallbacks.push(callback);
  }
  table._singleRowInsertionCallbacks = new Array();
  table._addSingleRowInsertionCallback = function(callback) {
    table._singleRowInsertionCallbacks.push(callback);
  }
  table._cellInsertionCallbacks = new Array();
  table._addCellInsertionCallback = function(callback) {
    table._cellInsertionCallbacks.push(callback);
  }
  table._cellMoveCallbacks = new Array();
  table._addCellMoveCallback = function(callback) {
    table._cellMoveCallbacks.push(callback);
  }

  q__initTableRows(table);
  q__initTableColumns(table, columns);
  q__initTableColumnEvents(table);
  q__initTableColumnStyles(table);
  q__initTableGridLines(table);

  table._removeAllRows = q__table_removeAllRows;
  table._insertRowsAfter = q__table_insertRowsAfter;
}

function q__table_removeAllRows() {
  var bodyRows = this._getBodyRows();
  for (var i = 0, count = bodyRows.length; i < count; i++) {
    var row = bodyRows[i];
    row.parentNode.removeChild(row);
  }
  this._bodyRows = undefined;

  this._rowStylesMap = {};
  this._cellStylesMap = {};
}

function q__table_insertRowsAfter(afterIndex, rowsToInsert, newRowsToStylesMap, newRowCellsToStylesMap) {
  var bodyRows = this._getBodyRows();
  var columns = this._columns;
  var afterRow = afterIndex != -1 ? bodyRows[afterIndex] : null;

  for (var i = 0, count = bodyRows.length, visibleRowCount = 0; i < count; i++) {
    var row = bodyRows[i];
    row._visibleRowIndex = visibleRowCount;
    if (row._isVisible())
      visibleRowCount++;
  }

  var visibleRowsUpToReferenceRow; // visible rows including bodyRows[afterIndex]
  if (!this._invisibleRowsAllowed)
    visibleRowsUpToReferenceRow = afterIndex + 1;
  else {
    visibleRowsUpToReferenceRow = 0;
    for (var i = 0; i <= afterIndex; i++) {
      var row = bodyRows[i];
      if (row._isVisible())
        visibleRowsUpToReferenceRow++;
    }
  }

  var visibleInsertedRows;
  if (!this._invisibleRowsAllowed)
    visibleInsertedRows = rowsToInsert.length;
  else {
    visibleInsertedRows = 0;
    for (var i = 0; i < rowsToInsert.length; i++) {
      var insertedRow = rowsToInsert[i];
      insertedRow._visible = (q__calculateStyleProperty(row, "display") != "none");
      if (insertedRow._visible)
        visibleInsertedRows++;
    }
  }

  var addedRowCount = rowsToInsert.length;
  var visibleRowsUntilNow =
  this._bodyRowCount += addedRowCount;
  for (var originalRowIndex = bodyRows.length - 1; originalRowIndex >= afterIndex && originalRowIndex >= 0; originalRowIndex--) {
    var newRowIndex = originalRowIndex + addedRowCount;
    var moveRow = originalRowIndex != afterIndex;
    var movedRow = bodyRows[originalRowIndex];

    if (moveRow) {
      bodyRows[newRowIndex] = movedRow;
      bodyRows[originalRowIndex] = null;
      movedRow._index = newRowIndex;

      this._rowStylesMap[newRowIndex] = this._rowStylesMap[originalRowIndex];
      this._rowStylesMap[originalRowIndex] = null;
    }

    for (var colIndex = 0, colCount = columns.length; colIndex < colCount; colIndex++) {
      var column = columns[colIndex];
      var bodyCells = column.body._cells;
      var movedCell = bodyCells[originalRowIndex];

      if (moveRow) {
        bodyCells[newRowIndex] = movedCell;
        bodyCells[originalRowIndex] = null;
        var originalCellKey = originalRowIndex + "x" + colIndex;
        var newCellKey = newRowIndex + "x" + colIndex;
        this._cellStylesMap[newCellKey] = this._cellStylesMap[originalCellKey];
        this._cellStylesMap[originalCellKey] = null;
      }
    }

    movedRow._notifyRowMoved(moveRow ? movedRow._visibleRowIndex + visibleInsertedRows : movedRow._visibleRowIndex);
  }

  var nextNode;
  var rowContainer;
  if (afterRow) {
    nextNode = afterRow.nextSibling;
    rowContainer = afterRow.parentNode;
  } else {
    var bodySection = q__getChildNodesWithNames(this, ["tbody"])[0];
    nextNode = bodyRows.length > 0 ? bodyRows[0] : null;
    rowContainer = bodySection;
  }

  var newVisibleRowsForNow = 0;
  for (var i = 0, count = rowsToInsert.length; i < count; i++) {
    var newRow = rowsToInsert[i];
    if (nextNode != null)
      rowContainer.insertBefore(newRow, nextNode);
    else
      rowContainer.appendChild(newRow);

    var newRowIndex = afterIndex + 1 + i;
    bodyRows[newRowIndex] = newRow;
    this._rowStylesMap[newRowIndex] = newRowsToStylesMap ? newRowsToStylesMap[i] : undefined;
    q__initTableRow(newRow, this, newRowIndex, visibleRowsUpToReferenceRow + newVisibleRowsForNow);
    if (newRow._isVisible())
      newVisibleRowsForNow++;

    for (var colIndex = 0, colCount = columns.length; colIndex < colCount; colIndex++) {
      var column = columns[colIndex];
      var cellStyleKey = newRowIndex + "x" + colIndex;
      this._cellStylesMap[cellStyleKey] = newRowCellsToStylesMap ? newRowCellsToStylesMap[i + "x" + colIndex] : undefined;
      var cell = newRow._getCellByColIndex(colIndex);

      var bodyCells = column.body._cells;
      bodyCells[newRowIndex] = cell;
      if (cell)
        for (var callbackIndex = 0, callbackCount = this._cellInsertionCallbacks.length; callbackIndex < callbackCount; callbackIndex++)
          this._cellInsertionCallbacks[callbackIndex](cell, newRow, column);
    }

    for (var callbackIndex = 0, callbackCount = this._singleRowInsertionCallbacks.length; callbackIndex < callbackCount; i++)
      this._singleRowInsertionCallbacks[callbackIndex](newRow);
  }

  for (var i = 0, count = this._rowInsertionCallbacks.length; i < count; i++) {
    this._rowInsertionCallbacks[i](this, afterIndex, rowsToInsert);
  }

}


// -------------------------- TABLE ROWS SUPPORT

function q__initTableRows(table) {
  table._getHeaderRows = function() {
    if (!this._headerRows)
      this._headerRows = q__getRowsInSection(this, "thead");
    return this._headerRows;
  }
  table._getBodyRows = function() {
    if (!this._bodyRows) {
      this._bodyRows = q__getRowsInSection(this, "tbody");
      this._bodyRowCount = this._bodyRows.length;
    }
    return this._bodyRows;
  }
  table._getFooterRows = function() {
    if (!this._footerRows)
      this._footerRows = q__getRowsInSection(this, "tfoot");
    return this._footerRows;
  }

  if (table._getHeaderRows().length > 0)
    table.header = {
      _getRows: function() {return table._getHeaderRows();} // todo: use _getRows() methods of appropriate sections instead of table's getXXXRows methods
    };
  if (table._getFooterRows().length > 0)
    table.footer = {
      _getRows: function() {return table._getFooterRows();}
    };
  table.body = {
    _getRows: function() {return table._getBodyRows();}
  }

  var headRows = table._getHeaderRows();
  for (var rowIndex = 0, rowCount = headRows.length; rowIndex < rowCount; rowIndex++) {
    var row = headRows[rowIndex];
    row._table = table;
    row._index = rowIndex;
    q__prepareRow(row);
  }
  var commonHeaderRowIndex = table._commonHeaderExists ? 0 : -1;
  var filterRowIndex = table._filterRowExists ? headRows.length - 1 : -1;
  table._filterRowIndex = filterRowIndex;
  table._columnHeadersRowIndexRange = [commonHeaderRowIndex + 1, table._filterRowExists ? headRows.length - 1 : headRows.length];
  table._commonHeaderRowIndex = commonHeaderRowIndex;

  if (commonHeaderRowIndex != -1)
    q__setRowStyle(headRows[commonHeaderRowIndex], {_commonHeaderRowClass: table._commonHeaderRowClass});
  if (filterRowIndex != -1)
    q__setRowStyle(headRows[filterRowIndex], {_filterRowClass: table._filterRowClass});
  for (var i = table._columnHeadersRowIndexRange[0], end = table._columnHeadersRowIndexRange[1]; i < end; i++)
    q__setRowStyle(headRows[i], {_headerRowClass: table._headerRowClass});

  var visibleRowCount = 0;
  var bodyRows = table._getBodyRows();
  for (var rowIndex = 0, rowCount = bodyRows.length; rowIndex < rowCount; rowIndex++) {
    var row = bodyRows[rowIndex];
    q__initTableRow(row, table, rowIndex, visibleRowCount);
    if (row._isVisible())
      visibleRowCount++;
  }
  var footRows = table._getFooterRows();
  var lastNonCommonFooter = footRows.length - 1;
  if (table._commonFooterExists)
    lastNonCommonFooter--;
  for (var rowIndex = 0, rowCount = footRows.length; rowIndex < rowCount; rowIndex++) {
    var row = footRows[rowIndex];
    row._table = table;
    row._index = rowIndex;
    q__prepareRow(row);

    if (rowIndex <= lastNonCommonFooter)
      q__setRowStyle(footRows[rowIndex], {_footerRowClass: table._footerRowClass});
  }
  if (table._commonFooterExists)
    q__setRowStyle(footRows[footRows.length - 1], {_commonFooterRowClass: table._commonFooterRowClass});
}

function q__setRowStyle(row, styleMappings) {
  var table = row._table;
  if (table._forceUsingCellStyles) {
    var cells = row._cells;
    for (var i = 0, count = cells.length; i < count; i++) {
      var cell = cells[i];
      q__setCellStyleMappings(cell, styleMappings);
    }
  } else {
    q__setElementStyleMappings(row, styleMappings);
    q__handleUnsupportedRowStyleProperties(row);
  }
}

function q__initTableRow(row, table, rowIndex, visibleRowsBefore) {
  row._table = table;
  row._index = rowIndex;
  row._selected = false;
  if (row._visible === undefined)
    row._visible = table._invisibleRowsAllowed ? q__calculateStyleProperty(row, "display") != "none" : true;

  row._mouseIsOver = false;
  if (!table._noDataRows && table._rolloverRowClass) {
    q__addEventHandlerSimple(row, "mouseover", "_mouseOverHandler");
    q__addEventHandlerSimple(row, "mouseout", "_mouseOutHandler");
  }
  if (row.className == "q_hiddenRow") {
    row.style.display = "none";
    row.className = "";
  }

  row._isVisible = function () {
    return this._visible;
  }
  row._setVisible = function (visible) {
    if (this._visible == visible)
      return;
    this._visible = visible;
    if (this.style.display)
      this.style.display = "";
    q__setElementStyleMappings(this, {_rowVisibilityStyle: visible ? "" : "q_hiddenRow"});
    q__updateCellWrappersStyleForRow(this);
  }

  row._mouseOverHandler = function() {
    this._mouseIsOver = true;
    this._updateStyle();
  }

  row._mouseOutHandler = function() {
    this._mouseIsOver = false;
    this._updateStyle();
  }

  q__prepareRow(row);

  function reinitializeStyle(visibleRowsBefore) {
    q__calculateInitialRowClass(row, table, visibleRowsBefore);
    if (!table._forceUsingCellStyles) {
      var individualRowClass = table._rowStylesMap[row._index];
      q__setElementStyleMappings(row, {_initialClass: row._initialClass, _individualRowClass: individualRowClass});
      q__handleUnsupportedRowStyleProperties(row);
    }
  }
  reinitializeStyle(visibleRowsBefore);

  row._rowMoveCallbacks = new Array();
  row._addRowMoveCallback = function(callback) {
    row._rowMoveCallbacks.push(callback);
  }
  row._notifyRowMoved = function(visibleRowsBefore) {
    for (var callbackIndex = 0, callbackCount = row._rowMoveCallbacks.length; callbackIndex < callbackCount; callbackIndex++)
      row._rowMoveCallbacks[callbackIndex](visibleRowsBefore);
    for (var i = 0, count = row._cells.length; i < count; i++) {
      var cell = row._cells[i];
      for (var callbackIndex = 0, callbackCount = table._cellMoveCallbacks.length; callbackIndex < callbackCount; callbackIndex++)
        table._cellMoveCallbacks[callbackIndex](cell, row, cell._column);
    }
  }

  row._addRowMoveCallback(function(visibleRowsBefore){
    reinitializeStyle(visibleRowsBefore);
    var columns = table._columns;
    var rowIndex = row._index;
    for (var colIndex = 0, colCount = columns.length; colIndex < colCount; colIndex++) {
      var column = columns[colIndex];
      var bodyCells = column.body._cells;
      var cell = bodyCells[rowIndex];
      if (cell)
        cell._updateStyle();
    }

  })

  row._updateStyle = function() {
    var rowTable = this._table;
    var addedClassName = q__combineClassNames([
            this._selected ? rowTable._selectionClass : null,
            this._mouseIsOver ? rowTable._rolloverRowClass : null]);
    if (row._addedClassName == addedClassName)
      return;
    row._addedClassName = addedClassName;
    var opera = q__isOpera();

    if (!rowTable._forceUsingCellStyles) {
      q__setElementStyleMappings(row, {_rolloverAndSelectionClass: addedClassName});
      q__updateCellWrappersStyleForRow(row);
      if (opera) {
        var oldBackground = row.style.background;
        row.style.background = "white";
        row.style.background = "#fefefe";
        row.style.background = oldBackground;
      }
    }

    if (rowTable._needUsingCellStyles === undefined) {
      rowTable._needUsingCellStyles = false;
      var columns = rowTable._columns;
      for (var i = 0, count = columns.length; i < count; i++) {
        var col = columns[i];
        if ((col.body && col.body._getCompoundClassName()) || col._useCellStyles) {
          rowTable._needUsingCellStyles = true;
          break;
        }
      }
    }

    if (rowTable._forceUsingCellStyles || rowTable._needUsingCellStyles){
      var cells = this._cells;
      for (var i = 0, count = cells.length; i < count; i++) {
        var cell = cells[i];
        var col = cell._column;
        if (!rowTable._forceUsingCellStyles && !(col.body && col.body._getCompoundClassName()) && !col._useCellStyles)
          continue;
        q__setElementStyleMappings(cell, {_rolloverAndSelectionClass: addedClassName});
        q__updateCellWrapperStyle(cell);
        if (opera) {
          var oldBackground = cell.style.background;
          cell.style.background = "white";
          cell.style.background = "#fefefe";
          cell.style.background = oldBackground;
        }
      }
    }
  }

}


function q__isClassWithBorders(table, className) {
  if (!className)
    return false;
  var propertyValues = q__evaluateStyleClassProperties_cached(className, ["borderLeft", "borderRight", "borderTop", "borderBottom"], table);
  var atLeastOneBorderIsPresent = propertyValues.borderLeft || propertyValues.borderRight || propertyValues.borderTop || propertyValues.borderBottom;
  return atLeastOneBorderIsPresent
}

function q__prepareRow(row) {
  var cells = q__getRowCells(row);
  row._cells = cells;
  row._cellsByColumns = new Array();
  var colIndex = 0;
  for (var cellIndex = 0, cellCount = cells.length; cellIndex < cellCount; cellIndex++) {
    var cell = cells[cellIndex];
    cell._row = row;
    var cellColSpan = q__getCellColSpan(cell);
    cell._colSpan = cellColSpan;
    cell._colIndex = colIndex;
    row._cellsByColumns[colIndex] = cell;
    colIndex += cellColSpan;
    if (cell.innerHTML == "")
      cell.innerHTML = "&#160;";
  }
  row._getCellByColIndex = function(index) {
    var cell = this._cellsByColumns[index];
    return cell ? cell : null;
  }
  row._removeColumn = function(column) {
    var colIndex = column._colIndex;
    var cellsByColumns = row._cellsByColumns;
    var cell = cellsByColumns[colIndex];
    while (!cell && colIndex >= 0) {
      cell = cellsByColumns[--colIndex];
    }
    if (!cell)
      throw "row._removeColumn: Can't find cell to remove: column._colIndex = " + column._colIndex;
    cell.parentNode.removeChild(cell);
    row._cells = q__getRowCells(row);
    if (cell._column != column) {
      if (!(cell._colSpan > 1))
        throw "row._removeColumn: colSpan greater than 1 expected, but was: " + cell._colSpan;
      cell._colSpan--;
      cell.colSpan = cell._colSpan;
    }
    for (var i = colIndex + 1, count = cellsByColumns.length; i < count; i++) {
      var shiftedCell = cellsByColumns[i];
      cellsByColumns[i - 1] = shiftedCell;
      shiftedCell._colIndex--;
    }
    cellsByColumns[cellsByColumns.length - 1] = undefined;
    cell._row = null;
  }
}

function q__updateCellWrappersStyleForRow(row) {
  if (row._noCellWrappersFound)
    return;
  var atLeastOneCellWrapperFound = false;
  var cells = row._cells;
  for (var i = 0, count = cells.length; i < count; i++) {
    var cell = cells[i];
    if (q__updateCellWrapperStyle(cell))
      atLeastOneCellWrapperFound = true;
  }
  if (!atLeastOneCellWrapperFound)
    row._noCellWrappersFound = true;
}

function q__updateCellWrapperStyle(cell) {
  var col = cell._column;
  if (col && !col.hasCellWrappers)
    return;

  if (cell._noCellWrapperFound)
    return false;
  var cellWrapper = cell._cellWrapper;
  if (!cellWrapper) {
    var nodes = q__findChildNodesByClass(cell, "q_cellWrapper", true);
    if (nodes.length == 0) {
      cell._noCellWrapperFound = true;
      return false;
    }
    q__assert(nodes.length == 1, "q__updateCellWrapperStyle: no more than one cellwrapper expected, but found: " + nodes.length);
    cellWrapper = nodes[0];
    q__assert(cellWrapper, "q__updateCellWrapperStyle: non-null cellWrapper expected");
    cell._cellWrapper = cellWrapper;
  }
  var newWrapperClass = q__combineClassNames([cell.className, cell._row.className, cell._row._table._additionalCellWrapperStyle]);
  if (cellWrapper.className != newWrapperClass)
    cellWrapper.className = newWrapperClass;
  return true;
}


function q__calculateInitialRowClass(row, table, visibleRowIndex) {
  var rowClass;
  if (!table._noDataRows)
    rowClass = (visibleRowIndex % 2 == 0)
            ? table._bodyRowClass
            : (table._bodyOddRowClass ? table._bodyOddRowClass : table._bodyRowClass);
  else
    rowClass = null;

  row._initialClass = rowClass;
  row._addedClassName = undefined;
}


// -------------------------- TABLE GRID-LINES SUPPORT


  function q__initTableGridLines(table) {

    function getSeparatorsForGroup(parentGroup) {
      var parentLevel = (parentGroup == table) ? 0 : parentGroup._hierarchyLevel;
      var separatingColumnsAtLevel = parentLevel + 1;
      var index = table._deepestColumnHierarchyLevel - separatingColumnsAtLevel;
      var horizSeparationLevel = separatingColumnsAtLevel + 1;
      var horizSeparationIndex = table._deepestColumnHierarchyLevel - horizSeparationLevel;
      var result = {};

      var verticalGridLines = table._verticalGridLines ? table._verticalGridLines.split(",") : [];
      var verticalGridLines_length = verticalGridLines.length;
      result.body = verticalGridLines_length ? verticalGridLines[index >= verticalGridLines_length ? verticalGridLines_length - 1 : index] : null;

      var headerVerticalGridLines = table._headerVertSeparator ? table._headerVertSeparator.split(",") : [];
      var headerVerticalGridLines_length = headerVerticalGridLines.length;
      result.header = headerVerticalGridLines_length ? headerVerticalGridLines[index >= headerVerticalGridLines_length ? headerVerticalGridLines_length - 1 : index] : null;

      var footerVerticalGridLines = table._footerVertSeparator ? table._footerVertSeparator.split(",") : [];
      var footerVerticalGridLines_length = footerVerticalGridLines.length;
      result.footer = footerVerticalGridLines_length ? footerVerticalGridLines[index >= footerVerticalGridLines_length ? footerVerticalGridLines_length - 1 : index] : null;

      var multiHeaderSeparators = table._multiHeaderSeparator ? table._multiHeaderSeparator.split(",") : [];
      var multiHeaderSeparators_length = multiHeaderSeparators.length;
      result.multiHeaderSeparator = multiHeaderSeparators_length
              ? multiHeaderSeparators[horizSeparationIndex >= multiHeaderSeparators_length ? multiHeaderSeparators_length - 1 : horizSeparationIndex]
              : null;

      var multiFooterSeparators = table._multiFooterSeparator ? table._multiFooterSeparator.split(",") : [];
      var multiFooterSeparators_length = multiFooterSeparators.length;
      result.multiFooterSeparator = multiFooterSeparators_length
              ? multiFooterSeparators[horizSeparationIndex >= multiFooterSeparators_length ? multiFooterSeparators_length - 1 : horizSeparationIndex]
              : null;

      return result;
    }

    function isEmptyLineStyle(lineStyle) {
      return !lineStyle || lineStyle.indexOf("none") != -1 || lineStyle.indexOf("0px") != -1 || lineStyle.indexOf("0 ") != -1;
    }

    var tableHeader = table.header;
    if (tableHeader) {
      var headRows = tableHeader._getRows();
      tableHeader._updateCommonHeaderSeparator = function() {
        if (!table._commonHeaderExists)
          return;
        var commonHeaderRow = headRows[0];
        var commonHeaderCell = commonHeaderRow._cells[0];
        q__setCellStyleProperty(commonHeaderCell, "borderBottom", table._commonHeaderSeparator);
      }

      function getLastRowCells(lastBeforeFilter) {
        var lastRowCells;
        if (table._filterRowExists && !lastBeforeFilter)
          lastRowCells = headRows[headRows.length - 1]._cells;
        else {
          lastRowCells = new Array();
          // This algorithm searches for the cells adjacent with the header section's bottom edge. It is simplified based
          // on the fact that all the vertically-spanned cells rendered on the server-side always extend to the bottom.
          for (var i = 0, count = headRows.length - (table._filterRowExists ? 1 : 0); i < count; i++) {
            var row = headRows[i];
            var cells = row._cells;
            for (var j = 0, jcount = cells.length; j < jcount; j++) {
              var cell = row._cells[j];
              if (cell.rowSpan > 1 || i == count - 1)
                lastRowCells.push(cell);
            }
          }
        }
        return lastRowCells;
      }

      tableHeader._updateSeparatorStyle = function() {
        var lastRowCells = getLastRowCells(false);
        for (var i = 0, count = lastRowCells.length; i < count; i++) {
          var cell = lastRowCells[i];
          q__setCellStyleProperty(cell, "borderBottom", table._headerHorizSeparator != null ? table._headerHorizSeparator : table._horizontalGridLines);
        }

      }
      tableHeader._updateFilterRowSeparatorStyle = function() {
        if (!table._filterRowExists)
          return;
        // it is essential to assign bottom border of a row above the filter rather than top border of the filter row
        // itself because otherwise IE will make a one-pixel space between vertical separators and this horizontal line
        var lastRowCells = getLastRowCells(true);
        for (var i = 0, count = lastRowCells.length; i < count; i++) {
          var cell = lastRowCells[i];
          q__setCellStyleProperty(cell, "borderBottom", table._filterRowSeparator);
        }
      }

      tableHeader._updateColumnSeparatorStyles = function() {
        var filterRow = table._filterRowExists ? headRows[table._filterRowIndex] : null;
        var filterRowCells = filterRow ? filterRow._cells : null;

        function setHeaderVerticalGridlines(parentGroup) {
          var separators = getSeparatorsForGroup(parentGroup);
          var verticalSeparator = !isEmptyLineStyle(separators.header) ? separators.header : separators.body;
          var columns = parentGroup == table ? table._columnHierarchy : parentGroup.subColumns;
          for (var i = 0, count = columns.length; i < count; i++) {
            var col = columns[i];
            if (i < count - 1) {
              function setRightBorderForThisGroup(col) {
                var cell = col.header ? col.header._cell : null;
                if (cell) {
                  q__setCellRightBorder(cell, verticalSeparator);
                  if (!col.subColumns && filterRowCells)
                    q__setCellRightBorder(filterRowCells[col._colIndex], verticalSeparator);
                }
                if (col.subColumns)
                  setRightBorderForThisGroup(col.subColumns[col.subColumns.length - 1]);
              }
              setRightBorderForThisGroup(col);
            }
            if (col.subColumns) {
              var cell = col.header ? col.header._cell : null;
              if (cell)
                q__setCellStyleProperty(cell, "borderBottom", separators.multiHeaderSeparator);
              setHeaderVerticalGridlines(col);
            }
          }
        }
        setHeaderVerticalGridlines(table);
      }

      tableHeader._updateCommonHeaderSeparator();
      tableHeader._updateFilterRowSeparatorStyle();
      tableHeader._updateColumnSeparatorStyles();
      tableHeader._updateSeparatorStyle();
    }

    var tableFooter = table.footer;
    if (tableFooter) {
      var footRows = tableFooter._getRows();

      tableFooter._updateSeparatorStyle = function() {
        if (footRows.length == 0)
          return;
        var row = footRows[0];
        var rowCells = row._cells;
        for (var i = 0, count = rowCells.length; i < count; i++) {
          var cell = rowCells[i];
          q__setCellStyleProperty(cell, "borderTop", table._footerHorizSeparator != null ? table._footerHorizSeparator : table._horizontalGridLines);
        }
      }

      tableFooter._updateCommonFooterSeparator = function() {
        if (!table._commonFooterExists)
          return;
        var footerRows = table._footerRows;
        var commonFooterRow = footerRows[footerRows.length - 1];
        var commonFooterCell = commonFooterRow._cells[0];
        q__setCellStyleProperty(commonFooterCell, "borderTop", table._commonFooterSeparator);
      }

      tableFooter._updateColumnSeparatorStyles = function() {
        function setFooterVerticalGridlines(parentGroup) {
          var separators = getSeparatorsForGroup(parentGroup);
          var verticalSeparator = !isEmptyLineStyle(separators.footer) ? separators.footer : separators.body;
          var columns = parentGroup == table ? table._columnHierarchy : parentGroup.subColumns;
          for (var i = 0, count = columns.length; i < count; i++) {
            var col = columns[i];
            if (i < count - 1) {
              function setRightBorderForThisGroup(col) {
                var cell = col.footer ? col.footer._cell : null;
                if (cell) q__setCellRightBorder(cell, verticalSeparator);
                if (col.subColumns)
                  setRightBorderForThisGroup(col.subColumns[col.subColumns.length - 1]);
              }
              setRightBorderForThisGroup(col);
            }
            if (col.subColumns) {
              var cell = col.footer ? col.footer._cell : null;
              if (cell) {
                q__setCellStyleProperty(cell, "borderTop", separators.multiFooterSeparator);
              }
              setFooterVerticalGridlines(col);
            }
          }
        }
        setFooterVerticalGridlines(table);
      }

      tableFooter._updateCommonFooterSeparator();
      tableFooter._updateColumnSeparatorStyles();
      tableFooter._updateSeparatorStyle();
    }

    var tableBody = table.body;
    {
      function updateBodyCellBorders(cell, rowIndex, column, rowCount, colCount) {
        if (rowIndex != rowCount - 1)
          q__setCellStyleProperty(cell, "borderBottom", table._horizontalGridLines);
        if (column._colIndex != colCount - 1)
          q__setCellRightBorder(cell, column.body._rightBorder);
      }

      tableBody._updateVerticalGridlines = function() {
        function setBodyVerticalGridlines(parentGroup) {
          var separators = getSeparatorsForGroup(parentGroup);
          var gridLine = separators.body;
          var columns = parentGroup == table ? table._columnHierarchy : parentGroup.subColumns;
          for (var i = 0, count = columns.length; i < count; i++) {
            var col = columns[i];
            if (i < count - 1) {
              var rightBorderCol = col;
              while (rightBorderCol.subColumns)
                rightBorderCol = rightBorderCol.subColumns[rightBorderCol.subColumns.length - 1];
              rightBorderCol.body._rightBorder = gridLine;
            }
            if (col.subColumns)
              setBodyVerticalGridlines(col);
          }
        }
        setBodyVerticalGridlines(table);

        var columns = table._columns;
        for (var i = 0, colCount = columns.length; i < colCount; i++) {
          var column = columns[i];
          var bodyCells = column.body ? column.body._cells : [];

          for (var bodyCellIndex = 0, cellCount = bodyCells.length; bodyCellIndex < cellCount; bodyCellIndex++) {
            var cell = bodyCells[bodyCellIndex];
            if (!cell)
              continue;
            updateBodyCellBorders(cell, bodyCellIndex, column, cellCount, colCount);
          }
        }
      }

      table._addCellInsertionCallback(function(cell, row, column) {
        updateBodyCellBorders(cell, row._index, column, table._bodyRowCount, table._columns.length);
      });
      table._addCellMoveCallback(function(cell, row, column) {
        updateBodyCellBorders(cell, row._index, column, table._bodyRowCount, table._columns.length);
      });

      tableBody._updateVerticalGridlines();
    }

  }

// -------------------------- TABLE COLUMNS SUPPORT

function q__initTableColumns(table, columnHiearachy) {
  table._columnHierarchy = columnHiearachy;
  var deepestColumnHierarchyLevel = 0;
  var realColumns = function processColumns(parentColumn, oneLevelColumns, hierarchyLevel) {
    if (hierarchyLevel > deepestColumnHierarchyLevel)
      deepestColumnHierarchyLevel = hierarchyLevel;
    var collectedColumns = new Array();
    for (var i = 0, count = oneLevelColumns.length; i < count; i++) {
      var col = oneLevelColumns[i];
      col._hierarchyLevel = hierarchyLevel;
      q__initTableColumnOrGroup(col, table);
      col._parentColumn = parentColumn;
      if (col.subColumns) {
        var collectedSubcolumns = processColumns(col, col.subColumns, hierarchyLevel + 1);
        collectedColumns = collectedColumns.concat(collectedSubcolumns);
      } else {
        collectedColumns.push(col);
      }
    }
    return collectedColumns;
  } (null, table._columnHierarchy, 1);
  table._deepestColumnHierarchyLevel = deepestColumnHierarchyLevel;

  table._columns = realColumns;
  var colCount = realColumns.length;

  var colTags = q__getChildNodesWithNames(table, ["col"]);
  if (colTags.length == 0) {
    var colGroup = q__getChildNodesWithNames(table, ["colgroup"])[0];
    colTags = colGroup ? q__getChildNodesWithNames(colGroup, ["col"]) : [];
  }
  q__assert(colTags.length == colCount, "q__initTableColumns: colTags.length(" + colTags.length + ") != colCount(" + colCount + ")");

  for (var colIndex = 0; colIndex < colCount; colIndex++) {
    var column = realColumns[colIndex];
    var colTag = colTags[colIndex];
    q__initTableColumn(column, colTag, colIndex);
  }

  table._addCellInsertionCallback(function(cell, row, column){
    q__initTableBodyCell(cell, column);
    cell._updateStyle();
  });
}

function q__initTableColumnOrGroup(column, table) {
  column._table = table;

  var headRows = table._getHeaderRows();
  if (column.header && column.header.pos) {
    var columnHeaderPos = column.header.pos;
    var row = headRows[columnHeaderPos.row];
    var cell = column.header._cell = row._cells[columnHeaderPos.cell];
    q__initTableHeaderCell(cell, column);
  }

  if (column.filter && column.filter.pos) {
    var columnFilterPos = column.filter.pos;
    var row = headRows[columnFilterPos.row];
    var cell = column.filter._cell = row._cells[columnFilterPos.cell];
    q__initTableFilterCell(cell, column);
  }

  var footRows = table._getFooterRows();
  if (column.footer && column.footer.pos) {
    var columnFooterPos = column.footer.pos;
    var row = footRows[columnFooterPos.row];
    var cell = column.footer._cell = row._cells[columnFooterPos.cell];
    q__initTableFooterCell(cell, column);
  }

  column._getCompoundClassName = function() {
    var className = column.className;
    if (column._parentColumn)
      className = q__combineClassNames([className, column._parentColumn._getCompoundClassName()]);
    return className;
  }

  if (!column.body)
    column.body = {};
  column.body._getCompoundClassName = function() {
    var className = column.body.className;
    if (column._parentColumn)
      className = q__combineClassNames([className, column._parentColumn.body._getCompoundClassName()]);
    return className;
  }

  column._getCompoundEventContainers = function() {
    var result = new Array();
    for (var c = column; c; c = c._parentColumn)
      result.push(c);
    return result;
  }

  column.body._getCompoundEventContainers = function() {
    var result = new Array();
    for (var c = column; c; c = c._parentColumn)
      result.push(c.body);
    return result;
  }

  column._updateStyle = function() {
    var headerCell = column.header ? column.header._cell : null;
    if (headerCell)
      headerCell._updateStyle();

    var filterCell = column.filter ? column.filter._cell : null;
    if (filterCell)
      filterCell._updateStyle();

    var footerCell = column.footer ? column.footer._cell : null;
    if (footerCell)
      footerCell._updateStyle();
  }
}

function q__initTableColumn(column, colTag, colIndex) {
  column._colTag = colTag;
  column._colIndex = colIndex;

  var table = column._table;

  var bodyRows = table._getBodyRows();
  var bodyRowCount = bodyRows.length;
  column.body._cells = new Array();

  for (var i = 0; i < bodyRowCount; i++) {
    var row = bodyRows[i];
    var cell = row._getCellByColIndex(colIndex);
    if (cell) {
      q__initTableBodyCell(cell, column);
    }
    column.body._cells[i] = cell;
  }

  if (column._super_updateStyle)
    throw "q__initTableColumn can be called only once per column";
  column._super_updateStyle = column._updateStyle;
  column._updateStyle = function() {
    column._super_updateStyle();

    column._useCellStyles = q__getUseCellStylesForColumn(column);
    var colTagClassName = !column._useCellStyles ? column._getCompoundClassName() : "";
    column._colTag.className = colTagClassName ? colTagClassName : "";
    column._cellStyles = q__prepareCellStylesForColStyleSimulation(column);

    var bodyCells = column.body ? column.body._cells : [];
    for (var bodyCellIndex = 0, cellCount = bodyCells.length; bodyCellIndex < cellCount; bodyCellIndex++) {
      var cell = bodyCells[bodyCellIndex];
      if (cell)
        cell._updateStyle();
    }

  }

}

function q__initTableHeaderCell(cell, column) {
  cell._column = column;
  cell._colIndex = column._colIndex; // todo: review cell._colIndex usages. Remove cell._colIndex property and use appropriate column's property

  cell._updateStyle = function() {
    q__applySimulatedColStylesToCell(cell);

    var column = cell._column;
    q__setCellStyleMappings(cell, {_compoundColumnClassName: column._getCompoundClassName(), _colHeaderClass: column.header ? column.header.className : null});
  }

}

function q__initTableFilterCell(cell, column) {
  cell._column = column;
  cell._colIndex = column._colIndex;

  cell._updateStyle = function() {
    q__applySimulatedColStylesToCell(cell);

    var column = cell._column;
    q__setCellStyleMappings(cell, {_compoundColumnClassName: column._getCompoundClassName(), _colFilterClass: column.filter ? column.filter.className : null});
  }

}

function q__initTableFooterCell(cell, column) {
  cell._column = column;
  cell._colIndex = column._colIndex;

  cell._updateStyle = function() {
    q__applySimulatedColStylesToCell(cell);

    var column = cell._column;
    q__setCellStyleMappings(cell, {_compoundColumnClassName: column._getCompoundClassName(), _colFooterClass: column.footer ? column.footer.className : null});
  }
}

function q__initTableBodyCell(cell, column) {
  cell._column = column;

  cell._updateStyle = function() {
    var column = cell._column;
    var row = cell._row;
    var table = row._table;
    var rowIndex = row._index;
    var colIndex = column._colIndex;

    if (!table._noDataRows) {
      q__applySimulatedColStylesToCell(cell);
    }

    var cellStyleMappings = {}
    if (table._forceUsingCellStyles || column._useCellStyles) {
      cellStyleMappings._rowInitialClass = row._initialClass;
      var individualRowClass = table._rowStylesMap[row._index];
      cellStyleMappings._rowIndividualClass = individualRowClass;
    }

    if (table._noDataRows) {
      q__assignNoDataCellStyle(cell);
    } else {
      var cellKey = rowIndex + "x" + colIndex;
      var individualCellStyle = table._cellStylesMap[cellKey];
      cellStyleMappings._individualCellStyle = individualCellStyle;

      var columnClassName = column._useCellStyles ? column._getCompoundClassName() : null;
      if (columnClassName)
        cellStyleMappings._columnClass = columnClassName;
      var compoundBodyClassName = column.body._getCompoundClassName();
      if (compoundBodyClassName) {
        cellStyleMappings._rowInitialClass = row._initialClass;
        cellStyleMappings._columnBodyClass = compoundBodyClassName;
      }
    }
    q__setElementStyleMappings(cell, cellStyleMappings);
    q__updateCellWrapperStyle(cell);
  }

}

/*
 * Fix for JSFC-2834. First column style should not be applied to column tag in order to avoid applying it to common
 * header and common footer rows that span across all columns.
 */
function q__getUseCellStylesForColumn(column) {
  var table = column._table;
  if (table._forceUsingCellStyles || column._forceUsingCellStyles)
    return true;
  var thisIsColGroup = column._subColumns;
  if (thisIsColGroup)
    return true;

  var useCellStylesToAvoidApplyingFirstColStyleToCommonHeader =
          column._colIndex == 0 && (table._commonHeaderExists || table._commonFooterExists);

  // cell styles should be used instead of col tag style for first column if there is a no-data message row,
  // otherwise first col style will be applied to the no-data message row, which shouldn't be the case (JSFC-2890)
  var useFirstColumnCellStylesWhenNoData = table._noDataRows && column._colIndex == 0;

  return useCellStylesToAvoidApplyingFirstColStyleToCommonHeader || useFirstColumnCellStylesWhenNoData;
}

function q__assignNoDataCellStyle(cell) {
  var column = cell._column;
  var row = cell._row;
  var table = row._table;
  if (!(table._forceUsingCellStyles || column._useCellStyles)) {
    // first column style shouldn't be applied to the no-data-row
    // no-data row should derive unspecified properties from body section instead
    var noDataRowClass = table._rowStylesMap[0];
    var noDataRowStyle = noDataRowClass ? q__evaluateStyleClassProperties_cached(noDataRowClass, ["backgroundColor", "background"], table) : {};
    if (!noDataRowStyle.backgroundColor && !noDataRowStyle.background) {
      var bodySection = q__getChildNodesWithNames(table, ["tbody"])[0];
      var propertyValue = q__calculateStyleProperty(bodySection, "background-color");
      if (!propertyValue || propertyValue == "transparent") {
        propertyValue = q__calculateStyleProperty(table, "background-color");
      }
      if (!propertyValue || propertyValue == "transparent")
        propertyValue = !q__isSafari() ? "Window" : "white";
      if (propertyValue)
        q__setCellStyleProperty(cell, "backgroundColor", propertyValue);
    }
  }

}

function q__initTableColumnEvents(table) {

  function assignBodyCellEvents(cell, compoundBodyEvents, compoundGeneralEvents) {
    q__assignColumnCellEvents(cell, compoundBodyEvents);
    q__assignColumnCellEvents(cell, compoundGeneralEvents);
  }

 function assignColEvents(oneLevelColumns, additionalGeneralEvents, additionalBodyEvents) {
    for (var i = 0, count = oneLevelColumns.length; i < count; i++) {
      var column = oneLevelColumns[i];
      var compoundGeneralEvents = additionalGeneralEvents ? [column].concat(additionalGeneralEvents) : [column];
      var compoundBodyEvents = additionalBodyEvents ? [column.body].concat(additionalBodyEvents) : [column.body];

      var headerCell = column.header ? column.header._cell : null;
      if (headerCell)
        q__assignColumnCellEvents(headerCell, compoundGeneralEvents, column.header);

      var filterCell = column.filter ? column.filter._cell : null;
      if (filterCell)
        q__assignColumnCellEvents(filterCell, compoundGeneralEvents, column.filter);

      if (!column.subColumns && column.body && !table._noDataRows) {
        var bodyCells = column.body ? column.body._cells : [];
        for (var bodyCellIndex = 0, cellCount = bodyCells.length; bodyCellIndex < cellCount; bodyCellIndex++) {
          var cell = bodyCells[bodyCellIndex];
          if (cell)
            assignBodyCellEvents(cell, compoundBodyEvents, compoundGeneralEvents);
        }
      }

      var footerCell = column.footer ? column.footer._cell : null;
      if (footerCell)
        q__assignColumnCellEvents(footerCell, compoundGeneralEvents, column.footer);

      if (column.subColumns) {
        assignColEvents(column.subColumns, compoundGeneralEvents, compoundBodyEvents);
      }
    }
  }
  assignColEvents(table._columnHierarchy, [], []);

  table._addCellInsertionCallback(function(cell, row, column) {
    assignBodyCellEvents(cell, column.body._getCompoundEventContainers(), column._getCompoundEventContainers());
  });

}

function q__initTableColumnStyles(table) {
  function assignColStyles(oneLevelColumns) {
    for (var i = 0, count = oneLevelColumns.length; i < count; i++) {
      var col = oneLevelColumns[i];
      col._updateStyle();
      if (col.subColumns)
        assignColStyles(col.subColumns);
    }
  }
  assignColStyles(table._columnHierarchy);
}

/**
 * Some CSS styles that can be applid to <col> tags don't behave in a cross-browser way. We extract these styles here
 * for their further applying to column cells in order to simulate the proper behavior.
 */
function q__prepareCellStylesForColStyleSimulation(column) {
  var isExplorer = q__isExplorer();
  var isMozilla = q__isMozilla();
  var isOpera = q__isOpera();

  var table = column._table;
  var colTag = column._colTag;
  if (isMozilla) {
    column._forceCellAlign = colTag.hasAttribute("align") ? colTag.align : null;
    column._forceCellVAlign = colTag.hasAttribute("valign") ? colTag.vAlign : null;
  }

  var colTagClassName = colTag.className;
  if (column._useCellStyles || !colTagClassName)
    return null;

  var cellStyles = null;
  var colProperties;
  if (isMozilla || isOpera) {
    colProperties = ["width", "textAlign", "color", "fontFamily", "fontSize", "fontWeight", "fontStyle", "borderLeft", "borderRight"];
  } else if (isExplorer) {
    colProperties = ["fontFamily", "fontSize", "borderLeft", "borderRight"];
  } else
    colProperties = [];
  var colStyleProperties = q__evaluateStyleClassProperties_cached(colTagClassName, colProperties, table);
  if (isMozilla || isOpera) {
    if (!cellStyles)
      cellStyles = {};
    cellStyles._width = colStyleProperties.width;//q__evaluateStyleClassProperty(colTag.className, "width");
    cellStyles._textAlign = colStyleProperties.textAlign;//q__getUserStylePropertyValue(colTag, "text-align", "start", "left");
    cellStyles._verticalAlign = q__getUserStylePropertyValue(colTag, "vertical-align", "baseline", "auto");
    cellStyles._lineHeight = q__getUserClassPropertyValue(colTag, "line-height", "normal");

    cellStyles._paddingLeft = q__getUserStylePropertyValue(colTag, "padding-left", "0px");
    cellStyles._paddingRight = q__getUserStylePropertyValue(colTag, "padding-right", "0px");
    cellStyles._paddingTop = q__getUserStylePropertyValue(colTag, "padding-top", "0px");
    cellStyles._paddingBottom = q__getUserStylePropertyValue(colTag, "padding-bottom", "0px");

    cellStyles._color = colStyleProperties.color;
    cellStyles._fontWeight = colStyleProperties.fontWeight;
    cellStyles._fontStyle = colStyleProperties.fontStyle;
  }
  if (isMozilla || isExplorer) {
    if (!cellStyles)
      cellStyles = {};
    cellStyles._fontFamily = colStyleProperties.fontFamily;
    cellStyles._fontSize = colStyleProperties.fontSize;
    cellStyles._borderLeft = colStyleProperties.borderLeft;
    cellStyles._borderRight = colStyleProperties.borderRight;
  }
  if (cellStyles && cellStyles._color) {
    // use cell styles for this column to solve color CSS attribute precedence issue in Mozilla (JSFC-2823)
    column._useCellStyles = true;
    if (column.body)
      q__setElementStyleMappings(column.body, {_colTagClassName: colTagClassName});
    cellStyles = null;
  } else {
    if (!(cellStyles._textAlign || cellStyles._verticalAlign ||
          cellStyles._paddingLeft || cellStyles._paddingRight || cellStyles._paddingTop ||
          cellStyles._paddingBottom || cellStyles._width || cellStyles._height || cellStyles._color ||
          cellStyles._fontFamily || cellStyles._fontSize || cellStyles._fontWeight || cellStyles._fontStyle ||
          cellStyles._borderLeft || cellStyles._borderRight))
      cellStyles = null;
  }

  return cellStyles;
}

function q__clearCellStyleProperties(cell, propertyNames) {
  if (!cell)
    return;

  for (var i = 0, count = propertyNames.length; i < count; i++) {
    var propertyName = propertyNames[i];
    cell.style[propertyName] = null;
  }

}

function q__setCellStyleProperty(cell, propertyName, propertyValue) {
  if (!cell || !propertyValue)
    return;

  try {
    cell.style[propertyName] = propertyValue;
  } catch (e) {
    q__logError("q__setCellStyleProperty: couldn't set style property \"" + propertyName + "\" to \"" + propertyValue + "\" ; original error: " + e.message);
    throw e;
  }
}

function q__setCellRightBorder(cell, borderValue) {
  if (!cell || !borderValue)
    return;

  try {
    cell.style.borderRight = borderValue;
  } catch (e) {
    q__logError("q__setCellRightBorder: invalid borderValue: \"" + borderValue + "\" ; it must be a valid CSS declaration of form \"1px solid gray\" ; original error: " + e.message);
    throw e;
  }
}

function q__setCellStyleMappings(cell, styleMappings) {
  q__setElementStyleMappings(cell, styleMappings);
  q__updateCellWrapperStyle(cell);
}

function q__assignColumnCellEvents(cell, eventContainers, additionalEventContainer) {
  if (additionalEventContainer)
    q__assignEvents(cell, additionalEventContainer);
  for (var i = 0, count = eventContainers.length; i < count; i++) {
    var eventContainer = eventContainers[i];
    q__assignEvents(cell, eventContainer);
  }
}

function q__applySimulatedColStylesToCell(cell) {
  var column = cell._column;

  if (column._forceCellAlign)
    cell.align = column._forceCellAlign;
  if (column._forceCellVAlign)
    cell.vAlign = column._forceCellVAlign;

  var cellStyles = column._cellStyles;
  if (cellStyles) {
    q__setCellStyleProperty(cell, "textAlign", cellStyles._textAlign);
    q__setCellStyleProperty(cell, "verticalAlign", cellStyles._verticalAlign);
    q__setCellStyleProperty(cell, "width", cellStyles._width);
    q__setCellStyleProperty(cell, "lineHeight", cellStyles._lineHeight);

    q__setCellStyleProperty(cell, "color", cellStyles._color);
    q__setCellStyleProperty(cell, "fontFamily", cellStyles._fontFamily);
    q__setCellStyleProperty(cell, "fontSize", cellStyles._fontSize);
    q__setCellStyleProperty(cell, "fontWeight", cellStyles._fontWeight);
    q__setCellStyleProperty(cell, "fontStyle", cellStyles._fontStyle);

    q__setCellStyleProperty(cell, "paddingLeft", cellStyles._paddingLeft);
    q__setCellStyleProperty(cell, "paddingRight", cellStyles._paddingRight);
    q__setCellStyleProperty(cell, "paddingTop", cellStyles._paddingTop);
    q__setCellStyleProperty(cell, "paddingBottom", cellStyles._paddingBottom);
    q__setCellStyleProperty(cell, "borderLeft", cellStyles._borderLeft);
    q__setCellStyleProperty(cell, "borderRight", cellStyles._borderRight);
    cell._simulatedColStylesApplied = true;
  } else {
    if (cell._simulatedColStylesApplied) {
      q__clearCellStyleProperties(cell,
              ["textAlign", "verticalAlign", "width", "lineHeight", "color", "fontFamily", "fontSize", "fontWeight",
               "fontStyle", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom", "borderLeft", "borderRight"]);
      cell._simulatedColStylesApplied = false;
    }
  };
}

function q__assignEvents(element, eventsContainer) {
  if (!eventsContainer || eventsContainer.length == 0)
    return;
  var eventPrefix = "on";
  for (var propertyName in eventsContainer) {
    if (!q__stringStartsWith(propertyName, eventPrefix))
      continue;
    var eventScript = eventsContainer[propertyName];

    var handlerFunction;
    eval('handlerFunction = function(event) {return eval(arguments.callee.prototype._handlerScript); }');
    handlerFunction.prototype._handlerScript = eventScript;
    var eventName = propertyName.substring(eventPrefix.length);
    q__addEventHandler(element, eventName, handlerFunction);
  }
}

function q__removeTableColumn(column) {
  var table = column._table;
  var bodyRows = table.body._getRows();
  for (var i = 0, count = bodyRows.length; i < count; i++) {
    var row = bodyRows[i];
    row._removeColumn(column);
  }
}


//AUTO GENERATED CODE

window['q_loadedLibrary:/quipukit/demo/qk_internalResource/teamdev/jsf/renderkit/util/tableUtil-1.6.2.js'] = true;