//
// library for data transfer between HTML forms and XML instances
//
// The form fields are mapped to XML nodes by naming conventions:
// - all mapped fields need an attribute "xmlpath"
// - fields with path "a.b.c" are mapped to the body of node <root><a><b><c>
// - to adress multiple subnodes od same tag, use "a:0" or "a:1" and so on
// - nodes that become empty are pruned recursively from the tree
//
// form input types supported are <input> and <select> fields. All <option> tags 
// inside <select> tags must have explicit value attributes.
//
// to fill a from from an XML document, use: loadForm (form, xmlDoc)
// to save a form back to XML, use: saveForm (form, xmlDoc)
//
// version: Nov 12th, 2002 (A. Neumann)
//

var xmlPathPrefix = "";

function initForm (form, path)
  {
  loadXML (xdata, path);
  loadForm (form, xdata);
  }

function loadXML (doc, path)
  {
  doc.async = false;
  doc.load(path);
  doc.setProperty("SelectionLanguage", "XPath");

  if (doc.parseError.errorCode != 0)
    {
    alert("XML Error loading XML file '" + path + "': " + doc.parseError.reason + " in line " + doc.parseError.line);
    return;
    }
  }

function loadForm (form, data)
  {
  for (var i = 0; i < form.elements.length; i ++)
    {
    var control = form.elements[i];
    var path = control.getAttribute("xmlpath");
    if (path != null)
      {
      var node = getTreeNode (data, path);
      var value = getBodyValue (node);
      if (value == null)
        value = "";
      var frmt = getNodeFormat (node);
      if (control.tagName == "INPUT")
        {
	      if (frmt == null)
	        frmt = control.getAttribute("xmlfrmt");
	      if (frmt != null)
	        {
	        var formatter = new Formatter (frmt);
	        value = formatter.format(value);
	        }
	      if (control.type == "checkbox")
          {
          if (value)
            control.checked = true;
          else
            control.checked = false;
          }
        else
          control.value = value;
        }
      else if (control.tagName == "SELECT")
        {
        for (var j = control.options.length-1; j >= 0; j --)
          {
          var option = control.options[j];
          if ( option.value != value && option.optional == "1"  )
            {
            control.remove(j);
            }
          }
        for (var j = 0; j < control.options.length; j ++)
          {
          var option = control.options[j];
          if (option.value == value)
            {
            option.selected = true;
            break;
            }
          }
        }
      }
    }
  }


function compressFormData (form, data)
  {
  var res = "";
  for (var i = 0; i < form.elements.length; i ++)
    {
    var control = form.elements[i];
    var path = control.getAttribute("xmlpath");
    if (path != null)
      res += compressNodeValue (data, path);
    }
  return res;
  }

function compressTree (data, prefix)
  {
  var subnode = getSubNodeSegmented (data.documentElement, xmlPathPrefix + prefix);
  if (subnode == null)
    return "";
  return compressSubNodes (subnode, xmlPathPrefix + prefix);
  }

function compressSubNodes (node, prefix)
  {
  var res = "";
  var childs = node.childNodes;
  for (var i = 0; i < childs.length; i ++)
    {
    if (childs[i].nodeType == 1)
      res += compressSubNodes (childs[i], prefix + "." + childs[i].tagName);
    else
      res += prefix + "=" + childs[i].nodeValue + "|";
    }
  return res;
  }

function compressNodeValue (data, path)
  {
  var value = getTreeValue (data, path);
  if (value == "")
    return "";
  return xmlPathPrefix + path + "=" + value + "|";
  }


function loadSection (section, data)
  {
  var spans = section.getElementsByTagName("span");
  for (var i = 0; i < spans.length; i ++)
    {
    var span = spans.item(i);
    var path = span.getAttribute("xmlpath");
    if (path != null)
      {
      var node = getTreeNode (data, path);
      var value = getBodyValue (node);
      if (value == null)
        value = "";
      var frmt = getNodeFormat (node);
      if (frmt == null)
        frmt = span.getAttribute("xmlfrmt");
      if (frmt != null)
        {
        var formatter = new Formatter (frmt);
        value = formatter.format(value);
        }
      span.innerHTML = value;
      }
    }
  }

function getTreeValue (data, path)
  {
  var value = getNodeValue (data.documentElement, xmlPathPrefix + path);
  if (value == null)
    value = "";
  return value;
  }
  
function getTreeNode (data, path)
  {
  return getNodeNode (data.documentElement, xmlPathPrefix + path);
  }
  
function setTreeValue (data, path, value)
  {
  setNodeValue (data.documentElement, xmlPathPrefix + path, value);
  }
  
function removeTreeValue (data, path)
  {
  removeNodeValue (data.documentElement, xmlPathPrefix + path);
  }
  
function setNodeValue (node, path, value)
  {
  //alert ("setNodeValue (" + node.tagName + ", " + path + ", " + value + ")");
  var pos = path.indexOf(".");
  var subnode;
  if (pos != -1)
    {
    subnode = makeSubNode (node, path.substr(0,pos));
    path = path.substr(pos+1);
    setNodeValue (subnode, path, value);
    return;
    }
  subnode = makeSubNode (node, path);
  var textnode = subnode.firstChild;
  if (textnode == null)
    textnode = makeTextChild (subnode);
  textnode.nodeValue = value;
  }
  
function removeNodeValue (node, path)
  {
  var pos = path.indexOf(".");
  var subnode;
  if (pos != -1)
    {
    subnode = getSubNode (node, path.substr(0,pos));
    if (subnode == null)
      return;
    path = path.substr(pos+1);
    removeNodeValue (subnode, path);
    return;
    }
  subnode = getSubNode (node, path);
  if (subnode == null)
    return;
  var parent = subnode.parentNode;
  parent.removeChild (subnode);
  while (parent.firstChild == null)
    {
    var p1 = parent.parentNode;
    p1.removeChild (parent);
    parent = p1;
    }
  }
  
function makeSubNode (node, path)
  {
  if (node == null)
    return;
  var pos = path.indexOf(":");
  var tag;
  var index = 0;
  if (pos != -1)
    {
    tag = path.substr(0,pos);
    index += path.substr(pos+1);
    }
  else
    tag = path;
  var subnodes = node.getElementsByTagName (tag);
  //alert ("makeSubnode (" + tag + "#" + index + "), has " + subnodes.length);
  while (subnodes == null || subnodes.length <= index)
    {
    sub = node.ownerDocument.createElement (tag);
    node.appendChild (sub);
    subnodes = node.getElementsByTagName (tag);
    }
  //alert ("makeSubnode created " + subnodes[index].tagName);
  return subnodes[index];
  }
  
function makeTextChild (node)
  {
  var sub = node.ownerDocument.createTextNode("");
  node.appendChild (sub);
  return sub;
  }
  
function getNodeValue (node, path)
  {
  var subnode = getNodeNode (node, path);
  return getBodyValue (subnode);
  }

function getNodeNode (node, path)
  {
  if (node == null)
    return null;
  var pos = path.indexOf(".");
  var subnode;
  if (pos != -1)
    {
    subnode = getSubNode (node, path.substr(0,pos));
    if (subnode == null)
      return null;
    path = path.substr(pos+1);
    return getNodeNode (subnode, path);
    }
  subnode = getSubNode (node, path);
  return subnode;
  }

function getBodyValue (node)
  {
  if (node == null)
    return null;
  var subchild = node.firstChild;
  if (subchild == null)
    return null;
  return subchild.nodeValue;
  }

function getNodeFormat (node)
  {
  if (node == null)
    return null;
  var frmt = node.getAttribute ("format");
  return frmt;
  }

function getSubNodeSegmented (node, path)
  {
  var pos = path.indexOf(".");
  var subnode;
  if (pos != -1)
    {
    subnode = getSubNode (node, path.substr(0,pos));
    if (subnode == null)
      return null;
    path = path.substr(pos+1);
    return getSubNodeSegmented (subnode, path);
    }
  return getSubNode (node, path);
  }

function getSubNode (node, path)
  {
  if (node == null)
    return null;
  var pos = path.indexOf(":");
  var tag;
  var index = 0;
  if (pos != -1)
    {
    tag = path.substr(0,pos);
    index += path.substr(pos+1);
    }
  else
    tag = path;

  var childs = node.childNodes;
  if (childs == null)
    return null;
  for (var i = 0; i < childs.length; i ++)
    if (childs[i].nodeType == 1 && childs[i].tagName == tag)
      {
      if (index == 0)
        return childs[i];
      index --;
      }
  return null;
  }
  
function showResult (data)
  {
  var w = window.open ("about:blank", "result");
  w.document.write ("<pre>" + (makePrefixXML (data.XMLDocument, "")).split("<").join("&lt;") + "</pre>");
  }

function swap (val,src,dst)
  {
  var arr 
  var res;
  arr = val.split(src);
  if (arr.length > 1)
    res = arr.join(dst);
  else
    res = val;
  return res;
  }

function htEncode (txt)
  {
  txt = swap (txt,"&", "&amp;");
  txt = swap (txt, "<", "&lt;");
  txt = swap (txt, ">", "&gt;");
  txt = swap (txt, "\"", "&quot;");
  return txt;
  }
  
  
function makePrefixXML (node, prefix)
  {
  var type = node.nodeType;
  var res = "";
  if (prefix != null)
    res = prefix;
  if (type == 9)
    {
    var childs = node.childNodes;
    for (var i = 0; i < childs.length; i ++)
      if (childs[i].nodeType != 1)
        res += makePrefixXML (childs[i], prefix);
    res += makePrefixXML(node.documentElement, prefix);
    return res;
    }
  if (type == 1)
    {
    res += "<" + node.tagName;
    var atts = node.attributes;
    for (var i = 0; i < atts.length; i ++)
      if (atts[i].nodeValue != null && atts[i].nodeName.indexOf("_gx_") != 0)
        res += " " + atts[i].nodeName + "=\"" + htEncode(atts[i].nodeValue) + "\"";
    if (prefix == null)
      return res;
    var childs = node.childNodes;
    if (childs.length == 0)
      res += "/>\n";
    else if (childs.length == 1 && childs[0].nodeType == 3)
      res += ">" + htEncode(childs[0].nodeValue) + "</" + node.tagName + ">\n";
    else
      {
      res += ">\n";
      for (var i = 0; i < childs.length; i ++)
        res += makePrefixXML (childs[i], "  " + prefix);
      res += prefix + "</" + node.tagName + ">\n";
      }
    return res;
    }
  if (type == 3)
    return prefix + htEncode(node.nodeValue);
  if (type == 8)
    return prefix + "<!--" + node.nodeValue + "-->\n";
  if (type == 7)
    return prefix + "<?" + node.nodeName + " " + node.nodeValue + " ?>\n";
  return prefix + "[[unknown node type " + type + "]]\n";
  }

function saveForm (form, data)
  {
  for (var i = 0; i < form.elements.length; i ++)
    {
    var control = form.elements[i];
    var path = control.getAttribute("xmlpath");
    if (path != null)
      {
      var value = "";
      if (control.tagName == "INPUT")
        {
	      if (control.type == "checkbox")
          {
          if (control.checked)
            value = "1";
          else
            value = "";
          }
        else
          value = control.value;
	      var frmt = control.getAttribute("xmlfrmt");
	      if (value != null && value != "" && frmt != null)
	        {
	        var formatter = new Formatter (frmt);
	        value = formatter.parse(value);
	        }
        }
      else if (control.tagName == "SELECT")
        {
        for (var j = 0; j < control.options.length; j ++)
          {
          var option = control.options[j];
					if (option.selected)
            {
            value = option.value;
            break;
            }
          }
        }
      if (value == null || value == "")
        removeTreeValue (data, path);
      else
        setTreeValue (data, path, value);
      }
    }
  }

function Formatter (frmt)
  {
  this.fixdecimals = 0;
  this.maydecimals = 0;
  this.sep = false;
  this.postfix = null;
  this.ifempty = "";
  // flag allowing negative numbers
  this.allowNegative = true;

  var c = "";
  var haddec = false;
  var a = frmt.split('|');
  if (a.length > 1)
    {
    this.ifempty = a[0];
    frmt = a[1];
    }
  for (var i = 0; i < frmt.length; i ++)
    {
    c = frmt.charAt(i);
    if (c == "+")
    	this.allowNegative = false;
    else if (c == ".")
      this.sep = true;
    else if (c == ",")
      haddec = true;
    else if (c == "#" || c == "9")
      {
      if (haddec)
        this.maydecimals ++;
      }
    else if (c == "0")
      {
      if (haddec)
        this.fixdecimals ++;
      }
    else
      {
      this.postfix = frmt.substring (i);
      break;
      }
    }
  
  function format (inval)
    {
    if (inval == null || inval == "undefined" || inval == "") 
      return this.ifempty;
    if (inval == "0" && this.ifempty != "") return this.ifempty;

    var num;
    if (typeof(inval) == "number")
    	num = inval;
    else
    	{
	    var c = inval.charAt(0);
  	  if (c != "-" && (c < "0" || c > "9")) return inval;
    	num = parseFloat (inval);
    	}
    if (num == NaN) return inval;
    var neg = false;
    if (num < 0)
      {
      neg = true;
      num = - num;
      }
    var whole = Math.floor (num);
    var exponent = 1;
    var dec = 0;
    if (whole != num)
      {
      for (var i = 0; i < this.fixdecimals + this.maydecimals; i ++)
        exponent *= 10;
      num = Math.round (num * exponent) / exponent;
      whole = Math.floor (num);
	    dec = num - whole;
	    dec += 0.0000000001;
      }
    
    var result = "" + whole;
    if (this.sep && result.length > 6)
      result = result.substr(0,result.length-6) + "." + result.substr(result.length-6);
    if (this.sep && result.length > 3)
      result = result.substr(0,result.length-3) + "." + result.substr(result.length-3);
    for (var i = 0; i < this.fixdecimals || i < this.fixdecimals + this.maydecimals && dec > 0.00001; i ++)
      {
      //alert ("fix " + this.fixdecimals + ", may " + this.maydecimals + ", dec " + dec);
      if (i == 0) result += ",";
      dec *= 10;
      var dnum = Math.floor(dec);
      dec -= dnum;
      result += dnum;
      }
    if (neg)
      result = "-" + result;
    if (this.postfix)
      result += this.postfix;
    return result;      
    }
  this.format = format;
    
  function parse (inval)
    {
    if (inval == null || inval == "undefined" || inval == "" || inval == this.ifempty) return "";
    var result = "";
    if (this.postfix != null)
      {
      var pos = inval.indexOf (this.postfix);
      if (pos != -1)
        inval = inval.substr (0, pos);
      }
	  for (var i = 0; i < inval.length; i ++)
	    {
	    c = inval.charAt(i);
	    if (c >= "0" && c <= "9")
	      result += c;
	    else if (c == ",")
	      result += ".";
	    else if (this.allowNegative && c == "-")
	      result += "-";
      else if (c == " " || c == ".")
	      ;
	    }
	  return result;
    }
  this.parse = parse;

	function reformat (inval)
		{
		return format (parse (inval));
		}
  this.reformat = reformat;
	
  function parseFloat1 (inval)
    {
    var result = this.parse (inval);
    if (result == "") return 0;
    return parseFloat (result);
    }
  this.parseFloat = parseFloat1;

  function parseInt1 (inval)
    {
    var result = this.parse (inval);
    if (result == "") return 0;
    return parseInt (result);
    }
  this.parseInt = parseInt1;
  }    

function formatOut (inval, format)
  {
  var fo = new Formatter (format);
  return fo.format (inval);
  }
  
function formatIn (inval, format)
  {
  var fo = new Formatter (format);
  return fo.parse (inval);
  }
  
function subElement (node, tag)
  {
  if (node == null) 
    return null;
  var childs = node.getElementsByTagName(tag);
  if (childs == null || childs.length <= 0)
    return null;
  return childs[0];
  }

function getElementText (node, tag)
  {
  var sub = subElement (node, tag);
  if (sub == null)
    return "";
  return sub.text;
  } 
