function GZoomControl(oBoxStyle,oOptions,oCallbacks) {
  GZoomControl.G.style = {
    nOpacity:.2,
    sColor:"#000",
    sBorder:"2px solid blue"
  };
  var style=GZoomControl.G.style;
  for (var s in oBoxStyle) {
    style[s]=oBoxStyle[s]
  };
  var aStyle=style.sBorder.split(' ');
  style.nOutlineWidth=parseInt(aStyle[0].replace(/\D/g,''));
  style.sOutlineColor=aStyle[2];
  style.sIEAlpha='alpha(opacity='+(style.nOpacity*100)+')';
	
	GZoomControl.G.options={
		bForceCheckResize:false,
		sButtonHTML:'zoom ...',
		oButtonTop:120,
		oButtonLeft:3,
		oButtonStartingStyle:{width:'52px',border:'1px solid black',padding:'0px 5px 1px 5px'},
		oButtonStyle:{background:'#FFF'},
		sButtonZoomingHTML:'Drag a region on the map',
		oButtonZoomingStyle:{background:'#FF0'},
		nOverlayRemoveMS:6000,
		bStickyZoom:false
	};
	
	for (var s in oOptions) {GZoomControl.G.options[s]=oOptions[s]};
	
	if (oCallbacks == null) {oCallbacks={}};
	GZoomControl.G.callbacks=oCallbacks;
}

GZoomControl.prototype = new GControl();

GZoomControl.G={
	bDragging:false,
	mct:null,
	mcr:null,
	mcb:null,
	mcl:null,
	oMapPos:null,
	oOutline:null,
	nMapWidth:0,
	nMapHeight:0,
	nMapRatio:0,
	nStartX:0,
	nStartY:0,
	nBorderCorrect:0,
	zoomActive:false
};

GZoomControl.prototype.initialize = function(oMap) {
  var G=GZoomControl.G;
	var oMC=oMap.getContainer();
	var oButton=this.initButton_(oMC);
	var o = document.createElement("div");
  	o.id='gzoom-map-cover';
	o.innerHTML='<div id="gzoom-outline" style="position:absolute;display:none;"></div><div id="gzoom-mct" style="position:absolute;display:none;"></div><div id="gzoom-mcl" style="position:absolute;display:none;"></div><div id="gzoom-mcr" style="position:absolute;display:none;"></div><div id="gzoom-mcb" style="position:absolute;display:none;"></div>';
	acl.style([o],{position:'absolute',top:0,display:'none',overflow:'hidden',cursor:'crosshair',zIndex:101});
	oMC.appendChild(o);
	
	GEvent.addDomListener(oButton, 'click', GZoomControl.prototype.buttonClick_);
	GEvent.addDomListener(oMC.firstChild.firstChild, 'mousedown', GZoomControl.prototype.mouseClick_);
	GEvent.addDomListener(o, 'mousedown', GZoomControl.prototype.coverMousedown_);
	GEvent.addDomListener(document, 'mousemove', GZoomControl.prototype.drag_);
	GEvent.addDomListener(document, 'mouseup', GZoomControl.prototype.mouseup_);

	G.oMapPos=acl.getElementPosition(oMap.getContainer());
	G.oOutline=$id("gzoom-outline");	
	G.oButton=$id("gzoom-control");
	G.mc=$id("gzoom-map-cover");
	G.mct=$id("gzoom-mct");
	G.mcr=$id("gzoom-mcr");
	G.mcb=$id("gzoom-mcb");
	G.mcl=$id("gzoom-mcl");
	G.oMap = oMap;

	G.nBorderCorrect = (window.ActiveXObject) ?  0 : G.style.nOutlineWidth*2;	//1998 called, it wants it's doctype back
	this.setDimensions_();
	this.initStyles_();
	return oButton;
};

GZoomControl.prototype.getDefaultPosition = function() {
    var G=GZoomControl.G;
	return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(G.options.oButtonLeft, G.options.oButtonTop));
};

GZoomControl.prototype.initButton_=function(oMapContainer) {
	var G=GZoomControl.G;
	var oButton = document.createElement('div');
	oButton.innerHTML=G.options.sButtonHTML;
	oButton.id='gzoom-control';
	acl.style([oButton],{cursor:'pointer',zIndex:200});
	acl.style([oButton],G.options.oButtonStartingStyle);
	acl.style([oButton],G.options.oButtonStyle);
	oMapContainer.appendChild(oButton);
	return oButton;
};

GZoomControl.prototype.setButtonMode_=function(sMode){
	var G=GZoomControl.G;
	if (sMode=='zooming') {
		G.oButton.innerHTML=G.options.sButtonZoomingHTML;
		acl.style([G.oButton],G.options.oButtonZoomingStyle);
	} else {
		G.oButton.innerHTML=G.options.sButtonHTML;
		acl.style([G.oButton],G.options.oButtonStyle);
	}
};

GZoomControl.prototype.buttonClick_=function(){
	if (GZoomControl.G.mc.style.display=='block')
	{ 
		GZoomControl.prototype.setButtonMode_('normal');
		GZoomControl.prototype.resetDragZoom_();
		GZoomControl.G.zoomActive = false;
	} 
	else 
	{
	    GZoomControl.G.zoomActive = true;
	    GZoomControl.prototype.setButtonMode_('zooming');
		GZoomControl.prototype.initCover_();
	}
};

GZoomControl.prototype.mouseClick_=function(e){
    if (!e) e = window.event;
	if (e.button == 2 && GZoomControl.G.mc.style.display!='block')
	{
	    GZoomControl.prototype.setButtonMode_('zooming');
		GZoomControl.prototype.initCover_(true);
		GZoomControl.prototype.coverMousedown_(e);
	}
};

GZoomControl.prototype.initCover_=function(rightClick){
	var G=GZoomControl.G;
	if(G.zoomActive || rightClick)
	{
	    G.oMapPos=acl.getElementPosition(G.oMap.getContainer());
	    GZoomControl.prototype.setDimensions_();
	    acl.style([G.mc],{display:'block',background:G.style.sColor});
	    acl.style([G.oOutline],{width:'0px',height:'0px'});
	    if(GZoomControl.G.callbacks['buttonClick'] !=null)
	    {
		    GZoomControl.G.callbacks.buttonClick()
	    };
	 }
};

GZoomControl.prototype.coverMousedown_ = function(e){
	var G=GZoomControl.G;
	var oPos = GZoomControl.prototype.getRelPos_(e);
	G.nStartX=oPos.left;
	G.nStartY=oPos.top;

	acl.style([G.mc],{background:'transparent',opacity:1,filter:'alpha(opacity=100)'});
	acl.style([G.oOutline],{left:G.nStartX+'px',top:G.nStartY+'px',display:'block',width:'1px',height:'1px'});
	G.bDragging=true;

	G.mct.style.top=(G.nStartY-G.nMapHeight)+'px';
	G.mct.style.display='block';
	G.mcl.style.left=(G.nStartX-G.nMapWidth)+'px';
	G.mcl.style.top=(G.nStartY)+'px';
	G.mcl.style.display='block';

	G.mcr.style.left=(G.nStartX)+'px';
	G.mcr.style.top=(G.nStartY)+'px';
	G.mcr.style.display='block';
	G.mcb.style.left=(G.nStartX)+'px';
	G.mcb.style.top=(G.nStartY)+'px';
	G.mcb.style.width='0px';
	G.mcb.style.display='block';
	
	if (G.callbacks.dragStart !=null){G.callbacks.dragStart(G.nStartX,G.nStartY)};
	return false;
};

GZoomControl.prototype.drag_=function(e){
	var G=GZoomControl.G;
	if(G.bDragging) 
	{
		var oPos=GZoomControl.prototype.getRelPos_(e);
		var oRec=GZoomControl.prototype.getRectangle_(G.nStartX,G.nStartY,oPos,G.nMapRatio);
		G.oOutline.style.top=oRec.top+"px";
		G.oOutline.style.left=oRec.left+"px";
		G.oOutline.style.width=(Math.max((oRec.width-G.nBorderCorrect),1))+"px";
		G.oOutline.style.height=(Math.max((oRec.height-G.nBorderCorrect),1))+"px";
		
        G.mct.style.top=(oRec.top-G.nMapHeight)+"px";

        G.mcl.style.top=(Math.max(oRec.top,0))+"px";
        G.mcl.style.left=(oRec.left-G.nMapWidth)+"px";
        
        G.mcr.style.top=(Math.max(oRec.top,0))+"px";
        G.mcr.style.left=(oRec.left+oRec.width)+"px";
        
        G.mcb.style.width=(oRec.width)+"px";
        G.mcb.style.top=(oRec.top+oRec.height)+"px";
        G.mcb.style.left=(oRec.left)+"px";		
		if (G.callbacks.dragging !=null){G.callbacks.dragging(G.nStartX,G.nStartY,oRec.nEndX,oRec.nEndY)};

		return false;
	}  
};
GZoomControl.prototype.mouseup_=function(e){
	var G=GZoomControl.G;
	if (G.bDragging) {
	
		var oPos = GZoomControl.prototype.getRelPos_(e);
		G.bDragging=false;
		var oRec = GZoomControl.prototype.getRectangle_(G.nStartX,G.nStartY,oPos,G.nMapRatio);

        var wasRightClick = (e.button == 2);
        GZoomControl.prototype.setButtonMode_('normal');
		GZoomControl.prototype.resetDragZoom_(wasRightClick);

        if(oRec.height <= 1 && oRec.width <= 1) return;
        
		var nwpx=new GPoint(oRec.left,oRec.top);
		var nepx=new GPoint(oRec.left+oRec.width,oRec.top);
		var sepx=new GPoint(oRec.left+oRec.width,oRec.top+oRec.height);
		var swpx=new GPoint(oRec.left,oRec.top+oRec.height);
		var nw = G.oMap.fromContainerPixelToLatLng(nwpx); 
		var ne = G.oMap.fromContainerPixelToLatLng(nepx); 
		var se = G.oMap.fromContainerPixelToLatLng(sepx); 
		var sw = G.oMap.fromContainerPixelToLatLng(swpx); 

		var oZoomArea = new GPolyline([nw,ne,se,sw,nw],G.style.sOutlineColor,G.style.nOutlineWidth+1,.4);

		G.oMap.addOverlay(oZoomArea);
		setTimeout (function(){G.oMap.removeOverlay(oZoomArea)},G.options.nOverlayRemoveMS);  

		oBounds=new GLatLngBounds(sw,ne);
		nZoom=G.oMap.getBoundsZoomLevel(oBounds);
		oCenter=oBounds.getCenter();
		G.oMap.setCenter(oCenter, nZoom);

		if (G.callbacks.dragEnd !=null){G.callbacks.dragEnd(nw,ne,se,sw,nwpx,nepx,sepx,swpx)};

		if (G.options.bStickyZoom) {
		    GZoomControl.prototype.setButtonMode_('zooming');
		    GZoomControl.prototype.initCover_()
		};		
	}
};

GZoomControl.prototype.resetDragZoom_=function(rightClick) {
	var G=GZoomControl.G;
	if(G.zoomActive || rightClick)
	{
	    acl.style([G.mc,G.mct,G.mcr,G.mcb,G.mcl],{display:'none',opacity:G.style.nOpacity,filter:G.style.sIEAlpha});
	    G.oOutline.style.display='none';
	}
};

GZoomControl.prototype.setDimensions_=function() {
	var G=GZoomControl.G;
	if (G.options.bForceCheckResize)
	{
		G.oMap.checkResize()
	};
	var oSize = G.oMap.getSize();
	G.nMapWidth  = oSize.width;
	G.nMapHeight = oSize.height;
	G.nMapRatio  = G.nMapHeight/G.nMapWidth;
	acl.style([G.mc,G.mct,G.mcr,G.mcb,G.mcl],{width:G.nMapWidth+'px', height:G.nMapHeight+'px'});
};

GZoomControl.prototype.initStyles_=function(){
	var G=GZoomControl.G;
	acl.style([G.mc,G.mct,G.mcr,G.mcb,G.mcl],{filter:G.style.sIEAlpha,opacity:G.style.nOpacity,background:G.style.sColor});
	G.oOutline.style.border=G.style.sBorder;
};

GZoomControl.prototype.getRelPos_=function(e) {
	var oPos=acl.getMousePosition (e);
	var G=GZoomControl.G;
	return {top:(oPos.top-G.oMapPos.top),left:(oPos.left-G.oMapPos.left)};
};

GZoomControl.prototype.getRectangle_=function(nStartX,nStartY,oPos,nRatio){
    var _top,_left,_height,_width;
	var dX=oPos.left-nStartX;
	var dY=oPos.top-nStartY;	
	_top = (dY >= 0) ? nStartY : nStartY + dY;
	_left = (dX >= 0) ? nStartX : nStartX + dX;
    var x = {
        top:_top,
        left:_left,
        dX:dX,
        dY:dY,
        height:Math.abs(dY),
        width:Math.abs(dX)
        }
	return x;
};


function $id(sId) { return document.getElementById(sId); }

if (!window['acldefined']) {var acl={};window['acldefined']=true;}//only set the acl namespace once, then set a flag

acl.getMousePosition=function(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.documentElement.scrollLeft?document.documentElement.scrollLeft:document.body.scrollLeft);
		posy = e.clientY + (document.documentElement.scrollTop?document.documentElement.scrollTop:document.body.scrollTop);
	}	
	return {left:posx, top:posy};  
};

acl.getElementPosition=function(eElement) {
	var nLeftPos = eElement.offsetLeft;
	var nTopPos = eElement.offsetTop;
	var eParElement = eElement.offsetParent;
	while (eParElement != null ) 
	{
		nLeftPos += eParElement.offsetLeft;
		nTopPos += eParElement.offsetTop;  
		eParElement = eParElement.offsetParent;
	}
	return {left:nLeftPos, top:nTopPos};
};

acl.style=function(a,o){
	if (typeof(a)=='string') 
	{
		a=acl.getManyElements(a);
	}
	for (var i=0;i<a.length;i++)
	{
		for (var s in o) { a[i].style[s]=o[s];}
	}
};
acl.getManyElements=function(s)
{		
	t=s.split(',');
	a=[];
	for (var i=0;i<t.length;i++)
	{
		a[a.length]=$id(t[i])
	};
	return a;
};
