function InitRouter(getLefttop, Geo2Screen, Screen2Geo, getDatafunction) {

	this.archive = {points: new Array(), routes: new Array()};
	this.todraw = {points: new Array(), routes: new Array()};

	this.setpointevents = function(drawfunction) {
		var self = this;
		drawfunction = drawfunction || (this.eventsdrawfunction || function(){});
		return {
			onremove: function() {
				if (this.div.deleted) return false;
				this.div.deleted = true;
				if (this.div.start_path) {
					this.div.start_path.deleted = true;
					this.div.start_path.endpoint.end_path = false;
				};
				if (this.div.end_path) {
					this.div.end_path.deleted = true;
					this.div.end_path.startpoint.start_path = false;
				};

				if (this.div.start_path&&this.div.end_path) {
					self.getpath(this.div.end_path.startpoint, this.div.start_path.endpoint, false, function(p1,p2,dat,par,lt,trans) {
						self.setpath(dat,lt,p1,p2,trans);
						self.modarray(self.todraw.points,1,1,false);
						self.modarray(self.todraw.routes,2,2,[dat],drawfunction);
					});
				} else {
					self.modarray(self.todraw.points,1,1,false);
					self.modarray(self.todraw.routes,2,1,false,drawfunction);
				};
			},
			onmousedown: function(mouse, main) {
				if (this.div.busy) return false;
				if (this.div.eventscallback&&this.div.eventscallback.onmousedown_before) this.div.eventscallback.onmousedown_before(this.div, mouse);
				this.div.dcoords = mouse;
				this.div.dcoords.click = true;
				if (this.div.eventscallback&&this.div.eventscallback.onmousedown_after) this.div.eventscallback.onmousedown_after(this.div, mouse);
			},
			ondrag: function(mouse, main) {
				if (!this.div.dcoords) return true;
				var dx = mouse.x - this.div.dcoords.x;
				var dy = mouse.y - this.div.dcoords.y;
				self.movepoint(this.div, {x: dx, y:dy}, false, true);
				this.div.dcoords = mouse;
				var div = this.div;
				if (this.div.eventscallback&&this.div.eventscallback.onmousemove) this.div.eventscallback.onmousemove(this.div, mouse);
				if (!this.div.interval) this.div.interval = window.setInterval(function(e) {div.onmouseupF(true)}, 100);
			},
			onmouseup: function(redraw) {
				if (!redraw) {
					if (!this.div.dcoords) return false;
					window.clearInterval(this.div.interval);
					this.div.interval = false;
					this.div.busy = false;
					if (this.div.dcoords.click) {
						this.div.dcoords = false;
						if (this.div.eventscallback) {
							if (this.div.eventscallback.onmouseup_before) this.div.eventscallback.onmouseup_before(this.div);
							if (this.div.eventscallback.onmouseup_after) this.div.eventscallback.onmouseup_after(this.div);
							if (this.div.eventscallback.onclick) this.div.eventscallback.onclick(this.div);
						};
						return false;
					};
					if (this.div.eventscallback&&this.div.eventscallback.onmouseup_before) this.div.eventscallback.onmouseup_before(this.div);
				};
				if (redraw&&this.div.busy) return false;
				var lefttop = getLefttop();
				if (this.div.start_path||this.div.end_path) {
					this.div.busy = true;
					var div = this.div;
					self.redrawpaths([this.div.start_path, this.div.end_path], function (ar) {
						drawfunction();
						if (!redraw&&div.eventscallback) {
							if (div.eventscallback.onmouseup_after) div.eventscallback.onmouseup_after(div);
							if (div.eventscallback.onclick&&div.dcoords.click) div.eventscallback.onclick(div);
							div.dcoords = false;
						};
						div.busy = false;
					}, lefttop, function(par) {if (redraw&&!self.dotransform) return true});
				} else if (!redraw&&this.div.eventscallback) {
					if (this.div.eventscallback) {
						if (this.div.eventscallback.onmouseup_before) this.div.eventscallback.onmouseup_before(this.div);
						if (this.div.eventscallback.onmouseup_after) this.div.eventscallback.onmouseup_after(this.div);
						if (this.div.eventscallback.onclick&&this.div.dcoords.click) this.div.eventscallback.onclick(this.div);
					};
					this.div.dcoords = false;
				};
			}
		};
	};

	this.modarray = function(array, dub, num, elements, callback) {
		var Do;
		var self = this;
		if (dub==1) {
			Do = function(n) {
				if (array[n].deleted) {
					array[n].deleted = false;
					if (elements) {
						elements.unshift(i,num);
						array.splice.apply(array,elements);
					} else array.splice(i,num);
					i--;
				};
			};
		} else {
			Do = function(n) {
				self.modarray(array[n], (dub-1), num, elements);
			};
		};
		for (var i=0; i<array.length; i++) Do(i);
		if (callback) callback();
	};

	this.removeroute = function(route, array, redraw, redrawf) {
		if (!array) array = this.todraw.routes;
		var points = [];
		for (var i=0; i<route.length; i++) {
			route[i].deleted = true;
			if (points[points.length-1]!==route[i].startpoint)	points.push(route[i].startpoint);
			points.push(route[i].endpoint);
			if (route[i].startpoint.start_path) route[i].startpoint.start_path = false;
			if (route[i].endpoint.end_path) route[i].endpoint.end_path = false;
		};
		this.modarray(array,2,1);
		if (redraw) if (redrawf) redrawf();
		 else if (this.redrawfunctions.routes) this.redrawfunctions.routes();
		return points;
	};

	this.insertpoint = function(point, start, end, array, callback) {
		var paths = {};
		if (!array) array = this.todraw.routes;
		var oldroute = false;
		var self = this;
		var endfunction = function(paths) {
			var Ar = new Array();
			if (paths.start&&paths.start.path) {
				Ar.push(paths.start.path);
			};
			if (paths.end&&paths.end.path) {
				Ar.push(paths.end.path);
			};
			if (oldroute) {
				oldroute.deleted = true;
				self.modarray(array,2,1,Ar);
			} else {
				if (paths.start&&paths.start.path&&start.end_path) {
					start.end_path.deleted = true;
					self.modarray(array,2,0,Ar);
				};
				if (paths.end&&paths.end.path&&end.start_path) {
					start.start_path.deleted = true;
					self.modarray(array,2,0,Ar);
				};
			};
			callback(Ar);
		};
		if (start) {
			paths.start = {};
			oldroute = start.start_path;
			this.setroute([start, point], function(ar) {
				paths.start.path = ar.routes[0][0];
				if ((paths.end&&paths.end.path)||(!paths.end)) endfunction(paths);
			});
		};
		if (end) {
			paths.end = {};
			oldroute = end.end_path;
			this.setroute([point, end], function(ar) {
				paths.end.path = ar.routes[0][0];
				if ((paths.start&&paths.start.path)||(!paths.start)) endfunction(paths);
			});
		}
	};

	this.movepoint = function(point,coords,jump,update,lefttop) {
		var dcoords = coords;
		if (coords) {
			if (jump) {
				dcoords.x = coords.x - point.coords.x;
				dcoords.y = coords.y - point.coords.y;
			};
			point.style.left = (point.offsetLeft+dcoords.x) + 'px';
			point.style.top = (point.offsetTop+dcoords.y) + 'px';
			point.coords.x += dcoords.x;
			point.coords.y += dcoords.y;
		};
		if (update) {
			if (!lefttop) lefttop = getLefttop();
			dcoords = {x: point.coords.x, y: point.coords.y};
			dcoords.x += lefttop.x;
			dcoords.y += lefttop.y;
			dcoords = Screen2Geo(dcoords.x, dcoords.y);
			point.geocoords = dcoords;
			if (typeof(update)=='function') this.redrawpaths([point.start_path, point.end_path],update,lefttop);
		};
	};

	this.redrawpaths = function(paths, endfunction, lefttop, breakfunction) {
		if (!lefttop) lefttop = getLefttop();
		if (!breakfunction) breakfunction = function(par) {return false};
		var array = {paths: new Array(), nums: new Array()};
		var self = this;
		for (var i=0; i<paths.length; i++) {
			if (!paths[i]) {
				array.nums.push(i);
				if (array.nums.length==paths.length) {
					array.routes = [array.paths];
					delete array.paths;
					delete array.nums;
					if (endfunction) endfunction(array);
				};
				continue;
			};
			this.getpath(paths[i].startpoint, paths[i].endpoint, lefttop, function(p1,p2,dat,par,lt,trans) {
				if (breakfunction([p1,p2,dat,par,lt,trans])) return false;
				if (dat) {
					for (var j in dat) {
						par.p[j] = dat[j];
					};
					self.setpath(par.p, lt, false, false, trans);
				};
				array.paths[par.n] = par.p;
				array.nums.push(par.n);

				if (array.nums.length==paths.length) {
					array.routes = [array.paths];
					delete array.paths;
					delete array.nums;
					if (endfunction) endfunction(array);
				};
			}, {p: paths[i], n: i});
		};
	};

	this.setdata = function(points, endfunction, lefttop) {
		if (!lefttop) lefttop = getLefttop();
		var archiv = {points: new Array(), paths: new Array(), nums: new Array()};
		var self = this;
		var end = function(p1,p2,dat,par,lt,trans) {
			if (dat) {
				archiv.paths[par] = dat;
				self.setpath(dat,lt,p1,p2,trans);
			};
			archiv.nums.push(par);
			if (archiv.nums.length<(archiv.points.length-1)) return false;
			archiv.routes = [archiv.paths];
			delete archiv.paths;
			delete archiv.nums;
			endfunction(archiv);
		};
		if (!points[0]) return endfunction();
		archiv.points.push(this.setanypoint(points[0].coords, points[0].content, lefttop, points[0].eventscallback, points[0].point_properties));
		for (var i=1; i<points.length-1; i++) {
			if (!points[i]) return endfunction();
			archiv.points.push(this.setanypoint(points[i].coords, points[i].content, lefttop, points[i].eventscallback, points[i].point_properties));
			this.getpath(archiv.points[archiv.points.length-2], archiv.points[archiv.points.length-1], lefttop, end, (i-1));
		};
		archiv.points.push(this.setanypoint(points[points.length-1].coords, points[points.length-1].content, lefttop, points[points.length-1].eventscallback, points[points.length-1].point_properties));
		this.getpath(archiv.points[archiv.points.length-2], archiv.points[archiv.points.length-1], lefttop, end, (points.length-2));
	};

	this.setroute = function(points, endfunction, lefttop) {
		if (!lefttop) lefttop = getLefttop();
		var archiv = {paths: new Array(), nums: new Array()};
		var self = this;
		var end = function(p1,p2,dat,par,lt,trans) {
			if (dat) {
				archiv.paths[par] = dat;
				self.setpath(dat,lt,p1,p2,trans);
			};
			archiv.nums.push(par);
			if (archiv.nums.length<(points.length-1)) return false;
			archiv.routes = [archiv.paths];
			delete archiv.paths;
			delete archiv.nums;
			endfunction(archiv);
		};

		for (var i=1; i<points.length-1; i++) {
			if (!points[i-1]||!points[i]) return endfunction();
			this.getpath(points[i-1], points[i], lefttop, end, (i-1));
		};
		this.getpath(points[points.length-2], points[points.length-1], lefttop, end, (points.length-2));
	};

	this.setpath = function(path,lefttop,startP,endP,nogeo) {
		if (!lefttop) lefttop = getLefttop();
		if (!path) return false;
		var coords;
		if (startP) {
			path.startpoint = startP;
			startP.start_path = path;
		};
		if (endP) {
			path.endpoint = endP;
			endP.end_path = path;
		};

		if (!path.geom) return false;
		if (!nogeo) {
			for (var i=0; i<path.geom.length; i++) {
				coords = Geo2Screen(path.geom[i].x, path.geom[i].y);
				coords.x = Math.round((coords.x*1)-lefttop.x);
				coords.y = Math.round((coords.y*1)-lefttop.y);
				//path.geom[i] = {gx: path.geom[i].x, gy: path.geom[i].y, x: coords.x, y: coords.y};
				path.geom[i].gx = path.geom[i].x;
				path.geom[i].x = coords.x;
				path.geom[i].gy = path.geom[i].y;
				path.geom[i].y = coords.y
			};
		} else {
			for (var i=0; i<path.geom.length; i++) {
				path.geom[i].x = Math.round((path.geom[i].x*1)-lefttop.x);
				path.geom[i].y = Math.round((path.geom[i].y*1)-lefttop.y);
			};
		};
	};

	this.getpath = function(p1, p2, lefttop, lastfunction, param) {
		//if (!lefttop) lefttop = getLefttop();
		var self = this;
		getDatafunction(p1, p2, function(data, trans) {
			lefttop = getLefttop();
			if (typeof(data)!='object') data = false;
			if (data.points) {
				data.geom = data.points;
				for (var i=0; i<data.geom.length; i++) {
					data.geom[i] = {x: data.geom[i].pt.x, y: data.geom[i].pt.y, attr: data.geom[i].attr};
				};
			};
			if (lastfunction) lastfunction(p1, p2, data, param, lefttop, self.dotransform);
		});
	};

	this.setanypoint = function(coords, content, lefttop, events, point_properties) {
		if (!lefttop) lefttop = getLefttop();
		var point = document.createElement('div');

		if (coords.gx||coords.gy) {
			point.geocoords = {gx: coords.gx, gy: coords.gy};
		} else {
			point.coords = {x: coords.x, y: coords.y};
			coords.x += lefttop.x;
			coords.y += lefttop.y;
			coords = Screen2Geo(coords.x, coords.y);
			point.geocoords = coords;
		};
		point.content = content;

		if (events) {
			point.eventscallback = events;
		};
		if (point_properties) {
			point.point_properties = point_properties;
		};
		return point;
	};

	this.savedata = function(data) {
		for (var i in data) {
			if (!this.archive[i]||(!data[i].length)) this.archive[i] = data[i];
			else {
				for (var j=0; j<data[i].length; j++) {
					if (data[i][j]) this.archive[i].push(data[i][j]);
				};
			};
		};
	};

	this.data2draw = function(data, draw, drawfunctions, lefttop) {
		for (var i in data) {
			if (this.todraw[i]&&data[i].length) {
				for (var j=0; j<data[i].length; j++) {
					if (data[i][j]) this.todraw[i].push(data[i][j]);
				};
			};
		};
		if (draw) this.drawdata(this.todraw, drawfunctions, lefttop);
	};

	this.drawfunctions = {};
	this.redrawfunctions = {};

	this.drawdata = function(draw_data, drawfunctions, lefttop) {
		if (!lefttop) lefttop = getLefttop();
		drawfunctions = drawfunctions||this.drawfunctions;
		this.drawpoints(draw_data.points, drawfunctions.points, lefttop);
		this.drawroutes(draw_data.routes, drawfunctions.routes, lefttop);
	};

	this.drawpoints = function(points, drawfunction, lefttop) {
		drawfunction = drawfunction||this.drawfunctions.points;
		if (!points||!drawfunction) return false;
		if (!lefttop) lefttop = getLefttop();
		var coords;
		var p;
		for (var i=0; i<points.length; i++) {
			coords = Geo2Screen(points[i].geocoords.gx, points[i].geocoords.gy);
			coords.x = Math.round((coords.x*1)-lefttop.x);
			coords.y = Math.round((coords.y*1)-lefttop.y);
			points[i].coords = coords;
			if (!points[i].nodeName) {
				p = points[i];
				points[i] = drawfunction([points[i]]);
				points[i] = points[i][0].line;
				if (points[i].start_path) points[i].start_path.startpoint = points[i];
				if (points[i].end_path) points[i].end_path.endpoint = points[i];
			} else drawfunction([points[i]]);
		};
	};

	this.drawroutes = function(routes, drawfunction, lefttop, norecalc) {
		drawfunction = drawfunction||this.drawfunctions.routes;
		if (!routes||!drawfunction) return false;
		if (!lefttop) lefttop = getLefttop();
		var coords;
		var array = new Array();
		if (norecalc!='norecalc') {
			for (var i=0; i<routes.length; i++) {
				if (!routes[i][0]) continue;
				array.push({line: [routes[i][0].startpoint.geocoords, routes[i][0].geom[0]], color: 'rgb(100,200,0)'});
				for (var l=0; l<routes[i].length; l++) {
					if (!routes[i][l]) continue;
					array.push({line: routes[i][l].geom, attributes: routes[i][l].attributes, dir: routes[i][l].dir, color: 'rgb(0,150,200)', circles: routes[i][l].m_debug_circles});
					array.push({line: [routes[i][l].geom[routes[i][l].geom.length-1], routes[i][l].endpoint.geocoords], color: 'rgb(100,200,0)'});
					for (var k=(array.length-3); k<array.length; k++) {
						for (var j=0; j<array[k].line.length; j++) {
							if (array[k].line[j].gx=='undefined'||array[k].line[j].gy=='undefined') return false;
							coords = Geo2Screen(array[k].line[j].gx, array[k].line[j].gy);
							coords.x = Math.round((coords.x*1)-lefttop.x);
							coords.y = Math.round((coords.y*1)-lefttop.y);
							array[k].line[j].x = coords.x;
							array[k].line[j].y = coords.y;
						};
					};
				};
			};
		} else {
			for (var i=0; i<routes.length; i++) {
				if (!routes[i][0]) continue;
				array.push({line: [routes[i][0].startpoint.coords, routes[i][0].geom[0]], color: 'rgb(100,200,0)'});
				for (var l=0; l<routes[i].length; l++) {
					if (!routes[i][l]) continue;
					array.push({line: routes[i][l].geom, attributes: routes[i][l].attributes, dir: routes[i][l].dir, color: 'rgb(0,150,200)'});
					array.push({line: [routes[i][l].geom[routes[i][l].geom.length-1], routes[i][l].endpoint.coords], color: 'rgb(100,200,0)'});
				};
			};
		};
		drawfunction(array);
	};

	this.changePointProp = function(point, props) {
		if (!point.point_properties) point.point_properties = {};
		for (var i in props) {
			point.point_properties[i] = props[i];
		};
		this.drawpoints([point]);
	};

	this.Draw = function(cllback, drawfunctions, lefttop) {
		this.drawdata(this.todraw, drawfunctions, lefttop);
		if (cllback) cllback();
	};
}