Table in JavaScript : Table Grid « GUI Components « JavaScript DHTML






Table in JavaScript

 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN">
<html>
<head>

<!-- JsTable.js -->
<script language=JavaScript>
/****
    JsTable v0.4.5 (20050725) written by AT Mulyana (atmulyana@yahoo.com)
  This libary is to create DHTML widget of Table on web page which is
  editable, cell navigatable and its content is submittable to web server.
  The target browser for this widget are :
    - Netscape 7.1+ / Mozilla 1.4+ or other Gecko browser whose the same
      or the newer version of Gecko engine.
    - Internet Explorer 6+
    - Opera 7.5+
    I hope you get the best version so that this script can run correctly
  You should get the documentaion to see how to use this script
    
  Copyright (C) 2005 AT Mulyana (atmulyana@yahoo.com)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Send the bug report to 'atmulyana@yahoo.com' (Tell the exact version of your
browser and under which Operating System you run the script)

Currently, this script is personal work of myself, not involving other people or
company including the company where I work.
*****/

/****** Checks the browser family ******/
function JsTableCheckBrowser()
{
  var navFamily = navigator.userAgent.toLowerCase();
  if (navFamily.indexOf('opera')!=-1) { 
    var ver = parseFloat( navFamily.substring(navFamily.indexOf('opera')+6,
      navFamily.length) );
    if (ver >= 7.5) navFamily = 'Opera7';
    else navFamily = '';
  } else if (navFamily.indexOf('msie')!=-1) {
    var ver = parseFloat( navFamily.substring(navFamily.indexOf('msie')+5,
      navFamily.length) );
    if (ver >= 6.0) navFamily = 'IE6';
    else navFamily = '';
  } else if (navFamily.indexOf('gecko')!=-1) {
    var ver = parseFloat( navFamily.substring(navFamily.indexOf('gecko')+6,
      navFamily.length) );
    if (ver >= 20030624) navFamily = 'Gecko'; //Good Gecko; Netscape 7.1+/Mozilla 1.4+
    else navFamily = '';
  } else {
    navFamily = '';
  }
  
  return navFamily;
}
var JsTableNavFamily = JsTableCheckBrowser();


/********** Adds additional useful methods to the standart objects **********/
String.prototype.strip = function() { //Removes leading and trailing white space(s)
   return this.toString().replace(/^\s+/,'').replace(/\s+$/,'');
};

String.prototype.isNumeric = function() { //Does the string evaluate to a numeric value?
  var re = /-?\s*\d*\.?\d+/;
  var val = this.toString();
  val = val.strip();
  var valid = true;
  var found = re.exec(val);
  if (!found) valid=false;
  else valid = (found[0].length==val.length);
  return valid;
};

//Can the string be used as variabel name?
String.prototype.isQualifiedVarName = function() {
  var re = /[a-zA-Z_]\w*/;
  var val = this.toString();
  var valid = true;
  var found = re.exec(val);
  if (!found) valid=false;
  else valid = (found[0].length==val.length);
  return valid;
};

//Does the string evaluate to a date value? (format: 'dd-mm-yyyy' or 'mm-dd-yyyy')
//If true then returns a normalized date value
String.prototype.isDate = function(sFormat) {
  var sFormat = (sFormat+'').toLowerCase();
   //Only 'd-m-y' or 'm-d-y' is accepted (It implies the paramater may
   //be omitted ==> 'd-m-y' will be used)
  if (sFormat!='m-d-y') sFormat = 'd-m-y';
  var idxDay = 0;
  var idxMonth = 1;
  if (sFormat=='m-d-y') {
    idxDay = 1;
    idxMonth = 0;
  }
  
  var sDate = this.toString().strip();
  var bValid = true;
  var arMonthlong = [0,31,28,31,30,31,30,31,31,30,31,30,31];
  var regDate = /(\d{1,2}\s*-\s*\d{1,2}\s*-\s*\d{4,4})/;
  
  var ar = regDate.exec(sDate);
  if (ar) {
    bValid = (sDate.length==ar[0].length);
    if (!bValid) return false;
  } else
    return false;
  
  sDate = sDate.replace(/\s+/g,''); //Removes all white spaces
  var arDate = sDate.split('-');
  var iYear = parseInt(arDate[2],10);
  var iMonth = parseInt(arDate[idxMonth],10);
  var iDay = parseInt(arDate[idxDay],10);
  bValid = (iMonth > 0) && (iMonth <= 12);
  if (bValid) {
    bValid = (iDay > 0);
    if (iMonth==2) {
      if (iYear%4) bValid = bValid && (iDay <= 28);
      else bValid = bValid && (iDay <= 29);
    } else {
      bValid = bValid && (iDay <= arMonthlong[iMonth]);
    }
  }
  
  if (bValid) {
    if (iDay < 10) iDay = '0' + iDay;
    if (iMonth < 10) iMonth = '0' + iMonth;
    iYear = '0000'.substring(0, 4 - (iYear+'').length) + iYear;
    if (sFormat=='m-d-y') return (iMonth+'-'+iDay+'-'+iYear);
    return (iDay+'-'+iMonth+'-'+iYear);
  }
  return bValid;
};

//Does the string evaluate to a date-time value? (format: 'dd-mm-yyyy hh:nn:ss'
//or 'mm-dd-yyyy hh:nn:ss')
//If true then returns a normalized date-time value
String.prototype.isDateTime = function(sFormat) {
  var sDt = this.toString().strip();
  var re = /((\d{1,2}\s*-\s*\d{1,2}\s*-\s*\d\d\d\d)\s+(\d{1,2}\s*:\s*\d{1,2}:\s*\d{1,2}))/;
  var ar = sDt.match(re);
  var bValid = true;
  if (ar) {
    bValid = (sDt.length==ar[0].length);
    if (!bValid) return bValid;
  } else
    return false;
  
  //Checks the date part
  var sDate = ar[2].isDate(sFormat);
  if (!sDate) return false;
  
  //Checks the time part
  ar[3] = ar[3].replace(/\s+/g,''); //Removes all white spaces
  var arTime = ar[3].split(':');
  var iHour = parseInt(arTime[0],10);
  var iMinute = parseInt(arTime[1],10);
  var iSecond = parseInt(arTime[2],10);
  bValid = (iHour >= 0) && (iHour < 24);
  if (bValid) bValid = (iMinute >= 0) && (iMinute < 60);
  if (bValid) bValid = (iSecond >= 0) && (iSecond < 60);
  
  if (bValid) {
    if (iHour < 10) iHour = '0' + iHour;
    if (iMinute < 10) iMinute = '0' + iMinute;
    if (iSecond < 10) iSecond = '0' + iSecond;
    return (sDate+' '+iHour+':'+iMinute+':'+iSecond);
  }
  return bValid;
};

//Gives denomination to the string if it evaluates to a numeric value
String.prototype.giveDenomination = function(denominator,fracDigits) {
  var pointDec;
  if (denominator!='.') { //Ignores denominator except ',' and '.'
    denominator = ',';
    var pointDec = '.';
  } else pointDec = ',';
  var sNumber = this.toString();
  if (!sNumber.isNumeric()) return sNumber;
  sNumber = sNumber.replace(/\s/g,'');
  var sSign = '';
  if (sNumber.charAt(0)=='-') {
    sSign = '-';
    sNumber = sNumber.substring(1,sNumber.length);
    if (parseFloat(sNumber)==0.0) sSign = '';
  }
  var sDec = '';
  var i = sNumber.indexOf('.');
  if (i!=-1) {
    sDec = sNumber.substring(i+1,sNumber.length); sDec = sDec.replace(/0+$/,'');
    sNumber = sNumber.substring(0,i);
    if (!sNumber) sNumber = '0';
  }
  if (fracDigits) {
    fracDigits = parseInt(fracDigits);
    fracDigits = (isNaN(fracDigits)? 0 : fracDigits);
    if (!sDec) sDec = '0';
    sDec = Math.round( parseFloat('0.'+sDec)* Math.pow(10,fracDigits) ) + '';
    while (sDec.length < fracDigits) sDec = '0' + sDec;
    if (sDec.length > fracDigits) {
      i = parseInt(sDec.substring(0,1));
      sDec = sDec.substring(1,sDec.length);
      sNumber = (parseInt(sNumber)+i) + '';
    }
  }
  var arNum = new Array();
  while (sNumber.length > 3) {
    arNum[arNum.length] = sNumber.substring(sNumber.length-3,sNumber.length);
    sNumber = sNumber.substring(0,sNumber.length-3);
  }
  if (sNumber.length > 0) arNum[arNum.length] = sNumber;
  arNum.reverse();
  if (sDec) sDec = pointDec + sDec;
  return (sSign+arNum.join(denominator)+sDec);
};

//Gives denomination to the string if it evaluates to a numeric value
String.prototype.removeDenomination = function(denominator,alterPointDecToPeriod,ignoreNumeric) {
  var pointDec = ',';
  if (denominator!='.') { //Ignores denominator except ',' and '.'
    denominator = ',';
    pointDec = '.';
  }
  var sNumber = this.toString();
  sNumber = sNumber.replace(new RegExp('\\'+denominator,'g'),'');
  var sNumber1 = sNumber.replace(new RegExp('\\'+pointDec,'g'),'.');
  if (sNumber1.isNumeric() || ignoreNumeric) {
    if (alterPointDecToPeriod) return sNumber1;
    else return sNumber;
  }
  return this.toString();
};

//To search if the 'elm' is inside the array;
//The 'elm' is not an object
Array.prototype.isThere = function(elm) {
  var s = this.join('|~&*&~|');
  return (s.indexOf(elm+'')!=-1);
};

//Returns the date value with format 'dd-mm-yyyy'
Date.prototype.ddmmyyyy = function() {
  day = this.getDate();
  month = this.getMonth()+1;
  year = this.getFullYear();
  if (day<10) day='0'+day;
  if (month<10) month='0'+month;
  return (day+'-'+month+'-'+year);
};

//Returns the date value with format 'mm-dd-yyyy'
Date.prototype.mmddyyyy = function() {
  day = this.getDate();
  month = this.getMonth()+1;
  year = this.getFullYear();
  if (day<10) day='0'+day;
  if (month<10) month='0'+month;
  return (month+'-'+day+'-'+year);
};

//Returns the date value with format 'yyyy-mm-dd'
Date.prototype.sqldate = function() {
  day = this.getDate();
  month = this.getMonth()+1;
  year = this.getFullYear();
  if (day<10) day='0'+day;
  if (month<10) month='0'+month;
  if (year<10) year='0'+year;
  return (year+'-'+month+'-'+day);
};

//Returns the time-stamp with format according to parameter 'format'
//Possible values (incasesensitive) for parameter format:
//  'd-m-y'    ==> 'dd-mm-yyyy hh:nn:ss'
//  'm-d-y'    ==> 'mm-dd-yyyy hh:nn:ss'
//  'sql'    ==> 'yyyy-mm-dd hh:nn:ss'
Date.prototype.timeStamp = function(format) {
  format = (format+'').toLowerCase();
   //Only 'd-m-y' or 'm-d-y' or 'sql' is accepted (It implies the paramater may
   //be omitted ==> 'd-m-y' will be used)
  if (format!='m-d-y' && format!='sql') format = 'd-m-y';
  
  hour = this.getHours();
  minute = this.getMinutes();
  second = this.getSeconds();
  if (hour<10) hour='0'+hour;
  if (minute<10) minute='0'+minute;
  if (second<10) second='0'+second;
  if (format=='m-d-y') return (this.mmddyyyy()+' '+hour+':'+minute+':'+second);
  if (format=='sql') return (this.sqldate()+' '+hour+':'+minute+':'+second);
  return (this.ddmmyyyy()+' '+hour+':'+minute+':'+second);
};

//Changes the date value according to parameter 'ddmmyyyy'
//'ddmmyyyy' is the date value with format 'dd-mm-yyyy'
Date.prototype.setFrom_ddmmyyyy = function(ddmmyyyy) {
  ddmmyyyy = ddmmyyyy+'';
  ddmmyyyy = ddmmyyyy.isDate();
  if (!ddmmyyyy) return;
  var dates = ddmmyyyy.split('-');
  this.setFullYear( parseInt(dates[2],10), parseInt(dates[1],10)-1, parseInt(dates[0],10) );
}

//Changes the date value according to parameter 'mmddyyyy'
//'mmddyyyy' is the date value with format 'mm-dd-yyyy'
Date.prototype.setFrom_mmddyyyy = function(mmddyyyy) {
  mmddyyyy = mmddyyyy+'';
  mmddyyyy = mmddyyyy.isDate('m-d-y');
  if (!mmddyyyy) return;
  var dates = mmddyyyy.split('-');
  this.setFullYear( parseInt(dates[2],10), parseInt(dates[0],10)-1, parseInt(dates[1],10) );
}

//Changes the date value according to parameter 'sqldate'
//'sqldate' is the date value with format 'yyyy-mm-dd'
Date.prototype.setFrom_sqldate = function(sqldate) {
  sqldate = sqldate+'';
  var dates = sqldate.split('-'); //No Checking
  this.setFullYear( parseInt(dates[0],10), parseInt(dates[1],10)-1, parseInt(dates[2],10) );
}


if (JsTableNavFamily) { /************ The browser fulfils the requirement ***************/

function JsTableRemoveAllText(oParent)
{
  var sText = '';
  var oChild, sNodeName;
  var idx = 0;
  while (idx < oParent.childNodes.length) {
    oChild = oParent.childNodes[idx];
    sNodeName = oChild.nodeName;
    if (sNodeName!='INPUT') oParent.removeChild(oChild);
    if (sNodeName=='#text') {
      sText += oChild.nodeValue;
    } else if (sNodeName=='BR') {
      sText += '\n';
    } else {
      idx++;
      continue;
    }
  }
  return sText;
}

/********** All about column models to set the colums behavior in the JsTable *******/
var JsTableCellEditorSelect = null;
function JsTableSetCellEditorSelect()
{
  if (!JsTableCellEditorSelect) return;
  JsTableCellEditorSelect.select();
  JsTableCellEditorSelect.readOnly = false;
  JsTableCellEditorSelect = null;
}
var JsTableColumnIndex = 0;
function JsTableColumnClass(columnName,inputName,width,align)
{
  this.columnName = columnName || String.fromCharCode(160);
  this.inputName = inputName || ('col'+JsTableColumnIndex);
  JsTableColumnIndex++;
  align = (align+'').toLowerCase();
  this.align = ( ('left|center|right'.indexOf(align)!=-1)? align : 'left');
  this.width = ( isNaN(parseInt(width))? 20 : parseInt(width) );
  
  this.getInputElm = function(oCell) {
    var arElms = oCell.getElementsByTagName('INPUT');
    for (var i=0; i<arElms.length; i++) {
      if (arElms[i].getAttribute('name') == this.inputName) return arElms[i];
    }
  };
  
  this.getCellEditor = function(oCell,oJsTable) {
    return null;
  };
  this.isValid = function(val,oJsTable) {
    return true;
  };
  this.fixedValue = function(val,oJsTable) {
    if (typeof val == 'undefined') return '';
    return val.toString();
  };
  this.getValue = function(oCell,oJsTable) {
    return this.getInputElm(oCell).value;
  };
  this._getShownValue = function(val,oJsTable) {
    return val;
  };
  this.getShownValue = function(oCell,oJsTable) {
    return this._getShownValue(this.getInputElm(oCell).value,oJsTable);
  };
  this.getShownValueOnEdit = function(oCell,oJsTable) {
    return this.getShownValue(oCell,oJsTable);
  };
  this._setShownValue = function(val,oCell,oJsTable) {
    JsTableRemoveAllText(oCell);
    var arText = (val+'').split('\n');
    var oText, oBR;
    for (var i=0; i<arText.length; i++) {
      oText = document.createTextNode(arText[i]);
      oCell.appendChild(oText);
      if (i<arText.length-1) {
        oBR = document.createElement('BR');
        oCell.appendChild(oBR);
      }
    }
  };
  this.setValue = function(val,oCell,oJsTable,oCellEditor) {
    val = this.fixedValue(val,oJsTable);
    this.getInputElm(oCell).value = val;
    if (oCellEditor) oCellEditor.value = this.getShownValue(oCell,oJsTable);
    else this._setShownValue( this._getShownValue(val,oJsTable), oCell, oJsTable);
  };
  this._setCellEditorSelect = function(oCellEditor) {
    JsTableCellEditorSelect = oCellEditor;
    setTimeout('JsTableSetCellEditorSelect()',0);
  };
  this.setEditorOnEdit = function(oCellEditor,oCell,oJsTable) {
    var val = this.getShownValueOnEdit(oCell,oJsTable);
    oCellEditor.value = val;
    oCellEditor.readOnly = false;
    this._setCellEditorSelect(oCellEditor);
  };
  this.setEditorOnCommit = function(oCellEditor,oCell,oJsTable) {
    var val = oCellEditor.value;
    oCellEditor.readOnly = true;
    oCellEditor.select(); //IE6, to hide text cursor
    if (JsTableNavFamily=='IE6') oJsTable._onclickIE6 = [oJsTable.row, oJsTable.col];
    oCellEditor.value = ""; //clear highlight selected text if any
    if (!this.isValid(val,oJsTable)) {
      if (oJsTable.tableModel.oldValIfInvalid) val = this.getShownValue(oCell,oJsTable);
    }
    this.setValue(val,oCell,oJsTable,oCellEditor);
  };
  this.setEditorOnArrive = function(oCellEditor,oCell,oJsTable) {
    var sText = JsTableRemoveAllText(oCell);
    //var oSbl = (oCell.nextSibling ? oCell.nextSibling : oCell.previousSibling);
    if (JsTableNavFamily=='Opera7') {
      oCellEditor.style.height = (oCell.offsetHeight-2) + 'px';
    } else if (document.compatMode=='CSS1Compat') {
      oCellEditor.style.width = (oCell.offsetWidth-3) + 'px';
      oCellEditor.style.height = (oCell.offsetHeight-4) + 'px';
    } else {
      oCellEditor.style.width = oCell.offsetWidth + 'px';
      oCellEditor.style.height = (oCell.offsetHeight-2) + 'px';
    }
    oCell.style.padding = '0px';
    oCellEditor.value = sText;
    oCellEditor.style.textAlign = this.align;
    oCellEditor = oCell.appendChild(oCellEditor);
  };
  this.setEditorOnRearrive = function(oCellEditor,oCell,oJsTable) {
     //Invoked when the currently active cell is the same as before
  };
  this.setEditorOnLeave = function(oCellEditor,oCell,oJsTable) {
    oCell.removeChild(oCellEditor);
    oCell.style.padding = '';
    if (oCell.style.removeProperty) {
      oCell.style.removeProperty('padding');
      //oCell.style.removeProperty('height');
    } else {
      oCell.style.padding = '';
      //oCell.style.height = '';
    }
    this._setShownValue(this.getShownValue(oCell,oJsTable),oCell,oJsTable);
  };
  this.containsEditor = function(oCell,oCellEditor) {
    //It returns integer because some consideration (See method oJsTable.setFocus
    //and JsTableColumnBoolean.containsEditor)
    if (oCell.contains(oCellEditor)) return -1;
    return 0;
  };
  this.addCell = function(oRow,oJsTable,val,primaryKey) {
    val = this.fixedValue(val);
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oInput = document.createElement("INPUT");
    oInput.type = 'hidden';
    oInput.setAttribute('name',this.inputName);
    oInput.setAttribute('id',this.inputName); //IE6
    oInput.value = val;
    oCell.appendChild(oInput);
    this._setShownValue( this._getShownValue(val,oJsTable), oCell,oJsTable);
    return oCell;
  };
}

function JsTableColumnString(columnName,inputName,width,align)
{
  this.base = JsTableColumnClass;
  this.base(columnName,inputName,width,align);
  
  this._addCell_ = this.addCell;
  this.addCell = function(oRow,oJsTable,val,primaryKey) {
    var oCell = this._addCell_(oRow,oJsTable,val,primaryKey);
    oCell.onkeypress = JsTableOnStringKeyPress;
    return oCell;
  }
}

function JsTableColumnNumber(columnName,inputName,width,align,denomination,fracDigits)
{
  this.base = JsTableColumnClass;
  align = align || 'right';
  this.base(columnName,inputName,width,align);
  this.denomination = (denomination?true:false);
  this.fracDigits = parseInt(fracDigits); if (isNaN(fracDigits)) fracDigits = 0;
  
  this.isValid = function (val,oJsTable) {
    val = (val+'').removeDenomination(oJsTable.tableModel.numberDenominator,true);
    return val.isNumeric();
  };
  this.fixedValue = function (val,oJsTable) {
    var denominator = oJsTable.tableModel.numberDenominator;
    if (typeof val != 'number') val = (val+'').removeDenomination(denominator,true,true);
    val = parseFloat(val+'');
    if (isNaN(val)) return 0;
    val = (val+'').giveDenomination(denominator,this.fracDigits);
    val = parseFloat(val.removeDenomination(denominator,true));
    return val;
  };
  this.getValue = function(oCell,oJsTable) {
    return parseFloat(this.getInputElm(oCell).value);
  };
  this._getShownValue = function(val,oJsTable) {
    val = val+'';
    var denominator = oJsTable.tableModel.numberDenominator;
    val = val.giveDenomination(denominator,this.fracDigits);
    if (this.denomination) return val;
    return val.replace(new RegExp('\\'+denominator,'g'),'');
  };
  this.getShownValueOnEdit = function(oCell,oJsTable) {
    var val = this.getInputElm(oCell).value;
    var denominator = oJsTable.tableModel.numberDenominator;
    var pointDec = ',';
    if (denominator==',') pointDec = '.';
    return val.replace(/\./,pointDec);
  };
  this.addCell = function(oRow,oJsTable,val,primaryKey) {
    val = this.fixedValue(val,oJsTable);
    var denominator = oJsTable.tableModel.numberDenominator;
    var shownVal = (val+'').giveDenomination(denominator,this.fracDigits);
    if (!this.denomination) shownVal = shownVal.removeDenomination(denominator,false,true);
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oInput = document.createElement("INPUT");
    oInput.type = 'hidden';
    oInput.setAttribute('name',this.inputName);
    oInput.setAttribute('id',this.inputName); //IE6
    oInput.value = val;
    oCell.appendChild(oInput);
    oCell.appendChild(document.createTextNode(shownVal));
    oCell.style.whiteSpace = 'nowrap';
    oCell.onkeypress = JsTableOnNumberKeyPress;
    return oCell;
  };
}

function JsTableColumnBoolean(columnName,inputName,width,align)
{
  this.base = JsTableColumnClass;
  align = align || 'center';
  this.base(columnName,inputName,width,align);
  
  this.setPrimaryKey = function(oCell,primaryKey) {
    this.getInputElm(oCell).value = primaryKey+'';
  };
  this.getCellEditor = function(oCell,oJsTable) {
    return this.getInputElm(oCell);
  };
  this.isValid = function (val) {
    return true; //Always true as usual evaluation in JavaScript, all values of all types
          //can be evaluated as boolean
  };
  this.fixedValue = function (val) {
       if (val) return true;
    return false;
  };
  this.getValue = function(oCell,oJsTable) {
    return this.getInputElm(oCell).checked;
  };
  this.getShownValue = function(oCell,oJsTable) {
    return this.getInputElm(oCell).checked;
  };
  this._setShownValue = function(val,oCell,oJsTable) {
  };
  this.setValue = function(val,oCell,oJsTable,oCellEditor) {
    this.getInputElm(oCell).checked = this.fixedValue(val);
  };
  this.setEditorOnEdit = function(oCellEditor,oCell,oJsTable) {
    oCellEditor.checked = !oCellEditor.checked;
    oCell._checked = oCellEditor.checked;
    oJsTable.onEdit = false;
  };
  this.setEditorOnCommit = function(oCellEditor,oCell,oJsTable) {
  };
  this.setEditorOnArrive = function(oCellEditor,oCell,oJsTable) {
    oCell.className = 'cell-focus';
    if (oCell._onclick) //oCellEditor.checked = !oCellEditor.checked;
      oJsTable.setOnEdit(); //Also checks if the cell is editable, at once
  };
  this.setEditorOnRearrive = function(oCellEditor,oCell,oJsTable) {
    this.setEditorOnArrive(oCellEditor,oCell,oJsTable);
  };
  this.setEditorOnLeave = function(oCellEditor,oCell,oJsTable) {
    oCell.className = '';
  };
  this.containsEditor = function(oCell,oCellEditor) {
    return 1;
  };
  this.addCell = function(oRow,oJsTable,bChecked,val) {
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oEditor = document.createElement("INPUT");
    oEditor.type = 'checkbox';
    oEditor.className = 'checkbox';
    oEditor.setAttribute('name',this.inputName);
    oEditor.setAttribute('id',this.inputName); //IE6
    oEditor.value = val+'';
    oEditor.checked = this.fixedValue(bChecked);
    oEditor.defaultChecked = oEditor.checked;
    oCell.appendChild(oEditor);
    oEditor.onblur = JsTableOnBlur;
    oEditor.onclick = JsTableOnCheckboxClick;
    oEditor.onkeydown = JsTableOnCheckboxKeyDown;
    oEditor.onkeypress = JsTableOnCheckboxKeyPress;
    return oCell;
  };
}

//This constructor is used to create object for option values in
//column whose type of JsTableColumnOptions
function JsTableOptionValues(arValuesSent,arValuesShown)
{
  if (!arValuesSent) arValuesSent = [];
  if (!arValuesShown) arValuesShown = [];
  for (var i=0; i<arValuesSent.length; i++) {
    if (typeof arValuesShown[i] == 'undefined') arValuesShown[i] = arValuesSent[i];
    this[arValuesSent[i]] = arValuesShown[i];
  }
}

function JsTableColumnOptions(columnName,inputName,width,align,options)
{
  this.base = JsTableColumnClass;
  this.base(columnName,inputName,width,align);
  this.options = options || (new JsTableOptionValues());
  
  var o = document.createElement('SELECT');
  o.style.overflow = 'hidden';
  o.style.margin = '0px';
  o.style.padding = '0px';
  o.style.borderStyle = 'none';
  o.style.width = '100%';
  for (var optVal in this.options) {
    opt = document.createElement('OPTION');
    opt.style.margin = '0px';
    opt.appendChild(document.createTextNode(this.options[optVal]));
    opt.setAttribute('value',optVal);
    o.appendChild(opt);
    //o.add(opt,null);
  }
  this.cellEditor = o;
  this.cellEditor._selectedIndex = -1;
  this.cellEditor.onblur = JsTableOnBlur;
  //this.cellEditor.onkeydown = JsTableOnKeyDown;
  //this.cellEditor.onkeypress = JsTableOnKeyPress;
  this.cellEditor.onkeyup = function (e) {
    if (this._selectedIndex >= 0) {
      this.selectedIndex = this._selectedIndex;
      this._selectedIndex1 = this._selectedIndex;
      this._selectedIndex = -1;
    }
  }
  /*if (JsTableNavFamily=='Opera7') { //For Linux
    this.cellEditor.onkeypress = function (e) {
      if (this._selectedIndex < 0 || (typeof this._selectedIndex=='undefined')) {
        this._repeatedKeypress = true;;
      }
    }
    this.cellEditor.onchange = function (e) {
      if (this._repeatedKeypress) {
        this.selectedIndex = this._selectedIndex1;
        this._repeatedKeypress = false;
      }
    }
  }*/
  
  this.getCellEditor = function(oCell,oJsTable) {
    if ( oJsTable.tableModel.isCellEditable(oCell.parentNode.rowIndex,oCell.cellIndex) )
      return this.cellEditor;
    return oJsTable.cellEditor;
  };
  this.isValid = function (val) {
    return (typeof this.options[val] != 'undefined');
  };
  this.fixedValue = function (val) {
       if (typeof val != 'undefined')
      if (typeof this.options[val] != 'undefined') return val;
    for (var optVal in this.options) return optVal;//the first option
  };
  this._getShownValue = function(val,oJsTable) {
    return this.options[val];
  };
  this.setEditorOnEdit = function(oCellEditor,oCell,oJsTable) {
  };
  this.setEditorOnCommit = function(oCellEditor,oCell,oJsTable) {
  };
  this.setEditorOnArrive = function(oCellEditor,oCell,oJsTable) {
    oCell.style.padding = '0px';
    JsTableRemoveAllText(oCell);
    var val = this.getValue(oCell,oJsTable);
    if (oCellEditor.options) { //Combo box
      for (var i=0; i<oCellEditor.length; i++)
        if (oCellEditor.options[i].value==val) break;
      oCellEditor = oCell.appendChild(oCellEditor);
      oCellEditor.selectedIndex = i;
      if (oCell._keydown) oCellEditor._selectedIndex = i;
    } else { //Text box
      if (JsTableNavFamily=='Opera7') {
        oCellEditor.style.height = (oCell.offsetHeight-2) + 'px';
      } else if (document.compatMode=='CSS1Compat') {
        oCellEditor.style.width = (oCell.offsetWidth-3) + 'px';
        oCellEditor.style.height = (oCell.offsetHeight-4) + 'px';
      } else {
        oCellEditor.style.width = oCell.offsetWidth + 'px';
        oCellEditor.style.height = (oCell.offsetHeight-2) + 'px';
      }
      oCellEditor.value = this._getShownValue(val,oJsTable);
      oCellEditor.style.textAlign = 'left';
      oCell.appendChild(oCellEditor);
    }
  };
  this.setEditorOnRearrive = function(oCellEditor,oCell,oJsTable) {
    if (oCell._keydown) oCellEditor._selectedIndex = oCellEditor.selectedIndex;
  };
  this.setEditorOnLeave = function(oCellEditor,oCell,oJsTable) {
    if (oCell.style.removeProperty) {
      oCell.style.removeProperty('padding');
    } else {
      oCell.style.padding = '';
    }
    if (oCellEditor._selectedIndex >= 0) {
      oCellEditor.selectedIndex = oCellEditor._selectedIndex;
      oCellEditor._selectedIndex = -1;
    }
    var val = oCellEditor.value;
    if (!oCellEditor.options) val = this.getInputElm(oCell).value; //Text box
    oCell.removeChild(oCellEditor);
    this.getInputElm(oCell).value = val;
    this._setShownValue( this._getShownValue(val,oJsTable), oCell,oJsTable);
  };
}

function JsTableColumnDate(columnName,inputName,width,align,saveAsSQLformat)
{
  this.base = JsTableColumnClass;
  align = align || 'center';
  this.base(columnName,inputName,width,align);
  this.saveAsSQLformat = (saveAsSQLformat?true:false);
  
  this.isValid = function (val,oJsTable) {
    val = val+'';
    return val.isDate(oJsTable.tableModel.dateFormat);
  };
  this.fixedValue = function (val,oJsTable) {
       var valid = this.isValid(val,oJsTable);
    if (valid) return valid;
    return '00-00-0000';
  };
  this.getValue = function(oCell,oJsTable) {
    return this.getShownValue(oCell,oJsTable);
  };
  this._getShownDate = function(val,dateFormat) {
    if (this.saveAsSQLformat) {
      if (val=='0000-00-00') return '00-00-0000';
      var dt = new Date();
      dt.setFrom_sqldate(val);
      if (dateFormat=='m-d-y') return dt.mmddyyyy();
      return dt.ddmmyyyy();
    }
    return val;
  };
  this.getShownValue = function(oCell,oJsTable) {
    var val = this.getInputElm(oCell).value;
    return this._getShownDate(val,oJsTable.tableModel.dateFormat);
  };
  this._getSavedDate = function(val,dateFormat) {
    if (this.saveAsSQLformat) {
      dt = new Date();
      if (val=='00-00-0000') return '0000-00-00';
      else if (dateFormat=='m-d-y') dt.setFrom_mmddyyyy(val);
      else dt.setFrom_ddmmyyyy(val);
      return dt.sqldate();
    }
    return val;
  };
  this.setValue = function(val,oCell,oJsTable,oCellEditor) {
    val = this.fixedValue(val,oJsTable);
    this.getInputElm(oCell).value = this._getSavedDate(val, oJsTable.tableModel.dateFormat);
    if (oCellEditor) oCellEditor.value = this.getShownValue(oCell,oJsTable);
    else this._setShownValue(val,oCell,oJsTable);
  };
  this.addCell = function(oRow,oJsTable,val) {
    val = this.fixedValue(val,oJsTable);
    var savedval = this._getSavedDate(val,oJsTable.tableModel.dateFormat);;
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oInput = document.createElement("INPUT");
    oInput.type = 'hidden';
    oInput.setAttribute('name',this.inputName);
    oInput.setAttribute('id',this.inputName); //IE6
    oInput.value = savedval;
    oCell.appendChild(oInput);
    oCell.appendChild(document.createTextNode(val));
    oCell.style.whiteSpace = 'nowrap';
    oCell.onkeypress = JsTableOnNumberKeyPress;
    oCell.dateCol = true;
    return oCell;
  };
}

function JsTableColumnDateTime(columnName,inputName,width,align,saveAsSQLformat)
{
  this.base = JsTableColumnDate;
  this.base(columnName,inputName,width,align,saveAsSQLformat);
  
  this.isValid = function (val,oJsTable) {
    val = val+'';
    return val.isDateTime(oJsTable.tableModel.dateFormat);
  }
  this.fixedValue = function (val,oJsTable) {
       var valid = this.isValid(val,oJsTable);
    if (valid) return valid;
    return '00-00-0000 00:00:00';
  }
  this.getShownValue = function(oCell,oJsTable) {
    var val = this.getInputElm(oCell).value;
    var dt = val.split(/\s+/);
    var showndate = this._getShownDate(dt[0],oJsTable.tableModel.dateFormat);
    return (showndate+' '+dt[1]);
  };
  this.setValue = function(val,oCell,oJsTable,oCellEditor) {
    val = this.fixedValue(val,oJsTable);
    var dt = val.split(/\s+/);
    var saveddate = this._getSavedDate(dt[0],oJsTable.tableModel.dateFormat);
    this.getInputElm(oCell).value = saveddate + ' ' + dt[1];
    if (oCellEditor) oCellEditor.value = this.getShownValue(oCell,oJsTable);
    else this._setShownValue(val,oCell,oJsTable);
  };
  this.addCell = function(oRow,oJsTable,val) {
    val = this.fixedValue(val,oJsTable);
    var dt = val.split(/\s+/);
    var saveddate = this._getSavedDate(dt[0],oJsTable.tableModel.dateFormat);;
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oInput = document.createElement("INPUT");
    oInput.type = 'hidden';
    oInput.setAttribute('name',this.inputName);
    oInput.setAttribute('id',this.inputName); //IE6
    oInput.value = saveddate + ' ' + dt[1];
    oCell.appendChild(oInput);
    oCell.appendChild(document.createTextNode(val));
    oCell.style.whiteSpace = 'nowrap';
    oCell.onkeypress = JsTableOnNumberKeyPress;
    oCell.datetimeCol = true;
    return oCell;
  };
}

function JsTableColumnLink(columnName,inputName,width,align,identifier,href)
{
  this.base = JsTableColumnClass;
  align = align || 'center';
  this.base(columnName,inputName,width,align);
  this.identifier = identifier;
  this.href = href+'';
  
  this.setPrimaryKey = function(oCell,primaryKey) {
    var oLink = this.getInputElm(oCell);
    var sHref = oLink.getAttribute("orgHref");
    sHref = sHref.replace(/__ROW__/g, oCell.parentNode.rowIndex);
    oLink.href = sHref.replace(/__PRIMARY_KEY__/g, primaryKey);
    oLink.setAttribute('primaryKey',primaryKey);
  };
  this.setRowIndex = function(oCell) {
    var oLink = this.getInputElm(oCell);
    var sHref = oLink.getAttribute("orgHref");
    sHref = sHref.replace(/__ROW__/g, oCell.parentNode.rowIndex);
    oLink.href = sHref.replace(/__PRIMARY_KEY__/g, oLink.getAttribute('primaryKey'));
  };
  this.getInputElm = function(oCell) {
    var arElms = oCell.getElementsByTagName('A');
    for (var i=0; i<arElms.length; i++) {
      if (arElms[i].getAttribute('name') == this.inputName) return arElms[i];
    }
  };
  this.getCellEditor = function(oCell,oJsTable) {
    return this.getInputElm(oCell);
  };
  this.getValue = function(oCell,oJsTable) {
    return this.getInputElm(oCell).href;
  };
  this.getShownValue = function(oCell,oJsTable) {
    return this.getInputElm(oCell).firstChild;
  };
  this.getShownValueOnEdit = function(oCell,oJsTable) {
  };
  this._setShownValue = function(val,oCell,oJsTable) {
    if (typeof val != 'object') val = document.createTextNode(val+'');
    var oLink = this.getInputElm(oCell);
    if (oLink.firstChild) oLink.replaceChild(val,oLink.firstChild);
    else oLink.appendChild(val);
  };
  this.setIdentifier = function(identifier,oCell,oJsTable) {
    this._setShownValue(identifier,oCell,oJsTable);
    this.identifier = identifier;
  };
  this.setValue = function(sHref,oCell,oJsTable,oCellEditor) {
    var oLink = this.getInputElm(oCell);
    oLink.href = sHref.replace(/__PRIMARY_KEY__/g,primaryKey);
    oLink.setAttribute('orgHref',sHref);
  };
  this.setEditorOnEdit = function(oCellEditor,oCell,oJsTable) {
    oJsTable.onEdit = false;
  };
  this.setEditorOnCommit = function(oCellEditor,oCell,oJsTable) {
  };
  this.setEditorOnArrive = function(oCellEditor,oCell,oJsTable) {
    oCell.className = 'cell-focus';
  };
  this.setEditorOnRearrive = function(oCellEditor,oCell,oJsTable) {
    this.setEditorOnArrive(oCellEditor,oCell,oJsTable);
  };
  this.setEditorOnLeave = function(oCellEditor,oCell,oJsTable) {
    oCell.className = '';
  };
  this.containsEditor = function(oCell,oCellEditor) {
    return 1;
  };
  this.addCell = function(oRow,oJsTable,sHref,primaryKey,iRow) {
    var oCell = oRow.insertCell(oRow.cells.length);
    oCell.style.textAlign = this.align;
    var oLink = document.createElement("A");
    oLink.setAttribute('name',this.inputName);
    if (!sHref) sHref = this.href;
    sHref = sHref + '';
    oLink.setAttribute('orgHref',sHref);
    oLink.setAttribute('primaryKey',primaryKey);
    sHref = sHref.replace(/__PRIMARY_KEY__/g,primaryKey);
    if (typeof oRow.rowIndex != 'undefined') iRow = oRow.rowIndex; //undefined for Opera7
    oLink.href = sHref.replace(/__ROW__/g,iRow);
    oCell.appendChild(oLink);
    var identifier = this.identifier;
    if (typeof this.identifier == 'object') {
      if (this.identifier.cloneNode) identifier = this.identifier.cloneNode(true);
      else identifier = this.identifier.toString();
    }
    this._setShownValue(identifier,oCell,oJsTable);
    if (JsTableNavFamily=='Opera7') {
      oLink.onclick = JsTableOnLinkClickOpera7;
      oLink.onmouseup = JsTableOnLinkMouseUpOpera7;
      //oLink.onkeypress = JsTableOnLinkKeyPress;
      oLink.onkeydown = JsTableOnLinkKeyDownOpera7;
    }
    oLink.onblur = JsTableOnBlur;
    return oCell;
  };
}


/***** Sets needed CSS *********************************/
function JsTableSetCSS()
{
  var noJsTableStyle = true;
  var o, hasMethodInsert = true;
  if (document.styleSheets) {
    if (!document.styleSheets.length) {
      o = document.getElementsByTagName('HEAD');
      if (o) {
        o = o[0];
        o.appendChild(document.createElement('STYLE'));
      }
    }
    var cssRules = 'cssRules';
    if (JsTableNavFamily=='IE6') cssRules = 'rules';
    var selectorText;
    for (i=0; i<document.styleSheets.length; i++) {
      o = document.styleSheets[i][cssRules];
      for (j=0; j<o.length; j++) {
        selectorText = o[j].selectorText.toLowerCase();
        if (selectorText=='.jstable' || selectorText=='table.jstable') {
          noJsTableStyle = false;
          break;
        }
      }
      if (!noJsTableStyle) break;
    }
    if (!document.styleSheets.length) hasMethodInsert = false; //We cannot insert new rules via DOM method
  } else {//if (document.styleSheets)
    hasMethodInsert = false;
  }
  
  var addRule = function(selector,sRule,o,iPhase) {
  }
  if (noJsTableStyle && hasMethodInsert) {
    o = document.styleSheets[0];
    if (JsTableNavFamily=='IE6') {
      addRule = function(selector,sRule,oStyle,iPhase) {
        oStyle.addRule(selector,sRule);
      }
    } else {
      addRule = function(selector,sRule,oStyle,iPhase) {
        oStyle.insertRule(selector + '{' + sRule + '}', o.cssRules.length);
      }
    }
  } else if (noJsTableStyle && !hasMethodInsert) {
    o = null;
      addRule = function(selector,sRule,oStyle,iPhase) {
        if (iPhase==1) document.write('<'+'style>\n');
        document.write(selector + '{' + sRule + '}\n');
        if (iPhase==2) document.write('<'+'/style>\n');
      }
  }
  var sCellStyle = 'font-family:arial,sans-serif; font-size:12px; ';
  var sCellEditorStyle = 'background-color:#cccccc; margin:0px; padding:0px; ' + sCellStyle;
  sCellStyle = 'background-color:white; height:18px; ' + sCellStyle;
  addRule('.JsTable', 'background-color:black', o, 1);
  addRule('.JsTable TH', sCellStyle, o, 0);
  addRule('.JsTable TD', sCellStyle, o, 0);
  addRule('.JsTable TEXTAREA', sCellEditorStyle, o, 0);
  addRule('.JsTable INPUT', sCellEditorStyle, o, 0);
  addRule('.JsTable INPUT.checkbox', 'background-color:transparent', o, 0);
  addRule('.JsTable .cell-focus', 'background-color:#cccccc', o, 0);
  addRule('.JsTable SELECT', 'height:16px; ' + sCellEditorStyle, o, 2);
}
JsTableSetCSS();

var JsTableGetKeyCode = function(e) {};
if (JsTableNavFamily=='Gecko') {
//Makes the event object in Gecko like the one in IE
  function JsTableGeckoEventButton() {
    switch (this.which) {
      case 1: return 1;
      case 2: return 4;
      case 3: return 2;
    }
  }
  function JsTableGeckoEventSrcElement() {
    return this.target;
  }
  function JsTableGeckoEventFromElement() {
    return this.relatedTarget;
  }
  function JsTableGeckoEventToElement() {
    return this.relatedTarget;
  }
  function JsTableGeckoEventReturnValue(bVal) {
    if (!bVal) this.preventDefault();
  }
  function JsTableGeckoEventCancelBubble(bVal) {
    if (bVal) this.stopPropagation();
  }
  function JsTableGeckoEventOffsetX() {
    return this.layerX;
  }
  function JsTableGeckoEventOffsetY() {
    return this.layerY;
  }
  //Other than Gecko, which doesn't support getter and setter statement, will not fail here
  eval("Event.prototype.button getter= JsTableGeckoEventButton");
  eval("Event.prototype.srcElement getter= JsTableGeckoEventSrcElement");
  eval("Event.prototype.fromElement getter= JsTableGeckoEventFromElement");
  eval("Event.prototype.toElement getter= JsTableGeckoEventToElement");
  eval("Event.prototype.returnValue setter= JsTableGeckoEventReturnValue");
  eval("Event.prototype.cancelBubble setter= JsTableGeckoEventCancelBubble");
  eval("Event.prototype.offsetX getter= JsTableGeckoEventOffsetX");
  eval("Event.prototype.offsetY getter= JsTableGeckoEventOffsetY");
  
//Emulates method contains in IE
  Element.prototype.contains = function(oNode) {
    while (oNode) {
      if (this == oNode) return true;
      oNode = oNode.parentNode;
    }
    return false;
  }

  JsTableGetKeyCode = function(e) {
    if (e.type=='keypress') {
      if (e.charCode) return e.charCode;
      else switch (e.keyCode) {
        case 8:  //Backspace
        case 9:  //Tab
        case 13: //Enter
          return e.keyCode;
        default:
          return e.keyCode*1000; //Multiply by 1000 to avoid overlapping with printable keyCode 
      }
    }
    return e.keyCode;
  };
} else if (JsTableNavFamily=='Opera7') {
  //Opera7 has different value for middle button from IE has, but we forgive it
  /*Event.prototype.button getter= function() {
    switch (this.button) {
      case 1: return 1;
      case 2: return 2;
      case 3: return 4;
    }
  }*/
  
  JsTableKeyCodeOnKeyDown = 0;
  JsTableGetKeyCode = function(e) {
    //Opera 7.1 has different keyCode for extended keys (thousands value)
    if (e.type=='keypress') {
      switch (e.keyCode) {
        case 113: if (JsTableKeyCodeOnKeyDown==81) return 81; // q
        case 57346:
          return 113000; //F2
        case 37:
        case 57387:
          return 37000;  //Left Arrow
        case 38:
        case 57385:
          return 38000;  //Up Arrow
        case 39: //Right arrow or single quote ( ' ) on Opera 7.2+
        case 57388:
          if (JsTableKeyCodeOnKeyDown==222) return 39; //single quote ( ' )
          return 39000;  //Right Arrow
        case 40:
        case 57386: return 40000;  //Down Arrow
        case 57395: return 46000;  //Delete
        case 57381: return 36000;  //Home
        case 57382: return 35000;  //End
        default   : return e.keyCode;
      }
    } else {
      JsTableKeyCodeOnKeyDown = e.keyCode;
      switch (e.keyCode) {
        case 57346: return 113; //F2
        case 57387: return 37;  //Left Arrow
        case 57385: return 38;  //Up Arrow
        case 57388: return 39;  //Right Arrow
        case 57386: return 40;  //Down Arrow
        case 57395: return 46;  //Delete
        case 57381: return 36;  //Home
        case 57382: return 35;  //End
        default   : return e.keyCode;
      }
    }
  };
} else { //IE 6
  JsTableGetKeyCode = function(e) {
    return e.keyCode;
  };
}

/************** Event Handler ********************/
function JsTableGetObjectsOnEvent(sTagName,e)
{
  if (!e) e = window.event;
  var oTarget = e.srcElement;
  var objs = {target: oTarget};
  while (sTagName.indexOf('|'+oTarget.nodeName+'|')==-1) {
    if (oTarget.nodeName=='TABLE') return null;
    if (oTarget.nodeName=='HTML') return null;
    oTarget = oTarget.parentNode;
    if (!oTarget) return null;
  }
  if (oTarget.nodeName=='TD' || oTarget.nodeName=='TH') {
    objs.cellEditor = null;
    objs.cell = oTarget;
  } else {
    objs.cellEditor = oTarget;
    objs.cell = oTarget.parentNode;
  }
  if (!objs.cell) return null;
  objs.row = objs.cell.parentNode;
  if (!objs.row) return null;
  objs.tbl = (objs.row.parentNode ? objs.row.parentNode.parentNode : null);
  if (!objs.tbl) return null;
  objs.jsTable = window['_'+objs.tbl.id];
  objs.event = e;
  objs.keyCode = JsTableGetKeyCode(e);
  return objs;
}

var JsTableColToResize = -1;
var JsTableOffsetXstartToResize = -1;
var JsTableObjectTblOnResize = null;
function JsTableHeaderOnMouseMove(e)
{
  if (!e) e = window.event;
  var oTarget = e.srcElement;
  var oTbl = oTarget;
  while (oTbl.nodeName!='TABLE') {
    oTbl = oTbl.parentNode;
    if (!oTbl) return;
  }
  var oRow = oTbl.rows[0];
  var bOnEdge = false;
  if (oTarget.tagName=='TH') {
    if (e.offsetX==0 && oTarget.cellIndex!=0) {
      JsTableColToResize = oTarget.cellIndex - 1; //Resize the left side column
      bOnEdge = true;
    }
    if (e.offsetX==oTarget.offsetWidth-1) {
      JsTableColToResize = oTarget.cellIndex; //Resize the left side column
      bOnEdge = true;
    }
  }
  
  if (bOnEdge) {
    oTbl.style.cursor = 'w-resize';
  } else {
    oTbl.style.cursor = 'default';
    JsTableColToResize = -1;
  }
}
function JsTableHeaderOnMouseOver(e)
{
  if (!e) e = window.event;
  var oTbl = e.srcElement;
  if (oTbl.tagName!='TABLE') return;
  if (!e.fromElement) return;
  if (e.fromElement.tagName!='TH') return;
  var oRow = oTbl.rows[0];
  var bOnEdge = (e.offsetY < oRow.offsetHeight && e.offsetX > 1); //On the edge precisely
  if (bOnEdge) {
    var iLeft = oRow.cells[0].offsetWidth + 1 + oRow.cells[1].offsetWidth;
    var iCol = 1;
    while (e.offsetX > iLeft) {
      iCol++;
      if (iCol==oRow.cells.length) break;
      iLeft += oRow.cells[iCol].offsetWidth + 1;
    }
    JsTableColToResize = iCol - 1; //Resize the left side column
    oTbl.style.cursor = 'w-resize';
  } else {
    oTbl.style.cursor = 'default';
    JsTableColToResize = -1;
  }
}
function JsTableHeaderOnMouseOut(e)
{
  if (!e) e = window.event;
  var oTarget = e.toElement;
  var oTbl = oTarget;
  while (oTbl) {
    if (oTbl.nodeName=='TABLE') break;
    oTbl = oTbl.parentNode;
  }
  if (!oTbl) {
    JsTableColToResize = -1;
    if (this.style) this.style.cursor = 'default';
  }
}
function JsTableStopDragOnResize(oJsTable,bHasFocus)
{
  oTbl = oJsTable.table;
  oTbl.style.cursor = 'default';
  oTbl.onmousemove = JsTableHeaderOnMouseMove;
  oTbl.onmouseover = JsTableHeaderOnMouseOver;
  oTbl.onmouseout = JsTableHeaderOnMouseOut;
  oTbl.onmouseup = null;
  if (document.releaseCapture) document.releaseCapture();
  else {
    document.removeEventListener('mouseup',JsTableOnResizeOnMouseUp,true);
    document.removeEventListener('mouseout',JsTableOnResizeOnMouseOut,true);
  }
  JsTableColToResize = -1;
  JsTableOffsetXstartToResize = -1;
  JsTableObjectTblOnResize = null;
  if (bHasFocus) oJsTable.setFocus(true);
}
function JsTableOnResizeOnMouseUp(e)
{
  if (!JsTableObjectTblOnResize) return;
  if (!e) e = window.event;
  var dx = e.screenX - JsTableOffsetXstartToResize;
  var oJsTable = window['_'+JsTableObjectTblOnResize.id];
  var bHasFocus = oJsTable.hasFocus;
  if (bHasFocus) oJsTable.setFocus(false);
  var oCol = oJsTable.tableModel.columns[JsTableColToResize];
  oCol.width += dx;
  if (oCol.width < 20) oCol.width = 20;
  var oTbl = oJsTable.table;
  for (var i=0; i<oTbl.rows.length; i++) {
    oTbl.rows[i].cells[JsTableColToResize].style.width = oCol.width + 'px';
    oTbl.rows[i].cells[JsTableColToResize].width = oCol.width;
  }
  JsTableStopDragOnResize(oJsTable,bHasFocus);
}
function JsTableOnResizeOnMouseOut(e)
{
  if (!JsTableObjectTblOnResize) return;
  if (!e) e = window.event;
  if (!e.toElement) {
    var oJsTable = window['_'+JsTableObjectTblOnResize.id];
    var bHasFocus = oJsTable.hasFocus;
    JsTableStopDragOnResize(oJsTable,bHasFocus);
  }
}

function JsTableOnMouseDown(e)
{
  if (JsTableColToResize >= 0) {
    if (!e) e = window.event;
    JsTableOffsetXstartToResize = e.screenX;
    JsTableObjectTblOnResize = this;
    this.onmousemove = null;
    this.onmouseover = null;
    this.onmouseout = null;
    if (JsTableNavFamily=='IE6') {
      this.setCapture();
      this.onmouseup = JsTableOnResizeOnMouseUp;
      this.onmouseout = JsTableOnResizeOnMouseOut;
    } else {
      document.addEventListener('mouseup',JsTableOnResizeOnMouseUp,true);
      document.addEventListener('mouseout',JsTableOnResizeOnMouseOut,true);
    }
  }
  var o = JsTableGetObjectsOnEvent('|TD|TH|',e);
  if (!o) return;
  var iRow = o.row.rowIndex;
  var iCol = o.cell.cellIndex;
  if (iRow < 1) {
    if (o.tbl.rows.length <= 1) return;
    iRow = 1;
  }
  if ('|TEXTAREA|INPUT|SELECT|A|'.indexOf('|'+o.target.nodeName+'|')!=-1) o.cellEditor = o.target;
  o.cell._onclick = true;
  if (JsTableNavFamily=='IE6' && o.jsTable.hasFocus && o.jsTable.row==iRow && o.jsTable.col==iCol) {
    if (o.target!=o.cellEditor) o.jsTable._onclickIE6 = [-1,-1];
    else if (o.target.nodeName=='TEXTAREA' ||
      (o.target.nodeName=='INPUT' && o.target.type=='text')) o.jsTable._onclickIE6 = [iRow,iCol];
  }
  o.jsTable.setActiveCell(iRow,iCol);
  if (o.cellEditor!=o.target) {
    o.event.returnValue = false;
    return false;
  }
}

function JsTableOnDblClick(e)
{
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|',e);
  if (!o) return;
  o.jsTable.setOnEdit();
}

function JsTableOnBlur(e)
{
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|SELECT|A|',e);
  if (!o) return;
  if (o.jsTable._onclickIE6)
    o.jsTable._onclickIE6 = (o.jsTable._onclickIE6[0]!=o.row.rowIndex
      || o.jsTable._onclickIE6[1]!=o.cell.cellIndex);
  if (o.jsTable._onmove) o.jsTable._onmove = false;
  else if (o.jsTable._onclickIE6) //IE 6
    //When JsTable has focus and then the active cell is clicked but not on the CellEditor,
    //such as checkbox or link, the blur event will occur but we want the JsTable still has focus
    o.cellEditor.focus();
  else if (o.jsTable.row==o.row.rowIndex && o.jsTable.col==o.cell.cellIndex)//used especially for Opera 7
    //must be the active cell which experiences the blur event. The event can happen on the previous
    //active cell whose different cell editor from the one owned the current active cell.
    //o.jsTable._onmove is useful when the cell editor remains the same
    { o.jsTable._onblur=true; o.jsTable.setFocus(false); }
  
  o.jsTable._onclickIE6 = false;
}

function JsTableOnKeyPress(e)
{
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|SELECT|',e);
  if (!o) return;
  var bRetVal = true;
  switch (o.keyCode) {
    case 37000: //Left Arrow
    case 38000: //Up Arrow
    case 39000: //Right Arrow
    case 40000: //Down Arrow
      if (o.jsTable.onEdit) break;
    case 13: //Enter
      if (o.cellEditor.nodeName=='A') break;
    case 113000: //F2
    case 27: //Esc
      o.event.returnValue = false;
      o.event.cancelBubble = true;
      bRetVal = false;
    default:
      
  }
  
  if (typeof o.jsTable.onkeypress == 'function') o.jsTable.onkeypress(o.event);
  if (!bRetVal) return false;
}

function JsTableGetNewCoordOnArrow(iArrowKey,iRow,iCol,oTbl,oRow)
{
  switch (iArrowKey) {
    case 37: //Left
    case 37000:
      return {row: iRow, col: ( (iCol>0)?(iCol-1):iCol )};
    case 38: //Up
    case 38000:
      return {row: ( (iRow>1)?(iRow-1):iRow ), col: iCol};
    case 39: //Right
    case 39000:
      return {row: iRow, col: ( (iCol<oRow.cells.length-1)?(iCol+1):iCol )};
    case 40: //Down
    case 40000:
      return {row: ( (iRow<oTbl.rows.length-1)?(iRow+1):iRow ), col: iCol};
  }
}

function JsTableOnKeyDown(e)
{
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|SELECT|A|',e);
  if (!o) return;
  var iRow = o.row.rowIndex;
  var iCol = o.cell.cellIndex;
  var bRetVal = true;
  //Arrow keys
  if (o.keyCode >= 37 && o.keyCode <= 40) {
    if (!o.jsTable.onEdit) {
      var oCoord = JsTableGetNewCoordOnArrow(o.keyCode,iRow,iCol,o.tbl,o.row);
      o.cell._keydown = true;
      o.jsTable.setActiveCell(oCoord.row, oCoord.col);
      bRetVal = false;
    }
  } else {
    switch (o.keyCode) {
    case 13: //Enter
      if (o.cellEditor.nodeName=='A') break;
      if (!o.jsTable.onEdit) o.jsTable.setOnEdit();
      else {
        o.jsTable.setOnCommit();
        var keyCode = 0;
        if (o.jsTable.tableModel.enterAfterEdit == JsTable.ENTER_MOVE_DOWN) keyCode = 40;
        if (o.jsTable.tableModel.enterAfterEdit == JsTable.ENTER_MOVE_RIGHT) keyCode = 39;
        if (keyCode) {
          var oCoord = JsTableGetNewCoordOnArrow(keyCode,iRow,iCol,o.tbl,o.row);
          o.cell._keydown = true;
          o.jsTable.setActiveCell(oCoord.row, oCoord.col);
        }
      }
      bRetVal = false; break;
    case 113: //F2
      o.jsTable.setOnEdit();
      bRetVal = false; break;
    case 27: // Escape
      o.jsTable.setOnCommit();
      bRetVal = false; break;
    }
  }
  
  if (typeof o.jsTable.onkeydown == 'function') o.jsTable.onkeydown(o.event);
  if (!bRetVal) {
    o.event.returnValue = false;
    o.event.cancelBubble = true;
    return false;
  }
}

function JsTableOnCheckboxClick(e)
{
  var o = JsTableGetObjectsOnEvent('|INPUT|',e);
  if (!o) return;
  o.event.returnValue = false;
  o.cellEditor.checked = o.cell._checked;
}

function JsTableOnCheckboxKeyDown(e)
{
  var o = JsTableGetObjectsOnEvent('|INPUT|',e);
  if (!o) return;
  switch (o.keyCode) {
    case 32: //Space
      o.jsTable.setOnEdit();
    case 113: //F2
    case 27: // Escape
      o.event.returnValue = false;
      o.event.cancelBubble = true;
      return false;
    case 13: //Enter
      o.jsTable.setOnEdit();
      o.jsTable.onEdit = true;
  }
}

function JsTableOnCheckboxKeyPress(e)
{
  var o = JsTableGetObjectsOnEvent('|INPUT|',e);
  if (!o) return;
  if (o.keyCode==32) { //space
    o.event.returnValue = false;
    o.event.cancelBubble = true;
    return false;
  }
}

function JsTableOnStringKeyPress(e) {
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|',e);
  if (!o) return;
  if (!o.jsTable.onEdit && (o.keyCode >= 32 && o.keyCode <= 126)) { //typeable character
    o.cellEditor.readOnly = false;
    o.cellEditor.select(); //IE6
    o.jsTable.onEdit = true;
    o.cellEditor.value = '';//String.fromCharCode(o.keyCode);
  }
}

function JsTableOnNumberKeyPress(e)
{
  var o = JsTableGetObjectsOnEvent('|TEXTAREA|INPUT|',e);
  if (!o) return;
  
  var keyCode = o.keyCode;
  var val = o.cellEditor.value;
  var bTrueChar = ((keyCode >= 48 && keyCode <= 57)   //numeric keys
    || keyCode == 45);   //hyphen (-)
  var bDecOk = true;
  if (o.cell.dateCol || o.cell.datetimeCol) {
    //if (keyCode==45) return;//hyphen
    if (o.cell.datetimeCol)
      bTrueChar = bTrueChar || keyCode==32 || keyCode==58; //space, : (colon)
  } else {
    var pointDec = ',';
    if (o.jsTable.tableModel.numberDenominator==',') pointDec = '.';
    if (pointDec==',' && keyCode==44) {
      bTrueChar = true;
      bDecOk = (val.indexOf(',')==-1);//ok if decimal point not yet exist
    } else if (pointDec=='.' && keyCode==46) {
      bTrueChar = true;
      bDecOk = (val.indexOf('.')==-1);//ok if decimal point not yet exist
    }
    //if (keyCode==45 && val.indexOf('-')!=0) return; //hyphen
  }
  if (!o.jsTable.onEdit) {
    if (bTrueChar) {
      o.cellEditor.readOnly = false;
      o.cellEditor.select(); //IE6
      o.jsTable.onEdit = true;
      o.cellEditor.value = '';//String.fromCharCode(keyCode);
      return;
    }
  } else if ((bTrueChar && bDecOk)
  //  || keyCode == 13 || keyCode == 27  //Enter, Escape
    || keyCode == 46000 || keyCode == 8 || keyCode == 9 //Delete, Backspace, Tab
    || (keyCode >= 37000 && keyCode <= 40000)   //Arrow
    || keyCode == 0   //Some extended keys on Opera
    || keyCode == 36000 || keyCode == 35000  //Home, end
  ) {
    return;
  }
  o.event.returnValue = false;
  o.event.cancelBubble = true;
  return false;
}

function JsTableOnLinkClickOpera7(e)
{ 
  if (this._nocancelclick) this._nocancelclick = false;
  else return false;
}
var JsTableOnLinkMouseUpJsTableObject = null;
var JsTableOnLinkMouseUpLinkObject = null;
function JsTableOnLinkMouseUpOpera7(e) {
  var o = JsTableGetObjectsOnEvent('|A|',e);
  if (!o) return;
  JsTableOnLinkMouseUpLinkObject = o.cellEditor;
  JsTableOnLinkMouseUpJsTableObject = o.jsTable;
  setTimeout(
  'if (JsTableOnLinkMouseUpJsTableObject) { '
    + 'JsTableOnLinkMouseUpJsTableObject.setFocus(true); '
    + 'JsTableOnLinkMouseUpJsTableObject=null; } '
  + 'if (JsTableOnLinkMouseUpLinkObject) { '
    + 'JsTableOnLinkMouseUpLinkObject._nocancelclick=true; '
    + 'JsTableOnLinkMouseUpLinkObject.click(); '
    + 'JsTableOnLinkMouseUpLinkObject=null; }',0);
}
function JsTableOnLinkKeyDownOpera7(e)
{
  if (!e) e = window.event;
  if (e.keyCode==13) this._nocancelclick = true;
}
/************ End: Event Handler ****************************/


function JsTableModel(columns,phpStyle,numberDenominator,dateFormat,oldValIfInvalid,
  withPrimaryKey)
{
  //columns is a collection of instance of JsTableColumnClass
  if (typeof columns != 'object') columns = [];
  else if (!columns.length) columns = [];
  this.columns = columns;
  
  //Only ',' or '.' accepted
  if (numberDenominator != ',') numberDenominator = '.';
  this.numberDenominator = numberDenominator;
  
  //Only 'd-m-y' or 'm-d-y' accepted
  dateFormat = (dateFormat+'').toLowerCase();
  if (dateFormat != 'm-d-y') dateFormat = 'd-m-y';
  this.dateFormat = dateFormat;
  
  if (oldValIfInvalid) this.oldValIfInvalid = true;
  else this.oldValIfInvalid = false;
  
  if (phpStyle) {
    for (var i=0;i<columns.length;i++) columns[i].inputName += '[]';
  }
  
  this.withPrimaryKey = (withPrimaryKey ? true : false);
  this.enterAfterEdit = JsTable.ENTER_STAY;
  
  this.isCellEditable = function(iRow,iCol) {
    return true;
  };
}


function JsTable(sId,oTableModel,oParent,oNextSibling)
{
  function errorId() {
    alert('The table Id is invalid or has been existed');
    return null;
  }
  if (typeof sId != 'string' || !sId) return errorId();
  if (!sId.isQualifiedVarName()) return errorId();
  if ( (typeof window[sId]!='undefined') || (typeof window['_'+sId]!='undefined')
    || document.getElementById(sId)) return errorId();
  
  if (!oParent && !oParent.insertBefore) {
    if (document.forms.length) {
      oParent = document.forms[document.forms.length];
    } else {
      oParent = document.body.appendChild(document.createElement('FORM'));
    }
  }
  
  var oTbl = document.createElement('TABLE');
  //oTbl.onmousemove = JsTableHeaderOnMouseMove;
  oTbl.id = sId;
  oTbl.className = 'JsTable';
  oTbl.cellSpacing = 1;
  oTbl.cellPadding = 0;
  
  /* Creates Column Headers */
  var o = oTbl.createTHead();
  o = o.insertRow(0);
  var columns = oTableModel.columns;
  var oText, oCell;
  for (var i=0; i<columns.length; i++) {
    oText = document.createTextNode(columns[i].columnName);
    oCell = o.appendChild(document.createElement('TH'));
    oCell.style.width = columns[i].width + "px";
    oCell.appendChild(oText);
  }
  if (JsTableNavFamily == 'IE6') { //if (JsTableNavFamily != 'Gecko')
    oTbl.onmousemove = JsTableHeaderOnMouseMove;
    oTbl.onmouseover = JsTableHeaderOnMouseOver;
    oTbl.onmouseout = JsTableHeaderOnMouseOut;
  }
  
  if (!oNextSibling) oNextSibling = null;
  oTbl = oParent.insertBefore(oTbl,oNextSibling);
  window[sId] = oTbl;
  window['_'+sId] = this;
  this.table = oTbl;
  this.id = sId;
  this.tableModel = oTableModel;
  
  if (true) { //(JsTableNavFamily=='Opera7') {
    o = document.createElement('INPUT');
    o.type = 'text';
  } else {
    o = document.createElement('TEXTAREA');
    if (JsTableNavFamily=='IE6') o.style.overflow = 'hidden';
    else o.style.overflow = 'auto';
  }
  o.readOnly = true;
  o.style.width = '100%';
  o.style.height = '100%';
  o.style.margin = '0px';
  o.style.borderStyle = 'solid';
  o.style.borderWidth = '1px';
  //o.style.padding = '1px 1px 1px 0px';
  o.style.paddingRight = '1px';
  o.style.borderColor = '#cccccc';
  this.cellEditor = o;
  
  this.row = -1;
  this.col = -1;
  this.onEdit = false;
  this.hasFocus = false;
  
  oTbl.onmousedown = JsTableOnMouseDown;
  oTbl.onkeydown = JsTableOnKeyDown;
  oTbl.onkeypress = JsTableOnKeyPress;
  //oTbl.onblur = JsTableOnBlur;
  this.cellEditor.ondblclick = JsTableOnDblClick;
  //this.cellEditor.onkeydown = JsTableOnKeyDown;
  //this.cellEditor.onkeypress = JsTableOnKeyPress;
  this.cellEditor.onblur = JsTableOnBlur;
  
  this.onkeydown = function(e) {};
  this.onkeypress = function(e) {};
  this.oncellchange = function() {};
  this.oncommit = function() {};
  
  this._keys = [];
}

JsTable.prototype.getObjectsOnCell = function(iRow,iCol) {
  var oColumn = this.tableModel.columns[iCol];
  var oCell = this.table.rows[iRow];
  if (oCell) oCell = oCell.cells[iCol];
  var oCellEditor = oColumn.getCellEditor(oCell,this);
  if (!oCellEditor) oCellEditor = this.cellEditor;
  return {column: oColumn, cellEditor: oCellEditor, cell: oCell};
};

JsTable.prototype.getColumn = function(iCol) {
  return this.tableModel.columns[iCol];
};

JsTable.prototype.setHeader = function(sHeader, iCol) {
  var oCell = this.table.rows[0].cells[iCol];
  if (!oCell) return;
  JsTableRemoveAllText(oCell);
  oCell.appendChild(document.createTextNode(sHeader+""));
};

JsTable.prototype.setValue = function(val, iRow, iCol) {
  if (iRow < 1 || iCol < 0) return;
  var o = this.getObjectsOnCell(iRow,iCol);
  if (!o.cell) return;
  var oCellEditor = null;
  if (o.cellEditor) if (o.cell.contains(o.cellEditor)) oCellEditor = o.cellEditor;
  o.column.setValue(val,o.cell,this,oCellEditor);
};
JsTable.prototype.getValue = function(iRow, iCol) {
  var oTbl = this.table;
  if (iRow < 1) return;
  var oRow = oTbl.rows[iRow];
  if (!oRow) return;
  var oCell = oRow.cells[iCol];
  if (!oCell) return;
  var oColumn = this.tableModel.columns[iCol];
  oColumn.getValue(oCell,this);
};

//arguments list the values for each cell
JsTable.prototype.insertRow = function(args,primaryKey,iRow) {
  var oTbl = this.table;
  var arColumn = this.tableModel.columns;
  
  if (!iRow) iRow = oTbl.rows.length;
  iRow = parseInt(iRow);
  if (isNaN(iRow)) return;
  if (iRow > oTbl.rows.length || iRow < 1) iRow = oTbl.rows.length;
  if (!this.tableModel.withPrimaryKey) primaryKey = iRow;
  
  var arColKey = [];
  var i, j = 0;
  var oRow = oTbl.insertRow(iRow);
  for (i=0; i<arColumn.length; i++) {
    arColumn[i].addCell(oRow, this, args[i], primaryKey, iRow);
    oRow.cells[i].style.width = arColumn[i].width + 'px';
    if (arColumn[i].setPrimaryKey) {
      arColKey[j] = i;
      j++;
    }
  }
  
  if (!this.tableModel.withPrimaryKey) {
    for (var i=iRow+1; i<oTbl.rows.length; i++) {
      for (j=0; j<arColKey.length; j++) {
        var oCell = oTbl.rows[i].cells[arColKey[j]];
        arColumn[arColKey[j]].setPrimaryKey(oCell,i);
      }
    }
  }
  
  var oInput, oCell = oRow.cells[0];
  //The new row must have all keys
  for (i=0; i<this._keys.length; i++) {
    oInput = document.createElement('INPUT');
    oInput.type = 'hidden';
    oInput.setAttribute('name',this._keys[i]);
    oCell.appendChild(oInput);
  }
};

JsTable.prototype.addMatrix = function(args2,primaryKeys) {
  for (var i=0; i<args2.length; i++) {
    this.insertRow(args2[i],primaryKeys[i]);
  }
};

JsTable.prototype.deleteRow = function(iRow) {
  var oTbl = this.table;
  iRow = parseInt(iRow);
  if (isNaN(iRow)) return;
  if (iRow < 1 || iRow >= oTbl.rows.length) return;
  var bHasFocus = this.hasFocus;
  if (iRow == this.row && bHasFocus) this.setFocus(false);
  oTbl.deleteRow(iRow);
  var arColumn = this.tableModel.columns;
  if (!this.tableModel.withPrimaryKey) {
    for (var j=0; j<arColumn.length; j++) {
      if (typeof arColumn[j].setPrimaryKey!='function') continue;
      for (var i=iRow; i<oTbl.rows.length; i++) {
        var oCell = oTbl.rows[i].cells[j];
        arColumn[j].setPrimaryKey(oCell,i);
      }
    }
  }
  for (var j=0; j<arColumn.length; j++) {
    if (typeof arColumn[j].setRowIndex!='function') continue;
    for (var i=iRow; i<oTbl.rows.length; i++) {
      var oCell = oTbl.rows[i].cells[j];
      arColumn[j].setRowIndex(oCell);
    }
  }
  
  if (iRow >= oTbl.rows.length) iRow = oTbl.rows.length-1;
  if (iRow > 0) this.setActiveCell(iRow,this.col);
  else this.row = -1;
};

JsTable.prototype.insertKey = function(sKeyName,val,iRow) {
  var arColumn = this.tableModel.columns;
  for (var i=0; i<arColumn.length; i++)
    if (arColumn[i].inputName==sKeyName) {
      alert('JsTable.insertKey : this key name has been owned by the input of a column');
      return;
    }
  var oTbl = this.table;
  if (!iRow) iRow = 1;
  iRow = parseInt(iRow);
  if (isNaN(iRow)) return;
  if (iRow < 1 || iRow >= oTbl.rows.length) return;
  
  var iLength = 0;
  if ((typeof val != 'object') || (typeof val.length != 'number')) {
    val = [val];
    iLength = 1;
  } else 
    iLength = val.length;
  if (iRow+iLength > oTbl.rows.length) iLength = oTbl.rows.length - iRow;
  
  var oCell = oTbl.rows[iRow].cells[0];
  var bKeyExist = false;
  var arInputs = oCell.getElementsByTagName('INPUT');
  for (var i=0; i<arInputs.length; i++)
    if (arInputs[i].getAttribute('name')==sKeyName) {
      bKeyExist = true;
      break;
    }
  if (bKeyExist) {
    for (var i=0; i<iLength; i++) {
      oCell = oTbl.rows[iRow+i].cells[0];
      arInputs = oCell.getElementsByTagName('INPUT');
      for (var j=0; j<arInputs.length; j++)
        if (arInputs[j].getAttribute('name')==sKeyName) {
          arInputs[j].value = val[i];
          break;
        }
    }
  } else {
    var j = iRow + iLength;
    //All rows must have this key
    for (var i=0; i<oTbl.rows.length; i++) {
      oCell = oTbl.rows[i].cells[0];
      var oInput = document.createElement('INPUT');
      oInput.type = 'hidden';
      oInput.setAttribute('name',sKeyName);
      if (i >= iRow && i < j) oInput.value = val[i-iRow];
      oCell.appendChild(oInput);
    }
  }
};

JsTable.prototype.setActiveCell = function(iRow, iCol) {
  if (iRow < 1 || iRow >= this.table.rows.length
     || iCol < 0 || iCol >= this.tableModel.columns.length) return;
  if (!this.hasFocus) {
    this._oncellchange = (iRow != this.row || iCol != this.col);
    this.row = iRow;
    this.col = iCol;
    this.setFocus(true);
    return;
  }
  
  var o = this.getObjectsOnCell(iRow,iCol);
  if (this.row == iRow && this.col == iCol) {
    o.column.setEditorOnRearrive(o.cellEditor,o.cell,this);
    o.cell._onclick = false;
    o.cell._keydown = false;
    return;
  } else {
    if (this.row > 0 && this.col >= 0) {
      var oldCell = this.getObjectsOnCell(this.row,this.col);
      o.cell._keydown = oldCell.cell._keydown;
      oldCell.cell._keydown = false;
      //oldCell.cellEditor.blur();
      this._onmove = true;
      this.setFocus(false);
    }
  }
  
  this._oncellchange = true;
  this.row = iRow;
  this.col = iCol;
  this.setFocus(true);
};
  
JsTable.prototype.setOnEdit = function() {
  if (!this.tableModel.isCellEditable(this.row,this.col)) return;
  if (this.onEdit) return;
  if (this.row < 1 || this.col < 0) return;
  var o = this.getObjectsOnCell(this.row,this.col);
  this.onEdit = true;
  o.column.setEditorOnEdit(o.cellEditor,o.cell,this);
  //Enter cannot be canceled on Opera
  //if (o.cellEditor.value) o.cellEditor.value = o.cellEditor.value.replace(/\n/g,'').replace(/\r/g,'');
};

JsTable.prototype.setOnCommit = function() {
  if (!this.onEdit) return;
  if (this.row < 1 || this.col < 0) return;
  var o = this.getObjectsOnCell(this.row,this.col);
  this.onEdit = false;
  //Enter cannot be canceled on Opera
  //if (o.cellEditor.value) o.cellEditor.value = o.cellEditor.value.replace(/\n/g,'').replace(/\r/g,'');
  o.column.setEditorOnCommit(o.cellEditor,o.cell,this);
  this.oncommit();
};

JsTableActiveCellEditor = null;
JsTableSetCellEditorFocus = function() {
  if (!JsTableActiveCellEditor) return;
  JsTableActiveCellEditor.focus();
  var oTbl = JsTableActiveCellEditor;
  for (var i=0; i<4; i++) {
    oTbl = oTbl.parentNode;
    if (!oTbl) return;
  }
  //.parentNode.parentNode.parentNode.parentNode;
  var oJsTable = window['_'+oTbl.id];
  oJsTable._onmove = false;
  JsTableActiveCellEditor = null;
};
JsTable.prototype._setCellEditorFocus = function(oCellEditor) {
  JsTableActiveCellEditor = oCellEditor;
  setTimeout('JsTableSetCellEditorFocus()',0);
};

JsTable.prototype.setFocus = function(bGetFocus) {
  if (this.hasFocus == bGetFocus) return;
  if (this.row < 1 || this.col < 0) return;
  var o = this.getObjectsOnCell(this.row,this.col);
  if (bGetFocus) {
    this.hasFocus = true;
    var iCont = o.column.containsEditor(o.cell,o.cellEditor);
    if (iCont ^ -1) //o.column.constructor==JsTableColumnBoolean || !o.cell.contains(o.cellEditor)
      o.column.setEditorOnArrive(o.cellEditor,o.cell,this);
    o.cell._onclick = false;
    o.cell._keydown = false;
    if (this._oncellchange) this.oncellchange();
    this._oncellchange = false;
    this._setCellEditorFocus(o.cellEditor);
  } else {
    this.hasFocus = false;
    if (o.column.containsEditor(o.cell,o.cellEditor)) {
      if (this.onEdit) this.setOnCommit();
      o.column.setEditorOnLeave(o.cellEditor,o.cell,this);
      if (o.column.containsEditor(o.cell,o.cellEditor)) //such as checkbox, Link
        if (!this._onblur) {
           if (o.cellEditor.blur)  o.cellEditor.blur;
        } else {
          this._onblur = false;
        }
    }
  }
};

JsTable.ENTER_STAY = 0;
JsTable.ENTER_MOVE_DOWN = 1;
JsTable.ENTER_MOVE_RIGHT = 2;
} /**************** if (JsTableNavFamily) *****************************/
</script>
<style><!--
/*
.JsTable .cell-focus {
  background-color: red;
}
*/
--></style>
<script language=JavaScript>
<!--
var g_oJsTable;
function setValues(iRow, str, num, denNum, bool, opt, date, datetime )
{
  //alert(iRow + ' : ' + str + ',' + num + ',' + denNum + ',' + bool + ',' + opt + ',' + date);
  g_oJsTable.setValue(str,      iRow, 0);
  g_oJsTable.setValue(num,      iRow, 1);
  g_oJsTable.setValue(denNum,   iRow, 2);
  g_oJsTable.setValue(bool,     iRow, 3);
  g_oJsTable.setValue(opt,      iRow, 4);
  g_oJsTable.setValue(date,     iRow, 5);
  g_oJsTable.setValue(datetime, iRow, 6);
}
function addRow()
{
  g_oJsTable.insertRow([]);
  if (g_oJsTable.row > 0) g_oJsTable.setFocus(true);
  else g_oJsTable.setActiveCell(1,g_oJsTable.col);
  
}
window.editedRowIndex = 0;
function editRow(iRow)
{
  var win = window.open('./JsTable-selectValue.htm', 'JsTableSelectValues',
    'left=40,top=40,height=400,width=720,resizable=1,scrollbars=1');
  if (!win.opener) win.opener = window;
  window.editedRowIndex = iRow;
}
function deleteRow(iRow)
{
  g_oJsTable.deleteRow(iRow);
}

function showTable()
{
  if (!JsTableNavFamily) { //If the browser doesn't support JsTable
    document.open('text/html','replace');
    document.write("<h3>Your browser does not support</h3>");
    document.write("Use the following browsers:<ul>");
    document.write("<li>Gecko browsers which is equivalent with or newer than Mozilla 1.4/Netscape 7.1</li>");
    document.write("<li>Opera 7.5 or newer</li>");
    document.write("<li>Internet Explorer 6</li></ul>");
    document.write("Some versions is still buggy. Have the best browser.");
    document.close();
    return;
  }
  
  var oSbm = document.getElementById('submit');
  var align;
  var columns = [
    new JsTableColumnString('String','string',120),
    new JsTableColumnNumber('Number','number',75),
    new JsTableColumnNumber('Denominated Number','denNumber',75,'right',true,2),
    new JsTableColumnBoolean('Boolean','boolean',50),
    new JsTableColumnOptions( 'Options','options',100,align,
      new JsTableOptionValues(['Satu','Dua','Tiga'],['_Satu','_Dua','_Tiga']) ),
    new JsTableColumnDate('Date','date',70,'center'),
    new JsTableColumnDateTime('Date Time','datetime',140,'center',true),
    new JsTableColumnLink('','link',50,'center','Edit',
      'javascript:editRow(__ROW__)'),
    new JsTableColumnLink('','link',50,'center',document.createTextNode('Delete'),
      'javascript:deleteRow(__ROW__)')
  ];
//  JsTableColumnLink(columnName,inputName,width,align,identifier,href)
  var oTableModel = new JsTableModel(columns,true,'.','d-m-y',true);
  oTableModel.enterAfterEdit = JsTable.ENTER_MOVE_RIGHT;
  oTableModel.isCellEditable = function(iRow,iCol) {
    //if (iRow==1) return false;
    return true;
  };
  g_oJsTable = new JsTable('trans',oTableModel,
    document.getElementById('showTable'),oSbm);
  
  g_oJsTable.insertRow(['aaa',1234.8,22456,true,'Tiga','9-4-2005','31-7-1978 21:0:0']);
  g_oJsTable.insertRow(['yaa',-.98,'234x6',false,'Dua','','27-1-1981 9:0:0']);
  for (var i=1; i<5; i++) {
    g_oJsTable.insertRow([]);
  }
}
//-->
</script>
</head>
<body onload='showTable()' style="width:90%">
<p>Hello this is editable, navigatable and submitable table. Click on the table to give it the focus.
<br>The decimal point of numbers in the table is represented by comma ( , ) not period ( . ).
The date time format is 'dd-mm-yyyy hh:nn:ss'</p>
<noscript><h2>Turn on your JavaScript!!</h2></noscript>
<form method=post action='JsTable.php' target='submittedValues'>
<div id=showTable align=center style="width:100%">
  <div id=submit>
    <input type=submit value=Submit>
    <input type=button value='Add Row' onclick="addRow()">
  </div>
  <div>Button 'Submit' is to submit the content of table to web server.
  Button 'Add Row' is to add row in the table</div>
</div>
</form>

<div>&nbsp;</div>
<div align=center>
<h4 style="width:791px; margin:0px"><div align=left>How To Edit and Navigate :</div></h4>
<div align=left style="font-family:sans-serif;font-size:13px; border-style:solid; border-width:1px;
border-color:black; background-color:#c0c0c0; color:#404000; height:150px; width:775px;
overflow:scroll; padding:8px; text-align:justify">

To give focus to table, click on the table. The cell on which we click,
automatically become the active cell. The active cell is the cell in which
we can edit its content. If usually its cell
editor is hidden for that cell, when the cell is active, it reveals its
cell editor, so we can edit it.<br>
<br>
Navigation:<br>
&nbsp;&nbsp;&nbsp; We can move the active cell by pressing the arrow
keys on keyboard by intuitive direction. We can also click on a cell,
if we want that cell become active.<br>
<br>
Editing:<br>
&nbsp;&nbsp;&nbsp; To edit the cell's content needs some steps which is
different for different cell editor. I will explain for each cell
editor.<br>
<ul>

  <li><u>Textbox</u> (element <span
 style="font-family: monospace; font-size: 12px;">&lt;TEXTAREA&gt;</span>
or <span style="font-family: monospace; font-size: 12px;">&lt;INPUT
type=text&gt;</span>)<br>
To edit, press Enter. Then we will go into the state, we call it <span
 style="font-style: italic;">editing state</span>. In editing state,
the text cursor will appear and thus we can edit the cell's content.
When editing state, the arrow keys cannot be used to navigate but used
to move text cursor instead. To end the editing state, press Enter
again. Another way to go into editing state is by pressing F2 (on
Opera, F2 has the meaning for the browser itself). To end the editing
state, press Esc. Pressing any valid character will also set the JsTable
into editing state automatically (as of version 0.4.5).
We can also double click on a cell (may be triple
click) if we want to edit the cell. But to end the editing state, still
use the keyboard. Clicking on another cell, when the active cell is
edited, will end the editing state of current active cell and move the
active cell to the cell has just been clicked. When on the number column,
you may only press the valid character for a number. So do the date column
and datetime column.</li>
  <li><u>Checkbox</u> (element <span
 style="font-family: monospace; font-size: 12px;">&lt;INPUT
type=checkbox&gt;</span>)<br>

To change the state of the checkbox, press Enter or Space. Clicking on
the cell will also change the checkbox' state.</li>
  <li><u>Combobox</u> (element <span
 style="font-family: monospace; font-size: 12px;">&lt;SELECT&gt;</span>)<br>
To edit, press Enter and then going into editing state. Use up arrow
and down arrow, to change the option. Press enter again, to end the
editing state. Key F2 can also be used but key Esc may not be useful in
all browsers. Esc may reset the option. Another way to change the
option, when in editing state, press Alt+down_arrow, the the combobox
will reveal its option, choose option by pressing down arrow or up
arrow and then press Enter to end the editing state. This way doesn't
run perfectly in all browsers.<br>
If you want to use mouse, do with usual steps: click on the combobox
and choose the option.</li>
  <li><u>Link</u><br>
The Link cannot be edited but only execute its link's target. To
execute its target, press Enter or click the Link.<br>
  </li>
</ul>

This software is licensed under GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License or (at your option) any later version.<br>
See the JsTable's <a href='./JsTable.js' target='_blank'>source</a> and the
<a href='./JsTable_Documentation.html' target='_blank'>documentation</a>.<br>
Send the bugs report to <a href="mailto:almulyana@yahoo.com">almulyana@yahoo.com</a>.
</div>
<small>Copyright &copy; 2005 AT Mulyana</small>
</div>

</body>
</html> 

           
         
  








JsTable20050725.zip( 34 k)

Related examples in the same category

1.Table Sorter Proof of Concept
2.Create 100 random rows and do the column sorting
3.Sortable table column
4.Table row style with 'mouse roll over' effect
5.Pagable Table
6.Repeat table column at the table bottom
7.Editable table cell
8.Table row with mouse roll over
9.Adding a table row
10.List box ( Grid Table )
11.List view (Selection between two list)
12.Basic Grid (Table)
13.Grid (Table) with colored cells
14.Build a Grid (Table) from CSV data file
15.Build a Grid (Tree) from a tab separated file
16.Build a Grid (Table) from yahoo quotes (IE only)
17.Grid (Table) with image in cells
18.Grid (Table) with multiple selection
19.Grid (Table) with row header and tooltips
20.One page two grid (Table)
21.Data in Grid (Table) from XML file
22.Grid (Table) with Data Island and Column highlight
23.Grid (Table) with cell format and XML data set
24.Grid (Table) data from RSS (IE only)
25.Grid (Table) data from xml file 2
26.Grid (Table) with news feeder (IE only)
27.Grid (Table) with quotes feeder (IE only)
28.Grid (Table) in Unix style
29.Sortable Grids (table)
30.Selectable Cells Grid Table
31.Editable Grid Table cell
32.Call back action in Grid Table
33.Table Grid Rows Selection
34.Grid Table Columns Resize
35.Table Grid Column Renderers
36.Sorting for each column or double click to edit its contents.
37.Table Grid style and sort the style
38.Web Data Grid Table
39.Sortable Tables from Scratch with MochiKit
40.Ajax Table
41.Drag and drop table columns
42.Dynamic data grid: add a row, remove a row and sort column
43.super table
44.GUI for table creation
45.Table sorter
46.Sortable Table