/** copyright 2009 -2011  Detlef Hahn
 * ==================================
 *  sorry in den scripten befindet sich teilweise noch alter unbenutzer Code.
 *  unsbesondere werden unterschiedliche Formen von Evenhandler verwendet, da 
 *  ich damit noch experimentiere
 *  
 */

/**
 *   Erweiterung der Klasse String um einige Funktionen, die häufig benötigt werden
 * 
 */
if (typeof(String.prototype.trim) === "undefined") {
String.prototype.trim 	= function() { return this.replace(/^\s+|\s+$/g,""); };
}
if (typeof(String.prototype.ltrim) === "undefined") {
String.prototype.ltrim 	= function() { return this.replace(/^\s+/,""); } ;
}
if (typeof(String.prototype.rtrim) === "undefined") {
String.prototype.rtrim 	= function() { 	return this.replace(/\s+$/,""); };
}
if (typeof(String.prototype.strpos) === "undefined") {
String.prototype.strpos	= function( str, such, offset){
    var i = (str+'').indexOf(such, (offset ? offset : 0));
    return i === -1 ? false : i;
};
}

// ==========================================================================
function Fehlerbehandlung (Nachricht, Datei, Zeile) {
	   Fehler = "Fehlermeldung:\n" + Nachricht + "\n" + Datei + "\n" + Zeile;
	   zeigeFehler();
	   return false;
	 }

function zeigeFehler () {
	   alert(Fehler);
	 }
// ==========================================================================


/*
 * Mit dem dokumentweiten load-Ereignis (window.onload) haben wir eine Funktion nach dem 
 * vollständigen Laden des Dokuments ausgeführt. Das load-Ereignis tritt jedoch erst dann ein, 
 * wenn das gesamte Dokument mitsamt aller externen Ressourcen vom Webserver heruntergeladen wurde. 
 * Dazu gehören eingebettete Grafiken, Multimedia-Plugins und gegebenenfalls Iframes mit weiteren
 * HTML-Dokumenten. Je nachdem, welche externen Ressourcen eingebunden werden, kann das Dokument 
 * zum Zeitpunkt des load-Ereignisses schon längst im Browser aufgebaut sein und der Anwender 
 * kann es schon größtenteils lesen und bedienen.
 */
//window.onload =init;
// ===========================================================================
/*!
 * contentloaded.js
 *
 * Author: Diego Perini (diego.perini at gmail.com)
 * Summary: cross-browser wrapper for DOMContentLoaded
 * Updated: 20101020
 * License: MIT
 * Version: 1.2
 *
 * URL:
 * http://javascript.nwbox.com/ContentLoaded/
 * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
 *
 */

// @win window reference
// @fn function reference
function contentLoaded(win, fn) {

	var done = false, top = true,

	doc = win.document, root = doc.documentElement,

	add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
	rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
	pre = doc.addEventListener ? '' : 'on',

	initLoaded = function(e) {
		if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
		(e.type == 'load' ? win : doc)[rem](pre + e.type, initLoaded, false);
		if (!done && (done = true)) fn.call(win, e.type || e);
	},

	poll = function() {
		try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
		initLoaded('poll');
	};

	if (doc.readyState == 'complete') fn.call(win, 'lazy');
	else {
		if (doc.createEventObject && root.doScroll) {
			try { top = !win.frameElement; } catch(e) { }
			if (top) poll();
		}
		doc[add](pre + 'DOMContentLoaded', initLoaded, false);
		doc[add](pre + 'readystatechange', initLoaded, false);
		win[add](pre + 'load', initLoaded, false);
	}

}

contentLoaded(window,init);  // greift auch für IE < 9

// document.addEventListener("DOMContentLoaded", init, false);

var picText=[];
var Bilder=[];
var initTab=[];  // enthält alle Funktionen, die bei onLoad aktiviert werden müssen

function initObj(id,div,drag){
	this.id=id;
	this.div=div;
	this.drag=drag;
}

function init(){ 
	var i=0; 
	for(i=0;i<initTab.length;i++){ 
		initTab[i].drag=new dragObject(initTab[i].id,initTab[i].div); 
	}
}

// ======================================================================================
  var jsfile="basis.js";
  var about_js=[];
  var jetzt = new Date();
  about_js.push((jetzt.getTime()/1000)+" &nbsp; "+jsfile+"= 1.1 \tBuilddate=30.12.10 15:40");
  

 function supports_local_storage() {  
  	  try {  
  	    return 'localStorage' in window && window['localStorage'] !== null;  
  	  } catch(e){  
  	    return false;  
  	  }  
 } 
  
  
//usage: log('inside coolFunc',this,arguments);
//paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
 log.history = log.history || [];   // store logs to an array for reference
 log.history.push(arguments);
 if(this.console){
   console.log( Array.prototype.slice.call(arguments) );
 }
};



//catch all document.write() calls
(function(doc){
 var write = doc.write;
 doc.write = function(q){ 
   log('document.write(): ',arguments); 
   if (/docwriteregexwhitelist/.test(q)) write.apply(doc,arguments);  
 };
})(document);


  
  function  showAbout(id){
	  var i=0;
	  var jetzt = new Date();
	  toggleDiv(id);
	  var html="<br><p class=\"center\">  &nbsp; Copyright &copy;  2005 - "+jetzt.getFullYear()+" Detlef Hahn <br></p>\n";
	  for( i=0;i<about.length;i++){
		  html+=" &nbsp;  "+about[i].substr(7)+"<br>\n";
	  }  
	  for( i=0;i<about_js.length;i++){
		  html+=" &nbsp;  "+about_js[i].substr(7)+"<br>\n";
	  }
	  html+="<br>";;
	  document.getElementById("aboutText").innerHTML=html;
  	}
 
    /**
	*  entspricht etwa dem shell-command basename
	*  liefert den Programmnamen  zurück
	*  die Extension kann hier jedoch nicht mit abgeschnitten werden
	*  wandelt Backslash   \  (Windoof) in slash /
	*  
	*  @param  path		Pfad in Linux oder Windowsformat
  	*/
	function basename(path,ext) {
		var fln= path.replace(/\\/g,'/').replace( /.*\//, '' );
		if(typeof ext == "undefined"){ 
	      return fln;
		}
        var pos=fln.lastIndexOf(ext);
        if(pos > 0) return fln.substring(0,pos);
		// return fln.replace(/\.*$/,'');
	}
	


	function basename(path) {
	    return path.replace( /.*\//, "" );
	}

/*	function dirname(path) {
		return path.match( /.*\// );
	}
	*/
	function drop_ext(fln){
		return fln.replace(/\..*$/,"");
	} 
	/**
	*  entspricht dem shell-command dirname
	*  liefert Verzeichnisname ohne abschliessenden / zurück
	*  wandelt Backslash   \  (Windof) in slash /
	*  @param  path		Pfad in Linux oder Windowsformat
  	*/
	function dirname(path) {
	    return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, '');
	}

    /**
     * Entfern die File-Extension
     * @param 	fln
     * @return
     */
	function drop_ext(fln){
		return fln.replace(/\..*$/,"");
	}

	function IsNumeric(input){
	    var RE = /^-{0,1}\d*\.{0,1}\d+$/;
	    return (RE.test(input));
	}
	/**
	 * fügt einen neuen Knoten ein
	 * @param parent
	 * @param node
	 * @param referenceNode
	 * @return
	 */
	function insertAfter(parent, node, referenceNode) {
		parent.insertBefore(node, referenceNode.nextSibling);
	}
	
	/**
	 * Maskiert single Quotes und \n  indem ein \ vorangestellt wird
	 * @param text
	 * @return
	 */
	function escapeQuotes( text ) {
		var re = new RegExp( "'", "g" );
		text = text.replace( re, "\\'" );
		re = new RegExp( "\\n", "g" );
		text = text.replace( re, "\\n" );
		return escapeQuotesHTML( text );
	}

	/**
	 * ersetzt &amp; " < >  durch HTML-Entities
	 * @param text
	 * @return
	 */
	function escapeQuotesHTML( text ) {
		var re = new RegExp( '&amp;', "g" );
		text = text.replace( re, "&amp;amp;" );
		re = new RegExp( '"', "g" );
		text = text.replace( re, "&amp;quot;" );
		re = new RegExp( '&lt;', "g" );
		text = text.replace( re, "&amp;lt;" );
		re = new RegExp( '&gt;', "g" );
		text = text.replace( re, "&amp;gt;" );
		return text;
	}

	
	/**
	 * liefert die angefragten Klassenelement zurück
	 * 
	 * @param searchClass	erforderlich
	 * @param node			optional
	 * @param tag			optional
	 * @return
	 */
	function getElementsByClass(searchClass,node,tag) {
		var i=0;
		var j=0;
		var classElements = new Array();
		if ( node == null )
			node = document;
		if ( tag == null )
			tag = '*';
		var els = node.getElementsByTagName(tag);
		var elsLen = els.length;
		var pattern = new RegExp('(^|\\\\s)'+searchClass+'(\\\\s|$)');
		for (i = 0, j = 0; i < elsLen; i++) {
			if ( pattern.test(els[i].className) ) {
				classElements[j] = els[i];
				j++;
			}
		}
		return classElements;
	}

	/**
	 *  sucht Position von suchstr in str,  (analog PHP)
	 *  ist als einfache Funktion ausgelegt, kein Prototype von String
	 *  
	 * @param suchstr
	 * @param str
	 * @param offset
	 * @return
	 */
	function strpos ( suchstr, str, offset) {
	    // suche die Position von suchstr in str (beachte Offset)  
		var i = (str+'').indexOf(suchstr, (offset ? offset : 0));
		return i === -1 ? false : i;
	}
	
	/**
	 * ermittelt Stringlänge (analog zu PHP)
	 * @param str
	 * @return
	 */
	function strlen(str){
	   return str.length;
	}
	
	/**
	 * 
	 * @param search
	 * @param replace
	 * @param subject
	 * @return
	 */
	function str_replace (search, replace, subject)
	{
	  var result = "";
	  var  oldi = 0;
	  var i=0;
	  for (i = subject.indexOf (search) ; i > -1 ; i = subject.indexOf (search, i))
	  {
	    result += subject.substring (oldi, i);
	    result += replace;
	    i += search.length;
	    oldi = i;
	  }
	  return result + subject.substring (oldi, subject.length);
	}

	 function show_tip(n){
		    if(tiptext.length < n ) return; 
		    var txt=tiptext[n].split("|");
		    if(txt.length > 1 )  show_div("tip_div");
		    else return;
		    var html=txt[1]+'<br>'+txt[2];
		    document.getElementById("tip_txt_div").innerHTML=html;
		    	   
		    if(txt[0].length > 1 && document.getElementById("tip_pic").src != txt[0]) {
		    	document.getElementById("tip_pic").src=txt[0];
		    	show_div("tip_div_pic");
		    }
		    if(txt[0].length == 0) { hide_div("tip_pic_div");
		//    	document.getElementById("tip_pic").src="img/detlef.jpg";
		    }
		    var contdiv=document.getElementById("content").style;
		    if(contdiv.opacity)  {
		    	 contdiv.opacity -= 0.01;
		    	if(contdiv.opacity < .9)  { contdiv.opacity  = 0.99; }

		    }
		    if(  document.all&&!window.opera){ 
		        	 filterWert--; 
		             if(filterWert < 90) {filterWert=99; }
		    		 contdiv.filter = "alpha(opacity="+filterWert+")";
		     }
		   
		  // alert(document.getElementById("content").getAttributeNode("style").value); 
		 }
	
	/** ===============================================================================
	 *  
	 *  
	 ** ===============================================================================
	 */
	var shortcut={
		'all_shortcuts':{},//All the shortcuts are stored in this array
		'add': function(shortcut_combination,callback,opt) {
			//Provide a set of default options
			var default_options = {
				'type':'keydown',
				'propagate':false,
				'disable_in_input':false,
				'target':document,
				'keycode':false
			};
			if(!opt) opt = default_options;
			else {
				for(var dfo in default_options) {
					if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
				}
			}

			var ele = opt.target;
			if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
			var ths = this;
			shortcut_combination = shortcut_combination.toLowerCase();

			//The function to be called at keypress
			var func = function(e) {
				e = e || window.event;
				
				if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
					var element;
					if(e.target) element=e.target;
					else if(e.srcElement) element=e.srcElement;
					if(element.nodeType==3) element=element.parentNode;

					if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return null;
				}
		
				//Find Which key is pressed
				if (e.keyCode) code = e.keyCode;
				else if (e.which) code = e.which;
				var character = String.fromCharCode(code).toLowerCase();
				
				if(code == 188) character=","; //If the user presses , when the type is onkeydown
				if(code == 190) character="."; //If the user presses , when the type is onkeydown

				var keys = shortcut_combination.split("+");
				//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
				var kp = 0;
				
				//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
				var shift_nums = {
					"`":"~",
					"1":"!",
					"2":"@",
					"3":"#",
					"4":"$",
					"5":"%",
					"6":"^",
					"7":"&",
					"8":"*",
					"9":"(",
					"0":")",
					"-":"_",
					"=":"+",
					";":":",
					"'":"\"",
					",":"<",
					".":">",
					"/":"?",
					"\\":"|"
				};
				//Special Keys - and their codes
				var special_keys = {
					'esc':27,
					'escape':27,
					'tab':9,
					'space':32,
					'return':13,
					'enter':13,
					'backspace':8,
		
					'scrolllock':145,
					'scroll_lock':145,
					'scroll':145,
					'capslock':20,
					'caps_lock':20,
					'caps':20,
					'numlock':144,
					'num_lock':144,
					'num':144,
					
					'pause':19,
					'break':19,
					
					'insert':45,
					'home':36,
					'delete':46,
					'end':35,
					
					'pageup':33,
					'page_up':33,
					'pu':33,
		
					'pagedown':34,
					'page_down':34,
					'pd':34,
		
					'left':37,
					'up':38,
					'right':39,
					'down':40,
		
					'f1':112,
					'f2':113,
					'f3':114,
					'f4':115,
					'f5':116,
					'f6':117,
					'f7':118,
					'f8':119,
					'f9':120,
					'f10':121,
					'f11':122,
					'f12':123
				};
		
				var modifiers = { 
					shift: { wanted:false, pressed:false},
					ctrl : { wanted:false, pressed:false},
					alt  : { wanted:false, pressed:false},
					meta : { wanted:false, pressed:false}	//Meta is Mac specific
				};
	                        
				if(e.ctrlKey)	modifiers.ctrl.pressed = true;
				if(e.shiftKey)	modifiers.shift.pressed = true;
				if(e.altKey)	modifiers.alt.pressed = true;
				if(e.metaKey)   modifiers.meta.pressed = true;
	                        
				for(var i=0; k=keys[i],i<keys.length; i++) {
					//Modifiers
					if(k == 'ctrl' || k == 'control') {
						kp++;
						modifiers.ctrl.wanted = true;

					} else if(k == 'shift') {
						kp++;
						modifiers.shift.wanted = true;

					} else if(k == 'alt') {
						kp++;
						modifiers.alt.wanted = true;
					} else if(k == 'meta') {
						kp++;
						modifiers.meta.wanted = true;
					} else if(k.length > 1) { //If it is a special key
						if(special_keys[k] == code) kp++;
						
					} else if(opt['keycode']) {
						if(opt['keycode'] == code) kp++;

					} else { //The special keys did not match
						if(character == k) kp++;
						else {
							if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
								character = shift_nums[character]; 
								if(character == k) kp++;
							}
						}
					}
				}
				
				if(kp == keys.length && 
							modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
							modifiers.shift.pressed == modifiers.shift.wanted &&
							modifiers.alt.pressed == modifiers.alt.wanted &&
							modifiers.meta.pressed == modifiers.meta.wanted) {
					callback(e);
		
					if(!opt['propagate']) { //Stop the event
						//e.cancelBubble is supported by IE - this will kill the bubbling process.
						e.cancelBubble = true;
						e.returnValue = false;
		
						//e.stopPropagation works in Firefox.
						if (e.stopPropagation) {
							e.stopPropagation();
							e.preventDefault();
						}
						return false;
					}
				}
				return false;
			};
			this.all_shortcuts[shortcut_combination] = {
				'callback':func, 
				'target':ele, 
				'event': opt['type']
			};
			//Attach the function with the event
			if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
			else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
			else ele['on'+opt['type']] = func;
		},

		//Remove the shortcut - just specify the shortcut and I will remove the binding
		'remove':function(shortcut_combination) {
			shortcut_combination = shortcut_combination.toLowerCase();
			var binding = this.all_shortcuts[shortcut_combination];
			delete(this.all_shortcuts[shortcut_combination]);
			if(!binding) return;
			var type = binding['event'];
			var ele = binding['target'];
			var callback = binding['callback'];

			if(ele.detachEvent) ele.detachEvent('on'+type, callback);
			else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
			else ele['on'+type] = false;
		},
		
		// Remove all shutcuts in Liste
		'removeAll': function() {
			for(x in this.all_shortcuts) {
				this.remove(x);
			}
		} 
	};  // Ende shortcut
	
	function dump(arr,level) {
		var dumped_text = "";
		if(!level) level = 0;
		
		//The padding given at the beginning of the line.
		var level_padding = "";
		for(var j=0;j<level+1;j++) level_padding += "    ";
		
		if(typeof(arr) == 'object') { //Array/Hashes/Objects 
			for(var item in arr) {
				var value = arr[item];
				
				if(typeof(value) == 'object') { //If it is an array,
					dumped_text += level_padding + "'" + item + "' ...\n";
					dumped_text += dump(value,level+1);
				} else {
					dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
				}
			}
		} else { //Stings/Chars/Numbers etc.
			dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
		}
		return dumped_text;
	}
	
	/**
	 *  Zeigt Div in jedem Fall an
	 */

	 function show_div(id){
		 var div_el = document.getElementById(id);
	 	 if (div_el && (div_el.style.display != 'block'))
	       {  div_el.style.display='block';
	         var img_el = document.getElementById('img'+(id));
	         if(img_el != null) {img_el.src = '/common_classes/img/hide.png';}
	      }
	 }
	
	 function hide_div(id){
		 var div_el = document.getElementById(id);
		 if (div_el && (div_el.style.display != 'none'))
	       {  div_el.style.display='none'; }
	 }
	/** 
	 * ist auch in klasse menu
	*  erwarte die ID und setzt die CSS-Display-Eigenschaft auf none/block
	*  ersetzt scr des image damit + - erscheint 
	*  @param  id		id des Images
	*/
	if(typeof toggleDiv != 'function') 
		function toggleDiv(id)
     {    
	     var div_el = document.getElementById(id);
	     if(div_el == null){ return;}
         var img_el = null;
         img_el=document.getElementById('img'+(id));
         
         if (div_el.style.display && div_el.style.display != 'none')
         {  div_el.style.display='none';
            if(img_el !=null){ img_el.src = '/common_classes/img/show.png'; }
         }
         else
         {  div_el.style.display='block';
         if(img_el !=null){ img_el.src = '/common_classes/img/hide.png'; }
         }
     } 
	
	/**
	 * die (Übungs) Funktion erzeugt einen String mit der CSS-Regel des übergebenen Objects.
	 * 
	 * querySelector-/All liefert den/die  Knoten des Doms in denen der angegebene Selector 
	 * verwendet wird (cross-Reference)
	 * 
	 * https://developer.mozilla.org/en/DOM/document.querySelectorAll
	 * http://www.whatwg.org/specs/web-apps/current-work/multipage/urls.html#domtokenlist-0
	 * 
	 * @param obj		übergebenes Object  (z.B.  this aus eventhandler)
	 * 					oder per document.getElementById("id") ermitteltes Object
	 */
	function showClassName(obj){
		
		var classname=obj.className;						// get className  
		var tag=obj.tagName;								// get tagName	
		//var suche=tag.toLowerCase()+"."+classname+"@*";
		var selectors="."+classname;
		var nodeList=document.querySelectorAll(selectors);  //  selectors is a string containing one or more CSS selectors separated by commas.
		if(nodeList.length ){ 
			for(var i=0; i< nodeList.length;i++)	{
				alert("i="+i+" : "+"  Länge Tokenlist "+nodeList.length+"   "+nodeList.item(i).textContent);  // tokenlist
			}
		}
		
		document.getElementById("sheetRules").value+="gesucht : "+selectors+"\n";
		var ind=""+cssRule(selectors,"rulepointer");	// liefert Index in der Form  sheetnr/index
		if(ind != false) {
			var found=""+cssRule(ind);				// liefert komplette Regel  auch +cssRule(selectors)
			var csslocation=cssRule(ind,'ownermarkup');
			var tmp=found.split(';');
			var html=tag+" <;class='"+selectors+"'> \nRegel # "+ind+" in \ncss-Fln "+csslocation+"\n"+selectors+"  {\n";
			var i =0;
			for(i=0;i<tmp.length-1;i++)
				html+="   "+tmp[i]+";\n";
			html += "}\n";
		//	alert(html);
		}
	else {
		alert (selectors+" nicht gefunden");
		}
		return false;
	}
	
	
	// satz mit X
	function assignBorders(obj){
	obj.onmouseover = function () {
		this.style.borderWidth = '3px';
		this.style.width = '6px';
	
		};
	obj.onmouseout = function () {
		this.style.borderWidth = '';
		this.style.width = '';
	
		};
	}
	
	function changeLetterSize(val){
	  alert("noch nicht implementiert");
	}
// ================================================================================

	// https://developer.mozilla.org/en/DOM/element.classList
	// elementClasses is a DOMTokenList representing the class attribute of elementNodeReference.  
	// If the class attribute was not set or is empty elementClasses.length returns 0. 
	// element.classList is read-only.
	
	// Since other browser vendors have not yet implemented the HTML5 classList API, 
	// you still need fallback code. You can use this sample code as fallback.
	// http://demos.hacks.mozilla.org/openweb/classList/classList.js
	
	// Fallback für Classlist
	var containsClass = function (elm, className) {
	    if (document.documentElement.classList) {
	        containsClass = function (elm, className) {
	            return elm.classList.contains(className);
	        };
	    } else {
	        containsClass = function (elm, className) {
	            if (!elm || !elm.className) {
	                return false;
	            }
	            var re = new RegExp('(^|\\s)' + className + '(\\s|$)');
	            return elm.className.match(re);
	        }
	    };
	    return containsClass(elm, className);
	};

	var addClass = function (elm, className) {
	    if (document.documentElement.classList) {
	        addClass = function (elm, className) {
	            elm.classList.add(className);
	        };
	    } else {
	        addClass = function (elm, className) {
	            if (!elm) {
	                return false;
	            }
	            if (!containsClass(elm, className)) {
	                elm.className += (elm.className ? " " : "") + className;
	            }
	            return true;   // eingefügt wg returnwert
	        };
	    }
	    addClass(elm, className);
	    return true;              // hinzugefügt
	};

	var removeClass = function (elm, className) {
	    if (document.documentElement.classList) {
	        removeClass = function (elm, className) {
	            elm.classList.remove(className);
	        }
	    } else {
	        removeClass = function (elm, className) {
	            if (!elm || !elm.className) {
	                return false;
	            }
	            var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)", "g");
	            elm.className = elm.className.replace(regexp, "$2");
	            return true; // eingefügt wg returnwert
	        }
	    }
	    removeClass(elm, className);
	    return true;		// hinzugefügt
	}

	var toggleClass = function (elm, className)
	{
	    if (document.documentElement.classList) {
	        toggleClass = function (elm, className) {
	            return elm.classList.toggle(className);
	        }
	    } else {
	        toggleClass = function (elm, className) {
	            if (containsClass(elm, className))
	            {
	                removeClass(elm, className);
	                return false;
	            } else {
	                addClass(elm, className);
	                return true;
	            }
	        }
	    }
	    return toggleClass(elm, className);
	}
	// ================== Ende ClassList =====================================
	
	
	
	// --- each() Array-Iteration mit each()
	// Die Callbackfunktion erhaelt den Wert des Array-Elements als "this"
	// Als Argument wird der aktuelle Array-Index uebergeben
	// Kompatibel zu "Prototype"
	if (!this.$break) { $break = {};  } // Spezielle Exception, um aus each() auszubrechen
	Array.prototype.each = function(f,context) {
	  var i,n=this.length;
	  try {
	    for (i=0;i<n;++i) {
	      f.call(context||this[i],this[i],i);
	      }
	    }
	    catch(e) {
	      if (e!=$break) {throw e; }
	      }
	  };

	// --- subset(filter)
	// Die Teilmenge aller Arrayelemente bilden, fuer die die filter-Funktion true ergibt
	Array.prototype.subset = function(filter) {
	  var result = [];
	  this.each( function() {
	    if (filter.call(this)) { result.push(this); }
	    });
	  return result;
	  };

	// --- Browseruebergreifender HTTP-Request
	// callback,data,action,headerFields sind optional
	function doRequest(url,callback,data,action,headerFields) {
	  var field,value,
	      theData   = data || null;
	  var requestor =  getRequestor();
	
	  requestor.open(action||"GET",url,!!callback);  // Synchron &lt;=> kein Callback
	  if (callback) { requestor.onreadystatechange = function() {
	     if (requestor.readyState ==4) {
	       callback.call(requestor);  // Mit requestor = this aufrufen
	       }
	     };
	    }
	  if (headerFields) {
	    for (field in headerFields) {
	       requestor.setRequestHeader(field,headerFields[field]);
	       }
	    }
	  requestor.send(theData);
	  return callback ? requestor : requestor.responseText;
	}

	// --- Ein  XMLHTTPRequest-Objekt beschaffen
	function getRequestor() {
	  try {
	// Mozilla, Opera, Safari, MSIE >= 7
	    return new XMLHttpRequest();
	    } catch(e) {
	    try {
	// MSIE >= 6
	        return new ActiveXObject("Microsoft.XMLHTTP");
	    } catch(e) {
	        try {
	// MSIE >= 5
	          return new ActiveXObject("Msxml2.XMLHTTP");
	          } catch(e) {
	         alert("HTTP-Requestobjekt kann nicht erzeugt werden");
	        }
	      }
	    }
	  }

	// --- Shortcut fuer ElementById
	function byId(id) {
	  return document.getElementById(id);
	  }

	// --- Shortcut fuer ElementsByName (auch auf Element-Ebene)
	function byName(name,theParent) {
	  return byCondition( function() { return (this.name == name); }, theParent);
	  }

	// --- Das erste Element des angegebenen Namens
	function firstByName(name,theParent) {
	  var elems = byName(name,theParent);
	  return elems.length > 0 && elems[0] || null;
	  }

	// --- Elemente einer CSS-Klasse
	function byClass(className,theParent) {
	  var classPattern = new RegExp( "\\b" + className + "\\b");
	  return byCondition( function() { return this.className.match(classPattern); }, theParent);
	  }

	// --- Shortcut fuer getElementsByTagName
	function byTagName(tagname,theParent) {
	  var nodeList,i,n,result=[];
	// Wir verwenden die native Funktion, muessen dafuer aber NodeList -> Array mappen
	  nodeList = (theParent||document).getElementsByTagName(tagname);
	  for (i=0,n=nodeList.length;i<n;++i) result.push(nodeList[i]);
	  return result;
	  }


	// --- Uebergeordnetes Element mit einem bestimmten Elementnamen finden
	function getTagNamedParent(elem,parentTagName) {
	  var parentTagNamePattern = new RegExp( parentTagName, "i" );
	  return getParentByCondition( elem, function() {
	    return this.nodeName.match(parentTagNamePattern);
	    } );
	  }

	// --- Uebergeordnetes Element mit einer Bedingung finden
	function getParentByCondition(elem,condition) {
	  var node = elem;
	  while (node) {
	    node = node.parentNode;
	    if (condition.call(node)) {
	      return node;
	      }
	    }
	  return null;
	  }

	// --- Alle Elemente, die eine Bedingung erfuellen
	function byCondition( condition, theParent) {
	  var children, n, i, result = [];
	  children = (theParent || document).childNodes;
	  n = children.length;
	  for (i=0;i<n;++i) {
	    if (children[i].nodeType==1) {
	      if (condition.call(children[i])) { result.push( children[i] ); }
	      Array.prototype.push.apply( result,
	        byCondition( condition, children[i] ) );
	      }
	    }
	  return result;
	  }

	// --- Browseruebergreifende Registrierung einer Funktion
	function registerFor( theElement, theEvent, theHandler ) {
	  if (typeof theHandler != "function") {
	    alert( "Programmfehler: Nur Funktionen koennen registriert werden");
	    return;
	    }

	  var standard = !! window.addEventListener;
	  var eventNormalized = normalizeEventName(theEvent,standard);

	  var f = function(e) {
	    theHandler.call(getSource(e),e);
	    };

	  if (standard) {
	    theElement.addEventListener(eventNormalized, f, false);
	    }
	  else {
	    theElement.attachEvent(eventNormalized, f );
	    }
	  return f; // Fuer allfaellige Deregistrierung
	  }

	// --- Browseruebergreifende Deregistrierung
	function unregister( theElement, theEvent, f ) {
	  var standard = !! window.removeEventListener;
	  var eventNormalized = normalizeEventName(theEvent,standard);
	  if (standard) {
	    theElement.removeEventListener(eventNormalized,f,false);
	    }
	  else {
	    theElement.detachEvent(eventNormalized,f,false);
	    }
	  }

	// --- Browseruebergreifendes Event-Stop
	function stopEventPropagation( theEvent ) {
	  var e = theEvent || window.event;
	  if (e.stopPropagation) e.stopPropagation();
	  else e.cancelBubble = true;
	  }


	function normalizeEventName( theEvent, standard ) {
	  return standard ?
	    theEvent.replace(/^on/,"") :
	    theEvent.replace(/^(?!on)/,"on") ;
	  }

	// --- Browseruebergreifende Ermittlung einer Event-Source
	function getSource( theEvent ) {
	  return theEvent && theEvent.target || window.event.srcElement;
	  }

	// --- Browseruebergreifende Ermittlung eines key-Codes
	function getKeyCode(theEvent) {
	  var e = theEvent || window.event;
	  return e.keyCode || e.which;
	  }

	// --- Element mittels seiner ID ODER seines Namens beschaffen
	// Die ID hat dabei Vorrang
	function getNodeFromIdOrName(idOrName) {
	  return byId(idOrName) || firstByName(idOrName);
	  }

	function getElement( idOrNode ) {
	  return (typeof idOrNode == "string") ?
	         getNodeFromIdOrName(idOrNode) : idOrNode;
	  }

	// --- Einen Text ins DOM setzen
	// idOrNode ist entweder eine id oder ein Elementknoten
	// Der Text wird im textfoermigen Kindknoten gepflegt
	// Wenn ein solcher nicht existiert, wird er zuerst
	// noch angelegt
	// Ist das Element ein Inputfeld, so wird der Text in
	// dessen Value-Attribut gestellt
	function setText(idOrNode,text) {
	  var textNode;
	  var elem = getElement( idOrNode );
	  if (elem) {
	    if (elem.nodeName == "INPUT" || elem.nodeName == "SELECT") {
	      elem.value = text;
	      }
	    else {
	      textNode = elem.firstChild;
	      if (textNode && (textNode.nodeType==3)) {
	        textNode.data = text;
	        }
	      else {
	        textNode = document.createTextNode(text);
	        elem.appendChild(textNode);
	        }
	      }
	    }
	  }

	// --- Einen Text aus dem DOM abholen
	// Dies ist die zu setText() reziproke Methode, um Texte aus dem
	// aktuellen Dokument zu lesen. Liefert fuer input-Felder den
	// Wert des value-Attributs. Fuer alle anderen den Wert des
	// ersten, als textfoermig angenommenen Kindknotens (oder "", wenn
	// kein solcher existiert).
	function getText(idOrNode) {
	  var textNode;
	  var elem = getElement( idOrNode );

	  if (elem) {
	    if (elem.nodeName == "INPUT") { return elem.value; }
	    textNode = elem.firstChild;
	// Wenn das erste Kind ein Textknoten ist
	    if (textNode && (textNode.nodeType==3)) { return textNode.data; }
	    }

	  return "";
	  }

	// --- Navigation zu einer URL mit Uebergabe von Formularfeldern
	// Diese Felder verschmutzen nicht als Get-Parameter die URL, sondern
	// werden im Request mittels adhoc-Formular als POST-Felder gesendet.
	function gotoURL(url, fields) {

	  var name, field, domPort,domPortOwn;
	// Ggf. pruefen, ob Navigation erlaubt
	  if (self.gNoRedirect) {
	// Navigation auf andere Domaenen/Ports nicht erlaubt
	    if (url.match(/^https?:\/\/([\w.:]+)/)) {
	      domPort    = RegExp.$1;
	      domPortOwn = document.location.href.match(/^https?:\/\/([\w.:]+)/)[1];
	      if (domPort != domPortOwn) {
	        alert( msgText.noRedirect ||
	               "Umleitung auf ein anderes System ist nicht zugelassen");
	        isBusy = false;
	        return;
	        }
	      }
	    }

	// adhoc-Formular erzeugen, befuellen und abschicken
	  var submitter = document.createElement("form");
	  setAttributes(submitter,{action:url,method:"post"});

	// Formular mit versteckten Formularfeldern abfuellen
	  for (name in fields) {
	    field = document.createElement("input");
	    setAttributes(field,{type:"hidden",name:name,value:fields[name]});
	    submitter.appendChild(field);
	    }
	  document.body.appendChild(submitter);
	  submitter.submit();
	}

	/** --- CSS-Klasse eines Elements setzen oder entfernen
	 *  setzt den className eines einzelnen Elementes im Dom
	 *  während cssRule die css-Regel verändert und so auf alle
	 *  Elemente mit dieser Klasse Auswirkung hat
	 *  
	 * @param elem
	 * @param theClass
	 */
	function setClass(elem,theClass) {
	   var cl = elem.className;
	   if (!cl) {
	      elem.className = theClass;
	      return;
	      }
	   else if (!cl.match(new RegExp("\\b"+theClass+"\\b"))) {
	      elem.className = cl + " " + theClass;
	      }
	   }
	
	/**
	 * ersetzt / löscht das Element
	 * @param elem
	 * @param theClass
	 */
	function resetClass(elem,theClass) {
	   var cl = elem.className;
	   if (cl) {
	     elem.className = cl.replace( new RegExp("\\b"+theClass+"\\b","g"), "" ).
	                         replace(/^\s+/,"").
	                         replace(/\s+/g," ").
	                         replace(/ $/,"");
	     }                         
	   }

	// --- Attribute in einem Element setzen
	function setAttributes(elem,atts) {
	  var attName;
	  for (attName in atts) {
	    elem[attName] = atts[attName];
	    }
	  }

	// Empfohlener Vererbungsmechanismus der MDC
	function extend(childType, parentType) {
	  var p,proto;
	  if (typeof childType.prototype.__proto__ != "undefined") {
	    childType.prototype.__proto__ = parentType.prototype;
	    }
	  else {
	    for (p in parentType.prototype) {
	      if (typeof childType.prototype[p] == "undefined")
	        childType.prototype[p] = parentType.prototype[p];
	      }
	    }
	  return childType;
	}

	/* ------------------------------------------------------------------------------
     *
	 * Dummy-Funktion, falls eigentliches cssRule() nicht geladen werden konnte
	 * bzw, damit eclipse die Funktion nicht als fehlerhaft deklariert
	 * wird später geladen
	   ------------------------------------------------------------------------------
	*/
	function cssRule(target,attrib,value) { return false; }

// Wenn StackTrace aktiviert wird, dann geht der IE nicht mehr in den dbg-Mode	
//	window.onerror=printStackTrace;
	/**
	 *  http://eriwen.com/javascript/js-stack-trace/
	 */
	// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
	// Luke Smith http://lucassmith.name/ (2008)
	// Loic Dachary <loic@dachary.org> (2008)
	// Johan Euphrosine <proppy@aminche.com> (2008)
	// Oyvind Sean Kinsey http://kinsey.no/blog (2010)
	// Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)

	/**
	* Main function giving a function stack trace with a forced or passed in Error
	*
	* @cfg {Error} e The error to create a stacktrace from (optional)
	* @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
	* @return {Array} of Strings with functions, lines, files, and arguments where possible
	*/
	function printStackTrace(options) {
	    options = options || {guess: true};
	    var ex = options.e || null, guess = !!options.guess;
	    var p = new printStackTrace.implementation(), result = p.run(ex);
	    return (guess) ? p.guessAnonymousFunctions(result) : result;
	}

	printStackTrace.implementation = function() {
	};

	printStackTrace.implementation.prototype = {
	    run: function(ex) {
	        ex = ex || this.createException();
	        // Do not use the stored mode: different exceptions in Chrome
	        // may or may not have arguments or stack
	        var mode = this.mode(ex);
	        // Use either the stored mode, or resolve it
	        //var mode = this._mode || this.mode(ex);
	        if (mode === 'other') {
	            return this.other(arguments.callee);
	        } else {
	            return this[mode](ex);
	        }
	    },

	    createException: function() {
	        try {
	            this.undef();
	            return null;
	        } catch (e) {
	            return e;
	        }
	    },

	    /**
	* @return {String} mode of operation for the environment in question.
	*/
	    mode: function(e) {
	        if (e['arguments'] && e.stack) {
	            return (this._mode = 'chrome');
	        } else if (e.message && typeof window !== 'undefined' && window.opera) {
	            return (this._mode = e.stacktrace ? 'opera10' : 'opera');
	        } else if (e.stack) {
	            return (this._mode = 'firefox');
	        }
	        return (this._mode = 'other');
	    },

	    /**
	* Given a context, function name, and callback function, overwrite it so that it calls
	* printStackTrace() first with a callback and then runs the rest of the body.
	*
	* @param {Object} context of execution (e.g. window)
	* @param {String} functionName to instrument
	* @param {Function} function to call with a stack trace on invocation
	*/
	    instrumentFunction: function(context, functionName, callback) {
	        context = context || window;
	        var original = context[functionName];
	        context[functionName] = function instrumented() {
	            callback.call(this, printStackTrace().slice(4));
	            return context[functionName]._instrumented.apply(this, arguments);
	        };
	        context[functionName]._instrumented = original;
	    },

	    /**
	* Given a context and function name of a function that has been
	* instrumented, revert the function to it's original (non-instrumented)
	* state.
	*
	* @param {Object} context of execution (e.g. window)
	* @param {String} functionName to de-instrument
	*/
	    deinstrumentFunction: function(context, functionName) {
	        if (context[functionName].constructor === Function &&
	                context[functionName]._instrumented &&
	                context[functionName]._instrumented.constructor === Function) {
	            context[functionName] = context[functionName]._instrumented;
	        }
	    },

	    /**
	* Given an Error object, return a formatted Array based on Chrome's stack string.
	*
	* @param e - Error object to inspect
	* @return Array<String> of function calls, files and line numbers
	*/
	    chrome: function(e) {
	        var stack = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, '').
	          replace(/^\s+at\s+/gm, '').
	          replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2').
	          replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}()@$1').split('\n');
	        stack.pop();
	        return stack;
	    },

	    /**
	* Given an Error object, return a formatted Array based on Firefox's stack string.
	*
	* @param e - Error object to inspect
	* @return Array<String> of function calls, files and line numbers
	*/
	    firefox: function(e) {
	    	alert(e.stack);
	        return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
	    },

	    /**
	    * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
	    *
	    * @param e - Error object to inspect
	    * @return Array<String> of function calls, files and line numbers
	    */
	        opera10: function(e) {
	            var stack = e.stacktrace;
	            var lines = stack.split('\n'), ANON = '{anonymous}', lineRE = /.*line (\d+), column (\d+) in ((<anonymous function\:?\s*(\S+))|([^\(]+)\([^\)]*\))(?: in )?(.*)\s*$/i, i, j, len;
	            for (i = 2, j = 0, len = lines.length; i < len - 2; i++) {
	                if (lineRE.test(lines[i])) {
	                    var location = RegExp.$6 + ':' + RegExp.$1 + ':' + RegExp.$2;
	                    var fnName = RegExp.$3;
	                    fnName = fnName.replace(/<anonymous function\:?\s?(\S+)?>/g, ANON);
	                    lines[j++] = fnName + '@' + location;
	                }
	            }

	            lines.splice(j, lines.length - j);
	            return lines;
	        },

	        // Opera 7.x-9.x only!
	        opera: function(e) {
	            var lines = e.message.split('\n'), ANON = '{anonymous}', lineRE = /Line\s+(\d+).*script\s+(http\S+)(?:.*in\s+function\s+(\S+))?/i, i, j, len;

	            for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
	                //TODO: RegExp.exec() would probably be cleaner here
	                if (lineRE.test(lines[i])) {
	                    lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) + ' -- ' + lines[i + 1].replace(/^\s+/, '');
	                }
	            }

	            lines.splice(j, lines.length - j);
	            return lines;
	        },

	        // Safari, IE, and others
	        other: function(curr) {
	            var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10;
	            while (curr && stack.length < maxStackSize) {
	                fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
	                args = Array.prototype.slice.call(curr['arguments'] || []);
	                stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
	                curr = curr.caller;
	            }
	            return stack;
	        },

	        /**
	    * Given arguments array as a String, subsituting type names for non-string types.
	    *
	    * @param {Arguments} object
	    * @return {Array} of Strings with stringified arguments
	    */
	        stringifyArguments: function(args) {
	            var slice = Array.prototype.slice;
	            for (var i = 0; i < args.length; ++i) {
	                var arg = args[i];
	                if (arg === undefined) {
	                    args[i] = 'undefined';
	                } else if (arg === null) {
	                    args[i] = 'null';
	                } else if (arg.constructor) {
	                    if (arg.constructor === Array) {
	                        if (arg.length < 3) {
	                            args[i] = '[' + this.stringifyArguments(arg) + ']';
	                        } else {
	                            args[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']';
	                        }
	                    } else if (arg.constructor === Object) {
	                        args[i] = '#object';
	                    } else if (arg.constructor === Function) {
	                        args[i] = '#function';
	                    } else if (arg.constructor === String) {
	                        args[i] = '"' + arg + '"';
	                    }
	                }
	            }
	            return args.join(',');
	        },

	        sourceCache: {},

	        /**
	    * @return the text from a given URL.
	    */
	        ajax: function(url) {
	            var req = this.createXMLHTTPObject();
	            if (!req) {
	                return;
	            }
	            req.open('GET', url, false);
	            req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
	            req.send('');
	            return req.responseText;
	        },

	        /**
	    * Try XHR methods in order and store XHR factory.
	    *
	    * @return <Function> XHR function or equivalent
	    */
	        createXMLHTTPObject: function() {
	            var xmlhttp, XMLHttpFactories = [
	                function() {
	                    return new XMLHttpRequest();
	                }, function() {
	                    return new ActiveXObject('Msxml2.XMLHTTP');
	                }, function() {
	                    return new ActiveXObject('Msxml3.XMLHTTP');
	                }, function() {
	                    return new ActiveXObject('Microsoft.XMLHTTP');
	                }
	            ];
	            for (var i = 0; i < XMLHttpFactories.length; i++) {
	                try {
	                    xmlhttp = XMLHttpFactories[i]();
	                    // Use memoization to cache the factory
	                    this.createXMLHTTPObject = XMLHttpFactories[i];
	                    return xmlhttp;
	                } catch (e) {
	                }
	            }
	        },

	        /**
	    * Given a URL, check if it is in the same domain (so we can get the source
	    * via Ajax).
	    *
	    * @param url <String> source url
	    * @return False if we need a cross-domain request
	    */
	        isSameDomain: function(url) {
	            return url.indexOf(location.hostname) !== -1;
	        },

	        /**
	    * Get source code from given URL if in the same domain.
	    *
	    * @param url <String> JS source URL
	    * @return <Array> Array of source code lines
	    */
	        getSource: function(url) {
	            if (!(url in this.sourceCache)) {
	                this.sourceCache[url] = this.ajax(url).split('\n');
	            }
	            return this.sourceCache[url];
	        },

	        guessAnonymousFunctions: function(stack) {
	            for (var i = 0; i < stack.length; ++i) {
	                var reStack = /\{anonymous\}\(.*\)@(\w+:\/\/([\-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
	                var frame = stack[i], m = reStack.exec(frame);
	                if (m) {
	                    var file = m[1], lineno = m[4], charno = m[7] || 0; //m[7] is character position in Chrome
	                    if (file && this.isSameDomain(file) && lineno) {
	                        var functionName = this.guessAnonymousFunction(file, lineno, charno);
	                        stack[i] = frame.replace('{anonymous}', functionName);
	                    }
	                }
	            }
	            return stack;
	        },

	        guessAnonymousFunction: function(url, lineNo, charNo) {
	            var ret;
	            try {
	                ret = this.findFunctionName(this.getSource(url), lineNo);
	            } catch (e) {
	                ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString();
	            }
	            return ret;
	        },

	        findFunctionName: function(source, lineNo) {
	            // FIXME findFunctionName fails for compressed source
	            // (more than one function on the same line)
	            // TODO use captured args
	            // function {name}({args}) m[1]=name m[2]=args
	            var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
	            // {name} = function ({args}) TODO args capture
	            // /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/
	            var reFunctionExpression = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function\b/;
	            // {name} = eval()
	            var reFunctionEvaluation = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
	            // Walk backwards in the source lines until we find
	            // the line which matches one of the patterns above
	            var code = "", line, maxLines = 10, m;
	            for (var i = 0; i < maxLines; ++i) {
	                // FIXME lineNo is 1-based, source[] is 0-based
	                line = source[lineNo - i];
	                if (line) {
	                    code = line + code;
	                    m = reFunctionExpression.exec(code);
	                    if (m && m[1]) {
	                        return m[1];
	                    }
	                    m = reFunctionDeclaration.exec(code);
	                    if (m && m[1]) {
	                        //return m[1] + "(" + (m[2] || "") + ")";
	                        return m[1];
	                    }
	                    m = reFunctionEvaluation.exec(code);
	                    if (m && m[1]) {
	                        return m[1];
	                    }
	                }
	            }
	            return '(?)';
	        }
	    };


