newNode = function(params, debug) {

    /*
    expects an object of parameters, some optional:
        element*
        appendTo*
        insertBefore (child element of appendTo. If passed, insertBefore runs, not appendChild)
        fadeIn (if true, and jQuery is present in the site environment, created element will fade in, not abruptly appear). Will error if no jQuery.
        attrs (a sub-object of attr/value pairings, the like of which are allowed inside opening HTML tags)
        evts (a sub-object of evt/func pairings, without leading 'on')
        styles (a sub-object of attr/value pairings)
        html (inner html for new element)
        textNode (runs createTextNode on the new element and fills it with the passed text)
            label (if element is a field or select, and 'label' is passed containing text or html, also creates label)
        label styles: if label passed, this is an option object containing styles for the label
        options (a sub-object of attr/value pairings to build options if element == 'select')
        clear (both, left, right or nothing). creates a blank div with clear=[val] underneath the created element)
        br (if true, adds a <br /> after the created element)
    */

   //utility event registration method - mitya's clever method whereby even in IE 'this' inside the callback represents the element the event was bound to, not window
    arguments.callee.registerEvent = function(on, ev, func) {
        if (window.addEventListener)
            on.addEventListener(ev, func, false);
        else {
            if (!on.funcsCounter) on.funcsCounter = 0;
            on['func'+on.funcsCounter] = func;
            on.funcsCounter++;
            on.attachEvent("on"+ev, function() { for(var i=0; i<on.funcsCounter; i++) on['func'+i](); });
        }
    }

    //die if insufficient args passed
    if (typeof params != "object" || !params.element || !params.appendTo) return false;

    //if we get this far, params OK. Build.
    var node = document.createElement(params.element);

    //add attributes
    if (typeof params.attrs == "object") {
        for (var w in params.attrs) {
            var attrName = w != 'classs' ? w : 'className';
            node[attrName] = params.attrs[w];
        }
    }

    //add options if element == 'select'
    if (params.element.toLowerCase() == 'select' && typeof params.options == 'object') {
        for(var r in params.options) {
            var colonPos;
            var optionText = r;
            if (r.substr((colonPos = r.indexOf(':'))) == ':selected') {
                selected = 'selected'
                optionText = r.substr(0, colonPos);
            } else
                selected = null;
            var opt = arguments.callee({element: 'option', appendTo: node, textNode: optionText, attrs: {selected: selected, value: params.options[r]}});
            node.appendChild(opt);
        }
    }

    //populate with text?
    if (params.textNode)
        node.appendChild(document.createTextNode(params.textNode));

    //if element is field or select and label passed, create label too
    if ((" "+['input', 'textarea', 'select'].join(" ")+" ").match(eval("/\\b"+params.element+"\\b/")) && params.label)
        arguments.callee({element: 'label', fadeIn: params.fadeIn, appendTo: params.appendTo, html: params.label, styles: params.labelStyles});

    //register events
    if (typeof params.evts == "object") {
        for (var w in params.evts) arguments.callee.registerEvent(node, w, params.evts[w]);
    }

    //inner HTML?
    if (params.html) node.innerHTML = params.html;

    //add styles
    if (typeof params.styles == "object") {
        for (var y in params.styles) { node.style[y] = params.styles[y]; }
    }

    //publish
    !params.insertBefore ? params.appendTo.appendChild(node) : params.appendTo.insertBefore(node, params.insertBefore);

    //if fadeIn, start as hidden
    if (params.fadeIn) {
        node.style.display = 'none';
        $(node).fadeIn('slow');
    }

    //add clear afterwards?
    if (params.clear)
        arguments.callee({element: 'div', appendTo: params.appendTo, styles: {clear: params.clear}});

    //add br afterwards?
    if (params.br)
        arguments.callee({element: 'br', appendTo: params.appendTo});

    //assign new element to var, if called in an assignment
    return node;

}