var globalcounters = {}
var last_added_id='';

function initialize_subform(formid, data, with_preview) {
    if (data == null) return;

    var prefix = (arguments.length>2)?arguments[2]:formid;
    var subformid = (arguments.length>3)?arguments[3]:null;
    var type = (arguments.length>4)?arguments[4]:formid;

    for (var i=0; i<data.length; i++) {
        var plaindata = new Array();

        for (var k in data[i]) {
            if (typeof data[i][k] == "function") continue;
            plaindata[k] = data[i][k];
        }

        var newid;
        if (getSubformType(type, subformid) != "table")
            add(subformid, type, plaindata, with_preview);
        else {
            add_row(subformid, type, plaindata, with_preview);
        }
        newid = last_added_id;
        
        // lastly, handle all subforms.
        for (var k in data[i]) {
            if (typeof data[i][k] == "function") continue;
            
            var p = prefix + "[" + i + "][" + k + "]";
            
            if (typeof data[i][k] == 'object') {
                // subform!
                initialize_subform(k, data[i][k], p, newid, k);
            }
        }
    }
}

function set_element_name(e, name) {
    if (!document.all || e.type.toLowerCase() != "radio") {
        // simple solution for non-IE browsers and non-radio items
        e.name = name;
        return e;
    }

    var nxt = e.nextSibling;
    var parent = e.parentNode;

    var new_element = document.createElement(e.outerHTML.replace(/name=\w+/i, "name="+name));
    parent.removeChild(e);
    if (nxt) parent.insertBefore(new_element, nxt);
    else parent.appendChild(new_element);
    return new_element;
}


function elem(type, postfix) {
    if (arguments.length == 3 && arguments[2] != null && arguments[2].length>0) {
        // ref with subform id
        return document.getElementById(arguments[2]+"_"+type+"_"+postfix);
    } else
        return document.getElementById(type+"_"+postfix);
}

function repl(s, ar) {
    for (var key in ar) {
        if (typeof ar[key] == "function") continue;
        re = new RegExp('%'+key+'%', "g");
        s = s.replace(re, ar[key]);
    }
    return s;
}

function create_hidden(name, value) {
    var n = document.createElement("input");
    n.setAttribute("value", value);
    n.setAttribute("name", name);
    n.setAttribute("type", "hidden");
    if (arguments.length == 3)
        n.setAttribute("id", arguments[2]);
    return n;
}

function setvalue(elem, value) {
    if (elem.nodeType != 1) return false; // quietly { alert(value); return false; }
    var nodeName = elem.nodeName.toLowerCase();
    var type = elem.type.toLowerCase();

    if (nodeName == "input" && type == "file") {
        if (value.name) {
            // existing file, insert "keep" and "oldname" attribute, and a checkbox to delete current file.
            elem.parentNode.insertBefore(create_hidden(elem.getAttribute("realname")+"[keep]", "true"), elem);
            elem.parentNode.insertBefore(create_hidden(elem.getAttribute("realname")+"[oldname]", value.name, elem.id+"_oldname"), elem);
            elem.parentNode.insertBefore(document.createTextNode(value.name+" ("), elem);
            
            var n = document.createElement("input");
            n.setAttribute("type", "checkbox");
            n.setAttribute("value", "true");
            n.setAttribute("name", elem.getAttribute("realname")+"[delete_old]");
            elem.parentNode.insertBefore(n, elem);

            elem.parentNode.insertBefore(document.createTextNode(" Delete this file)"), elem);
            elem.parentNode.insertBefore(document.createElement("br"), elem);
        }
    } else if (nodeName == "input" && type == "checkbox") {
        elem.checked = value;
    } else if (nodeName == "input" && type == "radio") {
        // in radio boxes, we check the item if the value is equal to the element's value
        elem.checked = (elem.value == value);
    } else {
        // selects, textarea and inputs (who != file) all support .value = assignments
        elem.value = value;
    }
}


function formelems(obj) {
    var out = new Array();
    var x = new Array();
    x[0] = "input";
    x[1] = "textarea";
    x[2] = "select";
    for (var i=0; i<x.length; i++) {
        var lst = obj.getElementsByTagName(x[i]);
        for (var j=0; j<lst.length; j++) {
            var n = lst[j].getAttribute('name');
            out[out.length] = lst[j];
        }
    }
    return out;
}

function formelems_rows(tbl, row_id) {
    var out = new Array();
    for (var i=0; i<tbl.rows.length; i++) {
        if (tbl.rows[i].getAttribute("row_id") == row_id) {
            var l =formelems(tbl.rows[i]);
            for (n in l) {
                if (typeof l[n] == "function") continue;
                out[out.length] = l[n];
            }
        }
    }
    return out;
}

function deactivate_htmlareas(formid) {
    if (eval("window.HTMLArea") && eval("window.editors")) {
        for (var k in window.editors) {
            if (typeof window.editors[k] == "function") continue;
            if (k.substr(0, formid.length) == formid) 
                window.editors[k].deactivateEditor();
        }
    }
}

function activate_htmlareas(formid) {
    if (eval("window.HTMLArea") && eval("window.editors")) {
        for (var k in window.editors) {
            if (typeof window.editors[k] == "function") continue;
            if (k.substr(0, formid.length) == formid) {
                window.editors[k].activateEditor();
                //window.editors[k].generate();
            }
        }
    }
}

function getSubformType(type, subformid) {
    var t = elem(type, "insert", subformid).tagName;
    return t.toLowerCase();
}

function getParentName(type, subformid) {
    var p = elem(type, "insert", subformid).parentNode;
    while (p != null && p.getAttribute('subform') == null) p = p.parentNode;
    if (!p) alert('formlib::getParentName: No TOPFORM attribute found!');

    if (p.getAttribute('subform')!='true') {
        if (p.getAttribute('topform')) {
            return p.getAttribute('topform')+'['+p.id+']';
        } else {
            return p.id
                }
    }

    var num = p.getAttribute('num');
    var subformid = p.getAttribute('subformid');
    return getParentName(p.getAttribute('type'), subformid) + '['+num+']'+'['+type+']';
}    

function add_with_kind(subformid, type) {
    var select = elem(type, "kind");

    var kind = select.options[select.selectedIndex].value;
    if(kind.length) {
        add(subformid, type, null, kind);
    }
}

function add(subformid, type) 
{
    var hasdata = false;
    var haskind = false;
    var data;
    if (arguments.length > 2 && arguments[2]) {
        // we're adding with data
        data = arguments[2];
        hasdata = true;
    }
    var typewithkind = type;
    var kind;
    if ((arguments.length > 3 && arguments[3]) || hasdata && data["_kind"]) {
        kind = hasdata?data["_kind"]:arguments[3];
        typewithkind = type + "_" + kind;
        haskind = true;
    }

    with_preview = (arguments.length > 3 && arguments[3]);
    
    // always initialize elements with data in 'closed' state
    if (hasdata && typeof data["_state"] == "undefined") data["_state"] = with_preview ? "closed" : "open";
    
    if (getSubformType(type, subformid) == "table") {
        // subform type 'table'; call add_row instead
        if (hasdata && haskind)
            return add_row(subformid, type, data, kind);
        else if (hasdata)
            return add_row(subformid, type, data);
        else if (haskind)
            return add_row(subformid, type, null, kind);
        else
            return add_row(subformid, type);
    }

    if (globalcounters[type] == null)
        globalcounters[type] = 0;
    else
        globalcounters[type]++;

    var num = globalcounters[type];
    newid = type + num;
    
    var x = new Array();
    x['formid'] = newid;
    // add a new instance
    var insert = elem(type, "insert", subformid);

    var errornode = false;
    if (hasdata && data["_errors"] && data["_errors"].length) {
        x["errors"] = data["_errors"];
        errornode = document.createElement("div");
        errornode.setAttribute("id", newid+"_errors");
        var tpl = elem(typewithkind, "errors");
        errornode.innerHTML = repl(tpl.innerHTML, x);
        insert.appendChild(errornode);
    }
    
    var formnode = document.createElement("div");
    formnode.setAttribute("id", newid+"_form");
    formnode.setAttribute("num", num);
    formnode.setAttribute("subformid", subformid);
    formnode.setAttribute("subform", "true");
    formnode.setAttribute("type", type);
    var tpl = elem(typewithkind, "form");
    formnode.innerHTML = repl(tpl.innerHTML, x);

    // create the 'state' node
    var statenode = document.createElement("input");
    statenode.setAttribute("id", "_state");
    statenode.setAttribute("type", "hidden");
    statenode.setAttribute("name", "_state");
    statenode.setAttribute("subformid", subformid);
    statenode.setAttribute("value", "open");
    formnode.appendChild(statenode);

    if (haskind) {
        // create the _type node
        var kindnode = document.createElement("input");
        kindnode.setAttribute("id", "_kind");
        kindnode.setAttribute("type", "hidden");
        kindnode.setAttribute("name", "_kind");
        kindnode.setAttribute("value", kind);
        formnode.appendChild(kindnode);
    }
    // add the form node
    insert.appendChild(formnode);

    // formnode.setAttribute("className", "subform");

    var rendernode = document.createElement("div");
    rendernode.setAttribute("id", newid+"_render");
    rendernode.innerHTML = repl(elem(typewithkind, "render").innerHTML, x);

    insert.appendChild(rendernode);

    if (!hasdata || data["_state"] == "open") 
        elem(newid, "render").style.display = "none";
    else {
        deactivate_htmlareas(newid);
        elem(newid, "form").style.display = "none";
    }
    
    // find full path name
    n = getParentName(type, subformid);

    
    var newname = n + "[" + num + "]";

    if (typeof window.delayedCallbacks == "undefined") window.delayedCallbacks = [];
    window.delayedCallbacks[newid] = [];

    var l = formelems(formnode);
    for (var k in l) {
        if (typeof l[k] == "function") continue;
        var oldname = l[k].name;
        
        var oldid = l[k].id;
        if (!oldname) continue;

        l[k] = set_element_name(l[k], newname+oldname.replace(/^(\w+)/, "\[$1\]"));
	
        if (l[k].id) {
            if (l[k].id.substr(0, newid.length)!=newid) {
                l[k].id = newid + "_" + l[k].id;
            }
        } else
            l[k].id = newid + "_" + oldname;
        
        if (l[k].type == "file") {
            l[k].setAttribute("realname", l[k].name);
            l[k].name = newid + "_" + oldname;
        }
        l[k].oldname = oldname;
    }

    for (var k in l) {
        if (typeof l[k] == "function") continue;
        if (!l[k].oldname) continue;

        // this ugly hack takes care of settings arrays with data.
        // i confess it IS ugly, but it works.
        if (hasdata) {
            var s = "data."+l[k].oldname.replace("[", "['").replace("]", "']");
            var d = eval(s);
            if (d) setvalue(l[k], d);
            else if (s.match(/\['value'\]$/) && (d = eval(s.replace(/\['value'\]$/, "")))) {
                // special case for uploaddatatypes
                setvalue(l[k], d.name);
            }
        }

        var cbstring = "window.oncreate_"+typewithkind+"['"+l[k].oldname.replace(/\[.*$/, "")+"']";
        var callback = eval(cbstring);

        // Call global function oncreate_<subform>, if it exists.
        if (typeof callback == "function") {
            if ((!hasdata || hasdata && data["_state"] != "closed")) {
                if (hasdata && data[l[k].oldname])
                    callback(l[k].id, data[l[k].oldname]);
                else
                    callback(l[k].id, null);
            } else {
                window.delayedCallbacks[newid][window.delayedCallbacks[newid].length] = [callback, l[k].id, hasdata?data[l[k].oldname]:null];
            }
        }
        
        rendernode.innerHTML = rendernode.innerHTML.replace(new RegExp("%"+l[k].oldname+"%", "g"), "%"+l[k].name+"%");
    
    }
    
    // GLOBAL oncreate callback for oncrate_<subform>
    var callback = eval("window.oncreate_"+typewithkind+"['GLOBAL']");
    if (typeof callback == "function") {
        if (hasdata)
            callback(newid, data);
        else
            callback(newid, null);
    }

    update_render_values(typewithkind, elem(newid, "form"))
        last_added_id = newid;
}

function edit(formid, type) {
    var subformid = elem(formid, "_state").getAttribute('subformid');
    var tag = getSubformType(type, subformid);

    elem(formid, "_state").value = "open";

    deactivate_htmlareas(formid);

    if (tag == "table") {
        // 'table' type subform
        var i = move_rows( elem(type, "insert", subformid), elem(type, "store", subformid), formid+"_render");
        move_rows( elem(type, "store", subformid), elem(type, "insert", subformid), formid+"_form", i);
        
    } else {
        // regular subform
        elem(formid, "form").style.display = "block";
        elem(formid, "render").style.display = "none";
    }

    if (window.delayedCallbacks[formid] && window.delayedCallbacks[formid].length) {
        //alert(window.delayedCallbacks[formid].length);
        for (var i=0; i<window.delayedCallbacks[formid].length; i++) {
            var l = window.delayedCallbacks[formid][i];
            l[0](l[1], l[2]);
        }
        window.delayedCallbacks[formid] = [];
    }

    activate_htmlareas(formid);
}

function save(formid, type) {
    var s = elem(formid, "_state");
    var subformid = s.getAttribute('subformid');
    var tag = getSubformType(type, subformid);
    var typewithkind = elem(formid, "_kind")?type+"_"+elem(formid, "_kind").value:type;
    
    elem(formid, "_state").value = "closed";

    deactivate_htmlareas(formid);

    if (window.organicforms) {
        var n = s;
        while (n && n.tagName.toLowerCase() != "form")
            n = n.parentNode;
        if (n) {
            var f = window.organicforms[n.id];
            var nodes;
            if (tag == "table") {
                nodes = formelems_rows(elem(typewithkind, "insert", subformid), formid+"_form");
            } else {
                var e = elem(typewithkind, "form", subformid);
                nodes = formelems(e);
            }
            for (var k in nodes) {
                if (typeof nodes[k] == "function") continue;
                if (!nodes[k].id || !f.controls[nodes[k].id]) continue;
                f.validateElement(nodes[k]);
            }
        }
    }

    if (tag == "table") {
        // subform type 'table'
        
        update_render_values_rows(typewithkind, elem(type, "insert", subformid), formid+"_form");

        var i = move_rows( elem(type, "insert", subformid), elem(type, "store", subformid), formid+"_form");
        move_rows( elem(type, "store", subformid), elem(type, "insert", subformid), formid+"_render", i);
    } else {

        // regular subform    
        elem(formid, "form").style.display = "none";
        elem(formid, "_state").value = "closed";
        
        update_render_values(typewithkind, elem(formid, "form"));
    
        elem(formid, "render").style.display = "block";
    }

    activate_htmlareas(formid);
}

function del(formid, type) {
    var subformid = elem(formid, "_state").getAttribute('subformid');
    var tag = getSubformType(type, subformid);
    var typewithkind = elem(formid, "_kind")?type+"_"+elem(formid, "_kind").value:type;

    if (tag == "table") {
        // subform type 'table'
        insert = elem(typewithkind, "insert", subformid);
        for (var i=0; i<insert.rows.length; i++) {
            var s = insert.rows[i].getAttribute("row_id");
            if (s && s.substr(0, s.lastIndexOf("_")) == formid) {
                insert.deleteRow(i--);
            }
        }
        insert = elem(typewithkind, "store", subformid);
        for (var i=0; i<insert.rows.length; i++) {
            var s = insert.rows[i].getAttribute("row_id");
            if (s && s.substr(0, s.lastIndexOf("_")) == formid) {
                insert.deleteRow(i--);
            }
        }
        var e = elem(formid, "_state");
        e.parentNode.removeChild(e);
        var e = elem(formid, "_kind");
        if (e) e.parentNode.removeChild(e);

    } else {
        // regular subform        
        p = elem(formid, "form").parentNode;
        p.removeChild(elem(formid, "form"));
        p.removeChild(elem(formid, "render"));
        if (elem(formid, "errors"))
            p.removeChild(elem(formid, "errors"));
    }

    var callback_array = eval("window.ondelete_"+typewithkind);
    if (callback_array && callback_array.length>0) {
        for (var i in callback_array) {
            if (typeof callback_array[i] == "function") {
                callback_array[i](formid);
            }
        }
    }
}

function update_render_values(type, formobj) {
    // vervang 'render' values vanuit form object

    var inputs = formelems(formobj);

    var formid = formobj.id.replace(/_form$/, "");
    
    var dict = { };
    
    for (var k in inputs) {
        if (typeof inputs[k] == "function") continue;
        
        var value = false;
        if (eval("window.editors") && window.editors[inputs[k].id]) {
            value = window.editors[inputs[k].id].getHTML();
        } else if (inputs[k].type && inputs[k].type.toLowerCase() == "file") {
            var oldelem = document.getElementById(inputs[k].id+"_oldname");
            if (oldelem) value = oldelem.value;
        } else {
            value = inputs[k].value;
        }

        if (typeof inputs[k].oldname == "undefined") continue;
        dict[inputs[k].oldname] = value;
    }

    var r = document.getElementById(formid+"_render");
    if (!r) return;

    var tpl = elem(type, "render").innerHTML.replace(/%formid%/g, formid);
    r.innerHTML = simple_tpl(tpl, dict);
}

function simple_tpl(tpl, dict) {
    dict = flattern_array(dict);

    tpl = tpl.replace(/\{\$([A-Za-z_][\|\.A-Za-z0-9_]*)\}/g, function(w, g) {
        g = g.split("|");
        var cnt = dict[g[0]];
        for (var i=1;i<g.length;i++) cnt = eval(g[i])(cnt);
        return cnt || w;
    });
    tpl = tpl.replace(/\{\$([A-Za-z_][\|\.A-Za-z0-9_]*)\}/g, "");
    return tpl;
}

function flattern_array(dict, prefix) {
    if (typeof dict != "object") {
        var o = {};
        o[prefix] = dict;
        return o;
    }
    
    if (typeof dict.length != "undefined") {
        // numeric array
        var result = {};
        for (var i=0; i<dict.length; i++) {
            var p = (prefix != null ? prefix + "." : "") + i;
            var r = flattern_array(dict[i], p);
            for (var k in r) {
                if (typeof r[k] == "function") continue;
                result[k] = r[k];
            }
        }
        return result;
        
    } else {
        // object
        var result = {};
        for (var kx in dict) {
            if (typeof dict[kx] == "function") continue;

            var p = (prefix != null ? prefix + "." : "") + kx;
            var r = flattern_array(dict[kx], p);
            for (var k in r) {
                if (typeof r[k] == "function") continue;
                result[k] = r[k];
            }
        }
        return result;
        
    }
}

function copy_rows(source, dest, row_id, replace_data, num, type) {
    var n = 0;
    var insert_index = dest.rows.length;
    for (i=0; i<dest.rows.length; i++) {
        if (dest.rows[i].getAttribute("row_id") == "insertbefore") {
            insert_index = i;
            break;
        }
    }

    dest = dest.getElementsByTagName("TBODY")[0];

    for (i=0; i<source.rows.length; i++) {
        
        var tds = source.rows[i].getElementsByTagName("TD");
        var r = document.createElement("TR");
        var j = insert_index + n++;
        dest.insertBefore(r, (j<dest.childNodes.length)?dest.childNodes[j]:null);
        r.setAttribute("row_id", row_id);
        r.setAttribute("subform", "true");
        r.setAttribute("num", num);
        r.setAttribute("type", type);
        
        for (j=0; j<tds.length; j++) {
            var n = tds[j].cloneNode(true);
            n.innerHTML = repl(n.innerHTML, replace_data);
            n.setAttribute("subformid", row_id.substr(0, row_id.lastIndexOf("_")));
            r.appendChild(n);
        }
    }
}

function move_rows(source, dest, row_id) {
    var insert_index = 0;
    if (arguments.length>3) insert_index = arguments[3];
    
    var first_tr = -1;
    var n=0;
    dest = dest.getElementsByTagName("TBODY")[0];
    for (i=0; i<source.rows.length; i++) {
        if (source.rows[i].getAttribute("row_id") == row_id) {
            if (first_tr<0) first_tr=i;
            var j = insert_index+n++;
            dest.insertBefore(source.rows[i--], (j<dest.childNodes.length)?dest.childNodes[j]:null);
	    
        } else
            if (first_tr>=0) break;
    }
    return first_tr;
}


function add_row(subformid, type)
{
    var hasdata = false;
    var haskind = false;
    var data;
    if (arguments.length == 3) {
        // we're adding with data
        data = arguments[2];
        hasdata = true;
    }
    var typewithkind = type;
    var kind;
    if ((arguments.length > 3 && arguments[3]) || hasdata && data["_kind"]) {
        kind = hasdata?data["_kind"]:arguments[3];
        typewithkind = type + "_" + kind;
        haskind = true;
    }

    if (globalcounters[type] == null)
        globalcounters[type] = 0;
    else
        globalcounters[type]++;

    var num = globalcounters[type];
    newid = type + num;

    var x = new Array();
    x['formid'] = newid;
    // add a new instance
    var insert = elem(type, "insert", subformid);

    // create the 'store' node, if not existing
    var store = elem(type, "store", subformid);
    if (!store) {
        store = document.createElement("TABLE");
        store.appendChild(document.createElement("TBODY"));
        if (!subformid)
            store.setAttribute("id", type+"_store");
        else 
            store.setAttribute("id", subformid+"_"+type+"_store");
        // store.setAttribute("border", "10");
        store.style.display = "none";
        insert.parentNode.insertBefore(store, insert);
    }

    // create the 'state' node
    var statenode = document.createElement("input");
    statenode.setAttribute("id", "_state");
    statenode.setAttribute("type", "hidden");
    statenode.setAttribute("subformid", subformid);
    statenode.setAttribute("name", "_state");
    statenode.setAttribute("value",(!hasdata||!data["_state"])?"open":data["_state"]);
    insert.parentNode.insertBefore(statenode, insert);

    if (haskind) {
        // create the 'kind' node
        var kindnode = document.createElement("input");
        kindnode.setAttribute("id", "_kind");
        kindnode.setAttribute("type", "hidden");
        kindnode.setAttribute("name", "_kind");
        kindnode.setAttribute("value", kind);
        insert.parentNode.insertBefore(kindnode, insert);
    }
    
    var errornode = false;
    if (hasdata && data["_errors"] && data["_errors"].length) { 
        x["errors"] = data["_errors"];
        data["_state"] = "open";

        copy_rows(elem(typewithkind, "errors"), insert, newid+"_form", x, num, type);
        // copy_rows(elem(type, "errors"), store, newid+"_render", x);
    }

    if (statenode.value == "open") {
        copy_rows(elem(typewithkind, "form"), insert, newid+"_form", x, num, type);
        copy_rows(elem(typewithkind, "render"), store, newid+"_render", x, num, type);
    } else {
        copy_rows(elem(typewithkind, "render"), insert, newid+"_render", x, num, type);
        copy_rows(elem(typewithkind, "form"), store, newid+"_form", x, num, type);
    }

    
    // find full path name
    n = getParentName(type, subformid);
    newname = n + "[" + num + "]";

    var l = formelems_rows((statenode.value=="open")?insert:store, newid+"_form");
    l["_state"] = statenode;
    if (haskind)
        l["_kind"] = kindnode;
    
    for (var k in l) {
        if (typeof l[k] == "function") continue;
        
        var oldname = l[k].name;
        var oldid = l[k].id;

        if (!oldname) continue;

        l[k] = set_element_name(l[k], newname+oldname.replace(/^(\w+)/, "\[$1\]"));
	
        if (l[k].id) {
            if (l[k].id.substr(0, newid.length)!=newid) {
                l[k].id = newid + "_" + l[k].id;
            }
        } else
        	l[k].id = newid + "_" + oldname;
        
        if (l[k].type == "file") {
            l[k].setAttribute("realname", l[k].name);
            l[k].name = newid + "_" + oldname;
        }
        
        if (hasdata && data[oldname] != null) {
            setvalue(l[k], data[oldname]);
        }

        // update IDs in all rendernode template values
        var rendertgt = (statenode.value == "closed")?insert:store;
        for (var i=0; i<rendertgt.rows.length; i++) {	
            if (rendertgt.rows[i].getAttribute("row_id") == newid+"_render") {
                var tds = rendertgt.rows[i].getElementsByTagName("TD");
                for (var j=0; j<tds.length; j++)
                    tds[j].innerHTML = tds[j].innerHTML.replace(new RegExp("%"+oldname+"%", "g"), "%"+l[k].name+"%");
            }
        }
        
        // Call global function oncreate_<subform>, if it exists.
        var callback = eval("window.oncreate_"+typewithkind+"['"+oldname+"']");
        if (typeof callback == "function") {
            if (hasdata && data[k])
                callback(l[k].id, data[k]);
            else
                callback(l[k].id, null);
        }
    }

    // GLOBAL oncreate callback for oncrate_<subform>
    var callback = eval("window.oncreate_"+typewithkind+"['GLOBAL']");
    if (typeof callback == "function") {
        if (hasdata)
            callback(newid, data);
        else
            callback(newid, null);
    }

    if (statenode.value == "closed")
        update_render_values_rows(typewithkind, store, newid+"_form");

    last_added_id = newid;
}

function update_render_values_rows(type, tbl, row_id) {
    for (var i=0; i<tbl.rows.length; i++) {
        if (tbl.rows[i].getAttribute("row_id") == row_id) {
            update_render_values(type, tbl.rows[i]);
        }
    }
}


function swap_rows(tbl, index_a, index_b, tmptbl) {
    var id_a = tbl.rows[index_a].getAttribute("row_id");
    var id_b = tbl.rows[index_b].getAttribute("row_id");

    var tbody = tbl.getElementsByTagName("TBODY")[0];
    var n=0;
    for (var i=index_b; i<tbl.rows.length; i++) {
        if (tbl.rows[i].getAttribute("row_id") == id_b) 
            tbody.insertBefore(tbl.rows[i--], tbody.childNodes[index_a+n++]);
    }

    var elems_a, elems_b;
    if (id_a.match(/_form$/))
        elems_a = formelems_rows(tbl, id_a);
    else
        elems_a = formelems_rows(tmptbl, id_a.replace(/_render$/, "_form"));
    var s = elem(id_a.substr(0, id_a.lastIndexOf("_")), "_state");
    elems_a[s.name] = s;
    var s = elem(id_a.substr(0, id_a.lastIndexOf("_")), "_kind");
    if (s) elems_a[s.name] = s;
    
    if (id_b.match(/_form$/))
        elems_b = formelems_rows(tbl, id_b);
    else
        elems_b = formelems_rows(tmptbl, id_b.replace(/_render$/, "_form"));
    
    s = elem(id_b.substr(0, id_b.lastIndexOf("_")), "_state");
    elems_b[s.name] = s;
    s = elem(id_b.substr(0, id_b.lastIndexOf("_")), "_kind");
    if (s) elems_b[s.name] = s;
    swap_names(elems_a, elems_b);
    
}

function swap_divs(insert, a_id, b_id) {
    var y = false;
    for (var i=0; i<insert.childNodes.length; i++) {
        var x = insert.childNodes[i].id.substr(0, insert.childNodes[i].id.lastIndexOf("_"));
        if (x == b_id || x == a_id) { y = insert.childNodes[i]; break; }
    }
    if (!y) return;

    if (y.id.substr(0, y.id.lastIndexOf("_")) == a_id) {
        // move all b's before a
        if (elem(b_id, "errors")) insert.insertBefore(elem(b_id, "errors"), y);
        insert.insertBefore(elem(b_id, "form"), y);
        insert.insertBefore(elem(b_id, "render"), y);
    } else {
        // move all a's before b
        if (elem(a_id, "errors")) insert.insertBefore(elem(a_id, "errors"), y);
        insert.insertBefore(elem(a_id, "form"), y);
        insert.insertBefore(elem(a_id, "render"), y);
    }

    elems_a = formelems(elem(a_id, "form"));
    elems_b = formelems(elem(b_id, "form"));
    swap_names(elems_a, elems_b);
}

function move_down(formid, type) {
    var subformid = elem(formid, "_state").getAttribute('subformid');
    var tag = getSubformType(type, subformid);

    deactivate_htmlareas(formid);
    
    if (tag == "table") {
        var tbl = elem(type, "insert", subformid);
        var index_a = -1;
        var index_b = -1;
        var id_b;
        for (var i=0; i<tbl.rows.length; i++) {
            var r = tbl.rows[i].getAttribute("row_id");
            if (r && r.substr(0, formid.length) == formid) {
                if (index_a < 0) index_a = i;
            } else if (r && r !="insertbefore" && index_a >= 0) {
                index_b = i;
                id_b = r;
                break;
            }
        }
        if (index_b<0) return;
        swap_rows(tbl, index_a, index_b, elem(type, "store"));
    } else {

        var ins = elem(type, "insert", subformid);
        var other_id = false;
        
        for (var i=0; i<ins.childNodes.length; i++) {
            id = ins.childNodes[i].id.substr(0, ins.childNodes[i].id.lastIndexOf("_"));
            if (id == formid) {
                other_id = true;
            } else if (other_id == true) {
                other_id = id;
                break;
            }
        }
        if (other_id == true) return;

        swap_divs(ins, formid, other_id);
    }
    activate_htmlareas(formid);
}

function move_up(formid, type) {
    var subformid = elem(formid, "_state").getAttribute('subformid');
    var tag = getSubformType(type, subformid);

    deactivate_htmlareas(formid);

    if (tag == "table") {
        var tbl = elem(type, "insert", subformid);
        var index_a = -1;
        var index_b = -1;
        var id_b;
        var prev_r = false;
        for (var i=0; i<tbl.rows.length; i++) {
            var r = tbl.rows[i].getAttribute("row_id");
            if (r && r.substr(0, formid.length) == formid) {
                index_b = i;
                break;
            }
            else if (r && r != prev_r) index_a = i;
            prev_r = r;
        }
        if (index_a<0) return;

        swap_rows(tbl, index_a, index_b, elem(type, "store"), formid);
    } else {
        var ins = elem(type, "insert", subformid);
        var other_id = false;
        for (var i=0; i<ins.childNodes.length; i++) {
            id = ins.childNodes[i].id.substr(0, ins.childNodes[i].id.lastIndexOf("_"));
            if (id == formid) {
                break;
            } else
                other_id = id;
        }
        if (other_id == false) return;

        swap_divs(ins, formid, other_id);
    }
    activate_htmlareas(formid);
}



// determines the longest common prefix for both
// input collections, and swaps them together with
// everything numeric that comes after it.
function swap_names(elems_a, elems_b) {
    var ia, ib;
    for (k in elems_a) { if (typeof elems_a[k] == "function") continue; ia = elems_a[k]; break; }
    for (k in elems_b) { if (typeof elems_b[k] == "function") continue; ib = elems_b[k]; break; }

    var i=0;
    for (var i=0; (ia.name.substr(0, i) == ib.name.substr(0, i)); i++) ; i--;
    s = ia.name.substr(0, i).replace(/([\[\]])/g, "\\$1");
    var r = new RegExp("^"+s+"(\\d+)");
    
    repl_a = ia.name.substr(0, i)+ib.name.match(r)[1];
    repl_b = ia.name.substr(0, i)+ia.name.match(r)[1];

    for (k in elems_a) {
        if (typeof elems_a[k] == "function") continue; 
        n = elems_a[k].name;
        elems_a[k] = set_element_name(elems_a[k], elems_a[k].name.replace(r, repl_a));
        var y = document.getElementById("value_%"+n+"%");
        if (y) y.id = "tmp_value_%"+elems_a[k].name+"%";
    }
    for (k in elems_b) {
        if (typeof elems_b[k] == "function") continue; 
        n = elems_b[k].name;
        elems_b[k] = set_element_name(elems_b[k], elems_b[k].name.replace(r, repl_b));
        var y = document.getElementById("value_%"+n+"%");
        if (y) y.id = "value_%"+elems_b[k].name+"%";
    }
    for (k in elems_a) {
        if (typeof elems_a[k] == "function") continue; 
        elems_a[k] = set_element_name(elems_a[k], elems_a[k].name.replace(r, repl_a));
        var y = document.getElementById("tmp_value_%"+elems_a[k].name+"%");
        if (y) y.id = y.id.replace(/^tmp_/, "");
    }    
}
