var _selTrees = new Array, _selTree;

function getElemIdx(elem) {
   var  i, elems = elem.form.elements;
   for(i = 0; elem != elems[i]; i++);
   return i;
}

function clrSel(sel) {
   var opts = sel.options;
   for(var i = sel.length; --i > 0; opts[i] = null);
}

function chkSelTree(sel) {
   var parts = sel.name.split(';'), i, pair, args = new Array;
   if(parts.length > 1) {
      sel.setAttribute("name", parts[0]);
      sel.form.elements[sel.name] = sel;
      parts = parts[1].split(' ');
      for(i = 0; i < parts.length; i++) {
	 pair = parts[i].split('=');
	 args[pair[0]] = pair[1];
      }
   }

   if(args['tree'])
      _selTrees[_selTrees.length] = new SelTree(sel, args['tree'],
	 args['depth'], args['begPath'], args['endPath']);
   else  if(args['section'])
      _selTrees[_selTrees.length] = new SelTreeD(sel, args['section'],
	 args['depth'], args['cond'] ? eval(args['cond']) : args['colList'], args['floating']);
   else  _errMsg = null;
   if(_errMsg)  alert(_errMsg);
}

function srchSelTree(sel) {
   var i, j, node, elems;
   for(i = 0; i < _selTrees.length; i++) {
      node = _selTrees[i];
      if(node.tree) {
	 elems = node.elems;
	 for(j = 0; j < elems.length; j++)
	    if(elems[j] == sel)  { _selTree = node;  return j + 1; }
      }
      else {
	 if(node.form == sel.form && (j = getElemIdx(sel) - node.idx) >= 0 && j < node.depth)
	    { _selTree = node;  return j + 1; }
      }
   }
   return -1;
}

function chkSelTrees(f) {
   var i, elems = f.elements;
   for(i = 0; i < elems.length; i++)
      if(elems[i].type == "select-one") {
//	 _rmTabs(elems[i]);   // ... For Safari only.
	 chkSelTree(elems[i]);
      }
}
//---------------------------------------------------------------------------

// Public names: selPath Node(deprecated!)
// Cross-file names: SelTree selPath0

// MLPD: multi-level pulldown
// 0) depth; 1) keyword; 2) code

function SelTree(sel, tree, dep, beg, end) {

function goPath(tree, path, dep, flag) {
   var i = 0, j = -1, n, val;
   path = path.split('/');
   while(i < path.length) {
      val = path[i++];
      while((n = tree[++j].dep - i) >= 0)
	 if(n == 0 && tree[j][2] == val) {
	    path[i - 1] = flag == 2 ? j+1 : j;
	    break;
	 }
      if(n < 0)  return null;	    
   }
   if(flag == 1)  j = path[i - 1];   // lower bound
   else  while(tree[++j].dep > i);   // upper bound
   while(i < dep)  path[i++] = j;
   return path;
}

   var p, i, n, cnt, f, elems, parts, s;
   _errMsg = null;
   var msg = 'SELECT tree "' + tree + '": ';
   if(eval("typeof " + tree) != "object")
      { _errMsg = msg + "not found or illegal";  return; }
   tree = eval(tree);
 
   n = parseInt(dep);
   if(isNaN(n)) {
      parts = dep.split(',');
      dep = parts.length + 1;
   }
   else  if((dep = n) < 2)  { _errMsg = msg + "illegal depth";  return; }
   if(tree[tree.length - 1][0] > 0)
      tree[tree.length] = new Array(0, null);
   p = null;
   if(beg && (p = goPath(tree, beg, dep, 1)) == null)
      { _errMsg = msg + _begPathErrStr;  return; }
   this.begPath = p;
   p = null;
   if(end && (p = goPath(tree, end, dep, 2)) == null)
      { _errMsg = msg + _endPathErrStr;  return; }
   this.endPath = p;
   this.tree = tree;
   this.path = new Array(dep + 1);  this.path[0] = -1;
   this.elems = new Array(dep);
   this.form = f = sel.form;
   elems = f.elements;
   n = getElemIdx(sel);
   cnt = 0;
   for(i = 0; i < dep; i++) {
      if(i > 0)
	 // "self._mngMode != null" means management pages.
	 s = parts ? parts[i-1] : null;
	 sel = s ? (self._mngMode != null ? _getElemByName(s, f) : elems[s]) : elems[n+i];
      if(!sel) {
	 _errMsg = msg + (s ? 'SELECT element "' + s + '" not found' : 'too deep');
	 return;
      }
      if(sel.type != "select-one" && (i < dep - 1 || sel.type != "select-multiple"))
	 { _errMsg = msg + "select-one expected"; return; }

      if(i > 0 && sel.form != f) {
	 if(cnt == 0)  break;
	 if(sel.form._parent.value != f.name) {
	    _errMsg = msg + "as crossing forms, neighboring forms require parent-child relation";
	    return;
	 }
	 f = sel.form;
	 cnt = 0;
      }
      if(sel.name != "" && sel.name.charAt(0) != "_")  cnt++;

      sel.onfocus = _selMenu;
      if(sel.onchange == null)  sel.onchange = selPath;
      this.elems[i] = sel;
   }
   if(cnt == 0 && i < dep)
      _errMsg = msg + "as crossing forms, at least one SELECT has a field name in each form, except the last";
}

// Prepare the pulldown menu for the SELECT element "sel" at the dep'th level.
function _selMenu(sel, dep)  {
   var i, j, path, idx, p;

   if(dep) {
      clrSel(sel);
      idx = _selTree.path[dep];
   }
   else {
      dep = srchSelTree(sel = this);
      if(sel.length > 1)  return;
      idx = -1;
   }
   var tree = _selTree.tree;
   var begX = (path = _selTree.begPath) ? path[dep-1] : 0;
   var endX = (path = _selTree.endPath) ? path[dep-1] : tree.length - 1;
   path = _selTree.path;

   // Search for the ancestor selection already selected.
   dep0 = dep; 
   while(path[--dep0] == null);
   // A MLPD may spread into some forms. If the selected path
   // is not sufficient for the part of the MLPD in the form, we give up.
   if(_selTree.elems[dep0].form != sel.form)  return;

   i = path[dep0];
   if(begX > i)  i = begX - 1;
   j = 0;
   while(tree[++i][0] > dep0 && i < endX)
      if(tree[i][0] == dep) {
	 p = tree[i];
	 sel.options[++j] = new Option(p[1], p[2] ? p[2] : p[1], false, i == idx);
      }
}

function selPath(sel0, dep, idx) {
   if(dep == null) {
      if(sel0 == null || sel0.eventPhase != null)  sel0 = this;
      dep = srchSelTree(sel0);
   }
   var elems = _selTree.elems, tree = _selTree.tree, path = _selTree.path;
   var i, j, dep0;
   // Search for the ancestor selection already selected.
   dep0 = dep; 
   while(path[--dep0] == null);
 
   // 'idx' is the index to 'tree' related to the selected option.
   if(idx == null && (i = sel0.selectedIndex) > 0) {
      idx = path[dep0];
      if(_selTree.begPath && (j = _selTree.begPath[dep-1]) > idx)  idx = j - 1;
      while(i > 0)
	 if(tree[++idx][0] == dep)  i--;
      path[dep] = idx;
   }

   if(idx != null) {   // The current SELECT element has been selected.
      // There are ancestor selection(s) not selected yet, since the path can be
      // decided, so rearrange the pulldowns for them.
      if(dep0 < dep - 1) {
	 // Backtrace for ancestor nodes.
	 for(i = dep; --i > dep0; path[i] = idx)
	    while(tree[--idx][0] > i);
	 for(i = dep0; i < dep; i++)
	    _selMenu(elems[i], i+1);
      }
      else  _selMenu(sel0, dep);
      if(dep < elems.length) {   // show the next level
	 _selMenu(elems[dep], dep+1);   
	 path[++dep] = null;
      }
   }
   else  path[dep] = null;
   // Clear descendent selections.
   for(i = dep; i < elems.length; ) {
      clrSel(elems[i]);
      path[++i] = null;
   }
   if(self.selChgEx)  selChgEx(sel0);
}

function selPath0(p, node, vals) {
   _selTree = p;
   var elems = p.elems, path = p.path, i, f = node.form, idx;
   for(i = 0; i < elems.length && elems[i].form != f; i++);
   if(i == elems.length || (idx = path[i]) == null)  return;
   var tree = p.tree, sel, n = 1, dep0 = 0, s, q;
   // Part or all of the MLPD is in the form. If there is only part, the
   // ancestor SELECT element(s) should be selected.
   while(i < elems.length && elems[i].form == f) {
      sel = elems[i++];
      if((s = sel.name) != "" && s.charAt(0) != "-" &&
	 (s = vals[RTcolIdx(node, s)]) != "" && n > 0) {
	 while((n = tree[++idx][0] - dep0) > 0) {
	    q = tree[idx];
	    if(q[0] == i && (q[2] ? q[2] : q[1]) == s) {
	       path[dep0 = i] = idx;
	       break;
	    }
	 }
	 if(n > 0) {
	    if(path[i-1])  _selMenu(sel, i);
	    else  selPath(sel, i, idx);
	    continue;
	 }
      }
      clrSel(sel);
      path[i] = null;
   }
   if(i < elems.length && path[i] != null) {
      // The last element is for a multi-value form.
      if(elems[i].type == "select-multiple")  _selMenu(elems[i], i+1);
      _fixSelTree(p, i, RTchild(node, elems[i].form.name));
   }
}

