/**
 * yaf4ajn.js - Frontendcontroller 1.90.0
 * Copyright(c) 2009, Deutsche Telekom AG, Products & Innovation
 * GNU LESSER GENERAL PUBLIC LICENSE
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * What is it? 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 * This is the Javascript part of the AJAX Hello World 1.90.0 Demo from Deutsche 
 * Telekom AG, Products & Innovation for the iX article.
 * If this script is included in a webpage, it will start an AJAX call to a server 
 * and manipulate the html page depending on the answer from the server. 
 * Data exchange between the server and client happens with JSON data-inter
 * -change format.
 * Dependencies are prototype 1.6.0.3 and effects.js from script.aculo.us 1.8.2.
 * This code is tempered with jslint (see tools/jslint). It has a sound set of 
 * UnitTests (see tools/jsunit) and it has documentation (see tools/jsdoc and 
 * docs-directory).
 * This file is also available as a compressed version (see tools/compressed and
 * yaf4ajn-compressed.js in js-directory.)
 * 
 * Where to go? 
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 * First examine the fecPagecontrol variable in the HTML page (where this
 * script is included). It defines what the Frontendcontroller (fec) should do.
 * (e.g. scopes - load data given by the scopenames)
 * 
 * After the DOM of the webpage is loaded, these functions will be called
 * automatically:
 * >>> fec.base.initialize
 *     Initialize prototype-JS classes, read fecPagecontrol and start 
 *     dataLoad
 * >>> fec.base.dataLoad
 *     loads the data depending on given scopes in fecPagecontrol by firing AJAX
 *     requests to the server. If the server gives a valid response (HTTP2XX)
 *     processResponse is called
 * >>> fec.main.processResponse
 *     This function receives the response data from the server and starts to
 *     process it. Depending on the response, requests will be fired again, the 
 *     DOM of the will be manipulated or errormessages will be shown.
 *
 * Please use Firefox with Firebug-debugger to examine further functionality
 * of this script. 
 * 
 * Change Log:
 * v1.90.0
 * - renamed this file to yaf4ajn.js
 * - complete namespace refactoring (fec)
 * - extracted configuration variables (fec.config)
 * - replaced references to production environment (vespa, vsp -> fec)
 * - fixed jslint findings 
 * - added documentation
 */
/*! yaf4ajn.js - Deutsche Telekom AG, Products & Innovation */
/*jslint laxbreak: true */


/* ___________ INIT GLOBAL VARIABLES/OBJECTS ________________________________ */

var 
	/**
	 * Main object holder (namespace fec -> FrontEndController). In this object
	 * all important variables and functions are placed in a class-type hierarchy.
	 * 
	 * @namespace Main object holder.
	 */   
	fec = {}, 
	/**
	 * Holds the data that can be used by special DOM-setter (hook
	 * jsSetterPrefix)
	 * 
	 * @type object
	 * @default undefined
	 * @see fec.config.jsSetterPrefix
	 */
	fecSetterValue = undefined, 
	/** 
	 * Variable for an exception
	 * 
	 * @type object
	 * @default undefined
	 */
	e = undefined;

/** instance for class FecMain */
fec.main = undefined;
/** instance for class FecPageControl */ 
fec.pageControl = undefined;


/* ___________ CONFIGIGURATION  _____________________________________________ */
/**
 * Base configuration class
 * 
 * @namespace fec.config 
 */
fec.config = {
	/**
	 * Element-Id of your HTML-form. 
	 *
	 * @type string
	 * @default "fecForm"
	 * @constant
	 */	
	htmlIdForm : 'fecForm', 
	/**
	 * Prefix of element-Ids, which should be replaced.
	 * 
	 * @type string
	 * @default "fecData"
	 * @constant
	 */	
	htmlIdDataPrefix : 'fecData',   
	/**
	 * Prefix of element-Id, which will be shown after completion  (initially hidden)
	 * 
	 * @type string
	 * @default "fecScope"
	 * @constant
	 */	
	htmlIdScopePrefix : 'fecScope',
	/**
	 * Prefix of element-Id, which will be hidden after completion (initially visible)
	 * 
	 * @type string
	 * @default "fecScopeLoad"
	 * @constant
	 */	
	htmlIdLoaderPrefix : 'fecScopeLoad',
	/**
	 * Prefix of JS-Functions, which should be called to set data.
	 * 
	 * @type string
	 * @default "fecHookSetData"
	 * @constant
	 * @see fecHookSetDataValuekey
	 */	
	jsSetterPrefix : 'fecHookSetData', 
	/**
	 * Prefix of JS-Functions, which validate the form
	 * 
	 * @type string
	 * @default "fecHookCheckForm"
	 * @constant
	 * @see fecHookCheckFormScopename
	 */	
	jsCheckformPrefix : 'fecHookCheckForm',
	/**
	 * URL to the server for ajax request
	 * 
	 * @type string
	 * @default "/app/json"
	 * @constant
	 * @see http://www.prototypejs.org/api/ajax/request
	 */	
	urlAjaxRequest : '/app/json',
	/**
	 * URL to an errorpage
	 * 
	 * @type string
	 * @default "/error.html"
	 * @constant
	 */	
	urlErrorpage : 'error.html'
		
};
// look for miscellaneous config-objects at the end of the file


/* ___________ START INIT AFTER PAGE IS LOADED ______________________________ */
document.observe("dom:loaded", function() { fec.base.initialize(); });


/**
 * Base class for the main public fec methods 
 * 
 * @namespace fec.base 
 */
fec.base = {
    /**
	 * This function is called, after the DOM is loaded. It prepares the page,
	 * initializes classes and starts an AJAX request to load data from the
	 * server (dataLoad).
	 */		
	initialize: function(){
		try { fecHookInitialize(); } catch (e) {}
		if (fec.util.escapeFrame()) { return; }
		fec.util.keypressObserver(document);
		if (fec.main === undefined) { 
			fec.main = new FecMain(); 
		}
		if ('undefined' != typeof fecPagecontrol) { 
			fec.pageControl = new FecPageControl(fecPagecontrol);
			if (fec.pageControl.getScopes().length > 0) {
				fec.base.dataLoad();
				$$('form[id^="'+fec.config.htmlIdForm+'"]').each ( function (fe) {
					fec.util.submitObserver(fe.id);
				});
			}
		}
	},
	/**
	 * Prepares the page and starts loading data from the server.
	 */
	dataLoad: function (){
		fec.main.showSpinner(fec.pageControl.getScopes(),true);
		try { fec.form.clearErrorsInDom(); } catch(e){}
		fec.main.getServerData();
	},
	/**
	 * Prepares the page and starts sending data to the server. This function
	 * will be called if a form-submit event (through event observer) fires but
	 * it can also be called directly.
	 */
	dataSend: function () {
		fec.main.showScope('vspScopeFehler',false);
		if (fec.main.checkForm()) {
			fec.main.disableSubmitButtons(true);
			fec.main.sendServerData();
		} else { fec.main.scrollToTop(); }
	}
};

/**
 * Values Factory handles updating an html-element with a new value
 * 
 * @namespace fec.valuesFactory 
 */
fec.valuesFactory = {
	/**
	 * Updates a TD-Element with newValue. If the Value is boolean, default
	 * Strings are set.
	 * 
	 * @param {object} element Element that will be updated
	 * @param {object} newValue newValue
	 */ 
	td: function(element, newValue){
		if (typeof newValue == 'boolean') {
			newValue = newValue === true ? fec.msg.YES : fec.msg.NO; 
		}
		$(element).update(newValue.toString());
	},
	/**
	 * Updates a P-element by calling fec.valuesFactory.td .  
	 * 
	 * @see fec.valuesFactory.td
	 */
	p: function(element, newValue){
		fec.valuesFactory.td(element, newValue);
	},
	/**
	 * Updates a SPAN-element by calling fec.valuesFactory.td .  
	 * 
	 * @see fec.valuesFactory.td
	 */
	span: function(element, newValue){
		fec.valuesFactory.td(element, newValue);
	},
	/**
	 * Updates a INPUT-element, accordingly to the input type
	 * 
	 * @param {object} element Element that will be updated
	 * @param {object} newValue newValue will be HTML-unescaped
	 */
	input: function(element, newValue){
		switch (element.type.toLowerCase()) {
			case 'submit': return true;
			case 'hidden':
				if ($(element.name + 'Hidden')) {
					$(element.name + 'Hidden').update(newValue.toString().unescapeHTML());
				}
				fec.valuesFactory.textarea(element, newValue);
				break;
			case 'password':
			case 'text':
				fec.valuesFactory.textarea(element, newValue);
				break;
			case 'checkbox':
			case 'radio':
				fec.valuesFactory.inputSelector(element, newValue);
				break;
			}
		return false;
	},
	/**
	 * Handles lists of radio- and checbox-tags
	 * 
	 * @param {object} element Element that will be updated
	 * @param {object} newValue newValue
	 * @private
	 */
	inputSelector: function(element, newValue){
		fields = document.getElementsByName(element.name);
		for (var i = 0; i < fields.length; i++) {
			if (fields[i].type == 'radio') {
				if (fields[i].value == newValue.toString()) {
					fields[i].checked = true;
				}
			} else {
				if (fields[i].type == 'checkbox') {
					fields[i].checked = (newValue.toString() == "true") ? true : false;
				}
			}
		}
	},
	/**
	 * Updates a TEXTAREA-element
	 * 
	 * @param {object} element Element that will be updated
	 * @param {object} newValue newValue will be HTML-unescaped
	 */
	textarea: function(element, newValue){
		element.value = newValue.toString().unescapeHTML();
	},
	/**
	 * Selects the option of a SELECT-element
	 * 
	 * @param {object} element Element that will be updated
	 * @param {object} newValue newValue
	 */
	select: function(element, newValue){
		var value = '', opt;
		for (var i = 0; i < element.options.length; i++) {
			if (element.options[i].value == newValue) {
				element.selectedIndex = i;
				return true;
			}
		}
	}
};

/**
 * General form checking class - before data is submitted to the server, the
 * formdata can be checked through javascript hooks (fecHookCheckFormXYZ). This
 * class contains functions for validation.
 * 
 * @namespace fec.util
 * @see FecMain.checkForm
 */
fec.form = {
	/**
	 * Keeps the state of form-validation, only if this property is true data
	 * will be submitted to the server.
	 * 
	 * @type boolean
	 */	
	isValid: true,
	/**
	 * Counts the form-errors
	 * 
	 * @type number
	 */
	errorCount: 0,
	/**
	 * Prepares the page for JS-Form Validation. This method is called from the
	 * JS-validation-hook-functions.
	 * 
	 * @see fecHookCheckFormScopename
	 * @todo extract html to config-class
	 */
	initFormValidation: function() {
		this.errorCount = 0;
		this.isValid = true;
		var s = "";
		s += "\n<div id='vspFormError' class='large box naked image error' style='display:none;'>"; 
		s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+ fec.msg.FE004 +"'/>";
		s += "\n<h3>"+ fec.msg.FE003 +":</h3>"; 
		s += "\n<ul class='errorlist' id='vspFormErrorList'>\n</ul>"; 
		s += "\n</div>\n";
		s += "\n<div style='clear:both;height:1px'></div>";
		if($('vspFormError') === null) {
			if ($('vspScopeFehler')) { 
				$('vspScopeFehler').insert({'after':s}); 
			} 
		} else {
			if ($('vspFormError').childElements().size() < 1) {
				$('vspFormError').replace(s);
			} else {
				this.clearErrorsInDom();

			}
		}
	},
	/**
	 * Adds an error to the DOM and tries to change the class of the surrounding 
	 * HTML code of the input field
	 * 
	 * @param {object} element - HTML-Element that should me marked
	 * @param {string} errtxt - Errortext 
	 */
	setErrorInDom: function(element, errtxt) {
		this.errorCount++;
		try{
			try {
				var trElement = element.up('tr');
				trElement.addClassName('error');
				var tdElements = trElement.select('td');
				if (tdElements.length > 1) {
					tdElements[0].addClassName('left');
					tdElements[1].addClassName('right');
				}
				if (this.errorCount==1) {
					element.focus();
				} 
			} catch (e) {}
			if (errtxt) {
				$('vspFormErrorList').insert({'bottom':'<li>'+errtxt+'</li>\n'});
			}
		} catch(e) {}
		this.isValid = false;
	},
	/**
	 * Clear the errors on the page and try to remove error-CSS-classes.
	 */
	clearErrorsInDom: function() {
		// Entferne rote Umrandungen
		var trElements = $$("tr[class*='error']");
		try {
			for(var i=0; i<trElements.length; i++) {
				trElements[i].removeClassName('error');
				trElements[i].select('td').each(function(element) {
					if (element.hasClassName('left')) {
						element.removeClassName('left');
					}
					if (element.hasClassName('right')) {
						element.removeClassName('right');
					}
				});
			}
		} catch (e) {}
		// Entferne Fehlermeldung in Box und blende Box aus
		if ($('vspFormErrorList')) {
			$('vspFormErrorList').update("");
		}
		if ($('vspFormError')) {
			$('vspFormError').hide();
		}
		this.isValid = true;
	},
	/**
	 * Checks if the form ist valid and returns true or shows the ErrorBox.
	 * 
	 * @returns {boolean} valid or not
	 */
	checkIfFormValid: function() {
		var warn = $('vspFormError');
		if (this.isValid) {
			warn.hide();
			return true;
		} else {
			if ('undefined' != typeof Effect) {
				var x = new Effect.Appear( warn, { duration:0.3, keepBackgroundImage:true } );
			} else {
				warn.show();
			}
			return false;
		}
	},
	/**
	 * Checks if the value is a number.
	 * 
	 * @param {object} val - input value
	 * @returns {boolean} number or not
	 */
	checkTypeNumber: function(val) {
		var regex = /^\d+$/;
		return regex.test(val);
	},
	/**
	 * Converts a date Sring DD.MM.YYYY to date object
	 * 
	 * @param {String} val - Datestring in format DD.MM.YYYY
	 * @returns {Date|null} Date-object or null
	 */
	stringToDate: function (val) {
		var regex = /^\d{2}(\.)\d{2}\1\d{4}$/;
		if (regex.test(val)) {
			var parts = val.split(".");
			if (3 == parts.length) { 
				var testdate =  new Date(parts[2],parts[1]-1,parts[0]);
				var testyear = testdate.getFullYear();
				var testmonth = testdate.getMonth()+1; 	
				var testday = testdate.getDate(); 		
				if (testyear == parts[2] && testmonth == parts[1]*1 && testday == parts[0]*1) {
					return testdate;
				}
			}
		}
		return null;
	},
	/**
	 * checks if String is a date
	 * 
	 * @param {object} val - input value
	 * @returns {boolean} is a Date-object or not
	 */
	checkTypeDate: function(val) {
		var testdate = fec.form.stringToDate(val);
		return null !== testdate;
	},
	/**
	 * checks if the input value is in the past
	 * 
	 * @param {object} val - input value
	 * @returns {boolean}
	 */
	checkTypeDateInPast: function(val) {
		return fec.form.checkTypeDate(val) && fec.form.stringToDate(val) < new Date();
	},
	/**
	 * checks if the input value is a german zipcode
	 * 
	 * @param {object} val - input value
	 * @returns {boolean}
	 */
	checkTypePostleitzahl: function(val) {
		var regex = /^[0-9]{5}$/;
		return regex.test(val);
	},
	/**
	 * checks if the input value is a HausnummerMitZusatz ;)
	 * 
	 * @param {object} val - input value
	 * @returns {boolean}
	 */
	checkTypeHausnummerMitZusatz: function(val) {
		var regex = /^[1-9]{1}[0-9a-zA-Z \-]*$/;
		return regex.test(val);
	},
	/**
	 * Checks if the input value is a email adresse - intentionally weak check.
	 * Serverside validation should go deeper.
	 * 
	 * @param {object} val - input value
	 * @returns {boolean}
	 */
	checkTypeEmail: function(val) {
		var regex = /.*@.*\..*/;
		return regex.test(val);
	},
	/**
	 * regex check helper function
	 * 
	 * @param {Regex} regex - the regex test string
	 * @param {string} value - String on which the regex-condition is tested
	 * @returns {boolean}
	 */
	checkRegex: function(regex, val) {
		return regex.test(val);
	},
	/**
	 * Set an HTML input field to readonly.
	 * 
	 * @param {object} element - HTML input value element
	 * @param {boolean} setIt - set it to readonly or not
	 */
	setElementReadonly: function(element, setIt) {
		if(setIt) {
			element.readOnly = true;
			element.value = '';
			element.style.backgroundColor = '#EEEEEE';
		} else {
			element.readOnly = false;
			element.style.backgroundColor = '';
		}
	},
	/**
	 * Checks if a string validates to certain conditions and adds errors 
	 * if its not.
	 * 
	 * @param {object} input - HTML input value element
	 * @param {boolean} manda - is it mandatory?
	 * @param {number} min - minimal string length
	 * @param {number} max - maximal string length
	 * @param {string} text - The beginning of the error message, e.G. "The name" or "The birthday".
	 * @returns {boolean} valid or not
	 */
	checkTypeString: function(input, manda, min, max, text) {
		var retval = true;
		if (input) {
			var s = input.value;
			var err = text.strip();
			if (manda && s.length === 0) {
				fec.form.setErrorInDom(input, err+ " " + fec.msg.FE005);
				retval = false;
			} else if(s.length > 0){
				if (min === null || max === null) {
					if (min === null && max !== null && s.length > max) {
						err += " " + fec.util.formatMessage(fec.msg.FE006, {'MAX' : max});
					}
					if (max === null && min !== null && s.length < min) {
						err += " " + fec.util.formatMessage(fec.msg.FE007, {'MIN' : min});
					}					
				} else if (s.length < min || s.length > max) {
					err += " " + fec.util.formatMessage(fec.msg.FE008, {'MIN' : min, 'MAX' : max});
				}
				if (err.length > text.strip().length) {
					fec.form.setErrorInDom(input, err);
					retval = false;
				}
			}
		}
		return retval;		
	}
};

/**
 * General utils class
 * 
 * @namespace fec.util
 */
fec.util = {
	/** 
	 * StartTime for alertTime function (for basic profiling) 
	 */ 
	timeStart : new Date().getTime(),
	
	/** 
	 * Formats a message with object-values
	 * 
	 * @param {string} msg - message-String with placeholders
	 * @param {object} hobj - object with values
	 */ 
	formatMessage:function(msg, hobj) {
		var tokens = msg.match(/##\w+##/g);
		for (var i=0; i<tokens.length; i++) {
			var varStr = tokens[i].replace(/#/g, '');
			eval("msg = msg.replace(/"+tokens[i]+"/g, '"+hobj[varStr]+"');");
		}
		return msg;
	},
	/** 
	 * Adds functions to observation list, which are executed when DOM ist loaded  
	 * 
	 * @param {object} func - a function
	 */ 
	addLoadEvent:function(func) {
		document.observe("dom:loaded",func);
	},
	/** 
	 * Adds forms to observation list, which will processed when the submit event
	 * is fired  
	 * 
	 * @param {object} elem - Form-Element
	 */ 
	submitObserver: function(elem) {
		Event.observe(elem, 'submit', 
			function(event) {
				fec.base.dataSend(); 
				Event.stop(event);
			}
		);		
	},
	/** 
	 * Adds keypress-interception for the RETURN KEY - if return key is send, 
	 * then the defaultbutton behaviour will be executed or the form will be 
	 * processed (dataSend).     
	 * 
	 * @param {object} elem - document
	 */ 
 	keypressObserver: function (elem) {
		Event.observe(elem, 'keypress',function(event) {
 			if(event.keyCode == Event.KEY_RETURN) {
				try {
					var defaultbutton = $('defaultbutton');
					var onclick = defaultbutton.readAttribute("onclick");
					if (null !== onclick) {
						eval(onclick);
					} else {
						fec.base.dataSend();
					}
					Event.stop(event);
				} catch (e) {}
			}
		});
	},
	/** 
	 * Sets a gotoPage and processes the form (dataSend) without JS-validation   
	 * 
	 * @param {string} url - URL where the server should redirect
	 */ 
	submitWithGotoPage: function (url) {
		fec.pageControl.setGotoPage(url);
		fec.pageControl.setParameter('validate',false);
		fec.base.dataSend();
	},
	/** 
	 * Sets a action and processes the form (dataSend) without JS-validation   
	 * 
	 * @param {string} url - URL where the server should redirect
	 */ 
	submitWithAction: function (action) {
		fec.pageControl.setControl('paramsSend', action);
		fec.pageControl.setParameter('validate', false);
		fec.base.dataSend();
	},
	/** 
	 * Turn an undefined object into an empty string and add a suffix-string   
	 * 
	 * @param {object} value - tested object
	 * @param {string} suffix - suffix String
	 * @returns {string} 
	 */ 
	chkUndef: function(value, suffix) {
		return ((value) ? value:'') + ((suffix) ? suffix:'');
	},
	/** 
	 * Basic profiling - add this function in your code, and the time
	 * between the initial load of the page and the code will be put at the end 
	 * of the page
	 * 
	 * @param {string} uc - UseCase, addition information
	 */	
	alertTime: function(uc){
		var now = new Date().getTime();
		var s = '<br/>' + uc +': '+ (now - fec.util.timeStart) + 'ms'; 
		$$('body')[0].insert({'bottom':s});
	},
	/**
	 * Reads the URL query parameters, put the keys to lower case and return the
	 * parameters as a hash
	 * 
	 * @return {hash} query as hash object
	 */
	getQueryParams: function() {
		var query = new Hash();
		// try wegen %% in URI
		try {
			query = $H(window.location.search.toQueryParams());
			// alle keys toLower
			query = query.inject(new Hash(), 
				function(hash, obj) {
					hash.set(obj[0].toLowerCase(),obj[1]);
					return hash;
				}
			);
		} catch (e) {}
		return query;
	},	
	/**
	 * Shortcut to openWindow, with less amount of parameters.
	 * @see fec.util.openWindow 
	 */
	popup: function(w, h, site, opts) {
		fec.util.openWindow(site, 'popUpWin', w, h, null, null, null, opts, false);
	},
	/**
	 * Pop-Up function
	 * 
	 * @param {string} url - popup-url 
	 * @param {string} winname - window name  
	 * @param {number} width 
	 * @param {number} height 
	 * @param {null|boolean} toolbr - with a toolbar? 
	 * @param {null|boolean} scrollbrs - with scrollbars? 
	 * @param {null|boolean} resizable - resizable? 
	 * @param {string} winoptions - more options
	 * @param {null|boolean} trackit - refresh parent after popup closes 
	 * @returns {object} pop-window object  
	 */
	openWindow: function(url, winname, width, height, toolbr, scrollbrs, resizable, winoptions, trackit) {
	    var posx = screen.availWidth/2 - width/2;
	    var posy = screen.availHeight/2 - height/2;
	    var xyPos = 'top=' + posy + ',left=' + posx + ',screenX=' + posx + ',screenY=' + posy;
	    var opt = ''; 
		opt += 'width=' + width + ',height=' + height;
		if (toolbr !== null) {
			opt += ',toolbar=' + (toolbr ? 'yes':'no');
		}
		if (scrollbrs !== null) {	 
			opt += ',scrollbars=' + (scrollbrs ? 'yes':'no');
		}
		if (resizable !== null) { 
			opt += ',resizable=' + (resizable ? 'yes':'no');
		}
		opt += ', ' + xyPos;
		opt += (winoptions) ? ', '+winoptions : '';
		var newWindow = window.open(url, winname, opt);
		newWindow.focus();
		if (trackit !== null && trackit) {
			var x = new PeriodicalExecuter(function(pe) { 
				if (newWindow.closed) { 
					pe.stop();
					fec.util.redirectReplace(self.location.href); 
				}			
			}, 0.5);
		}
		return newWindow;
	},
	/**
	 * Get the document dimensions.
	 * 
	 * @returns {object} { width: Number, height: Number }
	 */
	getDimensions: function() {
		var dimensions = document.viewport.getDimensions();
		if (dimensions.width === 0 || dimensions.height === 0) {
			if (document.body) {
				dimensions.width = document.body.offsetWidth;
				dimensions.height = document.body.offsetHeight; 
			}
		}
		return dimensions;
	},
	resizeAndCenter: function(width, height) {
		self.resizeTo(width, height);
		self.moveTo(Math.floor((screen.availWidth-width)/2), Math.floor((screen.availHeight-height)/2));
	},
	maximizeWindow: function() {
		window.moveTo(0,0);
		window.resizeTo(screen.availWidth,screen.availHeight);
	},
	escapeFrame: function () {
		if (parent && (self != parent)) {
			try {
				parent.document.body.style.color = '#000000';
				parent.location.href = self.location.href;
				return true;
			} catch (e) {}
		}
		return false;
	},
	setLocation: function(url, toTop) {
		if (toTop && toTop.toString() == 'true') {
			top.location.replace(url);			
			top.focus();
		} else {
			self.location.replace(url); 
			self.focus();
		} 
	},
	getImageRoot: function(url) {
		var p = 'http://localhost:8080';
		if ($('defaultImage')) {
			var src = $('defaultImage').src;
			p = src.substr(0, src.search('fileadmin'));
		}
		p = p.endsWith('/') ? p : p+'/';
		if (url) {
			url = (url.startsWith('/') ? url.sub('/', '') : url);
		}
		return (url) ? p+url : p;
	},
	getPageAddressRoot: function(url){
		var u = '#{protocol}//#{domain}#{port}'.interpolate({
			protocol: location.protocol,

			domain: document.domain,
			port: location.port ? ':' + location.port : ''
			})	+ '/';
		if (url) {
			url = (url.startsWith('/') ? url.sub('/', '') : url);
		}
		return (url) ? u+url : u;
	},
	getErrorPage: function(errid, code) {
		return fec.config.urlErrorpage + ((errid) ? ('?errid='+errid) : '');
	},
	// func muss als "function() { #code# }" übergeben werden	
	delay: function(func,time) {
		window.setTimeout(func,time);
		
	},
	setDatumsfelder: function(eleTag, eleMonat, eleJahr, datum) {
		// fuelle Datumsfelder auf
		var y = "";
		if (eleTag.options.length == 1) {
			for (var i=1; i<=31; i++) {
				y = ( i<=9 ? '0'+i : ''+i );
				$(eleTag).insert({bottom: '\n<option value="'+y+'">'+y+'</option>'});
			}
			$(eleTag).selectedIndex = 0;
		}		
		var m = ['Januar','Februar','M&auml;rz','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'];
		if (eleMonat.options.length == 1) {
			for (i=0; i<m.length; i++) {
				y = i<9 ? '0'+ (i+1) : ''+(i+1);
				$(eleMonat).insert({bottom: '\n<option value="'+ y +'">'+m[i]+'</option>'});
			}
			$(eleMonat).selectedIndex = 0;
		}		
		var d = new Date();
		if (eleJahr.options.length == 1) {
			for (i=d.getFullYear(); i>=1900; i--) { 
				$(eleJahr).insert({bottom: '<option value="'+i+'">'+i+'</option>'});
			}
			$(eleJahr).selectedIndex = 0;
		}		
		if (datum) {
			try { 			
				d = datum.split('.');
				fec.valuesFactory.select(eleTag, d[0]);
				fec.valuesFactory.select(eleMonat, d[1]);
				fec.valuesFactory.select(eleJahr, d[2]);
 			} catch(e){}			
		}
	},
	redirectReplace: function(url) {
		fec.util.redirect(url, 10, true);
	},
	redirect: function(url, delay, replace) {
		try { fecHookRedirect(url, delay, replace); return true;} catch(e) {}
		setTimeout(
			function() { 
				// Parameter aus aktuellem Pfad mit hinzufügen, aber nur wenn keine ID dabei ist
				// sonst wird per ID immer wieder das Entity auf der Folgeseite geladen (siehe TBE-Beruf)
				var params = window.location.search;
				if (params.length > 0) {
					if (params.toUpperCase().indexOf('ID=') > -1) {
						// ID in den Params -> nichts anhängen
						// todo nur id aus params entfernen  
					} else {
						if(url.indexOf('?') != -1) {
							params = '&' + params.substring(1, params.length);
						} else { 
							url += params;
						}
					}
				}
				url = url.gsub(' ','');
				if (replace) {
					window.location.replace(url);
				} else  {
					window.location=url;
				}
			}.bind(this), (delay === undefined) ? 10 : delay );
		// wegen Back-Button 
		if (fec.main) {
			fec.main.disableForm(false);
		}
		return true;
	},
	capitalizeByChar: function(str, chr) {
		var parts = str.split('/'), len = parts.length;
		if (len == 1) {
			return parts[0];
		}
		var capitalized = str.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)	: parts[0];
		for (var i = 1; i < len; i++) {
			capitalized += '/'+ parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
		}
		return capitalized;
	},
	setKeepAlive: function() {
		var pb = '{ "referer": "'+fec.main.getReferer()+'", "times": ['+new Date().getTime()+'], "request": ["registernext"] }';
		var x = new Ajax.Request(fec.config.urlAjaxRequest, 
		{
			contentType: 'application/json', 
			asynchronous: true,
			postBody: pb
		});
		return x;		
	},
	runKeepAlive: function(delay) {
		var x = new PeriodicalExecuter(fec.util.setKeepAlive, delay ? delay : 60);
	},
	isCompleted: function() {
		return (fec.main && fec.main.completed.length > 0) ? true : false; 
	},
	addParamToUrl: function(url, name, value) {
		url += (url.include('?') ? '&' : '?') + name + '=' + encodeURIComponent(value);
		return url;
	},
	replaceAllCss: function(oldCss, newCss) {
		$$('div[class~="'+oldCss+'"]').each ( function(ele) {
			ele.removeClassName(oldCss);
			ele.addClassName(newCss);
		});						
	},
	getPageName: function() {
		var path = window.location.pathname;
		return path.substring(path.lastIndexOf('/') + 1);
	},
	eleId: function(eleName) {
		return fec.config.htmlIdDataPrefix + eleName; 
	}
};

/**
 * Cookie handling class
 * 
 * @namespace fec.cookies 
 */
fec.cookies = {
	/**
	 * Creates a cookie
	 * 
	 * @param {string} cname The name of the cookie. 
	 * @param {string} value The value of the cookie. 
	 * @param {null|number} hours Number of hours after the cookie will expire. 
	 * If hours is null, the cookie will be a session cookie.
	 */		
	set: function(cname, value, hours)  {  
		var expires = "";
		if (hours) {
			var date = new Date();
			date.setTime(date.getTime()+(hours*60*60*1000));
			expires = "; expires="+date.toGMTString();
		}
		var cstr = cname + "=" + escape(value) + expires + "; path=/";
		document.cookie = cstr;   
	},
	/**
	 * Returns the cookie value for the given name.
	 * 
	 * @param {string} cname The name of the cookie to retrieve. 
	 * @param {string} defaultValue The defaultValue for the cookie, if none
	 * is found. 
	 */		
	
	get: function(cname, defaultValue) {  
		var regex = new RegExp(cname + "\\s*=\\s*(.*?)(;|$)");  
		var cookies = document.cookie.toString();
		var match = cookies.match(regex);
		if (match) {
			return unescape(match[1]);
		}  
		return defaultValue;  
	},
	/**
	 * Removes a cookie
	 *  
	 * @param {string} cname The name of the cookie to be erased. 
	 */
	erase: function(cname) {  
		fec.cookies.set(cname, '', -1);  
	}
};

/* _____________________ CLASSES (PROTOTYPE) ________________________________ */

/**
 * Main processing class - This class holds the main functions for retrieving 
 * and submitting data && processing the server response && DOM manipulation 
 * 
 * @class FecMain 
 */
var FecMain = Class.create({
	/** URL to the server for ajax request	 */	
	ajaxRequestUrl: fec.config.urlAjaxRequest,
	/** experimental: if true you can develop html without a server */	
	staticOn: false,
	/** experimental for staticOn */	
	staticResponseUrl: '/staticResponse',
	/** holds the initial state of all form-buttons */	
	submitButtons: [],
	/** holds the data object from the response */	
	data: '',
	/** list of completed scopes */
	completed: $A([]),
	/** flag for JS-form-validation */
	isFormValid: false,
	/** will run automatically by instantiation */
	initialize: function() {
		// errorinit
		this.initErrorList();
	},
	/**
	 * Creates an Ajax-request to the server, depending on fecPagecontrol values.
	 * The server sends data back, that can be processes. 
	 * 
	 * @returns {boolean} true
	 */
	getServerData: function(scope){
		var request =  "", reqObj;
		var scopeArr = (scope) ? [scope]:fec.pageControl.getScopes();
		if(!this.isValueInArray(fec.pageControl.getControl('paramsLoad'),'void')){
			for(var i = 0; i < scopeArr.size(); i++) {
				if (request !== "") {
					reqObj = request.evalJSON(true);
					reqObj.request = [scopeArr[i]];
					request = Object.toJSON(reqObj);
				} else {
					request = this.getRequestBody('load', scopeArr[i]);
				}
				var x = new Ajax.Request(this.getAjaxRequestUrl('load'), 
				{
					contentType:'application/json', postBody: request,
					onSuccess:function(r){ 
						this.processResponse('load',r.responseText); 
					}.bind(this),
					on403:function(r){ 
						fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status,'403')); 
					}.bind(this),
					onFailure:function(r){
						fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status));
					}.bind(this)
				});
			}
		} else {
			this.showSpinner(scopeArr,false);
 			try{ executeVoid(); } catch(e){}
		}
		return true;
	},
	/**
	 * Creates an Ajax-request to the server, depending on fecPagecontrol values.
	 * The server receives data through request data (e.G. form-data).
	 * 
	 * @returns {boolean} true
	 */
	sendServerData: function() {
		var request = this.getRequestBody("send");
		var x = new Ajax.Request(this.getAjaxRequestUrl('send'), 
		{
			contentType:'application/json', postBody: request,
			onSuccess:function(r){ 
				this.processResponse('send',r.responseText); 
			}.bind(this),
			on403:function(r){ 
				fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status,'403')); 
			}.bind(this),
			onFailure:function(r){ 
				fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status));
			}.bind(this)
		});
		return true;
	},
	/**
	 * Creates an Ajax-request to a given url and returns the received data.
	 *  
	 * @param {string} url - Serverurl or a file-url (e.G. ../data.txt)
	 * @returns {object} received data object
	 */
	getAjaxData: function(url) {
		var data = '';
		var x = new Ajax.Request(url, 
		{
			contentType:'application/json', asynchronous:false,
			onSuccess:function(r){ data = r.responseText; }.bind(this),
			on403:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status,'403')); }.bind(this),
			onFailure:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status));}.bind(this)
		});
		return data;		
	},	

	/**
	 * Processes the response from the server
	 * 
	 * @param {string} type - load or send
	 * @param {object} response - server response object
	 */
	processResponse: function(type, response) {
		if (response !== "") {
			//response = response.substring(0, response.length - 1) + ', "error":{"PAGE":{"ERKUND":"","E37P2F":"Juhee","E37XXX":"FEHLER FEHLER"}} }';
			var vspData = response.evalJSON(true);
			// vspData fuer spezielle Setter in HTML verfuegbar machen
			this.data = $H(vspData.data);
			this.completed = $A(vspData.completed);
			try { fecHookInitData(this.data); } catch (e) {}
			if (vspData.redirect !== undefined) {
				fec.util.redirect(vspData.redirect);
				return;
			} 
			this.showContainer();
			this.processResponseTimes(vspData);
			if (this.processResponseError(vspData)) {
				if (vspData.gotoPage !== undefined) {
					// pc zuruecksetzen, falls er veraendert wurde (submitWithAction)
					fec.pageControl = new FecPageControl(fecPagecontrol);
					this.disableSubmitButtons(false);
					fec.util.redirect(fec.pageControl.getGotoUrl(vspData.gotoPage));
					return;
				}
				this.replaceWerteInDOM(vspData.data);
				if(vspData.retry && vspData.retry.length > 0) {
					var retryScope = vspData.retry[0];
					if (vspData.wait === undefined) { vspData.wait = 1000; }
					if (type == 'load') { 
						fec.util.delay( function() { fec.main.getServerData(retryScope); }, vspData.wait ); 
					}
					if (type == 'send') { 
						fec.util.delay( function() { fec.base.dataSend(); }, vspData.wait ); 
					}				
				} else {
					this.showSpinner(vspData.completed,false);
				}
			} else {
				// auch bei Fehler, Werte ersetzen
				this.replaceWerteInDOM(vspData.data);
				// Ladefehler anstatt Ladespinner
				if (type == 'load') {
					fec.util.replaceAllCss('ladebalken','ladebalken-error');
				} else {
					this.showSpinner(fec.pageControl.getScopes(),false);
				}
				// Hook fuer spezielle Fehlerbehandlung (zb siehe service-anmelden\index.html)
				try { fecHookHandleError(vspData); } catch (e) {}
								
			}

		} else {
			// ein leerer Response, kann vorkommen, wenn schnell geklicked wird -> keine Fehlerseite anzeigen
		}
	},
	/**
	 * @param {object} data
	 */	
	replaceWerteInDOM: function(data) {
		var domId = false, dataKeys = Object.keys(data), foundId = true;
		dataKeys = dataKeys.sortBy(function(s) {
		  return (/.*en$/).test(s) ? 0 : s.length;
		});
		try { fecHookInitially(); } catch (e) {}
		for (var index = 0, len = dataKeys.length; index < len; ++index) {
			foundId = true;
			var dataKey = dataKeys[index];
			domId = fec.config.htmlIdDataPrefix + dataKey.capitalize();
			// Die Variable fecSetterValue kann von dem zusaetzlichen Setter verwendet werden.
			fecSetterValue = ('string' == typeof data[dataKey]) ? data[dataKey].escapeHTML():data[dataKey];
			try { 
				eval('fecHookSetData'+dataKey.capitalize()+'();'); 
			} catch (e) { 
				if (this.setElementValue(domId, fecSetterValue) === false) { foundId = false; }
				// keine IDs per autoincrement updaten 
				if (dataKey.toLowerCase() != 'id') {
					InnerFor: for (var i = 1; i < 10; ++i) {
						try {
							var el = $(domId+i);
							this.setElementValue(el.id, fecSetterValue);							
						} catch(e) {
							break InnerFor;
						}
					}
				}
			}
		}
		try { fecHookFinally(); } catch (e) { }
	},
	/**
	 * @param {object} vspData
	 */	
	processResponseError: function(vspData) {
		if (vspData.error !== undefined) {
			fec.main.isFormValid = false; // Formvalidierung beim nächsten submit wieder anstossen
			this.disableForm(false);
			this.initErrorList();
			var errors = vspData.error, errorKey = "", errorValues = "";
			var errorKeys = Object.keys(errors);
			for (var index = 0, len = errorKeys.length; index < len; ++index) {
				errorKey = errorKeys[index];
				var errorsInner = $A(errors[errorKey]);
				errorsInner.each(function(err) {
					this.errorAdd(err,errorKey); 
				}.bind(this));
				fec.form.setErrorInDom($("vspData"+errorKey));
			}
			return false;
		} else {
			fec.main.showScope('vspScopeFehler',false);
		}
		return true;
	},
	/**
	 * @param {object} vspData
	 */	
	processResponseTimes: function(vspData) {
		if (vspData.times !== undefined) {
			vspData.times.push(new Date().getTime());
		}
	},
	
	/**
	 * @param {object} scope
	 * @param {boolean} show
	 */	
	showScope: function(scope, show) {
		var el = $(scope);
		if (el) {
			Element[(show) ? 'show' : 'hide'](el);
			return true;
		} else {
			return false;
		}
	},
	/**
	 * @param {object} scopes
	 * @param {boolean} show
	 */	
	showSpinner: function(scopes, show) {
		if(Object.isString(scopes)) {
			scopes = [scopes];
		}
		if(Object.isArray(scopes)) {
			for (var index = 0, len = scopes.length; index < len; ++index) {
				var scope = scopes[index];
				try {
					if (show) {
						this.showScope(fec.config.htmlIdScopePrefix + scope.capitalize(),false);
						this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),true);
						// Ladebalken Style wieder zuruecksetzen
						fec.util.replaceAllCss('ladebalken-error', 'ladebalken');
					} else {
						this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),false);
						if ('undefined' != typeof Effect) {
							var x = Effect.Appear($(fec.config.htmlIdScopePrefix + scope.capitalize()), { duration: 0.25 });
						} else {
							$(fec.config.htmlIdScopePrefix + scope.capitalize()).show();
						}
						// Weitere Elemente einblenden per Hook
						try{ fecHookShowScope(); }catch(e){}
					}
				} catch(e) {}
			}
		}
	},
	/**
	 * If a container element is in the page, toggle visibility. This is  
	 * usefull, when the dom shouldn't be visibility, before the response has 
	 * arrived.
	 */	
	showContainer: function() {
		if ($('container')) {
			$('container').show();
		} 
		if ($('containerWarten')) {
			$('containerWarten').hide();
		} 
	},
	/**
	 * @param {object} element
	 * @param {boolean} newValue
	 */	
	setElementValue: function(element, newValue) {
		var element_id = element;
		element = $(element);
		if (!element){
			element = document.getElementsByName(element_id)[0];
		}
		if (!element){ return false; }
		var method = element.tagName.toLowerCase();
		var parameter = fec.valuesFactory[method](element, newValue);		
	},
	/**
	 * 
	 */
	getVspDataFormElements: function() {
		var form = $(fec.config.htmlIdForm), elements = new Hash();
		if (null !== form) {
			elements = $H(this.serializeElements(form.getElements(),true));
			elements.each(function(ele){
				if(!ele[0].startsWith(fec.config.htmlIdDataPrefix)) {
					elements.unset(ele[0]);
				}
			});
		}
		return elements;
	},	
	/**
	 * @param {string} type
	 */
	getRequestData: function(type) {
		var elements = fec.main.getVspDataFormElements();
		if (type == 'send') {
			this.disableForm(true);
			try {
				var data = eval('fecHookGetRequestData' + fec.pageControl.getScopes()[0].capitalize()+'()');
				elements.update(data);
			} catch (e) { }
		}
		elements.update(this.getUriControlParams());
		if (elements.size() !== 0) {
			var formData = elements.toJSON();
			if (formData == '{}') { return ""; }
			return formData.gsub(fec.config.htmlIdDataPrefix,'');
		} 
		return '';
	},
	/**
	 * 
	 */
	getUriControlParams: function() {
		var h = fec.util.getQueryParams(), rv = new Hash();
		// URI Parameter die dem JSON Request angefuegt werden sollen, nur Kleinbuchstaben
		// confirmid, regid kommen von Bestaetigungsemail
		var controlParams = ['id','confirmid','regid','wdywtg','wdycf'];
		if(h.size() !== 0) {
			controlParams.each(function(key) {
				if (h.get(key) !== undefined) {
					rv.set(key.capitalize(),h.get(key));
				}
			}.bind(this));		
		}
		return rv;
	},
	/**
	 * @param {object} elements
	 * @param {object} options
	 */
	serializeElements: function(elements, options) {
		if (typeof options != 'object') {
			options = { hash: !!options };
		}
		else if (options.hash === undefined) {
			options.hash = true;
		}
		var key, value, submitted = false, submit = options.submit;
		var data = elements.inject({ }, 
			function(result, element) {
				if (!element.disabled && element.name) {
					key = element.name; value = $(element).getValue();
					if ((element.type != 'submit' || (!submitted &&
						submit !== false && (!submit || key == submit) && (submitted = true)))) {
						// msch hier besondere behandlung fuer checkboxes
						if(element.type === 'checkbox' && !(key in result)) {
							result[key] = (element.checked === true) ? "true":"false";
						} else {
							if (value != null) {
								if (key in result) {
									// a key is already present; construct an array of values
									if (!Object.isArray(result[key])) {
										result[key] = [result[key]];
									}
									result[key].push(value);
								} else {
									result[key] = value;
								}
							}
						}
					}
				}
			return result;
		});
		return options.hash ? data : Object.toQueryString(data);
	},

	
	/* ___________ GENERELLE FUNKTIONEN __________ */
	/**
	 * Validate if an object is an array and if it contains a given value  
	 * 
	 * @param {object} arr
	 * @param {object} value
	 */
	
	isValueInArray: function(arr, value) {
		if (!Object.isArray(arr)) {
			return false;
		} else if (arr.indexOf(value) > -1) {
			return true;
		}
	},
	/**
	 * Get the referer, and use experimental staticOn flag
	 */
	getReferer: function() { 
		var url = window.location.pathname;
		if (window.location.search !== "") {
			url = url + window.location.search;
		}
		url = url.replace('yaf4ajn.war','yaf4ajn');
		if(this.staticOn) {
			var pos = url.search(/yaf4ajn/);
			if(pos > 0) {
				url = url.substring(0,pos) + this.staticResponseUrl +'//yaf4ajn//'+ url.substring(pos + 'yaf4ajn'.length + 1,url.length);
			} else if (url.search(/index.php/) > 0) {
				url = '..' + this.staticResponseUrl +'/index.php';
			} else {
				url = window.location.pathname.replace(/yaf4ajn/g, this.staticResponseUrl+"/yaf4ajn");
			}
			pos = (url.lastIndexOf('\\') > pos) ? url.lastIndexOf('\\') : url.lastIndexOf('/');
			var file = url.substring(pos+1,url.length);
			if (file.indexOf('-') > 0) {
				 file = file.replace(file.match(/-.*\./g),'.');
				 url = url.substring(0, pos+1) + file;
			}
		}
		return url;
	},
	/**
	 * Get the data for the server request.
	 * 
	 * @param {string} type - load or send
	 * @param {string} scope
	 */
	getRequestBody: function(type, scope) { 
		var req = '{ "referer": "'+this.getReferer()+'", "times": ['+new Date().getTime()+']';
		if (type == 'load') {
			req += ', "request": '+ ((scope) ? ([scope]).toJSON():fec.pageControl.getScopes().toJSON());
		}
		if (type == 'send') {
			req += ', "request": ["' + fec.pageControl.getScopes()[0] + '"]';
		}
		req += this.getRequestBodyParams(type);
		var data = this.getRequestData(type);
		if (data !== "") {
			req += ', "data": '+data;
		}
		var gotoPage = fec.pageControl.getGotoPage();
		if (gotoPage !== "") {
			req += ', "gotoPage": "'+gotoPage+'"';
		}
		if (fec.pageControl.getPageId() !== "") {
			req += ', "pageid": "'+fec.pageControl.getPageId()+'"';
		}
		req += ' }';
		return req;
	},
	/**
	 * Get additional parameters for the server request to control the server.
	 * 
	 * @param {string} type - load or send
	 */
	getRequestBodyParams: function(type) {
		var params = $H({});
		if (type == 'load') {
			var pl = fec.pageControl.getControl('paramsLoad');
			if ( this.isValueInArray(pl,'relogin') ){ 
				params.set('action','relogin');
				if ($('fecParamWdywtg')) {
					params.set('wdywtg',$('fecParamWdywtg').value);
				}
				pl = pl.without('relogin');
			}
			if ( this.isValueInArray(pl,'merge') ){ 
				params.set('merge',true);
				pl = pl.without('merge');
			}
			if (pl && pl.size() > 0) {
				params.set('action',pl[0]);
			}
		}
		if (type == 'send') {
			var ps = fec.pageControl.getControl('paramsSend');
			if( ps && ps.size() > 0 ) {
				params.set('action',ps[0]);
			}
		}
		params = params.merge(fec.pageControl.getParameter());
		return (params.size() > 0) ? ', "parameter": '+params.toJSON() : "";
	},
	/**
	 * Disable the submit buttons.
	 * 
	 * @param {boolean} bol - disable or not
	 */
	disableSubmitButtons: function(bol) {
		var butValue;
		var buttons = $$('input[type="submit"]','input[type="button"]');
		if (this.submitButtons.size() === 0) {
			this.submitButtons = buttons.pluck('value');
		}
		//this.submitButtons.pop();
		for (var i = 0, len = buttons.size(); i < len; ++i) {
			butValue = fec.msg.FE010;
			for(var y = 0; y < buttons[i].title.length; ++y) {
				if (y%2 === 0) {
					butValue += " ";
				} else {
					butValue = " " + butValue;
				}
			}
			buttons[i].value=butValue;
			if (bol) {
				buttons[i].disable();
			} else {
				buttons[i].value = (this.submitButtons[i] === '') ? 'Weiter' : this.submitButtons[i];
				buttons[i].enable();
			}
		}
	},
	/**
	 * Disable the form.
	 * 
	 * @param {boolean} bol - disable or not
	 */
	disableForm: function(bol) {
		if ($(fec.config.htmlIdForm) !== null) {
			if (bol) {
				$(fec.config.htmlIdForm).disable();
			} else {
				$(fec.config.htmlIdForm).enable();
			}
		}
	},
	/**
	 * Returns the configured server url
	 * 
	 * @param {string} type - load or send
	 */
	getAjaxRequestUrl: function(type) {
		retVal = this.ajaxRequestUrl;
		if (this.staticOn) {
			retVal = ('send' == type) ? this.getReferer()+'.txt.send' : this.getReferer()+'.txt';
		}
		return retVal;
	},
	/**
	 * Scroll to top if possible.
	 */
	scrollToTop: function() {
    	var yPos =  window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
		if (yPos > 150)	{
			window.scrollTo(0,100);
		}
	},
	/**
	 * Scroll to bottom if possible.
	 */
	scrollToBottom: function() {
		window.scrollTo(0,document.viewport.getHeight());
	},
	/**
	 *  Checks the form and sets Variable isFormValid
	 *  
	 *  @returns {boolean} Form valid or not
	 */
	checkForm: function() {
		var retval = false;
		// Das Form nur pruefen wenn kein validate und wenn noch nicht geprueft wurde
		if (fec.pageControl.getParameter().get('validate') === undefined && !fec.main.isFormValid) {
			try {
				retval = eval(fec.config.jsCheckformPrefix + fec.pageControl.getScopes()[0].capitalize()+'();');
			} catch(e) { retval = true; }
		} else {
			retval = true;
		}
		fec.main.isFormValid = retval;
		return retval;
	},
	/**
	 * Inits the error output.
	 */
	initErrorList: function() {
		if($('vspScopeFehler') === null) {
			var s = "\n<div id='vspScopeFehler' style='display:none'>\n</div>\n";
			Try.these(
				function() {$$('h1')[0].insert({'after':s});},
				function() {$('content').insert({'top':s});},
				function() {$$('body')[0].insert({'top':s});}
			);			
			$('vspScopeFehler').hide();
		} else {
			$('vspScopeFehler').update('');
		}
	},
	/**
	 * Inits the error layout.
	 * 
	 * @param {string} errCss - Css class like error, warning, info
	 * @param {boolean} clear - init and clear existing errors
	 */
	initErrorBox: function(errCss, clear) {
		errCss = errCss.capitalize();
		if($('vspScopeFehler'+errCss) === null) {
			var s = "";
			if (errCss.toLowerCase()=='error') {
				s += "\n<div id='vspScopeFehler"+errCss+"' class='box large naked image "+errCss.toLowerCase()+"'>";
				s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+errCss.toLowerCase()+"'/>";
				s += "\n<ul id='vspErrorList"+errCss+"'></ul>";
				s += "\n</div>\n";
				s += "\n<div style='clear:both;height:1px'></div>";
			} else {
				s += "\n<div id='vspScopeFehler"+errCss+"' class='box large image "+errCss.toLowerCase()+"'>";
				s += "\n<h2 id='vspErrorHeader"+errCss+"'>" + fec.msg.FE001 +"</h2>";
				s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/48x48/"+errCss.toLowerCase()+".gif' alt='"+errCss.toLowerCase()+"'/>";
				s += "\n<ul id='vspErrorList"+errCss+"'></ul>";
				s += "\n</div>\n";
				s += "\n<div style='clear:both;height:1px'></div>";
			}
			$('vspScopeFehler').insert({'bottom':s});
			$('vspScopeFehler').hide();
		} else {
			if (clear) {
				$('vspErrorList'+errCss).update('');
			}
		}
	},
	/**
	 * Adds an error.
	 * 
	 * @param {object} err - error object
	 * @param {string} errtxt - a special errortext, that overrides the default
	 */
	errorAdd: function(err, errtxt) {
		var errObj = fec.main.getErrorObject(err, errtxt);
		// redirect?
		if (errObj.get('redir')) {
			var errUrl = fec.pageControl.getGotoUrl(errObj.get('redir'));
			// Todo jsunit
			// Verhindern, dass auf sich selbst geleitet wird
			if (errUrl === 'index.html' && errUrl === fec.util.getPageName()) {
				errUrl = '../' + errUrl;
			}
			fec.util.redirect(errUrl);
			return false;
		}
		// errObj verarbeiten
		var errCss = errObj.get('css') ? errObj.get('css').capitalize():"Error";
		this.initErrorBox(errCss,false);
		if (errObj.get('head')) {
			if ($('vspErrorHeader'+errCss)) {
				$('vspErrorHeader'+errCss).update(errObj.get('head'));
			}

		}
		//Fehler hinzufuegen 
		$('vspErrorList'+errCss).insert({'bottom':'<li>'+errObj.get('text')+'</li>\n'});
		//Fehler anzeigen
		this.showScope('vspScopeFehler',true);
		this.scrollToTop();
		this.disableSubmitButtons(false);
		this.showContainer();
		return true;
	},
	/**
	 * Resolves the error configuration.
	 * 
	 * @param {object} err - error object
	 * @param {string} errtxt - a special errortext, that overrides the default
	 */
	getErrorObject: function(err, errtxt) {
		//erstelle ErrorObjekt
		if (!errtxt || errtxt === "" || errtxt === "PAGE") {
			errtxt = fec.errorCode.EUNEXP;
		}
		var errid = Object.isString(err) ? err : err.Code;
		var errObj = new Hash();
		// Setze den Errortext als Template
		errObj.set('template', new Template(fec.errorCode[errid] ? fec.errorCode[errid] : errtxt));
		// setze die Templateparameter
		var params = new Hash();
		if (Object.isString(err)) {
			params.set('0', errtxt);
		} else {
			if (Object.isString(err.Args)) {
				params.set('0', err.Args);
			} else {
				if (err.Args) {
					for (var i = 0; i < err.Args.length; i++) {
						params.set(''+i, err.Args[i]);
					}
				}
			}
		}
		errObj.set('parameter', params);
		// merge mit vorhandender fec.errorConfig
		if (fec.errorConfig[errid]) {
			errObj = errObj.merge(fec.errorConfig[errid]);
		}
		var t = errObj.get('template').evaluate(errObj.get('parameter'));
		errObj.set('text', t);
		
		return errObj;
	}	
});


/**
 * PageControl class - This class holds the fecPagecontrol variable and 
 * offers getters and setters to the values 
 * 
 * @class FecPageControl 
 */
var FecPageControl = Class.create({
	/**
	 * Represents the pageControl object
	 * 
	 * @type object
	 * @default false
	 */
	pc: false,
	/**
	 * Parses the pageControl variable.
	 */
	initialize: function(pcString) {
		if (this.pc === false) {
			try { 
				// Wenn Fehler, dann saubere Fehlermeldung ausgeben
				// anstatt SyntaxException
				this.pc = pcString.evalJSON(true); 
			}catch(e){
				fec.main.errorAdd('',fec.msg.FE002);
			}
			this.processErrorConfig();
		}
	},
	/**
	 * @returns {array} array of scopes 
	 */
	getScopes: function(){ 
		return Object.isString(this.pc.scopes) ? [this.pc.scopes] : this.pc.scopes; 
	},
	/**
	 * @returns {object} Value for the given control-key.  
	 */
	getControl: function(control){ return this.pc[control]; },
	/**
	 * @params {string} control - Key for a server-control
	 * @params {string} value - Value for a server-control variable
	 */
	setControl: function(control, value){ 
		this.pc[control] = Object.isArray(value) ? value : [value]; 
	},	
	/**
	 * @returns {object} Server parameters for the request. 
	 */
	getParameter: function(){
	 	return (typeof this.pc.parameter == 'undefined') ? $H({}) : $H(this.pc.parameter);
	},
	/**
	 * Additional parameters to control the server (through request).
	 * 
	 * @params {string} key - key for an additional parameter
	 * @params {string} val - value for an additional parameter
	 */
	setParameter: function(key, value){
		var params = this.getParameter();
		params.set(key, value);
		this.pc.parameter = params;	
	},
	/**
	 * Set special parameter for a load request
	 * 
	 * @params {string} val - a loading parameter like void, merge
	 */
	setParamsLoad: function(val){
	 	this.pc.paramsLoad = val;
	},
	/**
	 * Server can return urls or ids. Ids are the better option, so the server
	 * doesn't have to know about urls. Urls can be configured completely in the
	 * frontend. 
	 * 
	 * @params {string} gotoid - An id to a page or an url. Url will be returned
	 * immediately, and id will be resolved.
	 */
	getGotoUrl: function(gotoid){
		if (gotoid.include('.')) {
			return gotoid;
		}
		var retval = '';
		gotoid = gotoid.toLowerCase();
		if (this.pc.gotos && this.pc.gotos[gotoid]) {
			retval = this.pc.gotos[gotoid];	
		} else {
			if (this.gotoPageUrls[gotoid]) {
				retval = this.gotoPageUrls[gotoid];
			} else {
				retval = fec.util.getErrorPage('getGoto_'+gotoid);
			}
		}
		return retval;
	},
	/**
	 * Mapping of URLs to Ids
	 */
	gotoPageUrls: {
		page_01_01 : "page0101.html"
	},		
	/**
	 * @returns {string} URL where the server should redirect after completion 
	 */
	getGotoPage: function(){
	 	return (typeof this.pc.gotoPage == 'undefined') ? "" : this.pc.gotoPage;
	},
	/**
	 * @params {string} gotoPage - URL where the server should redirect after 
	 * completion
	 */
	setGotoPage: function(gotoPage){
	 	this.pc.gotoPage = gotoPage;
	},
	/**
	 * @returns {string} pageId of pagecontrol or empty-string 
	 */
	getPageId: function() {
	 	return this.pc.pageid ? this.pc.pageid : "";
	},
	/**
	 * Parse a special error baheviour, given by the pageControl. So you can 
	 * define a special behaviour per page.
	 */
	processErrorConfig: function() {
		if (this.pc.errorconfig) {
			var errconfs = $H(this.pc.errorconfig);
			errconfs.each(function(err) {
				fec.errorConfig[err[0]] = err[1]; 
			}.bind(this));
		}
	}
});




/* _____________________ CONFIG MISCELLANEOUS _______________________________ */

/**
 * ErrorCode Mapping - Maps an ErrorCode from the response to a text.
 * 
 * @namespace fec.errorCode 
 */
fec.errorCode = {
	EUNEXP : 'unexpected error'
};

/** 
 * ErrorCode behaviour configuration - You can override the default behavior for 
 * an error. These values are available:
 *   redir - redirect immediately to redir-URL  
 *   head - title text
 *   css - css classname
 *   text - errortext, overrides fec.errorCode
 *
 * @namespace fec.errorConfig 
 */
fec.errorConfig = {
	EUNEXP : {
		head : 'Hinweis',
		css  : 'info'
	} 
};

/**
 * Messages mappings - Inhere you find all text-fragments that will be shown 
 * to the user.
 * 
 * @namespace fec.msg 
 */
fec.msg = {
	YES	: 'Ja',
	NO	: 'Nein',
	FE001 : 'Es ist ein Fehler aufgetreten!',
	FE002 : 'pageControl String nicht valide',
	FE003 : 'Bitte korrigieren Sie folgende Eingabefelder',
	FE004 : 'Formularfehler',
	FE005 : 'ist ein Pflichtfeld.',
	FE006 : 'darf maximal ##MAX## Zeichen lang sein.',
	FE007 : 'muss mindestens ##MIN## Zeichen lang sein.',
	FE008 : 'muss zwischen ##MIN## und ##MAX## Zeichen lang sein.',
	FE009 : 'Achtung, diese Webseite wurde nicht von der vorgesehenen URL "##HOST##" geladen. Bitte geben Sie hier und in den verlinkten Systemen keine pers&ouml;nliche Daten ein.',
	FE010 : 'Bitte warten ..'
};



/* _____________________ EXAMPLE HOOK IMPLEMENTATIONS _______________________ */
/**
 * An example for a special data-value setter. Default behaviour is replacing a 
 * data value (from the server) with the the corresponding element-id. But if
 * this is not enough you can implement special setters. Valuekey is the
 * data-key.
 * 
 * @example fecHookSetDataValuekey
 */
function fecHookSetDataValuekey() {
	if (fecSetterValue !== undefined) {
		// spezial handling of the setterValue
		$('elementid').update(fecSetterValue);
	}
}
/**
 * An example for a form-validation function, that will be called automatically.
 * "Scopename" must be the same as the page-scope-name, therefore you can have
 * only one check-funktion for every scope you use
 * 
 * @example fecHookCheckFormScopename
 */
function fecHookCheckFormScopename() {
	fecForm.initFormValidation();
	var inputs = document.getElementsByTagName('input'), input = false;
	for (var i = 0, len = inputs.length; i < len; i++){
		input = inputs[i];
		// different checks for every formfield
		if (input.name == fec.util.eleId('html-id-name')) {
			if (input.value === '') { 
				fecForm.setErrorInDom(input,"html-id-name is empty"); 
			}
		}
	}
	return fecForm.checkIfFormValid();
}


