/*
 * Copyright (c) 1998-2009 TeamDev Ltd. All Rights Reserved.
 * Use is subject to license terms.
 */

// ================================== PUBLIC API FUNCTIONS

/**
 * Selects all rows in a table identified by the passed tableId. The table must have a multiple row selection mode.
 */
function q_selectAllRows(tableId) {
  if (!tableId)
    throw "q_selectAllRows: Table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_selectAllRows: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_selectAllRows: The clientId passed refers to a component other than <q:dataTable> : " + tableId;

  table.__selectAllRows();
}

/**
 * Clears selection in a table identified by the passed tableId. This method works with any selection mode.
 */
function q_clearSelection(tableId) {
  if (!tableId)
    throw "q_clearSelection: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_clearSelection: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_clearSelection: The clientId passed refers to a component other than <q:dataTable> : " + tableId;
  //  if (table._selectableItems != "rows")
  //    throw "q_clearSelection: The specified table is not set up for row selection. Selectable items are: " + table._selectableItems + "; table's clientId is: " + tableId;

  table.__clearSelection();
}

/**
 * Returns true if the table has any row(s) selected. This method works with any selection mode.
 */
function q_isTableSelectionEmpty(tableId) {
  if (!tableId)
    throw "q_isTableSelectionEmpty: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_isTableSelectionEmpty: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_isTableSelectionEmpty: The clientId passed refers to a component other than <q:dataTable> : " + tableId;
  return table.__isSelectionEmpty();
}

/**
 * Returns a zero-based index of the currently selected row, or -1 if no row is selected. The table must be in the single
 * row selection mode for this method to work.
 */
function q_getSelectedRowIndex(tableId) {
  if (!tableId)
    throw "q_getSelectedRowIndex: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_getSelectedRowIndex: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_getSelectedRowIndex: The clientId passed refers to a component other than <q:dataTable> : " + tableId;

  return table.__getSelectedRowIndex();
}

/**
 * Selects a row with the specified index. The table must be in the single row selection mode for this method to work.
 */
function q_setSelectedRowIndex(tableId, rowIndex) {
  if (!tableId)
    throw "q_setSelectedRowIndex: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_setSelectedRowIndex: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_setSelectedRowIndex: The clientId passed refers to a component other than <q:dataTable> : " + tableId;

  table.__setSelectedRowIndex(rowIndex);
}

/**
 * Returns an array of zero-based indexes of the currently selected rows. The table must be in the multiple row
 * selection mode for this method to work.
 */
function q_getSelectedRowIndexes(tableId) {
  if (!tableId)
    throw "q_getSelectedRowIndexes: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_getSelectedRowIndexes: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_getSelectedRowIndexes: The clientId passed refers to a component other than <q:dataTable> : " + tableId;

  return table.__getSelectedRowIndexes();
}

/**
 * Selects rows whose indexes are passed in the rowIndeses array. The table must be in the multiple row selection mode
 * for this method to work.
 */
function q_setSelectedRowIndexes(tableId, rowIndexes) {
  if (!tableId)
    throw "q_setSelectedRowIndexes: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_setSelectedRowIndexes: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_setSelectedRowIndexes: The clientId passed refers to a component other than <q:dataTable> : " + tableId;

  table.__setSelectedRowIndexes(rowIndexes);
}

/**
 * Returns the number of rows in the table's body section..
 */
function q_getRowCount(tableId) {
  if (!tableId)
    throw "q_getRowCount: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_getRowCount: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_getRowCount: The clientId passed refers to a component other than <q:dataTable> : " + tableId;
  return table.__getRowCount();

}

/**
 * Reloads the table using Ajax without reloading the whole page. The table parameters such as selection and filter
 * values, as well as table's sub-components and facets are submitted to the server just like during the standard form
 * submission. This method can only be invoked on a table which has its "useAjax" attribute set to true (this is
 * default value).
 *
 * Note that this method reloads the table asynchronously, that is it initiates ajax request for reloading a table
 * and returns without waiting for this request to complete.
 *
 * Parameters:
 *   tableId - client ID of a table which should be refreshed. Data for all sub-components and facets of this table will be
 *             submitted like during an ordinary form submission.
 *   submittedComponentIds - Optional - can be null. An array of client IDs for components those should be submitted in
 *                           addition to the table and its inner components.
 *   serverAction - Optional - can be null. An action in the form of "backingBeanName.methodName", which should be
 *                  executed during this Ajax request. The method to which this action refers should be a public method
 *                  without parameters and having a "void" return type.
 */
function q_refreshTable(tableId, submittedComponentIds, serverAction) {
  if (!tableId)
    throw "q_reloadTable: table's clientId must be passed as a parameter";
  var table = document.getElementById(tableId);
  if (!table)
    throw "q_reloadTable: Invalid clientId passed - no such component was found: " + tableId;
  if (!table._q_dataTableComponentMarker)
    throw "q_reloadTable: The clientId passed refers to a component other than <q:dataTable> : " + tableId;
  if (!table._useAjax)
    throw "q_reloadTable: This method can't be invoked if Ajax is turned off for this table. Please set the useAjax attribute to true on the appropriate <q:dataTable> tag. table's clientId: " + tableId;
  q__reloadComponent(tableId, null, submittedComponentIds, serverAction);
}

// ================================== END OF PUBLIC API FUNCTIONS

// -------------------------- COMMON TABLE FUNCTIONS

function q__initDataTableAPI(table) {
  table.selectAllRows = function() {
    this.__selectAllRows();
  }
  table.clearSelection = function() {
    this.__clearSelection();
  }
  table.isSelectionEmpty = function() {
    return this.__isSelectionEmpty();
  }
  table.getSelectedRowIndex = function() {
    return this.__getSelectedRowIndex();
  }
  table.setSelectedRowIndex = function(rowIndex) {
    this.__setSelectedRowIndex(rowIndex);
  }
  table.getSelectedRowIndexes = function() {
    return this.__getSelectedRowIndexes();
  }
  table.setSelectedRowIndexes = function(rowIndexes) {
    this.__setSelectedRowIndexes();
  }
  table.getRowCount = function() {
    return this.__getRowCount();
  }
  table._q_dataTableComponentMarker = true;
}

function q__initTable(tableId, structureAndStyleParams, useAjax, rolloverClass, apiInitializationFunctionName) {
  var table = document.getElementById(tableId);
  if (!table)
    throw "q__initTable: couldn't find table by id: " + tableId;
  if (table._commonTableFunctionsInitialized)
    return;
  table._commonTableFunctionsInitialized = true;

  table._rolloverClass = rolloverClass;
  table._useAjax = useAjax;

  try {
    q__initTableStyles.apply(table, structureAndStyleParams);
  } finally {
    table.style.visibility = "visible";
    // can't just exclude the "q_initially_invisible" from table.className because of IE issue (JSFC-2337)
  }
  q__initApiFunctions(table);
  q__initTableMouseOvers(table);

  table._originalClassName = table.className;
  table._updateStyle = function() {
    var newClassName = q__combineClassNames([
      this._originalClassName,
      this._mouseIsOver ? this._rolloverClass : null,
      this._focused ? this._focusedClassName : null]);
    if (this.className != newClassName)
      this.className = newClassName;
  }

  table._unloadHandlers = [];
  table.onComponentUnload = function() {
    for (var i = 0, count = table._unloadHandlers.length; i < count; i++) {
      table._unloadHandlers[i]();
    }

    var filtersToHide = this._filtersToHide;
    if (!q__isExplorer() || q__isExplorer7() || !filtersToHide)
      return false;

    for (var i = 0, count = filtersToHide.length; i < count; i++) {
      var filter = filtersToHide[i];
      filter.style.visibility = "hidden";
    }
    return true;
  }
  if (apiInitializationFunctionName) {
    var initFunction = eval(apiInitializationFunctionName);
    if (initFunction)
      initFunction(table);
  }

  table._cleanUp = function() {
    table._bodyRows = new Array();
    table._headerRows = new Array();
    table._footerRows = new Array();
  }
}


function q__initTableMouseOvers(table) {
  table._mouseIsOver = false;
  if (table._rolloverClass) {
    q__addEventHandlerSimple(table, "mouseover", "_mouseOverHandler");
    q__addEventHandlerSimple(table, "mouseout", "_mouseOutHandler");
  }

  table._mouseOverHandler = function() {
    this._mouseIsOver = true;
    this._updateStyle();
  }

  table._mouseOutHandler = function() {
    this._mouseIsOver = false;
    this._updateStyle();
  }
}

function q__setCellProperty(cell, propertyName, propertyValue) {
  if (!cell)
    return;

  try {
    cell[propertyName] = propertyValue;
  } catch (e) {
    q__logError("q__setCellProperty: couldn't set cell property \"" + propertyName + "\" to \"" + propertyValue + "\" ; original error: " + e.message);
    throw e;
  }
}


function q__initApiFunctions(table) {
  table.__selectAllRows = function() {
    if (this._selectableItems != "rows")
      throw "selectAllRows: The table is not set up for row selection. Selectable items are: " + this._selectableItems + "; table's clientId is: " + this.id;
    if (!this._multipleSelectionAllowed)
      throw "selectAllRows: The table is not set up for multiple selection. Table's clientId is: " + this.id;
    this._selectAllItems();
  }
  table.__clearSelection = function() {
    this._unselectAllItems();
  }
  table.__isSelectionEmpty = function() {
    var selectedItems = this._getSelectedItems();
    if (!selectedItems || selectedItems.length == 0)
      return true;
    if (selectedItems[0] == -1)
      return true;
    return false;
  }
  table.__getSelectedRowIndex = function() {
    if (this._selectableItems != "rows")
      throw "getSelectedRowIndex: The specified table is not set up for row selection. Selectable items are: " + this._selectableItems + "; table's clientId is: " + this.id;
    if (this._multipleSelectionAllowed)
      throw "getSelectedRowIndex can only used on a table with single selection mode; table's clientId is: " + this.id;

    var selectedItems = this._getSelectedItems();
    if (selectedItems.length == 0)
      return -1;
    return selectedItems[0];
  }
  table.__setSelectedRowIndex = function(rowIndex) {
    if (this._selectableItems != "rows")
      throw "setSelectedRowIndex: The specified table is not set up for row selection. Selectable items are: " + this._selectableItems + "; table's clientId is: " + this.id;
    if (this._multipleSelectionAllowed)
      throw "setSelectedRowIndex can only used on a table with single selection mode; table's clientId is: " + this.id;
    var bodyRows = table._getBodyRows();
    if ((rowIndex != -1) && (rowIndex < 0 || rowIndex >= bodyRows.length))
      throw "setSelectedRowIndex parameter is out of range (" + rowIndex + "); table's clientId is: " + this.id + "; number of rows is: " + bodyRows.length;
    this._setSelectedItems(rowIndex != -1 ? [rowIndex] : []);
  }
  table.__getSelectedRowIndexes = function() {
    if (this._selectableItems != "rows")
      throw "getSelectedRowIndexes: The specified table is not set up for row selection. Selectable items are: " + this._selectableItems + "; table's clientId is: " + this.id;
    if (!this._multipleSelectionAllowed)
      throw "getSelectedRowIndexes can only used on a table with multiple selection mode; table's clientId is: " + this.id;

    var selectedItems = this._getSelectedItems();
    if (!selectedItems || (selectedItems.length == 1 && selectedItems[0] == -1))
      selectedItems = [];
    return selectedItems;
  }
  table.__setSelectedRowIndexes = function(rowIndexes) {
    if (this._selectableItems != "rows")
      throw "setSelectedRowIndexes: The specified table is not set up for row selection. Selectable items are: " + this._selectableItems + "; table's clientId is: " + this.id;
    if (!this._multipleSelectionAllowed)
      throw "setSelectedRowIndexes can only used on a table with multiple selection mode; table's clientId is: " + this.id;
    if (!rowIndexes)
      rowIndexes = [];

    var bodyRows = table.__getRowCount();
    for (var i = 0, count = rowIndexes.length; i < count; i++) {
      var rowIndex = rowIndexes[i];
      if (rowIndex < 0 || rowIndex >= bodyRows.length)
        throw "setSelectedRowIndexes parameter is out of range (" + rowIndex + "); table's clientId is: " + this.id + "; number of rows is: " + bodyRows.length;
    }
    this._setSelectedItems(rowIndexes);
  }
  table.__getRowCount = function() {
    if (this._noDataRows)
      return 0;
    var bodyRows = this._getBodyRows();
    var rowCount = bodyRows.length;
    return rowCount;
  }
}

// -------------------------- KEYBOARD NAVIGATION SUPPORT

function q__initTableKeyboardNavigation(tableId, controlPaging, focusedClassName, canPageBack, canPageForth, canSelectLastPage, tabindex) {
  var table = document.getElementById(tableId);
  table._controlPagingWithKeyboard = controlPaging;
  table._focusedClassName = focusedClassName;
  table._canPageBack = canPageBack;
  table._canPageForth = canPageForth;
  table._canSelectLastPage = canSelectLastPage;

  q__setupArtificialFocus(table.id, table._focusedClassName, tabindex);

  var pagingFld = document.getElementById(table.id + "::paging");
  if (pagingFld)
    pagingFld.value = "";
  table._performPagingAction = function(actionStr) {
    q__addHiddenField(this, this.id + "::paging", actionStr);
    q__submitTableInternal(this);
  }

  table._nextPage = function() {
    if (this._canPageForth) this._performPagingAction("selectNextPage");
  }
  table._previousPage = function() {
    if (this._canPageBack) this._performPagingAction("selectPrevPage");
  }
  table._firstPage = function() {
    if (this._canPageBack) this._performPagingAction("selectFirstPage");
  }
  table._lastPage = function() {
    if (this._canSelectLastPage) this._performPagingAction("selectLastPage");
  }
  table._selectPageNo = function(pageNo) {
    this._performPagingAction("selectPageNo:" + pageNo);
  }

  var eventName = (q__isSafariOnMac() || q__isOpera() || (q__isMozilla() && !q__isSafari())) ? "onkeypress" : "onkeydown";
  table._prevKeyHandler = table[eventName];
  table[eventName] = function (evt) {
    var e = evt ? evt : event;

    if (this._prevKeyHandler)
      this._prevKeyHandler(evt);

    var ctrlPressed = e.ctrlKey;
    var altPressed = e.altKey;
    var shiftPressed = e.shiftKey;
    var noModifiersPressed = !ctrlPressed && !altPressed && !shiftPressed;

    e.upPressed = false;
    e.downPressed = false;
    e.homePressed = false;
    e.endPressed = false;
    e.pageUpPressed = false;
    e.pageDownPressed = false;
    e.leftPressed = false;
    e.rightPressed = false;
    e.plusPressed = false;
    e.minusPressed = false;
    switch (e.keyCode) {
      case 33: // page up
      case 63276: /* Safari for Mac */
        e.pageUpPressed = true;
        break;
      case 34: // page down
      case 63277: /* Safari for Mac */
        e.pageDownPressed = true;
        break;
      case 35: // end
      case 63275:
        e.endPressed = true;
        break;
      case 36: // home
      case 63273:
        e.homePressed = true;
        break;
      case 37: // left
      case 63234:
        e.leftPressed = true;
        break;
      case 38: // up
      case 63232:
        e.upPressed = true;
        break;
      case 39: // right
      case 63235:
        e.rightPressed = true;
        break;
      case 40: // down
      case 63233:
        e.downPressed = true;
        break;
      case 107:
      case 43:
        e.plusPressed = true;
        break;
      case 109:
      case 45:
        e.minusPressed = true;
        break;
    }

    var passEvent = true;

    if (this._controlPagingWithKeyboard && !altPressed && !shiftPressed) {
      if (e.pageUpPressed) {
        passEvent = false;
        this._previousPage();
      }
      if (e.pageDownPressed) {
        passEvent = false;
        this._nextPage();
      }
      if (ctrlPressed && e.homePressed) {
        passEvent = false;
        this._firstPage();
      }
      if (ctrlPressed && e.endPressed) {
        passEvent = false;
        this._lastPage();
      }
    }

    if (this._selectionKeyboardSupport && this._selectionEnabled) {
      var rowCount = this.__getRowCount();
      if (this._multipleSelectionAllowed && !altPressed && !ctrlPressed) {      // ------ multiple selection
        var selectedRowIndexes = this.__getSelectedRowIndexes();
        var idx;
        if (selectedRowIndexes.length == 0)
          idx = -1;
        else if (selectedRowIndexes.length == 1)
          idx = selectedRowIndexes[0];
        else {
          idx = this._rangeEndRowIndex;
          if (!idx)
            idx = selectedRowIndexes[0];
        }

        if (!shiftPressed) {
          var newIdx = q__checkTableNavigation(this, idx, rowCount, e);
          if (newIdx != null) {
            passEvent = false;
            this.__setSelectedRowIndexes([newIdx]);
            q__scrollToRowIndexes(this, [newIdx]);
            this._baseRowIndex = null;
            this._baseSelectedRowIndexes = null;
            this._rangeEndRowIndex = null;
          }
        } else {
          var baseRowIndex = this._baseRowIndex;
          if (baseRowIndex == null) {
            baseRowIndex = idx != -1 ? idx : 0;
            this._baseRowIndex = baseRowIndex;
            this._baseSelectedRowIndexes = selectedRowIndexes;
          }
          var rangeEndRowIndex = this._rangeEndRowIndex;
          if (rangeEndRowIndex == null)
            rangeEndRowIndex = baseRowIndex;
          var newRangeEndRowIndex = q__checkTableNavigation(this, rangeEndRowIndex, rowCount, e);
          if (newRangeEndRowIndex != null) {
            passEvent = false;
            var newSelectedRowIndexes = q__combineSelectedRowsWithRange(this, this._baseSelectedRowIndexes, baseRowIndex, newRangeEndRowIndex);
            this._rangeEndRowIndex = newRangeEndRowIndex;
            this.__setSelectedRowIndexes(newSelectedRowIndexes);
            q__scrollToRowIndexes(this, newSelectedRowIndexes);
          }
        }

      }
      if (!this._multipleSelectionAllowed && noModifiersPressed) {              // ------ single selection
        var idx = this.__getSelectedRowIndex();
        var newIdx = q__checkTableNavigation(this, idx, rowCount, e);
        if (newIdx != null) {
          passEvent = false;
          this.__setSelectedRowIndex(newIdx);
          q__scrollToRowIndexes(this, [newIdx]);
        }
      }
    }
    if (passEvent) {
      if (this._onKeyboardNavigation)
        passEvent = this._onKeyboardNavigation(e);
    }
    if (!passEvent) {
      e.cancelBubble = true;
    }

    var tabPressed = e.keyCode == 9;
    if (!tabPressed /* allow focus traversal with Tab key while the table is focused */ && q__isExplorer6()) {
      // cancel the event to avoid rare IE6 crashes (JSFC-3783)
      var target = (e != null)
              ? (e.target ? e.target : e.srcElement)
              : null;

      var isTableFocused = false;
      if (target._table) {
        isTableFocused = target._table._q_dataTableComponentMarker || target._table._q_treeTableComponentMarker;
      } else if (target._q_dataTableComponentMarker || target._q_treeTableComponentMarker) {
        isTableFocused = true;
      }

      if (!isTableFocused && q__isControlFocusable(target)) {
        return true;
      }

      if (e.preventDefault)
        e.preventDefault();
      e.returnValue = false;
      return false;
    } else {

      return true;
    }
  }

  table._prevOnfocus_kn = table.onfocus;
  table.onfocus = function(e) {
    if (this._submitting)
      return;
    if (this._prevOnfocus_kn)
      this._prevOnfocus_kn(e);
    var focusFld = document.getElementById(this.id + "::focused");
    focusFld.value = "true";
  }

  table._prevOnblur_kn = table.onblur;
  table.onblur = function(e) {
    if (this._submitting)
      return;
    if (this._prevOnblur_kn)
      this._prevOnblur_kn(e);
    var focusFld = document.getElementById(this.id + "::focused");
    focusFld.value = "false";
  }

  var focusFld = document.getElementById(table.id + "::focused");
  if (focusFld.value == "true") {
    setTimeout(function() {
      table.focus()
    }, 1);
  }

  table._unloadHandlers.push(function() {
    q__deinitializeTableKeyboardNavigation(table);
  });
}

function q__deinitializeTableKeyboardNavigation(table) {
  table.onfocus = null;
  table.onblur = null;
  if (table._focusControl) {
    //    if (table._focused)
    //      table.blur();
    table._focusControl.parentNode.removeChild(table._focusControl);
  }
}

function q__scrollToRowIndexes(table, rowIndexes) {
  var bodyRows = table._getBodyRows();

  var boundingRect = null;
  for (var i = 0, count = rowIndexes.length; i < count; i++) {
    var rowIndex = rowIndexes[i];
    var row = bodyRows[rowIndex];
    var pos = q__getElementPos(row);
    var x = pos.left;
    var y = pos.top;
    var rect = new q__Rectangle(x, y, row.offsetWidth, row.offsetHeight);
    if (!boundingRect)
      boundingRect = rect;
    else
      boundingRect.addRectangle(rect);
  }

  if (boundingRect)
    q__scrollRectIntoView(boundingRect);
}


function q__combineSelectedRowsWithRange(table, baseSelectedRowIndexes, baseRowIndex, rangeEndRowIndex) {
  q__assert(baseRowIndex, "q__combineSelectedRowsWithRange: baseRowIndex should be specified");
  q__assert(rangeEndRowIndex, "q__combineSelectedRowsWithRange: rangeEndRowIndex should be specified");

  var result = new Array();
  var alreadyIncludedIndexes = new Array();
  var rangeStart, rangeEnd;
  if (baseRowIndex < rangeEndRowIndex) {
    rangeStart = baseRowIndex;
    rangeEnd = rangeEndRowIndex;
  } else {
    rangeStart = rangeEndRowIndex;
    rangeEnd = baseRowIndex;
  }

  if (baseSelectedRowIndexes)
    for (var i = 0, count = baseSelectedRowIndexes.length; i < count; i++) {
      var idx = baseSelectedRowIndexes[i];
      result.push(idx);
      if (idx >= rangeStart && idx <= rangeEnd)
        alreadyIncludedIndexes.push(idx);
    }

  var bodyRows = table._getBodyRows();
  for (var i = rangeStart; i <= rangeEnd; i++) {
    if (q__findValueInArray(i, alreadyIncludedIndexes) == -1) {
      var row = bodyRows[i];
      if (row._visible)
        result.push(i);
    }
  }
  return result;
}

function q__checkTableNavigation(table, idx, rowCount, e) {
  var newIndex = null;
  if (e.upPressed) {
    if (idx == -1)
      newIndex = q__getNeighboringVisibleRowIndex(table, -1, +1);
    else
      newIndex = q__getNeighboringVisibleRowIndex(table, idx, -1);
  }
  if (e.downPressed) {
    if (idx == -1)
      newIndex = q__getNeighboringVisibleRowIndex(table, -1, +1);
    else
      newIndex = q__getNeighboringVisibleRowIndex(table, idx, +1);
  }
  if (e.homePressed) {
    newIndex = q__getNeighboringVisibleRowIndex(table, idx, -rowCount);
  }
  if (e.endPressed) {
    newIndex = q__getNeighboringVisibleRowIndex(table, idx, rowCount);
  }
  /*
  if (e.pageUpPressed) {
    if (idx == -1)
      newIndex = q__getNeighboringVisibleRowIndex(table, -1, +1);
    else
      newIndex = q__getNeighboringVisibleRowIndex(table, idx, -5);
  }
  if (e.pageDownPressed) {
    if (idx == -1)
      newIndex = q__getNeighboringVisibleRowIndex(table, -1, +1);
    else
      newIndex = q__getNeighboringVisibleRowIndex(table, idx, +5);
  }
  */

  return newIndex;
}

function q__showControlOutline(control, outlineStyleClass) {
  if (!outlineStyleClass)
    return;
  if (!control._outline) {
    var outline = document.createElement("div");
    outline._control = control;
    outline._updatePosition = function() {
      var outlineToControlSpacingPx = 2;
      var pos = q__getElementPos(control);
      var x = pos.left;
      var y = pos.top;
      var width = this._control.offsetWidth;
      var height = this._control.offsetHeight;

      this.style.position = "absolute";
      this.style.left = (x - outlineToControlSpacingPx) + "px";
      this.style.top = (y - outlineToControlSpacingPx) + "px";
      this.style.width = (width + outlineToControlSpacingPx * 2) + "px";
      this.style.height = (height + outlineToControlSpacingPx * 2) + "px";
      this.style.zIndex = -1000;
    }
    control._outline = outline;
    document.body.appendChild(outline);
  } else {
    control._outline.style.visibility = "visible";
  }
  control._outline._updatePosition();
}

function q__hideControlOutline(control) {
  if (!control._outline)
    return;
  control._outline.style.visibility = "hidden";
}

var q__tableBlurCounter = 0;

function q__setupArtificialFocus(tableId, focusedClassName, tabindex) {
  var table = document.getElementById(tableId);

  table._focusedClassName = focusedClassName;

  table._focused = false;
  table._updateOutline = function() {
    if (this._outlineUpdateBlocked)
      return;
    this._updateStyle();
    if (this._focused)
      q__showControlOutline(this, null);
    else
      q__hideControlOutline(this);
  }

  table._blockOutlineUpdate = function() {
    this._outlineUpdateBlocked = true;
    this._focusedBeforeBlocking = this._focused;
  }

  table._unblockOutlineUpdate = function () {
    if (!q__tableBlurCounter)
      q__tableBlurCounter = 0;
    if (!q__isMozilla()) {
      setTimeout(function() {
        table._doUnblockOutlineUpdate();
      }, 1);
    } else {
      table._doUnblockOutlineUpdate();
    }
  }
  table._doUnblockOutlineUpdate = function() {
    this._outlineUpdateBlocked = false;
    if (this._focusedBeforeBlocking != null && this._focusedBeforeBlocking != this._focused) {
      this._focusedBeforeBlocking = null;
      if (this._focused) {
        if (this._prevOnfocusHandler_af)
          this._prevOnfocusHandler_af(null);
      } else {
        if (this._prevOnblurHandler_af)
          this._prevOnblurHandler_af(null);
      }
    }
    this._updateOutline();
  }

  table._prevOnfocusHandler_af = table.onfocus;
  table._prevOnblurHandler_af = table.onblur;

  table.onfocus = function(evt) {
    if (this._submitting)
      return;
    this._focused = true;
    if (this._prevOnfocusHandler_af && !this._outlineUpdateBlocked)
      this._prevOnfocusHandler_af(evt);

    table._updateOutline();
  }
  table.onblur = function(evt) {
    if (this._submitting)
      return;
    this._focused = false;
    if (this._prevOnblurHandler_af && !this._outlineUpdateBlocked)
      this._prevOnblurHandler_af(evt);
    if (!q__isMozilla()) {
      setTimeout(function() {
        table._updateOutline();
      }, 1);
    } else {
      table._updateOutline();
    }
  }

  var focusControl = q__createHiddenFocusElement(tabindex);

  focusControl._table = table;
  focusControl.onfocus = function(evt) {
    this._prevStatusText = window.status;
    window.status = "";
    return q__fireEvent(this._table, "onfocus", evt);
  }
  focusControl.onblur = function(evt) {
    window.status = this._prevStatusText;
    return q__fireEvent(this._table, "onblur", evt);
  }
  focusControl.onkeydown = function(evt) {
    return q__fireEvent(this._table, "onkeydown", evt);
  }
  focusControl.onkeyup = function(evt) {
    return q__fireEvent(this._table, "onkeyup", evt);
  }
  focusControl.onkeypress = function(evt) {
    return q__fireEvent(this._table, "onkeypress", evt);
  }

  table._focusControl = focusControl;

  table._focusOnClick = function(evt) {
    if (window.getSelection) {
      if (window.getSelection() != "")
        return; // don't switch focus to make text selection possible under FF (JSFC-1134)
    }
    var e = evt ? evt : event;
    if (this._focused)
      return;

    var target = (e != null)
            ? (e.target ? e.target : e.srcElement)
            : null;
    if (target.id && target.id == this.id)
      return;
    if (q__isControlFocusable(target))
      return;
    this._preventPageScrolling = true;
    this.focus();
    this._preventPageScrolling = false;
  }

  table._focusable = true;
  table.focus = function() {
    if (this._preventPageScrolling) {
      var pageScrollPos = q__getPageScrollPos();
      this._focusControl.style.left = pageScrollPos.x + "px";
      this._focusControl.style.top = pageScrollPos.y + "px";
    } else {
      this._focusControl.style.left = "";
      this._focusControl.style.top = "";
    }
    this._focusControl.focus();
  }

  table.blur = function() {
    this._focusControl.blur();
  }

  q__addEventHandlerSimple(table, "click", "_focusOnClick");
  q__addEventHandlerSimple(table, "mousedown", "_blockOutlineUpdate");
  q__addEventHandlerSimple(table, "mouseup", "_unblockOutlineUpdate");
  q__addEventHandlerSimple(table, "mouseout", "_unblockOutlineUpdate");

  table.parentNode.insertBefore(focusControl, table);
}

function q__fireEvent(object, eventName, param) {
  var handler = object[eventName];
  if (!handler)
    return;
  if (q__isExplorer())
    return object[eventName]();
  else
    return object[eventName](param);
}

function q__isControlFocusable(control) {
  if (!control)
    return false;

  if (control._focusable)
    return true;
  var tagName = control.tagName;
  if (!tagName)
    return false;
  tagName = tagName.toLowerCase();
  if (tagName == "input" ||
      tagName == "select" ||
      tagName == "textarea" ||
      tagName == "button" ||
      tagName == "a" ||
      (tagName == "span" && q__checkClassNameUsed(control, "rich-inplace-select")) ||
      (tagName == "div" && q__checkClassNameUsed(control, "rich-inplace")))
    return true;
  else
    return false;
}

function q__getNeighboringVisibleRowIndex(table, startRowIndex, stepCount) {
  var bodyRows = table._getBodyRows();
  if (stepCount == 0)
    return bodyRows[startRowIndex];
  var dir = (stepCount > 0) ? +1 : -1;
  var rowIndex = startRowIndex;
  var destRowIndex = startRowIndex;
  var stepsRemaining = (stepCount > 0) ? stepCount : -stepCount;
  while (stepsRemaining > 0) {
    rowIndex += dir;
    if (rowIndex < 0 || rowIndex >= bodyRows.length)
      break;
    var row = bodyRows[rowIndex];
    if (row._visible) {
      destRowIndex = rowIndex;
      stepsRemaining--;
    }
  }
  return destRowIndex;
}


// -------------------------- TABLE SELECTION SUPPORT

function q__initTableSelection(tableId, enabled, selectableItems,
                               multipleSelectionAllowed, selectedItems, selectionClass,
                               selectionChangeHandler, postEventOnSelectionChange, selectionColumnIndexes,
                               mouseSupport, keyboardSupport) {
  var table = document.getElementById(tableId);
  q__assert(table, "Couldn't find table by id: " + tableId);

  q__assert(!table._selectionInitialized, "q__initTableSelection shouldn't be called twice on the same table");
  table._selectionInitialized = true;

  // initialize fields
  table._selectionEnabled = !table._noDataRows && enabled;
  table._selectableItems = selectableItems;
  table._multipleSelectionAllowed = multipleSelectionAllowed;
  table._selectionClass = selectionClass;
  table._selectionColumnIndexes = selectionColumnIndexes;
  table._selectionMouseSupport = mouseSupport;
  table._selectionKeyboardSupport = keyboardSupport;

  // initialize function references
  table._selectItem = q__table_selectItem;
  table._unselectItem = q__table_unselectItem;
  table._getSelectedItems = function(items) {
    if (!this._selectedItems)
      this._selectedItems = [];
    return this._selectedItems;
  }
  table._setSelectionFieldValue = function (value) {
    var selectionFieldId = this.id + "::selection";
    var selectionField = document.getElementById(selectionFieldId);
    q__assert(selectionField, "Couldn't find selectionField by id: " + selectionFieldId);
    selectionField.value = value;
  }
  table._setSelectedItems = function(items, forceUpdate) {
    var changesArray = new Array();
    var changesArrayIndexes = new Array();
    var oldSelectedItemsStr = "";
    if (this._selectedItems)
      for (var i = 0; i < this._selectedItems.length; i++) {
        var item = this._selectedItems[i];
        if (i > 0)
          oldSelectedItemsStr += ",";
        oldSelectedItemsStr += item;
        changesArray[item] = "unselect";
        changesArrayIndexes.push(item);
      }
    if (!this._multipleSelectionAllowed && items && items.length > 1)
      items = [items[0]];
    if (items.length == 1 && items[0] == -1)
      items = [];
    this._selectedItems = items;
    var newSelectedItemsStr = "";
    if (this._selectedItems) {
      for (var i = 0; i < this._selectedItems.length; i++) {
        if (i > 0)
          newSelectedItemsStr += ",";
        var itemToSelect = this._selectedItems[i];
        q__assert(itemToSelect, "table._setSelectedItems: itemToSelect is undefined for index " + i);
        newSelectedItemsStr += itemToSelect;
        if (changesArray[itemToSelect] == "unselect" && !forceUpdate)
          changesArray[itemToSelect] = null;
        else
          changesArray[itemToSelect] = "select";
        changesArrayIndexes.push(itemToSelect);
      }
    }

    for (var i = 0, count = changesArrayIndexes.length; i < count; i++) {
      var changesArrayIndex = changesArrayIndexes[i];
      var change = changesArray[changesArrayIndex];
      if (change) {
        if (change == "select")
          this._selectItem(changesArrayIndex);
        if (change == "unselect")
          this._unselectItem(changesArrayIndex);
        changesArray[changesArrayIndex] = null;
      }
    }

    var selectionFieldValue = q__formatSelectedItems(this._selectableItems, this._selectedItems);
    this._setSelectionFieldValue(selectionFieldValue);
    if (!this._blockSelectionChangeNotifications && oldSelectedItemsStr != newSelectedItemsStr) {
      if (this._selectionChangeHandlers) {
        for (var handlerIdx = 0, handlerCount = this._selectionChangeHandlers.length;
             handlerIdx < handlerCount;
             handlerIdx++) {
          var handler = this._selectionChangeHandlers[handlerIdx];
          var obj = handler[0];
          var methodName = handler[1];
          obj[methodName]();
        }
      }
      if (this._postEventOnSelectionChange) {
        var eventFieldId = this.id + "::selectionEvent";
        var eventField = document.getElementById(eventFieldId);
        eventField.value = this._postEventOnSelectionChange;
        window._submittingTable = this;
        setTimeout(function() {
          q__submitEnclosingForm(window._submittingTable);
        }, 1);
      }
    }
  }

  table._selectAllItems = function() {
    q__assert(this._multipleSelectionAllowed, "table._selectAllItems: multiple selection is not allowed for table: " + this.id);

    if (this._noDataRows)
      return;
    if (this._selectableItems == "rows") {
      var rows = this._getBodyRows();
      var allItems = new Array();
      for (var i = 0, count = rows.length; i < count; i++)
        allItems[i] = i;
      this._setSelectedItems(allItems);
    }
  }

  table._unselectAllItems = function() {
    this._setSelectedItems([]);
  }

  table._isItemSelected = function(item) {
    var result = q__findValueInArray(item, selectedItems) != -1;
    return result;
  }

  table._toggleItemSelected = function(itemIndex) {
    if (itemIndex == -1) {
      q__logError("_toggleItemSelected: itemIndex == -1");
      return;
    }
    var selectedIndexes = this._selectedItems;
    var newArray = new Array();
    for (var i = 0, count = selectedIndexes.length; i < count; i++) {
      var idx = selectedIndexes[i];
      if (idx != itemIndex)
        newArray.push(idx);
    }
    if (newArray.length == selectedIndexes.length)
      newArray.push(itemIndex);
    this._setSelectedItems(newArray);
  }

  // run initialization code
  if (selectableItems == "rows") {
    var rows = table._getBodyRows();
    for (var i = 0, count = rows.length; i < count; i++) {
      var row = rows[i];
      q__initRowForSelection(row);
    }
  }

  table._setSelectedItems(selectedItems);
  table._setSelectionFieldValue("");

  if (selectionChangeHandler) {
    eval('table.onchange = function(event) {if (!event._qk_event)return;' + selectionChangeHandler + '}');
    // checking _qk_event is needed if this is a bubbled event from some child
    table._fireOnSelectionChange = function(e) {
      q__sendEvent(table, "change");
    }
    q__addTableSelectionChangeHandler(table, [table, "_fireOnSelectionChange"]);
  }
  table._postEventOnSelectionChange = postEventOnSelectionChange;

  table._addRowInsertionCallback(function(table, insertedAfterIndex, insertedRows) {
    var insertedRowCount = insertedRows.length;
    for (var i = 0; i < insertedRowCount; i++) {
      var insertedRow = insertedRows[i];
      q__initRowForSelection(insertedRow);
    }
    var selectedItems = table._getSelectedItems();
    if (table._selectableItems != "rows")
      throw "Not supported selectable item type: " + table._selectableItems;

    var selectedItemCount = selectedItems.length;
    if (selectedItemCount > 0) {
      var newSelectedItems = new Array();
      var selectionChanged = false;
      for (var i = 0; i < selectedItemCount; i++) {
        var rowIndex = selectedItems[i];
        if (rowIndex > insertedAfterIndex) {
          rowIndex += insertedRowCount;
          selectionChanged = true;
        }
        newSelectedItems.push(rowIndex);
      }
      /*if (selectionChanged) */
    {
      table._blockSelectionChangeNotifications = true;
      table._setSelectedItems(newSelectedItems, true);
      table._blockSelectionChangeNotifications = false;
    }
    }
  });
}

function q__initRowForSelection(row) {
  var table = row._table;
  if (table._selectionEnabled) {
    if (row._originalClickHandler)
      q__logError("q__initTableSelection: row click handler already initialized");
    row._originalClickHandler = row.onclick;
    row.onclick = q__tableRow_handleSelectionOnClick;
  }
  var cells = row._cells;
  var colIndex = 0;
  for (var cellIndex = 0, cellCount = cells.length; cellIndex < cellCount; cellIndex++) {
    var cell = cells[cellIndex];
    var cellSpan = q__getCellColSpan(cell);
    var colIndex = cell._colIndex;
    if ((colIndex != undefined) && (q__findValueInArray(colIndex, table._selectionColumnIndexes) != -1))
      q__initSelectionCell(cell);
    colIndex += cellSpan;
  }
}

function q__addTableSelectionChangeHandler(table, handler) {
  q__assert(handler, "q__addTableSelectionChangeHandler: handler must be specified. table.id = " + table.id);
  var handlers = table._selectionChangeHandlers;
  if (!handlers) {
    handlers = new Array();
    table._selectionChangeHandlers = handlers;
  }
  handlers.push(handler);
}

function q__initSelectionCell(cell) {
  var checkBoxAsArray = q__getChildNodesWithNames(cell, ["input"]);
//  q__assert(checkBoxAsArray.length == 1); // can be more than one because of service fields being rendered into the last table cell
  if (!checkBoxAsArray || checkBoxAsArray.length == 0)
    return;
  var checkBox = checkBoxAsArray[0];
  if (!checkBox)
    return;
  checkBox._cell = cell;
  var row = cell._row;
  var table = row._table;
  checkBox.checked = false;
  // fix for Mozilla's issue: reloading a page retains previous values for inputs regardless of their values received from server
  checkBox.disabled = !table._selectionEnabled;
  cell._selectionCheckBox = checkBox;
  cell.onclick = function(evt) {
    cell._handlingClick = true;
    try {
      var cellRow = this._row;
      var cellTable = cellRow._table;
      if (!cellTable._selectionEnabled)
        return;
      if (cellTable._multipleSelectionAllowed) {
        this._selectionCheckBox.checked = !this._selectionCheckBox.checked;
        this._selectionCheckBox.onclick(evt);
      } else {
        if (!this._selectionCheckBox.checked) {
          this._selectionCheckBox.checked = true;
        }
        this._selectionCheckBox.onclick(evt);
      }
    } finally {
      cell._handlingClick = false;
    }
  }
  cell.ondblclick = q__repeatClickOnDblclick;

  checkBox.onclick = function(evt) {
    if (evt)
      event = evt;
    var checkBoxCell = this._cell;
    if (!checkBoxCell._handlingClick)
      return;
    var checkBoxRow = checkBoxCell._row;
    var checkBoxTable = checkBoxRow._table;
    if (!checkBoxTable._selectionEnabled)
      return;
    if (checkBoxTable._selectableItems != "rows")
      return;
    if (!checkBoxTable._multipleSelectionAllowed) {
      if (this.checked)
        checkBoxTable._setSelectedItems([checkBoxRow._index]);
      else
        checkBoxTable._setSelectedItems([]);
      event.cancelBubble = true;
    } else {
      var selectedItems = checkBoxTable._getSelectedItems();
      var itemSelected = checkBoxTable._isItemSelected(row._index)
      checkBoxTable._toggleItemSelected(row._index);
      event.cancelBubble = true;
    }
  }
  checkBox.ondblclick = function(evt) {
    if (q__isExplorer())
      this.click();
    event.cancelBubble = true;
  }

  var cellRow = cell._row;
  if (!cellRow._selectionCheckBoxes)
    cellRow._selectionCheckBoxes = new Array();
  cellRow._selectionCheckBoxes.push(checkBox);
}

function q__tableRow_handleSelectionOnClick(evt) {
  if (this._originalClickHandler)
    this._originalClickHandler(evt);

  if (evt)
    event = evt;

  var table = this._table;
  if (!table._selectionMouseSupport)
    return;
  if (table._selectableItems == "rows") {
    table._baseRowIndex = null;
    table._baseSelectedRowIndexes = null;
    table._rangeEndRowIndex = null;
    if (!table._multipleSelectionAllowed) {
      table._setSelectedItems([this._index]);
    } else {
      if (event.ctrlKey || event.metaKey) {
        table._toggleItemSelected(this._index);
        var newSelectedRowIndexes = table.__getSelectedRowIndexes();
        table._baseRowIndex = (q__findValueInArray(this._index, newSelectedRowIndexes) != -1) ? this._index : null;
        table._baseSelectedRowIndexes = newSelectedRowIndexes;
        table._rangeEndRowIndex = null;
      } else
        table._setSelectedItems([this._index]);
    }
  }
}

function q__formatSelectedItems(selectableItems, selectedItemIndexes) {
  if (selectableItems == "rows" || selectableItems == "columns") {
    var result = "[";
    for (var i = 0; i < selectedItemIndexes.length; i++) {
      var itemIndex = selectedItemIndexes[i];
      if (result.length > 1)
        result += ",";
      result += itemIndex;
    }
    result += "]";
    return result;
  }
  q__logError("q__formatSelectedItems: unknown selectableItems: " + selectableItems);
}

function q__table_selectItem(itemIndex) {
  q__assert(itemIndex, "q__table_selectItem: itemIndex should be specified");
  if (this._selectableItems == "rows") {
    if (itemIndex == -1)
      return;
    var rows = this._getBodyRows();
    if (itemIndex < 0 || itemIndex >= rows.length)
      throw "Row index out of range: " + itemIndex;
    var row = rows[itemIndex];
    row._selected = true;
    row._updateStyle();
    q__setSelectionCheckboxesSelected(row, true);
  }
}

function q__table_unselectItem(itemIndex) {
  q__assert(itemIndex, "q__table_unselectItem: itemIndex should be specified");
  if (this._selectableItems == "rows") {
    if (itemIndex == -1)
      return;
    var rows = this._getBodyRows();
    var row = rows[itemIndex];

    row._selected = false;
    row._updateStyle();
    q__setSelectionCheckboxesSelected(row, false);
  }
}

function q__setSelectionCheckboxesSelected(row, selected) {
  if (row._selectionCheckBoxes) {
    for (var i = 0, count = row._selectionCheckBoxes.length; i < count; i++) {
      var checkbox = row._selectionCheckBoxes[i];
      checkbox.checked = selected;
    }
  }
}

function q__initCheckboxColHeader(headerId, colId) {
  var header = document.getElementById(headerId);
  var table = q__findParentNode(header, "TABLE");
  if (!table)
    throw "SelectAllCheckbox must be placed in a header of <q:dataTable> component. clientId = " + headerId;
  header._table = table;
  header._columnObjectId = colId;
  header.style.cursor = "default";

  if (!table._checkBoxColumnHeaders)
    table._checkBoxColumnHeaders = new Array();
  if (!table._checkBoxColumnHeaders[colId])
    table._checkBoxColumnHeaders[colId] = new Array();
  var colHeadersArray = table._checkBoxColumnHeaders[colId];
  colHeadersArray.push(header);

  header._updateFromCheckboxes = function(tableColumn) {
    var cells = tableColumn.body ? tableColumn.body._cells : [];
    var allChecked = true;
    var atLeastOneCheckboxFound = false;
    for (var i = 0, count = cells.length; i < count; i++) {
      var cell = cells[i];
      if (!cell)
        continue;
      var checkBox = cell._checkBox;
      if (!checkBox)
        continue;
      atLeastOneCheckboxFound = true;
      if (!checkBox.checked) {
        allChecked = false;
        break;
      }
    }
    this.checked = atLeastOneCheckboxFound ? allChecked : false;
  };

  header.onclick = function(e) {
    var cell = q__findAnyParentNode(this, ["td", "th"]);
    var col = cell._column;
    q__setAllCheckboxes(col, this.checked);
    var columnObj = document.getElementById(this._columnObjectId);
    columnObj._updateHeaderCheckBoxes();
    col._updateSubmissionField();
    var evt = q__getEvent(e);
    evt.cancelBubble = true;
  }
  header.ondblclick = function(e) {
    if (q__isExplorer())
      this.click();
    var evt = q__getEvent(e);
    evt.cancelBubble = true;
  }
}

function q__setAllCheckboxes(col, checked) {
  var cells = col.body ? col.body._cells : [];
  for (var i = 0, count = cells.length; i < count; i++) {
    var cell = cells[i];
    if (cell && cell._checkBox) // to account for the "no data" row
      cell._checkBox.checked = checked;
  }
}

function q__initSelectionHeader(headerId) {
  var header = document.getElementById(headerId);
  var table = q__findParentNode(header, "TABLE");
  if (!table)
    throw "SelectAllCheckbox must be placed in a header of <q:dataTable> component. clientId = " + headerId;
  header._table = table;
  header.style.cursor = "default";
  header._updateStateFromTable = function() {
    var headerTable = this._table;
    var selectedItems = headerTable._getSelectedItems();
    var bodyRows = headerTable._getBodyRows();
    if (selectedItems.length == 0) {
      this.checked = false;
    } else if (selectedItems.length == bodyRows.length) {
      this.checked = true;
    } else {
      this.checked = false;
    }
  };
  q__addTableSelectionChangeHandler(table, [header, "_updateStateFromTable"]);
  header.onclick = function(e) {
    if (this.disabled) {
      this.disabled = false;
      this.checked = true;
      this._table._selectAllItems();
    } else {
      if (this.checked)
        this._table._selectAllItems();
      else
        this._table._unselectAllItems();
    }
    var evt = q__getEvent(e);
    evt.cancelBubble = true;
  }
  header.ondblclick = function(e) {
    if (q__isExplorer())
      this.click();
    var evt = q__getEvent(e);
    evt.cancelBubble = true;
  }
}

// -------------------------- CHECKBOX COLUMN SUPPORT

function q__setCheckboxColIndexes(colId, checkedRowIndexes) {
  var columnObj = document.getElementById(colId);
  columnObj._setCheckedIndexes(checkedRowIndexes);
}

function q__initCheckboxCol(tableId, colIndex, colId, checkedRowIndexes) {
  var table = document.getElementById(tableId);
  var tableColumn = table._columns[colIndex];
  var columnObj = document.getElementById(colId);
  tableColumn._submissionField = columnObj;
  columnObj._tableColumn = tableColumn;

  columnObj._setCheckedIndexes = function(checkedIndexes) {
    var bodyCells = tableColumn.body ? tableColumn.body._cells : [];
    for (var i = 0, count = bodyCells.length; i < count; i++) {
      var cell = bodyCells[i];
      if (!cell)
        continue;
      cell._column = tableColumn;
      q__initCheckboxCell(cell, columnObj);
      if (cell._checkBox) {
        var shouldBeChecked = q__findValueInArray(i, checkedIndexes) != -1;
        cell._checkBox.checked = shouldBeChecked;
      }
    }
    tableColumn._updateSubmissionField();
  }

  tableColumn._updateSubmissionField = function() {
    var bodyCells = tableColumn.body ? tableColumn.body._cells : [];
    var field = this._submissionField;
    var selectedRows = "";
    for (var i = 0, count = bodyCells.length; i < count; i++) {
      var cell = bodyCells[i];
      if (!cell)
        continue;
      if (cell._checkBox && cell._checkBox.checked) {
        if (selectedRows.length > 0)
          selectedRows += ",";
        selectedRows += i;
      }
    }
    this._submissionField.value = selectedRows;
  }

  columnObj._setCheckedIndexes(checkedRowIndexes);

  if (table._checkBoxColumnHeaders) {
    var columnHeadersArray = table._checkBoxColumnHeaders[colId];
    columnObj._headers = columnHeadersArray;
  }
  columnObj._updateHeaderCheckBoxes = function() {
    if (!this._headers)
      return;
    var tableCol = this._tableColumn;
    for (var i = 0, count = this._headers.length; i < count; i++) {
      var header = this._headers[i];
      header._updateFromCheckboxes(tableCol);
    }
  }

  columnObj._updateHeaderCheckBoxes();
}

function q__initCheckboxCell(cell, colField) {
  if (cell._checkBoxCellInitialized)
    return;
  cell._checkBoxCellInitialized = true;
  var checkBoxAsArray = q__getChildNodesWithNames(cell, ["input"]);
  var checkBox = checkBoxAsArray[0];
  if (!checkBox)
    return;
  checkBox._cell = cell;
  cell._checkBox = checkBox;
  cell._columnObj = colField;
  cell.onclick = function(evt) {
    if (evt)
      event = evt;
    if (event._checkBoxClickProcessed) {
      event.cancelBubble = true;
      return;
    }
    var row = this._row;
    var table = row._table;
    this._checkBox.checked = !this._checkBox.checked;
    this._processCheckboxChange();
    event.cancelBubble = true;
    return false;
  }
  cell.ondblclick = q__repeatClickOnDblclick;

  checkBox.onclick = function(evt) {
    if (evt)
      event = evt;
    var checkBoxCell = this._cell;
    checkBoxCell._processCheckboxChange();
    event._checkBoxClickProcessed = true;
    event.cancelBubble = true;
  }
  checkBox.ondblclick = function(e) {
    if (q__isExplorer())
      this.click();
    event.cancelBubble = true;
  }

  cell._processCheckboxChange = function() {
    var columnObj = this._columnObj;
    columnObj._updateHeaderCheckBoxes();
    cell._column._updateSubmissionField();
  }
  return true;
}

// -------------------------- TABLE SORTING SUPPORT

function q__initTableSorting(tableId, columnSortableFlags, sortedColIndex, sortableHeaderClass, sortableHeaderRolloverClass,
                             sortedColClass, sortedColHeaderClass, sortedColBodyClass, sortedColFooterClass,
                             sortingImagesToPreload) {
  var table = document.getElementById(tableId);
  table._sortedColIndex = sortedColIndex;
  table._sortedColClass = sortedColClass;
  table._sortedColBodyClass = sortedColBodyClass;
  q__assert(table, "Couldn't find table by id: " + tableId);

  q__preloadImages(sortingImagesToPreload);

  for (var i = 0, count = table._columns.length; i < count; i++) {
    var columnSortable = columnSortableFlags[i];
    if (!columnSortable)
      continue;
    var column = table._columns[i]
    var colHeader = column.header ? column.header._cell : null;
    if (!colHeader)
      continue;

    q__setElementStyleMappings(colHeader, {_sortableHeaderClass: sortableHeaderClass});
    q__setCellProperty(colHeader, "_table", table);
    q__setCellProperty(colHeader, "_index", i);
    var clickHandler = function() {
      if (this._prevOnclick)
        this._prevOnclick();
      var focusField = document.getElementById(table.id + "::focused")
      if (focusField)
        focusField.value = true; // set true explicitly before it gets auto-set when the click bubbles up (JSFC-801)
      q__toggleColumnSorting(this._table, this._index);
    }

    colHeader._prevOnclick = colHeader.onclick;
    colHeader.onclick = clickHandler;

    table._sortableHeaderRolloverClass = sortableHeaderRolloverClass;
    colHeader._headerMouseOver = function() {
      q__setElementStyleMappings(this, {_sortedHeaderRolloverClass: table._sortableHeaderRolloverClass});
    }
    colHeader._headerMouseOut = function() {
      q__setElementStyleMappings(this, {_sortedHeaderRolloverClass: null});
    }
    q__addEventHandlerSimple(colHeader, "mouseover", "_headerMouseOver", colHeader);
    q__addEventHandlerSimple(colHeader, "mouseout", "_headerMouseOut", colHeader);
  }

  if (sortedColIndex != -1) {
    var column = table._columns[table._sortedColIndex];
    // Applying style to cells is needed for sorted column styles to have priority over
    // even/odd row styles - for backward compatibility with QK versions earlier than 1.2.2 (JSFC-2884)
    column._forceUsingCellStyles = true;

    var headerCell = (column.header) ? column.header._cell : null;
    var startIdx = table._commonHeaderExists ? 1 : 0;
    if (headerCell)
      q__setCellStyleMappings(headerCell, {
        _sortedColClass: (table._forceUsingCellStyles || column._useCellStyles) ? sortedColClass : null,
        _sortedColHeaderClass: sortedColHeaderClass});

    q__setElementStyleMappings(column, {_sortedColClass: table._sortedColClass});
    q__setElementStyleMappings(column.body, {_sortedColBodyClass: table._sortedColBodyClass});
    column._updateStyle();

    var footerCell = column.footer ? column.footer._cell : null;
    if (footerCell)
      q__setCellStyleMappings(footerCell, {
        _sortedColClass: (table._forceUsingCellStyles || column._useCellStyles) ? sortedColClass : null,
        _sortedColFooterClass: sortedColFooterClass});
  }

  table._sortingEnabled = true;
}

function q__toggleColumnSorting(table, columnIndex) {
  var sortingFieldId = table.id + "::sorting";
  var sortingField = document.getElementById(sortingFieldId);
  sortingField.value = "" + columnIndex;
  q__submitTableInternal(table);
}

function q__submitTableWithField(tableId, focusedField, additionalParams) {
  var focusedFieldId = focusedField ? focusedField.id : null;
  var table = document.getElementById(tableId);
  var focusFilterField = function() {
    var fieldId = focusedFieldId;
    if (!fieldId)
      return;
    var field = document.getElementById(fieldId);
    if (!field)
      return;
    if (field.focus)
      try {
        field.focus();
      } catch(e) {
        // ignore failed focus attempts
      }
  }
  q__submitTableInternal(table, function() {
    setTimeout(focusFilterField, 1);
  }, additionalParams);
}

function q__performPagerAction(tableId, field, paramName, paramValue) {
  var table = document.getElementById(tableId);
  q__submitTableWithField(tableId, field, [[paramName, paramValue]]);
}

function q__submitTableInternal(table, completionCallback, additionalParams) {
  var useAjax = table._useAjax;
  if (!useAjax) {
    if (additionalParams)
      for (var i = 0, count = additionalParams.length; i < count; i++) {
        var paramEntry = additionalParams[i];
        q__addHiddenField(table, paramEntry[0], paramEntry[1]);
      }
    q__submitEnclosingForm(table);
  } else {
    q__sendAjaxRequestIfNoFormSubmission([table.id], null, null, completionCallback, null, additionalParams);
  }
}

function q__filterFieldKeyPressHandler(tableId, filterField, event) {
  if (event.keyCode == 13) {
    // filter only in cases when onchange is not fired by pressing Enter to avoid double filter requests
    var dropDownField = filterField.nodeName.toUpperCase() != "INPUT";
    var onchangeFired = dropDownField;
    if (!onchangeFired)
      q__filterDataTable(tableId, filterField);

    event.cancelBubble = true;
    return false;
  }
  var inFieldNavigation = (event.keyCode >= 35 && event.keyCode <= 40);
  if (inFieldNavigation)
    event.cancelBubble = true;
  return true;
}

function q__filterDataTable(tableId, filterField) {
  // setTimeout in the following script is needed to avoid page blinking when using combo-box filter in IE (JSFC-2263)
  setTimeout(function() {
    q__submitDropDownFilter(tableId, filterField);
  }, 1);
}

function q__submitDropDownFilter(tableId, filter) {
  var table = document.getElementById(tableId);
  if (table._useAjax)
    q__submitTableWithField(tableId, filter);
  else
    q__submitEnclosingForm(table);
}


function q__showTableFilter(tableId, filterId) {
  var table = document.getElementById(tableId);
  var f = document.getElementById(filterId);

  if (!table._filtersToHide)
    table._filtersToHide = new Array();
  table._filtersToHide.push(f);
}


// -------------------------- COLUMN RESIZING SUPPORT

function q__initTableColumnResizing(tableId, retainTableWidth, minColWidth, resizeHandleWidth, columnParams) {
  q__addLoadEvent(function() {
    if (minColWidth == null)
      minColWidth = 10;

    if (resizeHandleWidth == null)
      resizeHandleWidth = 9;
    else
      resizeHandleWidth = q__calculateNumericCSSValue(resizeHandleWidth);
    if (resizeHandleWidth < 1)
      resizeHandleWidth = 1;

    var table = document.getElementById(tableId);
    var tableBordersCollapsed = q__calculateStyleProperty(table, "border-collapse") == "collapse";
    var tableCellPadding = q__calculateNumericCSSValue(table.cellPadding);
    var colWidthsFieldId = table.id + "::colWidths";
    var colWidthsField = q__addHiddenField(table, colWidthsFieldId);

    function recalculateTableWidth(colWidths) {
      var totalWidth = 0;
      for (var i = 0, count = table._columns.length; i < count; i++) {
        var column = table._columns[i];
        var columnWidth = colWidths ? colWidths[i] : column.getWidth();
        totalWidth += columnWidth;
      }

      var borderLeft = q__calculateNumericStyleProperty(table, "border-left-width", true);
      var borderRight = q__calculateNumericStyleProperty(table, "border-right-width", true);
      if (tableBordersCollapsed) {
        if (q__isOpera() || q__isSafari()) {
          borderLeft = Math.floor(borderLeft / 2);
          borderRight = Math.ceil(borderRight / 2);
        }
      }
      var width = totalWidth + borderLeft + borderRight;
      table.style.width = width + "px";
      return width;
    }

    function getColWidths() {
      var colWidths = []
      for (var i = 0, count = table._columns.length; i < count; i++) {
        var column = table._columns[i];
        var columnWidth = column.getWidth();
        colWidths[i] = columnWidth;
      }
      return colWidths;
    }

    var colCount = table._columns.length;

    for (var colIndex = 0; colIndex < colCount; colIndex++) {
      var col = table._columns[colIndex];
      if (col.header && col.header._cell)
        col.header._cell.style.overflow = "hidden";
      if (col.filter && col.filter._cell)
        col.filter._cell.style.overflow = "hidden";
      if (col.footer && col.footer._cell)
        col.footer._cell.style.overflow = "hidden";
      var bodyCells = col.body._cells;
      for (var j = 0, jCount = bodyCells.length; j < jCount; j++) {
        var cell = bodyCells[j];
        if (!cell || cell.colSpan > 1)
          continue;
        cell.style.overflow = "hidden";
      }

      var thisColumnParams = columnParams[colIndex];
      if (thisColumnParams) {
        col._resizeable = thisColumnParams.resizeable;
        col._minResizingWidth = thisColumnParams.minWidth;
      }
      if (col._resizeable == undefined)
        col._resizeable = true;
      if (col._minResizingWidth == undefined)
        col._minResizingWidth = minColWidth;
      col._minResizingWidth = q__calculateNumericCSSValue(col._minResizingWidth);
      if (col._minResizingWidth < 0)
        col._minResizingWidth = 0;
    }

    table._addCellInsertionCallback(function(cell, row, column) {
      cell.style.overflow = "hidden";
    });

    for (var colIndex = 0; colIndex < colCount; colIndex++) {
      var column = table._columns[colIndex];

      column.setWidth = function(width, additionalAttempts) {
        if (!q__isOpera())
          additionalAttempts = 0;
        if (additionalAttempts) {
          this.setWidth(width, 0);
          var actualWidth;
          // additional attempts are needed because at least Opera doesn't always set the specified width from
          // the first attempt
          for (var attempt = 0; attempt < additionalAttempts; attempt++) {
            actualWidth = this.getWidth();
            if (actualWidth == width)
              break;
            this.setWidth(width, 0);
          }
          if (actualWidth != width)
            this.setWidth(actualWidth, 0);
          return actualWidth;
        }

        function measureColRightBorder(col) {
          if (col.header && col.header._cell)
            return q__calculateNumericStyleProperty(col.header._cell, "border-right-width", true);
          if (col.filter && col.filter._cell)
            return q__calculateNumericStyleProperty(col.filter._cell, "border-right-width", true);
          if (!table._noDataRows && col.body._cells.length > 0)
            return q__calculateNumericStyleProperty(col.body._cells[0], "border-right-width", true);
          if (col.footer && col.footer._cell)
            return q__calculateNumericStyleProperty(col.footer._cell, "border-right-width", true);
          return 0;
        }

        var strictMode = q__isStrictMode();
        var explorer = q__isExplorer();
        var paddingLeft = q__isOpera() || (explorer && strictMode) ? tableCellPadding : 0;
        var paddingRight = q__isOpera() || (explorer && strictMode) ? tableCellPadding : 0;
        var colTagWidth = width - paddingLeft - paddingRight;
        if (explorer && strictMode)
          colTagWidth -= measureColRightBorder(this);
        this._colTag.style.width = colTagWidth + "px";
        if (strictMode && (explorer || q__isMozilla()))
          return width;
        function setCellWidth(cell, width) {
          if (!cell)
            return;
          var w = width;
          if (!q__isExplorer()) {
            var paddingLeft = q__calculateNumericStyleProperty(cell, "padding-left", true);
            var paddingRight = q__calculateNumericStyleProperty(cell, "padding-right", true);
            if (!paddingLeft) paddingLeft = tableCellPadding;
            if (!paddingRight) paddingRight = tableCellPadding;
            w -= paddingLeft + paddingRight;
            var borderRight = q__calculateNumericStyleProperty(cell, "border-right-width", true);
            w -= borderRight;
            if (!tableBordersCollapsed) {
              var borderLeft = q__calculateNumericStyleProperty(cell, "border-left-width", true);
              w -= borderLeft;
            }
          }
          cell.style.width = w + "px";
        }
        if (this.header)
          setCellWidth(this.header._cell, width);
        if (this.filter)
          setCellWidth(this.filter._cell, width);
        for (var i = 0, count = this.body._cells.length; i < count; i++) {
          var cell = this.body._cells[i];
          if (!cell || cell.colSpan > 1)
            continue;
          setCellWidth(cell, width);
        }
        if (this.footer)
          setCellWidth(this.footer._cell, width);
        return width;
      }
      column.getWidth = function() {
        if (this.header && this.header._cell)
          return this.header._cell.offsetWidth;
        if (this.filter && this.filter._cell)
          return this.filter._cell.offsetWidth;
        for (var i = 0, count = this.body._cells.length; i < count; i++) {
          var cell = this.body._cells[i];
          if (!cell || cell.colSpan > 1)
            continue;
          return cell.offsetWidth;
        }
        if (this.footer && this.footer._cell)
          return this.footer._cell.offsetWidth;
        return this._colTag.offsetWidth;
      }

      if (!column.header || !column.header._cell)
        continue;

      var widthCompensationColIndex = -1;
      if (retainTableWidth) {
        for (var i = table._columns.length - 1; i >= 0; i--) {
          var c = table._columns[i];
          if (c._resizeable) {
            widthCompensationColIndex = i;
            break;
          }
        }
      }
      if (widthCompensationColIndex != -1 && colIndex >= widthCompensationColIndex)
        continue;

      if (!column._resizeable)
        continue;
      var resizeHandle = document.createElement("div");
      resizeHandle.style.cursor = "e-resize"
      resizeHandle.style.position = "absolute";
      resizeHandle.style.border = "0px none transparent";//"1px solid black";

      if (q__isExplorer()) {
        // IE needs an explicit background because otherwise this absolute div will "leak" some events to the underlying
        // component (when a mouse is directly over any of table's gridline)
        resizeHandle.style.background = "silver";
        resizeHandle.style.filter = "alpha(opacity=0)"
      }

      column.header._cell.appendChild(resizeHandle);
      column._resizeHandle = resizeHandle;
      resizeHandle._column = column;
      resizeHandle.onmouseover = function() {
        if (this._draggingInProgress)
          return;
        if (!table.parentNode)
          this.parentNode.removeChild(this);
        else
          this._updatePos();
      }
      resizeHandle.onmousedown = function (e) {
        q__handleDragStart(e, this);
      }
      resizeHandle.onclick = function(e) {
        q__breakEvent(e);
      }
      resizeHandle.ondragstart = function() {
        table._columnResizingInProgress = true;
        var resizeDecorator = document.createElement("div");
        resizeDecorator.style.position = "absolute";
        resizeDecorator.style.borderLeft = "0px none transparent";//"1px solid gray";
        table.parentNode.appendChild(resizeDecorator);
        this._column._resizeDecorator = resizeDecorator;
        resizeDecorator._column = this._column;

        resizeDecorator._updatePos = function() {
          var headerCell = this._column.header._cell;
          var cellPos = q__getElementRectangle(headerCell);
          var tablePos = q__getElementPos(table);

          this.style.top = cellPos.getMinY() + "px";
          this.style.left = cellPos.getMaxX() + "px";
          this.style.width = "0px";//"1px";
          this.style.height = tablePos.top + table.offsetHeight - cellPos.getMinY() + "px";
        }
        resizeDecorator._updatePos();
        var headerCell = this._column.header._cell;
        var cellPos = q__getElementRectangle(headerCell);
        this._dragStartCellPos = cellPos;
      }
      resizeHandle.setLeft = function(left) {
        this.style.left = left + "px";
        var newColRightEdge = left + Math.floor(resizeHandleWidth / 2) + 1;
        var newColWidth = newColRightEdge - this._dragStartCellPos.getMinX();
        if (newColWidth < this._column._minResizingWidth)
          newColWidth = this._column._minResizingWidth;

        var colWidths = getColWidths();
        table.style.width = "auto";
        var thisAndNextColWidth;
        var nextCol;
        if (widthCompensationColIndex != -1) {
          var nextColWidth = colWidths[widthCompensationColIndex];
          var thisColWidth = colWidths[this._column._colIndex];
          thisAndNextColWidth = thisColWidth + nextColWidth;
          nextCol = table._columns[widthCompensationColIndex];
          var maxWidthForThisCol = thisAndNextColWidth - nextCol._minResizingWidth;
          if (newColWidth > maxWidthForThisCol)
            newColWidth = maxWidthForThisCol;
          var actualNextColWidth = nextCol.setWidth(thisAndNextColWidth - newColWidth, 3);
          colWidths[widthCompensationColIndex] = actualNextColWidth;
          newColWidth = thisAndNextColWidth - actualNextColWidth;
        }

        var actualColWidth = this._column.setWidth(newColWidth, 3);
        colWidths[this._column._colIndex] = actualColWidth;
        if (widthCompensationColIndex != -1) {
          if (actualColWidth != newColWidth) {
            colWidths[widthCompensationColIndex] = nextCol.setWidth(thisAndNextColWidth - actualColWidth, 3);
          }
        }

        if (!retainTableWidth)
          recalculateTableWidth(colWidths);
        else
          table.style.width = table._originalWidth + "px";
        this._column._resizeDecorator._updatePos();
        q__repaintAreaForOpera(table);
        table._fixFF3ColResizingIssue();
      }
      resizeHandle.setTop = function(top) {
        this.style.top = top + "px";
      }
      resizeHandle.ondragend = function() {
        table._columnResizingInProgress = false;
        this._column._resizeDecorator.parentNode.removeChild(this._column._resizeDecorator);
        table._updateResizeHandlePositions();

        var totalWidth = 0;
        var colWidths = [];
        for (var i = 0; i < colCount; i++) {
          var col = table._columns[i];
          var colWidth = col.getWidth();
          colWidths[i] = colWidth + "px";
          totalWidth += colWidth;
        }

        colWidthsField.value = (q__isOpera() ? table.style.width : totalWidth + "px") + ":" +
                               "[" + colWidths.join(",") + "]";
        if (table._focusable) {
          if (!table._focused)
            table.focus();
        }
      }
      resizeHandle._updatePos = function() {
        var parentColumn = null;
        for (var col = this._column; col._parentColumn; col = col._parentColumn) {
          var indexAmongSiblings = q__findValueInArray(col, col._parentColumn.subColumns);
          var lastColInGroup = indexAmongSiblings == col._parentColumn.subColumns.length - 1;
          if (!lastColInGroup)
            break;
          if (col._parentColumn.header && col._parentColumn.header._cell)
            parentColumn = col._parentColumn;
        }
        var bottomCell = this._column.filter ? this._column.filter._cell : this._column.header._cell;
        var bottomCellPos = q__getElementRectangle(bottomCell);
        var topCellPos = parentColumn ? q__getElementRectangle(parentColumn.header._cell) : q__getElementRectangle(this._column.header._cell);

        var minY = topCellPos.getMinY();
        this.style.top = minY + "px";
        this.style.left = bottomCellPos.getMaxX() - Math.floor(resizeHandleWidth / 2) - 1 + "px";
        this.style.width = resizeHandleWidth + "px";
        this.style.height = bottomCellPos.getMaxY() - minY + "px";
      }

      column._resizeHandle._updatePos();
    }

    var colWidths = getColWidths();
    table.style.tableLayout = "fixed";

    table.style.width = "auto";
    for (var i = 0, count = colWidths.length; i < count; i++) {
      var column = table._columns[i];
      column.setWidth(colWidths[i]);
    }
    table._originalWidth = recalculateTableWidth(colWidths);

    table._updateResizeHandlePositions = function() {
      for (var i = 0, count = table._columns.length; i < count; i++) {
        var column = table._columns[i];
        if (column._resizeHandle)
          column._resizeHandle._updatePos();
      }
    }

    q__addEventHandler(window, "resize", table._updateResizeHandlePositions);
    q__addEventHandler(table, "mouseover", function() {
      if (!table._columnResizingInProgress)
        table._updateResizeHandlePositions();
    });

    table._unloadHandlers.push(function() {
      q__removeEventHandler(window, "resize", table._updateResizeHandlePositions);
    })

    table._fixFF3ColResizingIssue = function() { // See JSFC-3720
      if (! (q__isMozillaFF3() && !q__isStrictMode()))
        return;
      if (table._deepestColumnHierarchyLevel > 1) {
        var prevWidth = table.style.width;
        table.style.width = "0";
        table.styleWidth = prevWidth;
      }
    }


  });

}

//AUTO GENERATED CODE

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