
function GoogleMap( id, width, height, mapTypes, startMapType, startPointGeocode, startLevel, zoomLevel, phpHandler, initialCategory, initialSubCategory, icons ) {
    if(!GBrowserIsCompatible()) return;
    
    // clenup event
    $( window ).addEvent( 'unload', GUnload ); 
    
    this.id                  = id;
    this.mapId               = this.id+'_map';
    this.menuId              = this.id+'_menu';
    this.width               = width;
    this.height              = height;
    this.mapTypes            = mapTypes;
    this.startMapType        = startMapType;
    this.startPointGeocode   = startPointGeocode;
    this.startLevel          = startLevel;
    this.zoomLevel           = zoomLevel;
    this.objectsDisplayed    = 2;
    this.categoriesDisplayed = 2;
    this.phpHandler          = phpHandler;
    this.returnCategory      = 0;
    this.returnSubCategory   = 0;
    this.initialCategory     = initialCategory;
    this.initialSubCategory  = initialSubCategory;
    this.initialObjectDisplayed  = false;
    this.icons               = icons;
    
    this.mapContainer        = null;
    this.mapHandler          = null;
    this.geocoder            = null;
    
    this.menuCategoriesListData = [];
    this.menuObjectsListData    = [];
    
    this.container = $( this.id );
    this.container.set( 'class', 'google-map' );
    this.container.setStyle( 'width', this.width+'px' );
    this.container.setStyle( 'height', this.height+'px' );
    
    
    this.CreateMap();
    this.CreateMenu();
};

GoogleMap.prototype.CreateMap = function() {
    this.mapContainer = new Element('div', {id: this.mapId}); 
    this.mapContainer.addClass( 'google-map-body' );
    this.container.setStyle( 'width', this.width+'px' );
    this.container.setStyle( 'height', this.height+'px' );
    
    this.mapHandler = new GMap2( this.mapContainer, { size: new GSize(this.width,this.height) } );
    this.geocoder   = new GClientGeocoder();
    
    this.mapHandler.addControl( new GLargeMapControl() );

    if( 0 == (this.mapTypes & 1) ) 
        this.mapHandler.removeMapType( G_NORMAL_MAP );
    if( 0 == (this.mapTypes & 2) ) 
        this.mapHandler.removeMapType( G_SATELLITE_MAP );
    if( 0 == (this.mapTypes & 4) ) 
        this.mapHandler.removeMapType( G_HYBRID_MAP );
    if( 0 < (this.mapTypes & 8) ) 
        this.mapHandler.addMapType( G_PHYSICAL_MAP );

    switch( this.startMapType ) 
    {
        case 1:
            this.mapHandler.setMapType( G_NORMAL_MAP );
            break;	    	
        case 2:
            this.mapHandler.setMapType( G_SATELLITE_MAP );
            break;	    	
        case 4:
            this.mapHandler.setMapType( G_HYBRID_MAP );
            break;	    	
        case 8:
            this.mapHandler.setMapType( G_PHYSICAL_MAP );
            break;	    	
        default:
            this.mapHandler.setMapType( G_NORMAL_MAP );
            break;	    	
    }
    this.mapHandler.addControl( new GMapTypeControl() );

    this.infoWindow = new Element('div');
    this.infoWindow.addClass( 'mapTooltip' );
    this.infoWindowName         = new Element('h2');
    this.infoWindowName.inject( this.infoWindow );
    this.infoWindowAddressLine1 = new Element('span');
    this.infoWindowAddressLine1.inject( this.infoWindow );
    var br1 = new Element('br');
    br1.inject( this.infoWindow );
    this.infoWindowAddressLine2 = new Element('span');
    this.infoWindowAddressLine2.inject( this.infoWindow );
    var br2 = new Element('br');
    br2.inject( this.infoWindow );
    this.infoWindowAnchor = new Element('a', {target:'_blank'});
    this.infoWindowAnchor.addClass( 'moreButton' );
    this.infoWindowAnchor.inject( this.infoWindow );
    
    // insert map into dom model
    this.mapContainer.inject( this.container );

    // setup center
    if( this.geocoder ) {
        // invoker function pointing to `this` used as a callback for geocoder      
        function SetCenterInvoker( obj ) {
            var reference = obj;
            this.invoke = function( startPoint ) {
            	reference.mapHandler.setCenter( startPoint, reference.startLevel );	
            }
        };
        
        var setCenter = new SetCenterInvoker( this );
        
        
        this.geocoder.getLatLng( this.startPointGeocode, setCenter.invoke );
    }    
};

GoogleMap.prototype.CreateMenu = function() {
    this.menuContainer = new Element('div', {id: this.menuId}); 
    this.menuContainer.addClass( 'google-menu-container' );

    // additional id's
    this.menuHeaderId     = this.menuId+'_header';
    this.menuReturnId     = this.menuId+'_return';
    this.menuCategoriesId = this.menuId+'_categories';
    this.menuObjectsId    = this.menuId+'_objects';
    this.menuStatusId     = this.menuId+'_status';
    
    // return button
    this.menuReturn = new Element('span', {id: this.menuReturnId});  
    this.menuReturn.addClass( 'back' );
    this.menuReturn.addClass( 'nvisible' );
    this.menuReturnHref = new Element('a');  
    this.menuReturnHref.inject( this.menuReturn );
    // invoker for return button
    function ReturnInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnReturn();	
        }
    };
   	var returnInvoker = new ReturnInvoker( this );
   	this.menuReturn.addEvent( 'click', returnInvoker.invoke );
    this.menuReturn.inject( this.menuContainer );
    
    // header
    this.menuHeader = new Element('div', {id: this.menuHeaderId});  
    this.menuHeader.addClass( 'header' );
    this.menuHeader.inject( this.menuContainer );

    // body
    this.menuBody = new Element('div'); 
    this.menuBody.addClass( 'body' );
    this.menuBody.inject( this.menuContainer );
    
    // categories
    this.menuCategories = new Element('div', {id: this.menuCategoriesId}); 
    this.menuCategories.addClass( 'categories' );
    this.menuCategories.addClass( 'ndisplayed' );	
    this.menuCategories.inject( this.menuBody );
    // categories scroll up
    this.menuCategoriesUp = new Element('a', {unselectable: 'on'}); 
    this.menuCategoriesUp.addClass( 'scroll-up' );
    this.menuCategoriesUp.addClass( 'disabled' );
    // invoker for up scroller
    function CategoriesUpInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnCategoriesUp();	
        }
    };
   	var categoriesUpInvoker = new CategoriesUpInvoker( this );
   	this.menuCategoriesUp.addEvent( 'click', categoriesUpInvoker.invoke );
    this.menuCategoriesUp.inject( this.menuCategories );
    // categories list
    this.menuCategoriesList = new Element('ul'); 
    this.menuCategoriesList.inject( this.menuCategories );
    // categories scroll down
    this.menuCategoriesDown = new Element('a', {unselectable: 'on'}); 
    this.menuCategoriesDown.addClass( 'scroll-down' );
    this.menuCategoriesDown.addClass( 'disabled' );
    // invoker for down scroller
    function CategoriesDownInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnCategoriesDown();	
        }
    };
   	var categoriesDownInvoker = new CategoriesDownInvoker( this );
   	this.menuCategoriesDown.addEvent( 'click', categoriesDownInvoker.invoke );
    this.menuCategoriesDown.inject( this.menuCategories );
    
    // objects
    this.menuObjects = new Element('div', {id: this.menuObjectsId}); 
    this.menuObjects.addClass( 'objects' );
    this.menuObjects.addClass( 'ndisplayed' );	
    this.menuObjects.inject( this.menuBody );
    // objects header
    this.menuObjectsHeader = new Element('h3'); 
    this.menuObjectsHeader.inject( this.menuObjects );
    // objects scroll up
    this.menuObjectsUp = new Element('a', {unselectable: 'on'}); 
    this.menuObjectsUp.addClass( 'scroll-up' );
    this.menuObjectsUp.addClass( 'disabled' );
    // invoker for up scroller
    function ObjectsUpInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnObjectsUp();	
        }
    };
   	var objectsUpInvoker = new ObjectsUpInvoker( this );
   	this.menuObjectsUp.addEvent( 'click', objectsUpInvoker.invoke );
    this.menuObjectsUp.inject( this.menuObjects );
    // objects list
    this.menuObjectsList = new Element('ul'); 
    this.menuObjectsList.inject( this.menuObjects );
    // objects scroll down
    this.menuObjectsDown = new Element('a', {unselectable: 'on'}); 
    this.menuObjectsDown.addClass( 'scroll-down' );
    this.menuObjectsDown.addClass( 'disabled' );
    // invoker for down scroller
    function ObjectsDownInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnObjectsDown();	
        }
    };
   	var objectsDownInvoker = new ObjectsDownInvoker( this );
   	this.menuObjectsDown.addEvent( 'click', objectsDownInvoker.invoke );
    this.menuObjectsDown.inject( this.menuObjects );
    
    // search menu
    
    this.menuSearch = new Element('div'); 
    this.menuSearch.addClass( 'search' );
    this.menuSearch.inject( this.menuBody );
    // search menu text
    this.menuSearchForm = new Element('form', {action: 'return false;'} ); 
    this.menuSearchText = new Element('input',{type: 'text'}); 
    this.menuSearchText.addClass( 'text' );
    // invoker for search keydown
    function OnSearchSubmitInvoker( obj ) {
        var reference = obj;
        this.invoke = function( event ) {
            //if(!event) event = window.event;
       	    reference.OnSearch();
       	    return false;	
        }
    };
   	var searchSubmitInvoker = new OnSearchSubmitInvoker( this );
   	this.menuSearchForm.addEvent( 'submit', searchSubmitInvoker.invoke );
    this.menuSearchText.inject( this.menuSearchForm );
    this.menuSearchForm.inject( this.menuSearch );
    // search menu button
    this.menuSearchButton = new Element('input',{type: 'button'}); 
    this.menuSearchButton.addClass( 'button' );
    // invoker for search
    function OnSearchInvoker( obj ) {
            var reference = obj;
            this.invoke = function( event ) {
       	        reference.OnSearch();	
       	        
        }
    };
   	var searchInvoker = new OnSearchInvoker( this );
   	this.menuSearchButton.addEvent( 'click', searchInvoker.invoke );
    this.menuSearchButton.inject( this.menuSearchForm );
    
    
    // status bar
    var status = new StatusBar( this.menuStatusId, 'loading', 198, 16 );
    this.statusBar = status.generate();
    this.statusBar.inject( this.menuContainer );
    
    
    // insert map into dom model
    this.menuContainer.inject( this.container );
    
    
    // invoker function pointing to `this` used as a callback for AJAX      
    function SetLangDataInvoker( obj ) {
            var reference = obj;
            this.invoke = function( jsonData ) {
       	        reference.SetLangData( jsonData );	
        }
    };
        
    var SetLangData = new SetLangDataInvoker( this );
    
    this.statusBar.init();
    var request = new AJAX( this.phpHandler+'?action=getLangData', SetLangData.invoke );
    request.run();
};

GoogleMap.prototype.SetLangData = function( jsonData ) {
    if( !jsonData ) return;
    
	this.statusBar.stop();
	     
	this.langData = new Object();
	this.langData.menuHeader    = jsonData.lang.menuHeader;
	this.langData.returnButton  = jsonData.lang.returnButton;
	this.langData.objectsHeader = jsonData.lang.objectsHeader;
	this.langData.hrefUp        = jsonData.lang.hrefUp;
	this.langData.hrefDown      = jsonData.lang.hrefDown;
	this.langData.searchButton  = jsonData.lang.searchButton;
	this.langData.objectAnchor  = jsonData.lang.objectAnchor;
	
	this.menuHeader.appendText( this.langData.menuHeader ); 
	this.menuReturnHref.appendText( this.langData.returnButton ); 
	this.menuObjectsHeader.appendText( this.langData.objectsHeader );
    this.menuObjectsUp.appendText( this.langData.hrefUp );
    this.menuObjectsDown.appendText( this.langData.hrefDown );
    this.menuCategoriesUp.appendText( this.langData.hrefUp );
    this.menuCategoriesDown.appendText( this.langData.hrefDown );
    this.menuSearchButton.set( 'value', this.langData.searchButton );
    this.infoWindowAnchor.appendText( this.langData.objectAnchor );


    // now can build initial menu
    // invoker function pointing to `this` used as a callback for AJAX      
    function FillMenuInvoker( obj ) {
            var reference = obj;
            this.invoke = function( jsonData ) {
       	        reference.FillMenu( jsonData );	
        }
    };
        
    // remember because we don't whant to create too many clousures
    this.fillMenuInvoker = new FillMenuInvoker( this );

    this.statusBar.init();
    var request = new AJAX( this.phpHandler+'?action=getMenuData&catA='+this.initialCategory+'&catB='+this.initialSubCategory, this.fillMenuInvoker.invoke );
    request.run();
};

GoogleMap.prototype.FillMenu = function( jsonData ) {
    if( !jsonData ) return;
    
	this.statusBar.stop();

    // categories menu
    if( !jsonData.categories ) {
        this.menuCategories.addClass( 'ndisplayed' );	
    } else {
        this.menuCategories.addClass( 'ndisplayed' );	
    	
        if( jsonData.categoriesDisplayed ) {
        	this.categoriesDisplayed    = jsonData.categoriesDisplayed;
        }
        
        this.menuCategoriesListData = jsonData.categories;

        this.menuCategoriesList.empty();
        this.menuCategoriesUp.addClass( 'disabled' );
        this.menuCategoriesDown.addClass( 'disabled' );

        function CategoryClickInvoker( obj, i ) {
            var reference = obj;
            var id        = i;
            this.invoke = function( event ) {
       	            reference.OnCategoryClick( id );	
            }
        };
        
        for( i=0, iEnd=this.menuCategoriesListData.length; i<iEnd; i++ ) {
    	    var categoryLi = new Element('li', {unselectable: 'on'});
    	    var categoryA  = new Element('a');
    	    categoryA.appendText( this.menuCategoriesListData[i].name );
    	    // category click invoker
    	    var categoryClickInvoker = new CategoryClickInvoker( this, i );
    	    categoryA.addEvent( 'click', categoryClickInvoker.invoke );
    	    if( i >= this.categoriesDisplayed ) {
    	        categoryLi.addClass( 'ndisplayed' );	
    	    }
    	    categoryA.inject( categoryLi );
    	    categoryLi.inject( this.menuCategoriesList );
        }
        this.menuCategoriesListPointer = 0;
        
        // need to display scrollers?
        if( this.menuCategoriesListData.length <= this.categoriesDisplayed ) {
            this.menuCategoriesUp.addClass( 'ndisplayed' );
            this.menuCategoriesDown.addClass( 'ndisplayed' );	
        } else {
            this.menuCategoriesUp.removeClass( 'ndisplayed' );
            this.menuCategoriesDown.removeClass( 'ndisplayed' );
            this.menuCategoriesDown.removeClass( 'disabled' ); 	
        }

        // if selected flag is set
        if( jsonData.categorySelected != undefined && jsonData.categorySelected != -1 ) {
            // set selected
            this.menuCategoriesList.childNodes[jsonData.categorySelected].addClass('selected');
            // scroll seleted at top position
            for(i=0; i<jsonData.categorySelected;i++) {
                this.OnCategoriesDown();
            }
        }
        
        if( jsonData.categorySelectedName ) {
        	this.menuHeader.empty();
	        this.menuHeader.appendText( this.langData.menuHeader+' - '+jsonData.categorySelectedName ); 
        } else {
        	this.menuHeader.empty();
	        this.menuHeader.appendText( this.langData.menuHeader ); 
        }
        
        this.menuCategories.removeClass( 'ndisplayed' );	
    }
    
    if( jsonData.objectsDisplayed == undefined || jsonData.objects == undefined ) return;
    // objects menu
    if( jsonData.objects.length == 0 ) {
        this.menuObjects.addClass( 'ndisplayed' );	
    } else {
        this.menuObjects.addClass( 'ndisplayed' );	
        this.objectsDisplayed    = jsonData.objectsDisplayed;
        this.menuObjectsListData = jsonData.objects;
    
        this.menuObjectsList.empty();
        this.menuObjectsUp.addClass( 'disabled' );
        this.menuObjectsDown.addClass( 'disabled' );

        function ObjectClickInvoker( obj, i ) {
            var reference = obj;
            var id        = i;
            this.invoke = function( event ) {
       	        reference.OnObjectClick( id );	
            }
        };
    
    
        for( i=0, iEnd=this.menuObjectsListData.length; i<iEnd; i++ ) {
    	    var objectLi = new Element('li', {unselectable: 'on'});
    	    var objectA  = new Element('a');
    	    objectA.appendText( this.menuObjectsListData[i].name );
    	    // object click invoker
    	    var objectClickInvoker = new ObjectClickInvoker( this, i );
    	    objectA.addEvent( 'click', objectClickInvoker.invoke );
    	    if( i >= this.objectsDisplayed ) {
    	        objectLi.addClass( 'ndisplayed' );	
    	    }
    	    objectA.inject( objectLi );
    	    objectLi.inject( this.menuObjectsList );
        }
        this.menuObjectsListPointer = 0;
    
        // need to display scrollers?
        if( this.menuObjectsListData.length <= this.objectsDisplayed ) {
            this.menuObjectsUp.addClass( 'ndisplayed' );
            this.menuObjectsDown.addClass( 'ndisplayed' );	
        } else {
            this.menuObjectsUp.removeClass( 'ndisplayed' );
            this.menuObjectsDown.removeClass( 'ndisplayed' );
            this.menuObjectsDown.removeClass( 'disabled' ); 	
        }
        this.menuObjects.removeClass( 'ndisplayed' );	
    }
    
    // return button setup
    if( this.returnCategory ==0 && this.returnSubCategory == 0 ) {
       this.menuReturn.addClass( 'nvisible' );
	} else {
       this.menuReturn.removeClass( 'nvisible' );
	}
	
	if( !jsonData.initialObject ) return;
	if( this.initialObjectDisplayed ) return; 
	
    this.initialObjectDisplayed  = true;
	var object = jsonData.initialObject;
	
	if( object.lat != 0 && object.lng != 0 ) {
		object.latlng = new GLatLng( object.lat, object.lng );
    	this.OnLocalized( object );
    } else {
    	// if alredy geocoded
    	if( object.latlng ) {
    		this.OnLocalized( object );
    		return;
    	}
        // get coords
        if( this.geocoder ) {
            // invoker function pointing to `this` used as a callback for geocoder      
            function LocalizeInvoker( ref, o ) {
                var reference  = ref;
                var objectData = o;
                this.invoke = function( localizedPoint ) {
                	reference.statusBar.stop();
                	objectData.latlng = localizedPoint;
            	    reference.OnLocalized( objectData );	
                }
            };
        
            var gecodeQuery = 'polska, '+object.city+', '+object.street;
            var localizeInvoker = new LocalizeInvoker( this, object );
            this.statusBar.init();
            this.geocoder.getLatLng( gecodeQuery, localizeInvoker.invoke );
        }    
    }
};

///////////////////////////////////////////////////////////////////////////////////////////////

GoogleMap.prototype.OnReturn = function() {

	// setup return button
	if( this.returnSubCategory == 0 ) {
		this.returnCategory = 0;
	} else {
		this.returnSubCategory = 0;
	}

	//alert(this.returnCategory+' '+this.returnSubCategory);	
	
    this.statusBar.init();
    var request = new AJAX( this.phpHandler+'?action=getMenuData&catA='+this.returnCategory+'&catB='+this.returnSubCategory, this.fillMenuInvoker.invoke );
    request.run();
};

GoogleMap.prototype.OnSearch = function() {
    //alert( this.menuSearchText.value );	
	
    this.returnCategory    = 1;
    this.returnSubCategory = 0;
    
    this.statusBar.init();
    var request = new AJAX( this.phpHandler+'?action=getMenuSearchData&catA='+this.menuSearchText.value, this.fillMenuInvoker.invoke );
    request.run();
};

///////////////////////////////////////////////////////////////////////////////////////////////

GoogleMap.prototype.OnCategoriesUp = function() {
	if( this.menuCategoriesUp.hasClass( 'disabled' ) ) return;
	
	this.menuCategoriesListPointer--;

	this.menuCategoriesList.childNodes[this.menuCategoriesListPointer].removeClass( 'ndisplayed' );
	this.menuCategoriesList.childNodes[this.menuCategoriesListPointer+this.categoriesDisplayed].addClass( 'ndisplayed' );
	
	if( this.menuCategoriesListPointer == 0 ) {
	    this.menuCategoriesUp.addClass( 'disabled' );	
	}
	if( this.menuCategoriesListPointer + this.categoriesDisplayed < this.menuCategoriesListData.length ) {
	    this.menuCategoriesDown.removeClass( 'disabled' );	
	}
};

GoogleMap.prototype.OnCategoriesDown = function() {
	if( this.menuCategoriesDown.hasClass( 'disabled' ) ) return;
	
	this.menuCategoriesList.childNodes[this.menuCategoriesListPointer].addClass( 'ndisplayed' );
	this.menuCategoriesList.childNodes[this.menuCategoriesListPointer+this.categoriesDisplayed].removeClass( 'ndisplayed' );
	
	this.menuCategoriesListPointer++;
	
	if( this.menuCategoriesListPointer > 0 ) {
	    this.menuCategoriesUp.removeClass( 'disabled' );	
	}
	if( this.menuCategoriesListPointer + this.categoriesDisplayed == this.menuCategoriesListData.length ) {
	    this.menuCategoriesDown.addClass( 'disabled' );	
	}
};

GoogleMap.prototype.OnCategoryClick = function( categoryId ) {
    var category = this.menuCategoriesListData[categoryId].category;
    var subCategory = this.menuCategoriesListData[categoryId].subCategory;
	
	//alert(category+' '+subCategory);	

	// setup return button
	this.returnCategory    = category;
	this.returnSubCategory = subCategory;
	
    this.statusBar.init();
    var request = new AJAX( this.phpHandler+'?action=getMenuData&catA='+category+'&catB='+subCategory, this.fillMenuInvoker.invoke );
    request.run();
};

///////////////////////////////////////////////////////////////////////////////////////////////

GoogleMap.prototype.OnObjectsUp = function() {
	if( this.menuObjectsUp.hasClass( 'disabled' ) ) return;
	
	this.menuObjectsListPointer--;

	this.menuObjectsList.childNodes[this.menuObjectsListPointer].removeClass( 'ndisplayed' );
	this.menuObjectsList.childNodes[this.menuObjectsListPointer+this.objectsDisplayed].addClass( 'ndisplayed' );
	
	if( this.menuObjectsListPointer == 0 ) {
	    this.menuObjectsUp.addClass( 'disabled' );	
	}
	if( this.menuObjectsListPointer + this.objectsDisplayed < this.menuObjectsListData.length ) {
	    this.menuObjectsDown.removeClass( 'disabled' );	
	}
};

GoogleMap.prototype.OnObjectsDown = function() {
	if( this.menuObjectsDown.hasClass( 'disabled' ) ) return;
	
	this.menuObjectsList.childNodes[this.menuObjectsListPointer].addClass( 'ndisplayed' );
	this.menuObjectsList.childNodes[this.menuObjectsListPointer+this.objectsDisplayed].removeClass( 'ndisplayed' );
	
	this.menuObjectsListPointer++;
	
	if( this.menuObjectsListPointer > 0 ) {
	    this.menuObjectsUp.removeClass( 'disabled' );	
	}
	if( this.menuObjectsListPointer + this.objectsDisplayed == this.menuObjectsListData.length ) {
	    this.menuObjectsDown.addClass( 'disabled' );	
	}
};

GoogleMap.prototype.OnObjectClick = function( objectId ) {
    var object = this.menuObjectsListData[objectId];
	
	if( object.lat != 0 && object.lng != 0 ) {
		object.latlng = new GLatLng( object.lat, object.lng );
    	this.OnLocalized( object );
    } else {
    	// if alredy geocoded
    	if( object.latlng ) {
    		this.OnLocalized( object );
    		return;
    	}
        // get coords
        if( this.geocoder ) {
            // invoker function pointing to `this` used as a callback for geocoder      
            function LocalizeInvoker( ref, o ) {
                var reference  = ref;
                var objectData = o;
                this.invoke = function( localizedPoint ) {
                	reference.statusBar.stop();
                	objectData.latlng = localizedPoint;
            	    reference.OnLocalized( objectData );	
                }
            };
        
            var gecodeQuery = 'polska, '+object.city+', '+object.street;
            var localizeInvoker = new LocalizeInvoker( this, object );
            this.statusBar.init();
            this.geocoder.getLatLng( gecodeQuery, localizeInvoker.invoke );
        }    
    }
};

GoogleMap.prototype.OnLocalized = function( object ) {
    if( !object.latlng ) {
    	alert('Nie znalazlem!');
    	return;
    }
    
    this.infoWindowName.empty();
    this.infoWindowName.appendText( object.name );
    this.infoWindowAddressLine1.empty();
    this.infoWindowAddressLine1.appendText( object.streetPrefix+' '+object.street );
    if( object.houseNumber ) {
        this.infoWindowAddressLine1.appendText( ' '+object.houseNumber )
    }
    if( object.flatNumber ) {
        this.infoWindowAddressLine1.appendText( '/'+object.flatNumber )
    }
    this.infoWindowAddressLine2.empty();
    this.infoWindowAddressLine2.appendText( object.code+', '+object.city );
    this.infoWindowAnchor.set( 'href', object.url );
    
    // custom marker icons
    var baseIcon = new GIcon(G_DEFAULT_ICON);
    baseIcon.shadow = 'http://www.google.com/mapfiles/shadow50.png';
    baseIcon.iconSize = new GSize(29, 44);
    //baseIcon.shadowSize = new GSize(29, 44);
    baseIcon.iconAnchor = new GPoint(15, 44);
    baseIcon.infoWindowAnchor = new GPoint(15,44);
   	var customIcon = new GIcon(baseIcon);
    
    var marker = null;

    switch( object.type )
    {
    	case 1:
    	    customIcon.image = this.icons+'/marker1.png';
    	    marker = new GMarker( object.latlng, { icon:customIcon } );
     	    break;
    	case 2:
    	    customIcon.image = this.icons+'/marker2.png';
    	    marker = new GMarker( object.latlng, { icon:customIcon } );
     	    break;
    	case 4:
    	    customIcon.image = this.icons+'/marker4.png';
    	    marker = new GMarker( object.latlng, { icon:customIcon } );
     	    break;
    	default:
    	    customIcon.image = this.icons+'/marker.png';
    	    marker = new GMarker( object.latlng, { icon:customIcon } );
    	    break;
    }
    
     
    // invoker function pointing to `this` used as a callback      
    function MarkerClickInvoker( ref, o ) {
        var reference  = ref;
        var objectData = o;
        this.invoke = function() {
        	reference.OnMarkerClick( objectData );	
        }
    };
    var markerClickInvoker = new MarkerClickInvoker( this, object );
    GEvent.addListener( marker,"click", markerClickInvoker.invoke );
    this.mapHandler.setCenter( object.latlng, this.zoomLevel );
    this.mapHandler.addOverlay( marker );
    this.mapHandler.openInfoWindow( this.mapHandler.getCenter(), this.infoWindow );
};

GoogleMap.prototype.OnMarkerClick = function( object ) {
    this.infoWindowName.empty();
    this.infoWindowName.appendText( object.name );
    this.infoWindowAddressLine1.empty();
    this.infoWindowAddressLine1.appendText( object.streetPrefix+' '+object.street );
    if( object.houseNumber ) {
        this.infoWindowAddressLine1.appendText( ' '+object.houseNumber )
    }
    if( object.flatNumber ) {
        this.infoWindowAddressLine1.appendText( '/'+object.flatNumber )
    }
    this.infoWindowAddressLine2.empty();
    this.infoWindowAddressLine2.appendText( object.code+', '+object.city );
    this.infoWindowAnchor.set( 'href', object.url );

    this.mapHandler.setCenter( object.latlng, this.zoomLevel );
    this.mapHandler.openInfoWindow( this.mapHandler.getCenter(), this.infoWindow );
};

/* StatusBar */
function StatusBar( id, text, width, height )
{
    this.id         = id;
    this.text       = text;
    this.width      = width;
    this.height     = height;
    this.barWidth  = 40;
    this.barHeight = height-2;
    this.barTop    = 1;
    this.barStart  = -this.barWidth;
    
    this.step     = 3;
    this.dir      = true;
    this.interval = null;
    this.div      = null;
    this.span     = null;
};

StatusBar.prototype.generate = function() {
    var statusBar = new Element('div', {id: this.id});  
    statusBar.addClass( 'status-bar' );
    
    var span = new Element('span', {id: this.id+'_text'}); 
    span.setStyle( 'width', this.width+'px' );
    span.setStyle( 'height', this.height+'px' );
    span.setStyle( 'left', '0px' );
    span.setStyle( 'display', 'none' );
    span.appendText( this.text );
    span.inject( statusBar );

    
    var div = new Element('div', {id: this.id+'_bar'});  
    div.setStyle( 'width', this.barWidth+'px' );
    div.setStyle( 'height', this.barHeight+'px' );
    div.setStyle( 'top', this.barTop+'px' );
    div.setStyle( 'left', this.barStart+'px' );
    div.left = this.barStart;
    div.setStyle( 'display', 'none' );
    div.inject( statusBar );
 
    statusBar.init = this.__init;
    statusBar.stop = this.__stop;
    statusBar.obj  = this;
    statusBar.div  = div;
    statusBar.span = span;

    return statusBar;
};

StatusBar.prototype.__init = function() {
    var bar  = $(this.obj.id);
    
    this.span.setStyle( 'display', 'block' );
    this.div.setStyle( 'display', 'block' );

    this.obj.interval = setInterval( function() { StatusBar.__move( bar ); }, 25 );
};

StatusBar.prototype.__stop = function() {
    clearInterval( this.obj.interval );
    this.div.setStyle( 'left', this.obj.barStart+'px' );
    this.div.left           = this.obj.barStart;
    this.span.setStyle( 'display', 'none' );
    this.div.setStyle( 'display', 'none' );
};

StatusBar.__move = function( statusBar ) {
        var div = statusBar.div;
        var obj = statusBar.obj;
        
        if( obj.dir ) {
            div.left += obj.step;
            div.setStyle( 'left', div.left+'px' );
        } else {
            div.left -= obj.step;
            div.setStyle( 'left', div.left+'px' );
        }
        
        if( div.left >= obj.width + obj.barWidth ) 
            obj.dir = false;
        else if( div.left <= -obj.barWidth )
            obj.dir = true;
};


/* AJAX */
function AJAX( url, callback ) {
    this.url      = url;
    this.callback = callback;
};

AJAX.prototype.url      = null;
AJAX.prototype.callback = null;

AJAX.prototype.run = function() {
    var xmlhttp  = null;
    var callback = this.callback; 
    if (window.XMLHttpRequest) {
       // code for Mozilla, etc.
       xmlhttp = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        // code for IE
        xmlhttp = new ActiveXObject("msxml2.xmlhttp");
    }
    
    xmlhttp.onreadystatechange = function() {
        
        if(xmlhttp.readyState != 4) return;
		    if(xmlhttp.status != 200 && xmlhttp.status != 304) {
                  //alert('HTTP error ' + req.status);
			      return;
		    }
		    var xmldoc = xmlhttp.responseXML;
		    //alert(xmlhttp.responseText);
            var json = xmldoc.getElementsByTagName('json')[0].firstChild.nodeValue;
		    var data = eval('('+json+')');
            callback(data);    
    };

    xmlhttp.open("GET",this.url,true);
    xmlhttp.send(null);
};

