try {
  google.load("maps", "2", {"language":language});
} catch (e) {
  // something
}


String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}
String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}

var tourmapy = {  
  
  helper: {
    atlas: {},
    google: {},
    checkboxClick: false,
    tmpBoundsData: [],
    search: {}
  },
  
  language: {
    iso: 'en'
  },
  
  icon: {
    Default: null,
    None: null
  },
  
  WGS84: function(orig) {
    this.orig = orig;
    this.format = '';
    this.valid = false;
    this.lat = 0.0;
    this.long = 0.0;
    this.data = null;
       
    this.regs = {      
      DMS : new RegExp('^([0-9]{1,3})[°d]([0-9]{1,2})[\'m]([0-9]+[.][0-9]+|[0-9]+)["s] ?([NnSs]?)[^0-9]*([0-9]{1,3})[°d](-?[0-9]{1,2})[\'m]([0-9]+[.][0-9]+|[0-9]+)["s] ?([WwEe]?)'),
      DM : new RegExp('^(-?[0-9]{1,3})[°d]([0-9]+[.][0-9]+|[0-9]+)[\'m][^0-9-]*(-?[0-9]{1,3})[°d]([0-9]+[.][0-9]+|[0-9]+)[\'m]'),
      DD : new RegExp('^(-?[0-9]+[.][0-9]+|-?[0-9]+)[°d][^0-9-]*(-?[0-9]+[.][0-9]+|-?[0-9]+)[°d]')      
    } 
    
    if (this.regs.DMS.test(this.orig)) {
      this.data = this.regs.DMS.exec(this.orig);     
      this.lat = tourmapy.utilities.fromDMSToDD(this.data[1], this.data[2], this.data[3]);
      if (this.data[4] == 'S' || this.data[4] == 's') this.lat = -this.lat;
      this.long = tourmapy.utilities.fromDMSToDD(this.data[5], this.data[6], this.data[7]);
      if (this.data[8] == 'W' || this.data[8] == 'w') this.long = -this.long;
      this.valid = true;
      this.format = 'DMS';
    } else if (this.regs.DM.test(this.orig)) {
      this.data = this.regs.DM.exec(this.orig);     
      this.lat = tourmapy.utilities.fromDMSToDD(this.data[1], this.data[2], 0);
      this.long = tourmapy.utilities.fromDMSToDD(this.data[3], this.data[4], 0);
      this.valid = true;
      this.format = 'DM';
    } else if (this.regs.DD.test(this.orig)) {
      this.data = this.regs.DD.exec(this.orig);      
      this.lat = this.data[1];
      this.long =  this.data[2];
      this.valid = true;
      this.format = 'DD';
    }      
    
    this.toDisplayGPS = function() {
      return tourmapy.utilities.fromDecToDeg(Math.abs(this.lat)) + (this.lat > 0 ? 'N' : 'S') + '; ' 
        + tourmapy.utilities.fromDecToDeg(Math.abs(this.long)) + (this.long > 0 ? 'E' : 'W')
    },
     
    this.getLat = function() {
      return this.lat;
    }
    
    this.getLong = function() {
      return this.long;
    }
    
    this.isValid = function() {
      return this.valid;
    }
    
    this.getFormat = function() {
      return this.format;
    }
  },
  
  utilities: {
    alphabet: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'],
    
    isIE: function()	{
    	return /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent);
    },
    
    isFF: function()	{
    	return /firefox/i.test(navigator.userAgent);
    },    
  
    getWinSize: function(){
    	var iWidth = 0, iHeight = 0;
    	if (window.innerWidth && window.innerHeight) {
    		iWidth = window.innerWidth;
    		iHeight = window.innerHeight;
    	}	else if (document.documentElement && document.documentElement.clientHeight) {
    		iWidth = document.documentElement.clientWidth;
    		iHeight = document.documentElement.clientHeight;
    	} else if (document.body){
    		iWidth = document.body.clientWidth;
    		iHeight = document.body.clientHeight;
    	}		
    	
    	return {width:iWidth, height:iHeight};
    },	
    
    fromDecToDeg: function(value) {
      var deg = Math.floor(value);
      var a = (value - deg)*60;
      var min = Math.floor(a);
      var sec = (a - min)*60;
      sec = sec.toFixed(3);      
      return deg + "°" + min + "'" + sec + "\""; 
    },
    
    fromDDToDMS: function(value) {
      return fromDecToDeg(value);
    },
    
    fromDMSToDD: function(d, m, s) {
      var min = m/60;
      d = d - 0;
      if (d < 0) min = -min;
      var sec = s/3600;
      if (d < 0) sec = -sec;
      var dd = d + min + sec;
      return dd.toFixed(6); 
    },
    
    toDisplayGPS: function(lat, lng) {
      return this.fromDecToDeg(Math.abs(lat)) + (lat > 0 ? 'N' : 'S') + '; ' 
        + this.fromDecToDeg(Math.abs(lng)) + (lng > 0 ? 'E' : 'W')
    },
    
    createCookie: function(name,value,days) {
    	if (days) {
    		var date = new Date();
    		date.setTime(date.getTime()+(days*24*60*60*1000));
    		var expires = "; expires="+date.toGMTString();
    	}
    	else var expires = "";
    	document.cookie = name+"="+value+expires+"; path=/";
    },
    
    readCookie: function(name) {
    	var nameEQ = name + "=";
    	var ca = document.cookie.split(';');
    	for(var i=0;i < ca.length;i++) {
    		var c = ca[i];
    		while (c.charAt(0)==' ') c = c.substring(1,c.length);
    		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    	}
    	return null;
    },
    
    eraseCookie: function(name) {
    	createCookie(name,"",-1);
    },
    
    htmlspecialchars: function(str) {
      str=str.replace(/</g,'&lt;');
      str=str.replace(/>/g,'&gt;');
      str=str.replace(/"/g,'&#34;');
      str=str.replace(/'/g,'&#39;');
      return str;
    },
    
    addslashes: function (str) {
      str=str.replace(/\\/g,'\\\\');
      str=str.replace(/\'/g,'\\\'');
      str=str.replace(/\"/g,'\\"');
      str=str.replace(/\0/g,'\\0');
      return str;
    },
    
    stripslashes: function (str) {
      str=str.replace(/\\'/g,'\'');
      str=str.replace(/\\"/g,'"');
      str=str.replace(/\\0/g,'\0');
      str=str.replace(/\\\\/g,'\\');
      return str;
    },
    
    Base64: { 
    	// private property
    	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
     
    	// public method for encoding
    	encode : function (input) {
    	  input += "";
    		var output = "";
    		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    		var i = 0;
     
    		input = this._utf8_encode(input);
     
    		while (i < input.length) {
     
    			chr1 = input.charCodeAt(i++);
    			chr2 = input.charCodeAt(i++);
    			chr3 = input.charCodeAt(i++);
     
    			enc1 = chr1 >> 2;
    			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    			enc4 = chr3 & 63;
     
    			if (isNaN(chr2)) {
    				enc3 = enc4 = 64;
    			} else if (isNaN(chr3)) {
    				enc4 = 64;
    			}
     
    			output = output +
    			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
    			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
     
    		}
     
    		return output;
    	},
     
    	// public method for decoding
    	decode : function (input) {
    	  input += "";
    		var output = "";
    		var chr1, chr2, chr3;
    		var enc1, enc2, enc3, enc4;
    		var i = 0;
     
    		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
     
    		while (i < input.length) {
     
    			enc1 = this._keyStr.indexOf(input.charAt(i++));
    			enc2 = this._keyStr.indexOf(input.charAt(i++));
    			enc3 = this._keyStr.indexOf(input.charAt(i++));
    			enc4 = this._keyStr.indexOf(input.charAt(i++));
     
    			chr1 = (enc1 << 2) | (enc2 >> 4);
    			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    			chr3 = ((enc3 & 3) << 6) | enc4;
     
    			output = output + String.fromCharCode(chr1);
     
    			if (enc3 != 64) {
    				output = output + String.fromCharCode(chr2);
    			}
    			if (enc4 != 64) {
    				output = output + String.fromCharCode(chr3);
    			}
     
    		}
     
    		output = this._utf8_decode(output);
     
    		return output;
     
    	},
     
    	// private method for UTF-8 encoding
    	_utf8_encode : function (string) {
    		string = string.replace(/\r\n/g,"\n");
    		var utftext = "";
     
    		for (var n = 0; n < string.length; n++) {
     
    			var c = string.charCodeAt(n);
     
    			if (c < 128) {
    				utftext += String.fromCharCode(c);
    			}
    			else if((c > 127) && (c < 2048)) {
    				utftext += String.fromCharCode((c >> 6) | 192);
    				utftext += String.fromCharCode((c & 63) | 128);
    			}
    			else {
    				utftext += String.fromCharCode((c >> 12) | 224);
    				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
    				utftext += String.fromCharCode((c & 63) | 128);
    			}
     
    		}
     
    		return utftext;
    	},
     
    	// private method for UTF-8 decoding
    	_utf8_decode : function (utftext) {
    		var string = "";
    		var i = 0;
    		var c = c1 = c2 = 0;
     
    		while ( i < utftext.length ) {
     
    			c = utftext.charCodeAt(i);
     
    			if (c < 128) {
    				string += String.fromCharCode(c);
    				i++;
    			}
    			else if((c > 191) && (c < 224)) {
    				c2 = utftext.charCodeAt(i+1);
    				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
    				i += 2;
    			}
    			else {
    				c2 = utftext.charCodeAt(i+1);
    				c3 = utftext.charCodeAt(i+2);
    				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
    				i += 3;
    			}
     
    		}
     
    		return string;
    	}
     
    }
  },
  
  loader: {
    loadingCount: 0,
    
    begin: function(callerId) {
      if (this.loadingCount == 0) this.onLoadStart(callerId);      
      this.loadingCount++;
    },
    
    end: function(force, callerId) {
      if (force) {
        this.loadingCount = 0;
      }
      
      if (this.loadingCount > 0) this.loadingCount--;
      
      if (this.loadingCount == 0) this.onLoadFinish(callerId);
    },
    
    isLoading: function() {
      return (this.loadingCount > 0);
    },
    
    onLoadStart: function(callerId) {
      return;
    },
    
    onLoadFinish: function(callerId) {
      return;
    }
  },  
  
  map: {
    objectTypes: [],
    markers: [],
    layerTypes: [],
    layers: [],
    layersData: [],
    layersMarkers: [],
    layersLabels: [],    
    layersTurnedOff: [],
    layersGrid: [],
    mapObject: null,
    engine: "unknown",
    mapTypes: {
      TEST: null,
      
      tileToQuadKey: function( x, y, zoom) {
          var quad = "";
          for (var i = zoom; i > 0; i--){
              var mask = 1 << (i - 1);
              var cell = 0;
              if ((x & mask) != 0)
                  cell++;
              if ((y & mask) != 0)
                  cell += 2;
              quad += cell;
          }
          return quad;
      }
    },
    
    zoomIn: function() {
      this.setZoom(this.getZoom()+1);
    },
    
    zoomOut: function() {
      this.setZoom(this.getZoom()-1);
    },
    
    settings: {
      maxLayers: 3
    },
    
    beforeLoad: function() {
    },
    
    afterLoad: function() {      
    },

    load: function(divID) {
      //alert('Redefine map.load method');
    },
    
    init: function() { 
    },
    
    toggleLayer: function(typeID, on) {
      if (on) { // toggle on
        if (this.layerTypes.length >= this.settings.maxLayers) {          
          return false;
        }        
        if (typeID == 6 && this.getZoom() < 11) {
    		  alert(L_CROSS_COUNTRY_MAPS_NOT_AVAILABLE_IN_THIS_SCALE);
        }
        this.addLayer(typeID);
        this.loadLayers();
      } else { // off
        this.removeLayer(typeID);
        //this.loadLayers();
      }
      
      if (this.customToggleLayer) {
        return this.customToggleLayer(typeID, on);
      }
      
      return true;
    },
    toggleLayerPolyline: function(typeID, busID, checkbox) {
      if (checkbox) { 
        tourmapy.helper.checkboxClick = true;
      }      
      
      if (this.layers[typeID][busID]) {
        if (!checkbox.checked) {
          this.removeOverlay(this.layers[typeID][busID]);
          for (var i = 0; i < this.layersMarkers[typeID][busID].length; i++) {
            this.removeOverlay(this.layersMarkers[typeID][busID][i]);
          }
        } else {
          this.addOverlay(this.layers[typeID][busID]);
          for (var i = 0; i < this.layersMarkers[typeID][busID].length; i++) {
            this.addOverlay(this.layersMarkers[typeID][busID][i]);            
          }
          //if (this.engine == 'atlas') this.mapObject.update();          
        }
        if (!this.layersTurnedOff[typeID]) {
          this.layersTurnedOff[typeID] = [];
        }
        this.layersTurnedOff[typeID][busID] = !checkbox.checked;          
        var checkboxes = document.getElementsByName('chk_trasa_' + busID);
        for (var i=0; i<checkboxes.length; i++) {
          checkboxes[i].checked = checkbox.checked;
        }
      }
      return true;
    },
    addLayer: function(id) {
      
      var found = false;
      for (var i = 0; i < this.layerTypes.length; i++) {
        if (this.layerTypes[i] == id) {
          found = true;
          break;
        }
      }
      
      if (!found) {
        this.layerTypes.push(id);        
      }
      
      return true;    
    },
    removeLayer: function(id) {
      var newTypes = [];      
      for (var i = 0; i < this.layerTypes.length; i++) {        
        if (this.layerTypes[i] != id) {
          newTypes.push(this.layerTypes[i]);
        } else if (id != 998 && id != 999) { // do not attempt to remove layers we do not control
          
          if (this.layersMarkers[id]) {
            for (var busID in this.layersMarkers[id]) {
              if (!this.layersMarkers[id][busID]) continue;
              for (var j = 0; j < this.layersMarkers[id][busID].length; j++) {
                this.removeOverlay(this.layersMarkers[id][busID][j]);
              }
            }
          }
          
          if (this.layersLabels[id]) {
            for (var j = 0; j < this.layersLabels[id].length; j++) {
              if (this.layersLabels[id][j]) {
                this.removeOverlay(this.layersLabels[id][j]);                
              }
            }
          }
          
          if (!this.layers[id]) continue;
          for (var j = 0; j < this.layers[id].length; j++) {
            if (this.layers[id][j]) this.removeOverlay(this.layers[id][j]);
            delete this.layers[id][j];
          }
          delete this.layers[id];
        }        
      }      
      
      //this.layersTurnedOff[id] = [];
      
      this.layerTypes = newTypes;
    },
    getZoom: function() {
      //alert('Redefine map.getZoom method');
    },
    setZoom: function(zoom) {
      //alert('Redefine map.setZoom method');
    },
    setCenter: function(point) {
      if (this.engine == 'atlas') {
        this.mapObject.moveTo(point);
      } else {
        this.mapObject.setCenter(point);
      }
    },   
    
    checkResize: function() {
      //alert('Redefine map.checkResize method');
    },
    toggleObjectType: function(typeID, on) {
      if (on === null || on == 'undefined') {
        var on = !this.checkObjectType(typeID);
      }
      
      if (on) { // toggle on        
        this.addObjectType(typeID);
        this.loadObjects();
      } else { // off
        this.removeObjectType(typeID);
        this.loadObjects();
      }
    },
    addObjectType: function(id) {
      var found = false;
      for (var i = 0; i < this.objectTypes.length; i++) {
        if (this.objectTypes[i] == id) {
          found = true;
          break;
        }
      }
      if (!found) {
        this.objectTypes.push(id);        
      }
    },
    checkObjectType: function(id) {
      for (var i = 0; i < this.objectTypes.length; i++) {
        if (this.objectTypes[i] == id) {
          return true;
        }
      }
      
      return false;
    },    
    removeObjectType: function(id) {
      var newTypes = [];      
      for (var i = 0; i < this.objectTypes.length; i++) {
        if (this.objectTypes[i] != id) {
          newTypes.push(this.objectTypes[i]);
        }
      }
      
      this.objectTypes = newTypes;
    },    
    removeOverlay: function(overlay) {
      if (overlay) {
        this.mapObject.removeOverlay(overlay);
      }
    },
    addOverlay: function(overlay) {
      if (overlay) {
        this.mapObject.addOverlay(overlay);
      }
    },
    getBounds: function() {
      //alert('Redefine map.getBounds method');
      return [0,0,0,0];
    },
    createMarker: function(point, icon, title, html) {
      //alert('Redefine map.createMarker method');
      return null;
    },
    createPoint: function(lat, lng) {
     // alert('Redefine map.createPoint method');
      return null;
    },
    get: function() {
      return this.mapObject;
    },
    set: function(object) {
      this.mapObject = object;
    },
    setMapType: function() {
      //alert('Redefine map.setMapType method');
    },    
    loadObjects: function() {      
      var bounds = this.getBounds();
      var url = "/resource/tm_objects.php?lang="+tourmapy.params.get('lang', 'cz')+"&ids=" + this.objectTypes.join(',') + "&bounds=" + bounds.join(',') + "&e=" + this.engine + '&z=' + tourmapy.params.get('z', 8);
      tourmapy.ajax.task(url, function(response) {
        new Function(response)();
        }, 'objectLoader', true, true);
    },     
    
    loadLayers: function() {    
      var layerTypes = [];
      for (var i = 0; i < this.layerTypes.length; i++) {
        if (this.layerTypes[i] < 900 && !this.layersData[this.layerTypes[i]]) {
          layerTypes.push(this.layerTypes[i]);
        }
      }
      if (layerTypes.length > 0) {
        var url = "/resource/tm_layers2.php?lang="+tourmapy.params.get('lang', 'cz')+"&ids=" + layerTypes.join(',') + "&e=" + this.engine + '&z=' + tourmapy.params.get('z', 8) + '&nocache=' + tourmapy.params.get('nocache', 0);
        tourmapy.ajax.task(url, function(response) {
          new Function(response)();
          tourmapy.map.showLayers();
          }, 'layerLoader');        
      } else {
        this.showLayers();
        return false;
      }      
    },
    showLayers: function(redrawLabels) {
      if (!redrawLabels && redrawLabels !== false) {
        var redrawLabels = true;
      }
      
      var distance = this.getZoom() < 15 ? Math.pow(2, (19 - this.getZoom())) : 0;
      
      var pointsTotal = 0;
      
      var viewport = this.getBounds();
      var minRow = Math.floor(viewport[0]);
      var minCol = Math.floor(viewport[1]);
      var maxRow = Math.floor(viewport[2]);
      var maxCol = Math.floor(viewport[3]);
      
      for (var i = 0; i < this.layerTypes.length; i++) {
        var id = this.layerTypes[i];        
        if (id == 6 && this.getZoom() < 11) continue;
        if (!this.layersData[id] || this.layers[id] || !this.layersGrid[id]) continue;
        
        if (redrawLabels) if (this.layersLabels[id]) {
          for (var ll = 0; ll < this.layersLabels[id].length; ll++) {
            this.addOverlay(this.layersLabels[id][ll]);                       
          }
        }
        
        this.layers[id] = [];       
        
        var done = [];
        
        var counter = 0;
        var cellCounter = [];
        var cellLimit = 0;
        var zoom = this.getZoom();
        
        if (tourmapy.params.get('cellLimit', 0) > 0) {
          cellLimit = tourmapy.params.get('cellLimit', 0);
        }
        
        for (var col = minCol; col <= maxCol; col++) {
          if (!this.layersGrid[id][col]) continue;
          cellCounter[col] = [];
          for (var row = minRow; row <= maxRow; row++) {
            if (!this.layersGrid[id][col][row]) continue;
            cellCounter[col][row] = 0;
            var len = this.layersGrid[id][col][row].length;
            for (var x = 0; x < len; x++) {
              if (cellLimit > 0 && cellCounter[col][row] >= cellLimit) {
                break;
              }
              var j = this.layersGrid[id][col][row][x];
              if (!this.layersData[id][j]) continue;
              if (done[j]) {
                continue; 
              }             
              
              done[j] = true;              
              counter++;
              cellCounter[col][row]++;              
              
              var turnedOff = (true === (this.layersTurnedOff[id] && this.layersTurnedOff[id][this.layersData[id][j].bus_id]));
              
              var points = [];
              var display = false;
              var intersects = true;
              var last = null;               
              
              if (!turnedOff || !this.layers[id][this.layersData[id][j].bus_id]) {
                if (this.layersData[id][j].bounds) {
                  var b = this.layersData[id][j].bounds;
                  intersects = !( b.minLong > viewport[3]
                                || b.maxLong < viewport[1]
                                || b.minLat > viewport[2]
                                || b.maxLat < viewport[0]
                                );
                  /*
                  if (!tourmapy.helper.tmpBoundsData[id*100000+j]) {              
                    var p = this.createPolyline([
                        this.createPoint(b.minLat, b.minLong),
                        this.createPoint(b.minLat, b.maxLong),
                        this.createPoint(b.maxLat, b.maxLong),
                        this.createPoint(b.maxLat, b.minLong),
                        this.createPoint(b.minLat, b.minLong)
                        ]);
                      var a = this.createPoint(b.minLat, b.minLong);
                      this.addOverlay(this.createMarker(a, tourmapy.icon.Default, a.toDisplayGPS()));
                      a = this.createPoint(b.minLat, b.maxLong);
                      this.addOverlay(this.createMarker(a, tourmapy.icon.Default, a.toDisplayGPS()));
                      a = this.createPoint(b.maxLat, b.maxLong);
                      this.addOverlay(this.createMarker(a, tourmapy.icon.Default, a.toDisplayGPS()));
                      a = this.createPoint(b.maxLat, b.minLong);
                      this.addOverlay(this.createMarker(a, tourmapy.icon.Default, a.toDisplayGPS()));
                      this.addOverlay(p);
                      tourmapy.helper.tmpBoundsData[id*100000+j] = true;
                  }*/
                }
                
                if (intersects) {
                 for (var k=0; k < this.layersData[id][j].points.length; k++) {
                    var p = this.layersData[id][j].points[k];
                    if (k==0) {
                      last = p;
                    }
                    if ( distance == 0 || k == 0 || k == (this.layersData[id][j].points.length - 1) || p.distanceFrom(last) >= distance) {
                      points.push(p);
                      last = p;
                      display = (display || this.pointIsVisible(p));
                    }
                  }   
                }     
                
                if (display) {
                  pointsTotal += points.length;
                  var poly = this.createPolyline(points, '#' + this.layersData[id][j].color);                
                  if (!turnedOff) this.addOverlay(poly);                    
                  this.layers[id][this.layersData[id][j].bus_id] = poly;            
                }
                
                if (!turnedOff) {
                  for (var i = 0; i < this.layersMarkers[id][this.layersData[id][j].bus_id].length; i++) {              
                     this.addOverlay(this.layersMarkers[id][this.layersData[id][j].bus_id][i]);
                  }                  
                }
              }          
            }
          }
        }   
        //console.log(counter);   
      } 
    },
    createPolyline: function(points, color, opacity) {
      //alert('Redefine map.createPolyline method!');
    },
    createMarker: function(point, icon, title, html) {
      //alert('Redefine map.createMarker method!');
    },
    createLabel: function(point, label, html, bgcolor) {
      //alert('Redefine map.createLabel method!');
    },
    createBusLabel: function(point, label, html, bgcolor) {
      //alert('Redefine map.createLabel method!');
    },
    createPoint: function(lat, lng) {
      //alert('Redefine map.createPoint method!');
    },
    pointIsVisible: function(point) {
      return true;
    },
    addMarker: function(marker) {
      this.addOverlay(marker);
    },    
    addMarkers: function(markers) {
      for (var i = 0; i < markers.length; i++) {        
        this.addOverlay(markers[i]);
      } 
    }
  },
  
  ajax: {
    taskList: [],
    
    params: {
      values: [],
      names: [],
      
      clear: function() {
        this.names = [];
        this.values = [];
      },
      
      set: function(name, value) {
        if (value && value.pop) { // array
          for (var i=0; i < value.length; i++) {
            this.set(name+'_'+i, value[i]);
          }
        } else {
          var found = false;
          name = escape(name);            
          for (var i = 0; i < this.names.length; i++) {
            if (this.names[i] == name) {
              this.values[i] = escape(value);
              found = true;
              break;
            }
          }
          
          if (!found) {
            this.names.push(escape(name));
            this.values.push(escape(value));
          }
        }
      },
      
      get: function(name, defaultValue) {
        name = escape(name);
        for (var i = 0; i < this.names.length; i++) {
          if (this.names[i] == name) {
            return unescape(this.values[i]);
          }
        }
        
        return defaultValue;
      },
      
      toUrlParams: function() {
        var tmp = [];
        for (var i = 0; i < this.names.length; i++) {
          if (!this.values[i] || this.values[i] == '') continue;
          tmp.push(unescape(this.names[i]) + "=" + this.values[i]);
        }
        return tmp.join("&");
      }
    },    
    
    load: function(engine, lang) {
      this.engine = engine;
      this.lang = lang;
    },
    
    getXmlHttpObject: function() {
    	var xmlHttp = null;
    	try
    	  {
    	  // Firefox, Opera 8.0+, Safari
    	  xmlHttp=new XMLHttpRequest();
    	  }
    	catch (e)
    	  {
    	  // Internet Explorer
    	  try
    	    {
    	    	xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
    	    }
    	  catch (e)
    	    {
    	    try
    	      {
    	      	xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
    	      }
    	    catch (e)
    	      {
    	      	alert("Your browser DOES NOT support AJAX!");
    	      }
    	    }
    	  }
    	return xmlHttp;
    },
    
    /**
     * abort all tasks
     */
    halt: function() {
      for (var i = 0; i < this.taskList.length; i++) {
        if (this.taskList[i] && this.taskList[i].abort) {
         this.taskList[i].abort();
        }        
      }
      this.taskList = [];
    },
    
    /**
     * executes ajax task and optionaly notifies the loader
     */         
    task: function(url, action, id, notifyLoader, useParams) {
      if (notifyLoader == null) {
        notifyLoader = true;
      }
      
    	var xmlHttp = this.getXmlHttpObject();
      
      if (id) {
        if (this.taskList[id] && this.taskList[id].abort) {
            this.taskList[id].abort();            
            delete this.taskList[id];
        } else {
          if (notifyLoader) tourmapy.loader.begin(id);
        }
        this.taskList[id] = xmlHttp;
      }
      
      xmlHttp.onreadystatechange=function()
    	{
    			if (this.readyState==4) {
            if (this.status == 200) {
      			  if (action != null) {
                //try {
                  action(this.responseText);                
                //} catch (e) {
                  //if (console && console.log) console.log([e]);                
                //}
              }                          				  
      			} else {
      			  // fetched the wrong page or network error
            }
            if (notifyLoader) tourmapy.loader.end(false, id);
            tourmapy.ajax.taskList[id] = null;
      		}
    	}
    	
    	if (useParams && this.params.names.length > 0) {
    	 url = url + (url.indexOf('?') == -1 ? '?' : '&') + this.params.toUrlParams();
      }
    	
    	xmlHttp.open("GET",url,true);
    	xmlHttp.send(null);
    },
    /**
     * executes ajax task WITHOUT notifying the loader
     */         
    backgroundTask: function(url, action, id) {
      this.task(url, action, id, false);
    }
  },
  /**
   * routing functions (uses google.maps.Directions)
   */     
  routing: {
    directions: null,
    waypoints: [],
    action: null,
    avoidHighways: false,
    autoZoom: true,
    travelMode: 1,
    
    get: function() {
      return this.directions;
    },
    
    setWalkingMode: function(state) {
      if (state) this.travelMode = 2; //G_TRAVEL_MODE_WALKING;
      else this.travelMode = 1; //G_TRAVEL_MODE_DRIVING;
      
      return this.travelMode;
    },
    
    load: function(elementID) {
      alert('Redefine routing.load method');
    },
    
    initialize: function() {
      google.maps.Event.addListener(this.directions, 'error', function() {
        tourmapy.routing.routingFailed();
      });
    },
    
    /**
     * finds a route from start to finish    
     * @param from, to1, to2, ..., toN; variable list of waypoints
     */
    route: function() {      
      this.get().clear();
      if (arguments.length == 1) {
        if (arguments[0].join) {
          arguments = arguments[0];
        }
      }
      for (var i =0; i < arguments.length; i++) {
        if (arguments[i].length > 4 && arguments[i].charAt(0) == '{' && arguments[i].charAt(arguments[i].length - 1) == '}') {
          var str = arguments[i].substr(1, arguments[i].length - 2);
          var data = str.split('^', 3);
          if (data.length >= 2) {
            this.waypoints[i] = new google.maps.LatLng(data[0], data[1]);            
          } else {
            this.waypoints[i] = arguments[i];
          }
        } else {
          this.waypoints[i] = arguments[i];
        }
      }
      this.get().loadFromWaypoints(this.waypoints, {getPolyline: true, getSteps: true, avoidHighways: this.avoidHighways, preserveViewport: !this.autoZoom, travelMode: this.travelMode});
            
      return false;
    },
    clear: function() {
      alert('Redefine routing.clear method');
    },    
    routingFailed: function() {    
       
    }
  },
  
  geocoder: {
    obj: null,    
    userCallback: null,
    data: null,
    ready: false,
    
    get: function() {
      return this.obj;
    },
    
    load: function(countryCode) {
      if (!countryCode) {
        var countryCode = 'cz';
      }
      this.obj = new google.maps.ClientGeocoder();
      this.obj.setBaseCountryCode(countryCode);
      this.ready = true;
    },
    
    find: function(address, callback) {
      this.data = null;
      this.ready = false;
      
      this.obj.getLocations(address, function(response) { 
        tourmapy.geocoder.geocoderCallback(response, callback);
        });
    },
    
    reverseFind: function(lat, lng, callback) {      
      if (!callback) callback = null;
       
      this.data = null;
      this.ready = false;
      var p = new google.maps.LatLng(lat, lng);
      
      
      /*this.obj.getLocations(p, function(response) { 
        tourmapy.geocoder.geocoderCallback(response, callback, true);        
      });*/
      var url = '/resource/tm_reverse_geocoder.php?latlng=' + lat + ',' + lng + '&lng=' + tourmapy.params.get('lang', 'cs');
      tourmapy.ajax.task(url,
        function (response) {
          tourmapy.geocoder.geocoderReverseCallback(response, callback);
        }, 'geocoder');
    },
    
    isReady: function() {
      return this.ready;
    },
    
    accuracySort: function(a, b) {
      return (b.AddressDetails.Accuracy - a.AddressDetails.Accuracy);
    },
   
    geocoderReverseCallback: function(response, userCallback) {
      eval(response);
      
      this.data = [];
      for (var i = 0; i < results.length; i++) {        
        var p = results[i];
        
        var o = new Object();
        o['lat'] = 0; 
        o['long'] = 0;
        o['query'] = '';
        o['address'] = p;
        
        this.data.push(o);
      }
      
      if (userCallback !== null) {
        userCallback(this.data, 200);
      }
    },
     
    geocoderCallback: function(response, callback) {
      
      if (response.Status.code == 200) {      
        this.data = [];
        response.Placemark.sort(this.accuracySort);
        for (var i = 0; i < response.Placemark.length; i++) {        
          var p = response.Placemark[i];
          
          var o = new Object();
          o['lat'] = p.Point.coordinates[1]; 
          o['long'] = p.Point.coordinates[0];
          o['query'] = response.name;
          //o['locality'] = p.AddressDetails.Country.AdministrativeArea.Locality.LocalityName;
          o['address'] = p.address;
          
          this.data.push(o);
        }
      }    
      
      this.ready = true;
      
      if (callback !== null) {
        callback(this.data, response.Status.code);
      }      
    }
  },
  
  /**
   * reads and sets variables in url
   */     
  params: {
  	data: new Array(),
  	display: true,
  	useHashing: false,
  	
  	load: function() {
      
  		var hash = location.hash;
  		if (hash.charAt(0) == '#') {
  		  hash = hash.substr(1);
      }
      if (hash.indexOf('@') == -1) {
        hash = tourmapy.utilities.Base64.decode(hash);
      }
      var parts = hash.split('@');       
  		for (var i=0; i < parts.length; i++) {
  			var param = parts[i].split('=',2);
  			if (param.length > 1) {
  				this.data.push(param);				
  			} 
  		}
  	},
  	
  	encode: function(value) {
      var encoded = value + "";
      //encoded = tourmapy.utilities.Base64.encode(encoded);
      //encoded = escape(encoded);
      
      encoded = encoded.replace(/[@]/g, '{at}');
      
      return encoded;  
    },
    
    decode: function(value) {
      var decoded = value + "";
      //decoded = tourmapy.utilities.Base64.decode(decoded);
      
      decoded = decoded.replace(/\{at\}/g, '@');
      //decoded = unescape(value);
      
      return decoded;
    },
  	
  	show: function() {
  		var out = "";
  		for (var i=0; i<this.data.length;i++) {
  			out += this.data[i][0] + '=' + this.decode(this.data[i][1]) + "\n";
  		}		
  		alert(out);
  	},
  	
  	get: function(param, defaultValue, noDecode) {
  		for (var i=0; i<this.data.length;i++) {
  			if (this.data[i][0] == param && this.data[i][1] != '') {
  				if (!noDecode) return this.decode(this.data[i][1]);
  				else return this.data[i][1];
  			}
  		}
  		
  		if (defaultValue !== null) {
  		  return defaultValue;
      }
  		
  		return '';
  	},
  	
  	getInt: function(param, defaultValue) {
  		return parseInt(this.get(param, defaultValue));
  	},
  	
  	set: function(param, value) {
  	  value = this.encode(value);
  		for (var i=0; i<this.data.length;i++) {
  			if (this.data[i][0] == param) {
  				this.data[i][1] = value;
  				this.update();
  				return true;
  			}
  		}
  		
  		this.data.push(new Array(param, value));
  		this.update();
  		
  		return false;
  	},
  	
  	setArray: function(param, value) {
  	   this.set(param, value.join('|'));
    },
    
    getArray: function(param, value) {
       var string = this.get(param, value, true);
  	   if (string != null && string.split) {
          var data = string.split('|');
          var ret = [];
          for (var i=0; i < data.length; i++) {
            if (data[i] !== null && data[i] != '') {
              ret.push(this.decode(data[i]));
            }
          }
          return ret;
       }
  	   
  	   return value;
    },
    
    add2Array: function(param, value) {      
      var oldArr = this.getArray(param, []);
      var found = false;
      for (var i=0; i < oldArr.length; i++) {
        if (oldArr[i] == value) {
          found = true;
          break;
        }
      }
      
      if (!found) {
        oldArr.push(this.encode(value));
      }
      
      this.setArray(param, oldArr);
    },
    
    /**
     * removes one value from an array parameter
     */         
    delFromArray: function(param, value) {
      var oldArr = this.getArray(param, []);
      var newArr = [];
      for (var i=0; i < oldArr.length; i++) {
        if (oldArr[i] != value) {
          newArr.push(oldArr[i]);
        }
      }
      
      this.setArray(param, newArr);
    },
    
    /**
     * shows all parameters in url
     */         
  	update: function() {
  		if (this.display) {
        if (this.useHashing) location.hash = tourmapy.utilities.Base64.encode(this.hashstring());
        else location.hash = this.hashstring(); 
      }
   	},
  	
  	hashstring: function(overrides) {
  	  var tmp = new Array();
    	for (var i=0; i<this.data.length;i++) {
    		if (this.data[i][1] !== null && this.data[i][1] !== '') {
    		  if (!overrides || !overrides[this.data[i][0]]) {
            tmp.push(this.data[i][0] + '=' + this.data[i][1]);
          } else {
            tmp.push(this.data[i][0] + '=' + overrides[this.data[i][0]]);
          }
    		}
    	}
    	
    	return tmp.join('@');
    },
    
    /**
     * allows parameters to be shown in url
     */         
    showInUrl: function() {
      this.display = true;
      this.update();
    },
    
    /**
     * forbids parameters to be shown in url
     */
    hideInUrl: function() {
      this.display = false;
      location.hash = "#";
    },
    
    /**
     * tells wheter or not are parameters shown in url
     */
    areShown: function() {
      return this.display;
    }	
  }
}