(function($){ var defaults = { delimiter: ",", useArray: false}
$.Field = { version: "0.7", setDefaults: function(options){ $.extend(defaults, options);}}
$.fn.fieldArray = function(v){ var t = $type(v);
if( t == "undefined" ) return getValue(this);
if( t == "string" ||  t == "number" ){ v = v.toString().split(defaults.delimiter);
t = "array";}
if( t == "array" ) return setValue(this, v); return this;}
$.fn.getValue = function(){ return getValue(this).join(defaults.delimiter);}
var getValue = function(jq){ var v = [];
jq.each( function (lc){ var t = getType(this);
switch(t){ case "checkbox": case "radio": if( this.checked ) v.push(this.value); break;case "select": if( this.type == "select-one" ){ v.push( (this.selectedIndex == -1) ? "" : getOptionVal(this[this.selectedIndex]) );} else { for( var i=0; i < this.length; i++ ){ if( this[i].selected ){v.push(getOptionVal(this[i]));}}} break;case "text": v.push(this.value); break;}}); return v;}
$.fn.setValue = function(v){ return setValue(this, (!v ? [""] : v.toString().split(defaults.delimiter)));}
var setValue = function(jq, v){ jq.each( function (lc){ var t = getType(this), x;
switch(t){ case "checkbox": case "radio": if( valueExists(v, this.value) ) this.checked = true;
else this.checked = false; break;case "select": var bSelectOne = (this.type == "select-one");
for( var i=0; i < this.length; i++ ){ x = getOptionVal(this[i]);
bSelectItem = valueExists(v, x);
if(bSelectItem){ this[i].selected = true;
if(bSelectOne){ bKeepLooking = false; break;}} else if( !bSelectOne ) this[i].selected = false;}
if( bSelectOne && bKeepLooking ){ this[0].selected = true;} break;case "text": this.value = v.join(defaults.delimiter); break;}}); return jq;}
$.fn.formHash = function(inHash){ var bGetHash = (arguments.length == 0);
var stHash = {};
this.filter("form").each( function (){ var els = this.elements, el, n, stProcessed = {}, jel;
for( var i=0, elsMax = els.length; i < elsMax; i++ ){ el = els[i], n = el.name;
if( !n || stProcessed[n] ) continue;
var jel = $(el.tagName.toLowerCase() + "[@name='"+n+"']", this);
if(bGetHash){ stHash[n] = jel[defaults.useArray ? "fieldArray" : "getValue"]();} else if( !!inHash[n] ){ jel[defaults.useArray ? "fieldArray" : "setValue"](inHash[n]);}
stProcessed[n] = true;}}); return (bGetHash) ? stHash : this;}
$.fn.autoAdvance = function(){ return this.find(":text,:password,textarea").bind("keyup", function (e){ var $field = $(this), iMaxLength = parseInt($field.attr("maxlength"), 10); if( isNaN(iMaxLength) || ("|9|16|37|38|39|40|".indexOf("|" + e.keyCode + "|") > -1) ) return true;
if( $field.getValue().length >= $field.attr("maxlength") ){$field.moveNext().select();}});}
$.fn.moveNext = function(){ return this.moveIndex("next");}
$.fn.movePrev = function(){ return this.moveIndex("prev");}
$.fn.moveIndex = function(i){ var aPos = getFieldPosition(this);
if( i < 0 ) i = aPos[1].length-1;
else if( i >= aPos[1].length ) i = 0; return $(aPos[1][i]).trigger("focus");}
$.fn.getTabIndex = function(){ return getFieldPosition(this)[0];}
var getFieldPosition = function (jq){ var $field = jq.filter("input select textarea").get(0), aTabIndex = [], aPosIndex = [];
if( !$field ) return [-1, []];
$.each( $field.form.elements, function (i, o){ if( o.tagName != "FIELDSET" && !o.disabled ){ if( o.tabIndex > 0 ){aTabIndex.push(o);} else {aPosIndex.push(o);}}});
aTabIndex.sort( function (a, b){ return a.tabIndex - b.tabIndex;});
aTabIndex = $.merge(aTabIndex, aPosIndex);
for( var i=0; i < aTabIndex.length; i++ ){ if( aTabIndex[i] == $field ) return [i, aTabIndex];} return [-1, aTabIndex];}
$.fn.limitSelection = function(n, _e, _s){ var self = this;
var cb_onError = (!!_e) ? _e : function (n) { $('#error_msg').show(); return false; };
var cb_onSuccess = (!!_s) ? _s : function (n) { $('#error_msg').hide(); return true; };
var getCount = function (el){ if( el.type == "select-multiple" ) return $("option:selected", self).length;
else if( el.type == "checkbox" ) return self.filter(":checked").length; return 0;}
var undoSelect = function (){ setValue(self, getValue(self).slice(0, n)); return cb_onError(n, self);}
self.bind(
(!!self[0] && self[0].type == "select-multiple") ? "change" : "click", function (){ if( getCount(this) > n ){ return (this.type == "select-multiple") ? undoSelect() : cb_onError(n, self);}
cb_onSuccess(n, self); return true;}); return this;}
$.fn.createCheckboxRange = function(){ var iLastSelection = 0, me = this;
var findArrayPos = function (el){ var pos = -1;
$("input[@name='"+me[0].name+"']").each( function (i){ if( this == el ){ pos = i; return false;}}); return pos;}
this.each( function (lc){ if( this.type != "checkbox" ) return false;
var self = this;
var updateLastCheckbox = function (e){ iLastSelection = findArrayPos(e.target);}
var checkboxClicked = function (e){ var bSetChecked = this.checked, current = findArrayPos(e.target), iHigh, iLow;
if( !e.shiftKey ) return;
if( iLastSelection > current ){ iHigh = iLastSelection;
iLow = current-1;} else { iHigh = current;
iLow = iLastSelection-1;}
$("input[@name='"+self.name+"']:gt("+iLow+"):lt("+iHigh+")").attr("checked", bSetChecked ? "checked" : "");}
$(this).unbind("blur", updateLastCheckbox).unbind("click", checkboxClicked).bind("blur", updateLastCheckbox).bind("click", checkboxClicked); return true;});}
$.fn.sum = function(){ return sum(getValue(this));}
$.fn.avg = function(){ return avg(getValue(this));}
var sum = function (a){ var total = 0;
$.each(a, function (i, v){ var v = parseFloat(v, 10);
total += (isNaN(v) ? 0 : v);}); return total;}
var avg = function (a){ return sum(a)/a.length;}
var getType = function (el){ var t = el.type;
switch(t){ case "select": case "select-one": case "select-multiple":t = "select";break;case "text": case "hidden": case "textarea": case "password": case "button": case "submit": case "submit":t = "text";break;case "checkbox": case "radio":t = t;break;} return t;}
var getOptionVal = function (el){ return jQuery.browser.msie && !(el.attributes['value'].specified) ? el.text : el.value;}
var valueExists = function (a, v){ return ($.inArray(v, a) > -1);}
var $type = function (o){ var t = (typeof o).toLowerCase(); if( t == "object"){ if( o instanceof Array ) t = "array"; else if( o instanceof Date ) t = "date"; } return t;}
var $isType = function (o, v){ return ($type(o) == String(v).toLowerCase());}})(jQuery);