//
// Utils
//

//
// Extensions of $().is :
// $().all : true iff all elements match the selector
$.fn.all = function (sel) {
    return this.filter(sel).length === this.length; 
};
// $().none : true iff no elements match the selector
$.fn.none = function (sel) {
    return this.not(sel).length === this.length; 
};

// Shortcuts for ID and name selectors
function hasName(name) {
    return '[name="' + name + '"]'; 
}
function hasId(id) {
    return "#" + id; 
}

function makeInteger(str) {
    return + ( str.replace(/[^0-9]/g,'') ); 
}

function mainForm() {
    return $(hasName('MainForm')); 
}
function $formElement (name) {
    return $( hasName(name) ).filter('textarea,input,select'); 
}
function formElement (name) {
    return $formElement(name).get(0); 
}

function isChecked(name) {
    var elem = formElement(name);
    return elem && elem.checked;
}
function formValue(name) {
    var $elem = $formElement(name);
    
    if($elem.length === 1) {
	if($elem.is("[type=checkbox]")) {
	    return $elem[0].checked ? $elem.val() : null;
	} else {
	    return $elem.val();
	}
    } else if( $elem.all("[type=radio]") ) {
	return $elem.filter(":checked").val();
    } else {
	return null;
    }
}

//
// Form verification
//
function verifyFields(list) { // [[ field name, predicate, error string ], ...]
    $.each(list, function(i, v) {
	if(! v[1](formValue(v[0]), formElement(v[0]))) {
	    throw ({ err: v[2], fd: formElement( v[0] ) });
	}
    });
}

function handleVerifyError(e) {
    if(e.hasOwnProperty('err') && e.err) { window.alert(e.err); }
    if(e.hasOwnProperty('fd') && e.fd) { e.fd.focus(); }
}

// Some predicates for verification
function isEmptyString(str) {
    return str === '';
}
function isNotEmptyString(str) {
    return str !== '';
}
function isEmptyArray(arr) {
    return !($.isArray(arr) && arr.length);
}
function isNotEmptyArray(arr) {
    return $.isArray(arr) && arr.length;
}
function isPositive(num) {
    return num > 0;
}


//
// Ensure reciprocal focusing between related checkboxes / radio buttons & text fields
// Should usually be called on ready event, i.e. $(reciprocalFocus);
//
// The "rel" attribute should be on the checkbox, and give the ID of the text field, etc.
// that the checkbox controls
//

function reciprocalFocus() {
    var rel, relfield, eventname;

    $("input[type=checkbox], input[type=radio]")
	.click(function(e) {
	    // Install click handler on the checkbox to focus the associated field
	    rel = e.target.getAttribute('rel');
	    relfield = document.getElementById(rel) || formElement(rel);
	    if( e.target.checked && relfield ) { relfield.focus(); }
	} )
    
	.each(function() {
	    // Install change handlers for the associated field to enable the checkbox
	    rel = this.getAttribute('rel');
	    relfield = document.getElementById(rel) || formElement(rel);

	    if( relfield ) {
		eventname = (relfield.tagName === 'INPUT') ? 'click' : 'change';
		var that = this;
		$(relfield).bind(eventname, function(e) { that.checked = true; });
	    }
	});
}


//
// Given an object representing values for form fields (e.g. from a previous
// CGI request), make the state of the displayed form reflect them
//

function displayPreviousChoices(cgi) {
    var field, value, key, $formFields, descent;
   
    function setValue(value) {
	return function (i) {
	    if( $(this).is('[type=checkbox],[type=radio]') ) {
		this.checked = ( value.indexOf(this.value) !== -1 );
	    } else {
		this.value = value[i];
	    }
	}; 
    }

    for(field in cgi) {
	if(cgi.hasOwnProperty(field)) {
	    value = cgi[field];

	    $formFields = $formElement(field);
	    if(! $formFields.length ) {
		$formFields = $formElement( field + '[]' );
	    }

	    switch( $formFields.length ) {
	    case 0:
		// There are no fields named either 'field' or 'field[]'. In this case, we'll try
		// looking for fields named with explicit indices like 'field[a]' 
		if( $.isArray(value) || $.isPlainObject(value) ) {
		    descent = {};
		    for(key in value) {
			if(value.hasOwnProperty(key)) {
			    descent[ field + '[' + key + ']' ] = value[key];
			}
		    }

		    displayPreviousChoices(descent);
		}
		
		break;
		
	    case 1:
		// Just use jQuery's val() to set the value of the form element, and check if radio/checkbox
		$formFields.val(value);
		
		if( $formFields.is('[type=checkbox],[type=radio]') ) {
		    $formFields[0].checked = true;
		}
		break;
		
	    default: 
		// There are multiple items. In this case, we loop over and either check/uncheck
		// or use jQuery's val() to set the value
		if(! $.isArray(value) ) { 
		    value = [value];
		}
		
		$formFields.each(setValue(value));
		
		break;
	    }   
	}
    }    
}
