1 /**
  2  * yaf4ajn.js - Frontendcontroller 1.90.0
  3  * Copyright(c) 2009, Deutsche Telekom AG, Products & Innovation
  4  * GNU LESSER GENERAL PUBLIC LICENSE
  5  * http://www.gnu.org/licenses/lgpl-3.0.txt
  6  *
  7  * What is it? 
  8  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  9  * This is the Javascript part of the AJAX Hello World 1.90.0 Demo from Deutsche 
 10  * Telekom AG, Products & Innovation for the iX article.
 11  * If this script is included in a webpage, it will start an AJAX call to a server 
 12  * and manipulate the html page depending on the answer from the server. 
 13  * Data exchange between the server and client happens with JSON data-inter
 14  * -change format.
 15  * Dependencies are prototype 1.6.0.3 and effects.js from script.aculo.us 1.8.2.
 16  * This code is tempered with jslint (see tools/jslint). It has a sound set of 
 17  * UnitTests (see tools/jsunit) and it has documentation (see tools/jsdoc and 
 18  * docs-directory).
 19  * This file is also available as a compressed version (see tools/compressed and
 20  * yaf4ajn-compressed.js in js-directory.)
 21  * 
 22  * Where to go? 
 23  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 24  * First examine the fecPagecontrol variable in the HTML page (where this
 25  * script is included). It defines what the Frontendcontroller (fec) should do.
 26  * (e.g. scopes - load data given by the scopenames)
 27  * 
 28  * After the DOM of the webpage is loaded, these functions will be called
 29  * automatically:
 30  * >>> fec.base.initialize
 31  *     Initialize prototype-JS classes, read fecPagecontrol and start 
 32  *     dataLoad
 33  * >>> fec.base.dataLoad
 34  *     loads the data depending on given scopes in fecPagecontrol by firing AJAX
 35  *     requests to the server. If the server gives a valid response (HTTP2XX)
 36  *     processResponse is called
 37  * >>> fec.main.processResponse
 38  *     This function receives the response data from the server and starts to
 39  *     process it. Depending on the response, requests will be fired again, the 
 40  *     DOM of the will be manipulated or errormessages will be shown.
 41  *
 42  * Please use Firefox with Firebug-debugger to examine further functionality
 43  * of this script. 
 44  * 
 45  * Change Log:
 46  * v1.90.0
 47  * - renamed this file to yaf4ajn.js
 48  * - complete namespace refactoring (fec)
 49  * - extracted configuration variables (fec.config)
 50  * - replaced references to production environment (vespa, vsp -> fec)
 51  * - fixed jslint findings 
 52  * - added documentation
 53  */
 54 /*! yaf4ajn.js - Deutsche Telekom AG, Products & Innovation */
 55 /*jslint laxbreak: true */
 56 
 57 
 58 /* ___________ INIT GLOBAL VARIABLES/OBJECTS ________________________________ */
 59 
 60 var 
 61 	/**
 62 	 * Main object holder (namespace fec -> FrontEndController). In this object
 63 	 * all important variables and functions are placed in a class-type hierarchy.
 64 	 * 
 65 	 * @namespace Main object holder.
 66 	 */   
 67 	fec = {}, 
 68 	/**
 69 	 * Holds the data that can be used by special DOM-setter (hook
 70 	 * jsSetterPrefix)
 71 	 * 
 72 	 * @type object
 73 	 * @default undefined
 74 	 * @see fec.config.jsSetterPrefix
 75 	 */
 76 	fecSetterValue = undefined, 
 77 	/** 
 78 	 * Variable for an exception
 79 	 * 
 80 	 * @type object
 81 	 * @default undefined
 82 	 */
 83 	e = undefined;
 84 
 85 /** instance for class FecMain */
 86 fec.main = undefined;
 87 /** instance for class FecPageControl */ 
 88 fec.pageControl = undefined;
 89 
 90 
 91 /* ___________ CONFIGIGURATION  _____________________________________________ */
 92 /**
 93  * Base configuration class
 94  * 
 95  * @namespace fec.config 
 96  */
 97 fec.config = {
 98 	/**
 99 	 * Element-Id of your HTML-form. 
100 	 *
101 	 * @type string
102 	 * @default "fecForm"
103 	 * @constant
104 	 */	
105 	htmlIdForm : 'fecForm', 
106 	/**
107 	 * Prefix of element-Ids, which should be replaced.
108 	 * 
109 	 * @type string
110 	 * @default "fecData"
111 	 * @constant
112 	 */	
113 	htmlIdDataPrefix : 'fecData',   
114 	/**
115 	 * Prefix of element-Id, which will be shown after completion  (initially hidden)
116 	 * 
117 	 * @type string
118 	 * @default "fecScope"
119 	 * @constant
120 	 */	
121 	htmlIdScopePrefix : 'fecScope',
122 	/**
123 	 * Prefix of element-Id, which will be hidden after completion (initially visible)
124 	 * 
125 	 * @type string
126 	 * @default "fecScopeLoad"
127 	 * @constant
128 	 */	
129 	htmlIdLoaderPrefix : 'fecScopeLoad',
130 	/**
131 	 * Prefix of JS-Functions, which should be called to set data.
132 	 * 
133 	 * @type string
134 	 * @default "fecHookSetData"
135 	 * @constant
136 	 * @see fecHookSetDataValuekey
137 	 */	
138 	jsSetterPrefix : 'fecHookSetData', 
139 	/**
140 	 * Prefix of JS-Functions, which validate the form
141 	 * 
142 	 * @type string
143 	 * @default "fecHookCheckForm"
144 	 * @constant
145 	 * @see fecHookCheckFormScopename
146 	 */	
147 	jsCheckformPrefix : 'fecHookCheckForm',
148 	/**
149 	 * URL to the server for ajax request
150 	 * 
151 	 * @type string
152 	 * @default "/app/json"
153 	 * @constant
154 	 * @see http://www.prototypejs.org/api/ajax/request
155 	 */	
156 	urlAjaxRequest : '/app/json',
157 	/**
158 	 * URL to an errorpage
159 	 * 
160 	 * @type string
161 	 * @default "/error.html"
162 	 * @constant
163 	 */	
164 	urlErrorpage : '/error.html'
165 		
166 };
167 // look for miscellaneous config-objects at the end of the file
168 
169 
170 /* ___________ START INIT AFTER PAGE IS LOADED ______________________________ */
171 document.observe("dom:loaded", function() { fec.base.initialize(); });
172 
173 
174 /**
175  * Base class for the main public fec methods 
176  * 
177  * @namespace fec.base 
178  */
179 fec.base = {
180     /**
181 	 * This function is called, after the DOM is loaded. It prepares the page,
182 	 * initializes classes and starts an AJAX request to load data from the
183 	 * server (dataLoad).
184 	 */		
185 	initialize: function(){
186 		try { fecHookInitialize(); } catch (e) {}
187 		if (fec.util.escapeFrame()) { return; }
188 		fec.util.keypressObserver(document);
189 		if (fec.main === undefined) { 
190 			fec.main = new FecMain(); 
191 		}
192 		if ('undefined' != typeof fecPagecontrol) { 
193 			fec.pageControl = new FecPageControl(fecPagecontrol);
194 			if (fec.pageControl.getScopes().length > 0) {
195 				fec.base.dataLoad();
196 				$$('form[id^="'+fec.config.htmlIdForm+'"]').each ( function (fe) {
197 					fec.util.submitObserver(fe.id);
198 				});
199 			}
200 		}
201 	},
202 	/**
203 	 * Prepares the page and starts loading data from the server.
204 	 */
205 	dataLoad: function (){
206 		fec.main.showSpinner(fec.pageControl.getScopes(),true);
207 		try { fec.form.clearErrorsInDom(); } catch(e){}
208 		fec.main.getServerData();
209 	},
210 	/**
211 	 * Prepares the page and starts sending data to the server. This function
212 	 * will be called if a form-submit event (through event observer) fires but
213 	 * it can also be called directly.
214 	 */
215 	dataSend: function () {
216 		fec.main.showScope('vspScopeFehler',false);
217 		if (fec.main.checkForm()) {
218 			fec.main.disableSubmitButtons(true);
219 			fec.main.sendServerData();
220 		} else { fec.main.scrollToTop(); }
221 	}
222 };
223 
224 /**
225  * Values Factory handles updating an html-element with a new value
226  * 
227  * @namespace fec.valuesFactory 
228  */
229 fec.valuesFactory = {
230 	/**
231 	 * Updates a TD-Element with newValue. If the Value is boolean, default
232 	 * Strings are set.
233 	 * 
234 	 * @param {object} element Element that will be updated
235 	 * @param {object} newValue newValue
236 	 */ 
237 	td: function(element, newValue){
238 		if (typeof newValue == 'boolean') {
239 			newValue = newValue === true ? fec.msg.YES : fec.msg.NO; 
240 		}
241 		$(element).update(newValue.toString());
242 	},
243 	/**
244 	 * Updates a P-element by calling fec.valuesFactory.td .  
245 	 * 
246 	 * @see fec.valuesFactory.td
247 	 */
248 	p: function(element, newValue){
249 		fec.valuesFactory.td(element, newValue);
250 	},
251 	/**
252 	 * Updates a SPAN-element by calling fec.valuesFactory.td .  
253 	 * 
254 	 * @see fec.valuesFactory.td
255 	 */
256 	span: function(element, newValue){
257 		fec.valuesFactory.td(element, newValue);
258 	},
259 	/**
260 	 * Updates a INPUT-element, accordingly to the input type
261 	 * 
262 	 * @param {object} element Element that will be updated
263 	 * @param {object} newValue newValue will be HTML-unescaped
264 	 */
265 	input: function(element, newValue){
266 		switch (element.type.toLowerCase()) {
267 			case 'submit': return true;
268 			case 'hidden':
269 				if ($(element.name + 'Hidden')) {
270 					$(element.name + 'Hidden').update(newValue.toString().unescapeHTML());
271 				}
272 				fec.valuesFactory.textarea(element, newValue);
273 				break;
274 			case 'password':
275 			case 'text':
276 				fec.valuesFactory.textarea(element, newValue);
277 				break;
278 			case 'checkbox':
279 			case 'radio':
280 				fec.valuesFactory.inputSelector(element, newValue);
281 				break;
282 			}
283 		return false;
284 	},
285 	/**
286 	 * Handles lists of radio- and checbox-tags
287 	 * 
288 	 * @param {object} element Element that will be updated
289 	 * @param {object} newValue newValue
290 	 * @private
291 	 */
292 	inputSelector: function(element, newValue){
293 		fields = document.getElementsByName(element.name);
294 		for (var i = 0; i < fields.length; i++) {
295 			if (fields[i].type == 'radio') {
296 				if (fields[i].value == newValue.toString()) {
297 					fields[i].checked = true;
298 				}
299 			} else {
300 				if (fields[i].type == 'checkbox') {
301 					fields[i].checked = (newValue.toString() == "true") ? true : false;
302 				}
303 			}
304 		}
305 	},
306 	/**
307 	 * Updates a TEXTAREA-element
308 	 * 
309 	 * @param {object} element Element that will be updated
310 	 * @param {object} newValue newValue will be HTML-unescaped
311 	 */
312 	textarea: function(element, newValue){
313 		element.value = newValue.toString().unescapeHTML();
314 	},
315 	/**
316 	 * Selects the option of a SELECT-element
317 	 * 
318 	 * @param {object} element Element that will be updated
319 	 * @param {object} newValue newValue
320 	 */
321 	select: function(element, newValue){
322 		var value = '', opt;
323 		for (var i = 0; i < element.options.length; i++) {
324 			if (element.options[i].value == newValue) {
325 				element.selectedIndex = i;
326 				return true;
327 			}
328 		}
329 	}
330 };
331 
332 /**
333  * General form checking class - before data is submitted to the server, the
334  * formdata can be checked through javascript hooks (fecHookCheckFormXYZ). This
335  * class contains functions for validation.
336  * 
337  * @namespace fec.util
338  * @see FecMain.checkForm
339  */
340 fec.form = {
341 	/**
342 	 * Keeps the state of form-validation, only if this property is true data
343 	 * will be submitted to the server.
344 	 * 
345 	 * @type boolean
346 	 */	
347 	isValid: true,
348 	/**
349 	 * Counts the form-errors
350 	 * 
351 	 * @type number
352 	 */
353 	errorCount: 0,
354 	/**
355 	 * Prepares the page for JS-Form Validation. This method is called from the
356 	 * JS-validation-hook-functions.
357 	 * 
358 	 * @see fecHookCheckFormScopename
359 	 * @todo extract html to config-class
360 	 */
361 	initFormValidation: function() {
362 		this.errorCount = 0;
363 		this.isValid = true;
364 		var s = "";
365 		s += "\n<div id='vspFormError' class='large box naked image error' style='display:none;'>"; 
366 		s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+ fec.msg.FE004 +"'/>";
367 		s += "\n<h3>"+ fec.msg.FE003 +":</h3>"; 
368 		s += "\n<ul class='errorlist' id='vspFormErrorList'>\n</ul>"; 
369 		s += "\n</div>\n";
370 		s += "\n<div style='clear:both;height:1px'></div>";
371 		if($('vspFormError') === null) {
372 			if ($('vspScopeFehler')) { 
373 				$('vspScopeFehler').insert({'after':s}); 
374 			} 
375 		} else {
376 			if ($('vspFormError').childElements().size() < 1) {
377 				$('vspFormError').replace(s);
378 			} else {
379 				this.clearErrorsInDom();
380 
381 			}
382 		}
383 	},
384 	/**
385 	 * Adds an error to the DOM and tries to change the class of the surrounding 
386 	 * HTML code of the input field
387 	 * 
388 	 * @param {object} element - HTML-Element that should me marked
389 	 * @param {string} errtxt - Errortext 
390 	 */
391 	setErrorInDom: function(element, errtxt) {
392 		this.errorCount++;
393 		try{
394 			try {
395 				var trElement = element.up('tr');
396 				trElement.addClassName('error');
397 				var tdElements = trElement.select('td');
398 				if (tdElements.length > 1) {
399 					tdElements[0].addClassName('left');
400 					tdElements[1].addClassName('right');
401 				}
402 				if (this.errorCount==1) {
403 					element.focus();
404 				} 
405 			} catch (e) {}
406 			if (errtxt) {
407 				$('vspFormErrorList').insert({'bottom':'<li>'+errtxt+'</li>\n'});
408 			}
409 		} catch(e) {}
410 		this.isValid = false;
411 	},
412 	/**
413 	 * Clear the errors on the page and try to remove error-CSS-classes.
414 	 */
415 	clearErrorsInDom: function() {
416 		// Entferne rote Umrandungen
417 		var trElements = $$("tr[class*='error']");
418 		try {
419 			for(var i=0; i<trElements.length; i++) {
420 				trElements[i].removeClassName('error');
421 				trElements[i].select('td').each(function(element) {
422 					if (element.hasClassName('left')) {
423 						element.removeClassName('left');
424 					}
425 					if (element.hasClassName('right')) {
426 						element.removeClassName('right');
427 					}
428 				});
429 			}
430 		} catch (e) {}
431 		// Entferne Fehlermeldung in Box und blende Box aus
432 		if ($('vspFormErrorList')) {
433 			$('vspFormErrorList').update("");
434 		}
435 		if ($('vspFormError')) {
436 			$('vspFormError').hide();
437 		}
438 		this.isValid = true;
439 	},
440 	/**
441 	 * Checks if the form ist valid and returns true or shows the ErrorBox.
442 	 * 
443 	 * @returns {boolean} valid or not
444 	 */
445 	checkIfFormValid: function() {
446 		var warn = $('vspFormError');
447 		if (this.isValid) {
448 			warn.hide();
449 			return true;
450 		} else {
451 			if ('undefined' != typeof Effect) {
452 				var x = new Effect.Appear( warn, { duration:0.3, keepBackgroundImage:true } );
453 			} else {
454 				warn.show();
455 			}
456 			return false;
457 		}
458 	},
459 	/**
460 	 * Checks if the value is a number.
461 	 * 
462 	 * @param {object} val - input value
463 	 * @returns {boolean} number or not
464 	 */
465 	checkTypeNumber: function(val) {
466 		var regex = /^\d+$/;
467 		return regex.test(val);
468 	},
469 	/**
470 	 * Converts a date Sring DD.MM.YYYY to date object
471 	 * 
472 	 * @param {String} val - Datestring in format DD.MM.YYYY
473 	 * @returns {Date|null} Date-object or null
474 	 */
475 	stringToDate: function (val) {
476 		var regex = /^\d{2}(\.)\d{2}\1\d{4}$/;
477 		if (regex.test(val)) {
478 			var parts = val.split(".");
479 			if (3 == parts.length) { 
480 				var testdate =  new Date(parts[2],parts[1]-1,parts[0]);
481 				var testyear = testdate.getFullYear();
482 				var testmonth = testdate.getMonth()+1; 	
483 				var testday = testdate.getDate(); 		
484 				if (testyear == parts[2] && testmonth == parts[1]*1 && testday == parts[0]*1) {
485 					return testdate;
486 				}
487 			}
488 		}
489 		return null;
490 	},
491 	/**
492 	 * checks if String is a date
493 	 * 
494 	 * @param {object} val - input value
495 	 * @returns {boolean} is a Date-object or not
496 	 */
497 	checkTypeDate: function(val) {
498 		var testdate = fec.form.stringToDate(val);
499 		return null !== testdate;
500 	},
501 	/**
502 	 * checks if the input value is in the past
503 	 * 
504 	 * @param {object} val - input value
505 	 * @returns {boolean}
506 	 */
507 	checkTypeDateInPast: function(val) {
508 		return fec.form.checkTypeDate(val) && fec.form.stringToDate(val) < new Date();
509 	},
510 	/**
511 	 * checks if the input value is a german zipcode
512 	 * 
513 	 * @param {object} val - input value
514 	 * @returns {boolean}
515 	 */
516 	checkTypePostleitzahl: function(val) {
517 		var regex = /^[0-9]{5}$/;
518 		return regex.test(val);
519 	},
520 	/**
521 	 * checks if the input value is a HausnummerMitZusatz ;)
522 	 * 
523 	 * @param {object} val - input value
524 	 * @returns {boolean}
525 	 */
526 	checkTypeHausnummerMitZusatz: function(val) {
527 		var regex = /^[1-9]{1}[0-9a-zA-Z \-]*$/;
528 		return regex.test(val);
529 	},
530 	/**
531 	 * Checks if the input value is a email adresse - intentionally weak check.
532 	 * Serverside validation should go deeper.
533 	 * 
534 	 * @param {object} val - input value
535 	 * @returns {boolean}
536 	 */
537 	checkTypeEmail: function(val) {
538 		var regex = /.*@.*\..*/;
539 		return regex.test(val);
540 	},
541 	/**
542 	 * regex check helper function
543 	 * 
544 	 * @param {Regex} regex - the regex test string
545 	 * @param {string} value - String on which the regex-condition is tested
546 	 * @returns {boolean}
547 	 */
548 	checkRegex: function(regex, val) {
549 		return regex.test(val);
550 	},
551 	/**
552 	 * Set an HTML input field to readonly.
553 	 * 
554 	 * @param {object} element - HTML input value element
555 	 * @param {boolean} setIt - set it to readonly or not
556 	 */
557 	setElementReadonly: function(element, setIt) {
558 		if(setIt) {
559 			element.readOnly = true;
560 			element.value = '';
561 			element.style.backgroundColor = '#EEEEEE';
562 		} else {
563 			element.readOnly = false;
564 			element.style.backgroundColor = '';
565 		}
566 	},
567 	/**
568 	 * Checks if a string validates to certain conditions and adds errors 
569 	 * if its not.
570 	 * 
571 	 * @param {object} input - HTML input value element
572 	 * @param {boolean} manda - is it mandatory?
573 	 * @param {number} min - minimal string length
574 	 * @param {number} max - maximal string length
575 	 * @param {string} text - The beginning of the error message, e.G. "The name" or "The birthday".
576 	 * @returns {boolean} valid or not
577 	 */
578 	checkTypeString: function(input, manda, min, max, text) {
579 		var retval = true;
580 		if (input) {
581 			var s = input.value;
582 			var err = text.strip();
583 			if (manda && s.length === 0) {
584 				fec.form.setErrorInDom(input, err+ " " + fec.msg.FE005);
585 				retval = false;
586 			} else if(s.length > 0){
587 				if (min === null || max === null) {
588 					if (min === null && max !== null && s.length > max) {
589 						err += " " + fec.util.formatMessage(fec.msg.FE006, {'MAX' : max});
590 					}
591 					if (max === null && min !== null && s.length < min) {
592 						err += " " + fec.util.formatMessage(fec.msg.FE007, {'MIN' : min});
593 					}					
594 				} else if (s.length < min || s.length > max) {
595 					err += " " + fec.util.formatMessage(fec.msg.FE008, {'MIN' : min, 'MAX' : max});
596 				}
597 				if (err.length > text.strip().length) {
598 					fec.form.setErrorInDom(input, err);
599 					retval = false;
600 				}
601 			}
602 		}
603 		return retval;		
604 	}
605 };
606 
607 /**
608  * General utils class
609  * 
610  * @namespace fec.util
611  */
612 fec.util = {
613 	/** 
614 	 * StartTime for alertTime function (for basic profiling) 
615 	 */ 
616 	timeStart : new Date().getTime(),
617 	
618 	/** 
619 	 * Formats a message with object-values
620 	 * 
621 	 * @param {string} msg - message-String with placeholders
622 	 * @param {object} hobj - object with values
623 	 */ 
624 	formatMessage:function(msg, hobj) {
625 		var tokens = msg.match(/##\w+##/g);
626 		for (var i=0; i<tokens.length; i++) {
627 			var varStr = tokens[i].replace(/#/g, '');
628 			eval("msg = msg.replace(/"+tokens[i]+"/g, '"+hobj[varStr]+"');");
629 		}
630 		return msg;
631 	},
632 	/** 
633 	 * Adds functions to observation list, which are executed when DOM ist loaded  
634 	 * 
635 	 * @param {object} func - a function
636 	 */ 
637 	addLoadEvent:function(func) {
638 		document.observe("dom:loaded",func);
639 	},
640 	/** 
641 	 * Adds forms to observation list, which will processed when the submit event
642 	 * is fired  
643 	 * 
644 	 * @param {object} elem - Form-Element
645 	 */ 
646 	submitObserver: function(elem) {
647 		Event.observe(elem, 'submit', 
648 			function(event) {
649 				fec.base.dataSend(); 
650 				Event.stop(event);
651 			}
652 		);		
653 	},
654 	/** 
655 	 * Adds keypress-interception for the RETURN KEY - if return key is send, 
656 	 * then the defaultbutton behaviour will be executed or the form will be 
657 	 * processed (dataSend).     
658 	 * 
659 	 * @param {object} elem - document
660 	 */ 
661  	keypressObserver: function (elem) {
662 		Event.observe(elem, 'keypress',function(event) {
663  			if(event.keyCode == Event.KEY_RETURN) {
664 				try {
665 					var defaultbutton = $('defaultbutton');
666 					var onclick = defaultbutton.readAttribute("onclick");
667 					if (null !== onclick) {
668 						eval(onclick);
669 					} else {
670 						fec.base.dataSend();
671 					}
672 					Event.stop(event);
673 				} catch (e) {}
674 			}
675 		});
676 	},
677 	/** 
678 	 * Sets a gotoPage and processes the form (dataSend) without JS-validation   
679 	 * 
680 	 * @param {string} url - URL where the server should redirect
681 	 */ 
682 	submitWithGotoPage: function (url) {
683 		fec.pageControl.setGotoPage(url);
684 		fec.pageControl.setParameter('validate',false);
685 		fec.base.dataSend();
686 	},
687 	/** 
688 	 * Sets a action and processes the form (dataSend) without JS-validation   
689 	 * 
690 	 * @param {string} url - URL where the server should redirect
691 	 */ 
692 	submitWithAction: function (action) {
693 		fec.pageControl.setControl('paramsSend', action);
694 		fec.pageControl.setParameter('validate', false);
695 		fec.base.dataSend();
696 	},
697 	/** 
698 	 * Turn an undefined object into an empty string and add a suffix-string   
699 	 * 
700 	 * @param {object} value - tested object
701 	 * @param {string} suffix - suffix String
702 	 * @returns {string} 
703 	 */ 
704 	chkUndef: function(value, suffix) {
705 		return ((value) ? value:'') + ((suffix) ? suffix:'');
706 	},
707 	/** 
708 	 * Basic profiling - add this function in your code, and the time
709 	 * between the initial load of the page and the code will be put at the end 
710 	 * of the page
711 	 * 
712 	 * @param {string} uc - UseCase, addition information
713 	 */	
714 	alertTime: function(uc){
715 		var now = new Date().getTime();
716 		var s = '<br/>' + uc +': '+ (now - fec.util.timeStart) + 'ms'; 
717 		$$('body')[0].insert({'bottom':s});
718 	},
719 	/**
720 	 * Reads the URL query parameters, put the keys to lower case and return the
721 	 * parameters as a hash
722 	 * 
723 	 * @return {hash} query as hash object
724 	 */
725 	getQueryParams: function() {
726 		var query = new Hash();
727 		// try wegen %% in URI
728 		try {
729 			query = $H(window.location.search.toQueryParams());
730 			// alle keys toLower
731 			query = query.inject(new Hash(), 
732 				function(hash, obj) {
733 					hash.set(obj[0].toLowerCase(),obj[1]);
734 					return hash;
735 				}
736 			);
737 		} catch (e) {}
738 		return query;
739 	},	
740 	/**
741 	 * Shortcut to openWindow, with less amount of parameters.
742 	 * @see fec.util.openWindow 
743 	 */
744 	popup: function(w, h, site, opts) {
745 		fec.util.openWindow(site, 'popUpWin', w, h, null, null, null, opts, false);
746 	},
747 	/**
748 	 * Pop-Up function
749 	 * 
750 	 * @param {string} url - popup-url 
751 	 * @param {string} winname - window name  
752 	 * @param {number} width 
753 	 * @param {number} height 
754 	 * @param {null|boolean} toolbr - with a toolbar? 
755 	 * @param {null|boolean} scrollbrs - with scrollbars? 
756 	 * @param {null|boolean} resizable - resizable? 
757 	 * @param {string} winoptions - more options
758 	 * @param {null|boolean} trackit - refresh parent after popup closes 
759 	 * @returns {object} pop-window object  
760 	 */
761 	openWindow: function(url, winname, width, height, toolbr, scrollbrs, resizable, winoptions, trackit) {
762 	    var posx = screen.availWidth/2 - width/2;
763 	    var posy = screen.availHeight/2 - height/2;
764 	    var xyPos = 'top=' + posy + ',left=' + posx + ',screenX=' + posx + ',screenY=' + posy;
765 	    var opt = ''; 
766 		opt += 'width=' + width + ',height=' + height;
767 		if (toolbr !== null) {
768 			opt += ',toolbar=' + (toolbr ? 'yes':'no');
769 		}
770 		if (scrollbrs !== null) {	 
771 			opt += ',scrollbars=' + (scrollbrs ? 'yes':'no');
772 		}
773 		if (resizable !== null) { 
774 			opt += ',resizable=' + (resizable ? 'yes':'no');
775 		}
776 		opt += ', ' + xyPos;
777 		opt += (winoptions) ? ', '+winoptions : '';
778 		var newWindow = window.open(url, winname, opt);
779 		newWindow.focus();
780 		if (trackit !== null && trackit) {
781 			var x = new PeriodicalExecuter(function(pe) { 
782 				if (newWindow.closed) { 
783 					pe.stop();
784 					fec.util.redirectReplace(self.location.href); 
785 				}			
786 			}, 0.5);
787 		}
788 		return newWindow;
789 	},
790 	/**
791 	 * Get the document dimensions.
792 	 * 
793 	 * @returns {object} { width: Number, height: Number }
794 	 */
795 	getDimensions: function() {
796 		var dimensions = document.viewport.getDimensions();
797 		if (dimensions.width === 0 || dimensions.height === 0) {
798 			if (document.body) {
799 				dimensions.width = document.body.offsetWidth;
800 				dimensions.height = document.body.offsetHeight; 
801 			}
802 		}
803 		return dimensions;
804 	},
805 	resizeAndCenter: function(width, height) {
806 		self.resizeTo(width, height);
807 		self.moveTo(Math.floor((screen.availWidth-width)/2), Math.floor((screen.availHeight-height)/2));
808 	},
809 	maximizeWindow: function() {
810 		window.moveTo(0,0);
811 		window.resizeTo(screen.availWidth,screen.availHeight);
812 	},
813 	escapeFrame: function () {
814 		if (parent && (self != parent)) {
815 			try {
816 				parent.document.body.style.color = '#000000';
817 				parent.location.href = self.location.href;
818 				return true;
819 			} catch (e) {}
820 		}
821 		return false;
822 	},
823 	setLocation: function(url, toTop) {
824 		if (toTop && toTop.toString() == 'true') {
825 			top.location.replace(url);			
826 			top.focus();
827 		} else {
828 			self.location.replace(url); 
829 			self.focus();
830 		} 
831 	},
832 	getImageRoot: function(url) {
833 		var p = 'http://localhost:8080';
834 		if ($('defaultImage')) {
835 			var src = $('defaultImage').src;
836 			p = src.substr(0, src.search('fileadmin'));
837 		}
838 		p = p.endsWith('/') ? p : p+'/';
839 		if (url) {
840 			url = (url.startsWith('/') ? url.sub('/', '') : url);
841 		}
842 		return (url) ? p+url : p;
843 	},
844 	getPageAddressRoot: function(url){
845 		var u = '#{protocol}//#{domain}#{port}'.interpolate({
846 			protocol: location.protocol,
847 
848 			domain: document.domain,
849 			port: location.port ? ':' + location.port : ''
850 			})	+ '/';
851 		if (url) {
852 			url = (url.startsWith('/') ? url.sub('/', '') : url);
853 		}
854 		return (url) ? u+url : u;
855 	},
856 	getErrorPage: function(errid, code) {
857 		return fec.config.urlErrorpage + ((errid) ? ('?errid='+errid) : '');
858 	},
859 	// func muss als "function() { #code# }" übergeben werden	
860 	delay: function(func,time) {
861 		window.setTimeout(func,time);
862 		
863 	},
864 	setDatumsfelder: function(eleTag, eleMonat, eleJahr, datum) {
865 		// fuelle Datumsfelder auf
866 		var y = "";
867 		if (eleTag.options.length == 1) {
868 			for (var i=1; i<=31; i++) {
869 				y = ( i<=9 ? '0'+i : ''+i );
870 				$(eleTag).insert({bottom: '\n<option value="'+y+'">'+y+'</option>'});
871 			}
872 			$(eleTag).selectedIndex = 0;
873 		}		
874 		var m = ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'];
875 		if (eleMonat.options.length == 1) {
876 			for (i=0; i<m.length; i++) {
877 				y = i<9 ? '0'+ (i+1) : ''+(i+1);
878 				$(eleMonat).insert({bottom: '\n<option value="'+ y +'">'+m[i]+'</option>'});
879 			}
880 			$(eleMonat).selectedIndex = 0;
881 		}		
882 		var d = new Date();
883 		if (eleJahr.options.length == 1) {
884 			for (i=d.getFullYear(); i>=1900; i--) { 
885 				$(eleJahr).insert({bottom: '<option value="'+i+'">'+i+'</option>'});
886 			}
887 			$(eleJahr).selectedIndex = 0;
888 		}		
889 		if (datum) {
890 			try { 			
891 				d = datum.split('.');
892 				fec.valuesFactory.select(eleTag, d[0]);
893 				fec.valuesFactory.select(eleMonat, d[1]);
894 				fec.valuesFactory.select(eleJahr, d[2]);
895  			} catch(e){}			
896 		}
897 	},
898 	redirectReplace: function(url) {
899 		fec.util.redirect(url, 10, true);
900 	},
901 	redirect: function(url, delay, replace) {
902 		try { fecHookRedirect(url, delay, replace); return true;} catch(e) {}
903 		setTimeout(
904 			function() { 
905 				// Parameter aus aktuellem Pfad mit hinzufügen, aber nur wenn keine ID dabei ist
906 				// sonst wird per ID immer wieder das Entity auf der Folgeseite geladen (siehe TBE-Beruf)
907 				var params = window.location.search;
908 				if (params.length > 0) {
909 					if (params.toUpperCase().indexOf('ID=') > -1) {
910 						// ID in den Params -> nichts anhängen
911 						// todo nur id aus params entfernen  
912 					} else {
913 						if(url.indexOf('?') != -1) {
914 							params = '&' + params.substring(1, params.length);
915 						} else { 
916 							url += params;
917 						}
918 					}
919 				}
920 				url = url.gsub(' ','');
921 				if (replace) {
922 					window.location.replace(url);
923 				} else  {
924 					window.location=url;
925 				}
926 			}.bind(this), (delay === undefined) ? 10 : delay );
927 		// wegen Back-Button 
928 		if (fec.main) {
929 			fec.main.disableForm(false);
930 		}
931 		return true;
932 	},
933 	capitalizeByChar: function(str, chr) {
934 		var parts = str.split('/'), len = parts.length;
935 		if (len == 1) {
936 			return parts[0];
937 		}
938 		var capitalized = str.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)	: parts[0];
939 		for (var i = 1; i < len; i++) {
940 			capitalized += '/'+ parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
941 		}
942 		return capitalized;
943 	},
944 	setKeepAlive: function() {
945 		var pb = '{ "referer": "'+fec.main.getReferer()+'", "times": ['+new Date().getTime()+'], "request": ["registernext"] }';
946 		var x = new Ajax.Request(fec.config.urlAjaxRequest, 
947 		{
948 			contentType: 'application/json', 
949 			asynchronous: true,
950 			postBody: pb
951 		});
952 		return x;		
953 	},
954 	runKeepAlive: function(delay) {
955 		var x = new PeriodicalExecuter(fec.util.setKeepAlive, delay ? delay : 60);
956 	},
957 	isCompleted: function() {
958 		return (fec.main && fec.main.completed.length > 0) ? true : false; 
959 	},
960 	addParamToUrl: function(url, name, value) {
961 		url += (url.include('?') ? '&' : '?') + name + '=' + encodeURIComponent(value);
962 		return url;
963 	},
964 	replaceAllCss: function(oldCss, newCss) {
965 		$$('div[class~="'+oldCss+'"]').each ( function(ele) {
966 			ele.removeClassName(oldCss);
967 			ele.addClassName(newCss);
968 		});						
969 	},
970 	getPageName: function() {
971 		var path = window.location.pathname;
972 		return path.substring(path.lastIndexOf('/') + 1);
973 	},
974 	eleId: function(eleName) {
975 		return fec.config.htmlIdDataPrefix + eleName; 
976 	}
977 };
978 
979 /**
980  * Cookie handling class
981  * 
982  * @namespace fec.cookies 
983  */
984 fec.cookies = {
985 	/**
986 	 * Creates a cookie
987 	 * 
988 	 * @param {string} cname The name of the cookie. 
989 	 * @param {string} value The value of the cookie. 
990 	 * @param {null|number} hours Number of hours after the cookie will expire. 
991 	 * If hours is null, the cookie will be a session cookie.
992 	 */		
993 	set: function(cname, value, hours)  {  
994 		var expires = "";
995 		if (hours) {
996 			var date = new Date();
997 			date.setTime(date.getTime()+(hours*60*60*1000));
998 			expires = "; expires="+date.toGMTString();
999 		}
1000 		var cstr = cname + "=" + escape(value) + expires + "; path=/";
1001 		document.cookie = cstr;   
1002 	},
1003 	/**
1004 	 * Returns the cookie value for the given name.
1005 	 * 
1006 	 * @param {string} cname The name of the cookie to retrieve. 
1007 	 * @param {string} defaultValue The defaultValue for the cookie, if none
1008 	 * is found. 
1009 	 */		
1010 	
1011 	get: function(cname, defaultValue) {  
1012 		var regex = new RegExp(cname + "\\s*=\\s*(.*?)(;|$)");  
1013 		var cookies = document.cookie.toString();
1014 		var match = cookies.match(regex);
1015 		if (match) {
1016 			return unescape(match[1]);
1017 		}  
1018 		return defaultValue;  
1019 	},
1020 	/**
1021 	 * Removes a cookie
1022 	 *  
1023 	 * @param {string} cname The name of the cookie to be erased. 
1024 	 */
1025 	erase: function(cname) {  
1026 		fec.cookies.set(cname, '', -1);  
1027 	}
1028 };
1029 
1030 /* _____________________ CLASSES (PROTOTYPE) ________________________________ */
1031 
1032 /**
1033  * Main processing class - This class holds the main functions for retrieving 
1034  * and submitting data && processing the server response && DOM manipulation 
1035  * 
1036  * @class FecMain 
1037  */
1038 var FecMain = Class.create({
1039 	/** URL to the server for ajax request	 */	
1040 	ajaxRequestUrl: fec.config.urlAjaxRequest,
1041 	/** experimental: if true you can develop html without a server */	
1042 	staticOn: false,
1043 	/** experimental for staticOn */	
1044 	staticResponseUrl: '/staticResponse',
1045 	/** holds the initial state of all form-buttons */	
1046 	submitButtons: [],
1047 	/** holds the data object from the response */	
1048 	data: '',
1049 	/** list of completed scopes */
1050 	completed: $A([]),
1051 	/** flag for JS-form-validation */
1052 	isFormValid: false,
1053 	/** will run automatically by instantiation */
1054 	initialize: function() {
1055 		// errorinit
1056 		this.initErrorList();
1057 	},
1058 	/**
1059 	 * Creates an Ajax-request to the server, depending on fecPagecontrol values.
1060 	 * The server sends data back, that can be processes. 
1061 	 * 
1062 	 * @returns {boolean} true
1063 	 */
1064 	getServerData: function(scope){
1065 		var request =  "", reqObj;
1066 		var scopeArr = (scope) ? [scope]:fec.pageControl.getScopes();
1067 		if(!this.isValueInArray(fec.pageControl.getControl('paramsLoad'),'void')){
1068 			for(var i = 0; i < scopeArr.size(); i++) {
1069 				if (request !== "") {
1070 					reqObj = request.evalJSON(true);
1071 					reqObj.request = [scopeArr[i]];
1072 					request = Object.toJSON(reqObj);
1073 				} else {
1074 					request = this.getRequestBody('load', scopeArr[i]);
1075 				}
1076 				var x = new Ajax.Request(this.getAjaxRequestUrl('load'), 
1077 				{
1078 					contentType:'application/json', postBody: request,
1079 					onSuccess:function(r){ 
1080 						this.processResponse('load',r.responseText); 
1081 					}.bind(this),
1082 					on403:function(r){ 
1083 						fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status,'403')); 
1084 					}.bind(this),
1085 					onFailure:function(r){
1086 						fec.util.redirect(fec.util.getErrorPage('getServerData_http'+r.status));
1087 					}.bind(this)
1088 				});
1089 			}
1090 		} else {
1091 			this.showSpinner(scopeArr,false);
1092  			try{ executeVoid(); } catch(e){}
1093 		}
1094 		return true;
1095 	},
1096 	/**
1097 	 * Creates an Ajax-request to the server, depending on fecPagecontrol values.
1098 	 * The server receives data through request data (e.G. form-data).
1099 	 * 
1100 	 * @returns {boolean} true
1101 	 */
1102 	sendServerData: function() {
1103 		var request = this.getRequestBody("send");
1104 		var x = new Ajax.Request(this.getAjaxRequestUrl('send'), 
1105 		{
1106 			contentType:'application/json', postBody: request,
1107 			onSuccess:function(r){ 
1108 				this.processResponse('send',r.responseText); 
1109 			}.bind(this),
1110 			on403:function(r){ 
1111 				fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status,'403')); 
1112 			}.bind(this),
1113 			onFailure:function(r){ 
1114 				fec.util.redirect(fec.util.getErrorPage('sendServerData_http'+r.status));
1115 			}.bind(this)
1116 		});
1117 		return true;
1118 	},
1119 	/**
1120 	 * Creates an Ajax-request to a given url and returns the received data.
1121 	 *  
1122 	 * @param {string} url - Serverurl or a file-url (e.G. ../data.txt)
1123 	 * @returns {object} received data object
1124 	 */
1125 	getAjaxData: function(url) {
1126 		var data = '';
1127 		var x = new Ajax.Request(url, 
1128 		{
1129 			contentType:'application/json', asynchronous:false,
1130 			onSuccess:function(r){ data = r.responseText; }.bind(this),
1131 			on403:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status,'403')); }.bind(this),
1132 			onFailure:function(r){ fec.util.redirect(fec.util.getErrorPage('getAjaxData_http'+r.status));}.bind(this)
1133 		});
1134 		return data;		
1135 	},	
1136 
1137 	/**
1138 	 * Processes the response from the server
1139 	 * 
1140 	 * @param {string} type - load or send
1141 	 * @param {object} response - server response object
1142 	 */
1143 	processResponse: function(type, response) {
1144 		if (response !== "") {
1145 			//response = response.substring(0, response.length - 1) + ', "error":{"PAGE":{"ERKUND":"","E37P2F":"Juhee","E37XXX":"FEHLER FEHLER"}} }';
1146 			var vspData = response.evalJSON(true);
1147 			// vspData fuer spezielle Setter in HTML verfuegbar machen
1148 			this.data = $H(vspData.data);
1149 			this.completed = $A(vspData.completed);
1150 			try { fecHookInitData(this.data); } catch (e) {}
1151 			if (vspData.redirect !== undefined) {
1152 				fec.util.redirect(vspData.redirect);
1153 				return;
1154 			} 
1155 			this.showContainer();
1156 			this.processResponseTimes(vspData);
1157 			if (this.processResponseError(vspData)) {
1158 				if (vspData.gotoPage !== undefined) {
1159 					// pc zuruecksetzen, falls er veraendert wurde (submitWithAction)
1160 					fec.pageControl = new FecPageControl(fecPagecontrol);
1161 					this.disableSubmitButtons(false);
1162 					fec.util.redirect(fec.pageControl.getGotoUrl(vspData.gotoPage));
1163 					return;
1164 				}
1165 				this.replaceWerteInDOM(vspData.data);
1166 				if(vspData.retry && vspData.retry.length > 0) {
1167 					var retryScope = vspData.retry[0];
1168 					if (vspData.wait === undefined) { vspData.wait = 1000; }
1169 					if (type == 'load') { 
1170 						fec.util.delay( function() { fec.main.getServerData(retryScope); }, vspData.wait ); 
1171 					}
1172 					if (type == 'send') { 
1173 						fec.util.delay( function() { fec.base.dataSend(); }, vspData.wait ); 
1174 					}				
1175 				} else {
1176 					this.showSpinner(vspData.completed,false);
1177 				}
1178 			} else {
1179 				// auch bei Fehler, Werte ersetzen
1180 				this.replaceWerteInDOM(vspData.data);
1181 				// Ladefehler anstatt Ladespinner
1182 				if (type == 'load') {
1183 					fec.util.replaceAllCss('ladebalken','ladebalken-error');
1184 				} else {
1185 					this.showSpinner(fec.pageControl.getScopes(),false);
1186 				}
1187 				// Hook fuer spezielle Fehlerbehandlung (zb siehe service-anmelden\index.html)
1188 				try { fecHookHandleError(vspData); } catch (e) {}
1189 								
1190 			}
1191 
1192 		} else {
1193 			// ein leerer Response, kann vorkommen, wenn schnell geklicked wird -> keine Fehlerseite anzeigen
1194 		}
1195 	},
1196 	/**
1197 	 * @param {object} data
1198 	 */	
1199 	replaceWerteInDOM: function(data) {
1200 		var domId = false, dataKeys = Object.keys(data), foundId = true;
1201 		dataKeys = dataKeys.sortBy(function(s) {
1202 		  return (/.*en$/).test(s) ? 0 : s.length;
1203 		});
1204 		try { fecHookInitially(); } catch (e) {}
1205 		for (var index = 0, len = dataKeys.length; index < len; ++index) {
1206 			foundId = true;
1207 			var dataKey = dataKeys[index];
1208 			domId = fec.config.htmlIdDataPrefix + dataKey.capitalize();
1209 			// Die Variable fecSetterValue kann von dem zusaetzlichen Setter verwendet werden.
1210 			fecSetterValue = ('string' == typeof data[dataKey]) ? data[dataKey].escapeHTML():data[dataKey];
1211 			try { 
1212 				eval('fecHookSetData'+dataKey.capitalize()+'();'); 
1213 			} catch (e) { 
1214 				if (this.setElementValue(domId, fecSetterValue) === false) { foundId = false; }
1215 				// keine IDs per autoincrement updaten 
1216 				if (dataKey.toLowerCase() != 'id') {
1217 					InnerFor: for (var i = 1; i < 10; ++i) {
1218 						try {
1219 							var el = $(domId+i);
1220 							this.setElementValue(el.id, fecSetterValue);							
1221 						} catch(e) {
1222 							break InnerFor;
1223 						}
1224 					}
1225 				}
1226 			}
1227 		}
1228 		try { fecHookFinally(); } catch (e) { }
1229 	},
1230 	/**
1231 	 * @param {object} vspData
1232 	 */	
1233 	processResponseError: function(vspData) {
1234 		if (vspData.error !== undefined) {
1235 			fec.main.isFormValid = false; // Formvalidierung beim nächsten submit wieder anstossen
1236 			this.disableForm(false);
1237 			this.initErrorList();
1238 			var errors = vspData.error, errorKey = "", errorValues = "";
1239 			var errorKeys = Object.keys(errors);
1240 			for (var index = 0, len = errorKeys.length; index < len; ++index) {
1241 				errorKey = errorKeys[index];
1242 				var errorsInner = $A(errors[errorKey]);
1243 				errorsInner.each(function(err) {
1244 					this.errorAdd(err,errorKey); 
1245 				}.bind(this));
1246 				fec.form.setErrorInDom($("vspData"+errorKey));
1247 			}
1248 			return false;
1249 		} else {
1250 			fec.main.showScope('vspScopeFehler',false);
1251 		}
1252 		return true;
1253 	},
1254 	/**
1255 	 * @param {object} vspData
1256 	 */	
1257 	processResponseTimes: function(vspData) {
1258 		if (vspData.times !== undefined) {
1259 			vspData.times.push(new Date().getTime());
1260 		}
1261 	},
1262 	
1263 	/**
1264 	 * @param {object} scope
1265 	 * @param {boolean} show
1266 	 */	
1267 	showScope: function(scope, show) {
1268 		var el = $(scope);
1269 		if (el) {
1270 			Element[(show) ? 'show' : 'hide'](el);
1271 			return true;
1272 		} else {
1273 			return false;
1274 		}
1275 	},
1276 	/**
1277 	 * @param {object} scopes
1278 	 * @param {boolean} show
1279 	 */	
1280 	showSpinner: function(scopes, show) {
1281 		if(Object.isString(scopes)) {
1282 			scopes = [scopes];
1283 		}
1284 		if(Object.isArray(scopes)) {
1285 			for (var index = 0, len = scopes.length; index < len; ++index) {
1286 				var scope = scopes[index];
1287 				try {
1288 					if (show) {
1289 						this.showScope(fec.config.htmlIdScopePrefix + scope.capitalize(),false);
1290 						this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),true);
1291 						// Ladebalken Style wieder zuruecksetzen
1292 						fec.util.replaceAllCss('ladebalken-error', 'ladebalken');
1293 					} else {
1294 						this.showScope(fec.config.htmlIdLoaderPrefix + scope.capitalize(),false);
1295 						if ('undefined' != typeof Effect) {
1296 							var x = Effect.Appear($(fec.config.htmlIdScopePrefix + scope.capitalize()), { duration: 0.25 });
1297 						} else {
1298 							$(fec.config.htmlIdScopePrefix + scope.capitalize()).show();
1299 						}
1300 						// Weitere Elemente einblenden per Hook
1301 						try{ fecHookShowScope(); }catch(e){}
1302 					}
1303 				} catch(e) {}
1304 			}
1305 		}
1306 	},
1307 	/**
1308 	 * If a container element is in the page, toggle visibility. This is  
1309 	 * usefull, when the dom shouldn't be visibility, before the response has 
1310 	 * arrived.
1311 	 */	
1312 	showContainer: function() {
1313 		if ($('container')) {
1314 			$('container').show();
1315 		} 
1316 		if ($('containerWarten')) {
1317 			$('containerWarten').hide();
1318 		} 
1319 	},
1320 	/**
1321 	 * @param {object} element
1322 	 * @param {boolean} newValue
1323 	 */	
1324 	setElementValue: function(element, newValue) {
1325 		var element_id = element;
1326 		element = $(element);
1327 		if (!element){
1328 			element = document.getElementsByName(element_id)[0];
1329 		}
1330 		if (!element){ return false; }
1331 		var method = element.tagName.toLowerCase();
1332 		var parameter = fec.valuesFactory[method](element, newValue);		
1333 	},
1334 	/**
1335 	 * 
1336 	 */
1337 	getVspDataFormElements: function() {
1338 		var form = $(fec.config.htmlIdForm), elements = new Hash();
1339 		if (null !== form) {
1340 			elements = $H(this.serializeElements(form.getElements(),true));
1341 			elements.each(function(ele){
1342 				if(!ele[0].startsWith(fec.config.htmlIdDataPrefix)) {
1343 					elements.unset(ele[0]);
1344 				}
1345 			});
1346 		}
1347 		return elements;
1348 	},	
1349 	/**
1350 	 * @param {string} type
1351 	 */
1352 	getRequestData: function(type) {
1353 		var elements = fec.main.getVspDataFormElements();
1354 		if (type == 'send') {
1355 			this.disableForm(true);
1356 			try {
1357 				var data = eval('fecHookGetRequestData' + fec.pageControl.getScopes()[0].capitalize()+'()');
1358 				elements.update(data);
1359 			} catch (e) { }
1360 		}
1361 		elements.update(this.getUriControlParams());
1362 		if (elements.size() !== 0) {
1363 			var formData = elements.toJSON();
1364 			if (formData == '{}') { return ""; }
1365 			return formData.gsub(fec.config.htmlIdDataPrefix,'');
1366 		} 
1367 		return '';
1368 	},
1369 	/**
1370 	 * 
1371 	 */
1372 	getUriControlParams: function() {
1373 		var h = fec.util.getQueryParams(), rv = new Hash();
1374 		// URI Parameter die dem JSON Request angefuegt werden sollen, nur Kleinbuchstaben
1375 		// confirmid, regid kommen von Bestaetigungsemail
1376 		var controlParams = ['id','confirmid','regid','wdywtg','wdycf'];
1377 		if(h.size() !== 0) {
1378 			controlParams.each(function(key) {
1379 				if (h.get(key) !== undefined) {
1380 					rv.set(key.capitalize(),h.get(key));
1381 				}
1382 			}.bind(this));		
1383 		}
1384 		return rv;
1385 	},
1386 	/**
1387 	 * @param {object} elements
1388 	 * @param {object} options
1389 	 */
1390 	serializeElements: function(elements, options) {
1391 		if (typeof options != 'object') {
1392 			options = { hash: !!options };
1393 		}
1394 		else if (options.hash === undefined) {
1395 			options.hash = true;
1396 		}
1397 		var key, value, submitted = false, submit = options.submit;
1398 		var data = elements.inject({ }, 
1399 			function(result, element) {
1400 				if (!element.disabled && element.name) {
1401 					key = element.name; value = $(element).getValue();
1402 					if ((element.type != 'submit' || (!submitted &&
1403 						submit !== false && (!submit || key == submit) && (submitted = true)))) {
1404 						// msch hier besondere behandlung fuer checkboxes
1405 						if(element.type === 'checkbox' && !(key in result)) {
1406 							result[key] = (element.checked === true) ? "true":"false";
1407 						} else {
1408 							if (value != null) {
1409 								if (key in result) {
1410 									// a key is already present; construct an array of values
1411 									if (!Object.isArray(result[key])) {
1412 										result[key] = [result[key]];
1413 									}
1414 									result[key].push(value);
1415 								} else {
1416 									result[key] = value;
1417 								}
1418 							}
1419 						}
1420 					}
1421 				}
1422 			return result;
1423 		});
1424 		return options.hash ? data : Object.toQueryString(data);
1425 	},
1426 
1427 	
1428 	/* ___________ GENERELLE FUNKTIONEN __________ */
1429 	/**
1430 	 * Validate if an object is an array and if it contains a given value  
1431 	 * 
1432 	 * @param {object} arr
1433 	 * @param {object} value
1434 	 */
1435 	
1436 	isValueInArray: function(arr, value) {
1437 		if (!Object.isArray(arr)) {
1438 			return false;
1439 		} else if (arr.indexOf(value) > -1) {
1440 			return true;
1441 		}
1442 	},
1443 	/**
1444 	 * Get the referer, and use experimental staticOn flag
1445 	 */
1446 	getReferer: function() { 
1447 		var url = window.location.pathname;
1448 		if (window.location.search !== "") {
1449 			url = url + window.location.search;
1450 		}
1451 		url = url.replace('yaf4ajn.war','yaf4ajn');
1452 		if(this.staticOn) {
1453 			var pos = url.search(/yaf4ajn/);
1454 			if(pos > 0) {
1455 				url = url.substring(0,pos) + this.staticResponseUrl +'//yaf4ajn//'+ url.substring(pos + 'yaf4ajn'.length + 1,url.length);
1456 			} else if (url.search(/index.php/) > 0) {
1457 				url = '..' + this.staticResponseUrl +'/index.php';
1458 			} else {
1459 				url = window.location.pathname.replace(/yaf4ajn/g, this.staticResponseUrl+"/yaf4ajn");
1460 			}
1461 			pos = (url.lastIndexOf('\\') > pos) ? url.lastIndexOf('\\') : url.lastIndexOf('/');
1462 			var file = url.substring(pos+1,url.length);
1463 			if (file.indexOf('-') > 0) {
1464 				 file = file.replace(file.match(/-.*\./g),'.');
1465 				 url = url.substring(0, pos+1) + file;
1466 			}
1467 		}
1468 		return url;
1469 	},
1470 	/**
1471 	 * Get the data for the server request.
1472 	 * 
1473 	 * @param {string} type - load or send
1474 	 * @param {string} scope
1475 	 */
1476 	getRequestBody: function(type, scope) { 
1477 		var req = '{ "referer": "'+this.getReferer()+'", "times": ['+new Date().getTime()+']';
1478 		if (type == 'load') {
1479 			req += ', "request": '+ ((scope) ? ([scope]).toJSON():fec.pageControl.getScopes().toJSON());
1480 		}
1481 		if (type == 'send') {
1482 			req += ', "request": ["' + fec.pageControl.getScopes()[0] + '"]';
1483 		}
1484 		req += this.getRequestBodyParams(type);
1485 		var data = this.getRequestData(type);
1486 		if (data !== "") {
1487 			req += ', "data": '+data;
1488 		}
1489 		var gotoPage = fec.pageControl.getGotoPage();
1490 		if (gotoPage !== "") {
1491 			req += ', "gotoPage": "'+gotoPage+'"';
1492 		}
1493 		if (fec.pageControl.getPageId() !== "") {
1494 			req += ', "pageid": "'+fec.pageControl.getPageId()+'"';
1495 		}
1496 		req += ' }';
1497 		return req;
1498 	},
1499 	/**
1500 	 * Get additional parameters for the server request to control the server.
1501 	 * 
1502 	 * @param {string} type - load or send
1503 	 */
1504 	getRequestBodyParams: function(type) {
1505 		var params = $H({});
1506 		if (type == 'load') {
1507 			var pl = fec.pageControl.getControl('paramsLoad');
1508 			if ( this.isValueInArray(pl,'relogin') ){ 
1509 				params.set('action','relogin');
1510 				if ($('fecParamWdywtg')) {
1511 					params.set('wdywtg',$('fecParamWdywtg').value);
1512 				}
1513 				pl = pl.without('relogin');
1514 			}
1515 			if ( this.isValueInArray(pl,'merge') ){ 
1516 				params.set('merge',true);
1517 				pl = pl.without('merge');
1518 			}
1519 			if (pl && pl.size() > 0) {
1520 				params.set('action',pl[0]);
1521 			}
1522 		}
1523 		if (type == 'send') {
1524 			var ps = fec.pageControl.getControl('paramsSend');
1525 			if( ps && ps.size() > 0 ) {
1526 				params.set('action',ps[0]);
1527 			}
1528 		}
1529 		params = params.merge(fec.pageControl.getParameter());
1530 		return (params.size() > 0) ? ', "parameter": '+params.toJSON() : "";
1531 	},
1532 	/**
1533 	 * Disable the submit buttons.
1534 	 * 
1535 	 * @param {boolean} bol - disable or not
1536 	 */
1537 	disableSubmitButtons: function(bol) {
1538 		var butValue;
1539 		var buttons = $$('input[type="submit"]','input[type="button"]');
1540 		if (this.submitButtons.size() === 0) {
1541 			this.submitButtons = buttons.pluck('value');
1542 		}
1543 		//this.submitButtons.pop();
1544 		for (var i = 0, len = buttons.size(); i < len; ++i) {
1545 			butValue = fec.msg.FE010;
1546 			for(var y = 0; y < buttons[i].title.length; ++y) {
1547 				if (y%2 === 0) {
1548 					butValue += " ";
1549 				} else {
1550 					butValue = " " + butValue;
1551 				}
1552 			}
1553 			buttons[i].value=butValue;
1554 			if (bol) {
1555 				buttons[i].disable();
1556 			} else {
1557 				buttons[i].value = (this.submitButtons[i] === '') ? 'Weiter' : this.submitButtons[i];
1558 				buttons[i].enable();
1559 			}
1560 		}
1561 	},
1562 	/**
1563 	 * Disable the form.
1564 	 * 
1565 	 * @param {boolean} bol - disable or not
1566 	 */
1567 	disableForm: function(bol) {
1568 		if ($(fec.config.htmlIdForm) !== null) {
1569 			if (bol) {
1570 				$(fec.config.htmlIdForm).disable();
1571 			} else {
1572 				$(fec.config.htmlIdForm).enable();
1573 			}
1574 		}
1575 	},
1576 	/**
1577 	 * Returns the configured server url
1578 	 * 
1579 	 * @param {string} type - load or send
1580 	 */
1581 	getAjaxRequestUrl: function(type) {
1582 		retVal = this.ajaxRequestUrl;
1583 		if (this.staticOn) {
1584 			retVal = ('send' == type) ? this.getReferer()+'.txt.send' : this.getReferer()+'.txt';
1585 		}
1586 		return retVal;
1587 	},
1588 	/**
1589 	 * Scroll to top if possible.
1590 	 */
1591 	scrollToTop: function() {
1592     	var yPos =  window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
1593 		if (yPos > 150)	{
1594 			window.scrollTo(0,100);
1595 		}
1596 	},
1597 	/**
1598 	 * Scroll to bottom if possible.
1599 	 */
1600 	scrollToBottom: function() {
1601 		window.scrollTo(0,document.viewport.getHeight());
1602 	},
1603 	/**
1604 	 *  Checks the form and sets Variable isFormValid
1605 	 *  
1606 	 *  @returns {boolean} Form valid or not
1607 	 */
1608 	checkForm: function() {
1609 		var retval = false;
1610 		// Das Form nur pruefen wenn kein validate und wenn noch nicht geprueft wurde
1611 		if (fec.pageControl.getParameter().get('validate') === undefined && !fec.main.isFormValid) {
1612 			try {
1613 				retval = eval(fec.config.jsCheckformPrefix + fec.pageControl.getScopes()[0].capitalize()+'();');
1614 			} catch(e) { retval = true; }
1615 		} else {
1616 			retval = true;
1617 		}
1618 		fec.main.isFormValid = retval;
1619 		return retval;
1620 	},
1621 	/**
1622 	 * Inits the error output.
1623 	 */
1624 	initErrorList: function() {
1625 		if($('vspScopeFehler') === null) {
1626 			var s = "\n<div id='vspScopeFehler' style='display:none'>\n</div>\n";
1627 			Try.these(
1628 				function() {$$('h1')[0].insert({'after':s});},
1629 				function() {$('content').insert({'top':s});},
1630 				function() {$$('body')[0].insert({'top':s});}
1631 			);			
1632 			$('vspScopeFehler').hide();
1633 		} else {
1634 			$('vspScopeFehler').update('');
1635 		}
1636 	},
1637 	/**
1638 	 * Inits the error layout.
1639 	 * 
1640 	 * @param {string} errCss - Css class like error, warning, info
1641 	 * @param {boolean} clear - init and clear existing errors
1642 	 */
1643 	initErrorBox: function(errCss, clear) {
1644 		errCss = errCss.capitalize();
1645 		if($('vspScopeFehler'+errCss) === null) {
1646 			var s = "";
1647 			if (errCss.toLowerCase()=='error') {
1648 				s += "\n<div id='vspScopeFehler"+errCss+"' class='box large naked image "+errCss.toLowerCase()+"'>";
1649 				s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/16x16/warnung.png' alt='"+errCss.toLowerCase()+"'/>";
1650 				s += "\n<ul id='vspErrorList"+errCss+"'></ul>";
1651 				s += "\n</div>\n";
1652 				s += "\n<div style='clear:both;height:1px'></div>";
1653 			} else {
1654 				s += "\n<div id='vspScopeFehler"+errCss+"' class='box large image "+errCss.toLowerCase()+"'>";
1655 				s += "\n<h2 id='vspErrorHeader"+errCss+"'>" + fec.msg.FE001 +"</h2>";
1656 				s += "\n<img src='"+fec.util.getImageRoot()+"fileadmin/VOS/images/48x48/"+errCss.toLowerCase()+".gif' alt='"+errCss.toLowerCase()+"'/>";
1657 				s += "\n<ul id='vspErrorList"+errCss+"'></ul>";
1658 				s += "\n</div>\n";
1659 				s += "\n<div style='clear:both;height:1px'></div>";
1660 			}
1661 			$('vspScopeFehler').insert({'bottom':s});
1662 			$('vspScopeFehler').hide();
1663 		} else {
1664 			if (clear) {
1665 				$('vspErrorList'+errCss).update('');
1666 			}
1667 		}
1668 	},
1669 	/**
1670 	 * Adds an error.
1671 	 * 
1672 	 * @param {object} err - error object
1673 	 * @param {string} errtxt - a special errortext, that overrides the default
1674 	 */
1675 	errorAdd: function(err, errtxt) {
1676 		var errObj = fec.main.getErrorObject(err, errtxt);
1677 		// redirect?
1678 		if (errObj.get('redir')) {
1679 			var errUrl = fec.pageControl.getGotoUrl(errObj.get('redir'));
1680 			// Todo jsunit
1681 			// Verhindern, dass auf sich selbst geleitet wird
1682 			if (errUrl === 'index.html' && errUrl === fec.util.getPageName()) {
1683 				errUrl = '../' + errUrl;
1684 			}
1685 			fec.util.redirect(errUrl);
1686 			return false;
1687 		}
1688 		// errObj verarbeiten
1689 		var errCss = errObj.get('css') ? errObj.get('css').capitalize():"Error";
1690 		this.initErrorBox(errCss,false);
1691 		if (errObj.get('head')) {
1692 			if ($('vspErrorHeader'+errCss)) {
1693 				$('vspErrorHeader'+errCss).update(errObj.get('head'));
1694 			}
1695 
1696 		}
1697 		//Fehler hinzufuegen 
1698 		$('vspErrorList'+errCss).insert({'bottom':'<li>'+errObj.get('text')+'</li>\n'});
1699 		//Fehler anzeigen
1700 		this.showScope('vspScopeFehler',true);
1701 		this.scrollToTop();
1702 		this.disableSubmitButtons(false);
1703 		this.showContainer();
1704 		return true;
1705 	},
1706 	/**
1707 	 * Resolves the error configuration.
1708 	 * 
1709 	 * @param {object} err - error object
1710 	 * @param {string} errtxt - a special errortext, that overrides the default
1711 	 */
1712 	getErrorObject: function(err, errtxt) {
1713 		//erstelle ErrorObjekt
1714 		if (!errtxt || errtxt === "" || errtxt === "PAGE") {
1715 			errtxt = fec.errorCode.EUNEXP;
1716 		}
1717 		var errid = Object.isString(err) ? err : err.Code;
1718 		var errObj = new Hash();
1719 		// Setze den Errortext als Template
1720 		errObj.set('template', new Template(fec.errorCode[errid] ? fec.errorCode[errid] : errtxt));
1721 		// setze die Templateparameter
1722 		var params = new Hash();
1723 		if (Object.isString(err)) {
1724 			params.set('0', errtxt);
1725 		} else {
1726 			if (Object.isString(err.Args)) {
1727 				params.set('0', err.Args);
1728 			} else {
1729 				if (err.Args) {
1730 					for (var i = 0; i < err.Args.length; i++) {
1731 						params.set(''+i, err.Args[i]);
1732 					}
1733 				}
1734 			}
1735 		}
1736 		errObj.set('parameter', params);
1737 		// merge mit vorhandender fec.errorConfig
1738 		if (fec.errorConfig[errid]) {
1739 			errObj = errObj.merge(fec.errorConfig[errid]);
1740 		}
1741 		var t = errObj.get('template').evaluate(errObj.get('parameter'));
1742 		errObj.set('text', t);
1743 		
1744 		return errObj;
1745 	}	
1746 });
1747 
1748 
1749 /**
1750  * PageControl class - This class holds the fecPagecontrol variable and 
1751  * offers getters and setters to the values 
1752  * 
1753  * @class FecPageControl 
1754  */
1755 var FecPageControl = Class.create({
1756 	/**
1757 	 * Represents the pageControl object
1758 	 * 
1759 	 * @type object
1760 	 * @default false
1761 	 */
1762 	pc: false,
1763 	/**
1764 	 * Parses the pageControl variable.
1765 	 */
1766 	initialize: function(pcString) {
1767 		if (this.pc === false) {
1768 			try { 
1769 				// Wenn Fehler, dann saubere Fehlermeldung ausgeben
1770 				// anstatt SyntaxException
1771 				this.pc = pcString.evalJSON(true); 
1772 			}catch(e){
1773 				fec.main.errorAdd('',fec.msg.FE002);
1774 			}
1775 			this.processErrorConfig();
1776 		}
1777 	},
1778 	/**
1779 	 * @returns {array} array of scopes 
1780 	 */
1781 	getScopes: function(){ 
1782 		return Object.isString(this.pc.scopes) ? [this.pc.scopes] : this.pc.scopes; 
1783 	},
1784 	/**
1785 	 * @returns {object} Value for the given control-key.  
1786 	 */
1787 	getControl: function(control){ return this.pc[control]; },
1788 	/**
1789 	 * @params {string} control - Key for a server-control
1790 	 * @params {string} value - Value for a server-control variable
1791 	 */
1792 	setControl: function(control, value){ 
1793 		this.pc[control] = Object.isArray(value) ? value : [value]; 
1794 	},	
1795 	/**
1796 	 * @returns {object} Server parameters for the request. 
1797 	 */
1798 	getParameter: function(){
1799 	 	return (typeof this.pc.parameter == 'undefined') ? $H({}) : $H(this.pc.parameter);
1800 	},
1801 	/**
1802 	 * Additional parameters to control the server (through request).
1803 	 * 
1804 	 * @params {string} key - key for an additional parameter
1805 	 * @params {string} val - value for an additional parameter
1806 	 */
1807 	setParameter: function(key, value){
1808 		var params = this.getParameter();
1809 		params.set(key, value);
1810 		this.pc.parameter = params;	
1811 	},
1812 	/**
1813 	 * Set special parameter for a load request
1814 	 * 
1815 	 * @params {string} val - a loading parameter like void, merge
1816 	 */
1817 	setParamsLoad: function(val){
1818 	 	this.pc.paramsLoad = val;
1819 	},
1820 	/**
1821 	 * Server can return urls or ids. Ids are the better option, so the server
1822 	 * doesn't have to know about urls. Urls can be configured completely in the
1823 	 * frontend. 
1824 	 * 
1825 	 * @params {string} gotoid - An id to a page or an url. Url will be returned
1826 	 * immediately, and id will be resolved.
1827 	 */
1828 	getGotoUrl: function(gotoid){
1829 		if (gotoid.include('.')) {
1830 			return gotoid;
1831 		}
1832 		var retval = '';
1833 		gotoid = gotoid.toLowerCase();
1834 		if (this.pc.gotos && this.pc.gotos[gotoid]) {
1835 			retval = this.pc.gotos[gotoid];	
1836 		} else {
1837 			if (this.gotoPageUrls[gotoid]) {
1838 				retval = this.gotoPageUrls[gotoid];
1839 			} else {
1840 				retval = fec.util.getErrorPage('getGoto_'+gotoid);
1841 			}
1842 		}
1843 		return retval;
1844 	},
1845 	/**
1846 	 * Mapping of URLs to Ids
1847 	 */
1848 	gotoPageUrls: {
1849 		page_01_01 : "page0101.html"
1850 	},		
1851 	/**
1852 	 * @returns {string} URL where the server should redirect after completion 
1853 	 */
1854 	getGotoPage: function(){
1855 	 	return (typeof this.pc.gotoPage == 'undefined') ? "" : this.pc.gotoPage;
1856 	},
1857 	/**
1858 	 * @params {string} gotoPage - URL where the server should redirect after 
1859 	 * completion
1860 	 */
1861 	setGotoPage: function(gotoPage){
1862 	 	this.pc.gotoPage = gotoPage;
1863 	},
1864 	/**
1865 	 * @returns {string} pageId of pagecontrol or empty-string 
1866 	 */
1867 	getPageId: function() {
1868 	 	return this.pc.pageid ? this.pc.pageid : "";
1869 	},
1870 	/**
1871 	 * Parse a special error baheviour, given by the pageControl. So you can 
1872 	 * define a special behaviour per page.
1873 	 */
1874 	processErrorConfig: function() {
1875 		if (this.pc.errorconfig) {
1876 			var errconfs = $H(this.pc.errorconfig);
1877 			errconfs.each(function(err) {
1878 				fec.errorConfig[err[0]] = err[1]; 
1879 			}.bind(this));
1880 		}
1881 	}
1882 });
1883 
1884 
1885 
1886 
1887 /* _____________________ CONFIG MISCELLANEOUS _______________________________ */
1888 
1889 /**
1890  * ErrorCode Mapping - Maps an ErrorCode from the response to a text.
1891  * 
1892  * @namespace fec.errorCode 
1893  */
1894 fec.errorCode = {
1895 	EUNEXP : 'unexpected error'
1896 };
1897 
1898 /** 
1899  * ErrorCode behaviour configuration - You can override the default behavior for 
1900  * an error. These values are available:
1901  *   redir - redirect immediately to redir-URL  
1902  *   head - title text
1903  *   css - css classname
1904  *   text - errortext, overrides fec.errorCode
1905  *
1906  * @namespace fec.errorConfig 
1907  */
1908 fec.errorConfig = {
1909 	EUNEXP : {
1910 		head : 'Hinweis',
1911 		css  : 'info'
1912 	} 
1913 };
1914 
1915 /**
1916  * Messages mappings - Inhere you find all text-fragments that will be shown 
1917  * to the user.
1918  * 
1919  * @namespace fec.msg 
1920  */
1921 fec.msg = {
1922 	YES	: 'Ja',
1923 	NO	: 'Nein',
1924 	FE001 : 'Es ist ein Fehler aufgetreten!',
1925 	FE002 : 'pageControl String nicht valide',
1926 	FE003 : 'Bitte korrigieren Sie folgende Eingabefelder',
1927 	FE004 : 'Formularfehler',
1928 	FE005 : 'ist ein Pflichtfeld.',
1929 	FE006 : 'darf maximal ##MAX## Zeichen lang sein.',
1930 	FE007 : 'muss mindestens ##MIN## Zeichen lang sein.',
1931 	FE008 : 'muss zwischen ##MIN## und ##MAX## Zeichen lang sein.',
1932 	FE009 : 'Achtung, diese Webseite wurde nicht von der vorgesehenen URL "##HOST##" geladen. Bitte geben Sie hier und in den verlinkten Systemen keine persönliche Daten ein.',
1933 	FE010 : 'Bitte warten ..'
1934 };
1935 
1936 
1937 
1938 /* _____________________ EXAMPLE HOOK IMPLEMENTATIONS _______________________ */
1939 /**
1940  * An example for a special data-value setter. Default behaviour is replacing a 
1941  * data value (from the server) with the the corresponding element-id. But if
1942  * this is not enough you can implement special setters. Valuekey is the
1943  * data-key.
1944  * 
1945  * @example fecHookSetDataValuekey
1946  */
1947 function fecHookSetDataValuekey() {
1948 	if (fecSetterValue !== undefined) {
1949 		// spezial handling of the setterValue
1950 		$('elementid').update(fecSetterValue);
1951 	}
1952 }
1953 /**
1954  * An example for a form-validation function, that will be called automatically.
1955  * "Scopename" must be the same as the page-scope-name, therefore you can have
1956  * only one check-funktion for every scope you use
1957  * 
1958  * @example fecHookCheckFormScopename
1959  */
1960 function fecHookCheckFormScopename() {
1961 	fecForm.initFormValidation();
1962 	var inputs = document.getElementsByTagName('input'), input = false;
1963 	for (var i = 0, len = inputs.length; i < len; i++){
1964 		input = inputs[i];
1965 		// different checks for every formfield
1966 		if (input.name == fec.util.eleId('html-id-name')) {
1967 			if (input.value === '') { 
1968 				fecForm.setErrorInDom(input,"html-id-name is empty"); 
1969 			}
1970 		}
1971 	}
1972 	return fecForm.checkIfFormValid();
1973 }
1974 
1975 
1976