
with(jzGMap){

importFrom(jz,'newXMLHttpRequest innerText innerHTML update mapf extractf contains indexOf setCookie setCookieForever getCookie _ IE _doBREAK parseUrl scanChecksFor getId isArrayLike')
importFrom(Math,'max min')

$._tracksrv= '/' //TODO: isolate tracker code in seperate .js ro allow diffrent servers
$._routersrv= '/'

$.movetoscrn= function(marker,map){
			if (!(map && marker && marker.screenpos)) return
			latlng= map.fromContainerPixelToLatLng(marker.screenpos)
			marker.setLatLng(latlng)
			}

$.tag= function(name,ops,content){
		if (content === undefined ) content=ops, ops=false
		var atr= (!ops)?'':mapf(ops,function(v,k){ return ' '+(k=='cls'?'class':k)+'="'+v+'"' },[]).join('')
		return ['<',name,atr,'>',content,'</',name,'>'].join('')
		}

$.li= function(ops,content){ return tag('li',ops,content) }
$.ul= function(ops,content){ return tag('ul',ops,content) }
$.span= function(ops,content){ return tag('span',ops,content) }

$.closestTo= function(pts,tpt,minerr){
	//return index of closest point to tpt
	if (!minerr) minerr=0.001 //a meter
	var cDst=99999.0, cPos= -1
	//use mapf so we can break
	mapf(pts,function(pt,pos){
		var dst=mCalcDistance(pt,tpt)
		if (dst>=cDst) return
		//we got a closer pt
		cDst=dst, cPos=pos
		if (dst<minerr) return _doBREAK
		},false) //dont collect results
	return [cPos,cDst]
	}

$.indexBefore= function(pts,tpt,minerr){
	var ret= closestTo(pts,tpt,minerr)
	var pos=ret[0], dst=ret[1]
	if (pos==pts.length-1) return pos //first so start has to be before ....
	if (mCalcDistance(pts[pos+1],tpt)<mCalcDistance(pts[pos+1],pts[pos])) return pos+1
	return pos
	}

$.pts2e= function(pts,res){
	if (res === undefined) res=6.
	res= Math.pow(10.,res)
	
	var olat=0, olng=0, ret=[]
	dof(pts,function(ll){
		var lat=Math.round(res*ll.lat()), lng=Math.round(res*ll.lng())
		var xlat=lat-olat, xlng=lng-olng
		olat+=xlat
		olng+=xlng
		ret.push(xlat)
		ret.push(xlng)
		})
	return ret
	}

$.pts2es= function(pts,res,sep){
	if (!sep) sep= ','
	return pts2e(pts,res).join(sep)
	}

$.e2pts= function(edata,res){
	if (res == undefined) res=6.
	res= Math.pow(10.,res)
	var lat=0,lng=0
	return mapf(edata.length/2,function(i){
		var idx=i*2
		lat+=edata[idx]
		lng+=edata[idx+1]
		return newlatlng(lat/res,lng/res) })
	}

$.es2pst= function(es,res,sep){
	if (!sep) sep=','
	return e2pts(mapf(es.split(sep),parseFloat,[]),res)
	}

$._uiNames= [ 'tq', 'reg', 'local' ]
$.doCostUIUpdate= function(){
	if ( !( mapDiv && mapDiv.vMap && mapDiv.vMap.router && mapDiv.vMap.router.setInfo )) return

	var info={}, update=false

	var speeds={}
	dof(_uiNames,function(name){
	    var elem= getId(name+'-speed')
	    if (elem && parseFloat(elem.value)) speeds[name.charAt(0).toUpperCase()]=parseFloat(elem.value)*1000.,update=true
	    })

	if (update) info.speeds=speeds

	info.usetemps= opttemps= scanChecksFor('it-opt')=='temps', update=true
	var costs= mapf(_uiNames,function(name){ return scanChecksFor(name+'-scale')}).join(',')
	if (costs.length) info.cost=costs,update=true

	var units= scanChecksFor('it-um')
	if (units) {setunit(units); update=true; }

	//alert('cost update info='+mapf(info,function(v,k){return k+':'+v},[]).join(','))
	if (update) mapDiv.vMap.router.setInfo(info)
	}

$.doCostUIRecenter= function(){
	if ( mapDiv && mapDiv.vMap && mapDiv.vMap.router && mapDiv.vMap.router.setInfo ){
	    dof(['tq-scale','reg-scale','local-scale'],function(name){
		var elem=getId(name+'.2')
		if (elem) elem.checked='checked'
		})
	    }
	doCostUIUpdate()
	}

$.doInitCostUI= function(){
	if ( !( mapDiv && mapDiv.vMap && mapDiv.vMap.router && mapDiv.vMap.router.setInfo )) return
	
	var router= mapDiv.vMap.router

	var uiDiv= getId('it-options')
	if (!uiDiv) return //nothing to init

	if (router.speeds) {
	    var spdtxt=mapf(router.speeds,function(v){return (v/1000).toString()})
	    dof(_uiNames,function(nm){
		//if (!nm || nm.length<1) return
		//alert('num ='+(typeof nm)+' : '+nm)
		if (nm.charAt(0).toUpperCase() in spdtxt){
		    var elem= getId(nm+'-speed')
		    var val= spdtxt[nm.charAt(0).toUpperCase()]
		    if (elem && val) {
			dof(elem.options,function(option){ 
			    if (option.value==val){
				option.selected=true
				}
			    })
			} 
		    //else alert('no try id='+elem.id+' op val='+val)
		    }
		})
	    }

	if (router.cost) {
	    var costvalues= router.cost.split(',')
	    dof(_uiNames,function(nm,idx){
		var name= nm+'-scale',val=costvalues[idx]
		var elems= getElementsByNames(uiDiv,name,'input')
		dof(elems,function(option){ if (option.value==val) option.checked=true })
		})
	    }

	var um= unitname
	if (um)
	    dof(getElementsByNames(uiDiv,'it-um','input'),function(option){ if (option.value==um) option.checked=true })
	}


$.doRouterReset= function(){
	if ( !( mapDiv && mapDiv.vMap && mapDiv.vMap.router )) return
	mapDiv.vMap.router.reset()
	setunit('km')
	doInitCostUI()
	}

$.swapRouter= function(newRouter){
	if ( !( mapDiv && mapDiv.vMap && mapDiv.vMap.router)) return false

	var oldrouter= mapDiv.vMap.router
	var state= oldrouter.getState()
	oldrouter.clear()
	if (typeof newRouter == 'string') state.network=newRouter,newRouter=DynaRouter
	return mapDiv.vMap.router= newRouter(mapDiv.vMap,state)
	}
	

$.MRouterBase= Class(Object,{
	//Multi Point Router
	// This class represents a path between multiple waypoints
	// How the routing occurs between the diffrent waypoints is determined by the subclassing
	// such as Google roads, VX snowmobilw paths etc
	// a simple implementation is provided that assumes lines between way points
	// 2 sub classes are implemented as bases, one assumes routinge occurs between 2 way points
	// the other redoes the whole routing when ever a change occurs ..
	// The main class (this one) is responsible for managing the ordered way points ... 
	//set a init
	markers:false, //list of marker that amke up the path

	map:	false,
	//def vals can overide as args to init 
	pauseIcon: MidIcon,
	//pauseIcon: PauseIcon,
	routeStyle: {color: "#0bf559", weight:7, opacity:0.6},  // ffff33 c6ff00 4cf61e 0bf559 35e56f
	cookie: '', //name of cookie used to track waypoinst '' means dont use ...
	ewaypointid: '',
	showen: true,
	units: '', //use sys default
	tracker: '',
	addOnClick: true,
	network:'aucun', //name of network used tu buid this router
	_lasttrack:'',
	
	// attribute functions function that are passed as unbound arguments
	initHoverContent: function(){
		//WARNING this methos is an attribute 'the function' is passed as an
		//attribute to MArker when created so this will be refering TO THE MARKER
		//NOT the router ...
		var marker= this
		var router= marker.router
		var pos= router.markerPos(marker)
		if (pos<0) return '' //marker not in route cant do anything
		var beforeAfter= router.getBeforeAfter(pos)
		var before=beforeAfter[0], after=beforeAfter[1], total=before+after
		var ret= [], mess='', ret2=[]
		var info={ pos:pos, before:unittxt(before,this.units), after:unittxt(after,this.units), total:unittxt(total,this.units) } 
		if (pos==0) mess= _('DirDebut',info)
		else if (pos == router.markers.length-1) mess= _('DirFin',info)
		else mess=_('Etape $pos',info)
		ret.push(mess)
		if (before > 0. ) ret.push(_('$before de fait',info))
		if (after > 0. )  ret.push(_('$after à faire',info))
		if (total >0. && after > 0. && before > 0.)  ret.push (_('$total au total',info))
		ret.push(_('DirFooter',info))
		dof(ret,function(i){ if (i) ret2.push(i)})
		ret= ret2.join('<br/>')
		return ret
		},

	markerdragend: function(){ //function holder this is the marker ....
		//this is the marker
		var marker= this
		marker.router.moveMarker(marker)
		},


	makeOvlFor: function(map){
		return this.jz_init(map)
		},

	jz_init: function(map,ops){
		this.map= map
		//var div= map.getContainer()
		update(this,ops)
		//if (this.tracker) alert('got tracker ='+this.tracker)
		this.markers= []
		if (!map) {
			return this
			}
		this._elSClick= GEvent.bind(map,'singlerightclick',this,this.dorightclick)
		this._elDBClick= GEvent.bind(map,'dblclick',this,this.dodblclick)

		var waypoints
		if (this.rwaypoints){
			waypoints=this.rwaypoints
			//alert('using rwaypoints='+waypoints)
			}
		else {
			if (this.waypoints){
				waypoints=ops.waypoints.split(',')
				waypoints=mapf(waypoints.length/2,
					function(x){ return [parseFloat(waypoints[2*x]),parseFloat(waypoints[(2*x)+1])]},
					[])
				}
			else if (this.ewaypoints) {
				if (typeof this.ewaypoints == 'string'){
				    waypoints= this.ewaypoints.replace('[','').replace(']','')
				    waypoints= mapf(waypoints.split(','),function(v){ return parseInt(v)},[])
				    }
				else waypoints= this.ewaypoints
				waypoints= this.wpe2wp(waypoints)
				}
			else if ( (waypoints=getId('waypoints')) && waypoints.jz_data) waypoints=waypoints.jz_data
			else if ( (waypoints=getId('ewaypoints')) && waypoints.jz_data) waypoints= this.wpe2wp(waypoints.jz_data)
			else waypoints= false

			if (!waypoints && this.tracker) waypoints= this.initFromTracker().waypoints

			if (!waypoints && this.cookie) waypoints= this.initFromCookie()

			if (waypoints){
				waypoints=mapf(waypoints,
					function(wp){ return newLatLng(wp[0],wp[1]) })
				}
			}
		map.router=this
		this.initWaypoints(waypoints)
		//use helpers instead ...
		//GEvent.bind(map,'click',this,this.doclick)
		if (map.jzControls['Directions'])
			dodirectionscontrolchange(map.jzControls['Directions'].div_,getId('DirectionsControl.check').checked,'router init')
		return this
		},

	markerPos: function(marker){ return indexOf(this.markers,marker) },

	doclick: function(ovl,latlng){
		//Remember latlng not set if ovl is ....
		if (!ovl && this.addOnClick && this.showen) return this.addMarker(latlng)
		},

	dorightclick: function(pt,src,ovl){
		var pos= this.markerPos(ovl)
		if (pos>-1) this.removeMarker(ovl)
		return
		},

	dodblclick: function(ovl,latlng){
		//alert('go right click latlng='+latlng+' ovl='+ovl)
		return
		},

// 	dopathupdate: function(marker){
// 		pos= indexOf(this.markers,marker)
// 		if (pos<0) return //should be exception
// 		this.pathUpdate(pos)
// 		},

	wpe2wp: function(wpe){
		var lat=0., lng=0., idx=0, res=100000
		if (wpe && wpe.length && wpe[0].constructor === Array)
			return mapf(wpe,function(ell){
				lat+=ell[0]
				lng+=ell[1]
				return [lat/res,lng/res] })
		else
			return mapf(wpe.length/2,function(i){
				idx=i*2
				lat+=wpe[idx]
				lng+=wpe[idx+1]
				return [lat/res,lng/res] })
		},

	moveMarker: function(marker){
		var pos= this.markerPos(marker)
		if (pos < 0) return //not found nothing to remove TODO: through error
		return this.movePos(pos)
		},

	movePos: function(pos){ return this.pathUpdate(pos,'moved') },

	addMarker: function(latlng,icon){
		return this.insertPos(this.markers.length,latlng,icon)
		},

	//startend interface
	addMarkerAt: function(pos,latlng,icon){ 
		var markers=this.markers
		if (!icon) icon=this.pauseIcon
		var newmarker= markers[pos]= newMarker(latlng,{
			icon:icon,
			hoverContent: this.initHoverContent,
			dragend   :   this.markerdragend
			})
		newmarker.router= this
		this.map.addOverlay(newmarker)
		if (!this.showen && newmarker.hide) newmarker.hide()
		},

	removedMarkerAt: function(pos,marker){ 
		if (!marker) return
		this.map.removeOverlay(marker)
		marker.router=false
		},

	insertPos: function(pos,latlng,icon){ //pos is before
		this.markers.splice(pos,0,false)
		this.addMarkerAt(pos,latlng,icon)
		this.pathUpdate(pos,'inserted')
		},

	//API 
	removeLastMarker: function(){
		if (!this.markers.length) return
		this.removePos(this.markers.length-1)
		},

	removeMarker: function(marker){
		var pos= this.markerPos(marker)
		if (pos < 0){
			alert('blaaaa')
			return //not found nothing to remove TODO: through error
			}
		return this.removePos(pos)
		},

	removePos: function(pos){
		var marker= this.markers.splice(pos,1)[0]
		this.removedMarkerAt(pos,marker)
		this.pathUpdate(pos,'removed') //attention si dernier pos=pos-1 ... TODO:
		},

	// API

	getBeforeAfter:  function(pos){
		var before=0, after=0, self=this
		dof(pos,function(i){ before+= self.getDistance(i) })
		dof(this.markers.length-pos-1,function(i){ after+= self.getDistance(pos+i) })
		return [before,after]
		},
	
	getDistance: function(pos){ //get distance to next Marker OR length of segment pos
		return mCalcDistance(this.markers[pos].getLatLng(),this.markers[pos+1].getLatLng())*1000.
		},

	getTotalDistance: function(){ //get distance to next Marker OR length of segment pos
		var dst=0, self=this
		dof(this.markers.length-1,function(pos){dst+=self.getDistance(pos)})
		return dst
		},

	initWaypoints: function(waypoints){
		this.pathUpdate(0,"initstart")
		if (waypoints){ 
		    var router=this
		    dof(waypoints,function(wp){router.addMarker(wp)})
		    }
		this.pathUpdate(this.markers.length,"initend")
		},

	reloadAll: function(){ //reload the path implemented by sons
		},
		

	pathUpdate: function(pos,action){
		// this is called in all circumstances insert,move and remove
		// action is moved inserted or removed
		//base class does except manage the cookie
		this.cookieUpdate()
		//alert('doing '+action+' '+pos)
		return this.pathUpdateEnd(pos,action)
		},

	pathUpdateEnd: function(pos,action){ },//defined by son's 

	cookieUpdate: function(){
		if (!(this.cookie || this.ewaypointid||this.tracker)) return
		var info=this.getCookieInfo()
		var txt=mapf(info,function(v,k){ return k+':'+v},[]).join(',') 
		//if (txt) 
		if (this.cookie) {
			setCookieForever(this.cookie,'{'+txt+'}')
			}

		//url box updated exactly in same conditions so done here
		var urlbox= getId('url-box-text')
		if (urlbox) urlbox.value= jz.getfullurl()

		//now done in sethtml
		//if (this.tracker) this.setTracker(this.tracker)
		},

	setTracker: function(tracker){
		if (!tracker) return
		var qdata= 'waypoints='+this.getWppTxt()
		qdata+= '&dist='+this.getTotalDistance()
		qdata+= '&network='+this.network
		var q=_tracksrv+'app/set/'+tracker+'?'+qdata
		//alert('settracker= '+q)

		if (this._lasttrack==q) return //dont repeat your self ...
		this._lasttrack=q

		if (this.trackersetreq){
		    this.trackersetreq.abort && this.trackersetreq.abort()
		    this.trackersetreq=false
		    }
		var req=this.trackersetreq= newXMLHttpRequest()
		//alert('doing q='+q)
		req.open('GET',q)
		var self=this
		req.onreadystatechange= function(){
		    if (req.readyState != 4) return //wait for end ...
		    if (!jz.IE) req.onreadystatechange= undefined //IE does not like this line dno why ...
		    self.trackersetreq=false
		    //TODO:rest _lasttrack if error
		    }
		req.send(null)
		},

	getTracker: function(tracker){
		if (!tracker) return
		var req=this.trackergetreq= newXMLHttpRequest()
		var q=_tracksrv+'app/get/'+tracker
		//alert('doing q='+q)
		req.open('GET',q,false) //make request synchronus
		var aborted= false
		//req.onreadystatechange= function(){
		//    if (req.readyState != 4) return //wait for end ...
		//   if (!jz.IE) req.onreadystatechange= undefined //IE does not like this line dno why ...
		//    response= req.responseText
		//    }
		var timer= setTimeout(function(){ req.abort && req.abort(); aborted=true},5000) 
		req.send(null)
		clearTimeout(timer)
		var trackerData= {}
		if (!aborted){
		    //alert('got tracker='+req.responseText)
		    trackerData=parseUrl('?'+req.responseText)
		    if (trackerData.waypoints){
			trackerData.waypointsData= trackerData.waypoints.replace(/\|/g,',')
			var waypoints=trackerData.waypoints.split('|')
			waypoints=mapf(waypoints, function(wptxt){ coords=wptxt.split(','); return [parseFloat(coords[0]),parseFloat(coords[1])]} )
			trackerData.waypoints=waypoints
			}
		    }
		return trackerData
		},


	getState: function(){
		var state={}
		if (this.tracker) state.tracker=this.tracker
		if (this.dirHelp) state.dirHelp= this.dirHelp
		state.rwaypoints=this.dumpWaypoints()
		return state
		},


	getCookieInfo: function(){
		var txt=this.getWpeTxt()
		if (!txt) return {}
		return { WpeTxt: '['+txt+']' }
		},

	readCookieInfo: function(){
		if (!this.cookie) return {}
		var txt=getCookie(this.cookie)
		if (txt && txt.charAt(0)=='[') //old cookie
		    txt='{ WpeTxt:'+txt+'}'
		//alert(' cookie text '+this.cookie+'= "'+txt+'"')
		var ret={}
		if (txt) eval('ret= '+txt) // TODO: parse the coockie small security risk (nothing valuable to protect)
		return ret	  // also not ret= added to force parsing of static dict {K:v ...} as object and not code ...
		},

	initFromCookie: function(){
		var rcookie=this.readCookieInfo()
		//var txt=mapf(rcookie,function(v,k){ return k+':'+(typeof v)+'='+v},[]).join(',') 
		//alert('init from cookie '+this.cookie+' = '+txt)
		var ewaypoints= rcookie.WpeTxt
		delete rcookie.WpeTxt
		update(this,rcookie)
		if (!ewaypoints) return false
		return this.wpe2wp(ewaypoints)
		},

	initFromTracker: function(){
		var tracker= this.getTracker(this.tracker)
		if (!tracker) return false
		var txt=mapf(tracker,function(v,k){ return k+':'+(typeof v)+'='+v},[]).join(',') 
		//alert('init from tracker '+this.tracker+' = '+txt)
		return tracker
		},

	show: function(){
		dof(this.markers,function(m){ m.show() })
		this.showen= true
		},

	hide: function(){
		dof(this.markers,function(m){ m.hide() })
		this.showen= false
		},

	reset:	function(){
		//var self= this, fRemoveMarkerAt= MRouterBase.prototype.removedMarkerAt
		//dof(this.markers,function(marker,pos){ fRemoveMarkerAt.call(self,pos,marker) })
		var map=this.map
		dof(this.markers,function(marker){ map.removeOverlay(marker); marker.router=false })
		this.markers=[]
		this.cookieUpdate()
		this.initWaypoints(this.markers)
		},

	clear:	function(){
		//remove router could be un jz_init
		this.reset()
		GEvent.removeListener(this._elSClick)
		GEvent.removeListener(this._elDBClick)
		},

	getWaypoints: function(){ return this.markers },
	getPoints:	function(){ return this.markers },

	getWpTxt: function(){
		return jz.mapf(this.getWaypoints(),function(marker){
			var latlng= marker.getLatLng()
			return '['+latlng.lat().toFixed(6)+','+latlng.lng().toFixed(6)+']'
			}).join(',')
		},

	getWppTxt: function(){
		return jz.mapf(this.getWaypoints(),function(marker){
			var latlng= marker.getLatLng()
			return latlng.lat().toFixed(6)+','+latlng.lng().toFixed(6)
			}).join('|')
		},

	getWpeTxt: function(sep){
		var olat=0, olng=0, res=100000.
		if (!sep) sep=','
		return jz.mapf(this.getWaypoints(),function(marker){
			var ll= marker.getLatLng()
			var lat=Math.round(res*ll.lat()), lng=Math.round(res*ll.lng())
			var xlat=lat-olat, xlng=lng-olng
			olat+=xlat
			olng+=xlng
			return xlat+','+xlng
			}).join(sep)
		},

	dumpWaypoints: function(){
		return mapf(this.getWaypoints(),function(marker){ return marker.getLatLng() })
		},

	dumppts: function(){ 
		return mapf(this.getPoints(),function(marker){ return marker.getLatLng() })
		},

	dumpsegments: function(){
		var wpts= this.dumpWaypoints()
		// view each line between 2 way points as a segment
		//var ret=mapf(wpts.length-1,function(idx){ return [wpts[idx],wpts[idx+1]] },[])
		//or
		// view all waypoints as a single segment
		var ret=[ this.dumppts() ]
		return ret
		},

	dumpsegmentstrs: function(){
		var ret= mapf(this.dumpsegments(),function(vect){ return pts2es(vect)})
		return ret
		},

	dumpstrs: function(){
		var wptxt= this.getWpeTxt(',')
		var segs=  this.dumpsegmentstrs()
		//alert('dump str wptxt='+wptxt)end
		return  wptxt+'|'+segs.join('|')
		}
	})


$.StartEndManager =Class(MRouterBase,{
	startIcon: StartIcon,
	endIcon:   EndIcon,

	addMarkerAt: function(pos,latlng,icon){
		if (!icon){ //no icon maybe we have a better default
		    var markers=this.markers, mlen=markers.length
		    if (pos==0) { 
			icon= this.startIcon
			//we are going to add a start icon should we remove one?
			if (mlen>1 && markers[1].getIcon()==this.startIcon)
			    this.changeMarkerTo(1,this.pauseIcon)
			}
		    else if (pos==mlen-1){
			icon= this.endIcon
			//we are going to add a end icon should we remove one?
			if (mlen>1 && markers[mlen-2].getIcon()==this.endIcon)
			    this.changeMarkerTo(mlen-2,(mlen-2<=0)?this.startIcon:this.pauseIcon)
			}
		    }
		StartEndManager.jz_superP.addMarkerAt.call(this,pos,latlng,icon)
		},

	removedMarkerAt: function(pos,marker){
		if (marker){ //no icon maybe we have a better default
		    icon= marker.getIcon()
		    if (pos==0 && icon==this.startIcon) { //or pos==0 removing start
			
			//we are removing starticon sgould we readd it ?
			if (this.markers.length>0 && this.markers[0].getIcon()!=this.endIcon)
			    this.changeMarkerTo(0,this.startIcon)
			}
		    else if (pos==this.markers.length && icon==this.endIcon){ // or pos=this.markers.length
			//we are going to remove a end icon should we add one?
			if (this.markers.length>1 && this.markers[this.markers.length-1].getIcon()!=this.endIcon)
			    this.changeMarkerTo(this.markers.length-1,this.endIcon)
			}
		    }
		StartEndManager.jz_superP.removedMarkerAt.call(this,pos,marker)
		},


	changeMarkerTo: function(pos,icon){
		var markers=this.markers
		var oldmarker= markers[pos]
		if (!oldmarker) {alert('startend error'); return }

		if (oldmarker.getIcon()==icon) return //no need to change it ...
		latlng=oldmarker.getLatLng()
		markers[pos]= false
		this.removedMarkerAt(pos,oldmarker)
		this.addMarkerAt(pos,latlng,icon)
		}

	})


$.HelperManager= Class(StartEndManager,{ //Class(Object,{ //This is a plug-in to augment routers ....

	helperHoverContent: _('directions aide marqeur'),
	startIcon: StartIcon,
	endIcon:   EndIcon,
	offset: [10,-30],

	clearHelper: function(marker){
		if (marker.moveendlistener) { //stop tracking
			GEvent.removeListener(marker.moveendlistener)
			marker.screenpos= marker.moveendlistener= false
			}
		if (marker.movelistener) { //stop tracking
			GEvent.removeListener(marker.movelistener)
			marker.screenpos= marker.movelistener= false
			}
		this.map.removeOverlay(marker)
		marker.router= false
		},

	helperdragend: function(){//function holder this is the marker ....
		//this is the marker
		var marker= this, router=marker.router, map=router.map
		var latlng= marker.getLatLng()
		router.clearHelper(marker)
		if (marker === router.startHelper){
			router.startHelper= false
			router.insertPos(0,latlng)
			}
		else if (marker === router.endHelper){
			router.endHelper= false
			router.addMarker(latlng,router.endIcon)
			}
		},


	makeHelper: function(icon,pos){//add initial start stop icons
		var offset= this.offset
		var iwidth= this.startIcon && this.startIcon.size && this.startIcon.size.width || 20
		var map= this.map
		var div= map.getContainer()
		//var dcw= div.clientWidth, dch= div.clientHeight
		var dow= div.offsetWidth
		var doh= div.offsetHeight //you asked for it ...
		var dcw=dow, dch=doh		//alert('In make helper')
		
		if (offset[0]<0)	offset[0]= max(0,offset[0]+dcw-iwidth)
		else if (offset[0]<1)	offset[0]= max(0,offset[0]*dcw-iwidth)
		else			offset[0]= min(dcw,offset[0])

		if (offset[1]<0)	offset[1]= max(0,offset[1]+dch)
		else if (offset[1]<1)	offset[1]= max(0,offset[1]*dch)
		else			offset[1]= min(dch,offset[1])
		
		//alert('offset='+offset+' iwidth='+iwidth+' dcw='+dcw+' dch='+dch)
		
		var helperoffset= new GPoint(offset[0]+(iwidth*pos),offset[1])
		var helperpos= map.fromContainerPixelToLatLng(helperoffset)

		var helper= newMarker(helperpos,{
				icon:icon,
				hoverContent: this.helperHoverContent,
				dragend   :   this.helperdragend
				})
		update(helper,{
			isstart: icon==this.startIcon,
			screenpos: helperoffset,
			router: this,
			moveendlistener: GEvent.addListener(map,'moveend',function(){ movetoscrn(helper,map) }),
			movelistener: GEvent.addListener(map,'move',function(){ movetoscrn(helper,map) })
			})
		map.addOverlay(helper)
		if (!this.showen && helper.hide) helper.hide()
		return helper
		},

	addStartHelper: function(){ return this.startHelper= this.makeHelper(this.startIcon,0) },

	addEndHelper:  function(){ return this.endHelper=   this.makeHelper(this.endIcon,1) },
	
	//insertion points ...

	pathUpdateEnd: function(pos,mode){
		// Check if end have changed ....
		HelperManager.jz_superP.pathUpdateEnd.call(this,pos,mode)
		
		var markers=this.markers, ml=markers.length
		if (pos<2){//change occured at begging
		    //checkfor remove start
		    if (this.startHelper && (ml && markers[0].getIcon()==this.startIcon)){
			this.clearHelper(this.startHelper)
			this.startHelper=false
			}
		    else if (!this.startHelper && (!ml || markers[0].getIcon()!=this.startIcon)){
			this.addStartHelper()
			}
		    }
		if (pos>ml-3){//change at end check end
		    if (this.endHelper && ( ml && markers[ml-1].getIcon()==this.endIcon)){
			this.clearHelper(this.endHelper)
			this.endHelper=false
			}
		    else if (!this.endHelper && (!ml || markers[ml-1].getIcon()!=this.endIcon)){
			this.addEndHelper()
			}
		    }
		},

	/*
	 initWaypoints: function(waypoints){//add initial start stop icons
		HelperManager.jz_superP.initWaypoints.call(this,waypoints)
		var wplen= waypoints?waypoints.length:0
		if (wplen<1) this.addStartHelper()
		if (wplen<2) this.addEndHelper()
		},

	reset: function(){
		HelperManager.jz_superP.reset.call(this)
		if (!this.startHelper)  this.addStartHelper()
		if (!this.endHelper)	this.addEndHelper()
		},
	*/

	show: function(){
		HelperManager.jz_superP.show.call(this)
		if (this.startHelper) this.startHelper.show()
		if (this.endHelper) this.endHelper.show()
		},

	hide: function(){
		HelperManager.jz_superP.hide.call(this)
		if (this.startHelper) this.startHelper.hide()
		if (this.endHelper) this.endHelper.hide()
		},

	clear: function(){
		HelperManager.jz_superP.clear.call(this)
		if (this.startHelper) this.clearHelper(this.startHelper)
		if (this.endHelper) this.clearHelper(this.endHelper)
		},

	getState: function(){
		var state= HelperManager.jz_superP.getState.call(this)
		if (this.offset !== HelperManager.prototype.offset) state.offset= this.offset
		return state
		}
	})


$.SegRouter= Class(HelperManager,{
	//Segment based Router
	segments: false,
	html: false, // array of html fragments
	totaldist: 0, //Becerful managed by setHTML
	totaltime: 0, //same
	dirDivName: 'directions', //name of div that will contain html directions
	dirDiv: false,
	htmlfooter: '', //footer of directions
	showdetails: true, //Show route details


	jz_init: function(map,ops){
		this.segments= []
		this.html= []
		if (this.dirDivName){
			this.dirDiv= getId(this.dirDivName)
			if (this.dirDiv  && !ops.dirHelp) this.dirHelp= this.dirDiv.innerHTML //rember help text
			}
		SegRouter.jz_superP.jz_init.call(this,map,ops)
		return this
		},

	doSegmentClick: function(seg,latlng){
		var pos= indexOf(this.segments,seg)
		//alert('segclick got pos='+pos+' at latlng '+latlng)
		if (pos<0) return //unkown seg to cuase exception
		this.insertPos(pos+1,latlng)
		},

	reset: function(){
		var map= this.map
		dof(this.segments,function(seg){map.removeOverlay(seg)})
		this.segments= []
		this.html= []
		this.totaldist= 0
		this.totaltime= 0
		if (this.dirDiv && this.dirHelp) this.dirDiv.innerHTML= this.dirHelp
		SegRouter.jz_superP.reset.call(this)
		},
		
	reloadAll: function() {
		return this.reloadAllSegments()
		},

	reloadAllSegments: function(){
		var self=this
		dof(this.segments,function(v,pos){
		    //alert('update seg'+pos)
		    self.updateSeg(pos)
		    })
		},

	pathUpdateEnd: function(pos,action){
		// this is called in all circumstances insert,move and remove
		// action is moved inserted or removed
		SegRouter.jz_superP.pathUpdateEnd.call(this,pos,action)
		if (action=='inserted' && this.markers.length>1) this.insertSeg(pos)
		else if (action=='removed' && this.segments.length){
			if (pos<this.segments.length)
				 this.delSeg(pos)
			else this.delSeg(pos-1) 
			}
		if (this.markers.length && this.segments.length >= this.markers.length) alert(' lens eq ='+this.segments.length)
		if (pos >=1 && pos<=this.segments.length) this.updateSeg(pos-1)
		if (pos<this.segments.length) this.updateSeg(pos)
		// Check for help instead of directions
		this.setHtml()
		//if (action=='initend') alert('init end')
		},

	updateSeg: function(pos){
		var start= this.markers[pos].getLatLng()
		if (pos>=this.markers.length) alert('oups pos='+pos+' ml='+this.markers.length)
		var end= this.markers[pos+1].getLatLng()
		this.clearSeg(pos)
		var dst= jzGMap.mCalcDistance(start,end)*1000.
		var routeinfo= {
			summary: '',
			dist:  { meters: dst},
			time:  { seconds: dst/(50000./3600.)},
			steps:[{
			    html: unittxt(dst,this.units)+' from '+start.lat().toFixed(6)+','+start.lng().toFixed(6)+' to '+end.lat().toFixed(6)+','+end.lng().toFixed(6),
			    dist: { meters:dst} ,
			    time:  { seconds: dst/(50000./3600.)}
			    }]
			//otherhtml:html]
			//otherhtml:html
			}

		//this.clearSeg(pos) done in updateSeg

		this.setSeg(pos,[start,end],false,routeinfo)
		this.setHtml()
		//if (this.tracker) this.setTracker(this.tracker)
		},

	setSeg: function(pos,points,segPoly,html){
		this.html[pos]= html || ''
		if (!segPoly) segPoly= newPolyline(points,this.routeStyle)
		else update(segPoly,this.routeStyle)
		segPoly.clickable= true
		//segPoly.enableEditing()
		var thisrouter= this
		this.segments[pos]= segPoly
		this.map.addOverlay(segPoly)
		GEvent.addListener(segPoly,'click',function(latlng){ thisrouter.doSegmentClick(this,latlng) })
		if (!this.showen && segPoly.hide) segPoly.hide()
		},

	clearSeg: function(pos){
		this.html[pos]= ''
		var segPoly= this.segments[pos]
		if (segPoly) this.map.removeOverlay(segPoly)
		//TODO: remove listeners
		this.segments[pos]= false
		},

	insertSeg: function(pos){
		this.html.splice(pos,0,'')
		this.segments.splice(pos,0,false)
		},

	delSeg: function(pos){
		this.clearSeg(pos)
		this.segments.splice(pos,1)
		this.html.splice(pos,1)
		},

	getDistance: function(pos){ //get distance to next Marker OR length of segment pos
		var seg= this.segments[pos]
		if (seg) return seg.getLength()
		//OK no seg probably waiting for the ajax transaction ...
		//Anser 0 or NaN ... 0 for now ...
		return 0
		},

	getTotalDistance: function(){ return this.totaldist }, //managed by sethtml

	dumppts: function(){
		var lastpt=false, pts=[]
		dof(this.segments,function(seg){
			dof(seg.getVertexCount(),function(i){
				var pt= seg.getVertex(i)
				if (!lastpt || !( lastpt.lat()==pt.lat() && lastpt.lng()==pt.lng() )) pts.push(pt)
				lastpt= pt
				})
			})
		return pts
		},

	dumpsegments: function(){
		var ret= mapf(this.segments,function(seg){
			if (!seg) return []
			return mapf(seg.getVertexCount(),function(idx){ return seg.getVertex(idx)},[])
			})
		//alert(' dump seg segments='+ret)
		return ret
		},

	show: function(){
		dof(this.segments,function(seg){ if (seg.show) seg.show() })
		return SegRouter.jz_superP.show.call(this) // goto super cls ...
		},

	hide: function(){
		dof(this.segments,function(seg){ if (seg.hide) seg.hide() })
		return SegRouter.jz_superP.hide.call(this) // goto super cls ...
		},

	makeRouteSummary: function(vars){
		var state= vars.state || 'set'
		vars.etat= (state != 'set') ? '('+state+')':''
//		return span({cls:"dir-troncon-txt"}, _("Étape $i: $dist (environ $durt) par $mode $etat",vars))
		return span({cls:"dir-troncon-txt"}, _("Étape $i: $dist (environ $durt) ",vars))
		},

	makeStepSummary: function(vars){
		var step= vars.step
		var units= vars.units
		return li({cls:"dir-etape"},span({cls:"dir-etape-txt"},
			    _("$postxt $stephtml, pour $disthtml ( $timehtml )",{
				disthtml:unittxt(step.dist.meters,units),
				timehtml:timetxt(step.time.seconds),
				stephtml:step.html,
				postxt:unittxt(vars.cpos,units)
				})
			    ))
		},

	setHtml: function(){
		//if (!this.dirDiv) return //ok no directions we are done ..
		
		var html= this.html
		var pos= 0.0
		var time= 0.0
		if (html.length<1){
		    if (this.dirDiv) this.dirDiv.innerHTML= this.dirHelp || ''
		    this.totaldist=pos
		    this.totaltime=time
		    if (this.tracker) this.setTracker(this.tracker)
		    return
		    }

		var self= this
		var showdetails= this.showdetails

		var routes=mapf(html,function(routeinfo,i){
			var startPos=pos, startTime= time

			var dist='', durt=''
			if (routeinfo.dist){
				dist=unittxt(routeinfo.dist.meters,self.units)
				durt=timetxt(routeinfo.time.seconds)}

			var routeSummary= self.makeRouteSummary({i:i+1, dist:dist, durt:durt})
				
			var steps= mapf(routeinfo.steps,function(step,j){
				var stephtml= self.makeStepSummary({step:step, idx:j, units:self.units, cpos:pos, ctime:time})
				pos+= step.dist.meters
				time+= step.time.seconds
				return stephtml
				})

			if (!steps) steps= []
			var end
			if (i == html.length-1)
				 end= _("Arrivée à la fin du trajet")
			else end= _("Arrivée à l'étape $i",{i:i+1})
			steps.push( li({cls:"dir-etape"},span({cls:"dir-etape-txt"},unittxt(pos,this.units)+'  '+end))  )
			if (html.length>1)
				 return li({cls:"dir-troncon"},routeSummary+( showdetails && ul({cls:'dir-troncon'},steps.join('')) || ''))
			else return showdetails && steps.join('') || ''
			})

		var summary
		if (this.htmlsummary) summary=this.htmlsummary
		else { 
			var distxt= unittxt(pos,this.units)
			var tempstxt= timetxt(time)
			var mn= Math.round(time/60.)%60
			summary= _('<b>Distance $dist duré environ $temps </b>', {dist:distxt, temps:tempstxt})
			}

		if (html.length>1)
		    var allhtml= summary+ul({cls:'dir-list-troncons'},routes.join(''))+this.htmlfooter
		else
		    var allhtml= summary+ul({cls:'dir-troncon'},routes.join(''))+this.htmlfooter
		if (this.dirDiv) this.dirDiv.innerHTML= allhtml
		this.totaldist=pos
		this.totaltime=time
		if (this.tracker) this.setTracker(this.tracker)
		}

	//makeHelper: function(icon,pos){
	//	var ret= SegRouter.jz_superP.makeHelper.call(this,icon,pos)
	//	if (this.dirDiv && this.dirHelp) this.dirDiv.innerHTML= this.dirHelp
	//	return ret
	//	}

	})


$.GSegRouter= Class(SegRouter,{
	requests: false, //
	cookie: 'googleroute',
	dirDivName: 'directions', //name of div that will contain html directions
	reqdefops:  {
		getPolyline:true,
		getSteps:true,
		preserveViewport:true,
		locale: _('locale'),
		avoidHighways: false
		},

	getreqdefops: function(){ return this.reqdefops },

	jz_init: function(map,ops){
		this.requests= []
		GSegRouter.jz_superP.jz_init.call(this,map,ops) //super method
		return this
		},
	
	reset: function(){
		dof(this.requests,function(req){ req.clear() })
		this.requests= []
		GSegRouter.jz_superP.reset.call(this) //super method
		},

	clearSeg: function(pos){
		var req= this.requests[pos]
		if (req) req.clear()
		GSegRouter.jz_superP.clearSeg.call(this,pos) //super method
		},

	insertSeg: function(pos){
		this.requests.splice(pos,0,false)
		GSegRouter.jz_superP.insertSeg.call(this,pos) //super method
		},

	delSeg: function(pos){
		GSegRouter.jz_superP.delSeg.call(this,pos) //super method
		this.requests.splice(pos,1)
		},

	getGDirObj: function(){ return new GDirections() },

	updateSeg: function(pos){
		this.clearSeg(pos)
		var req= this.requests[pos]
		var start= this.markers[pos].getLatLng()
		if (pos>=this.markers.length) alert('oups pos='+pos+' ml='+this.markers.length)
		if (!this.markers[pos+1]) alert('oups2 pos='+pos+' ml='+this.markers.length+' mpos+1='+this.markers[pos+1])
		var end= this.markers[pos+1].getLatLng()

		if (!req) {
			req= this.requests[pos]= this.getGDirObj()
			GEvent.addListener(req,'load',GEvent.callbackArgs(this,this.doGDirLoad,req))
			GEvent.addListener(req,'error',GEvent.callbackArgs(this,this.doGDirError,req))
			}
		else { //do abort 
		    }
		var query= req.jz_query= [ ''+start.lat()+','+start.lng(), ''+end.lat()+','+end.lng() ]
		var ops= req.jz_ops= this.getreqdefops()
		req.loadFromWaypoints( query,ops) //dirflg:'h', avoidHighways:true, avoidHighway:true, avhwy:true, ddopt_avhwy:true})
		},

	doGDirLoad: function(gdir){
		var pos= indexOf(this.requests,gdir)
		if (pos<0) return //could through error ...

		//Be carfull cant clear becuase that wil reste the gdir
		var segPoly= gdir.getPolyline()
		var segSummary= gdir.getSummaryHtml() //if one route same as step summary
		if (gdir.getNumRoutes() != 1) {
			alert('gdir numroutes='+gdir.getNumRoutes()+' expected 1')
			return
			}
		var route= gdir.getRoute(0)
		var routeinfo= {
			summary: route.getSummaryHtml(),
			dist: route.getDistance(),
			time: route.getDuration(),
			steps: mapf(route.getNumSteps(), function(j){
				var step=route.getStep(j)
				return  { html: step.getDescriptionHtml(),
					dist: step.getDistance(),
					time: step.getDuration()}
				})
			}

		//this.clearSeg(pos) done in updateSeg
		this.setSeg(pos,false,segPoly,routeinfo)
		this.setHtml()
		//if (this.tracker) this.setTracker(this.tracker)
		},

	doGDirError: function(gdir){ //this will be the GDirectios object
		//alert('in google err')
		var statusinfo= gdir.getStatus()
		if (statusinfo.code == 620){
			//ok lets waut a while and try again
			setTimeout(function(){gdir.loadFromWaypoints( gdir.jz_query, gdir.jz_ops)},500+(Math.random()*2000))
			}
		else alert('Google error stat='+statusinfo.code+' '+statusinfo.request)
		}
	})


$.GSegRouterVelo= Class(GSegRouter,{
	cookie: 'googleroutevelo',
	reqdefops:  {
		getPolyline:true,
		getSteps:true,
		preserveViewport:true,
		locale: _('locale'),
		avoidHighways: true
		},

	getreqdefops:	function(){
		var ops=this.reqdefops
		ops['travelMode']= G_TRAVEL_MODE_WALKING //gmap may not be loaded when code loades
		return ops
		},

	getGDirObj: function(){ return new GDirections(undefined,this._dummydiv) },

	jz_init: function(map,ops){
		var _dummydiv= document.createElement('div')
		_dummydiv.style.position="absolute"
		_dummydiv.className='jz-hidden'
		document.body.appendChild(_dummydiv)
		this._dummydiv=_dummydiv
		GSegRouterVelo.jz_superP.jz_init.call(this,map,ops) //super method
		//alert(' v4 created '+_dummydiv+' gt='+G_TRAVEL_MODE_WALKING)
		return this
		}


	})


$.RSegRouter= Class(SegRouter,{
	requests: false, //
	cookie: 'motoneigeroute',
	network: 'def-network',
	routeStyle: {color: "#ffff00", weight:7, opacity:0.6},
	cost: '',
	showdetails: false, //Show route details
	
	jz_init: function(map,ops){
		if (!ops.cost){
		  var cost=parseUrl()['cst']
		  if (cost) ops['cost']=cost
		  }
		//alert('creating RSegRouter ops.cost='+ops.cost)
		this.requests= []
		//jz.superClsP(GSegRouter).jz_init.call(this,map,ops)
		RSegRouter.jz_superP.jz_init.call(this,map,ops) //super method
		doInitCostUI(this)
		return this
		},

	reset: function(){
		var self=this
		dof(this.requests,function(req){ self.clearReq(req)}) //works cause clear req does no reffer to this ..
		this.requests= []
		delete this.cost
		RSegRouter.jz_superP.reset.call(this) //super method
		},
	
	setInfo: function(info){
		update(this,info)
		this.cookieUpdate()
		this.reloadAll()
		},

	getCookieInfo: function(){
		var info=RSegRouter.jz_superP.getCookieInfo.call(this) //super method
		if (this.cost) info.cost= '"'+this.cost+'"'
		return info
		},

	clearReq: function(req){
		if (!req) return
		req.abort && req.abort()
		req._jz_router= req.onreadystatchange= undefined //help gc
		},

	clearSeg: function(pos){
		var req= this.requests[pos]
		if (req) this.clearReq(req)
		this.requests[pos]= false
		RSegRouter.jz_superP.clearSeg.call(this,pos) //super method
		},

	insertSeg: function(pos){
		this.requests.splice(pos,0,false)
		RSegRouter.jz_superP.insertSeg.call(this,pos) //super method
		},

	delSeg: function(pos){
		RSegRouter.jz_superP.delSeg.call(this,pos) //super method
		this.requests.splice(pos,1)
		},

	updateSeg: function(pos){
		this.clearSeg(pos)
		var start= this.markers[pos].getLatLng()
		var end= this.markers[pos+1].getLatLng()

		var query= _routersrv+_('app/findroute2?network=$network&start=$start&end=$end&cost=$cost',
			{network:this.network, start:start, end:end, cost:this.cost})

		//alert('rseg router doing query=\n'+query)
		var req= this.requests[pos]= newXMLHttpRequest()
		req.open('GET',query)
		req._jz_router= this
		//req._jz_pos= pos
		req.onreadystatechange= function(){
			if (req.readyState != 4) return //wait for end ...
			if (!req._jz_router) return //cause not much to do ...
			if (req.status == 200) req._jz_router.doUpdateSegReq(req)
			else alert('status ='+req.status+' '+req.statusText)
			if (!jz.IE) req.onreadystatechange= undefined //IE does not like this line dno why ...
			req._jz_router.requests[pos]= undefined
			req._jz_router=  undefined
			}
		req.send(null)
		},

	getStepInfoFromResp: function(stepnum,resp){
		var stepid= 'step_'+stepnum
		var dm= parseFloat(innerText(stepid+'.dist',resp))
		var ds= dm/(50000./3600.) // metre/(kmh/60min*60sec) 
		return {
			html: innerHTML(stepid+'.deschtml',resp),
			dist: { meters:dm},
			time: { seconds:ds},
			nomrue: innerText(stepid+'.nomrue',resp)
			}
		},
	    

	doUpdateSegReq: function(req){
		var pos= indexOf(this.requests,req)
		if (pos<0) return //could through error ...
		var resp= req.responseXML
		
		var pts=  innerText('epoints',resp)
		var lvls= innerText('lvls',resp)
		var numsteps= parseInt(innerText('numsteps',resp))
		var distance= parseFloat(innerText('distance',resp))
		if (!pts || !lvls) return false

		var self=this
		var steps= mapf(numsteps,function(stepnum){ return self.getStepInfoFromResp(stepnum,resp)})
		var html= innerHTML('tpath',resp)
		var debug= innerHTML('debug',resp)
		if (debug) html+=debug
		
		var routeinfo= {
			summary: '',
			dist:  { meters: distance},
			time:  { seconds: distance/(50000./3600.)},
			steps:steps,
			otherhtml:html
			}

		this.setSeg(pos, false, newEPolyline(pts,lvls,this.routeStyle),routeinfo)
		this.setHtml()
		//if (this.tracker) this.setTracker(this.tracker)
		},

	makeStepSummary: function(vars){
		var step= vars.step
		var units= vars.units
		return li({cls:"dir-etape"},span({cls:"dir-etape-txt"},
			    _("A $postxt prendre <b>$nomrue</b> pour $distxt ($timetxt)",{
				distxt:unittxt(step.dist.meters,units),
				timetxt:timetxt(step.time.seconds),
				nomrue:step.nomrue,
				postxt:unittxt(vars.cpos,units)
				})
			    ))
		}

	})

$._defSpeed= 50000.
$._speedKeys= ['T','R','L']
$.PMNSegRouter= Class(RSegRouter,{
	showdetails: true, //Show route details

	cost: '1.0,1.01,1.05',
	
	speeds: { T:_defSpeed, R:_defSpeed, L:_defSpeed },

	jz_init: function(map,ops){
		var speeds=ops.speeds
		if (!speeds) speeds=parseUrl()['spd']
		if (typeof speeds == 'string' && speeds) ops.speeds=this._parseSpeeds(speeds)
		var ret=PMNSegRouter.jz_superP.jz_init.call(this,map,ops) //super method
		},

	reset: function(){
		delete this.speeds

		if (this.dirDiv && getId('tq-pct'))
		    dof(['tq','reg','local'],function(name){
			var elemp=getId(name+'-pct')
			if (elemp) elemp.innerHTML= ''

			var elemd=getId(name+'-km')
			if (elemd) elemd.innerHTML= ''
			})
		
		PMNSegRouter.jz_superP.reset.call(this) //super method
		},

	getCookieInfo: function(){
		var info=PMNSegRouter.jz_superP.getCookieInfo.call(this) //super method
		if (this.speeds && this.speeds != PMNSegRouter.jz_superP.speeds)
		    var speeds=this.speeds
		    info.speeds= '"'+mapf(_speedKeys,function(v){ return speeds[v]}).join(',')+'"'
		return info
		},

	readCookieInfo: function(){
		var cookieinfo= PMNSegRouter.jz_superP.readCookieInfo.call(this)
		if (cookieinfo.speeds) cookieinfo.speeds= this._parseSpeeds(cookieinfo.speeds)
		return cookieinfo  // also not ret= added to force parsing of static dict {K:v ...} as object and not code ...
		},

	_parseSpeeds: function(spdstr){
		var spd={}
		dof(spdstr.split(','),function(v,k){ if (parseFloat(v)) spd[_speedKeys[k]]=parseFloat(v) })
		return spd
		},

	getStepInfoFromResp: function(stepnum,resp){
		var stepid= 'step_'+stepnum
		var dm= parseFloat(innerText(stepid+'.dist',resp))
		var nomrue=innerText(stepid+'.nomrue',resp)
		if (nomrue && nomrue.charAt(0) in this.speeds){
		    var ds= dm/(this.speeds[nomrue.charAt(0)]/3600.)// metre/(mh/60min*60sec
		    //alert('speed did ds='+ds+' was='+dm/(this.speeds[nomrue[0]]/3600.)+' nomrue='+nomrue)
		    }
		else {
		    var ds= dm/(_defSpeed/3600.)// metre/(mh/60min*60sec)
		    //alert('no speed nomrue='+nomrue) // Vlocal = traversiers
		    }
		return {
			html: innerHTML(stepid+'.deschtml',resp),
			dist: { meters:dm},
			time: { seconds:ds},
			nomrue:nomrue
			}
		},



	setHtml: function(){
		PMNSegRouter.jz_superP.setHtml.call(this) // super method

		if (!this.dirDiv || !getId('tq-pct')) return

		var tq= 0
		var reg= 0
		var local= 0
		var total= 0

		dof(this.html,function(routeinfo,i){
			dof(routeinfo.steps,function(step,j){
				var d=step.dist.meters
				total+= d
				if (step.nomrue){
				    switch (step.nomrue.charAt(0)){
					case 'T': tq+= d; break
					case 'R': reg+= d; break
					case 'L': local+= d; break
					}
				    }
				})
			})
		//alert('ops unpdate got total='+total+' tq='+tq+' reg='+reg+' local='+local)
		dof([['tq',tq],['reg',reg],['local',local]],function(info){
		    var name=info[0],dst=info[1]

		    var elemp=getId(name+'-pct')
		    if (elemp) elemp.innerHTML= Math.round(100*dst/total)+'%'

		    var elemd=getId(name+'-km')
		    if (elemd) elemd.innerHTML= unittxt(dst,this.units)
		    })
		}

    })



$.PolyRouter= Class(MRouterBase,{
	//Polyline based router
	//Line is a poly that spreads througn multiple waypoints
	poly: false,
	delayable: true,
	delayed: false,

	jz_init: function(map,ops){
		this.segments= false
		jz.superClsP(PolyRouter).jz_init.call(this,map,ops)
		return this
		},

	doPolyClick: function(seg,latlng){
		var pos= indexBefore(this.dumpWaypoints(),latlng,0.01)
		this.insertPos(pos,latlng)
		},

	initWaypoints: function(waypoints){
		if (!this.delayable) return jz.superClsP(PolyRouter).initWaypoints.call(this,waypoints)
		if (!waypoints) return

		this.delayed=true
		this.delayedaction=false
		this.delayedpos=false
		var router=this
		dof(waypoints,function(wp){router.addMarker(wp)})
		this.delayed=false
		if (this.delayedaction){
		    this.pathUpdate(this.delayedaction,this.delayedpos)
		    this.delayedaction= false
		    this.delayedpos= false
		    }
		},

	pathUpdate: function(pos,action){
		if (this.delayed) {
		    this.delayedaction= action
		    this.delayedpos= pos
		    return}

		this.cookieUpdate()
		return this.pathUpdateEnd(pos,action)
		},

	pathUpdateEnd: function(pos,action){
		// this is called in all circumstances insert,move and remove
		// action is moved inserted or removed

		//PolyRouter.jz_superP.pathUpdateEnd.call(this,pos,mode)

		this.clearPoly()
		if (this.markers.length<2) return //need atleast 2 points to draw a line ... bye
		this.setPoly(mapf(this.getPoints(),function(pt){return pt.getLatLng()})) //was:this.dumpWaypoints())
		},

	setPoly: function(points,poly){
		if (!poly) poly= newPolyline(points,this.routeStyle)
		else update(poly,this.routeStyle)
		poly.clickable= true
		var thisrouter= this
		GEvent.addListener(poly,'click',function(latlng){ thisrouter.doPolyClick(this,latlng) })
		this.poly= poly
		this.map.addOverlay(poly)
		if (!this.showen && poly.hide) poly.hide()
		},

	clearPoly: function(){
		if (this.poly) this.map.removeOverlay(this.poly)
		this.poly= false
		},

	show: function(){
		var poly= this.poly
		if (poly && poly.show) poly.show()
		return PolyRouter.jz_superP.show.call(this) // goto super cls ...
		},

	hide: function(){
		var poly=this.poly
		if (poly && poly.hide) poly.hide()
		return PolyRouter.jz_superP.hide.call(this) // goto super cls ...
		},

	dumppts: function(){
		var poly=this.poly
		if (!poly) return []
		return mapf(poly.getVertexCount(),function(i){ return poly.getVertex(i)})
		}

	})
$.MoveableManagedOverlay= Class(ManagedOverlay,{
	
	cached:		true,

	getLatLng: function(){ 
		if (this.ovl) return this.ovl.getLatLng()
		return this.getCenter()
		}
	})


$.BigPoly= Class(PolyRouter,{

	//use Overlay Manager for markers

	minZoom: 15,
	degsize: 0.01,

	jz_init: function(map,ops){
		this.ovlmanager= ManagedOverlayContainer()
		jz.superClsP(BigPoly).jz_init.call(this,map,ops)
		map.mapDiv.overlayManager.addOvl(this.ovlmanager)
		return this
		},

	//startend interface
	addMarkerAt: function(pos,latlng,icon){ 
		var lat=latlng.lat(),lng=latlng.lng(),siz=this.degsize
		if (!icon) icon=this.pauseIcon
		var managedovl= MoveableManagedOverlay(newLatLng(lat-siz,lng-siz),newLatLng(lat+siz,lng+siz),{
			router: this,
			makeovlfnc: newMarker,
			minZoom: this.minZoom,
			makeovlargs: [
			    latlng, //pos
			    {   icon:icon,
				//hoverContent: this.initHoverContent,
				dragend   :   this.markerdragend
				}]
			})

		//setup circuler ref
		managedovl.ovlattribs=  {
		    router:this,
		    managedovl:managedovl //circulare ref }
		    }
		this.markers[pos]= managedovl
		this.ovlmanager.addOvl(managedovl)
		if (!this.delayed) doMoveEnd(this.map.mapDiv,'force')
		},

	removedMarkerAt: function(pos,marker){ 
		if (!marker) return
		marker.router=false
		marker= this.ovlmanager.removeOvl(marker)
		if (marker && marker.ovl && marker.state=="show") this.map.removeOverlay(marker.ovl)
		},

	markerPos: function(marker){
		if (!marker.managedovl) return -1
		return indexOf(this.markers,marker.managedovl)
		}

    })


$._temp={

	markerdragend2: function(){ //function holder this is the marker ....
		//this is the marker
		var marker= this.managedovl
		//alert('DRAG END MARKER='+marker+' router='+marker.router)
		//move the proxy
		marker.router.moveMarker(marker)
		},

	moveMarker: function(marker){
		var pos= this.markerPos(marker)
		if (pos < 0) { alert('move '+marker+' not found'); return} //not found nothing to remove TODO: through error
		//alert('moving '+pos)
		return this.movePos(pos)
		},

	initHoverContent: function(){return ''}

	}
	
	
$.Simple= Class(PolyRouter,{
	//this is a ptah like Poly router but only reports begining and end as waypoints 
	//all other way points are condidered to be part of the segment
	getWaypoints: function(){ 
		//alert('simple get waypoints')
		var len= this.markers.length
		if (len<3) return this.markers //2 or less
		return [this.markers[0],this.markers[len-1]]
		}
	})


$.GPolyRouter= Class(PolyRouter,{

	//Polyline based router
	//Line is a poly that spreads througn multiple waypoints
	gdir: false, //gDirectionsObject for this router ...

	jz_init: function(map,ops){
		var gdir= this.gdir= new GDirections()
		GEvent.bind(gdir,'load',this,this.doGDirLoad)
		GEvent.bind(gdir,'error',this,this.doGDirError)
		this.dirDiv= getId('directions')
		//jz.superClsCall(GPolyRouter,'jz_init',this,map,ops)
		//PolyRouter.prototype.jz_init.call(this,map,ops)
		jz.superClsP(GPolyRouter).jz_init.call(this,map,ops)
		return this
		},

	pathUpdateEnd: function(pos,action){
		// this is called in all circumstances insert,move and remove
		// action is moved inserted or removed

		//? GPolyRouter.jz_superP.pathUpdateEnd.call(this,pos,mode)

		if (this.markers.length<2){
			//need atleast 2 points to draw a line ... bye
			this.clearPoly()
			return
			}
		this.gdir.clear()
		var query= mapf(mapf(this.markers,'getLatLng()'),'lat()+","+lng()')
		//alert('doing q='+query)
		this.gdir.loadFromWaypoints(query,{
			getPolyline:true,
			getSteps:true,
			preserveViewport:true,
			locale: 'fr_CA'
			}) //dirflg:'h', avoidHighways:true, avoidHighway:true, avhwy:true, ddopt_avhwy:true})
		},

	//TODO: add to language ....
	doGDirLoad: function(){
		var ut= function(d){ return unittxt(d,this.units)}
		var gdir= this.gdir
		this.clearPoly()
		this.setPoly(false,gdir.getPolyline())

		if (!this.dirDiv) return //ok no directions we are done ..

		var li= function(c){ return tag('li',c) }
		var tripSummary= '<span style="font-weight:bold; font-size:133%">Trajet:'+gdir.getSummaryHtml()+'</span>'
		var numRoutes= gdir.getNumRoutes()
		var pos= 0.0
		var routes= mapf(numRoutes,function(i){
			var route= gdir.getRoute(i)
			var routeSummary=  '<span style="font-weight:bold;">Étape '+(i+1)+': '+route.getSummaryHtml()+'</span>'
			var steps= mapf(route.getNumSteps(), function(j){
				var step=route.getStep(j), dist=step.getDistance(), time=step.getDuration(), at=pos
				pos+= dist.meters
				return li(ut(a)+' '+step.getDescriptionHtml()+', pour '+ut(dist.meters)+' ('+time.html+')')
				})
			if (i == numRoutes-1) steps.push(li('Arrivée à la fin du trajet'))
			else steps.push(li("Arrivée à l'étape "+(i+2)))
			return li(tag('ul',routeSummary+steps.join('')))
			})
		this.dirDiv.innerHTML= tag('ul',tripSummary+routes.join(''))
		},

	doGDirError: function(){ //this will be the GDirectios object
		//alert('in google err')
		var gdir= this.gdir
		var statusinfo= gdir.getStatus()
		alert('Google error stat='+statusinfo.code+' '+statusinfo.request)
		}
	})

//Directions router expects a target this is a plug in ....
$.TargetRouter= { //this is a plug in
	//target: new GLatLng(47.1,-71.1), //init force a trarget
	cookie: '',

	jz_init: function(map,ops){
		if (!ops) ops={}
		if (!ops.target){
			//if (!ops.cookie) ops.cookie=this.cookie //why?
			return this.constructor.jz_superCls(map,ops) //a TargetRouter expects a target ...
			}
		var ret= this.constructor.jz_superP.jz_init.call(this,map,ops)
		return ret
		},

	initWaypoints: function(waypoints){
		//alert('target router inwp='+waypoints)
		if (!waypoints) waypoints=[]
		if (waypoints.length<1) waypoints=[this.target]
		else if ( (waypoints.length == 1 ) || (mCalcDistance(waypoints[waypoints.length-1],this.target)>0.1)){
			//sorry cant keep waypoints no end or end is to far from target
			waypoints=[waypoints[0], this.target]
			}
		this.constructor.jz_superP.initWaypoints.call(this,waypoints)
		},

	//manage helpers Never show an end 

	addEndHelper:  function(){ return this.endHelper= false },

	
	addMarkerAt: function(pos,latlng,icon){
		if (icon==this.startIcon && this.markers.length==1 && this.afterinit) icon=this.endIcon
		this.constructor.jz_superP.addMarkerAt.call(this,pos,latlng,icon)
		//alert('target watch pos='+pos+'  len='+this.markers.length)
		if (pos==this.markers.length-1 && this.afterinit) this.markers[pos].hide() //dont show last icon
		},
	
	pathUpdateEnd: function(pos,mode){
		// Check if end have changed ....
		if (mode=='initstart') this.ininit= true,  this.afterinit= false
		if (mode=='initend')   this.ininit= false, this.afterinit= true

		this.constructor.jz_superP.pathUpdateEnd.call(this,pos,mode)
		//alert('pathUpdateend '+mode+' '+pos+' l='+this.markers.length)
		if (mode=='initend' && pos){
		    this.markers[pos-1].hide()
		    if (pos==1) this.addStartHelper()
		    }
		},
		
	show: function(){
		this.constructor.jz_superP.show.call(this)
		if (this.markers.length) this.markers[this.markers.length-1].hide()
		},

	doclick: function(ovl,latlng){
		//Remember latlng not set if ovl is ....
		if (!ovl && this.showen) return this.insertPos(0,latlng)
		}
	}


$.GDirRouter= Class(GSegRouter,TargetRouter)
GDirRouter.prototype.cookie= 'googletarget'

$.GDirRouterVelo= Class(GSegRouterVelo,TargetRouter)
GDirRouterVelo.prototype.cookie= 'gvelotarget'

$.RDirRouter= Class(RSegRouter,TargetRouter)

$.PMNDirRouter= Class(PMNSegRouter,TargetRouter)
PMNDirRouter.prototype.cookie= 'pmntarget'

$.prefixIs= function(str,prefix){ return str.substring(0,prefix.length)==prefix}

$.DynaRouter= Class(MRouterBase,{
	
	jz_init: function(map,ops){
		if (ops.tracker && !ops.rwaypoints && !ops.ewaypoints && !ops.waypoints){
		    // try tracker init
		    var tracker= this.getTracker(ops.tracker)
		    if (tracker.network && tracker.waypointsData){
			//alert('dyna init from tracker')
			ops.network=tracker.network
			ops.waypoints= tracker.waypointsData
			}
		    //else alert('empty tracker n='+tracker.network+' w='+tracker.waypoints)
		    }
		//else alert('NO TRACKER INIT'+ops.tracker+' && '+!ops.rwaypoints+' && '+!ops.ewaypoints+' && '+!ops.waypoints)
		    

		var network= (ops['network'] || 'google').toLowerCase()
		ops['network']= network
		if (contains(['google','route','routes'],network)) return GDirRouter(map,ops)
		if (network=='gvelo') return GDirRouterVelo(map,ops)
		if (prefixIs(network,'pmn') || prefixIs(network,'motoneige')){
		    ops['htmlfooter']= _('<br />&nbsp;<b>T</b>:Trans-Québec, <b>R</b>:Régional, <b>L</b>:Local')
		    return PMNDirRouter(map,ops)
		    }
		if (prefixIs(network,'velo')) return RDirRouter(map,ops)
		if (prefixIs(network,'moto')) return RDirRouter(map,ops)
		if (prefixIs(network,'pqd')) return RDirRouter(map,ops)
		if (prefixIs(network,'mseg')){
		    if (network=='msegpmn') ops.segmentCls= [Class(RSegment,{network:'pmn-vx',segType: 'motoneige (ViaExplora)' })]
		    else if (network=='msegvelovx') ops.segmentCls= [Class(RSegment,{network:'velo',segType: 'velo (ViaExplora)' })]
		    else if (network=='msegauto') ops.segmentCls= [GSegment]
		    else if (network=='msegvelog') ops.segmentCls= [GVeloSegment], ops.makedummydiv=true
		    else if (network=='msegl') ops.segmentCls= [ Segment ]
		    else
			ops.segmentCls= [ Segment, GSegment, GVeloSegment,Class(RSegment,{network:'pmn-vx',segType: 'motoneige (ViaExplora)'}), Class(RSegment,{network:'velo',segType: 'velo (ViaExplora)'})], ops.makedummydiv= true
		    return MutliSegRouter(map,ops)
		    }
		//if (network=='aucun') return BigPoly(map,ops)
		if (network=='aucun') return SegRouter(map,ops)
		return GSegRouter(map,ops)
		}
	})
//$.Router= GMRouter
$.Router=DynaRouter
//$.Router= GPolyRouter
$._info = function(a){
	//alert('welcome to _info')
	if (a.rwaypoints){
		a.rwaypoints=mapf(getId(a.rwaypoints).childNodes,
				function(node){
					if (node.nodeType==1 && node.id) return parseDataFor(node.id)
					else return jz._doIGNORE }
				)
		}
	else a.rwaypoints= false
	var eid=a.ewaypoints?a._elem.id+'_ewaypoints':''
	//alert('doing ied='+eid)
	var cost=parseUrl()['cost']||''
	var router= DynaRouter(mapDiv.vMap,{network:a.network,rwaypoints:a.rwaypoints, ewaypoints:a.ewaypoints, ewaypointid:eid, cookie:''})
	router.cookie=''
	//alert('router.cookie='+router.cookie)
	if (eid && getId(eid))
	    getId(eid).jz_getter=function(){ return router.dumpstrs() }
	return router
	}
	
//jz.DomClasses['Router']=jz.DelaiedFactory(jz.DomClasses.Data,_info )
jz.DomClasses['Router']=jz.DomClasses.Data
	
jz.doClickAddPoint= function(ovl,latlng){
	//Remember latlng not set if ovl is ....
	if (!ovl){
		//alert('jz.doClickAddPoint')
		return this.router.addMarker(latlng)
		}
	}

$.doRemoveLast= function(mapid){
	var router= getId(mapid).vMap.router
	if (!router){
		alert('No router getId='+jz.getId('form.overlays._map'))
		return
		}
	router.removeLastMarker()
	}

$.doRemoveFirst= function(mapid){
	var router= getId(mapid).vMap.router
	if (!router){
		alert('No router getId='+jz.getId('form.overlays._map'))
		return
		}
	router.removePos(0)
	}



$.MutliSegRouter= Class(SegRouter,{
	//Utilese des SSRouter SimpleSegRouter assembler une route avec multi router  

	segmentCls: false,
	makedummydiv: false,
	_dummydiv: false,//div used to fool walk instructions ...


	jz_init: function(map,ops){
		if (ops.makedummydiv) this.makedummydiv= ops.makedummydiv
		if (this.makedummydiv){
		    var _dummydiv= this._dummydiv=document.createElement('div')
		    _dummydiv.style.position="absolute"
		    _dummydiv.className='jz-hidden'
		    document.body.appendChild(_dummydiv)
		    }
		MutliSegRouter.jz_superP.jz_init.call(this,map,ops)
		return this
		},

	reset: function(){
		//from segrouter rest
		var map= this.map
		dof(this.segments,function(seg){seg.clear()})
		this.segments= []
		this.html= []
		this.totaldist= 0
		this.totaltime= 0
		if (this.dirDiv && this.dirHelp) this.dirDiv.innerHTML= this.dirHelp
		//jump over segrouter reset
		SegRouter.jz_superP.reset.call(this)
		},

	updateSeg: function(pos){
		var changed= this.segments[pos].update()
		if (changed) this.setHtml()
		//if (changed && this.tracker) this.setTracker(this.tracker)
		},

	insertSeg: function(pos){
		var segCls= this.segmentCls
		if (isArrayLike(segCls)) segCls= segCls[parseInt(Math.random()*segCls.length)]
		var newSeg= segCls({router:this})
		this.segments.splice(pos,0,newSeg)
		},

	delSeg: function(pos){
		this.clearSeg(pos)
		this.segments.splice(pos,1)
		},

	clearSeg: function(pos){
		this.segments[pos].clear()
		},

	dumppts: function(){
		var lastpt=false, pts=[]
		dof(this.segments,function(seg){
			dof(seg.getPoints(),function(pt){
				if (!lastpt || !( lastpt.lat()==pt.lat() && lastpt.lng()==pt.lng() )) pts.push(pt)
				lastpt= pt
				})
			})
		return pts
		},

	dumpsegments: function(){
		var ret= mapf(this.segments,function(seg){
			if (!seg) return []
			return seg.getPoints()
			})
		//alert(' dump seg segments='+ret)
		return ret
		},


	setHtml: function(){
		//if (!this.dirDiv) return //ok no directions we are done ..
		
		var pos= 0.0
		var time= 0.0
		var self= this
		
		var routes=mapf(this.segments,function(routeinfo,i){
			var startPos=pos, startTime= time

			var dist='', durt=''
			if (routeinfo.dist){
				dist=unittxt(routeinfo.dist.meters,self.units)
				durt=timetxt(routeinfo.time.seconds)
				}

			var routeSummary= self.makeRouteSummary({i:i+1, dist:dist, durt:durt, mode:routeinfo.segType, state:routeinfo.state})
				
			var steps= mapf(routeinfo.steps,function(step,j){
				var stephtml= self.makeStepSummary({step:step, idx:j, units:self.units, cpos:pos, ctime:time})
				pos+= step.dist.meters
				time+= step.time.seconds
				return stephtml
				})

			if (!steps) steps= []
			var end
			if (i == self.segments.length-1)
				 end= _("Arrivée à la fin du trajet")
			else end= _("Arrivée à l'étape $i",{i:i+1})
			steps.push( li({cls:"dir-etape"},span({cls:"dir-etape-txt"},unittxt(pos,this.units)+'  '+end))  )
			if (self.segments.length>1)
				 return li({cls:"dir-troncon"},routeSummary+ul({cls:'dir-troncon'},steps.join('')))
			else return steps.join('')
			})

		var summary
		if (this.htmlsummary) summary=this.htmlsummary
		else { 
			var distxt= unittxt(pos,this.units)
			var tempstxt= timetxt(time)
			var mn= Math.round(time/60.)%60
			summary= _('<b>Distance $dist duré environ $temps </b>', {dist:distxt, temps:tempstxt})
			}

		if (this.segments.length>1)
		    var allhtml= summary+ul({cls:'dir-list-troncons'},routes.join(''))+this.htmlfooter
		else
		    var allhtml= summary+ul({cls:'dir-troncon'},routes.join(''))+this.htmlfooter
		if (this.dirDiv) this.dirDiv.innerHTML= allhtml
		this.totaldist=pos
		this.totaltime=time
		}


    /* todo: insertPos updatePos  delSeg clearSeg */

	})





$.Segment= Class(Object,{
    
	segType: "vol d'oiseau",
	router: false, //Router segment belongs to
	set: false, //dst, time, ovl html steps are valide when set is true
	state: 'cleared', // cleared, set, error and waiting (if async)
	dist:false, // dist.meters
	time: false, //time.seconds
	steps: [],
	speed: 1.0, // m/sec
	ovl: false, //GOverlay
	html: '',//Html describing route
	showen: true,
	segStyle: {color: "#0bf559", weight:7, opacity:0.6},  // ffff33 c6ff00 4cf61e 0bf559 35e56f


	jz_init: function(){
		//make jz_init overidable
		var obj= this
		dof(arguments,function(vars){update(obj,vars)}) //default init
		return this
		},

	getPosInfo: function(){
		// return false if np router or not in router segments
		var router= this.router
		if (!router) {alert('no router'); return false }
		var pos= router.segments.indexOf(this)
		if (pos == -1) return false

		var start= router.markers[pos].getLatLng()
		var end=   router.markers[pos+1].getLatLng()

		return [start,end,pos]
		},

	update: function(){
		var posInfo= this.getPosInfo()
		if (!posInfo) return // TODO:Silent error

		var start=posInfo[0], end=posInfo[1], pos=posInfo[2]

		this.clear()
		this.getSeg(start,end,pos,this.segStyle)

		return true
		},

	getSeg: function(start,end,pos,style){
		this.dist= { meters: jzGMap.mCalcDistance(start,end)*1000.}
		this.time= { seconds: self.dist/self.speed }
		this.html= unittxt(this.dist.meters,this.units)+' from '+start.lat().toFixed(6)+','+start.lng().toFixed(6)+' to '+end.lat().toFixed(6)+','+end.lng().toFixed(6)+' using '+this.segType
		this.steps= [ this]
		this.setOvl(this.makeOvl([start,end]))
		this.set= true
		this.state= 'set'
		},

	makeOvl: function(points){ return newPolyline(points,this.segStyle) },

	setOvl: function(ovl){
		if (this.ovl) {
		    GEvent.clearInstanceListeners(ovl)
		    this.router.map.removeOverlay(this.ovl)
		    }
		this.ovl= ovl
		ovl.clickable= true
		var router= this.router
		var self= this
		router.map.addOverlay(ovl)
		GEvent.addListener(ovl,'click',function(latlng){ router.doSegmentClick(self,latlng) })
		if (!this.showen && ovl.hide) ovl.hide()
		},

	clear: function(){
		this.html= ''
		if (this.ovl){
		    //alert('remove ovl='+this.ovl)
		    this.router.map.removeOverlay(this.ovl)
		    }
		this.ovl= false
		this.dst= 0
		this.time= 0
		this,steps= []
		this.set= false
		this.state= 'cleared'
		},
    
	getLength: function(){
		if (!this.set) return 0 //TODO: silent error
		return this.dist.meters
		},

	hide: function(){
		this.showen= false
		if (!this.set) return
		this.ovl.hide()
		},

	show: function(){
		this.showen= true
		if (!this.set) return
		this.ovl.show()
		},

	getPoints: function(){
		if (!this.set) return []
		var ovl=this.ovl
		return mapf(ovl.getVertexCount(),function(i){ return ovl.getVertex(i) })
		}
	})





$.GSegment= Class(Segment,{
	segType: 'auto (google)',
	segStyle: {color: "#ffff00", weight:7, opacity:0.6},  // ffff33 c6ff00 4cf61e 0bf559 35e56f
	reqReTries: 5,
	reqTry: 0,

	reqdefops:  {
		getPolyline:true,
		getSteps:true,
		preserveViewport:true,
		locale: _('locale'),
		avoidHighways: false
		},

	getreqdefops: function(){ return this.reqdefops },

	clear: function(){
		this.clearReq()
		this.state= 'cleared'
		return  GSegment.jz_superP.clear.call(this)
		this.html= ''
		},

	clearReq: function(){
		if (!this.req) return

		GEvent.clearInstanceListeners(this.req)
		this.req.clear()
		this.req=false
		this.reqTry= 0
		},

	getSeg: function(start,end,pos,style){
		this.clearReq()

		var gdir= this.req= this.getGDirObj()
		GEvent.addListener(gdir,'load',GEvent.callbackArgs(this,this.doGDirLoad,gdir))
		GEvent.addListener(gdir,'error',GEvent.callbackArgs(this,this.doGDirError,gdir))
		var query= gdir.jz_query= [ ''+start.lat()+','+start.lng(), ''+end.lat()+','+end.lng() ]
		var ops= gdir.jz_ops= this.getreqdefops()
		this.state= 'waiting'
		gdir.loadFromWaypoints( query,ops) //dirflg:'h', avoidHighways:true, avoidHighway:true, avhwy:true, ddopt_avhwy:true})
		},

	getGDirObj: function(){ return new GDirections() },

	doGDirLoad: function(gdir){

		var ovl= update(gdir.getPolyline(),this.segStyle)
		this.setOvl(ovl)

		//?var segSummary= gdir.getSummaryHtml() //if one route same as step summary
		if (gdir.getNumRoutes() != 1) {
			alert('gdir numroutes='+gdir.getNumRoutes()+' expected 1')
			return
			}
		var route= gdir.getRoute(0)
		
		this.html= route.getSummaryHtml()+' using '+this.segType
		this.dist= route.getDistance()
		this.time= route.getDuration()
		this.steps= mapf(route.getNumSteps(), function(j){
			var step=route.getStep(j)
			return  { html: step.getDescriptionHtml(),
				dist: step.getDistance(),
				time: step.getDuration()}
			})

		this.set= true
		this.state= 'set'
		this.router.setHtml()
		//this.clearSeg(pos) done in updateSeg
		//this.setHtml()
		//if (this.tracker) this.setTracker(this.tracker)
		},

	doGDirError: function(gdir){ //this will be the GDirectios object
		//alert('in google err')
		var statusinfo= gdir.getStatus()
		if (statusinfo.code == 620){
			if (this.reqReTries && ++this.reqTry<this.reqReTries){
			    //ok lets waut a while and try again
			    setTimeout(function(){gdir.loadFromWaypoints( gdir.jz_query, gdir.jz_ops)},500+(Math.random()*2000))
			    return //wait
			    }
			}
		//alert('Google error stat='+statusinfo.code+' '+statusinfo.request)
		this.state= 'error '+statusinfo.code+' '+statusinfo.request
		this.router.setHtml()
		}


	})


$.GVeloSegment= Class(GSegment,{

	segType: 'voie pedestre (google)',
	segStyle: {color: "#80ff80", weight:3, opacity:0.8},  // ffff33 c6ff00 4cf61e 0bf559 35e56f

	reqdefops:  {
		getPolyline:true,
		getSteps:true,
		preserveViewport:true,
		locale: _('locale'),
		avoidHighways: true
		},

	getreqdefops:	function(){
		var ops=this.reqdefops
		ops['travelMode']= G_TRAVEL_MODE_WALKING //gmap may not be loaded when code loades
		return ops
		},

	getGDirObj: function(){ return new GDirections(undefined,this.router._dummydiv) }

	})



$.RSegment= Class(Segment,{
	network: 'def-network',
	segType: 'reseau ViaExplora',
	segStyle: {color: "#00ffff", weight:7, opacity:0.6},
	cost: '',
	
	jz_init: function(ops){
		if (!ops.cost){
		  var cost=parseUrl()['cst']
		  if (cost) ops['cost']=cost
		  }
		RSegment.jz_superP.jz_init.call(this,ops) //super method
		return this
		},


	clear: function(){
		this.clearReq()
		this.state= 'cleared'
		return  GSegment.jz_superP.clear.call(this)
		this.html= ''
		},

	clearReq: function(){
		var req= this.req
		if (!req) return
		req.abort && req.abort()
		req._jz_router= undefined //help gc
		req.onreadystatchange= undefined //help gc
		this.req=false
		this.reqTry= 0
		},


	getSeg: function(start,end,pos,style){
		this.clearReq()

		var query= _('/app/findroute2?network=$network&start=$start&end=$end&cost=$cost',
			{network:this.network, start:start, end:end, cost:this.cost})

		//alert('rseg router doing query=\n'+query)
		var req= this.req= newXMLHttpRequest()
		req.open('GET',query)
		req._jz_seg= this
		//req._jz_pos= pos
		req.onreadystatechange= function(){
			if (req.readyState != 4) return //wait for end ...
			if (!req._jz_seg) return //cause not much to do ...
			if (req.status == 200) req._jz_seg.doUpdateSeg(req)
			else req._jz_seg.doErrorSeg(req)
			if (!jz.IE) req.onreadystatechange= undefined //IE does not like this line dno why ...
			req._jz_seg.req= undefined
			req._jz_seg=  undefined
			}
		this.state= 'waiting'
		req.send(null)
		},

	getStepInfoFromResp: function(stepnum,resp){
		var stepid= 'step_'+stepnum
		var dm= parseFloat(innerText(stepid+'.dist',resp))
		var ds= dm/(50000./3600.) // metre/(kmh/60min*60sec) 
		return {
			html: innerHTML(stepid+'.deschtml',resp),
			dist: { meters:dm},
			time: { seconds:ds},
			nomrue: innerText(stepid+'.nomrue',resp)
			}
		},
	    

	doUpdateSeg: function(req){
		var resp= req.responseXML
		
		var pts=  innerText('epoints',resp)
		var lvls= innerText('lvls',resp)
		var numsteps= parseInt(innerText('numsteps',resp))
		var distance= parseFloat(innerText('distance',resp))
		if (!pts || !lvls) return this.doErrorSeg(req)
		var self=this
		this.steps= mapf(numsteps,function(stepnum){ return self.getStepInfoFromResp(stepnum,resp)})
		this.html= innerHTML('tpath',resp)+' using '+this.segType
		var debug= innerHTML('debug',resp)
		if (debug) this.html+=debug
		
		this.dist=  { meters: distance}
		this.time=  { seconds: distance/(50000./3600.)}

		this.setOvl(newEPolyline(pts,lvls,this.segStyle))

		this.set= true
		this.state= 'set'

		this.router.setHtml()
		//if (this.tracker) this.setTracker(this.tracker)
		},

	doErrorSeg: function(req){ //this will be the GDirectios object
		//alert('in google err')
		var status= req.status
		this.state= 'error '+status
		this.router.setHtml()
		},

	makeStepSummary: function(vars){
		var step= vars.step
		var units= vars.units
		return li({cls:"dir-etape"},span({cls:"dir-etape-txt"},
			    _("A $postxt prendre <b>$nomrue</b> pour $distxt ($timetxt)",{
				distxt:unittxt(step.dist.meters,units),
				timetxt:timetxt(step.time.seconds),
				nomrue:step.nomrue,
				postxt:unittxt(vars.cpos,units)
				})
			    ))
		}


	})

}//end with


