﻿/*
Forms library!
	by GTP, 09/2007
	
	
	=====( free functions )=====
	TODO:
	- abstract them better.  make sure they all work with options that can be provided form a host page.
	- audit the global variables.  are they all really necessary?
	- move everything into namespaces!!
	
	
	=====( FormElementActor base class )=====
	TODO:
	- see what else we should move in here
	
	
	=====( Action class )=====
	TODO:
	- actions (and validations) now have a pressing need to respond to more than one type of event
	
	
	=====( Validation class )=====
	TODO:
	- prevent submission if front-end validation detects errors.
	- clean up per-field error reporting (from backend response)
	- allow for multiple events for each validation type, or at least standardize for all of them
	
	Dudez, I'm in your commentz!!!!!
*/


if ( typeof(Avenet)=='undefined')
	Avenet={};
	
Object.extend(Avenet, { Forms:{}, Debug:{}, Utilities:{} });

Avenet.Forms.currentForm = null;

Avenet.Forms.FormElementActor = Class.create({
	e: null, type: null, parameters: $A([]), partner: null, condition: null, form: null, dependent: null,
	
	initialize: function( element, type ){
		this.e = element; this.type = type; this.parseClass();
		if( this.type == 'vMatch' || this.type == 'aTrigger' || this.type == 'aClear' || this.type == 'aSummarize' )
			this.getPartner();
		this.condition = this.parseConditions();
	},
	parseClass: function(){
		if( this.type.indexOf("_") != -1 ){ //split up class name to get parameters?
			var parts = this.type.split("_");
			this.type = parts[0]; this.parameters = parts.slice(1,parts.length);
		}
	},
	getPartner: function(){
		var formName = this.e.id.split('_')[0];
		
		// if it's a form element, grab it by name, otherwise, grab element by id
		var theForm = this.e.up('form');
		this.partner =	$(theForm[this.parameters[0]]) ||
						$(theForm.id + "_" + this.parameters[0]) ||
						$(theForm.id + "_" + this.parameters[1]) ||
						$(formName + "_" + this.parameters[0]) ||
						$(theForm).down("#" + this.parameters[0]);
		this.parameters = this.parameters.slice(1,this.parameters.length);
		if( this.partner == null ) {alert ( lpack.FormElementActorPartnerError.replace("{0}",  this.e.id) ); }
	},
	parseConditions: function(){
		var p = this.parameters;
		var theForm = this.e.up('form');
		if( p[0] == "when" && p[2] == "is" )
			if( this.e.hasClassName('otherLine') )
				return new Avenet.Forms.ActorCondition( this.e.up('div.list'), 'other' );
			else
				return new Avenet.Forms.ActorCondition( theForm.id + "_" + p[1], p[3] );
		else if( p[1] == "when" && p[3] == "is" )
			if( this.e.hasClassName('otherLine') )
				return new Avenet.Forms.ActorCondition( this.e.up('div.list'), 'other' );
			else
				return new Avenet.Forms.ActorCondition( theForm.id + "_" + p[2], p[4] );
		return null;
	},
	distillBoolean: function(){
		var v = this.e.value; var p = this.parameters[0];
		switch( this.e.type ){
			case "radio": return ( p == "show" ); break;
			case "checkbox": return ( (p == "show" && this.e.checked) || (p == "hide" && !this.e.checked) ); break;
			case "select-one":  return (!this.partner && this.e[this.e.selectedIndex].value != '') || (this.partner && this.partner.id.match(/OtherRow$/) && this.e[this.e.selectedIndex].value == 'other'); break;
			default: return !v.blank(); break;
		}
		return false;
	}
});

Object.extend( Avenet.Forms.FormElementActor, {
	createActors: function(form){
		// Create / attach SPECIFIED validation and action objects
		form.select("input, div.step, textarea, select, button, div.linkbuilder").each( function(e) {
			var vTypes = e.className.split(' ').findAll( function(c){ return c.match(/^[va][A-Z][a-zA-Z0-9]+/); } );
			vTypes.each(function(vType){
				switch(vType.charAt(0)){
					case 'a': new Avenet.Forms.Action(e, vType); break;
					default: new Avenet.Forms.Validation(e, vType); break;
				}
			});
			if( vTypes.length > 0 ) Avenet.Forms.Validation.bucket.push(e);    // save this element in the bucket
		});
		
		// Create and attach DEFAULT validation and action objects
		form.select("textarea").each( function(box) {
			if( !box.hasClassName('htmlAllowed') ) {
				new Avenet.Forms.Validation( box, 'vNoHTML' );
			}
		});
		form.select("div.list input.otherItem, select option.otherItem").each( function(e){ // default actions for "other" options
			var thisList = e.up('div.list');
			if( !thisList ) thisList = e.up('select');
			
			new Avenet.Forms.Action( e, 'aTrigger_' + thisList.id.split("_")[1] + 'OtherRow_show' );
			
			if( thisList.hasClassName('radio') ) thisList.select("input").each( function(e){
				if( !e.hasClassName('otherItem') && !e.hasClassName('otherLine') )
					new Avenet.Forms.Action( e, 'aTrigger_' + thisList.id.split("_")[1] + 'OtherRow_hide' );
			});
		});
	}
});


Avenet.Forms.Validation = Class.create( Avenet.Forms.FormElementActor, {
	initialize: function( $super, element, type ){
		$super( element, type );
		
		if( !this.e.validations ) { // first validation attached to this element?
			this.e.validations = $A([]); this.e.originalStyle = { }; this.e.skipValidation = false;
			
			// for each style we change in errorStyle, save the current style property for later...
			$H(Avenet.Forms.Validation.errorStyle).each(function(pair){
				this.e.originalStyle[pair.key] = this.e.parentNode.getStyle(pair.key);
			}.bind(this));
			
			this.e.originalStyle['color'] = 'Window';
			
			Object.extend(this.e, Avenet.Forms.Validation.ElementMethods ); // extend the element itself with some methods
		}
		
		if( this.type == "vMatch" ) new Avenet.Forms.Action( this.partner, "aClear_" + this.e.name );
		
		if( this.e.hasClassName('otherItem') ) return;  // special case: ignore! this validation belongs on the field, not the item
		if(this.e.className.match(/vRange/)) {
			if(this.e.minValue) {
				this.dependent = $(this.e.minValue);
			} else if(this.e.maxValue) {
				this.dependent = $(this.e.maxValue);
			}
		}
		this.e.validations.push( this );
		if(this.e.type == 'select-one') this.type += 'Menu';
		Event.observe( this.e, Avenet.Forms.Validation.typeToEvent(this.type), this.e.handleValidationEvent );
	},
	isOK: function(){
		var v = this.e.value;
		
		// short circuits...
		if( this.condition && !this.condition.isMet() ) return true;
		if( v.blank() && !this.e.className.match(/vReq/) ) return true;
		if( this.e.skipValidation ) return true;
		
		// cases...
		switch( this.type.split("_")[0] ) {
			case "vMoreThan2": return ( v.length > 2 ); break;
			case "vEmail": return ( v.match(/^[^()<>@,;:\"\[\]]+@([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]{2,})+)$/) ); break;
			case "vZip": return ( v.match(/^\d{5}(-\d{4})?$/) ); break;
			case "vUserName": return ( v.match(/^[a-zA-Z0-9_]+$/) ); break;
			case "vMatch": return ( this.partner.value == v ); break;
			case "vReq": return this.distillBoolean(); break;
			case "vReqMenu": return this.distillBoolean(); break;
			case "vDate": 
				var d = new Date(v);
				if(d == "NaN" || d == "Invalid Date") return false;
				return true;
				break;
				//return (d != "Invalid Date"); break;
			case "vPassword": return ( v.length >= 8 ); break;
			case "vNoHTML": return ( v.stripTags() == v ); break;
			case "vInteger": return ( v.match(/^[0-9]+$/) ); break;
			case "vDecimal": return ( v.match(/^[0-9]+(\.[0-9]+)?$/) ); break;
			case "vRange": return this.isValidRange(this.e); break;
			case "vUrlTitle": return (v.match(/^[a-zA-Z0-9_-]+$/)); break;
			case "vUrl": return v.match(/^(([-\w]+\.)+(com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|arpa|asia|cat|coop|edu|int|pro|tel|[a-z]{2})){1}(([/?#]){1}([\w\d:#@%/;$()~_?\+-=\\\.&])*){0,1}$/i); break;
			case "vCustom": return (this.parameters.length > 0) ? eval(this.parameters[0]).call(this) : false; break;
			case "vFileName":
				var fName = v.substring(v.lastIndexOf("\\"));
				fName = fName.substring(fName.lastIndexOf("/"));
				return !fName.match(/[:*?<>\"|#%]/); break;
			default: return true; break;
		}
		return false;
	},
	respondsTo: function( anEvent ) {
		return ( Avenet.Forms.Validation.typeToEvent(this.type) == anEvent );
	},
	isValidRange: function(element) {
		var v = element.className.match(/vDate/) ? new Date(element.value) : parseFloat(element.value);
		var minValue = v;
		var maxValue = v;
			
		if(element.minValue) {
			var dependent = $(element.minValue);
			if(dependent) {
				if(dependent.value != '')
					minValue = dependent.className.match(/vDate/) ? new Date(dependent.value) : parseFloat(dependent.value);
			} else if(element.className.match(/vDate/)) {
				minValue = new Date(element.minValue);
			} else {
				minValue = parseFloat(element.minValue);
			}
		}
		if(element.maxValue) {
			var dependent = $(element.maxValue);
			if(dependent) {
				if(dependent.value != '')
					maxValue = dependent.className.match(/vDate/) ? new Date(dependent.value) : parseFloat(dependent.value);
			} else if(element.className.match(/vDate/)) {
				maxValue = new Date(element.maxValue);
			} else {
				maxValue = parseFloat(element.maxValue);
			}
		}
		
		return v >= minValue && v <= maxValue;
	}
});

// Class (static) properties and methods
Object.extend( Avenet.Forms.Validation, {

	bucket: $A([]),  // a list of all elements that have validators
	errorMorphStyle: { backgroundColor: "#ff8" },    // look of form element when contents are in error
	errorAuxStyle: { margin: "-6px", position: "relative", padding: "5px", border: "solid 1px #dd0" },
	okAuxStyle: { margin: "0px", position: "relative", padding: "0", backgroundColor: "transparent", border: "none" },
	passwordAcceptableStyle: { backgroundColor: "#ffc" },
	passwordStrongStyle: { backgroundColor: "#cfc" },
	
	// return the type of event to respond to for each validation type
	typeToEvent: function( eventType ) {
		switch( eventType ) {
			case 'vReqMenu': return 'change';
			case 'vReq': return 'keyup';
			default: return 'keyup'; break;
		}
	},
	displayServerSideErrors: function( errorsHash, formId ) {
		var errors = $H(errorsHash);
		var forms = $$('form[id!=ContentBrowser]');
		if( errors.keys().length == 0 || (!formId && forms.length==0) ) return; // no errors?  no form? no work
		
		Avenet.Forms.Validation.clearServerSideErrors();

		// Display the error message
		errors.each(function(e,i){ 
			if (typeof e.value !== "function") {
				e.value.each(function(m) {
				    
				    var element;
				    if (formId==null) {
					    var found = false;
					    for(var ix=0;ix<forms.length;ix++){
					        if (!found) {
					            element = $(forms[ix].id + "_" + e.key + "Errors");
					            if (element!=null && element.style.display != 'none') found = true;
					        }
					    };
				    }
					else {
					    element = $(formId + "_" + e.key + "Errors");
					}
				    
					if (element!=null) {
					    var mess = "<ul class='errorDisplay'>";
						    mess += "<li class='vUrl vServerError'>" + m;
						    mess += "</li>";
						    mess += "</ul>";
					    element.update(mess);
					}
				});
			}
		});
		if( errors.keys().length > 0 )
			this.displayFirstAccordionError(formId);
	},
	displayFirstAccordionError: function(formId) {
		if(!formId) return;
		var e = $(formId).down('.errorDisplay');
		if(e != null) {
			//open the panel to display the first error message
			var p = e.up('.accordion_panel');
			if(p != null) { p.open(p); }
			//set focus to the first field with an error
			var s = e.up('.formError');
			var f = $(s.id.substring(0, s.id.length-6));
			if (f!=null) {
				f.focus();
				f.select();
			}
		}
	},
	clearServerSideErrors: function() {
		//$$('.fieldErrorBox').invoke("update", "");
		$$('.vServerError').each(function (e) {e.parentNode.remove();});
	},
	removeTags: function( e ){
		$(e).value = $(e).value.stripTags();
		$(e).validate();
	},
	validateStep: function( theStep ){
		theStep = $(theStep);
		var inputsWithValidations = theStep.select("input, select").select(function(e){
			return typeof(e.validations) != 'undefined' ? true : false;
		});
		if( inputsWithValidations.length == 0 ) return true;
		else {
			var allOK = true;
			inputsWithValidations.each(function(e){
				allOK = e.validate() && allOK;
			});
			return allOK;
		}
	},
	validateContainer: function( container, skipFileUploads ) {
		container = $(container);
		if( typeof( container ) == 'undefined' ) return false;
		if( typeof( skipFileUploads ) == 'undefined' ) skipFileUploads = false;
		
		// save out contents of magic boxen to original field
		if( tinyMCE ) tinyMCE.triggerSave();
		
		var inputsWithValidations = container.select("input, select, textarea").select(function(e) {
			if(skipFileUploads && e.type && e.type == 'file')
				return false;
			else
				return typeof(e.validations) != 'undefined' ? true : false;
		});
		
		if( inputsWithValidations.length == 0 ) return true;
		else {
			var allOK = true;
			inputsWithValidations.each(function(e) {
				allOK = e.validate() && allOK;
			});
			return allOK;
		}
	},
	ElementMethods: {
		// This is a mixin, to be attached to DOM elements that respond to validations
		indicatedOK: true, indicationTimer: null,
		
		validate: function( eventType, caller ){
			var failedValidations = [];
			var ok = this.validations.all( function(v){
				if( typeof( eventType ) != "undefined" ) {
					if( v.respondsTo(eventType) ) {
						if(v.dependent && (typeof(caller) == 'undefined' || v.dependent != caller.e)) v.dependent.validate(eventType, v);
						if( v.isOK() ) return true;
						else {
							failedValidations.push(v);
							return false;
						}
					}
				} else {
					if(v.dependent && (typeof(caller) == 'undefined' || v.dependent != caller.e)) 
						v.dependent.validate(eventType, v);
					if( v.isOK() ) return true;
					else {
						failedValidations.push(v);
						return false;
					}
				}
			});
			if( ok ) this.indicateOK( eventType );
			else this.indicateError( eventType );
			return ok;
		},
		handleValidationEvent: function(e){
			this.validate(e.type);
		},
		cancelEffect: function(e){
			if( this.effect ) this.effect.cancel();
		},
		morphIndicate: function(newStyle, finishFn){
			if( this.effect ) this.effect.cancel();
			this.effect = new Effect.Morph( this.parentNode, {
				style: newStyle, 
				duration: 0.5,
				afterFinish: function(){ if(finishFn) finishFn(); }
			});
		},
		ratePassword: function(){
			this.cancelEffect();
			if( this.value.match(/[^0-9A-Za-z]/) ) this.setStyle( Avenet.Forms.Validation.passwordStrongStyle );
			else this.setStyle( Avenet.Forms.Validation.passwordAcceptableStyle );
		},
		indicateOK: function( eventType ){
			if( this.indicationTimer ) clearTimeout(this.indicationTimer);
			//var errEl = $(this.form.id + '_' + this.name + "Errors");
			var errEl = $(this.id + "Errors");
			
			var passwordValidator = this.validations.find(function(v){return v.type=='vPassword'});
			if( passwordValidator )passwordValidator.e.ratePassword();  //special case
			
			if( !this.indicatedOK ) {
				//errEl.style.height = "0px";
				errEl.update();
				/*this.morphIndicate( this.originalStyle, function() {
					this.parentNode.setStyle(Avenet.Forms.Validation.okAuxStyle);				
				}.bind(this) );*/
				this.parentNode.setStyle(Avenet.Forms.Validation.okAuxStyle);	
			}

			this.indicatedOK = true;
		},
		indicateError: function( eventType ){
			if( this.indicationTimer ) clearTimeout(this.indicationTimer);

			this.indicationTimer = setTimeout( function(){
				var mess = "<ul class='errorDisplay'>";
				//var errEl = $(this.form.id + '_' + this.name + "Errors");
				var errEl = $(this.id + "Errors");
				
				if( this.indicatedOK ) {
					errEl.style.height = "auto";
				
					this.parentNode.setStyle(Avenet.Forms.Validation.errorAuxStyle);
					this.morphIndicate( Avenet.Forms.Validation.errorMorphStyle );
				}
				var errHash = new Hash();
				this.validations.each(function(v){
					if( !v.isOK() ) {
						if(errHash.get(v.type) == undefined)
						{
							var errorMessages = {
								vEmail: lpack.ValidationEmail,
								vZip: lpack.ValidationZip,
								vUserName: lpack.ValidationUserName,
								vMatch: lpack.ValidationMatch,
								vReq: lpack.ValidationReq,
								vReqMenu: lpack.ValidationReqMenu,
								vDate: lpack.ValidationDate,
								vPassword: lpack.ValidationPassword,
								vNoHTML: lpack.ValidationNoHTML,
								vInteger: lpack.ValidationInteger,
								vDecimal: lpack.ValidationDecimal,
								vRange: lpack.ValidationRange,
								vUrl: lpack.ValidationUrl,
								vUrlTitle: lpack.ValidationUrlTitle,
								vFileName: lpack.ValidationFileName
							};
							
							var errorMessage = (errorMessages[v.type]) || eval('lpack.' + v.e.name + 'ValidationMessage');
	
							mess += "<li class='e" + v.type + "'>" + errorMessage;
							if( v.type == 'vNoHTML' ) mess += ' <a href="javascript:Avenet.Forms.Validation.removeTags(\'' + v.e.id + '\')">' + lpack.ValidationClickHere + '</a>';
							mess += "</li>";
							errHash.set(v.type, v.type);
						}
					}
				});
				mess += "</ul>";
				
				errEl.update(mess);
				this.indicatedOK = false;
				
				// if we're in an accordion or a tab set, open up the proper pane
				var parentPanel = $(this).up('.accordion_panel');
				if( parentPanel != null && !parentPanel.content.visible() ) parentPanel.open();
				else if(parentPanel == null) {
					var parentTab = $(this).up('.group');
					if(parentTab != null) {
					    var isSet = this.setTabFocus(parentTab);
					    if(!isSet) {
						    parentTab = parentTab.up('.group');
						    if(parentTab != null) this.setTabFocus(parentTab);
					    }
					}
				}
			}.bind(this), 3000);
		},
		
		setTabFocus: function(parentTab) {
		    var retValue = false;
			var tabDiv = parentTab.previous('.tabDiv');
			if(tabDiv != null) {
				var tabContainer = tabDiv.down('.tabContainer');
				if(tabContainer != null && tabContainer.tab != null) {
				    tabContainer.tab.setActiveTab(parentTab.id);
				    retValue = true;
				}
			}
			return retValue;
		}
	}
});


// This class is filled with hatred, and filthiness.  It must be reformed!
Avenet.Forms.ActorCondition = Class.create({
	condition: null, element: null,
	initialize: function( element, condition ) {
		this.condition = condition;
		this.element = element;
	},
	isMet: function() {
		var v;
		var e = $(this.element);
		if( e.hasClassName('list') && e.type != 'select-one' ) { // this means the "element" is really a group of checks or radios
			var form = e.up('form');
			var type = form[e.id.split('_')[1]][0].type;
			v = $F(form.getInputs(type, this.element.split('_')[1]).find(function(e){return e.checked; }));
		}
		else v = $F(e);
		return (v == this.condition);
	}
});



// add the summarize method to prototype's existing Form class
// this, er, doesn't really have the sauciness that was intended.  but it works for now.
Object.extend( Form.Methods, {
	summarize: function(form, summaryArea){
		var formData = form.serialize(true);
		var result = "<table>";
		for(var i = 0; i < formData.length; i++) {
			var e = $(form.id + '_' + formData[i].Key)
			var label = '';
			var data = '';
			if(e) {
				if(e.hasClassName('line')) {
					if($(form.id + "_" + formData[i].Key + "Label") != null)
						label = $(form.id + "_" + formData[i].Key + "Label").innerHTML;
					// Mask the password or confirm password data
					if(formData[i].Key == 'Password' || formData[i].Key == 'ConfirmPassword') {
						label = '';
					}
					else data = formData[i].Value;
				}
				else if(e.hasClassName('list')) {
					if($(form.id + "_" + formData[i].Key + "Label") != null) {
						label = $(form.id + "_" + formData[i].Key + "Label").innerHTML;
						
					}
					
					var option = e.select('input[value="' + formData[i].Value + '"]')[0];
					if(option) {
						data = $(option.id + "Label").innerHTML;
					} else {
						option = e.select('option[value="' + formData[i].Value + '"]')[0];
						if(option) {
							data = option.text;
						}
					}
					if(!label) label = data;
					//label = $(form.id + "_" + formData[i].Value + "Label").innerHTML;
				
					if(!data) data = formData[i].Value;
				}
			}
			
			if(!data) data = lpack.EmptySummary;
			if(label)
				result += "<tr><td><span style='font-weight:bold'>" + label + "</span>:</td><td>" + data + "</td></tr>";
		}
		result += "</table>";
		$(summaryArea).update(result);
	}
});



// ACTION CLASS!!!

Avenet.Forms.Action = Class.create( Avenet.Forms.FormElementActor, {
	initialize: function( $super, element, type ){
		$super( element, type );
		
		// special case: if this is an option, the event recipient is the SELECT, not the OPTION
		if( this.e.match('option') ) this.e = this.e.up('select');
		
		// first action attached to this element?
		if( !this.e.actions ) {
			this.e.actions = $A([]);
			Object.extend(this.e, Avenet.Forms.Action.ElementMethods );
		}

		// if this is a trigger, attach event handlers to partner, also
		if( this.type == "aTrigger" ) 
			Object.extend(this.partner, Avenet.Forms.Action.ElementMethods );
		
		this.e.actions.push( this );
		
		if( this.type == "aSummarize" )
			Avenet.Forms.Action.registerActivateCallback( this.e, this.handleSummarize.bind(this) ); // ELIMINATED ID
		else
			Event.observe( this.e, Avenet.Forms.Action.typeToEvent(this.type),this.e.handleActionEvent );
	},
	respondsTo: function( anEvent ) {
		return ( Avenet.Forms.Action.typeToEvent(this.type) == anEvent );
	},
	handleTrigger: function() {
		if( this.distillBoolean() ) this.partner.show();
		else this.partner.hide();
	},
	handleClear: function() {
		this.partner.value = "";
	},
	handleStepButton: function() {
		var whichWay = (this.type == 'aNextStep' ? 1 : -1);
		if( whichWay == -1 || (  whichWay == 1 && Avenet.Forms.Validation.validateStep(this.e.up('.step'))  ) )
			Avenet.Forms.enableStep( Avenet.Forms.Action.findStepNumber(this.e) + whichWay, whichWay );
		return false;
	},
	handleSubmit: function() {
		var formContents = this.e.up('form').serialize( true );
		var formContainer = this.e.up('.formContainer');
		
		var callbackFunction = this.e.statusCallback;
		
		//formContents.push({ Key: 'eFolioUrl', Value : document.body.down('.eFolioUrl').value});
		formContents.push({ Key: 'migration', Value : document.body.down('.migration').value});
		formContents.push({ Key: 'sid', Value : document.body.down('.sid').value});
		
		new Ajax.Request(basePath + 'Services/SignUpService.svc/Register', {     // WARNING: hard-coded
			postBody: $JSON({hash: formContents}),
			formId: formContainer.down('form').id,
			onSuccess: function ( transport ) {
				var response = transport.responseText.evalJSON(true);
				if( response.Status == true ) {
					formContainer.update(response.SuccessMessage);
				}
				
				if (callbackFunction != "undefined") {
					callbackFunction(response);
				}
			},
			onFailure: function( transport ) {
				$('loadingStatus').update(lpack.FormElementActorSumitError);
			}
		});
		return false;
	},
	handleSummarize: function(){
		var f = this.e.up('form');
		Form.Methods.summarize(f, this.partner);
	},
	handleCall: function(e){
		var theCall = eval(this.parameters[0]);
		if( theCall ) theCall.bind(this)();
		//e.preventDefault();
		//return false;
	}
});

// Class (static) methods and whatnot
Object.extend( Avenet.Forms.Action, {
	// return the type of event to respond to for each validation type
	typeToEvent: function( eventType ) {
		switch( eventType ) {
			case 'aNextStep': case 'aBackStep': case 'aSubmit': case 'aCall':
				return 'click'; break;
			case 'aTrigger': return Prototype.Browser.IE ? 'click' : 'change'; break;
			default: return 'keyup'; break;
		}
	},
	// for stepped forms
	findStepNumber: function( e ){
		var button = $(e);
		return Try.these(
			function() { return parseInt(button.id.match(/step(\d+)(Back|Next)Button/)[1]) },
			function() { return parseInt(button.up(".step").id.match(/step(\d+)/)[1]) }
		) || false;
	},
	registerActivateCallback: function(which, handler){
		which = $(which);
		if( which ) which.onActivate = handler;
	},
	// mixin to be attached to elements that respond to actions
	ElementMethods: {
		handleActionEvent: function(e){
			this.actions.each( function( action ){
				if( action.respondsTo(e.type) )
					switch( action.type ) {
						case "aTrigger": action.handleTrigger(); break;
						case "aClear": action.handleClear(); break;
						case "aNextStep": case "aBackStep": action.handleStepButton(); break;
						case "aSubmit": action.handleSubmit(); break;
						case "aCall": action.handleCall(); break;
					}
			});
		},
		// Methods to be attached to DOM objects
		showElement: function() {
			if( this.style.display != "block" ) this.style.display = "block";
		},
		hideElement: function() {
			if( this.style.display != "none" ) this.style.display = "none";
		}
	}
});



// Functions in the Avenet.Forms namespace

var loadingGraphic = null;

// Note: you will be hunted down and killed if you touch fetchForm. DON'T DO IT.
Object.extend( Avenet.Forms, {
	// Someday, we'll rewrite the wretched fetch form...
	
	fetchForm2: function(formName, formContainer, formOptions) {
	},
	
	fetchForm: function( formName, formContainer, formOptions ) {
		var options = {
			contentId: null,
			tempFormLocation: false,
			tempLanguageLocation: false,
			callbackScope: null,
			onFailure: null,
			onSuccess: null,
			beforePrepareForm: null,
			afterPrepareForm: null,
			formDisplayedCallback: null,
			languagePack: null,
			enableAutoSave: false,
			onAutoSaveStarted: null,
			disabledByDefault: true,
			isContentForm: false,
			elToShow: $(formContainer),
			formHtml: null,
			contentObject: null, //Avenet.Forms.getFormContent,
			taskListId: null,
			ignoreDefaultShell: false,
			showForm: true,
			help: null,
			reportView: false
		};
		
		Object.extend(options, formOptions || { });
		options.languagePack = options.languagePack == '' ? null : options.languagePack;
	
		formContainer = $(formContainer);
		formContainer.autoSaver = null;
		formContainer.autoSaveValues = null;
		formContainer.taskListId = options.taskListId;
		formContainer.reportView = options.reportView;
		
		// unload any existing magic boxen
		Avenet.Forms.unloadMagicBoxes(formContainer);
		
		// empty and hide the form container
		formContainer.update();
		options.elToShow.setStyle({ opacity: 0.0 });
		
		// show loading graphic
		if (options.showForm)
			Avenet.Forms.showLoadingGraphic( formContainer ); //check this in ie on hidden div tags. style="display:none;"

		function processForm(formHtml, transport) {
			formContainer.NewTags = formContainer.ContentTags = null;
				
			// if we're already inside a form, strip out the surrounding form tag from the XSL output
			if( formContainer.up('form') ) 
				formContainer.update(Avenet.Forms.stripFormTag(formHtml));
			else 
				formContainer.update(formHtml);
			
			var formContent = null;
			
			if (options.contentId != null) {
				var postBody = { contentId:options.contentId };
				
				if (options.requestContentOptions) Object.extend(postBody, options.requestContentOptions);
				
				new Ajax.Request( Avenet.ServiceUrls.contentService + "/RequestContent",{
					formId: formName,
					postBody: $JSON(postBody),
					asynchronous: false,
					onSuccess: function ( transport) {
						response = transport.responseText.evalJSON(true);
						formContainer.values = response.ResponseObject;
						if(response.ResponseObject["autoSaveObj"] || response.ResponseObject["autoSaveObj"] != null) {
							formContainer.autoSaveValues = response.ResponseObject["autoSaveObj"];
						}
						else {
							formContainer.autoSaveValues = null;
						}
					}
				});
			}
			else if (options.enableAutoSave) {
				new Ajax.Request( Avenet.ServiceUrls.contentService + '/RequestAutosaveContent',{
				postBody: $JSON({ formId: formContainer.down('form').id, contentId: null }),
				asynchronous: false,
				onSuccess: function ( transport) {
				    response = transport.responseText.evalJSON(true);
				    formContainer.values = Avenet.Forms.findForm(formContainer).serialize(true);
				    if(response.Status) {
					    formContainer.autoSaveValues = response.ResponseObject;	
					}
					else {
						formContainer.autoSaveValues = null;
					}
				}
				});
			}
				
			// if contentObject is a function, execute it and get the data, otherwise, it IS the data

			bindAutoSave = false;
			if (Object.isFunction(options.contentObject)) 
				options.contentObject( options, formName, processFormData );
			else if (options.contentObject != null) {
			    processFormData(options.contentObject, transport);
		    }
			else {
			    if(formContainer.autoSaveValues != null) {
			        bindAutoSave = true;
				    processFormData( formContainer.autoSaveValues, transport );
				 }
			    else {
			        processFormData( formContainer.values, transport );
			    }
			}
			
			function processFormData(formContent, transport) {
				if( options.preview ) {/* disable all server callback buttons, links heere. */ }
				
				if( Object.isFunction(options.beforePrepareForm) )
					options.beforePrepareForm.call(options.callbackScope, formContainer);

				Avenet.Forms.prepareForm(formContainer, options.disabledByDefault);
				
				if( Object.isFunction(options.afterPrepareForm) )
					options.afterPrepareForm.call(options.callbackScope, formContainer);
				
				if (formContent != null) {
					Avenet.Forms.bindValues( formContainer, formContent );
					
					if (options.enableAutoSave) {
						formContainer.autoSaveValues = Avenet.Forms.findForm(formContainer).serialize(true);
					}
				}
				
				if( Object.isFunction(options.onSuccess) )
					options.onSuccess.call(options.callbackScope, formHtml, transport);
				
				if (options.showForm) {
					setTimeout(function() {
						options.elToShow.enableFirstStep = formContainer.enableFirstStep; 
						Avenet.Forms.showForm(options.elToShow, options.disabledByDefault, function() { 
							if (options.formDisplayedCallback)
								options.formDisplayedCallback();
							Avenet.Forms.destroyLoadingGraphic();
						});
					}, 10);  // setTimeout is to fix IE glitch
				}
			}
			
			//formContainer.values = Avenet.Forms.findForm(formContainer).serialize(true); //make the content available for the form.			
			if(options.enableAutoSave) { //autosave has been enabled, attatch it here
				Avenet.Forms.initAutoSave(formContainer, formName, options.contentId);
				if (options.onAutoSaveStarted) {
					if (bindAutoSave)
						options.onAutoSaveStarted(true);
					else
						options.onAutoSaveStarted(false);
				}
			}
		};

		if (options.formHtml) {
			processForm(options.formHtml);
		}
		else {		
		// retrieve the form
			new Ajax.Request( Avenet.ServiceUrls.formsService + '/RequestForm',{
				postBody: $JSON({
					form: formName, 
					tempFormLocation: options.tempFormLocation, 
					tempLanguageLocation: options.tempLanguageLocation, 
					languagePack: options.languagePack,
					ignoreDefaultShell: options.ignoreDefaultShell,
					reportView: options.reportView
				}),
				allowRequestOnAwaitingForReauthenticate: true,
				onFailure: function( transport ) {
					var response = transport.responseText.evalJSON(true);
					Avenet.Forms.destroyLoadingGraphic(formContainer);
					alert(response.Message);
					if (options.onFailure != null) {
						options.onFailure(options.callbackScope, transport);
					}
				},
				onSuccess: function ( transport ) {
					Avenet.Forms.currentForm = formName;
					
					var response = transport.responseText.evalJSON(true);
					var formHtml = response.SuccessMessage; //the html of the form.
					if(response.FormTitle != undefined)
						formContainer.FormTitle = response.FormTitle;
					else
						formContainer.FormName = '';
				    formContainer.enableFirstStep = true;
					if(response.ResponseObject.FormTypeId == 30) formContainer.enableFirstStep = false;
					formContainer.FormId = response.ResponseObject.FormId;
					// This should be in the onSuccess callback and not here. For now, prevent it from breaking when used
					//  outside of the adminui.
					if (typeof(ui) != "undefined" && typeof(ui.editor) != "undefined") {
						if(options.isContentForm && ui) { 
							ui.editor.workspace.currentForm = response.ResponseObject;
						}//the actual form object. //or should this be attatched to the form container (>1 forms on a page?)
						
						if(options.help != null) {
						    options.help.addItem(response.Help);
						}
						else if(!options.isPopupForm) {
							// This statement really doesn't belong here...
							ui.editor.help.addItem(response.Help);
						}
					}
					
					processForm(formHtml, transport);
				}
			});
		}
	},
	
	initAutoSave: function(formContainer, formId, contentId) { 
		formContainer.autoSaver = new Avenet.Forms.AutoSave(formContainer);
		formContainer.autoSaver.start(30, formId, contentId);
	},
	
	stopAutoSave: function(formContainer) {
		container = $(formContainer);
		if(container.autoSaver != null)
			container.autoSaver.stop();
	},
	
	setFormTitle: function(formName, formTitleElement) {
		var element = formTitleElement;
		if (typeof formTitleElement == "string") element = $(formTitleElement);
		if (element != null) element.innerHTML = formName;
	},
	prepareForm: function( formContainer, accordionDisabledByDefault ){
		formContainer = $(formContainer);
		Avenet.Forms.FormElementActor.createActors(Avenet.Forms.findForm(formContainer));
		
		//Avenet.Forms.loadAccordions(formContainer.id, accordionDisabledByDefault);
		Avenet.Forms.loadTabs(formContainer);
		Avenet.Forms.loadGrids(formContainer);
		Avenet.Forms.loadLinkBuilders(formContainer);
		Avenet.Forms.loadMagicBoxes(formContainer);
		Avenet.Forms.loadRatings(formContainer);
		Avenet.Forms.loadFeedbackControl(formContainer);
		Avenet.Forms.adjustTabIndex(formContainer);
	},
	adjustTabIndex: function( formContainer ) {
		formContainer.select('input,a,button,textarea,select').each(function(element) {
			if (element.hasClassName('magicBox')) {
				var iframeId = element.id + "_ifr";
				var iframe = formContainer.select("#" + iframeId);
				
				if (iframe.length && element.tabIndex) {
					iframe[0].tabIndex = element.tabIndex * 10;
				}
			}
			else {
				if (element.tabIndex) {
					element.tabIndex *= 10;
				}
			}
		});
	},
	showForm: function( formContainer, accordionDisabledByDefault, finishCallback ) {
		formContainer = $(formContainer);
		
		formContainer.effect = new Effect.Appear(formContainer, {
			duration: 0.1, 
			afterFinish: function() {
				var inputControls = formContainer.select('input,textarea,button,select');
				
				if (inputControls.length) {
					if (inputControls[0].tagName === 'INPUT') {
						setTimeout(function foo() {
							var formId = inputControls[0].form.id.toLowerCase()
							if(formId != 'url' && formId != 'signupmn' && formId != 'signuppa' && formId != 'tag') { // TODO: Fix - This is a temporary fix since the logic below causes the "URL Type" field not not be displayed correctly in FF.
								try {
								    inputControls[0].focus();
    								inputControls[0].select();
    						    } catch (e) {}
							}
						}, 50);
					}
				}
				
				finishCallback ? finishCallback() : Avenet.Forms.destroyLoadingGraphic();
			}, 
			queue:'end'
		});
		
		Avenet.Forms.loadAccordions(formContainer, accordionDisabledByDefault);
	},
	bindValues: function(formId, hash){
		var formContainer = $(formId);
		formContainer.getElementsBySelector('input', 'select', 'textarea').each( function(b) {
			var value = hash.hasOwnProperty(b.name) ? hash[b.name] : null;
			//if(value != null) { //revert still needs to validate if these are null.
				switch(b.type) {
					case 'text': case 'textarea': case 'password': //case 'select-one':
							b.value = (value) ? value : '';
						break;
					case 'select-one':
						var count = 0;
						b.selectedIndex = 0;
						if(value != null) {
							b.childElements().each(function (o) {
								if(value == o.value) { 
									b.selectedIndex = count; 
									if(o.value == 'other' && o.hasClassName('otherItem')) $(b.id + 'OtherRow').show();
									
								}
								
								count++;
							});
						}
						break;
					case 'checkbox': case 'radio':
						if(value == null) { b.checked = false; }
						else { b.checked = (value.toString().split(',').indexOf(b.value) > -1) ? true : false; }
						
						if(b.checked && b.actions) {
							b.actions.each(function(a) {
								if(a.type == 'aTrigger') a.handleTrigger();
							});
						}
						//if(b.actions && b.actions.length > 0) {
						//	b.actions.each(function(a) {
						//	if(a.type == 'aTrigger') a.handleTrigger();
						//	});
						//}
						break;
				}
			//}
		});
		
		formContainer.select('.linkbuilder').each(function(b) { //linkbuilders
			b.content = null;
			field = b.id.split("_")[1];
			var v = hash.hasOwnProperty(field) ? hash[field]: null;
			if(!Object.isUndefined(v) && Object.isArray(v)) { 
				b.content = v;
				b.lb.bindValues();
			}
			
			if(b.hasClassName('requiresItem') && b.content == null && (formContainer.id != 'buildCenterColumn' || formContainer.reportView)) { //
			
			    Element.classNames(b).each(function(cn) {
			        if(cn.startsWith('clusterToDisplay__')) {
			            var id = '#' + formContainer.FormId + '_' + cn.split('Display__')[1];
			            
			            //remove the rubric for unanswered linkbuilders.
			            var els = formContainer.select(id)
			            if(els.length == 1) els[0].hide();
			            
			            //remove the linkbuilder as well.
			            var currentRow = b.up('.row');
			            currentRow.hide();
			            currentRow.previousSiblings()[0].hide();
			        }  
			    });
			}
		});
		
		var feedbackEl = formContainer.down('.feedbackControl');
		
		if (feedbackEl != null && formContainer.values) {
			// Find meh some feedbacks...
			var contentFeedback = formContainer.values.hasOwnProperty('ContentFeedback') ? formContainer.values.ContentFeedback : null;
			feedbackEl.control.bindValues(contentFeedback);
		}
		
		var theForm = formContainer.getElementsBySelector('form').first(); //output static text values from hash.
		formContainer.select('.literal').each(function(b) {
			var key = b.id.replace(theForm.id + '_', '');
			var value = hash.hasOwnProperty(key) ? hash[key] : null;
			if(value != null) {
				b.update(value);
			}
		});
		
		formContainer.select('.image').each(function(b) { //upload files.
			var key = b.id.replace(theForm.id + '_', '').replace('Thumbnail', '');
			var value = hash.hasOwnProperty(key) ? hash[key] : null;
			if(value != null && value.match(/(.gif)|(.jpg)|(.jpeg)|(.png)$/i)) {
				b.select('.uploadImage').each(function (c) { c.remove(); });
				
				var img = new Element('img', {'class': 'uploadImage', 'src': basePath + 'Uploads/' + value.substring(0, value.lastIndexOf('.')) + '_thumb' + value.substring(value.lastIndexOf('.')) + '?r=' + Math.floor(Math.random()*1000), 'style': 'width: 80px; height: 80px;'});
				b.appendChild(img);
			}
		});
		
		formContainer.NewTags = null;
		formContainer.ContentTags = hash.hasOwnProperty('ContentTags') ? hash['ContentTags'] : null;
		
		//Avenet.Forms.loadLinkBuilders(formContainer);
	},
	showLoadingGraphic: function( formContainer ) {
		formContainer = $(formContainer);
		if( loadingGraphic ) return;
		if( !formContainer.visible() || formContainer.ancestors().any(function(a){ return !a.visible(); }) )
			formContainer = document.body;
		
		var theImage = $(document.createElement('img'));
		theImage.src = basePath + 'images/ajaxLoader.gif';

		var imageWidth = 32;
		var x = (formContainer.getDimensions().width / 2) - (imageWidth / 2) - 3;
		x += formContainer.cumulativeOffset().left;
		var y = formContainer.cumulativeOffset().top + 20;
		
		theImage.setStyle({
			position: 'absolute',
			left: x + 'px',
			top: y + 'px',
			backgroundColor: 'white',
			border: 'solid 1px #ccc',
			padding: '2px',
			opacity: 0.5
		});
		document.body.appendChild( theImage );
		return loadingGraphic = theImage;
	},
	destroyLoadingGraphic: function( formContainer ) {
		if( loadingGraphic ) {
			loadingGraphic.parentNode.removeChild(loadingGraphic);
			delete loadingGraphic;
			loadingGraphic = null;
		}
	},
	stripFormTag: function( formHTML, formContainer ) {
		// strip out some gunk
		formHTML = formHTML.replace(/[\n\r]/g, "");
		// match the part inside the form tag
		var matches = /^<form[^>]*>(.*)<\/form>/m.exec(formHTML);
		if( matches != null && matches.length > 1 ) return matches[1];
		return formHTML;
	},
	// transparently find a form (optionally of class 'cssClass') either surrounding or inside a form container
	findForm: function(formContainer, cssClass){
		typeof cssClass == "undefined" ? cssClass = "" : cssClass = "." + cssClass;
		if( $(formContainer).up('form' + cssClass) ) return $(formContainer).up('form' + cssClass);
		else return $(formContainer).down('form' + cssClass);
	},
	// for accordions only
	enableStep: function( theStep, whichWay ) {
	    if ($( 'step' + theStep).style.display == 'none')
	        if (theStep == 1) theStep = 2;
	        else theStep = theStep + whichWay;
		$( 'step' + theStep ).enable().open();
	},
	loadFeedbackControl: function(formContainer) {
		var feedbackEl = $(formContainer).down('.feedbackControl');
		
		if (feedbackEl) {
			new Avenet.eFolio.Admin.FeedbackControl(feedbackEl);
		}
	},
	
	loadRatings: function(formContainer) {
		$(formContainer).select('.ratingsContainer').each(function(el) {
			var r = new Rating(el, {});
		});
	},
	loadAccordions: function( formContainer, disabledByDefault) {
		var accordions = $(formContainer).select('.accordion');
		var options = { disabledByDefault:disabledByDefault };
		if( accordions.length > 0 ) {
			accordions.each(function(accordion) {
				var newOptions = options;
				if( accordion.up('form[id^="Signup"]') || accordion.up('form[id^="UpgradeSignup"]') )
					Object.extend( newOptions, { fixedHeight:true } );
				var a = new Accordion(accordion, newOptions);
			});
			if(formContainer.enableFirstStep) Avenet.Forms.enableStep(1);
		}
	},
	loadLinkBuilders: function(formContainer) {
		$(formContainer).select('.linkBuilderFrame').each(function(lb) {
			new Avenet.eFolio.Admin.LinkBuilder(lb);
		});
	},
	loadTabs: function( formContainer) {
		$(formContainer).select('.tabContainer').each(function(tab) {
			tab.tab = new Control.Tabs(tab);
			$(formContainer).tab = tab
		});
	},
	
	loadGrids: function( formContainer ) {
		$(formContainer).select('.datalistgrid').each(function(gridDiv) {
			var records = [];
			var columns = [];
	
			var dataType = null;
			
			$w(gridDiv.className).each(function(classPart) {
				// link builder type starts with type_
				if (classPart.indexOf('type_') > -1) {
					dataType = classPart.substr(5);
				}
			});
			
			/*
			var typeDef = Avenet.eFolio.DataViews[dataType].LinkBuilderGrid;
			$H(typeDef).each(function(pair) {
				records.push({ name: pair.key });
				columns.push({ header: pair.value, width: 100, sortable: true, dataIndex: pair.key });
			});
			*/
			records.push({name: 'Title'});
			columns.push({ header: 'Title', width: 100, sortable: true, dataIndex: 'Title' });
			records.push({name: 'Summary'});
			columns.push({ header: 'Summary', width: 100, sortable: true, dataIndex: 'Summary' });
			
			var record = Ext.data.Record.create(records);
			var reader = new Ext.data.JsonReader({
				root: 'ResponseObject'
			}, record);
			
			var grid = new Ext.grid.GridPanel({
				store: new Ext.data.Store({
					reader: reader
				}),
				columns: columns,
				id: gridDiv.id + '_grid',
				autoHeight: true,
				renderTo: gridDiv,
				border: false
			});
			
			/* test code for grid, please leave
			var data = {};
			data[reader.meta.root] = [];
			for (j = 0 ; j < 5; j++) {
				var r = {};
				
				for (k = 0; k < columns.length; k++) {
					r[columns[k].dataIndex] = "Test";	
				}
				
				data[reader.meta.root].push(r);
			}
			
			grid.getStore().loadData(data);*/
		});
	},
	unloadMagicBoxes: function( formContainer, popupContainer ) {
		popupContainer = (popupContainer)||false;
		if( typeof(tinyMCE) != 'undefined' )
			formContainer.select('.magicBox').each(function(mb){
				if(popupContainer) {
					// This is necessary to handle the magic box in a popup in IE7.
					var parent = $(mb.id);
					
					// This is a special case when the tab containing the magic box(es) is not currently visible.
					if (parent.up('div.group') != null) {
						parent.up('div.group').show();
					}
			
					try {
						parent.show().focus();
						parent.hide();
					}
					catch(e) {
						// This is failing on t
					}
				}

				try {
				tinyMCE.remove(tinyMCE.get(mb.id));
				} 
				catch (e) {
				}
				//mb.parentNode.select('.mceEditor').each(function(editor){ editor.remove(); });
			});
	},

	loadMagicBoxes: function( formContainer ) {
		if( typeof(tinyMCE) != 'undefined' )
			formContainer.select('.magicBox').each(function(mb) {
				tinyMCE.init({
					mode : "exact",
					elements: mb.id,
					theme: "advanced",
					plugins : "spellchecker,paste,table,media",
					theme_advanced_buttons1 : "bold,italic,strikethrough,forecolorpicker,backcolorpicker,justifyleft,justifycenter,justifyright,bullist,numlist,sub,sup,link,unlink",
					theme_advanced_buttons2 : "fontsizeselect,fontselect,charmap,table,pastetext,pasteword,spellchecker,code",
					theme_advanced_buttons3 : "",
					width: "99%",
					height: mb.getHeight(),
					init_instance_callback : "Avenet.Forms.setupEventHandlers",
					theme_advanced_toolbar_location : "top",
					spellchecker_languages : "+English=en"
				});
				//mb.parentNode.select('.mceEditor').each(function(editor){ editor.show(); });
			});
			
	},
	
	hideMyButtons: function(editorInstance) {
		var containerEl = editorInstance.getContainer()
		Avenet.Forms.hideElement($(containerEl).select('tr[class="mceFirst"]')[0]);
	},

	hideElement: function(element) {
		element.style.display = 'none';
	},

	showMyButtons: function(editorInstance) {
		var containerEl = editorInstance.getContainer()
		Avenet.Forms.showElement($(containerEl).select('tr[class="mceFirst"]')[0]);
		
		$H(tinymce.EditorManager.editors).each(function(pair) {
			 if (editorInstance != pair.value) {
				Avenet.Forms.hideMyButtons(pair.value);
			 }
		});
	},

	showElement: function(element) {
		if(tinymce.isIE) {
			element.style.display = 'block';
		} else {
			element.style.display = 'table-row';
		}
	},

	setupEventHandlers: function(editorInstance) {
		tinymce.dom.Event.add(editorInstance.settings.content_editable ? editorInstance.getBody() : (tinymce.isGecko ? editorInstance.getDoc() : editorInstance.getWin()), 'focus', function(e) {
			Avenet.Forms.showMyButtons(editorInstance);
			if (typeof(ui) != 'undefined' && ui.editor) ui.editor.help.setFocus(editorInstance.editorId.split('_')[1]);
		});
	},
	
	/* modal form dialogs */
	
	showModalForm: function( formName, formLoadedCallback ) {
		if (!Avenet.Forms.modalForm) {
			var formDiv = $(document.createElement('div'));
			formDiv.className = "modalDialog";
			document.body.appendChild(formDiv);
			
			Avenet.Forms.fetchForm( formName, formDiv, { 
				onSuccess: function() {
					var adminPopup = $('adminPopup');
					if(adminPopup.instanceCount && adminPopup.instanceCount > 0) {
						Ext.get($('adminPopup_' + adminPopup.instanceCount)).mask();
					} else {			
						Ext.get(document.body).mask();
					}
			
					var inputs = formDiv.select('input');
					if (inputs.length != 0) inputs[0].focus();
					if(typeof(formLoadedCallback) != 'undefined' && Object.isFunction(formLoadedCallback)) {
						formLoadedCallback();
					}
				}.bind(this)
			});
			
			Avenet.Forms.modalForm = formDiv;
		}
	},
	clearModalForm: function() {
		if (Avenet.Forms.modalForm != null) {
			Avenet.Forms.modalForm.parentNode.removeChild(Avenet.Forms.modalForm);
			Avenet.Forms.modalForm = null;
			
			var adminPopup = $('adminPopup');
			if(adminPopup.instanceCount && adminPopup.instanceCount > 0) {
				Ext.get($('adminPopup_' + adminPopup.instanceCount)).unmask();
			} else {
				Ext.get(document.body).unmask();
			}
		}
	},
	redirectClient: function(url) {
		window.location.href = url.replace(/^(~\/)/, basePath);
	},
	
	autoSaveContent: function(formContainer, formId, contentId, successCallback, failureCallback) {
		// Turned off for now...
//		if (false) {
			var formContents = Avenet.Forms.findForm(formContainer).serialize(true);
			
			//if(!contentId.match(Avenet.Forms.guidRegex())) contentId = null;
			if(formId == contentId) contentId = null;

			new Ajax.Request(Avenet.ServiceUrls.contentService + '/AutosaveContent', {
				postBody: $JSON({
					formId: formId,
					contentId: contentId,
					hash:formContents
				}),
				onSuccess: function ( transport ) {
					var response = transport.responseText.evalJSON(true);
					if( response.Status == true && typeof(successCallback) != "undefined" )
						successCallback(response.SuccessMessage);
					else if(response.Status == false && typeof(failureCallback) != "undefined" )
						failureCallback(response.SuccessMessage);
				},
				onFailure: function( transport ) {
					var response = transport.responseText.evalJSON(true);
					if( typeof(failureCallback) != "undefined" ) failureCallback(response.Message);
				}
			});
//		}
	},
	
	guidRegex: function() {
		return re = new RegExp(/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})/);
	},
	
	isFormValid: function(formContainer, id) {
		if(typeof(id) == 'undefined') id = '';
		if(!Avenet.Forms.Validation.validateContainer(formContainer, id.match(Avenet.Forms.guidRegex()))) 
			return false;
		else
			return true;
	},
	
	saveContent: function( formContainer, id, successCallback, failureCallback, mergeContents, deleteAutoSave, otherSaveParameters ) {
		if (!Avenet.Forms.isFormValid(formContainer, id)) {
			return false;
		}
		var saveContentFunc = function(artifactId) {
			if (typeof deleteAutoSave == "undefined") deleteAutoSave = false;
			
			formContents = Avenet.Forms.findForm(formContainer).serialize(true);
			
			// You can pass in a json object to merge into the serialize results. content suggestions does this
			//  to add back in values that don't appear on the form.
			if (mergeContents) {
				var t = unpackDictionary(formContents);
				t = $H(mergeContents).merge(t);
				formContents = packDictionary(t._object);
			}
			
			formContainer = $(formContainer);
			
			var service;
			if(id.match(Avenet.Forms.guidRegex())) {
				service = Avenet.ServiceUrls.contentService + '/UpdateContent';
			} else {
				service = Avenet.ServiceUrls.contentService + '/InsertContent';
			}
			
			//these keys cannot be a form field.
			
			formContents.push({Key: 'ArtifactId', Value: ((typeof(artifactId) != 'undefined') ? artifactId : null)});
			formContents.push({Key: 'QCategoryId', Value: ((typeof(formContainer.categoryId) != 'undefined') ? formContainer.categoryId : null)});
			formContents.push({Key: 'NewTags', Value: ((typeof(formContainer.NewTags) != 'undefined') ? formContainer.NewTags : null)});
			formContents.push({Key: 'ContentTags', Value: ((typeof(formContainer.ContentTags) != 'undefined') ? formContainer.ContentTags : null)});
			if(typeof(formContainer.taskListId) != 'undefined' && formContainer.taskListId != null)
				formContents.push({Key: 'TaskListId', Value: formContainer.taskListId });
			
			var saveParams =  {id:id, hash:formContents, deleteAutoSave: deleteAutoSave};
			
			if (otherSaveParameters) Object.extend(saveParams, otherSaveParameters);
			
			new Ajax.Request(service, {
				postBody: $JSON(saveParams),
				asynchronous: false,
				formId: this.currentForm,
				onSuccess: function ( transport ) {
					var response = transport.responseText.evalJSON(true);
					if( response.Status == true && typeof(successCallback) != "undefined" )
					  successCallback(response); //SuccessMessage
					else if(response.Status == false && typeof(failureCallback) != "undefined" )
					  failureCallback(response.FailureMessage); //FailureMessage
				},
				onFailure: function( transport ) {
					var response = transport.responseJSON;
					if( typeof(failureCallback) != "undefined" ) failureCallback(response.FailureMessage);
				}
			});
		}.bind(this);
		
		var fileUploads = $(formContainer).select('input[type="file"]');
		if(fileUploads != null && fileUploads.length > 0 && fileUploads.any(function(i) { return i.value.length > 0; })) {
			var iframeId = 'uploadTarget';
			if(!$(iframeId)) {
				var iframe = new Element('iframe', {'id': iframeId, 'name': iframeId, 'src': '', 'style': 'width:0;height:0;border:0px solid #fff'});
				$(formContainer).appendChild(iframe);
		
				Event.observe(iframe, 'load', function() {
					Avenet.Forms.destroyLoadingGraphic();
					var doc = iframe.contentWindow ? iframe.contentWindow.document : (iframe.contentDocument || window.frames[iframeId].document);
					var response = doc.body.innerHTML.evalJSON(true);
					
					if(response.Status == true) {
						saveContentFunc(response.ResponseObject[0].ResponseObject.ArtifactId);
					} else {
						var errors = '';
						response.ResponseObject.each(function(i) {
							errors += i.FailureMessage + '\n';
						});
						
						if(errors.length == 0) errors = response.FailureMessage;
						alert(errors);
					}
				}.bind(this));
			}
			
			Avenet.Forms.showLoadingGraphic(formContainer);

			var form = Avenet.Forms.findForm(formContainer);
			form.setAttribute('target', iframeId);
			form.setAttribute('action', Avenet.ServiceUrls.uploadService + '?id=' + id + '&fid=' + form.id);
			form.setAttribute('method', 'POST');
			form.setAttribute('enctype', 'multipart/form-data');
			form.setAttribute('encoding', 'multipart/form-data'); // Required for IE 6/7
			form.submit();
			
			return false;
		}

        
        
		saveContentFunc();
	},

	deleteContent: function( contentId, successCallback, failureCallback ) {
		new Ajax.Request(Avenet.ServiceUrls.contentService + '/DeleteContent', {
			postBody: $JSON({contentId:contentId}),
			onSuccess: function ( transport ) {
				var response = transport.responseText.evalJSON(true);
				if( response.Status == true && typeof(successCallback) != "undefined" )
				  successCallback(response.SuccessMessage);
				else if(response.Status == false && typeof(failureCallback) != "undefined" )
				  failureCallback(response.SuccessMessage);
			},
			onFailure: function( transport ) {
				var response = transport.responseText.evalJSON(true);
				if( typeof(failureCallback) != "undefined" ) failureCallback(response.Message);
			}
		});
	},
	formatDateTime: function(s, format, nullValue){
		if(format == null) format = 'n/j/Y g:i A';
		if(nullValue == null) nullValue = '';
		if(s == null) return nullValue;
		return eval('new ' + s.replace('/','').replace('/','')).format(format);
	},
	renderCalendar: function(o, type) {
		new CalendarDateSelect($($(o).parentNode).previous(), {time: type=='datetime', year_range:58, onchange: $($(o).parentNode).previous().validate});
	}
});

Avenet.Forms.AutoSave = Class.create({
	formContainer: null,
	updater: null,
	formId: null,
	contentId: null,
	active: false,
	
	initialize: function(formContainer, options) {
		this.formContainer = $(formContainer);
		Object.extend(this, options);
	},
	
	start: function(interval, formId, contentId) {
		//console.log('autosave starting, at interval ' + interval);
		this.formId = formId;
		this.contentId = contentId;
		this.match = true;
		this.updater = new PeriodicalExecuter(function() {
		   this.save();
		}.bind(this), interval);
		this.active = true;
	},
	
	save: function() {
		//console.log('autosave saving');
		
		var adminPopup = $('adminPopup');
		
		// We can't autosave if a popup is displayed. The input fields are masked by ext and won't serialize resulting in incorrect serialization
		//  data.
		if (adminPopup.instanceCount && adminPopup.instanceCount > 0) {
			return;
		}
		
		var current = Avenet.Forms.findForm(this.formContainer).serialize(true);
		if(this.formContainer.autoSaveValues == undefined || this.formContainer.autoSaveValues == null) {
			this.formContainer.autoSaveValues = current;
		}
		
		counter = 0;
		if(current.length != this.formContainer.autoSaveValues.length) {
			this.match = false;
		}
		else {
			current.each(function(v) {
				if(v.Value.trim && v.Value.trim() == this.formContainer.autoSaveValues[counter].Value.trim())
				{}
				else {
					this.match = false;
				}
					counter++;
				}.bind(this));
		}
		
		if(!this.match) {
			this.match = true;
			this.formContainer.autoSaveValues = Avenet.Forms.findForm(this.formContainer).serialize(true);
			Avenet.Forms.autoSaveContent(this.formContainer, this.formId, this.contentId, this.onSuccess.bind(this), this.onFailure.bind(this));
		}
	},
	
	stop: function() {
		//console.log('autosave stopping');
		if (this.updater != null && this.active) {
			this.updater.stop();
			this.updater = null;
			this.save();
			this.active = false;
		}
		
	},
	
	stopWithoutSaving: function() {
		//console.log('autosave stopping without saving');
		if (this.updater != null && this.active) {
			this.updater.stop();
			this.updater = null;
			this.active = false;
		}
	},
	
	onSuccess: function() {
	},
	
	onFailure: function() {
		this.stop();
	}	
});
