//<script>
//MultiLang.js

/**
	Funktion zum Einsetzen lokalisierter Texte. Ersetzungen von Platzhaltern {0}, {1}, etc. werden vorgenommen.
	@param source {string|object} String mit dem Variablennamen der Struktur, die die Übersetzungen enthält oder Objekt, welches ein Feld <i>languageObjectName</i> mit dieser Information enthält.
	@param key {string} Schlüssel der Textresource
	@param defaultText {string} Standardtext - Wird als Fallbackvariante zurückgegeben, falls das Quellobjekt nicht vorhanden ist oder der angegebene Schlüssel darin nicht existiert.
	@param replacementArgs {Array|...} Array mit den Werten für die Ersetzung der Platzhalter oder fortlaufend weiter notierte Funktionsargumente. Existiert für einen Platzhalter kein Wert im Array/der Liste, so wird eine leere Zeichenfolge eingesetzt.

	@sample 
		ml("transObj", "LNG_ErrText", "File {0} not found in Folder {1}.", ["test.txt", "temp"]);
	  //oder	
		ml("transObj", "LNG_ErrText", "File {0} not found in Folder {1}.", "test.txt", "temp");
*/
function ml(source, key, defaultText, replacementArgs)
{
	var languageObjectName=source;
	
	if(typeof languageObjectName=="object")
		languageObjectName=languageObjectName.languageObjectName;
	
	replacementArgs=replacementArgs || [];
	
	if(typeof replacementArgs.pop == "function") //isArray?
		replacementArgs=Array.prototype.slice.call(arguments, 3);
	
	if(self[languageObjectName] && self[languageObjectName][key])
		var text=self[languageObjectName][key];
	else
		var text=defaultText;

	// {0},{1} usw. ersetzen
	return text.replace(/\{(\d+)\}/g, function(wholeMatch, parenthesisOne) {return typeof replacementArgs[parenthesisOne] != 'undefined' ? replacementArgs[parenthesisOne] : "";} );
}//<script>
//JQuery.js
/* prevent execution of jQuery if included more than once */
if(typeof window.jQuery == "undefined") {
/*
 * jQuery 1.1.2 - New Wave Javascript
 *
 * Copyright (c) 2007 John Resig (jquery.com)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * $Date: 2007-02-28 12:03:00 -0500 (Wed, 28 Feb 2007) $
 * $Rev: 1465 $
 */

// Global undefined variable
window.undefined = window.undefined;
var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this )
		return new jQuery(a,c);

	// Make sure that a selection was provided
	a = a || document;
	
	// HANDLE: $(function)
	// Shortcut for document ready
	if ( jQuery.isFunction(a) )
		return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
	
	// Handle HTML strings
	if ( typeof a  == "string" ) {
		// HANDLE: $(html) -> $(array)
		var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
		if ( m )
			a = jQuery.clean( [ m[1] ] );
		
		// HANDLE: $(expr)
		else
			return new jQuery( c ).find( a );
	}
	
	return this.setArray(
		// HANDLE: $(array)
		a.constructor == Array && a ||

		// HANDLE: $(arraylike)
		// Watch for when an array-like object is passed as the selector
		(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

		// HANDLE: $(*)
		[ a ] );
};

// Map over the $ in case of overwrite
if ( typeof $ != "undefined" )
	jQuery._$ = $;
	
// Map the jQuery namespace to the '$' one
var $ = jQuery;

jQuery.fn = jQuery.prototype = {
	jquery: "1.1.2",

	size: function() {
		return this.length;
	},
	
	length: 0,

	get: function( num ) {
		return num == undefined ?

			// Return a 'clean' array
			jQuery.makeArray( this ) :

			// Return just the object
			this[num];
	},
	pushStack: function( a ) {
		var ret = jQuery(a);
		ret.prevObject = this;
		return ret;
	},
	setArray: function( a ) {
		this.length = 0;
		[].push.apply( this, a );
		return this;
	},
	each: function( fn, args ) {
		return jQuery.each( this, fn, args );
	},
	index: function( obj ) {
		var pos = -1;
		this.each(function(i){
			if ( this == obj ) pos = i;
		});
		return pos;
	},

	attr: function( key, value, type ) {
		var obj = key;
		
		// Look for the case where we're accessing a style value
		if ( key.constructor == String )
			if ( value == undefined )
				return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined;
			else {
				obj = {};
				obj[ key ] = value;
			}
		
		// Check to see if we're setting style values
		return this.each(function(index){
			// Set all the styles
			for ( var prop in obj )
				jQuery.attr(
					type ? this.style : this,
					prop, jQuery.prop(this, obj[prop], type, index, prop)
				);
		});
	},

	css: function( key, value ) {
		return this.attr( key, value, "curCSS" );
	},

	text: function(e) {
		if ( typeof e == "string" )
			return this.empty().append( document.createTextNode( e ) );

		var t = "";
		jQuery.each( e || this, function(){
			jQuery.each( this.childNodes, function(){
				if ( this.nodeType != 8 )
					t += this.nodeType != 1 ?
						this.nodeValue : jQuery.fn.text([ this ]);
			});
		});
		return t;
	},

	wrap: function() {
		// The elements to wrap the target around
		var a = jQuery.clean(arguments);

		// Wrap each of the matched elements individually
		return this.each(function(){
			// Clone the structure that we're using to wrap
			var b = a[0].cloneNode(true);

			// Insert it before the element to be wrapped
			this.parentNode.insertBefore( b, this );

			// Find the deepest point in the wrap structure
			while ( b.firstChild )
				b = b.firstChild;

			// Move the matched element to within the wrap structure
			b.appendChild( this );
		});
	},
	append: function() {
		return this.domManip(arguments, true, 1, function(a){
			this.appendChild( a );
		});
	},
	prepend: function() {
		return this.domManip(arguments, true, -1, function(a){
			this.insertBefore( a, this.firstChild );
		});
	},
	before: function() {
		return this.domManip(arguments, false, 1, function(a){
			this.parentNode.insertBefore( a, this );
		});
	},
	after: function() {
		return this.domManip(arguments, false, -1, function(a){
			this.parentNode.insertBefore( a, this.nextSibling );
		});
	},
	end: function() {
		return this.prevObject || jQuery([]);
	},
	find: function(t) {
		return this.pushStack( jQuery.map( this, function(a){
			return jQuery.find(t,a);
		}), t );
	},
	clone: function(deep) {
		return this.pushStack( jQuery.map( this, function(a){
			var a = a.cloneNode( deep != undefined ? deep : true );
			a.$events = null; // drop $events expando to avoid firing incorrect events
			return a;
		}) );
	},

	filter: function(t) {
		return this.pushStack(
			jQuery.isFunction( t ) &&
			jQuery.grep(this, function(el, index){
				return t.apply(el, [index])
			}) ||

			jQuery.multiFilter(t,this) );
	},

	not: function(t) {
		return this.pushStack(
			t.constructor == String &&
			jQuery.multiFilter(t, this, true) ||

			jQuery.grep(this, function(a) {
				return ( t.constructor == Array || t.jquery )
					? jQuery.inArray( a, t ) < 0
					: a != t;
			})
		);
	},

	add: function(t) {
		return this.pushStack( jQuery.merge(
			this.get(),
			t.constructor == String ?
				jQuery(t).get() :
				t.length != undefined && (!t.nodeName || t.nodeName == "FORM") ?
					t : [t] )
		);
	},
	is: function(expr) {
		return expr ? jQuery.filter(expr,this).r.length > 0 : false;
	},

	val: function( val ) {
		return val == undefined ?
			( this.length ? this[0].value : null ) :
			this.attr( "value", val );
	},

	html: function( val ) {
		return val == undefined ?
			( this.length ? this[0].innerHTML : null ) :
			this.empty().append( val );
	},
	domManip: function(args, table, dir, fn){
		var clone = this.length > 1; 
		var a = jQuery.clean(args);
		if ( dir < 0 )
			a.reverse();

		return this.each(function(){
			var obj = this;

			if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") )
				obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody"));

			jQuery.each( a, function(){
				fn.apply( obj, [ clone ? this.cloneNode(true) : this ] );
			});

		});
	}
};

jQuery.extend = jQuery.fn.extend = function() {
	// copy reference to target object
	var target = arguments[0],
		a = 1;

	// extend jQuery itself if only one argument is passed
	if ( arguments.length == 1 ) {
		target = this;
		a = 0;
	}
	var prop;
	while (prop = arguments[a++])
		// Extend the base object
		for ( var i in prop ) target[i] = prop[i];

	// Return the modified object
	return target;
};

jQuery.extend({
	noConflict: function() {
		if ( jQuery._$ )
			$ = jQuery._$;
		return jQuery;
	},

	// This may seem like some crazy code, but trust me when I say that this
	// is the only cross-browser way to do this. --John
	isFunction: function( fn ) {
		return !!fn && typeof fn != "string" && !fn.nodeName && 
			typeof fn[0] == "undefined" && /function/i.test( fn + "" );
	},
	
	// check if an element is in a XML document
	isXMLDoc: function(elem) {
		return elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
	},

	nodeName: function( elem, name ) {
		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
	},
	// args is for internal usage only
	each: function( obj, fn, args ) {
		if ( obj.length == undefined )
			for ( var i in obj )
				fn.apply( obj[i], args || [i, obj[i]] );
		else
			for ( var i = 0, ol = obj.length; i < ol; i++ )
				if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break;
		return obj;
	},
	
	prop: function(elem, value, type, index, prop){
			// Handle executable functions
			if ( jQuery.isFunction( value ) )
				value = value.call( elem, [index] );
				
			// exclude the following css properties to add px
			var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;

			// Handle passing in a number to a CSS property
			return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ?
				value + "px" :
				value;
	},

	className: {
		// internal only, use addClass("class")
		add: function( elem, c ){
			jQuery.each( c.split(/\s+/), function(i, cur){
				if ( !jQuery.className.has( elem.className, cur ) )
					elem.className += ( elem.className ? " " : "" ) + cur;
			});
		},

		// internal only, use removeClass("class")
		remove: function( elem, c ){
			elem.className = c ?
				jQuery.grep( elem.className.split(/\s+/), function(cur){
					return !jQuery.className.has( c, cur );	
				}).join(" ") : "";
		},

		// internal only, use is(".class")
		has: function( t, c ) {
			t = t.className || t;
			// escape regex characters
			c = c.replace(/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
			return t && new RegExp("(^|\\s)" + c + "(\\s|$)").test( t );
		}
	},
	swap: function(e,o,f) {
		for ( var i in o ) {
			e.style["old"+i] = e.style[i];
			e.style[i] = o[i];
		}
		f.apply( e, [] );
		for ( var i in o )
			e.style[i] = e.style["old"+i];
	},

	css: function(e,p) {
		if ( p == "height" || p == "width" ) {
			var old = {}, oHeight, oWidth, d = ["Top","Bottom","Right","Left"];

			jQuery.each( d, function(){
				old["padding" + this] = 0;
				old["border" + this + "Width"] = 0;
			});

			jQuery.swap( e, old, function() {
				if (jQuery.css(e,"display") != "none") {
					oHeight = e.offsetHeight;
					oWidth = e.offsetWidth;
				} else {
					e = jQuery(e.cloneNode(true))
						.find(":radio").removeAttr("checked").end()
						.css({
							visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0"
						}).appendTo(e.parentNode)[0];

					var parPos = jQuery.css(e.parentNode,"position");
					if ( parPos == "" || parPos == "static" )
						e.parentNode.style.position = "relative";

					oHeight = e.clientHeight;
					oWidth = e.clientWidth;

					if ( parPos == "" || parPos == "static" )
						e.parentNode.style.position = "static";

					e.parentNode.removeChild(e);
				}
			});

			return p == "height" ? oHeight : oWidth;
		}

		return jQuery.curCSS( e, p );
	},

	curCSS: function(elem, prop, force) {
		var ret;
		
		if (prop == "opacity" && jQuery.browser.msie)
			return jQuery.attr(elem.style, "opacity");
			
		if (prop == "float" || prop == "cssFloat")
		    prop = jQuery.browser.msie ? "styleFloat" : "cssFloat";
		    
		if (!force && elem.style[prop])
			ret = elem.style[prop];

		else if (document.defaultView && document.defaultView.getComputedStyle) {

			if (prop == "cssFloat" || prop == "styleFloat")
				prop = "float";

			prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
			var cur = document.defaultView.getComputedStyle(elem, null);

			if ( cur )
				ret = cur.getPropertyValue(prop);
			else if ( prop == "display" )
				ret = "none";
			else
				jQuery.swap(elem, { display: "block" }, function() {
				    var c = document.defaultView.getComputedStyle(this, "");
				    ret = c && c.getPropertyValue(prop) || "";
				});

		} else if (elem.currentStyle) {

			var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});
			ret = elem.currentStyle[prop] || elem.currentStyle[newProp];
			
		}

		return ret;
	},
	
	clean: function(a) {
		var r = [];

		jQuery.each( a, function(i,arg){
			if ( !arg ) return;

			if ( arg.constructor == Number )
				arg = arg.toString();
			
			 // Convert html string into DOM nodes
			if ( typeof arg == "string" ) {
				// Trim whitespace, otherwise indexOf won't work as expected
				var s = jQuery.trim(arg), div = document.createElement("div"), tb = [];

				var wrap =
					 // option or optgroup
					!s.indexOf("<opt") &&
					[1, "<select>", "</select>"] ||
					
					(!s.indexOf("<thead") || !s.indexOf("<tbody") || !s.indexOf("<tfoot")) &&
					[1, "<table>", "</table>"] ||
					
					!s.indexOf("<tr") &&
					[2, "<table><tbody>", "</tbody></table>"] ||
					
				 	// <thead> matched above
					(!s.indexOf("<td") || !s.indexOf("<th")) &&
					[3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
					
					[0,"",""];

				// Go to html and back, then peel off extra wrappers
				div.innerHTML = wrap[1] + s + wrap[2];
				
				// Move to the right depth
				while ( wrap[0]-- )
					div = div.firstChild;
				
				// Remove IE's autoinserted <tbody> from table fragments
				if ( jQuery.browser.msie ) {
					
					// String was a <table>, *may* have spurious <tbody>
					if ( !s.indexOf("<table") && s.indexOf("<tbody") < 0 ) 
						tb = div.firstChild && div.firstChild.childNodes;
						
					// String was a bare <thead> or <tfoot>
					else if ( wrap[1] == "<table>" && s.indexOf("<tbody") < 0 )
						tb = div.childNodes;

					for ( var n = tb.length-1; n >= 0 ; --n )
						if ( jQuery.nodeName(tb[n], "tbody") && !tb[n].childNodes.length )
							tb[n].parentNode.removeChild(tb[n]);
					
				}
				
				arg = [];
				for (var i=0, l=div.childNodes.length; i<l; i++)
					arg.push(div.childNodes[i]);
			}

			if ( arg.length === 0 && !jQuery.nodeName(arg, "form") )
				return;
			
			if ( arg[0] == undefined || jQuery.nodeName(arg, "form") )
				r.push( arg );
			else
				r = jQuery.merge( r, arg );

		});

		return r;
	},
	
	attr: function(elem, name, value){
		var fix = jQuery.isXMLDoc(elem) ? {} : {
			"for": "htmlFor",
			"class": "className",
			"float": jQuery.browser.msie ? "styleFloat" : "cssFloat",
			cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
			innerHTML: "innerHTML",
			className: "className",
			value: "value",
			disabled: "disabled",
			checked: "checked",
			readonly: "readOnly",
			selected: "selected"
		};
		
		// IE actually uses filters for opacity ... elem is actually elem.style
		if ( name == "opacity" && jQuery.browser.msie && value != undefined ) {
			// IE has trouble with opacity if it does not have layout
			// Force it by setting the zoom level
			elem.zoom = 1; 

			// Set the alpha filter to set the opacity
			return elem.filter = elem.filter.replace(/alpha\([^\)]*\)/gi,"") +
				( value == 1 ? "" : "alpha(opacity=" + value * 100 + ")" );

		} else if ( name == "opacity" && jQuery.browser.msie )
			return elem.filter ? 
				parseFloat( elem.filter.match(/alpha\(opacity=(.*)\)/)[1] ) / 100 : 1;
		
		// Mozilla doesn't play well with opacity 1
		if ( name == "opacity" && jQuery.browser.mozilla && value == 1 )
			value = 0.9999;
			

		// Certain attributes only work when accessed via the old DOM 0 way
		if ( fix[name] ) {
			if ( value != undefined ) elem[fix[name]] = value;
			return elem[fix[name]];

		} else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method") )
			return elem.getAttributeNode(name).nodeValue;

		// IE elem.getAttribute passes even for style
		else if ( elem.tagName ) {
			if ( value != undefined ) elem.setAttribute( name, value );
			if ( jQuery.browser.msie && /href|src/.test(name) && !jQuery.isXMLDoc(elem) ) 
				return elem.getAttribute( name, 2 );
			return elem.getAttribute( name );

		// elem is actually elem.style ... set the style
		} else {
			name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
			if ( value != undefined ) elem[name] = value;
			return elem[name];
		}
	},
	trim: function(t){
		return t.replace(/^\s+|\s+$/g, "");
	},

	makeArray: function( a ) {
		var r = [];

		if ( a.constructor != Array )
			for ( var i = 0, al = a.length; i < al; i++ )
				r.push( a[i] );
		else
			r = a.slice( 0 );

		return r;
	},

	inArray: function( b, a ) {
		for ( var i = 0, al = a.length; i < al; i++ )
			if ( a[i] == b )
				return i;
		return -1;
	},
	merge: function(first, second) {
		var r = [].slice.call( first, 0 );

		// Now check for duplicates between the two arrays
		// and only add the unique items
		for ( var i = 0, sl = second.length; i < sl; i++ )
			// Check for duplicates
			if ( jQuery.inArray( second[i], r ) == -1 )
				// The item is unique, add it
				first.push( second[i] );

		return first;
	},
	grep: function(elems, fn, inv) {
		// If a string is passed in for the function, make a function
		// for it (a handy shortcut)
		if ( typeof fn == "string" )
			fn = new Function("a","i","return " + fn);

		var result = [];

		// Go through the array, only saving the items
		// that pass the validator function
		for ( var i = 0, el = elems.length; i < el; i++ )
			if ( !inv && fn(elems[i],i) || inv && !fn(elems[i],i) )
				result.push( elems[i] );

		return result;
	},
	map: function(elems, fn) {
		// If a string is passed in for the function, make a function
		// for it (a handy shortcut)
		if ( typeof fn == "string" )
			fn = new Function("a","return " + fn);

		var result = [], r = [];

		// Go through the array, translating each of the items to their
		// new value (or values).
		for ( var i = 0, el = elems.length; i < el; i++ ) {
			var val = fn(elems[i],i);

			if ( val !== null && val != undefined ) {
				if ( val.constructor != Array ) val = [val];
				result = result.concat( val );
			}
		}

		var r = result.length ? [ result[0] ] : [];

		check: for ( var i = 1, rl = result.length; i < rl; i++ ) {
			for ( var j = 0; j < i; j++ )
				if ( result[i] == r[j] )
					continue check;

			r.push( result[i] );
		}

		return r;
	}
});
 
/*
 * Whether the W3C compliant box model is being used.
 *
 * @property
 * @name $.boxModel
 * @type Boolean
 * @cat JavaScript
 */
new function() {
	var b = navigator.userAgent.toLowerCase();

	// Figure out what browser is being used
	jQuery.browser = {
		safari: /webkit/.test(b),
		opera: /opera/.test(b),
		msie: /msie/.test(b) && !/opera/.test(b),
		mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
	};

	// Check to see if the W3C box model is being used
	jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
};

jQuery.each({
	parent: "a.parentNode",
	parents: "jQuery.parents(a)",
	next: "jQuery.nth(a,2,'nextSibling')",
	prev: "jQuery.nth(a,2,'previousSibling')",
	siblings: "jQuery.sibling(a.parentNode.firstChild,a)",
	children: "jQuery.sibling(a.firstChild)"
}, function(i,n){
	jQuery.fn[ i ] = function(a) {
		var ret = jQuery.map(this,n);
		if ( a && typeof a == "string" )
			ret = jQuery.multiFilter(a,ret);
		return this.pushStack( ret );
	};
});

jQuery.each({
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after"
}, function(i,n){
	jQuery.fn[ i ] = function(){
		var a = arguments;
		return this.each(function(){
			for ( var j = 0, al = a.length; j < al; j++ )
				jQuery(a[j])[n]( this );
		});
	};
});

jQuery.each( {
	removeAttr: function( key ) {
		jQuery.attr( this, key, "" );
		this.removeAttribute( key );
	},
	addClass: function(c){
		jQuery.className.add(this,c);
	},
	removeClass: function(c){
		jQuery.className.remove(this,c);
	},
	toggleClass: function( c ){
		jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this, c);
	},
	remove: function(a){
		if ( !a || jQuery.filter( a, [this] ).r.length )
			this.parentNode.removeChild( this );
	},
	empty: function() {
		while ( this.firstChild )
			this.removeChild( this.firstChild );
	}
}, function(i,n){
	jQuery.fn[ i ] = function() {
		return this.each( n, arguments );
	};
});

jQuery.each( [ "eq", "lt", "gt", "contains" ], function(i,n){
	jQuery.fn[ n ] = function(num,fn) {
		return this.filter( ":" + n + "(" + num + ")", fn );
	};
});

jQuery.each( [ "height", "width" ], function(i,n){
	jQuery.fn[ n ] = function(h) {
		return h == undefined ?
			( this.length ? jQuery.css( this[0], n ) : null ) :
			this.css( n, h.constructor == String ? h : h + "px" );
	};
});
jQuery.extend({
	expr: {
		"": "m[2]=='*'||jQuery.nodeName(a,m[2])",
		"#": "a.getAttribute('id')==m[2]",
		":": {
			// Position Checks
			lt: "i<m[3]-0",
			gt: "i>m[3]-0",
			nth: "m[3]-0==i",
			eq: "m[3]-0==i",
			first: "i==0",
			last: "i==r.length-1",
			even: "i%2==0",
			odd: "i%2",

			// Child Checks
			"nth-child": "jQuery.nth(a.parentNode.firstChild,m[3],'nextSibling',a)==a",
			"first-child": "jQuery.nth(a.parentNode.firstChild,1,'nextSibling')==a",
			"last-child": "jQuery.nth(a.parentNode.lastChild,1,'previousSibling')==a",
			"only-child": "jQuery.sibling(a.parentNode.firstChild).length==1",

			// Parent Checks
			parent: "a.firstChild",
			empty: "!a.firstChild",

			// Text Check
			contains: "jQuery.fn.text.apply([a]).indexOf(m[3])>=0",

			// Visibility
			visible: 'a.type!="hidden"&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden"',
			hidden: 'a.type=="hidden"||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden"',

			// Form attributes
			enabled: "!a.disabled",
			disabled: "a.disabled",
			checked: "a.checked",
			selected: "a.selected||jQuery.attr(a,'selected')",

			// Form elements
			text: "a.type=='text'",
			radio: "a.type=='radio'",
			checkbox: "a.type=='checkbox'",
			file: "a.type=='file'",
			password: "a.type=='password'",
			submit: "a.type=='submit'",
			image: "a.type=='image'",
			reset: "a.type=='reset'",
			button: 'a.type=="button"||jQuery.nodeName(a,"button")',
			input: "/input|select|textarea|button/i.test(a.nodeName)"
		},
		".": "jQuery.className.has(a,m[2])",
		"@": {
			"=": "z==m[4]",
			"!=": "z!=m[4]",
			"^=": "z&&!z.indexOf(m[4])",
			"$=": "z&&z.substr(z.length - m[4].length,m[4].length)==m[4]",
			"*=": "z&&z.indexOf(m[4])>=0",
			"": "z",
			_resort: function(m){
				return ["", m[1], m[3], m[2], m[5]];
			},
			_prefix: "z=a[m[3]];if(!z||/href|src/.test(m[3]))z=jQuery.attr(a,m[3]);"
		},
		"[": "jQuery.find(m[2],a).length"
	},
	
	// The regular expressions that power the parsing engine
	parse: [
		// Match: [@value='test'], [@foo]
		/^\[ *(@)([a-z0-9_-]*) *([!*$^=]*) *('?"?)(.*?)\4 *\]/i,

		// Match: [div], [div p]
		/^(\[)\s*(.*?(\[.*?\])?[^[]*?)\s*\]/,

		// Match: :contains('foo')
		/^(:)([a-z0-9_-]*)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/i,

		// Match: :even, :last-chlid
		/^([:.#]*)([a-z0-9_*-]*)/i
	],

	token: [
		/^(\/?\.\.)/, "a.parentNode",
		/^(>|\/)/, "jQuery.sibling(a.firstChild)",
		/^(\+)/, "jQuery.nth(a,2,'nextSibling')",
		/^(~)/, function(a){
			var s = jQuery.sibling(a.parentNode.firstChild);
			return s.slice(jQuery.inArray(a,s) + 1);
		}
	],

	multiFilter: function( expr, elems, not ) {
		var old, cur = [];

		while ( expr && expr != old ) {
			old = expr;
			var f = jQuery.filter( expr, elems, not );
			expr = f.t.replace(/^\s*,\s*/, "" );
			cur = not ? elems = f.r : jQuery.merge( cur, f.r );
		}

		return cur;
	},
	find: function( t, context ) {
		// Quickly handle non-string expressions
		if ( typeof t != "string" )
			return [ t ];

		// Make sure that the context is a DOM Element
		if ( context && !context.nodeType )
			context = null;

		// Set the correct context (if none is provided)
		context = context || document;

		// Handle the common XPath // expression
		if ( !t.indexOf("//") ) {
			context = context.documentElement;
			t = t.substr(2,t.length);

		// And the / root expression
		} else if ( !t.indexOf("/") ) {
			context = context.documentElement;
			t = t.substr(1,t.length);
			if ( t.indexOf("/") >= 1 )
				t = t.substr(t.indexOf("/"),t.length);
		}

		// Initialize the search
		var ret = [context], done = [], last = null;

		// Continue while a selector expression exists, and while
		// we're no longer looping upon ourselves
		while ( t && last != t ) {
			var r = [];
			last = t;

			t = jQuery.trim(t).replace( /^\/\//i, "" );

			var foundToken = false;

			// An attempt at speeding up child selectors that
			// point to a specific element tag
			var re = /^[\/>]\s*([a-z0-9*-]+)/i;
			var m = re.exec(t);

			if ( m ) {
				// Perform our own iteration and filter
				jQuery.each( ret, function(){
					for ( var c = this.firstChild; c; c = c.nextSibling )
						if ( c.nodeType == 1 && ( jQuery.nodeName(c, m[1]) || m[1] == "*" ) )
							r.push( c );
				});

				ret = r;
				t = t.replace( re, "" );
				if ( t.indexOf(" ") == 0 ) continue;
				foundToken = true;
			} else {
				// Look for pre-defined expression tokens
				for ( var i = 0; i < jQuery.token.length; i += 2 ) {
					// Attempt to match each, individual, token in
					// the specified order
					var re = jQuery.token[i];
					var m = re.exec(t);

					// If the token match was found
					if ( m ) {
						// Map it against the token's handler
						r = ret = jQuery.map( ret, jQuery.isFunction( jQuery.token[i+1] ) ?
							jQuery.token[i+1] :
							function(a){ return eval(jQuery.token[i+1]); });

						// And remove the token
						t = jQuery.trim( t.replace( re, "" ) );
						foundToken = true;
						break;
					}
				}
			}

			// See if there's still an expression, and that we haven't already
			// matched a token
			if ( t && !foundToken ) {
				// Handle multiple expressions
				if ( !t.indexOf(",") ) {
					// Clean the result set
					if ( ret[0] == context ) ret.shift();

					// Merge the result sets
					jQuery.merge( done, ret );

					// Reset the context
					r = ret = [context];

					// Touch up the selector string
					t = " " + t.substr(1,t.length);

				} else {
					// Optomize for the case nodeName#idName
					var re2 = /^([a-z0-9_-]+)(#)([a-z0-9\\*_-]*)/i;
					var m = re2.exec(t);
					
					// Re-organize the results, so that they're consistent
					if ( m ) {
					   m = [ 0, m[2], m[3], m[1] ];

					} else {
						// Otherwise, do a traditional filter check for
						// ID, class, and element selectors
						re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;
						m = re2.exec(t);
					}

					// Try to do a global search by ID, where we can
					if ( m[1] == "#" && ret[ret.length-1].getElementById ) {
						// Optimization for HTML document case
						var oid = ret[ret.length-1].getElementById(m[2]);
						
						// Do a quick check for the existence of the actual ID attribute
						// to avoid selecting by the name attribute in IE
						if ( jQuery.browser.msie && oid && oid.id != m[2] )
							oid = jQuery('[@id="'+m[2]+'"]', ret[ret.length-1])[0];

						// Do a quick check for node name (where applicable) so
						// that div#foo searches will be really fast
						ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];

					} else {
						// Pre-compile a regular expression to handle class searches
						if ( m[1] == "." )
							var rec = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");

						// We need to find all descendant elements, it is more
						// efficient to use getAll() when we are already further down
						// the tree - we try to recognize that here
						jQuery.each( ret, function(){
							// Grab the tag name being searched for
							var tag = m[1] != "" || m[0] == "" ? "*" : m[2];

							// Handle IE7 being really dumb about <object>s
							if ( jQuery.nodeName(this, "object") && tag == "*" )
								tag = "param";

							jQuery.merge( r,
								m[1] != "" && ret.length != 1 ?
									jQuery.getAll( this, [], m[1], m[2], rec ) :
									this.getElementsByTagName( tag )
							);
						});

						// It's faster to filter by class and be done with it
						if ( m[1] == "." && ret.length == 1 )
							r = jQuery.grep( r, function(e) {
								return rec.test(e.className);
							});

						// Same with ID filtering
						if ( m[1] == "#" && ret.length == 1 ) {
							// Remember, then wipe out, the result set
							var tmp = r;
							r = [];

							// Then try to find the element with the ID
							jQuery.each( tmp, function(){
								if ( this.getAttribute("id") == m[2] ) {
									r = [ this ];
									return false;
								}
							});
						}

						ret = r;
					}

					t = t.replace( re2, "" );
				}

			}

			// If a selector string still exists
			if ( t ) {
				// Attempt to filter it
				var val = jQuery.filter(t,r);
				ret = r = val.r;
				t = jQuery.trim(val.t);
			}
		}

		// Remove the root context
		if ( ret && ret[0] == context ) ret.shift();

		// And combine the results
		jQuery.merge( done, ret );

		return done;
	},

	filter: function(t,r,not) {
		// Look for common filter expressions
		while ( t && /^[a-z[({<*:.#]/i.test(t) ) {

			var p = jQuery.parse, m;

			jQuery.each( p, function(i,re){
		
				// Look for, and replace, string-like sequences
				// and finally build a regexp out of it
				m = re.exec( t );

				if ( m ) {
					// Remove what we just matched
					t = t.substring( m[0].length );

					// Re-organize the first match
					if ( jQuery.expr[ m[1] ]._resort )
						m = jQuery.expr[ m[1] ]._resort( m );

					return false;
				}
			});

			// :not() is a special case that can be optimized by
			// keeping it out of the expression list
			if ( m[1] == ":" && m[2] == "not" )
				r = jQuery.filter(m[3], r, true).r;

			// Handle classes as a special case (this will help to
			// improve the speed, as the regexp will only be compiled once)
			else if ( m[1] == "." ) {

				var re = new RegExp("(^|\\s)" + m[2] + "(\\s|$)");
				r = jQuery.grep( r, function(e){
					return re.test(e.className || "");
				}, not);

			// Otherwise, find the expression to execute
			} else {
				var f = jQuery.expr[m[1]];
				if ( typeof f != "string" )
					f = jQuery.expr[m[1]][m[2]];

				// Build a custom macro to enclose it
				eval("f = function(a,i){" +
					( jQuery.expr[ m[1] ]._prefix || "" ) +
					"return " + f + "}");

				// Execute it against the current filter
				r = jQuery.grep( r, f, not );
			}
		}

		// Return an array of filtered elements (r)
		// and the modified expression string (t)
		return { r: r, t: t };
	},
	
	getAll: function( o, r, token, name, re ) {
		for ( var s = o.firstChild; s; s = s.nextSibling )
			if ( s.nodeType == 1 ) {
				var add = true;

				if ( token == "." )
					add = s.className && re.test(s.className);
				else if ( token == "#" )
					add = s.getAttribute("id") == name;
	
				if ( add )
					r.push( s );

				if ( token == "#" && r.length ) break;

				if ( s.firstChild )
					jQuery.getAll( s, r, token, name, re );
			}

		return r;
	},
	parents: function( elem ){
		var matched = [];
		var cur = elem.parentNode;
		while ( cur && cur != document ) {
			matched.push( cur );
			cur = cur.parentNode;
		}
		return matched;
	},
	nth: function(cur,result,dir,elem){
		result = result || 1;
		var num = 0;
		for ( ; cur; cur = cur[dir] ) {
			if ( cur.nodeType == 1 ) num++;
			if ( num == result || result == "even" && num % 2 == 0 && num > 1 && cur == elem ||
				result == "odd" && num % 2 == 1 && cur == elem ) return cur;
		}
	},
	sibling: function( n, elem ) {
		var r = [];

		for ( ; n; n = n.nextSibling ) {
			if ( n.nodeType == 1 && (!elem || n != elem) )
				r.push( n );
		}

		return r;
	}
});
/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code orignated from 
 * Dean Edwards' addEvent library.
 */
jQuery.event = {

	// Bind an event to an element
	// Original by Dean Edwards
	add: function(element, type, handler, data) {
		// For whatever reason, IE has trouble passing the window object
		// around, causing it to be cloned in the process
		if ( jQuery.browser.msie && element.setInterval != undefined )
			element = window;

		// if data is passed, bind to handler
		if( data ) 
			handler.data = data;

		// Make sure that the function being executed has a unique ID
		if ( !handler.guid )
			handler.guid = this.guid++;

		// Init the element's event structure
		if (!element.$events)
			element.$events = {};

		// Get the current list of functions bound to this event
		var handlers = element.$events[type];

		// If it hasn't been initialized yet
		if (!handlers) {
			// Init the event handler queue
			handlers = element.$events[type] = {};

			// Remember an existing handler, if it's already there
			if (element["on" + type])
				handlers[0] = element["on" + type];
		}

		// Add the function to the element's handler list
		handlers[handler.guid] = handler;

		// And bind the global event handler to the element
		element["on" + type] = this.handle;

		// Remember the function in a global list (for triggering)
		if (!this.global[type])
			this.global[type] = [];
		this.global[type].push( element );
	},

	guid: 1,
	global: {},

	// Detach an event or set of events from an element
	remove: function(element, type, handler) {
		if (element.$events) {
			var i,j,k;
			if ( type && type.type ) { // type is actually an event object here
				handler = type.handler;
				type    = type.type;
			}
			
			if (type && element.$events[type])
				// remove the given handler for the given type
				if ( handler )
					delete element.$events[type][handler.guid];
					
				// remove all handlers for the given type
				else
					for ( i in element.$events[type] )
						delete element.$events[type][i];
						
			// remove all handlers		
			else
				for ( j in element.$events )
					this.remove( element, j );
			
			// remove event handler if no more handlers exist
			for ( k in element.$events[type] )
				if (k) {
					k = true;
					break;
				}
			if (!k) element["on" + type] = null;
		}
	},

	trigger: function(type, data, element) {
		// Clone the incoming data, if any
		data = jQuery.makeArray(data || []);

		// Handle a global trigger
		if ( !element )
			jQuery.each( this.global[type] || [], function(){
				jQuery.event.trigger( type, data, this );
			});

		// Handle triggering a single element
		else {
			var handler = element["on" + type ], val,
				fn = jQuery.isFunction( element[ type ] );

			if ( handler ) {
				// Pass along a fake event
				data.unshift( this.fix({ type: type, target: element }) );
	
				// Trigger the event
				if ( (val = handler.apply( element, data )) !== false )
					this.triggered = true;
			}

			if ( fn && val !== false )
				element[ type ]();

			this.triggered = false;
		}
	},

	handle: function(event) {
		// Handle the second event of a trigger and when
		// an event is called after a page has unloaded
		if ( typeof jQuery == "undefined" || jQuery.event.triggered ) return;

		// Empty object is for triggered events with no data
		event = jQuery.event.fix( event || window.event || {} ); 

		// returned undefined or false
		var returnValue;

		var c = this.$events[event.type];

		var args = [].slice.call( arguments, 1 );
		args.unshift( event );

		for ( var j in c ) {
			// Pass in a reference to the handler function itself
			// So that we can later remove it
			args[0].handler = c[j];
			args[0].data = c[j].data;

			if ( c[j].apply( this, args ) === false ) {
				event.preventDefault();
				event.stopPropagation();
				returnValue = false;
			}
		}

		// Clean up added properties in IE to prevent memory leak
		if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;

		return returnValue;
	},

	fix: function(event) {
		// Fix target property, if necessary
		if ( !event.target && event.srcElement )
			event.target = event.srcElement;

		// Calculate pageX/Y if missing and clientX/Y available
		if ( event.pageX == undefined && event.clientX != undefined ) {
			var e = document.documentElement, b = document.body || {scrollLeft:0, scrollTop:0};
			event.pageX = event.clientX + (e.scrollLeft || b.scrollLeft);
			event.pageY = event.clientY + (e.scrollTop || b.scrollTop);
		}
				
		// check if target is a textnode (safari)
		if (jQuery.browser.safari && event.target.nodeType == 3) {
			// store a copy of the original event object 
			// and clone because target is read only
			var originalEvent = event;
			event = jQuery.extend({}, originalEvent);
			
			// get parentnode from textnode
			event.target = originalEvent.target.parentNode;
			
			// add preventDefault and stopPropagation since 
			// they will not work on the clone
			event.preventDefault = function() {
				return originalEvent.preventDefault();
			};
			event.stopPropagation = function() {
				return originalEvent.stopPropagation();
			};
		}
		
		// fix preventDefault and stopPropagation
		if (!event.preventDefault)
			event.preventDefault = function() {
				this.returnValue = false;
			};
			
		if (!event.stopPropagation)
		{
			//document.title="adding stop Prop";
			event.stopPropagation = function() {
				this.cancelBubble = true;
			};
		}	
		return event;
	}
};

jQuery.fn.extend({
	bind: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.add( this, type, fn || data, data );
		});
	},
	one: function( type, data, fn ) {
		return this.each(function(){
			jQuery.event.add( this, type, function(event) {
				jQuery(this).unbind(event);
				return (fn || data).apply( this, arguments);
			}, data);
		});
	},
	unbind: function( type, fn ) {
		return this.each(function(){
			jQuery.event.remove( this, type, fn );
		});
	},
	trigger: function( type, data ) {
		return this.each(function(){
			jQuery.event.trigger( type, data, this );
		});
	},
	toggle: function() {
		// Save reference to arguments for access in closure
		var a = arguments;

		return this.click(function(e) {
			// Figure out which function to execute
			this.lastToggle = this.lastToggle == 0 ? 1 : 0;
			
			// Make sure that clicks stop
			e.preventDefault();
			
			// and execute the function
			return a[this.lastToggle].apply( this, [e] ) || false;
		});
	},
	hover: function(f,g) {
		
		// A private function for handling mouse 'hovering'
		function handleHover(e) {
			// Check if mouse(over|out) are still within the same parent element
			var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
	
			// Traverse up the tree
			while ( p && p != this ) try { p = p.parentNode } catch(e) { p = this; };
			
			// If we actually just moused on to a sub-element, ignore it
			if ( p == this ) return false;
			
			// Execute the right function
			return (e.type == "mouseover" ? f : g).apply(this, [e]);
		}
		
		// Bind the function to the two event listeners
		return this.mouseover(handleHover).mouseout(handleHover);
	},
	ready: function(f) {
		// If the DOM is already ready
		if ( jQuery.isReady )
			// Execute the function immediately
			f.apply( document, [jQuery] );
			
		// Otherwise, remember the function for later
		else {
			// Add the function to the wait list
			jQuery.readyList.push( function() { return f.apply(this, [jQuery]) } );
		}
	
		return this;
	}
});

jQuery.extend({
	/*
	 * All the code that makes DOM Ready work nicely.
	 */
	isReady: false,
	readyList: [],
	
	// Handle when the DOM is ready
	ready: function() {
		// Make sure that the DOM is not already loaded
		if ( !jQuery.isReady ) {
			// Remember that the DOM is ready
			jQuery.isReady = true;
			
			// If there are functions bound, to execute
			if ( jQuery.readyList ) {
				// Execute all of them
				jQuery.each( jQuery.readyList, function(){
					this.apply( document );
				});
				
				// Reset the list of functions
				jQuery.readyList = null;
			}
			// Remove event lisenter to avoid memory leak
			if ( jQuery.browser.mozilla || jQuery.browser.opera )
				document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
		}
	}
});

new function(){

	jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," +
		"mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + 
		"submit,keydown,keypress,keyup,error").split(","), function(i,o){
		
		// Handle event binding
		jQuery.fn[o] = function(f){
			return f ? this.bind(o, f) : this.trigger(o);
		};
			
	});
	
	// If Mozilla is used
	if ( jQuery.browser.mozilla || jQuery.browser.opera )
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
	
	// If IE is used, use the excellent hack by Matthias Miller
	// http://www.outofhanwell.com/blog/index.php?title=the_window_onload_problem_revisited
	else if ( jQuery.browser.msie ) {
	
		// Only works if you document.write() it
		//document.write("<scr" + "ipt id= __ie_init  defer=true    src= //:><\/script>"); //buggy [nico]
			
		document.write("<s" + 'cript id="__ie_init" defer="defer" src="/' + '/:"></s' + "cript>");	
	
		// Use the defer script hack
		var script = document.getElementById("__ie_init");
		
		// script does not exist if jQuery is loaded dynamically
		if ( script ) 
			script.onreadystatechange = function() {
				if ( this.readyState != "complete" ) return;
				this.parentNode.removeChild( this );
				jQuery.ready();
			};
	
		// Clear from memory
		script = null;
	
	// If Safari  is used
	} else if ( jQuery.browser.safari )
		// Continually check to see if the document.readyState is valid
		jQuery.safariTimer = setInterval(function(){
			// loaded and complete are both valid states
			if ( document.readyState == "loaded" || 
				document.readyState == "complete" ) {
	
				// If either one are found, remove the timer
				clearInterval( jQuery.safariTimer );
				jQuery.safariTimer = null;
	
				// and execute any waiting functions
				jQuery.ready();
			}
		}, 10); 

	// A fallback to window.onload, that will always work
	jQuery.event.add( window, "load", jQuery.ready );
	
};

// Clean up after IE to avoid memory leaks
if (jQuery.browser.msie)
	jQuery(window).one("unload", function() {
		var global = jQuery.event.global;
		for ( var type in global ) {
			var els = global[type], i = els.length;
			if ( i && type != 'unload' )
				do
					jQuery.event.remove(els[i-1], type);
				while (--i);
		}
	});
jQuery.fn.extend({
	loadIfModified: function( url, params, callback ) {
		this.load( url, params, callback, 1 );
	},
	load: function( url, params, callback, ifModified ) {
		if ( jQuery.isFunction( url ) )
			return this.bind("load", url);

		callback = callback || function(){};

		// Default to a GET request
		var type = "GET";

		// If the second parameter was provided
		if ( params )
			// If it's a function
			if ( jQuery.isFunction( params ) ) {
				// We assume that it's the callback
				callback = params;
				params = null;

			// Otherwise, build a param string
			} else {
				params = jQuery.param( params );
				type = "POST";
			}

		var self = this;

		// Request the remote document
		jQuery.ajax({
			url: url,
			type: type,
			data: params,
			ifModified: ifModified,
			complete: function(res, status){
				if ( status == "success" || !ifModified && status == "notmodified" )
					// Inject the HTML into all the matched elements
					self.attr("innerHTML", res.responseText)
					  // Execute all the scripts inside of the newly-injected HTML
					  .evalScripts()
					  // Execute callback
					  .each( callback, [res.responseText, status, res] );
				else
					callback.apply( self, [res.responseText, status, res] );
			}
		});
		return this;
	},
	serialize: function() {
		return jQuery.param( this );
	},
	evalScripts: function() {
		return this.find("script").each(function(){
			if ( this.src )
				jQuery.getScript( this.src );
			else
				jQuery.globalEval( this.text || this.textContent || this.innerHTML || "" );
		}).end();
	}

});

// If IE is used, create a wrapper for the XMLHttpRequest object
if ( !window.XMLHttpRequest )
	XMLHttpRequest = function(){
		return new ActiveXObject("Microsoft.XMLHTTP");
	};

// Attach a bunch of functions for handling common AJAX events

jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
	jQuery.fn[o] = function(f){
		return this.bind(o, f);
	};
});

jQuery.extend({
	get: function( url, data, callback, type, ifModified ) {
		// shift arguments if data argument was ommited
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = null;
		}
		
		return jQuery.ajax({
			url: url,
			data: data,
			success: callback,
			dataType: type,
			ifModified: ifModified
		});
	},
	getIfModified: function( url, data, callback, type ) {
		return jQuery.get(url, data, callback, type, 1);
	},
	getScript: function( url, callback ) {
		return jQuery.get(url, null, callback, "script");
	},
	getJSON: function( url, data, callback ) {
		return jQuery.get(url, data, callback, "json");
	},
	post: function( url, data, callback, type ) {
		if ( jQuery.isFunction( data ) ) {
			callback = data;
			data = {};
		}

		return jQuery.ajax({
			type: "POST",
			url: url,
			data: data,
			success: callback,
			dataType: type
		});
	},

	// timeout (ms)
	//timeout: 0,
	ajaxTimeout: function( timeout ) {
		jQuery.ajaxSettings.timeout = timeout;
	},
	ajaxSetup: function( settings ) {
		jQuery.extend( jQuery.ajaxSettings, settings );
	},

	ajaxSettings: {
		global: true,
		type: "GET",
		timeout: 0,
		contentType: "application/x-www-form-urlencoded",
		processData: true,
		async: true,
		data: null
	},
	
	// Last-Modified header cache for next request
	lastModified: {},
	ajax: function( s ) {
		// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
		s = jQuery.extend({}, jQuery.ajaxSettings, s);

		// if data available
		if ( s.data ) {
			// convert data if not already a string
			if (s.processData && typeof s.data != "string")
    			s.data = jQuery.param(s.data);
			// append data to url for get requests
			if( s.type.toLowerCase() == "get" ) {
				// "?" + data or "&" + data (in case there are already params)
				s.url += ((s.url.indexOf("?") > -1) ? "&" : "?") + s.data;
				// IE likes to send both get and post data, prevent this
				s.data = null;
			}
		}

		// Watch for a new set of requests
		if ( s.global && ! jQuery.active++ )
			jQuery.event.trigger( "ajaxStart" );

		var requestDone = false;

		// Create the request object
		var xml = new XMLHttpRequest();

		// Open the socket
		xml.open(s.type, s.url, s.async);

		// Set the correct header, if data is being sent
		if ( s.data )
			xml.setRequestHeader("Content-Type", s.contentType);

		// Set the If-Modified-Since header, if ifModified mode.
		if ( s.ifModified )
			xml.setRequestHeader("If-Modified-Since",
				jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" );

		// Set header so the called script knows that it's an XMLHttpRequest
		xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");

		// Make sure the browser sends the right content length
		if ( xml.overrideMimeType )
			xml.setRequestHeader("Connection", "close");
			
		// Allow custom headers/mimetypes
		if( s.beforeSend )
			s.beforeSend(xml);
			
		if ( s.global )
		    jQuery.event.trigger("ajaxSend", [xml, s]);

		// Wait for a response to come back
		var onreadystatechange = function(isTimeout){
			// The transfer is complete and the data is available, or the request timed out
			if ( xml && (xml.readyState == 4 || isTimeout == "timeout") ) {
				requestDone = true;
				
				// clear poll interval
				if (ival) {
					clearInterval(ival);
					ival = null;
				}
				
				var status;
				try {
					status = jQuery.httpSuccess( xml ) && isTimeout != "timeout" ?
						s.ifModified && jQuery.httpNotModified( xml, s.url ) ? "notmodified" : "success" : "error";
					// Make sure that the request was successful or notmodified
					if ( status != "error" ) {
						// Cache Last-Modified header, if ifModified mode.
						var modRes;
						try {
							modRes = xml.getResponseHeader("Last-Modified");
						} catch(e) {} // swallow exception thrown by FF if header is not available
	
						if ( s.ifModified && modRes )
							jQuery.lastModified[s.url] = modRes;
	
						// process the data (runs the xml through httpData regardless of callback)
						var data = jQuery.httpData( xml, s.dataType );
	
						// If a local callback was specified, fire it and pass it the data
						if ( s.success )
							s.success( data, status );
	
						// Fire the global callback
						if( s.global )
							jQuery.event.trigger( "ajaxSuccess", [xml, s] );
					} else
						jQuery.handleError(s, xml, status);
				} catch(e) {
					status = "error";
					jQuery.handleError(s, xml, status, e);
				}

				// The request was completed
				if( s.global )
					jQuery.event.trigger( "ajaxComplete", [xml, s] );

				// Handle the global AJAX counter
				if ( s.global && ! --jQuery.active )
					jQuery.event.trigger( "ajaxStop" );

				// Process result
				if ( s.complete )
					s.complete(xml, status);

				// Stop memory leaks
				if(s.async)
					xml = null;
			}
		};
		
		// don't attach the handler to the request, just poll it instead
		var ival = setInterval(onreadystatechange, 13); 

		// Timeout checker
		if ( s.timeout > 0 )
			setTimeout(function(){
				// Check to see if the request is still happening
				if ( xml ) {
					// Cancel the request
					xml.abort();

					if( !requestDone )
						onreadystatechange( "timeout" );
				}
			}, s.timeout);
			
		// Send the data
		try {
			xml.send(s.data);
		} catch(e) {
			jQuery.handleError(s, xml, null, e);
		}
		
		// firefox 1.5 doesn't fire statechange for sync requests
		if ( !s.async )
			onreadystatechange();
		
		// return XMLHttpRequest to allow aborting the request etc.
		return xml;
	},

	handleError: function( s, xml, status, e ) {
		// If a local callback was specified, fire it
		if ( s.error ) s.error( xml, status, e );

		// Fire the global callback
		if ( s.global )
			jQuery.event.trigger( "ajaxError", [xml, s, e] );
	},

	// Counter for holding the number of active queries
	active: 0,

	// Determines if an XMLHttpRequest was successful or not
	httpSuccess: function( r ) {
		try {
			return !r.status && location.protocol == "file:" ||
				( r.status >= 200 && r.status < 300 ) || r.status == 304 ||
				jQuery.browser.safari && r.status == undefined;
		} catch(e){}
		return false;
	},

	// Determines if an XMLHttpRequest returns NotModified
	httpNotModified: function( xml, url ) {
		try {
			var xmlRes = xml.getResponseHeader("Last-Modified");

			// Firefox always returns 200. check Last-Modified date
			return xml.status == 304 || xmlRes == jQuery.lastModified[url] ||
				jQuery.browser.safari && xml.status == undefined;
		} catch(e){}
		return false;
	},

	/* Get the data out of an XMLHttpRequest.
	 * Return parsed XML if content-type header is "xml" and type is "xml" or omitted,
	 * otherwise return plain text.
	 * (String) data - The type of data that you're expecting back,
	 * (e.g. "xml", "html", "script")
	 */
	httpData: function( r, type ) {
		var ct = r.getResponseHeader("content-type");
		var data = !type && ct && ct.indexOf("xml") >= 0;
		data = type == "xml" || data ? r.responseXML : r.responseText;

		// If the type is "script", eval it in global context
		if ( type == "script" )
			jQuery.globalEval( data );

		// Get the JavaScript object, if JSON is used.
		if ( type == "json" )
			eval( "data = " + data );

		// evaluate scripts within html
		if ( type == "html" )
			jQuery("<div>").html(data).evalScripts();

		return data;
	},

	// Serialize an array of form elements or a set of
	// key/values into a query string
	param: function( a ) {
		var s = [];

		// If an array was passed in, assume that it is an array
		// of form elements
		if ( a.constructor == Array || a.jquery )
			// Serialize the form elements
			jQuery.each( a, function(){
				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
			});

		// Otherwise, assume that it's an object of key/value pairs
		else
			// Serialize the key/values
			for ( var j in a )
				// If the value is an array then the key names need to be repeated
				if ( a[j] && a[j].constructor == Array )
					jQuery.each( a[j], function(){
						s.push( encodeURIComponent(j) + "=" + encodeURIComponent( this ) );
					});
				else
					s.push( encodeURIComponent(j) + "=" + encodeURIComponent( a[j] ) );

		// Return the resulting serialization
		return s.join("&");
	},
	
	// evalulates a script in global context
	// not reliable for safari
	globalEval: function( data ) {
		if ( window.execScript )
			window.execScript( data );
		else if ( jQuery.browser.safari )
			// safari doesn't provide a synchronous global eval
			window.setTimeout( data, 0 );
		else
			eval.call( window, data );
	}

});
jQuery.fn.extend({

	show: function(speed,callback){
		var hidden = this.filter(":hidden");
		speed ?
			hidden.animate({
				height: "show", width: "show", opacity: "show"
			}, speed, callback) :
			
			hidden.each(function(){
				this.style.display = this.oldblock ? this.oldblock : "";
				if ( jQuery.css(this,"display") == "none" )
					this.style.display = "block";
			});
		return this;
	},

	hide: function(speed,callback){
		//debugger
		var visible = this.filter(":visible");
		speed ?
			visible.animate({
				height: "hide", width: "hide", opacity: "hide"
			}, speed, callback) :
			
			visible.each(function(){
				this.oldblock = this.oldblock || jQuery.css(this,"display");
				if ( this.oldblock == "none" )
					this.oldblock = "block";
				this.style.display = "none";
			});
		return this;
	},

	// Save the old toggle function
	_toggle: jQuery.fn.toggle,
	toggle: function( fn, fn2 ){
		var args = arguments;
		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ?
			this._toggle( fn, fn2 ) :
			this.each(function(){
				jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]
					.apply( jQuery(this), args );
			});
	},
	slideDown: function(speed,callback){
		return this.animate({height: "show"}, speed, callback);
	},
	slideUp: function(speed,callback){
		return this.animate({height: "hide"}, speed, callback);
	},
	slideToggle: function(speed, callback){
		return this.each(function(){
			var state = jQuery(this).is(":hidden") ? "show" : "hide";
			jQuery(this).animate({height: state}, speed, callback);
		});
	},
	fadeIn: function(speed, callback){
		return this.animate({opacity: "show"}, speed, callback);
	},
	fadeOut: function(speed, callback){
		return this.animate({opacity: "hide"}, speed, callback);
	},
	fadeTo: function(speed,to,callback){
		return this.animate({opacity: to}, speed, callback);
	},
	animate: function( prop, speed, easing, callback ) {
		return this.queue(function(){
		
			this.curAnim = jQuery.extend({}, prop);
			var opt = jQuery.speed(speed, easing, callback);
			
			for ( var p in prop ) {
				var e = new jQuery.fx( this, opt, p );
				if ( prop[p].constructor == Number )
					e.custom( e.cur(), prop[p] );
				else
					e[ prop[p] ]( prop );
			}
			
		});
	},
	queue: function(type,fn){
		if ( !fn ) {
			fn = type;
			type = "fx";
		}
	
		return this.each(function(){
			if ( !this.queue )
				this.queue = {};
	
			if ( !this.queue[type] )
				this.queue[type] = [];
	
			this.queue[type].push( fn );
		
			if ( this.queue[type].length == 1 )
				fn.apply(this);
		});
	}

});

jQuery.extend({
	
	speed: function(speed, easing, fn) {
		var opt = speed && speed.constructor == Object ? speed : {
			complete: fn || !fn && easing || 
				jQuery.isFunction( speed ) && speed,
			duration: speed,
			easing: fn && easing || easing && easing.constructor != Function && easing
		};

		opt.duration = (opt.duration && opt.duration.constructor == Number ? 
			opt.duration : 
			{ slow: 600, fast: 200 }[opt.duration]) || 400;
	
		// Queueing
		opt.old = opt.complete;
		opt.complete = function(){
			jQuery.dequeue(this, "fx");
			if ( jQuery.isFunction( opt.old ) )
				opt.old.apply( this );
		};
	
		return opt;
	},
	
	easing: {},
	
	queue: {},
	
	dequeue: function(elem,type){
		type = type || "fx";
	
		if ( elem.queue && elem.queue[type] ) {
			// Remove self
			elem.queue[type].shift();
	
			// Get next function
			var f = elem.queue[type][0];
		
			if ( f ) f.apply( elem );
		}
	},

	/*
	 * I originally wrote fx() as a clone of moo.fx and in the process
	 * of making it small in size the code became illegible to sane
	 * people. You've been warned.
	 */
	
	fx: function( elem, options, prop ){

		var z = this;

		// The styles
		var y = elem.style;
		
		// Store display property
		var oldDisplay = jQuery.css(elem, "display");

		// Make sure that nothing sneaks out
		y.overflow = "hidden";

		// Simple function for setting a style value
		z.a = function(){
			if ( options.step )
				options.step.apply( elem, [ z.now ] );

			if ( prop == "opacity" )
				jQuery.attr(y, "opacity", z.now); // Let attr handle opacity
			else if ( parseInt(z.now) ) // My hate for IE will never die
				y[prop] = parseInt(z.now) + "px";
			
			y.display = "block"; // Set display property to block for animation
		};

		// Figure out the maximum number to run to
		z.max = function(){
			return parseFloat( jQuery.css(elem,prop) );
		};

		// Get the current size
		z.cur = function(){
			var r = parseFloat( jQuery.curCSS(elem, prop) );
			return r && r > -10000 ? r : z.max();
		};

		// Start an animation from one number to another
		z.custom = function(from,to){
			z.startTime = (new Date()).getTime();
			z.now = from;
			z.a();

			z.timer = setInterval(function(){
				z.step(from, to);
			}, 13);
		};

		// Simple 'show' function
		z.show = function(){
			if ( !elem.orig ) elem.orig = {};

			// Remember where we started, so that we can go back to it later
			elem.orig[prop] = this.cur();

			options.show = true;

			// Begin the animation
			z.custom(0, elem.orig[prop]);

			// Stupid IE, look what you made me do
			if ( prop != "opacity" )
				y[prop] = "1px";
		};

		// Simple 'hide' function
		z.hide = function(){
			if ( !elem.orig ) elem.orig = {};

			// Remember where we started, so that we can go back to it later
			elem.orig[prop] = this.cur();

			options.hide = true;

			// Begin the animation
			z.custom(elem.orig[prop], 0);
		};
		
		//Simple 'toggle' function
		z.toggle = function() {
			if ( !elem.orig ) elem.orig = {};

			// Remember where we started, so that we can go back to it later
			elem.orig[prop] = this.cur();

			if(oldDisplay == "none")  {
				options.show = true;
				
				// Stupid IE, look what you made me do
				if ( prop != "opacity" )
					y[prop] = "1px";

				// Begin the animation
				z.custom(0, elem.orig[prop]);	
			} else {
				options.hide = true;

				// Begin the animation
				z.custom(elem.orig[prop], 0);
			}		
		};

		// Each step of an animation
		z.step = function(firstNum, lastNum){
			var t = (new Date()).getTime();

			if (t > options.duration + z.startTime) {
				// Stop the timer
				clearInterval(z.timer);
				z.timer = null;

				z.now = lastNum;
				z.a();

				if (elem.curAnim) elem.curAnim[ prop ] = true;

				var done = true;
				for ( var i in elem.curAnim )
					if ( elem.curAnim[i] !== true )
						done = false;

				if ( done ) {
					// Reset the overflow
					y.overflow = "";
					
					// Reset the display
					y.display = oldDisplay;
					if (jQuery.css(elem, "display") == "none")
						y.display = "block";

					// Hide the element if the "hide" operation was done
					if ( options.hide ) 
						y.display = "none";

					// Reset the properties, if the item has been hidden or shown
					if ( options.hide || options.show )
						for ( var p in elem.curAnim )
							if (p == "opacity")
								jQuery.attr(y, p, elem.orig[p]);
							else
								y[p] = "";
				}

				// If a callback was provided, execute it
				if ( done && jQuery.isFunction( options.complete ) )
					// Execute the complete function
					options.complete.apply( elem );
			} else {
				var n = t - this.startTime;
				// Figure out where in the animation we are and set the number
				var p = n / options.duration;
				
				// If the easing function exists, then use it 
				z.now = options.easing && jQuery.easing[options.easing] ?
					jQuery.easing[options.easing](p, n,  firstNum, (lastNum-firstNum), options.duration) :
					// else use default linear easing
					((-Math.cos(p*Math.PI)/2) + 0.5) * (lastNum-firstNum) + firstNum;

				// Perform the next step of the animation
				z.a();
			}
		};
	
	}
});
}
//<script>
//JQuery.dimensions.js
/* Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * $LastChangedDate$
 * $Rev$
 *
 * Version: 1.0a
 */

(function($){

$.fn.extend({
	_height: $.fn.height,
	_width: $.fn.width,
	
	/**
	 * If used on document, returns the document's height (innerHeight)
	 * If used on window, returns the viewport's (window) height
	 * See core docs on height() to see what happens when used on an element.
	 *
	 * @example $("#testdiv").height()
	 * @result 200
	 *
	 * @example $(document).height()
	 * @result 800
	 *
	 * @example $(window).height()
	 * @result 400
	 *
	 * @name height
	 * @type Object
	 * @cat Plugins/Dimensions
	 */
	height: function(val) {
		if ( this[0] == window )
			return self.innerHeight ||
				$.boxModel && document.documentElement.clientHeight || 
				document.body.clientHeight;
		
		if ( this[0] == document )
			return Math.max( document.body.scrollHeight, document.body.offsetHeight );
		
		return this._height(val);
	},
	
	/**
	 * If used on document, returns the document's width (innerWidth)
	 * If used on window, returns the viewport's (window) width
	 * See core docs on height() to see what happens when used on an element.
	 *
	 * @example $("#testdiv").width()
	 * @result 200
	 *
	 * @example $(document).width()
	 * @result 800
	 *
	 * @example $(window).width()
	 * @result 400
	 *
	 * @name width
	 * @type Object
	 * @cat Plugins/Dimensions
	 */
	width: function(val) {
		if ( this[0] == window )
			return self.innerWidth ||
				$.boxModel && document.documentElement.clientWidth ||
				document.body.clientWidth;

		if ( this[0] == document )
			return Math.max( document.body.scrollWidth, document.body.offsetWidth );

		return this._width(val);
	},
	
	/**
	 * Returns the inner height value (without border) for the first matched element.
	 * If used on document, returns the document's height (innerHeight)
	 * If used on window, returns the viewport's (window) height
	 *
	 * @example $("#testdiv").innerHeight()
	 * @result 800
	 *
	 * @name innerHeight
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	innerHeight: function() {
		return this[0] == window || this[0] == document ?
			this.height() :
			this.is(':visible') ?
				this[0].offsetHeight - num(this, 'borderTopWidth') - num(this, 'borderBottomWidth') :
				this.height() + num(this, 'paddingTop') + num(this, 'paddingBottom');
	},
	
	/**
	 * Returns the inner width value (without border) for the first matched element.
	 * If used on document, returns the document's Width (innerWidth)
	 * If used on window, returns the viewport's (window) width
	 *
	 * @example $("#testdiv").innerWidth()
	 * @result 1000
	 *
	 * @name innerWidth
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	innerWidth: function() {
		return this[0] == window || this[0] == document ?
			this.width() :
			this.is(':visible') ?
				this[0].offsetWidth - num(this, 'borderLeftWidth') - num(this, 'borderRightWidth') :
				this.width() + num(this, 'paddingLeft') + num(this, 'paddingRight');
	},
	
	/**
	 * Returns the outer height value (including border) for the first matched element.
	 * Cannot be used on document or window.
	 *
	 * @example $("#testdiv").outerHeight()
	 * @result 1000
	 *
	 * @name outerHeight
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	outerHeight: function() {
		return this[0] == window || this[0] == document ?
			this.height() :
			this.is(':visible') ?
				this[0].offsetHeight :
				this.height() + num(this,'borderTopWidth') + num(this, 'borderBottomWidth') + num(this, 'paddingTop') + num(this, 'paddingBottom');
	},
	
	/**
	 * Returns the outer width value (including border) for the first matched element.
	 * Cannot be used on document or window.
	 *
	 * @example $("#testdiv").outerHeight()
	 * @result 1000
	 *
	 * @name outerHeight
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	outerWidth: function() {
		return this[0] == window || this[0] == document ?
			this.width() :
			this.is(':visible') ?
				this[0].offsetWidth :
				this.width() + num(this, 'borderLeftWidth') + num(this, 'borderRightWidth') + num(this, 'paddingLeft') + num(this, 'paddingRight');
	},
	
	/**
	 * Returns how many pixels the user has scrolled to the right (scrollLeft).
	 * Works on containers with overflow: auto and window/document.
	 *
	 * @example $("#testdiv").scrollLeft()
	 * @result 100
	 *
	 * @name scrollLeft
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	/**
	 * Sets the scrollLeft property and continues the chain.
	 * Works on containers with overflow: auto and window/document.
	 *
	 * @example $("#testdiv").scrollLeft(10).scrollLeft()
	 * @result 10
	 *
	 * @name scrollLeft
	 * @param Number value A positive number representing the desired scrollLeft.
	 * @type jQuery
	 * @cat Plugins/Dimensions
	 */
	scrollLeft: function(val) {
		if ( val != undefined )
			return this.each(function() {
				if (this == window || this == document)
					window.scrollTo( val, $(window).scrollTop() );
				else
					this.scrollLeft = val;
			});
		
		if ( this[0] == window || this[0] == document )
			return self.pageXOffset ||
				$.boxModel && document.documentElement.scrollLeft ||
				document.body.scrollLeft;
				
		return this[0].scrollLeft;
	},
	
	/**
	 * Returns how many pixels the user has scrolled to the bottom (scrollTop).
	 * Works on containers with overflow: auto and window/document.
	 *
	 * @example $("#testdiv").scrollTop()
	 * @result 100
	 *
	 * @name scrollTop
	 * @type Number
	 * @cat Plugins/Dimensions
	 */
	/**
	 * Sets the scrollTop property and continues the chain.
	 * Works on containers with overflow: auto and window/document.
	 *
	 * @example $("#testdiv").scrollTop(10).scrollTop()
	 * @result 10
	 *
	 * @name scrollTop
	 * @param Number value A positive number representing the desired scrollTop.
	 * @type jQuery
	 * @cat Plugins/Dimensions
	 */
	scrollTop: function(val) {
		if ( val != undefined )
			return this.each(function() {
				if (this == window || this == document)
					window.scrollTo( $(window).scrollLeft(), val );
				else
					this.scrollTop = val;
			});
		
		if ( this[0] == window || this[0] == document )
			return self.pageYOffset ||
				$.boxModel && document.documentElement.scrollTop ||
				document.body.scrollTop;

		return this[0].scrollTop;
	},
	
	/**
	 * Returns the location of the element in pixels from the top left corner of the viewport.
	 *
	 * For accurate readings make sure to use pixel values for margins, borders and padding.
	 * 
	 * Known issues:
	 *  - Issue: A div positioned relative or static without any content before it and its parent will report an offsetTop of 0 in Safari
	 *    Workaround: Place content before the realtive div ... and set height and width to 0 and overflow to hidden
	 *
	 * @example $("#testdiv").offset()
	 * @result { top: 100, left: 100, scrollTop: 10, scrollLeft: 10 }
	 *
	 * @example $("#testdiv").offset({ scroll: false })
	 * @result { top: 90, left: 90 }
	 *
	 * @example var offset = {}
	 * $("#testdiv").offset({ scroll: false }, offset)
	 * @result offset = { top: 90, left: 90 }
	 *
	 * @name offset
	 * @param Map options Optional settings to configure the way the offset is calculated.
	 * @option Boolean margin Should the margin of the element be included in the calculations? True by default.
	 * @option Boolean border Should the border of the element be included in the calculations? True by default.
	 * @option Boolean padding Should the padding of the element be included in the calculations? False by default.
	 * @option Boolean scroll Should the scroll offsets of the parent elements be included in the calculations? True by default.
	 *                        When true it adds the totla scroll offets of all parents to the total offset and also adds two properties
	 *                        to the returned object, scrollTop and scrollLeft. If scroll offsets are not needed, this can be a big
	 *                        performance boost if set to false.
	 * @options Boolean lite Will use offsetLite instead of offset when set to true. False by default.
	 * @param Object returnObject An object to store the return value in, so as not to break the chain. If passed in the
	 *                            chain will not be broken and the result will be assigned to this object.
	 * @type Object
	 * @cat Plugins/Dimensions
	 * @author Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
	 */
	offset: function(options, returnObject) {
		var x = 0, y = 0, sl = 0, st = 0,
		    elem = this[0], parent = this[0], op, parPos, elemPos = $.css(elem, 'position'),
		    mo = $.browser.mozilla, ie = $.browser.msie, sf = $.browser.safari, oa = $.browser.opera,
		    absparent = false, relparent = false, 
		    options = $.extend({ margin: true, border: true, padding: false, scroll: true, lite: false }, options || {});
		
		if (options.lite) return this.offsetLite(options, returnObject);

		if (elem.tagName.toLowerCase() == 'body') {
			// Safari is the only one to get offsetLeft and offsetTop properties of the body "correct"
			// Except they all mess up when the body is positioned absolute or relative
			x = elem.offsetLeft;
			y = elem.offsetTop;
			// Mozilla ignores margin and subtracts border from body element
			if (mo) {
				x += num(elem, 'marginLeft') + (num(elem, 'borderLeftWidth')*2);
				y += num(elem, 'marginTop')  + (num(elem, 'borderTopWidth') *2);
			} else
			// Opera ignores margin
			if (oa) {
				x += num(elem, 'marginLeft');
				y += num(elem, 'marginTop');
			} else
			// IE does not add the border in Standards Mode
			if (ie && jQuery.boxModel) {
				x += num(elem, 'borderLeftWidth');
				y += num(elem, 'borderTopWidth');
			}
		} else {
			do {
				parPos = $.css(parent, 'position');
			
				x += parent.offsetLeft;
				y += parent.offsetTop;

				// Mozilla and IE do not add the border
				if (mo || ie) {
					// add borders to offset
					x += num(parent, 'borderLeftWidth');
					y += num(parent, 'borderTopWidth');

					// Mozilla does not include the border on body if an element isn't positioned absolute and is without an absolute parent
					if (mo && parPos == 'absolute') absparent = true;
					// IE does not include the border on the body if an element is position static and without an absolute or relative parent
					if (ie && parPos == 'relative') relparent = true;
				}

				op = parent.offsetParent;
				do {
					if (options.scroll) {
						sl += parent.scrollLeft;
						st += parent.scrollTop;
					}
				
					if (mo && parent != elem && $.css(parent, 'overflow') != 'visible') {
						x += num(parent, 'borderLeftWidth');
						y += num(parent, 'borderTopWidth');
					}
				
					parent = parent.parentNode;
				} while (parent != op);
				parent = op;

				if (parent && (parent.tagName.toLowerCase() == 'body' || parent.tagName.toLowerCase() == 'html')) {
					// Safari and IE Standards Mode doesn't add the body margin for elments positioned with static or relative
					if ((sf || (ie && $.boxModel)) && elemPos != 'absolute' && elemPos != 'fixed') {
						x += num(parent, 'marginLeft');
						y += num(parent, 'marginTop');
					}
					// Mozilla does not include the border on body if an element isn't positioned absolute and is without an absolute parent
					// IE does not include the border on the body if an element is positioned static and without an absolute or relative parent
					if ( (mo && !absparent && elemPos != 'fixed') || 
					     (ie && elemPos == 'static' && !relparent) ) {
						x += num(parent, 'borderLeftWidth');
						y += num(parent, 'borderTopWidth');
					}
					break; // Exit the loop
				}
			} while (parent);
		}

		var returnValue = handleOffsetReturn(elem, options, x, y, sl, st);

		if (returnObject) { $.extend(returnObject, returnValue); return this; }
		else              { return returnValue; }
	},
	
	offsetLite: function(options, returnObject) {
		var x = 0, y = 0, sl = 0, st = 0, parent = this[0], op, 
		    options = $.extend({ margin: true, border: true, padding: false, scroll: true }, options || {});
				
		do {
			x += parent.offsetLeft;
			y += parent.offsetTop;

			op = parent.offsetParent;
			if (options.scroll) {
				do {
					sl += parent.scrollLeft;
					st += parent.scrollTop;
					parent = parent.parentNode;
				} while(parent != op);
			}
			parent = op;
		} while (parent && parent.tagName.toLowerCase() != 'body' && parent.tagName.toLowerCase() != 'html');

		var returnValue = handleOffsetReturn(this[0], options, x, y, sl, st);

		if (returnObject) { $.extend(returnObject, returnValue); return this; }
		else              { return returnValue; }
	}
});

/**
 * Handles converting a CSS Style into an Integer.
 * @private
 */
var num = function(el, prop) {
	return parseInt($.css(el.jquery?el[0]:el,prop))||0;
};

/**
 * Handles the return value of the offset and offsetLite methods.
 * @private
 */
var handleOffsetReturn = function(elem, options, x, y, sl, st) {
	if ( !options.margin ) {
		x -= num(elem, 'marginLeft');
		y -= num(elem, 'marginTop');
	}

	// Safari and Opera do not add the border for the element
	if ( options.border && ($.browser.safari || $.browser.opera) ) {
		x += num(elem, 'borderLeftWidth');
		y += num(elem, 'borderTopWidth');
	} else if ( !options.border && !($.browser.safari || $.browser.opera) ) {
		x -= num(elem, 'borderLeftWidth');
		y -= num(elem, 'borderTopWidth');
	}

	if ( options.padding ) {
		x += num(elem, 'paddingLeft');
		y += num(elem, 'paddingTop');
	}

	// Opera thinks offset is scroll offset for display: inline elements
	if ($.browser.opera && options.scroll && $.css(elem, 'display') == 'inline') {
		sl -= elem.scrollLeft;
		st -= elem.scrollTop;
	}

	return options.scroll ? { top: y - st, left: x - sl, scrollTop:  st, scrollLeft: sl }
	                      : { top: y, left: x };
};

})(jQuery);//<script>
/**
 * Interface Elements for jQuery
 * utility function
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 *
 */

jQuery.iUtil = {
	getPosition : function(e)
	{
		var x = 0;
		var y = 0;
		var es = e.style;
		var restoreStyles = false;
		if (jQuery(e).css('display') == 'none') {
			var oldVisibility = es.visibility;
			var oldPosition = es.position;
			restoreStyles = true;
			es.visibility = 'hidden';
			es.display = 'block';
			es.position = 'absolute';
		}
		var el = e;
		while (el){
			x += el.offsetLeft + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderLeftWidth)||0:0);
			y += el.offsetTop + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderTopWidth)||0:0);
			el = el.offsetParent;
		}
		el = e;
		while (el && el.tagName  && el.tagName.toLowerCase() != 'body')
		{
			x -= el.scrollLeft||0;
			y -= el.scrollTop||0;
			el = el.parentNode;
		}
		if (restoreStyles == true) {
			es.display = 'none';
			es.position = oldPosition;
			es.visibility = oldVisibility;
		}
		return {x:x, y:y};
	},
	getPositionLite : function(el)
	{
		var x = 0, y = 0;
		while(el) {
			x += el.offsetLeft || 0;
			y += el.offsetTop || 0;
			el = el.offsetParent;
		}
		return {x:x, y:y};
	},
	getSize : function(e)
	{
		var w = jQuery.css(e,'width');
		var h = jQuery.css(e,'height');
		var wb = 0;
		var hb = 0;
		var es = e.style;
		if (jQuery(e).css('display') != 'none') {
			wb = e.offsetWidth;
			hb = e.offsetHeight;
		} else {
			var oldVisibility = es.visibility;
			var oldPosition = es.position;
			es.visibility = 'hidden';
			es.display = 'block';
			es.position = 'absolute';
			wb = e.offsetWidth;
			hb = e.offsetHeight;
			es.display = 'none';
			es.position = oldPosition;
			es.visibility = oldVisibility;
		}
		return {w:w, h:h, wb:wb, hb:hb};
	},
	getSizeLite : function(el)
	{
		return {
			wb:el.offsetWidth||0,
			hb:el.offsetHeight||0
		};
	},
	getClient : function(e)
	{
		var h, w, de;
		if (e) {
			w = e.clientWidth;
			h = e.clientHeight;
		} else {
			de = document.documentElement;
			w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
			h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;
		}
		return {w:w,h:h};
	},
	getScroll : function (e)
	{
		var t=0, l=0, w=0, h=0, iw=0, ih=0;
		if (e && e.nodeName.toLowerCase() != 'body') {
			t = e.scrollTop;
			l = e.scrollLeft;
			w = e.scrollWidth;
			h = e.scrollHeight;
			iw = 0;
			ih = 0;
		} else  {
			if (document.documentElement) {
				t = document.documentElement.scrollTop;
				l = document.documentElement.scrollLeft;
				w = document.documentElement.scrollWidth;
				h = document.documentElement.scrollHeight;
			} else if (document.body) {
				t = document.body.scrollTop;
				l = document.body.scrollLeft;
				w = document.body.scrollWidth;
				h = document.body.scrollHeight;
			}
			iw = self.innerWidth||document.documentElement.clientWidth||document.body.clientWidth||0;
			ih = self.innerHeight||document.documentElement.clientHeight||document.body.clientHeight||0;
		}
		return { t: t, l: l, w: w, h: h, iw: iw, ih: ih };
	},
	getMargins : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('marginTop') || '';
		var r = el.css('marginRight') || '';
		var b = el.css('marginBottom') || '';
		var l = el.css('marginLeft') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getPadding : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('paddingTop') || '';
		var r = el.css('paddingRight') || '';
		var b = el.css('paddingBottom') || '';
		var l = el.css('paddingLeft') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getBorder : function(e, toInteger)
	{
		var el = jQuery(e);
		var t = el.css('borderTopWidth') || '';
		var r = el.css('borderRightWidth') || '';
		var b = el.css('borderBottomWidth') || '';
		var l = el.css('borderLeftWidth') || '';
		if (toInteger)
			return {
				t: parseInt(t)||0,
				r: parseInt(r)||0,
				b: parseInt(b)||0,
				l: parseInt(l)||0
			};
		else
			return {t: t, r: r,	b: b, l: l};
	},
	getPointer : function(event)
	{
		var x = event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)) || 0;
		var y = event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)) || 0;
		return {x:x, y:y};
	},
	traverseDOM : function(nodeEl, func)
	{
		func(nodeEl);
		nodeEl = nodeEl.firstChild;
		while(nodeEl){
			jQuery.iUtil.traverseDOM(nodeEl, func);
			nodeEl = nodeEl.nextSibling;
		}
	},
	purgeEvents : function(nodeEl)
	{
		jQuery.iUtil.traverseDOM(
			nodeEl,
			function(el)
			{
				for(var attr in el){
					if(typeof el[attr] === 'function') {
						el[attr] = null;
					}
				}
			}
		);
	},
	centerEl : function(el, axis)
	{
		var clientScroll = jQuery.iUtil.getScroll();
		var windowSize = jQuery.iUtil.getSize(el);
		if (!axis || axis == 'vertically')
			jQuery(el).css(
				{
					top: clientScroll.t + ((Math.max(clientScroll.h,clientScroll.ih) - clientScroll.t - windowSize.hb)/2) + 'px'
				}
			);
		if (!axis || axis == 'horizontally')
			jQuery(el).css(
				{
					left:	clientScroll.l + ((Math.max(clientScroll.w,clientScroll.iw) - clientScroll.l - windowSize.wb)/2) + 'px'
				}
			);
	},
	fixPNG : function (el, emptyGIF) {
		var images = jQuery('img[@src*="png"]', el||document), png;
		images.each( function() {
			png = this.src;				
			this.src = emptyGIF;
			this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + png + "')";
		});
	}
};

// Helper function to support older browsers!
[].indexOf || (Array.prototype.indexOf = function(v, n){
	n = (n == null) ? 0 : n;
	var m = this.length;
	for (var i=n; i<m; i++)
		if (this[i] == v)
			return i;
	return -1;
});


/**
 * Interface Elements for jQuery
 * Draggable
 *
 * http://interface.eyecon.ro
 *
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 */
 
/**
 * Create a draggable element with a number of advanced options including callback, Google Maps type draggables,
 * reversion, ghosting, and grid dragging.
 * 
 * @name Draggable
 * @descr Creates draggable elements that can be moved across the page.
 * @param Hash hash A hash of parameters. All parameters are optional.
 * @option String handle (optional) The jQuery selector matching the handle that starts the draggable
 * @option DOMElement handle (optional) The DOM Element of the handle that starts the draggable
 * @option Boolean revert (optional) When true, on stop-drag the element returns to initial position
 * @option Boolean ghosting (optional) When true, a copy of the element is moved
 * @option Integer zIndex (optional) zIndex depth for the element while it is being dragged
 * @option Float opacity (optional) A number between 0 and 1 that indicates the opacity of the element while being dragged
 * @option Integer grid (optional) (optional) A number of pixels indicating the grid that the element should snap to
 * @option Array grid (optional) A number of x-pixels and y-pixels indicating the grid that the element should snap to
 * @option Integer fx (optional) Duration for the effect (like ghosting or revert) applied to the draggable
 * @option String containment (optional) Define the zone where the draggable can be moved. 'parent' moves it inside parent
 *                           element, while 'document' prevents it from leaving the document and forcing additional
 *                           scrolling
 * @option Array containment An 4-element array (left, top, width, height) indicating the containment of the element
 * @option String axis (optional) Set an axis: vertical (with 'vertically') or horizontal (with 'horizontally')
 * @option Function onStart (optional) Callback function triggered when the dragging starts
 * @option Function onStop (optional) Callback function triggered when the dragging stops
 * @option Function onChange (optional) Callback function triggered when the dragging stop *and* the element was moved at least
 *                          one pixel
 * @option Function onDrag (optional) Callback function triggered while the element is dragged. Receives two parameters: x and y
 *                        coordinates. You can return an object with new coordinates {x: x, y: y} so this way you can
 *                        interact with the dragging process (for instance, build your containment)
 * @option Boolean insideParent Forces the element to remain inside its parent when being dragged (like Google Maps)
 * @option Integer snapDistance (optional) The element is not moved unless it is dragged more than snapDistance. You can prevent
 *                             accidental dragging and keep regular clicking enabled (for links or form elements, 
 *                             for instance)
 * @option Object cursorAt (optional) The dragged element is moved to the cursor position with the offset specified. Accepts value
 *                        for top, left, right and bottom offset. Basically, this forces the cursor to a particular
 *                        position during the entire drag operation.
 * @option Boolean autoSize (optional) When true, the drag helper is resized to its content, instead of the dragged element's sizes
 * @option String frameClass (optional) When is set the cloned element is hidden so only a frame is dragged
 * @type jQuery
 * @cat Plugins/Interface
 * @author Stefan Petre
 */

jQuery.iDrag =	{
	helper : null,
	dragged: null,
	destroy : function()
	{
		return this.each(
			function ()
			{
				if (this.isDraggable) {
					this.dragCfg.dhe.unbind('mousedown', jQuery.iDrag.draginit);
					this.dragCfg = null;
					this.isDraggable = false;
					if(jQuery.browser.msie) {
						this.unselectable = "off";
					} else {
						this.style.MozUserSelect = '';
						this.style.KhtmlUserSelect = '';
						this.style.userSelect = '';
					}
				}
			}
		);
	},
	draginit : function (e)
	{
	var d1=new Date();
		if (jQuery.iDrag.dragged != null) {
			jQuery.iDrag.dragstop(e);
			return false;
		}
		var elm = this.dragElem;
		jQuery(document)
			.bind('mousemove', jQuery.iDrag.dragmove)
			.bind('mouseup', jQuery.iDrag.dragstop);
		elm.dragCfg.pointer = jQuery.iUtil.getPointer(e);
		elm.dragCfg.currentPointer = elm.dragCfg.pointer;
		elm.dragCfg.init = false;
		elm.dragCfg.fromHandler = this != this.dragElem;
		jQuery.iDrag.dragged = elm;
		if (elm.dragCfg.si && this != this.dragElem) {
				parentPos = jQuery.iUtil.getPosition(elm.parentNode);
				sliderSize = jQuery.iUtil.getSize(elm);
				sliderPos = {
					x : parseInt(jQuery.css(elm,'left')) || 0,
					y : parseInt(jQuery.css(elm,'top')) || 0
				};
				dx = elm.dragCfg.currentPointer.x - parentPos.x - sliderSize.wb/2 - sliderPos.x;
				dy = elm.dragCfg.currentPointer.y - parentPos.y - sliderSize.hb/2 - sliderPos.y;
				jQuery.iSlider.dragmoveBy(elm, [dx, dy]);
		}
	var d2=new Date();
//	alert(d2.getTime()-d1.getTime());
		return jQuery.selectKeyHelper||false;
	},

	dragstart : function(e)
	{
	var d1=new Date();
	
		var elm = jQuery.iDrag.dragged;
		elm.dragCfg.init = true;

		var dEs = elm.style;

		elm.dragCfg.oD = jQuery.css(elm,'display');
		elm.dragCfg.oP = jQuery.css(elm,'position');
		if (!elm.dragCfg.initialPosition)
			elm.dragCfg.initialPosition = elm.dragCfg.oP;

		elm.dragCfg.oR = {
			x : parseInt(jQuery.css(elm,'left')) || 0,
			y : parseInt(jQuery.css(elm,'top')) || 0
		};
		elm.dragCfg.diffX = 0;
		elm.dragCfg.diffY = 0;
		if (jQuery.browser.msie) {
			var oldBorder = jQuery.iUtil.getBorder(elm, true);
			elm.dragCfg.diffX = oldBorder.l||0;
			elm.dragCfg.diffY = oldBorder.t||0;
		}

		elm.dragCfg.oC = jQuery.extend(
			jQuery.iUtil.getPosition(elm),
			jQuery.iUtil.getSize(elm)
		);
		if (elm.dragCfg.oP != 'relative' && elm.dragCfg.oP != 'absolute') {
			dEs.position = 'relative';
		}

		jQuery.iDrag.helper.empty();
		var clonedEl = elm.cloneNode(true);
		
		jQuery(clonedEl).css(
			{
				display:	'block',
				left:		'0px',
				top: 		'0px'
			}
		);
		clonedEl.style.marginTop = '0';
		clonedEl.style.marginRight = '0';
		clonedEl.style.marginBottom = '0';
		clonedEl.style.marginLeft = '0';
		jQuery.iDrag.helper.append(clonedEl);
		
		var dhs = jQuery.iDrag.helper.get(0).style;

		if (elm.dragCfg.autoSize) {
			dhs.width = 'auto';
			dhs.height = 'auto';
		} else {
			dhs.height = elm.dragCfg.oC.hb + 'px';
			dhs.width = elm.dragCfg.oC.wb + 'px';
		}

		dhs.display = 'block';
		dhs.marginTop = '0px';
		dhs.marginRight = '0px';
		dhs.marginBottom = '0px';
		dhs.marginLeft = '0px';

		//remeasure the clone to check if the size was changed by user's functions
		jQuery.extend(
			elm.dragCfg.oC,
			jQuery.iUtil.getSize(clonedEl)
		);

		if (elm.dragCfg.cursorAt) {
			if (elm.dragCfg.cursorAt.left) {
				elm.dragCfg.oR.x += elm.dragCfg.pointer.x - elm.dragCfg.oC.x - elm.dragCfg.cursorAt.left;
				elm.dragCfg.oC.x = elm.dragCfg.pointer.x - elm.dragCfg.cursorAt.left;
			}
			if (elm.dragCfg.cursorAt.top) {
				elm.dragCfg.oR.y += elm.dragCfg.pointer.y - elm.dragCfg.oC.y - elm.dragCfg.cursorAt.top;
				elm.dragCfg.oC.y = elm.dragCfg.pointer.y - elm.dragCfg.cursorAt.top;
			}
			if (elm.dragCfg.cursorAt.right) {
				elm.dragCfg.oR.x += elm.dragCfg.pointer.x - elm.dragCfg.oC.x -elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.right;
				elm.dragCfg.oC.x = elm.dragCfg.pointer.x - elm.dragCfg.oC.wb + elm.dragCfg.cursorAt.right;
			}
			if (elm.dragCfg.cursorAt.bottom) {
				elm.dragCfg.oR.y += elm.dragCfg.pointer.y - elm.dragCfg.oC.y - elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.bottom;
				elm.dragCfg.oC.y = elm.dragCfg.pointer.y - elm.dragCfg.oC.hb + elm.dragCfg.cursorAt.bottom;
			}
		}
		elm.dragCfg.nx = elm.dragCfg.oR.x;
		elm.dragCfg.ny = elm.dragCfg.oR.y;

		if (elm.dragCfg.insideParent || elm.dragCfg.containment == 'parent') {
			parentBorders = jQuery.iUtil.getBorder(elm.parentNode, true);
			elm.dragCfg.oC.x = elm.offsetLeft + (jQuery.browser.msie ? 0 : jQuery.browser.opera ? -parentBorders.l : parentBorders.l);
			elm.dragCfg.oC.y = elm.offsetTop + (jQuery.browser.msie ? 0 : jQuery.browser.opera ? -parentBorders.t : parentBorders.t);
			jQuery(elm.parentNode).append(jQuery.iDrag.helper.get(0));
		}
		if (elm.dragCfg.containment) {
			jQuery.iDrag.getContainment(elm);
			elm.dragCfg.onDragModifier.containment = jQuery.iDrag.fitToContainer;
		}

		if (elm.dragCfg.si) {
			jQuery.iSlider.modifyContainer(elm);
		}

		dhs.left = elm.dragCfg.oC.x - elm.dragCfg.diffX + 'px';
		dhs.top = elm.dragCfg.oC.y - elm.dragCfg.diffY + 'px';
		//resize the helper to fit the clone
		dhs.width = elm.dragCfg.oC.wb + 'px';
		dhs.height = elm.dragCfg.oC.hb + 'px';

		jQuery.iDrag.dragged.dragCfg.prot = false;

		if (elm.dragCfg.gx) {
			elm.dragCfg.onDragModifier.grid = jQuery.iDrag.snapToGrid;
		}
		if (elm.dragCfg.zIndex != false) {
			jQuery.iDrag.helper.css('zIndex', elm.dragCfg.zIndex);
		}
		if (elm.dragCfg.opacity) {
			jQuery.iDrag.helper.css('opacity', elm.dragCfg.opacity);
			if (window.ActiveXObject) {
				jQuery.iDrag.helper.css('filter', 'alpha(opacity=' + elm.dragCfg.opacity * 100 + ')');
			}
		}

		if(elm.dragCfg.frameClass) {
			jQuery.iDrag.helper.addClass(elm.dragCfg.frameClass);
			jQuery.iDrag.helper.get(0).firstChild.style.display = 'none';
		}
		if (elm.dragCfg.onStart)
			elm.dragCfg.onStart.apply(elm, [clonedEl, elm.dragCfg.oR.x, elm.dragCfg.oR.y]);
		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.highlight(elm);
		}
		if (elm.dragCfg.ghosting == false) {
			dEs.display = 'none';
		}
//	var d2=new Date();
//	alert(d2.getTime()-d1.getTime());	
		return false;
	},

	getContainment : function(elm)
	{
//	var d1=new Date();
		
		if (elm.dragCfg.containment.constructor == String) {
			if (elm.dragCfg.containment == 'parent') {
				elm.dragCfg.cont = jQuery.extend(
					{x:0,y:0},
					jQuery.iUtil.getSize(elm.parentNode)
				);
				var contBorders = jQuery.iUtil.getBorder(elm.parentNode, true);
				elm.dragCfg.cont.w = elm.dragCfg.cont.wb - contBorders.l - contBorders.r;
				elm.dragCfg.cont.h = elm.dragCfg.cont.hb - contBorders.t - contBorders.b;
			} else if (elm.dragCfg.containment == 'document') {
				var clnt = jQuery.iUtil.getClient();
				elm.dragCfg.cont = {
					x : 0,
					y : 0,
					w : clnt.w,
					h : clnt.h
				};
			}
		} else if (elm.dragCfg.containment.constructor == Array) {
			elm.dragCfg.cont = {
				x : parseInt(elm.dragCfg.containment[0])||0,
				y : parseInt(elm.dragCfg.containment[1])||0,
				w : parseInt(elm.dragCfg.containment[2])||0,
				h : parseInt(elm.dragCfg.containment[3])||0
			};
		}
		elm.dragCfg.cont.dx = elm.dragCfg.cont.x - elm.dragCfg.oC.x;
		elm.dragCfg.cont.dy = elm.dragCfg.cont.y - elm.dragCfg.oC.y;
	
//	var d2=new Date();
//	alert(d2.getTime()-d1.getTime());
	},

	hidehelper : function(dragged)
	{
		if (dragged.dragCfg.insideParent || dragged.dragCfg.containment == 'parent') {
			jQuery('body', document).append(jQuery.iDrag.helper.get(0));
		}
		jQuery.iDrag.helper.empty().hide().css('opacity', 1);
		if (window.ActiveXObject) {
			jQuery.iDrag.helper.css('filter', 'alpha(opacity=100)');
		}
	},

	dragstop : function(e)
	{

		jQuery(document)
			.unbind('mousemove', jQuery.iDrag.dragmove)
			.unbind('mouseup', jQuery.iDrag.dragstop);

		if (jQuery.iDrag.dragged == null) {
			return;
		}
		var dragged = jQuery.iDrag.dragged;

		jQuery.iDrag.dragged = null;

		if (dragged.dragCfg.init == false) {
			return false;
		}
		if (dragged.dragCfg.so == true) {
			jQuery(dragged).css('position', dragged.dragCfg.oP);
		}
		var dEs = dragged.style;

		if (dragged.si) {
			jQuery.iDrag.helper.css('cursor', 'move');
		}
		if(dragged.dragCfg.frameClass) {
			jQuery.iDrag.helper.removeClass(dragged.dragCfg.frameClass);
		}

		if (dragged.dragCfg.revert == false) {
			if (dragged.dragCfg.fx > 0) {
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally') {
					var x = new jQuery.fx(dragged,{duration:dragged.dragCfg.fx}, 'left');
					x.custom(dragged.dragCfg.oR.x,dragged.dragCfg.nRx);
				}
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically') {
					var y = new jQuery.fx(dragged,{duration:dragged.dragCfg.fx}, 'top');
					y.custom(dragged.dragCfg.oR.y,dragged.dragCfg.nRy);
				}
			} else {
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally')
					dragged.style.left = dragged.dragCfg.nRx + 'px';
				if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically')
					dragged.style.top = dragged.dragCfg.nRy + 'px';
			}
			jQuery.iDrag.hidehelper(dragged);
			if (dragged.dragCfg.ghosting == false) {
				jQuery(dragged).css('display', dragged.dragCfg.oD);
			}
		} else if (dragged.dragCfg.fx > 0) {
			dragged.dragCfg.prot = true;
			var dh = false;
			if(jQuery.iDrop && jQuery.iSort && dragged.dragCfg.so) {
				dh = jQuery.iUtil.getPosition(jQuery.iSort.helper.get(0));
			}
			jQuery.iDrag.helper.animate(
				{
					left : dh ? dh.x : dragged.dragCfg.oC.x,
					top : dh ? dh.y : dragged.dragCfg.oC.y
				},
				dragged.dragCfg.fx,
				function()
				{
					dragged.dragCfg.prot = false;
					if (dragged.dragCfg.ghosting == false) {
						dragged.style.display = dragged.dragCfg.oD;
					}
					jQuery.iDrag.hidehelper(dragged);
				}
			);
		} else {
			jQuery.iDrag.hidehelper(dragged);
			if (dragged.dragCfg.ghosting == false) {
				jQuery(dragged).css('display', dragged.dragCfg.oD);
			}
		}

		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.checkdrop(dragged);
		}
		if (jQuery.iSort && dragged.dragCfg.so) {
			jQuery.iSort.check(dragged);
		}
		if (dragged.dragCfg.onChange && (dragged.dragCfg.nRx != dragged.dragCfg.oR.x || dragged.dragCfg.nRy != dragged.dragCfg.oR.y)){
			dragged.dragCfg.onChange.apply(dragged, dragged.dragCfg.lastSi||[0,0,dragged.dragCfg.nRx,dragged.dragCfg.nRy]);
		}
		if (dragged.dragCfg.onStop)
			dragged.dragCfg.onStop.apply(dragged);
		return false;
	},

	snapToGrid : function(x, y, dx, dy)
	{
		if (dx != 0)
			dx = parseInt((dx + (this.dragCfg.gx * dx/Math.abs(dx))/2)/this.dragCfg.gx) * this.dragCfg.gx;
		if (dy != 0)
			dy = parseInt((dy + (this.dragCfg.gy * dy/Math.abs(dy))/2)/this.dragCfg.gy) * this.dragCfg.gy;
		return {
			dx : dx,
			dy : dy,
			x: 0,
			y: 0
		};
	},

	fitToContainer : function(x, y, dx, dy)
	{
		dx = Math.min(
				Math.max(dx,this.dragCfg.cont.dx),
				this.dragCfg.cont.w + this.dragCfg.cont.dx - this.dragCfg.oC.wb
			);
		dy = Math.min(
				Math.max(dy,this.dragCfg.cont.dy),
				this.dragCfg.cont.h + this.dragCfg.cont.dy - this.dragCfg.oC.hb
			);

		return {
			dx : dx,
			dy : dy,
			x: 0,
			y: 0
		}
	},

	dragmove : function(e)
	{
		if (jQuery.iDrag.dragged == null || jQuery.iDrag.dragged.dragCfg.prot == true) {
			return;
		}

		var dragged = jQuery.iDrag.dragged;

		dragged.dragCfg.currentPointer = jQuery.iUtil.getPointer(e);
		if (dragged.dragCfg.init == false) {
			distance = Math.sqrt(Math.pow(dragged.dragCfg.pointer.x - dragged.dragCfg.currentPointer.x, 2) + Math.pow(dragged.dragCfg.pointer.y - dragged.dragCfg.currentPointer.y, 2));
			if (distance < dragged.dragCfg.snapDistance){
				return;
			} else {
				jQuery.iDrag.dragstart(e);
			}
		}

		var dx = dragged.dragCfg.currentPointer.x - dragged.dragCfg.pointer.x;
		var dy = dragged.dragCfg.currentPointer.y - dragged.dragCfg.pointer.y;

		for (var i in dragged.dragCfg.onDragModifier) {
			var newCoords = dragged.dragCfg.onDragModifier[i].apply(dragged, [dragged.dragCfg.oR.x + dx, dragged.dragCfg.oR.y + dy, dx, dy]);
			if (newCoords && newCoords.constructor == Object) {
				dx = i != 'user' ? newCoords.dx : (newCoords.x - dragged.dragCfg.oR.x);
				dy = i != 'user' ? newCoords.dy : (newCoords.y - dragged.dragCfg.oR.y);
			}
		}

		dragged.dragCfg.nx = dragged.dragCfg.oC.x + dx - dragged.dragCfg.diffX;
		dragged.dragCfg.ny = dragged.dragCfg.oC.y + dy - dragged.dragCfg.diffY;

		if (dragged.dragCfg.si && (dragged.dragCfg.onSlide || dragged.dragCfg.onChange)) {
			jQuery.iSlider.onSlide(dragged, dragged.dragCfg.nx, dragged.dragCfg.ny);
		}

		if(dragged.dragCfg.onDrag)
			dragged.dragCfg.onDrag.apply(dragged, [dragged.dragCfg.oR.x + dx, dragged.dragCfg.oR.y + dy]);
			
		if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'horizontally') {
			dragged.dragCfg.nRx = dragged.dragCfg.oR.x + dx;
			jQuery.iDrag.helper.get(0).style.left = dragged.dragCfg.nx + 'px';
		}
		if (!dragged.dragCfg.axis || dragged.dragCfg.axis == 'vertically') {
			dragged.dragCfg.nRy = dragged.dragCfg.oR.y + dy;
			jQuery.iDrag.helper.get(0).style.top = dragged.dragCfg.ny + 'px';
		}
		
		if (jQuery.iDrop && jQuery.iDrop.count > 0 ){
			jQuery.iDrop.checkhover(dragged);
		}
		return false;
	},

	build : function(o)
	{
		if (!jQuery.iDrag.helper) {
			jQuery('body',document).append('<div id="dragHelper"></div>');
			jQuery.iDrag.helper = jQuery('#dragHelper');
			var el = jQuery.iDrag.helper.get(0);
			var els = el.style;
			els.position = 'absolute';
			els.display = 'none';
			els.cursor = 'move';
			els.listStyle = 'none';
			els.overflow = 'hidden';
			if (window.ActiveXObject) {
				el.unselectable = "on";
			} else {
				els.mozUserSelect = 'none';
				els.userSelect = 'none';
				els.KhtmlUserSelect = 'none';
			}
		}
		if (!o) {
			o = {};
		}
		return this.each(
			function()
			{
				if (this.isDraggable || !jQuery.iUtil)
					return;
				if (window.ActiveXObject) {
					this.onselectstart = function(){return false;};
					this.ondragstart = function(){return false;};
				}
				var el = this;
				var dhe = o.handle ? jQuery(this).find(o.handle) : jQuery(this);
				if(jQuery.browser.msie) {
					dhe.each(
						function()
						{
							this.unselectable = "on";
						}
					);
				} else {
					dhe.css('-moz-user-select', 'none');
					dhe.css('user-select', 'none');
					dhe.css('-khtml-user-select', 'none');
				}
				this.dragCfg = {
					dhe: dhe,
					revert : o.revert ? true : false,
					ghosting : o.ghosting ? true : false,
					so : o.so ? o.so : false,
					si : o.si ? o.si : false,
					insideParent : o.insideParent ? o.insideParent : false,
					zIndex : o.zIndex ? parseInt(o.zIndex)||0 : false,
					opacity : o.opacity ? parseFloat(o.opacity) : false,
					fx : parseInt(o.fx)||null,
					hpc : o.hpc ? o.hpc : false,
					onDragModifier : {},
					pointer : {},
					onStart : o.onStart && o.onStart.constructor == Function ? o.onStart : false,
					onStop : o.onStop && o.onStop.constructor == Function ? o.onStop : false,
					onChange : o.onChange && o.onChange.constructor == Function ? o.onChange : false,
					axis : /vertically|horizontally/.test(o.axis) ? o.axis : false,
					snapDistance : o.snapDistance ? parseInt(o.snapDistance)||0 : 0,
					cursorAt: o.cursorAt ? o.cursorAt : false,
					autoSize : o.autoSize ? true : false,
					frameClass : o.frameClass || false
					
				};
				if (o.onDragModifier && o.onDragModifier.constructor == Function)
					this.dragCfg.onDragModifier.user = o.onDragModifier;
				if (o.onDrag && o.onDrag.constructor == Function)
					this.dragCfg.onDrag = o.onDrag;
				if (o.containment && ((o.containment.constructor == String && (o.containment == 'parent' || o.containment == 'document')) || (o.containment.constructor == Array && o.containment.length == 4) )) {
					this.dragCfg.containment = o.containment;
				}
				if(o.fractions) {
					this.dragCfg.fractions = o.fractions;
				}
				if(o.grid){
					if(typeof o.grid == 'number'){
						this.dragCfg.gx = parseInt(o.grid)||1;
						this.dragCfg.gy = parseInt(o.grid)||1;
					} else if (o.grid.length == 2) {
						this.dragCfg.gx = parseInt(o.grid[0])||1;
						this.dragCfg.gy = parseInt(o.grid[1])||1;
					}
				}
				if (o.onSlide && o.onSlide.constructor == Function) {
					this.dragCfg.onSlide = o.onSlide;
				}

				this.isDraggable = true;
				dhe.each(
					function(){
						this.dragElem = el;
					}
				);
				dhe.bind('mousedown', jQuery.iDrag.draginit);
			}
		)
	}
};

/**
 * Destroy an existing draggable on a collection of elements
 * 
 * @name DraggableDestroy
 * @descr Destroy a draggable
 * @type jQuery
 * @cat Plugins/Interface
 * @example $('#drag2').DraggableDestroy();
 */

jQuery.fn.extend(
	{
		DraggableDestroy : jQuery.iDrag.destroy,
		Draggable : jQuery.iDrag.build
	}
);

/**
 * Interface Elements for jQuery
 * Droppables
 * 
 * http://interface.eyecon.ro
 * 
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt) 
 * and GPL (GPL-LICENSE.txt) licenses.
 *   
 *
 */

/**
 * With the Draggables plugin, Droppable allows you to create drop zones for draggable elements.
 *
 * @name Droppable
 * @cat Plugins/Interface
 * @param Hash options A hash of options
 * @option String accept The class name for draggables to get accepted by the droppable (mandatory)
 * @option String activeclass When an acceptable draggable is moved, the droppable gets this class
 * @option String hoverclass When an acceptable draggable is inside the droppable, the droppable gets
 *                           this class
 * @option String tolerance  Choose from 'pointer', 'intersect', or 'fit'. The pointer options means
 *                           that the pointer must be inside the droppable in order for the draggable
 *                           to be dropped. The intersect option means that the draggable must intersect
 *                           the droppable. The fit option means that the entire draggable must be
 *                           inside the droppable.
 * @option Function onDrop   When an acceptable draggable is dropped on a droppable, this callback is
 *                           called. It passes the draggable DOMElement as a parameter.
 * @option Function onHover  When an acceptable draggable is hovered over a droppable, this callback
 *                           is called. It passes the draggable DOMElement as a parameter.
 * @option Function onOut    When an acceptable draggable leaves a droppable, this callback is called.
 *                           It passes the draggable DOMElement as a parameter.
 * @example                  $('#dropzone1').Droppable(
 *                             {
 *                               accept : 'dropaccept', 
 *                               activeclass: 'dropzoneactive', 
 *                               hoverclass:	'dropzonehover',
 *                               ondrop:	function (drag) {
 *                                              alert(this); //the droppable
 *                                              alert(drag); //the draggable
 *                                        },
 *                               fit: true
 *                             }
 *                           )
 */

jQuery.iDrop = {
	fit : function (zonex, zoney, zonew, zoneh)
	{
		return 	zonex <= jQuery.iDrag.dragged.dragCfg.nx && 
				(zonex + zonew) >= (jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.w) &&
				zoney <= jQuery.iDrag.dragged.dragCfg.ny && 
				(zoney + zoneh) >= (jQuery.iDrag.dragged.dragCfg.ny + jQuery.iDrag.dragged.dragCfg.oC.h) ? true :false;
	},
	intersect : function (zonex, zoney, zonew, zoneh)
	{
		return 	! ( zonex > (jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.w)
				|| (zonex + zonew) < jQuery.iDrag.dragged.dragCfg.nx 
				|| zoney > (jQuery.iDrag.dragged.dragCfg.ny + jQuery.iDrag.dragged.dragCfg.oC.h) 
				|| (zoney + zoneh) < jQuery.iDrag.dragged.dragCfg.ny
				) ? true :false;
	},
	pointer : function (zonex, zoney, zonew, zoneh)
	{
		return	zonex < jQuery.iDrag.dragged.dragCfg.currentPointer.x
				&& (zonex + zonew) > jQuery.iDrag.dragged.dragCfg.currentPointer.x 
				&& zoney < jQuery.iDrag.dragged.dragCfg.currentPointer.y 
				&& (zoney + zoneh) > jQuery.iDrag.dragged.dragCfg.currentPointer.y
				? true :false;
	},
	overzone : false,
	highlighted : {},
	count : 0,
	zones : {},
	
	highlight : function (elm)
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		var i;
		jQuery.iDrop.highlighted = {};
		var oneIsSortable = false;
		for (i in jQuery.iDrop.zones) {
			if (jQuery.iDrop.zones[i] != null) {
				var iEL = jQuery.iDrop.zones[i].get(0);
				if (jQuery(jQuery.iDrag.dragged).is('.' + iEL.dropCfg.a)) {
					if (iEL.dropCfg.m == false) {
						iEL.dropCfg.p = jQuery.extend(
							jQuery.iUtil.getPositionLite(iEL),
							jQuery.iUtil.getSizeLite(iEL)
						);//jQuery.iUtil.getPos(iEL);
						iEL.dropCfg.m = true;
					}
					if (iEL.dropCfg.ac) {
						jQuery.iDrop.zones[i].addClass(iEL.dropCfg.ac);
					}
					jQuery.iDrop.highlighted[i] = jQuery.iDrop.zones[i];
					//if (jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
					if (jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
						iEL.dropCfg.el = jQuery('.' + iEL.dropCfg.a, iEL);
						elm.style.display = 'none';
						jQuery.iSort.measure(iEL);
						iEL.dropCfg.os = jQuery.iSort.serialize(jQuery.attr(iEL, 'id')).hash;
						elm.style.display = elm.dragCfg.oD;
						oneIsSortable = true;
					}
					if (iEL.dropCfg.onActivate) {
						iEL.dropCfg.onActivate.apply(jQuery.iDrop.zones[i].get(0), [jQuery.iDrag.dragged]);
					}
				}
			}
		}
		//if (jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
		if (oneIsSortable) {
			jQuery.iSort.start();
		}
	},
	/**
	 * remeasure the droppable
	 * 
	 * useful when the positions/dimensions for droppables 
	 * are changed while dragging a element
	 * 
	 * this works for sortables too but with a greate processor 
	 * penality because remeasures each sort items too
	 */
	remeasure : function()
	{
		jQuery.iDrop.highlighted = {};
		for (i in jQuery.iDrop.zones) {
			if (jQuery.iDrop.zones[i] != null) {
				var iEL = jQuery.iDrop.zones[i].get(0);
				if (jQuery(jQuery.iDrag.dragged).is('.' + iEL.dropCfg.a)) {
					iEL.dropCfg.p = jQuery.extend(
						jQuery.iUtil.getPositionLite(iEL),
						jQuery.iUtil.getSizeLite(iEL)
					);
					if (iEL.dropCfg.ac) {
						jQuery.iDrop.zones[i].addClass(iEL.dropCfg.ac);
					}
					jQuery.iDrop.highlighted[i] = jQuery.iDrop.zones[i];
					
					if (jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
						iEL.dropCfg.el = jQuery('.' + iEL.dropCfg.a, iEL);
						elm.style.display = 'none';
						jQuery.iSort.measure(iEL);
						elm.style.display = elm.dragCfg.oD;
					}
				}
			}
		}
	},
	
	checkhover : function (e)
	{
		if (jQuery.iDrag.dragged == null) {
			return;
		}
		jQuery.iDrop.overzone = false;
		var i;
		var applyOnHover = false;
		var hlt = 0;
		for (i in jQuery.iDrop.highlighted)
		{
			var iEL = jQuery.iDrop.highlighted[i].get(0);
			if ( 
					jQuery.iDrop.overzone == false
					 && 
					jQuery.iDrop[iEL.dropCfg.t](
					 	iEL.dropCfg.p.x, 
						iEL.dropCfg.p.y, 
						iEL.dropCfg.p.wb, 
						iEL.dropCfg.p.hb
					)
					 
			) {
				if (iEL.dropCfg.hc && iEL.dropCfg.h == false) {
					jQuery.iDrop.highlighted[i].addClass(iEL.dropCfg.hc);
				}
				//chec if onHover function has to be called
				if (iEL.dropCfg.h == false &&iEL.dropCfg.onHover) {
					applyOnHover = true;
				}
				iEL.dropCfg.h = true;
				jQuery.iDrop.overzone = iEL;
				//if(jQuery.iSort && jQuery.iDrag.dragged.dragCfg.so) {
				if(jQuery.iSort && iEL.dropCfg.s && jQuery.iDrag.dragged.dragCfg.so) {
					jQuery.iSort.helper.get(0).className = iEL.dropCfg.shc;
					jQuery.iSort.checkhover(iEL);
				}
				hlt ++;
			} else if(iEL.dropCfg.h == true) {
				//onOut function
				if (iEL.dropCfg.onOut) {
					iEL.dropCfg.onOut.apply(iEL, [e, jQuery.iDrag.helper.get(0).firstChild, iEL.dropCfg.fx]);
				}
				if (iEL.dropCfg.hc) {
					jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.hc);
				}
				iEL.dropCfg.h = false;
			}
		}
		if (jQuery.iSort && !jQuery.iDrop.overzone && jQuery.iDrag.dragged.so) {
			jQuery.iSort.helper.get(0).style.display = 'none';
			//jQuery('body').append(jQuery.iSort.helper.get(0));
		}
		//call onhover
		if(applyOnHover) {
			jQuery.iDrop.overzone.dropCfg.onHover.apply(jQuery.iDrop.overzone, [e, jQuery.iDrag.helper.get(0).firstChild]);
		}
	},
	checkdrop : function (e)
	{
		var i;
		for (i in jQuery.iDrop.highlighted) {
			var iEL = jQuery.iDrop.highlighted[i].get(0);
			if (iEL.dropCfg.ac) {
				jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.ac);
			}
			if (iEL.dropCfg.hc) {
				jQuery.iDrop.highlighted[i].removeClass(iEL.dropCfg.hc);
			}
			if(iEL.dropCfg.s) {
				jQuery.iSort.changed[jQuery.iSort.changed.length] = i;
			}
			if (iEL.dropCfg.onDrop && iEL.dropCfg.h == true) {
				iEL.dropCfg.h = false;
				iEL.dropCfg.onDrop.apply(iEL, [e, iEL.dropCfg.fx]);
			}
			iEL.dropCfg.m = false;
			iEL.dropCfg.h  = false;
		}
		jQuery.iDrop.highlighted = {};
	},
	destroy : function()
	{
		return this.each(
			function()
			{
				if (this.isDroppable) {
					if (this.dropCfg.s) {
						id = jQuery.attr(this,'id');
						jQuery.iSort.collected[id] = null;
						jQuery('.' + this.dropCfg.a, this).DraggableDestroy();
					}
					jQuery.iDrop.zones['d' + this.idsa] = null;
					this.isDroppable = false;
					this.f = null;
				}
			}
		);
	},
	build : function (o)
	{
		return this.each(
			function()
			{
				if (this.isDroppable == true || !o.accept || !jQuery.iUtil || !jQuery.iDrag){
					return;
				}
				this.dropCfg = {
					a : o.accept,
					ac: o.activeclass||false, 
					hc:	o.hoverclass||false,
					shc: o.helperclass||false,
					onDrop:	o.ondrop||o.onDrop||false,
					onHover: o.onHover||o.onhover||false,
					onOut: o.onOut||o.onout||false,
					onActivate: o.onActivate||false,
					t: o.tolerance && ( o.tolerance == 'fit' || o.tolerance == 'intersect') ? o.tolerance : 'pointer',
					fx: o.fx ? o.fx : false,
					m: false,
					h: false
				};
				if (o.sortable == true && jQuery.iSort) {
					id = jQuery.attr(this,'id');
					jQuery.iSort.collected[id] = this.dropCfg.a;
					this.dropCfg.s = true;
					if(o.onChange) {
						this.dropCfg.onChange = o.onChange;
						this.dropCfg.os = jQuery.iSort.serialize(id).hash;
					}
				}
				this.isDroppable = true;
				this.idsa = parseInt(Math.random() * 10000);
				jQuery.iDrop.zones['d' + this.idsa] = jQuery(this);
				jQuery.iDrop.count ++;
			}
		);
	}
};

/**
 * Destroy an existing droppable on a collection of elements
 * 
 * @name DroppableDestroy
 * @descr Destroy a droppable
 * @type jQuery
 * @cat Plugins/Interface
 * @example $('#drag2').DroppableDestroy();
 */

jQuery.fn.extend(
	{
		DroppableDestroy : jQuery.iDrop.destroy,
		Droppable : jQuery.iDrop.build
	}
);

 
/**
 * Recalculate all Droppables
 *
 * @name $.recallDroppables
 * @type jQuery
 * @cat Plugins/Interface
 * @example $.recallDroppable();
 */

jQuery.recallDroppables = jQuery.iDrop.remeasure;


/**
 * Interface Elements for jQuery
 * Slider
 * 
 * http://interface.eyecon.ro
 * 
 * Copyright (c) 2006 Stefan Petre
 * Dual licensed under the MIT (MIT-LICENSE.txt) 
 * and GPL (GPL-LICENSE.txt) licenses.
 *   
 *
 */

jQuery.iSlider = {
	tabindex : 1,
	set : function (values)
	{
		var values = values;
		return this.each(
			function()
			{
				this.slideCfg.sliders.each(
					function (key) 
					{ 
						this.style.left = '0px';
						this.style.top = '0px';
						jQuery.iSlider.dragmoveBy(this,values[key]);
					}
				);
			}
		);
	},
	
	get : function()
	{
		var values = [];
		this.each(
			function(slider)
			{
				if (this.isSlider) {
					values[slider] = [];
					var elm = this;
					var sizes = jQuery.iUtil.getSize(this);
					this.slideCfg.sliders.each(
						function (key) 
						{
							var x = this.offsetLeft;
							var y = this.offsetTop;
							xproc = parseInt(x * 100 / (sizes.w - this.offsetWidth));
							yproc = parseInt(y * 100 / (sizes.h - this.offsetHeight));
							values[slider][key] = [xproc||0, yproc||0, x||0, y||0];
						}
					);
				}
			}
		);
		return values;
	},
	
	modifyContainer : function (elm)
	{
		elm.dragCfg.containerMaxx = elm.dragCfg.cont.w - elm.dragCfg.oC.wb;
		elm.dragCfg.containerMaxy = elm.dragCfg.cont.h - elm.dragCfg.oC.hb;
		if (elm.SliderContainer.slideCfg.restricted ) {
			next = elm.SliderContainer.slideCfg.sliders.get(elm.SliderIteration+1);
			if (next) {
				elm.dragCfg.cont.w = (parseInt(jQuery(next).css('left'))||0) + elm.dragCfg.oC.wb;
				elm.dragCfg.cont.h = (parseInt(jQuery(next).css('top'))||0) + elm.dragCfg.oC.hb;
			}
			prev = elm.SliderContainer.slideCfg.sliders.get(elm.SliderIteration-1);
			if (prev) {
				var prevLeft = parseInt(jQuery(prev).css('left'))||0;
				var prevTop = parseInt(jQuery(prev).css('left'))||0;
				elm.dragCfg.cont.x += prevLeft;
				elm.dragCfg.cont.y += prevTop;
				elm.dragCfg.cont.w -= prevLeft;
				elm.dragCfg.cont.h -= prevTop;
			}
		}
		elm.dragCfg.maxx = elm.dragCfg.cont.w - elm.dragCfg.oC.wb;
		elm.dragCfg.maxy = elm.dragCfg.cont.h - elm.dragCfg.oC.hb;
		if(elm.dragCfg.fractions) {
			elm.dragCfg.gx = ((elm.dragCfg.cont.w - elm.dragCfg.oC.wb)/elm.dragCfg.fractions) || 1;
			elm.dragCfg.gy = ((elm.dragCfg.cont.h - elm.dragCfg.oC.hb)/elm.dragCfg.fractions) || 1;
			elm.dragCfg.fracW = elm.dragCfg.maxx / elm.dragCfg.fractions;
			elm.dragCfg.fracH = elm.dragCfg.maxy / elm.dragCfg.fractions;
		}
		
		elm.dragCfg.cont.dx = elm.dragCfg.cont.x - elm.dragCfg.oR.x;
		elm.dragCfg.cont.dy = elm.dragCfg.cont.y - elm.dragCfg.oR.y;
		
		jQuery.iDrag.helper.css('cursor', 'default');
	},
	
	onSlide : function(elm, x, y)
	{
		if (elm.dragCfg.fractions) {
				xfrac = parseInt(x/elm.dragCfg.fracW);
				xproc = xfrac * 100 / elm.dragCfg.fractions;
				yfrac = parseInt(y/elm.dragCfg.fracH);
				yproc = yfrac * 100 / elm.dragCfg.fractions;
		} else {
			xproc = parseInt(x * 100 / elm.dragCfg.containerMaxx);
			yproc = parseInt(y * 100 / elm.dragCfg.containerMaxy);
		}
		elm.dragCfg.lastSi = [xproc||0, yproc||0, x||0, y||0];
		if (elm.dragCfg.onSlide)
			elm.dragCfg.onSlide.apply(elm, elm.dragCfg.lastSi);
	},
	
	dragmoveByKey : function (event)
	{
		pressedKey = event.charCode || event.keyCode || -1;
		
		switch (pressedKey)
		{
			//end
			case 35:
				jQuery.iSlider.dragmoveBy(this.dragElem, [2000, 2000] );
			break;
			//home
			case 36:
				jQuery.iSlider.dragmoveBy(this.dragElem, [-2000, -2000] );
			break;
			//left
			case 37:
				jQuery.iSlider.dragmoveBy(this.dragElem, [-this.dragElem.dragCfg.gx||-1, 0] );
			break;
			//up
			case 38:
				jQuery.iSlider.dragmoveBy(this.dragElem, [0, -this.dragElem.dragCfg.gy||-1] );
			break;
			//right
			case 39:
				jQuery.iSlider.dragmoveBy(this.dragElem, [this.dragElem.dragCfg.gx||1, 0] );
			break;
			//down;
			case 40:
				jQuery.iDrag.dragmoveBy(this.dragElem, [0, this.dragElem.dragCfg.gy||1] );
			break;
		}
	},
	
	dragmoveBy : function (elm, position) 
	{
		if (!elm.dragCfg) {
			return;
		}
		
		elm.dragCfg.oC = jQuery.extend(
			jQuery.iUtil.getPosition(elm),
			jQuery.iUtil.getSize(elm)
		);
		
		elm.dragCfg.oR = {
			x : parseInt(jQuery.css(elm, 'left'))||0,
			y : parseInt(jQuery.css(elm, 'top'))||0
		};
		
		elm.dragCfg.oP = jQuery.css(elm, 'position');
		if (elm.dragCfg.oP != 'relative' && elm.dragCfg.oP != 'absolute') {
			elm.style.position = 'relative';
		}
		
		jQuery.iDrag.getContainment(elm);
		jQuery.iSlider.modifyContainer(elm);		
		
		dx = parseInt(position[0]) || 0;
		dy = parseInt(position[1]) || 0;
		
		nx = elm.dragCfg.oR.x + dx;
		ny = elm.dragCfg.oR.y + dy;
		if(elm.dragCfg.fractions) {
			newCoords = jQuery.iDrag.snapToGrid.apply(elm, [nx, ny, dx, dy]);
			if (newCoords.constructor == Object) {
				dx = newCoords.dx;
				dy = newCoords.dy;
			}
			nx = elm.dragCfg.oR.x + dx;
			ny = elm.dragCfg.oR.y + dy;
		}
		
		newCoords = jQuery.iDrag.fitToContainer.apply(elm, [nx, ny, dx, dy]);
		if (newCoords && newCoords.constructor == Object) {
			dx = newCoords.dx;
			dy = newCoords.dy;
		}
		
		nx = elm.dragCfg.oR.x + dx;
		ny = elm.dragCfg.oR.y + dy;
		
		if (elm.dragCfg.si && (elm.dragCfg.onSlide || elm.dragCfg.onChange)) {
			jQuery.iSlider.onSlide(elm, nx, ny);
		}
		nx = !elm.dragCfg.axis || elm.dragCfg.axis == 'horizontally' ? nx : elm.dragCfg.oR.x||0;
		ny = !elm.dragCfg.axis || elm.dragCfg.axis == 'vertically' ? ny : elm.dragCfg.oR.y||0;
		elm.style.left = nx + 'px';
		elm.style.top = ny + 'px';
	},
	
	build : function(o) {
		return this.each(
			function()
			{
				if (this.isSlider == true || !o.accept || !jQuery.iUtil || !jQuery.iDrag || !jQuery.iDrop){
					return;
				}
				toDrag = jQuery(o.accept, this);
				if (toDrag.size() == 0) {
					return;
				}
				var params = {
					containment: 'parent',
					si : true,
					onSlide : o.onSlide && o.onSlide.constructor == Function ? o.onSlide : null,
					onChange : o.onChange && o.onChange.constructor == Function ? o.onChange : null,
					handle: this,
					opacity: o.opacity||false
				};
				if (o.fractions && parseInt(o.fractions)) {
					params.fractions = parseInt(o.fractions)||1;
					params.fractions = params.fractions > 0 ? params.fractions : 1;
				}
				if (toDrag.size() == 1)
					toDrag.Draggable(params);
				else {
					jQuery(toDrag.get(0)).Draggable(params);
					params.handle = null;
					toDrag.Draggable(params);
				}
				toDrag.keydown(jQuery.iSlider.dragmoveByKey);
				toDrag.attr('tabindex',jQuery.iSlider.tabindex++);	
				
				this.isSlider = true;
				this.slideCfg = {};
				this.slideCfg.onslide = params.onslide;
				this.slideCfg.fractions = params.fractions;
				this.slideCfg.sliders = toDrag;
				this.slideCfg.restricted = o.restricted ? true : false;
				sliderEl = this;
				sliderEl.slideCfg.sliders.each(
					function(nr)
					{
						this.SliderIteration = nr;
						this.SliderContainer = sliderEl;
					}
				);
				if (o.values && o.values.constructor == Array) {
					for (i = o.values.length -1; i>=0;i--) {
						if (o.values[i].constructor == Array && o.values[i].length == 2) {
							el = this.slideCfg.sliders.get(i);
							if (el.tagName) {
								jQuery.iSlider.dragmoveBy(el, o.values[i]);
							}
						}
					}
				}
			}
		);
	}
};
jQuery.fn.extend(
	{
		/**
		 * Create a slider width options
		 * 
		 * @name Slider
		 * @description Create a slider width options
		 * @param Hash hash A hash of parameters. All parameters are optional.
		 * @option Mixed accepts string to select slider indicators or DOMElement slider indicator
		 * @option Integer factions (optional) number of sgments to divide and snap slider
		 * @option Function onSlide (optional) A function to be executed whenever slider indicator it is moved
		 * @option Function onChanged (optional) A function to be executed whenever slider indicator was moved
		 * @option Array values (optional) Initial values for slider indicators
		 * @option Boolean restricted (optional) if true the slider indicator can not be moved beyond adjacent indicators
		 * @type jQuery
		 * @cat Plugins/Interface
		 * @author Stefan Petre
		 */
		Slider : jQuery.iSlider.build,
		/**
		 * Set value/position for slider indicators
		 * 
		 * @name SliderSetValues
		 * @description Set value/position for slider indicators
		 * @param Array values array width values for each indicator
		 * @type jQuery
		 * @cat Plugins/Interface
		 * @author Stefan Petre
		 */
		SliderSetValues : jQuery.iSlider.set,
		/**
		 * Get value/position for slider indicators
		 * 
		 * @name SliderSetValues
		 * @description Get value/position for slider indicators
		 * @type jQuery
		 * @cat Plugins/Interface
		 * @author Stefan Petre
		 */
		SliderGetValues : jQuery.iSlider.get
	}
);ï»¿//<script>
//Map.js

/** 
	ReprÃ¤sentiert eine geografische Ausdehnung
	@constructor
	@param {double} x1
	@param {double} y1
	@param {double} x2
	@param {double} y2
*/
function ExtentObj(x1, y1, x2, y2)
{
	/** xMin 
		@type double */
	this.x1=x1;
	/** yMin 
		@type double */
	this.y1=y1; 
	/** xMax 
		@type double */
	this.x2=x2;
	/** yMax 
		@type double */
	this.y2=y2;
	
	/** Breite 
		@returns {double} */
	this.w=function(){return this.x2-this.x1;}
	
	/** HÃ¶he 
		@returns {double} */
	this.h=function(){return this.y2-this.y1;}
}

/**
	@class
	Global verfÃ¼gbares Kartenobjekt Singleton<br><br>
	Alle Ereignisse werden Ã¼ber das globale Objekt glbEventObject ausgelÃ¶st.<br>
	@static
*/
glbMap=new function() // Singleton
{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// events (nur fÃ¼r Doku-Zwecke)
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	/**
		Wird ausgelÃ¶st, nachdem das client-seitige Map-Control vollstÃ¤ndig initialisiert wurde.<br>
		Alle Befehle zur Konfiguration des Startkartenbildes sind gesetzt. Die abschlieÃŸende Anforderung der Startkarte
		steht allerdings noch aus, so dass weitere Anpassungen an dieser Stelle noch mÃ¶glich sind.
		@event
		@param {object} eventArgs Referenz auf glbMap
	*/
	this.onMapControlInitialized = function() {};
	
	/**
		Wird ausgelÃ¶st, nachdem sich der angezeigte Kartenausschnitt geÃ¤ndert hat.<br>
		(Hinweis: Zugriff auf den neuen Ausschnitt erfolgt mit glbMap.extent)
		@event
		@param {object} eventArgs Referenz auf glbMap
	*/
	this.onMapExtentChanged = function() {};
	
	/**
		Wird ausgelÃ¶st, nachdem das Kartenbild der "einfachen" Karte aktualisiert wurde.
		@event
		@param {object} eventArgs Referenz auf glbMap
	*/
	this.onMapImageLoaded = function() {};
	
	/**
		Wird ausgelÃ¶st, wenn die Kartenparameter nach Sitzungsablauf auf dem Server aus den
		Wiederherstellungsinformationen wiederhergestellt wurden. Dieses Ereignis kann benutzt
		werden, um weitergehende Wiederherstellungsaktionen auszufÃ¼hren, etwa das Laden von
		UserLayern.
		@event
		@param {object} eventArgs Referenz auf glbMap
	*/
	this.onMapSessionReInitialized = function() {};
	
	/**
		Wird ausgelÃ¶st, wenn im Modus MOUSACTION oder mit aktivem Werkzeug "Map.SimpleClick"
		in die Karte geklickt wird.
		@event
		@param {object} eventArgs Info-Objekt
		@config {int} x client-seitiger x-Wert des Klickpunkts in der Karte
		@config {int} y client-seitiger y-Wert des Klickpunkts in der Karte
		@config {double} gx geografischer x-Wert des Klickpunkts in der Karte
		@config {double} gy geografischer y-Wert des Klickpunkts in der Karte
		@config {int} pageX client-seitiger x-Wert des Klickpunkts in der Seite
		@config {int} pageY client-seitiger y-Wert des Klickpunkts in der Seite
		@config {int} currentMapScale aktueller KartenmaÃŸstab
		@config {double} geo2px aktuelles VerhÃ¤ltnis von einer geografischen Einheit zu Pixel (z.B. Meter pro Pixel)
	*/
	this.onMapSimpleClick = function() {};
	
	/**
		@event
		@deprecated Der Name wurde in <a href="#glbMap.onMapSimpleClick">onMapSimpleClick</a> geÃ¤ndert.
	*/
	this.onMapDefaultButtonClicked = function() {};
		
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// public properties
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	/** (readonly) derzeitige KartengrÃ¶ÃŸe als Objekt mit {width, height} 
	    @type object */
	this.mapSize={width:null, height:null};
		
	/** (readonly) Aktuelle geografische Ausdehnung der Karte
		@type ExtentObj */
	this.extent=null; // Anm: daneben gibt es noch den extent der "Klassischen Karte" - der wird fÃ¼r die eine oder andere Berechnung benÃ¶tigt
	
	/** (readonly) Aktueller KartenmaÃŸstab
		@type bool */
	this.currentMapScale=null;
	
	/** (readonly) Kartenanforderung lÃ¤uft gerade und das Control ist blockiert.
		@type bool */
	this.locked=true;
	
	/** (readonly) Manager der Tile Ebenen im Control. (Steht nur zur VerfÃ¼gung, wenn
		dieses Feature korrekt initialisiert wurde.)
		@type TileLayerManager*/
	this.tileMap=null;
	
	/** (readonly) Manager fÃ¼r die Ãœberlagerung der Karte mit eigenen (aktiven) Inhalten
		@type MapOverlayManager*/
	this.overlays=null;
	
	/** (readonly) Shape-Objekt, vorgesehen fÃ¼r das Redlining im Map-Control.
		@type ShapeObject*/
	this.shape=null;
	
	/**
		Aktueller Feature-Type, der fÃ¼r die Erstellung von Geometrien mit dem Redlinewerkzeug benutzt wird.<br>
		MÃ¶gliche Werte: "POINT", "LINE" (Standard), "POLY", "CIRCLE"
		@type {string}
	*/
	this.activeShapeFeatureType="LINE";
	
	/** (readonly) Shape-Objekt, vorgesehen fÃ¼r Markierungs- und Kennzeichnungszwecke im Map-Control.
		@type ShapeObject*/
	this.markerShape=null; // mal sehn, ob das Ã¶ffentlich wird ...
	
	/** (readonly) Ãœbersichtskarte ist gerade geÃ¶ffnet.
		@type bool
	*/
	this.overViewMapIsOpen = false;
	
	/** (readonly) Die "einfache" Karte ist gerade aktiv.
		@type bool
	*/
	this.simpleMapActive=true;
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// private properties
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	this.languageObjectName = "glbMapMultiLang";
	
	this.basePath="";
	this.pixelGifSrc=null;
	
	this.scriptBlockId=1;
	this.head=null;
	
	this.currentTool=null;
	
	this.overviewMap = null;
	
	this.showMapTipWithDot=true;
	this.initialized=false;
	this.mapOffset={left:0, top:0};
	this.els={
				glbMapDiv:new Array(),
				glbMapImg:new Array(),
				glbZoom:new Array()
			 }; // hier kommen alle HTML Element Referenzen rein
				// das glb... ist noch eine Altlast, wollte ich nicht alles Umbenennen
	
	this.lastFixedExtent=null; // der letzte feststehende Extent, also z.B. bevor man die Karte rumschiebt oder dynamisch zoomt, etc.
	this.extentHistory=new Array();
	this.extentHistoryPointer=0;
	this.minScale=0;
	this.sessionReInitInfo=""; // base64-kodierter Status der Iwan-Session
	
	this.finalAction=null;
	this.lastFinalAction=null;
	this.mouseActionsInProgress=false;
	this.shapeEditConstraint=null;
	this.delayedSimpleClick=null;
	this.delayedMouseWheelActionFinished=null;
	this.delayedContextMenuActivation=null;
	this.mouseWheelSum=0;
	
	this.sessionPingTimer=null;
	this.mouseStart={x:null, y:null};
	this.lastMouseCoord={x:null, y:null};
	this.lastMouseButton=-1;
	this.lastZoomRectSize={CX:0, CY:0, DX:0, DY:0};
	this.switchMaps=false;
	this.commandQueue=new Array();
	
	this.zoomResizeStartLevel=null; // betrifft tileMap

	this.zoomActive=false;
	this.zoomType=null;
	this.rubberBandColorSel="fuchsia";
	this.rubberBandColorZoom="red";
	this.rubberBandWidth=2;
	this.mapOverviewBox=null;
	this.lastCursor="default";
	
	this.mapToolMode="MOUSEACTION";
	this.disableMouseActionMode=false;
	
	this.curMapIdx=0;
	this.prvMapIdx=1;
	
	this.mapTipIsShown=false;
	this.mapTipShadow=null;
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// functions
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	//this.calcMapOffset=_calcMapOffset;
	this.calcMapOffset=_calcMapOffsetWithJQuery;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	this.init = function()
	{
		var config=this.config;

		this.sessionSpecifier=config.sessionSpecifier || "";
		this.mapSize={width:config.width, height:config.height};
		this.basePath=config.basePath;
		this.head = document.getElementsByTagName("head")[0];
		
		this.disableMouseActionMode=config.disableMouseActionMode;
		if(this.disableMouseActionMode)
			this.mapToolMode="EXPLICIT";

		this.initGuiElements()

		// DOM Elemente cachen
		this.els.glbEventDiv=this.getEl("theMapDivEvent");
			
		this.els.glbMapDiv[1]=this.getEl("theMapDiv1");
		this.els.glbMapDiv[0]=this.getEl("theMapDiv0");
		this.els.glbMapImg[1]=this.getEl("theMap1");
		this.els.glbMapImg[0]=this.getEl("theMap0");
	
		this.els.zoomRect=this.getEl("zoomRect");
	
		this.els.glbVMLDiv=this.getEl("theVMLDiv");
		this.els.glbVMLDivShape=this.getEl("theVMLDivShape");
		this.els.glbVMLDivMarker=this.getEl("theVMLDivMarker");
		this.els.glbOverlayDiv=this.getEl("theOverlayDiv");
		
		this.pixelGifSrc=this.els.glbMapImg[0].src;
	/*	
		if(glbBrowserCaps.IE5gte)
			this.els.imagePrepFrame=document.frames["MapImagePrepFrame"];
		else
		{
			var prepFrame=document.createElement("IFRAME");
			prepFrame.style.position="absolute";
			prepFrame.style.visibility="hidden";
			(document.body || document.documentElement).appendChild(prepFrame);
			//this.els.imagePrepFrame=document.getElementById("MapImagePrepFrame").contentDocument;
			this.els.imagePrepFrame=prepFrame.contentDocument;
		}
	*/	
		// den Maptip etwas stylen:
		Ext.get("infoDivExtBox").boxWrap();
				
		// Events zuweisen
		// fÃ¼r Events wird ExtJs genommen
		Ext.EventManager.addListener(this.els.glbEventDiv, "dblclick", this.onMapMouseDblClick, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousemove", this.onMapMouseMoveSimple, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousedown", this.onMapMouseDown, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousewheel", this.onMapMouseWheelAction, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mouseout", this.onMapMouseOut, this, true);
		// weitere Events werden erst beim onMouseDown registriert
			
		this.switchMaps=true; // kleiner Hack, damit die Eventhandler fÃ¼r onload/onerror belegt werden
				
		// ShapeObject
		var drawMode=glbBrowserCaps && glbBrowserCaps.VmlAvailable ? "VML" : "JSGRAPH";
		this.shape=new ShapeObj(document.getElementById("theVMLDivShape"), null, drawMode); // braucht: sWidth, sHeight, bound;
		this.markerShape=new ShapeObj(Ext.get("theVMLDivMarker").dom, null, drawMode); // braucht: sWidth, sHeight, bound; 
		this.updateShapeGlobalVars();
						
		//FuÃŸpunkt der Karte initial ermitteln (wird dann bei jedem MouseDown aufgefrischt)
		window.setTimeout("glbMap.calcMapOffset()", 500);
				
		//Schauen, ob eine bestimmte Aktion bzw. Aktivierung eines Werkzeugs per Klick auf einen Button ausgelÃ¶st wird.
		glbEventObject.attachEvent("onButtonClicked", this.onButtonClicked, null, this);
		
		// Events zum Maptip an/ausschalten (nur noch aus KompatibilitÃ¤t drinne)
		glbEventObject.attachEvent("requestMapTipShowCommand", this.showInfoDiv, null, this);
		glbEventObject.attachEvent("requestMapTipHideCommand", this.hideInfoDiv, null, this);
		
		window.setInterval(function() {glbMap.sessionPing()}, config.sessionPingInterval*1000);
		
		this.overlays = new MapOverlayManager({overlayContainerDiv:"theOverlayDiv", mapControl:this});
		
		if(config.tileSettings)
			this.initTileMap(config);
					
		if(config.overviewMapSettings && config.overviewMapSettings.isConfigured)
			this.initOverviewMap(config.overviewMapSettings);
		
		this.initialized=true;
		
		this.addCommand("INIT"); //wird serverseitig auch schon gemacht, aber schadet hier nix und man weiÃŸ ja nie ...
		this.setMapSize(this.mapSize.width, this.mapSize.height);
		this.zoomToDefaultExtent();
		
		if(glbBrowserCaps.IE6lte && config.IE6ChromaKey)
		{
			if(config.IE6ChromaKey=="PNG8")
				this.setImageType("Png8");
			else
			{
				this.els.glbMapImg[1].style.filter="chroma(color="+config.IE6ChromaKey+")";
				this.els.glbMapImg[0].style.filter="chroma(color="+config.IE6ChromaKey+")";
			}
		}
		
		glbEventObject.fireEvent("onMapControlInitialized", this);
		
		this.refreshMap();
		
		if(false) // fÃ¼r schnelle Tests
		{
			glbMap.shape.currentSelectedShapeMode="POLY";
			glbMap.shape.event.attachEvent("onBeforeAddPart", function(eee){
				confirm("weiter") ?  "" : eee.cancel=true;
				glbMap.cleanUpMouseAction();
			})
		}
	}
	
	this.initTileMap = function(config)
	{
		// noch ein paar Parameter ergÃ¤nzen, die erst clientseitig bekannt sind 
		var tConfig=config.tileSettings;
		tConfig.mapControl=this;
		tConfig.tileContainerDiv=document.getElementById("theTileContainerDiv");
		tConfig.viewPortSize={width:this.mapSize.width, height:this.mapSize.height};
		tConfig.pixelWhiteImage=this.pixelGifSrc;
		
		this.tileMap=new TileLayerManager(tConfig);
	}
	
	this.initOverviewMap = function(config)
	{
		this.overviewMap = new OverviewMap(config);
		
		if(config.startOpened)
			this.openOverviewMap(true);
		else
		{
			// Hack, fÃ¼r den IE7 - das scheint erst das Rendering des "Ã–ffnen"-Icons richtig zu machen.
			window.setTimeout(function()
			{
				Ext.get("ovmCloseIcon").applyStyles("visibility:visible;");
				Ext.get("overviewMap").applyStyles("overflow:hidden;");
				var a=Ext.get("ovmCloseIcon").dom.offsetTop;
			}, 10);
		}
	}
	
	this.initGuiElements = function()
	{
		var _this=this;
		
		this.getEl("LoadingMapDiv").style.marginLeft="-"+(this.getEl("LoadingMapDiv").offsetWidth/2)+"px";
		
		$('#zoomSlider_rail').Slider(
										{
											accept : '#zoomSlider_handle',
											fractions : 0,
											onSlide : function(cordx, cordy, x , y)
											{
												_this.hideInfoDiv();
												//TODO: irgendwas einfallen lassen
												//this.overlays.hide();
												//if(_this.shape)
												//	_this.shape.hide();
												//if(this.markerShape)
												//	_this.markerShape.hide();
												_this.onNaviSliderMove(cordy);
											},
											onChange : function(cordx, cordy, x , y)
											{
												_this.onNaviSliderRelease(cordy);
											},
											values: [[0,50]]
										}
									)
		$('#simpleNavi')
			.css({top:"15px", right:"20px", /*left:(this.mapSize.width-85)+"px",*/ opacity:0.5})
			.mouseover(function() { window.clearTimeout(this.getAttribute('mouseOutTimeout')); if($(this).css("opacity")!="1") $(this).fadeTo('fast', 1); } )
			.mouseout(function()  { this.setAttribute('mouseOutTimeout', window.setTimeout("$('#simpleNavi').fadeTo('fast', 0.5);", 200)); } );
		
		$('#toolbar')
			.css({/*top:"15px", right:"20px", left:(this.mapSize.width-85)+"px",*/ opacity:0.65})
			.mouseover(function() { window.clearTimeout(this.getAttribute('mouseOutTimeout')); if($(this).css("opacity")!="1") $(this).fadeTo('fast', 1); } )
			.mouseout(function()  { this.setAttribute('mouseOutTimeout', window.setTimeout("$('#toolbar').fadeTo('fast', 0.65);", 200)); } );

		for(var k in this.config.guiElements)
			this.displayGuiElements(k, this.config.guiElements[k]);		
	}
	
	this.addEventDivEvents = function()
	{
		this.hideInfoDiv();
		
		// beim mouseup und mousemove wird document als EventSrc genommen, um MouseCapture im nicht IE nahe zu kommen
		Ext.EventManager.addListener(document, "mousemove", this.onMapMouseMove, this, true);
		Ext.EventManager.addListener(document, "mouseup", this.onMapMouseUp, this, true);
		Ext.EventManager.addListener(document, "keydown", this.onMapKeyDown, this, true);
		Ext.EventManager.addListener(document, "keyup", this.onMapKeyUp, this, true);
		Ext.EventManager.addListener(document, "contextmenu", _stopContextMenu);
	
		// mouseDown und mouseWheel wÃ¤hrend anderer Kartenaktionen nicht berÃ¼cksichtigen
		Ext.EventManager.removeListener(this.els.glbEventDiv, "mousedown", this.onMapMouseDown);
		Ext.EventManager.removeListener(this.els.glbEventDiv, "mousewheel", this.onMapMouseWheelAction);
		Ext.EventManager.removeListener(this.els.glbEventDiv, "mousemove", this.onMapMouseMoveSimple);
	}
	
	this.removeEventDivEvents = function()
	{
		Ext.EventManager.removeListener(document, "mousemove", this.onMapMouseMove);
		Ext.EventManager.removeListener(document, "mouseup", this.onMapMouseUp);
		Ext.EventManager.removeListener(document, "keydown", this.onMapKeyDown);
		Ext.EventManager.removeListener(document, "keyup", this.onMapKeyUp);
		
		if(this.delayedContextMenuActivation==null)
			this.delayedContextMenuActivation=new Ext.util.DelayedTask();
		this.delayedContextMenuActivation.delay(200, function() {Ext.EventManager.removeListener(document, "contextmenu", _stopContextMenu)}, this, null);
	
		// mouseDown, mouseWheel, einfaches mouseMove wieder anschalten
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousedown", this.onMapMouseDown, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousewheel", this.onMapMouseWheelAction, this, true);
		Ext.EventManager.addListener(this.els.glbEventDiv, "mousemove", this.onMapMouseMoveSimple, this, true);
	}
	
	/**
		Diese Funktion sollte unbedingt aufgerufen werden, wenn die Ã¼bliche Mausaktionsfolge
		mousedown - mousemove - mouseup in der Karte von auÃŸen unterbrochen wurde. Dies kann z.B. nach
		Reaktionen auf Events des glbMap.shape-Objektes nÃ¶tig sein, in denen alert oder confirm benutzt wird
		und die es dem Nutzer ermÃ¶glichen die Maustaste loszulassen, ohne das dass Map-Control dies registrieren kann.
	*/
	this.cleanUpMouseAction = function()
	{
		var _this=this;
		window.setTimeout(function() {_this.cleanMouseActionData();}, 1);
	}
	
	this.cleanMouseActionData = function(actionCanceled)
	{
		if(!this.mouseActionsInProgress)
			return
		this.mouseActionsInProgress=false;
		this.lastFinalAction=this.finalAction;
		this.finalAction=null;
		this.showZoomRect(false);
		this.lastMouseButton=-1;
		this.removeEventDivEvents();
		if(this.els.glbEventDiv.releaseCapture)
			this.els.glbEventDiv.releaseCapture();
		if(actionCanceled)
		{
			this.moveElement(this.els.glbVMLDiv, 0, 0)
			this.overlays.paint(/*resetContainer*/true); //setzt den glbOverlayDiv wieder auf 0,0 und zeichnet die Objekte neu
			this.moveElement(this.els.glbMapDiv[this.curMapIdx], 0, 0);
			var img=Ext.Element.get(this.els.glbMapImg[this.curMapIdx]);
			img.setLeftTop(0, 0);
			img.setSize(this.mapSize.width, this.mapSize.height);
		}
	}
	
	/**
		KartengrÃ¶ÃŸe (neu) festlegen
		@param {object} size neue KartengrÃ¶ÃŸe
		@config {int} width Breite
		@config {int} height HÃ¶he
	*/
	this.resizeMap = function(size)
	{
		var initializedOnCall = this.initialized;
		var w=size.width;
		var h=size.height;
		
		this.mapSize.width=w;
		this.mapSize.height=h;
		
		if(!this.initialized)
		{
			this.config.width=size.width;
			this.config.height=size.height; // ursprÃ¼ngliche config parameter Ã¼berschreiben
			this.init();
		}
		else
		{
			this.calcMapOffset();
			this.els.glbMapImg[this.curMapIdx].src=this.pixelGifSrc;
		}
		
		var sizeElems = [
							this.els.glbMapDiv[0],
							this.els.glbMapDiv[1],
							this.els.glbMapImg[0],
							this.els.glbMapImg[1],
							this.els.glbEventDiv,
							this.els.glbVMLDiv,
							this.els.glbVMLDivShape,
							this.els.glbVMLDivMarker,
							this.getEl("theMapOuterDiv"),
							this.getEl("cssCleaner"),
							this.getEl("theMapDivDummy")
						];
		for(var i=0; i<sizeElems.length; i++)
		{
			sizeElems[i].style.width=w+"px";
			sizeElems[i].style.height=h+"px";
		}	
		
		// Positionierungen
	//	$('#simpleNavi').css({top:"15px", left:(this.mapSize.width-85)+"px"})
		
		this.updateShapeGlobalVars();
		
		if(initializedOnCall) // beim Aufruf der Funktion war die Karte schon initialisiert
		{
			
			if(this.simpleMapActive)
			{
				if(this.tileMap)
					this.tileMap.resizeViewPort(size);
				this.setMapSize(w, h);
				this.refreshMap();
			}
			else
			{
				this.tileMap.resizeViewPort(size);
				this.tileMap.setViewPosition({gx:(this.extent.x1+this.extent.x2)/2, gy:(this.extent.y1+this.extent.y2)/2});
			}
		}
	}
	
	function _calcMapOffsetWithJQuery(el)
	{
		if(el==null)
			el=this.els.glbEventDiv;

		var xy = Ext.get(el).getXY();
		
		this.calcMapOffset.top=xy[0];
		this.calcMapOffset.top=xy[1];
		
		this.mapOffset.top =xy[1]-el.scrollTop*2;     // das scrolling wird doppelt abgezogen, weil es bei
		this.mapOffset.left=xy[0]-el.scrollLeft*2;	// top und left schon einmal mit drin steckt

		/*	
		var offsets=jQuery(el).offset({padding:true});
				
		this.mapOffset.top =offsets.top-el.scrollTop*2;     // das scrolling wird doppelt abgezogen, weil es bei
		this.mapOffset.left=offsets.left-el.scrollLeft*2;	// top und left schon einmal mit drin steckt 
		*/
	}
	
	function _calcMapOffset(el)
	{
		if(el==null)
		{
			el=this.els.glbEventDiv;
			this.mapOffset={left:0, top:0};
		}
		if(el.offsetParent!=null)
			var ret=this.calcMapOffset(el.offsetParent);
				
		this.mapOffset.top +=el.offsetTop-el.scrollTop;
		this.mapOffset.left+=el.offsetLeft-el.scrollLeft;
	}
	
	this.showZoomRect = function(vis, selection)
	{
		this.els.zoomRect.style.visibility=(vis)?"visible":"hidden";
	}
	
	this.setZoomRect = function(CX, CY, DX, DY, modKey)
	{
		// merken und falls nÃ¶tig mit den gemerkten Werten belegen
		this.lastZoomRectSize.CX = CX = CX!=null ? CX : this.lastZoomRectSize.CX;
		this.lastZoomRectSize.CY = CY = CY!=null ? CY : this.lastZoomRectSize.CY;
		this.lastZoomRectSize.DX = DX = DX!=null ? DX : this.lastZoomRectSize.DX;
		this.lastZoomRectSize.DY = DY = DY!=null ? DY : this.lastZoomRectSize.DY;
		
		if(this.finalAction=="zoom-rect") // allgemeine Variante
		{
			if(CX<DX)
			{
				if(!modKey)
					var sign="+";
				else
					var sign="-";
			}
			else
			{
				if(!modKey)
					var sign="-";
				else
					var sign="+";
			}
		}
		else if(this.finalAction=="zoom-in")
			var sign="+";
		else if(this.finalAction=="zoom-out")
			var sign="-";
		else
			var sign="";
		
		var x1 = Math.max(Math.min(CX, DX), 0);
		var y1 = Math.max(Math.min(CY, DY), 0);
		var x2 = Math.min(Math.max(CX, DX), this.mapSize.width);
		var y2 = Math.min(Math.max(CY, DY), this.mapSize.height);
		
		this.els.zoomRect.style.left=x1+"px";
		this.els.zoomRect.style.top=y1+"px";
		this.els.zoomRect.style.width=Math.max(4, (x2-x1))+"px";
		this.els.zoomRect.style.height=Math.max(4, (y2-y1))+"px";
		
		Ext.Element.get("zoomRectModeSign").dom.style.fontSize=Math.max(0, Math.min(x2-x1-7, y2-y1-7)*0.5)+"px";
		if(Math.max(0, Math.min(x2-x1-7, y2-y1-7)*0.5)<4)
			sign="";
		
		Ext.Element.get("zoomRectModeSign").dom.innerHTML=sign;
		Ext.Element.get("zoomRectBorder").fitToParent();
		Ext.Element.get("zoomRectInlay").fitToParent();
	}
	
	this.moveElement = function(el, x, y)
	{
		el.style.left=x+"px";
		el.style.top=y+"px";
	}
	
	this.updateShapeGlobalVars = function()
	{
		// diese globalen Variablen mÃ¼ssen fÃ¼r das Shape-Objekt vorhanden sein
		// ist nicht schÃ¶n, aber halt historisch gewachsen und weit verbreitet
		sWidth=this.mapSize.width;
		sHeight=this.mapSize.height;
		bound=this.extent;
	}
	
	this.getEl = function(name)
	{
		return Ext.get(name).dom;
	}
	
	/**
		Karte an Ã¼bergebener Koordinate zentrieren
		@param x {double} X-Wert (geografisch)
		@param y {double} Y-Wert (geografisch)
	*/
	this.pan = function(x, y)
	{
		if(this.simpleMapActive)
			this.addCommand("PAN", {x:x, y:y});
		else
			this.tileMap.setViewPosition({gx:x, gy:y});
	}
	
	/**
		Kartenausschnitt festlegen
		@param xMin {double} X-Wert linke untere Kordinate (geografisch)
		@param yMin {double} Y-Wert linke untere Kordinate (geografisch)
		@param xMax {double} X-Wert rechte obere Kordinate (geografisch)
		@param yMax {double} Y-Wert rechte obere Kordinate (geografisch)
	*/
	this.zoomIn = function(xMin, yMin, xMax, yMax)
	{
		if(this.simpleMapActive)
			this.addCommand("ZOOMIN", {xMin:xMin, yMin:yMin, xMax:xMax, yMax:yMax});
		else
		{
			var res=this.tileMap.calculateBestFit({min:{gx:xMin, gy:yMin}, max:{gx:xMax, gy:yMax}});
			if(res.differsFromCurrentTileView)
				this.tileMap.setViewPosition(res.centerPoint, res.level);
		}		
	}
	
	/**
		MaÃŸstab verdoppeln und Karte an Ã¼bergebener Koordinate zentrieren
		@param x {double} X-Wert (geografisch)
		@param y {double} Y-Wert (geografisch)
	*/
	this.zoomOut = function(x, y)
	{
		if(this.simpleMapActive)
			this.addCommand("ZOOMOUT", {x:x, y:y});
		else
		{
			this.tileMap.zoomOut();
		}
	}
	
	/**
		Ebenenliste neu setzen. Die Ebenen sind mit "|" zu trennen ("L1|L2|L3|Grenzen|L4"). Das SchlÃ¼sselwort "ALL" kann
		an Stelle der Liste verwendet werden, um alle Ebenen des Projektes anzuzeigen. Die Reihenfolge der Ebenen
		in der Liste ist gleichzeitig die Zeichenreihenfolge bei der Bilderstellung.
		@param {string} layerList "|" - getrennte Liste mit Ebenennamen oder "ALL"
	*/
	this.setLayerList = function(layerList)
	{
		this.addCommand("SETLAYERLIST", {layerList:layerList});
	}
	
	this.setMapSize = function(width, height)
	{
		if(this.simpleMapActive)
			this.addCommand("SETMAPSIZE", {mapWidth:width, mapHeight:height});
	}
	
	/**
		Stellt den Bild-Typ der Karte auf den angeforderten Wert
		@param {string} imageType mÃ¶gliche Werte: Ecw|Emf|Gif|Jpg|Png|Png8|Png24|Png32
	*/
	this.setImageType = function(imageType)
	{
		this.addCommand("SETIMAGETYPE", {imageType:imageType});
	}
	
	/** 
		Selektieren und zoomen auf ein/mehrere Objekt(e) einer Ebene.
		@example glbMap.showObject("BUILDINGS", "str = 'HauptstraÃŸe' AND hnr = 12", "ZoomAlways", 250);
		@param layer {string} Ebenenname
		@param iSqlWhere {string} ISQL Filter Ausdruck (nur die Bedingungen des WHERE-Teiles); siehe <a target="_blank" href="http://www.webmapserver.de/isql">http://www.webmapserver.de/isql<a>
		@param [zoomBehavior] {string} Zoom-Verhalten im Trefferfall: "ZoomAlways" (Standard), "NoZoom" [derzeit noch nicht impl.: "ZoomIfPartialyOutOfBounds", "ZoomIfFullyOutOfBounds"]
		@param [offset] {double} Abstand beim Zoomen zum Kartenrand um das/die Objekt(e) herum; Standard:100
	*/
	this.showObject = function(layer, iSqlWhere, zoomBehavior, offset)
	{
		if(this.simpleMapActive)
		{
			// Enum absichern
			var zoomBehaviorEnum = {
										NoZoom:"NoZoom", // niemals zoomen
										ZoomIfFullyOutOfBounds:"ZoomIfFullyOutOfBounds", // nur zoomen, wenn vollstÃ¤ndig auÃŸerhalb
										ZoomIfPartialyOutOfBounds:"ZoomIfPartialyOutOfBounds", // auch zoomen, wenn teilweise auÃŸerhalb
										ZoomAlways:"ZoomAlways" // immer auf Objekt zoomen
								   }
			offset=(offset && !isNaN(offset)) ? offset : 100;
			zoomBehavior=zoomBehaviorEnum[zoomBehavior] || "ZoomAlways";
			
			this.addCommand("SHOWOBJECT", {layer:layer, iSqlWhere:iSqlWhere, zoomBehavior:zoomBehavior, offset:offset});
		}
		else
			alert(ml(this, "ERROR_ZoomToObject", "Zoom auf Objekt ist nicht mÃ¶glich, wenn nur Tile-Ebenen genutzt werden"));
	}
	
	/**
		Kartenausschnitt auf Ausdehnung der Ã¼bergebenen Ebenen setzen
		@param layerList {string} "|" - getrennte Liste mit Ebenennamen oder "ALL"
	*/
	this.zoomToLayers = function(layerList)
	{
		if(this.simpleMapActive)
		{
			this.addCommand("ZOOMTOLAYERS", {layerList:layerList});
		}
		else
			alert(ml(this, "ERROR_ZoomToLayers", "Zoom auf die Ausdehnung bestimmter Ebenen ist nicht mÃ¶glich, wenn nur Tile-Ebenen genutzt werden"));
	}
	
	/**
		Kartenausschnitt auf die Standardausdehnung zurÃ¼cksetzen. Diese wird serverseitig beim MapControl
		durch InitExtent oder InitLayer festgelegt. Wurde keine dieser Angaben getroffen, wird auf die Ausdehnung aller momentan sichtbaren Ebenen gezoomt. 
	*/
	this.zoomToDefaultExtent = function()
	{
		if(this.simpleMapActive)
			this.addCommand("ZOOMTODEFAULTEXTENT");
	}
	
	/**
		Zoom auf eine Koordinate. Auf Wunsch kann die Position mit einem Punkt des Shape Objektes markiert werden.
		@param x {double} X-Wert (geografisch)
		@param y {double} Y-Wert (geografisch)
		@param [offset] {double} Abstand von der Koordinate zum Kartenrand; Standard:1000
		@param [showMarker] {bool} Koordinate markieren
		
	*/
	this.zoomToPoint = function(x, y, offset, showMarker)
	{
		x=parseFloat(x);
		y=parseFloat(y);
		offset=offset || 1000;
		this.zoomIn(x-offset, y-offset, x+offset, y+offset);
		if(showMarker)
		{
			if(this.markerShape)
			{
				this.markerShape.clearAll();
				this.markerShape.addPart("POINT");
				this.markerShape.addGeoPoint(x, y);
				this.markerShape.paint();
			}
		}
	}
	
	/** @private */
	this.getImage = function(doCreate)
	{
		if(this.simpleMapActive)
			this.addCommand("GETIMAGE", {doCreate:doCreate ? "true" : "false"});
	}
	
	/**
		Kartenbild neu vom Mapserver anfordern. Dabei werden alle seit der letzen Kartenaktualisierung
		im Befehls-Queue aufgelaufenen Aktionen ausgefÃ¼hrt. 
	*/
	this.refreshMap = function()
	{
		if(!this.simpleMapActive)
		{
			this.lastFixedExtent=new ExtentObj(this.extent.x1, this.extent.y1, this.extent.x2, this.extent.y2);
			
			this.overlays.hide();
			this.shape.hide();
			if(this.markerShape)
				this.markerShape.hide();
			this.moveElement(this.els.glbVMLDiv, 0, 0);
			this.overlays.paint(/*resetContainer*/true); //setzt den glbOverlayDiv wieder auf 0,0 und zeichnet die Objekte neu
			this.moveElement(this.els.glbMapDiv[0], 0, 0);
			this.moveElement(this.els.glbMapDiv[1], 0, 0);
			
			var s=this.els.glbMapImg[0].style;
			s.left="0px";
			s.top="0px";
			s.width=this.mapSize.width+"px";
			s.height=this.mapSize.height+"px";
			
			var s=this.els.glbMapImg[1].style;
			s.left="0px";
			s.top="0px";
			s.width=this.mapSize.width+"px";
			s.height=this.mapSize.height+"px";
			
			this.shape.paint();
			this.overlays.show();
			this.shape.show();
			if(this.markerShape)
			{
				this.markerShape.paint();
				this.markerShape.show();
			}
			
			return;
		}
		this.hideInfoDiv();
		if(!this.simpleMapActive)
			return;
		
		this.prepareNewImage();
	}
	
	this.addCommand = function(commandName, arguments)
	{
		this.commandQueue[this.commandQueue.length]={commandName:commandName, arguments:arguments}
	}
	
	/**
		FÃ¼gt an das zuletz abgesetzte Kartenkommando noch weitere Parameter an. Dies wird u.U. benÃ¶tigt,
		wenn fÃ¼r bestimmte Kommandos noch ein serverseitiges Processing erfolgt. (Events des serverseitigen MapActionManager Objektes)
		@param args {object} Objekt mit SchlÃ¼ssel - Wert Paaren, die dem letzten Befehl noch angefÃ¼gt werden
	*/
	this.addAdditionalArgsToLastCommand = function(args)
	{
		if(this.commandQueue.length<1)
			return alert(ml(this, "ERROR_AddAdditionalArgsToLastCommand_1", "glbMap.addAdditionalArgsToLastCommand kann erst nach einem Mapserverkommando aufgerufen werden."));
		for(var k in args)
		{
			var commandArgs = this.commandQueue[this.commandQueue.length-1].arguments;
			if(commandArgs[k])
				return alert(ml(this, "ERROR_AddAdditionalArgsToLastCommand_2", "Das Argument '{0}' ist bereits vorhanden.", [k]));
			else
				commandArgs[k]=args[k];
		}
	}
	
	this.getCommands = function(keepQueue)
	{
		if(this.commandQueue.length==0)
			return "";
		
		var commands=new Array();
		var args=new Array();
		for(var i=0; i<this.commandQueue.length; i++)
		{
			var cmd=this.commandQueue[i];
			commands[commands.length]=cmd.commandName;
			for(var k in cmd.arguments)
				args[args.length]="C"+i+"_"+k+"="+escape(cmd.arguments[k]);
		}
		if(!keepQueue)
			this.commandQueue=new Array();
		
		return "COMMANDS="+escape(commands.join(","))+"&"+args.join("&")+"&";
	}
		
	this.prepareNewImage = function(keepExtentHistory)
	{
		this.locked=true;
		this.getEl("LoadingMapDiv").style.visibility="visible";
		this.lastCursor=this.els.glbEventDiv.style.cursor;
		this.els.glbEventDiv.style.cursor="progress";
		
		this.addCommand("PREPAREIMAGE");
		var args=this.getCommands();
		this.sendMapCommand(args);
	}
	
	this.sendMapCommand = function(args)
	{
		//TODO: wahrscheinlich mal auf Post umstellen
		var url=this.basePath+"webmap.aspx?";
		var sessionSpecifier="SessionSpecifier="+escape(this.sessionSpecifier)+"&";
		var reInitInfo="reInitInfo="+escape(this.sessionReInitInfo).replace(/\+/g, "%2B")+"&";
		url+=sessionSpecifier+args+"noCache="+((new Date()).getTime())+"&";
		//HACK: TODO: Posten ermÃ¶glichen
		if(url.length+reInitInfo.length<2000)
			url+=reInitInfo;

		var script = document.createElement("script");
		script.setAttribute("src", url);
		script.setAttribute("type", "text/javascript");
		script.setAttribute("id", "MapControlScript_"+this.sessionSpecifier+"_"+(this.scriptBlockId++));
		this.head.appendChild(script);
		//this.els.imagePrepFrame.location.href=url;
	}
	
	this.getNewImage = function(url, debug)
	{
		var eventListenersRemoved=false;
		if(this.switchMaps) // wenn Karten zu tauschen sind
		{
			//this.els.glbMapImg[this.curMapIdx].onload=null; 
			var img=this.els.glbMapImg[this.curMapIdx];
			Ext.EventManager.removeListener(img, "load", this.onMapLoadEndProxy[this.curMapIdx]); // damit dann problemlos ein Ein-Pixel-Bild zugewiesen werden kann ohne onload zu verursachen;
			Ext.EventManager.removeListener(img, "error", this.onMapLoadEndProxy[this.curMapIdx]);
			eventListenersRemoved=true;
			
			var temp=this.curMapIdx; // Indizes tauschen
			this.curMapIdx=this.prvMapIdx;
			this.prvMapIdx=temp;
		}
		
		if(glbBrowserCaps.isOpera75)
		{
			var img=new Image();
			img.name   = this.els.glbMapImg[this.curMapIdx].name;
			img.width  = this.els.glbMapImg[this.curMapIdx].width;
			img.height = this.els.glbMapImg[this.curMapIdx].height;
			img.title  = "Karte";
			this.els.glbMapImg[this.curMapIdx].parentNode.replaceChild(img, this.els.glbMapImg[this.curMapIdx]);
			this.els.glbMapImg[this.curMapIdx]=img;
			eventListenersRemoved=true;
		}
		else
			var img=this.els.glbMapImg[this.curMapIdx];

		if(debug)
			this.els.imagePrepFrame.location.href=url;
		else
		{
			//img.onload=_e_onMapLoadEnd;
			//img.onerror=_e_onMapLoadError;
			if(eventListenersRemoved)
			{
				Ext.EventManager.addListener(img, "load", this.onMapLoadEndProxy[this.curMapIdx], this, true);
				Ext.EventManager.addListener(img, "error", this.onMapLoadEndProxy[this.curMapIdx], this, true);
			}
			//alert(img.src+"\n"+eventListenersRemoved+"\n"+this.curMapIdx+"\n"+img.id)
			img.src=url+"noCache="+((new Date()).getTime());
		}
	}
	
	/** @ignore */
	this.stepForward = function()
	{
		
	}
	
	/** @ignore */
	this.stepBack = function()
	{
		var e=this.extentHistory[this.extentHistoryPointer];
		//this.prepareNewImage("SINGLE_ACTION=ZoomIn&x1="++"&y1="++"&x2="++"&y2="++"&");
	}
	
	this.moveMap = function(rx, ry)
	{
		if(!this.simpleMapActive)
			return alert(ml(this, "ERROR_MoveMap", "Diese Funktion steht im Moment nicht zur VerfÃ¼gung, wenn nur Tile Layer angezeigt werden."));
			//nicht Ã¼bersetzen - kommt wieder weg die Meldung
		
		if(this.locked)
			return;
		
		if(!this.tileMap)
		{
			rx*=-1.5;
			ry*=-1.5;
			var img=Ext.Element.get(this.els.glbMapImg[this.curMapIdx]);
			var xy=img.getXY();
			var _this=this;
			img.setXY(
						[xy[0]+this.mapSize.width*rx, xy[1]+this.mapSize.height*ry], 
						{
							duration:0.35,
							easing:"easeIn",
							callback:function() { _this.finishMoveMap(rx, ry); },
							scope:this
						}
					 );
		}
		else
			this.finishMoveMap(rx, ry)
	}
	
	this.finishMoveMap = function(rx, ry)
	{
		rx*=-1;
		this.switchMaps=true;
		this.pan((this.lastFixedExtent.x1+this.lastFixedExtent.x2)/2+(this.lastFixedExtent.x2-this.lastFixedExtent.x1)*rx, (this.lastFixedExtent.y1+this.lastFixedExtent.y2)/2+(this.lastFixedExtent.y2-this.lastFixedExtent.y1)*ry);
		this.refreshMap();
	}
/*	
	function _changeTool(args)
	{
		this.hideInfoDiv();
		
		switch(args.command)
		{
			case "disable-all":
				this.currentTool=null;
				glbEventObject.fireEvent("onAfterMapToolChanged", {command:"disable-all"});
				break;
			case "zoom-in":
			case "zoom-out":
			case "pan":
			case "map-tip":
					this.currentTool=args.command;
					glbEventObject.fireEvent("onAfterMapToolChanged", {command:this.currentTool});
				break;
				
			case "pen":
					this.currentTool=args.command;
					var sc=args.subCommand;
					if(sc)
						this.penSubMode=sc;
					else
						this.penSubMode=null;
					
					if(args.shapeFeatureType)
						this.shape.currentSelectedShapeMode=args.shapeFeatureType;
					
					var pc = args.propagateCommand || this.currentTool;
					glbEventObject.fireEvent("onAfterMapToolChanged", {command:pc});
				break;
			case "grab-coord":
					this.currentTool=args.command;
					this.grabCoordMode=args.grabCoordMode|| "default";
					
					var pc = args.propagateCommand || this.currentTool;
					glbEventObject.fireEvent("onAfterMapToolChanged", {command:pc});
				break;
			
			// Direktkommandos
			case "zoom-all":
					this.prepareNewImage("SINGLE_ACTION=ExtentToVisible&");
				break;
			case "zoom-layers":
					this.prepareNewImage("SINGLE_ACTION=EXTENTTOLAYERS&LAYERLIST="+args.layerList+"&");
				break;
			case "set-extent":
					this.prepareNewImage("Single_Action=ZoomIn&x1="+args.x1+"&y1="+args.y1+"&x2="+args.x2+"&y2="+args.y2+"&");
				break;
			case "reload-map":
					this.prepareNewImage("SINGLE_ACTION=CREATEIMAGE&");
				break;
			case "set-layerlist":
					this.prepareNewImage("SINGLE_ACTION=SETLAYERLIST&LAYERLIST="+args.layerList+"&");
				break;
			case "show-object":
					var zoomBehavior = {
											NoZoom:"NoZoom", // niemals zoomen
											ZoomIfFullyOutOfBounds:"ZoomIfFullyOutOfBounds", // nur zoomen, wenn vollstÃ¤ndig auÃŸerhalb
											ZoomIfPartialyOutOfBounds:"ZoomIfPartialyOutOfBounds", // auch zoomen, wenn teilweise auÃŸerhalb
											ZoomAlways:"ZoomAlways" // immer auf Objekt zoomen
									   }
					var offset=100;
					if(args.offset && !isNaN(args.offset))
						offset=args.offset;

					this.prepareNewImage("SINGLE_ACTION=SHOWOBJECT&"
											+"LAYER="+args.layer+"&"
											+"ISQLWHERE="+escape(args.iSqlWhere)+"&"
											+"OFFSET="+offset+"&"
											+"ZOOMBEHAVIOR="+(zoomBehavior[args.zoomBehavior] || "ZoomAlways")+"&"
										);
				break;
		}
	}
*/	
	this.setExtent = function(minX, minY, maxX, maxY)
	{
		var e=new ExtentObj(minX, minY, maxX, maxY);
		this.extent=e;
		
		//den scale aus dem Extent und der IWAN typischen DPI berechnen
		if(!this.simpleMapActive)
			this.currentMapScale=Math.round((maxX-minX)/(this.mapSize.width*0.0003125)); // IWAN Screen-Meter pro Pixel Konstante (17" Monitor mit 320mm Breite und 1024 px)
		
//document.title="Scale: "+this.currentMapScale;		
		
	/*	
		this.extentHistory[this.extentHistory.length]=this.extent;
		this.extentHistoryPointer=this.extentHistory.length-1;
		glbEventObject.fireEvent("cmdDisableCommand", {command:'step-forward'})
		glbEventObject.fireEvent("cmdEnableCommand", {command:'step-back'})
	*/			
		
		this.updateShapeGlobalVars();
		glbEventObject.fireEvent("onMapExtentChanged", this);
	}
	
	this.setMapInfo = function(info) // info={ sessionReInitInfo:'{1}', isReInitialized:{2} x1:{3}, y1:{4}, x2:{5}, y2:{6}, srs:'{7}', scale:{8}, minScale:{9}, maxScale:{10} }
	{
		this.minScale=parseInt(info.minScale, 10);
		this.currentMapScale=info.scale;
		
		this.sessionReInitInfo=info.sessionReInitInfo;
		if(info.isReInitialized)
			glbEventObject.fireEvent("onMapSessionReInitialized", this);
		
		this.setExtent(info.x1, info.y1, info.x2, info.y2); // aktualisiert this.extent - bei Existenz einer TileMap ist da etwas mehr dynamik drin
		this.lastFixedExtent=new ExtentObj(info.x1, info.y1, info.x2, info.y2); // das ist der klassische Extent der einfachen Karte vom Mapserver - wird fÃ¼r pan, zoom - Berechnungen benÃ¶tigt
			
		// constraints durch die TileMap berÃ¼cksichtigen - evtl. den Ausschnitt noch mal leicht anpassen
		if(this.tileMap)
		{
			var res=this.tileMap.calculateBestFit({min:{gx:this.extent.x1, gy:this.extent.y1}, max:{gx:this.extent.x2, gy:this.extent.y2}});
			if(res.differsFromCurrentTileView)
			{
				var _this=this;
				this.mapOnLoadEndSingleExecuteFn=function() {_this.tileMap.setViewPosition(res.centerPoint, res.level);};
			}
			// leichte Korrektur vornehmen und das auch im zugehÃ¶rigen Extent-Objekt mitfÃ¼hren
			this.zoomIn(res.extent.min.gx, res.extent.min.gy, res.extent.max.gx, res.extent.max.gy);
			this.lastFixedExtent=new ExtentObj(res.extent.min.gx, res.extent.min.gy, res.extent.max.gx, res.extent.max.gy);
		}
		
		// die Karte abholen
		this.getImage(true);
		var args=this.getCommands();
		
		var url=this.basePath+"webmap.aspx?";
		var sessionSpecifier="SessionSpecifier="+escape(this.sessionSpecifier)+"&";
		url+=sessionSpecifier+args+"noCache="+((new Date()).getTime())+"&";
		this.getNewImage(url);
		
		//this.els.imagePrepFrame.location.href=this.pixelGifSrc; // der IE wÃ¼rde den IFrameinhalt laden, wenn man den zurÃ¼ck Knopf nimmt oder die Seite aus den Favouriten nimmt
	}
	
	this.prepareEventObject = function(/*ExtJs EventObj*/ e)
	{
		var crx=0; // der IE macht im Standardmode Mist mit dem HTML Border (2px)
		var cry=0; // hier wird einfach etwas zwischen den Browsern gemittelt

		var x=e.getPageX();
		var y=e.getPageY();
		var modKey=e.altKey?'ALT':(e.ctrlKey?'CTRL':(e.shiftKey?'SHIFT':''));
		var btn=e.button;
	
		var imageX = x - this.mapOffset.left + crx;
		var imageY = y - this.mapOffset.top + cry;
		
		// gleich noch in Geokoords
		var gx=gy=null;
		if(this.extent)
		{
			var m2px=(this.extent.x2-this.extent.x1)/this.mapSize.width;
			gx=this.extent.x1+imageX*m2px;
			gy=this.extent.y1+(this.mapSize.height-imageY)*m2px;
		}
		
		var extendedEvent = {
								x:imageX,
								y:imageY,
								gx:gx,
								gy:gy,
								pageX:e.getPageX(),
								pageY:e.getPageY(),
								btn:btn,
								modKey:modKey
							  };
		Ext.apply(extendedEvent, e); // e ist ein Singleton und wird immer wieder benutzt, deswegen
									 // werden die e-Eigenschaften zu den zusÃ¤tzlichen kopiert und nicht umgekehrt
		return extendedEvent;
	}
	
	/**
		Berechnet aus einer Bildschirmkoordinate die geografische Koordinate im aktuellen Kartenbild.
		Die linke obere Bildecke ist dabei die Koordinate (0,0).
 		@param x {int} X-Wert
		@param y {int} Y-Wert
		@returns {object} Objekt mit {x, y} (geografisch)
	*/
	this.screenToGeo = function(x, y, /*nur fÃ¼r interne Nutzung*/useLastFixedExtent)
	{
		var extent=useLastFixedExtent ? this.lastFixedExtent : this.extent;
		var ret={x:0, y:0};
		if(extent)
		{
			var m2px=(extent.x2-extent.x1)/this.mapSize.width;
			ret.x=extent.x1+x*m2px;
			ret.y=extent.y1+(this.mapSize.height-y)*m2px;
		}
		return ret;
	}
	
	this.geoToScreen = function(gx, gy, /*nur fÃ¼r interne Nutzung*/useLastFixedExtent)
	{
		var extent=useLastFixedExtent ? this.lastFixedExtent : this.extent;
		var ret={x:0, y:0};
		if(extent)
		{
			var m2px=(extent.x2-extent.x1)/this.mapSize.width;
			ret.x=(gx-extent.x1)/m2px;
			ret.y=this.mapSize.height-((gy-extent.y1)/m2px);
		}
		return ret;
	}
	
	this.m2px = function()
	{
		if(this.extent && this.mapSize.width)
			return (this.extent.x2-this.extent.x1)/this.mapSize.width;
		else
			return null; // ist nicht optimal, aber was will man machen ...
	}
	
	/**
		Interaktionselemente ein-/ausblenden
		@param elements {string/array} ElementId oder Array von diesen: "zoomSlider", "navigationCross", "toolbar", "overviewMap"
		@param show {bool} spezifizierte Elemente anzeigen oder ausblenden (true==anzeigen)
	*/
	this.displayGuiElements = function(/*string/array*/elements, show)
	{
		// elements=["zoomSlider", "navigationCross", "toolbar"];		
		if(typeof elements=="string")
			elements=[elements];
			
		for(var i=0; i<elements.length; i++)
		{
			var el=Ext.get(elements[i]);
			if(el)
			{
				if(show)
				{
					el.dom.style.display="block";
					var a=Ext.get("ovmCloseIcon").dom.offsetTop; // Hack, fÃ¼r den IE7 - das scheint in manchen FÃ¤llen erst das Rendering richtig zu machen.
				}
				else
				{
					el.dom.style.display="none";
				}
			}
		}
	}
	
	//-----------------------------------------------------------------------
	// Events
	
	this.onMapLoadEndProxy=[
								function(e)
								{
									var idx=0;
									this.onMapLoadEnd(e);
								},
								function(e)
								{
									var idx=1;
									this.onMapLoadEnd(e);
								}
							];
	
	this.onMapLoadEnd = function(e)
	{
		this.overlays.hide();
		this.shape.hide();
		if(this.markerShape)
			this.markerShape.hide();
		if(this.switchMaps)
		{
			this.els.glbMapDiv[this.curMapIdx].style.zIndex="501";
			this.els.glbMapDiv[this.prvMapIdx].style.zIndex="500";
			this.moveElement(this.els.glbVMLDiv, 0, 0);
			this.overlays.paint(/*resetContainer*/true); //setzt den glbOverlayDiv wieder auf 0,0 und zeichnet die Objekte neu
			this.moveElement(this.els.glbMapDiv[this.prvMapIdx], 0, 0);
			this.els.glbMapImg[this.prvMapIdx].src=this.pixelGifSrc;
			var s=this.els.glbMapImg[this.prvMapIdx].style;
			s.zoom="1.01";
			s.zoom="";
			s.left="0px";
			s.top="0px";
			s.width=this.mapSize.width+"px";
			s.height=this.mapSize.height+"px";
			
			this.switchMaps=false; //damit ist die Tauscherei abgeschlossen
		}
		
		if(this.mapOnLoadEndSingleExecuteFn)
		{
			this.mapOnLoadEndSingleExecuteFn();
			this.mapOnLoadEndSingleExecuteFn=null;
		}
		
		this.shape.paint();
		this.overlays.show();
		this.shape.show();
		if(this.markerShape)
		{
			this.markerShape.paint();
			this.markerShape.show();
		}
		this.getEl("LoadingMapDiv").style.visibility="hidden";
		
		// zoomSlider anpassen
		this.ignoreNextSliderMove=true;
		var ratio=(this.currentMapScale-this.scaleLimits.min)/this.scaleLimits.max;
		var zoomSliderPxHeight=124-15;
		$('#zoomSlider_rail').SliderSetValues([[0,Math.round(zoomSliderPxHeight*ratio)]]);
		
		this.locked=false;
		this.els.glbEventDiv.style.cursor=this.lastCursor;
		glbEventObject.fireEvent("onMapImageLoaded", this);
	}
this.flag=false;
	
	this.onMapLoadError = function(e)
	{
		this.locked=false;
		this.els.glbEventDiv.style.cursor=this.lastCursor;
		alert(ml(this, "ERROR_MapLoad", "Fehler beim Laden der Karte"));
	}
		
	this.onMapMouseDown = function(e)
	{	
		e.stopEvent(); // Durchreichen des Events verhindern

		if(this.locked)
			return; // wenn Karte noch lÃ¤dt
		
		this.addEventDivEvents();
		
		this.mouseActionsInProgress=true; // muss beim Canceln geprÃ¼ft werden, weil das Cancel nicht mehrfach ausgefÃ¼hrt werden soll
		
		this.calcMapOffset(); //nur im MouseDown machen - das reicht
		
		if(this.els.glbEventDiv.setCapture) // Mouse-Capture
			this.els.glbEventDiv.setCapture();
		
		e=this.prepareEventObject(e); // erweitert das EventObject
		this.lastMouseButton=e.button;
		
		if(this.tileMap)
			this.tileMap.setRefPosition();

		// EXPLICIT oder MOUSEACTION
		// Im Modus EXPLICIT passiert alles mit der linken Maustaste und das aktuelle Werkzeug
		// muss explizit gesetzt sein (z.B. per Button)
		// Der Modus MOUSEACTION macht die Karteninteraktionen halbwegs intelligent und der Linksklick
		// wird als Aktionsbefehl interpretiert (Standardaktion oder gesetztes Werkzeug)
		//
		// Werte fÃ¼r this.currentTool sind in folgender Form: Map.Pan, Map.ZoomIn, Map.ZoomOut, ... (gewÃ¤hltes Werkzeug falls zutreffend)
		// Werte fÃ¼r this.finalAction sind in folgender Form: pan, zoom-in, zoom-out, ... (tatsÃ¤chlich ausgefÃ¼hrte Aktion)
		if(this.mapToolMode=="MOUSEACTION")
		{
			this.mouseStart={x:e.x, y:e.y}; // Startkoordinate merken
			
			if(e.button==1)
			{
				this.prepareZoomResize();			
				this.els.glbEventDiv.style.cursor="row-resize";
			}
			if(e.button==2)
				this.els.glbEventDiv.style.cursor="default";
		}		
		
		// this.mapToolMode=="EXPLICIT" - ist nicht so entscheidend hier sondern:
		// es ist mÃ¶glich, dass zu Mausaction trotzdem noch ein Werkzeug dazu kommt
		if(this.currentTool!=null)
		{
			var currentTool=this.currentTool;
		
			switch(currentTool)
			{
				case "Map.ZoomIn":
				case "Map.ZoomOut":
						if(e.btn==2) return;
						this.mouseStart={x:e.x, y:e.y};
						this.showZoomRect(true);
						this.setZoomRect(e.x, e.y, e.x, e.y);
						this.finalAction=currentTool=="Map.ZoomIn" ? "zoom-in" : "zoom-out";
						this.zoomActive=true;
					break;
				case "Map.Pan":
						if(e.btn==2) return;
						this.mouseStart={x:e.x, y:e.y}
						this.finalAction="pan";
					break;
				case "Map.SimpleClick":
						this.finalAction="simple-click";
					break;
				case "Map.Redline": // Pen
						this.finalAction="pen";
						if(this.shape.drawVertices)
						{
							var snapDist=5; //px
							if(this.shapeEditConstraint)
							{
								if(this.shapeEditConstraint=="MoveVertex")
								{
									if(this.shape.selectNearestVertex(e.x, e.y, snapDist))
									{
										if(this.shape.drawMode!=this.shape.DM.GDI)
											this.shape.paint();
										this.movingVertex=true;
									}
								}
								else if(this.shapeEditConstraint=="AddVertex")
								{
									if(this.shape.addAndSelectVertex(e.x, e.y, snapDist))
									{
										this.shape.paint();
										this.movingVertex=true;
									}
								}
								else if(this.shapeEditConstraint=="DropVertex")
								{
									if(this.shape.dropVertex(e.x, e.y, snapDist))
										this.shape.paint();
								}
							}
							else
							{
								if(this.shape.selectNearestVertex(e.x, e.y, snapDist))
								{
									if(this.shape.drawMode!=this.shape.DM.GDI)
										this.shape.paint();
									this.movingVertex=true;
								}
								else
								{
									if(this.shape.addAndSelectVertex(e.x, e.y, snapDist))
									{
										this.shape.paint();
										this.movingVertex=true;
									}
								}
							}
						}
						else
						{
							if(e.btn==0)
							{
								if(!this.shape.openPartAvailable())
								{
									var ret=this.shape.addPart(this.activeShapeFeatureType || "ARC");
									if(!ret)
									{
										this.cleanUpMouseAction();
										return;
									}
								}
								var ret=this.shape.addScreenPoint(e.x, e.y);

								if(!ret)
									this.cleanUpMouseAction();
								this.shape.paint();
							}
							else
							{
								if(this.shape.openPartAvailable())
								{
									this.shape.clearLast();
									this.shape.paint();
								}
							}
						}
					break;
				/*
				case "grab-coord": // Punkt abgreifen
						var canceled=false;
						if(e.btn==2)
							canceled=true;

						var m2px=(this.extent.x2-this.extent.x1)/this.mapSize.width;

						glbEventObject.fireEvent("onAfterMapCoordGrabed", {x:e.x, y:e.y, gx:e.gx, gy:e.gy, m2px:m2px, currentScale:this.currentMapScale, canceled:canceled});
					break;*/
			}
		}
	}
	
	this.onMapMouseMoveSimple = function(e)
	{
		e=this.prepareEventObject(e);
		this.lastMouseCoord={x:e.x, y:e.y};
		
		if(this.currentTool=="Map.Redline" || this.currentTool=="Map.RedlineAndFullAuto")
		{
			if(this.movingVertex)
			{
				var ret=this.shape.moveVertex(e.x, e.y);
				if(/*this.shape.drawMode!this.shape.DM.VML ||*/ !ret)
					this.shape.paint();
			}
			
			this.shape.mouseMoveAction(e);
			this.shape.showInfo(e);
		}
	}

this.mc=0;
this.maxMc=0;	
	this.onMapMouseMove = function(e)
	{	
		//DEBUG
		if(interuptedMouse && interuptCount>maxInteruptCount)
			Ext.EventManager.removeListener(document, "mousemove", this.onMapMouseMove);
		
		if(this.locked)
			return; // wÃ¤hrend Karte noch lÃ¤dt
		
		e=this.prepareEventObject(e);
		this.lastMouseCoord={x:e.x, y:e.y};
	
		if(this.mapToolMode=="MOUSEACTION")
		{
			if(this.lastMouseButton>=0)
			{
				// die finalAction wird einmal aktiviert und bleibt dann bestehen 
				// (es wird also kein Klick, wenn man nach dem Karte rumschieben wieder an den Ausgangspunkt zurÃ¼ckkehrt)
				if(this.finalAction==null)
				{
					var threshold=1;
					if(Math.abs(this.mouseStart.x-e.x)>threshold || Math.abs(this.mouseStart.y-e.y)>threshold)
					{
						if(this.lastMouseButton==0) // linke Maustaste + ziehen --> pan
						{
							this.finalAction="pan";
							this.els.glbEventDiv.style.cursor="move";
						}
						if(this.lastMouseButton==1)
							this.finalAction="zoom-resize";
						else if(this.lastMouseButton==2) // rechte Maustaste + ziehen --> zoom-in/out
						{
							this.finalAction="zoom-rect";
							this.showZoomRect(true);
						}
					}
				}
			}
		}
		
		switch(this.finalAction)
		{
			case "pan":
					this.moveElement(this.els.glbMapDiv[this.curMapIdx], e.x-this.mouseStart.x, e.y-this.mouseStart.y);
					this.moveElement(this.els.glbVMLDiv, e.x-this.mouseStart.x, e.y-this.mouseStart.y);
					this.moveElement(this.els.glbOverlayDiv, e.x-this.mouseStart.x, e.y-this.mouseStart.y);
					if(this.tileMap)
						this.tileMap.setOffsetToRefPosition(e.x-this.mouseStart.x, e.y-this.mouseStart.y)
				break;
			case "zoom-rect":
			case "zoom-in":
			case "zoom-out":
					this.setZoomRect(this.mouseStart.x, this.mouseStart.y, e.x, e.y, e.ctrlKey);
				break;
			case "zoom-resize":
					this.zoomResize(e);
				break;
			case "pen":
					if(this.movingVertex)
					{
						var ret=this.shape.moveVertex(e.x, e.y);
						if(/*this.shape.drawMode!this.shape.DM.VML ||*/ !ret)
							this.shape.paint();
					}
					
					this.shape.mouseMoveAction(e);
					this.shape.showInfo(e);
				break;
		}
		
		var _this=this;
		
		if(interuptedMouse && interuptCount>maxInteruptCount)
		{
			interuptCount=0;
			wt=window.setTimeout(function() {Ext.EventManager.addListener(document, "mousemove", _this.onMapMouseMove, _this, true);}, 1);
		}
		interuptCount++;
		//DEBUG
		//document.title=interuptCount
	}
	
	interuptedMouse=true;
	interuptCount=0; maxInteruptCount=10;
	wt=null;
	
	this.onMapMouseUp = function(e)
	{	
		interuptCount=0;
		if(wt)
		{
			window.clearTimeout(wt);
			wt=null;
		}
				
		if(this.locked)
			return; // wÃ¤hrend Karte noch lÃ¤dt

		e=this.prepareEventObject(e);

		var currentAction=this.finalAction;       // hier einige Dinge lokal merken, weil es in der nÃ¤chsten Fkt.
		var lastMouseButton=this.lastMouseButton; // bereits zurÃ¼ckgesetzt werden
		this.cleanMouseActionData(); //einiges Reseten -> ist nÃ¶tig fÃ¼r reibungsloses funktionieren der Map-Overlays
		
		if(this.mapToolMode=="MOUSEACTION")
		{
			this.els.glbEventDiv.style.cursor="default"; //"help";
			if(currentAction==null)
			{
				if(lastMouseButton==0)
					currentAction="simple-click";
				else if(lastMouseButton==2)
					currentAction="zoom-out-click";
			}
		}
		
		var threshold=1;
		switch(currentAction)
		{
			case "pan":
					if(this.simpleMapActive)
					{
						if(Math.abs(e.x-this.mouseStart.x)>0 || Math.abs(e.y-this.mouseStart.y)>0)
						{
							var px=(this.mapSize.width/2) -(e.x-this.mouseStart.x);
							var py=(this.mapSize.height/2)-(e.y-this.mouseStart.y);
							// in Geokoordinaten
							var m2px=(this.lastFixedExtent.x2-this.lastFixedExtent.x1)/this.mapSize.width;
							var gx=this.lastFixedExtent.x1+px*m2px;
							var gy=this.lastFixedExtent.y1+(this.mapSize.height-py)*m2px;
							
							this.switchMaps=true;
							this.pan(gx, gy);
							this.refreshMap();
						}
					}
					else
						this.refreshMap();
				break;
			case "zoom-rect":
			case "zoom-in":
			case "zoom-out":
					if(Math.abs(e.x-this.mouseStart.x)>threshold || Math.abs(e.y-this.mouseStart.y)>threshold)
					{
						var ex=(e.x<0)?0:(e.x>this.mapSize.width)?this.mapSize.width:e.x;
						var ey=(e.y<0)?0:(e.y>this.mapSize.height)?this.mapSize.height:e.y;
						var x1=Math.min(ex, this.mouseStart.x);
						var y1=Math.max(ey, this.mouseStart.y); // ist geografisch min
						var x2=Math.max(ex, this.mouseStart.x);
						var y2=Math.min(ey, this.mouseStart.y); // ist geografisch max
						
						// nach rechts gezogen wirds im FullAuto-Modus ein zoom-in, nach links ein zoom-out, der modifier Key kehrt das um
						var modifier=e.ctrlKey ? -1 : 1;
						if(currentAction=="zoom-in" || ((e.x-this.mouseStart.x)*modifier>=0 && currentAction=="zoom-rect")) // zoom-in
						{
							// nix zu tun
						}
						else // zoom-out
						{
							var dcx=x2-x1;
							var dcy=y2-y1;
							var cRatio=dcx/dcy;
							var mRatio=this.mapSize.width/this.mapSize.height;
							if(cRatio<mRatio) //dann ist y die lÃ¤ngere Kante
								var zoomRatio=(this.mapSize.height/dcy)/2;
							else
								var zoomRatio=(this.mapSize.width/dcx)/2;
								
							// Mittelpunkt bleibt; Extent um zoomRatio vergrÃ¶ÃŸern
							var mx=this.mapSize.width/2;
							var my=this.mapSize.height/2;
							x1=mx-(this.mapSize.width*zoomRatio); x2=mx+(this.mapSize.width*zoomRatio); 
							y1=my-(this.mapSize.height*zoomRatio); y2=my+(this.mapSize.height*zoomRatio);
						}
						
						var pMin=this.screenToGeo(x1, y1, this.simpleMapActive);
						var pMax=this.screenToGeo(x2, y2, this.simpleMapActive);
						this.zoomIn(pMin.x, pMin.y, pMax.x, pMax.y);
						this.refreshMap();
					}
				break;
			case "zoom-resize":
					this.finishZoomResize();
				break;
			case "zoom-out-click":
					this.zoomOut(e.gx, e.gy);
					this.refreshMap();
				break;
			case "simple-click":
					//onBeforeSimpleClick
					//defaultAction -> mapTip
					this.delayedSimpleClick=new Ext.util.DelayedTask(this.simpleClick, this, [e]);
					this.delayedSimpleClick.delay(200);
				break;
			case "pen":
					if(this.shape.movingVertex)
					{
						this.shape.movingVertex=false;
						this.shape.deselectVertex();
						if(this.shape.drawMode!=this.shape.DM.VML)
							this.shape.paint();
					}
				break;
		}
		
		/* Pen?
		
		if(this.movingVertex)
		{
			this.movingVertex=false;
			this.shape.deselectVertex();
			if(this.shape.drawMode!=this.shape.DM.VML)
				this.shape.paint();
		}
		*/		
	}
	
	this.onMapMouseWheelAction = function(e)
	{
		this.hideInfoDiv();
		
		e.stopEvent();
		if(this.locked)
			return;
		
		if(this.delayedMouseWheelActionFinished==null)
			this.delayedMouseWheelActionFinished=new Ext.util.DelayedTask(this.mouseWheelActionFinished, this, []);
		
		e=this.prepareEventObject(e);
	
		var amount=10;
		this.mouseWheelSum+=e.getWheelDelta();
		
		// simuliet ein zoom-resize mit der Mouseposition als Zoompunkt
		this.mouseStart.x=this.lastMouseCoord.x; // kommt im Mozilla aus e nicht zuverlÃ¤ssig raus
		this.mouseStart.y=this.lastMouseCoord.y;	
		
		this.prepareZoomResize();
		var minimalArgs={y:this.mouseStart.y-this.mouseWheelSum*amount};
		this.zoomResize(minimalArgs);
	
		if(this.simpleMapActive)
			this.delayedMouseWheelActionFinished.delay(600);
		else
			this.delayedMouseWheelActionFinished.delay(1);
	}
	
	this.mouseWheelActionFinished = function()
	{
		this.zoomResizeStartLevel = null;
		this.mouseWheelSum=0;
		this.finishZoomResize();
	}
	
	this.onMapKeyDown = function(e)
	{
		e.stopEvent();

		switch(e.getKey())
		{
			case e.ESC:
					if(!this.tileMap || (this.tileMap && this.finalAction=="zoom-rect"))
						this.cleanMouseActionData(true);
				break;
			case 16: //shiftKey
			case 17: //ctrlKey
			case 18: //altKey
					if(this.finalAction=="zoom-rect")
						this.setZoomRect(null, null, null, null, true)
				break;
		}
	}
	
	this.onMapKeyUp = function(e)
	{
		e.stopEvent();
		switch(e.getKey())
		{
			case 16: //shiftKey
			case 17: //ctrlKey
			case 18: //altKey
					if(this.finalAction=="zoom-rect")
						this.setZoomRect(null, null, null, null, false)
				break;
		}
	}
	
	// Wird verzÃ¶gert ausgefÃ¼hrt, um einen eventuellen Doppelklick abzuwarten
	this.simpleClick = function(e)
	{
		this.delayedSimpleClick=null;
		
		var m2px=(this.extent.x2-this.extent.x1)/this.mapSize.width;

		/*[deprecated]*/glbEventObject.fireEvent("onMapDefaultButtonClicked", {x:e.x, y:e.y, gx:e.gx, gy:e.gy, pageX:e.pageX, pageY:e.pageY, currentMapScale:this.currentMapScale, geo2px:m2px});
		var args={suppressSimpleClickEvent:false, x:e.x, y:e.y, gx:e.gx, gy:e.gy, currentMapScale:this.currentMapScale, geo2px:m2px};
		glbEventObject.fireEvent("onMapCustomSimpleClick", args);
		if(!(args.suppressSimpleClickEvent===true))
			glbEventObject.fireEvent("onMapSimpleClick", {x:e.x, y:e.y, gx:e.gx, gy:e.gy, pageX:e.pageX, pageY:e.pageY, currentMapScale:this.currentMapScale, geo2px:m2px});
	}
	
	this.onNaviSliderMove = function(ratio)
	{
		if(this.ignoreNextSliderMove)
		{
			this.ignoreNextSliderMove=false;
			return;
		}
		if(!this.extent)
			return;
		ratio*=0.01;
		var m2px=(this.extent.x2-this.extent.x1)/this.mapSize.width;
		var targetScale=this.scaleLimits.min+(this.scaleLimits.max-this.scaleLimits.min)*ratio;
		
		var relativeRatio=this.currentMapScale/targetScale;
		var imageRatio=this.mapSize.width/this.mapSize.height;
		
		var width=this.mapSize.width*relativeRatio;
		var height=width/imageRatio;
		
		var left=0+((this.mapSize.width/2)-(width/2));
		var top=0+((this.mapSize.height/2)-(height/2));
		
		var img=Ext.Element.get(this.els.glbMapImg[this.curMapIdx]);
		img.setLeftTop(left, top);
		img.setSize(width, height);
	}
	
	this.onNaviSliderRelease = function(ratio)
	{
		if(!this.extent)
			return;
		ratio*=0.01;
		var m2px=(this.extent.x2-this.extent.x1)/this.mapSize.width;
		var targetScale=this.scaleLimits.min+(this.scaleLimits.max-this.scaleLimits.min)*ratio;
		
		var relativeRatio=targetScale/this.currentMapScale;
		var imageRatio=this.mapSize.width/this.mapSize.height;
		
		var width=(this.extent.x2-this.extent.x1)*relativeRatio;
		var height=width/imageRatio;

		var x1=this.extent.x1+(((this.extent.x2-this.extent.x1)/2)-(width/2));
		var y1=this.extent.y1+(((this.extent.y2-this.extent.y1)/2)-(height/2));
		var x2=x1+width;
		var y2=y1+height;
		
		this.switchMaps=true;
		this.zoomIn(x1, y1, x2, y2);
		this.refreshMap();
	}
	
	this.zoomResize = function(e)
	{
		var img=Ext.Element.get(this.els.glbMapImg[this.curMapIdx]);
		
		if(this.tileMap)
		{
			var treshold = 10; // alle n Pixel ein ZoomLevel
			var diff=this.mouseStart.y-e.y;
			if(diff>=0)
			{
				var diffLevels=Math.floor(diff/treshold);
				var f=Math.pow(2, diffLevels);
			}
			else
			{
				var diffLevels=Math.ceil(diff/treshold);
				var f=1/Math.pow(2, Math.abs(diffLevels));;
			}
			
			this.tileMap.setZoomLevel(this.zoomResizeStartLevel+diffLevels);
			
			var width=this.mapSize.width*f;
			var height=this.mapSize.height*f;
			var left=0-(width-this.mapSize.width)/2;
			var top =0-(height-this.mapSize.height)/2;

			img.setLeftTop(left, top);
			img.setSize(width, height);		
		}
		else
		{				
			var diff=this.mouseStart.y-e.y;
			diff=Math.max(diff, -1*this.mapSize.height/2+5)
			
			if(diff>0)
				diff=diff+diff*diff/50;

			
			var ratio=this.mapSize.height/this.mapSize.width;
			
			var width=this.mapSize.width+(diff*2/ratio);
			var height=this.mapSize.height+(diff*2);
			var left=0-(diff/ratio) + ((this.mapSize.width/2)-this.mouseStart.x)*(width/this.mapSize.width)-((this.mapSize.width/2)-this.mouseStart.x);
			var top=0-diff + ((this.mapSize.height/2)-this.mouseStart.y)*(height/this.mapSize.height)-((this.mapSize.height/2)-this.mouseStart.y);

			img.setLeftTop(left, top);
			img.setSize(width, height);
		}
	}
	
	this.prepareZoomResize = function()
	{
		this.overlays.hide();
		this.shape.hide();
		if(this.markerShape)
			this.markerShape.hide();
		if(this.tileMap && this.zoomResizeStartLevel==null)
			this.zoomResizeStartLevel = this.tileMap.getCurrentLevel();
	}
	
	this.finishZoomResize = function()
	{
		if(this.tileMap)
			this.zoomResizeStartLevel = null;
		
		var img=Ext.Element.get(this.els.glbMapImg[this.curMapIdx]);
		var box=img.getBox(false, true);
		
		var ratioX=this.mapSize.width/box.width;
		var ratioY=this.mapSize.height/box.height;
		
		var xMin=0-ratioX*box.x;
		var yMin=0-ratioY*box.y;
		
		var xMax=xMin+(this.mapSize.width-((box.width-this.mapSize.width)*ratioX));
		var yMax=yMin+(this.mapSize.height-((box.height-this.mapSize.height)*ratioY));
		
		var pMin=this.screenToGeo(xMin, yMin, true);
		var pMax=this.screenToGeo(xMax, yMax, true);
		
		if(this.simpleMapActive)
			this.zoomIn(pMin.x, pMin.y, pMax.x, pMax.y);
		
		this.refreshMap();
		
		this.switchMaps=true;
	}
	
	function _stopContextMenu(e)
	{
		if(!(e.ctrlKey && e.altKey))
		{
			e.stopEvent();
			return false;
		}
	}
	
	/**
		Halbiert den aktuellen KartenmaÃŸstab 
	*/
	this.zoomInSimple = function()
	{
		if(this.locked && !this.extent)
			return;
		var dx=(this.extent.x2-this.extent.x1)/4;
		var dy=(this.extent.y2-this.extent.y1)/4;
		this.zoomIn(this.extent.x1+dx, this.extent.y1+dy, this.extent.x2-dx, this.extent.y2-dy);
		this.refreshMap();
	}
	
	/**
		Verdoppelt den aktuellen KartenmaÃŸstab
	*/	
	this.zoomOutSimple = function()
	{
		if(this.locked && !this.extent)
			return;
		this.zoomOut((this.extent.x1+this.extent.x2)/2, (this.extent.y1+this.extent.y2)/2);
		this.refreshMap();
	}
	
	/**
		Informationsbubble anzeigen.<br>
		Wird eine vollstÃ¤ndige Bubble genutzt, sollte auch die SchlieÃŸen-Funktion implementiert werden 
		(x in der Ecke und Aufruf von glbMap.hideInfoDiv()). 
		@param config {object} Config-Objekt 
		@config {int} x X-Wert der Bildschirmkoordinate des ursprÃ¼nglichen Klickpunktes
		@config {int} y Y-Wert der Bildschirmkoordinate des ursprÃ¼nglichen Klickpunktes
		@config {html} content Markup mit Inhalt der Bubble oder vollstÃ¤ndige Bubble (siehe isFullBubble)
		@config {bool} [isFullBubble] Gibt an, ob content nur der Inhalt (false) oder schon eine eigenstÃ¤ndige Bubble (true) ist.
	*/
	this.showInfoDiv = function(args) //args:{x, y, content, isFullBubble} // x,y = original Klickpunkt
	{
		var x=args.x;
		var y=args.y;
		var content=args.content;
		var useEmptyTemplate=args.isFullBubble; // falls das true ist, dann kommt alles aus content, sonst nur die Treffer
		
		this.mapTipIsShown=true;
		
		var infoDiv;
		if(useEmptyTemplate)
		{
			infoDiv=document.getElementById('emptyInfoDiv');
			infoDiv.innerHTML=content;
		}
		else
		{
			infoDiv=document.getElementById('infoDiv');
			var infoDivContent=document.getElementById('infoDivContent');
			infoDivContent.innerHTML=content;
		}
		
		var ox=22;
		var oy=22;
		if(x>(this.mapSize.width/2))
			ox=(ox+infoDiv.offsetWidth)*(-1);
		if(y>(this.mapSize.height/2))
			oy=(oy+infoDiv.offsetHeight)*(-1);

		if(y+oy+infoDiv.offsetHeight>this.mapSize.height)
			oy=this.mapSize.height-infoDiv.offsetHeight-y; // sollte das oben rausragen, dann wirds spÃ¤ter wieder auf 0 getrimmt
		
		var maxX=this.mapSize.width-infoDiv.offsetWidth-1;
				
		var tempLeft=(Math.min(x+ox,maxX));
		var finalLeft=(Math.max(tempLeft,0));
		var finalTop=(Math.max(y+oy,0))
		infoDiv.style.left=finalLeft+"px";
		infoDiv.style.top=finalTop+"px";
		infoDiv.style.visibility="visible";
		
	//	if(this.mapTipShadow==null)
	//		this.mapTipShadow = new Ext.Shadow({mode:"drop"});
		
	//	this.mapTipShadow.show(infoDiv);
		
		if(this.showMapTipWithDot && this.markerShape)
		{
			this.markerShape.clearAll();
			this.markerShape.addPart("POINT", null, "red");
			this.markerShape.addScreenPoint(x, y);
			this.markerShape.addPart("ARC", null, "#FF5555");
			this.markerShape.addScreenPoint(x, y);
			this.markerShape.addScreenPoint(finalLeft+infoDiv.offsetWidth/2, finalTop+infoDiv.offsetHeight/2);
			this.markerShape.finish();
			this.markerShape.paint();
		}
	}
	
	/**
		Informationsbubble in der Karte ausblenden
	*/
	this.hideInfoDiv = function()
	{
		if(!this.mapTipIsShown)
			return;
		
	//	this.mapTipShadow.hide();
		
		this.mapTipIsShown=false;
		
		if(this.showMapTipWithDot && this.markerShape)
		{
			this.markerShape.clearAll();
			this.markerShape.paint();
		}
		var infoDiv=document.getElementById('infoDiv');
		var emptyInfoDiv=document.getElementById('emptyInfoDiv');
		if(infoDiv)
			infoDiv.style.visibility="hidden";
		if(emptyInfoDiv)
			emptyInfoDiv.style.visibility="hidden";
	}
	
	function onInfoDivMouseDown(ev)
	{
		var e=handleEvent(ev);
		if(e.btn==2)
			this.hideInfoDiv();
	}
	
	this.sessionPing = function()
	{
		var img=new Image();
		img.src=this.basePath+"webmap.aspx?COMMANDS=SESSIONPING&noCache="+((new Date()).getTime());
	}
	
	this.onMapMouseDblClick = function(e)
	{	
		e.stopEvent();
		if(this.locked)
			return; // wÃ¤hrend Karte noch lÃ¤dt
		
		if(this.lastFinalAction=="simple-click")
			if(!this.delayedSimpleClick) // --> simpleClick wurde schon ausgefÃ¼hrt, aber das DblClick kommt totzdem noch, weils langsamer eingestellt ist
				return;
			else
			{
				// den einfachen Klick nicht zulassen
				this.delayedSimpleClick.cancel();
				this.delayedSimpleClick=null;
			}
			
		e=this.prepareEventObject(e);
		
		if(this.mapToolMode=="MOUSEACTION" && this.lastFinalAction!="pen")
		{
			// zoom-in an Klickposition
			var x1=e.x-this.mapSize.width/4;
			var y1=e.y+this.mapSize.height/4;
			var x2=e.x+this.mapSize.width/4;
			var y2=e.y-this.mapSize.height/4;
			
			var pMin=this.screenToGeo(x1, y1);
			var pMax=this.screenToGeo(x2, y2);
			this.zoomIn(pMin.x, pMin.y, pMax.x, pMax.y);
			this.refreshMap();
		}
		
		if(this.lastFinalAction=="pen")
		{
			this.shape.finish();
		}
	}
	
	this.onMapMouseOut = function(e)
	{
		this.shape.suspendMouseMoveAction();
	}
	
	/**
		Ã–ffnet die Ãœbersichtskarte<br>
		(siehe auch <a href="#glbMap.closeOverviewMap">closeOverviewMap</a>)
		@param {bool} suppressAnimation Die Animation des Vorganges wird unterbunden wenn true Ã¼bergeben wird.
	*/
	this.openOverviewMap = function(suppressAnimation)
	{
		if(this.overViewMapIsOpen)
			return;
		
		if(this.overviewMap==null)
			return alert("openOverviewMap kann nicht ausgefÃ¼hrt werden, da keine Ãœbersichtskarte eingerichtet ist.");
		
		this.overViewMapIsOpen=true;
		
		var anim=suppressAnimation ? false : {duration:0.35, easing:"easeIn", callback:_finalStep };
		
		//function() {(function() {Ext.get("ovmCloseIcon").applyStyles("visibility:visible;");}).defer(50); }
		
		var s=this.overviewMap.getSize();
		Ext.get("ovmOpenIcon").applyStyles("visibility:hidden;");
		if(suppressAnimation)
		{
			Ext.get("overviewMap").setSize(s.width, s.height, false);
			_finalStep();
		}
		else
			window.setTimeout(function() { Ext.get("overviewMap").setSize(s.width, s.height, anim); }, 10);
		
		function _finalStep()
		{
			Ext.get("ovmCloseIcon").applyStyles("visibility:visible;");
			Ext.get("overviewMap").applyStyles("overflow:visible;");
			var a=Ext.get("ovmCloseIcon").dom.offsetTop;
		}
	}
	
	/**
		SchlieÃŸt die Ãœbersichtskarte<br>
		(siehe auch <a href="#glbMap.openOverviewMap">openOverviewMap</a>)
		@param {bool} suppressAnimation Die Animation des Vorganges wird unterbunden wenn true Ã¼bergeben wird.
	*/
	this.closeOverviewMap = function(suppressAnimation)
	{
		if(!this.overViewMapIsOpen)
			return;
		
		this.overViewMapIsOpen=false;
		
		var anim=suppressAnimation ? false : {duration:0.35, easing:"easeOut", callback:_finalStep };
		
		//function() { if(true || glbBrowserCaps.IE6lte) Ext.get("overviewMap").applyStyles("overflow:hidden;"); (function() {Ext.get("ovmOpenIcon").applyStyles("visibility:visible;");}).defer(50); }
		
		Ext.get("ovmCloseIcon").applyStyles("visibility:hidden;");
		if(suppressAnimation)
		{
			Ext.get("overviewMap").setSize(24, 24, false);
			_finalStep();
		}
		else
		{
			Ext.get("overviewMap").applyStyles("overflow:hidden;");
			window.setTimeout(function() { Ext.get("overviewMap").setSize(24, 24, anim); }, 10);
		}
		function _finalStep()
		{
			Ext.get("ovmOpenIcon").applyStyles("visibility:visible;");
			Ext.get("overviewMap").applyStyles("overflow:hidden;");
			var a=Ext.get("ovmOpenIcon").dom.offsetTop;
		}
	}
	
	/**
		Aktiviert die "einfache" Karte.<br>
		(siehe auch <a href="#glbMap.deactivateSimpleMap">deactivateSimpleMap</a>)
	*/
	this.activateSimpleMap = function()
	{
		if(this.simpleMapActive)
			return;
		
		this.simpleMapActive=true;
		
		this.els.glbMapDiv[0].style.visibility="visible";
		this.els.glbMapDiv[1].style.visibility="visible";
		
		this.setMapSize(this.mapSize.width, this.mapSize.height);
		this.zoomIn(this.extent.x1, this.extent.y1, this.extent.x2, this.extent.y2);
		this.refreshMap();
	}
	
	/**
		
		Deaktiviert die "einfache" Karte.<br>
		Diese Funktion sollte nur benutzt werden, wenn statt dessen Tile-Layer angezeigt werden.<br>
		(siehe auch <a href="#glbMap.activateSimpleMap">activateSimpleMap</a>)
	*/
	this.deactivateSimpleMap = function()
	{
		if(!this.simpleMapActive)
			return;
		
		this.simpleMapActive=false;
		
		this.els.glbMapDiv[0].style.visibility="hidden";
		this.els.glbMapDiv[1].style.visibility="hidden";
	}
	
	/**
		Setzt das aktive Kartenwerkzeug.<br>
		MÃ¶gliche Befehle sind: Map.FullAuto|Map.Pan|Map.ZoomIn|Map.ZoomOut|Map.SimpleClick|Map.Redline|Map.Redline_MoveVertex|Map.Redline_AddVertex|Map.Redline_DropVertex
		@param {string|object} args der Befehl als Zeichenkette oder ein config-Objekt
		@config command {string} der Befehl
		@config [cursor] {string} CSS Cursor Angabe zur Darstellung des Mauspfeils Ã¼ber der Karte.
		@config [toggleMode] {bool} true zum automatischen "Abschalten" des Werkzeuges, falls dieses gerade schon das aktive sein sollte.
		@config [propagateCommand] {string} Nach Setzen des Werkzeuges wird der Status aller anhÃ¤ngenden Buttons aktualisiert, indem ein Event mit dem 
											command als Argument ausgelÃ¶st wird. Ist jedoch propagateCommand gesetzt, dann wird das vorrangig
											als Argument eingesetzt. Damit kann man ein anderes Commando weitergeben lassen, als intern gesetzt wurde.
		@returns {string} Das bisher aktive command. 
	*/
	this.setTool = function(args)
	{
		if(typeof args == "string")
		{
			var command=args;
			args={};
		}
		else
			var command=(args && args.command) || "";
		
		var lastTool=this.currentTool;
		
		if(!this.disableMouseActionMode && (command=="Map.FullAuto" || command=="Map.RedlineAndFullAuto"))
			this.mapToolMode="MOUSEACTION";
		else
			this.mapToolMode="EXPLICIT";
		
		if(command!="Map.Redline_MoveVertex" && command!="Map.Redline_AddVertex" && command!="Map.Redline_DropVertex")
		{
			this.shape.stopVertexEdit();
			this.shape.paint();
		}
		var requestedTool=command;
		switch(command)
		{
			case "Map.FullAuto":
					this.currentTool=null;
					this.els.glbEventDiv.style.cursor=args.cursor || "default"; //"help";
				break;
			case "Map.Pan":
					this.els.glbEventDiv.style.cursor=args.cursor || "move";
					this.currentTool=command;
				break;
			case "Map.ZoomIn":
			case "Map.ZoomOut":
					this.els.glbEventDiv.style.cursor=args.cursor || "default";
					this.currentTool=command;
				break;
			case "Map.Redline_MoveVertex":
			case "Map.Redline_AddVertex":
			case "Map.Redline_DropVertex":
					this.shape.startVertexEdit();
					this.shape.paint();
					this.shapeEditConstraint=command.replace(/^.*?_/, "");
			case "Map.Redline":
			case "Map.RedlineAndFullAuto": // Pen+FullAuto zoom
					this.currentTool="Map.Redline";
					this.els.glbEventDiv.style.cursor=args.cursor || "default";
					
				break;
			case "Map.SimpleClick":
					this.currentTool=command;
					this.els.glbEventDiv.style.cursor=args.cursor || "help";
					// gedachte Nutzung: haupsÃ¤chlich Maptip bzw. alles, wo man was an einem
					//                   Klickpunkt in der Karte machen will
				break;	
			case "Map.ExternalMouseControl":
					this.currentTool=command;
					this.els.glbEventDiv.style.cursor=args.cursor || "default";
					// es werden nur Events Ã¼ber alle MouseEreignisse nach auÃŸen gegeben
					// gedachte Nutzung: spezielle Anwendungen (vor allem  Editierzeugs)
				break;
			default:
					return	alert(ml(this, "ERROR_SetTool", "Unbekanntes Kommando '{0}' in Funktion glbMap.setTool", [command]));
				break;
		}
		
		if(!this.disableMouseActionMode && lastTool==requestedTool && args.toggleMode)
		{
			this.currentTool=null;
			this.mapToolMode="MOUSEACTION";
			this.els.glbEventDiv.style.cursor="default"; //"help";
			if(this.shape.drawVertices)
			{
				this.shape.stopVertexEdit();
				this.shape.paint();
			}
		}	
		else
			ManagedButton.selectButton(args.propagateCommand || command);
		
		return lastTool;
	}
	
	/**
		Gibt den momentan aktive Modus zurÃ¼ck, der zur Bestimmung der letztlich tatsÃ¤chlich ausgefÃ¼hrten
		Kartenaktion benutzt wird (MOUSEACTION - automatisch anhand der Mausaktion des Nutzers, 
		EXPLICIT - das gesetzte Kartenwerkzeug wird benutzt).
		@returns {string} MOUSEACTION|EXPLICIT
	*/
	this.getMapToolMode = function()
	{
		if(this.currentTool==null)
			return "MOUSEACTION";
		else
			return "EXPLICIT";
	}
	
	/**
		Gibt das momentan aktive Kartenwerkzeug zurÃ¼ck.
		@returns {string} das momentan aktive Kartenwerkzeug
	*/
	this.getCurrentMapTool = function()
	{
		if(this.currentTool=="Map.Redline" && this.mapToolMode=="MOUSEACTION")
			return "Map.RedlineAndFullAuto"
		else
			return this.currentTool;
	}
	
	/**
		Eventhandler fÃ¼r Clicks auf KnÃ¶pfe.
		Die Aktion fÃ¼hrt entweder zum Setzen eines Kartenwerkzeugs oder
		fÃ¼hrt eine Aktion direkt aus.
		@private
	*/
	this.onButtonClicked = function(args)
	{
		if(args.command.indexOf("Map.")==0)
		{		
			switch(args.command)
			{
				case "Map.DefaultExtent":
						this.zoomToDefaultExtent();
						this.refreshMap();
					break;
				case "Map.HistoryBack":
						//this.historyBack();
					break;
				case "Map.HistoryForward":
						//this.historyForward();
					break;
				case "Map.RedlineClear":
						this.shape.clearAll();
					break;
				// weitere denkbare: Map.Print	
				default:
					args.toggleMode=true;
					this.setTool(args);
			}
		}
	}

}();

/** @ignore */
function OverviewMap(config)
{
	var _fullExtent = config.fullExtent;
	var _imageSize = config.imageSize;
	var _currentExtent = null;
	var _eventDiv = Ext.get("ovmEventDiv");
	var _eventDivOffset = {top:0, left:0};
	var _zoomRect = Ext.get("ovmZoomRect");
	var _zoomRectBorder = Ext.get("ovmZoomRectBorder");
	var _extentMarker = Ext.get("ovmExtentMarker");
	var _spotMarker = Ext.get("ovmSpotMarker");
	var _currentMarker = _extentMarker;
	var _currentDimensions = {realLeft:0, realTop:0, realWidth:1, realHeight:1};
	var _flagMarkerDrag = null; // beim mousedown den Marker ziehen oder neues Rechteck machen?
	var _mouseStart = {x:0, y:0};
	var _markerStart = {x:0, y:0};

	Ext.get("ovmImage").dom.src=config.imagePath;
	var _this = this;
	
	_initialize();
	
	// Achtung: Event-Registrierungen stehen am Ende ! (weil dort erst alle Funktionen bekannt sind)
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//private Funktionen
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	function _initialize()
	{
		Ext.get("ovmContainer").applyStyles({
												width:_imageSize.width,
												height:_imageSize.height
												//unnötig: clip:"rect(0,"+_imageSize.width+","+_imageSize.height+",0)"
										   });
		//_eventDiv.fitToParent();
		_eventDiv.applyStyles({
								//backgroundColor:"red",
								//filter:"alpha(opacitiy=30)",
								width:_imageSize.width,
								height:_imageSize.height
							 });
	}
	
	function _screenToGeo(x, y)
	{
		var ret={x:0, y:0};
		if(_currentExtent)
		{
			var m2px=(_fullExtent.x2-_fullExtent.x1)/_imageSize.width;
			ret.x=_fullExtent.x1+x*m2px;
			ret.y=_fullExtent.y1+(_imageSize.height-y)*m2px;
		}
		return ret;
	}
	
	function _geoToScreen(x, y)
	{
		var ret={x:0, y:0};
		if(_currentExtent)
		{
			var m2px=(_fullExtent.x2-_fullExtent.x1)/_imageSize.width;
			ret.x=Math.round((x-_fullExtent.x1)/m2px);
			ret.y=Math.round(_imageSize.height-((y-_fullExtent.y1)/m2px));
		}
		return ret;
	}
		
	function _showZoomRect(vis)
	{
		_zoomRect.dom.style.visibility=(vis)?"visible":"hidden";
	}
	
	function _setZoomRect(CX, CY, DX, DY)
	{
		var x1 = Math.max(Math.min(CX, DX), 0);
		var x2 = Math.min(Math.max(CX, DX), _imageSize.width);
		var y1 = Math.max(Math.min(CY, DY), 0);
		var y2 = Math.min(Math.max(CY, DY), _imageSize.height);
		
		_zoomRect.dom.style.left=x1+"px";
		_zoomRect.dom.style.top=y1+"px";
		_zoomRect.dom.style.width=Math.max(4, (x2-x1))+"px";
		_zoomRect.dom.style.height=Math.max(4, (y2-y1))+"px";
		
		_zoomRectBorder.fitToParent();
	}
	
	function _prepareEventObject(/*ExtJs EventObj*/ e, recalcOffset)
	{
		var crx=0;
		var cry=0;

		var x=e.getPageX();
		var y=e.getPageY();
		var modKey=e.altKey?'ALT':(e.ctrlKey?'CTRL':(e.shiftKey?'SHIFT':''));
		var btn=e.button;
		
		if(recalcOffset)
			_recalcOffset();
	
		var imageX = x - _eventDivOffset.left - 2*_eventDiv.dom.scrollLeft + crx;
		var imageY = y - _eventDivOffset.top - 2*_eventDiv.dom.scrollTop + cry;
		
		var extendedEvent = {
								x:imageX,
								y:imageY,
								btn:btn,
								modKey:modKey
							  };
		Ext.apply(extendedEvent, e); // e ist ein Singleton und wird immer wieder benutzt, deswegen
									 // werden die e-Eigenschaften zu den zusätzlichen kopiert und nicht umgekehrt
		return extendedEvent;
	}
	
	function _recalcOffset()
	{
		_eventDivOffset={left:_eventDiv.getX(), top:_eventDiv.getY()};
	}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// öffentliche Funktionen
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	this.getSize = function()
	{
		return _imageSize;
	}
	
	/** @ignore */
	this.onExtentChangedHandler = function(mapObj)
	{
		this.setExtent(mapObj.extent);
		this.updateMarker();
		_recalcOffset();
	}
	
	/** @ignore */
	this.onMouseDownHandler = function(e)
	{
		e=_prepareEventObject(e, true);
		e.stopEvent();
		
		if(e.button!=0)
			return;
		
		this.startInteraction();

		var ml=_currentMarker.dom.offsetLeft;
		var mt=_currentMarker.dom.offsetTop;

		if (!e.ctrlKey && e.x>=ml && e.x<=ml+_currentMarker.dom.offsetWidth && e.y>=mt && e.y<=mt+_currentMarker.dom.offsetHeight)
		{
			_flagMarkerDrag = true;
			_mouseStart.x=e.x;
			_mouseStart.y=e.y;
			_markerStart.x=ml;
			_markerStart.y=mt;
		}
		else
		{
			_flagMarkerDrag = false;
			_mouseStart.x=e.x;
			_mouseStart.y=e.y;
			_markerStart.x=e.x;
			_markerStart.y=e.y;
			
			_showZoomRect(true);
			_setZoomRect(e.x, e.y, e.x, e.y);
		}
		
		if(_eventDiv.dom.setCapture)
			_eventDiv.dom.setCapture();
	}
	
	/** @ignore */
	this.onMouseMoveHandler = function(e)
	{
		e=_prepareEventObject(e);
		e.stopEvent();
		
		if(_flagMarkerDrag==true)
		{
			_currentMarker.dom.style.left=(_markerStart.x+e.x-_mouseStart.x)+"px";
			_currentMarker.dom.style.top =(_markerStart.y+e.y-_mouseStart.y)+"px"; 
		}
		else
		{
			var x=_markerStart.x+e.x-_mouseStart.x;
			var y=_markerStart.y+e.y-_mouseStart.y;
			_setZoomRect(_markerStart.x, _markerStart.y, x, y);
		}
	}
	
	/** @ignore */
	this.onMouseMoveHandlerNoButton = function(e)
	{
		e=_prepareEventObject(e);
		e.stopEvent();

		if (!e.ctrlKey && e.x>=_currentMarker.dom.offsetLeft && e.x<=_currentMarker.dom.offsetLeft+_currentMarker.dom.offsetWidth && e.y>=_currentMarker.dom.offsetTop && e.y<=_currentMarker.dom.offsetTop+_currentMarker.dom.offsetHeight)
			_eventDiv.dom.style.cursor="move";
		else
			_eventDiv.dom.style.cursor="default";
	}
	
	/** @ignore */
	this.onMouseUpHandler = function(e)
	{
		e=_prepareEventObject(e);
		e.stopEvent();
		
		if(_flagMarkerDrag)
		{
			_flagMarkerDrag=null;
			//Mittelpunkt der Box oder des Kreuzes ausrechnen
			var mx=_currentDimensions.realLeft+_currentDimensions.realWidth/2 -(_mouseStart.x-e.x);
			var my=_currentDimensions.realTop +_currentDimensions.realHeight/2-(_mouseStart.y-e.y);

			var centerPoint=_screenToGeo(mx, my);
			
			glbMap.pan(centerPoint.x, centerPoint.y);
			glbMap.refreshMap();
		}
		else
		{
			_flagMarkerDrag=null;
			_showZoomRect(false);
			
			if(Math.abs(_mouseStart.x-e.x)<2 && Math.abs(_mouseStart.y-e.y)<2)
			{
				var centerPoint=_screenToGeo(e.x, e.y);
				glbMap.pan(centerPoint.x, centerPoint.y);
				glbMap.refreshMap();
			}
			else
			{
				var x1 = Math.max(Math.min(e.x, _mouseStart.x), 0);
				var x2 = Math.min(Math.max(e.x, _mouseStart.x), _imageSize.width);
				var y1 = Math.max(Math.min(e.y, _mouseStart.y), 0);
				var y2 = Math.min(Math.max(e.y, _mouseStart.y), _imageSize.height);
				
				var p1=_screenToGeo(x1, y1);
				var p2=_screenToGeo(x2, y2);

				glbMap.zoomIn(p1.x, p2.y, p2.x, p1.y);
				glbMap.refreshMap();
			}
		}
		if(_eventDiv.dom.releaseCapture)
			_eventDiv.dom.releaseCapture();
		
		this.endInteraction();
	}
	
	/** @ignore */
	this.setExtent = function(extent)
	{
		_currentExtent = extent;
	}
			
	/** @ignore */		
	this.updateMarker = function()
	{
		// min,max bei y wird gedreht von Geo- auf Bildschirmkoordinatenursprung
		var min = _geoToScreen(_currentExtent.x1, _currentExtent.y2);
		var max = _geoToScreen(_currentExtent.x2, _currentExtent.y1);
		
		// prüfen ob zu klein und dann Kreuz nehmen	
		var useSpotMarker = ((max.x-min.x)<10 || (max.y-min.y<10)) ? true : false;
		
		_currentMarker.applyStyles("visibility:hidden");
		
		if(useSpotMarker)
		{
			_currentMarker = _spotMarker;
			_currentMarker.dom.style.left=((min.x+max.x)/2-_currentMarker.dom.offsetWidth/2)+"px";
			_currentMarker.dom.style.top =((min.y+max.y)/2-_currentMarker.dom.offsetHeight/2)+"px";
			_currentDimensions.realLeft=_currentMarker.dom.offsetLeft;
			_currentDimensions.realTop=_currentMarker.dom.offsetTop;
			_currentDimensions.realWidth=_currentMarker.dom.offsetWidth;
			_currentDimensions.realHeight=_currentMarker.dom.offsetHeight;
		}
		else
		{
			_currentMarker = _extentMarker;
			
			// die Markerbox wird in der Größe begrenzt, damit der div beim sehr starken herauszoomen
			// nicht wahnsinnig groß wird
			_currentMarker.dom.style.left=(Math.max(min.x, -300))+"px";
			_currentMarker.dom.style.top =(Math.max(min.y, -300))+"px";
	
	        var w = (Math.min(max.x-(Math.max(min.x, -300)), 1000));
	        var h = (Math.min(max.y-(Math.max(min.y, -300)), 1000));
	        w=Math.max(0,w);
	        h=Math.max(0,h);
			_currentMarker.dom.style.width =w+"px";
			_currentMarker.dom.style.height=h+"px";
			
			// die eigentlichen Pixelgrößen, zum internen Rechnen
			_currentDimensions.realLeft=min.x;
			_currentDimensions.realTop=min.y;
			_currentDimensions.realWidth=max.x-min.x;
			_currentDimensions.realHeight=max.y-min.y;
			
			Ext.Element.get("ovmExtentMarkerBorder").fitToParent();
			Ext.Element.get("ovmExtentMarkerInlay").fitToParent();			
		}
		_currentMarker.applyStyles("visibility:visible;");
	}
	
	/** Wird vom mousedown ausgelöst und macht die übrigen Handler aktiv */
	/** @ignore */
	this.startInteraction = function()
	{
		Ext.EventManager.removeListener(_eventDiv.dom, "mousedown", this.onMouseDownHandler);
		Ext.EventManager.removeListener(_eventDiv.dom, "mousemove", this.onMouseMoveHandlerNoButton);
		
		Ext.EventManager.addListener(document, "mousemove", this.onMouseMoveHandler, this, true);
		Ext.EventManager.addListener(document, "mouseup", this.onMouseUpHandler, this, true);
	}
	
	/** Unregistriert alle Mausinteraktionshandler und schaltet wieder auf mousedown */
	/** @ignore */
	this.endInteraction = function()
	{
		Ext.EventManager.addListener(_eventDiv.dom, "mousedown", this.onMouseDownHandler, this, true);
		Ext.EventManager.addListener(_eventDiv.dom, "mousemove", this.onMouseMoveHandlerNoButton, this, true);
		
		Ext.EventManager.removeListener(document, "mousemove", this.onMouseMoveHandler);
		Ext.EventManager.removeListener(document, "mouseup", this.onMouseUpHandler);
	}
	
	glbEventObject.attachEvent("onMapExtentChanged", this.onExtentChangedHandler, null, this);
	Ext.EventManager.addListener(_eventDiv.dom, "mousedown", this.onMouseDownHandler, this, true);
	Ext.EventManager.addListener(_eventDiv.dom, "mousemove", this.onMouseMoveHandlerNoButton, this, true);
}
ï»¿//<script>

// allgemeiner Hinweis: alle Koordinaten in den Membern 
//							 x,  y sind Pixel
//							gx, gy sind jeweilige Geo-Einheiten
//							tx, ty sind Kachelnummern (Tile)


function TileLayerManager(config)
{
	//statisch:
	TileLayerManager.tileSize=256;
	TileLayerManager.prefetchBuffer=0; // Tiles die um das sichtbare Bild herum noch zusÃ¤tzlich abgeholt werden sollen
	TileLayerManager.pixelGif= config.pixelWhiteImage || "res.ashx?t=123&Res=SharedResources/extJs2/images/default/s.gif";
	TileLayerManager.bestFitLevelTollerance=0.005;
	
	TileLayerManager.useImageElementAsTile=true; // Images als Kacheln gegenÃ¼ber Divs mit Background-Image vorziehen, wenn mÃ¶glich
	TileLayerManager.infoOverlay=false; // !! erzwingt implizit Divs !! - zum Testen und gucken
	TileLayerManager.displayImage=true; // zu Testzwecken das Anfordern der Bild-Url abschalten
	TileLayerManager.preventBrowserCaching=false; // global das Caching abschalten - nur zu Testzwecken
	
	
	var _prj = config.projection; // die derzeitige Projektion
	var _mapControl = config.mapControl; // Ref. auf das MapControl, in dem dieser TileManager enthalten ist
	var _container = config.tileContainerDiv; // container-Div fÃ¼r die Kacheln
	var _cs = _container.style;
	var _containerOrigin = {x:null, y:null, gx:null, gy:null}; // bezogen auf den Ursprungspunkt der Projektion
	var _containerRefPosition = {x:null, y:null}; // Referenzposition fÃ¼r Pan mit der Maus (ist die Position beim MouseDown)
	var _viewPortSize = {width:config.viewPortSize.width, height:config.viewPortSize.height}; // GrÃ¶ÃŸe der sichtbaren Karten
	var _isViewInitialized=false;
	var _extent = {min:{gx:0, gy:0}, max:{gx:1, gy:1}};
	var _layers = {}; // die derzeit verwalteten Tile Layer
	var _level=1; // derzeitiger ZoomLevel
	var _dt=1; // Anzahl der Kacheln im aktuellen Level
	
	var _this=this; // Referenz auf this fÃ¼r private Methoden
	
	function _init()
	{
		if(!(_prj instanceof Projection)) // falls _prj "nur" ein config Objekt ist
			_prj = new Projection(_prj);
		
		if(config.layers && config.layers.length) // Layer automatisch anlegen, falls hier schon welche Ã¼bergeben werden
			for(var i=0; i<config.layers.length; i++)
				_this.addTileLayer(config.layers[i]);		
	}
	
	function _getQuadKey(tx, ty)
	{
		var quadKey = "";
		
		// bit interleaving
		for (var level=_level; level>0; level--)
		{
			var quadrant = 0;
			var bitValue = 1 << level - 1; // Grundwert an der Bitposition des jeweiligen Levels
			if ((tx & bitValue)==bitValue) quadrant++;
			if ((ty & bitValue)==bitValue) quadrant+=2;
			quadKey+=quadrant.toString();
		}
		return quadKey;		
	}
		
	function _clearTiles()
	{
		for(var k in _layers)
			_layers[k].removeAllTiles();
	}
	
	this.getCurrentLevel = function()
	{
		return _level;
	}
	
	this.setRefPosition = function()
	{
		_containerRefPosition={
								offsetLeft:_container.offsetLeft,
								offsetTop:_container.offsetTop,
								originX:_containerOrigin.x,
								originY:_containerOrigin.y
							  }
	}
	
	this.setOffsetToRefPosition = function(dx, dy)
	{
		_cs.left=(_containerRefPosition.offsetLeft+dx)+"px";
		_cs.top =(_containerRefPosition.offsetTop+dy)+"px";
				
		this.checkTiles();
	}
	
	this.addTileLayer = function(config)
	{
		var id = typeof config.id != "undefined" ? config.id : ((new Date()).getTime())+"_"+Math.random();

		if(_layers[id]) // bei gleicher ID erst mal aufrÃ¤umen
		{
			_layers[id].removeAllTiles();
			delete _layers[id];
		}
		
		if(config.zIndex == undefined)
		{
			var maxZIndex=-1;
			for(var k in _layers)
				maxZIndex=_layers[k].zIndex;
			config.zIndex=maxZIndex+1;
		}
		
		config.enclosingTileManager=this;
		
		_layers[id]=new TileLayer(config);
	}
	
	this.setZoomLevel = function(level)
	{
		if(level!=_level)
			this.setViewPosition(null, level);
	}	
	
	this.zoomIn = function(steps)
	{
		steps=steps || 1;
		this.setZoomLevel(_level+steps);
	}
	
	this.zoomOut = function(steps)
	{
		steps=steps || 1;
		this.setZoomLevel(_level-steps);
	}
	
	this.calculateBestFit = function(origExtent, scaleLimits) // extent:{min:{gx,gy}, max:{gx,gy}}, apply
	{
		//returns: {extent:null, centerPoint:null, level:null};
		
		// den extent tief kopieren, sonst verpfuscht man das Original
		var extent={min:{gx:origExtent.min.gx, gy:origExtent.min.gy},
					max:{gx:origExtent.max.gx, gy:origExtent.max.gy}};
		
		var viewPortRatio=_viewPortSize.width/_viewPortSize.height;
		var dgx=extent.max.gx-extent.min.gx;
		var dgy=extent.max.gy-extent.min.gy;
		var extentRatio=dgx/dgy;
		
		if(viewPortRatio>extentRatio) // in x-Richtung anstÃ¼ckeln
		{
			extent.min.gx-=(dgy*viewPortRatio-dgx)/2;
			extent.max.gx+=(dgy*viewPortRatio-dgx)/2;
			dgx=extent.max.gx-extent.min.gx; //dxg noch mal aktualisieren
		}
		else // in y-Richtung anstÃ¼ckeln
		{
			extent.min.gy-=(dgx/viewPortRatio-dgy)/2;
			extent.max.gy+=(dgx/viewPortRatio-dgy)/2;
		}
		
		//TODO: konfigurierbar machen
		var bestFitFuzzynes=0.475; //legt fest, wie groÃŸzÃ¼gig damit umgegangen wird, wenn der angeforderte Extent aufgrund der fixen Level schon leicht Ã¼ber die KartenrÃ¤nder hinausragt
		
		//Wie viele Kacheln bekommt man in der angepeilten GrÃ¶ÃŸe aufs Gesamtgebiet
		var numTiles=(dgx+bestFitFuzzynes)/(_viewPortSize.width/TileLayerManager.tileSize);
		//und der Exponent von 2 zu dieser Zahl ist der angepeilte (noch krumme) ZoomLevel
		var level=Math.log((_prj.boundary.max.gx-_prj.boundary.min.gx)/numTiles) / Math.log(2); // log n zur Basis b ist: Math.log(n)/Math.log(b)
		//ZoomLevel muss natÃ¼rlich noch auf ganze Zahl gebracht werden
		level=Math.floor(level+TileLayerManager.bestFitLevelTollerance+bestFitFuzzynes);
		
		var centerPoint={gx:(extent.min.gx+extent.max.gx)/2, gy:(extent.min.gy+extent.max.gy)/2};
		
		var geo2pxHorizontal=(_prj.boundary.max.gx-_prj.boundary.min.gx) / (Math.pow(2, level)*TileLayerManager.tileSize);
		var geo2pxVertical  =(_prj.boundary.max.gy-_prj.boundary.min.gy) / (Math.pow(2, level)*TileLayerManager.tileSize);
		
		extent={min:{gx:centerPoint.gx-(geo2pxHorizontal*_viewPortSize.width/2),
					 gy:centerPoint.gy-(geo2pxVertical  *_viewPortSize.height/2)},
				max:{gx:centerPoint.gx+(geo2pxHorizontal*_viewPortSize.width/2),
					 gy:centerPoint.gy+(geo2pxVertical  *_viewPortSize.height/2)}};
		
		var currentCenterPoint={gx:(_extent.min.gx+_extent.max.gx)/2, gy:(_extent.min.gy+_extent.max.gy)/2};
				
		var differsFromCurrentTileView=true;
		if(level==_level)
			if(Math.abs(centerPoint.gx-currentCenterPoint.gx)<geo2pxHorizontal*2 &&
			   Math.abs(centerPoint.gy-currentCenterPoint.gy)<geo2pxVertical*2)
				differsFromCurrentTileView=false;
		
		return {extent:extent,
				centerPoint:centerPoint,
				level:level,
				differsFromCurrentTileView:differsFromCurrentTileView};
	}
	
	this.setViewPosition = function(p, level) // p={gx, gy}
	{
		if(p==null)
		{
			if(_containerOrigin.gx == null) // noch vollkommen uninitialisiert? -> mitte des Gesamtbereiches nehmen
				p = {gx:(_prj.boundary.min.x + _prj.boundary.max.x)/2, gy:(_prj.boundary.min.y + _prj.boundary.max.y)/2};
			else 
			{   // aktuelle Kartenmitte beibehalten 
				p = {x:_containerOrigin.x-_container.offsetLeft+_viewPortSize.width/2, y:_containerOrigin.y-_container.offsetTop+_viewPortSize.height/2};
				_prj.pixel2Geo(p, _level, p); // fÃ¼r Mittelpunkt x,y noch gx,gy berechnen
			}		
		}
		if(level != null && !isNaN(level))
			_level = level;
		
		_dt = Math.pow(2, _level);
		
		var centerPixel=_prj.geo2Pixel(p, _level); // Center-Punkt in Pixeln relativ zum Prj-Ursprung

		// ContainerDiv zurÃ¼cksetzen
		_cs.top="0px";
		_cs.left="0px";
		
		// linke obere Ecke des ContainerDivs in px und geo relativ zum Prj-Ursprung
		_containerOrigin.x=Math.floor(centerPixel.x - _viewPortSize.width/2);
		_containerOrigin.y=Math.floor(centerPixel.y - _viewPortSize.height/2);
		_prj.pixel2Geo(_containerOrigin, level, _containerOrigin); // die Ecke noch in Geokoordinaten
		
		_isViewInitialized=true;
		
		_clearTiles();
		this.checkTiles();
	}
	
	this.checkTiles = function()
	{
		if(!_isViewInitialized)
			return;
		
		if(_level < 1)
		{
			_clearTiles()
			return;
		}
		
		var tileSize=TileLayerManager.tileSize;
		
		// min = left/top; max = right/bottom
		var minViewPort = {x:_containerOrigin.x-_container.offsetLeft, y:_containerOrigin.y-_container.offsetTop};
		var maxViewPort = {x:_containerOrigin.x-_container.offsetLeft+_viewPortSize.width, y:_containerOrigin.y-_container.offsetTop+_viewPortSize.height};
		
		var minTile = {tx:Math.floor(minViewPort.x/tileSize), ty:Math.floor(minViewPort.y/tileSize)};
		var maxTile = {tx:Math.floor(maxViewPort.x/tileSize), ty:Math.floor(maxViewPort.y/tileSize)};
		
		var b=TileLayerManager.prefetchBuffer;
		
		// vor der Aktion alle Kacheln als unbenutzt markieren
		for(var k in _layers)
			_layers[k].markAllTilesAsUnused();
		
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//dieser Block sorgt dafÃ¼r, dass x und y von der Bildmitte nach auÃŸen
		//laufen und so das zeigen, was den Nutzer auch am meisten interessiert und nicht
		//erst ein paar Randkacheln, die vielleicht zu 90% auÃŸerhalb des sichtbaren Bereiches liegen
		var temp=[];
		var indexer=[];
		var x=0, y=0;
		var midX=((maxTile.tx+b)-(minTile.tx-b));
		var midY=((maxTile.ty+b)-(minTile.ty-b));

		for(var tx=minTile.tx-b; tx<=maxTile.tx+b; tx++)
		{
			for(var ty=minTile.ty-b; ty<=maxTile.ty+b; ty++)
			{
				var sort=Math.pow(0.5-(x/midX), 2)+Math.pow(0.5-(y/midY), 2);
				var db= x+", "+y+"\n\n"
					  + (Math.pow(0.5-(x/midX), 2))+" + "+(Math.pow(0.5-(y/midY), 2))+" = "+sort+"\n\n"
					  + midX +", "+ midY
				temp.push({tx:tx, ty:ty, sortHint:sort, debug:db}); // alle Kachelpositionen sammeln
				y++;
			}
			x++; y=0;
		}
//		temp.reverse();
		temp.sort(function(a, b) { return a.sortHint > b.sortHint ? 1 : -1; });
		indexer=temp;
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		
		// die Kacheln ablaufen und prÃ¼fen
//		for(var tx=minTile.tx-b; tx<=maxTile.tx+b; tx++)
//			for(var ty=minTile.ty-b; ty<=maxTile.ty+b; ty++)
			for(var i=0; i<indexer.length; i++)
			{
				var tx=indexer[i].tx;
				var ty=indexer[i].ty;
				if(tx<0 || ty<0 || tx>=_dt || ty>=_dt) // keine Kacheln auÃŸerhalb des def. Bereiches erstellen
					continue;
				var quadKey=_getQuadKey(tx, ty);
				for(var k in _layers)
				{
					if(_layers[k].visible!==false)
					{
						_layers[k].checkAndCreateTileOnDemand({
												quadKey:quadKey,
												epsgCode:_prj.epsgCode,
												container:_container,
												tx:tx,
												ty:ty,
												left:tx*tileSize-_containerOrigin.x,
												top:ty*tileSize-_containerOrigin.y});
					}
				}
//				alert(indexer[i].debug)
			}
		
		// nach der Aktion alle unbenutzt gebliebenen Kacheln entfernen
		for(var k in _layers)
			_layers[k].removeAllUnusedTiles();
		
		_prj.pixel2Geo(minViewPort, _level, minViewPort);
		_prj.pixel2Geo(maxViewPort, _level, maxViewPort);
		
		_extent.min.gx=minViewPort.gx;
		_extent.min.gy=maxViewPort.gy;
		_extent.max.gx=maxViewPort.gx;
		_extent.max.gy=minViewPort.gy;
		
		_mapControl.setExtent(_extent.min.gx, _extent.min.gy, _extent.max.gx, _extent.max.gy);
	}
	
	this.getLayerById = function(id)
	{
		return _layers[id];
	}
		
	this.resizeViewPort = function(size) // size={width, height}
	{
		_viewPortSize.width=size.width;
		_viewPortSize.height=size.height;
		_extent.min.gx=0;
	}
	
	this.getLayerCount = function()
	{
		var c=0;
		for(var k in _layers)
			c++;
		return c;
	}
	
	_init(); // Konstruktoraufruf (am Ende !!!)
}

function TileLayer(config)
{
	var _urls = config.urls;
	var _enclosingTileManager=config.enclosingTileManager;
	var _format = config.format;
	var _timeStamp = config.timeStamp || "0";
	var _tiles = {};
	var _opacity = config.opacity || 1;
	var _chromaKey = config.chromaKey;
	var _IE6AlphaChannel = config.IE6AlphaChannel || "";
	var _chromaKey = config.chromaKey;
	var _preventBrowserCaching=config.preventBrowserCaching;
	
	this.zIndex = config.zIndex || 1;
	this.visible = config.visible===false ? false : true;
	this.minimumZoomLevel = config.minimumZoomLevel;
	this.maximumZoomLevel = config.maximumZoomLevel;
	
	var _this=this;
	
	function _getUrl(quadKey, epsgCode)
	{
		var idx=0;
		
		if(_urls.length>1)
			idx=parseInt(quadKey, 10) % _urls.length;
		
		var url = _urls[idx].replace(/%quadKey%/gi, quadKey).replace(/%epsgCode%/gi, epsgCode);
		if(typeof config.urlPostEdit == "function")
			url = config.urlPostEdit(url, _urls[idx], quadKey, epsgCode)
		
		if(url.search(/(\&|\?)$/)==-1) // endet nicht mit & oder ?
		{
			if(url.indexOf("?")==-1)
				url+="?";
			else
				url+="&";
		}
		
		if(TileLayerManager.preventBrowserCaching || _preventBrowserCaching)
			url+="noCache="+(new Date()).getTime()+"_"+Math.random()+"&";	
		else if(_timeStamp)
			url+="timeStamp="+escape(_timeStamp)+"&";
		
		if(_format)
			url+="format="+escape(_format)+"&";

		return url;
	}
	
	function _addTile(config)
	{
		// config-Objekt fÃ¼r die Kachel noch um Props aus dem Layer erweitern
		config.url=_getUrl(config.quadKey, config.epsgCode);
		config.opacity=_opacity;
		config.chromaKey=_chromaKey;
		config.zIndex=_this.zIndex;
		config.IE6AlphaChannel=_IE6AlphaChannel;
		
		_tiles[config.quadKey] = new Tile(config);
	}
	
	this.checkAndCreateTileOnDemand = function(config)
	{
		if(!_tiles[config.quadKey])
			_addTile(config);
		else
			_tiles[config.quadKey].unused=false;
	}
	
	this.markAllTilesAsUnused = function()
	{
		for(var k in _tiles)
			_tiles[k].unused=true;
	}
		
	this.removeAllTiles = function()
	{
		for(var k in _tiles)
			this.removeTile(k);
	}
	
	this.removeAllUnusedTiles = function()
	{
		for(var k in _tiles)
			if(_tiles[k].unused)
				this.removeTile(k);
	}
	
	this.removeTile = function(quadKey)
	{
		if(_tiles[quadKey])
		{
			_tiles[quadKey].dispose();
			delete _tiles[quadKey];
		}
	}
	
	this.setZIndex = function(zIndex)
	{
		this.zIndex=zIndex;
		for(var k in _tiles)
			_tiles[k].setZIndex(zIndex);
	}
	
	this.setOpacity = function(opacity)
	{
		_opacity=opacity;
		for(var k in _tiles)
			_tiles[k].setOpacity(opacity);
	}
	
	this.show = function()
	{
		if(this.visible===true)
			return;
			
		this.visible=true;
		_enclosingTileManager.checkTiles();
	}
	
	this.hide = function()
	{
		if(this.visible===false)
			return;
		
		this.visible=false;
		this.removeAllTiles();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// statisch Member von Tile
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tile.renderBatchSize=10;
Tile.renderBatchCheckInterval=50;
Tile.batchRenderIsRunning=false;
Tile.renderingQueue=[];
Tile.batchRenderTimeoutId=null;
Tile.elementsPoolDivs=[];
Tile.elementsPoolImages=[];
Tile.created=0;
Tile.reused=0;
Tile.getElementFromPool = function(container, requestImage)
{
	//document.title="c: "+Tile.created+" | r: "+Tile.reused;
	var pool=requestImage ? Tile.elementsPoolImages : Tile.elementsPoolDivs;
	
	if(pool.length)
	{
		Tile.reused++
		var el = pool.pop();
		return el;
	}
	else
	{
		Tile.created++
		if(requestImage)
		{
			var el=new Image();
			el.src=TileLayerManager.pixelGif;
		}
		else
			var el=document.createElement("div");
		
		container.appendChild(el);
		el.style.width=TileLayerManager.tileSize+"px";
		el.style.height=TileLayerManager.tileSize+"px";
		el.style.position="absolute";
		
		return el;
	}
}

Tile.returnElementToPool = function(el)
{
	el.style.left="500000px";
	el.style.filter="";
	
	if(el.tagName=="DIV")
	{
		el.style.backgroundImage="none";
		Tile.elementsPoolDivs.push(el);
	}
	else
	{
		el.src=TileLayerManager.pixelGif;
		Tile.elementsPoolImages.push(el);
	}
}

// asynchrones Batchrendering
// (wird derzeit nicht genutzt, weils sich nach den Tests erst mal nicht als
//  das gÃ¼nstigste herausgestellt hat - trotzdem behalten - man weiÃŸ ja nie ...) 
Tile.addToQueue = function (renderDelegate)
{
	Tile.renderingQueue.push(renderDelegate);
	Tile.runBatch();
}

Tile.runBatch = function()
{
	if(Tile.renderingQueue.length)
	{
		if(Tile.batchRenderTimeoutId)
		{
			window.clearTimeout(Tile.batchRenderTimeoutId);
			Tile.batchRenderTimeoutId=null
		}
		
		if(Tile.renderingQueue.length>=Tile.renderBatchSize)
		{
			Tile.renderBatch();
			return;
		}
		
		Tile.batchRenderTimeoutId=window.setTimeout(Tile.renderBatch, Tile.renderBatchCheckInterval);
	}
}

Tile.renderBatch = function()
{
	Tile.batchRenderTimeoutId=null;
	for(var i=0; i<Tile.renderBatchSize, Tile.renderingQueue.length>0; i++)
	{
		var renderDelegate=Tile.renderingQueue.shift();
		if(renderDelegate)
			renderDelegate();
	}
	Tile.runBatch();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function Tile(config)
{
	var _domEl;
	var _s;
	var _domReady=false;
	var _disposed=false;
	var _chromaKey=config.chromaKey;
	var _IE6AlphaChannel=config.IE6AlphaChannel || null;

	var _this = this;
	
	function _init()
	{
		_createDomElement();
		//_createDomRequestHandle=window.setTimeout(function() {_this.createDomElementAsync.call(_this)}, 20);
		//Tile.addToQueue(function() {_this.createDomElementAsync.call(_this)});
	}
	
	function _createDomElement()
	{
		if(_disposed)
			return;
		
		var IE6AlphaByDXFilter=false;
		var chromaKey=null;
		
		if(_chromaKey || (_IE6AlphaChannel && _IE6AlphaChannel!='png' && glbBrowserCaps.IE6lte))
			chromaKey = _chromaKey || _IE6AlphaChannel;		
		else
			IE6AlphaByDXFilter = _IE6AlphaChannel=='png' && glbBrowserCaps.IE55gte && glbBrowserCaps.IE6lte;
		
		if(TileLayerManager.useImageElementAsTile && !TileLayerManager.infoOverlay && !IE6AlphaByDXFilter)
		{
			_domEl=Tile.getElementFromPool(config.container, true);
			if(TileLayerManager.displayImage)
				window.setTimeout(function()
				{	
					_domEl.src=config.url;
				}, 1);
			_s=_domEl.style;
		}
		else
		{
			_domEl=Tile.getElementFromPool(config.container, false);
			if(TileLayerManager.infoOverlay)
				_domEl.innerHTML="<table style='color:red; width:256px; height:256px; text-align:center; vertical-align:middle; border:1px outset #FFFFFF;'><tr><td>"
								+"<b><span style='background-color:#FFFFFF;'>&nbsp;("+config.tx+", "+config.ty+")&nbsp;</span><br><span style='background-color:#FFFFFF;'>&nbsp;"+config.quadKey+"&nbsp;</span><br>"
								+"<span style='background-color:#FFFFFF;'>&nbsp;Level: "+config.quadKey.length+"&nbsp;</span></b><br>"
								+"<span style='background-color:transparent; color:black; font-size:9px;'>"+config.url+"</span>"
								+"</td></tr></table>";
			_s=_domEl.style;
			
			if(TileLayerManager.displayImage)
			{
				window.setTimeout(function()
				{
					if(IE6AlphaByDXFilter)
						_s.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + config.url + "',sizingMethod='crop')";
					else
					{
						_s.backgroundImage="url("+config.url+")";
					}
				}, 1);
			}
			_s.backgroundRepeat="no-repeat";
		}
		
		if(chromaKey)
			_s.filter="chroma(color="+chromaKey+")";
			
		_this.setPosition(config.top, config.left);
		_this.setZIndex(config.zIndex);
		_this.setOpacity(config.opacity)
		
		_domReady=true;
	}
	
	this.createDomElementAsync = function()
	{
		_createDomElement();
	}
	
	this.dispose = function()
	{
		_disposed=true;

		if(_domReady && _domEl)
			Tile.returnElementToPool(_domEl);
	}
	
	this.setPosition = function(top, left)
	{
		_s.left=left+"px";
		_s.top=top+"px";
	}

	this.setOpacity = function(opacity)
	{
		opacity=Math.min(1, Math.max(0, opacity));
		if(Ext.isIE)
		{
			_s.filter = (_s.filter || '').replace(/alpha\([^\)]*\)/gi, "") +
					   (opacity == 1 ? "" : " alpha(opacity=" + opacity * 100 + ")");
		}
		else
			_s.opacity = opacity;
	}
	
	this.setZIndex = function(zIndex)
	{
		_s.zIndex = zIndex;
	}
	
	_init(); // Konstruktoraufruf (erst zum Schluss !!!)
}

/**
	Beschreibt eine Projektion und stellt bestimmte Berechnungsfunktionen zur VerfÃ¼gung
	@constructor
	@config {object} boundary Maximale Ausdehnung dieser Projektion in Geokoordinaten {min:{gy,gx}, max:{gx,gy}}
	@config {int} [maximumZoomLevel] Maximale Zoomstufe (0 oder leer fÃ¼r unbegrenzt)  
	@config {string} [title] Bezeichnung der Projektion
*/
function Projection(config)
{
	this.epsgCode = config.epsgCode || -1;
	this.title = config.title || "unnamed projection";
	this.boundary = config.boundary;
	this.maxLevel = config.maximumZoomLevel || 1E6;
	this.minLevel = 0;	
	
	var _this = this;
	var _dgx, _dgy;
	
	
	function _init()
	{
		_dgx = _this.boundary.max.gx - _this.boundary.min.gx;
		_dgy = _this.boundary.max.gy - _this.boundary.min.gy;
	}
	
	this.pixel2Geo = function(p, level, /*applyTo*/pRet)
	{
		var dx = dy = TileLayerManager.tileSize * Math.pow(2, level);
		if(!pRet)
			pRet={gx:0, gy:0};
		
		pRet.gx = this.boundary.min.gx + (_dgx/dx) * p.x;
		pRet.gy = this.boundary.min.gy + (_dgy/dy) * (dy-p.y);
		
		return pRet;
	}
	
	this.geo2Pixel = function(p, level, /*applyTo*/pRet)
	{
		var dx = dy = TileLayerManager.tileSize * Math.pow(2, level);
		if(!pRet)
			pRet={x:0, y:0};
		
		pRet.x = Math.round((p.gx-this.boundary.min.gx) / (_dgx/dx));
		pRet.y = Math.round(dy - ((p.gy-this.boundary.min.gy) / (_dgy/dy)));
		
		return pRet;
	}
	
	/* erst mal nicht so wichtig
	this.getMetersPerPixel = function(mapCenterGeo, level)
	{
	
	}
	
	this.convertToLatLon = function(p)
	{
	}
	
	this.convertFromLatLon = function(p)
	{
	}
	
	*/
	
	_init(); // Konstruktoraufruf (erst zum Schluss !!!)
}ï»¿//<script> OverlaySupport.js

MapOverlayManager = function(config)
{
	var _container = Ext.get(config.overlayContainerDiv);
	var _mapControl = config.mapControl
	
	var _overlays={};
	var _idPrefix="_OL_";
	var _suspendPainting=false;
	
	var _count=0; //dient vor allem als sekundÃ¤res Sortiermerkmal nach dem zIndex
		
	var _this=this;
	
	this.add = function(m) //{gx, gy, html, id, group, zIndex}
	{
		if(!m.html)
			return;
		m.id = m.id || "_DEFAULT_";
		m.group=m.group || "_DEFAULT_";
		m.index=_count++;
		m.zIndex=m.zIndex && parseInt(zIndex) || 0; 
		
		_overlays[m.id]=m;
		
		if(!_suspendPainting)
			this.paint();
	}
	
	this.remove = function(id)
	{
		delete _overlays[id];
		
		if(!_suspendPainting)
			this.paint();
	}
	
	this.removeGroup = function(group)
	{
		for(var k in _overlays)
			if(_overlays[k].group==group)
				delete _overlays[k];
		
		if(!_suspendPainting)
			this.paint();
	}
	
	this.removeAll = function()
	{
		_overlays={};
		
		if(!_suspendPainting)
			this.paint();
	}
	
	this.paint = function(resetContainer)
	{
		var b=[];
		for(var k in _overlays)
		{
			var m=_overlays[k];
			var p=_mapControl.geoToScreen(m.gx, m.gy, true);
			var buffer=20; 
			
			if(p.x>buffer*-1 && p.y>buffer*-1 && p.x<_mapControl.mapSize.width+buffer && p.y<_mapControl.mapSize.width+buffer)
			{
				// /*automatische Zentrierung mit expressions*/ b.push("<div id='"+_idPrefix+m.id+"' name='"+_idPrefix+"' style='position:absolute; left:"+p.x+"px; top:"+p.y+"px; margin-left:expression(this.offsetWidth/-2); margin-top:expression(this.offsetHeight/-2);'>");
				/*top, left*/ b.push("<div id='"+_idPrefix+m.id+"' name='"+_idPrefix+"' style='position:absolute; left:"+p.x+"px; top:"+p.y+"px;'>");
				// /*margin-left, margin-top*/ b.push("<div id='"+_idPrefix+m.id+"' name='"+_idPrefix+"' style='position:absolute; margin-left:"+p.x+"px; margin-top:"+p.y+"px;'>");
					b.push(m.html);
				b.push("</div>");
			}
		}
		_container.update(b.join(""));
		//_container.dom.innerHTML=b.join("");
		//log("ResetContainer: "+resetContainer);
		if(resetContainer)
		{
			_container.dom.style.left="0px";
			_container.dom.style.top="0px";
		}
	}
	
	this.suspendPainting = function()
	{
		_suspendPainting=true;
	}
	
	this.resumePainting = function()
	{
		_suspendPainting=false;
		this.paint();
	}
	
	this.show = function()
	{
		_container.dom.style.visibility="visible";
	}
	
	this.hide = function()
	{
		_container.dom.style.visibility="hidden";
	}
		
	function _init()
	{
		//glbEventObject.attachEvent("onMapExtentChanged", _this.paint, null, _this);
	}
	
	_init();
}

//<script>
// depends on glbEventObject

// Die Button-Instanzen kümmern sich mehr oder weniger nur um ihr Aussehen.
// Das Management findet in den statischen Methoden der ManagedButton Klasse (praktisch übergeordnet) statt.

function ManagedButton(args)
{
	if(!ManagedButton.staticMembersRegistered)
	{
		// Static Properties
		ManagedButton.staticMembersRegistered=true;
		ManagedButton.allButtons=new Array();
		ManagedButton.mapToolChangedHandle=null;
		ManagedButton.suffixDisabled="disabled";
		ManagedButton.suffixSelected="active";
		// Static Methods
		ManagedButton.registerButton=_static_registerButton;
		ManagedButton.getButtonById=_static_getButtonById;
		ManagedButton.selectButton=_static_selectButton;
		ManagedButton.deselectButton=_static_deselectButton;
		ManagedButton.disableButton=_static_disableButton;
		ManagedButton.enableButton=_static_enableButton;
		ManagedButton.disableButtonSet=_static_disableButtonSet;
		ManagedButton.enableButtonSet=_static_enableButtonSet;
		// Events auf statische Methoden
		//   Eigentlich sind das keine Events sondern eine Möglichkeit der losen Kopplung.
		//   Wenn man weiß, dass die ManagedButton-Klasse zur Verfügung steht, sollten die statischen
		//   Methoden direkt aufgerufen werden
		glbEventObject.attachEvent("doSelectButton", ManagedButton.selectButton);
		glbEventObject.attachEvent("doDeselectButton", ManagedButton.deselectButton);
		glbEventObject.attachEvent("doDisableButton", ManagedButton.disableButton);
		glbEventObject.attachEvent("doEnableButton", ManagedButton.enableButton);
	}
	
	if(args.initStaticOnly)
		return;
	
	// Properties
	this.clientId=args.clientId;
	this.command=args.command;
	this.buttonGroup=args.buttonGroup;
	this.buttonSet=args.buttonSet;
	this.selected=args.selected;
	this.disabled=args.disabled;
	this.type=args.type;
	
	// Methoden
	this.initialize=_initialize;
	this.pressButton=_pressButton;
	this.releaseButton=_releaseButton;
	this.disableButton=_disableButton;
	this.enableButton=_enableButton;
	this.getDummy=_getDummy;
	
	// Konstruktoraufruf
	this.initialize();
	
	function _initialize()
	{
		ManagedButton.registerButton(this);
		
		// HTML-Events einrichten
		var a=document.getElementById("BUTTON_"+this.clientId);
		a.onmousedown=_e_onMouseDown;
		
		if(this.type=="Click")
		{
			a.onmouseup=_e_onMouseUp;
			a.onmouseout=_e_onMouseOut;	
		}
	}
	
	function _getDummy()
	{
		return "?"+((new Date()).getTime());
	}
	
	function _pressButton()
	{
		if(this.selected)
			return;
		if(this.disabled)
			return;
		var img=document.getElementById("BUTTON_IMG_"+this.clientId);
		var reg=/^(.*)(\.(?:gif|png|jpg))(.*)$/i;
		img.src=img.src.replace(reg, "$1_" + ManagedButton.suffixSelected + "$2"+"$3"); //+this.getDummy();
		this.selected=true;
	}
	
	function _releaseButton()
	{
		var img=document.getElementById("BUTTON_IMG_"+this.clientId);
		img.src=img.src.replace("_"+ManagedButton.suffixSelected+".", "."); //+this.getDummy();
		this.selected=false;
	}
	
	function _disableButton()
	{
		if(this.disabled)
			return;
		if(this.selected)
			this.releaseButton();
		var btn=document.getElementById("BUTTON_"+this.clientId);
		btn.style.cursor="default";
		var img=document.getElementById("BUTTON_IMG_"+this.clientId);
		var reg=/^(.*)(\.(?:gif|png|jpg))(.*)$/i;
		img.src=img.src.replace(reg, "$1_" + ManagedButton.suffixDisabled + "$2"+"$3"); //+this.getDummy();
		this.disabled=true;
	}
	
	function _enableButton()
	{
		var btn=document.getElementById("BUTTON_"+this.clientId);
		btn.style.cursor="pointer";
		var img=document.getElementById("BUTTON_IMG_"+this.clientId);
		img.src=img.src.replace("_"+ManagedButton.suffixDisabled+".", "."); //+this.getDummy();
		this.disabled=false;
	}
	
	// EventMapper --------------------------------------------------------------------
	function _e_onMouseDown(e)
	{
		if(typeof event == 'object')
		{
			event.returnValue=false;
			event.cancelBubble=true;
		}
		
		if(!e)
			e=event;
		if(e.button==2)
			return false;
		
		var btn=ManagedButton.getButtonById(this.firstChild.getAttribute("ClientId"));
		if(this.firstChild.getAttribute('Type')=='Switch')
		{
			if(!btn.disabled)
			{
				ManagedButton.deselectButton(this.firstChild.getAttribute('Command')); // ob der Knopf wieder angeschalten wird entscheidet die Anwendung später
				var eventArgs={command:this.firstChild.getAttribute('Command'), forwardedCommand:null, srcButton:btn};
				if(glbEventObject.fireEvent('onBeforeButtonClick', eventArgs))
					glbEventObject.fireEvent('onButtonClicked', eventArgs);
			}
		}
		else
		{
			if(!btn.disabled)
			{
				btn.pressButton();
			}
		}
		return false;
	}
	
	function _e_onMouseUp(e)
	{
		if(!e)
			e=event;
		if(e.button==2)
			return false;
			
		var btn=ManagedButton.getButtonById(this.firstChild.getAttribute("ClientId"));
		if(!btn.disabled)
		{
			btn.releaseButton();
			var eventArgs={command:this.firstChild.getAttribute('Command'), forwardedCommand:null, srcButton:btn};
			if(glbEventObject.fireEvent('onBeforeButtonClick', eventArgs))
				glbEventObject.fireEvent('onButtonClicked', eventArgs);
		}
	}
	
	function _e_onMouseOut(e)
	{
		if(!e)
			e=event;
		if(e.button==2)
			return false;
		
		var btn=ManagedButton.getButtonById(this.firstChild.getAttribute("ClientId"));
		if(!btn.disabled)
		{
			btn.releaseButton();
		}
	}
	// Static Methods
	function _static_registerButton(button)
	{
		ManagedButton.allButtons[ManagedButton.allButtons.length]=button;
	}
	
	function _static_getButtonById(clientId)
	{
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
			if(buttons[i].clientId==clientId)
				return buttons[i];
		return null;
	}
	
	function _static_selectButton(args)
	{
		if(typeof args == "string")
			var command=args;
		else
			var command=(args && args.command) || "";
		
		// 1. Die Gruppen der Knöpfe ermitteln, die gedrückt werden sollen und die betreffenden anschalten
		// 2. Alle übrigen darin befindlichen Buttons ausschalten.
		// --> erlaubt mehrere Knöpfe mit der gleichen Aktion (und dazu noch in verschieden Gruppen, obwohl das praktisch nicht sinnvoll scheint)
		var buttonGroups=new Object();
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
		{
			if(buttons[i].command==command)
			{
				buttonGroups[buttons[i].buttonGroup]=true;
				if(!buttons[i].selected)
					buttons[i].pressButton();
			}
			else if(command=="uncheck-all")
			{
				buttonGroups[buttons[i].buttonGroup]=true;
			}
		}
		
		for(var group in buttonGroups)
			for(var i=0; i<buttons.length; i++)
				if(buttons[i].buttonGroup==group && buttons[i].selected && buttons[i].command!=command)
					buttons[i].releaseButton();
	}
	
	function _static_deselectButton(args)
	{
		if(typeof args == "string")
			var command=args;
		else
			var command=(args && args.command) || "";
			
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
		{
			if(buttons[i].command==command)
			{
				if(buttons[i].selected)
					buttons[i].releaseButton();
			}
		}	
	}
	
	function _static_deselectButtonGroup(buttonGroupName)
	{
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
		{
			if(buttons[i].buttonGroup==buttonGroupName)
			{
				if(buttons[i].selected)
				{
					lastSelectedCommand=buttons[i].command;
					buttons[i].releaseButton();
				}
			}
		}
		
		return lastSelectedCommand;
	}
	
	function _static_disableButton(args)
	{
		if(typeof args == "string")
			var command=args;
		else
			var command=(args && args.command) || "";
		
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
			if(buttons[i].command==command && buttons[i].disabled==false)
				buttons[i].disableButton();
	}
	
	function _static_enableButton(args)
	{
		if(typeof args == "string")
			var command=args;
		else
			var command=(args && args.command) || "";
		
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
			if(buttons[i].command==command && buttons[i].disabled==true)
				buttons[i].enableButton();
	}
	
	function _static_disableButtonSet(setName)
	{
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
			if(buttons[i].buttonSet==setName)
				buttons[i].disableButton();
	}
	
	function _static_enableButtonSet(setName)
	{
		var buttons=ManagedButton.allButtons;
		for(var i=0; i<buttons.length; i++)
			if(buttons[i].buttonSet==setName)
				buttons[i].enableButton();
	}
}

new ManagedButton({initStaticOnly:true}); //erstellt und verwirft ein ButtonObjekt, damit die statischen Member initialisiert werdenï»¿//Shape.js
//<script>

/*
//debugger;
alert("testing");
var a=null;
*/
//execScript('on error resume next : a=IsObject(CreateObject("Scripting.Dictionary"))', "VBScript")
//alert(a)


/******** Hilfsfunktionen ************************************************/
	function Point(x,y)
	{
		this.x=x;this.y=y;
		return this;
	}
	function screen2Geo(bWidth,bHoehe,x,y,bnd)
	{
		return new Point((bnd.w()/bWidth)*x+bnd.x1,(bnd.h()/bHoehe)*(bHoehe-y)+bnd.y1);
	}
	function geo2Screen(bWidth,bHoehe,x,y,bnd)
	{
		return new Point(Math.round((x-bnd.x1)/(bnd.w()/bWidth)),Math.round(bHoehe-((y-bnd.y1)/((bnd.h()/bHoehe)))));
	}

/******************** ShapeObjekt - Digitalisierung **********************/
 /* globale Variablen sWidth, sHeight (Screen) und 
	bound-Objekt erforderlich !!*/
	
	/**	@ignore	*/
	function /*abstract*/ BaseGeom()
	{
		this.pArr=new Array(); // Speicherung in geografischen Koordinaten
		this.shapeObject=null; // hier wird die Ref. auf das Managerobjekt eingetragen		
		/*virtual*/ this.type=null;
		this.isGeom=true;		// ist was zum Selektieren
		this.isAnno=false;		// ist nur zum Anzeigen
		this.glbId=null;		// wird vom ShapeObj gesetzt
		
		this.closed=false;
		this.highlighted=false;
		this.calculatedLength=null; // zum cachen
		this.calculatedArea=null; 	// zum cachen
		this.color=null;			// objektspezifische Farbe
		
		// Geometriehandling
		this.addGeoPoint=_B_addGeoPoint;
		this.addScreenPoint=_B_addScreenPoint;
		this.buildFromKoordArr=_B_buildFromKoordArr;
		this.checkLength=_B_checkLength; // bei Geometrietypen, die nur aus begrenzter Anzahl von StÃ¼tzpunkten bestehen kÃ¶nnen
		this.validate=_B_validate; // PrÃ¼fen, ob das hinzufÃ¼gen des letzen Punktes zulÃ¤ssig war und evtl. wieder entfernen
		this.getGeoPoint=_B_getGeoPoint;
		this.getScreenPoint=_B_getScreenPoint;
		this.clear=_B_clear;
		this.clearLast=_B_clearLast;
		this.mouseMoveAction=function(){};
		this.getInputForm=_B_getInputForm;
		this.processKeyInputVals=_B_processKeyInputVals;
		/*virtual*/ this.finish=null; //abschlieÃŸen und validieren
		
		// Informationen
		this.getVML=_B_getVML;
		this.getGDIPoints=_B_getGDIPoints;
		this.drawJsGraph=_B_drawJsGraph;
		this.getGeomString=_B_getGeomString;
		this.getGeomInfo=_B_getGeomInfo; // LÃ¤ng, FlÃ¤che, ...
		this.calcLength=_B_calcLength;
		this.getExtent=_B_getExtent;
		/*virtual*/ this.calcArea=null; // zu speziell
		
		// Hilfsfunktionen
		this.checkNotCrossing=_B_checkNotCrossing;
		this.getM=_B_getM;
		this.getN=_B_getN;
		this.formatUnit=_B_formatUnit;
		
		// fÃ¼r Edit Stuff
		this.validateMinMaxVertexCount=_B_validateMinMaxVertexCount;
		
		/******************************************************************************/
		function _B_validateMinMaxVertexCount()
		{
			return true;
		}
		
		function _B_getExtent()
		{
			var e={x0:null, y0:null, x1:null, y1:null};
			for(var i=0; i<this.pArr.length; i++)
			{
				e.x0=e.x0!=null ? Math.min(e.x0,this.pArr[i].x) : this.pArr[i].x;
				e.y0=e.y0!=null ? Math.min(e.y0,this.pArr[i].y) : this.pArr[i].y;
				e.x1=e.x1!=null ? Math.max(e.x1,this.pArr[i].x) : this.pArr[i].x;
				e.y1=e.y1!=null ? Math.max(e.y1,this.pArr[i].y) : this.pArr[i].y;
			}
			return e.x0!=null ? e : null;
		}		
		
		function _B_getGDIPoints(/*byRef*/ tempLn, color, vertexColor)
		{
			color=this.closed && this.color || color || 'red';
			if (this.pArr.length>1) // Linie malen
			{
				var points=new Array();
				for(var i=0; i<this.pArr.length; i++)
				{
					var p=this.getScreenPoint(i);
					points[points.length]=p.x;
					points[points.length]=p.y;
				}
				tempLn[tempLn.length]={
										geom:"iARC:"+points.join("|"),
										color:color
									  };
			}
			else
			{
				var p=this.getScreenPoint(0)
				tempLn[tempLn.length]={
										geom:"iPoint:"+p.x+"|"+p.y,
										color:color
									  }
			}
		}
				
		function _B_getVML(/*byRef*/ tempLn, /*byRef*/tempScripts, color, vertexColor)
		{
			var hasVertexColor=vertexColor?true:false; // bessere Performance als bei String
			color= this.closed && this.color || color || 'red';
			
			if (this.pArr.length>1) // Linie malen
			{			
				var cr=0; //Korrektur fÃ¼r Ãœbereinstimmung von Klickpunkt und Vertex
				var p1=null;
				var p2=null;
				for (i=1; i<this.pArr.length; i++)
				{
					var p1=p2 || this.getScreenPoint(i-1);
					var p2=this.getScreenPoint(i);
					
					var gp1=gp2 || this.pArr[i-1];
					var gp2=this.pArr[i];
				/*	// -->AuÃŸerhalb liegende Linien nicht zeichnen (bringt keine groÃŸen Punkte)
					if( this.type=="POLY" &&
						(gp1.x<bound.x1 || gp1.x>bound.x2 || gp1.y<bound.y1 || gp1.y>bound.y2) &&
						(gp2.x<bound.x1 || gp2.x>bound.x2 || gp2.y<bound.y1 || gp2.y>bound.y2)
					  )
						continue;
				*/		
					var id="";
					if(hasVertexColor)
					{
						if(this.pArr[i-1].isSelected)
							id="id='LINE_BEFORE_SELVERTEX'";
						if(this.pArr[i].isSelected)
							id="id='LINE_AFTER_SELVERTEX'";
					}
					tempLn[tempLn.length]='<v:line '+id+' strokecolor="'+color+'" strokeweight="2px" from="'+(p1.x+cr)+' '+(p1.y+cr)+'" to="'+(p2.x+cr)+' '+(p2.y+cr)+'"></v:line>';
					//tempLn[tempLn.length]='<v:line '+id+' strokecolor="'+color+'" coordorigin="0 0" strokeweight="2px" from="'+(p1.x+cr)+' '+(p1.y+cr)+'" to="'+(p2.x+cr)+' '+(p2.y+cr)+'"></v:line>';
					//tempLn[tempLn.length]='<v:shape style="position:absolute;top:1;left:1;width:500;height:500" strokecolor="'+color+'" strokeweight="2px" fillcolor="gray" path="e m 10,10 l 700,10 700,700 10,700, 10,10 m 40,40 l 200,40 200,200 40,900 40,40 e"><v:fill opacity="10%"></fill></v:shape>'
				}
			}
			else if (this.pArr.length==1 && !hasVertexColor) // nur Punkt darstellen
			{
				var cr=-3; // Korrektur wegen Punktbreite
				size=7;
				cr=(((size-1)/2)+1)*-1;
				tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(this.getScreenPoint(0).x+cr)+';top:'+(this.getScreenPoint(0).y+cr)+';width:'+size+'px;height:'+size+'px" fillcolor="'+color+'"></v:oval>';
			}
			
			// wenn vertexColor angegeben ist, dann die StÃ¼tzpunkte noch mal extra hervorheben
			if(hasVertexColor)
			{
				var p;
				var id=null;
				var count=(this.type=='POLY')?this.pArr.length-1:this.pArr.length;
				for(var i=0; i<count; i++)
				{
					p=this.getScreenPoint(i);
					var cr=-4;
					if(i==0)
					{
						var oldVertexColor=vertexColor;
						vertexColor="#99EEFF";
					}
					
					if(this.pArr[i].isSelected)
						tempLn[tempLn.length]='<v:oval id="SELVERTEX" style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="red"></v:oval>';
					else
						tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="'+vertexColor+'"></v:oval>';
					
					if(i==0)
						vertexColor=oldVertexColor;
				}
			}
		}
		
		function _B_drawJsGraph(/*byRef*/ tempScripts, color, vertexColor)
		{
			color=this.closed && this.color || color || 'red';
			var g=this.shapeObject.jsGraphics;
			var p1=null;
			var p2=null;
			g.setColor(color);
			if (this.pArr.length>1) // Linie malen
			{			
				var cr=0; //Korrektur fÃ¼r Ãœbereinstimmung von Klickpunkt und Vertex
				for (i=1; i<this.pArr.length; i++)
				{
					var p1=p2 || this.getScreenPoint(i-1);
					var p2=this.getScreenPoint(i);
					g.drawLine(p1.x+cr, p1.y+cr, p2.x+cr, p2.y+cr);
				}
			}
			else if (this.pArr.length==1) // nur Punkt darstellen
			{
				var cr=-2; // Korrektur wegen Punktbreite
				size=7;
				cr=(((size-1)/2)+1)*-1;
				g.fillEllipse(this.getScreenPoint(0).x+cr, this.getScreenPoint(0).y+cr, size, size);
			}
			
			// wenn vertexColor angegeben ist, dann die StÃ¼tzpunkte noch mal extra hervorheben
			if(vertexColor!=null)
			{
				var p;
				var id=null;
				g.setColor(vertexColor);
				for(var i=0; i<this.pArr.length; i++)
				{
					if(this.pArr[i].isSelected)
						g.setColor("red");
					p=this.getScreenPoint(i);
					g.fillRect(p.x-3, p.y-3, 7, 7, id);
					if(i==0)
					{
						var oldVertexColor=vertexColor;
						vertexColor="#99EEFF";
					}
					
					if(this.pArr[i].isSelected)
						g.setColor(vertexColor);
					
					if(i==0)
						vertexColor=oldVertexColor;
				}
			}
		}
		
		function _B_getGeomString(separator, closePoly) // einfache Punktliste mit dem Typ als PrÃ¤fix
		{
			separator=separator || '|';
			var prÃ¤fix='g'+this.type+':';
			var tempLn=new Array();
			if(this.pArr.length>0 && this.closed)
			{
				for (i=0; i<this.pArr.length; i++)
				{
					if(i==this.pArr.length-1 && this.type=='POLY' && !closePoly)
						continue;
					if(!isNaN(this.pArr[i].x) && !isNaN(this.pArr[i].y))
					{
						tempLn[tempLn.length]=Math.round(this.getGeoPoint(i).x*100000)/100000; //x
						tempLn[tempLn.length]=Math.round(this.getGeoPoint(i).y*100000)/100000; //y
					}
					else
					{
						tempLn[tempLn.length]=this.pArr[i].x; //bei Anno der Text
						tempLn[tempLn.length]=this.pArr[i].y; //bei Anno die Styles
					}
				}
				return prÃ¤fix+tempLn.join(separator);
			}
			else
				return '';
		}
		
		function _B_addGeoPoint(gx, gy)
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			this.pArr[this.pArr.length]=new Point(gx, gy);
			if(!this.validate())
			{
				this.pArr.length--;
				this.shapeObject.lastAddOperationSuccess=false;
			}
			else
				this.shapeObject.lastAddOperationSuccess=true;
			
			return this.checkLength();
		}
		
		function _B_addScreenPoint(sx, sy)
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			this.pArr[this.pArr.length]=screen2Geo(sWidth, sHeight, sx, sy, bound);
			if(!this.validate())
			{
				this.pArr.length--;
				this.shapeObject.lastAddOperationSuccess=false;
			}
			else
				this.shapeObject.lastAddOperationSuccess=true;
				
			return this.checkLength();
		}
		
		function _B_buildFromKoordArr(points)
		{
			for(var i=1; i<points.length; i+=2)
			{
				//this.addGeoPoint(parseFloat(points[i-1]), parseFloat(points[i])); // zu langsam wegen Validierung
				this.pArr[this.pArr.length]={x:parseFloat(points[i-1]), y:parseFloat(points[i])}
			}
		}
		
		function _B_getGeoPoint(i)
		{
			if (i<0 || i>=this.pArr.length) return null;
				return new Point(this.pArr[i].x, this.pArr[i].y);
		}
		
		function _B_getScreenPoint(i)
		{
			if (i<0 || i>=this.pArr.length) return null;
			return geo2Screen(sWidth, sHeight, this.pArr[i].x, this.pArr[i].y, bound);
		}
		
		function _B_checkLength()
		{
			return true; // standardmÃ¤ÃŸig keine LÃ¤ngenbegrenzung
		}
		
		function _B_validate()
		{
			return true; // standardmÃ¤ÃŸig keine EinschrÃ¤nkungen
		}
		
		function _B_clear()
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			this.pArr.length=0;
		}
		
		function _B_clearLast()
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			if(this.pArr.length>0)
				this.pArr.length--;
		}
		
		function _B_getGeomInfo(e)
		{
			return	[
						"<tr><td>Strecke:&nbsp;</td><td>"+this.formatUnit(this.calcLength(), "L")+"</td></tr>"
					]
		}
		
		function _B_calcLength()
		{
			var ret=0, i=0;
			if(!this.pArr || this.pArr.length<2)
				return 0;		
			
			for(i=1; i<this.pArr.length; i++)
				ret+=Math.sqrt( Math.pow((this.pArr[i-1].x-this.pArr[i].x),2) + Math.pow((this.pArr[i-1].y-this.pArr[i].y),2) );

			this.len=ret;
			return ret;
		}
		
		function _B_getInputForm(/*byRef*/ a)
		{
			a[a.length]="Rechtswert:<br>";
			a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT onpaste='shapeOnPaste()'><br>";
			a[a.length]="Hochwert:<br>";
			a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
			
			
		}
		
		function _B_processKeyInputVals(els)
		{
			var x=(els && els[0] && els[0].value) || "";
			var y=(els && els[1] && els[1].value) || "";
			
			if(isNaN(x) || isNaN(y) || !x || !y)
				return null;
			else	
				return new Point(parseFloat(x), parseFloat(y));
		}
		
		function _B_getM(p2, p1) //Anstieg der Geraden berechnen
		{
			var tempdx=p2.x-p1.x;
			var tempdy=p2.y-p1.y;
			if (tempdx==0) tempdx=0.001; //Verhindert eine spÃ¤tere Division durch 0 bzw. parallele Linien und ist ausreichend genau
			if (tempdy==0) tempdy=0.001; //                    - " -
			return tempdy/tempdx;	
		}

		function _B_getN(p1, m) //Schnittpunkt mit y-Achse berechnen
		{
			return p1.y - m*p1.x;
		}
		
		function _B_checkNotCrossing(specialType)
		{
			function ccw(x0, y0, x1, y1, x2, y2)
			{
				var dx1,dx2,dy1,dy2;
				dx1 = x1-x0;
				dy1 = y1-y0;
				dx2 = x2-x0;
				dy2 = y2-y0;
				if(dx1*dy2>dy1*dx2)						return +1;
				if(dx1*dy2<dy1*dx2)						return -1;
				if((dx1*dx2<0)||(dy1*dy2<0))			return -1;
				if((dx1*dx1+dy1*dy1)<(dx2*dx2+dy2*dy2))	return +1;
				return 0;

			}
			
			function intersect(pa1, pa2, pb1, pb2)
			{
				return((ccw(pa1.x,pa1.y, pa2.x,pa2.y, pb1.x,pb1.y)*
						ccw(pa1.x,pa1.y, pa2.x,pa2.y, pb2.x,pb2.y))<=0)
					&&((ccw(pb1.x,pb1.y, pb2.x,pb2.y, pa1.x,pa1.y)*
						ccw(pb1.x,pb1.y, pb2.x,pb2.y, pa2.x,pa2.y))<=0);

			}

			
			var type=specialType || this.type;
			
			if (!(	type=='POLYEND'  || 
					type=='POLY'	 ||
					type=='CALC_AREA'))
				return true; //Schneiden von Segmenten nur bei diesen Typen prÃ¼fen
						
			var i, k;          //ZÃ¤hlvariablen
			var xs, mr, nr, mt, nt, tempm;		//Schnittpunkt aus y=mr*x+nr und y=mt*x+nt
		 
			for (i=0; i<this.pArr.length-1; i++)
			{
				 mr=this.getM(this.pArr[i+1], this.pArr[i]);
				 nr=this.getN(this.pArr[i], mr); 
				 		 
				if (i<this.pArr.length-2)
				{	// Referenzlinie (r) beginnend mit der ÃœbernÃ¤chsten (k) auf Schnittpunkt testen
					for(k=i+2; k<this.pArr.length-1; k++)
					{
						 if (type=='POLYEND' && i==0 && k==this.pArr.length-2)
							continue; //Beim SchlieÃŸen vom Poly haben erster und letzter Punkt zwangslÃ¤ufig die gleiche Koordinate --> ist aber OK
					/*	
							if(intersect(this.pArr[i],this.pArr[i+1],  this.pArr[k],this.pArr[k+1]))
								;//return false;
					*/	
						 mt=this.getM(this.pArr[k+1], this.pArr[k]);
						 nt=this.getN(this.pArr[k], mt);
												 
						 tempm=mr-mt;
						 if (tempm==0)
							tempm=0.0001;  //Verhindert Division durch 0 bei parallelen Linien (ist aber ausreichend genau bei der Feststellung von Kreuzungen)
						 
						 xs=(nt-nr)/tempm;
						 // PrÃ¼fen ob Schnittpunkt im Bereich beider Linien liegt
						 if (((xs>this.pArr[i].x && xs<this.pArr[i+1].x) || (xs>this.pArr[i+1].x && xs<this.pArr[i].x)) && ((xs>this.pArr[k].x && xs<this.pArr[k+1].x) || (xs>this.pArr[k+1].x && xs<this.pArr[k].x)))
							return false;
						
					}
				}
		 /*
					 Der folgende Bereich prÃ¼ft auf Schnittpunkte zwischen einer imaginÃ¤ren
					Linie vom letzten zum ersten Punkt und dem Rest
		*/			
				 
				if(type=='CALC_AREA')
				{
					
					// jetzt noch r mit der Verbindung vom letzten zum ersten Punkt prÃ¼fen
					// - dabei jedoch die Linie von p0 zu p1 nicht mit prÃ¼fen, da schneiden nicht mÃ¶glich und gelegentlich Rundungsfehler auftreten
					if (i>0 && i<(k-1)) //k wurde beim Verlassen der Schleife noch mal erhÃ¶ht
					{
				/*		;
						//if(intersect(this.pArr[i],this.pArr[i+1],  this.pArr[k],this.pArr[0]))
						//	return false;
				*/		
						mt=this.getM(this.pArr[k], this.pArr[0]);
						nt=this.getN(this.pArr[k], mt);
					 
						tempm=mr-mt;
						if (tempm==0)
							tempm=0.0001;  //Verhindert Division durch 0 bei parallelen Linien (ist aber ausreichend genau bei der Feststellung von Kreuzungen)
												 
						xs=(nt-nr)/tempm;
					 
						if (((xs>this.pArr[i].x && xs<this.pArr[i+1].x) || (xs>this.pArr[i+1].x && xs<this.pArr[i].x)) && ((xs>this.pArr[k].x && xs<this.pArr[0].x) || (xs>this.pArr[0].x && xs<this.pArr[k].x)))
							return false;
					
					}			  
				}
		   }
		   return true;
		}
		
		function _B_formatUnit(val, type)
		{
			if(isNaN(val))
				return val;
			
			var unit='';
			if(type=="A") // FlÃ¤che
			{
				if(val>=1000000)
				{
					val=Math.round(val/1000)/1000; // drei Stellen nach dem Komma
					unit='km<sup>2</sup>'
				}
				else if(val>=10000)
				{
					val=Math.round(val/100)/100; // drei Stellen nach dem Komma
					unit='ha'
				}
				else
				{
					val=Math.round(val);
					unit='m<sup>2</sup>';
				}
				if(val==0)
				{
					val='xxx';
					unit='';
				}
				//Nachkommastellen auffÃ¼llen	
				if(res=/\.(\d*)/.exec(val))
				{
					var digits=res[1].length;
					if(digits==1)
						val+="00";
					if(digits==2)
						val+="0";
				}
			}
			else if(type=="L") // LÃ¤nge
			{
				if (val>=1000)
				{
					val=parseInt(val/10)/100;
					unit='km'
				}
				else
				{
					val=parseInt(val);
					unit='m';
				}
			}
			else
			{
				val=parseInt(val*100)/100;
			}
		
			return (new String(val)).replace(/\./, ",")+"&nbsp;"+unit;
		}
	}
	
	PointGeom.prototype=new BaseGeom();
	/**	@ignore	*/
	function PointGeom() // : BaseGeom
	{
		/*const*/ this.type="POINT";
		
		this.checkLength=_PT_checkLength;
		this.getGeomInfo=_PT_getGeomInfo;
		this.finish=_PT_finish;
		this.validateMinMaxVertexCount=_PT_validateMinMaxVertexCount;
		
		function _PT_validateMinMaxVertexCount(dir) // dir ist +1 oder -1 und gibt an ob Vertex gelÃ¶scht oder hinzugefÃ¼gt werden soll
		{
			var strivedCount=this.pArr.length+dir;
			
			if(this.closed && strivedCount<1)
				return -1;
				//return alert("Ein Punkt kann im Modus StÃ¼tzpunkte bearbeiten nicht gelÃ¶scht werden.");
				
			if(this.closed && strivedCount>1)
				return alert("Ein Punkt erlaubt im Modus StÃ¼tzpunkte hinzufÃ¼gen keine weiteren Punkte.");
		
			return 1;
		}
		
		function _PT_checkLength()
		{
			if(this.pArr.length < 1)
				return true;
			else
				return false;
		}
		
		function _PT_getGeomInfo(e)
		{
			if(this.pArr.length==1)
			{
				var p=this.getGeoPoint(0);
				return [
						"<tr><td>RW:&nbsp;</td><td>"+this.formatUnit(p.x)+"</td></tr>",
						"<tr><td>HW:&nbsp;</td><td>"+this.formatUnit(p.y)+"</td></tr>"
					   ];
			}
			else
				return ["<tr><td colspan=2>k.A.</td></tr>"]
		}
		
		function _PT_finish()
		{
			// hier kann nichts schief gehen
			if(this.pArr.length==1)
			{
				this.closed=true;
				return true;
			}
		}
	}
	
	LineGeom.prototype=new BaseGeom();
	function LineGeom() // : BaseGeom
	{
		/*const*/ this.type="ARC";
		
		this.getGeomInfo=_L_getGeomInfo;
		this.finish=_L_finish;
		this.mouseMoveAction=_L_mouseMoveAction;
		this.validateMinMaxVertexCount=_L_validateMinMaxVertexCount;
		
		function _L_validateMinMaxVertexCount(dir) // dir ist +1 oder -1 und gibt an ob Vertex gelÃ¶scht oder hinzugefÃ¼gt werden soll
		{
			var strivedCount=this.pArr.length+dir;
			
			if(this.closed && strivedCount<2)
				if(confirm("Bei geschlossenen Linien mÃ¼ssen mindestens 2 StÃ¼tzpunkte erhalten bleiben.\n\nSoll gleich die ganze Linie gelÃ¶scht werden?"))
					return -1;
				else
					return 0

			return 1;
		}
		
		function _L_getGeomInfo(e)
		{
			var diff=0;
			var showNewSeg=false;
			if(this.pArr.length>0 && !this.closed && e)
			{
				//var pM=screen2Geo(sWidth, sHeight, e.clientX, e.clientY, bound);
				var pM=screen2Geo(sWidth, sHeight, e.x, e.y, bound);
				var i=this.pArr.length;
				diff=Math.sqrt( Math.pow((this.pArr[i-1].x-pM.x),2) + Math.pow((this.pArr[i-1].y-pM.y),2) );
				if(this.pArr.length>1)
					showNewSeg=true;
			}
			
			var ret=["<tr><td>LÃ¤nge:&nbsp;</td><td>"+this.formatUnit(this.calcLength(e)+diff, "L")+"</td></tr>"];
			if(showNewSeg)
				ret[ret.length]="<tr><td style=color:red;>Neuer&nbsp;Teil:</td><td style=color:red>"+this.formatUnit(diff, "L")+"</td></tr>"
					
			return ret;
		}
		
		function _L_finish()
		{
			if(this.pArr.length<2)
			{
				alert('Eine Linie muss mindestens zwei StÃ¼tzpunkte haben!')
				return false;
			}
			else
			{
				this.closed=true;
				return true;
			}
		}
		
		function _L_mouseMoveAction(vml, e)
		{
			//var pM=new Point(e.clientX, e.clientY); // Mousepunkt
			var pM=new Point(e.x, e.y); // Mousepunkt
			
			if(this.shapeObject.drawMode==this.shapeObject.DM.VML)
			{
				var el=vml.document.getElementById("DYN_LINE");

				if(el==null)
				{
					var pE=this.getScreenPoint(this.pArr.length-1); // Endpunkt
					el=vml.document.createElement("v:line");
					el.from= pE.x +','+ pE.y;
					el.to	 = pM.x +','+ pM.y;
					el.strokecolor=this.shapeObject.unfinishedObjectColor || "fuchsia";
					el.strokeweight=2;
					el.setAttribute('id', 'DYN_LINE');
					vml.insertBefore(el);
					
					//Linestyle
					var str=vml.document.createElement("v:stroke");
					str.dashstyle="shortDot";
					el.insertBefore(str);
				}
				else
				{
					el.to	 = pM.x +','+ pM.y;
				}
			}
		}
	}	
	
	PolygonGeom.prototype=new BaseGeom();
	/**	@ignore	*/
	function PolygonGeom()
	{
		/*const*/ this.type="POLY";
		
		this.getGeomInfo=_PO_getGeomInfo;
		this.calcArea=_PO_calcArea;
		this.calcLength=_PO_calcLength;
		this.finish=_PO_finish;
		
		this.closePoly=_PO_closePoly;
		this.validate=_PO_validate;
		this.mouseMoveAction=_PO_mouseMoveAction;
		
		this.validateMinMaxVertexCount=_PO_validateMinMaxVertexCount;
		
		function _PO_validateMinMaxVertexCount(dir) // dir ist +1 oder -1 und gibt an ob Vertex gelÃ¶scht oder hinzugefÃ¼gt werden soll
		{
			var strivedCount=this.pArr.length+dir;
			
			if(this.closed && strivedCount<4) // ! bei geschlossenen Polys ist der letzte und erste SP identisch (also doppelt)
				if(confirm("Bei geschlossenen Polygonen mÃ¼ssen mindestens 3 StÃ¼tzpunkte erhalten bleiben.\n\nSoll gleich das ganze Polygon gelÃ¶scht werden?"))
					return -1;
				else
					return 0;
			
			return 1;
		}
		
		function _PO_finish(dontValidate)
		{
			if(this.pArr.length<3)
			{
				alert('Eine Polygon muss mindestens drei StÃ¼tzpunkte haben!')
				return false;
			}
			else if(!this.closePoly(dontValidate))
			{
				return false;
			}
			else
			{
				this.closed=true;
				return true;
			}
		}
		
		function _PO_closePoly(dontValidate)
		{
			// diese Fkt. wird erst aufgerufen, wenn min. 3 StÃ¼tzpunkte da sind !!
			// letzte Vertex rausschmeiÃŸen, wenn sie mit der 1. Ã¼bereinander liegt
			var temp=null
			if(this.pArr[this.pArr.length-1].x==this.pArr[0].x && this.pArr[this.pArr.length-1].y==this.pArr[0].y)
			{
				temp=this.pArr[this.pArr.length-1];
				this.pArr.length--;
			}
			
			this.pArr[this.pArr.length]=this.pArr[0]; //Screen2Geo(sWidth,sHeight,this.GetScreenPoint(0).x,this.GetScreenPoint(0).y,bound);
			if (!dontValidate && !this.checkNotCrossing('POLYEND'))
			{
				alert('Das Polygon kann nicht geschlossen werden.\n\nDie Verbindung zwischen dem letzten und dem ersten\nPunkt wÃ¼rde ein anderes Segemt schneiden.');
				this.pArr.length--;
				if(temp)
					this.pArr[this.pArr.length]=temp;
				return false;
			}
			this.calculatedArea=null;
			this.calculatedLength=null;
			return true;
		}
		
		function _PO_validate()
		{
			if(!this.checkNotCrossing())
			{
				alert("Segmente eines Polygons dÃ¼rfen sich nicht schneiden!");
				return false;
			}
			else
				return true;
		}
		
		function _PO_getGeomInfo(e)
		{
			if(this.pArr.length>0 && e)
			{
				//var pM=screen2Geo(sWidth, sHeight, e.clientX, e.clientY, bound);
				var pM=screen2Geo(sWidth, sHeight, e.x, e.y, bound);
				var i=this.pArr.length;
				var diff=Math.sqrt( Math.pow((this.pArr[i-1].x-pM.x),2) + Math.pow((this.pArr[i-1].y-pM.y),2) );
			}
					
			var ret=[
						"<tr><td>Umfang:&nbsp;</td><td>"+this.formatUnit(this.calcLength(e), "L")+"</td></tr>",
						"<tr><td>FlÃ¤che:&nbsp;</td><td>"+this.formatUnit(this.calcArea(e), "A")+"</td></tr>"
						//"<tr><td style=color:red;>bis Maus:</td><td style=color:red>"+this.formatUnit(diff, "L")+"</td></tr>"
					]
			
			if(!this.closed)
				ret[ret.length]="<tr><td style=color:red;>TeilstÃ¼ck:</td><td style=color:red>"+this.formatUnit(diff, "L")+"</td></tr>"
					
	
			return ret;
		}
		
		function _PO_calcLength(e)
		{
			if(this.pArr.length>300)
				return "{&nbsp;zu&nbsp;komplex&nbsp;}"
			
			var ret=0, i=0;
			if(!this.pArr || this.pArr.length<2)
				return 0;		
			
			for(i=1; i<this.pArr.length; i++)
				ret+=Math.sqrt( Math.pow((this.pArr[i-1].x-this.pArr[i].x),2) + Math.pow((this.pArr[i-1].y-this.pArr[i].y),2) );
			
			//TODO: bis hier kÃ¶nnte man cachen
			
			if(e && !this.closed) // Poly schlieÃŸen Ã¼ber Mauspunkt
			{
				//var pM=screen2Geo(sWidth, sHeight, e.clientX, e.clientY, bound);
				var pM=screen2Geo(sWidth, sHeight, e.x, e.y, bound);
				ret+=Math.sqrt( Math.pow((this.pArr[i-1].x-pM.x),2) + Math.pow((this.pArr[i-1].y-pM.y),2) );
				ret+=Math.sqrt( Math.pow((pM.x-this.pArr[0].x),2) + Math.pow((pM.y-this.pArr[0].y),2) );
			}
			else // direkt schlieÃŸen
			{
				ret+=Math.sqrt( Math.pow((this.pArr[i-1].x-this.pArr[0].x),2) + Math.pow((this.pArr[i-1].y-this.pArr[0].y),2) );
			}
			return ret;
		}
		
		function _PO_calcArea(e)
		{
			if(this.pArr.length>300)
				return "{&nbsp;zu&nbsp;komplex&nbsp;}"
				
			d1=new Date();
				
			var flaeche=0;
			var i=0;
			
			if(!(this.pArr.length>2 || (this.pArr.length>1 && e)))
				return 0;
			
			if(e && !this.closed) // Poly schlieÃŸen Ã¼ber Mauspunkt
			{
				//var pM=screen2Geo(sWidth, sHeight, e.clientX, e.clientY, bound);
				var pM=screen2Geo(sWidth, sHeight, e.x, e.y, bound);
				this.pArr[this.pArr.length]=pM; // temporÃ¤r den Mauspunkt einfÃ¼gen, damit der mit getestet werden kann
				if(!this.checkNotCrossing("CALC_AREA"))
				{
					this.pArr.length--; // temporÃ¤ren Punkt wieder lÃ¶schen
					return 0;
				}
				this.pArr.length--; // temporÃ¤ren Punkt wieder lÃ¶schen						
				
				__PO_sumArea.call(this); // Hilfsfunktion
				flaeche+=(this.pArr[i-1].x - pM.x)*(this.pArr[i-1].y + pM.y);
				flaeche+=(pM.x - this.pArr[0].x)*(pM.y + this.pArr[0].y);
			}
			else //direkt schlieÃŸen
			{
				if(!this.calculatedArea)
					if(!this.checkNotCrossing("CALC_AREA"))
						return 0;
				
				__PO_sumArea.call(this); // Hilfsfunktion
				flaeche+=(this.pArr[i-1].x - this.pArr[0].x)*(this.pArr[i-1].y + this.pArr[0].y);
			}
			flaeche*=0.5;
			
			d2=new Date()
			//top.window.document.title="Area: "+(d2.getTime()-d1.getTime())/1000;
			
			return Math.abs(flaeche);
				
				// kleine Arbeitsfunktion
				function __PO_sumArea()
				{
					if(this.calculatedArea)
					{
						flaeche=this.calculatedArea;
						i=this.pArr.length;
					}
					else
					{
						for (i=1; i<this.pArr.length; i++)
							flaeche+=(this.pArr[i-1].x - this.pArr[i].x)*(this.pArr[i-1].y + this.pArr[i].y);
						this.calculatedArea=flaeche;
					}
				}
		}
		
		function _PO_mouseMoveAction(vml, e)
		{
			//var pM=new Point(e.clientX, e.clientY); // Mousepunkt
			var pM=new Point(e.x, e.y); // Mousepunkt
			if(this.shapeObject.drawMode==this.shapeObject.DM.VML)
			{
				var el1=vml.document.getElementById("DYN_LINE1");
				var el2=vml.document.getElementById("DYN_LINE2");

				if(el1==null) // geht mal davon aus, dass dann auch el2==null ist
				{
					var pS=this.getScreenPoint(0); // Startpunkt
					var pE=this.getScreenPoint(this.pArr.length-1); // Endpunkt
					
					el1=vml.document.createElement("v:line");
					el1.from = parseInt(pE.x) +'px,'+ parseInt(pE.y)+'px';
					el1.to	 = parseInt(pM.x) +'px,'+ parseInt(pM.y)+'px';
					el1.strokecolor=this.shapeObject.unfinishedObjectColor || "fuchsia";
					el1.strokeweight=2;
					el1.setAttribute('id', 'DYN_LINE1');
					vml.insertBefore(el1);
					
					el2=vml.document.createElement("v:line");
					el2.from = parseInt(pS.x) +'px,'+ parseInt(pS.y)+'px';
					el2.to	 = parseInt(pM.x) +'px,'+ parseInt(pM.y)+'px';
					el2.strokecolor=this.shapeObject.unfinishedObjectColor || "fuchsia";
					el2.strokeweight=2;
					el2.setAttribute('id', 'DYN_LINE2');
					
					vml.insertBefore(el2);
			
					//Linestyle
					var str=vml.document.createElement("v:stroke");
					str.dashStyle="shortdot";
					str.strokeWeight="2";
					var str2=str.cloneNode();
					el1.insertBefore(str);
					el2.insertBefore(str2);
					
				//	top.window.status=el1.from+"  |  "+el1.to;
				}
				else
				{
					el1.to = parseInt(pM.x) +'px,'+ parseInt(pM.y)+'px';
					el2.to = parseInt(pM.x) +'px,'+ parseInt(pM.y)+'px';
					
				//	top.window.status=el1.from+"  |  "+el1.to;
				}
			}
		}
	}
	
	CircleGeom.prototype=new BaseGeom();
	/**	@ignore	*/
	function CircleGeom() // : BaseGeom
	{
		/*const*/ this.type="CIRCLE";
		
		this.calcArea=_C_calcArea;
		this.getGeomInfo=_C_getGeomInfo;
		this.buildFromKoordArr=_C_buildFromKoordArr;
		this.finish=_C_finish;
		this.checkLength=_C_checkLength;
		this.getVML=_C_getVML;
		this.getGDIPoints=_C_getGDIPoints;
		this.drawJsGraph=_C_drawJsGraph;
		this.getGeomString=_C_getGeomString;
		this.getInputForm=_C_getInputForm;
		this.processKeyInputVals=_C_processKeyInputVals;
		this.mouseMoveAction=_C_mouseMoveAction;
		this.validateMinMaxVertexCount=_C_validateMinMaxVertexCount;
		this.getExtent=_C_getExtent;
		
		function _C_validateMinMaxVertexCount(dir) // dir ist +1 oder -1 und gibt an ob Vertex gelÃ¶scht oder hinzugefÃ¼gt werden soll
		{
			var strivedCount=this.pArr.length+dir;
			if(this.closed && strivedCount<2)
				if(confirm("Einem abgeschlossenen Kreis kann kein einzelner Ankerpunkt gelÃ¶scht werden.\n\nSoll gleich der ganze Kreis gelÃ¶scht werden?"))
					return -1;
				else
					return 0;
			
			if(this.closed && strivedCount>2)
				return 0;
			
			return 1;
		}		
		
		function _C_getExtent()
		{
			var e={x0:null, y0:null, x1:null, y1:null};
			if(this.pArr.length==1)
			{
				e.x0=e.x1=pArr[0].x;
				e.y0=e.y1=pArr[0].y;
			}
			else if(this.pArr.length==2)
			{
				var pM=this.pArr[0];
				var pA=this.pArr[1];
				var rad=Math.sqrt( Math.pow((pA.x-pM.x),2) + Math.pow((pA.y-pM.y),2) );
				e.x0=this.pArr[0].x-rad;
				e.y0=this.pArr[0].y-rad;
				e.x1=this.pArr[0].x+rad;
				e.y1=this.pArr[0].y+rad;
			}
			return e.x0!=null ? e : null;	
		}
		
		function _C_buildFromKoordArr(points)
		{
			var px=parseFloat(points[0]);
			var py=parseFloat(points[1]);
			var r =parseFloat(points[2]);
			
			this.pArr[this.pArr.length]={x:px, y:py};
			this.pArr[this.pArr.length]={x:px+r, y:py};
		}
		
		function _C_calcArea()
		{
			return null;
		}
		
		function _C_getGeomInfo(e)
		{
			if(this.pArr.length>0 && !this.closed && e)
			{
				var pM=this.pArr[0];
				//var pA=screen2Geo(sWidth, sHeight, e.clientX, e.clientY, bound);
				var pA=screen2Geo(sWidth, sHeight, e.x, e.y, bound);
			}
			else if(this.pArr.length==2)
			{
				var pM=this.pArr[0];
				var pA=this.pArr[1];
			}
			else
				return ['k.A.'];
			
			var rad=Math.sqrt( Math.pow((pA.x-pM.x),2) + Math.pow((pA.y-pM.y),2) );
			var a=rad*rad*Math.PI;
			var u=rad*2*Math.PI;
			var ret=[
						"<tr><td>Radius:&nbsp;</td><td>"+this.formatUnit(rad, "L")+"</td></tr>",
						"<tr><td>Umfang:&nbsp;</td><td>"+this.formatUnit(u, "L")+"</td></tr>",
						"<tr><td>FlÃ¤che:&nbsp;</td><td>"+this.formatUnit(a, "A")+"</td></tr>"
					];
					
			return ret;
		}
		
		function _C_checkLength()
		{
			if(this.pArr.length < 2)
				return true;
			else
				return false;
		}
		
		function _C_getVML(/*byRef*/ tempLn, /*byRef*/tempScripts, color, vertexColor)
		{
			color=this.closed && this.color || color || 'red';
			var cr=0; //Korrektur fÃ¼r Ãœbereinstimmung von Klickpunkt und Vertex
			if (this.pArr.length>1) // Kreis malen
			{			
				var rad=Math.sqrt(
								Math.pow(
										  (this.getScreenPoint(0).x+cr)-(this.getScreenPoint(1).x+cr),2
										)
							   +Math.pow(
										  (this.getScreenPoint(0).y+cr)-(this.getScreenPoint(1).y+cr),2
										)
								 );
					tempLn[tempLn.length]="<v:oval style='position:absolute;top:"+((this.getScreenPoint(0).y+cr)-rad)+";left:"+((this.getScreenPoint(0).x+cr)-rad)+";width:"+(rad*2)+";height:"+(rad*2)+";' strokecolor='"+color+"' strokeweight='2px'><v:fill on='False'/></v:oval>";
			}
			else if (this.pArr.length==1) // nur Punkt darstellen
			{
				var cr=-3; // Korrektur wegen Punktbreite
				tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(this.getScreenPoint(0).x+cr)+';top:'+(this.getScreenPoint(0).y+cr)+';width:5px;height:5px" fillcolor="'+color+'"></v:oval>';
			}
			
			// wenn vertexColor angegeben ist, dann die StÃ¼tzpunkte noch mal extra hervorheben
			if(vertexColor!=null)
			{
				var p;
				var id=null;
				for(var i=0; i<this.pArr.length; i++)
				{
					p=this.getScreenPoint(i);
					var cr=-3;
					if(this.pArr[i].isSelected)
						tempLn[tempLn.length]='<v:oval id="SELVERTEX" style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="red"></v:oval>';
					else
						tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="'+vertexColor+'"></v:oval>';
				}
			}
		}
		
		function _C_getGDIPoints(/*byRef*/ tempLn, color, vertexColor)
		{
			color=color || 'red';
			var cr=0;
			if (this.pArr.length>1) // Linie malen
			{
				var p=this.getScreenPoint(0)
				var rad=Math.sqrt(
								Math.pow(
										  (this.getScreenPoint(0).x+cr)-(this.getScreenPoint(1).x+cr),2
										)
							   +Math.pow(
										  (this.getScreenPoint(0).y+cr)-(this.getScreenPoint(1).y+cr),2
										)
								 );
				
				var points=new Array();
				points[points.length]=p.x
				points[points.length]=p.y;
				points[points.length]=rad;
				
				
				tempLn[tempLn.length]={
										geom:"iCIRCLE:"+points.join("|"),
										color:color
									  }
			}
			else
			{
				var p=this.getScreenPoint(0)
				tempLn[tempLn.length]={
										geom:"iPoint:"+p.x+"|"+p.y,
										color:color
									  }
			}
		}
		
		function _C_drawJsGraph(/*byRef*/ tempScripts, color, vertexColor)
		{
			color=this.closed && this.color || color || 'red';
			var g=this.shapeObject.jsGraphics;
			g.setColor(color);
			var cr=0;
			if (this.pArr.length>1) // Linie malen
			{			
				var rad=Math.sqrt(
								Math.pow(
										  (this.getScreenPoint(0).x+cr)-(this.getScreenPoint(1).x+cr),2
										)
							   +Math.pow(
										  (this.getScreenPoint(0).y+cr)-(this.getScreenPoint(1).y+cr),2
										)
								 );
				g.drawEllipse(this.getScreenPoint(0).x-rad, this.getScreenPoint(0).y-rad, rad*2, rad*2);
			}
			else if (this.pArr.length==1) // nur Punkt darstellen
			{
				var cr=-2; // Korrektur wegen Punktbreite
				g.fillEllipse(this.getScreenPoint(0).x+cr, this.getScreenPoint(0).y+cr, 5, 5);
			}
			
			if(vertexColor!=null)
			{
				var p;
				g.setColor(vertexColor);
				for(var i=0; i<this.pArr.length; i++)
				{
					if(this.pArr[i].isSelected)
						g.setColor("red");
					p=this.getScreenPoint(i);
					g.fillRect(p.x-3, p.y-3, 7, 7);
					if(this.pArr[i].isSelected)
						g.setColor(vertexColor);
				}
			}
		}
		
		function _C_getGeomString(separator) // einfache Punktliste mit dem Typ als PrÃ¤fix
		{
			separator=separator || '|';
			var prÃ¤fix='g'+this.type+':';
			var tempLn=new Array();
			
			if(this.pArr.length>1  && this.closed)
			{	
				var rad=Math.sqrt(
								Math.pow(
										  (this.pArr[0].x)-(this.pArr[1].x),2
										)
							   +Math.pow(
										  (this.pArr[0].y)-(this.pArr[1].y),2
										)
								 );
				tempLn[tempLn.length]=this.pArr[0].x+'|'+this.pArr[0].y+'|'+rad;
				return prÃ¤fix+tempLn.join(separator);
			}
			else
				return '';
		}
		
		function _C_finish()
		{
			if(this.pArr.length<2)
			{
				alert('Sie mÃ¼ssen noch einen Radius fÃ¼r den Kreis festlegen!')
				return false;
			}
			else
			{
				this.closed=true;
				return true;
			}
		}
		
		function _C_getInputForm(/*byRef*/ a)
		{
			if(this.pArr.length==0)
			{
				a[a.length]="Rechtswert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT onpaste='shapeOnPaste()'><br>";
				a[a.length]="Hochwert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
			}
			else if(this.pArr.length==1)
			{
				a[a.length]="Radius (in Metern):<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
			}
			
		}
		
		function _C_processKeyInputVals(els)
		{
			if(els.length>=2)
			{			
				var x=(els && els[0] && els[0].value) || "";
				var y=(els && els[1] && els[1].value) || "";
			
				if(isNaN(x) || isNaN(y) || !x || !y)
					return null;
				else	
					return new Point(parseFloat(x), parseFloat(y));
			}
			else
			{
				var r=(els && els[0] && els[0].value) || "";
				if(isNaN(r) || !r)
					return null;
				else	
					return new Point(this.pArr[0].x+parseFloat(r), this.pArr[0].y);
			}
		}
		
		function _C_mouseMoveAction(vml, e)
		{
			if(this.pArr.length!=1)
				return;
			var cr=0;
			//var pM=new Point(e.clientX, e.clientY); // Mousepunkt
			var pM=new Point(e.x, e.y); // Mousepunkt
			var pS=this.getScreenPoint(0); // Startpunkt
			var rad=Math.sqrt(
					  Math.pow(
					 		   (pS.x+cr)-(pM.x+cr),2
					  		  )
					+ Math.pow(
					 		   (pS.y+cr)-(pM.y+cr),2
					 		  )
					);
			
			var el=vml.document.getElementById("DYN_CIRCLE");
			if(el==null)
			{
				el=vml.document.createElement("v:oval");
				el.style.position="absolute";
				el.style.posLeft  =pS.x+cr-rad;
				el.style.posTop   =pS.y+cr-rad;
				el.style.posWidth =rad*2;
				el.style.posHeight=rad*2
				
				el.strokecolor=this.shapeObject.unfinishedObjectColor || "fuchsia";
				el.strokeweight=2;
				el.setAttribute('id', 'DYN_CIRCLE');
				vml.insertBefore(el);
				
				//Fillstyle
				var f=vml.document.createElement("v:fill");
				f.on="false";
				el.insertBefore(f);
				//Linestyle
				var s=vml.document.createElement("v:stroke");
				s.dashStyle="shortDot";
				el.insertBefore(s);
			}
			else
			{
				el.style.position="absolute";
				el.style.posLeft  =pS.x+cr-rad;
				el.style.posTop   =pS.y+cr-rad;
				el.style.posWidth =rad*2;
				el.style.posHeight=rad*2;
			}
		}
	}
	
	AnnoGeom.prototype=new BaseGeom();
	/**	@ignore	*/
	function AnnoGeom() // : BaseGeom
	{
		/*const*/ this.type="ANNO";
		this.isGeom=false;		// ist was zum Selektieren
		this.isAnno=true;		// ist nur zum Anzeigen
		
		this.getGeomInfo=_A_getGeomInfo;
		this.buildFromKoordArr=_A_buildFromKoordArr;
		this.addScreenPoint=_A_addScreenPoint;
		this.addGeoPoint=_A_addGeoPoint;
		this.finish=_A_finish;
		this.checkLength=_A_checkLength;
		this.getVML=_A_getVML;
		this.getGDIPoints=_A_getGDIPoints
		this.drawJsGraph=_A_drawJsGraph;
		this.getInputForm=_A_getInputForm;
		this.processKeyInputVals=_A_processKeyInputVals;
		this.validateMinMaxVertexCount=_A_validateMinMaxVertexCount;
		this.getExtent=_A_getExtent;
		
		function _A_validateMinMaxVertexCount(dir) // dir ist +1 oder -1 und gibt an ob Vertex gelÃ¶scht oder hinzugefÃ¼gt werden soll
		{
			var strivedCount=this.pArr.length+dir;
			
			if(this.closed && strivedCount<2)
				return -1;
				//return alert("LÃ¶schen des Ankerpunktes nicht mÃ¶glich.");
			
			if(this.closed && strivedCount>2)
				return alert("Es ist nur ein Ankerpunkt erlaubt.");	

			return 1;
		}
		
		function _A_getExtent()
		{
			var e={x0:null, y0:null, x1:null, y1:null};
			if(this.pArr.length)
			{
				e.x0=e.x1=this.pArr[0].x;
				e.y0=e.y1=this.pArr[0].y;
			}
			return e.x0!=null ? e : null;
		}
		
		function _A_buildFromKoordArr(points)
		{
			var px=parseFloat(points[0]);
			var py=parseFloat(points[1]);
			var t =points[2];
			var s =points[3]
			
			this.pArr[this.pArr.length]={x:px, y:py};
			this.pArr[this.pArr.length]={x:t,  y:s};
		}
		
		function _A_getGeomInfo()
		{
			return ["k.a."];
		}
		
		function _A_getVML(/*byRef*/ tempLn, /*byRef*/tempScripts, color, vertexColor)
		{
			color=this.closed && this.color || color || 'red';
			
			if (this.pArr.length>1) // Text anzeigen
			{			
				var text   = this.pArr[1].x;
				var styles = this.pArr[1].y.split("Â°");
				
				var	size   = styles[0];
				var	color  = styles[1];
				var	bgColor= styles[2];
				var	bold   = styles[3]=="1"?"font-weight:bold;" :"";
				var	italic = styles[4]=="1"?"font-style:italic;":"";
				var	align  = styles[5];
				var	font   = styles[6];
				
				// Meter wieder in Pixel zurÃ¼ckrechnen
				var pw=sWidth;
				var gw=bound.x2-bound.x1;
				var m2px=pw/gw;
				size=size*m2px;
				
				var domId=this.glbId+"_"+Math.random(); // glbId kann mal bei Multiparts mehrfach die selbe sein
					
				var p0=this.getScreenPoint(0);
				
				if(align.charAt(0)=="u")
					; // vertical upper
				else if(align.charAt(0)=="c") 
					tempScripts.push("document.getElementById('ANNO_"+domId+"').style.top = ("+p0.y+"-document.getElementById('ANNO_"+domId+"').offsetHeight/2)+'px';"); // vertical center
				else
					tempScripts.push("document.getElementById('ANNO_"+domId+"').style.top = ("+p0.y+"-document.getElementById('ANNO_"+domId+"').offsetHeight)+'px';"); // vertical lower
					
									
				if(align.charAt(1)=="l")
					; // left
				else if(align.charAt(1)=="c")
					tempScripts.push("document.getElementById('ANNO_"+domId+"').style.left = ("+p0.x+"-document.getElementById('ANNO_"+domId+"').offsetWidth/2)+'px';"); //center
				else
					tempScripts.push("document.getElementById('ANNO_"+domId+"').style.left = ("+p0.x+"-document.getElementById('ANNO_"+domId+"').offsetWidth)+'px';"); //right
				
				text=text.replace(/\$/g,"&#x26;")
						 .replace(/</g,"&lt;")
						 .replace(/>/g,"&gt;")
				
				tempLn[tempLn.length]='<div id="ANNO_'+domId+'" nowrap style="position:absolute; left:'+p0.x+'px; top:'+p0.y+'px; color:'+color+'; background-color:'+bgColor+'; font-size:'+size+'px; font-family:'+font+';'+bold+italic+'">'+text+'</div>';
			
				// wenn vertexColor angegeben ist, dann die StÃ¼tzpunkte noch mal extra hervorheben
				if(vertexColor!=null)
				{
					var p;
					var id=null;
					for(var i=0; i<1; i++)
					{
						p=this.getScreenPoint(i);
						var cr=-3;
						if(this.pArr[i].isSelected)
							tempLn[tempLn.length]='<v:oval id="SELVERTEX" style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="red"></v:oval>';
						else
							tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(p.x+cr)+';top:'+(p.y+cr)+';width:7px;height:7px" fillcolor="'+vertexColor+'"></v:oval>';
					}
				}
			}
			else if (this.pArr.length==1) // nur Punkt darstellen
			{
				var cr=-3; // Korrektur wegen Punktbreite
				tempLn[tempLn.length]='<v:oval style="position:absolute;left:'+(this.getScreenPoint(0).x+cr)+';top:'+(this.getScreenPoint(0).y+cr)+';width:5px;height:5px" fillcolor="'+color+'"></v:oval>';
			}
		}
		
		function _A_getGDIPoints(/*byRef*/ tempLn, /*byRef*/ tempScripts, color, vertexColor)
		{
			color=color || 'red';
			var cr=0;
			if (this.pArr.length==1) // Punkt malen
			{
				var p=this.getScreenPoint(0)
				tempLn[tempLn.length]={
										geom:"iPoint:"+p.x+"|"+p.y,
										color:color
									  }
			}
			else // Text holen
			{
				var temp=new Array();
				this.getVML(/*byRef*/temp, /*byRef*/ tempScripts, color);
				tempLn[tempLn.length]={
										html:temp.join("")
									  };
			}
		}
		
		function _A_drawJsGraph(/*byRef*/tempScripts, color)
		{
			color=this.closed && this.color || color || 'red';
			var g=this.shapeObject.jsGraphics;
			g.setColor(color);
			
			var cr=0;
			if (this.pArr.length==1) // Punkt malen
			{
				var cr=-2; // Korrektur wegen Punktbreite
				g.fillEllipse(this.getScreenPoint(0).x+cr, this.getScreenPoint(0).y+cr, 5, 5);
			}
			else // Text holen
			{
				var temp=new Array();
				this.getVML(/*byRef*/temp,  /*byRef*/ tempScripts, color);
				g.insertAnyHtml(temp.join(""));
			}
		}
		
		function _A_checkLength()
		{
			// wenn der Ankerpunkt gesetzt wurde und nicht ohnehin schon die
			// Tastatureingabe aktiv ist --> KeyInput anzeigen
			if(this.pArr.length == 1 && !(this.shapeObject.keyInputActive && this.shapeObject.keyInputFixed))
				this.shapeObject.displayInputForm();
			
			if(this.pArr.length < 2)
				return true;
			else
				return false;
		}
		
		function _A_getInputForm(/*byRef*/ a)
		{
			if(this.pArr.length==0)
			{
				a[a.length]="Rechtswert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT  onpaste='shapeOnPaste()'><br>";
				a[a.length]="Hochwert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
			}
			else if(this.pArr.length==1)
			{
				a[a.length]="Text:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
				a[a.length]="TextgrÃ¶ÃŸe:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT>";
				a[a.length]=	"<option value=8>&nbsp;&nbsp;8 px";
				a[a.length]=	"<option value=10>10 px";
				a[a.length]=	"<option value=12>12 px";
				a[a.length]=	"<option value=14>14 px";
				a[a.length]=	"<option value=16>16 px";
				a[a.length]=	"<option value=18>18 px";
				a[a.length]=	"<option value=20 selected>20 px";
				a[a.length]=	"<option value=25>25 px";
				a[a.length]=	"<option value=30>30 px";
				a[a.length]=	"<option value=35>35 px";
				a[a.length]=	"<option value=40>40 px";
				a[a.length]=	"<option value=50>50 px";
				a[a.length]=	"<option value=75>75 px";
				a[a.length]=	"<option value=100>100 px";
				a[a.length]="</select><br>";
				a[a.length]="Textfarbe:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT>";
				a[a.length]=	"<option value=red>rot";
				a[a.length]=	"<option value=black>schwarz";
				a[a.length]=	"<option value=green>grÃ¼n";
				a[a.length]=	"<option value=blue>blau";
				a[a.length]=	"<option value=yellow>gelb";
				a[a.length]=	"<option value=purple>violett";
				a[a.length]=	"<option value=white>weiÃŸ";
				a[a.length]="</select><br>";
				a[a.length]="Hintergrundfarbe:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT>";
				a[a.length]=	"<option value=transparent>keine";
				a[a.length]=	"<option value=white selected>weiÃŸ";
				a[a.length]=	"<option value=red>rot";
				a[a.length]=	"<option value=black>schwarz";
				a[a.length]=	"<option value=green>grÃ¼n";
				a[a.length]=	"<option value=blue>blau";
				a[a.length]=	"<option value=yellow>gelb";
				a[a.length]=	"<option value=purple>violett";
				a[a.length]="</select><br>";
				a[a.length]="Schriftstil:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT><option value=normal>normal<option value=bold>fett<option value=italic>kursiv<option value=bolditalic>fettkursiv</select><br>";
				a[a.length]="Ausrichtung:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT><option value=cc>zentriert<option value=cl>linksbÃ¼ndig<option value=cr>rechtsbÃ¼ndig</select>";
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value='arial'>"; //font
			}
		}
		
		function _A_processKeyInputVals(els)
		{
			if(els.length==2) // RW, HW
			{			
				var x=(els && els[0] && els[0].value) || "";
				var y=(els && els[1] && els[1].value) || "";
			
				if(isNaN(x) || isNaN(y) || !x || !y)
					return null;
				else	
					return new Point(parseFloat(x), parseFloat(y));
			}
			else // Text + Eigenschaften
			{
				var t=(els && els[0] && els[0].value) || "";
				var s=[
						(els && els[1] && els[1].value) || "14",
						(els && els[2] && els[2].value) || "red",
						(els && els[3] && els[3].value) || "white",
						(els && els[4] && els[4].value.search(/bold/i)!=-1)?"1":"0",
						(els && els[4] && els[4].value.search(/italic/i)!=-1)?"1":"0",
						(els && els[5] && els[5].value) || "cc",
						(els && els[6] && els[6].value) || "arial",
					  ]; // style
				
				if(s[0]) // Pixelangabe auf Basis des aktuellen Bildes in Karteneinheiten umrechnen
				{
					var pw=sWidth;
					var gw=bound.x2-bound.x1;
					var m2px=pw/gw;
					
					s[0]=s[0]/m2px;
				}
								
				if(t=="")
					return null;
				else	
					return {x:t, y:s.join("Â°"), isText:true};
			}
		}
		
		function _A_addGeoPoint(gx, gy, isText)
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			
			if(isText) // die zweite Koordinate wird fÃ¼r den Text misbraucht
				this.pArr[this.pArr.length]={x:gx, y:gy};
			else
			{
				if(this.pArr.length==1) // schon eine Koordinate da
					this.pArr.length--; // dann die gesetzte wieder wegschmeiÃŸen
				
				this.pArr[this.pArr.length]=new Point(gx, gy);
			}
			
			if(!this.validate())
			{
				this.pArr.length--;
				this.shapeObject.lastAddOperationSuccess=false;
			}
			else
				this.shapeObject.lastAddOperationSuccess=true;
			
			return this.checkLength();
		}
		
		function _A_addScreenPoint(sx, sy, isText)
		{
			this.calculatedLength=null;
			this.calculatedArea=null;
			
			if(isText) // die zweite Koordinate wird fÃ¼r den Text misbraucht
				this.pArr[this.pArr.length]={x:sx, y:sy};
			else
			{
				if(this.pArr.length==1) // schon eine Koordinate da
					this.pArr.length--; // dann die gesetzte wieder wegschmeiÃŸen
				
				this.pArr[this.pArr.length]=screen2Geo(sWidth, sHeight, sx, sy, bound);
			}
			if(!this.validate())
			{
				this.pArr.length--;
				this.shapeObject.lastAddOperationSuccess=false;
			}
			else
				this.shapeObject.lastAddOperationSuccess=true;
				
			return this.checkLength();
		}
		
		function _A_finish()
		{
			if(this.pArr.length<2)
			{
				alert('Sie mÃ¼ssen noch einen Text eingeben!')
				return false;
			}
			else
			{
				this.closed=true;
				return true;
			}
		}
	}
	
	SymbolGeom.prototype=new AnnoGeom();
	/**	@ignore	*/
	function SymbolGeom() // : AnnoGeom
	{
		this.type="SYMBOL";
		
		this.getInputForm=_S_getInputForm
		
		function _S_getInputForm(/*byRef*/ a)
		{
			if(this.pArr.length==0)
			{
				a[a.length]="Rechtswert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT  onpaste='shapeOnPaste()'><br>";
				a[a.length]="Hochwert:<br>";
				a[a.length]="<input id=SHAPE_KEYINPUT class=SHAPE_INPUT><br>";
			}
			else if(this.pArr.length==1)
			{
				var click="onclick='document.forms[0].elements[0].value=this.innerText; if(top.iwan.shape.addKeyInputVals(document.getElementsByName(\"SHAPE_KEYINPUT\"))) top.iwan.shape.dropInputForm();'";
				var click_just="onclick='document.forms[0].elements[0].value=this.innerText; document.forms[0].elements[5].value=this.justify; if(top.iwan.shape.addKeyInputVals(document.getElementsByName(\"SHAPE_KEYINPUT\"))) top.iwan.shape.dropInputForm();'";
			
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value='l'>";
				a[a.length]="SymbolgrÃ¶ÃŸe:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT>";
				a[a.length]=	"<option value=8>&nbsp;&nbsp;8 px";
				a[a.length]=	"<option value=10>10 px";
				a[a.length]=	"<option value=12>12 px";
				a[a.length]=	"<option value=14>14 px";
				a[a.length]=	"<option value=16>16 px";
				a[a.length]=	"<option value=18>18 px";
				a[a.length]=	"<option value=20>20 px";
				a[a.length]=	"<option value=25>25 px";
				a[a.length]=	"<option value=30 selected>30 px";
				a[a.length]=	"<option value=35>35 px";
				a[a.length]=	"<option value=40>40 px";
				a[a.length]=	"<option value=50>50 px";
				a[a.length]=	"<option value=75>75 px";
				a[a.length]=	"<option value=100>100 px";
				a[a.length]="</select><br>";
				a[a.length]="Symbolfarbe:<br>";
				a[a.length]="<select id=SHAPE_KEYINPUT class=SHAPE_INPUT>";
				a[a.length]=	"<option value=red>rot";
				a[a.length]=	"<option value=black>schwarz";
				a[a.length]=	"<option value=green>grÃ¼n";
				a[a.length]=	"<option value=blue>blau";
				a[a.length]=	"<option value=yellow>gelb";
				a[a.length]=	"<option value=purple>violett";
				a[a.length]=	"<option value=white>weiÃŸ";
				a[a.length]="</select><br><br>";
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value='transparent'>"; //BG
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value=''>"; //bold,italic
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value='cc'>"; //justify
				a[a.length]="<input id=SHAPE_KEYINPUT type=hidden class=SHAPE_INPUT value='Wingdings'>"; //font
				
				a[a.length]="<style>span.sym {position:relative;font-family:wingdings; width:18px; text-align:center; padding:0px; margin:2px; font-size:14px; border:1px solid #555555; cursor:hand}</style>";
				// Marker - lmnpÂ°Â±Â¶
				a[a.length]="<span class='sym' "+click+">l</span>";
				a[a.length]="<span class='sym' "+click+">m</span>";
				a[a.length]="<span class='sym' "+click+">n</span>";
				a[a.length]="<span class='sym' "+click+">p</span>";
				a[a.length]="<span class='sym' "+click+">Â°</span>";
				a[a.length]="<span class='sym' "+click+">Â±</span>";
				a[a.length]="<span class='sym' "+click+">Â«</span>";
				a[a.length]="<span class='sym' "+click+">Â¶</span><br>";
				
				// Pfeile - Ã©ÃªÃ¬Ã«Ã­Ã®Ã§Ã¨
				a[a.length]="<span justify='uc' class='sym' "+click_just+">Ã©</span>";
				a[a.length]="<span justify='lc' class='sym' "+click_just+">Ãª</span>";
				a[a.length]="<span justify='ur' class='sym' "+click_just+">Ã¬</span>";
				a[a.length]="<span justify='ul' class='sym' "+click_just+">Ã«</span>";
				a[a.length]="<span justify='lr' class='sym' "+click_just+">Ã®</span>";
				a[a.length]="<span justify='ll' class='sym' "+click_just+">Ã­</span>";
				a[a.length]="<span justify='cl' class='sym' "+click_just+">Ã§</span>";
				a[a.length]="<span justify='cr' class='sym' "+click_just+">Ã¨</span><br>";
				
				// Zahlen
				a[a.length]="<span class='sym' "+click+">&#x81;</span>";
				a[a.length]="<span class='sym' "+click+">&#x82;</span>";
				a[a.length]="<span class='sym' "+click+">&#x83;</span>";
				a[a.length]="<span class='sym' "+click+">&#x84;</span>";
				a[a.length]="<span class='sym' "+click+">&#x85;</span>";
				a[a.length]="<span class='sym' "+click+">&#x86;</span>";
				a[a.length]="<span class='sym' "+click+">&#x87;</span>";
				a[a.length]="<span class='sym' "+click+">&#x88;</span>";
			}
		}	
	}
	
	/**
		@class
		ShapeObj - Managerklasse zum Halten und Darstellen von einfachen Geometrien
	*/	
	function ShapeObj() //(canvas, infoDiv, drawMode)
	{
		/* @constructor */
		//this.constructorDummy=function(){}
	//	constructor
	//	param {HtmlElement} canvas Div, der als Container fÃ¼r die Darstellung dienen soll
	//	param {HtmlElement} [infoDiv]
		
		var target=arguments[0];
		var infoDiv=arguments[1];
		var drawMode=arguments[2];
		
		//Konstanten
		this.DM={
					VML:1,			// DrawMode VML
					JSGRAPH:2,		// DrawMode alternative JS-Graphic LIB
					GDI:3			// DrawMode Rendering Behavior mit GDI (ActiveX)
				}
	
		this.target=target;
		this.infoDiv=infoDiv;
		this.alwaysVisible=true;
		this.drawMode=this.DM[drawMode] ? this.DM[drawMode] : this.DM.VML;
		//this.drawMode=this.DM.GDI;
		this.GDIInitialized=false;
		
		try
		{
			this.jsGraphics=new jsGraphics(target);
		}
		catch(err)
		{
			this.jsGraphics=null;
			this.drawMode=this.DM.VML;
		}
		//Falls kein EventObj vorhanden ist...
		this.event=new Object();
			this.event.attachEvent=function(){};
			this.event.fireEvent=function(){};
			this.event.releaseEvent=function(){};
		
		var topAccessible=false;
		try
		{
			var dummyTest=top.name; // lÃ¶st Exception aus oder halt nicht.
			topAccessible=true;
		}
		catch(e) {}
	
		if(typeof(EventObj)=="function")
			this.event=new EventObj(); // cardoMap, IGW, Asp.Net MapCtrl.
		else if(topAccessible && top && typeof(top.EventObj)=="function")
			this.event=new top.EventObj(); // cardo
		else if(typeof(glbEventObject)!="undefined")
			this.event=glbEventObject;
			
		this.parts=new Array();
		this.glbId=1; // wird verwendet, um aufeinanderfolgende Multiparts wiederzuerkennen und zu mergen
		this.partCount=0;
		this.currPart=-1;
		this.keyInputActive=false;
		this.keyInputFixed=false;
		this.currentSelectedShapeMode="LINE";
		this.drawVertices=false;
		
		/** Standardfarbe zur Darstellung von abgeschlossenen Geometrien, die selbst keine Farbe gesetzt haben.<br>
			(Eine Ã„nderung dieses Wertes wirkt auch auf alle bereits bestehenden Objekte.)
			@type string
		*/
		this.defaultObjectColor="red";
		
		/** Standardfarbe zur Darstellung von Geometrien, die gerade erstellt werden, d.h. noch nicht abgeschlossen sind.
			@type string
		*/
		this.unfinishedObjectColor="fuchsia";
		
		// Allgemeine Methoden
		this.addPart=_S_addPart;
		this.dropPart=_S_dropPart;
		//this.clearAll=_S_clearAll;
		//this.getGeomStrings=_S_getGeomStrings;
		//this.addGeomStrings=_S_addGeomStrings;
		this.showInfo=_S_showInfo;
		this.refreshInfo=_S_refreshInfo;
		this.openPartAvailable=_S_openPartAvailable;
		this.displayInputForm=_S_displayInputForm;
		this.dropInputForm=_S_dropInputForm;
		this.addKeyInputVals=_S_addKeyInputVals;
		//this.inExtent=_S_inExtent;
		this.setDrawMode=_S_setDrawMode;
		
		// Tunnels zu den Methoden des aktiven Parts
		this.addScreenPoint=_S_addScreenPoint;
		this.addGeoPoint=_S_addGeoPoint;
		//this.paint=_S_paint;
		this.show=_S_show;
		this.hide=_S_hide;
		this.clear=_S_clear;
		this.clearLast=_S_clearLast;
		this.finish=_S_finish;
		this.mouseMoveAction=_S_mouseMoveAction;
		this.suspendMouseMoveAction=_S_suspendMouseMoveAction;
		
		// Edit Stuff
		this.selectedVertex=null;
		this.selectedVertexMouseStart=null; // Geokoordinate des ersten Klickpunktes mit der Maus
		this.selectedVertexStart=null; // Geokoordinate des Ursprungspunktes
		this.selectedPartType=null;
		this.selectedPart=null;
		
		this.startVertexEdit=_S_startVertexEdit;
		this.stopVertexEdit=_S_stopVertexEdit;
		
		this.deselectVertex=_S_deselectVertex;
		this.selectNearestVertex=_S_selectNearestVertex;
		this.moveVertex=_S_moveVertex;
		this.addAndSelectVertex=_S_addAndSelectVertex;
		this.dropVertex=_S_dropVertex;
		this.getRealGeomPartIdx=_S_getRealGeomPartIdx;
		
		// Wechselfarben
		this.getNextColor=_S_getNextColor;
		this.setColorArray=_S_setColorArray;
		
		this.colors=null;
		//this.colors=["red","green","RGB(255,128,0)","blue","RGB(100,170,255)"];
		this.colorIdx=0;
		
		function _S_getNextColor()
		{
			var ret=null;
			if(this.colors && this.colors.length)
			{
				ret=this.colors[this.colorIdx];
				this.colorIdx++;
				if(this.colorIdx>=this.colors.length) 
					this.colorIdx=0;
			}
			return ret;
		}
		
		function _S_setColorArray(colors)
		{
			if(colors && colors.length)
				this.colors=colors;
			else
				this.colors=null;
		}
		
		function _S_startVertexEdit()
		{
			this.drawVertices=true;
		}
		
		function _S_stopVertexEdit()
		{
			this.drawVertices=false;
			this.event.fireEvent("onAfterVertexEdit", this);
		}
		
		function _S_selectNearestVertex(sx, sy, snapDist)
		{
			if(!this.partCount)
				return false;
			
			var d1=new Date();
			
			var d;
			var nearestDist=null;
			var p=screen2Geo(sWidth, sHeight, sx, sy, bound);
			this.selectedVertexMouseStart=p;
			
			var pArr=this.pArr;
			var pCount=0;
			
			// multiparts gleich mit berÃ¼cksichtigen
			while(part=(this.parts[this.currPart-pCount] && this.parts[this.currPart-pCount].id==this.id)?this.parts[this.currPart-pCount] : null)
			{
				pArr=part.pArr;
				for(var i=0; i<pArr.length; i++)
				{
					//quadratischer Abstand
					d=(p.x-pArr[i].x)*(p.x-pArr[i].x)+(p.y-pArr[i].y)*(p.y-pArr[i].y)
					if(nearestDist==null || d<nearestDist)
					{
						nearestDist=d;
						this.selectedVertex=pArr[i];
						this.selectedVertex.idx=i;
						this.selectedVertex.partIdx=this.parts.length-pCount-1;
						this.selectedPartType=part.type;
						this.selectedPart=part;
					}
				}
				pCount++;
			}
			
			var px2m=sWidth/(bound.x2-bound.x1);
			if(Math.sqrt(nearestDist)*px2m<=snapDist) // quadratischer Abstand !!
			{
				this.selectedVertexStart={x:this.selectedVertex.x, y:this.selectedVertex.y};
				this.selectedVertex.isSelected=true;
			
				if(this.drawMode==this.DM.GDI && this.selectedPart.isGeom)
				{
					this.target.functions="GEOMETRYPOINTSELECT="+this.getRealGeomPartIdx(this.selectedVertex.partIdx)+"|0|"+this.selectedVertex.idx+"|darkred|PS_SOLID|2|RED|BS_SOLID|HS_CROSS"
					this.target.style.visibility="hidden";
					this.target.style.visibility="visible";
				}
			}
			else
			{
				this.selectedVertex=null;
				return false; // nicht nahe genug an Vertex
			}

			var d2=new Date()
			//top.window.document.title="Vertex selektieren: "+((d2.getTime()-d1.getTime())/1000)+" s"

			return true;
		}
				
		function _S_addAndSelectVertex(sx, sy, snapDist)
		{
			var d1=new Date();
			
			if(!this.partCount)
				return;
				
			var d;
			var p=screen2Geo(sWidth, sHeight, sx, sy, bound);
			this.selectedVertexMouseStart=p;
			var nearestDist=null;
			var nearestStartVertexIdx=null;
			var nearestStartVertexParams=null;
			var vertexIdx=null;
			var partIdx=null;
			
			var part=null;
			var pArr=this.pArr;
			var affectedPart=null;
			var pCount=0;
			
			// multiparts gleich mit berÃ¼cksichtigen
			while(part=(this.parts[this.currPart-pCount] && this.parts[this.currPart-pCount].id==this.id)?this.parts[this.currPart-pCount] : null)
			{
				pArr=part.pArr;
				for(var i=1; i<pArr.length; i++)
				{
					d=___getDistance(pArr[i-1].x,pArr[i-1].y, pArr[i].x,pArr[i].y, /*-*/ p.x,p.y );
					if(isNaN(d.dist))
						continue; // kann bei ANNO und SYMBOL vorkommen, weil eine Koordinate als TexttrÃ¤ger missbraucht wird
					if(nearestDist==null || d.dist<nearestDist)
					{
						nearestDist=d.dist;
						nearestStartVertexIdx=i-1;
						vertexIdx=i;
						partIdx=this.parts.length-pCount-1;
						nearestStartVertexParams=d;
						affectedPart=part;
					}
				}
				pCount++;
			}
			if(nearestDist==null)
				return false;
				
			var px2m=sWidth/(bound.x2-bound.x1);
			if(nearestDist*px2m<=snapDist) // quadratischer Abstand !!
			{
				if(!affectedPart.validateMinMaxVertexCount(1))
					return false;
				
				// einfÃ¼gen
				for(var i=affectedPart.pArr.length; i>nearestStartVertexIdx+1; i--)
					affectedPart.pArr[i]=affectedPart.pArr[i-1]; // von hinten beginnend alle StÃ¼tzpunkte bis vor den EinfÃ¼gepunkt eins hoch 
				affectedPart.pArr[i]=nearestStartVertexParams.p; // schieben und dann den neuen Wert an die frei Stelle setzten
					
				this.selectedVertex=affectedPart.pArr[i];
				this.selectedVertex.isSelected=true;
				this.selectedVertex.idx=vertexIdx;
				this.selectedVertex.partIdx=partIdx;
				this.selectedVertexStart={x:this.selectedVertex.x, y:this.selectedVertex.y};
				this.selectedPartType=affectedPart.type;
				this.selectedPart=affectedPart;
			}
			else
				return false; // nicht nahe genug an der Strecke
			
			var d2=new Date()
			//top.window.document.title="Vertex hinzufÃ¼gen: "+((d2.getTime()-d1.getTime())/1000)+" s"
			
			this.event.fireEvent("onAfterPartChanged",this);	
			return true;
			
				// Hilfsfunktion
				function ___getDistance(xA,yA, xB,yB, /*-*/ xC,yC) // Abstand von C zur Strecke AB
				{
					// bei Google in einer Newsgroup gefunden
					// nimmt entweder das Lot auf Strecke oder den Abstand 
					// zum Start oder Endpunkt der Strecke
					var s = ( (xB-xA)*(xC-xA) + (yB-yA)*(yC-yA) ) /
					      ( (xB-xA)*(xB-xA) + (yB-yA)*(yB- yA) )    ;
					if (s < 0.0)   s = 0.0 ;
					if (s > 1.0)   s = 1.0 ;
					var xP = xA + s*(xB - xA) ;
					var yP = yA + s*(yB - yA) ;
					var abstand = Math.sqrt( (xC-xP)*(xC-xP)  +  (yC-yP)*(yC-yP) ) ;
					return {p:{x:xP,y:yP}, dist:abstand};
				}
		}
		
		function _S_moveVertex(sx, sy)
		{
			var p=screen2Geo(sWidth, sHeight, sx, sy, bound);
			var pms=this.selectedVertexMouseStart;
			var ps=this.selectedVertexStart;
			
			this.selectedVertex.x=ps.x+(p.x-pms.x);
			this.selectedVertex.y=ps.y+(p.y-pms.y);
			
			var ps=geo2Screen(sWidth, sHeight, this.selectedVertex.x, this.selectedVertex.y, bound);
			var cr=-3;
			
			this.selectedPart.calculatedArea=null;
			
			this.event.fireEvent("onAfterPartChanged",this); // CHECK: neu am 11.01.2006 --> Seiteneffekte ??
			
			// Optimierung: nur die betroffene Vertex bewegen
			if(this.selectedPartType=='POLY' || this.selectedPartType=='ARC')
			{
				if(this.drawMode==this.DM.VML)
				{
					var v=this.target.document.getElementById("SELVERTEX");
					if(v)
					{
						v.style.left=ps.x+cr;
						v.style.top=ps.y+cr;
					}
					var lb=this.target.document.getElementById("LINE_BEFORE_SELVERTEX");
					if(lb)
						lb.from=ps.x+","+ps.y;
					var la=this.target.document.getElementById("LINE_AFTER_SELVERTEX");
					if(la)
						la.to=ps.x+","+ps.y;
					return true;
				}
				else if(this.drawMode==this.DM.GDI)
				{
					if(this.selectedVertex.idx==0 && this.selectedPartType=="POLY")
						var add="/MODIFYGEOMETRY="+this.getRealGeomPartIdx(this.selectedVertex.partIdx)+"|0|"+(this.parts[this.selectedVertex.partIdx].pArr.length-1)+"|"+ps.x+"|"+ps.y;
					else
						var add='';
					this.target.functions="MODIFYGEOMETRY="+this.getRealGeomPartIdx(this.selectedVertex.partIdx)+"|0|"+this.selectedVertex.idx+"|"+ps.x+"|"+ps.y
										 +add;
					this.target.style.visibility="hidden";
					this.target.style.visibility="visible";
					//top.window.document.title="moving vertex mit idx: "+this.selectedVertex.idx;
					return true;
				}
			}
			else
				return false; // ein paint() sollte ausgelÃ¶st werden
		}
		
		function _S_deselectVertex()
		{
			if(!this.partCount)
				return;
			
			var idx=this.selectedVertex.idx;
			this.selectedVertex.isSelected=null;
			this.selectedVertex.idx=null; // kann sich potentiell Ã¤ndern, deswegen lieber auf null setzen um MissverstÃ¤ndnise zu vermeiden
			this.selectedVertex.partIdx=null; // kann sich potentiell Ã¤ndern, deswegen lieber auf null setzen um MissverstÃ¤ndnise zu vermeiden
			this.selectedVertex=null;
			this.selectedPart=null;
			
			var v=this.target.document.getElementById("SELVERTEX");
			if(v)
				v.fillColor=idx==0 ? "#99EEFF" : "blue";
		}
		
		function _S_dropVertex(sx, sy, snapDist)
		{
			if(!this.partCount)
				return false;
			
			var d;
			var nearestDist=null;
			var nearestVertexIdx=null;
			var partIdx=null;
			var p=screen2Geo(sWidth, sHeight, sx, sy, bound);
			
			var part=null;
			var pArr=this.pArr;
			var affectedPart=null;
			var pCount=0;
			
			// multiparts gleich mit berÃ¼cksichtigen
			while(part=(this.parts[this.currPart-pCount] && this.parts[this.currPart-pCount].id==this.id)?this.parts[this.currPart-pCount] : null)
			{
				pArr=part.pArr;
				for(var i=0; i<pArr.length; i++)
				{
					//quadratischer Abstand
					d=(p.x-pArr[i].x)*(p.x-pArr[i].x)+(p.y-pArr[i].y)*(p.y-pArr[i].y)
					if(nearestDist==null || d<nearestDist)
					{
						nearestDist=d;
						nearestVertexIdx=i;
						partIdx=this.parts.length-pCount-1;
						affectedPart=part;
					}
				}
				pCount++;
			}
			if(nearestVertexIdx!=null)
			{
				var px2m=sWidth/(bound.x2-bound.x1);
				if(Math.sqrt(nearestDist)*px2m<=snapDist)
				{
					var ret=affectedPart.validateMinMaxVertexCount(-1);
					if(ret==0) // Vertex und Geometrie nicht lÃ¶schen
						return false;
					
					if(ret==-1) // ganze Geometrie lÃ¶schen, weil Grundbedingung nicht mehr erfÃ¼llt ist
					{
						this.dropPart(partIdx)
					}
					else
					{
						affectedPart.calculatedArea=null;					
						if(affectedPart.pArr.length==1) // wenn letzter StÃ¼tzpunkt
						{
							//this.dropPart(partIdx)
							this.clearLast(); // sollte nur auftreten kÃ¶nnen, wenn das Objekt noch offen und damit das aktuelle ist
						}
						else
						{
							affectedPart.pArr=affectedPart.pArr.slice(0,nearestVertexIdx).concat(affectedPart.pArr.slice(nearestVertexIdx+1,affectedPart.pArr.length));
							if(nearestVertexIdx==0 && affectedPart.type=="POLY" && affectedPart.closed && affectedPart.pArr.length>3)
							{
								affectedPart.pArr.length--;
								affectedPart.pArr[affectedPart.pArr.length]=affectedPart.pArr[0];
							}
						}
					}
				}
				else
					return false; // nicht nahe genug an der Vertex
			}
			this.event.fireEvent("onAfterPartChanged",this);
			return true;
		}
		
		function _S_getRealGeomPartIdx(idx) // ZÃ¤hlt nur die "echten" Geometrien bis zum gewÃ¼nschten Index
		{
			// z.B. bei ANNO,ANNO,POLY wÃ¼rde man 2 reingeben und 0 rausbekommen
			// darf nur mit Indizes aufgerufen werden, die auf eine "echte" Geom zeigen !
			if(this.parts[idx] && this.parts[idx].isAnno)
				return null;
				
			var geomIdx=-1;
			for(var i=0; i<this.parts.length; i++)
			{
				if(i>idx)
					break;
				if(this.parts[i].isGeom)
					geomIdx++;
			}
			return geomIdx;
		}
		
		function _S_setDrawMode(newMode, nonverbose)
		{
			if(newMode==null)
				newMode=prompt("Zeichenmodus:\nVML    JSGRAPH    GDI", "");

			if(newMode && !isNaN(newMode))
			{
				for(var k in this.DM)
					if(this.DM[k]==newMode)
					{
						newMode=k;
						break;
					}
			}
			newMode=newMode.toUpperCase();
			if(this.DM[newMode])
			{
				if(!this.GDIInitialized && this.DM[newMode]==this.DM.GDI)
				{
					var activeXEnabled=true;
					try
					{
					//	var a=new ActiveXObject("Scripting.Dictionary");
						a=null;
					}
					catch(e)
					{
						var activeXEnabled=false;
					}
					if(activeXEnabled)
					{
						var oleDoc=this.target.document.getElementById("OLE").contentWindow.document;
						oleDoc.open();
						oleDoc.write('<OBJECT	 ID=GDI_RENDERER'
									+'			 CLASSID="clsid:CFDD0BBD-9906-11d3-953D-0000F804E031"'
									+'			 codebase="IDUHtmlGeometryRenderer.cab"'
									+'			 declare>'
									+'	</OBJECT>');
						oleDoc.close();
							
						// auf diese Weise wird leider kein Download initiiert, deswegen das Theater vorher,
						// damit die clsid schon registriert ist
						var obj=this.target.document.createElement("OBJECT");
						this.target.document.body.appendChild(obj);
						obj.setAttribute("id","GDI_RENDERER");
						//obj.setAttribute("codebase","IDUHtmlGeometryRenderer.cab"); // hat leider keinen Effekt so
						obj.setAttribute("classid","clsid:CFDD0BBD-9906-11d3-953D-0000F804E031");
							
						top.iwan.shape.target.addBehavior("#GDI_RENDERER");
						this.GDIInitialized=true;
					}
					else
					{
						return alert("ActiveX ist nicht zugelassen.")
					}
				}
				this.drawMode=this.DM[newMode];
				return true;
			}
			else
				return alert("Unbekannter Zeichenmodus '"+newMode+"'");
		}
		
		function _S_openPartAvailable()
		{
			if(this.partCount)
				return !this.parts[this.currPart].closed; // min. ein Part enthalten und offen
			else
				return false; // noch gar kein Part vorhanden
		}
		
		function _S_addPart(type, glbId, color) // wenn glbId Ã¼bergeben wird, dann die nehmen, sonst hochzÃ¤hlen
		{
			var eventArgs={shape:this, geomType:type, color:color, cancel:false};
			this.event.fireEvent("onBeforeAddPart", eventArgs, true);
			
			if(eventArgs.cancel)
			{
				return false;
			}
			type=eventArgs.geomType;
			
			if(type=="LINE")
				type="ARC";
						
			var geomObjPtr=null;
			switch(type.toUpperCase())
			{
				case "POINT":
						geomObjPtr=PointGeom;
					break;
				case "ARC":
						geomObjPtr=LineGeom;
					break;
				case "CIRCLE":
						geomObjPtr=CircleGeom;
					break;
				case "POLY":
						geomObjPtr=PolygonGeom;
					break;
				case "ANNO":
						geomObjPtr=AnnoGeom;
					break;
				case "SYMBOL":
						geomObjPtr=SymbolGeom;
					break;
			}
			
			if(geomObjPtr!=null)
			{
				this.parts[this.partCount]=new geomObjPtr();
				this.parts[this.partCount].pArr=new Array(); // nÃ¶tig, da sonst das von der Basisklasse fÃ¼r alle genommen wird - is halt doch nur JavaScript
				this.parts[this.partCount].shapeObject=this;
				this.parts[this.partCount].color=color || null;
				var id=glbId || this.glbId++;
				this.parts[this.partCount].glbId=id;
				this.partCount++;
				this.currPart++;
				return true;
			}
			return false;
		}
		
		function _S_dropPart(idx) // u.U. kÃ¶nnte man hier noch einen Index Ã¼bergeben
		{
			// TODO:
			// Hier mÃ¼sste noch irgendwie die Synchronisation zur
			// Tastatureingabe rein --> Probelm Endlosschleife
			if(this.partCount)
			{
				if(idx!=null && !isNaN(idx))
				{
					idx=parseInt(idx);
					if(isNaN(idx) || idx>=this.partCount)
						return;
					this.parts=this.parts.slice(0, idx).concat(this.parts.slice(idx+1, this.parts.length))
					this.partCount--;
					this.currPart--;
				}
				else
				{
					this.parts.length--;
					this.partCount--;
					this.currPart--;
				}
				
				this.paint();
				this.event.fireEvent("onAfterPartChanged",this);
				
				if(this.partCount==0)
					this.drawVertices=false;
			}
		}
		
		/**
			LÃ¶scht alle im Objekt enthaltenen Geometrien.
		*/
		this.clearAll = function()
		{
			if(this.keyInputActive)
			{
				this.dropInputForm();
				this.keyInputActive=true; //beibehalten
			}
			
			this.parts.length=0;
			this.partCount=0;
			this.currPart=-1;
			this.drawVertices=false;
			this.paint();
			
			if(this.keyInputActive)
				this.displayInputForm();
			this.event.fireEvent("onAfterPartChanged",this);
		}
		
		function _S_mouseMoveAction(e)
		{
			if(!this.drawVertices && this.partCount && !this.parts[this.currPart].closed && this.parts[this.currPart].pArr.length)
				this.parts[this.currPart].mouseMoveAction(this.target, e);
		}
		
		function _S_suspendMouseMoveAction(keepInfoDiv)
		{
			// kleiner Hack, damit direkt nach dem Neuzeichnen oder MouseOut
			// auch die Strichellinien an unfertigen Objekten erscheinen
			// und auch der InfoDiv aktuell ist
			if(this.parts[this.currPart] && this.parts[this.currPart].pArr.length)
			{
				var tempP=this.parts[this.currPart].getScreenPoint(this.parts[this.currPart].pArr.length-1);
				//this.mouseMoveAction({clientX:tempP.x, clientY:tempP.y});
				this.mouseMoveAction({x:tempP.x, y:tempP.y});
				//this.refreshInfo({clientX:tempP.x, clientY:tempP.y});
				this.refreshInfo({x:tempP.x, y:tempP.y});
			}
			if(this.infoDiv && !keepInfoDiv)
				this.infoDiv.style.visibility="hidden";
		}
		
		/**
			Gibt die im Objekt enthaltenen Geometrien in Form der IWAN-TextreprÃ¤sentation zurÃ¼ck.
			@param {string} [typeFilter] "GEOM" um nur "richtige" Geometrien zu erhalten, "ANNO" um Annotation und Symbol zu erhlaten oder leer lassen, um alles zu bekommen.
			@param {string} [separator] Separator fÃ¼r die Koordinaten (Standard ist "|")
			@param {bool} [closePoly] Bei Polygonen noch einen schlieÃŸenden StÃ¼tzpunkt einfÃ¼gen, der sich mit dem ersten deckt. (Standard: false)
			@returns {string[]} Array mit Geometrien in der IWAN-TextreprÃ¤sentation
		*/
		this.getGeomStrings = function(typeFilter, separator, closePoly) // type=GEOM|ANNO oder nichts, dann gibts beides
		{
			var type=typeFilter;
			var ret=new Array();
			for(var i=0; i<this.partCount; i++)
			{
				if(type=="GEOM" && !this.parts[i].isGeom)
					continue;
				if(type=="ANNO" && !this.parts[i].isAnno)
					continue;
				
				var part=this.parts[i];
				var temp=part.getGeomString(separator, closePoly); // geomstring holen
				if(lastId==part.glbId) //falls multipart --> mergen
				{
					_S__merge(/*byRef*/ret, temp);
					temp=''; // damit's nicht noch mal eingefÃ¼gt wird --> ist ja schon drin
				}
				var lastId=part.glbId;
							
				if(temp!='')
					ret[ret.length]=temp;
			}
			return ret;
			
			function _S__merge(/*byRef*/ret, merge)
			{
				//			 $1 ist Typ, $2 Parts, $3 ist Punktliste,
				var temp=/^(g[a-zA-Z]+):(\([^)]+\))?(.*)$/.exec(ret[ret.length-1]);
				var type=temp[1]
				var parts=temp[2]
				var oldGeoms=temp[3].split("|")
				
				var newGeom=merge.split(":")[1];
				
				if(type=="gPOINT" || type=="gMULTIPOINT")
				{
					type="gMULTIPOINT"
					var parts="";
				}
				else
				{
					if(parts=='')
						parts="0"
					else
						parts=parts.replace(/\(|\)/g, '');
					
					parts+="|"+(oldGeoms.length/2); // 0-basierter Index !
					parts="("+parts+")";
				}
				
				// neu zusammensetzen
				ret[ret.length-1]=type+":"+parts+temp[3]+"|"+newGeom;
			}
		}
		
		function _S_addScreenPoint(sx, sy, isText)
		{
			if(!bound)
				return;
				
			if(!this.parts[this.currPart].closed)
			{
				var keepOpen=this.parts[this.currPart].addScreenPoint(sx, sy, isText);
				if(!keepOpen)
				{
					var ret=this.parts[this.currPart].finish();
					if(ret)
					{
						this.event.fireEvent("onAfterGeomFinished", this);
						this.event.fireEvent("onAfterPartChanged", this);
					}
				}
				return this.lastAddOperationSuccess;
			}
		}
		
		function _S_addGeoPoint(gx, gy, isText)
		{
			if(!this.parts[this.currPart].closed)
			{
				var keepOpen=this.parts[this.currPart].addGeoPoint(gx, gy, isText);
				if(!keepOpen)
				{
					var ret=this.parts[this.currPart].finish();
					if(ret)
					{
						this.event.fireEvent("onAfterGeomFinished", this);
						this.event.fireEvent("onAfterPartChanged", this);
					}
				}
				return this.lastAddOperationSuccess;
			}
		}
		/**
			Veranlasst das Neuzeichnen der Geometrien. Die Verwendung dieser Methode ist nur selten nÃ¶tig, da sie i.d.R. implizit aufgerufen wird.
		*/
		this.paint = function()
		{
			if(!bound)
				return;
				
			var tempLn=new Array();
			var tempScripts=new Array();
			var oneClosedAvail=false;
			var atLeastOnePartDrawn=false;
			var vertexColor=this.drawVertices ? "blue" : null;
			
			if(this.drawMode==this.DM.JSGRAPH)
				this.jsGraphics.clear();
			
			this.colorIdx=0;
			for(var i=0; i<this.partCount; i++)
			{
				oneClosedAvail=oneClosedAvail | this.parts[i].closed;
				var color=this.parts[i].closed ? (this.getNextColor() || this.defaultObjectColor || "red") : this.unfinishedObjectColor || "fuchsia";
				
				if(this.drawMode==this.DM.VML)
					this.parts[i].getVML(/*byRef*/ tempLn, /*byRef*/tempScripts, color, vertexColor);
				else if(this.drawMode==this.DM.JSGRAPH) // DM.JSGRAPH
				{
					this.jsGraphics.setStroke(2);
					this.parts[i].drawJsGraph(/*byRef*/tempScripts, color, vertexColor);
					atLeastOnePartDrawn=true;
				}
				else // DM.GDI
				{
					this.parts[i].getGDIPoints(/*byRef*/ tempLn, /*byRef*/tempScripts, color, vertexColor);
				}
			}
			
			// hier ist die zentralste Stelle, das zu machen - ist zwar manchmal Ã¼berflÃ¼ssig, aber macht keinen groÃŸen Aufwand
			if(typeof showShapeDrag=="function")
				showShapeDrag(oneClosedAvail); // HACK: externer CODE !!!
						
			if(this.drawMode==this.DM.VML)
			{
				this.target.functions="GEOMETRY="; // wenn vorher GDI Mode war
				this.target.innerHTML=tempLn.join(""); // <?xml:namespace prefix = v /> ???
			}
			else if(this.drawMode==this.DM.JSGRAPH) // DM.JSGRAPH
			{
				this.target.functions="GEOMETRY="; // wenn vorher GDI Mode war
				if(atLeastOnePartDrawn)
					this.jsGraphics.paint();
			}
			else // DM.GDI
			{
				var functions=new Array();
				var html=new Array(); //fÃ¼r Annos
				//html[html.length]='<div nowrap style="position:absolute; left:expression(1+offsetWidth); top:expression(1+offsetWidth); color:red; font-size:20px; font-family:arial;">&nbsp;</div>'
				functions[functions.length]="GEOMETRY=";
				var geomCount=0;
				if(tempLn.length)
				{
					this.target.innerHTML="";
					for(var i=0; i<tempLn.length; i++)
					{
						if(tempLn[i].geom)
						{
							functions[functions.length]="ADDGEOMETRY="+tempLn[i].geom;
							functions[functions.length]='GEOMETRYPENSTYLE='+geomCount+'|'+tempLn[i].color+'|PS_SOLID|2';
							geomCount++;
						}
						else
						{
							html[html.length]=tempLn[i].html;
						}
					}
					//prompt("",html.join("\n\n"));
					if(this.selectedVertex && this.selectedPart.isGeom)
						functions[functions.length]="GEOMETRYPOINTSELECT="+this.getRealGeomPartIdx(this.selectedVertex.partIdx)+"|0|"+this.selectedVertex.idx+"|darkred|PS_SOLID|2|red|BS_SOLID|HS_CROSS";
				}
				functions[functions.length]='SHOWBASEPOINTS='+(this.drawVertices?"true":"false");
				this.target.innerHTML=html.join("");
				this.target.functions=functions.join("/");
				this.target.style.visibility="hidden";
				this.target.style.visibility="visible";
			}
			
			if(tempScripts.length>0)
			{
				//prompt("sc",tempScripts.join(""))
				eval(tempScripts.join(""));
			}
			this.suspendMouseMoveAction(true);	
		}
		
		function _S_show()
		{
			this.target.style.visibility='visible';
		}
		function _S_hide()
		{
			this.target.style.visibility='hidden';
		}
		function _S_clear()
		{
			if(this.keyInputActive)
			{
				this.dropInputForm();
				this.keyInputActive=true; //beibehalten
			}
			
			if(this.partCount>0)
				ret=this.parts[this.currPart].clear();
			
			if(this.keyInputActive)
				this.displayInputForm();
		}
		function _S_clearLast()
		{
			if(this.keyInputActive)
			{
				this.dropInputForm();
				this.keyInputActive=true; //beibehalten
			}
			
			if(this.partCount>0)
			{
				this.parts[this.currPart].clearLast();
				if(this.parts[this.currPart].pArr.length==0)
				{
					this.dropPart();
				}
					
			}
			if(this.keyInputActive)
				this.displayInputForm();
		}
		
		function _S_finish()
		{
			var ret=false;
			if(this.partCount>0)
			{
				ret=this.parts[this.currPart].finish();
				if(ret)
					this.paint();
			}
			if(ret)
			{
				this.event.fireEvent("onAfterGeomFinished", this);
				this.event.fireEvent("onAfterPartChanged", this);
			}
			return ret;
		}
		
		function _S_showInfo(e)
		{
			if(this.partCount>0 && this.infoDiv)
			{
				this.infoDiv.style.visibility='visible';
				//this.infoDiv.style.posLeft=e.clientX+30;
				//this.infoDiv.style.posTop=e.clientY+10;
				this.infoDiv.style.posLeft=e.x+30;
				this.infoDiv.style.posTop=e.y+10;
				
				this.refreshInfo(e);
			}
		}
				
		function _S_refreshInfo(e)
		{
			if(this.infoDiv)
			{
				var res=this.parts[this.currPart].getGeomInfo(e);
				infoDiv.innerHTML="<table cellspacing=0 cellpadding=1>"+res.join("")+"</table>";
			}
		}
		
		function _S_displayInputForm()
		{
			if(!this.partCount || this.parts[this.currPart].closed)
				this.addPart(this.currentSelectedShapeMode);

			var div=this.target.parentElement.parentElement.document.createElement("DIV");
			div.style.cssText="position:absolute;left:2;top:2;font-family:arial;font-size:10px;z-index:100000000;background-color:#EEEEEE;border:2px outset white;padding:1px;";
			div.setAttribute("id", "SHAPE_INPUT_CONTAINER");
				
			var arr=new Array();
				
			var border=this.keyInputFixed?"border:1px inset white":"border:1px solid #999999";
			arr[arr.length]="<form style='margin:0px;' onsubmit='return false;'>"
			arr[arr.length]="<div style='font-size:11px;margin-bottom:5px;'>";
			arr[arr.length]="	<img align=absMiddle onclick='var s=top.iwan.shape; if(s.keyInputFixed){this.style.border=\"1px solid #999999\"; s.keyInputFixed=false;}else{this.style.border=\"1px inset white\";s.keyInputFixed=true;}' style='cursor:hand;"+border+"' src='/tools/imgTools/pin.gif'>";
			arr[arr.length]="	Geometrie Tastatureingabe";
			arr[arr.length]="</div>";
			arr[arr.length]="<style>"
			arr[arr.length]="	.SHAPE_INPUT{font-size:10px;width:130;}"	
			arr[arr.length]="</style>"
	
			this.parts[this.currPart].getInputForm(/*byRef*/ arr)
			
			arr[arr.length]="</form>"
			
			arr[arr.length]="<div align=right style='margin-top:5px;'>";
			arr[arr.length]="	<input type=button style='font-size:9px;width:17;height:17;background-color:#EEEEEE;padding:0 0 3 0; border:1px solid #999999;' value=x onclick='top.iwan.shape.dropInputForm()'>";
			arr[arr.length]="	<input type=submit style='font-size:9px;width:17;height:17;background-color:#EEEEEE;padding:0 0 3 0; border:1px solid #999999;' value=+ onclick='if(top.iwan.shape.addKeyInputVals(document.getElementsByName(\"SHAPE_KEYINPUT\"))) top.iwan.shape.dropInputForm();'>";
			arr[arr.length]="</div>";
			div.innerHTML=arr.join("");
				
			this.target.parentElement.parentElement.insertBefore(div);
			this.keyInputActive=true;
		}
		
		function _S_dropInputForm()
		{
			var div=this.target.parentElement.parentElement.document.getElementById("SHAPE_INPUT_CONTAINER");
			if(div!=null)
				this.target.parentElement.parentElement.removeChild(div);
			
			if(this.parts.length && this.parts[this.currPart].pArr.length==0) // wenn noch ein part existiert, aber gar kein Punkt dazu getan wurde
					this.dropPart();					 // den Part wieder wegschmeiÃŸen
						
			this.keyInputActive=false;
		}
		
		function _S_addKeyInputVals(els)
		{
			if(!this.partCount || this.parts[this.currPart].closed) // ist nÃ¶tig, wenn bei offenem Input das letzte Objekt gelÃ¶scht wurde
				this.addPart(this.currentSelectedShapeMode);
			
			var p=this.parts[this.currPart].processKeyInputVals(els);
			if(p==null)
				return alert("Bitte Ã¼berprÃ¼fen Sie die eingegebenen Werte.");
			else
			{
				this.addGeoPoint(p.x, p.y, p.isText); // isText gilt nur bei AnnoGeom
				this.paint();
				if(this.keyInputFixed)
				{
					this.dropInputForm();
					this.displayInputForm();
				}
				else
					return true;
			}
		}
		/**
			FÃ¼gt dem Objekt weitere Geometrien hinzu.<br>
			Neben den auf http://webmapserver.de/theGeometry dokumentierten Formaten gibt es noch:<br><br>
			<i>gAnno:x|y|Text|TextgrÃ¶ÃŸe in KarteneinheitenÂ°FarbeÂ°HintergrundfarbeÂ°BoldÂ°ItalicÂ°AusrichtungÂ°Schriftart</i><br>
			<i>gSymobol:x|y|Zeichen|SymbolgrÃ¶ÃŸe in KarteneinheitenÂ°FarbeÂ°HintergrundfarbeÂ°BoldÂ°ItalicÂ°AusrichtungÂ°Schriftart</i><br><br>
			Als Zeichen bei Symbol sollte eine <a style="text-decoration:underline;" target="_blank" href="http://unicode.org/charts/symbols.html">Unicode</a> Entity-Referenz in Verbindung mit einer Unicode Schriftart (z.B. Arial) benutzt werden.<br>
			@example glbMap.shape.addGeomStrings(["gSYMBOL:5418310.04081|5655735.82258|&amp;#x263A;|1500Â°redÂ°transparentÂ°0Â°0Â°ccÂ°Arial"]);
			@param {string|string[]} geometries String oder Array von Strings mit Geometrien in der IWAN TextreprÃ¤sentation (nur geografische Koordinaten!)
			@param {bool} [noMultiPartWarning] unterdrÃ¼cken des Warnhinweises beim HinzufÃ¼gen von Multipart-Geometrien
			@param {string} [color] HTML Farbangabe, falls die Geometrien nicht in der Standardfarbe dargestellt werden sollen.
		*/
		this.addGeomStrings = function(geometries, noMultiPartWarning, color) // nur geografische Koords
		{
			var arr=geometries;
			if(typeof arr=="string")
				arr=[arr];
			for(var i=0; i<arr.length; i++)
			{
				//			 $1 ist Typ, $2 Parts, $3 ist Punktliste,
				var temp=/^g?([a-zA-Z]+):(\([^)]+\))?(.*)$/.exec(arr[i]);
				var type=(temp && temp[1] || "UNKNOWN").toUpperCase();
				var withMultipart=false;
				switch(type)
				{
					// Build in Objecte, die bis auf ANNO auch so in IWAN existieren
					case "POLY":
					case "ARC":
					case "POINT":
					case "CIRCLE":
					case "ANNO":
					case "SYMBOL":
							var parts=temp[2];
							var pointList=temp[3].split("|");
							
							if(parts)
							{
								var multiId=this.glbId++; // fÃ¼r alle Teile des Multiparts die gleiche Id
								withMultipart=true;
								parts=parts.replace(/\(|\)/g, '');
								parts=parts.split('|');
								parts[parts.length]=pointList.length/2; // letzten Punkt noch als Part hinzufÃ¼gen
								for(var j=0; j<parts.length-1; j++)
								{
									this.addPart(type, multiId, color);
									var from=parts[j]*2; // parts-Angabe bezieht sich immer auf X|Y
									var to=parts[j+1]*2;
									this.parts[this.currPart].buildFromKoordArr(pointList.slice(from, to));
									//erst wenn Schnittpunkttest robust funktioniert:
									if(!this.parts[this.currPart].finish(true/*dont validate Poly*/))
										this.dropPart();
								}
							}
							else
							{
								this.addPart(type, null, color);
								this.parts[this.currPart].buildFromKoordArr(pointList);
								this.parts[this.currPart].closed=true;
								//erst wenn Schnittpunkttest robust funktioniert:
								this.parts[this.currPart].finish(true/*dont validate Poly*/);
							}
						break;
					// IWAN Geoms, die in keine direkte Build-in Entsprechung haben
					case "MULTIPOINT":
							var pointList=temp[3].split("|");
							var withMultipart=true;
							var multiId=this.glbId++; // fÃ¼r alle Teile des Multiparts die gleiche Id
							for(var i=1; i<pointList.length; i+=2)
							{
								this.addPart("POINT", multiId, color);
								this.parts[this.currPart].buildFromKoordArr([pointList[i-1], pointList[i]]);
								if(!this.parts[this.currPart].finish())
									this.dropPart();
							}
						break;
					// TODO: falls mal nÃ¶tig
					//case "BOX":
					//	break;
					
					default:
						alert("Mindestens ein Objekt konnte nicht hinzugefÃ¼gt werden\n\nDer Geometrietyp '"+type+"' wird derzeit nicht unterstÃ¼zt.");
				}
			}
			noMultiPartWarning=true; // ist eigentlich Ã¼berflÃ¼ssig, da multies ganz gut berÃ¼cksichtigt werden
			if(withMultipart && !noMultiPartWarning)
				alert("Mehrteiliges Objekt wurde in Einzelelemente aufgelÃ¶st !");
				
			this.event.fireEvent("onAfterPartChanged",this);
			this.paint();
		}
		
		/**
			Bestimmt, ob die im Objekt enthaltenen Geometrien im aktuellen Kartenausschnitt liegen.
			(Dieser ist durch die interne Kopplung mit dem Map-Control bekannt.)
			@param {object} [e] Referenz auf ein leeres Extent-Objekt (alle Member mÃ¼ssen null sein).
								Dieses Objekt wird nach Durchlauf der Funktion die Ausdehnung aller Geometrien umfassen.
			@config {double} x0 Achtung! Diese Struktur ist 0-basiert
			@config {double} y0 und nicht wie das ExtentObj 1-basiert !
			@config {double} x1	
			@config {double} y1			
			@param {double} [offset] Abstand in Karteneinheiten, der in allen Richtungen noch auf e zugegeben werden soll.
			@returns {int} -1 - keine Aussage mÃ¶glich (keine Geometrie oder kein Karten-Extent verfÃ¼gbar)<br>
						   &nbsp;0 - Geometrien vollstÃ¤ndig auÃŸerhalb des Ausschnitts<br>
						   &nbsp;1 - Geometrien teilweise innerhalb des Ausschnitts<br>
						   &nbsp;2 - Geometrien vollstÃ¤ndig innerhalb des Ausschnitts
		*/
		this.inExtent = function(/*byRef*/e, offset)
		{
			if(!bound)
				return 0;
			
			if(!e)
				var e={x0:null, y0:null, x1:null, y1:null};
			
			for(var i=0; i<this.parts.length; i++)
			{
				var pe=this.parts[i].getExtent();
				if(pe==null)
					continue;
				e.x0=e.x0!=null ? Math.min(e.x0,pe.x0) : pe.x0;
				e.y0=e.y0!=null ? Math.min(e.y0,pe.y0) : pe.y0;
				e.x1=e.x1!=null ? Math.max(e.x1,pe.x1) : pe.x1;
				e.y1=e.y1!=null ? Math.max(e.y1,pe.y1) : pe.y1;
			}
			if(e.x0==null)
				return -1;

			// !! bound ist 1 basiert, e ist 0 basiert !!
			if(bound.x1<e.x0 && bound.y1<e.y0 && bound.x2>e.x1 && bound.y2>e.y1)
				var ret=2;
			else if(bound.x1<e.x1 && bound.y1<e.y1 && bound.x2>e.x0 && bound.y2>e.y0)
				var ret=1;
			else 
				var ret=0;
			
			if(offset)
			{
				e.x0-=offset;
				e.y0-=offset;
				e.x1+=offset;
				e.y1+=offset;
			}	
			return ret;
		}
	}
ï»¿//WZ_JSGraphics.js
/* This notice must be untouched at all times.
<script>

wz_jsgraphics.js    v. 2.3
The latest version is available at
http://www.walterzorn.com
or http://www.devira.com
or http://www.walterzorn.de

Copyright (c) 2002-2004 Walter Zorn. All rights reserved.
Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
Last modified: 29. 9. 2004

Performance optimizations for Internet Explorer
by Thomas Frank and John Holdsworth.
fillPolygon method implemented by Matthieu Haller.

High Performance JavaScript Graphics Library.
Provides methods
- to draw lines, rectangles, ellipses, polygons
  with specifiable line thickness,
- to fill rectangles and ellipses
- to draw text.
NOTE: Operations, functions and branching have rather been optimized
to efficiency and speed than to shortness of source code.

LICENSE: LGPL

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
or see http://www.gnu.org/copyleft/lesser.html 
*/


var jg_ihtm, jg_ie, jg_fast, jg_dom, jg_moz,
jg_n4 = (document.layers && typeof document.classes != "undefined");


function chkDHTM(x, i)
{
	x = document.body || null;
	jg_ie = x && typeof x.insertAdjacentHTML != "undefined";
	jg_dom = (x && !jg_ie &&
		typeof x.appendChild != "undefined" &&
		typeof document.createRange != "undefined" &&
		typeof (i = document.createRange()).setStartBefore != "undefined" &&
		typeof i.createContextualFragment != "undefined");
	jg_ihtm = !jg_ie && !jg_dom && x && typeof x.innerHTML != "undefined";
	jg_fast = jg_ie && document.all && !window.opera;
	jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";
}


function pntDoc()
{
	this.wnd.document.write(jg_fast? this.htmRpc() : this.htm.join(""));
	this.htm = new Array();
}


function pntCnvDom()
{
	var x = document.createRange();
	x.setStartBefore(this.cnv);
	x = x.createContextualFragment(jg_fast? this.htmRpc() : this.htm.join(""));
	this.cnv.appendChild(x);
	this.htm = new Array();
}


function pntCnvIe()
{
	this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast? this.htmRpc() : this.htm.join(""));
	this.htm = new Array();
}


function pntCnvIhtm()
{
	// BAD
	this.cnv.innerHTML += this.htm.join("");
	this.htm = new Array();
}


function pntCnv()
{
	this.htm = new Array();;
}


function mkDiv(x, y, w, h, id)
{
	id=id ? "id='"+id+"'" : "";
	if(this.checkCnv(x,y))
		this.htm[this.htm.length] = '<div '+id+' style="position:absolute;'+
			'font-size:0px;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:' + w + 'px;'+
			'height:' + h + 'px;'+
			'clip:rect(0,'+w+'px,'+h+'px,0);'+
			'background-color:' + this.color +
			(!jg_moz? ';overflow:hidden' : '')+
			';"><\/div>';
}

function mkDivIe(x, y, w, h)
{
	if(this.checkCnv(x,y))
		this.htm[this.htm.length] = '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';
}

// nur zum Drucken
function mkDivPrt(x, y, w, h)
{
	if(this.checkCnv(x,y))
		this.htm[this.htm.length] = '<div style="position:absolute;'+
			'font-size:0px;'+
			'border-left:' + w + 'px solid ' + this.color + ';'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:0px;'+
			'height:' + h + 'px;'+
			'clip:rect(0,'+w+'px,'+h+'px,0);'+
			'background-color:' + this.color +
			(!jg_moz? ';overflow:hidden' : '')+
			';"><\/div>';
}

// Netscape 4 ist egal
function mkLyr(x, y, w, h)
{
	if(this.checkCnv(x,y))
		this.htm[this.htm.length] = '<layer '+
			'left="' + x + '" '+
			'top="' + y + '" '+
			'width="' + w + '" '+
			'height="' + h + '" '+
			'bgcolor="' + this.color + '"><\/layer>\n';
}


var regex =  /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
function htmRpc()
{
	return this.htm.join("").replace(
		regex,
		'<div style="font-size:0px;overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2;top:$3;width:$4;height:$5"></div>\n');
}


function htmPrtRpc()
{
	return this.htm.join("").replace(
		regex,
		'<div style="font-size:0px;overflow:hidden;position:absolute;background-color:'+
		'$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1"></div>\n');
}


function mkLin(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	
	// Bounding Box checken
	//if(x2<0 || x1>this.cnv.parentElement.parentElement.offsetWidth  || Math.max(y1,y2)<0 || Math.min(y1,y2)>this.cnv.parentElement.parentElement.offsetHeight)
	if(x2<0 || x1>this.cnv.offsetWidth  || Math.max(y1,y2)<0 || Math.min(y1,y2)>this.cnv.offsetHeight)
		return;
	
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	if (dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while ((dx--) > 0)
		{
			++x;
			if (p > 0)
			{
				this.mkDiv(ox, y, x-ox, 1);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this.mkDiv(ox, y, x2-ox+1, 1);
	}

	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if (y2 <= y1)
		{
			while ((dy--) > 0)
			{
				if (p > 0)
				{
					this.mkDiv(x++, y, 1, oy-y+1);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this.mkDiv(x2, y2, 1, oy-y2+1);
		}
		else
		{
			while ((dy--) > 0)
			{
				y += yIncr;
				if (p > 0)
				{
					this.mkDiv(x++, oy, 1, y-oy);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this.mkDiv(x2, oy, 1, y2-oy+1);
		}
	}
}


function mkLin2D(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	
	// Bounding Box checken
	//if(x2<0 || x1>this.cnv.parentElement.parentElement.offsetWidth  || Math.max(y1,y2)<0 || Math.min(y1,y2)>this.cnv.parentElement.parentElement.offsetHeight)
	if(x2<0 || x1>this.cnv.offsetWidth  || Math.max(y1,y2)<0 || Math.min(y1,y2)>this.cnv.offsetHeight)
		return;
	
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1;

	var s = this.stroke;
	if (dx >= dy)
	{
		if (s-3 > 0)
		{
			var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.ceil(s/2);

		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx,
		ox = x;
		while ((dx--) > 0)
		{
			++x;
			if (p > 0)
			{
				this.mkDiv(ox, y, x-ox+ad, _s);
				y += yIncr;
				p += pru;
				ox = x;
			}
			else p += pr;
		}
		this.mkDiv(ox, y, x2-ox+ad+1, _s);
	}

	else
	{
		if (s-3 > 0)
		{
			var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;
			_s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
		}
		else var _s = s;
		var ad = Math.round(s/2);

		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy,
		oy = y;
		if (y2 <= y1)
		{
			++ad;
			while ((dy--) > 0)
			{
				if (p > 0)
				{
					this.mkDiv(x++, y, _s, oy-y+ad);
					y += yIncr;
					p += pru;
					oy = y;
				}
				else
				{
					y += yIncr;
					p += pr;
				}
			}
			this.mkDiv(x2, y2, _s, oy-y2+ad);
		}
		else
		{
			while ((dy--) > 0)
			{
				y += yIncr;
				if (p > 0)
				{
					this.mkDiv(x++, oy, _s, y-oy+ad);
					p += pru;
					oy = y;
				}
				else p += pr;
			}
			this.mkDiv(x2, oy, _s, y2-oy+ad+1);
		}
	}
}


function mkLinDott(x1, y1, x2, y2)
{
	if (x1 > x2)
	{
		var _x2 = x2;
		var _y2 = y2;
		x2 = x1;
		y2 = y1;
		x1 = _x2;
		y1 = _y2;
	}
	var dx = x2-x1, dy = Math.abs(y2-y1),
	x = x1, y = y1,
	yIncr = (y1 > y2)? -1 : 1,
	drw = true;
	if (dx >= dy)
	{
		var pr = dy<<1,
		pru = pr - (dx<<1),
		p = pr-dx;
		while ((dx--) > 0)
		{
			if (drw) this.mkDiv(x, y, 1, 1);
			drw = !drw;
			if (p > 0)
			{
				y += yIncr;
				p += pru;
			}
			else p += pr;
			++x;
		}
		if (drw) this.mkDiv(x, y, 1, 1);
	}

	else
	{
		var pr = dx<<1,
		pru = pr - (dy<<1),
		p = pr-dy;
		while ((dy--) > 0)
		{
			if (drw) this.mkDiv(x, y, 1, 1);
			drw = !drw;
			y += yIncr;
			if (p > 0)
			{
				++x;
				p += pru;
			}
			else p += pr;
		}
		if (drw) this.mkDiv(x, y, 1, 1);
	}
}


function mkOv(left, top, width, height)
{
	var a = width>>1, b = height>>1,
	wod = width&1, hod = (height&1)+1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	ox = 0, oy = b,
	aa = (a*a)<<1, bb = (b*b)<<1,
	st = (aa>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa*((b<<1)-1),
	w, h;
	while (y > 0)
	{
		if (st < 0)
		{
			st += bb*((x<<1)+3);
			tt += (bb<<1)*(++x);
		}
		else if (tt < 0)
		{
			st += bb*((x<<1)+3) - (aa<<1)*(y-1);
			tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
			w = x-ox;
			h = oy-y;
			if (w&2 && h&2)
			{
				this.mkOvQds(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1);
				this.mkOvQds(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1);
			}
			else this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h);
			ox = x;
			oy = y;
		}
		else
		{
			tt -= aa*((y<<1)-3);
			st -= (aa<<1)*(--y);
		}
	}
	this.mkDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod);
	this.mkDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod);
}


function mkOv2D(left, top, width, height)
{
	var s = this.stroke;
	width += s-1;
	height += s-1;
	var a = width>>1, b = height>>1,
	wod = width&1, hod = (height&1)+1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa = (a*a)<<1, bb = (b*b)<<1,
	st = (aa>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa*((b<<1)-1);

	if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))
	{
		var ox = 0, oy = b,
		w, h,
		pxl, pxr, pxt, pxb, pxw;
		while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - (aa<<1)*(y-1);
				tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
				w = x-ox;
				h = oy-y;

				if (w-1)
				{
					pxw = w+1+(s&1);
					h = s;
				}
				else if (h-1)
				{
					pxw = s;
					h += 1+(s&1);
				}
				else pxw = h = s;
				this.mkOvQds(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h);
				ox = x;
				oy = y;
			}
			else
			{
				tt -= aa*((y<<1)-3);
				st -= (aa<<1)*(--y);
			}
		}
		this.mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);
		this.mkDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod);
	}

	else
	{
		var _a = (width-((s-1)<<1))>>1,
		_b = (height-((s-1)<<1))>>1,
		_x = 0, _y = _b,
		_aa = (_a*_a)<<1, _bb = (_b*_b)<<1,
		_st = (_aa>>1)*(1-(_b<<1)) + _bb,
		_tt = (_bb>>1) - _aa*((_b<<1)-1),

		pxl = new Array(),
		pxt = new Array(),
		_pxb = new Array();
		pxl[0] = 0;
		pxt[0] = b;
		_pxb[0] = _b-1;
		while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
				pxl[pxl.length] = x;
				pxt[pxt.length] = y;
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - (aa<<1)*(y-1);
				tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
				pxl[pxl.length] = x;
				pxt[pxt.length] = y;
			}
			else
			{
				tt -= aa*((y<<1)-3);
				st -= (aa<<1)*(--y);
			}

			if (_y > 0)
			{
				if (_st < 0)
				{
					_st += _bb*((_x<<1)+3);
					_tt += (_bb<<1)*(++_x);
					_pxb[_pxb.length] = _y-1;
				}
				else if (_tt < 0)
				{
					_st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1);
					_tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3);
					_pxb[_pxb.length] = _y-1;
				}
				else
				{
					_tt -= _aa*((_y<<1)-3);
					_st -= (_aa<<1)*(--_y);
					_pxb[_pxb.length-1]--;
				}
			}
		}

		var ox = 0, oy = b,
		_oy = _pxb[0],
		l = pxl.length,
		w, h;
		for (var i = 0; i < l; i++)
		{
			if (typeof _pxb[i] != "undefined")
			{
				if (_pxb[i] < _oy || pxt[i] < oy)
				{
					x = pxl[i];
					this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy);
					ox = x;
					oy = pxt[i];
					_oy = _pxb[i];
				}
			}
			else
			{
				x = pxl[i];
				this.mkDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod);
				this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
				ox = x;
				oy = pxt[i];
			}
		}
		this.mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);
		this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
	}
}


function mkOvDott(left, top, width, height)
{
	var a = width>>1, b = height>>1,
	wod = width&1, hod = height&1,
	cx = left+a, cy = top+b,
	x = 0, y = b,
	aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
	st = (aa2>>1)*(1-(b<<1)) + bb,
	tt = (bb>>1) - aa2*((b<<1)-1),
	drw = true;
	while (y > 0)
	{
		if (st < 0)
		{
			st += bb*((x<<1)+3);
			tt += (bb<<1)*(++x);
		}
		else if (tt < 0)
		{
			st += bb*((x<<1)+3) - aa4*(y-1);
			tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
		}
		else
		{
			tt -= aa2*((y<<1)-3);
			st -= aa4*(--y);
		}
		if (drw) this.mkOvQds(cx, cy, -x, x+wod, -y, y+hod, 1, 1);
		drw = !drw;
	}
}


function mkRect(x, y, w, h)
{
	var s = this.stroke;
	this.mkDiv(x, y, w, s);
	this.mkDiv(x+w, y, s, h);
	this.mkDiv(x, y+h, w+s, s);
	this.mkDiv(x, y+s, s, h-s);
}


function mkRectDott(x, y, w, h)
{
	this.drawLine(x, y, x+w, y);
	this.drawLine(x+w, y, x+w, y+h);
	this.drawLine(x, y+h, x+w, y+h);
	this.drawLine(x, y, x, y+h);
}


function jsgFont()
{
	this.PLAIN = 'font-weight:normal;';
	this.BOLD = 'font-weight:bold;';
	this.ITALIC = 'font-style:italic;';
	this.ITALIC_BOLD = this.ITALIC + this.BOLD;
	this.BOLD_ITALIC = this.ITALIC_BOLD;
}
var Font = new jsgFont();


function jsgStroke()
{
	this.DOTTED = -1;
}
var Stroke = new jsgStroke();


function jsGraphics(id, wnd)
{
	this.setColor = new Function('arg', 'this.color = arg.toLowerCase();');

	this.checkCnv = function(x,y)
	{
		//if(x<0 || x>this.cnv.parentElement.parentElement.offsetWidth || y<0 || y>this.cnv.parentElement.parentElement.offsetHeight)
		if(x<0 || x>this.cnv.offsetWidth || y<0 || y>this.cnv.offsetHeight)
			return false;
		else
			return true;
	}	
	
	this.setStroke = function(x)
	{
		this.stroke = x;
		if (!(x+1))
		{
			this.drawLine = mkLinDott;
			this.mkOv = mkOvDott;
			this.drawRect = mkRectDott;
		}
		else if (x-1 > 0)
		{
			this.drawLine = mkLin2D;
			this.mkOv = mkOv2D;
			this.drawRect = mkRect;
		}
		else
		{
			this.drawLine = mkLin;
			this.mkOv = mkOv;
			this.drawRect = mkRect;
		}
	};


	this.setPrintable = function(arg)
	{
		this.printable = arg;
		if (jg_fast)
		{
			this.mkDiv = mkDivIe;
			this.htmRpc = arg? htmPrtRpc : htmRpc;
		}
		else this.mkDiv = jg_n4? mkLyr : arg? mkDivPrt : mkDiv;
	};


	this.setFont = function(fam, sz, sty)
	{
		this.ftFam = fam;
		this.ftSz = sz;
		this.ftSty = sty || Font.PLAIN;
	};


	this.drawPolyline = this.drawPolyLine = function(x, y, s)
	{
		for (var i=0 ; i<x.length-1 ; i++ )
			this.drawLine(x[i], y[i], x[i+1], y[i+1]);
	};


	this.fillRect = function(x, y, w, h, id)
	{
		this.mkDiv(x, y, w, h, id);
	};


	this.drawPolygon = function(x, y)
	{
		this.drawPolyline(x, y);
		this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);
	};


	this.drawEllipse = this.drawOval = function(x, y, w, h)
	{
		this.mkOv(x, y, w, h);
	};
	
	this.insertAnyHtml=function(html)
	{
		this.htm[this.htm.length]=html;
	};

	this.fillEllipse = this.fillOval = function(left, top, w, h)
	{
		var a = (w -= 1)>>1, b = (h -= 1)>>1,
		wod = (w&1)+1, hod = (h&1)+1,
		cx = left+a, cy = top+b,
		x = 0, y = b,
		ox = 0, oy = b,
		aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
		st = (aa2>>1)*(1-(b<<1)) + bb,
		tt = (bb>>1) - aa2*((b<<1)-1),
		pxl, dw, dh;
		if (w+1) while (y > 0)
		{
			if (st < 0)
			{
				st += bb*((x<<1)+3);
				tt += (bb<<1)*(++x);
			}
			else if (tt < 0)
			{
				st += bb*((x<<1)+3) - aa4*(y-1);
				pxl = cx-x;
				dw = (x<<1)+wod;
				tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
				dh = oy-y;
				this.mkDiv(pxl, cy-oy, dw, dh);
				this.mkDiv(pxl, cy+oy-dh+hod, dw, dh);
				ox = x;
				oy = y;
			}
			else
			{
				tt -= aa2*((y<<1)-3);
				st -= aa4*(--y);
			}
		}
		this.mkDiv(cx-a, cy-oy, w+1, (oy<<1)+hod);
	};



/* fillPolygon method, implemented by Matthieu Haller.
This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.
C source of GD 1.8.4 found at http://www.boutell.com/gd/

THANKS to Kirsten Schulz for the polygon fixes!

The intersection finding technique of this code could be improved
by remembering the previous intertersection, and by using the slope.
That could help to adjust intersections to produce a nice
interior_extrema. */
	this.fillPolygon = function(array_x, array_y)
	{
		var i;
		var y;
		var miny, maxy;
		var x1, y1;
		var x2, y2;
		var ind1, ind2;
		var ints;

		var n = array_x.length;

		if (!n) return;


		miny = array_y[0];
		maxy = array_y[0];
		for (i = 1; i < n; i++)
		{
			if (array_y[i] < miny)
				miny = array_y[i];

			if (array_y[i] > maxy)
				maxy = array_y[i];
		}
		for (y = miny; y <= maxy; y++)
		{
			var polyInts = new Array();
			ints = 0;
			for (i = 0; i < n; i++)
			{
				if (!i)
				{
					ind1 = n-1;
					ind2 = 0;
				}
				else
				{
					ind1 = i-1;
					ind2 = i;
				}
				y1 = array_y[ind1];
				y2 = array_y[ind2];
				if (y1 < y2)
				{
					x1 = array_x[ind1];
					x2 = array_x[ind2];
				}
				else if (y1 > y2)
				{
					y2 = array_y[ind1];
					y1 = array_y[ind2];
					x2 = array_x[ind1];
					x1 = array_x[ind2];
				}
				else continue;

				 // modified 11. 2. 2004 Walter Zorn
				if ((y >= y1) && (y < y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);

				else if ((y == maxy) && (y > y1) && (y <= y2))
					polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
			}
			polyInts.sort(integer_compare);

			for (i = 0; i < ints; i+=2)
			{
				w = polyInts[i+1]-polyInts[i]
				this.mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);
			}
		}
	};


	this.drawString = function(txt, x, y)
	{
		this.htm[this.htm.length] = '<div style="position:absolute;white-space:nowrap;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'font-family:' +  this.ftFam + ';'+
			'font-size:' + this.ftSz + ';'+
			'color:' + this.color + ';' + this.ftSty + '">'+
			txt +
			'<\/div>';
	}


	this.drawImage = function(imgSrc, x, y, w, h)
	{
		this.htm[this.htm.length] = '<div style="position:absolute;'+
			'left:' + x + 'px;'+
			'top:' + y + 'px;'+
			'width:' +  w + ';'+
			'height:' + h + ';">'+
			'<img src="' + imgSrc + '" width="' + w + '" height="' + h + '">'+
			'<\/div>';
	}


	this.clear = function()
	{
		this.htm = new Array();
		if (this.cnv) this.cnv.innerHTML = this.defhtm;
	};


	this.mkOvQds = function(cx, cy, xl, xr, yt, yb, w, h)
	{
		this.mkDiv(xr+cx, yt+cy, w, h);
		this.mkDiv(xr+cx, yb+cy, w, h);
		this.mkDiv(xl+cx, yb+cy, w, h);
		this.mkDiv(xl+cx, yt+cy, w, h);
	};

	this.setStroke(1);
	this.setFont('verdana,geneva,helvetica,sans-serif', String.fromCharCode(0x31, 0x32, 0x70, 0x78), Font.PLAIN);
	this.color = '#000000';
	this.htm = new Array();
	this.wnd = wnd || window;

	if (!(jg_ie || jg_dom || jg_ihtm)) chkDHTM();
	if (typeof id != 'object' || !id) this.paint = pntDoc;
	else
	{
		this.cnv=id;
		/*this.cnv = document.all? (this.wnd.document.all[id] || null)
			: document.getElementById? (this.wnd.document.getElementById(id) || null)
			: null;
		*/
		this.defhtm = (this.cnv && this.cnv.innerHTML)? this.cnv.innerHTML : '';
		this.paint = jg_dom? pntCnvDom : jg_ie? pntCnvIe : jg_ihtm? pntCnvIhtm : pntCnv;
	}

	this.setPrintable(false);
}



function integer_compare(x,y)
{
	return (x < y) ? -1 : ((x > y)*1);
}

