////////////////////////////////////////////////////////////////////
// ActionObj class
ActionObj.CLASS_NAME = 'ActionObj';

// Database query limit
ActionObj.DB_QUERY_START = 0;
ActionObj.DB_SEARCH_LIMIT = 100;

// Constants for Action URLS
ActionObj.ACTION_CACHE = 'Cache';
ActionObj.ACTION_CREATE = 'Create';
ActionObj.ACTION_DELETE = 'Delete';
ActionObj.ACTION_GET_OBJS = 'Get';
ActionObj.ACTION_UPDATE = 'Update';

// Constants for object state
ActionObj.OBJ_STATE_CREATING = 'Creating';
ActionObj.OBJ_STATE_DELETING = 'Deleting';
ActionObj.OBJ_STATE_EDITING  = 'Editing'
ActionObj.OBJ_STATE_VIEWING  = 'Viewing';

// Constants for edit type
ActionObj.EDIT_TYPE_IN_CUR_WIN = "EditInCurWin";
ActionObj.EDIT_TYPE_IN_NEW_WIN = "EditInNewWin";

// Constants for box dimensions
ActionObj.DEFAULT_BOX_HEIGHT = "250px";
ActionObj.DEFAULT_BOX_WIDTH = "300px";

ActionObj.totalChecked = 0;
ActionObj.uniqueId = 0;
ActionObj.sortBy = "";
ActionObj.sortDir = "";
ActionObj.sortLegend = undefined;

// Fields for check state
ActionObj.prevChkID = 0;
ActionObj.prevChkSrc = '';

/*
ActionObj.htObj = new Hashtable();
ActionObj.totalObj = 0;

ActionObj.htCachedClasses = new Hashtable();
ActionObj.htLoadedClasses = new Hashtable();

*/

// Constants for view type
ActionObj.VIEW_TYPE_TABLE = "Table";
ActionObj.VIEW_TYPE_WINDOWS = "Windows";

/*
ActionObj.BLANK_HTML_URL = IBCSUtils.MY_WEBS_URL + "IBCSBase/php/blankHTML.html";
ActionObj.BLANK_TEXT_URL = IBCSUtils.MY_WEBS_URL + "IBCSBase/php/blankText.php";
*/

// Type of display for arrow action links
ActionObj.ARROW_ACTION_REGULAR = 'reg';
ActionObj.ARROW_ACTION_POPUP = 'pop';
ActionObj.ARROW_ACTION_OPEN_TYPE = ActionObj.ARROW_ACTION_POPUP;

// Constants for window opening
ActionObj.OPEN_WIN_STYLE_BROWSER_DEFAULT = 'Browser Default';
ActionObj.OPEN_WIN_STYLE_POPUP = 'Popup';
ActionObj.OPEN_WIN_STYLE = ActionObj.OPEN_WIN_STYLE_POPUP;


// Constructor
function ActionObj(cls, obj, xml) {
  this.cls = cls;
  this.obj = obj;
  this.isChecked = false;
  this.isInner = false;
  this.isVisible = false;
  this.rowCls = 'even';

  ActionObj.totalObj++;

  // Set object fields from xml
  if (xml != null) {
    this.objState = ActionObj.OBJ_STATE_VIEWING;
    for (var fn in cls.OBJ_FIELDS) {
      var fI = cls.OBJ_FIELDS[fn];
      obj[fn] = $attr(xml, fI.xml);
    }
    cls.htObj.put(obj.getID(), obj);
  } else {
    this.objState = ActionObj.OBJ_STATE_CREATING;
  }
  if (cls.ALL_FIELDS == null) {
    cls.ALL_FIELDS = {};
    for (var fn in cls.OBJ_FIELDS) {
      var fI = cls.OBJ_FIELDS[fn];
      cls.ALL_FIELDS[fn] = fI;
    }
    if (cls.SPL_FIELDS != undefined) {
      for (var fn in cls.SPL_FIELDS) {
      	var fI = cls.SPL_FIELDS[fn];
  	cls.ALL_FIELDS[fn] = fI;
      }
    }
  }
	
  this.id = ActionObj.uniqueId;
  ActionObj.uniqueId++;

  // Create table row
  this.arrTblRows = new Array();
  var cell;
  if (this.cls.TBL_CFG != undefined) {
    this.arrTblRowCells = {};
    this.TBL_CFG = this.cls.TBL_CFG;
    var numRows = this.TBL_CFG.length;
    for (var i=0; i<numRows; i++) {
      var tableRow = document.createElement('tr');
      this.arrTblRows.push(tableRow);
      tableRow.vAlign = 'top';
      tableRow.className = 'even';
      var rowCfg = this.TBL_CFG[i];
      var numCells = rowCfg.length;
      for (var j=0; j<numCells; j++) {
        var cI = rowCfg[j];
        var f = cI.f;
        var cs = $addParam('1', 'cs', cI);
        var rs = $addParam('1', 'rs', cI);
        cell = tableRow.insertCell(j);
	cell.colSpan = cs;
	cell.rowSpan = rs;
	this.arrTblRowCells[f] = cell;
      }
      if (i==0) {
        var cb = this.getCheckboxText();
        cell = tableRow.insertCell(0);
        cell.rowSpan = numRows;
        cell.innerHTML = '<div class="btn" onclick="ActionMenu.open({\'actionObjID\':'+this.id+',\'div\':this})">A</a></div>';
        cell = tableRow.insertCell(0);
        cell.rowSpan = numRows;
        cell.innerHTML = cb;
      }
    }
    var rowID = this.id; 
//    tableRow.onclick = function() {ActionObj.rowClicked(rowID)};
  }

  // Create info box
  var infoBox = document.createElement('table');
  this.infoBox = infoBox;
  infoBox.className = "infoTbl";
  infoBox.onmouseover = function() {ActionObj.hlgtInfoBox(this)};
  infoBox.onmouseout = function() {ActionObj.unhlgtInfoBox(this)};

  var height = $addParam(ActionObj.DEFAULT_BOX_HEIGHT, 'BOX_HEIGHT', this.cls);
  var width = $addParam(ActionObj.DEFAULT_BOX_WIDTH, 'BOX_WIDTH', this.cls);

  var cell;
  var row;

  infoBox.style.width = width;
  infoBox.style.height = height;

  // Create the info box title row
  row = infoBox.insertRow(0);
  var infoBoxTitle = row.insertCell(0);
  infoBoxTitle.className = 'infoTblTop';
  this.infoBoxTitle = infoBoxTitle;

  // Create the info box body row
  row = this.infoBox.insertRow(1);
  var infoBoxBody = row.insertCell(0);
  infoBoxBody.className = 'infoTblMiddle';
  this.infoBoxBody = infoBoxBody;

  // Create the info box actions row
  row = this.infoBox.insertRow(2);
  var infoBoxActions = row.insertCell(0);
  infoBoxActions.className = 'infoTblBottom';
  this.infoBoxActions = infoBoxActions;

  ActionObj.htObj.put(this.id, this);
}

/////////////////////////////////////////////////////////////////////////////////////
// Static functions 
/////////////////////////////////////////////////////////////////////////////////////

/*

*/

// Static method to create an arrow action link
ActionObj.createArrowActionLink = function(cfg) {
  var url = cfg.url;
  var t = cfg.t;

  var filtersDisplay = $addParam('hide', 'filtersDisplay', cfg);
  var autoSearch = $addParam('Y', 'autoSearch', cfg);

  url = IBCSUtils.MY_WEBS_URL + url + '&filtersDisplay=' + filtersDisplay + '&autoSearch=' + autoSearch;

  var img = '<img src="' + IBCSUtils.ACTION_ARROW_IMG + '" border=0 align="absmiddle">';

  var link = "";
  switch(ActionObj.ARROW_ACTION_OPEN_TYPE) {
    case ActionObj.ARROW_ACTION_REGULAR:
      link = '<a href="' + url + '" target="_new">' + img + '</a>';
      break;

    case ActionObj.ARROW_ACTION_POPUP:
      var w = $addParam('1400', 'w', cfg);
      var h = $addParam('', 'h', cfg);
      var winID = $addParam('', 'id', cfg);
      link = ActionObj.createPopUpLink(img, url, w, h, winID);
      break;
  }

  var u = t + link;
  return u;
}

// Static method to create a popup link
ActionObj.createPopUpLink = function(linkText, url, w, h, winID) {
  var cfg2 = "{'u':'" + url + "','id':'" + winID + "','w':'" + w + "','h':'" + h + "'}";
  var link = "<a href=\"javascript:ActionObj.popup(" + cfg2 + ")\">" + linkText + "</a>";
  return link;
}

// Static method to popup a window
ActionObj.popup = function(cfg) {
  var url = cfg.u;

  var url = $addParam('http://www.interactivebrokers.com', 'u', cfg);
  var winID = $addParam('', 'id', cfg);
  var w = $addParam('1000', 'w', cfg);
  var h = $addParam('800', 'h', cfg);
  var pos = $addParam('center', 'p', cfg);

  var myleft = "";
  var mytop = "";

  if (pos == "center") {
    myleft = (screen.width) ? (screen.width-w) / 2:100;
    mytop = (screen.height) ? (screen.height-h) / 2:100;
  } else if ((pos !='center' && pos != "random") || pos == null) {
    myleft = 0; mytop = 20
  }

  var settings = "width=" + w + ", height=" + h + ",top=" + mytop + ",left=" + myleft + ", scrollbars=yes, location=no, directories=no, status=no, menubar=no, toolbar=no, resizable=yes";
  var win = window.open(url, winID, settings);
}


ActionObj.popupActionObjData = function(vars) {
  var blankUrl = ActionObj.BLANK_HTML_URL;
  var win = IBCSUtils.openWin(blankUrl,vars.winName,vars.winWidth,vars.winHeight,'center','front');

  var d = win.document;

  d.title = vars.title;

  var b = "";

  var cssUrl = IBCSUtils.MY_APPS_BASE_URL + 'php/css/';
  var jsUrl = IBCSUtils.MY_APPS_BASE_URL + 'php/js/';
  var c = '<html><head><title>' + vars.title + '</title>' +
          '<link rel="stylesheet" type="text/css" href="' + cssUrl + 'templateStyle.css">' +
          '<link rel="stylesheet" type="text/css" href="' + cssUrl + 'ibcsStyle.css">' +
          '<link rel="stylesheet" type="text/css" href="' + cssUrl + 'infoBoxStyle.css">' +

          '<script type="text/javascript" src="' + jsUrl + 'Hashtable.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'FormFilter.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'CSVFunc.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl  + 'IBCSUtilsV2.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'ActionObjV3.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'ActionMenuV2.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'NewWin.js"></script>' +
          '<script type="text/javascript" src="' + jsUrl + 'SortableTable.js"></script>';

  c += '</head><body>';
  c += b;
  c += '</body></html>';

  d.write(c);
  d.close();
}


// See if an object is already cached
ActionObj.testObjExists = function(cls, id) {
  var o = cls.actionObj.cls.htObj.get(id);
  if (o != undefined) {
    return true;
  }
  return false;
}

// Static method to create hash of actionobjids to be used by a function
ActionObj.createIDListForAction = function(cfg) {
  var htAOID = new Hashtable();

  // An object is passed so use it
  if (cfg != undefined && cfg.actionObjID != undefined) {
    var aoID = cfg.actionObjID;
     htAOID.put(aoID, aoID);
     return htAOID; 
  }

  // Loop thru the checkboxes
  var cbs = document.getElementsByName("cbList[]");
  var numCb = cbs.length;
  for (i=0; i<numCb; i++) {
    if (cbs[i].checked) {
      var aoID = cbs[i].id;
      aoID = aoID.substr(3,aoID.length);
      htAOID.put(aoID, aoID);
    }
  }

  if (htAOID.size() == 0) {
    alert("No items selected!!");
  }
 
  return htAOID; 
}

// Static method to run a get on a series of objects
ActionObj.showRelatedInfo = function(aoCfg, filterCfg) {
  var filters = filterCfg.filters;
  var key = filterCfg.key;
  var specialFilters = filterCfg.specialFilters;
  var url = filterCfg.url;

  var winID = $addParam('', 'winID', filterCfg)
  var winWidth = $addParam('1200', 'winWidth', filterCfg)
  var winHeight = $addParam('800', 'winHeight', filterCfg)


  var htAOID = ActionObj.createIDListForAction(aoCfg);
  var size = htAOID.size();
  if (size==0) {
    return;
  }

  var htKey = new Hashtable();
  for (var id in htAOID.hash) {
    var ao = ActionObj.get(id);
    var objID = ao.obj[key];
    if (!htKey.containsKey(objID)) {
      filters += objID + ",";
      htKey.put(objID, objID);
    }
  } 
  filters = filters.substr(0, filters.length-1);

  url += filters + specialFilters + "&filtersDisplay=hide&autoSearch=Y";
  IBCSUtils.openWin(url,winID,winWidth,winHeight,'center','front');
}


// Static method to try a bulk function on a group of objects
ActionObj.tryBulkFunc = function(obj, func) {

  var htAOID = new Hashtable();
  var numChecked = 0;
  var cbs = document.getElementsByName("cbList[]");
  var numCb = cbs.length;
  for (i=0; i<numCb; i++) {
    if (cbs[i].checked) {
      var aoID = cbs[i].id;
      aoID = aoID.substr(3,aoID.length);
      htAOID.put(aoID, aoID);
      numChecked++;
    }
  }

  if (numChecked == 0) {
    alert("Cannot perform action when no items are selected!");
    return;
  }

  var doIt = confirm("A total of " + numChecked + " items are selected.\nAre you sure you want to perform the action '" + func + "' on all these items?");
  if (!doIt) {
    return;
  }

  try {
    var vars = {'htAOID':htAOID,'isBulk':'Y'};
    var t = obj + "." + func + "(vars)";
    eval(t);
  } catch(err) {
    alert("Error tryinig: " + t + "\n" + err);
  }
}

// Static function to change the check state
ActionObj.changeCheckState = function(aoID, src) {
  var cb = $('chk'+aoID);

  // When the user clicks on the checkbox, the row will click will occur first
  // and then the checkbox. We need to reverse the row click change.
  if (aoID == ActionObj.prevChkID && src != ActionObj.prevChkSrc && src == 'cb') {
    cb.checked = !(cb.checked);
  }

  var checked = cb.checked;
  var actionObj = ActionObj.get(aoID);

  if (checked) {
    actionObj.isChecked = true;
    ActionObj.setCheckTotal(ActionObj.totalChecked+1);
  } else {
    actionObj.isChecked = false;
    ActionObj.setCheckTotal(ActionObj.totalChecked-1);
  }

  ActionObj.prevChkID = aoID;
  ActionObj.prevChkSrc = src;

  actionObj.updateGUI();
}

// Static method called when the row a row is clicked on
ActionObj.rowClicked = function(aoID) {
  var cb = $('chk' + aoID);
  cb.checked = !(cb.checked);

//  alert("rowClicked setting: " + cb.checked);

  ActionObj.changeCheckState(aoID, 'row');
}

// Static method to update the checked total
ActionObj.setCheckTotal = function(checkTotal) {
  ActionObj.totalChecked = Math.max(0, checkTotal);
  var t = '&nbsp;';
  if (ActionObj.totalChecked>0) {
    t = '<b>' + ActionObj.totalChecked + '</b> selected';
  }
  $('itemSelectCount').innerHTML = t;
}

// Static function to check/uncheck all ActionObj checkboxes
ActionObj.checkAll = function() {
  var cbs = document.getElementsByName("cbList[]");
  var numCb = cbs.length;

  var chk = true;
  if (ActionObj.totalChecked == numCb) {
    chk = false;
  }

  ActionObj.totalChecked = 0;
  for (i=0; i<numCb; i++) {
    cbs[i].checked = chk;
    var aoID = cbs[i].id;
    aoID = aoID.substr(3,aoID.length);
    ActionObj.changeCheckState(aoID, 'cb');
  }
}

// Static function to uncheck all ActionObj checkboxes
ActionObj.uncheckAll = function() {
  var cbs = document.getElementsByName("cbList[]");
  var numCb = cbs.length;

  var chk = false;

  ActionObj.totalChecked = 0;
  for (i=0; i<numCb; i++) {
    cbs[i].checked = chk;
    var aoID = cbs[i].id;
    aoID = aoID.substr(3,aoID.length);
    ActionObj.changeCheckState(aoID, 'cb');
  }
}

// Static method to check the query results
ActionObj.checkQueryInfo = function(xmlDoc) {
  var qiXml = xmlDoc.documentElement.getElementsByTagName("qi")[0];
  var time = $attr(qiXml, 'tm');
  var totalRecords = $attr(qiXml, 'tr');

  return {'time':time,'totalRecords':totalRecords};
}

ActionObj.get = function(aoID) {
  return ActionObj.htObj.get(aoID);
}

ActionObj.remove = function(aoID) {
  try {
    ActionObj.totalObj--;
    var actionObj = ActionObj.htObj.remove(aoID);
    actionObj.cls.htObj.remove(actionObj.obj.getID());
  } catch(e) {}
}

ActionObj.clearClass = function(cls) {
  if (cls == null) {
    return;
  }

  // Do not remove objects from classes that need to be cached
  if (ActionObj.htCachedClasses.containsKey(cls.CLASS_NAME)) {
    return;
  }

  var numRemoved = ActionObj.removeClass(cls);

  // Should be removed
  if (cls.onClear) {
    alert("This is old and needs to be removed. Please inform chrish!\n" + cls);
    cls.onClear();
  }
}

ActionObj.removeClass = function(cls) {
  var numRemoved = 0;
  for (var id in cls.htObj.hash) {
    var obj = cls.htObj.get(id);
    if (obj != null) {
      ActionObj.remove(obj.actionObj.id);
      numRemoved++;
    }
  }
  cls.htObj.clear();
  cls.htObj = new Hashtable();
  return numRemoved;
}

ActionObj.createEditFieldText = function(actionObj, fn) {
  var aoID = actionObj.id;
  var o = actionObj.obj;
  var fI = actionObj.cls.ALL_FIELDS[fn];
  var type = fI.t;
 
  var u = $addParam('', 'u', fI);
  var myCls = 'editBox';
  var myDis = '';
  if (u == '' || (actionObj.objState == ActionObj.OBJ_STATE_EDITING && u == 'C')) {
    myCls = 'viewBox';
    myDis = 'Y';
  }

  var t = "Not supported";
  switch(type) {
    case 'tf':
    case 'dbl':
    case 'int':
    case 'url':
      t = $mkTF({'id':fn+aoID,'name':fn,'value':o[fn],'cls':myCls,'dis':myDis});
      break;
    case 'ta':
      t = $mkTA({'id':fn+aoID,'name':fn,'value':o[fn],'cls':myCls,'dis':myDis});
      break;
    case 'sel':
      eval("var ht = " + fI.arr + ".htObj");
      t = $mkSel({'id':fn+aoID,'name':fn,'ht':ht,'sel':o[fn],'cls':myCls,'dis':myDis});
      break;
    case 'dt':
      var id = fn+aoID;
      t = $mkTF({'id':id,'name':fn,'value':o[fn],'cls':myCls,'dis':myDis,'ro':'Y','clk':'CalPopup.showDateSelector(\'' + id + '\')'});
      break;
    case 'cre':
      t = o.createdDate + ":" + o.createdBy;
      break;
    case 'mod':
      t = o.modifiedDate + ":" + o.modifiedBy;
      break;
    case 'logo':
      t = o.getLogo();
      break;
    case 'box':
      var func = fI.f;
      eval("t = o." + func);
      break;
    case 'fc':
    case 'func':
      var func = fI.f;
      var c = fI.c;
      if (c != undefined) {
        var cmd = "t = " + c + "." + func + "(o)";
        eval(cmd);
      } else {
        eval("t = o." + func);
      }
      if (fI.a && fI.a == 'r') {
        t = '<div style="float:right; padding-right:5px;">' + t + '</div>';
      }
      break;
    case 'empty':
      t = "&nbsp;";
      break;
  }
  return t;
}

ActionObj.createViewFieldText = function(actionObj, fn) {
  var o = actionObj.obj;
  var fI = actionObj.cls.ALL_FIELDS[fn];

  try {
    var type = fI.t;
  } catch(e) {
    alert("createViewFieldText() prob with fieldName: " + fn + ", actionObj: " + actionObj);
  }

  var t = "Not supported";
  switch(type) {
    case 'tf':
    case 'ta':
    case 'dt':
    case 'int':
    case 'dbl':
      t = o[fn];
      if (type == 'int' || type == 'dbl' || (fI.a && fI.a == 'r')) {
          t = '<div style="text-align: right; padding:0; margin: 0">' + t + '</div>';
      }
      break;
    case 'sel':
      if (fI.arr.translateObj != undefined) {
        t = fI.arr.translateObj(o[fn]);
      } else {
        eval("var co = " + fI.arr + ".htObj.get(o[fn])");
        t = "Not Cached";
        if (co != undefined) {
          t = co.toString();
        }
      }
      break;
    case 'url':
      t = IBCSUtils.formatWebsite(o[fn]);
      break;
    case 'cre':
      t = o.createdDate + ":" + o.createdBy;
      break;
    case 'mod':
      t = o.modifiedDate + ":" + o.modifiedBy;
      break;
    case 'logo':
      t = o.getLogo();
      break;
    case 'box':
      var func = fI.f;
      eval("t = o." + func);
      break;
    case 'ofl':
      t = ActionObj.getObjField(o, fI);
      if (fI.a && fI.a == 'r') {
        t = '<div style="text-align: right; padding:0; margin: 0">' + t + '</div>';
      }
      break;
    case 'fc':
    case 'func':
      var func = fI.f;
      var c = fI.c;
      if (c != undefined) {
        var cmd = "t = " + c + "." + func + "(o)";
        eval(cmd);
      } else {
        eval("t = o." + func);
      }
      if (fI.a && fI.a == 'r') {
        t = '<div style="float:right; padding-right:5px;">' + t + '</div>';
      }
      break;
    case 'empty':
      t = "&nbsp;";
      break;
  }
  return t;
}

// Make a tiny row
ActionObj.makeInnerObj = function(cls) {
  var o = new cls;
  o.actionObj.isInner = true;
  o.actionObj.updateGUI();
  return o;
}

// Static method to change the sort by field
ActionObj.sortResults = function(elem, id) {
  var sortDir = 'ASC';
  // User is clicking on a different field - use ASC sort
  if (id != ActionObj.sortBy) {
    if (ActionObj.sortLegend != undefined) {
      ActionObj.sortLegend.className = null;
    }
  } else {
    if (ActionObj.sortDir == 'ASC') {
      sortDir = 'DESC';
    }
  }

  ActionObj.sortBy = id;
  ActionObj.sortDir = sortDir;
  ActionObj.sortLegend = elem;
  elem.className = sortDir;

  // Get the objects from the server via AJAX
  if (ActionObj.curClass != undefined) {
    ActionObj.startFromZero(ActionObj.curClass);
  }
}

ActionObj.addRowsToInnerTable = function(vars) {
  var aoID = vars.aoID;
  var ht = vars.ht;
  var tblID = vars.tblID;
	
  var tbl = $(tblID+aoID);

  var rc = 0;
  for (var iID in ht.hash) {
    var obj = ht.get(iID);
    obj.actionObj.isInner = true;
    var rowCls = 'even';
    if (rc++ % 2 == 0) {
      rowCls = 'odd';
    }
    obj.actionObj.setRowCls(rowCls);
    obj.actionObj.updateGUI();
    var nR = obj.actionObj.arrTblRows.length;
    for (var i=0; i<nR; i++) {
      tbl.appendChild(obj.actionObj.arrTblRows[i]);
    }
  }
}


// Make an inner table
ActionObj.makeInnerTable = function(vars) {
  var actionObj = vars.ao;
  var id = vars.id;
  var style = vars.st;
  var colHead = vars.ch;
  var btns = vars.btn;
  var nc = vars.nc;

  var t = '<table id="' + id + actionObj.id + '" class="stdtbl" style="' + style + '">';
  t += '<tr>' + colHead + '</tr>';
	
  var btnTxt = "";
  for (var b in btns) {
    var bi = btns[b];
    btnTxt += $mkAOBtn(bi, actionObj);
  }
  var allBtn = '<div class="btn" onclick="javascript:$chkAll()">All</div>';
	
  btnTxt= '<tr><td>' + allBtn + '</td><td colspan="' + (nc+1) + '">' + btnTxt + '</td></tr>';
  t += btnTxt;
  t += '</table>';
  return t;
}

// Static method called after retrieving an xml of objects from the server
// Creates js objects and caches them
ActionObj.cacheAjaxObjects = function(xmlDoc, vars) {
	
  var e = ActionObj.checkXMLForErrors(xmlDoc);
  var w = ActionObj.checkXMLForWarnings(xmlDoc);

  var sourceObj = vars.objType;

  var arrNewCls = new Array();
  var arrNewObj = new Array();

  // Loop thru obj array and create the objects
  var clsXml = xmlDoc.documentElement.getElementsByTagName("cls");
  for (var i=0; i<clsXml.length; i++) {
    var cls = $attr(clsXml[i], 't');

    ActionObj.htLoadedClasses.put(cls, cls);

    try {
      eval("var o = new " + cls + "()");
    } catch(err) {
      e += "No class defined for: " + cls + "!\n";
      continue;
    }

    arrNewCls.push(o);
    var objXml = clsXml[i].getElementsByTagName("obj");
    for (var j=0; j<objXml.length; j++) {
      try {
        eval("var o = new " + cls + "(objXml[j])");
        arrNewObj.push(o);
      } catch(err) {
        e += err + "\n";
      }
    }
  }

  // Loop the classes and see if any need to run an onCache method
  var nC = arrNewCls.length;
  for (var i=0; i<nC; i++) {
    var o = arrNewCls[i];

    if (o.actionObj.cls.onCache) {
      o.actionObj.cls.onCache();
    }
    ActionObj.remove(o.actionObj.id);
  }
	
  // Update the GUIs after all the xml has been parsed and stored
  if (vars.noGUIUpdate == undefined) {
    var nO = arrNewObj.length;
    for (var i=0; i<nO; i++) {
      var o = arrNewObj[i];
      o.actionObj.updateGUI();
    }
  }

  // Show errors/warnings if any
  IBCSUtils.checkErrorsWarnings(e, w);

  if (vars.noCountUpdate != undefined) {
    if (vars.specialFunc != undefined) {
      var f = vars.specialFunc;
      f(xmlDoc, vars);
    }
    return;
  }

  IBCSUtils.submitBtnInactive();
  
  // Update the result count
  var qi = ActionObj.checkQueryInfo(xmlDoc);
  var time = qi.time;
  var totalRecords = qi.totalRecords;

//  alert("TotalRecords: " + totalRecords);

  var startAt = ActionObj.DB_QUERY_START+1;
  var endAt = Math.min(totalRecords, startAt+ActionObj.DB_SEARCH_LIMIT-1);
  if (totalRecords == 0) {
    startAt = 0;
    var resultCntTxt = "None found";
  } else {
    var resultCntTxt = startAt + "-" + endAt + " of " + totalRecords;
    if (endAt<totalRecords) {
      resultCntTxt += ' <a href="javascript:ActionObj.getNext()">Next</a>';
    }
    if (startAt != 1) {
      resultCntTxt = '<a href="javascript:ActionObj.getPrev()">Prev</a> ' + resultCntTxt;
    }
  }

//  alert("Result count Text: " + resultCntTxt);
  
  $('resultTm').innerHTML = ' (' + time + ' sec)';
  $('resultCountArea').innerHTML = resultCntTxt;
	
  // Refresh the view
  ActionObj.showSearchResults();

  if (vars.specialFunc != undefined) {
    var f = vars.specialFunc;
    f(xmlDoc, vars);
  }
}

// Get the next 100 records starting from 0
ActionObj.startFromZero = function(cls) {
  ActionObj.DB_QUERY_START = 0;
  IBCSUtils.submitSearch(cls);
}

// Get the next group of records
// The amount is set from ActionObj.DB_SEARCH_LIMIT and normally 100
ActionObj.getNext = function() {
  ActionObj.DB_QUERY_START += ActionObj.DB_SEARCH_LIMIT;
  IBCSUtils.submitSearch(ActionObj.curClass);
}

// Get the previous group of records
// The amount is set from ActionObj.DB_SEARCH_LIMIT and normally 100
ActionObj.getPrev = function() {
  ActionObj.DB_QUERY_START -= ActionObj.DB_SEARCH_LIMIT;
  IBCSUtils.submitSearch(ActionObj.curClass);
}

// Static method to request a cache of objects
ActionObj.cacheObj = function(vars) {
  var cls = vars.cls;
  ActionObj.htCachedClasses.put(cls, cls);

  cls = eval(cls);
  var url = ActionObj.getAjaxURL({'action':ActionObj.ACTION_CACHE,'cls':cls});

  // Send an xml to cache the objects
  var vars2 = new Array();
  vars2.abortFunction = AJAXConn.abortedAjaxQuery;
  vars2.desc = cls.CLASS_NAME + " Cache Objects";
  vars2.functionToCall = ActionObj.cacheAjaxObjects;
  vars2.objType = cls;
  vars2.params = vars;
  vars2.url = url;
  vars2.noCountUpdate = true;
  vars2.noGUIUpdate = true;
  AJAXConn.getXML(vars2);
}

ActionObj.changeDisplayType = function() {
  ActionMenu.closeNew();
  ActionObj.showSearchResults();
}

// Static function to check an xml returned by ajax for errors
ActionObj.checkXMLForErrors = function(xmlDoc) {
  var t = "";
  var errorXml = xmlDoc.documentElement.getElementsByTagName("error");
  for (var i=0; i<errorXml.length; i++) {
    var mesg = $attr(errorXml[i], 'm');
    t += mesg + "\n";
  }
  return t;
}

// Static function to check an xml returned by ajax for errors
ActionObj.checkXMLForWarnings = function(xmlDoc) {
  var t = "";
  var warningXml = xmlDoc.documentElement.getElementsByTagName("warning");
  for (var i=0; i<warningXml.length; i++) {
    var mesg = $attr(warningXml[i], 'm');
    t += mesg + "\n";
  }
  return t;
}

// Static method to save editing an object
ActionObj.getAjaxURL = function(vars) {
  var action = vars.action;
  var cls = vars.cls;
  var obj = vars.obj;

  var actionNew = action + cls.CLASS_NAME;

  var appUrl = "";
  if (cls.PKG_ROOT != undefined) {
    eval("appUrl = " + cls.PKG_ROOT + ".APP_URL");
  } else if (cls.APP_URL != undefined) {
    appUrl = cls.APP_URL;
  }

  var url = IBCSUtils.MY_WEBS_URL + appUrl + "?action=" + actionNew;
  switch(action) {
    case ActionObj.ACTION_GET_OBJS:
      if (typeof(cls.ACTION_GET_OBJ_FILTERS) == "object") {
        var numElems = cls.ACTION_GET_OBJ_FILTERS.length;
        for(var i=0; i<numElems; i++) {
          var cfg = cls.ACTION_GET_OBJ_FILTERS[i];
          url += AJAXConn.addAjaxQryFlt(cfg);
 	}
      }
      break;
  }

//  alert("url: " + url);
//  return "";

  return url;
}


// Static method to remove an action object and its related object
// Used on cancel create and delete of object
ActionObj.removeObj = function(vars) {
  var id = vars.actionObjID;
  var actionObj = ActionObj.get(id);

  var dispType = $getRBVal('dispType');

  if (actionObj.isInner) {
    var nR = actionObj.arrTblRows.length;
    for (var i=0; i<nR; i++) {
      var tableRow = actionObj.arrTblRows[i];
      tableRow.parentNode.removeChild(tableRow);
    }
  } else if (dispType == ActionObj.VIEW_TYPE_TABLE) {
    var tbl = $('objTbl');
    var nR = actionObj.arrTblRows.length;
    for (var i=0; i<nR; i++) {
      var tableRow = actionObj.arrTblRows[i];
      tbl.removeChild(tableRow);
    }
  } else if (dispType == ActionObj.VIEW_TYPE_WINDOWS) {
    var infoBox = actionObj.infoBox;
    var ta = $('searchResultsArea');
    ta.removeChild(infoBox);
  }
  ActionObj.htObj.remove(id);
}

// Static method to show the edit div
ActionObj.showEditDiv = function(o, title) {

  var body = ActionObj.getObjBody(o.actionObj)

  var actions = '<div class="btn" onclick="ActionObj.cancelEdit()")>Cancel</div><div class="btn" onclick="ActionObj.saveEdit(\'' + o.actionObj.id + '\')")>Save</div>';
  var winCfg = {'id':'editDiv','t':title,'a':actions,'b':body};
  var win = IBCSUtils.creWin(winCfg);
  document.body.appendChild(win);
}

// Create an object to be used for editing
ActionObj.createEditObj = function(cls, objState) {

  // Create new object to set its values
  var o = new cls();
  o.actionObj.objState = objState;

  // Determine which object params need to be set
  var htObjSettings = new Hashtable();
  for (var k in cls.OBJ_FIELDS) {
    var fI = cls.OBJ_FIELDS[k];
    var u = $addParam('', 'u', fI);
    if (u != 'A' && u != 'P') {
      continue;
    }
    var htVals = new Hashtable();
    htObjSettings.put(k, htVals);
  }
  ActionObj.curEditFields = htObjSettings;

  return o;
}

// Static method to create a new ActionObject sub class
ActionObj.create = function(cls) {

  var o = ActionObj.createEditObj(cls, ActionObj.OBJ_STATE_CREATING);

  // Display the edit div
  var title = 'Create ' + cls.CLASS_NAME;
  ActionObj.showEditDiv(o, title);
}

// Static method to delete one or more ActionObj sub classes
ActionObj.del = function(cls, cfg) {
  var htAOID = ActionObj.createIDListForAction(cfg);
  var numObj = htAOID.size();
  if (numObj==0) {
    return;
  }

  var doIt = confirm("A total of " + htAOID.size() + " items are selected.\nAre you sure you want to delete all of these '" + cls.CLASS_NAME + "'?");
  if (!doIt) {
    return;
  }

  for (var id in htAOID.hash) {
    var ao = ActionObj.get(id);
    var vars = {'actionObjID':ao.id};
    ActionObj.deleteObj(vars);
  }
}

// Static method to delete the object
ActionObj.deleteObj = function(vars) {
  var id = vars.actionObjID;
  var actionObj = ActionObj.get(id);
  var cls = actionObj.cls;
  var obj = actionObj.obj;

  actionObj.objState = ActionObj.OBJ_STATE_DELETING;

  // Add the id fields
  var idFilters = "";
  for (var fID in cls.OBJ_FIELDS) {
    var fI = cls.OBJ_FIELDS[fID];
    var u = $addParam('', 'u', fI);

    // An id field that is not set by user but is needed to be passed
    if (u == 'ID') {
      var v = obj[fID];
      idFilters += AJAXConn.addAjaxQryFlt2(v, fID);
    }
  }

  var appUrl = "";
  if (cls.PKG_ROOT != undefined) {
    eval("appUrl = " + cls.PKG_ROOT + ".APP_URL");
  } else if (cls.APP_URL != undefined) {
    appUrl = cls.APP_URL;
  }

  var url = IBCSUtils.MY_WEBS_URL + appUrl + "?action=" + ActionObj.ACTION_DELETE + cls.CLASS_NAME + idFilters;

  // Send an xml to update the object
  var vars2 = new Array();
  vars2.abortFunction = AJAXConn.abortedAjaxQuery;
  vars2.desc = "Delete " + cls.CLASS_NAME;
  vars2.functionToCall = ActionObj.updateObjDataFromXML;
  vars2.objType = cls;
  vars2.params = vars;
  vars2.url = url;
  AJAXConn.getXML(vars2);
}

// Static method to edit one or more ActionObject sub classes
ActionObj.edit = function(cls, cfg) {
  var htAOID = ActionObj.createIDListForAction(cfg);
  var numObj = htAOID.size();
  if (numObj==0) {
    return;
  }
  ActionObj.curEditIDs = htAOID;

  var o = ActionObj.createEditObj(cls, ActionObj.OBJ_STATE_EDITING);

  var htObjSettings = ActionObj.curEditFields;

  // Now loop thru all the objects the user checked and get the values for the needed fields
  // If there is only one value for the field this is used, otherwise empty
  for (var k in htAOID.hash) {
    var aoID = htAOID.get(k);
    var ao = ActionObj.get(aoID);
    if (ao==null) {
      continue;
    }

    for (var k2 in htObjSettings.hash) {
      var ht = htObjSettings.get(k2);
      var val = ao.obj[k2];
      ht.put(val, val);
    }
  }

  // Loop thru vals and set them if there is only one val
  for (var k in htObjSettings.hash) {
    var ht = htObjSettings.get(k);
    var numItems = ht.size();
    if (numItems != 1) {
      continue;
    }
    for (var k2 in ht.hash) {
      var val = ht.get(k2);
      o[k] = val;
    }
  }

  // Display the edit div
  var title = 'Edit ' + numObj + " " + cls.CLASS_NAME;
  ActionObj.showEditDiv(o, title);
}

// Cancel editing an object
ActionObj.cancelEdit = function() {
  ActionObj.curEditFields = null;
  ActionObj.curEditIDs = null;
  var div = $('editDiv');
  if (div != null) {
    document.body.removeChild(div);
  }
}

// Save one or more objects that have been edited
ActionObj.saveEdit = function(aoID) {
  var editAO = ActionObj.get(aoID);
  var objState = editAO.objState;
  var rootCls = editAO.cls;
  var rootObj = editAO.obj;

  var htFields = ActionObj.curEditFields;

  var errors = "";

  // If the user is creating a new object then the editObj and srcObj are the same
  // and there is no need to loop thru the id's
  if (objState == ActionObj.OBJ_STATE_CREATING) {
    var ok = ActionObj.validateEditFields(editAO, editAO, htFields);
    if (ok) {
      ActionObj.cancelEdit();
    }
    return;
  }

  // Loop the thru the vals one by one and try to submit them
  for (var k in ActionObj.curEditIDs.hash) {
    var srcAOID = ActionObj.curEditIDs.get(k);
    var srcAO = ActionObj.get(srcAOID);
    if (srcAO==null) {
      continue;
    }
    ActionObj.validateEditFields(editAO, srcAO, htFields);
  }

  ActionObj.uncheckAll();
  ActionObj.cancelEdit();
}

// Validat the fields and if there are no errors send in the request
ActionObj.validateEditFields = function(editAO, srcAO, htFields) {
  var aoID = editAO.id;
  var cls = editAO.cls;

  var editObj = editAO.obj;
  var srcObj = srcAO.obj;

  var errors = "";
  var url = "";

  for (var fID in cls.OBJ_FIELDS) {
    var fI = cls.OBJ_FIELDS[fID];
    var u = $addParam('', 'u', fI);

    // An id field that is not set by user but is needed to be passed
    if (u == 'ID') {
      var v = srcObj[fID];
      url += AJAXConn.addAjaxQryFlt2(v, fID);
      continue;
    }

    if (u != 'A' && u != 'P') {
      continue;
    }

    // This is a field that must be set by the user and passed to the server
    var fn = fI.n;
    var req = fI.u;
    var type = fI.t;

    var eID = fID+aoID;

    var editVal = $(eID).value;
    var srcVal = srcObj[fID];

    // If somethings is set on the edit use its val
    var val = '';
    if (editVal != '') {
      val = editVal; 
    // Otherwise use the source
    } else if (srcVal != undefined) {
      val = srcVal;
    }

    var isEmpty = FormFilter.isEmpty(val);

    if (req == 'P') {
      if (isEmpty) {
        continue;
      }
    }

    switch(type) {
      case 'dt':
        if (val == "0000/00/00" || val == "0000-00-00") {
          errors += fI.n + " must have a valid date!\n";
        }
        break;
      case 'tf':
      case 'sel':
      case 'ta':
      case 'url':
        errors += FormFilter.addError(!isEmpty, fn + " cannot be empty!");
        break;
      case 'int':
        errors += FormFilter.addError(FormFilter.isInt(val), fn + " is not an integer!");
        break;
      default:
        errors += "Dont know how to handle type: " + type + " for fID: " + fID + "\n";
        break;
    }

    url += AJAXConn.addAjaxQryFlt2(val, fID);
  }

  // Call additional validation function if it exists
  if (typeof(editAO.validateEdit) == "function") {
    errors += editAO.validateEdit();
  }

  if (!IBCSUtils.checkErrorsWarnings(errors, "")) {
    return false;
  }

  var action = "";
  if (editAO.objState == ActionObj.OBJ_STATE_EDITING) {
    action = ActionObj.ACTION_UPDATE;
  } else if (editAO.objState == ActionObj.OBJ_STATE_CREATING) {
    action = ActionObj.ACTION_CREATE;
  }

  var actionNew = action + cls.CLASS_NAME;

  var appUrl = "";
  if (cls.PKG_ROOT != undefined) {
    eval("appUrl = " + cls.PKG_ROOT + ".APP_URL");
  } else if (cls.APP_URL != undefined) {
    appUrl = cls.APP_URL;
  }

  url = IBCSUtils.MY_WEBS_URL + appUrl + "?action=" + actionNew + url;

  // Send an xml to update the object
  var vars = {'actionObjID':srcAO.id};
  ActionObj.sendAjaxQuery({'desc':cls.CLASS_NAME + " Save Edit",'objType':cls,'params':vars,'url':url});

  return true;
}

















// Static method to show history of an object
ActionObj.historyObj = function(vars) {
  var id = vars.actionObjID;
  var actionObj = ActionObj.get(id);
  var obj = actionObj.obj;

  alert("ActionObj show history for: " + id);
}

// Static method to get the actions string for an object
ActionObj.getObjActions = function(actionObj) {
  var obj = actionObj.obj;

  var menuItems = new Array();

 var vars = "{'actionObjID':'" + actionObj.id + "'}";

  if (actionObj.cls.getActions != undefined) { 
    var arrAction = actionObj.cls.getActions();
    var numActions = arrAction.length;
    for(var i=0; i<numActions; i++) {
      var ht = arrAction[i];
      var func = ht.func;
      func += "(" + vars + ")";
      var disp = ht.disp;
      menuItems[i] = {'func':func, 'disp':disp};
    }
  }
  return menuItems;
}

ActionObj.getActionsText = function(actionObj) {
  var actionsText = "";
  var menuItems = ActionObj.getObjActions(actionObj);
	
  var numMenuItems = 0;
  try {
    numMenuItems = menuItems.length;
  } catch (e) {}

  for(var i=0; i<numMenuItems; i++) {
    var ht = menuItems[i];
    var func = ht.func;
    var disp = ht.disp;
    actionsText += '<div class="btn" onclick="' + func + '")>' + disp + '</div>';
  }

  if (actionsText == "") {
    actionsText = "&nbsp;";
  }
  return actionsText;
}

// Static method to get the body text for an object
ActionObj.getObjBody = function(actionObj) {
  var aoID = actionObj.id;
  var cls = actionObj.cls;
  var obj = actionObj.obj;

  var boxCls = actionObj.getBoxCls();

  var b = '<table class="' + boxCls + '">';

  var fieldFunc = ActionObj.createViewFieldText;
  if (actionObj.objState != ActionObj.OBJ_STATE_VIEWING) {
    fieldFunc = ActionObj.createEditFieldText;
  }
	
  if (cls.BOX_SECTIONS != undefined) {
    var numSec = cls.BOX_SECTIONS.length;
    for(var i=0; i<numSec; i++) {
      var secCfg = cls.BOX_SECTIONS[i];

      // Code to handle box sections which are only displayed based on certain object states
      var v = secCfg.v;
      if (v != undefined) {
        if (v == "C" && actionObj.objState != ActionObj.OBJ_STATE_CREATING) {
          continue;
        } else if (v == "V" && actionObj.objState != ActionObj.OBJ_STATE_VIEWING) {
          continue;
	}
      }

      var secHd = secCfg.n;
      if (secHd != "") {
        b += $mkTblRowHd(secCfg, actionObj);
      }
      var f = secCfg.f;
      var arrF = f.split("|");

      for(var j=0; j<arrF.length; j++) {
        var fn = arrF[j];
        var fI = cls.ALL_FIELDS[fn];
        try {
          var type = fI.t;
        } catch(e) {
          alert("Problem with cls: " + cls.CLASS_NAME + ", fieldName(fn): " + fn + ", fI: " + fI + ", error: " + e);
	}

        switch(type) {
          case 'box':
            b += fieldFunc(actionObj, fn);
            break;
          case 'fc':
            b += $mkTblRow(fI.n+":", fieldFunc(actionObj, fn));
            break;
          case 'func':
            b += $mkWideRow(fieldFunc(actionObj, fn), 2);
            break;
          case 'map':
            b += $addMapDiv({'id':'mapDiv'+aoID});
    	    break;
  	  default:
            b += $mkTblRow(fI.n+":", fieldFunc(actionObj, fn));
  	    break;
        }
      }
    }
  } else if (typeof(obj.getBody) == "function") {
    b += obj.getBody();
  }
  b += "</table>";
  return b;
}

// Static method to Highlight an info box
ActionObj.hlgtInfoBox = function(infoBox) {
  infoBox.className = 'infoTblHL'; 
}

// Static method to Unhighlight an info box
ActionObj.unhlgtInfoBox = function(infoBox) {
  infoBox.className = 'infoTbl';
}

// Static method to send an ajax call and update the object
ActionObj.sendAjaxQuery = function(vars) {
  vars.abortFunction = AJAXConn.abortedAjaxQuery;
  vars.functionToCall = ActionObj.updateObjDataFromXML;
  AJAXConn.getXML(vars);
}

// Static method called to update an object after changing it via AJAX
ActionObj.updateObjDataFromXML = function(xmlDoc, vars) {
  var params = vars.params;

  var actionObjID = params.actionObjID;
  var actionObj = ActionObj.get(actionObjID);

  var errors = ActionObj.checkXMLForErrors(xmlDoc);
  var warnings = ActionObj.checkXMLForWarnings(xmlDoc);

//  alert("In static updateObjDataFromXML() aoID: " + actionObjID + ", ao: " + actionObj);

  // Loop thru obj array and create the objects
  var clsXml = xmlDoc.documentElement.getElementsByTagName("cls");
  for (var i=0; i<clsXml.length; i++) {
    var cls = $attr(clsXml[i], 't');

    try {
      eval("var c = new " + cls + "()");
    } catch(err) {
      e += "No class defined for: " + cls + "!\n";
      continue;
    }

    var objXml = clsXml[i].getElementsByTagName("obj");
    var numObj = objXml.length;

    for (var j=0; j<numObj; j++) {
      try {
        var testID = c.testID(objXml[j]);
        var isCached = ActionObj.testObjExists(c, testID);
        if (isCached) {
          var oldO = c.actionObj.cls.htObj.get(testID);
          oldO.actionObj.updateObjDataFromXML(objXml[j]);
        } else {
          eval("var o = new " + cls + "(objXml[j])");
        }
      } catch(e) {
        errors += e + "\n";
      }
    }

    ActionObj.remove(c.actionObj.id);
  }

  // Special logic for creates
  if (actionObj.objState == ActionObj.OBJ_STATE_CREATING) {
    var cls = actionObj.cls;
    var obj = actionObj.obj;
    var clsXml = xmlDoc.documentElement.getElementsByTagName("cls");
    var len = clsXml.length;
    for (var i=0; i<clsXml.length; i++) {
      var clsName = $attr(clsXml[i], 't');
      if (clsName != cls.CLASS_NAME) {
        continue;
      }
      var objXml = clsXml[i].getElementsByTagName("obj")[0];
      actionObj.updateObjDataFromXML(objXml);
    }
  }

  IBCSUtils.checkErrorsWarnings(errors, warnings);
}

// Static method to display the search results
ActionObj.showSearchResults = function() {
  IBCSUtils.chgRadioHL('dispType');
  var dispType = $getRBVal('dispType');

  if (dispType == ActionObj.VIEW_TYPE_TABLE) {
    ActionObj.showSearchResultsAsList();
  } else if (dispType == ActionObj.VIEW_TYPE_WINDOWS) {
    ActionObj.showSearchResultsAsWindows();
  }
  $shElm('searchResultsArea');
} 

// Static method to display ajax search results in a table
ActionObj.showSearchResultsAsList = function() {
  tbl = document.createElement('table');
  tbl.className = "stdtbl";
  tbl.id = "objTbl";

  var cls  = ActionObj.curClass;

  // Create column head
  $('searchResultsArea').innerHTML = "";
  $('searchResultsArea').appendChild(tbl);
	
  if (cls == null) {
    return;
  }

  var cell;
  var tblRowHead = document.createElement('tr');
  tblRowHead.className = 'colhead';
  cell = document.createElement('th');
  cell.innerHTML = '<a href="javascript:ActionObj.checkAll()">All</a>';
  tblRowHead.appendChild(cell);
  cell = document.createElement('th');
  cell.style.textAlign = 'left';
  cell.innerHTML = '&nbsp;';
  tblRowHead.appendChild(cell);

  // Append the column headers
  for (var i=0; i<cls.TBL_ROW_HD.length; i++) {
    var colName = cls.TBL_ROW_HD[i];
    cell = document.createElement('th');
    cell.innerHTML = colName;
    tblRowHead.appendChild(cell);
  }

  tbl.appendChild(tblRowHead);

  var tblHeader = "";
  if (cls.TBL_ROW_HD != undefined) {
    tblHeader += '<table class="stdtbl" id="objTbl"><tr class="colhead"><th><a href="javascript:ActionObj.checkAll()">All</a><th>&nbsp;</th>';
    for (var i=0; i<cls.TBL_ROW_HD.length; i++) {
      var colName = cls.TBL_ROW_HD[i];  
      tblHeader += '<th>' + colName + '</th>';
    }
    tblHeader += '</tr>';
  } else if (typeof cls.getListViewHeader == 'function') {
    tblHeader = cls.getListViewHeader();
  }

  // Special list list function - use this instead of default
  if (cls.specialListDispFunc != undefined) {
    eval("content = " + cls.specialListDispFunc);
  } else {
    var rc = 0;
    for (var k in cls.htObj.hash) {
      var rowCls = 'even';
      if (rc++ % 2 == 0) {
        rowCls = 'odd';
      }
      var obj = cls.htObj.get(k);
      if (obj == null) {
        continue;
      } 
      obj.actionObj.setRowCls(rowCls);
      for (var i=0; i<obj.actionObj.arrTblRows.length; i++) {
        if (i==0) {
          obj.actionObj.arrTblRows[i].cells[0].innerHTML = obj.actionObj.getCheckboxText();
        }
        tbl.appendChild(obj.actionObj.arrTblRows[i]);
      }
      if (typeof(obj.onStateChange) == "function") {
        obj.onStateChange();
      }
    }
  }
  $('searchResultsArea').appendChild(tbl);
}

// Static method to display ajax search results as separate boxes
ActionObj.showSearchResultsAsWindows = function() {
  $('searchResultsArea').innerHTML = "";
  var cls = ActionObj.curClass;
  var arrCellData = new Array();
  if (cls != null) {
    for (var k in cls.htObj.hash) {
      var obj = cls.htObj.get(k);
      if (obj == undefined) {
        continue;
      }
      $('searchResultsArea').appendChild(obj.actionObj.infoBox);
      if (typeof(obj.onStateChange) == "function") {
        obj.onStateChange();
      }
    }
  }

/*
  if (cls.specialTblFunc != undefined) {
    cls.specialTblFunc();
  }
*/
}

/*

// Func
'getIBEntity':{'t':'ofl','c':'CPTIBEntity','f1':'ibEntity','f2':'getIBEntityWImg','n':'IBEntity'},

// Field
'getMarginType':{'t':'ofl','c':'CPTMarginType','f1':'marginType','f2':'longName','n':'MarginType'},

// Instance method to IB entity with a country img
CPTIBEntity.prototype.getIBEntityWImg = function() {
  eval("var ip = " + CPTIBEntity.PKG_ROOT + ".APP_PATH");
  ip = IBCSUtils.MY_WEBS_URL + ip + "/images/IBEntities/" + this.shortName + ".gif";
  var str = '<img src="' + ip + '">' + this.longName;
  return str;
}

*/

// Static method to try and get a child object that is related to the parent object based on the id
ActionObj.getObjField = function(obj, fI) {
  var cls = fI.c;
  var field1  = fI.f1;
  var field2 = fI.f2;

  var key = "";

  var type1 = typeof(obj[field1]);
  switch(type1) { 
    case 'string':
      key = obj[field1];
      break;
    case 'function':
      eval("key = obj." + field1 + "()");
      break;
    default:
      return "Unk type1: " + type1;
  }

  try {
    var cmd = "var o = " + cls + ".htObj.get(key)";
    eval(cmd);
  } catch(e) {
    alert("Action.getObjField() error: " + e);
  }

  if (o == undefined) {
    return "NoObj for key: " + key;
  }

  var retVal = "-";
  var type2 = typeof(o[field2]);
  switch(type2) {
    case 'string':
      retVal = o[field2];
      break;
    case 'function':
      eval("retVal = o." + field2 + "()");
      break;
    default:
      alert("ActionObj.getObjField Problem with Type: " + type1 + ", field2: " + field2);
  }

  return retVal;
}

/////////////////////////////////////////////////////////////////////////////////////
// Instance functions 
/////////////////////////////////////////////////////////////////////////////////////

// Instance method to get the inner table class for the box view
ActionObj.prototype.getBoxCls = function() {
  var cls = 'inTbl';
  if (this.isChecked) {
    cls = 'inTblChk';
  } else if (this.objState == ActionObj.OBJ_STATE_EDITING || this.objState == ActionObj.OBJ_STATE_CREATING) {
    cls = 'inTblEdit';
  }
  return cls;
}

// Instance method to get the checkbox used for multi actions 
ActionObj.prototype.getCheckboxText = function() {
  var chk = '';
  if (this.isChecked) {
    chk = ' checked ';
  }
  var t = '<input type="checkbox" class="aoCB" name="cbList[]" id="chk' + this.id + '" ' + chk + 'onchange="ActionObj.changeCheckState(' + this.id + ', \'cb\')">';
//  var t = '<input type="checkbox" class="aoCB" name="cbList[]" id="chk' + this.id + '" ' + chk + 'onchange="ActionObj.changeCheckState(this, \'cb\')">';
  return t;
}

// Instance method to get the row class for the table view
ActionObj.prototype.getRowCls = function() {
  var cls = this.rowCls;
  if (this.isChecked) {
    cls = 'checked';
  } else if (this.objState == ActionObj.OBJ_STATE_EDITING || this.objState == ActionObj.OBJ_STATE_CREATING) {
    cls = 'editing';
  }
  return cls;
}

// Instance method to set the row class for table view
ActionObj.prototype.setRowCls = function(rowCls) {
  this.rowCls = rowCls;
  var cls = this.rowCls;
  for (var i=0; i<this.arrTblRows.length; i++) { 
    this.arrTblRows[i].className = this.getRowCls();
  }

//  var cb = $('chk' + this.id);
//  alert("cb: " + cb);
}

// Instance method to update the table row view
ActionObj.prototype.updateGUI = function() {

  // Update info box
  if (this.objState != ActionObj.OBJ_STATE_CREATING) {
    var title = this.obj.getTitle();
  } else {
    var title = "Creating " + this.cls.CLASS_NAME;
  }
	
  this.updateTitle(title);
  this.updateActions(ActionObj.getActionsText(this));
	
  var body = ActionObj.getObjBody(this)

  // Update table row
  if (this.TBL_CFG != undefined) {
    var fieldFunc = ActionObj.createViewFieldText;
    if (this.objState != ActionObj.OBJ_STATE_VIEWING) {
      fieldFunc = ActionObj.createEditFieldText;
    }

    var len2 = this.arrTblRows.length;

    var numRows = this.TBL_CFG.length;
    for (var i=0; i<numRows; i++) {
      this.arrTblRows[i].className = this.getRowCls();
      var rowCfg = this.TBL_CFG[i];
      var numCells = rowCfg.length;
      for (var j=0; j<numCells; j++) {
        var cI = rowCfg[j];
        var fields = cI.f;
	var arrF = fields.split("|");

        var t = "";
	for(var k=0; k<arrF.length; k++) {
          var fn = arrF[k];
          var s = fieldFunc(this, fn);
	  if (s != "") {
            t += '<div class="arrowObj">' + s + '</div>';
	  }
	}
        var cell = this.arrTblRowCells[fields];
	cell.innerHTML = t;
      }
    }
  }
  this.updateBody(body);
}

// Instance method to update the attached object with new xml
ActionObj.prototype.updateObjDataFromXML = function(xml) {

  // Set object fields from xml
  for (var fn in this.cls.OBJ_FIELDS) {
    var fI = this.cls.OBJ_FIELDS[fn];
    this.obj[fn] = $attr(xml, fI.xml);
  }

  this.updateGUI();
  if (typeof(this.obj.onStateChange) == "function") {
    this.obj.onStateChange();
  }

  if (this.objState == ActionObj.OBJ_STATE_CREATING) {
    alert(this.cls.CLASS_NAME + " created SUCCESSFULLY!");
  } else if (this.objState == ActionObj.OBJ_STATE_DELETING) {
    this.cls.htObj.remove(this.obj.getID());
    ActionObj.removeObj({'actionObjID':this.id});
  }

}

ActionObj.prototype.updateActions = function(actions) {
  if (actions == "") {
    actions = "&nbsp;";
  }
  var actionsText = actions + '<span class="BR"><span class="BL"></span></span>';
  this.infoBoxActions.innerHTML = actionsText;
}

ActionObj.prototype.updateBody = function(body) {
  var bodyText = '<div class="infoTblMiddle"><div class="infoTblInner">' + body + '</div></div>';
  this.infoBoxBody.innerHTML = bodyText;
}

ActionObj.prototype.updateTitle = function(title) {
  var cb = this.getCheckboxText();

  var titleText =  '<span class="TR"><span class="TL"></span></span>' +
  '<div class="infoTblTitle">' + cb + '<label for="chk' + this.id + '">' + title + '</label></div>';
  this.infoBoxTitle.innerHTML = titleText;
}

ActionObj.mkTblCellLinkR = function(text, func) {
  var t = text;
  if (text != 0) {
    t = '<a href="javascript:' + func + '">' + text + '</a>';
  }
  return ActionObj.rightAlignTblCell(t);
}

ActionObj.rightAlignTblCell = function(t) {
  var t = '<div style="float:right; padding:5px;">' + t + '</div>';
  return t;
}

