О немО себе[code]/maps/

Mission:Start map

Goal:Reduce fails

Step1. Test yourself

In the beggining - test your own map application.
Do this in two steps:
Impliment some king of "loading guard", it will fires if map wont start in 40 seconds

 setTimeout(function(){
    somehow test what map is ready and workable.
    for example - map will fires event "weAreDone".
    catch it!
 },40000);
 /* i thing 40sec is alot of time*/
and then define global error catcher

function global_error_catcher(msg, url, num, more){
    if(msg && msg.name){
        var _event=msg;
        msg=(_event.name+"] "+e.fileName+":"+e.lineNumber+"\n"+e+"\n"+e.stack);
    }
    if(typeof(msg)=='object'){
     var evt=msg;
     msg='';
     for(i in evt)msg+=i+":"+evt[i]+"\n";
    }
    var textout=("global error:\n"+msg+",\n"+url+":"+num+"\n:"+more);
    new Ajax.Request("loader/report_site_error.php",{parameters:{'message':textout},async:true});
    return true;
 }
 window.onerror=global_error_catcher;
Then put this application online and see logs.
So i say

"if map working for you - it may not work for others"


And this is true.

some time ago i got bug report "hey men, there is no map on your site".
I test it - map exists, i test my site on other PC.. map exists..
I try to ask friends to test site... and.. MAP EXISTS!!!
but next day i`v got same bug report from other user..
So i impliment listed debug scripts and then i see.. 2000 users per day see my site with out map. 2000 users of 50k - 4%
So how to fix it

Step2. Start map

First include google loader
 
function add_script(src){
    var fileref=document.createElement("script");
    fileref.type="text/javascript";
      fileref.src=src;
      document.getElementsByTagName("head")[0].appendChild(fileref);
}

function _do_requestLoader(domain){
    add_script("http://www.google."+domain+"/jsapi?hl=ru&key="+GM_APIKEY);
}

var requestLoader_try=0;
function requestLoader(){
    if(requestLoader_try==0)_do_requestLoader("your domain code(.us,.ru,not just .com)");
    else                    _do_requestLoader("com");
    requestLoader_try++;
    
    map.__init_loadingCycle(0);
}
Next try to call it and include google maps

GoogleMap.prototype.__init_loadingCycle = function (tick){
    clearTimeout(this.__loadingTm); 
    if(tick++>200){
        requestLoader();
        return;
    }
    var thisworker=this;
    //we have google loaded. sometimes( apple\safari google loads in 2-5 tick! )
    if(window.google && typeof(window.google)!=undefined && typeof(window.google.load)=='function'){
        this.console("google start at "+tick+" tick");
        thisworker._enterState('GMaps.load',function(){
        google.load("maps", "3", {other_params:"sensor=true",
                    "callback" : function(){
                        if(thisworker.isClosed){
                            thisworker.console("google post-activaion");
                            return;
                        }
                        thisworker._enterState("google start",function(){
                        thisworker.start();
                        },0);
                        },                
                    "sensor":"true",
                    //and some others params like
                    "language" : "ru",
                    "hl":"ru",
                    "base_domain":"maps.google.ru"
                    });
        },0);
    }
    else{
        //start timer
        this.__loadingTm=setTimeout(function(){thisworker.__init_loadingCycle(tick)},10);
    }
}
Next wait until maps starts, and request maps projection

//lest define projection helper
function GoogleV3ProjectionHelperOverlay(map)
{
    this.setMap(map);
}

GoogleMap.prototype.start = function ()
{
    var _this=this;
    //leave loading state
    this._leaveState(this.loadingState);
    var myOptions = {
      zoom: this.driver.getZoom(),
      center: this.fromLatLng(this.driver.getPosition()),
      mapTypeId: this.decodeMapType(this.driver.getMapType()),
      disableDefaultUI:true,
      scrollwheel:true,
      keyboardShortcuts:false      
    }
    var thisworker=this;    
    this.api = new google.maps.Map(this.divId,myOptions);        
    this.helperOverlay=0;
    this.startIniter=0;
    
    //then define help projector
    GoogleV3ProjectionHelperOverlay.prototype = new google.maps.OverlayView();
    GoogleV3ProjectionHelperOverlay.prototype.draw = function () {
        if (!this.ready) {
            google.maps.event.trigger(this, 'ready');            
            this.ready = true;
            
            //if this overlay ready - start maps
            this.tearMapOn(0);
        }
    };
    
    GoogleV3ProjectionHelperOverlay.prototype.tearMapOn = function(tick){
        //if it have panes - start map
        if(this.getPanes()){
                 _this._enterState("google-doStartInit - "+tick,function(){                 
                 _this.doStartInit();
            });
        }
        else{
            var __this=this;
            setTimeout(function(){__this.tearMapOn(tick++)},1);
        }
    }
    
    //sometimes it good to know how much time took loading of all tiles of map
    this.idleTimer=google.maps.event.addListener(this.api,'idle',function(){
     _this.weAreDone();
     google.maps.event.removeListener(_this.idleTimer);   
    });
    
    this.api.setCenter(myOptions.center);
    this.api.setZoom(myOptions.zoom);
    this.api.setMapTypeId(myOptions.mapTypeId);
    
    //call some callback on API init
    this.onReadyAPI('google','init',this);
    
    //init helperOverlay
    this.helperOverlay=new GoogleV3ProjectionHelperOverlay(this.api);
    this.helperOverlay.setMap(this.api);
    this.helperOverlay.draw();
}

//this function will be called this helperOverlay starts
GoogleMap.prototype.doStartInit = function ()
{
   if( this.startIniter )
     google.maps.event.removeListener(this.startIniter);   
   this.onMapReady();
}

Map started!
and after this step we can convert latlng to pixels as in v2. Many users want fromLatLngToContainerPixel - here it is!

GoogleMap.prototype.modPointByOffset=function(point,fac){
     var bounds=this.api.getBounds();//this.getBounds();
     var swPoint = this.fromLatLngToDivPixel(bounds.getSouthWest());
     var nePoint = this.fromLatLngToDivPixel(bounds.getNorthEast());
     return {x:point.x-swPoint.x*fac,y:point.y-nePoint.y*fac};
}
     
GoogleMap.prototype.fromLatLngToContainerPixel=function(a){
    var point=this.fromLatLngToDivPixel(a);
    return this.modPointByOffset(point,1);
}
GoogleMap.prototype.fromContainerPixelToLatLng=function(a){
    var point=this.modPointByOffset(a,-1);
    return this.fromDivPixelToLatLng(point);
}
GoogleMap.prototype.fromLatLngToDivPixel      =function(a){
    if(!this.helperOverlay) return {x:0,y:0};
    return this.helperOverlay.getProjection().fromLatLngToDivPixel(a);

}
GoogleMap.prototype.fromDivPixelToLatLng      =function(a){
    return this.toLatLng(this.helperOverlay.getProjection().fromDivPixelToLatLng(a));
}

Step3. Test it again

So we use enter and leavestate functions. The log working flow. If some event "enters", but not "leaves" - we will log.

GoogleMap.prototype._leaveState = function(_event){
    var _this=this;
    _event.leave=_this.microtime();
    _this.console("["+_event.name+"] took " +(_event.leave-_event.enter));
    return _event;
}

GoogleMap.prototype.__callState = function (_event){
    try
    {
            _event.func();
            this._leaveState(_event) ;
    }catch(e){
        _event.error=e;
        this.console("state-error ["+_event.name+"] "+e.fileName+":"+e.lineNumber+"\n"+e+"\n"+e.stack);
        this._leaveState(_event) ;
        this.__stateException(_event,e);
        ;
    }
}

GoogleMap.prototype._enterState = function(name,func,sync,catcheble){
    var _event={'name':name,'func':func,'enter':this.microtime(),'leave':0,'catcheble':catcheble};
    var _this=this;
    this.console('entering ['+name+']'+this._stages.length);
    this._stages.push(_event);
    if(func){
        clearTimeout(this._stageTimeout);
        if(sync){ _this.__callState(_event);
        }
        else{
            this._stageTimeout=setTimeout(
                function(){
                    _this.__callState(_event);
                },1);
        }
    }
    return _event;
}


See error logs again.

W\O this patches 1-5% of request fails

with this patches still some request fails, but just some of this.

so test yourself and see. I test this on 4 sites with 150k unique visitors per day. And i think it is true that map exists not for all of then then you use "standart" way
What about you?

All of sections, i list here, is allready done by me. I need just to wrap code into article, and, yap, english article :)
If you want to know more - ask

How to start Google V3

Вперив взгляд в платяной шкаф, Кащей забормотал волшебную тарабарщину и взмахнул руками
Царь, царевич, король, королевич..