function formConfirm(str)
{
    if (!str) str = "Do you really want to do this?";
    return confirm(str);
}

function getText(str, name, dflt)
{
    var newname = prompt(str, dflt);
    if (!newname) return false;
    var obj = obj_id(name);
    if (!obj) { alert("can't find object id: "+name); return false; }
    obj.value = newname;
    return true;
}

function isBlank(obj, name)
{
    if (!obj.value) return name + " missing value";
    return "";
}

function validateSelect(selection, name, prompt)
{
    if (!selection) return prompt + "nothing selected.";
    return "";
}

function validateAcct(acct, name, prompt)
{
    if (!acct) return "Account name empty";
    if (acct.length < 4 || acct.length > 12)  {
	return prompt + "must be between 4 and 12 chars";
    }
    return "";
}

function validateEmail(e, name, prompt)
{
    if (!e) return prompt + "missing";
    if (/[\w\.\-]+\@[\w\.\-]+\.\w+$/.test(e) == false) {
	return prompt + "format invalid. Please fix.\nYou used '"+e+"'";
    }
    return "";
}

function validateZipcode(zip, name, prompt)
{
    var cc = /cc_/i.test(name)? "cc_" : "";
    var country = obj_id(cc+"country").value;
    if (country && /united states/i.test(country) == false) return "";
    if ( /^\d{5}$/.test(zip) == true ) return "";
    return "US Zipcodes Must be 5 digits.";
}

function validateState(state, name, prompt)
{
    var cc = /cc_/i.test(name)? "cc_" : "";
    var country = obj_id(cc+"country").value;
    if (country && /united states/i.test(country) == false) return "";
    if ( /^-/.test(state) == true ) return prompt;
    return "";
}

function validatePasswd(pw, name, prompt)
{
    var l = pw.length;
    if (!pw) return "Password is blank.";
    if (l < 5 || l > 10) {
	return "Password must be a mix of letters and numbers between 5-10 characters long.";
    }
    // check that it contains at least one number and one letter
    var got_letter = 0;
    var got_number = 0;
    for (var i = 0; i < l; i++) {
	if (pw.charAt(i) >= "0" && pw.charAt(i) <= "9") {
	    got_number++;
	} else if (pw.charAt(i) >= "a" && pw.charAt(i) <= "z") {
	    got_letter++;
	}
	if (got_letter && got_number) return "";
    }
    return "Password must contain at least one number and one letter.";
}

function validateWebsite(sitename, name, prompt)
{
    if (!sitename || /^(http:\/\/|www).+/i.test(sitename) == false) {
	return prompt;
    }
    return "";
}

function validateCheckbox(form, name, prompt)
{
    var i, last_e;
    for (i=form.elements.length-1; i >= 0; i--) {
	var e = form.elements[i];
	if (!e || e.type != "radio" && e.type != "checkbox") continue;
	if (e.checked && e.name == name) return "";
	last_e = e;
    }
    // e.focus(); not in IE7
    return prompt;
}

tags = [
    { name: "email", validate: validateEmail, prompt: "Email Address: " },
    { name: "acct", validate: validateAcct, prompt: "Account Name:" },
    { name: "passwd1", validate: validatePasswd, prompt: "Passowrd: " },
    { name: "zip", validate: validateZipcode, prompt: "" },
    { name: "cc_zip", validate: validateZipcode, prompt: "" },
    { name: "state", validate: validateState, prompt: "Select a State" },
    { name: "cc_state", validate: validateState, prompt: "Select a State" },
    { name: "website", validate: validateWebsite, prompt: "Website URL required (must start with 'http://' or 'www')" }
];

var non_blanks = [ ]; // node: ..., string: ... (like tags[], but only checks "")

var finals = [ ]; // validate: ..., name: ..., prompt ... }

var formCancelled = 0; // see notes below in isReady()
function cancelForm() { formCancelled++ };

function isReady(form)
{
    var ret = "";
    var i, node;

    // user may have pressed the "Cancel" button, in which case, assume the
    // form's callback action knows what to do. NOTE that the form must
    // use a cancel button as: <input ... onclick="return cancelForm()" ... >
    if (formCancelled) return true;

    for (i = non_blanks.length-1; i >= 0; i--) {
	if (non_blanks[i] == null) continue;
        node = form[non_blanks[i].node];
        if (node == null) continue;
	if (s = isBlank(node, non_blanks[i].name)) {
	    ret += s + '\n';
	    if (node.focus) node.focus(); // place focus here
	}
    }
    for (i = tags.length-1; i >= 0; i--) { // if array is empty, no forms!
        node = form[tags[i].name]; // go in reverse order for (*) below
        if (!node) continue;
	if (s = tags[i].validate(node.value, tags[i].name, tags[i].prompt)) {
	    ret += s + '\n';    // (*) force the focus to the first element
	    node.focus();	// that had an error, not the last,
	}			// hence the reverse for() loop
    }
    for (i = 0; i < finals.length; i++) {
	if (s = finals[i].validate(form, finals[i].name, finals[i].prompt)) {
	    ret += s + '\n';
	}
    }
    if (ret) alert(ret); // if errors, report it/them.
    return ret == ""; // return "true" if no errors
}

function submit_form(name, checkit)
{
    var form = document.forms[name];
    if (!form) alert('form ' + name + ' not found?');
    else if (!form.submit) alert("form "+name+ " has no submit() function?");
    else if (checkit && !isReady(form)) return;
    else {
	for (var i=2; i<submit_form.arguments.length; i+=2) {
	    form[submit_form.arguments[i]].value = submit_form.arguments[i+1];
	}
	form.submit();
    }
}

// for selectboxes
function update_status(node)
{
    var status = obj_id(node.name + '_status');
    var val = node.value? node.value : "(no selection)";
    status.innerHTML = '<div class="tiny" style="color: yellow">' + node.value + "</div>";
}

/************* Checkboxes **************/

/*
 gather_checked is a function that gathers all the checkbox items in the
 whole document with a common prefix. This is to enable the query of items
 that are NOT within a <form> ... </form> block.

 Each of the values of the items is added to the value of the "listname"
 (hidden) <input> item that IS within the form (initialized to "").

 gather_checked gets each item's value, and places them all (separated by
 semi-colons) in the "value" of the hidden <input> item.
 
 Here's a form:

    <form action="callback.pl" onsubmit="return gather_checked('cards', 'Delete?')">
    <input type="hidden" id="cards" name="cards" value="">
    <input type="submit">
    </form>

    ELSEWHERE:
    <input type="checkbox" id="cards_item1" value="...">
    <input type="checkbox" id="cards_item2" value="...">
    [...]
    <input type="checkbox" id="cards_itemN" value="...">

 The naming conventions REQUIRE that each checkbox's id ends in "_item"+digit.
 gather_checked() loops until no more objects are found with the _item+digit
 suffix.  If "max" is not 0, then the loop will go until "max" is reached,
 regardless of whether interim items are found.  This allows for nodes
 to be dynamically removed from the list incrementally without screwing
 up this function.

 The cgi script gets the items selected by doing:
    my @list = $query->param('card_list');
    # here, list will be an array of values from <input value...>
    # derived from items that the user checked

 Avoid prompting by passing "" as the prompt value.
 The function prompts an error if no items are selected, regardless of prompt.
 NOTE: the listname_item's don't have to be INSIDE any <form>s, which is
 nice for having checkboxes along side of smaller forms...

 NOTE: this is NOT a function to be added to the isReady() group...
 to use with isReady(), do this:
    <form action="..." onsubmit="isReady(this) && gather_checked(...)">

 */
function gather_checked(listname, prompt, max)
{
    var i = 0;
    var total = 0;
    var list = obj_id(listname);
    if (!list) { alert ( listname + " object not found."); return false; }
    list.value = ""; // be sure to reset--this could be called repeatedly
    while (++i) {
	var item = obj_id(listname+'_item' + i);
	if (!item) {
	    if (!max || i > max) break;
	    else continue;
	}
	if (item.checked) { total++; list.value += item.value+';'; }
    }
    if (total == 0) { alert('Nothing selected.'); return false; }
    if (prompt && !window.confirm (total + " items selected:\n" + prompt)) {
	return false;
    }
    return true;
}

function highlight_td(node)
{
    if (!node) return;
    node.style.border = "solid blue 1px";
    node.style.background = "#ccccff";
    node.style.foreground = "white";
}

function unhighlight_td(node)
{
    if (!node) return;
    node.style.border = "solid white 1px";
    node.style.background = "white";
    node.style.foreground = "black";
}

function toggle_checkbox(box)
{
    if (!box) return;
    if (box.checked) highlight_td(box.offsetParent);
    else unhighlight_td(box.offsetParent);
    // var obj = ob_id('checked_items')
}

/* Select all checkboxes whose "id" value is prefix+increment. (foo1, foo2, ...)
 * If called by selecting a "check all" toggle (then "me" is an object),
 * select or deselect all based on me.checked.
 * If "me" is either true or false, then just use that value.
 * If "max" is 0, then only do item 0. (this is probably unused?)
 * If "max" > 0, don't look for ids beyond max.
 * If "max" -1, then special-case: "prefix" is a NAME, not an ID. Get all
 * checkboxes with that name, and do them all. (Most likely, all who call
 * this function should probably be using this method instead.)
 */
function select_all(me, prefix, max)
{
    var i = 0;
    var val = me == false? false : me == true? true : obj_id(me).checked;
    if (max == -1) {
	var objs = document.getElementsByName(prefix);
	for (i = 0; objs[i]; i++) {
	    objs[i].checked = val;
	}
	return;
    }
    while (++i) {
	var item = obj_id(prefix + i);
	if (!item) {
	    if (!max || i > max) return;
	    else continue;
	}
	item.checked = val;
	if (val) { highlight_td(item.offsetParent); }
	else { unhighlight_td(item.offsetParent); }
    }
}

/* uncheck (reset) the "all" checkbox if "me" (a checkbox in the group)
 * got unchecked.
 */
function reset_allcb(me, id)
{
    if (me.checked == false) obj_id(id).checked = false;
}
