
// .innerHTML isn't allowed in XHTML, so we have to work with DOM objects a lot
// so helper functions are very helpful!
function TXT(string) {
	return document.createTextNode(string);
}
// Also since prototype has no way of removing/updating a text node object within
// another node, I have to provide one myself. sigh xhtml&prototype are not a happy couple.
function setTXT(node, string) {
	var c = node.firstChild;
	while(c) {
		node.removeChild(c);
		c = node.firstChild;
	}
	node.appendChild(TXT(string));
}
// Due to various awful reasons for how onbeforeunload and prototype work
// We use this to wrap it ourselves
function AddBeforeUnload(newfun) {
	var oldfun = window.onbeforeunload;
	if(oldfun) {
		window.onbeforeunload = function () {
			return newfun() || oldfun();
		}
	} else {
		window.onbeforeunload = newfun;
	}
}

// This is hack to get IE6 working with the box since it doesn't work with position:fixed
if(Prototype.Browser.IE) {
	if(parseFloat(navigator.appVersion.split("MSIE")[1]) < 7) {
		setInterval(function() {
			var swh = document.viewport.getDimensions();
			var stl = document.viewport.getScrollOffsets();
			$$(".appinfoBox").each(function(b) {
				var bwh = $(b).getDimensions();
				
				b.style.position = "absolute";
				b.style.left = (stl.left + swh.width - bwh.width) + "px";
				b.style.top = (stl.top + swh.height - bwh.height) + "px";
				b.style.right = "auto";
				b.style.bottom = "auto";
			});
		},100);
	}
}

// These auto-advance the focus when a field is filled
function AdvanceFormDown(event) {
	var element = event.element();
	element.AdvanceFormDownLen = element.value.length;
}
function AdvanceFormUp(event) {
	var element = event.element();
	if(element.value.length == element.maxLength &&
	   element.AdvanceFormDownLen < element.value.length) {
		element.AdvanceFormDownLen = element.value.length;
		$(element.AdvanceTarget).focus();
	}
}
function AdvanceFormHook(first, next) {
	$(first).AdvanceTarget = next;
	$(first).observe('keydown', AdvanceFormDown)
	        .observe('keyup', AdvanceFormUp);
}

 
var WidgetForm = Class.create({
	initialize: function(outer, phpcallbackurl, secretkey) {
		// initialize properties
		this.callback = phpcallbackurl;
		this.secretkey = secretkey;
		this.sections = [];
		this.changed = false;
		this.container = new Element('div');
		this.outercontainer = $(outer);
		
		// construct the updateBox in its own scope, then pass it out
		this.updatebox = (function() {
			var submit = new Element('a', {
				'class': 'buttonLink',
				'href': '#'
			});
			
			submit.appendChild(TXT("Save"));
			
			var box = new Element('div', {
				'class': 'appinfoBox'
			});
			
			var status = new Element('span', {
				'style': 'color: #ff0000'
			});
			status.appendChild(TXT("Unsaved"));
			
			box.appendChild(submit);
			box.appendChild(new Element('div', {'style': 'clear:both;'}));
			box.appendChild(TXT("Status: "));
			box.appendChild(status);
			box.hide();
			
			$(outer).appendChild(box);
			submit.observe('click', this.saveChanges.bindAsEventListener(this));

			return box;
		}).call(this); // <- note the immediate calling

		this.errorbox = (function() {
			var submit = new Element('a', {
				'class': 'buttonLink',
				'href': '#'
			});
			
			submit.appendChild(TXT("Save"));
			
			var box = new Element('div', {
				'class': 'appinfoBox'
			});
			
			var status = new Element('span', {
				'style': 'color: #ff0000'
			});
			status.appendChild(TXT("Unsaved"));
			
			box.appendChild(submit);
			var err = new Element('div', {'style': 'clear:both;'});
			this.errorcontainer = err;
			err.appendChild(TXT("errors go here"));
			box.appendChild(err);
			box.appendChild(TXT("Status: "));
			box.appendChild(status);
			box.hide();
			
			$(outer).appendChild(box);
			submit.observe('click', this.saveChanges.bindAsEventListener(this));

			return box;
		}).call(this); // <- note the immediate calling

		this.pendingbox = (function() {
			var box = new Element('div', {
				'class': 'appinfoBox'
			});
			
			box.appendChild(new Element('img', {src: 'img/load.gif'}));
			box.appendChild(TXT(" Saving... "));
			box.hide();
			
			$(outer).appendChild(box);

			return box;
		}).call(this); // <- note the immediate calling
		
		AddBeforeUnload(this.confirmUnload.bind(this));
	},
	
	confirmUnload: function(ev) {
		if(this.changed) {
			// for interoperability with Tabber2.js
			// .down() first because our div may be the nav_tab
			var TabberParent = this.outercontainer.down().up(".hidden");
			if(TabberParent) {
				// then we're (a) modified (b) using tabber (c) on a hidden tab
				// so show this tab
				TabberSelect(TabberParent.id.sub("_wrapper",""));
			}
			return "You have made changes that have not been saved.";
		}
	},
	
	changesMade: function(ev) {
		if(this.changed == false) {
			this.changed = true;
			this.errorbox.hide();
			// TODO more
		}
		// TODO should be above, but as a race condition patch, put her here
		// Also amend it so that we don't show if the error box is showing
		if(! this.errorbox.visible())
			this.updatebox.show();
	},
	
	saveChanges: function(ev) {
		// ignore the actual link
		if(ev) // if we're being called as an event handler
			ev.stop();

		// if no widget returns "true", then let's go save for real!
		if(! this.sections.invoke('specialPreSave').any())
			this.saveChangesReal(ev);
	},
	saveChangesReal: function(ev) {
		// javascript plays weird with this sometimes.
		var cthis = this;

		var sndtxt = {};
		var valerr = this.sections.invoke('accumulateResponse', sndtxt).find(Prototype.K);
		
		if(valerr) {
			this.stateError(valerr.error);
			valerr.widget.focus();
			return;
		}
		
		this.statePending();
		
		sndtxt.secretkey = this.secretkey;
		
		// so we fetch the value for each field, now send it!
		new Ajax.Request(this.callback, {
			method: 'post',
			parameters: sndtxt,
			onSuccess: function(transport) {
				if(transport.responseText == "ok") {
					// TODO in the distant future
					// we might want to fix the race condition
					// change -> save -> pending oh wait change again! -> done saving
					// but didn't save that last change, this.changed is now false
					// But, not a priority, as the save box will still be shown :)
					cthis.changed = false;
					cthis.stateOkay();
					
					cthis.sections.invoke('specialPostSave');
				} else if(transport.responseText == "login") {
					// TODO hide the pending box
					// TODO show the login box
					cthis.stateLogin();
				} else if(transport.responseText.substring(0,3) == "go:") {
					cthis.changed = false;
					window.location = transport.responseText.substring(3,transport.responseText.length);
				} else {
					cthis.stateError(transport.responseText);
				}
			},
			onFailure: function() {
				cthis.stateError("Unable to connect to server.");
			}
		});
	},
	stateOkay: function() {
		this.pendingbox.hide();
		// specifically do not hide the update box here
	},
	stateError: function(errstr) {
		this.updatebox.hide();
		this.pendingbox.hide();
		setTXT(this.errorcontainer,errstr);
		this.errorbox.show();
	},
	statePending: function() {
		this.errorbox.hide();
		this.updatebox.hide();
		this.pendingbox.show();
	},
	stateLogin: function() {
		this.stateError("Session expired? Please login.");
	},
	
	showForm: function () {
		this.outercontainer.appendChild(this.container);
		this.sections.invoke('displayed');
	},
	getWF: function () {
		return this;
	}
});

var WidgetSection = Class.create({
	initialize: function(WF, text) {
		var bigbox = new Element('div', {
			'class': 'mainContentBlueBox'
		});
		bigbox.appendChild(new Element('a', {
			'name': text,
			'id': text
		}));
		var btxt = new Element('span', {
			'class': 'bold'
		});
		btxt.appendChild(TXT(text));
		bigbox.appendChild(btxt);
		
		var contentbox = new Element('div', {
			'class': 'BlueBoxContent'
		});
		
		// initialize my properties
		this.WF = WF;
		this.widgets = [];
		this.container = contentbox;
		
		// add myself to the WF
		this.WF.container.appendChild(bigbox);
		this.WF.container.appendChild(contentbox);

		this.WF.sections.push(this);
	},
	displayed: function() {
		this.widgets.invoke('displayed');
	},
	specialPreSave: function() {
		// returns true if any widget's specialPreSave function returns true
		return this.widgets.invoke('specialPreSave').any();
	},
	specialPostSave: function() {
		return this.widgets.invoke('specialPostSave');
	},
	getWF: function() {
		return this.WF.getWF();
	},
	accumulateResponse: function(sndtxt) {
		// mutate the sndtxt object to add stuff to send to the server
		// return false for success, otherwise return and error object
		return this.widgets.invoke('accumulateResponse', sndtxt).find(Prototype.K);
	}
});

var WidgetSubSection = Class.create(WidgetSection, {
	// dance a difficult dance!
	// pretend to be a widget to it's parent!
	// pretend to be a section to it's children
	// good luck! ;)
	initialize: function(WS, text) {
		var field = new Element('div', {'class': 'field'});
		var label = new Element('div', {'class': 'label'});
		label.appendChild(TXT(text + ":"));
		var elements = new Element('div', {'class': 'smallfield'});
		var clear = new Element('div', {'style': 'clear: both;'});
		field.appendChild(label);
		field.appendChild(elements);
		field.appendChild(clear);
		
		WS.container.appendChild(field);
		WS.widgets.push(this);

		this.WS = WS;
		this.WF = WS;
		this.widgets = [];
		this.container = elements;
	}
});

// SECTION 2 section2:    Widget!

var WidgetElement = Class.create({
	initialize: function(WS, text, field, param, value) {
		this.WS = WS;
		this.field = field;
		this.observefields = [];
		this.parameters = param;
		this.oldValue = value;
		
		this.WS.widgets.push(this);
		
		this.addField(TXT(text + ":"), this.createWidget(value));
	},
	addField: function(left, right) {
		var field = new Element('div', {
			'class': 'field',
			'id': this.field + 'field'
		});
		var label = new Element('div', {
			'class': 'label'
		});
		label.appendChild(left);
		var elements = new Element('div', {
			'class': 'elements'
		});
		
		if(!Object.isArray(right)) {
			right = [right];
		}

		for(var i = 0; i < right.length; i++) {
			var eelement = new Element('div', {
				'class': (i==0) ? 'first_element' : 'extra_element'
			});
			eelement.appendChild(right[i]);
			elements.appendChild(eelement);
		}
		
		var clear = new Element('div', {
			'style': 'clear: both;'
		});
		field.appendChild(label);
		field.appendChild(elements);
		field.appendChild(clear);
		
		this.WS.container.appendChild(field);
		this.fieldelement = field;
	},
	InputField: function(type, field, style, value, maxlen) {
		var rname = field;
		if(type == 'radio') {
			// pass radios in as "field#" where # is the ID for this single button
			rname = field.substr(0, field.length - 1);
		}
		var elem = new Element('input', {
			'type': type,
			'name': rname,
			'id': field,
			'class': 'WidgetFormField',
			'style': style
		});
		if(value) elem.value = value;
		if(maxlen) { 
			elem.maxLength = maxlen;
			elem.size = maxlen;
		}
		
		this.observefields.push(elem);
		
		return elem;
	},
	TextField: function(field, rows, cols, style, value) {
		var elem = new Element('textarea', {
			'name': field,
			'id': field,
			'class': 'WidgetFormField',
			'style': style,
			'rows': rows,
			'cols': cols
		});
		if(value) elem.value = value;
		
		this.observefields.push(elem);
		
		return elem;
	},
	SelectField: function(field, multiple, style) {
		var elem = new Element('select', {
			'name': field,
			'id': field,
			'class': 'WidgetFormField',
			'multiple': multiple,
			'style': style
		});
		
		this.observefields.push(elem);
		
		return elem;
	},
	getWF: function() {
		return this.WS.getWF();
	},
	changed: function(ev) {
		this.getWF().changesMade(ev);
	},

	inputFocused: function(ev) {
		var el = ev.element();
		el.originalValue = el.value;
	},
	
	changesPossible: function(ev) {
		var el = ev.element();
		// TODO if I add this back in it doesn't work? Why?
		// like the first key press doesn't work, but the second does.
		//if(el.value != el.originalValue)
			this.changed(ev);
	},
	stringValue: function() {
		return $F(this.field);
	},
	displayed: function() {
		var cthis = this; // javascript :angry shaking fist:
		this.observefields.each( function(elem) {
			elem.observe('change', cthis.changed.bindAsEventListener(cthis));
			elem.observe('focus', cthis.inputFocused.bindAsEventListener(cthis));
			elem.observe('keypress', cthis.changesPossible.bindAsEventListener(cthis));
			if(elem.type == "radio")
				elem.observe('click', cthis.changesPossible.bindAsEventListener(cthis));
		});
	},
	validate: function() {
		// by default, no special validation
		return null;
	},
	focus: function() {
		// default implementation:
		this.observefields[0].focus();
	},
	specialPreSave: function() {
		return false;
		// special widgets like the photo upload will upload their photo,
		// update their own internal state
		// then remember that it's now uploaded so it doesn't need to do so again
		// so when specialPreSave gets called again, it says "a-okay, now send the form!"
	},
	specialPostSave: function() {
	},
	accumulateResponse: function(sndtxt) {
		// mutate the sndtxt object to add stuff to send to the server
		// return false for success, otherwise return and error object
		var str = this.validate();
		if(str) return { widget: this, error: str }
		
		str = this.stringValue();
		// strings are fine, arrays are fine, anything else gets the JSON treatment
		if(str && !Object.isString(str) && !Object.isArray(str)) {
			str = Object.toJSON(str);
		}
		sndtxt[this.field] = str;
		
		return false;
	},
	hide: function() {
		this.fieldelement.hide();
	},
	show: function() {
		this.fieldelement.show();
	}
});

var FatWidgetElement = Class.create(WidgetElement, {
	addField: function(left, right) {
		var field = new Element('div', {
			'class': 'field',
			'id': this.field + 'field'
		});
		var label = new Element('span', {
			'class': 'bold'
		});
		label.appendChild(left);
		var clear = new Element('div', {
			'style': 'clear: both;'
		});
		field.appendChild(label);
		field.appendChild(right);
		field.appendChild(clear);
		
		this.WS.container.appendChild(field);
		
		this.fieldelement = field;
	}
});

// SECTION 3 section3:   Widgets!
/* to celebrate the start of this section of the source code, here is a cow
                                       /;    ;\
                                   __  \\____//
                                  /{_\_/   `'\____
                                  \___   (o)  (o  }
       _____________________________/          :--'  
   ,-,'`@@@@@@@@       @@@@@@         \_    `__\
  ;:(  @@@@@@@@@        @@@             \___(o'o)
  :: )  @@@@          @@@@@@        ,'@@(  `===='       
  :: : @@@@@:          @@@@         `@@@:
  :: \  @@@@@:       @@@@@@@)    (  '@@@'
  ;; /\      /`,    @@@@@@@@@\   :@@@@@)
  ::/  )    {_----------------:  :~`,~~;
 ;;'`; :   )                  :  / `; ;
;;;; : :   ;                  :  ;  ; :              
`'`' / :  :                   :  :  : :
    )_ \__;      ";"          :_ ;  \_\       `,','
    :__\  \    * `,'*         \  \  :  \   *  8`;'*  *
        `^'     \ :/           `^'  `-^-'   \v/ :  \/ 
*/

var inputWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		return this.InputField("text",this.field,"width:221px;",value,null);
	}
	// since we used same field name, we don't need to override stringValue
});

var dateWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var val = value && value.split('-');
		if(!val || !val.length || val.length != 3)
			val = $A("Year","Month","Day");

		this.month = this.SelectField(this.field+"month",false,"");
		this.day = this.SelectField(this.field+"day",false,"");
		this.year = this.SelectField(this.field+"year",false,"");
		
		var i;

		var opt = new Element('option', {'selected': ("Year" == val[0]),'value': "Year"});
		opt.appendChild( TXT( "Year" ) );
		this.year.appendChild( opt );
		for(i=Math.max(2008,new Date().getFullYear()); i > 1900; i--) {
			opt = new Element('option', {'selected': (i == val[0]),	'value': i});
			opt.appendChild( TXT( i ) );
			this.year.appendChild( opt );
		}

		var mnth = this.month;
		var opts = $w("Month Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
		if(Number(val[1]) != Number.NaN)
			val[1] = opts[Number(val[1])];
		opts.each(function(d) {
			opt = new Element('option', {'selected': (d == val[1]),	'value': d});
			opt.appendChild( TXT( d ) );
			mnth.appendChild( opt );
		});

		var opt = new Element('option', {'selected': ("Day" == val[2]),'value': "Day"});
		opt.appendChild( TXT( "Day" ) );
		this.day.appendChild( opt );
		for(i=1; i < 32; i++) {
			opt = new Element('option', {'selected': (i == val[2]),	'value': i});
			opt.appendChild( TXT( i ) );
			this.day.appendChild( opt );
		}

		var div = new Element('div');
		div.appendChild(this.month)
		div.appendChild(this.day)
		div.appendChild(this.year)
		return div;
	},
	validate: function() {
		// Make sure either none or all are selected
		if(this.year.value == "Year" ||
		   this.month.value == "Month" ||
		   this.day.value == "Day") {
			if(!(this.year.value == "Year" &&
			   this.month.value == "Month" &&
			   this.day.value == "Day")) {
				return "Please finish filling in the date";
			}
		}
		return null;
	},
	stringValue: function() {
		if(this.year.value == "Year" &&
		   this.month.value == "Month" &&
		   this.day.value == "Day") {
			return null;
		}
		return this.month.value + " " + this.day.value + ", " + this.year.value;
	}
});

var ssninputWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var vals = [];
		// If we have a value, parse it
		if(typeof value == "string")
			vals = value.split("-");
		// If the value we got doesn't parse, ignore it
		if(vals.length != 3)
			value = null;
			
		var widget = new Element('div'); // just a container
		this.ssnf1 = this.InputField("text",this.field+"1","",value && vals[0],3);
		this.ssnf2 = this.InputField("text",this.field+"2","",value && vals[1],2);
		this.ssnf3 = this.InputField("text",this.field+"3","",value && vals[2],4);
		widget.appendChild(this.ssnf1);
		widget.appendChild(TXT(" - "));
		widget.appendChild(this.ssnf2);
		widget.appendChild(TXT(" - "));
		widget.appendChild(this.ssnf3);
		
		return widget;
	},
	
	stringValue: function() {
		var txt = [$F(this.field+"1"), $F(this.field+"2"), $F(this.field+"3")].join('-');
		return txt;
	},
	
	displayed: function($super) {
		$super(); // Call the original!
		
		// Now, hook our own events.
		AdvanceFormHook(this.ssnf1, this.ssnf2);
		AdvanceFormHook(this.ssnf2, this.ssnf3);
	}
});

var essayWidget = Class.create(FatWidgetElement, {
	createWidget: function(value) {
		return this.TextField(this.field, 6, 25, "width:100%;", value);
	}
	// default stringValue is a-o-k
});

var bigtextWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		return this.TextField(this.field, 4, 25, "width:300px;", value);
	}
	// default stringValue is a-o-k
});

var radioWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		this.initialvalue = value;
		var cont = new Element('div'); // just a container
		for(var i = 0; i < this.parameters.length; i++) {
			var wdgt = this.InputField('radio',this.field + i, "", this.parameters[i]);
			var lbl = new Element('label', { 'for': this.field + i });
			lbl.appendChild( TXT( "  " + this.parameters[i] + "  " ) );
			cont.appendChild(wdgt);
			cont.appendChild(lbl);
		}
		return cont;
	},
	stringValue: function () {
		for(var i = 0; i < this.parameters.length; i++) {
			if($(this.field + i).checked) {
				return this.parameters[i];
			}
		}
		return null;
	},
	displayed: function($super) {
		$super(); // Call the original!
		for(var i = 0; i < this.parameters.length; i++) {
			if(this.parameters[i] == this.initialvalue) {
				$(this.field + i).checked = "checked";
			}
		}
	}
});

var selectWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var cont = this.SelectField(this.field, false, "");
		this.parameters.each( function(str) {
			var opt = new Element('option', {
				'selected': (str == value),
				'value': str
			});
			
			opt.appendChild( TXT( str ) );
			
			cont.appendChild( opt );
		});
		this.selectelement = cont;
		return cont;
	}
	// default stringValue should work
});

var addressWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var vals = [];
		if(typeof value == "string")
			vals = value.split('&&&');
		var street = vals[0];
		var city = vals[1];
		var state = vals[2];
		var postal = vals[3];
		var country = vals[4];
		
		this.street = this.InputField('text', this.field + "street", "width:221px", street, null);
		this.city = this.InputField('text', this.field + "city", "width:110px", city, null);
		this.state = this.InputField('text', this.field + "state", "width:30px", state, null);
		this.postal = this.InputField('text', this.field + "postal", "width:65px", postal, null);

		this.country = this.SelectField(this.field + "country", false, "");
				
		var opts = this.country;
		this.parameters.each( function(str) {
			var opt = new Element('option', {
				'selected': (str == country),
				'value': str
			});
			
			opt.appendChild( TXT( str ) );
			
			opts.appendChild( opt );
		});
		
		var cont = new Element('div'); // container
		cont.appendChild(this.city);
		cont.appendChild(TXT(" "));
		cont.appendChild(this.state);
		cont.appendChild(TXT(" "));
		cont.appendChild(this.postal);
		
		return [this.street, cont, this.country];
	},
	stringValue: function () {
		var txt = [this.street.value, this.city.value, this.state.value, this.postal.value, this.country.value].join('&&&');
		return txt;
	}
});

var passwordWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		return this.InputField("password",this.field,"",value,null);
	}
	// since we used same field name, we don't need to override stringValue
});

var confpassWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		return [this.InputField("password",this.field+"1","",value,null),
		        TXT( "Retype to confirm new password:" ),
		        this.InputField("password",this.field+"2","",value,null),
		        new Element('div', {'id':this.field+"confirm", 'style': 'color: #f00'})];
	},
	stringValue: function () {
		return $F(this.field+"1");
	},
	validate: function () {
		var p1 = $F(this.field+"1");
		var p2 = $F(this.field+"2");
		if(p1 != p2) {
			return "The new passwords you entered are not the same.";
		}
		return null;
	},
	displayed: function() {
		var b = this.checkit.bindAsEventListener(this);
		$(this.field+"1").observe('keyup', b).observe('change', b);
		$(this.field+"2").observe('keyup', b).observe('change', b);
	},
	checkit: function() {
		if($F(this.field+"1") != $F(this.field+"2")) {
			setTXT($(this.field+"confirm"), "Passwords do not match.");
		} else {
			setTXT($(this.field+"confirm"), "");
		}
	}
});

var multientryWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var cont = new Element('div').setStyle({
			height:'100px',
			overflow:'auto',
			width:'400px',
			border:'2px solid #CECECE'
		});
		if(!Object.isArray(value)) {
			value = $A(value);
		}
		var cthis = this;
		this.parameters.each(function(str) {
			if(str!='') {
				var opt = new Element('div');
				var checkbox = new Element('input', {'type': 'checkbox'}).setStyle({
					margin: '2px'
				});
				var selected = value.include(str);
				checkbox.setValue(selected);
				checkbox.value = str;
				cthis.observefields.push(checkbox);
				var label = new Element('label');
				label.appendChild(TXT(str));
				opt.appendChild(checkbox);
				opt.appendChild(label);
				cont.appendChild(opt);
				if(selected) {
					opt.setStyle({
						background: '#CECECE'
					});
				}
				checkbox.observe('click', function() {
					if(checkbox.getValue()) {
						opt.setStyle({
							background: '#CECECE'
						});
					}
					else {
						opt.setStyle({
							background: '#FFFFFF'
						});
					}
				});
			}
		});
		
		return cont;
	},
	stringValue: function() {
		var ret = new Array();
		this.observefields.each(function(checkbox) {
			if(checkbox.getValue()) {
				ret.push(checkbox.value);
			}
		});
		return ret;
	}
});

var responderWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var cont = new Element('div');
		this.inputfield = this.InputField("text", this.field, "", value, null);
		cont.appendChild(this.inputfield);
		// we need a container to constrain the size of the input box
		return cont;
	},
	displayed: function() {
		// displayed should only ever be called once
		attach_dropdown($(this.field), this.parameters, this.setval.bind(this)); // parameters is url of responder
	},
	setval: function(val) {
		this.inputfield.value = val;
		this.changed();
	}
});

var myschoolWidget = Class.create(FatWidgetElement, {
	createWidget: function(value) {
		this.myschoolval = value; // save for later
		this.myschoolcontainer = new Element('div');
		this.myschoolcontainer.appendChild(new Element('a', {
			'name': "mys" + value.id,
			'id': "mys" + value.id
		}));

		var rbut = new Element('div', {style:"float:right"});
		var rbuta = new Element('a', {href:"#", 'class':'buttonLink'});
		rbuta.appendChild(TXT("Delete"));
		rbut.appendChild(rbuta);
		this.myschoolbutton = rbut;
		this.myschoolcontainer.appendChild(rbut);
		
		var ttxt = new Element('div', {style:"float:left; margin-top:1px;"});
		ttxt.appendChild(TXT("Type: "));
		this.myschoolcontainer.appendChild(ttxt);
		this.myschooltype = this.SelectField("ms"+value.id+"_type", false, "float:left;");
		this.myschoolcontainer.appendChild(this.myschooltype);
		
		
		var mtxt = new Element('div', {style:'clear:both'});
		mtxt.appendChild(TXT("My thoughts:"));
		this.myschoolcontainer.appendChild(mtxt);

		this.myschoolthoughts = this.TextField("ms"+value.id+"_thoughts", 4, 25, "width: 100%", value.my_thoughts);
		this.myschoolcontainer.appendChild(this.myschoolthoughts);

		var cthis = this;
		this.parameters.each(function(b) {
			var opt = new Element('option', {
				'selected': (value.type == b),
				'value': b
			});
			opt.appendChild( TXT( b ) );
			cthis.myschooltype.appendChild(opt);
		});
		
		return this.myschoolcontainer;
	},
	stringValue: function () {
		this.myschoolval.my_thoughts = this.myschoolthoughts.getValue();
		this.myschoolval.type = this.myschooltype.getValue();
		return this.myschoolval; // it's okay to return non-string stuff. stringvalue is now a misnomer :(
	},
	msdelete: function (e) {
		this.myschoolval.action = 'delete';
		$(this.field+"field").hide();
		e.stop();
		this.changed();
	},
	displayed: function ($super) {
		$super(); // call parent!
		this.myschoolbutton.observe('click', this.msdelete.bindAsEventListener(this));
	}
});

var photoWidget = Class.create(WidgetElement, {
	createWidget: function (value) {
		var AU = new AjaxUploader({
			'field': this.field,
			'callback': 'widgets/imgupload.php',
			'extra': {'secretkey': this.getWF().secretkey}
		});
		
		this.AUval = null;
		this.AU = AU;
		this.observefields.push(AU.input);
		
		return AU.getHTML();
	},
	displayed: function ($super) {
		$super();
		this.AU.hook();
	},
	stringValue: function () {
		return this.AUval;
	},
	specialPreSave: function() {
		// if the user doesn't change files, we're good to go
		if(!this.AU.changed)
			return false;
		// otherwise we got fun to take care of.
		this.getWF().statePending();
		var cthis = this;
		this.AU.submit({
			onSuccess: function(response) {
				if(response.substring(0,3) == "ok:") {
					var res = response.substring(3,response.length).split('.');
					cthis.AUval = {
						'filename': res[0],
						'extension': res[1]
					};
					//alert("success: " + res[0] + "." + res[1]);
					cthis.AU.changed = false;
					cthis.getWF().saveChanges();
				} else if(response == "login") {
					cthis.getWF().stateLogin();
				} else {
					cthis.getWF().stateError(response);
				}
			},
			onFailure: function() {
				cthis.getWF().stateError("File upload failed. Check your internet connection?");
			}
		});
		return true;
	},
	specialPostSave: function() {
		this.AUval = null;
	}
});

var hiddenWidget = Class.create(WidgetElement, {
	addField: function(l,r) {
		// display nothing.
	},
	createWidget: function(v) {
		this.storedValue = v;
		return null; // display nothing
	},
	stringValue: function() {
		return this.storedValue;
	}	
});

var captchaWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var div = new Element('div');
		div.appendChild(new Element('img', {
			'src': 'captcha.php'
		}));
		div.appendChild(new Element('br'));
		div.appendChild(this.InputField("text", this.field, "", null, null));
		return div;
	} // TODO: add "what's this?"
});

var percentWidget = Class.create(WidgetElement, {
	createWidget: function(value) {
		var div = new Element('div');
		var mif = this.InputField("text",this.field,"width:51px;",value,null);
		div.appendChild(mif);
		div.appendChild(TXT("%"));
		return div;
	}
	// since we used same field name, we don't need to override stringValue
});

