function GoogleMap(id) {
    var map = {
        'id': id,
        'html': null,
        'google': null,
        'xcoord': 0,
        'ycoord': 0,
        'zoom': 2,
        'type': 'frontend'
    }

    /* types:
     * frontend,
     * backend,
     * map-thumb
    */

    var module = {
        'markerList': false,
        'pointer': false,
        'geoSearch': false,
        'filter': false,
        'ZoomDisplay': false,
        'KML': false
    }

    var pointer = null;
    var filterID = null;

    var geoSearch = {
        'obj': null,
        'text': ''
    }

    var KML = {
        'file': 'http://'+window.location.hostname+'/kml/landkarten.kml',
        'obj': null,
        'sheet': null,
        'title': '',
        'desc': '',
        'show': false,
        'overlay': null
    }

    var sheets = {
        'Europa': [],
        'Asien': [],
        'Afrika': [],
        'Amerika': [],
        'Australien': []
    };

    /* * * * * * * * * * * */


    var searchText = '';

    var newMarker = [];
    var i = 0;
    var markerArray = {
        'landkarten': [],
        'sprachfuehrer': [],
        'reisefuehrer': [],
        'musik': [],
        'sachbuecher': [],
        'countries': [],
        'continents': []
    };

    var markerManager = {
        'landkarten': null,
        'sprachfuehrer': null,
        'reisefuehrer': null,
        'musik': null,
        'sachbuecher': null,
        'countries': null,
        'continents': null
    };

    var sidebar = null;
    var searchObj = null;
    var counter = 0;
    var hovered = null;

    /* * * * * * * * * * * */


    this.setX = function(x) {
        map['xcoord'] = x;
    }

    this.setY = function(y) {
        map['ycoord'] = y;
    }

    this.setZ = function(z) {
        map['zoom'] = z;
    }

    this.setType = function(type) {
        map['type'] = type;
    }

    this.jumpToPoint = function(cat, i, id) {
        map['google'].setCenter(
            markerArray[cat][i]["marker"].getLatLng(),
            markerArray[cat][i]["zmin"]
        );

        for(var x in markerArray) {
            for(k=0; k<markerArray[x].length; k++) {
                markerArray[x][k]["active"] = 0;
            }
        }

        markerArray[cat][i]["active"] = 1;

        GEvent.trigger(markerArray[cat][i]["marker"], 'hover');


        if(hovered!=null && !hovered.isHidden()) {
            hovered.unhover();
            hovered = null;
        }

        hovered = markerArray[cat][i]["marker"];

        hovered.hover();
    }

    this.jumpPointerToLatLng = function(e, obj) {
        if(e.keyCode!=13)
            return;

        var x = pointer.getLatLng().lat();
        var y = pointer.getLatLng().lng();

        if(obj.name=='xcoord') {
            x = floatval(str_replace(',', '.', obj.value));
        }

        if(obj.name=='ycoord')
            y = floatval(str_replace(',', '.', obj.value));

        var new_point = new GLatLng(x, y);

        pointer.setLatLng(new_point);

        map['google'].setCenter(new_point);
        _getCoordinates();
        return;
    }

    this.jumpToCountry = function(cID) {
        for(i=0;i<CountrySelectionArray.length;i++) {
            if(CountrySelectionArray[i]['id']==cID) {
                var c = CountrySelectionArray[i];

                if(hovered!=null && !hovered.isHidden()) {
                    hovered.unhover();
                    hovered = null;
                }

                map['google'].setCenter(new GLatLng(c['xcoord'], c['ycoord']), c['zoom']);

                return;
            }
        }
    }

    this.getZoom = function() {
        return map['google'].getZoom();
    }

    this.render = function() {
        if(KML['sheet']!=null) {
            GDownloadUrl(KML['file'], function(data, responseCode) {
                _findSheet(data);
                _loadMap();
            });
        } else {
            _loadMap();
        }
    }

    this.setMarkerList = function() {
        module['markerList'] = true;
    }

    this.setZoomDisplay = function() {
        module['ZoomDisplay'] = true;
    }

    this.setGeoSearch = function(text) {
        module['geoSearch'] = true;
        geoSearch['text'] = text;
    }

    this.setPointer = function(xcoord, ycoord, zoom) {
        module['pointer'] = true;
        map['xcoord'] = xcoord;
        map['ycoord'] = ycoord;
        map['zoom'] = zoom | 2;
    }

    this.setMarker = function (x, y, Title, Tag, minZoom, maxZoom, style, pID, ID, img) {
        var newMarker =
            new RKH_Marker(new GLatLng(x, y), style, Title);

        if(style=='countries' || style=='continents') {
            _setCountryMarker(x, y, maxZoom, Title, newMarker);
        }
        else {
            _setProductMarker(ID, pID, style, Tag, Title, newMarker, img);
        }

        var i = markerArray[style].length;

        markerArray[style][i] = new Object();
        markerArray[style][i]["marker"] = newMarker;
        markerArray[style][i]["zmin"] = minZoom;
        markerArray[style][i]["zmax"] = maxZoom;
        markerArray[style][i]["title"] = Title;

        counter++;
    }

    this.setFilter = function(id) {
        module['filter'] = true;
        filterID = id;
    }

    this.toggleMarkers = function(name, status) {
        if(status) {
            markerManager[name].show();
            counter = counter + markerArray[name].length;
        }
        else {
            markerManager[name].hide();
            counter = counter - markerArray[name].length;
        }

        _updateMarkerList();
    }

    this.backendJump = function(pID, ID) {
        var url = document.markers.action;

        url = url + '&newID='+ ID + '&newPID=' + pID;

        document.markers.action = url;


        document.markers.submit();
    }

    this.setKML = function(sheet) {
        module['KML'] = true;

        KML['overlay'] = new GGeoXml(KML['file']);

        if(sheet!=null) {
            KML['sheet'] = sheet;
            KML['show'] = true;
        }
    }

    this.toggleMap = function(type) {
        if(!module['KML'] || !module['markerList'])
            return;

        if(type=='products' && KML['show']) {
            _unloadSheets();
            document.getElementById('MARKERLISTC').style.display = 'block';
            _loadMarkers();
            sidebar.activate();
            map['google'].closeInfoWindow();
            KML['show'] = false;
            _updateMarkerList();
            return;
        }

        if(type=='maps' && !KML['show']) {
            _unloadMarkers();
            document.getElementById('MARKERLISTC').style.display = 'none';
            _loadSheets();
            sidebar.deactivate();
            KML['show'] = true;
            return;
        }
    }

    function _unloadSheets() {
        map['google'].removeOverlay(KML['overlay']);

        if(KML['obj']!=null)
            map['google'].removeOverlay(KML['obj']);

        /*for(var name in sheets) {
            for(var i=0; i<sheets[name].length;i++) {
                map['google'].removeOverlay(sheets[name][i]['obj']);
            }
        }*/
    }

    function _findSheet(data) {
        var xml = GXml.parse(data);

        var placemarks = xml.documentElement.getElementsByTagName('Placemark');

        for(var i=0; i<placemarks.length; i++) {
            if(placemarks[i].getAttribute('id')==KML['sheet']) {
                /* Koordinaten ermitteln */
                var coords = GXml.value(placemarks[i].getElementsByTagName('coordinates')[0]);

                coords = coords.split(',0 ');
                coords.pop();

                var COORDS = new Array();

                for(var x=0; x<coords.length;x++) {
                    var latlng = coords[x].split(',');

                    var lat = latlng[1];
                    var lng = latlng[0];

                    COORDS.push(new GLatLng(lat, lng));
                }


                /* Füll-Farbe ermitteln */
                var style =
                    GXml.value(placemarks[i].getElementsByTagName('styleUrl')[0]);

                style = style.replace(/#/, '');

                var styles = xml.documentElement.getElementsByTagName("Style");

                for(var z=0; z<styles.length;z++) {
                    if(styles[z].getAttribute('id')==style) {
                        var polyStyle =
                            styles[z].getElementsByTagName('PolyStyle')[0];

                        var lineStyle =
                            styles[z].getElementsByTagName('LineStyle')[0];

                        var background =
                            '#'+
                            GXml.value(polyStyle.getElementsByTagName('color')[0]).slice(6, 8)+
                            GXml.value(polyStyle.getElementsByTagName('color')[0]).slice(4, 6)+
                            GXml.value(polyStyle.getElementsByTagName('color')[0]).slice(2, 4);

                        var fillOpa =
                            GXml.value(polyStyle.getElementsByTagName('color')[0]).slice(0, 2);
                        fillOpa = 100/255*hexdec(fillOpa);
                        fillOpa = round(fillOpa, 0);
                        fillOpa = fillOpa/100;

                        var lineColor =
                            '#'+
                            GXml.value(lineStyle.getElementsByTagName('color')[0]).slice(6, 8)+
                            GXml.value(lineStyle.getElementsByTagName('color')[0]).slice(4, 6)+
                            GXml.value(lineStyle.getElementsByTagName('color')[0]).slice(2, 4);

                        var lineOpa =
                            GXml.value(lineStyle.getElementsByTagName('color')[0]).slice(0, 2);
                        lineOpa = 100/255*hexdec(lineOpa);
                        lineOpa = round(lineOpa, 0);
                        lineOpa = lineOpa/100;


                        var lineWidth =
                            GXml.value(lineStyle.getElementsByTagName('width')[0]);

                    }
                }


                /* Objekt erzeugen */
                KML['obj'] =
                    new GPolygon(COORDS, '#fff', lineWidth, 1, background, fillOpa);

                KML['title'] =
                    GXml.value(placemarks[i].getElementsByTagName('name')[0]);

                KML['desc'] =
                    GXml.value(placemarks[i].getElementsByTagName('description')[0]);

                    /* new GPolygon(COORDS, '#000', 1, 1, '#fff', 1); */
            }
        }
    }

    function _loadSheets() {
        map['google'].addOverlay(KML['overlay']);

        if(KML['obj']!=null) {
            setTimeout(function() {
                map['google'].addOverlay(KML['obj']);

                KML['obj'].setFillStyle({opacity: 0});

                GEvent.addListener(KML['obj'], 'click', function(point) {
                    map['google'].openInfoWindowHtml(point, '<h2 style="margin: 0px; font: bold 16px arial, sans-serif">'+KML['title']+'</h2><p style="margin: 0px; font: normal 13px arial, sans-serif;">'+KML['desc']+'</p>');
                });
            }, 1200);
        }

        /*
        for(var name in sheets) {
            for(var i=0; i<sheets[name].length;i++) {
                map['google'].addOverlay(sheets[name][i]['obj']);
            }
        }*/
    }

    function _getCoordinates() {
        if(!module['pointer'])
            return;

        document.getElementById('xcoord').value =
            ''+pointer.getLatLng().lat();

        document.getElementById('ycoord').value =
            ''+pointer.getLatLng().lng();
    }

    function _getZoom() {
        if(!module['pointer'])
            return;

        var zmin = document.getElementById('zmin');
        var zmax = document.getElementById('zmax');

        if(!zmin.readOnly)
            zmin.value = map['google'].getZoom();

        if(!zmax.readOnly)
            zmax.value = map['google'].getZoom();
    }

    function _loadMap() {
        map['html'] = document.getElementById(map['id']);
        
        if(map['type']=='frontend' && map['html'].offsetWidth>0)
            map['html'].style.height = map['html'].offsetWidth;
        else {
            map['html'].style.height = '581px';
            map['html'].style.width = '581px';
        }
        
        if(map['type']!='map-thumb')
            map['google'] = new GMap2(map['html']);

        if(map['type']=='map-thumb')
            map['google'] = new GMap2(map['html'], {size: new GSize(155, 100), draggableCursor: 'pointer', draggingCursor: 'pointer'});

        if(KML['obj']!=null) {
            map['xcoord'] = KML['obj'].getBounds().getCenter().lat();
            map['ycoord'] = KML['obj'].getBounds().getCenter().lng();

            map['zoom'] = map['google'].getBoundsZoomLevel(KML['obj'].getBounds())-1;
        }

        map['google'].setCenter(new GLatLng(map['xcoord'], map['ycoord']), map['zoom'], G_PHYSICAL_MAP);


        if(map['type']=='frontend') {
            GEvent.addListener(map['google'], "move", function() {
                _updateMarkerList();
            });

            GEvent.addListener(map['google'], "zoomend", function() {
                _updateMarkerList();
            });
        }

        if(map['type']=='backend') {
            GEvent.addListener(map['google'], 'moveend', function() {
                _getZoom();
            });
        }

        if(map['type']!='map-thumb') {
            /* Navigationsleiste anzeigen, Kartentyp festlegen */
            map['google'].addControl(new GLargeMapControl());

            /* Maßstab
            //map['google'].addControl(new GScaleControl()); */

            /* Mini-Karte
            //map['google'].addControl(new GOverviewMapControl()); */

            /* Zoom */
            _loadDragZoomControl();

            _loadSearchControl();
            _loadZoomDisplay();

            _loadPointer();
            _loadSidebar();
        } else {
            map['google'].disableDragging();
            GEvent.addListener(map['google'], 'click', function() {
                window.location.href = "world.php?kmlSheet="+urlencode(KML['sheet']);
            });
        }

        /* Kartentyp-Auswahl */
         if(map['type']=='frontend') {
            map['google'].addMapType(G_PHYSICAL_MAP);
            map['google'].addMapType(G_NORMAL_MAP);
            map['google'].addMapType(G_SATELLITE_MAP);
            map['google'].removeMapType(G_HYBRID_MAP);

            map['google'].addControl(new GMapTypeControl());
        }

        if(!KML['show']) _loadMarkers();


        if(KML['show'] && map['type']=='map-thumb')
            map['google'].addOverlay(KML['obj']);


        if(KML['show'] && map['type']=='frontend')
            _loadSheets();

        _updateMarkerList();
    }

    function unloadCurrentSheet() {
        map['google'].removeOverlay(KML['obj']);
    }

    function _loadPointer() {
        if(!module['pointer'])
            return;

        pointer =
            new RKH_Marker(new GLatLng(map['xcoord'], map['ycoord']), 'setting');

        GEvent.addListener(pointer, 'drag', function() {
            _getCoordinates();
        });

        GEvent.addListener(pointer, 'dragend', function() {
            map['google'].panTo(pointer.getLatLng());
        });

        map['google'].addOverlay(pointer);
    }

    function _loadZoomDisplay() {
        if(!module['ZoomDisplay'])
            return;

        var container = document.createElement('input');

        var idNode = document.createAttribute('id');
        idNode.nodeValue = 'GoogleMap-ZoomDisplay';

        var valueNode = document.createAttribute('value');
        valueNode.nodeValue = map['google'].getZoom();

        var valueDisabled = document.createAttribute('disabled');
        valueDisabled.nodeValue = 'disabled';

        container.setAttributeNode(idNode);
        container.setAttributeNode(valueNode);
        container.setAttributeNode(valueDisabled);

        map['html'].appendChild(container);

        GEvent.addListener(map['google'], "zoomend", function() {
            document.getElementById('GoogleMap-ZoomDisplay').value = map['google'].getZoom();
        });
    }

    function _loadSearchControl() {
        if(!module['geoSearch'])
            return;

        var attribValue = document.createAttribute('value');
        attribValue.nodeValue = geoSearch['text'];

        var attribStyle = document.createAttribute('style');
        attribStyle.nodeValue = 'margin-top: 5px;';

        searchObj = document.createElement('input');
        searchObj.setAttributeNode(attribValue);
        searchObj.setAttributeNode(attribStyle);


        var label = document.createElement('label');

        var attribStyle = document.createAttribute('style');
        attribStyle.nodeValue =
            'float: left; font-weight: bold; margin: 5px 10px 0px 5px;';

        var labelText = document.createTextNode('Geo-Suche:');

        label.setAttributeNode(attribStyle);
        label.appendChild(labelText);


        var attribStyle = document.createAttribute('style');
        attribStyle.nodeValue =
            'border: #000 1px solid; background: #fff; ' +
            'position: absolute; right: 15px; top: 7px; ' +
            'height: 28px; display: block; width: 240px;';

        var container = document.createElement('div');
        container.setAttributeNode(attribStyle);
        container.appendChild(label);
        container.appendChild(searchObj);

        map['html'].appendChild(container);

        searchObj.focus();

        function SearchControl() {
        }

        GEvent.addDomListener(searchObj, 'keypress', function(e) {
            if(e.keyCode==13) {
                _searchAddress(searchObj);
            }
        });
    }

    function _loadSidebar() {
        if(!module['filter'])
            return;

        sidebar = new GoogleMapsSidebar(filterID, counter);

        sidebar.addCategory(utf8_decode('Reiseführer'), 'reisefuehrer');
        sidebar.addCategory('Landkarten');
        sidebar.addCategory(utf8_decode("Sprachführer"), 'sprachfuehrer');
        sidebar.addCategory(utf8_decode('Sachbücher'), 'sachbuecher');
        sidebar.addCategory('Musik');

        sidebar.render();

        if(KML['show'])
            sidebar.deactivate();
    }

    function _setCountryMarker(x, y, maxZoom, Title, newMarker) {
        GEvent.addListener(newMarker, 'click', function() {
            map['google'].setCenter(new GLatLng(x, y), maxZoom+1);
             UnTip();
        });

        GEvent.addListener(newMarker, 'mouseover', function() {
            Tip('<b>'+Title+'</b>',
                BGCOLOR, '#fffae1', BORDERCOLOR, '#ca006c',
                FONTCOLOR, '#000066');
        });

        GEvent.addListener(newMarker, 'mouseout', function() {
            UnTip();
        });
    }

    function _setProductMarker(ID, pID, style, tag, title, newMarker, img) {
        if(module['pointer']) {
            GEvent.addListener(newMarker, 'click', function() {
                newMarker.openInfoWindowHtml(
                    'Produkt: <b>' + title + '</b><br/>'+
                    'Stichwort: ' + (tag || '<i>nicht vergeben</i>') + '<br/>' +
                    'Typ: ' + (style || '<i>unbekannt</i>') + '<br/>'+
                    '<u style="color: #cc0000; cursor: pointer" ' +
                    'onclick="GoogleMap.backendJump('+pID+', '+ID+')">' +
                    'diesen Marker bearbeiten'+
                    '</u><br/><span style="color: #cc0000;">' +
                    'Hinweis: Der aktuelle Marker wird mit den momentanen ' +
                    'Werten gespeichert.'
                );
            });
        }
        else {
            var infoWindowText =
                '<div class="InfoWindow-frontend">'+
                    '<h2>' + title + '</h2>'+
                    '<img src="./images/'+img+'" />'+
                    'Detailliertere Produktinformationen finden Sie '+
                    '<a href="product_info.php?products_id='+pID+'">hier</a>.'+
                '</div>';

            GEvent.addListener(newMarker, 'click', function() {
                newMarker.openInfoWindowHtml(infoWindowText);
            });

            GEvent.addListener(newMarker, 'hover', function() {
                newMarker.openInfoWindowHtml(infoWindowText);
            });
        }

        GEvent.addListener(newMarker, 'mouseover', function() {
            Tip('<b>'+title+'</b>',
                BGCOLOR, '#fffae1', BORDERCOLOR, '#ca006c',
                FONTCOLOR, '#000066');
        });

        GEvent.addListener(newMarker, 'mouseout', function() {
            UnTip();
        });
    }

    function _updateMarkerList() {
        if(!module['markerList'] || KML['show'])
            return;

        var sector = map['google'].getBounds();

        if(map['google'].getZoom()>4) {
            document.getElementById('MARKERLISTC').style.display = 'block';
        } else {
            document.getElementById('MARKERLISTC').style.display = 'none';
            return;
        }


        var obj = document.getElementById('MarkerList');
        var children = obj.childNodes;

        while(obj.hasChildNodes()) {
            obj.removeChild(obj.lastChild);
        }

        var l = 0;
        for(var cat in markerArray) {
            if(cat=='countries' || cat=='continents')
                continue;

            for(i=0; i<markerArray[cat].length; i++) {
                if(sector.containsLatLng(markerArray[cat][i]["marker"].getLatLng())
                    && map['google'].getZoom() >= markerArray[cat][i]["zmin"]
                    && map['google'].getZoom() <= markerArray[cat][i]["zmax"]
                    && !markerManager[cat].isHidden()) {
                    var li = document.createElement('li');

                    var ClassNodeValue = '';

                    if(l++==0) {
                        var ClassNodeValue = 'first';
                    }

                    if(markerArray[cat][i]["active"]==1) {
                        ClassNodeValue = ClassNodeValue + ' active';
                    }

                    if(ClassNodeValue.length>0) {
                        var Class = document.createAttribute('class');
                        Class.nodeValue = ClassNodeValue;
                        li.setAttributeNode(Class);
                    }


                    var id = document.createAttribute('id');
                        id.nodeValue = 'MarkerList_'+l;

                    li.setAttributeNode(id);


                    var anker = document.createElement('a');

                    var title =
                        str_replace(
                            ['<BR>', '<br/>'],
                            ['<br>', '<br>'],
                            markerArray[cat][i]["title"]
                        );

                    var arr = split('<br>', title);

                    for(k=0;k<arr.length;k++) {
                        var text = document.createTextNode(arr[k]);
                        anker.appendChild(text);

                        if(k<arr.length-1) {
                            var br = document.createElement('br');
                            anker.appendChild(br);
                        }
                    }

                    var href = document.createAttribute('href');
                    href.nodeValue=
                        "javascript:GoogleMap.jumpToPoint('"+cat+"', "+i+", 'MarkerList_"+l+"');";

                    anker.setAttributeNode(href);

                    li.appendChild(anker);
                    obj.appendChild(li);
                }
            }
        }
    }

    function _loadMarkers() {
        for(var name in markerArray) {
            markerManager[name] = new MarkerManager(map['google']);
            
            for(i=0;i<markerArray[name].length;i++) {
                var zmax = markerArray[name][i]['zmax'];
                var zmin = markerArray[name][i]['zmin'];

                if(zmax>17)
                    zmax = 17;

                if(zmin>17)
                    zmin = 17;

                if(zmax<0)
                    zmax = 0;

                if(zmin<0)
                    zmin = 0;
                
                markerManager[name].addMarker(
                    markerArray[name][i]['marker'],
                    zmin, zmax
                );
            }

            markerManager[name].refresh();
        }
    }

    function _unloadMarkers() {
        for(var name in markerManager) {
            markerManager[name].clearMarkers();
        }
    }







    /* * * * * * * * * * * */



    this.searchAddress = function(str, e) {
        if(e.keyCode==13) {
            var geocoder = new GClientGeocoder();

            geocoder.getLatLng(
                str,
                function(point) {
                    if (!point) {
                        alert("Adresse nicht gefunden!");
                    } else {
                        map['google'].setCenter(point, 5);
                    }
                }
            );
        }
    }

    function _loadDragZoomControl() {
        // add zoom control....
        var boxStyleOpts = { opacity: .2, border: "2px solid yellow" };
        var otherOpts = {
            buttonHTML: "<img src='../images/zoom-control-inactive.png' />",
            buttonZoomingHTML: "<img src='../images/zoom-control-active.png' />",
            buttonStartingStyle: {width: '17px', height: '17px'},
            overlayRemoveTime: 0 };

        var callbacks = {};


        if(module['pointer']) {
            var callbacks = {
                dragend: function(nw,ne,se,sw,nwpx,nepx,sepx,swpx) {
                    pointer.setLatLng(map['google'].getCenter());

                    _getCoordinates();
                    _getZoom();
                }
            };
        }

        map['google'].addControl(new DragZoomControl(boxStyleOpts, otherOpts, callbacks),
            new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(27,7)));
    }

    function _searchAddress(obj) {
        var geocoder = new GClientGeocoder();

        geocoder.getLatLng(
            obj.value,
            function(point) {
                if (!point) {
                    alert("Adresse nicht gefunden!");
                } else {
                    pointer.setLatLng(point);
                    _getCoordinates();
                    map['google'].setCenter(point, 5);
                    _getZoom();
                }
            }
        );
    }
}

