/*--------------------------------------------------------------------------

  This is part of the Multithreading system of the Karoo project.

  (C) 2009,2010 Brian Modra <brian@zwartberg.com>

  This library is free software; you can redistribute them and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License,
  or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with these libraries; if not, write to the Free Software Foundation,
  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

--------------------------------------------------------------------------*/

var widgets = null;
function initWidgets(surf)
{
    if (!widgets) {
	widgets = new KarooWidgets(surf);
	widgets.traverse(document);
    }
}

function karooWidgetListItemMouseOverHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;

    while (target.parentNode && (!target.parentNode.widget_id)) {
	target = target.parentNode;
    }
    if (target.parentNode && target.parentNode.widget_enabled) {
	target.parentNode.focus();

	var selected = target.parentNode.widget_get_selected();
	var count = 0;

	for (var n = target.parentNode.firstChild; n; n = n.nextSibling) {
	    if (n.nodeType == 1) {
		var className = n.widget_get_classname();
		if (n == target) {
		    if (count == selected) {
			n.className = className + "_selected";
		    }
		    else {
			n.className = className + "_highlight";
		    }
		}
		else {
		    if (count == selected) {
			n.className = className + "_selected";
		    }
		    else {
			n.className = className;
		    }
		}
		count++;
	    }
	}
    }
    return true;
}

function karooWidgetListItemMouseOutHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target.parentNode && (!target.parentNode.widget_id)) {
	target = target.parentNode;
    }

    if (target.parentNode && target.parentNode.widget_enabled) {
	var selected = target.parentNode.widget_get_selected();
	var count = 0;

	for (var n = target.parentNode.firstChild; n; n = n.nextSibling) {
	    if (n.nodeType == 1) {
		if (n == target) {
		    var className = n.widget_get_classname();
		    
		    if (count == selected) {
			n.className = className + "_selected";
		    }
		    else {
			n.className = className;
		    }
		}
		count++;
	    }
	}
    }
    return true;
}

function karooWidgetMenuItemMouseOverHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target.parentNode && target.widget_type != "menuitem") {
	target = target.parentNode;
    }
    if (target.widget_enabled) {
	target.focus();
	var className = target.widget_get_classname();
	target.className = className + "_highlight";
    }
    return true;
}

function karooWidgetMenuItemMouseOutHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target.parentNode && target.widget_type != "menuitem") {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	target.focus();
	var className = target.widget_get_classname();
	target.className = className;
    }
    return true;
}

function karooWidgetMenuItemMouseUpHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }
    if (target.widget_enabled) {
	var className = target.widget_get_classname();
	target.className = className + "_highlight";
    }
    return true;
}

function karooWidgetMenuItemMouseDownHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }
    if (target.widget_enabled) {
	target.focus();
	var className = target.widget_get_classname();
	target.className = className + "_selected";
    }
    return true;
}

function karooWidgetMenuItemClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target.parentNode && target.widget_type != "menuitem") {
	target = target.parentNode;
    }
    if (target.widget_enabled) {	
	if (target.widget_list) {
	    if (target.widget_list.style.visibility == "visible" || target.widget_list.style.visibility == "inherit") {
		target.widget_popdown();
	    }
	    else {
		widget_traverse_popdown(document);
		target.widget_popup();
	    }
	}
	else {
	    widget_traverse_popdown(document);
	    if (target.widget_action) {
		try {
		    eval(target.widget_action)(target);
		    if (e.preventDefault) {
			e.preventDefault();
		    }
		    return false;
		}  catch (e) {}
	    }
	}
    }
    return true;
}

function karooWidgetListMouseOverHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;

    while (target && (!target.widget_id)) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	target.focus();
    }
    return true;
}

function karooWidgetListMouseOutHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target && (!target.widget_id)) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {	
	target.blur();
    }

    return true;
}

function karooWidgetCalendarMouseOverHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;

    while (target && (!target.widget_id)) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	if (target.widget_textbox) {
	    target.widget_textbox.focus();
	}

	var className = "widget_calendar";
	if (target.className) {
	    if (target.className.length > 9 && target.className.substr(target.className.length - 9) == "_selected") {
		className = target.className.substr(0,target.className.length - 9);
	    }
	    else {
		className = target.className;
	    }
	}
	target.className = className + "_selected";
    }
    return true;
}

function karooWidgetCalendarMouseOutHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target && (!target.widget_id)) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	var className = "widget_calendar";
	if (target.className) {
	    if (target.className.length > 9 && target.className.substr(target.className.length - 9) == "_selected") {
		className = target.className.substr(0,target.className.length - 9);
	    }
	    else {
		className = target.className;
	    }
	}
	target.className = className;
    }
    return true;
}

function karooWidgetListItemClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while (target.parentNode && (!target.parentNode.widget_id)) {
	target = target.parentNode;
    }
    var parent = target;
    while (parent.parentNode && parent.widget_type != "list") {
	parent = parent.parentNode;
    }
    if (parent && parent.widget_enabled) {	
	widget_traverse_popdown(document);
	
	var count = 0;

	for (var n = parent.firstChild; n; n = n.nextSibling) {
	    if (n.nodeType == 1) {
		if (n == target) {
		    target.parentNode.widget_select(count, true);
		    if (parent.widget_action) {
			try {
			    eval(parent.widget_action)(parent);
			    if (e.preventDefault) {
				e.preventDefault();
			    }
			    return false;
			} catch (e) {}
		    }
		    return true;
		}
		count++;
	    }
	}
    }
    return true;
}

function karooWidgetListKeyPressHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }
    if (target.widget_enabled) {
	var unicode = e.charCode? e.charCode: e.keyCode;
	if (unicode == 38) {
	    var selected = target.widget_get_selected();
	    if (selected > 0) {
		target.widget_select(selected-1);
	    }
	    else if (selected == -1) {
		target.widget_select(0);
	    }
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	}
	else if (unicode == 40) {
	    var selected = target.widget_get_selected();
	    target.widget_select(selected+1);
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	}
    }
    else {
	if (e.preventDefault) {
	    e.preventDefault();
	}
	return false;
    }
    return true;
}

function karooWidgetListDblClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled && target.widget_action) {
	try {
	    eval(target.widget_action)(target);
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	} catch (e) {}
    }

    return true;
}

function karooWidgetMenuItemDblClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled && target.widget_action) {
	try {
	    eval(target.widget_action)(target);
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	} catch (e) {}
    }

    return true;
}

function karooWidgetCalendarDblClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled && target.widget_action) {
	try {
	    eval(target.widget_action)(target);
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	} catch (e) {}
    }

    return true;
}

function karooWidgetCheckboxClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	widget_traverse_popdown(document);
	if (target.widget_action) {
	    try {
		eval(target.widget_action)(target);
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    } catch (e) {}
	}
    }

    return true;
}

function karooWidgetButtonMouseOverHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled && target.className) {
	var className = target.widget_get_classname();
	target.className = className + "_highlight";
    }

    return true;
}

function karooWidgetButtonMouseOutHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	target.className = target.widget_get_classname();
    }

    return true;
}

function karooWidgetButtonMouseUpHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	target.className = target.widget_get_classname() + "_highlight";
    }

    return true;
}

function karooWidgetButtonMouseDownHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	target.className = target.widget_get_classname() + "_selected";
    }

    return true;
}

function karooWidgetButtonClickHandler(e)
{
    if (window.event) {
	e = event;
    }
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	widget_traverse_popdown(document);
	if (target.widget_action) {
	    try {
		eval(target.widget_action)(target);
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    } catch (e) {}
	}
    }
    
    if (e.preventDefault) {
	e.preventDefault();
    }

    return false;
}

function karooWidgetTextKeyPressHandler(e)
{
    if (window.event) {
	e = event;
    }
    var unicode = e.charCode? e.charCode: e.keyCode;
    var actualkey = String.fromCharCode(unicode);
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }
    if (target.widget_enabled) {
	if (unicode == 90 && target.widget_ctrl) {
	    if (target.widget_initial_value) {
		target.value = target.widget_initial_value;
		target.widget_initial_value = null;
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    }
	    else if (target.widget_initial_checked) {
		target.checked = target.widget_initial_checked;
	    }
	}
	if (unicode == 122) {
	    if (target.widget_initial_value) {
		target.value = target.widget_initial_value;
		target.widget_initial_value = null;
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    }
	    else if (target.widget_initial_checked) {
		target.checked = target.widget_initial_checked;
	    }
	}
	else if (target.widget_numeric) {
	    var nine = '9'.charCodeAt(0);
	    var dot = '.'.charCodeAt(0);
	    var plus = '+'.charCodeAt(0);
	    var minus = '-'.charCodeAt(0);
	    if (!(unicode <= nine || unicode == dot || unicode == plus || unicode == minus)) {
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    }
	}

	if (target.widget_initial_value == null) {
	    target.widget_initial_value = target.value;
	}
    }
    else {
	if (e.preventDefault) {
	    e.preventDefault();
	}
	return false;
    }
    return true;
}

function karooWidgetCalendarKeyPressHandler(e)
{
    if (window.event) {
	e = event;
    }
    var unicode = e.charCode? e.charCode: e.keyCode;
    var actualkey = String.fromCharCode(unicode);
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	var check_needed = false;

	var className = "calendar";
	if (target.widget_textbox.className) {
	    if (target.widget_textbox.className.length > 4
		&& target.widget_textbox.className.substr(target.widget_textbox.className.length - 4) == "_bad") {
		className = target.widget_textbox.className.substr(0, target.widget_textbox.className.length - 4)
	    }
	    else {
		className = target.widget_textbox.className;
	    }
	}
	
	if (unicode == 90 && target.widget_ctrl) {
	    target.widget_set_date(target.widget_initial_value);
	    if (target.widget_change) {
		try { eval(target.widget_change)(target); } catch (e) {}
	    }
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	}
	else if (unicode == 122) {
	    target.widget_set_date(target.widget_initial_value);
	    if (target.widget_change) {
		try { eval(target.widget_change)(target); } catch (e) {}
	    }
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    return false;
	}
	else if (unicode == 38 || unicode == 40) {
	    var field = null;
	    var pos = getCaretPosition(target.widget_textbox);
	    if (target.widget_format == "dmy") {
		if (pos < 3) {
		    field = "day";
		}
		else if (pos < 6) {
		    field = "month";
		}
		else {
		    field = "year";
		}
	    }
	    else if (target.widget_format == "mdy") {
		if (pos < 3) {
		    field = "month";
		}
		else if (pos < 6) {
		    field = "day";
		}
		else {
		    field = "year";
		}
	    }
	    else if (target.widget_format == "ymd") {
		if (pos < 5) {
		    field = "year";
		}
		else if (pos < 8) {
		    field = "month";
		}
		else {
		    field = "day";
		}
	    }
	    
	    var day = target.widget_date.getDate();
	    var month = target.widget_date.getMonth();
	    var year = target.widget_date.getFullYear();
	    if (field == "day") {
		if (unicode == 38) {
		    day++;
		}
		else {
		    day--;
		}
	    }
	    else if (field == "month") {
		if (unicode == 38) {
		    month++;
		}
		else {
		    month--;
		}
	    }
	    else {
		if (unicode == 38) {
		    year++;
		}
		else {
		    year--;
		}
	    }
	    var new_date = new Date();
	    new_date.setFullYear(year, month, day);
	    target.widget_set_date(new_date);
	    setCaretPosition(target.widget_textbox, pos);
	    target.widget_textbox.className = className;
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    if (target.widget_change) {
		try { eval(target.widget_change)(target); } catch (e) {}
	    }
	    return false;
	}
	else if (unicode == 46) {
	    var val = target.widget_textbox.value;
	    var sel = getSelection(target.widget_textbox);
	    if (sel.end > sel.start) {
		target.widget_textbox.value = val.substr(0,sel.start) + val.substr(sel.end);
		check_needed = true;
	    }
	    else {
		target.widget_textbox.value = val.substr(0,sel.start) + val.substr(sel.end+1);
		check_needed = true;
	    }
	    setCaretPosition(target.widget_textbox, sel.start);
	}
	else if (unicode == 8) {
	    var val = target.widget_textbox.value;
	    var sel = getSelection(target.widget_textbox);
	    if (sel.end > sel.start) {
		target.widget_textbox.value = val.substr(0,sel.start) + val.substr(sel.end);
		check_needed = true;
		setCaretPosition(target.widget_textbox, sel.start);
	    }
	    else if (sel.start > 0) {
		target.widget_textbox.value = val.substr(0,sel.start-1) + val.substr(sel.end);
		check_needed = true;
		setCaretPosition(target.widget_textbox, sel.start-1);
	    }
	}
	else if (unicode >= 48 && unicode <= 57) {
	    var num = unicode - 48;
	    var val = target.widget_textbox.value;
	    var sel = getSelection(target.widget_textbox);
	    if (sel.end > sel.start) {
		var newval = val.substr(0,sel.start) + num + val.substr(sel.end);
		target.widget_textbox.value = newval;
	    }
	    else {
		var newval = val.substr(0,sel.start) + num + val.substr(sel.end);
		target.widget_textbox.value = newval;
	    }
	    check_needed = true;
	    setCaretPosition(target.widget_textbox, sel.start+1);
	}
	
	var bad = false;
	var done = false;
	if (check_needed) {
	    var val = target.widget_textbox.value;
	    var ind1 = val.indexOf('/');
	    if (ind1 < 1) {
		bad = true;
	    }
	    else {
		var ind2 = val.indexOf('/', ind1+1);
		if (ind2 < ind1 + 2) {
		    bad = true;
		}
		else {
		    var day = 1;
		    var month = 0;
		    var year = 1970;
		    if (target.widget_format == "dmy") {
			day = parseInt(val.substr(0, ind1),10);
			ind1++;
			month = parseInt(val.substr(ind1, ind2-ind1),10);
			ind2++;
			year = parseInt(val.substr(ind2),10);
		    }
		    else if (target.widget_format == "mdy") {
			month = parseInt(val.substr(0, ind1),10);
			ind1++;
			day = parseInt(val.substr(ind1, ind2-ind1),10);
			ind2++;
			year = parseInt(val.substr(ind2),10);
		    }
		    else if (target.widget_format == "ymd") {
			year = parseInt(val.substr(0, ind1),10);
			ind1++;
			month = parseInt(val.substr(ind1, ind2-ind1),10);
			ind2++;
			day = parseInt(val.substr(ind2),10);
		    }
		    month--;
		    if (year < 1970 || year==Number.NaN || day==Number.NaN || month==Number.NaN || day < 1 || day > 31 || month < 0 || month > 11) {
			bad = true;
		    }
		    else {
			var new_date = new Date();
			new_date.setFullYear(year, month, day);
			var pos = getCaretPosition(target.widget_textbox);
			target.widget_set_date(new_date);
			setCaretPosition(target.widget_textbox, pos);
			if (target.widget_change) {
			    try { eval(target.widget_change)(target); } catch (e) {}
			}
		    }
		}
	    }

	    done = true;
	}
	
	if (bad) {
	    className = className + "_bad";
	}
	target.widget_textbox.className = className;
	
	if (done) {
	    if (e.preventDefault) {
		e.preventDefault();
	    }
	    if (e.stopPropagation) {
		e.stopPropagation();
	    }
	    return false;
	}
	else {
	    return true;
	}
    }
    else {
	if (e.preventDefault) {
	    e.preventDefault();
	}
	return false;
    }
}

function getCaretPosition(tbox)
{
    var caretPos = 0;
    if (document.selection) {
	tbox.focus();
	var selctn = document.selection.createRange();
	selctn.moveStart('character', -tbox.value.length);
	caretPos = selctn.text.length;
    }
    else if (tbox.selectionStart) {
	caretPos = tbox.selectionStart;
    }
    return (caretPos);
}

function getSelection(tbox)
{
    if (document.selection) {
        var bookmark = document.selection.createRange().getBookmark()
        var selection = tbox.createTextRange()
        selection.moveToBookmark(bookmark)

        var before = tbox.createTextRange()
        before.collapse(true)
        before.setEndPoint("EndToStart", selection)
	
        var beforeLength = before.text.length
        var selLength = selection.text.length
	
        return {
            start: beforeLength,
            end: beforeLength + selLength
        }
    }
    else {
        return {
            start: tbox.selectionStart,
            end: tbox.selectionEnd
        }
    }
}


function setCaretPosition(tbox, pos)
{
    if (tbox.setSelectionRange) {
	tbox.focus();
	tbox.setSelectionRange(pos, pos);
    }
    else if (tbox.createTextRange) {
	var range = tbox.createTextRange();
	range.collapse(true);
	range.moveEnd('character', pos);
	range.moveStart('character', pos);
	range.select();
    }
}

function karooWidgetTextKeyDownHandler(e) // needed to cover for IE inconsistency
{
    if (karooWidgetKeyDownHandler(e)) {
	return karooWidgetTextKeyPressHandler(e);
    }
    else {
	return false;
    }
}

function karooWidgetCalendarKeyDownHandler(e) // needed to cover for IE inconsistency
{
    if (karooWidgetKeyDownHandler(e)) {
	return karooWidgetCalendarKeyPressHandler(e);
    }
    else {
	return false;
    }
}

function karooWidgetListKeyDownHandler(e) // needed to cover for IE inconsistency
{
    if (karooWidgetKeyDownHandler(e)) {
	return karooWidgetListKeyPressHandler(e);
    }
    else {
	return false;
    }
}

function karooWidgetKeyDownHandler(e)
{
    if (window.event) {
	e = event;
    }
    var unicode = e.charCode? e.charCode: e.keyCode;
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	if (unicode == 16) {
	    target.widget_shift = true;
	}
	else if (unicode == 17) {
	    target.widget_ctrl = true;
	}
	else if (unicode == 18) {
	    target.widget_alt = true;
	}
    }
    else {
	if (e.preventDefault) {
	    e.preventDefault();
	}
	return false;
    }

    return true;
}

function karooWidgetKeyUpHandler(e)
{
    if (window.event) {
	e = event;
    }
    var unicode = e.charCode? e.charCode: e.keyCode;
    if (!e.target) {
	e.target = e.srcElement;
    }
    var target = e.target;
    while ((!target.widget_id) && target.parentNode) {
	target = target.parentNode;
    }

    if (target.widget_enabled) {
	if (unicode == 16) {
	    target.widget_shift = false;
	}
	else if (unicode == 17) {
	    target.widget_ctrl = false;
	}
	else if (unicode == 18) {
	    target.widget_alt = false;
	}
	else if (unicode == 13) {
	    if (target.widget_action) {
		try { eval(target.widget_action)(target); } catch (e) {}
		if (e.preventDefault) {
		    e.preventDefault();
		}
		return false;
	    }
	}
    }
    else {
	if (e.preventDefault) {
	    e.preventDefault();
	}
	return false;
    }

    return true;
}

function KarooWidgets(surf)
{
    if (!surf) {
	surf = "/surf/";
    }
    this.surf_url = surf;
    this.unique_id = 0;

    this.traverse = function(node)
    {
	if (node.nodeType == 8) {
	    var opts = node.nodeValue;
	    var ind;
	    for (ind = 0; ind < opts.length; ind++) {
		var c = opts.charAt(ind);
		if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
		    break;
		}
	    }
	    if (ind < opts.length && opts.substr(ind, 7) == "widget ") {
		ind += 7;
		for (; ind < opts.length; ind++) {
		    var c = opts.charAt(ind);
		    if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
			break;
		    }
		}
		var target = this.getWidgetElement(node);
		if (target) {
		    var nodeName = target.nodeName.toLowerCase();
		    if (opts.substr(ind,5) == "text ") {
			this.setupText(target, opts.substr(ind+5));
		    }
		    else if (opts.substr(ind,7) == "button ") {
			this.setupButton(target, opts.substr(ind+7));
		    }
		    else if (opts.substr(ind,9) == "checkbox ") {
			this.setupCheckbox(target, opts.substr(ind+9));
		    }
		    else if (opts.substr(ind,9) == "calendar ") {
			this.setupCalendar(target, opts.substr(ind+9));
		    }
		    else if (opts.substr(ind,5) == "list ") {
			this.setupList(target, opts.substr(ind+5));
		    }
		    else if (opts.substr(ind,8) == "menubar ") {
			this.setupMenuBar(target, opts.substr(ind+8));
		    }
		    else if (opts.substr(ind,9) == "menuitem ") {
			this.setupMenuItem(target, opts.substr(ind+9));
		    }
		    else if (opts.substr(ind,9) == "horizbar ") {
			this.setupHorizBar(target, opts.substr(ind+9));
		    }
		    else if (opts.substr(ind,6) == "value ") {
			this.setupValue(target, opts.substr(ind+6));
		    }
		    else if (nodeName == "textarea") {
			this.setupText(target, opts.substr(ind));
		    }
		    else if (nodeName == "input" && target.type == "text") {
			this.setupText(target, opts.substr(ind));
		    }
		    else if (nodeName == "input" && target.type == "submit") {
			this.setupButton(target, opts.substr(ind));
		    }
		    else if (nodeName == "input" && target.type == "checkbox") {
			this.setupCheckbox(target, opts.substr(ind));
		    }
		}
	    }
	}
	for (var n = node.firstChild; n; n = n.nextSibling) {
	    this.traverse(n);
	}
    }

    this.getWidgetElement = function(node)
    {
	for (var n = node.previousSibling; n; n = n.previousSibling) {
	    if (n.nodeType == 1) {
		return n;
	    }
	}
	if (node.parentNode && node.parentNode.nodeType == 1) {
	    return node.parentNode;
	}
	return null;
    }

    this.processOpts = function(target, opts, mode)
    {
	target.widget_enabled = true;
	if (mode == "text") {
	    target.widget_numeric = false;
	}
	else if (mode == "calendar") {
	    target.widget_date = null;
	    target.widget_format = null;
	}
	else if (mode == "list") {
	    target.widget_width_automatic = true;
	}
	var ind;
	do {
	    for (ind = 0; ind < opts.length; ind++) {
		var c = opts.charAt(ind);
		if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
		    break;
		}
	    }
	    opts = opts.substr(ind);
	    var colon = opts.indexOf(':');
	    var semi_colon = opts.indexOf(';');
	    ind = opts.indexOf(' ');
	    var nxt = ind;
	    if (nxt == -1) {
		nxt = opts.length;
	    }
	    if (colon != -1 && semi_colon > colon && colon < nxt) {
		var key = opts.substr(0, colon);
		colon++;
		while (opts.charAt(colon) == ' ') {
		    colon++;
		}
		var val = opts.substr(colon, semi_colon - colon);
		opts = opts.substr(semi_colon+1);
		target["widget_"+key] = val;
	    }
	    else if (nxt > 0) {
		var word = opts.substr(0, nxt);
		opts = opts.substr(nxt+1);
		if (word == "numeric") {
		    if (mode == "text") {
			target.widget_numeric = true;
		    }
		}
		else if (word == "today") {
		    if (mode == "calendar") {
			target.widget_date = new Date();
		    }
		}
		else if ((word == "dmy" || word == "mdy" || word == "ymd") && mode == "calendar") {
		    target.widget_format = word;
		}
		else if (word == "manual-width" && mode == "list") {
		    target.widget_width_automatic = false;
		}
		else if (word == "disabled") {
		    target.widget_enabled = false;
		}
	    }
	} while (ind != -1);
    }

    this.setupMethods = function(target)
    {
	target.widget_get_classname = function()
	{
	    var className = this.className;
	    if (this.widget_type) {
		className = "widget_" + this.widget_type;
	    }
	    else if (this.parentNode && this.parentNode.widget_type == "list") {
		className = "widget_list_item";
	    }
	    if (this.className) {
		if (this.className.length > 9 && this.className.substr(this.className.length-9) == "_selected") {
		    className = this.className.substr(0, this.className.length-9);
		}
		else if (this.className.length > 10 && this.className.substr(this.className.length-10) == "_highlight") {
		    className = this.className.substr(0, this.className.length-10);
		}
		else if (this.className.length > 9 && this.className.substr(this.className.length-9) == "_disabled") {
		    className = this.className.substr(0, this.className.length-9);
		}
		else if (this.className.length > 4 && this.className.substr(this.className.length-4) == "_bad") {
		    className = this.className.substr(0, this.className.length-4);
		}
		else {
		    className = this.className;
		}
	    }
	    return className;
	};
	target.widget_set_enabled = function(enabled, force)
	{
	    var changed = this.widget_enabled != enabled;
	    this.widget_enabled = enabled;
	    if (changed || force) {
		if (enabled) {
		    this.className = this.widget_get_classname();
		}
		else {
		    this.className = this.widget_get_classname() + "_disabled";
		}
	    }
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1 && n.widget_id) {
		    try { n.widget_set_enabled(enabled, force); } catch (e) {}
		}
	    }
	};
	target.widget_reset = function() {
	    if (this.widget_initial_value) {
		this.value = this.widget_initial_value;
		this.widget_initial_value = null;
	    }
	    else if (this.widget_initial_checked) {
		this.checked = this.widget_initial_checked;
	    }
	};
    	target.widget_execute = function() {
	    if (this.widget_action) {
		try { eval(this.widget_action)(this); } catch (e) {}
	    }
	};
    }

    this.setupText = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "text";
	target.widget_initial_value = null;

	this.processOpts(target, opts, "text");
	this.setupMethods(target);

	if (window.addEventListener) { 
	    target.addEventListener("keypress", karooWidgetTextKeyPressHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	}
	else {
	    target.attachEvent("onkeydown", karooWidgetTextKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	}
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupButton = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "button";

	this.processOpts(target, opts, "button");
	this.setupMethods(target);

	var is_image = false;
	if (target.nodeName.toLowerCase() != "input") {
	    is_image = true;
	    if (!target.className) {
		target.className = "widget_button";
	    }
	}

	if (window.addEventListener) { 
	    if (is_image) {
		target.addEventListener("mousedown", karooWidgetButtonMouseDownHandler, true);
		target.addEventListener("mouseup", karooWidgetButtonMouseUpHandler, true);
		target.addEventListener("mouseover", karooWidgetButtonMouseOverHandler, true);
		target.addEventListener("mouseout", karooWidgetButtonMouseOutHandler, true);
	    }
	    target.addEventListener("click", karooWidgetButtonClickHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	}
	else {
	    if (is_image) {
		target.attachEvent("onmousedown", karooWidgetButtonMouseDownHandler);
		target.attachEvent("onmouseup", karooWidgetButtonMouseUpHandler);
		target.attachEvent("onmouseover", karooWidgetButtonMouseOverHandler);
		target.attachEvent("onmouseout", karooWidgetButtonMouseOutHandler);
	    }
	    target.attachEvent("onclick", karooWidgetButtonClickHandler);
	    target.attachEvent("onkeydown", karooWidgetKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	}
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupCheckbox = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "checkbox";
	target.widget_initial_checked = target.checked;

	this.processOpts(target, opts, "checkbox");
	this.setupMethods(target);

	if (window.addEventListener) { 
	    target.addEventListener("click", karooWidgetCheckboxClickHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	}
	else {
	    target.attachEvent("onclick", karooWidgetCheckboxClickHandler);
	    target.attachEvent("onkeydown", karooWidgetKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	}

	if (!target.className || target.className == "") {
	    target.className = "widget_checkbox";
	}
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupCalendar = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "calendar";
	target.widget_textbox = null;

	this.processOpts(target, opts, "calendar");
	if (!target.widget_date) {
	    target.widget_date = new Date();
	}
	target.widget_initial_value = target.widget_date;

	if (!target.className) {
	    target.className = "widget_calendar";
	}

	this.setupMethods(target);

	for (var n = target.firstChild; n; n = n.nextSibling) {
	    if (n.nodeType == 1 && n.nodeName.toLowerCase() == "input" && n.getAttribute("type") == "text") {
		target.widget_textbox = n;
		if (!target.widget_textbox.className) {
		    target.widget_textbox.className = "widget_calendar";
		}
		break;
	    }
	}

	target.widget_previous_month = function()
	{
	    var new_date = new Date(this.widget_date.getTime());
	    var month = this.widget_date.getMonth();
	    var year = this.widget_date.getFullYear();
	    month--;
	    if (month < 0) {
		month += 12;
		year--;
	    }
	    new_date.setFullYear(year, month);
	    this.widget_set_date(new_date);
	    if (this.widget_change) {
		try { eval(this.widget_change)(this); } catch (e) {}
	    }
	}

	target.widget_next_month = function()
	{
	    var new_date = new Date(this.widget_date.getTime());
	    var month = this.widget_date.getMonth();
	    var year = this.widget_date.getFullYear();
	    month++;
	    if (month > 11) {
		month -= 12;
		year++;
	    }
	    new_date.setFullYear(year, month);
	    this.widget_set_date(new_date);
	    if (this.widget_change) {
		try { eval(this.widget_change)(this); } catch (e) {}
	    }
	}

	target.widget_set_day = function(day)
	{
	    var new_date = new Date(this.widget_date.getTime());
	    new_date.setDate(day);
	    this.widget_set_date(new_date);
	    if (this.widget_change) {
		try { eval(this.widget_change)(this); } catch (e) {}
	    }
	}

	target.widget_set_date = function(new_date)
	{
	    this.widget_date = new_date;
	    var currentDate = new Date(new_date.getTime());
	    currentDate.setDate(1);
	    var currentMonth = currentDate.getMonth();
	    var dayOfMonth = -currentDate.getDay();

	    var n = this.widget_month_td.firstChild;
	    while (n) {
		var nxt = n.nextSibling;
		if (n.nodeType == 3) {
		    this.widget_month_td.removeChild(n);
		}
		n = nxt;
	    }

	    this.widget_month_td.appendChild(document.createTextNode(this.widget_months[currentMonth]));
	
	    var tr = this.widget_first_row;
	    while (tr) {
		var nxt = tr.nextSibling;
		tr.parentNode.removeChild(tr);
		tr = nxt;
	    }
	    this.widget_first_row = null;
	    while (currentDate.getMonth() == currentMonth) {
		tr = document.createElement("tr");
		if (!this.widget_first_row) {
		    this.widget_first_row = tr;
		}
		tr.setAttribute("class", "widget_calendar_days");
		for (var i = 0; i < 7; i++) {
		    dayOfMonth++;
		    if (dayOfMonth > 0) {
			currentDate.setDate(dayOfMonth);
		    }
		    var n = this.widget_weekdays[i];
		    td = document.createElement("td");
		    td.setAttribute("class", "widget_calendar_"+n);
		    if (currentDate.getMonth() == currentMonth) {
			if (dayOfMonth > 0) {
			    td.appendChild(document.createTextNode(dayOfMonth));
			    if (dayOfMonth == this.widget_date.getDate()) {
				td.setAttribute("class", "widget_calendar_"+n+"_selected");
			    }
			    else {
				td.setAttribute("onmouseover", "this.className='widget_calendar_"+n+"_highlight'");
				td.setAttribute("onmouseout", "this.className='widget_calendar_"+n+"'");
				td.setAttribute("onclick", "widget_traverse_popdown(document); this.parentNode.parentNode.parentNode.parentNode.widget_set_day("+dayOfMonth+");");
			    }
			}
		    }
		    tr.appendChild(td);
		}
		this.widget_tbody.appendChild(tr);
	    }
	    if (this.widget_textbox) {
		this.widget_textbox.value = this.widget_get_date_string();
	    }
	}

	target.widget_get_date_string = function(strict)
	{
	    var month = this.widget_date.getMonth() + 1;
	    var day = this.widget_date.getDate();
	    var year = this.widget_date.getFullYear();
	    if (strict && month < 10) {
		month = "0" + month;
	    }
	    if (strict && day < 10) {
		day = "0" + day;
	    }
	    if (this.widget_format == "mdy") {
		return month + "/" + day + "/" + year;
	    }
	    else if (this.widget_format == "ymd") {
		return year + "/" + month + "/" + day;
	    }
	    else {
		return day + "/" + month + "/" + year;
	    }
	}

	target.widget_create = function()
	{
	    this.widget_months = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
	    this.widget_weekdays = new Array('sunday','monday','tuesday','wednesday','thursday','friday','saturday');

	    this.widget_first_row = null;
	    var table = document.createElement("table");
	    this.widget_table = table;
	    table.setAttribute("cellpadding", 0);
	    table.setAttribute("cellspacing", 0);
	    var tbody = document.createElement("tbody");
	    var tr = document.createElement("tr");
	    tr.setAttribute("class", "widget_calendar_days");
	    var td = document.createElement("td");
	    td.setAttribute("class", "widget_calendar_previous_month");
	    td.setAttribute("onmouseover", "this.className='widget_calendar_previous_month_highlight';");
	    td.setAttribute("onmouseout", "this.className='widget_calendar_previous_month';");
	    td.setAttribute("onclick", "widget_traverse_popdown(document); this.parentNode.parentNode.parentNode.parentNode.widget_previous_month();");
	    var img = document.createElement("img");
	    img.setAttribute("src", this.widget_parent.surf_url+"left_arrow.png");
	    img.setAttribute("alt", "&lt;");
	    img.setAttribute("class", "widget_calendar_previous_month");
	    td.appendChild(img);
	    tr.appendChild(td);
	    td = document.createElement("td");
	    td.setAttribute("class", "widget_calendar_month");
	    td.setAttribute("colspan", 5);
	    tr.appendChild(td);
	    this.widget_month_td = td;
	    td = document.createElement("td");
	    td.setAttribute("class", "widget_calendar_next_month");
	    td.setAttribute("onmouseover", "this.className='widget_calendar_next_month_highlight';");
	    td.setAttribute("onmouseout", "this.className='widget_calendar_next_month';");
	    td.setAttribute("onclick", "widget_traverse_popdown(document); this.parentNode.parentNode.parentNode.parentNode.widget_next_month();");
	    var img = document.createElement("img");
	    img.setAttribute("src", this.widget_parent.surf_url+"right_arrow.png");
	    img.setAttribute("alt", "&gt;");
	    img.setAttribute("class", "widget_calendar_next_month");
	    td.appendChild(img);
	    tr.appendChild(td);
	    tbody.appendChild(tr);

	    tr = document.createElement("tr");
	    tr.setAttribute("class", "widget_calendar_days");
	    var wd = new Array('S','M','T','W','T','F','S');
	    for (var i = 0; i < 7; i++) {
		var d = wd[i];
		var n = this.widget_weekdays[i];
		var th = document.createElement("th");
		th.setAttribute("class", "widget_calendar_"+n);
		th.appendChild(document.createTextNode(d));
		tr.appendChild(th);
	    }
	    tbody.appendChild(tr);
	    this.widget_tbody = tbody;
	    table.appendChild(tbody);
	    this.appendChild(table);
	}

	target.widget_destroy = function()
	{
	    this.removeChild(this.widget_table);
	    this.widget_table = null;
	    this.widget_tbody = null;
	    this.widget_month_td = null;
	}

	target.widget_create();
	target.widget_set_date(target.widget_date);
	if (window.addEventListener) { 
	    target.addEventListener("dblclick", karooWidgetCalendarDblClickHandler, true);
	    target.addEventListener("mouseover", karooWidgetCalendarMouseOverHandler, true);
	    target.addEventListener("mouseout", karooWidgetCalendarMouseOutHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	    target.addEventListener("keypress", karooWidgetCalendarKeyPressHandler, true);
	}
	else {
	    target.attachEvent("ondblclick", karooWidgetCalendarDblClickHandler);
	    target.attachEvent("onmouseover", karooWidgetCalendarMouseOverHandler);
	    target.attachEvent("onmouseout", karooWidgetCalendarMouseOutHandler);
	    target.attachEvent("onkeydown", karooWidgetCalendarKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	}
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupList = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_selected = -1;
	target.widget_selected_element = null;
	target.widget_id = ++this.unique_id;
	target.widget_type = "list";

	this.processOpts(target, opts, "list");
	this.setupMethods(target);

	target.widget_get_selected = function()
	{
	    return parseInt(this.widget_selected);
	}

	target.widget_get_size = function()
	{
	    var count = 0;
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1) {
		    count++;
		}
	    }
	    return count;
	}

	target.widget_select = function(selected, no_scroll)
	{
	    selected = parseInt(selected);
	    var count = 0;
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1) {
		    count++;
		}
	    }

	    if (selected >= count) {
		selected = count-1;
	    }

	    this.widget_selected = selected;

	    count = 0;
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1) {
		    var className = n.widget_get_classname();

		    if (count == selected) {
			target.widget_selected_element = n;
			n.className = className + "_selected";
		    }
		    else {
			n.className = className;
		    }
		    count++;
		}
	    }

	    if (!no_scroll) {
		var scrollHeight = Math.max(this.scrollHeight, this.clientHeight) - this.clientHeight;
		this.scrollTop = count == 0? 0 : (scrollHeight * (selected + (selected/count)) / count);
	    }

	    if (this.parentNode.widget_type == "menuitem" && this.parentNode.widget_span) {
		this.parentNode.widget_select();
	    }
	}

	target.widget_set_row_handlers = function()
	{
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1) {
		    if (window.addEventListener) {
			n.addEventListener("click", karooWidgetListItemClickHandler, true);
			n.addEventListener("mouseover", karooWidgetListItemMouseOverHandler, true);
			n.addEventListener("mouseout", karooWidgetListItemMouseOutHandler, true);
			n.addEventListener("keypress", karooWidgetListKeyPressHandler, true);
		    }
		    else {
			n.attachEvent("onclick", karooWidgetListItemClickHandler);
			n.attachEvent("onmouseover", karooWidgetListItemMouseOverHandler);
			n.attachEvent("onmouseout", karooWidgetListItemMouseOutHandler);
			n.attachEvent("onkeypress", karooWidgetListKeyPressHandler);
		    }
		}
	    }
	}

	target.widget_setup_items = function()
	{
	    for (var n = this.firstChild; n; n = n.nextSibling) {
		if (n.nodeType == 1) {
		    this.widget_parent.setupMethods(n);
		}
	    }
	    this.widget_set_row_handlers();
	}
	
	target.widget_setup_items();

	target.widget_select(target.widget_selected);

	if (window.addEventListener) { 
	    target.addEventListener("dblclick", karooWidgetListDblClickHandler, true);
	    target.addEventListener("keypress", karooWidgetListKeyPressHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	    target.addEventListener("mouseover", karooWidgetListMouseOverHandler, true);
	    target.addEventListener("mouseout", karooWidgetListMouseOutHandler, true);
	}
	else {
	    target.attachEvent("ondblclick", karooWidgetListDblClickHandler);
	    target.attachEvent("onkeydown", karooWidgetListKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	    target.attachEvent("onmouseover", karooWidgetListMouseOverHandler);
	    target.attachEvent("onmouseout", karooWidgetListMouseOutHandler);
	}

	if (!target.className || target.className == "") {
	    target.className = "widget_list";
	}
	var span = document.createElement("span");
	target.appendChild(span);
	span.style.visibility = "hidden";

	if (target.widget_width_automatic) {
	    var max = 0;
	    for (var child = target.firstChild; child; child = child.nextSibling) {
		if (child.nodeType == 1) {
		    var wid = target.widget_parent.childTextWidth(child, span);
		    if (wid > max) {
			max = wid;
		    }
		}
	    }
	    target.removeChild(span);
	    
	    target.style.width = (max+20)+"px";
	}
	target.style.visibility = "inherit";
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupMenuBar = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "menubar";

	this.processOpts(target, opts, "menubar");
	this.setupMethods(target);

	for (var n = target.firstChild; n; n = n.nextSibling) {
	    if (n.widget_type == "value") {
		n.className = "widget_value_in_menuitem";
	    }
	}

	var maxH = 0;
	var w = 0;
	for (var n = target.firstChild; n; n = n.nextSibling) {
	    if (n.offsetWidth) {
		w += n.offsetWidth + 2;
	    }
	    if (n.offsetHeight) {
		var h = n.offsetHeight;
		if (h > maxH) {
		    maxH = h;
		}
	    }
	    if (n.widget_id) {
		n.style.cssFloat = "left";
	    }
	}
	target.style.height = maxH + "px";
	target.style.width = w + "px";

	if ((!target.className) || target.className == "") {
	    target.className = "widget_menubar";
	}
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupMenuItem = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_selected = -1;
	target.widget_id = ++this.unique_id;
	target.widget_type = "menuitem";
	target.widget_list = null;

	this.processOpts(target, opts, "menuitem");
	this.setupMethods(target);

	if (window.addEventListener) { 
	    target.addEventListener("dblclick", karooWidgetMenuItemDblClickHandler, true);
	    target.addEventListener("keydown", karooWidgetKeyDownHandler, true);
	    target.addEventListener("keyup", karooWidgetKeyUpHandler, true);
	    target.addEventListener("mouseover", karooWidgetMenuItemMouseOverHandler, true);
	    target.addEventListener("mouseout", karooWidgetMenuItemMouseOutHandler, true);
	    target.addEventListener("click", karooWidgetMenuItemClickHandler, true);
	    target.addEventListener("mousedown", karooWidgetMenuItemMouseDownHandler, true);
	    target.addEventListener("mouseup", karooWidgetMenuItemMouseUpHandler, true);
	}
	else {
	    target.attachEvent("ondblclick", karooWidgetMenuItemDblClickHandler);
	    target.attachEvent("onkeydown", karooWidgetKeyDownHandler);
	    target.attachEvent("onkeyup", karooWidgetKeyUpHandler);
	    target.attachEvent("onmouseover", karooWidgetMenuItemMouseOverHandler);
	    target.attachEvent("onmouseout", karooWidgetMenuItemMouseOutHandler);
	    target.attachEvent("onclick", karooWidgetMenuItemClickHandler);
	    target.attachEvent("onmousedown", karooWidgetMenuItemMouseDownHandler);
	    target.attachEvent("onmouseup", karooWidgetMenuItemMouseUpHandler);
	}

	target.widget_span = null;
	target.widget_img = null;
	for (var n = target.firstChild; n; n = n.nextSibling) {
	    if (n.nodeType == 1) {
		if (n.nodeName.toLowerCase() == "span") {
		    target.widget_span = n;
		    if (target.widget_img) {
			break;
		    }
		}
		else if (n.nodeName.toLowerCase() == "img") {
		    target.widget_img = n;
		    if (target.widget_span) {
			break;
		    }
		}
	    }
	}
	if (!target.widget_span) {
	    target.widget_span = document.createElement("span");
	    target.insertBefore(target.widget_span, target.firstChild);
	}

	for (var n = target.firstChild; n; n = n.nextSibling) {
	    if (n.widget_type == "list") {
		target.widget_list = n;
		break;
	    }
	}

	if (target.widget_list && !target.widget_img) {
	    target.widget_img = document.createElement("img");
	    target.widget_img.setAttribute("class", "widget_down_arrow");
	    target.widget_img.setAttribute("src", target.widget_parent.surf_url+"down_arrow.png");
	    target.widget_img.setAttribute("alt", "");
	    target.insertBefore(target.widget_img, target.widget_list);
	}

	if (target.widget_list) {
	    target.widget_get_selected = function()
	    {
		return this.widget_list.widget_get_selected();
	    }
	}

	target.widget_select = function()
	{
	    if (this.widget_list) {
		var max = 0;
		var maximg = 0;
		var sel = null;
		var i = 0;
		for (var child = this.widget_list.firstChild; child; child = child.nextSibling) {
		    if (child.nodeType == 1) {
			var wid = this.widget_parent.childImageWidth(child);
			if (wid > maximg) {
			    maximg = wid;
			}
		    }
		}
		for (var child = this.widget_list.firstChild; child; child = child.nextSibling) {
		    if (child.nodeType == 1) {
			if (i == this.widget_list.widget_selected) {
			    sel = child;
			}
			else {
			    var wid = this.widget_parent.childTextWidth(child, this.widget_span);
			    if (wid > max) {
				max = wid;
			    }
			}
			i++;
		    }
		}
		if (sel) {
		    var wid = this.widget_parent.childTextWidth(sel, this.widget_span);
		    if (wid > max) {
			max = wid;
		    }
		}
		
		max += this.widget_img.offsetWidth+4;
		if (maximg > max) {
		    max = maximg;
		}
		this.widget_list.style.position = "absolute";
		this.widget_list.style.width = (max+2)+"px";
		this.style.width = max+"px";
		this.widget_list.style.visibility = "hidden";
		this.widget_list.className = "widget_list_in_menuitem";
	    }
	}

	var className = target.widget_get_classname();
	target.className = className;

	target.widget_popdown = function()
	{
	    if (this.widget_list) {
		this.widget_list.style.visibility = "hidden";
	    }
	}

	target.widget_popup = function()
	{
	    if (this.widget_list) {
		var y = this.offsetTop || 0;
		var x = this.offsetLeft || 0;
		for (var n = this.offsetParent; n; n = n.offsetParent) {
		    if (n.getStyle) {
			var pos = n.getStyle("position");
			if (pos == "relative" || pos == "absolute") {
			    break;
			}
		    }
		    if (n.nodeName == "BODY") {
			break;
		    }
		    y += n.offsetTop  || 0;
		    x += n.offsetLeft || 0;
		}
		
		x += this.offsetWidth;
		x -= this.widget_list.offsetWidth;
		y += this.offsetHeight;
		this.widget_list.style.top = y + "px";
		this.widget_list.style.left = x + "px";
		this.widget_list.style.visibility = "inherit";
		this.widget_list.style.position = "absolute";
	    }
	}
	
	target.widget_select();
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.childTextWidth = function(node, span)
    {
	if (node.nodeType == 3) {
	    if (node.parentNode == span) {
		return 0;
	    }
	    span.innerHTML = node.nodeValue;
	    if (node.parentNode && node.parentNode.style && node.parentNode.style.visibility == "hidden") {
		return 0;
	    }
	    else {
		return span.offsetWidth;
	    }
	}
	else if (node.nodeType == 1) {
	    var wid = 0;
	    try {
		if (node.nodeName.toLowerCase() == "img") {
		    if (wid == 0) {
			span.innerHTML = node.getAttribute("alt");
		    }
		}
	    }
	    catch (e) {}
	    for (var n = node.firstChild; n; n = n.nextSibling) {
		wid += this.childTextWidth(n, span);
	    }
	    return wid;
	}
	else {
	    return 0;
	}
    }

    this.childImageWidth = function(node)
    {
	if (node.nodeType == 1) {
	    var wid = 0;
	    try {
		if (node.nodeName.toLowerCase() == "img") {
		    wid = node.offsetWidth;
		}
	    }
	    catch (e) {}
	    for (var n = node.firstChild; n; n = n.nextSibling) {
		wid += this.childImageWidth(n);
	    }
	    return wid;
	}
	else {
	    return 0;
	}
    }

    this.setupHorizBar = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "horizbar";

	this.processOpts(target, opts, "horizbar");
	if (target.widget_width) {
	    target.widget_width = parseInt(target.widget_width);
	}
	else {
	    target.widget_width = 0;
	}
	this.setupMethods(target);

	target.widget_body = target.firstChild;
	while (target.widget_body
	       && !(target.widget_body.nodeType == 1 && target.widget_body.nodeName.toLowerCase() == "tbody")) {
	    target.widget_body = target.widget_body.nextSibling;
	}
	if (target.widget_body == null) {
	    target.widget_body = target;
	}

	target.widget__action = function(rown, celln)
	{
	    if (this.widget_action) {
		try { eval(this.widget_action)(this, rown, celln); } catch (e) {}
	    }
	}

	target.widget__select = function(rown, celln)
	{
	    if (this.widget_select) {
		try { eval(this.widget_select)(this, rown, celln); } catch (e) {}
	    }
	}

	target.widget_get_row = function(rownum)
	{
	    var row = null;
	    for (var i = 0; i <= rownum; i++) {
		if (!row) {
		    row = this.widget_body.firstChild;
		}
		else {
		    row = row.nextSibling;
		}
		while (row && row.widget_horizbar_type != "row") {
		    row = row.nextSibling;
		}
		if (!row) {
		    row = this.widget_append_rows(1);
		}
	    }
	    return row;
	}

	target.widget_get_cell = function(row, cellnum)
	{
	    var cell = null;
	    for (var i = 0; i <= cellnum; i++) {
		if (!cell) {
		    cell = row.firstChild;
		}
		else {
		    cell = cell.nextSibling;
		}
		while (cell && cell.widget_horizbar_type != "cell") {
		    cell = cell.nextSibling;
		}
		if (!cell) {
		    cell = this.widget_append_cells(row, 1);
		}
	    }
	    return cell;
	}

	target.widget_set_cell = function(rownum, cellnum, text, mode)
	{
	    var cell = null;
	    var row = this.widget_get_row(rownum);
	    if (row) {
		cell = this.widget_get_cell(row, cellnum);
		if (cell) {
		    if (!mode) {
			mode = 0;
		    }
		    cell.widget_mode = mode;
		    cell.className = cell.widget_get_classname();
		    var n = cell.firstChild;
		    while (n) {
			var nxt = n.nextSibling;
			if (n.nodeType == 3) {
			    cell.removeChild(n);
			}
			n = nxt;
		    }
		    if (text) {
			var txt = document.createTextNode(text);
			cell.appendChild(txt);
		    }
		}
	    }
	    return cell;
	}

	target.widget_deselect = function()
	{
	    for (var row = this.widget_body.firstChild; row; row = row.nextSibling) {
		while (row && row.widget_horizbar_type != "row") {
		    row = row.nextSibling;
		}
		if (row) {
		    row.widget_deselect();
		}
	    }
	}

	target.widget_append_cells = function(row, numcells)
	{
	    var celln = 0;
	    for (var n = row.firstChild; n; n = n.nextSibling) {
		if (n.widget_horizbar_type == "cell") {
		    celln++;
		}
	    }
	    var cell = null;
	    for (var i = 0; i < numcells; i++) {
		cell = document.createElement("td");
		cell.setAttribute("class", this.className);
		cell.widget_selected = false;
		cell.widget_cell_number = celln++;
		cell.widget_get_classname = function()
		{
		    var mode = this.widget_mode;
		    if (mode) {
			mode = parseInt(mode);
		    }
		    else {
			mode = 0;
		    }
		    var className = "widget_horizbar";
		    if (this.className) {
			if (this.className.length > 17 && this.className.substr(this.className.length-17) == "_started_selected") {
			    className = this.className.substr(0, this.className.length-17);
			}
			else if (this.className.length > 14 && this.className.substr(this.className.length-14) == "_done_selected") {
			    className = this.className.substr(0, this.className.length-14);
			}
			else if (this.className.length > 18 && this.className.substr(this.className.length-18) == "_started_highlight") {
			    className = this.className.substr(0, this.className.length-18);
			}
			else if (this.className.length > 15 && this.className.substr(this.className.length-15) == "_done_highlight") {
			    className = this.className.substr(0, this.className.length-15);
			}
			else if (this.className.length > 9 && this.className.substr(this.className.length-9) == "_selected") {
			    className = this.className.substr(0, this.className.length-9);
			}
			else if (this.className.length > 10 && this.className.substr(this.className.length-10) == "_highlight") {
			    className = this.className.substr(0, this.className.length-10);
			}
			else if (this.className.length > 9 && this.className.substr(this.className.length-9) == "_disabled") {
			    className = this.className.substr(0, this.className.length-9);
			    mode = 0;
			}
			else if (this.className.length > 8 && this.className.substr(this.className.length-8) == "_started") {
			    className = this.className.substr(0, this.className.length-8);
			}
			else if (this.className.length > 5 && this.className.substr(this.className.length-5) == "_done") {
			    className = this.className.substr(0, this.className.length-5);
			}
			else {
			    className = this.className;
			}
		    }
		    switch (mode) {
		    case 1: className = className + "_started"; break;
		    case 2: className = className + "_done"; break;
		    }
		    return className;
		}
		cell.widget_highlight = function()
		{
		    this.className = this.widget_get_classname() + "_highlight";
		}
		cell.widget_unhighlight = function()
		{
		    if (this.widget_selected) {
			this.className = this.widget_get_classname() + "_selected";
		    }
		    else {
			this.className = this.widget_get_classname();
		    }
		}
		cell.widget_select = function()
		{
		    if (this.widget_selected) {
			this.widget_selected = false;
			this.className = this.widget_get_classname();
		    }
		    else {
			var n = this.parentNode;
			while (n && n.widget_type != "horizbar") {
			    n = n.parentNode;
			}
			if (n) {
			    n.widget_deselect();
			}
			this.widget_selected = true;
			this.className = this.widget_get_classname() + "_selected";
			this.widget__select();
		    }
		}
		cell.widget_deselect = function()
		{
		    if (this.widget_selected) {
			this.widget_selected = false;
			this.className = this.widget_get_classname();
		    }
		}
		cell.widget__action = function()
		{
		    this.parentNode.widget__action(this.widget_cell_number);
		}
		cell.widget__select = function()
		{
		    this.parentNode.widget__select(this.widget_cell_number);
		}
		cell.setAttribute("onmouseover", "this.widget_highlight();");
		cell.setAttribute("onmouseout", "this.widget_unhighlight();");
		cell.setAttribute("onclick", "widget_traverse_popdown(document); this.widget_select();");
		cell.setAttribute("ondblclick", "this.widget__action();");
		cell.widget_horizbar_type = "cell";
		row.appendChild(cell);
	    }
	    return cell;
	}

	target.widget_append_rows = function(numrows)
	{
	    var rown = 0;
	    for (var n = this.widget_body.firstChild; n; n = n.nextSibling) {
		if (n.widget_horizbar_type == "row") {
		    rown++;
		}
	    }
	    var row = null;
	    for (var i = 0; i < numrows; i++) {
		row = document.createElement("tr");
		row.widget_horizbar_type = "row";
		row.setAttribute("class", this.className);
		row.widget_row_number = rown++;
		row.widget__action = function(celln)
		{
		    for (var n = this.parentNode; n; n = n.parentNode) {
			if (n.widget_type == "horizbar") {
			    n.widget__action(this.widget_row_number, celln);
			    return;
			}
		    }
		}
		row.widget__select = function(celln)
		{
		    for (var n = this.parentNode; n; n = n.parentNode) {
			if (n.widget_type == "horizbar") {
			    n.widget__select(this.widget_row_number, celln);
			    return;
			}
		    }
		}
		row.widget_deselect = function()
		{
		    for (var cell = this.firstChild; cell; cell = cell.nextSibling) {
			while (cell && cell.widget_horizbar_type != "cell") {
			    cell = cell.nextSibling;
			}
			if (cell) {
			    cell.widget_deselect();
			}
		    }
		}
		this.widget_append_cells(row, this.widget_width);
		this.widget_body.appendChild(row);
	    }
	    return row;
	}

	target.widget_resize = function(numrows)
	{
	    var child = this.widget_body.firstChild;
	    for (var i = 0; i < numrows; i++) {
		while (child && child.widget_horizbar_type != "row") {
		    child = child.nextSibling;
		}
		if (!child) {
		    this.widget_append_rows(1);
		}
		else {
		    child = child.nextSibling;
		}
	    }
	    while (child) {
		var nxt = child.nextSibling;
		if (child.widget_horizbar_type == "row") {
		    this.widget_body.removeChild(child);
		}
		child = nxt;
	    }
	}

	target.widget_resize_row = function(rownum, numcols)
	{
	    var row = this.widget_get_row(rownum);
	    var n = numcols == 0? row.firstChild : this.widget_get_cell(row, numcols-1).nextSibling;
	    while (n) {
		var nxt = n.nextSibling;
		if (n.widget_horizbar_type == "cell") {
		    n.parentNode.removeChild(n);
		}
		n = nxt;
	    }
	}

	target.className = target.widget_get_classname();
	target.widget_set_enabled(target.widget_enabled, true);
    }

    this.setupValue = function(target, opts)
    {
	target.widget_parent = this;
	target.widget_id = ++this.unique_id;
	target.widget_type = "value";

	this.processOpts(target, opts, "value");
	this.setupMethods(target);

	target.widget_set_text = function(txt)
	{
	    var n = this.firstChild;
	    while (n) {
		var nxt = n.nextSibling;
		n.parentNode.removeChild(n);
		n = nxt;
	    }
	    n = document.createTextNode(txt);
	    this.appendChild(n);
	}

	target.widget_highlight = function(highlight)
	{
	    var className = this.widget_get_classname();
	    if (highlight) {
		className += "_highlight";
	    }
	    this.className = className;
	}

	target.className = target.widget_get_classname();
	target.widget_set_enabled(target.widget_enabled, true);
    }

}

function elementToString(elem)
{
    if (!elem)
	return "null";

    var str = elem.constructor.toString();

    if (str.match(/array/i) != null) {
	str += '\n';
	for (var i = 0; i < elem.length; i++) {
	    var s = elementToString(elem[i]);
	    str += '['+i+"]: "+s;
	}
	return str;
    }

    if (elem.nodeType == 1) {
	str += '<' + elem.nodeName;
    }
    else if (elem.nodeType == 3) {
	str += elem.data;
    }
    

    if (elem.attributes != null) {
	for (var i = 0; i < elem.attributes.length; i++) {
	    var attr = elem.attributes[i];
	    str += ' ' + attr.name + "=\"" + attr.value + '"';
	}
    }

    if (elem.firstChild) {
	str += ">\n";
	for (var node = elem.firstChild; node != null; node = node.nextSibling) {
	    str += elementToString(node, false);
	}
	str += "</" + elem.nodeName + ">\n";
    }
    else if (elem.nodeType == 1)
	str += "/>\n";

    return str;
}

function findElementsWithProperty(elem, prop, val, arr, max)
{
    if (!elem)
	return;
    if (max != -1 && arr.length >= max) {
	return;
    }

    var str = elem.constructor.toString();

    if (str.match(/array/i) != null) {
	for (var i = 0; i < elem.length; i++) {
	    findElementsWithProperty(elem[i], prop, val, arr, max);
	    if (max != -1 && arr.length >= max) {
		return;
	    }
	}
	return;
    }

    if (elem.nodeType == 1) {
	var v = elem[prop];
	if (v) {
	    if (!val) {
		arr.push(elem);
	    }
	    else if (v == val) {
		arr.push(elem);
	    }
	}
    }

    for (var n = elem.firstChild; n; n = n.nextSibling) {
	findElementsWithProperty(n, prop, val, arr, max);
	if (max != -1 && arr.length >= max) {
	    return;
	}
    }
}

function findElementByNodeName(elem, nodeName)
{
    if (!elem)
	return null;

    var str = elem.constructor.toString();

    if (str.match(/array/i) != null) {
	for (var i = 0; i < elem.length; i++) {
	    var n = findElementByNodeName(elem[i], nodeName);
	    if (n) {
		return n;
	    }
	}
	return null;
    }

    if (elem.nodeType == 1) {
	if (elem.nodeName.toLowerCase() == nodeName.toLowerCase()) {
	    return elem;
	}
    }

    for (var n = elem.firstChild; n; n = n.nextSibling) {
	var nn = findElementByNodeName(n, nodeName);
	if (nn) {
	    return nn;
	}
    }
    return null;
}

function widget_traverse_popdown(node)
{
    if (node.widget_list) {
	node.widget_popdown();
    }
    for (var n = node.firstChild; n; n = n.nextSibling) {
	widget_traverse_popdown(n);
    }
}

