﻿function toQuotes(s) {
	var ss = s.toString();
	var s1 = new String();
	for (var i = 0; i < ss.length; ++i){
		var c = ss.charAt(i);
		if (c == '\\' || c == '"' || c == '\t' || c == '\r' || c == '\n')
			s1 += "\\";
		if (c == '\t')
			c = 't';
		if (c == '\r')
			c = 'r';
		if (c == '\n')
			c = 'n';
		s1 += c;
	}
	return "\""+s1+"\"";
}

function isMireoArrayType(tp){
	return tp.charAt(0) == '[';
}

function isMireoDataTableType(tp){
	return tp == "DataTable";
}

function getMireoArrayElementType(tp){
	return tp.substring(1);
}

var MIREOPUSH = '\uFDE0';
		
var MIREOPOP = '\uFDE1';

var MIREOARRAYSEPARATOR = '\uFDE2';

var MIREOPARAMEND = '\uFDE3';

var MIREOEXCEPTION = '\uFDE4';

var MIREOPUSHPOPRE = "["+MIREOPUSH+MIREOPOP+"]"

function DataTable(columnnames, columntypes, tablename){
	if (tablename)
		this.tablename = tablename;
	else
		this.tablename = "";
	this.columnnames = new Array(columnnames.length);
	for (var i = 0; i < columnnames.length; ++i)
		this.columnnames[i] = columnnames[i];
	
	this.columntypes = new Array(columntypes.length);
	for (var i = 0; i < columntypes.length; ++i)
		this.columntypes[i] = columntypes[i];
	
	this.rows = new Array();
	this.NewRow = function(){
		var ret = new DataRow(this);
		return ret;
	};
	this.ImportRow = function(row){
		var r = new DataRow(this);
		for(var i = 0; i < this.columnnames.length; ++i){
			r[this.columnnames[i]] = row[this.columnnames[i]];
		}
		this.rows.push(r);
	}
	
	this.AddColumn = function(col_name, col_type){
		this.columntypes.push(col_type);
		this.columnnames.push(col_name);
	}
	
	function DataRow(table){
		this.dataTable = table;
		for (var i = 0; i < table.columnnames.length; ++i)
			this[table.columnnames[i]] = null;
		this.Delete = function(){
			for (var i = 0; i < this.dataTable.rows.length; ++i){
				if (this.dataTable.rows[i] === this){
					this.dataTable.rows.splice(i,1);
				}
			}
		}
	}
}

DataTable.FindTable = function(dataset, tablename){
	for(var i = 0; i < dataset.length; ++i){
		if (dataset[i].tablename == tablename){
			return dataset[i];
		}
	}
	return null;
}


function serializeMireoCollection(objtype, sz, fetch, ret){
	if(sz == 0){
		return;
	}
	var first = fetch(0);
	if(objtype == "number" || objtype == "string" || objtype == "bool"){
		for(var i = 0; i < sz; ++i){
			serializeMireoObject(fetch(i), objtype, ret);
			ret.push(MIREOARRAYSEPARATOR);
		}
		return;
	}
	if(isMireoArrayType(objtype) || isMireoDataTableType(objtype)){
		for(var i = 0; i < sz; ++i){
			ret.push(MIREOPUSH);
			serializeMireoObject(fetch(i), objtype, ret);
			ret.push(MIREOPOP);
		}
		return;
	}
	
	var cl = typenameToClass(objtype);
	
	var ma = cl.MEMBER_ARRAY;
	var ta = cl.TYPE_ARRAY;
	
	for(var i = 0; i < ma.length; ++i){
		ret.push(MIREOPUSH);
		serializeMireoCollection(ta[i], sz, function(idx){return fetch(idx)[ma[i]];}, ret);
		ret.push(MIREOPOP);
	}
}

function serializeMireoObject(obj, objtype, ret){
	if(isMireoArrayType(objtype)){
		serializeMireoCollection(getMireoArrayElementType(objtype), obj.length, function(idx){return obj[idx];}, ret);
		return;
	}
	if(isMireoDataTableType(objtype)){
		ret.push(MIREOPUSH);
		serializeMireoObject(obj.tablename, "string", ret);
		ret.push(MIREOPOP);
		ret.push(MIREOPUSH);
		serializeMireoObject(obj.columnnames, "[string", ret);
		ret.push(MIREOPOP);
		ret.push(MIREOPUSH);
		serializeMireoObject(obj.columntypes, "[string", ret);
		ret.push(MIREOPOP);
		for(var i = 0; i < obj.columnnames.length; ++i){
			var name = obj.columnnames[i];
			ret.push(MIREOPUSH);
			serializeMireoCollection(obj.columntypes[i], obj.rows.length, function(idx){return obj.rows[idx][name];}, ret);
			ret.push(MIREOPOP);
		}		
		return;
	}
	
	if(obj == null){
		return;
	}
	
	if(objtype == "string"){
		ret.push(obj);
		return;
	}
	if(objtype == "bool"){
		ret.push(obj ? "1" : "0");
		return;
	}
	if(objtype == "number"){
		ret.push(obj.toString().replace(/,/g,"."));
		return;
	}
	
	var cl = typenameToClass(objtype);
	
	var ma = cl.MEMBER_ARRAY;
	var ta = cl.TYPE_ARRAY;
	
	for(var i = 0; i < ma.length; ++i){
		ret.push(MIREOPUSH);
		serializeMireoObject(obj[ma[i]], ta[i], ret);
		ret.push(MIREOPOP);
	}
}

function post_to_server(urldir, path, cmd, callback) {
	var url;
	if(typeof MIREO_REDIRECT_BY != "undefined"){
		url = urldir+"/"+MIREO_REDIRECT_BY;
		cmd = MIREO_REDIRECT_TO + "/" + path + "*" + cmd;
	}else if(typeof MIREO_REDIRECT_BY_URL != "undefined"){
		url = MIREO_REDIRECT_BY_URL;
		cmd = MIREO_REDIRECT_TO + "/" + path + "*" + cmd;
	}else{
		url = urldir+"/"+path;
	}
	var _xmlHttp = getXMLHTTP();
	if(_xmlHttp) {
		if (callback){
			if(post_to_server.open_first){
				_xmlHttp.open("POST", url, true);
			}
			_xmlHttp.onreadystatechange = function(){
				if (_xmlHttp.readyState == 4){
					var responseText = _xmlHttp.responseText;
					var status = _xmlHttp.status;
					var statusText = _xmlHttp.statusText;
					releaseXMLHTTP(_xmlHttp);
					switch(status){
						case 200:
							callback(responseText);
							break;
						case 12029:
						case 12030:
						case 12031:
						case 12152:
						case 12159:
							post_to_server(urldir, path, cmd, callback);
							break;
						default:
						{
							var ex = new WebException(statusText);
							ex.status = status;
							callback(ex);
						}
					}
				}
			};
			if(!post_to_server.open_first){
				_xmlHttp.open("POST", url, true);
			}
		}else{
			_xmlHttp.open("POST", url, false);
		}
		_xmlHttp.setRequestHeader("ContentType","text/plain");
		_xmlHttp.send(cmd);
		if (!callback){
			var readyState = _xmlHttp.readyState;
			var status = _xmlHttp.status;
			var statusText = _xmlHttp.statusText;
			var responseText = _xmlHttp.responseText;
			releaseXMLHTTP(_xmlHttp);
			if(readyState == 4 && status != 200){
				var ex = new WebException(statusText);
				ex.status = status;
				throw ex;
			}else{
				return responseText;
			}
		}
	}else{
		return null;
	}
	
	function getXMLHTTP() {
		if(post_to_server.XMLHTTPObjects.length == 0){
			return createXMLHTTP();
		}
		return post_to_server.XMLHTTPObjects.pop();
	}
	
	function createXMLHTTP() {
		var A = null;
		try {
			A = new ActiveXObject("Msxml2.XMLHTTP");
			post_to_server.open_first = true;
		}	
		catch(e){
			try {
				A = new ActiveXObject("Microsoft.XMLHTTP");
				post_to_server.open_first = true;
			} 
			catch(e){
				A = null;
			}
		}
		if(!A && typeof XMLHttpRequest != "undefined") {
			A = new XMLHttpRequest();
			post_to_server.open_first = false;
		}
		return A;
	}
	
	function releaseXMLHTTP(obj) {
		if(post_to_server.open_first){
			obj.abort();
		}
		obj.onreadystatechange = new Function();
		
		if(!window.console || !window.console.firebug){
			post_to_server.XMLHTTPObjects.push(obj);
		}
	}
}

post_to_server.XMLHTTPObjects = new Array();

function typenameToClass(objtype){
	var ret = typenameToClass.classes[objtype];
	if(ret == null){
		typenameToClass.classes[objtype] = ret = eval(objtype);
	}
	return ret;
}

typenameToClass.classes = [];

function deserializeMireoObject(objtype, objvalue, from, to){
	function Scanner(){
		this.curr = from;
	
		this.token_start = from;
		this.token_end = from
		
		this.next = function(){
			if(this.end()){
				return;
			}
			if(objvalue.charAt(this.curr) != MIREOPUSH){
				throw new WebException("Wrong return value format:"+(this.curr-from)+" "+objvalue.substring(from, to)+" "+objvalue);
			}
			var level = 1;
			var re = new RegExp(MIREOPUSHPOPRE, "g");
			this.token_start = this.curr + 1;
			re.lastIndex = this.token_start;
			while(level > 0){
				re.exec(objvalue);
				var idx = re.lastIndex - 1;
				if(idx == -1){
					throw new WebException("Wrong return value format:"+objvalue.substring(from, to)+" "+objvalue);
				}
				var c = objvalue.charAt(idx);
				if(c == MIREOPUSH){
					++level;
				}else if(c == MIREOPOP){
					--level;
				}
			}
			this.curr = re.lastIndex;
			this.token_end = this.curr - 1;
		}
		
		this.end = function(){
			return this.curr == to;
		}
		
		this.next();
	}

	if(from < to && objvalue.charAt(0) == MIREOEXCEPTION){
		throw new WebException(objvalue.substring(from+1, to));
	}
	if(objtype == "void"){
		return null;
	}
	if(objtype == "string"){
		return objvalue.substring(from, to);
	}
	if(objtype == "bool"){
		return objvalue.substring(from, to) == "1";
	}
	if(objtype == "number"){
		return parseFloat(objvalue.substring(from, to));
	}
	
	if(isMireoArrayType(objtype)){
		var tp = getMireoArrayElementType(objtype);
		if(tp == "number" || tp == "bool" || tp == "string"){
			var ret = objvalue.substring(from, to).split(MIREOARRAYSEPARATOR);
			ret.pop();
			if(tp == "string"){
				return ret;
			}
			if(tp == "number"){
				for(var i = 0; i < ret.length; ++i){
					ret[i] = parseFloat(ret[i]);
				}
				return ret;
			}
			for(var i = 0; i < ret.length; ++i){
				ret[i] = (ret[i] == "1");
			}
			return ret;
		}
		if(isMireoArrayType(tp) || isMireoDataTableType(tp)){
			var ret = [];
			for(var scanner = new Scanner(); !scanner.end(); scanner.next()){
				ret.push(deserializeMireoObject(tp, objvalue, scanner.token_start, scanner.token_end));
			}
			return ret;
		}
		
		var cl = typenameToClass(tp);
		
		if(cl && cl.TYPE_ARRAY){
			var scanner = new Scanner();
			var ret = null;
			for(var i = 0; i < cl.TYPE_ARRAY.length; ++i, scanner.next()){
				var arr = deserializeMireoObject("["+cl.TYPE_ARRAY[i], objvalue, scanner.token_start, scanner.token_end);
				if(ret == null){
					ret = [];
					for(var j = 0; j < arr.length; ++j){
						ret.push(new cl());
					}
				}
				for(var j = 0; j < arr.length; ++j){
					ret[j][cl.MEMBER_ARRAY[i]] = arr[j];
				}
			}
			return ret;
		}
		return [];
	}
	
	if(isMireoDataTableType(objtype)){
		var scanner = new Scanner();
		var tablename = deserializeMireoObject("string", objvalue, scanner.token_start, scanner.token_end);
		scanner.next();
		var columnnames = deserializeMireoObject("[string", objvalue, scanner.token_start, scanner.token_end);
		scanner.next();
		var columntypes = deserializeMireoObject("[string", objvalue, scanner.token_start, scanner.token_end);
		
		var ret = new DataTable(columnnames, columntypes, tablename);
		
		for(var i = 0; i < columnnames.length; ++i){
			scanner.next();
			var arr = deserializeMireoObject("["+columntypes[i], objvalue, scanner.token_start, scanner.token_end);
			if(ret.rows.length == 0){
				for(var j = 0; j < arr.length; ++j){
					ret.rows.push(ret.NewRow());
				}
			}
			for(var j = 0; j < arr.length; ++j){
				ret.rows[j][columnnames[i]] = arr[j];
			}
		}
		
		return ret;
	}
	
	var cl = typenameToClass(objtype);
		
	if(cl && cl.TYPE_ARRAY){
		var scanner = new Scanner();
		var ret = new cl();
		for(var i = 0; i < cl.TYPE_ARRAY.length; ++i, scanner.next()){
			ret[cl.MEMBER_ARRAY[i]] = deserializeMireoObject(cl.TYPE_ARRAY[i], objvalue, scanner.token_start, scanner.token_end);
		}
		return ret;
	}
	return null;
}

function WebException(message){
	this.message = message;
}

WebException.prototype.toString = function(){
	return this.message;
}

WebException.validate = function(o){
	if (o.constructor == WebException)
		throw o;
}

function remote_call(url, f, params, rt, nparams, ptypes){
	
	var cmd = [];
	cmd.push(f);
	cmd.push(":");
	for(var i = 0; i < nparams; ++i){
		serializeMireoObject(params[i], ptypes[i], cmd);
		cmd.push(MIREOPARAMEND);
	}
	cmd = cmd.join("");
	if (params.length > nparams){
		post_to_server(_url_dir_,url, cmd,
			function(retstr){
				try{
					WebException.validate(retstr);
					var o = deserializeMireoObject(rt,retstr,0,retstr.length);
				}catch(e){
					params[nparams](e);
					return;
				}
				params[nparams](o);				
			}
		)
	}else{
		var ret = null;
		for(;;){
			try{
				ret = post_to_server(_url_dir_, url, cmd);
			}catch(e){
				if(e.status){
					switch(e.status){
						case 12029:
						case 12030:
						case 12031:
						case 12152:
						case 12159:
							continue;
					}
				}
				throw e;
			}
			return deserializeMireoObject(rt, ret, 0, ret.length);
		}
	}
}

var type_map = new Object();
function setClassMembers(obj, params){
	var ma = obj.constructor.MEMBER_ARRAY;
	var ta = obj.constructor.TYPE_ARRAY;
	for (var i = 0; i < ma.length; ++i){
		if (i < params.length){
			obj[ma[i]] = params[i];
		}else{
			obj[ma[i]] = createDefaultParam(ta[i]);
		}
	}
	
	function createDefaultParam(tp){
		if (tp.charAt(0) == '['){
			return new Array();
		}
		if (tp == "string"){
			return "";
		}
		if (tp == "number"){
			return 0;
		}
		if (tp == "bool"){
			return false;
		}
		if (tp == "DataTable"){
			return new DataTable([],[],"");
		}
		var ret = null;
		if (ret = type_map[tp])
		    return ret;
		else
		    type_map[tp] = ret = eval("new " + tp + "();");
		return ret;
	}
}

var _r_f_body_ = "setClassMembers(this,arguments);";

var _url_dir_ = 'http://' + window.location.hostname;
if(window.location.port != null && window.location.port != "" && window.location.port != 80){
    _url_dir_ += (':' + window.location.port);
}
_url_dir_ += window.location.pathname;
_url_dir_ = _url_dir_.substring(0, _url_dir_.lastIndexOf('/'));

function createFunctionString(classname, functionname, rettype, nparameters, ptypes){
	var prms = new Array();
	for(var i = 0; i < ptypes.length; ++i){
		prms.push(toQuotes(ptypes[i]));
	}

	if (rettype == "void"){
		return "remote_call("+classname+".URL,"+toQuotes(functionname)+",arguments,"+toQuotes(rettype)+","+nparameters+",["+prms.join(',')+"]);";
	}else{
		return "return remote_call("+classname+".URL,"+toQuotes(functionname)+",arguments,"+toQuotes(rettype)+","+nparameters+",["+prms.join(',')+"]);";
	}
}


var Point=new Function("",_r_f_body_);Point.TYPE_ARRAY=["number","number"];Point.MEMBER_ARRAY=["x","y"];Point.URL="System.Drawing.Point,ExternTypes.ms2";Point.op_Implicit=new Function("",createFunctionString("Point","op_Implicit","PointF",1,["Point"]));
Point.op_Explicit=new Function("",createFunctionString("Point","op_Explicit","Size",1,["Point"]));
Point.op_Addition=new Function("",createFunctionString("Point","op_Addition","Point",2,["Point","Size"]));
Point.op_Subtraction=new Function("",createFunctionString("Point","op_Subtraction","Point",2,["Point","Size"]));
Point.op_Equality=new Function("",createFunctionString("Point","op_Equality","bool",2,["Point","Point"]));
Point.op_Inequality=new Function("",createFunctionString("Point","op_Inequality","bool",2,["Point","Point"]));
Point.Add=new Function("",createFunctionString("Point","Add","Point",2,["Point","Size"]));
Point.Subtract=new Function("",createFunctionString("Point","Subtract","Point",2,["Point","Size"]));
Point.Ceiling=new Function("",createFunctionString("Point","Ceiling","Point",1,["PointF"]));
Point.Truncate=new Function("",createFunctionString("Point","Truncate","Point",1,["PointF"]));
Point.Round=new Function("",createFunctionString("Point","Round","Point",1,["PointF"]));
var PointF=new Function("",_r_f_body_);PointF.TYPE_ARRAY=["number","number"];PointF.MEMBER_ARRAY=["x","y"];PointF.URL="System.Drawing.PointF,ExternTypes.ms2";PointF.op_Addition=new Function("",createFunctionString("PointF","op_Addition","PointF",2,["PointF","Size"]));
PointF.op_Subtraction=new Function("",createFunctionString("PointF","op_Subtraction","PointF",2,["PointF","Size"]));
PointF.op_Addition=new Function("",createFunctionString("PointF","op_Addition","PointF",2,["PointF","SizeF"]));
PointF.op_Subtraction=new Function("",createFunctionString("PointF","op_Subtraction","PointF",2,["PointF","SizeF"]));
PointF.op_Equality=new Function("",createFunctionString("PointF","op_Equality","bool",2,["PointF","PointF"]));
PointF.op_Inequality=new Function("",createFunctionString("PointF","op_Inequality","bool",2,["PointF","PointF"]));
PointF.Add=new Function("",createFunctionString("PointF","Add","PointF",2,["PointF","Size"]));
PointF.Subtract=new Function("",createFunctionString("PointF","Subtract","PointF",2,["PointF","Size"]));
PointF.Add=new Function("",createFunctionString("PointF","Add","PointF",2,["PointF","SizeF"]));
PointF.Subtract=new Function("",createFunctionString("PointF","Subtract","PointF",2,["PointF","SizeF"]));
var Rectangle=new Function("",_r_f_body_);Rectangle.TYPE_ARRAY=["number","number","number","number"];Rectangle.MEMBER_ARRAY=["x","y","width","height"];Rectangle.URL="System.Drawing.Rectangle,ExternTypes.ms2";Rectangle.FromLTRB=new Function("",createFunctionString("Rectangle","FromLTRB","Rectangle",4,["number","number","number","number"]));
Rectangle.op_Equality=new Function("",createFunctionString("Rectangle","op_Equality","bool",2,["Rectangle","Rectangle"]));
Rectangle.op_Inequality=new Function("",createFunctionString("Rectangle","op_Inequality","bool",2,["Rectangle","Rectangle"]));
Rectangle.Ceiling=new Function("",createFunctionString("Rectangle","Ceiling","Rectangle",1,["RectangleF"]));
Rectangle.Truncate=new Function("",createFunctionString("Rectangle","Truncate","Rectangle",1,["RectangleF"]));
Rectangle.Round=new Function("",createFunctionString("Rectangle","Round","Rectangle",1,["RectangleF"]));
Rectangle.Inflate=new Function("",createFunctionString("Rectangle","Inflate","Rectangle",3,["Rectangle","number","number"]));
Rectangle.Intersect=new Function("",createFunctionString("Rectangle","Intersect","Rectangle",2,["Rectangle","Rectangle"]));
Rectangle.Union=new Function("",createFunctionString("Rectangle","Union","Rectangle",2,["Rectangle","Rectangle"]));
var Size=new Function("",_r_f_body_);Size.TYPE_ARRAY=["number","number"];Size.MEMBER_ARRAY=["width","height"];Size.URL="System.Drawing.Size,ExternTypes.ms2";Size.op_Implicit=new Function("",createFunctionString("Size","op_Implicit","SizeF",1,["Size"]));
Size.op_Addition=new Function("",createFunctionString("Size","op_Addition","Size",2,["Size","Size"]));
Size.op_Subtraction=new Function("",createFunctionString("Size","op_Subtraction","Size",2,["Size","Size"]));
Size.op_Equality=new Function("",createFunctionString("Size","op_Equality","bool",2,["Size","Size"]));
Size.op_Inequality=new Function("",createFunctionString("Size","op_Inequality","bool",2,["Size","Size"]));
Size.op_Explicit=new Function("",createFunctionString("Size","op_Explicit","Point",1,["Size"]));
Size.Add=new Function("",createFunctionString("Size","Add","Size",2,["Size","Size"]));
Size.Ceiling=new Function("",createFunctionString("Size","Ceiling","Size",1,["SizeF"]));
Size.Subtract=new Function("",createFunctionString("Size","Subtract","Size",2,["Size","Size"]));
Size.Truncate=new Function("",createFunctionString("Size","Truncate","Size",1,["SizeF"]));
Size.Round=new Function("",createFunctionString("Size","Round","Size",1,["SizeF"]));
var DPoint=new Function("",_r_f_body_);DPoint.TYPE_ARRAY=["number","number"];DPoint.MEMBER_ARRAY=["x","y"];DPoint.URL="web_invoke.DPoint,ExternTypes.ms2";var DRect=new Function("",_r_f_body_);DRect.TYPE_ARRAY=["number","number","number","number"];DRect.MEMBER_ARRAY=["x1","y1","x2","y2"];DRect.URL="web_invoke.DRect,ExternTypes.ms2";var GeoAddress=new Function("",_r_f_body_);GeoAddress.TYPE_ARRAY=["string","string","string","string","string","string","string","string","string","string","string","DPoint","DRect"];GeoAddress.MEMBER_ARRAY=["_country","_city","_area","_PLZ","_street","_house_no","_POI","_type","_phone","_tag","_source","pt","rc"];GeoAddress.URL="web_invoke.GeoAddress,ExternTypes.ms2";GeoAddress.ArrayToArray=new Function("",createFunctionString("GeoAddress","ArrayToArray","[GeoAddress",1,["[GeoAddressCli"]));
function Global(){}Global.URL="mapserver.Global,MapServer.ms2";
Global.GetMapMercatorBounds=new Function("",createFunctionString("Global","GetMapMercatorBounds","Rectangle",0,[]));
Global.GetMapBounds=new Function("",createFunctionString("Global","GetMapBounds","DRect",0,[]));
function MapSearchWeb(){}MapSearchWeb.URL="mireo.NET.MapSearchWeb,MapSearchWeb.ms2";
MapSearchWeb.Search=new Function("",createFunctionString("MapSearchWeb","Search","[GeoAddress",1,["string"]));
MapSearchWeb.SearchCkey=new Function("",createFunctionString("MapSearchWeb","SearchCkey","[GeoAddress",2,["string","string"]));
MapSearchWeb.GetGeoAddressGeometry=new Function("",createFunctionString("MapSearchWeb","GetGeoAddressGeometry","WebGeoAddressGeometry",2,["GeoAddress","[number"]));
MapSearchWeb.GetGeoAddressGeometryCkey=new Function("",createFunctionString("MapSearchWeb","GetGeoAddressGeometryCkey","WebGeoAddressGeometry",3,["GeoAddress","[number","string"]));
MapSearchWeb.GetGAGeoCompressed=new Function("",createFunctionString("MapSearchWeb","GetGAGeoCompressed","WebGAGeoCompressed",2,["GeoAddress","[number"]));
MapSearchWeb.GetGAGeoCompressedCkey=new Function("",createFunctionString("MapSearchWeb","GetGAGeoCompressedCkey","WebGAGeoCompressed",3,["GeoAddress","[number","string"]));
MapSearchWeb.GetRoute=new Function("",createFunctionString("MapSearchWeb","GetRoute","WebRoute",2,["[GeoAddress","[number"]));
MapSearchWeb.GetRouteCkey=new Function("",createFunctionString("MapSearchWeb","GetRouteCkey","WebRoute",3,["[GeoAddress","[number","string"]));
MapSearchWeb.GetRouteCompressed=new Function("",createFunctionString("MapSearchWeb","GetRouteCompressed","WebRouteCompressed",2,["[GeoAddress","[number"]));
MapSearchWeb.GetRouteCompressedCkey=new Function("",createFunctionString("MapSearchWeb","GetRouteCompressedCkey","WebRouteCompressed",3,["[GeoAddress","[number","string"]));
MapSearchWeb.GetRouteCompressedWithAdvices=new Function("",createFunctionString("MapSearchWeb","GetRouteCompressedWithAdvices","WebRouteCompressedWithAdvices",2,["[GeoAddress","[number"]));
MapSearchWeb.GetRouteCompressedWithAdvicesCkey=new Function("",createFunctionString("MapSearchWeb","GetRouteCompressedWithAdvicesCkey","WebRouteCompressedWithAdvices",3,["[GeoAddress","[number","string"]));
MapSearchWeb.AdvancedGetRoute=new Function("",createFunctionString("MapSearchWeb","AdvancedGetRoute","WebRouteCompressedWithAdvices",4,["[GeoAddress","CalculationOptions","bool","[number"]));
MapSearchWeb.AdvancedGetRouteCkey=new Function("",createFunctionString("MapSearchWeb","AdvancedGetRouteCkey","WebRouteCompressedWithAdvices",5,["[GeoAddress","CalculationOptions","bool","[number","string"]));
MapSearchWeb.FindClosestStreet=new Function("",createFunctionString("MapSearchWeb","FindClosestStreet","GeoAddress",1,["DPoint"]));
MapSearchWeb.FindClosestStreetCkey=new Function("",createFunctionString("MapSearchWeb","FindClosestStreetCkey","GeoAddress",2,["DPoint","string"]));
MapSearchWeb.GetMapMercatorBoundsCkey=new Function("",createFunctionString("MapSearchWeb","GetMapMercatorBoundsCkey","Rectangle",1,["string"]));
MapSearchWeb.GetMapMercatorBounds=new Function("",createFunctionString("MapSearchWeb","GetMapMercatorBounds","Rectangle",0,[]));
MapSearchWeb.GetMapBoundsCkey=new Function("",createFunctionString("MapSearchWeb","GetMapBoundsCkey","DRect",1,["string"]));
MapSearchWeb.GetMapBounds=new Function("",createFunctionString("MapSearchWeb","GetMapBounds","DRect",0,[]));
MapSearchWeb.GetAllRegions=new Function("",createFunctionString("MapSearchWeb","GetAllRegions","[string",0,[]));
MapSearchWeb.GetAllRegionsCkey=new Function("",createFunctionString("MapSearchWeb","GetAllRegionsCkey","[string",1,["string"]));
MapSearchWeb.AdvancedSearchCKey=new Function("",createFunctionString("MapSearchWeb","AdvancedSearchCKey","[GeoAddress",2,["SearchQuery","string"]));
MapSearchWeb.AdvancedSearch=new Function("",createFunctionString("MapSearchWeb","AdvancedSearch","[GeoAddress",1,["SearchQuery"]));
var WebRoute=new Function("",_r_f_body_);WebRoute.TYPE_ARRAY=["number","number","[DPoint","[number","[[Point"];WebRoute.MEMBER_ARRAY=["length","time_length","points","zoom_factors","geometries"];WebRoute.URL="mireo.NET.WebRoute,MapSearchWeb.ms2";var WebRouteCompressed=new Function("",_r_f_body_);WebRouteCompressed.TYPE_ARRAY=["number","number","[DPoint","[number","[number","[number","[number"];WebRouteCompressed.MEMBER_ARRAY=["length","time_length","points","zoom_factors","geometry_x","geometry_y","zoom_levels"];WebRouteCompressed.URL="mireo.NET.WebRouteCompressed,MapSearchWeb.ms2";var WebGeoAddressGeometry=new Function("",_r_f_body_);WebGeoAddressGeometry.TYPE_ARRAY=["[number","[[[Point"];WebGeoAddressGeometry.MEMBER_ARRAY=["zoom_factors","geometries"];WebGeoAddressGeometry.URL="mireo.NET.WebGeoAddressGeometry,MapSearchWeb.ms2";var WebGAGeoCompressed=new Function("",_r_f_body_);WebGAGeoCompressed.TYPE_ARRAY=["[number","[number","[number","[number"];WebGAGeoCompressed.MEMBER_ARRAY=["zoom_factors","geometry_x","geometry_y","zoom_levels"];WebGAGeoCompressed.URL="mireo.NET.WebGAGeoCompressed,MapSearchWeb.ms2";function BrowserType() {
	this.type = 0;
	this.version = 0;
	this.os = null;

	var binfo = navigator.userAgent.toLowerCase();
	if(binfo.indexOf("opera") != -1) this.type = 4;
	else if(binfo.indexOf("msie") != -1 && document.all) {
		this.type = 1;
		if(binfo.indexOf("msie 5"))	this.version = 5;
	}
	else if(binfo.indexOf("safari") != -1) {
		this.type = 3;
		if(binfo.indexOf("safari/125") != -1) this.version = 1;
	}
	else if(binfo.indexOf("mozilla") != -1){
		this.type = 2;
		if(binfo.indexOf("firefox/1") != -1) this.version = 1;
		else if(binfo.indexOf("firefox/2") != -1) this.version = 2;
		else this.version = 3;
	}

	if(binfo.indexOf("x11;") != -1) this.os = 1;
}

var browser = new BrowserType();
/*function PixelPoint(x, y) {
	this.x = x;
	this.y = y;
}

PixelPoint.prototype.toString = function() {
	return "(" + this.x + ", " + this.y + ")";
}

PixelPoint.prototype.equals = function(oth) {
	if(!oth) return false;
	return this.x == oth.x && this.y == oth.y;
}

function Size(w, h) {
	this.width = w;
	this.height = h;
}

Size.prototype.toString = function() {
	return "(" + this.width + ", " + this.height + ")";
}

Size.prototype.equals = function(oth) {
	if(!oth) return false;
	return this.width == oth.width && this.height == oth.height;
}

function WPoint(xM, yM) {
	this.xM = xM;
	this.yM = yM;
}

WPoint.prototype.toString = function() {
	return "(" + this.xM + ", " + this.yM + ")";
}

WPoint.prototype.equals = function(oth) {
	if(!oth) return false;
	return this.xM == oth.xM && this.yM == oth.yM;
}*/

var Sphere = {};

Sphere.RMF_MERIDIAN_BIN = 134217728;
Sphere.RMF_MERIDIAN_LAT = 180.0;
Sphere.M_PI_2 = 1.57079632679489661923;
Sphere.M_PI_4 = 0.785398163397448309616;
Sphere.MetersPerWorld = 0.149053797;

Sphere.Int2Deg = function(val){	return val / Sphere.RMF_MERIDIAN_BIN * Sphere.RMF_MERIDIAN_LAT; }
Sphere.Rad2Deg = function(val){ return val * 180.0 / Math.PI; }
Sphere.Int2Rad = function(val){ return val / Sphere.RMF_MERIDIAN_BIN * Math.PI;}
Sphere.Deg2Rad = function(val){ return val * Math.PI / 180.0; }
Sphere.Deg2Int = function(val){ return Math.round(val/Sphere.RMF_MERIDIAN_LAT*Sphere.RMF_MERIDIAN_BIN);}
Sphere.Rad2Int = function(val){ return Math.round(val/Math.PI*Sphere.RMF_MERIDIAN_BIN);}

Sphere.World2Deg = function(wp){
	var x = Sphere.Int2Deg(wp.x);
	var y = Sphere.Rad2Deg(2 * Math.atan(Math.exp(Sphere.Int2Rad(wp.y))) - Sphere.M_PI_2);
	return new DPoint(x, y);
}
Sphere.Deg2World = function(pt){
	var x = Sphere.Deg2Int(pt.x);
	var y = Sphere.Rad2Int(Math.log(Math.tan(Sphere.M_PI_4 + Sphere.Deg2Rad(pt.y)/2)));
	return new Point(x, y);
}

Sphere.World2Pix = function(wp, zf){
	return new Point(Math.round(wp.x/zf),Math.round(-wp.y/zf));
}
/*inline mireo::point<int> Deg2World(const mireo::point<double>& pt) {
	int x = ::Deg2Int(pt.x);
	int y = ::Rad2Int(log(tan(M_PI_4 + ::Deg2Rad(pt.y)/2)));
	return mireo::point<int>(x, y);
}
inline mireo::point<double> World2Deg(const mireo::point<int>& pt) {
	double x = ::Int2Deg(pt.x);
	double y = ::Rad2Deg(2 * atan(exp(::Int2Rad(pt.y))) - M_PI_2);
	return mireo::point<double>(x, y);
}*/
Math.log10 = function(x){ return Math.log(x) / Math.LN10; }

function IsDRectNull(rc) {
	return rc.x1 == 0 && rc.y1 == 0 && rc.x2 == 0 && rc.y2 == 0;
}
function inheritBase(object, base){
	if (arguments.length == 2)
		base.apply(object);
	else{
		var args = new Array(arguments.length - 2);
		for (var i = 0; i < args.length; ++i){
			args[i] = arguments[i+2];
		}
		base.apply(object,args);
	}
}

function inheritPrototype(base, derived){
	for(var i in base.prototype){
		derived.prototype[i] = base.prototype[i];
	}
}

function BinarySearch(arr, cmp, x) {
    var i = 0;
    var j = arr.length - 1;
    var k, r;
    do {
        k = Math.floor((i + j) / 2);
        if ((r = cmp(x, arr[k])) > 0)
            i = k + 1;
        else if (r < 0)
            j = k - 1; 
    } while ((r != 0) && (i <= j));
    return k; 
}

function findPosX(obj)
{
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

function findPosY(obj)
{
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;
	return curtop;
}


function getMouseCoordinates(e)
{
	var posx = 0;
	var posy = 0;
	if (!e) var e = window.event;
	if (e.pageX || e.pageY)
	{
		posx = e.pageX;
		posy = e.pageY;
	}
	else if (e.clientX || e.clientY)
	{
		posx = e.clientX;// + document.body.scrollLeft;
		posy = e.clientY;// + document.body.scrollTop;
		if (window.pageXOffset)
		{
			posx += window.pageXOffset;
			posy += window.pageYOffset;
		}
		else if (document.documentElement && document.documentElement.scrollTop)
		{
			posx += document.documentElement.scrollLeft;
			posy += document.documentElement.scrollTop;
		}
		else if (document.body)
		{
			posy += document.body.scrollLeft;
			posy += document.body.scrollTop;
		}

	}
	return new Point(posx, posy);
}

function format(expr, places) {
	var str = eval(expr).toString();
	while (str.length < places) str = '0' + str;
	return str;
}	

function numFormat(val, ctype){
	if(val == null) return '';
	switch(ctype){
		case 'TMLEN': {
			var hrs = Math.floor(val / 3600);
			var mins = Math.floor((val - hrs * 3600) / 60);
			var secs = val % 60;
			var ret = format(hrs, 2) + ':' + format(mins, 2) + ':' + format(secs, 2);
			return ret;
		}
		case 'DISTANCE':
			return '' + Math.floor(val / 100) / 10 + ' km';
	}
	return null;
}
function never(){ return false; }

var ElementCreator = {}

ElementCreator.createChildDiv = function(left, top, width, height, bkColor, fFam, fSize, tAlign){
	var dv = document.createElement('div');
	dv.style.position = 'absolute';
	dv.style.left = left + 'px';
	dv.style.top = top + 'px';
	dv.style.width = width + 'px';
	dv.style.height = height + 'px';

	if(fFam) dv.style.fontFamily = fFam;
	if(fSize) dv.style.fontSize = fSize;
	if(tAlign) dv.style.textAlign = tAlign;
	if(bkColor) dv.style.backgroundColor = bkColor;
	return dv;
}

ElementCreator.createCanvas = function(width, height) {
    var canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
	return canvas;
}

ElementCreator.createImage = function(left, top, src, width, height, zIndex, bTransp, size){
	var im = document.createElement(browser.type == 1 && bTransp == true ? "DIV" : "IMG");
	im.style.position = 'absolute';
	im.style.left = left + 'px';
	im.style.top = top + 'px';
	if(width != null) im.style.width = width + 'px';
	if(height != null) im.style.height = height + 'px';
	if(zIndex != null) im.style.zIndex = zIndex;
	if (browser.type == 1 && bTransp == true){
		im.style.fontSize = "1px";
		var method = 'crop';
		if (size != null && size){
			method = 'scale';
		}
		im.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod="+method+")";
	}
	else {
		im.src = src;
		im.galleryImg = false;
	}
	if(browser.type == 1){
		im.unselectable = "on";
		im.onselectstart = never;
	} 
	else im.style.MozUserSelect="none";
	im.oncontextmenu = never;
	return im;
}

ElementCreator.createImageRelative = function(src, width, height, bTransp){
	var im = document.createElement(browser.type == 1 && bTransp == true ? "DIV" : "IMG");
	if(width != null) im.style.width = width + 'px';
	if(height != null) im.style.height = height + 'px';
	if (browser.type == 1 && bTransp == true){
		im.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='crop')";
	}
	else {
		im.align='top';
		im.src = src;
		im.galleryImg = false;
	}
	if(browser.type == 1){
		im.unselectable = "on";
		im.onselectstart = never;
	} 
	else im.style.MozUserSelect="none";
	im.oncontextmenu = never;
	return im;
}


ElementCreator.changeImage = function(im, left, top, src, width, height, zIndex, bTransp){
	im.style.position = 'absolute';
	im.style.left = left + 'px';
	im.style.top = top + 'px';
	if(width != null) im.style.width = width + 'px';
	if(height != null) im.style.height = height + 'px';
	if(zIndex != null) im.style.zIndex = zIndex;
	if (browser.type == 1 && bTransp == true){
		im.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizingMethod='crop')";
	}
	else {
		im.src = src;
		im.galleryImg = false;
	}
	if(browser.type == 1){
		im.unselectable = "on";
		im.onselectstart = never;
	} 
	else im.style.MozUserSelect="none";
	im.oncontextmenu = never;
	return im;
}

function Dragger(elem, left, top, W, useZoom){
	this.zoom = false;
	this.src = elem;
	this.drag = false;
	this.ondragstart = null;
	this.ondrag = null;
	this.ondragend = null;
	this.onmove = null;
	this.onclick = null;
	this.disabled = false;
	this.dragPoint = new Point(0, 0);
	this.clickStartPos = new Point(0, 0);
	this.src.style.position = "absolute";
	this.moveTo(left, top);
	this.mouseDownHandler = eventHandler(this, "onMouseDown");
	this.mouseMoveHandler = eventHandler(this, "onMouseMove");
	this.mouseUpHandler = eventHandler(this, "onMouseUp");
	if(browser.type == 2)
		attachToEvent(window, "mouseout", eventHandler(this, "onWindowMouseOut"))
	
	this.eventSrc = this.src.setCapture ? this.src : window;
	attachToEvent(this.src, "mousedown", this.mouseDownHandler)
	
	attachToEvent(this.src, "dblclick", eventHandler(this, "onDoubleClick"))
	
	if (useZoom){
		this.onzoom = null;
		this.zoomDiv = document.createElement("DIV");
		this.zoomDiv.style.border = "solid navy 1px";
		this.zoomDiv.style.display = "none";
		this.zoomDiv.style.position = "absolute";
		this.zoomDiv.zIndex = this.zoomDiv.style.zIndex = 64;
		this.zoomDiv.style.fontSize = "1px";
		this.src.appendChild(this.zoomDiv);
	}
}




Dragger.prototype.getMouseCoordinates = function(evt) {
	var coords = getMouseCoordinates(evt);
	if(this.zoomDiv){
		coords.x -= findPosX(this.zoomDiv.parentNode);
		coords.y -= findPosY(this.zoomDiv.parentNode);
	}
	return coords;
}

Dragger.prototype.moveTo = function(x, y) {
	if(this.left != x || this.top != y) {
		this.left = x;
		this.top = y;
		this.src.style.left = this.left + 'px';
		this.src.style.top = this.top + 'px';
		if(this.onmove) this.onmove()
	}
}

Dragger.prototype.onMouseDown = function(e) {
	//hrvoje dodao jer mu je potrebno
	if (typeof jQuery != "undefined") {
	    $(document).triggerHandler("mousedown.simpletip");
	}
	this.mouseDownPos = this.getMouseCoordinates(e);
	if(e.cancelDrag){
		if (browser.type == 2){
			blurAll();
		}else{
			this.src.focus();
		}
		return;
	}
	this.drag = true;
	this.dragPoint.x = e.screenX;
	this.dragPoint.y = e.screenY;
	attachToEvent(this.eventSrc, "mousemove", this.mouseMoveHandler);
	attachToEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
	
	if(this.src.setCapture)	this.src.setCapture();
	
	this.clickStartTime = (new Date()).getTime();
	this.clickStartPos.x = e.screenX;
	this.clickStartPos.y = e.screenY;
	
	this.zoom = e.shiftKey;
	
	if(this.zoom){
		this.zoomStartPos = this.getMouseCoordinates(e);
	}else{
		if(this.ondragstart) this.ondragstart(e);
	}
	this.origCursor = this.src.style.cursor;
	cancelBubbling(e);
}


Dragger.prototype.onMouseMove = function(e){
	var x = this.left + (e.screenX - this.dragPoint.x);
	var y = this.top + (e.screenY - this.dragPoint.y);
	if(this.src.minX != null && x < this.src.minX) x = this.src.minX;
	if(this.src.maxX != null && x > this.src.maxX) x = this.src.maxX;
	if(this.src.minY != null && y < this.src.minY) y = this.src.minY;
	if(this.src.maxY != null && y > this.src.maxY) y = this.src.maxY;
	var drx = x - this.left;
	var dry = y - this.top;
	if (this.zoom){
		if (this.zoomDiv){
			this.src.style.cursor = 'SE-resize';
			var pos = this.getMouseCoordinates(e);
			this.zoomDiv.style.left = Math.min(this.zoomStartPos.x, pos.x) + "px";
			this.zoomDiv.style.top = Math.min(this.zoomStartPos.y, pos.y) + "px";
			this.zoomDiv.style.width = Math.abs(this.zoomStartPos.x - pos.x) + "px";
			this.zoomDiv.style.height = Math.abs(this.zoomStartPos.y - pos.y) + "px";
			this.zoomDiv.style.display = "block";
		}
	}else{
		this.src.style.cursor = 'move';
		this.moveTo(x, y);
		if(this.ondrag){
			this.ondrag(new Size(drx, dry));
		}
		this.dragPoint.x += drx;
		this.dragPoint.y += dry;
	}
}

Dragger.prototype.onMouseUp = function(e){
	detachFromEvent(this.eventSrc, "mousemove", this.mouseMoveHandler);
	detachFromEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
	this.src.style.cursor = this.origCursor;

	if(document.releaseCapture)
		document.releaseCapture();

	if (browser.type == 2){
		blurAll();
	}else{
		this.src.focus();
	}
	var now = (new Date()).getTime();
	if(this.zoom){
		if (this.zoomDiv){
			this.zoomDiv.style.display = "none";
			if(this.onzoom){
				e.startPos = this.zoomStartPos;
				e.endPos = this.getMouseCoordinates(e);
				this.onzoom(e);
			}
		}
	}else{
		if(this.ondragend)
			this.ondragend(e);
	}
	if (this.onclick){		
		if(now - this.clickStartTime <= 500 && (Math.abs(this.clickStartPos.x - e.screenX) <= 2 && 
			Math.abs(this.clickStartPos.y - e.screenY) <= 2)){
			e.pos = this.getMouseCoordinates(e);
			this.onclick(e);
		}
	}
	this.drag = false;
}


Dragger.prototype.onWindowMouseOut = function(e){
	if(!e.relatedTarget && this.drag)
		this.onMouseUp(e);
}

Dragger.prototype.onDoubleClick = function(e){
	if (this.ondblclick){
		e.pos = this.mouseDownPos ? this.mouseDownPos : this.getMouseCoordinates(e);
		this.ondblclick(e);
	}
};

function measureHTML(h){
	var div = document.createElement("DIV");
	div.style.cssText = "position:absolute; left:-3000px; display:table;";
	div.innerHTML = h;
	document.body.appendChild(div);
	if (div.childNodes[0] != null && div.childNodes[0].nodeName == "DIV"){
		var ret = new Size(div.childNodes[0].offsetWidth, div.childNodes[0].offsetHeight);
		document.body.removeChild(div);
		return ret;
	}
	var ret = new Size(div.offsetWidth, div.offsetHeight);
	document.body.removeChild(div);
	return ret;
}

function blurAll(){
	var blurText;
	if (!blurAll.added){
		blurText = document.createElement("INPUT");
		blurText.id = 'blurText'
		blurText.type = 'button';
		blurText.style.cssText = "position:absolute;display:none";
		document.body.appendChild(blurText);
		blurAll.added = true;
	}else{
		blurText = document.getElementById('blurText');
	}
	
	blurText.focus();
	blurText.blur();
	
}

blurAll.added = false;

function wheel(e){
    var delta = 0;
    if (!e) // For IE. 
            e = window.e;
    if (e.wheelDelta) { // IE/Opera.
            delta = e.wheelDelta/120;
    } else if (e.detail) { // Mozilla case. In Mozilla, sign of delta is different than in IE. Also, delta is multiple of 3.
            delta = -e.detail/3; 
    }
    return Math.round(delta); 
}

function redirected_path(path){
	if(typeof MIREO_REDIRECT_TO != "undefined"){
		return MIREO_REDIRECT_TO + "/" + path;
	}else{
		return path;
	}
}

function chain(f1, f2) {
    if (!f1) {
        return f2;
    }
    return function() {
        f1.apply(null, arguments);
        f2.apply(null, arguments);   
    };
}

function eventHandler(object, handler) {
	return function(b) {
		if(!b) b = window.event;
		if(b && !b.target) b.target = b.srcElement;
		object[handler](b);
	}
}

function attachToEvent(elem, evt, handler) {
	if(elem.addEventListener) elem.addEventListener(evt, handler, false);
	else if(elem.attachEvent) elem.attachEvent("on" + evt, handler);
	else elem["on" + evt] = handler;
}

function detachFromEvent(elem, evt, handler) {
	if(elem.removeEventListener) elem.removeEventListener(evt, handler, false);
	else if(elem.detachEvent) elem.detachEvent("on" + evt,handler);
	else elem["on" + handler] = null;
}

function attachToWheel(elem, handler){
    if(elem.addEventListener){ 
        elem.addEventListener('DOMMouseScroll',handler, false); //mozilla
        elem.addEventListener('mousewheel', handler, false); // Opera, Chrome
    }
    else if(elem.attachEvent) elem.attachEvent("onmousewheel", handler); //IE
}

function cancelBubbling(e){
	if(browser.type == 1)
		window.event.cancelBubble = true;
	else {
		e.cancelBubble = true;
		e.preventDefault();
		e.stopPropagation();
	}
}

function SetTimeoutObject(f, object, args){
	this.f = f;
	this.object = object;
	this.args = args;
	this.name = "setTimeoutObject" + (SetTimeoutObject.counter++);
	eval(this.name + "= this;");
}

SetTimeoutObject.counter = 0;

SetTimeoutObject.prototype.exec = function(){
	this.f.apply(this.object, this.args);
}

function setTimeoutFunction(f, to, object){
	var args = new Array(arguments.length - 3);
	for (var i = 0; i < args.length; ++i){
		args[i] = arguments[i+3];
	}
	var set_timeout_object = new SetTimeoutObject(f, object, args);
	
	return window.setTimeout(set_timeout_object.name+".exec()", to);
}

function Scale(cont, left, top) {
	this.sclen = 200; first_ticklen = this.sclen / 4;
	this.main_div = ElementCreator.createChildDiv(left, top, this.sclen + 2 * 20, 37);
	this.scale_div = ElementCreator.createChildDiv(10, 0, this.sclen, 5);
	this.scale_div.style.border = '1px solid black';
	this.scale_div.style.overflow = 'hidden';
	for(var i = 0; i < 4; ++i)
		this.scale_div.appendChild(ElementCreator.createChildDiv(i * first_ticklen, 0, first_ticklen, 5, i % 2 ? 'white' : 'black'));
	var zero = ElementCreator.createChildDiv(0, 8, 20, 10, null, 'Arial, sans serif', '11px', 'center');
	zero.innerHTML = '0';

	this.maxval = ElementCreator.createChildDiv(this.sclen - 20, 8, 60, 10, null, 'Arial, sans serif', '11px', 'center');
	this.maxval.innerHTML = '';

	this.main_div.appendChild(zero);
	this.main_div.appendChild(this.maxval);
	this.main_div.appendChild(this.scale_div);
	this.main_div.style.zIndex = 99;
	cont.appendChild(this.main_div);
}

Scale.prototype.changeLocation = function(left, top){
	this.main_div.style.left = left + 'px';
	this.main_div.style.top = top + 'px';
}

Scale.prototype.ResolutionChanged = function(res){
	var width = this.sclen;
	var maximum = width * res;
	var rounded = Math.pow(10, Math.floor(Math.log10(maximum)));
	var maxmeters = Math.floor(maximum / rounded) * rounded;
	var new_ticklen = Math.ceil(Math.floor(width * maxmeters / maximum) / 4);
	var ticks = this.scale_div.childNodes;
	for(var i = 0; i < ticks.length; ++i){
		var dv = ticks.item(i);
		dv.style.width = new_ticklen + 'px';
		dv.style.left = (i * new_ticklen) + 'px';
	}
	this.scale_div.style.width = (4 * new_ticklen) + 'px';
	this.maxval.style.left = (4 * new_ticklen - 20) + 'px';
	this.maxval.innerHTML = '40000 km';
	if(maxmeters > 900) this.maxval.innerHTML = Math.floor(maxmeters / 1000) + ' km';
	else this.maxval.innerHTML = maxmeters + ' m';
	this.ticklen = new_ticklen;
}

function ZoomSlider(cont, left, top){
	this.slider_orig = 16;
	var dv = ElementCreator.createChildDiv(left, top, 20, 220);
	var bg = ElementCreator.createImage(4, this.slider_orig, redirected_path('map/images/zoom_bg.gif'));
	this.zoomIn = ElementCreator.createImage(2, 0, redirected_path('map/images/zoom_plus.gif'));
	this.zoomOut = ElementCreator.createImage(2, 140, redirected_path('map/images/zoom_minus.gif'));
	this.zoomSlider = ElementCreator.createImage(0, 50, redirected_path('map/images/zoom_slider.gif'));
	
	bg.style.height = '123px';
//	bg.style.width = '156px';
	dv.appendChild(bg);
	
	attachToEvent(this.zoomIn, "mousedown", eventHandler(this, "onZoomIn"));
	attachToEvent(this.zoomOut, "mousedown", eventHandler(this, "onZoomOut"));
	
	dv.appendChild(this.zoomIn);
	dv.appendChild(this.zoomOut);
	dv.appendChild(this.zoomSlider);
	cont.appendChild(dv);
	this.zoomSlider.minX = 0;
	this.zoomSlider.maxX = 0;
	this.zoomSlider.minY = 17;
	this.zoomSlider.maxY = 128;
	this.dragObject = new Dragger(this.zoomSlider, 0, (this.slider_orig + 2 * 11) + 1);
	this.dragObject.ondragend = eventHandler(this, "onDragEnd");
	
	this.zfM = MireoMap.zfM;
	this.zf = 10;
	this.dragObject.moveTo(0, (this.slider_orig + this.zf * 11) + 1);
	this.main_div = dv;
}

ZoomSlider.prototype.changeLocation = function(left, top){
	this.main_div.style.left = left + 'px';
	this.main_div.style.top = top + 'px';
}

ZoomSlider.prototype.onDragEnd = function(e){
	var y = parseInt(this.zoomSlider.style.top);
	var zf = Math.floor((y - this.slider_orig + 5.5) / 11);
	if(zf == this.zf) return;
	this.zf = zf;
	this.dragObject.moveTo(0, (this.slider_orig + zf * 11) + 1);
	if(this.onzoomchange) this.onzoomchange(this.zfM[zf]);
}

ZoomSlider.prototype.setZoomIndex = function(zf){
	if (this.updateZoomIndex(zf))
		if(this.onzoomchange) this.onzoomchange(this.zfM[zf]);
}

ZoomSlider.prototype.updateZoomIndex = function(zf){
	if(zf == this.zf) return false;
	this.zf = zf;
	this.dragObject.moveTo(0, (this.slider_orig + zf * 11) + 1);
	return true;
}

ZoomSlider.prototype.onZoomIn = function(){
	if (this.zf > 0) this.setZoomIndex(this.zf-1);
}

ZoomSlider.prototype.onZoomOut = function(){
	if (this.zf < this.zfM.length - 1) this.setZoomIndex(this.zf+1);
}
// JScript File

function MapSwitcher(cont, left, top, mapviewtype){
    
    var dv = ElementCreator.createChildDiv(left-275, top, 210, 25, null);   
    
    //Satellite Button
    this.dvS = ElementCreator.createChildDiv(140, 0, 70, 25, null);   
    this.dvS.innerHTML = "<input type='button' onclick='' value='Satellite' id='SView' style='width:100%; text-align: center; font-weight: normal; cursor:pointer;'/>"
    attachToEvent(this.dvS, "click", eventHandler(this, "onClick"));
    dv.appendChild(this.dvS);
    
    //Map Button
    this.dvM = ElementCreator.createChildDiv(70, 0, 70, 25, null);    
    this.dvM.innerHTML = "<input type='button' onclick='' value='Map' id='MView' style='width:100%; text-align: center; font-weight: normal; cursor:pointer;'/>"
    attachToEvent(this.dvM, "click", eventHandler(this, "onClick"));
    dv.appendChild(this.dvM);
    
    //Hybrid Button
    this.dvH = ElementCreator.createChildDiv(0, 0, 70, 25, null);  
    this.dvH.innerHTML = "<input type='button' onclick='' value='Hybrid' id='HView' style='width:100%; text-align: center; font-weight: normal; cursor:pointer;'/>"
    attachToEvent(this.dvH, "click", eventHandler(this, "onClick"));
    dv.appendChild(this.dvH);

     this.main_div = dv;     
     cont.appendChild(dv);     
    
    //Set Initial Button Appereance
    if (mapviewtype == 1){
        this.myid = "SView"; 
                
        this.dvS.childNodes[0].focus();  
        this.dvS.childNodes[0].style.fontWeight = "bold";
        
        this.dvM.childNodes[0].blur();   
        this.dvM.childNodes[0].style.fontWeight = "normal";
        
        this.dvH.childNodes[0].blur();   
        this.dvH.childNodes[0].style.fontWeight = "normal";
    }
    else if (mapviewtype == 0){
        this.myid = "MView"; 
        
        this.dvS.childNodes[0].blur();   
        this.dvS.childNodes[0].style.fontWeight = "normal";
        
        this.dvM.childNodes[0].focus();     
        this.dvM.childNodes[0].style.fontWeight = "bold";       
        
        this.dvH.childNodes[0].blur();   
        this.dvH.childNodes[0].style.fontWeight = "normal";    
    }
    
    else {
        this.myid = "HView"; 
        
        this.dvS.childNodes[0].blur();   
        this.dvS.childNodes[0].style.fontWeight = "normal";
        
        this.dvM.childNodes[0].blur();     
        this.dvM.childNodes[0].style.fontWeight = "normal";       
        
        this.dvH.childNodes[0].focus();   
        this.dvH.childNodes[0].style.fontWeight = "bold";    
    }

}

MapSwitcher.prototype.onClick = function(e){
    if ((this.myid == "MView" || this.myid == "HView") && e.target.id == "SView"){
        if (this.onmapviewchangeS) this.onmapviewchangeS();
        this.myid = "SView" ;
        
        this.dvS.childNodes[0].focus();                
        this.dvS.childNodes[0].style.fontWeight = "bold";
        
        this.dvM.childNodes[0].blur();                
        this.dvM.childNodes[0].style.fontWeight = "normal";
        
        this.dvH.childNodes[0].blur();   
        this.dvH.childNodes[0].style.fontWeight = "normal";
    }
    else if ( (this.myid == "SView" || this.myid == "HView") && e.target.id == "MView"){ 
        if (this.onmapviewchangeM) this.onmapviewchangeM();
        this.myid = "MView" ;
        
        this.dvS.childNodes[0].blur();                
        this.dvS.childNodes[0].style.fontWeight = "normal";
        
        this.dvM.childNodes[0].focus();               
        this.dvM.childNodes[0].style.fontWeight = "bold";
        
        this.dvH.childNodes[0].blur();   
        this.dvH.childNodes[0].style.fontWeight = "normal";

    }
        
    else if ( (this.myid == "MView" || this.myid == "SView") &&  e.target.id == "HView" ){
        if (this.onmapviewchangeH) this.onmapviewchangeH();
        this.myid = "HView" ;
        
        this.dvS.childNodes[0].blur();                
        this.dvS.childNodes[0].style.fontWeight = "normal";
        
        this.dvM.childNodes[0].blur();               
        this.dvM.childNodes[0].style.fontWeight = "normal";
        
        this.dvH.childNodes[0].focus();   
        this.dvH.childNodes[0].style.fontWeight = "bold";
    
    }

}

MapSwitcher.prototype.changeLocation = function(left, top){
	this.main_div.style.left = left - 275 + 'px';
	this.main_div.style.top = top + 'px'; 
}
function PanControl(cont, left, top){
    
    var dv = ElementCreator.createChildDiv(left-80, top, 70, 70);  
    dv.style.display = "block";
    //var dv = ElementCreator.createChildDiv(left-80, top, 70, 70, "#BDBEC0", "Arial, sans serif", "12px", "center");   
    //var dv = ElementCreator.createImage(left - 80, top, 'map/images/PanControl/PanControlBackground.png',70, 70, 0, true)
    
    this.pan = new Array();    
    this.pan.push(ElementCreator.createImage(5, 5, 'map/images/PanControl/PanNeutral.png',60, 60, 1, true));
    this.pan.push(ElementCreator.createImage(5, 5, 'map/images/PanControl/PanRight.png',60, 60, 1, true));    
    this.pan.push(ElementCreator.createImage(5, 5, 'map/images/PanControl/PanDown.png',60, 60, 1, true));
    this.pan.push(ElementCreator.createImage(5, 5, 'map/images/PanControl/PanLeft.png',60, 60, 1, true));
    this.pan.push(ElementCreator.createImage(5, 5, 'map/images/PanControl/PanUp.png',60, 60, 1, true));
    
    dv.appendChild(this.pan[0]);
    attachToEvent(this.pan[0], "mousedown", this.eventHandler("onMouseDown"));   
    //this.pan[0].style.border = "solid navy 1px"
    
    
    dv.appendChild(ElementCreator.createImage(0, 0, 'map/images/PanControl/PanControlBackground.png',70, 70, 0, true))
    
    this.main_div = dv;
    cont.appendChild(this.main_div);        
    
    this.motion = 0;
}

PanControl.prototype.startMotion = function(x, y){
    var newWCenter = new Point(0, 0);
    newWCenter.x = map.wcenter.x + x * map.zf;
    newWCenter.y = map.wcenter.y + y * map.zf;
    map.moveCenterToW(newWCenter, true )
    
    this.motion =  this.setTimeout("this.startMotion(" + x + "," + y + ")", 200);
}

PanControl.prototype.stopMotion = function(){
    alert("stop Motion, this.motion = " + this.motion);
    clearTimeout(this.motion);
}

PanControl.prototype.onMouseDown = function(e){
    var pt = getMouseCoordinates(e);
	pt.x -= findPosX(this.pan[0]) ;
	pt.y -= findPosY(this.pan[0]) ;
    var x = pt.x;
    var y = pt.y;
    var r = (x - 30)*(x - 30) + (y - 30)*(y - 30);
    
    this.evt = null;    
    var target = null;
    
    if (r < 900){
        if (y > (60 - x)){
            if (y < x)  { //alert ("Right")
                this.evt = {keyCode : 39};
                target = this.pan[1];
            }
            else { //alert("Down");
                target = this.pan[2];
                this.evt = {keyCode : 40};
                
            }
        }    
        else{
        if (y < x) {//alert("Up");
            target = this.pan[4];
            this.evt = {keyCode : 38};
        }
        else { //alert("Left");
            target = this.pan[3];
            this.evt = {keyCode : 37};
        }
        }
    }
    this.main_div.replaceChild(target, this.pan[0]);
    
    if(this.evt){
	    map.onKeyDown(this.evt);
    	
	    this.eventSrc = target.setCapture ? target : window;
	    if(this.eventSrc.setCapture) this.eventSrc.setCapture();
    	
	    this.mouseUpHandler = this.eventHandler("onMouseUp");
    	
	    attachToEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
    	
	    cancelBubbling(e);
	}
}

PanControl.prototype.onMouseUp = function(){

    if(this.mouseUpHandler){
        detachFromEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
        this.mouseUpHandler = null;
    }
    this.main_div.replaceChild(this.pan[0], this.main_div.childNodes[0]);
    map.onKeyUp(this.evt);
    
    if(this.eventSrc.releaseCapture)
		this.eventSrc.releaseCapture();
	this.eventSrc = null;
}

PanControl.prototype.changeLocation = function(left, top){
	this.main_div.style.left = left - 80 + 'px';
	this.main_div.style.top = top + 'px'; 
}

function ZoomSliderHT(map, cont, left, top){
	
	this.map = map;
	
	this.slider_orig = 32;
	var dv = ElementCreator.createChildDiv(left, top, 32, 194);
	//dv.style.top = ""; dv.style.bottom = top + 'px';
	
	var bg;
	var bg_wrapper;
	
    this.zoomSlider = ElementCreator.createImage(7, 43, redirected_path('map/images/ZoomSliderHT/Dragger.png'), 18, 10, 0, true);
	
	this.zoomOut = ElementCreator.createImage(0, 0, redirected_path('map/images/ZoomSliderHT/ZoomOut.png'), 32, 26, 0, true);
	this.zoomOut.style.top = ""; this.zoomOut.style.bottom = "0px";
	
	bg_wrapper = ElementCreator.createChildDiv(0, 0, 32, 168,'#BDBEC0');
	bg_wrapper.style.top =""; bg_wrapper.style.bottom = "26px";
	
	this.zoomIn = ElementCreator.createImage(0, 5, redirected_path('map/images/ZoomSliderHT/ZoomIn.png'), 32, 20, 0, true);		
	this.zoomIn.style.top =""; this.zoomIn.style.bottom = "171px";
	
	bg = ElementCreator.createImage(10, this.slider_orig + 1, redirected_path('map/images/ZoomSliderHT/ZoomBar.png'), 12, 120, 0, true);
	bg.style.top = ""; bg.style.bottom = 194 - this.slider_orig - 1 - 120 + 'px';
		
	
	dv.appendChild(bg_wrapper);
	dv.appendChild(bg);	
	
	attachToEvent(this.zoomIn, "mousedown", eventHandler(this, "onZoomIn"));
	attachToEvent(this.zoomOut, "mousedown", eventHandler(this, "onZoomOut"));
	
	dv.appendChild(this.zoomIn);
	dv.appendChild(this.zoomOut);
	dv.appendChild(this.zoomSlider);
	
	cont.appendChild(dv);
	this.zoomSlider.minX = 7;
	this.zoomSlider.maxX = 7;
	this.zoomSlider.minY = (this.slider_orig + 0 * 11) + 1
	this.zoomSlider.maxY = (this.slider_orig + (MireoMap.zfM.length - 1) * 11) + 1;
	this.dragObject = new Dragger(this.zoomSlider, 7, (this.slider_orig + 2 * 11) + 1);
	this.dragObject.ondragend = eventHandler(this, "onDragEnd");
	
	this.zfM = MireoMap.zfM;
	this.zf = 10;
	this.dragObject.moveTo(7, (this.slider_orig + this.zf * 11) + 1);
	this.main_div = dv;
	this.main_div.id = "zoom_slider_main_div";
}

ZoomSliderHT.prototype.changeLocation = function(left, top){
    this.main_div.style.left  = left + 'px';
	//this.main_div.style.bottom = top + 'px';
}

ZoomSliderHT.prototype.onDragEnd = function(e){
	var y = parseInt(this.zoomSlider.style.top);
	var zf = Math.floor((y - this.slider_orig + 5.5) / 11);
	if(zf == this.zf) return;
	this.zf = zf;
	this.dragObject.moveTo(7, (this.slider_orig + zf * 11) + 1);
	if(this.onzoomchange) this.onzoomchange(this.zfM[zf]);
}

ZoomSliderHT.prototype.setZoomIndex = function(zf){
	if (this.updateZoomIndex(zf))
		if(this.onzoomchange) this.onzoomchange(this.zfM[zf]);
}

ZoomSliderHT.prototype.updateZoomIndex = function(zf){
	if(zf == this.zf) return false;
	this.zf = zf;
	this.dragObject.moveTo(7, (this.slider_orig + zf * 11) + 1);
	return true;
}

ZoomSliderHT.prototype.onZoomIn = function(){
	if (this.zf > 0) this.setZoomIndex(this.zf-1);
}

ZoomSliderHT.prototype.onZoomOut = function(){
	if (this.zf < this.zfM.length - 1) this.setZoomIndex(this.zf+1);
}

function MapSwitcherHT(map, cont, left, top, mapviewtype, initInnerText){
      
    var dv = ElementCreator.createChildDiv(left , top, 300, 32, null, "Arial, sans serif", "12px", "center"); 
    
    
    this.map = map;
      
    this.hideButton = ElementCreator.createImage(0, 0, redirected_path('map/images/MapSwitcherHT/HideButton.png'),32, 32, 0, true);
    this.hideButton.id = "hideButton";
    attachToEvent(this.hideButton, "click", eventHandler(this, "onHide"));    
    
    this.fullScreen = ElementCreator.createImage(33, 0, redirected_path('map/images/MapSwitcherHT/FullScreen.png'), 32, 32, 0, true);
    this.fullScreen.id = "fullScreen";
    attachToEvent(this.fullScreen, "click", eventHandler(this, "onFullScreen"));   
    this.fullScreenStatus = false;
         
    dv.appendChild(this.hideButton);       
    this.sep0 = ElementCreator.createChildDiv(32, 0, 1, 32, "white"); dv.appendChild(this.sep0); this.sep0.id = "sep0";
    dv.appendChild(this.fullScreen);
    this.sep1 = ElementCreator.createChildDiv(65, 0, 1, 32, "white"); dv.appendChild(this.sep1); this.sep1.id = "sep1";
        
    dv.style.width = this.measureWidth(dv) + 'px';
    
    dv.style.display = "block";
    this.main_div = dv;     
    this.main_div.id = "map_switcher_main_div";
    
    cont.appendChild(dv);     
    
    this.changeLanguage(initInnerText);
        
    this.showButton = ElementCreator.createImage(left - 35, top, redirected_path('map/images/MapSwitcherHT/ShowButton.png'), 32, 32, 0, true);
    this.showButton.id = "showButton";
    this.showButton.style.left = ""; this.showButton.style.right = "2px";
    this.showButton.style.display = "none";
    attachToEvent(this.showButton, "click", eventHandler(this, "onShow"));
    cont.appendChild(this.showButton);
    
    this.viewSize = new Size(this.main_div.offsetWidth, this.main_div.offsetHeight);
    
    var switcher = this;
    map.changeLanguage = function(innerText){
		switcher.changeLanguage(innerText);
    }
 }
MapSwitcherHT.prototype.measureWidth = function (dv){
    var totalWidth = 0;
    for (var i=0; i < dv.childNodes.length; i++){
        totalWidth += parseInt(dv.childNodes[i].style.width);
    }
    return totalWidth;
}

MapSwitcherHT.prototype.changeLanguage = function (innerText){

    if (this.main_div.childNodes.length > 4){
        for (var i=this.main_div.childNodes.length - 1 ; i>=4 ; i--){            
            this.main_div.removeChild(this.main_div.childNodes[i]);
        }        
        this.main_div.style.width = this.measureWidth(this.main_div) + 'px'; //update MAIN_DIV width 
    }
       
    var left = parseInt(this.main_div.style.width);
    var size = [0, 0, 0];
    var width = 0;
    for (var i=0 ; i<innerText.length; i++){
        size[i] = measureHTML(innerText[i]).width;
        if (size[i] > width) width = size[i];
    }
    
    this.Map = ElementCreator.createChildDiv(left, 0, width, 32, "#BDBEC0"); 
    this.Map.id = "MView";   
    this.Map.style.cssText += '; z-index:1;text-align:center;color:black;cursor:pointer;font-weight:normal; line-height: 32px;';
    this.Map.innerHTML = innerText[0];
    this.Map.style.lineHeight = "32px";
    attachToEvent(this.Map, "click", eventHandler(this, "onChangeView"));          
        
    this.Satellite = ElementCreator.createChildDiv(left + width + 1, 0, width, 32, "#BDBEC0");    
    this.Satellite.id = "SView";
    this.Satellite.style.cssText += '; z-index:1;text-align:center;color:black;cursor:pointer;font-weight:normal; line-height: 32px;';
    this.Satellite.innerHTML = innerText[1];
    attachToEvent(this.Satellite, "click", eventHandler(this, "onChangeView"));          
    
    this.Hybrid = ElementCreator.createChildDiv(left + 2*width + 2, 0, width, 32, "#BDBEC0");    
    this.Hybrid.id = "HView";
    this.Hybrid.style.cssText += '; z-index:1;text-align:center;color:black;cursor:pointer;font-weight:normal; line-height: 32px;';
    this.Hybrid.innerHTML =  innerText[2];
    attachToEvent(this.Hybrid, "click", eventHandler(this, "onChangeView"));          
    
    this.main_div.appendChild(this.Map);
    this.sep2 = ElementCreator.createChildDiv(left + width, 0, 1, 32, "white"); this.main_div.appendChild(this.sep2); this.sep2.id = "sep2";    
    this.main_div.appendChild(this.Satellite);
    this.sep3 = ElementCreator.createChildDiv(left + 2*width + 1, 0, 1, 32, "white"); this.main_div.appendChild(this.sep3); this.sep3.id = "sep3"; 
    this.main_div.appendChild(this.Hybrid);
    this.sep4 = ElementCreator.createChildDiv(left + 3*width + 2, 0, 1, 32, "white"); this.main_div.appendChild(this.sep4); this.sep4.id = "sep4";
       
    switch (this.map.mapviewtype) {
        case 0 : 
            this.Map.style.background = "#D1D2D4";
            this.Map.style.color = "White";
            this.myid = "MView";
            break;
        case 1:
            this.Satellite.style.background = "#D1D2D4";
            this.Satellite.style.color = "White";
            this.myid = "SView";
            break;        
        case 2:
            this.Hybrid.style.background = "#D1D2D4";
            this.Hybrid.style.color = "White";
            this.myid = "HView";
            break;            
    
    }
    this.main_div.style.width = this.measureWidth(this.main_div) + 'px';
    this.main_div.style.left = parseInt(this.map.pan_control.main_div.style.left) - parseInt(this.main_div.style.width) + 'px';
}

    
MapSwitcherHT.prototype.onChangeView = function(e){ 
   if ((this.myid == "MView" || this.myid == "HView") && e.target.id == "SView"){
        if (this.onmapviewchangeS) this.onmapviewchangeS();
        this.myid = "SView" ;
    
        this.Satellite.style.background = "#D1D2D4";
        this.Satellite.style.color = "White";
    
        this.Map.style.background = "#BDBEC0";
        this.Map.style.color = "Black";
    
        this.Hybrid.style.background = "#BDBEC0";
        this.Hybrid.style.color = "Black";
    }
    else if ( (this.myid == "SView" || this.myid == "HView") && e.target.id == "MView"){ 
        if (this.onmapviewchangeM) this.onmapviewchangeM();
        this.myid = "MView" ;
        
        this.Satellite.style.background = "#BDBEC0";
        this.Satellite.style.color = "Black";
    
        this.Map.style.background = "#D0D1D3";
        this.Map.style.color = "White";
    
        this.Hybrid.style.background = "#BDBEC0";
        this.Hybrid.style.color = "Black";

    }        
    else if ( (this.myid == "MView" || this.myid == "SView") &&  e.target.id == "HView" ){
        if (this.onmapviewchangeH) this.onmapviewchangeH();
        this.myid = "HView" ;
        
        this.Satellite.style.background = "#BDBEC0";
        this.Satellite.style.color = "Black";
    
        this.Map.style.background = "#BDBEC0";
        this.Map.style.color = "Black";
    
        this.Hybrid.style.background = "#D0D1D3";
        this.Hybrid.style.color = "White";
    
    }
}

MapSwitcherHT.prototype.changeLocation = function(left, top){
	this.main_div.style.left = parseInt(this.map.pan_control.main_div.style.left) - parseInt(this.main_div.style.width) + 'px';
	this.main_div.style.top = top + 'px';
}

MapSwitcherHT.prototype.onHide = function(){
    this.main_div.style.display = "none";
    this.map.pan_control.main_div.style.display = "none";
    this.map.zoom_slider.main_div.style.display = "none";   
    this.showButton.style.display = "block";
}

MapSwitcherHT.prototype.onShow = function(){
    this.showButton.style.display = "none";
    this.main_div.style.display = "block";
    this.map.zoom_slider.main_div.style.display = "block";
    this.map.pan_control.main_div.style.display = "block"; 
}

MapSwitcherHT.prototype.onFullScreen = function(){
	if (!this.fullScreenStatus){
		this.fullScreenStatus = true;
		ElementCreator.changeImage(this.fullScreen, 33, 0, redirected_path('map/images/MapSwitcherHT/NormalScreen.png'), 32, 32, 0, true);
    }else{
		this.fullScreenStatus = false;
		ElementCreator.changeImage(this.fullScreen, 33, 0, redirected_path('map/images/MapSwitcherHT/FullScreen.png'), 32, 32, 0, true);
	}
    if(this.map.fullscreen){
		this.map.fullscreen(this.fullScreenStatus);
    }
}

/*
MapSwitcherHT.prototype.onHide = function(){
    this.main_div.style.overflow = "hidden";
    map.zoom_slider.main_div.style.overflow = "hidden";
    
    this.main_div.style.zIndex = 0;
    map.zoom_slider.main_div.style.zIndex = 0;
    map.pan_control.main_div.style.zIndex = 1;
        
    var stepx = Math.round( (map.viewSize.width - parseInt(this.main_div.style.left))/50 ) ;
    var stepy = Math.round( (map.viewSize.height - parseInt(map.zoom_slider.main_div.style.bottom))/50 );
    
    
    this.hideMS(stepx, stepy);
}

MapSwitcherHT.prototype.onShow = function(){
    this.showButton.style.display = "none";
	var left = map.viewSize.width + parseInt(map.pan_control.main_div.style.width);
    var bottom = map.viewSize.height + parseInt(map.pan_control.main_div.style.height);
    map.pan_control.main_div.style.left = left + 'px';
    map.pan_control.main_div.style.bottom = bottom + 'px';
    map.pan_control.main_div.style.display = "block";
    
    alert('left = ' + left + ' top = ' + bottom)
    
    this.showPC();
}

MapSwitcherHT.prototype.hideMS = function (stepx, stepy){   
    if ( this.main_div.style.left < this.map.pan_control.main_div.style.left && this.map.zoom_slider.main_div.style.bottom < this.map.pan_control.main_div.style.bottom) {      
        this.main_div.style.left = parseInt(this.main_div.style.left) + stepx + 'px';
        this.map.zoom_slider.main_div.style.bottom = parseInt(this.map.zoom_slider.main_div.style.bottom) + stepy + 'px';
        
        var diffx = 2 + parseInt(this.main_div.style.left) + parseInt(this.main_div.style.width) - parseInt(this.map.pan_control.main_div.style.left) - parseInt(this.map.pan_control.main_div.style.width);
        var diffy = 2 + parseInt(this.map.zoom_slider.main_div.style.bottom) + parseInt(this.map.zoom_slider.main_div.style.height) - parseInt(this.map.pan_control.main_div.style.bottom) - parseInt(this.map.pan_control.main_div.style.height);

        if (diffx > 0 && parseInt(this.main_div.style.width) > diffx ){
            this.main_div.style.width = parseInt(this.main_div.style.width) - diffx + 'px';
        }
        if (diffy > 0 && parseInt(this.map.zoom_slider.main_div.style.height) > diffy ){
            this.map.zoom_slider.main_div.style.height = parseInt(this.map.zoom_slider.main_div.style.height) - diffy + 'px';
            this.map.zoom_slider.zoomSlider.style.top = parseInt(this.map.zoom_slider.zoomSlider.style.top) - stepy  + 'px';
        }
        
        setTimeoutFunction(this.hideMS, 50, this, stepx, stepy);        
    }
    
    else{
        this.main_div.style.display = "none";
        this.map.zoom_slider.main_div.style.display = "none";
        this.hidePC();
    }

}

MapSwitcherHT.prototype.hidePC = function(){
    
    if ( ( (parseInt(this.map.pan_control.main_div.style.left) + 0) < this.map.viewSize.width ) && ( (parseInt(this.map.pan_control.main_div.style.bottom) + 0) < this.map.viewSize.height )  ){
        this.map.pan_control.main_div.style.left = parseInt(this.map.pan_control.main_div.style.left) + 4 + 'px';
        this.map.pan_control.main_div.style.bottom = parseInt(this.map.pan_control.main_div.style.bottom) + 4 + 'px';
        setTimeoutFunction(this.hidePC, 50, this);
    }
    else{
        this.map.pan_control.main_div.style.display = "none";
        this.showButton.style.display = "block";
    }

}

MapSwitcherHT.prototype.showPC = function(){
    alert( parseInt(this.map.pan_control.main_div.style.left) + parseInt(this.map.pan_control.main_div.style.width) + "2px")
    alert(this.map.viewSize.width + "px")
    
    if ( ( parseInt(this.map.pan_control.main_div.style.left) + parseInt(this.map.pan_control.main_div.style.width) + "2px" < this.map.viewSize.width + "px" ) ){
        this.map.pan_control.main_div.style.left = parseInt(this.map.pan_control.main_div.style.left) - 4 + 'px';
        this.map.pan_control.main_div.style.bottom = parseInt(this.map.pan_control.main_div.style.bottom) - 4 + 'px';
        setTimeoutFunction(this.showPC, 50, this);
    }
}
*/
function PanControlHT(map, cont, left, top){
    
    var dv = ElementCreator.createChildDiv(left, top, 76, 76);  
    /*dv.style.left = "";    dv.style.right = "2px";    */
    //dv.style.top = ""; dv.style.bottom = top + 'px';
    
    dv.style.display = "block";
    
    this.map = map;
    
    this.pan = new Array();    
    this.pan.push(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanNeutral.png'),76, 76, 2, true));
    this.pan.push(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanRight.png'),76, 76, 2, true));    
    this.pan.push(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanDown.png'),76, 76, 2, true));
    this.pan.push(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanLeft.png'),76, 76, 2, true));
    this.pan.push(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanUp.png'),76, 76, 2, true));
    
    dv.appendChild(this.pan[0]);
    attachToEvent(this.pan[0], "mousedown", eventHandler(this, "onMouseDown"));       
    
    dv.appendChild(ElementCreator.createImage(0, 0, redirected_path('map/images/PanControl/PanControlBackground.png'),76, 70, 1 , true))
    
    this.main_div = dv;
    this.main_div.id = "pan_control_main_div";
    this.main_div.style.zIndex = 1;
    cont.appendChild(this.main_div);        

}

PanControlHT.prototype.onMouseDown = function(e){
    var pt = getMouseCoordinates(e);
	pt.x -= findPosX(this.pan[0]) ;
	pt.y -= findPosY(this.pan[0]) ;
    var x = pt.x;
    var y = pt.y;
    var r = (x - 38)*(x - 38) + (y - 38)*(y - 38);
    
    this.evt = null;    
    var target = null;
    
    if (r > 32*32) return;
        if (y > (76 - x)){
            if (y < x)  { //alert ("Right")
                this.evt = {keyCode : 39};
                target = this.pan[1];
            }
            else { //alert("Down");
                target = this.pan[2];
                this.evt = {keyCode : 40};
                
            }
        }    
        else{
        if (y < x) {//alert("Up");
            target = this.pan[4];
            this.evt = {keyCode : 38};
        }
        else { //alert("Left");
            target = this.pan[3];
            this.evt = {keyCode : 37};
        }
    }
        
    this.main_div.replaceChild(target, this.pan[0]);
    
    if(this.evt){
	    this.map.onKeyDown(this.evt);
    	
	    this.eventSrc = target.setCapture ? target : window;
	    if(this.eventSrc.setCapture) this.eventSrc.setCapture();
    	
	    this.mouseUpHandler = eventHandler(this, "onMouseUp");
    	
	    attachToEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
    	
	    cancelBubbling(e);
	}
}

PanControlHT.prototype.onMouseUp = function(){

    if(this.mouseUpHandler){
        detachFromEvent(this.eventSrc, "mouseup", this.mouseUpHandler);
        this.mouseUpHandler = null;
    }
    this.main_div.replaceChild(this.pan[0], this.main_div.childNodes[0]);
    this.map.onKeyUp(this.evt);
    
    if(this.eventSrc.releaseCapture)
		this.eventSrc.releaseCapture();
	this.eventSrc = null;
}

PanControlHT.prototype.changeLocation = function(left, top){	
	this.main_div.style.left =  left + 'px';
	//this.main_div.style.top = ""; this.main_div.style.bottom = top + 'px';
}
function HTMapView(){
    this.mapcopyright = new Array();    
    this.mapcopyright.push("\u00A92009 T-HT - Map data \u00A92009 Mireo, Interactive1"); // 0 - map    - black
    this.mapcopyright.push("\u00A92009 T-HT - Image \u00A92009 DigitalGlobe, Interactive1"); // 1 - satellite - white
    this.mapcopyright.push("\u00A92009 T-HT - Map data \u00A92009 Mireo, Image \u00A92009 DigitalGlobe, Interactive1"); // 2 - hybrid - white
    
    this.mapcopyrightcolor = ["black", "white", "white"];   
    this.innerText = ["Map", "Satellite", "Hybrid"];
}

HTMapView.prototype.createChildControls = function(map){
     map.pan_control = new PanControlHT (map, map.container, map.viewSize.width - 80, 2); // left - top
     map.zoom_slider = new ZoomSliderHT(map, map.container, map.viewSize.width - 36, 72); // left - top
     
     map.map_switcher = new MapSwitcherHT (map, map.container, map.viewSize.width - 80, 2, map.mapviewtype, this.innerText);
     map.createCopyright(map.pageview.mapcopyright[map.mapviewtype], map.pageview.mapcopyrightcolor[map.mapviewtype]);
}

HTMapView.prototype.onResize = function(map){
    map.pan_control.changeLocation(map.viewSize.width - 80, 2);
    map.zoom_slider.changeLocation(map.viewSize.width - 36, 72);
    map.map_switcher.changeLocation(map.viewSize.width - 80, 2);
}


var SELECT_PEN = 0;
var SELECT_BRUSH = 1;
var DRAW_LINE = 2;
var DRAW_POLYLINE = 3;
var DRAW_RECTANGLE = 4;
var DRAW_ELLIPSE = 5;
var FILL_POLYGON = 6;
var FILL_RECTANGLE = 7;
var FILL_ELLIPSE = 8;
var DRAW_DIRECTED_POLYLINE = 9;
var SET_ALPHA_LEVEL = 10;

function GraphicsCommand(cmd_id, cmd_prms){
	this.commandID = cmd_id;
	this.commandParams = cmd_prms;
}

GraphicsCommand.toBase64String = function(commands){
	return bytesToBase64(commandsToBytes(commands));
	
	function commandsToBytes(arr){
		var ret = new Array();
		for (var i = 0; i < arr.length; ++i){
			var commandID = arr[i].commandID;
			var commandParams = arr[i].commandParams;
			var len = commandParams.length;
			ret.push(commandID&255);
			ret.push((commandID >> 8)&255);
			if (commandID == DRAW_POLYLINE || commandID == FILL_POLYGON || commandID == DRAW_DIRECTED_POLYLINE){
				var n = len/2;
				ret.push(n&255);
				ret.push((n >> 8)&255);
			}
			for (var j = 0; j < len; ++j){
				var x = commandParams[j];
				ret.push(x&255);
				ret.push((x >> 8)&255);
			}
		}
		return ret;
	}
	
	function bytesToBase64(arr){
		var encTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
		var ret = new Array();
		var i = 0;
		do{
			var b1 = arr[i++];
			var b2 = arr[i++];
			var b3 = arr[i++];
			var c1 = b1 >> 2;
			var c2 = ((b1 & 3) << 4) | (b2 >> 4);
			var c3 = ((b2 & 15) << 2) | (b3 >> 6);
			var c4 = b3 & 63;
			if (isNaN(b2)) {
				c3 = c4 = 64;
			}else if (isNaN(b3)){
				c4 = 64;
			}
			ret.push(encTable.charAt(c1));
			ret.push(encTable.charAt(c2));
			ret.push(encTable.charAt(c3));
			ret.push(encTable.charAt(c4));
		}while(i < arr.length);
		var rs = ret.join("");
		
		return rs;
	}
}

GraphicsCommand.selectPen = function(a, r, g, b, w){
	return new GraphicsCommand(SELECT_PEN, arguments);
}

GraphicsCommand.selectBrush = function(a, r, g, b){
	return new GraphicsCommand(SELECT_BRUSH, arguments);
}

GraphicsCommand.drawLine = function(x1, y1, x2, y2){
	return new GraphicsCommand(DRAW_LINE, arguments);
}

GraphicsCommand.drawPolyline = function(){
	if (arguments.length != 1){
		return new GraphicsCommand(DRAW_POLYLINE, arguments);
	}else{
		return new GraphicsCommand(DRAW_POLYLINE, arguments[0]);
	}
}

GraphicsCommand.drawRectangle = function(x, y, w, h){
	return new GraphicsCommand(DRAW_RECTANGLE, arguments);
}

GraphicsCommand.drawEllipse = function(x, y, w, h){
	return new GraphicsCommand(DRAW_ELLIPSE, arguments);
}

GraphicsCommand.fillPolygon = function(){
	if (arguments.length != 1){
		return new GraphicsCommand(FILL_POLYGON, arguments);
	}else{
		return new GraphicsCommand(FILL_POLYGON, arguments[0]);
	}
}

GraphicsCommand.fillRectangle = function(x, y, w, h){
	return new GraphicsCommand(FILL_RECTANGLE, arguments);
}

GraphicsCommand.fillEllipse = function(x, y, w, h){
	return new GraphicsCommand(FILL_ELLIPSE, arguments);
}

GraphicsCommand.drawDirectedPolyline = function(){
	if (arguments.length != 1){
		return new GraphicsCommand(DRAW_DIRECTED_POLYLINE, arguments);
	}else{
		return new GraphicsCommand(DRAW_DIRECTED_POLYLINE, arguments[0]);
	}
}

GraphicsCommand.setAlphaLevel = function(a){
	return new GraphicsCommand(SET_ALPHA_LEVEL, arguments);
}
/* 
	MireoMap public interface
	

	************ PUBLIC METHODS **********

	function setCenterAndZoom(pt, z);
		pt - tocka s WGS koordinatama
		z - indeks u zoom faktor tablici (0, 1, 2, 3, ...., max_zoom_idx)
		// postavlja mapu s centrom u pt i zoom faktorom iz indeksa

	function moveCenterTo(pt, bContinous);
		pt - tocka s WGS koordinatama
		// pomice mapu do novog centra uz isti zoom faktor kakav je bio;
		   ako je nova tocka na tekucem patchworku onda se radi move a ne novi setCenterAndZoom
		   ako je uz to i bContinous==true onda se radi animirani pomak
	
	function getCenter();
		return pt
		// pt je tocka u WGS koordinatama
		
	function getZoomLevel();
		return int
		// vraca indeks iz tablice zoom faktora
	
	function setZoomLevel(z);
		z - indeks u zoom faktor tablici (0, 1, 2, 3, ...., max_zoom_idx)
		// mijenja zoom level uz isti centar
		
	function getBounds();
		return rect;
		// rect je WGS pravokutnik s koordinatama vidljvog dijela mape (ne patchworka)
	
	function setBounds(rect);
		rect - WGS pravokutnik
		//postavlja vidljivi dio mape (ne patchworka);
	
	function getMapDataBounds();
		return rect
		// rect je WGS pravokutnik s totalnim granicama mape koju nam server zeli dati

	function addOvermapDrawer(drawer);
		return void;
		// dodaje overmap drawer tipa RouteDrawer ili GeoAddressDrawer
		
	function removeOvermapDrawer(drawer)
		return void;
		// uklanja overmap drawer tipa RouteDrawer ili GeoAddressDrawer
		
	function addMapMarker(marker)
		return void;
		// dodaje marker tipa MapMarker

	function removeMapMarker(marker)
		return void;
		// uklanja marker tipa MapMarker
		
	function displayInfoWindow(pt, html, offset)
		pt - WGS tocka na kojoj treba prikazati info window
		html - string s HTML tekstom koji se insertira u info DIV u info windowu
		offset - opcionalni argument, koliko piksela prema gore ce biti pomaknut info window
	
	function displayInfoWindowOnMarker(marker, html)
		marker - varijabla tipa MapMarker na kojoj zelimo pokazati info window
		html - string s HTML tekstom koji se insertira u info DIV u info windowu
	
	function closeInfoWindow()
		// zatvara info window ako je bio otvorit
		
	
	************ EVENTS **********
	
	click(pt, overlay_element)
	zoomchanged(oldz, newz)
	move(newcenterpt)
*/

var MireoDefaultCopyright = "Map data and technology \u00A92004-2005 Mireo";

function DefaultMapView(){
    this.mapcopyright = new Array();
    this.mapcopyright.push(MireoDefaultCopyright);
    this.mapcopyright.push(MireoDefaultCopyright);
    this.mapcopyright.push(MireoDefaultCopyright);
    
    this.mapcopyrightcolor = ["black", "black", "black"];
}

DefaultMapView.prototype.createChildControls = function(map){
    map.zoom_slider = new ZoomSlider(map.container, map.viewSize.width - 50, 30);
	//map.map_switcher = new MapSwitcher (map.container, map.viewSize.width, 30, map.mapviewtype);
	map.createCopyright(map.pageview.mapcopyright[map.mapviewtype], map.pageview.mapcopyrightcolor[map.mapviewtype]);
}

DefaultMapView.prototype.onResize = function(map){
        map.zoom_slider.changeLocation(map.viewSize.width - 50, 30);
	    //map.map_switcher.changeLocation(map.viewSize.width, 30);
	    map.copyright.style.left = (map.viewSize.width - 230) + 'px';
	    map.copyright.style.top = (map.viewSize.height - 20) + 'px';
}
//PUBLIC
function MireoMap(dv, pageview){  
    
    if(!pageview){
        this.pageview = new DefaultMapView();
    }
    else this.pageview = pageview;
    
	this.container = dv;
	dv.style.overflow = "hidden";

	this.panning = true;
	
	this.keyUpDown = 0;
	this.keyLeftRight = 0;
	this.ctrl = false;
	
	this.zf = null;
	this.mercRes = null;
	this.mapviewtype = 0; // 0-map, 1-satellite, 2-hybrid
	
	this.viewSize = new Size(this.container.offsetWidth, this.container.offsetHeight);
	this.calcPatchSize();
	this.createDragDiv();
	this.createOverlay();
	this.createMarkerContainer();
	this.markers = new Array();
	this.sort_markers = true;
	
	this.dragObject = new Dragger(this.dragDiv, 0, 0, null, true);
	this.dragObject.ondrag = eventHandler(this, "onDrag");

	this.dragObject.ondragend = eventHandler(this, "onDragEnd");

	this.dragObject.onzoom = eventHandler(this, "onZoom");


	this.dragObject.onclick = eventHandler(this, "onClick");

	this.dragObject.ondblclick = eventHandler(this, "onDblClick");

	this.onmousewheel = attachToWheel(this.dragDiv, eventHandler(this, "onMouseWheel"));

	this.scale = new Scale(this.container, 20, this.viewSize.height - 50);	

    this.pageview.createChildControls(this); // Postavi ZoomSlider, MapSwitcher, Copyright, po potrebi PanControl
  
    this.zoom_slider.onzoomchange = eventHandler(this, "onZoomChange");
    
    if(this.map_switcher){
		this.map_switcher.onmapviewchangeS = eventHandler(this, "onMapViewChangeS");
		this.map_switcher.onmapviewchangeM = eventHandler(this, "onMapViewChangeM");
		this.map_switcher.onmapviewchangeH = eventHandler(this, "onMapViewChangeH");
	}
	
	if(typeof MIREO_CKEY != "undefined") {
        this.dataBounds = Global.GetMapMercatorBoundsCkey(MIREO_CKEY);
    } else {
        this.dataBounds = Global.GetMapMercatorBounds();
    }
	
	this.wBoundsLT = new Point(this.dataBounds.x, this.dataBounds.y);
	this.wBoundsRB = new Point(this.dataBounds.x + this.dataBounds.width, this.dataBounds.y + this.dataBounds.height);
	attachToEvent(window.document,"keydown", eventHandler(this, "onKeyDown"));
	attachToEvent(window.document,"keyup", eventHandler(this, "onKeyUp"))

    this.borders = new Array();    
    this.borders.push( ElementCreator.createImageRelative(redirected_path('map/images/Borders/upper_right.png'),13, 11, true) );
    this.borders.push( ElementCreator.createImageRelative(redirected_path('map/images/Borders/lower_right.png'),13, 11, true) );
    this.borders.push( ElementCreator.createImageRelative(redirected_path('map/images/Borders/lower_left.png'),13, 11, true) );
    this.borders.push( ElementCreator.createImageRelative(redirected_path('map/images/Borders/upper_left.png'),13, 11, true) );
    
    var me = this;
    
    if(browser.type == 2 && browser.version < 3){
		document.onmousemove = function(e){
			me.mouseX = e.pageX;
			me.mouseY = e.pageY;
		}
    }
}

MireoMap.imgSize = 256;
MireoMap.blankImg = new Image(MireoMap.imgSize, MireoMap.imgSize);

if(typeof MIREO_REDIRECT_TO != "undefined"){
	MireoMap.imgUrl = MIREO_REDIRECT_TO + '/MapServer.aspx?'
	MireoMap.blankImg.src = MIREO_REDIRECT_TO + '/blankMap.gif';
}else{
	var dir = 'http://' + window.location.host;
	dir += window.location.pathname; 
	dir = dir.substring(0, dir.lastIndexOf('/'));

	MireoMap.imgUrl = dir + '/MapServer.aspx?';
	MireoMap.blankImg.src = dir + '/blankMap.gif';
}


MireoMap.zfM = [8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192];

//PUBLIC
MireoMap.prototype.setCenterAndZoom = function(pt, z){
	this.SetWMap(Sphere.Deg2World(pt),this.indexToZoomFactor(z));
}

//PUBLIC
MireoMap.prototype.moveCenterTo = function(pt, bContinous){
	this.moveCenterToW(Sphere.Deg2World(pt), bContinous);
}

//PUBLIC
MireoMap.prototype.getCenter = function(){
	return Sphere.World2Deg(this.wcenter);
}

//PUBLIC
MireoMap.prototype.getZoomLevel = function(){
	return this.zoomFactorToIndex(this.zf);
}

//PUBLIC
MireoMap.prototype.setZoomLevel = function(z){
	this.SetWMap(this.wcenter,this.indexToZoomFactor(z));
}

//PUBLIC
MireoMap.prototype.getBounds = function(){
	var tlpix = new Point(-this.dragObject.left, -this.dragObject.top);
	var brpix = new Point(tlpix.x + this.viewSize.width, tlpix.y + this.viewSize.height);
	var tldeg = Sphere.World2Deg(this.DragDivPixToWMerc(tlpix));
	var brdeg = Sphere.World2Deg(this.DragDivPixToWMerc(brpix));
	return new DRect(tldeg.x, tldeg.y, brdeg.x, brdeg.y);
}

//PUBLIC
MireoMap.prototype.setBounds = function(rect){
	var cx = (rect.x1 + rect.x2)/2;
	var cy = (rect.y1 + rect.y2)/2;
	var pt1w = Sphere.Deg2World(new DPoint(rect.x1, rect.y1));
	var pt2w = Sphere.Deg2World(new DPoint(rect.x2, rect.y2));
	var zfx = Math.abs(pt2w.x - pt1w.x)/this.viewSize.width;
	var zfy = Math.abs(pt2w.y - pt1w.y)/this.viewSize.height;
	var z = this.zoomFactorToIndex(Math.max(zfx, zfy));
	this.setCenterAndZoom(new DPoint(cx, cy), z);
}

//PUBLIC
MireoMap.prototype.getMapDataBounds = function(){
	return dataBounds;
}

//PUBLIC
MireoMap.prototype.addOverMapDrawer = function(omd){
	omd.setMap(this);
	this.overlay.appendChild(omd.img);
}

//PUBLIC
MireoMap.prototype.removeOverMapDrawer = function(omd){
    if(omd.img.parentNode == this.overlay){
        this.overlay.removeChild(omd.img);
	}
}

//PUBLIC
MireoMap.prototype.addClientMapDrawer = function(cdrawer){
	cdrawer.SetMap(this);
	this.overlay.appendChild(cdrawer.canvas);
	cdrawer.Set2dContext();
}

//PUBLIC
MireoMap.prototype.removeClientMapDrawer = function(cdrawer){
    if(cdrawer.canvas.parentNode == this.overlay){
        this.overlay.removeChild(cdrawer.canvas);
	}
}

//PUBLIC
MireoMap.prototype.addMapMarker = function(marker){
	marker.div.setPosition(this.WMercToDragDivPix(marker.position));
	this.markers.push(marker);
	this.markerContainer.appendChild(marker.div);
	if(this.sort_markers){
		this.sortMarkers();
	}
}

//PUBLIC
MireoMap.prototype.updateMapMarker = function(marker, icon, position, toolTip)
{
    if (icon != null)
        marker.setIcon(icon);
    if (position != null)
    {   
        marker.position = Sphere.Deg2World(position);  
	    marker.div.setPosition(this.WMercToDragDivPix(marker.position));
	}
	if (toolTip != null)
	    marker.div.setToolTip(toolTip);	    
}

//PUBLIC
MireoMap.prototype.removeMapMarker = function(marker){
	this.markerContainer.removeChild(marker.div);
	for(var i = 0; i < this.markers.length; ++i){
		if (this.markers[i] == marker){
			this.markers.splice(i,1);
			break;	
		}
	}
}

//PUBLIC
MireoMap.prototype.displayInfoWindow = function(pt, html, offset, addr){
	if (addr == null){
		addr = new GeoAddress();
		addr.pt = pt;
	}
	this.displayInfoWindowW(Sphere.Deg2World(pt), html, offset, addr);
}

//PUBLIC
MireoMap.prototype.displayInfoWindowOnMarker = function(marker, html, addr){
	if (addr == null){
		addr = new GeoAddress();
		addr._POI = marker.image.title;
		addr.pt = Sphere.World2Deg(marker.position);
	}
	this.displayInfoWindowW(marker.position, html, marker.height, addr);
}

//PUBLIC
MireoMap.prototype.closeInfoWindow = function(){
	if (this.infoWindow != null){
		this.removeMapMarker(this.infoWindow);
		this.infoWindow = null;
	}
}

//PUBLIC
MireoMap.prototype.getTopLeft = function(){
	var x = parseInt(this.mapImages[0][0].style.left);
	var y = parseInt(this.mapImages[0][0].style.top);
    return new Point(x, y);
}



MireoMap.prototype.createCopyright = function(html, color){
    var ddiv = '<DIV style="font-family: Arial, sans serif; font-size:10px; text-align:right">' + html + '</DIV>';
    var size = measureHTML(ddiv);	
	this.copyright = ElementCreator.createChildDiv(this.viewSize.width - size.width, this.viewSize.height - 20, 
		size.width, 15, null, 'Arial, sans serif', '10px', 'right');
    
    this.copyright.style.left=''; this.copyright.style.top='';
    this.copyright.style.right = '10px'; 
    this.copyright.style.bottom = '5px';
	this.copyright.innerHTML = html;
	this.copyright.style.color = color;
	this.container.appendChild(this.copyright);
}

MireoMap.prototype.updateCopyright = function(html, color){
    var ddiv = '<DIV style="font-family: Arial, sans serif; font-size:10px; text-align:right">' + html + '</DIV>';
    var size = measureHTML(ddiv);	
    
    this.copyright.style.width = size.width + 'px';
    this.copyright.innerHTML = html;
    this.copyright.style.color = color;    
    
}

MireoMap.prototype.viewSizeChanged = function(){
	if (this.mapImages == null) return;
	var bigger_size = this.container.offsetWidth > this.viewSize.width || this.container.offsetHeight > this.viewSize.height;
	var dx = this.zf*(this.container.offsetWidth - this.viewSize.width)/2
	var dy = -this.zf*(this.container.offsetHeight - this.viewSize.height)/2
	var newCenter = new Point(this.wcenter.x + dx, this.wcenter.y + dy);
	
	this.viewSize = new Size(this.container.offsetWidth, this.container.offsetHeight);
	this.calcPatchSize();
	this.scale.changeLocation(20, this.viewSize.height - 50);
	
	this.pageview.onResize(this); //...changeLocation
	
	this.changePatchwork();
	
	switch(this.pointOffBoundsX(newCenter)){
		case -1:
			newCenter.x  = this.wBoundsLT.x;
			break;
		case 1:
			newCenter.x = this.wBoundsRB.x - 1;
			break;
	}
	switch(this.pointOffBoundsY(newCenter)){
		case -1:
			newCenter.y  = this.wBoundsLT.y;
			break;
		case 1:
			newCenter.y = this.wBoundsRB.y - 1;
			break;
	}
	this.wcenter = newCenter;
	if (this.move){
		this.move(Sphere.World2Deg(this.wcenter));
	}
	this.scale.ResolutionChanged(this.GetResolution());
	if (bigger_size){
		for (var i = 0; i < this.overlay.childNodes.length; ++i){
			this.overlay.childNodes[i].redraw();
		}
	}
}

MireoMap.prototype.displayInfoWindowPaspartue = function (){
    var deltaL = 0;
    if (this.zoom_slider.main_div.style.left)  
        var deltaR = this.viewSize.width - parseInt(this.zoom_slider.main_div.style.left);
        else 
            var deltaR = parseInt(this.zoom_slider.main_div.style.width) + parseInt(this.zoom_slider.main_div.style.right);
    var deltaT = (this.map_switcher ? parseInt(this.map_switcher.main_div.style.top) + parseInt(this.map_switcher.main_div.style.height) : 0);
    var deltaScale = this.viewSize.height - parseInt(this.scale.main_div.style.top);
    if (this.copyright.style.top) 
        var deltaCopyright = this.viewSize.height - parseInt(this.copyright.style.top);
        else 
            var deltaCopyright = parseInt(this.copyright.style.height) + parseInt(this.copyright.style.bottom);
    var deltaB = Math.max(deltaScale, deltaCopyright);
    
    this.infoWindow.left += deltaL;
    this.infoWindow.right += deltaR;
    this.infoWindow.top -= deltaT;
    this.infoWindow.bottom += deltaB;
}


function selectInfoWindow(iwinName){
    MireoMap.InfoWin = iwinName;
}

MireoMap.prototype.displayInfoWindowW = function(pt, html, offset, addr){
	if (offset == null) offset = 0;
	this.closeInfoWindow();
	
    if(!MireoMap.InfoWin){
        selectInfoWindow(InfoWindow);
    }
    
	this.infoWindow = new MireoMap.InfoWin(this, pt, html, offset, addr);
	this.infoWindow.bringToTop = true;
	
	//this.infoWindow.setInfo(pt, html, offset, addr);
	this.addMapMarker(this.infoWindow);
	
	if(this.ondisplayinfowindow) this.ondisplayinfowindow(this.infoWindow);
	
	var left = -this.dragObject.left;
	var top = -this.dragObject.top;
	var right = left + this.viewSize.width;
	var bottom = top + this.viewSize.height;
	var move = false;
	var x = this.wcenter.x;
	var y = this.wcenter.y;
	
	this.displayInfoWindowPaspartue();
	
	var ptPix = new Point(0,0);
	ptPix = this.WMercToDragDivPix(pt);
	
	ptPix.x = ptPix.x - findPosX(this.container) + findPosX(this.dragDiv) ;
	ptPix.y = ptPix.y - findPosY(this.container) + findPosY(this.dragDiv) - offset;
		
	if (this.infoWindow.left < left){
		move = true;
		x -= (left - (this.infoWindow.left))*this.zf
		ptPix.x += (left - (this.infoWindow.left));
	}else if(this.infoWindow.right > right){
		move = true;
		x -= (right - (this.infoWindow.right))*this.zf
		ptPix.x += right - (this.infoWindow.right);
	}
	if (this.infoWindow.top < top){
		move = true;
		y += (top - (this.infoWindow.top))*this.zf
        ptPix.y += top - (this.infoWindow.top);
	}else if(this.infoWindow.bottom > bottom){
		move = true;
		y += (bottom - (this.infoWindow.bottom))*this.zf
		ptPix.y -= bottom - this.infoWindow.bottom;
	}

	var newCenterPoint = new Point(x,y);
	
	if (newCenterPoint != new Point(x,y)){
	    move = true;
	}
	if(move){
		this.moveCenterToW(newCenterPoint, true);
	}
}


MireoMap.prototype.moveCenterToW = function(newcenter, bContinous){
	var zf = this.zf;
	if (bContinous && this.pointOnPatchwork(newcenter)){
		var contArr = new Array();
		var step = 30*zf;
		var deltax = newcenter.x - this.wcenter.x;
		var deltay = newcenter.y - this.wcenter.y;
		var delta = Math.sqrt(deltax*deltax + deltay*deltay);
		var stepx = step*deltax/delta;
		var stepy = step*deltay/delta;
		var x = this.wcenter.x;
		var y = this.wcenter.y;
		var lim = delta - step;
		for (var i = 0; i < lim; i += step){
			x += stepx;
			y += stepy;
			contArr.push(new Point(x, y));
		}
		contArr.push(newcenter);
		this.contMove(contArr, 0);
	}else{
		this.setCenter(new Point(newcenter.x, newcenter.y));
		this.setOverlay();
	}
}

MireoMap.prototype.indexToZoomFactor = function(i){
	if (i < 0) return MireoMap.zfM[0];
	if (i >= MireoMap.zfM.length) return MireoMap.zfM[MireoMap.zfM.length - 1];
	return MireoMap.zfM[i];
}

MireoMap.prototype.zoomFactorToIndex = function(zf){
	for (var i = 0; i < MireoMap.zfM.length; ++i)
		if (zf <= MireoMap.zfM[i])
			return i;
	return MireoMap.zfM.length - 1;
}

MireoMap.prototype.roundZoomFactor = function(zf){
	return MireoMap.zfM[this.zoomFactorToIndex(zf)];
}

MireoMap.prototype.changeZoomFactor = function(zf){
	this.zf = zf;
	this.rzf = 1 / this.zf;
	this.mercRes = MireoMap.imgSize * this.zf;
}

MireoMap.prototype.GetResolution = function(){
	var wc = Sphere.World2Deg(this.wcenter);
	return (this.zf * Sphere.MetersPerWorld) * Math.cos(Sphere.Deg2Rad(wc.y));
}

MireoMap.prototype.moveCenter = function(x, y){
	this.wcenter.x += x;
	this.wcenter.y += y;
	this.scale.ResolutionChanged(this.GetResolution());
}

MireoMap.prototype.pointOnPatchwork = function(wp){
	var pt = this.WMercToPatchPix(wp);
	var size = this.patchPixSize();
	return pt.x >= 0 && pt.y >= 0 && pt.x < size.width && pt.y < size.height;
}

MireoMap.prototype.contMove = function(contArr, idx){
	if (idx != null){
		this.setCenterSilent(contArr[idx]);
		++idx;
		if (idx < contArr.length){
			setTimeoutFunction(this.contMove, 10, this, contArr, idx);
		}else{
			this.setOverlay();
			if (this.move){
				this.move(Sphere.World2Deg(this.wcenter));
			}
		}
	}else{
		if (this.keyUpDown == 0 && this.keyLeftRight == 0){
			this.setOverlay();
			if (this.move){
				this.move(Sphere.World2Deg(this.wcenter));
			}
		}else{
			var movex = 10*this.keyLeftRight*this.zf;
			var movey = -10*this.keyUpDown*this.zf;
			if (this.ctrl){
				movex *= 10;
				movey *= 10;
			}
			this.setCenterSilent(new Point(this.wcenter.x + movex, this.wcenter.y + movey));
			setTimeoutFunction(this.contMove, 1, this, null, null);
		}
	}
}

MireoMap.prototype.setOverlay = function(){
	for (var i = 0; i < this.overlay.childNodes.length; ++i){
		this.overlay.childNodes[i].redraw();
	}
	this.overlay.style.top = this.mapImages[0][0].style.top;
	this.overlay.style.left = this.mapImages[0][0].style.left;
}

MireoMap.prototype.pointOffBoundsX = function(wpt){
	if (wpt.x < this.wBoundsLT.x)
		return -1; 
	if (wpt.x >= this.wBoundsRB.x)
		return 1;
	return 0;
	
}

MireoMap.prototype.pointOffBoundsY = function(wpt){
	if (wpt.y < this.wBoundsLT.y)
		return -1;
	if (wpt.y >= this.wBoundsRB.y)
		return 1;
	return 0;
}

MireoMap.prototype.setCenter = function(wc){
	this.setCenterSilent(wc);
	if (this.move){
		this.move(Sphere.World2Deg(this.wcenter));
	}
}

MireoMap.prototype.setCenterSilent = function(wc){
	wc = new Point(wc.x, wc.y);
	switch(this.pointOffBoundsX(wc)){
		case -1:
			wc.x  = this.wBoundsLT.x;
			break;
		case 1:
			wc.x = this.wBoundsRB.x - 1;
			break;
	}
	switch(this.pointOffBoundsY(wc)){
		case -1:
			wc.y  = this.wBoundsLT.y;
			break;
		case 1:
			wc.y = this.wBoundsRB.y - 1;
			break;
	}
	var dx = (this.wcenter.x - wc.x)/this.zf;
	var dy = (wc.y - this.wcenter.y)/this.zf;
	this.dragObject.moveTo(this.dragObject.left + dx, this.dragObject.top + dy);
	this.MoveBy(dx, dy);
}

MireoMap.prototype.SetWMap = function(wc, zf) {
	wc = new Point(wc.x, wc.y);
	switch(this.pointOffBoundsX(wc)){
		case -1:
			wc.x  = this.wBoundsLT.x;
			break;
		case 1:
			wc.x = this.wBoundsRB.x - 1;
			break;
	}
	switch(this.pointOffBoundsY(wc)){
		case -1:
			wc.y  = this.wBoundsLT.y;
			break;
		case 1:
			wc.y = this.wBoundsRB.y - 1;
			break;
	}
	var oldz = this.zf;
	this.createPatchwork();
	this.wcenter = wc;
	
	this.changeZoomFactor(zf);
	
	var mr = this.mercRes;
	
	var dwtl = new Point(wc.x - (this.viewSize.width / 2) * zf, wc.y + (this.viewSize.height / 2) * zf);
	var wtl = new Point(Math.floor(dwtl.x / mr) * mr, Math.ceil(dwtl.y / mr) * mr);
	
	var pt = new Point((wtl.x - dwtl.x) / zf, (dwtl.y - wtl.y) / zf);
	this.currMTL = wtl;
	this.currTL = pt;
	this.reloadPatchwork();
	this.dragObject.moveTo(pt.x, pt.y);
	this.scale.ResolutionChanged(this.GetResolution());
	for (var i = 0; i < this.overlay.childNodes.length; ++i){
		this.overlay.childNodes[i].redraw();
	}
	for (var i = 0; i < this.markerContainer.childNodes.length; ++i){
		var node = this.markerContainer.childNodes[i];
		node.setPosition(this.WMercToDragDivPix(node.owner.position));
	}
	this.overlay.style.top = this.mapImages[0][0].style.top;
	this.overlay.style.left = this.mapImages[0][0].style.left;
	this.zoom_slider.updateZoomIndex(this.zoomFactorToIndex(this.zf));
	if (this.zoomchanged){
		this.zoomchanged(oldz, this.zf);
	}
	
	var pixlb = this.WMercToDragDivPix(this.wBoundsLT);
	var pixrt = this.WMercToDragDivPix(this.wBoundsRB);
	this.dragDiv.minX = -pixrt.x + this.viewSize.width/2;
	this.dragDiv.maxX = -pixlb.x + this.viewSize.width/2;
	this.dragDiv.minY = -pixlb.y + this.viewSize.height/2;
	this.dragDiv.maxY = -pixrt.y + this.viewSize.height/2;	
	
	if (this.move){
		this.move(Sphere.World2Deg(this.wcenter));
	}
}


MireoMap.prototype.calcPatchSize = function() {
	this.patchSize = new Size(Math.floor((this.viewSize.width - 2) / MireoMap.imgSize) + 2,
		Math.floor((this.viewSize.height - 2) / MireoMap.imgSize) + 2);
}

MireoMap.prototype.createPatchwork = function() {
	if(this.mapImages != null) return;
	this.mapImages = new Array();
	var imsz = MireoMap.imgSize;
	for (var j = 0; j < this.patchSize.width; ++j){
		var crow = new Array();
		this.mapImages.push(crow);
		for (var i = 0; i < this.patchSize.height; ++i){
			var img = this.createPatchImage(j * imsz, i * imsz, imsz);
			crow.push(img);
		}
	}
}

MireoMap.prototype.changePatchwork = function(){
	var imsz = MireoMap.imgSize;
	if (this.mapImages == null){
		this.mapImages = new Array();
	}
	var szx = this.mapImages.length;
	for (var j = szx; j < this.patchSize.width; ++j){
		this.mapImages.push(new Array());
	}
	for (var j = this.patchSize.width; j < szx; ++j){
		var crow = this.mapImages[this.mapImages.length - 1];
		for (var i = 0; i < crow.length; ++i){
			this.dragDiv.removeChild(crow[i]);
		}
		this.mapImages.pop();
	}
	for (var j = 0; j < this.patchSize.width; ++j){
		var crow = this.mapImages[j];
		var szy = crow.length;
		for (var i = szy; i < this.patchSize.height; ++i){
			var pixX = (j * imsz) + this.mapImages[0][0].pixX;
			var pixY = (crow.length * imsz) + this.mapImages[0][0].pixY;
			
			var img = this.createPatchImage(pixX, pixY, imsz);
			this.setImgSource(img, this.currMTL.x + j * this.mercRes,
				this.currMTL.y - crow.length * this.mercRes);
			crow.push(img);
		}
		for (var i = this.patchSize.height; i < szy; ++i){
			this.dragDiv.removeChild(crow[crow.length-1]);
			crow.pop(img);
		}
	}
	this.MoveBy(0,0);
	this.patchworkChanged();
}

MireoMap.prototype.createOverlay = function(){
	var os = this.patchPixSize();
	this.overlay = ElementCreator.createChildDiv(0, 0, os.width, os.height);
	this.overlay.style.zIndex = 2;
	this.dragDiv.appendChild(this.overlay);
}

MireoMap.prototype.sortMarkers = function(){
	this.markers.sort(function(m1,m2){return m2.position.y - m1.position.y;});
	for (var i = 0; i < this.markers.length; ++i){
		if(this.markers[i].bringToTop){
			this.markers[i].div.style.zIndex = i + this.markers.length;
		}else if (this.markers[i].shadow){
			this.markers[i].div.style.zIndex = i;
		}else{
			this.markers[i].div.style.zIndex = -1;
		}
	}
}


MireoMap.prototype.createPatchImage = function(px, py, imsz) {
	var img = document.createElement("IMG");
	img.style.position = 'absolute';
	img.pixX = px; img.pixY = py;
	img.style.left = px + 'px';
	img.style.top = py + 'px';
	img.style.width = imsz + 'px';
	img.style.height = imsz + 'px';
	img.style.MozUserFocus = "normal";
	img.galleryImg = false;
	if(browser.type == 1){
		img.unselectable = "on";
		img.onselectstart = never;
	} 
	else img.style.MozUserSelect="none";
	img.oncontextmenu = never;
	this.dragDiv.appendChild(img);
	return img;
}

MireoMap.prototype.setImgSource = function(img, mercX, mercY){
	if(mercX != null) img.mercX = mercX;
	if(mercY != null) img.mercY = mercY;
	img.src = MireoMap.blankImg.src;
    var src = MireoMap.imgUrl + 'lwM=' + img.mercX + '&twM=' + img.mercY + '&zf=' + this.zf + 
	                            '&w=' + MireoMap.imgSize + '&h=' + MireoMap.imgSize + 
	                            '&type=' + this.mapviewtype;
    if(typeof MIREO_CKEY != "undefined") {
        src = src + '&ckey=' + MIREO_CKEY;
    }
    img.src = src;
}

MireoMap.prototype.createDragDiv = function(){
	this.dragDiv = document.createElement("div");
	this.dragDiv.style.position = "absolute";
	this.dragDiv.style.top = "0px";
	this.dragDiv.style.left = "0px";
	this.dragDiv.style.zIndex = 0;
	this.container.appendChild(this.dragDiv);
}

MireoMap.prototype.createMarkerContainer = function(){
	this.markerContainer = document.createElement("div");
	this.markerContainer.style.position = "absolute";
	this.markerContainer.style.top = "0px";
	this.markerContainer.style.left = "0px";
	this.markerContainer.style.zIndex = 10;
	this.dragDiv.appendChild(this.markerContainer);
}

MireoMap.prototype.reloadPatchwork = function(){
	var imsz = MireoMap.imgSize;
	for (var j = 0; j < this.patchSize.width; ++j){
		var crow = this.mapImages[j];
		for (var i = 0; i < this.patchSize.height; ++i){
			var img = crow[i];
			img.pixX = (j * imsz); img.pixY = (i * imsz);
			img.style.left = (j * imsz) + 'px';
			img.style.top = (i * imsz) + 'px';
			this.setImgSource(img, this.currMTL.x + j * this.mercRes,
				this.currMTL.y - i * this.mercRes);
		}
	}
	this.patchworkChanged();
}

MireoMap.prototype.patchPixSize = function() {
	return new Size(this.patchSize.width * MireoMap.imgSize, this.patchSize.height * MireoMap.imgSize);
}

MireoMap.prototype.WMercToPatchPix = function(wpoint){
	return new Point(Math.round((wpoint.x - this.currMTL.x) * this.rzf),
		Math.round((this.currMTL.y - wpoint.y) * this.rzf));
}
                   
MireoMap.prototype.DragDivPixToWMerc = function(point){
	return new Point((this.dragObject.left + point.x - this.currTL.x)* this.zf + this.currMTL.x,
	                  this.currMTL.y - (this.dragObject.top + point.y - this.currTL.y) * this.zf);
}

MireoMap.prototype.WMercToDragDivPix = function(wpoint){
	return new Point(Math.round((wpoint.x - this.currMTL.x)/this.zf + this.currTL.x - this.dragObject.left),
	                 Math.round((this.currMTL.y - wpoint.y)/this.zf + this.currTL.y - this.dragObject.top ));
}

MireoMap.prototype.onZoomChange = function(zf){
	this.SetWMap(this.wcenter, zf);
}

MireoMap.prototype.onMapViewChangeM = function(){
    this.mapviewtype = 0;
    this.SetWMap (this.wcenter, this.zf);
    this.updateCopyright(this.pageview.mapcopyright[this.mapviewtype], this.pageview.mapcopyrightcolor[this.mapviewtype]);    
}

MireoMap.prototype.onMapViewChangeS = function(){
    this.mapviewtype = 1;
    this.SetWMap (this.wcenter, this.zf);
    this.updateCopyright(this.pageview.mapcopyright[this.mapviewtype], this.pageview.mapcopyrightcolor[this.mapviewtype]);
}

MireoMap.prototype.onMapViewChangeH = function(){
    this.mapviewtype = 2;
    this.SetWMap (this.wcenter, this.zf);
    this.updateCopyright(this.pageview.mapcopyright[this.mapviewtype], this.pageview.mapcopyrightcolor[this.mapviewtype]);
}

MireoMap.prototype.onDrag = function(sz){
	this.dragged = true;
	this.MoveBy(sz.width, sz.height);
}

MireoMap.prototype.onDragEnd = function(e){
	if (!this.fast_overlay && (this.overlay.style.top != this.mapImages[0][0].style.top || this.overlay.style.left != this.mapImages[0][0].style.left)){
		for (var i = 0; i < this.overlay.childNodes.length; ++i){
			this.overlay.childNodes[i].redraw();
		}
		this.overlay.style.top = this.mapImages[0][0].style.top;
		this.overlay.style.left = this.mapImages[0][0].style.left;
	}
	if (this.move && this.dragged){
		this.move(Sphere.World2Deg(this.wcenter));
	}
	this.dragged = false;
}

MireoMap.prototype.onZoom = function(e){
	var x = Math.round((e.startPos.x + e.endPos.x)/2);
	var y = Math.round((e.startPos.y + e.endPos.y)/2);
	this.wcenter = this.DragDivPixToWMerc(new Point(x,y));
	var w = this.viewSize.width;
	var h = this.viewSize.height;
	var nw = Math.abs(e.endPos.x - e.startPos.x);
	var nh = Math.abs(e.endPos.y - e.startPos.y);
	var fact1 = nw/w;
	var fact2 = nh/h;
	var fact = Math.max(fact1, fact2);
	var zf;
	if (e.endPos.x > e.startPos.x){
		zf = Math.round(this.zf*fact);
	}else{
		zf = Math.round(this.zf/fact);
	}
	this.SetWMap(this.wcenter, this.roundZoomFactor(zf));
}

MireoMap.prototype.getMarkerOnPoint = function(x, y){
	var markers = this.markers;
	var overlay_element = null;
	if(this.sort_markers){
		for (var i = markers.length - 1; i >= 0; --i){
			if (markers[i].isClicked(x, y)){
				overlay_element = markers[i];
				break;
			}
		}
	}else{
		for (var i = 0; i < markers.length; ++i){
			if((overlay_element == null || markers[i].div.style.zIndex >= overlay_element.div.style.zIndex) && markers[i].isClicked(x, y)){
				overlay_element = markers[i];
			}
		}
	}
	return overlay_element;
}

MireoMap.prototype.onClick = function(e){
	if (this.click){
		var x = e.pos.x;
		var y = e.pos.y;
		var overlay_element = this.getMarkerOnPoint(x, y);
		this.click(Sphere.World2Deg(this.DragDivPixToWMerc(e.pos)), overlay_element, e);
	}
	if(document.focus){
		document.focus();
	}
}

MireoMap.prototype.onDblClick = function(e){
	if (this.dblclick){
		var x = e.pos.x;
		var y = e.pos.y;
		var overlay_element = this.getMarkerOnPoint(x, y);
		this.dblclick(Sphere.World2Deg(this.DragDivPixToWMerc(e.pos)), overlay_element, e);
	}
}

MireoMap.prototype.MoveBy = function(x, y){
	var imhz = this.patchSize.width; 
	var imvt = this.patchSize.height;
	var imw = MireoMap.imgSize;
	var zf = this.zf;
	this.currTL.x += x; this.currTL.y += y; 
	var tl = this.currTL;

	while((tl.x + imhz * imw) < this.viewSize.width) this.flipRight();
	while((tl.y + imvt * imw) < this.viewSize.height) this.flipBottom();
	while(tl.x > 0) this.flipLeft();
	while(tl.y > 0) this.flipTop();
	this.moveCenter(-x * zf, y * zf);
}

MireoMap.prototype.flipRight = function(){
	var col = this.mapImages.shift();
	var imhz = this.patchSize.width; 
	var imw = MireoMap.imgSize;
	var zf = this.zf;
	for(var i = 0; i < col.length; ++i){
		var img = col[i];
		img.pixX += imhz * imw;
		img.style.left = img.pixX + 'px';
		img.mercX += imhz * imw * zf;
		this.setImgSource(img);
	}
	this.mapImages.push(col);
	this.currTL.x += MireoMap.imgSize;
	this.currMTL.x += MireoMap.imgSize * this.zf;
	this.patchworkChanged();
}

MireoMap.prototype.flipLeft = function(){
	var imhz = this.patchSize.width; 
	var imw = MireoMap.imgSize;
	var zf = this.zf;
	var col = this.mapImages.pop();
	for(var i = 0; i < col.length; ++i){
		var img = col[i];
		img.pixX -= imhz * imw;
		img.style.left = img.pixX + 'px';
		img.mercX -= imhz * imw * zf;
		this.setImgSource(img);
	}
	this.mapImages.unshift(col);
	this.currTL.x -= MireoMap.imgSize;
	this.currMTL.x -= MireoMap.imgSize * this.zf;
	this.patchworkChanged();
}

MireoMap.prototype.flipTop = function(){
	var imvt = this.patchSize.height;
	var imw = MireoMap.imgSize;
	var zf = this.zf;
	
	var strip = this.mapImages;
	for(var i = 0; i < strip.length; ++i){
		var img = strip[i].pop();
		img.pixY -= imvt * imw;
		img.style.top = img.pixY + 'px';
		img.mercY += imvt * imw * zf;
		this.setImgSource(img);
		strip[i].unshift(img);
	}
	this.currTL.y -= MireoMap.imgSize;
	this.currMTL.y += MireoMap.imgSize * this.zf;
	this.patchworkChanged();

}

MireoMap.prototype.flipBottom = function(){
	var imvt = this.patchSize.height;
	var imw = MireoMap.imgSize;
	var zf = this.zf;
	
	var strip = this.mapImages;
	for(var i = 0; i < strip.length; ++i){
		var img = strip[i].shift();
		img.pixY += imvt * imw;
		img.style.top = img.pixY + 'px';
		img.mercY -= imvt * imw * zf;
		this.setImgSource(img);
		strip[i].push(img);
	}
	this.currTL.y += MireoMap.imgSize;
	this.currMTL.y -= MireoMap.imgSize * this.zf;
	this.patchworkChanged();
}

MireoMap.prototype.onKeyDown = function(e){
	if(e.ctrlKey||(e.altKey||e.metaKey)){
		return true
	}
	if(e.target&&(e.target.nodeName=="INPUT"&&e.target.getAttribute("type").toLowerCase()=="text"||e.target.nodeName=="TEXTAREA")){
		return true
	}
	
}

MireoMap.prototype.onKeyDown = function(e){
	if (this.ignoreKeyEvent(e))
		return true;
	var moving = (this.keyUpDown != 0 || this.keyLeftRight != 0);
	switch(e.keyCode){
		case 38:
			this.keyUpDown = -1;
			if (!moving) this.contMove();
			return false;
		case 40:
			this.keyUpDown = 1;
			if (!moving) this.contMove();
			return false;
		case 37:
			this.keyLeftRight = -1;
			if (!moving) this.contMove();
			return false;
		case 39:
			this.keyLeftRight = 1;
			if (!moving) this.contMove();
			return false;
		case 17:
			this.ctrl = true;
			return false;
		case 61:case 43:case 187:case 107:
			this.setZoomLevel(this.zoomFactorToIndex(this.zf)-1);
			return false;
		case 45:case 95:case 189:case 109:
			this.setZoomLevel(this.zoomFactorToIndex(this.zf)+1);
			return false;
	}
	return true;
}

MireoMap.prototype.onKeyUp=function(e){
	switch(e.keyCode){
		case 38:
		case 40:
			this.keyUpDown = 0;
			return false;
		case 37:
		case 39:
			this.keyLeftRight = 0;
			return false
		case 17:
			this.ctrl = false;
			return false;
	}
}

MireoMap.prototype.ignoreKeyEvent=function(e){
  if(e.target&&(e.target.nodeName=="INPUT"&&(e.target.type=="text"||e.target.getAttribute("type").toLowerCase()=="text")||e.target.nodeName=="TEXTAREA")){
    return true;
  }
  return false;
}

MireoMap.prototype.onMouseWheel = function(e){
	if(e.preventDefault){
		e.preventDefault();
	}else{
		e.returnValue = false;
	}
    
    if(this.wheel_zooming != null) return;
    
    var zf_tmp = this.getZoomLevel();
    (wheel(e) > 0) ? (zf_tmp -= 1) : (zf_tmp += 1);
    
    if ((zf_tmp < 0 ) || (zf_tmp >= MireoMap.zfM.length)) return;
        
    var zfVal = this.indexToZoomFactor(zf_tmp);
   
    var pt = (browser.type == 2 && browser.version < 3) ? new Point(this.mouseX, this.mouseY) : getMouseCoordinates(e);
    var pt2 = new Point(pt.x, pt.y);
    
	pt.x -= findPosX(this.dragDiv);
	pt.y -= findPosY(this.dragDiv);   
    
    var ptWMerc = this.DragDivPixToWMerc(pt);
    var ptWGS = Sphere.World2Deg(ptWMerc); 
    
	pt2.x -= findPosX(this.container);
	pt2.y -= findPosY(this.container);
      
    var mapCenterPix = new Point(0,0);
    mapCenterPix.x = Math.round(parseInt(this.viewSize.width)/2);
    mapCenterPix.y = Math.round(parseInt(this.viewSize.height)/2);
    
    var distPix = new Point(0,0);
    distPix.x = mapCenterPix.x - pt2.x;
    distPix.y = mapCenterPix.y - pt2.y;
           
    var newCenterW = new Point (0,0);
    newCenterW.x = ptWMerc.x + distPix.x * zfVal; 
    newCenterW.y = ptWMerc.y - distPix.y * zfVal; 
    
    var newCenterWGS = Sphere.World2Deg(newCenterW);
    (wheel(e) > 0) ? (this.MoveBorders(-1, pt2.x, pt2.y, 0)) : ( this.MoveBorders(1, pt2.x, pt2.y, 0)); // zoom_in = -1, zoom_out = 1
    this.setCenterAndZoom(newCenterWGS,zf_tmp);
    
}

MireoMap.prototype.MoveBorders = function(zoom_type, posx, posy, offset){
    if(this.wheel_zooming == null){
        this.wheel_zooming = 1;
        for (var i=0; i<this.borders.length; i++){
            this.container.appendChild(this.borders[i])
        }
    }
    var dx = zoom_type == -1 ? 20 : -80;
    var signx = [1, 1, -1, -1]; var signy = [-1, 1, 1, -1];
    for(var i = 0; i < 4; ++i){
    	this.borders[i].style.position = 'absolute';
        this.borders[i].style.left = posx + signx[i] * (dx + offset) + 'px';  
        this.borders[i].style.top = posy + signy[i] * (dx + offset) + 'px';
    }
    
    if (offset < 60){
        offset += 20;
        setTimeoutFunction(this.MoveBorders, 100, this, zoom_type, posx, posy, offset);
    }
    else{
        for (var i=0; i<this.borders.length; i++){
            this.container.removeChild(this.borders[i]);
        }
        this.wheel_zooming = null;
    }
    
}


MireoMap.prototype.patchworkChanged = function(){
	if(!this.fast_overlay){
		return;
	}
	for (var i = 0; i < this.overlay.childNodes.length; ++i){
		this.overlay.childNodes[i].redraw();
	}
	this.overlay.style.top = this.mapImages[0][0].style.top;
	this.overlay.style.left = this.mapImages[0][0].style.left;
}
function MapIcon(image, imsize, imhotspot, shadow, shsize, shhotspot){
	this.image = image;
	this.imsize = new Size(parseInt(imsize.width), parseInt(imsize.height));
	this.imhotspot = new Point(parseInt(imhotspot.x), parseInt(imhotspot.y));
	if (shadow){
		this.shadow = shadow;
		this.shsize = new Size(parseInt(shsize.width), parseInt(shsize.height));
		this.shhotspot = new Point(parseInt(shhotspot.x), parseInt(shhotspot.y));
	}
}

function MarkerBase(position, tt)
{
	if (position)
		this.position = Sphere.Deg2World(position);
	else
		this.position = new Point(0,0);
	this.div = document.createElement("div");
	this.div.style.position = "absolute";
	this.div.style.top = "0px";
	this.div.style.left = "0px";
	this.div.style.zIndex = 0;
	this.div.owner = this;
	if (tt){
	    this.div.style.cursor = 'pointer';
	    this.div.title = tt;
    }
    this.div.setToolTip = function(tt){
        this.owner.setToolTip(tt);
    } 
}

MarkerBase.prototype.setToolTip = function(tt){
	this.div.title = tt;
}

MarkerBase.prototype.isClicked = function(x, y){
	return (x >= this.x1 && x <= this.x2 && y >= this.y1 && y <= this.y2);
}

function MapMarker(mapIcon, position, tt){
	if (!mapIcon)
		return;    
	this.width = mapIcon.imsize.width;
	this.height = mapIcon.imsize.height;
	this.hotspot = mapIcon.imhotspot;
	inheritBase(this, MarkerBase, position, tt);
    this.image = ElementCreator.createImage(-mapIcon.imhotspot.x, -mapIcon.imhotspot.y, mapIcon.image, mapIcon.imsize.width, mapIcon.imsize.height, 0, true);
    this.div.appendChild(this.image);
    if (mapIcon.shadow){
	    this.shadow = ElementCreator.createImage(-mapIcon.shhotspot.x, -mapIcon.shhotspot.y, mapIcon.shadow, mapIcon.shsize.width, mapIcon.shsize.height, -2, true);
	    this.div.appendChild(this.shadow);
    }
    this.div.setPosition = function(pt){
		this.owner.setPosition(pt);
	}
}

inheritPrototype(MarkerBase, MapMarker);

MapMarker.prototype.setPosition = function(pt){
	var x = pt.x;
	var y = pt.y;
	this.div.style.left = x + 'px';
	this.div.style.top = y + 'px';
	this.x1 = x - this.hotspot.x;
	this.y1 = y - this.hotspot.y;
	this.x2 = this.x1 + this.width;
	this.y2 = this.y1 + this.height;	
}

MapMarker.prototype.setIcon = function(mapIcon)
{
    if (browser.type == 1)
	    this.image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + mapIcon.image + "', sizingMethod='crop')";
    else 
    {
	    this.image.src = mapIcon.image;
	    this.image.galleryImg = false;
    }	
}

function MapMarkerDrawable(csize, chotspot, position, tt)
{
    this.ctx = null;
    this.width = parseInt(csize.width);
	this.height = parseInt(csize.height);
	this.hotspot = new Point(parseInt(chotspot.x), parseInt(chotspot.y));
	inheritBase(this, MarkerBase, position, tt);
	this.canvas = ElementCreator.createCanvas(this.width, this.height);
	this.div.appendChild(this.canvas);
	this.div.setPosition = function(pt){
		this.owner.setPosition(pt);
	}
}

inheritPrototype(MarkerBase, MapMarkerDrawable);

MapMarkerDrawable.prototype.setPosition = function(pt){
	this.x1 = pt.x - this.hotspot.x;
	this.y1 = pt.y - this.hotspot.y;
	this.div.style.left = this.x1 + 'px';
	this.div.style.top = this.y1 + 'px';
	this.x2 = this.x1 + this.width;
	this.y2 = this.y1 + this.height;	
}

MapMarkerDrawable.prototype.Get2dContext = function()
{
    if (this.ctx) return this.ctx;
    if (!this.canvas.getContext)
        G_vmlCanvasManager.initElement(this.canvas);        
    try {
        this.ctx = this.canvas.getContext('2d');
        return this.ctx;
    } catch(e) {
        return null;
    }     
}

function POIMarker(mapIcon, poi){
	inheritBase(this, MapMarker, mapIcon, new DPoint(poi.X, poi.Y), poi.DESCRIPTION);
	this.POI = poi;
}

inheritPrototype(MapMarker,POIMarker);

function CaptionMarker(mapIcon, position, caption, tooltip){
	inheritBase(this, MapMarker, mapIcon, position, tooltip);
	this.captionDiv = document.createElement("DIV");
	var cssText= "z-index:2;left:6px;top:4px;position:absolute;color:white;text-align:center;width:22px;height:12px;font-weight:bold;font-family:Arial;";
	if (caption == null || caption.length < 4){
		cssText += ";font-size:11px;";
	}else{
		cssText += ";font-size:9px;";
	}
	this.captionDiv.style.cssText = cssText;
	this.captionDiv.style.left = (6 - this.hotspot.x)+"px";
	this.captionDiv.style.top = (4 - this.hotspot.y)+"px";
	if (caption != null)
		this.captionDiv.innerHTML = caption;
	this.div.appendChild(this.captionDiv);
}

inheritPrototype(MapMarker,CaptionMarker);

CaptionMarker.prototype.setCaption = function(caption){
	this.captionDiv.innerHTML = caption;
	if (caption == null || caption.length < 4){
		this.captionDiv.style.fontSize = "11px";
	}else{
		this.captionDiv.style.fontSize = "9px";
	}
}

CaptionMarker.prototype.setCss = function(cssText){
	this.captionDiv.style.cssText = cssText;
}
function OverMapDrawer(){
	this.map = null;
	this.img = ElementCreator.createImage(0,0,OverMapDrawer.transparent.src,1,1,1,true);
}

OverMapDrawer.transparent = new Image(1,1);

if(typeof MIREO_REDIRECT_TO != "undefined"){
	OverMapDrawer.drawer_url = MIREO_REDIRECT_TO + '/OffscreenDrawer.aspx?';
	OverMapDrawer.transparent.src = MIREO_REDIRECT_TO + '/Transparent.png';
}else{
	var dir = 'http://' + window.location.host;
	dir += window.location.pathname; 
	dir = dir.substring(0, dir.lastIndexOf('/'));

	OverMapDrawer.drawer_url = dir + '/OffscreenDrawer.aspx?';
	OverMapDrawer.transparent.src = dir + '/Transparent.png';
}

OverMapDrawer.prototype.setMap = function(map){
	this.map = map;
	this.img.owner = this;
	this.img.redraw = function(){
		this.owner.redraw();
	}
	this.preload = new Image();
	this.preload.src = OverMapDrawer.transparent.src;
	attachToEvent(this.preload,"load", eventHandler(this, "onLoad"))
}


OverMapDrawer.prototype.redraw = function(){
	var size = this.map.patchPixSize();
	this.img.style.left = this.map.mapImages[0][0].style.left;
	this.img.style.top = this.map.mapImages[0][0].style.top;
	this.img.style.width = size.width +'px';
	this.img.style.height = size.height + 'px';
	this.clearSrcImg();
	var arr = this.getGraphicsCommands(); //define in derived classes
	if (arr != null && arr.length != 0){
		var str = GraphicsCommand.toBase64String(arr);
		this.setSrcImg(str, size.width, size.height);
	}
}

OverMapDrawer.prototype.clearSrcImg = function(){
	ElementCreator.changeImage(this.img,0,0,OverMapDrawer.transparent.src,1,1,1,true);
}

OverMapDrawer.prototype.setSrcImg = function(cmd, w, h){
	this.preload.width = w;
	this.preload.height = h;
	this.preload.src = OverMapDrawer.drawer_url + 'c=' + cmd + '&w=' + w + '&h=' + h;
}

OverMapDrawer.prototype.onLoad = function(e){
	if (this.preload.complete){
		ElementCreator.changeImage(this.img,0,0,this.preload.src,this.preload.width,this.preload.height,1,true);
	}
}

OverMapDrawer.isLineOnMap = function(x1, y1, x2, y2, w, h){
	var x1code = -1*(x1 < 0) + (x1 > w);
	var y1code = -1*(y1 < 0) + (y1 > h);
	var x2code = -1*(x2 < 0) + (x2 > w);
	var y2code = -1*(y2 < 0) + (y2 > h);

	if (x1 == x2){
		if (x1code != 0)
			return false;
		return y1code != y2code || y1code == 0;
	}else{
		if ((x1code == 0 && y1code == 0) || (x2code == 0 && y2code == 0))
			return true;
		if (x1code == x2code){
			if (x1code != 0){
				return false;
			}else{
				return y1code != y2code;
			}
		}
		if (y1code == y2code){
			if (y1code != 0){
				return false;
			}else{
				return x1code != x2code;
			}
		}
		var k = (y2 - y1)/(x2 - x1);
		var l = y1 - k*x1;
		var l1 = l;
		var l2 = k*w + l;
		return !((l1 < 0 && l2 < 0) || (l1 > h && l2 > h));
	}
}

OverMapDrawer.isPolylineOnMap = function(arr, w, h){
	for (var i = 0; i <= arr.length - 4; i+=2){
		if (OverMapDrawer.isLineOnMap(arr[i], arr[i+1], arr[i+2], arr[i+3], w, h))
			return true;
	}
	return false;
}

function DirectedPolylineDrawer(a, r, g, b, w, br, bg, bb){
	inheritBase(this, OverMapDrawer);
	this.geometries = null;
	this.penCmd = GraphicsCommand.selectPen(255,r,g,b,w);
	this.brushCmd = GraphicsCommand.selectBrush(255, br, bg, bb);
	this.alphaCmd = GraphicsCommand.setAlphaLevel(a);
}

inheritPrototype(OverMapDrawer, DirectedPolylineDrawer);

DirectedPolylineDrawer.prototype.setGeometries = function(geometries){
	this.geometries = geometries;
}

DirectedPolylineDrawer.prototype.setPen = function(a,r,g,b,w){
	this.penCmd = GraphicsCommand.selectPen(255,r,g,b,w);
	this.alphaCmd = GraphicsCommand.setAlphaLevel(a);
}

DirectedPolylineDrawer.prototype.setBrush = function(r,g,b){
	this.brushCmd = GraphicsCommand.selectBrush(255,r,g,b);
}


DirectedPolylineDrawer.prototype.getGraphicsCommands = function(){
	if (this.geometries == null)
		return new Array();
	var max_pts = 220;
	var size = this.map.patchPixSize();
	var sizewidth = size.width;
	var sizeheight = size.height;
	var z = this.map.getZoomLevel();
	var pts_n;
	do{
		var ret = new Array();
		var graphicsCommand = new Array();
		var geometry = this.geometries[z];
		if (geometry == null || geometry.length == 0)
			return ret;
		ret.push(this.alphaCmd);
		ret.push(this.penCmd);
		ret.push(this.brushCmd);
		pts_n = 0;
		var on_map = false;
		var x1;
		var y1;
		var map = this.map;
		var pt = map.WMercToPatchPix(geometry[0]);
		var x2 = pt.x;
		var y2 = pt.y;
		var len = geometry.length;
		for (var i = 1; i < len; ++i){
			x1 = x2;
			y1 = y2;
			pt = map.WMercToPatchPix(geometry[i]);
			x2 = pt.x;
			y2 = pt.y;
			if (OverMapDrawer.isLineOnMap(x1, y1, x2, y2, sizewidth, sizeheight)){
				if (!on_map){
					graphicsCommand.push(x1);
					graphicsCommand.push(y1);
					++pts_n;
				}
				graphicsCommand.push(x2);
				graphicsCommand.push(y2);
				++pts_n;
				on_map = true;
			}else{
				if (graphicsCommand.length > 2){
					ret.push(GraphicsCommand.drawDirectedPolyline(graphicsCommand));
					++pts_n;
				}
				on_map = false;
				graphicsCommand = new Array();
			}
		}
		if (graphicsCommand.length > 2){
			ret.push(GraphicsCommand.drawDirectedPolyline(graphicsCommand));
		}
		if (ret.length == 3){
			ret = new Array();
			return ret;
		}
		++z;
	}while(pts_n > max_pts);
	return ret;
}

function PolypolylineDrawer(a, r, g, b, w){
	inheritBase(this, OverMapDrawer);
	this.geometries = null;
	this.penCmd = GraphicsCommand.selectPen(255,r,g,b,w);
	this.alphaCmd = GraphicsCommand.setAlphaLevel(a);
}

inheritPrototype(OverMapDrawer, PolypolylineDrawer);

PolypolylineDrawer.prototype.setGeometries = function(geometries){
	this.geometries = geometries;
}

PolypolylineDrawer.prototype.setPen = function(a,r,g,b,w){
	this.penCmd = GraphicsCommand.selectPen(255,r,g,b,w);
	this.alphaCmd = GraphicsCommand.setAlphaLevel(a);
}

PolypolylineDrawer.prototype.getGraphicsCommands = function(){
	if (this.geometries == null)
		return new Array();
	var max_pts = 220;
	var pts_n;
	var size = this.map.patchPixSize();
	var z = this.map.getZoomLevel();
	var w = size.width;
	var h = size.height
	do{
		var ret = new Array();
		pts_n = 0;
		ret.push(this.alphaCmd);
		ret.push(this.penCmd);
		var geometry = this.geometries[z];
		for(var i = 0; i < geometry.length; ++i){
			var polyline = geometry[i];
			var pixpolyline = new Array();
			if (polyline.length > 0){
				var n = 1;
				var oldPix = this.map.WMercToPatchPix(polyline[0]);
				pixpolyline.push(oldPix.x);
				pixpolyline.push(oldPix.y);
				++n;
				for (var j = 1; j < polyline.length; ++j){
						var pix = this.map.WMercToPatchPix(polyline[j]);
						if (pix.x != oldPix.x || pix.y != oldPix.y){
							pixpolyline.push(pix.x);
							pixpolyline.push(pix.y);
							++n;
						}
						oldPix = pix;
				}
				var pplen = pixpolyline.length;
				while(pplen >= 4 && !OverMapDrawer.isLineOnMap(pixpolyline[0], pixpolyline[1], pixpolyline[2], pixpolyline[3], w, h)){
					pixpolyline.shift();
					pixpolyline.shift();
					--n;
					pplen -= 2;
				}
				
				while(pplen >= 4 && !OverMapDrawer.isLineOnMap(pixpolyline[pplen-4], pixpolyline[pplen-3], pixpolyline[pplen-2], pixpolyline[pplen-1], w, h)){
					pixpolyline.pop();
					pixpolyline.pop();
					--n;
					pplen -= 2;
				}
				if (pplen >= 4){
					ret.push(GraphicsCommand.drawPolyline(pixpolyline));
					pts_n += n;
				}
			}
		}
		++z;
	}while(pts_n > max_pts && z < this.geometries.length);
	if (ret.length == 2){
		ret = new Array();
	}
	return ret;
}

function GeoAddressDrawer(a,r,g,b,w){
	inheritBase(this, PolypolylineDrawer, a, r, g, b, w);
	this.houseNumberShown = false;
	this.houseNumberMarker = new CaptionMarker(GeoAddressDrawer.houseNumberIcon,new Point(0,0), "");
}

inheritPrototype(PolypolylineDrawer,GeoAddressDrawer);


if(typeof MIREO_REDIRECT_TO != "undefined"){
	GeoAddressDrawer.houseNumberIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/HouseNumber.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
}else{
	GeoAddressDrawer.houseNumberIcon = new MapIcon("map/images/HouseNumber.png", new Size(32, 32), new Point(16, 31), "map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
}

GeoAddressDrawer.lineTypes = new Array("Route",
                                       "Pedestrian zone",
                                       "Walkway",
                                       "Highway",
                                       "Fast road",
                                       "Regional road",
                                       "Main road",
                                       "Local road",
                                       "Connecting road",
                                       "Slow road",
                                       "Minor road",
                                       "Service road",
                                       "Railway",
                                       "Ferry"
                                      )
	
GeoAddressDrawer.isLineType = function(str){
	var num = parseInt(str);
	if(!isNaN(num)){
		return num >= 1001;
	}
	for (var i = 0; i < GeoAddressDrawer.lineTypes.length; ++i){
		if (GeoAddressDrawer.lineTypes[i] == str){
			return true;
		}
	}
	return false;
}

GeoAddressDrawer.prototype.setMap = function(map){
	if (this.houseNumberShown && this.map != null){
		this.map.removeMapMarker(this.houseNumberMarker);
		this.houseNumberShown = false;
	}
	if (this.houseNumberShown && map != null){
		this.map.addMapMarker(this.houseNumberMarker);
		this.houseNumberShown = true;
	}
	OverMapDrawer.prototype.setMap.call(this, map);
}

function uncompress_gm(gm) {
	var ret = new WebGeoAddressGeometry();
	
	ret.zoom_factors = gm.zoom_factors;
	ret.geometries = new Array();
	var polylines = new Array();
	for(var i = 0; i < ret.zoom_factors.length; ++i) {
		ret.geometries.push(new Array());
		polylines.push(new Array());
	}
	
	var zoom_levels = gm.zoom_levels;
	var geometry_x = gm.geometry_x;
	var geometry_y = gm.geometry_y;
	var len = zoom_levels.length;
	var geometries = ret.geometries;
	
	for(var i = 0; i < len; ++i) {
		var pt = { x:geometry_x[i], y:geometry_y[i] };
		if(pt.x != 0 && pt.y != 0) {
			var zoom_level = zoom_levels[i];
			for(var j = 0; j <= zoom_level; ++j)
				polylines[j].push(pt);
		}
		else {
			for(var zf = 0; zf < ret.zoom_factors.length; ++zf) {
				if(polylines[zf].length > 1)
					geometries[zf].push(polylines[zf]);
				polylines[zf] = new Array();
			}
		}
	}
	for(var zf = 0; zf < ret.zoom_factors.length; ++zf)
		if(polylines[zf].length > 1)
			geometries[zf].push(polylines[zf]);
	
	return ret;
}

function uncompress_route(route) {
	var ret = new WebRoute();
	ret.length = route.length;
	ret.time_length = route.time_length;
	ret.points = route.points;
	ret.zoom_factors = route.zoom_factors;
	ret.geometries = new Array();
	var geometry_x = route.geometry_x;
	var geometry_y = route.geometry_y;
	for (var i = 0; i < ret.zoom_factors.length; ++i){
		ret.geometries.push(new Array());
	}
	var zoom_levels = route.zoom_levels;
	var len = route.zoom_levels.length;
	var geometries = ret.geometries;
	for (var i = 0; i < len; ++i){
		var zoom_level = zoom_levels[i];
		var pt = { x:geometry_x[i], y:geometry_y[i] };
		for (var j = 0; j <= zoom_level; ++j){
			geometries[j].push(pt);
		}
	}
	return ret;
}

GeoAddressDrawer.prototype.setGeoAddress = function(ga, zoom){
	if(this.houseNumberShown && this.map != null) {
		this.map.removeMapMarker(this.houseNumberMarker);
		this.houseNumberShown = false;
	}
	var that = this;
	if (!ga) {
		this.setGeometries(null);
		this.point = null;
		this.redraw();
	}
	else {
		if (GeoAddressDrawer.isLineType(ga._type)){
			this.setGeometries(null);
			this.point = null;
			var map = this.map;
			if(zoom) {
				if(IsDRectNull(ga.rc))
					window.setTimeout(function(){ map.setCenterAndZoom(ga.pt, 1); }, 50);
				else
					window.setTimeout(function(){ map.setBounds(ga.rc); }, 50);
			}
			else this.redraw();
			
	        if(typeof MIREO_CKEY != "undefined") {
    			MapSearchWeb.GetGAGeoCompressedCkey(ga, MireoMap.zfM, MIREO_CKEY, geometryCallback);
            } else {
    			MapSearchWeb.GetGAGeoCompressed(ga, MireoMap.zfM, geometryCallback);
            }

		}
		else {
			this.setGeometries(null);
			this.point = new Point(ga.pt.x, ga.pt.y);
			var map = this.map;
			if(zoom)
				window.setTimeout(function(){ map.setCenterAndZoom(ga.pt, 1); }, 50);
			else this.redraw();
		}
	}
	function geometryCallback(geometry) {
		WebException.validate(geometry);
		if(ga._house_no && ga._house_no != "") {
			that.houseNumberMarker.setCaption(ga._house_no);
			that.houseNumberMarker.position = Sphere.Deg2World(ga.pt);
			if(that.map) {
				that.map.addMapMarker(that.houseNumberMarker);
				that.houseNumberShown = true;
			}
		}
		that.setGeometries(uncompress_gm(geometry).geometries);
		window.setTimeout(function(){that.redraw();}, 50);
	}
}

GeoAddressDrawer.prototype.zoomToGeoAddress = function(ga){
	this.setGeoAddress(ga, true);
}

function RouteDrawer(a,r,g,b,w,br,bg,bb){
	inheritBase(this, DirectedPolylineDrawer,a, r, g, b, w, br, bg, bb);
	this.routeStartMarker = new MapMarker(RouteDrawer.routeStartIcon);
	this.routeEndMarker = new MapMarker(RouteDrawer.routeEndIcon);
	this.routePointMarkers = new Array();
	this.routePoints = 0;
}

inheritPrototype(DirectedPolylineDrawer,RouteDrawer);

if(typeof MIREO_REDIRECT_TO != "undefined"){
	RouteDrawer.routeStartIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RouteStart.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RouteStartShadow.png", new Size(32, 32), new Point(7, 31));
	RouteDrawer.routeEndIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RouteEnd.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RouteEndShadow.png", new Size(32, 32), new Point(7, 31));
	RouteDrawer.routePointIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RoutePoint.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
}else{
	RouteDrawer.routeStartIcon = new MapIcon("map/images/RouteStart.png", new Size(32, 32), new Point(16, 31), "map/images/RouteStartShadow.png", new Size(32, 32), new Point(7, 31));
	RouteDrawer.routeEndIcon = new MapIcon("map/images/RouteEnd.png", new Size(32, 32), new Point(16, 31), "map/images/RouteEndShadow.png", new Size(32, 32), new Point(7, 31));
	RouteDrawer.routePointIcon = new MapIcon("map/images/RoutePoint.png", new Size(32, 32), new Point(16, 31), "map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
}


RouteDrawer.prototype.setMap = function(map){
	if (this.geometries != null && this.map != null){
		this.map.removeMapMarker(this.routeStartMarker);
		for (var i = 0; i < this.routePoints; ++i){
			this.map.removeMapMarker(this.routePointMarkers[i]);
		}
		this.map.removeMapMarker(this.routeEndMarker);
	}
	if (this.geometries != null && map != null){
		map.addMapMarker(this.routeStartMarker);
		for (var i = 0; i < this.routePoints; ++i){
			map.addMapMarker(this.routePointMarkers[i]);
		}
		map.addMapMarker(this.routeEndMarker);
	}
	OverMapDrawer.prototype.setMap.call(this, map);
}

RouteDrawer.prototype.setRoute = function(route){
	this.setRoutePrivate(route);
	this.redraw();
}

RouteDrawer.prototype.setRoutePrivate = function(route){
	if (this.geometries != null && this.map != null){
		this.map.removeMapMarker(this.routeStartMarker);
		for (var i = 0; i < this.routePoints; ++i){
			this.map.removeMapMarker(this.routePointMarkers[i]);
		}
		this.map.removeMapMarker(this.routeEndMarker);
		this.routePoints = 0;
	}
	if (!route){
		this.setGeometries(null);
	}else{
		for (var i = this.routePointMarkers.length; i < route.points.length - 2; ++i){
			this.routePointMarkers.push(new CaptionMarker(RouteDrawer.routePointIcon,new Point(0,0), (i+1).toString()));
		}
		this.routePoints = route.points.length - 2;
		
		this.routeStartMarker.position = Sphere.Deg2World(route.points[0]);
		for (var i = 0; i < route.points.length - 2; ++i){
			this.routePointMarkers[i].position = Sphere.Deg2World(route.points[i+1]);
		}
		this.routeEndMarker.position = Sphere.Deg2World(route.points[route.points.length-1]);
		
		if (this.map){
			this.map.addMapMarker(this.routeStartMarker);
			for (var i = 0; i < this.routePoints; ++i){
				this.map.addMapMarker(this.routePointMarkers[i]);
			}
			this.map.addMapMarker(this.routeEndMarker);
		}
		this.setGeometries(route.geometries);
	}
}

RouteDrawer.prototype.zoomToRoute = function(route){
	if (route){
		this.setRoutePrivate(route);
	}
	if (this.geometries != null && this.map != null){
		var geometry = this.geometries[0];
		var left = Number.POSITIVE_INFINITY;
		var right = Number.NEGATIVE_INFINITY;
		var top = Number.POSITIVE_INFINITY;
		var bottom = Number.NEGATIVE_INFINITY;
		for (var i = 0; i < geometry.length; ++i){
			var pt = geometry[i];
			if (pt.x < left)
				left = pt.x;
			if (pt.x > right)
				right = pt.x;
			if (pt.y < top)
				top = pt.y;
			if (pt.y > bottom)
				bottom = pt.y;
		}
		var pt1 = Sphere.World2Deg(new Point(left, top));
		var pt2 = Sphere.World2Deg(new Point(right, bottom));
		if (left != Number.MAX_VALUE){
			this.map.setBounds(new DRect(pt1.x, pt1.y, pt2.x, pt2.y));
		}
	}
}

function InfoWindow(map, pt, html, offset, addr){

    this.iwinType = 0;
	
	this.map = map;
	
	this.position = new Point(0, 0);
	
	this.offset = 0;
	this.offsetx = 0;
	this.offsety = 0;
	
	var sz = measureHTML(html);
	if (sz.width < 200) sz.width = 200;
	sz.width += 10; sz.height += 10;
	this.createImageDiv(sz.width, sz.height + 15);
	
	this.createShadowDiv(sz.width, sz.height);
	
	this.innerDiv = ElementCreator.createChildDiv(12 - sz.width/2, -122 - sz.height, sz.width, sz.height, 'white');
	this.innerDiv.style.overflow = "hidden";
	this.innerDiv.style.zIndex = 1;
	
	this.innerDiv.style.overflow = 'hidden';
	
	this.closeDiv = ElementCreator.createChildDiv(sz.width/2 - 50, -122, 50, 15, 'white', "Arial", '12px');
	this.closeDiv.style.cssText += ';z-index:1;text-align:right;text-decoration:underline;color:blue;cursor:pointer;';
	this.closeDiv.onclick = function(){map.closeInfoWindow();};
	this.closeDiv.innerHTML = "Close";
	
	this.image.appendChild(this.innerDiv);
	this.image.appendChild(this.closeDiv);
	
	this.innerDiv.onmousedown = this.onMouseDown;
	this.closeDiv.onmousedown = this.onMouseDown;
	
	this.div = document.createElement("div");
	this.div.style.position = "absolute";
	this.div.style.top = "0px";
	this.div.style.left = "0px";
	this.div.style.zIndex = 0;
	this.div.owner = this;;
	this.div.setPosition = function(pt){
		this.owner.setPosition(pt);
	}
	this.div.appendChild(this.image);
	this.div.appendChild(this.shadow);
	this.setInfo(pt, html, offset, addr);
	
	this.innerSize = sz;
	
	this.LT = new Size(parseInt(this.image.childNodes[3].style.left), parseInt(this.image.childNodes[0].style.top));
	this.arrowL = parseInt(parseInt(this.image.childNodes[8].style.width)/2);
	this.routerPos = new Size(12 - this.innerSize.width/2, 183 - 305);
	this.color = 'white';
}

InfoWindow.prototype.createImageDiv = function(width, height){
	this.image = document.createElement('DIV');
	this.image.style.cssText = 'position:absolute;';
	this.image.owner = this;
	this.image.setPosition = function(pt){
		this.owner.setImagePosition(pt);
	}
	var w_2 = Math.floor(width/2);
	var _w_2 = Math.floor(-width/2);
	this.image.appendChild(ElementCreator.createImage(_w_2 + 6, -114 - height, redirected_path('map/images/InfoWin/N.png'), width, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(w_2 + 6, -114 - height, redirected_path('map/images/InfoWin/NE.png'), 10, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(_w_2 - 4, -114 - height, redirected_path('map/images/InfoWin/NW.png'), 10, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(w_2 + 6, -104 - height, redirected_path('map/images/InfoWin/E.png'), 10, height, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(_w_2 - 4, -104 - height, redirected_path('map/images/InfoWin/W.png'), 10, height, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(w_2 + 6, -104, redirected_path('map/images/InfoWin/SE.png'), 10, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(_w_2 - 4, -104, redirected_path('map/images/InfoWin/SW.png'), 10, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(_w_2 + 6, -104, redirected_path('map/images/InfoWin/S.png'), width, 10, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(0, -96, redirected_path('map/images/InfoWin/Arrow.png'), 26, 96, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(_w_2 + 6, -104 - height, redirected_path('map/images/InfoWin/inner.gif'), width, height, 0, true,true));
		
	var infoWindowSize = new Size(0,0);	
	for (var i = 0; i<8; i++){
	    infoWindowSize.width += parseInt(this.image.childNodes[i].style.width);
	    infoWindowSize.height += parseInt(this.image.childNodes[i].style.height);
	}
	infoWindowSize.width /=2; //10px vise
	infoWindowSize.height /=2;	infoWindowSize.height += parseInt(this.image.childNodes[8].style.height); // 10px vise
	this.IWinSize = infoWindowSize;
}


InfoWindow.prototype.createShadowDiv = function(width, height){
	var shadow_height = Math.floor(-InfoWindow.sin*(height/2)) + 20;
	var shadow_width = Math.floor(width+InfoWindow.cos*(height/2)) + 20;
	
	this.shadow = document.createElement('DIV');
	this.shadow.style.cssText = 'position:absolute;z-index:-1';
	this.shadow.owner = this;
	this.shadow.setPosition = function(pt){
		this.owner.setShadowPosition(pt);
	}
	
	this.shadow.appendChild(ElementCreator.createImage(Math.floor(-144 * shadow_width/370) + 45, -48 - shadow_height, redirected_path('map/images/InfoWin/shBack.png'), shadow_width, shadow_height, 1, true, true));
	this.shadow.appendChild(ElementCreator.createImage(0, -48 - shadow_height, redirected_path('map/images/InfoWin/shMask.png'), 64, shadow_height, 1, true, true));
	this.shadow.appendChild(ElementCreator.createImage(0, -48, redirected_path('map/images/InfoWin/shArrow.png'), 64, 48, 1, true, true));
}

InfoWindow.sin = Math.sin(-Math.PI/4);
InfoWindow.cos = Math.cos(-Math.PI/4);
InfoWindow.fact = 2;

InfoWindow.prototype.setImagePosition = function(){
	if (this.offset < 0){
		this.top += this.offset;
	}
	this.image.style.top = this.offset + 'px';
}

InfoWindow.prototype.setShadowPosition = function(){
	this.shadow.style.left = this.offsetx + 'px';
	this.shadow.style.top = this.offsety + 'px';
}

InfoWindow.prototype.setPosition = function(pt){
	var x = pt.x;
	var y = pt.y;
	this.left = Math.round(x - this.innerSize.width/2 - 10);
	this.top = y - this.innerSize.height - 136;
	this.right = this.left + this.innerSize.width + 30;
	this.bottom = y;
	if (this.offset < 0){
		this.top += this.offset;
	}
	this.div.style.left = x+'px';
	this.div.style.top = y+'px';
	this.setImagePosition();
	this.setShadowPosition();
}

InfoWindow.prototype.appendChild = function(obj){
	this.image.appendChild(obj);
}

InfoWindow.prototype.removeChild = function(obj){
	this.image.removeChild(obj);
}


InfoWindow.prototype.setInfo = function(wp, html, offset, addr){
	this.position = wp;
	this.addr = addr;
	this.innerDiv.innerHTML = html;
	if (offset != null){
		this.offset = -offset;
		this.offsetx = Math.round(-offset*InfoWindow.sin/InfoWindow.fact);
		this.offsety = Math.round(-offset*InfoWindow.cos/InfoWindow.fact);
	}
}

InfoWindow.prototype.onMouseDown=function(b){
	if(b){
		b.cancelDrag=true
	}else{
		cancelBubbling(b)
	}
}

InfoWindow.prototype.isClicked = function(){
	return false;
}


// JScript File
//  LT infowindow

function InfoWindowTransparent(map, pt, html, offset, addr){
   
	this.map = map;
	
	this.position = new Point(0, 0);
	
	this.offset = 0;
	this.offsetx = 0;
	this.offsety = 0;
	
	var sz = measureHTML(html);
	if (sz.width < 200) sz.width = 200;
	sz.width += 10; sz.height += 10;
	this.createImageDiv(sz.width + 15, sz.height + 30);
	
	this.createShadowDiv(sz.width + 15, sz.height + 30);
	
    this.headerDiv = ElementCreator.createChildDiv(this.Needle.width, 5, sz.width-25, this.Needle.height-5, null, "Arial", '12px');
    this.headerDiv.style.cssText += '; z-index:1;text-align:center;color:white;cursor:text;font-weight:normal';
    this.headerDiv.innerHTML = "Street";     
    
	this.htmlDiv = ElementCreator.createChildDiv(this.Needle.width + 10, this.Needle.height+10, sz.width, sz.height, null, "Arial", '12px');
	this.htmlDiv.style.cssText += '; z-index:1;text-align:left;color:black;cursor:text; font-weight:bold';
	
	this.closeDiv = ElementCreator.createChildDiv(this.IWinSize.width-60, this.IWinSize.height-25, 50, 15, null, "Arial", '12px');
	this.closeDiv.style.cssText += '; z-index:1;text-align:right;text-decoration:underline;color:blue;cursor:pointer;';	
	this.closeDiv.onclick = function(){map.closeInfoWindow();};
	this.closeDiv.innerHTML = "Close"; 
		
	this.image.appendChild(this.closeDiv);
	this.image.appendChild(this.htmlDiv);
	
	this.closeDiv.onmousedown = this.onMouseDown;
	this.htmlDiv.onmousedown = this.onMouseDown; 
	
	this.image.appendChild(this.headerDiv);
	this.headerDiv.onmousedown = this.onMouseDown;
	
	this.div = document.createElement("div");
	this.div.style.position = "absolute";
	this.div.style.top = "0px";
	this.div.style.left = "0px";
	this.div.style.zIndex = 0;
	this.div.owner = this;;
	this.div.setPosition = function(pt){
		this.owner.setPosition(pt);
	}
	this.div.appendChild(this.image);	
	this.div.appendChild(this.shadow);
	
	this.setInfo(pt, html, offset, addr);
	
	this.innerSize = sz;
	
	this.LT = new Size(parseInt(this.shadow.childNodes[0].style.left) + parseInt(this.shadow.childNodes[0].style.width), parseInt(this.image.childNodes[1].style.top));
	this.arrowL = 0;
	this.routerPos = new Size(this.Needle.width + 10, this.IWinSize.height - 25);
	this.color = null;
}

InfoWindowTransparent.prototype.createImageDiv = function(width, height){
	this.image = document.createElement('DIV');
	this.image.style.cssText = 'position:absolute;';
	this.image.owner = this;
	this.image.setPosition = function(pt){
		this.owner.setImagePosition(pt);
	}
	var w1 = 25;
	var h1 = 20;
	this.Needle = new Size(w1, h1);
	this.image.appendChild(ElementCreator.createImage(0, 0, redirected_path('map/images/InfoWinTransparent/needleTL.png'), w1, h1, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(w1, 0, redirected_path('map/images/InfoWinTransparent/header_gray.png'), width, h1, 0, true,true));
	this.image.appendChild(ElementCreator.createImage(w1, h1, redirected_path('map/images/InfoWinTransparent/background_white.png'), width, height, 0,true,true));
	
	var infoWindowSize = new Size(0,0);
	infoWindowSize.width = w1 + width;
	infoWindowSize.height = h1 + height;	
	this.IWinSize = infoWindowSize;
	
}


InfoWindowTransparent.prototype.createShadowDiv = function(width, height){ 
    var shadow_height = this.IWinSize.height;
    var shadow_width = this.IWinSize.width;
    this.shadow = document.createElement('DIV');
	this.shadow.style.cssText = 'position:absolute;z-index:-1';
	this.shadow.owner = this;
	this.shadow.setPosition = function(pt){
		this.owner.setShadowPosition(pt);
	}
	
	this.shadow.appendChild(ElementCreator.createImage(this.IWinSize.width, 2, redirected_path('map/images/InfoWinTransparent/shadowR.png'), 5, this.IWinSize.height-2, 1, true, true));
	this.shadow.appendChild(ElementCreator.createImage(this.Needle.width + 2, this.IWinSize.height, redirected_path('map/images/InfoWinTransparent/shadowB.png'), this.IWinSize.width - this.Needle.width - 2 , 5, 1, true, true));
	this.shadow.appendChild(ElementCreator.createImage(this.IWinSize.width, this.IWinSize.height, redirected_path('map/images/InfoWinTransparent/shadowBR.png'), 5, 5, 1, true, true));
    
}

InfoWindowTransparent.prototype.setImagePosition = function(){
	if (this.offset < 0){
		this.top += this.offset;
	}
	this.image.style.top = this.offset + 'px';
}

InfoWindowTransparent.prototype.setShadowPosition = function(){
	this.shadow.style.left = this.offsetx + 'px';
	this.shadow.style.top = this.offsety + 'px';
}

InfoWindowTransparent.prototype.setPosition = function(pt){
	var x = pt.x;
	var y = pt.y;
	this.left = x + 2;
	this.top = y - 2;
	this.right = x + this.IWinSize.width + 5; 
	this.bottom = y + this.IWinSize.height + 5;
	if (this.offset < 0){
		this.top += this.offset;
	}
	this.div.style.left = x + 'px';
	this.div.style.top = y + 'px';
	this.setImagePosition();
	this.setShadowPosition();
}

InfoWindowTransparent.prototype.appendChild = function(obj){
	this.image.appendChild(obj);
}

InfoWindowTransparent.prototype.removeChild = function(obj){
	this.image.removeChild(obj);
}


InfoWindowTransparent.prototype.setInfo = function(wp, html, offset, addr){
	this.position = wp;
	this.addr = addr;
	var htmlSize = measureHTML(html);
    this.htmlDiv.innerHTML = html;
	if (offset != null){
		this.offset = -offset;
		this.offsetx = 0; 
		this.offsety = this.offset; 
	}
}

InfoWindowTransparent.prototype.onMouseDown=function(b){
	if(b){
		b.cancelDrag=true
	}else{
		cancelBubbling(b)
	}
}

InfoWindowTransparent.prototype.isClicked = function(){
	return false;
}



// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


// Known Issues:
//
// * Patterns are not implemented.
// * Radial gradient are not implemented. The VML version of these look very
//   different from the canvas one.
// * Clipping paths are not implemented.
// * Coordsize. The width and height attribute have higher priority than the
//   width and height style values which isn't correct.
// * Painting mode isn't implemented.
// * Canvas width/height should is using content-box by default. IE in
//   Quirks mode will draw the canvas using border-box. Either change your
//   doctype to HTML5
//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
//   or use Box Sizing Behavior from WebFX
//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
// * Non uniform scaling does not correctly scale strokes.
// * Optimize. There is always room for speed improvements.

// Only add this code if we do not already have a canvas implementation
if (!document.createElement('canvas').getContext) {

(function() {

  // alias some functions to make (compiled) code shorter
  var m = Math;
  var mr = m.round;
  var ms = m.sin;
  var mc = m.cos;
  var abs = m.abs;
  var sqrt = m.sqrt;

  // this is used for sub pixel precision
  var Z = 10;
  var Z2 = Z / 2;

  /**
   * This funtion is assigned to the <canvas> elements as element.getContext().
   * @this {HTMLElement}
   * @return {CanvasRenderingContext2D_}
   */
  function getContext() {
    return this.context_ ||
        (this.context_ = new CanvasRenderingContext2D_(this));
  }

  var slice = Array.prototype.slice;

  /**
   * Binds a function to an object. The returned function will always use the
   * passed in {@code obj} as {@code this}.
   *
   * Example:
   *
   *   g = bind(f, obj, a, b)
   *   g(c, d) // will do f.call(obj, a, b, c, d)
   *
   * @param {Function} f The function to bind the object to
   * @param {Object} obj The object that should act as this when the function
   *     is called
   * @param {*} var_args Rest arguments that will be used as the initial
   *     arguments when the function is called
   * @return {Function} A new function that has bound this
   */
  function bind(f, obj, var_args) {
    var a = slice.call(arguments, 2);
    return function() {
      return f.apply(obj, a.concat(slice.call(arguments)));
    };
  }

  var G_vmlCanvasManager_ = {
    init: function(opt_doc) {
      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
        var doc = opt_doc || document;
        // Create a dummy element so that IE will allow canvas elements to be
        // recognized.
        doc.createElement('canvas');
        doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
      }
    },

    init_: function(doc) {
      // create xmlns
      if (!doc.namespaces['g_vml_']) {
        doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
                           '#default#VML');

      }
      if (!doc.namespaces['g_o_']) {
        doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
                           '#default#VML');
      }

      // Setup default CSS.  Only add one style sheet per document
      if (!doc.styleSheets['ex_canvas_']) {
        var ss = doc.createStyleSheet();
        ss.owningElement.id = 'ex_canvas_';
        ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
            // default size is 300x150 in Gecko and Opera
            'text-align:left;width:300px;height:150px}' +
            'g_vml_\\:*{behavior:url(#default#VML)}' +
            'g_o_\\:*{behavior:url(#default#VML)}';

      }

      // find all canvas elements
      var els = doc.getElementsByTagName('canvas');
      for (var i = 0; i < els.length; i++) {
        this.initElement(els[i]);
      }
    },

    /**
     * Public initializes a canvas element so that it can be used as canvas
     * element from now on. This is called automatically before the page is
     * loaded but if you are creating elements using createElement you need to
     * make sure this is called on the element.
     * @param {HTMLElement} el The canvas element to initialize.
     * @return {HTMLElement} the element that was created.
     */
    initElement: function(el) {
      if (!el.getContext) {

        el.getContext = getContext;

        // Remove fallback content. There is no way to hide text nodes so we
        // just remove all childNodes. We could hide all elements and remove
        // text nodes but who really cares about the fallback content.
        el.innerHTML = '';

        // do not use inline function because that will leak memory
        el.attachEvent('onpropertychange', onPropertyChange);
        el.attachEvent('onresize', onResize);

        var attrs = el.attributes;
        if (attrs.width && attrs.width.specified) {
          // TODO: use runtimeStyle and coordsize
          // el.getContext().setWidth_(attrs.width.nodeValue);
          el.style.width = attrs.width.nodeValue + 'px';
        } else {
          el.width = el.clientWidth;
        }
        if (attrs.height && attrs.height.specified) {
          // TODO: use runtimeStyle and coordsize
          // el.getContext().setHeight_(attrs.height.nodeValue);
          el.style.height = attrs.height.nodeValue + 'px';
        } else {
          el.height = el.clientHeight;
        }
        //el.getContext().setCoordsize_()
      }
      return el;
    }
  };

  function onPropertyChange(e) {
    var el = e.srcElement;

    switch (e.propertyName) {
      case 'width':
        el.style.width = el.attributes.width.nodeValue + 'px';
        el.getContext().clearRect();
        break;
      case 'height':
        el.style.height = el.attributes.height.nodeValue + 'px';
        el.getContext().clearRect();
        break;
    }
  }

  function onResize(e) {
    var el = e.srcElement;
    if (el.firstChild) {
      el.firstChild.style.width =  el.clientWidth + 'px';
      el.firstChild.style.height = el.clientHeight + 'px';
    }
  }

  G_vmlCanvasManager_.init();

  // precompute "00" to "FF"
  var dec2hex = [];
  for (var i = 0; i < 16; i++) {
    for (var j = 0; j < 16; j++) {
      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
    }
  }

  function createMatrixIdentity() {
    return [
      [1, 0, 0],
      [0, 1, 0],
      [0, 0, 1]
    ];
  }

  function matrixMultiply(m1, m2) {
    var result = createMatrixIdentity();

    for (var x = 0; x < 3; x++) {
      for (var y = 0; y < 3; y++) {
        var sum = 0;

        for (var z = 0; z < 3; z++) {
          sum += m1[x][z] * m2[z][y];
        }

        result[x][y] = sum;
      }
    }
    return result;
  }

  function copyState(o1, o2) {
    o2.fillStyle     = o1.fillStyle;
    o2.lineCap       = o1.lineCap;
    o2.lineJoin      = o1.lineJoin;
    o2.lineWidth     = o1.lineWidth;
    o2.miterLimit    = o1.miterLimit;
    o2.shadowBlur    = o1.shadowBlur;
    o2.shadowColor   = o1.shadowColor;
    o2.shadowOffsetX = o1.shadowOffsetX;
    o2.shadowOffsetY = o1.shadowOffsetY;
    o2.strokeStyle   = o1.strokeStyle;
    o2.globalAlpha   = o1.globalAlpha;
    o2.arcScaleX_    = o1.arcScaleX_;
    o2.arcScaleY_    = o1.arcScaleY_;
    o2.lineScale_    = o1.lineScale_;
  }

  function processStyle(styleString) {
    var str, alpha = 1;

    styleString = String(styleString);
    if (styleString.substring(0, 3) == 'rgb') {
      var start = styleString.indexOf('(', 3);
      var end = styleString.indexOf(')', start + 1);
      var guts = styleString.substring(start + 1, end).split(',');

      str = '#';
      for (var i = 0; i < 3; i++) {
        str += dec2hex[Number(guts[i])];
      }

      if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
        alpha = guts[3];
      }
    } else {
      str = styleString;
    }

    return {color: str, alpha: alpha};
  }

  function processLineCap(lineCap) {
    switch (lineCap) {
      case 'butt':
        return 'flat';
      case 'round':
        return 'round';
      case 'square':
      default:
        return 'square';
    }
  }

  /**
   * This class implements CanvasRenderingContext2D interface as described by
   * the WHATWG.
   * @param {HTMLElement} surfaceElement The element that the 2D context should
   * be associated with
   */
  function CanvasRenderingContext2D_(surfaceElement) {
    this.m_ = createMatrixIdentity();

    this.mStack_ = [];
    this.aStack_ = [];
    this.currentPath_ = [];

    // Canvas context properties
    this.strokeStyle = '#000';
    this.fillStyle = '#000';

    this.lineWidth = 1;
    this.lineJoin = 'miter';
    this.lineCap = 'butt';
    this.miterLimit = Z * 1;
    this.globalAlpha = 1;
    this.canvas = surfaceElement;

    var el = surfaceElement.ownerDocument.createElement('div');
    el.style.width =  surfaceElement.clientWidth + 'px';
    el.style.height = surfaceElement.clientHeight + 'px';
    el.style.overflow = 'hidden';
    el.style.position = 'absolute';
    surfaceElement.appendChild(el);

    this.element_ = el;
    this.arcScaleX_ = 1;
    this.arcScaleY_ = 1;
    this.lineScale_ = 1;
  }

  var contextPrototype = CanvasRenderingContext2D_.prototype;
  contextPrototype.clearRect = function() {
    this.element_.innerHTML = '';
  };

  contextPrototype.beginPath = function() {
    // TODO: Branch current matrix so that save/restore has no effect
    //       as per safari docs.
    this.currentPath_ = [];
  };

  contextPrototype.moveTo = function(aX, aY) {
    var p = this.getCoords_(aX, aY);
    this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
    this.currentX_ = p.x;
    this.currentY_ = p.y;
  };

  contextPrototype.lineTo = function(aX, aY) {
    var p = this.getCoords_(aX, aY);
    this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});

    this.currentX_ = p.x;
    this.currentY_ = p.y;
  };

  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
                                            aCP2x, aCP2y,
                                            aX, aY) {
    var p = this.getCoords_(aX, aY);
    var cp1 = this.getCoords_(aCP1x, aCP1y);
    var cp2 = this.getCoords_(aCP2x, aCP2y);
    bezierCurveTo(this, cp1, cp2, p);
  };

  // Helper function that takes the already fixed cordinates.
  function bezierCurveTo(self, cp1, cp2, p) {
    self.currentPath_.push({
      type: 'bezierCurveTo',
      cp1x: cp1.x,
      cp1y: cp1.y,
      cp2x: cp2.x,
      cp2y: cp2.y,
      x: p.x,
      y: p.y
    });
    self.currentX_ = p.x;
    self.currentY_ = p.y;
  }

  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
    // the following is lifted almost directly from
    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes

    var cp = this.getCoords_(aCPx, aCPy);
    var p = this.getCoords_(aX, aY);

    var cp1 = {
      x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
      y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
    };
    var cp2 = {
      x: cp1.x + (p.x - this.currentX_) / 3.0,
      y: cp1.y + (p.y - this.currentY_) / 3.0
    };

    bezierCurveTo(this, cp1, cp2, p);
  };

  contextPrototype.arc = function(aX, aY, aRadius,
                                  aStartAngle, aEndAngle, aClockwise) {
    aRadius *= Z;
    var arcType = aClockwise ? 'at' : 'wa';

    var xStart = aX + mc(aStartAngle) * aRadius - Z2;
    var yStart = aY + ms(aStartAngle) * aRadius - Z2;

    var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
    var yEnd = aY + ms(aEndAngle) * aRadius - Z2;

    // IE won't render arches drawn counter clockwise if xStart == xEnd.
    if (xStart == xEnd && !aClockwise) {
      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
                       // that can be represented in binary
    }

    var p = this.getCoords_(aX, aY);
    var pStart = this.getCoords_(xStart, yStart);
    var pEnd = this.getCoords_(xEnd, yEnd);

    this.currentPath_.push({type: arcType,
                           x: p.x,
                           y: p.y,
                           radius: aRadius,
                           xStart: pStart.x,
                           yStart: pStart.y,
                           xEnd: pEnd.x,
                           yEnd: pEnd.y});

  };

  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
  };

  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
    var oldPath = this.currentPath_;
    this.beginPath();

    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
    this.stroke();

    this.currentPath_ = oldPath;
  };

  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
    var oldPath = this.currentPath_;
    this.beginPath();

    this.moveTo(aX, aY);
    this.lineTo(aX + aWidth, aY);
    this.lineTo(aX + aWidth, aY + aHeight);
    this.lineTo(aX, aY + aHeight);
    this.closePath();
    this.fill();

    this.currentPath_ = oldPath;
  };

  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
    var gradient = new CanvasGradient_('gradient');
    gradient.x0_ = aX0;
    gradient.y0_ = aY0;
    gradient.x1_ = aX1;
    gradient.y1_ = aY1;
    return gradient;
  };

  contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
                                                   aX1, aY1, aR1) {
    var gradient = new CanvasGradient_('gradientradial');
    gradient.x0_ = aX0;
    gradient.y0_ = aY0;
    gradient.r0_ = aR0;
    gradient.x1_ = aX1;
    gradient.y1_ = aY1;
    gradient.r1_ = aR1;
    return gradient;
  };

  contextPrototype.drawImage = function(image, var_args) {
    var dx, dy, dw, dh, sx, sy, sw, sh;

    // to find the original width we overide the width and height
    var oldRuntimeWidth = image.runtimeStyle.width;
    var oldRuntimeHeight = image.runtimeStyle.height;
    image.runtimeStyle.width = 'auto';
    image.runtimeStyle.height = 'auto';

    // get the original size
    var w = image.width;
    var h = image.height;

    // and remove overides
    image.runtimeStyle.width = oldRuntimeWidth;
    image.runtimeStyle.height = oldRuntimeHeight;

    if (arguments.length == 3) {
      dx = arguments[1];
      dy = arguments[2];
      sx = sy = 0;
      sw = dw = w;
      sh = dh = h;
    } else if (arguments.length == 5) {
      dx = arguments[1];
      dy = arguments[2];
      dw = arguments[3];
      dh = arguments[4];
      sx = sy = 0;
      sw = w;
      sh = h;
    } else if (arguments.length == 9) {
      sx = arguments[1];
      sy = arguments[2];
      sw = arguments[3];
      sh = arguments[4];
      dx = arguments[5];
      dy = arguments[6];
      dw = arguments[7];
      dh = arguments[8];
    } else {
      throw Error('Invalid number of arguments');
    }

    var d = this.getCoords_(dx, dy);

    var w2 = sw / 2;
    var h2 = sh / 2;

    var vmlStr = [];

    var W = 10;
    var H = 10;

    // For some reason that I've now forgotten, using divs didn't work
    vmlStr.push(' <g_vml_:group',
                ' coordsize="', Z * W, ',', Z * H, '"',
                ' coordorigin="0,0"' ,
                ' style="width:', W, 'px;height:', H, 'px;position:absolute;');

    // If filters are necessary (rotation exists), create them
    // filters are bog-slow, so only create them if abbsolutely necessary
    // The following check doesn't account for skews (which don't exist
    // in the canvas spec (yet) anyway.

    if (this.m_[0][0] != 1 || this.m_[0][1]) {
      var filter = [];

      // Note the 12/21 reversal
      filter.push('M11=', this.m_[0][0], ',',
                  'M12=', this.m_[1][0], ',',
                  'M21=', this.m_[0][1], ',',
                  'M22=', this.m_[1][1], ',',
                  'Dx=', mr(d.x / Z), ',',
                  'Dy=', mr(d.y / Z), '');

      // Bounding box calculation (need to minimize displayed area so that
      // filters don't waste time on unused pixels.
      var max = d;
      var c2 = this.getCoords_(dx + dw, dy);
      var c3 = this.getCoords_(dx, dy + dh);
      var c4 = this.getCoords_(dx + dw, dy + dh);

      max.x = m.max(max.x, c2.x, c3.x, c4.x);
      max.y = m.max(max.y, c2.y, c3.y, c4.y);

      vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
                  'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
                  filter.join(''), ", sizingmethod='clip');")
    } else {
      vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
    }

    vmlStr.push(' ">' ,
                '<g_vml_:image src="', image.src, '"',
                ' style="width:', Z * dw, 'px;',
                ' height:', Z * dh, 'px;"',
                ' cropleft="', sx / w, '"',
                ' croptop="', sy / h, '"',
                ' cropright="', (w - sx - sw) / w, '"',
                ' cropbottom="', (h - sy - sh) / h, '"',
                ' />',
                '</g_vml_:group>');

    this.element_.insertAdjacentHTML('BeforeEnd',
                                    vmlStr.join(''));
  };

  contextPrototype.stroke = function(aFill) {
    var lineStr = [];
    var lineOpen = false;
    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
    var color = a.color;
    var opacity = a.alpha * this.globalAlpha;

    var W = 10;
    var H = 10;

    lineStr.push('<g_vml_:shape',
                 ' filled="', !!aFill, '"',
                 ' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
                 ' stroked="', !aFill, '"',
                 ' path="');

    var newSeq = false;
    var min = {x: null, y: null};
    var max = {x: null, y: null};

    for (var i = 0; i < this.currentPath_.length; i++) {
      var p = this.currentPath_[i];
      var c;

      switch (p.type) {
        case 'moveTo':
          c = p;
          lineStr.push(' m ', mr(p.x), ',', mr(p.y));
          break;
        case 'lineTo':
          lineStr.push(' l ', mr(p.x), ',', mr(p.y));
          break;
        case 'close':
          lineStr.push(' x ');
          p = null;
          break;
        case 'bezierCurveTo':
          lineStr.push(' c ',
                       mr(p.cp1x), ',', mr(p.cp1y), ',',
                       mr(p.cp2x), ',', mr(p.cp2y), ',',
                       mr(p.x), ',', mr(p.y));
          break;
        case 'at':
        case 'wa':
          lineStr.push(' ', p.type, ' ',
                       mr(p.x - this.arcScaleX_ * p.radius), ',',
                       mr(p.y - this.arcScaleY_ * p.radius), ' ',
                       mr(p.x + this.arcScaleX_ * p.radius), ',',
                       mr(p.y + this.arcScaleY_ * p.radius), ' ',
                       mr(p.xStart), ',', mr(p.yStart), ' ',
                       mr(p.xEnd), ',', mr(p.yEnd));
          break;
      }


      // TODO: Following is broken for curves due to
      //       move to proper paths.

      // Figure out dimensions so we can do gradient fills
      // properly
      if (p) {
        if (min.x == null || p.x < min.x) {
          min.x = p.x;
        }
        if (max.x == null || p.x > max.x) {
          max.x = p.x;
        }
        if (min.y == null || p.y < min.y) {
          min.y = p.y;
        }
        if (max.y == null || p.y > max.y) {
          max.y = p.y;
        }
      }
    }
    lineStr.push(' ">');

    if (!aFill) {
      var lineWidth = this.lineScale_ * this.lineWidth;

      // VML cannot correctly render a line if the width is less than 1px.
      // In that case, we dilute the color to make the line look thinner.
      if (lineWidth < 1) {
        opacity *= lineWidth;
      }

      lineStr.push(
        '<g_vml_:stroke',
        ' opacity="', opacity, '"',
        ' joinstyle="', this.lineJoin, '"',
        ' miterlimit="', this.miterLimit, '"',
        ' endcap="', processLineCap(this.lineCap), '"',
        ' weight="', lineWidth, 'px"',
        ' color="', color, '" />'
      );
    } else if (typeof this.fillStyle == 'object') {
      var fillStyle = this.fillStyle;
      var angle = 0;
      var focus = {x: 0, y: 0};

      // additional offset
      var shift = 0;
      // scale factor for offset
      var expansion = 1;

      if (fillStyle.type_ == 'gradient') {
        var x0 = fillStyle.x0_ / this.arcScaleX_;
        var y0 = fillStyle.y0_ / this.arcScaleY_;
        var x1 = fillStyle.x1_ / this.arcScaleX_;
        var y1 = fillStyle.y1_ / this.arcScaleY_;
        var p0 = this.getCoords_(x0, y0);
        var p1 = this.getCoords_(x1, y1);
        var dx = p1.x - p0.x;
        var dy = p1.y - p0.y;
        angle = Math.atan2(dx, dy) * 180 / Math.PI;

        // The angle should be a non-negative number.
        if (angle < 0) {
          angle += 360;
        }

        // Very small angles produce an unexpected result because they are
        // converted to a scientific notation string.
        if (angle < 1e-6) {
          angle = 0;
        }
      } else {
        var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
        var width  = max.x - min.x;
        var height = max.y - min.y;
        focus = {
          x: (p0.x - min.x) / width,
          y: (p0.y - min.y) / height
        };

        width  /= this.arcScaleX_ * Z;
        height /= this.arcScaleY_ * Z;
        var dimension = m.max(width, height);
        shift = 2 * fillStyle.r0_ / dimension;
        expansion = 2 * fillStyle.r1_ / dimension - shift;
      }

      // We need to sort the color stops in ascending order by offset,
      // otherwise IE won't interpret it correctly.
      var stops = fillStyle.colors_;
      stops.sort(function(cs1, cs2) {
        return cs1.offset - cs2.offset;
      });

      var length = stops.length;
      var color1 = stops[0].color;
      var color2 = stops[length - 1].color;
      var opacity1 = stops[0].alpha * this.globalAlpha;
      var opacity2 = stops[length - 1].alpha * this.globalAlpha;

      var colors = [];
      for (var i = 0; i < length; i++) {
        var stop = stops[i];
        colors.push(stop.offset * expansion + shift + ' ' + stop.color);
      }

      // When colors attribute is used, the meanings of opacity and o:opacity2
      // are reversed.
      lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
                   ' method="none" focus="100%"',
                   ' color="', color1, '"',
                   ' color2="', color2, '"',
                   ' colors="', colors.join(','), '"',
                   ' opacity="', opacity2, '"',
                   ' g_o_:opacity2="', opacity1, '"',
                   ' angle="', angle, '"',
                   ' focusposition="', focus.x, ',', focus.y, '" />');
    } else {
      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
                   '" />');
    }

    lineStr.push('</g_vml_:shape>');

    this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
  };

  contextPrototype.fill = function() {
    this.stroke(true);
  }

  contextPrototype.closePath = function() {
    this.currentPath_.push({type: 'close'});
  };

  /**
   * @private
   */
  contextPrototype.getCoords_ = function(aX, aY) {
    var m = this.m_;
    return {
      x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
      y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
    }
  };

  contextPrototype.save = function() {
    var o = {};
    copyState(this, o);
    this.aStack_.push(o);
    this.mStack_.push(this.m_);
    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
  };

  contextPrototype.restore = function() {
    copyState(this.aStack_.pop(), this);
    this.m_ = this.mStack_.pop();
  };

  function matrixIsFinite(m) {
    for (var j = 0; j < 3; j++) {
      for (var k = 0; k < 2; k++) {
        if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
          return false;
        }
      }
    }
    return true;
  }

  function setM(ctx, m, updateLineScale) {
    if (!matrixIsFinite(m)) {
      return;
    }
    ctx.m_ = m;

    if (updateLineScale) {
      // Get the line scale.
      // Determinant of this.m_ means how much the area is enlarged by the
      // transformation. So its square root can be used as a scale factor
      // for width.
      var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
      ctx.lineScale_ = sqrt(abs(det));
    }
  }

  contextPrototype.translate = function(aX, aY) {
    var m1 = [
      [1,  0,  0],
      [0,  1,  0],
      [aX, aY, 1]
    ];

    setM(this, matrixMultiply(m1, this.m_), false);
  };

  contextPrototype.rotate = function(aRot) {
    var c = mc(aRot);
    var s = ms(aRot);

    var m1 = [
      [c,  s, 0],
      [-s, c, 0],
      [0,  0, 1]
    ];

    setM(this, matrixMultiply(m1, this.m_), false);
  };

  contextPrototype.scale = function(aX, aY) {
    this.arcScaleX_ *= aX;
    this.arcScaleY_ *= aY;
    var m1 = [
      [aX, 0,  0],
      [0,  aY, 0],
      [0,  0,  1]
    ];

    setM(this, matrixMultiply(m1, this.m_), true);
  };

  contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
    var m1 = [
      [m11, m12, 0],
      [m21, m22, 0],
      [dx,  dy,  1]
    ];

    setM(this, matrixMultiply(m1, this.m_), true);
  };

  contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
    var m = [
      [m11, m12, 0],
      [m21, m22, 0],
      [dx,  dy,  1]
    ];

    setM(this, m, true);
  };

  /******** STUBS ********/
  contextPrototype.clip = function() {
    // TODO: Implement
  };

  contextPrototype.arcTo = function() {
    // TODO: Implement
  };

  contextPrototype.createPattern = function() {
    return new CanvasPattern_;
  };

  // Gradient / Pattern Stubs
  function CanvasGradient_(aType) {
    this.type_ = aType;
    this.x0_ = 0;
    this.y0_ = 0;
    this.r0_ = 0;
    this.x1_ = 0;
    this.y1_ = 0;
    this.r1_ = 0;
    this.colors_ = [];
  }

  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
    aColor = processStyle(aColor);
    this.colors_.push({offset: aOffset,
                       color: aColor.color,
                       alpha: aColor.alpha});
  };

  function CanvasPattern_() {}

  // set up externs
  G_vmlCanvasManager = G_vmlCanvasManager_;
  CanvasRenderingContext2D = CanvasRenderingContext2D_;
  CanvasGradient = CanvasGradient_;
  CanvasPattern = CanvasPattern_;

})();

} // if
function ClientMapDrawer(_redrawCallBack) {
	this.ctx = null;
	this.map = null;
	this.canvas = ElementCreator.createCanvas(0, 0);
	this.elems = {};
	this.lockRd = false;
	this.redrawCallBack = _redrawCallBack;
}

//PUBLIC
ClientMapDrawer.prototype.SetMap = function(map) {
	this.map = map;
	this.canvas.owner = this;
	this.stick2map(this.map);
	this.canvas.redraw = function() {
		this.owner.redraw();
	};
}

//PUBLIC
ClientMapDrawer.prototype.Set2dContext = function() {
    if (!this.canvas.getContext) G_vmlCanvasManager.initElement(this.canvas);
    this.ctx = this.canvas.getContext('2d');
}

//PUBLIC
ClientMapDrawer.prototype.Draw = function(ctxw, zoomTo) {
    if (zoomTo && ctxw.GetCenterAndZoom) {
        var caz = ctxw.GetCenterAndZoom(this.map),
            nwc = caz.wcenter,
            nzi = caz.zindex,
            owc = this.map.wcenter,
            ozi = this.map.getZoomLevel(); 
        if (nwc.x != owc.x || nwc.y != owc.y || nzi != ozi) { 
            this.lockRd = true;
            this.map.setCenterAndZoom(caz.center, nzi);
        }
    }
    var elem = this.elems[ctxw.id];
    if (elem && elem.Delete) {
        elem.Delete(this.map);
    } 
    this.elems[ctxw.id] = ctxw;
    this.RedrawAll();
    if (this.lockRd) {
        this.lockRd = false;
    }    
}

//PUBLIC
ClientMapDrawer.prototype.Delete = function(id) {
    var elem;
    if (elem = this.elems[id]) {
        if (elem.Delete) {
            elem.Delete(this.map);
        }
        delete this.elems[id];
        this.RedrawAll();
    }
}

ClientMapDrawer.prototype.stick2map = function(map) {
    var size = map.patchPixSize();
	this.canvas.width = size.width;
	this.canvas.height = size.height;
}

ClientMapDrawer.prototype.redraw = function() {
    if (this.lockRd) return;
    this.RedrawAll();
}

ClientMapDrawer.prototype.clearCanvas = function() {
	if (!this.ctx) return;
	this.elems = {};
	this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}

ClientMapDrawer.prototype.RedrawAll = function() {
	if (!this.ctx) return;
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
	this.stick2map(this.map);
    for (var id in this.elems) {
        this.elems[id].Draw(this.ctx, this.map);
    }
    if (this.redrawCallBack) {
        this.redrawCallBack();
    }
}
function Utils() {
    this.RMF_MERIDIAN_BIN = 134217728;
    this.RMF_MERIDIAN_LAT = 180;
    this.M_PI_2 = Math.PI / 2;
    this.M_PI_4 = Math.PI / 4;
    this.INVALID_COURSE = -32767;
    
    this.deg2rad = Math.PI / 180;
    this.deg2int = this.RMF_MERIDIAN_BIN / this.RMF_MERIDIAN_LAT;
    this.rad2int = this.RMF_MERIDIAN_BIN / Math.PI;
    
    this.rad2deg = 1 / this.deg2rad;
    this.int2deg = 1 / this.deg2int;
    this.int2rad = 1 / this.rad2int;
    
    if (typeof MIREO_REDIRECT_TO != "undefined") {
	    this.routeStartIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RouteStart.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RouteStartShadow.png", new Size(32, 32), new Point(7, 31));
	    this.routeEndIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RouteEnd.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RouteEndShadow.png", new Size(32, 32), new Point(7, 31));
	    this.routePointIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/RoutePoint.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
        this.houseNumberIcon = new MapIcon(MIREO_REDIRECT_TO+"/map/images/HouseNumber.png", new Size(32, 32), new Point(16, 31), MIREO_REDIRECT_TO+"/map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
    } else {
	    this.routeStartIcon = new MapIcon("map/images/RouteStart.png", new Size(32, 32), new Point(16, 31), "map/images/RouteStartShadow.png", new Size(32, 32), new Point(7, 31));
	    this.routeEndIcon = new MapIcon("map/images/RouteEnd.png", new Size(32, 32), new Point(16, 31), "map/images/RouteEndShadow.png", new Size(32, 32), new Point(7, 31));
	    this.routePointIcon = new MapIcon("map/images/RoutePoint.png", new Size(32, 32), new Point(16, 31), "map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
        this.houseNumberIcon = new MapIcon("map/images/HouseNumber.png", new Size(32, 32), new Point(16, 31), "map/images/RoutePointShadow.png", new Size(32, 32), new Point(7, 31));
    }
    
    this.Deg2World_AA = function(pta) {
        var x = Math.round(this.deg2int * pta[0]),
            y = Math.round(this.rad2int * (Math.log(Math.tan(this.M_PI_4 + (this.deg2rad * pta[1] * 0.5)))));
        return [x, y]
    };

    this.Deg2World_OA = function(pto) {
        var x = Math.round(this.deg2int * pto.x),
            y = Math.round(this.rad2int * (Math.log(Math.tan(this.M_PI_4 + (this.deg2rad * pto.y * 0.5)))));
        return [x, y]
    };
    
    this.World2Deg_AA = function(wpa){
	    var x = this.int2deg * wpa[0],
	        y = this.rad2deg * (2 * Math.atan(Math.exp(this.int2rad * wpa[1])) - this.M_PI_2);
	    return [x, y];
    };
    
    this.World2Deg_OA = function(wpo){
	    var x = this.int2deg * wpo.x,
	        y = this.rad2deg * (2 * Math.atan(Math.exp(this.int2rad * wpo.y)) - this.M_PI_2);
	    return [x, y];
    };

    this.World2Pix_AA = function(map, wpa){
	    return [ Math.round((wpa[0] - map.currMTL.x) * map.rzf), 
	             Math.round((map.currMTL.y - wpa[1]) * map.rzf) ];
    };

    this.World2Pix_OA = function(map, wpo){
	    return [ Math.round((wpo.x - map.currMTL.x) * map.rzf), 
	             Math.round((map.currMTL.y - wpo.y) * map.rzf) ];
    };

    this.getWorldPatchworkBounds = function(map) {
	    return { "x1" : map.currMTL.x,
	             "y1" : map.currMTL.y,
	             "x2" : map.currMTL.x + map.patchSize.width * MireoMap.imgSize * map.zf,
	             "y2" : map.currMTL.y - map.patchSize.height * MireoMap.imgSize * map.zf };
    };

    this.getWGSPatchworkBounds = function(map) {
	    var tl = this.World2Deg_OA(map.currMTL),
	        br = this.World2Deg_AA([ map.currMTL.x + map.patchSize.width * MireoMap.imgSize * map.zf, 
	                                 map.currMTL.y - map.patchSize.height * MireoMap.imgSize * map.zf ]);
	    return { "x1" : tl[0],
	             "y1" : tl[1],
	             "x2" : br[0],
	             "y2" : br[1] };
    };
}

var cutil = new Utils();

function ctxBaseDrawer(id) {
    this.id = id;
    this.cmdarr = new Array();
}

function ctxArrowDrawer(id) {
    inheritBase(this, ctxBaseDrawer, id);
    this.oneThird = 1 / 3;
    this.arrows = [];
}

ctxArrowDrawer.prototype.project = function(angle, radius) {
    var dx = radius * Math.cos(angle * cutil.deg2rad);
    var dy = radius * Math.sin(angle * cutil.deg2rad);
    return [dx, dy];                
};

ctxArrowDrawer.prototype.arrowCmd = function(vhi, radius, colorStr) {
    var wpt = cutil.Deg2World_OA(vhi.pt),
        angle = (vhi.course == cutil.INVALID_COURSE ? 0 : vhi.course) - 90,
        ftip = this.project(angle, radius),
        ltip = this.project(angle - 45, -radius),
        rtip = this.project(angle + 45, -radius),
        btip = this.project(angle, -(radius * this.oneThird)),
        f = function(ctx, x, y) {
            ctx.fillStyle = colorStr;
            ctx.beginPath();
            ctx.moveTo(x + btip[0], y + btip[1]);
            ctx.lineTo(x + ltip[0], y + ltip[1]);
            ctx.lineTo(x + ftip[0], y + ftip[1]);
            ctx.lineTo(x + rtip[0], y + rtip[1]);
            ctx.closePath();
            ctx.fill();
            return { 
                "x" : x, 
                "y" : y, 
                "radius" : radius, 
                "data" : vhi 
            };    
        };
    return { 
        "exec" : f, 
        "wpt" : wpt, 
        "utc" : vhi.utc,
        "radius" : radius 
    };
};

ctxArrowDrawer.prototype.drawArrow = function(vhi, radius, colorStr) {
    if (!vhi.pt.x && !vhi.pt.y) return;
    this.cmdarr.push(this.arrowCmd(vhi, radius, colorStr));
};

ctxArrowDrawer.prototype.Draw = function(ctx, map) {
    var ci, pix;
    this.arrows = []; 
    for (var i = 0; i < this.cmdarr.length; i++) {
        ci = this.cmdarr[i];
        pix = cutil.World2Pix_AA(map, ci.wpt);
        this.arrows.push(ci.exec(ctx, pix[0], pix[1]));
    }
};

function ctxHistoryDrawer(id) {
    inheritBase(this, ctxArrowDrawer, id);
    this.top = Number.NEGATIVE_INFINITY;
    this.left = Number.POSITIVE_INFINITY;
    this.right = Number.NEGATIVE_INFINITY;
    this.bottom = Number.POSITIVE_INFINITY;
    this.faster = this.cmdarr;
    this.bounds = null;
    this.size = null;
    this.stype = "t";
    this.pc = false;
}

inheritPrototype(ctxArrowDrawer, ctxHistoryDrawer);

ctxHistoryDrawer.prototype.drawHistory = function(vhistory, radius, colorf) {
    var vhi, pt, cmd, len = vhistory.length;
    for (var i = 0; i < len; i++) {
        vhi = vhistory[i];
        pt = vhi.pt;
        if (!pt.x && !pt.y) continue;
        if (pt.x) {
            if (pt.x < this.left)
                this.left = pt.x;
            if (pt.x > this.right)
                this.right = pt.x;
        }
        if (pt.y) {
            if (pt.y > this.top)
                this.top = pt.y;
            if (pt.y < this.bottom)
                this.bottom = pt.y;
        } 
        cmd = this.arrowCmd(vhi, radius, colorf(i, len));
        this.cmdarr.push(cmd);
    }
    this.bounds = { 
        "x1" : this.left,
        "y1" : this.top, 
        "x2" : this.right, 
        "y2" : this.bottom 
    };
};

ctxHistoryDrawer.prototype.precompute = function() {   
    clearTimeout(this.tid);
    this.Xcmdarr = this.cmdarr.slice();
    this.Ycmdarr = this.cmdarr.slice();
    this.Xcmdarr.sort(function(o1, o2) { return o1.wpt[0] - o2.wpt[0]; });
    this.Ycmdarr.sort(function(o1, o2) { return o1.wpt[1] - o2.wpt[1]; });
    this.pc = true;
};

ctxHistoryDrawer.prototype.getfast = function(map) {
    var msize = map.patchPixSize();
    var mb = cutil.getWGSPatchworkBounds(map);
    if (mb.x1 < this.left && mb.x2 > this.right && mb.y1 > this.top && mb.y2 < this.bottom) {
        this.faster = this.cmdarr;
        this.stype = "t";
        return;
    }
    var wlt = cutil.Deg2World_AA([mb.x1, mb.y1]);
    var wrb = cutil.Deg2World_AA([mb.x2, mb.y2]);   
    var l = BinarySearch(this.Xcmdarr, function(o1, o2) { return o1 - o2.wpt[0]; }, wlt[0]);
    var r = BinarySearch(this.Xcmdarr, function(o1, o2) { return o1 - o2.wpt[0]; }, wrb[0]);
    var t = BinarySearch(this.Ycmdarr, function(o1, o2) { return o1 - o2.wpt[1]; }, wlt[1]);
    var b = BinarySearch(this.Ycmdarr, function(o1, o2) { return o1 - o2.wpt[1]; }, wrb[1]);
    var xf = ((l-r) >= (t-b)) ? true : false;
    if (xf) {
        this.faster = this.Xcmdarr.slice(l, r);
        this.size = msize.height;
        this.stype = "x";
    }
    else {
        this.faster = this.Ycmdarr.slice(b, t);
        this.size = msize.width;
        this.stype = "y";
    }
    this.faster.sort(function(o1, o2) { return o1.utc - o2.utc; });
};

ctxHistoryDrawer.prototype.GetCenterAndZoom = function(map) {
	var center = new Point((this.bounds.x1 + this.bounds.x2) / 2, 
	                       (this.bounds.y1 + this.bounds.y2) / 2),
	    pt1w = cutil.Deg2World_AA([this.bounds.x1, this.bounds.y1]),
	    pt2w = cutil.Deg2World_AA([this.bounds.x2, this.bounds.y2]),
	    zfx = Math.abs(pt2w[0] - pt1w[0]) / map.viewSize.width,
	    zfy = Math.abs(pt2w[1] - pt1w[1]) / map.viewSize.height;    
    return {
        "center" : center,
        "wcenter" : Sphere.Deg2World(center), 
        "zindex" :  map.zoomFactorToIndex(Math.max(zfx, zfy))
    };  
};

ctxHistoryDrawer.prototype.Draw = function(ctx, map) {
    if (this.pc) this.getfast(map);
    var fi, pix, x, y, last;
    this.arrows = [];
    for (var i = 0; i < this.faster.length; i++) {
        fi = this.faster[i];
        pix = cutil.World2Pix_AA(map, fi.wpt);
        x = pix[0];
        y = pix[1];
        if (this.stype == "y" && (x < 0 || x > this.size)) continue;
        else if (this.stype == "x" && (y < 0 || y > this.size)) continue;
        if (last) {
            var dr = last.radius + fi.radius;
            if ((Math.abs(last.x - x) <= dr) && (Math.abs(last.y - y) <= dr)) continue;
        }
        last = fi.exec(ctx, x, y);
        this.arrows.push(last);
    }
    if (!this.pc) {
        var self = this,
            tid = setTimeout(function() {
            clearTimeout(tid);
            self.precompute();
            tid = null;            
        }, 0);
    }
};

function ctxRouteDrawer(id, route) {
    inheritBase(this, ctxBaseDrawer, id);
    this.delta = 45 * Math.PI / 180;
    this.geometries = route.geometries;
    this.points = route.points;
    this.bounds = this.getBounds(route.geometries[0]);
    this.startMarker = new MapMarker(cutil.routeStartIcon);
    this.endMarker = new MapMarker(cutil.routeEndIcon);
    this.markers = [];
}

ctxRouteDrawer.prototype.removeRoutePoints = function(map) {
	while (this.markers.length) {
		map.removeMapMarker(this.markers.pop());
	}
};

ctxRouteDrawer.prototype.addRoutePoints = function(map) {
	this.removeRoutePoints(map);
	this.startMarker.position = Sphere.Deg2World(this.points[0]);
	map.addMapMarker(this.startMarker);
	this.markers.push(this.startMarker);
	for (var marker, i = 1; i < this.points.length-1; i++) {
	    marker = new CaptionMarker(cutil.routePointIcon, new Point(0, 0), i.toString());
	    marker.position = Sphere.Deg2World(this.points[i]);
	    map.addMapMarker(marker);
	    this.markers.push(marker);
	}
	this.endMarker.position = Sphere.Deg2World(this.points[this.points.length-1]);
	map.addMapMarker(this.endMarker);
	this.markers.push(this.endMarker);
};

ctxRouteDrawer.prototype.getBounds = function(geometry) {
	var left = Number.POSITIVE_INFINITY,
	    right = Number.NEGATIVE_INFINITY,
	    top = Number.POSITIVE_INFINITY,
	    bottom = Number.NEGATIVE_INFINITY,
	    pt;
	for (var i = 0; i < geometry.length; i++){
		pt = geometry[i];
		if (pt.x < left)
			left = pt.x;
		if (pt.x > right)
			right = pt.x;
		if (pt.y < top)
			top = pt.y;
		if (pt.y > bottom)
			bottom = pt.y;
	}
	var pt1 = cutil.World2Deg_AA([left, top])
	    pt2 = cutil.World2Deg_AA([right, bottom]);
	return {
	    "x1" : pt1[0],
	    "y1" : pt1[1],
	    "x2" : pt2[0],
	    "y2" : pt2[1]
	};
}

ctxRouteDrawer.prototype.GetCenterAndZoom = ctxHistoryDrawer.prototype.GetCenterAndZoom; 

ctxRouteDrawer.prototype.isRectOnRect = function(x1, y1, x2, y2) {
    return function (pt1, pt2) {
        if (pt1.x < pt2.x) {
            if ((pt1.x > x2) || (pt2.x < x1)) return false;
        } else {
            if ((pt2.x > x2) || (pt1.x < x1)) return false;
        }
        if (pt1.y < pt2.y) {
            if ((pt1.y > y1) || (pt2.y < y2)) return false;
        } else {
            if ((pt2.y > y1) || (pt1.y < y2)) return false;
        }
        return true;
    }
};

ctxRouteDrawer.prototype.project = function(angle, radius) {
    var dx = radius * Math.cos(angle);
    var dy = radius * Math.sin(angle);
    return [dx, dy]; 
};

ctxRouteDrawer.prototype.drawArrow = function(ctx, x, y, angle, radius) {
    var self = this;
    return function() {
        var ftip = self.project(angle, radius),
            ltip = self.project(angle - self.delta, -radius),
            rtip = self.project(angle + self.delta, -radius),
            btip = self.project(angle, -(radius / 3));
        ctx.beginPath();
        ctx.moveTo(x + ftip[0], y + ftip[1]);
        ctx.lineTo(x + ltip[0], y + ltip[1]);
        ctx.lineTo(x + btip[0], y + btip[1]);
        ctx.lineTo(x + rtip[0], y + rtip[1]);
        ctx.closePath();
        ctx.fill();
    };     
};

ctxRouteDrawer.prototype.drawRoute = function(width, dist, lineColor, arrowColor) {
    var self = this;
    for (var i = 0; i < this.geometries.length; i++) {
        var f = function(g) {
            return function(ctx, map, iRoR) {
                var arr = [], pix1, pix2, move, totalD, 
                    x1, y1, x2, y2, di, dy, dx, k, angle;
                ctx.strokeStyle = lineColor;
                ctx.fillStyle = arrowColor;
                ctx.lineWidth = width;
                ctx.lineJoin = "round";
                ctx.lineCap = "round";
                ctx.mitterLimit = 0;
                ctx.beginPath();
                move = true;
                totalD = 0;
                for (var j = 1; j < g.length; j++) {
                    if (iRoR(g[j-1], g[j])) {
                        x1 = x2;
                        y1 = y2;
                        if (move) {
                            pix1 = cutil.World2Pix_OA(map, g[j-1]);
                            x1 = pix1[0];
                            y1 = pix1[1];
                            ctx.moveTo(x1, y1);
                            move = false;
                        }
                        pix2 = cutil.World2Pix_OA(map, g[j]);
                        x2 = pix2[0];
                        y2 = pix2[1];
                        ctx.lineTo(x2, y2);
                        dx = x2 - x1;
                        dy = y2 - y1;
                        k = (dx != 0) ? (dy / dx) : null,
                        angle = Math.atan2(dy, dx);
                        di = Math.sqrt((dx*dx) + (dy*dy));
                        totalD += di;
                        var radius, dx2, xt, x, y;
                        while (totalD > dist) {
                            totalD = totalD - dist;
                            radius = di - totalD;
                            if (k != null) {
                                dx2 = radius / Math.sqrt(1 + (k * k));
                                xt = x1 + dx2;
                                if (xt < x1 || xt > x2) {
                                    dx2 *= (-1);
                                }
                                x = x1 + dx2;
                                y = y1 + k * dx2; 
                            } else {
                                x = x1;
                                y = (dy > 0) ? (y1 + radius) : (y1 - radius);
                            }
                            arr.push(self.drawArrow(ctx, x, y, angle, 2 * width));
                        }
                    } else if (!move) {
                        move = true;
                        totalD = 0;
                    }
                }
                ctx.stroke();
                for (var i = 0; i < arr.length; i++) {
                    arr[i]();
                }
            };
        };
        this.cmdarr.push(f(this.geometries[i]));
    }
};

ctxRouteDrawer.prototype.Delete = function(map) {
    this.removeRoutePoints(map);  
};

ctxRouteDrawer.prototype.Draw = function(ctx, map) {
    var f = this.cmdarr[map.getZoomLevel()],
        wb = cutil.getWorldPatchworkBounds(map);
    this.addRoutePoints(map);
    f(ctx, map, this.isRectOnRect(wb.x1, wb.y1, wb.x2, wb.y2));   
};

function ctxGeoAddressDrawer(id, ga) {
    inheritBase(this, ctxBaseDrawer, id);
    this.ga = ga;
    this.bounds = null;
    if (!ga.singlePoint) {
        this.bounds = this.getBounds(ga.geometries[0]);
    }
    this.houseNumberShown = false;
    this.houseNumberMarker = null;
    if (ga.houseNumber) {
        this.houseNumberMarker = new CaptionMarker(cutil.routePointIcon, new Point(0, 0), ga.houseNumber.caption);
		this.houseNumberMarker.position = ga.houseNumber.position;
    }
}

ctxGeoAddressDrawer.prototype.getBounds = function(geometry) {
	var left = Number.POSITIVE_INFINITY,
	    right = Number.NEGATIVE_INFINITY,
	    top = Number.POSITIVE_INFINITY,
	    bottom = Number.NEGATIVE_INFINITY,
	    gi, pt;
	for (var i = 0; i < geometry.length; i++){
		gi = geometry[i];
		for (var j = 0; j < gi.length; j++) {
		    pt = gi[j];
		    if (pt.x < left)
			    left = pt.x;
		    if (pt.x > right)
			    right = pt.x;
		    if (pt.y < top)
			    top = pt.y;
		    if (pt.y > bottom)
			    bottom = pt.y;
	    }
	}
	var pt1 = cutil.World2Deg_AA([left, top])
	    pt2 = cutil.World2Deg_AA([right, bottom]);
	return {
	    "x1" : pt1[0],
	    "y1" : pt1[1],
	    "x2" : pt2[0],
	    "y2" : pt2[1]
	};
};

ctxGeoAddressDrawer.prototype.GetCenterAndZoom = function(map) {
    if (this.ga.singlePoint) {
        return {
            "center" : this.ga.singlePoint,
            "wcenter" : this.ga.singlePointW, 
            "zindex" :  1
        };
    } else {
        return ctxHistoryDrawer.prototype.GetCenterAndZoom.call(this, map);
    }
};

ctxGeoAddressDrawer.prototype.drawGeoAddress = function(width, colorStr) {
    var self = this;
    for (var i = 0; i < this.ga.geometries.length; i++) {
        var f = function(g) {
            return function(ctx, map) {
                ctx.strokeStyle = colorStr;
                ctx.fillStyle = colorStr;
                ctx.lineWidth = width;
                ctx.lineJoin = "round";
                ctx.lineCap = "round";
                ctx.mitterLimit = 0;
                ctx.beginPath();
                var pix = cutil.World2Pix_OA(map, g[0][0]), gj;
                for (var j = 0; j < g.length; j++) {
                    gj = g[j];
                    pix = cutil.World2Pix_OA(map, gj[0]);
                    ctx.moveTo(pix[0], pix[1]);
                    for (var k = 1; k < gj.length; k++) {
                        pix = cutil.World2Pix_OA(map, gj[k]);
                        ctx.lineTo(pix[0], pix[1]);
                    }
                }
                ctx.stroke();
            };
        };
        this.cmdarr.push(f(this.ga.geometries[i]));
    }
};

ctxGeoAddressDrawer.prototype.Delete = function(map) {
    if (this.houseNumberShown && this.houseNumberMarker) {
        map.removeMapMarker(this.houseNumberMarker);
        this.houseNumberShown = false;
    } 
};

ctxGeoAddressDrawer.prototype.Draw = function(ctx, map) {
    if (!this.houseNumberShown && this.houseNumberMarker) {
        map.addMapMarker(this.houseNumberMarker);
        this.houseNumberShown = true;
    }
    if (!this.ga.singlePoint) {
        var f = this.cmdarr[map.getZoomLevel()],
            wb = cutil.getWorldPatchworkBounds(map);
        f(ctx, map);
    }  
};
