try {
  google.load("maps", "2", {"language":language});
} catch (e) {
  // something
}

var tourmapy = {
  helper: {
    atlas: {},
    google: {},
    checkboxClick: false
  },
  
  icon: {
    Default: null,
    None: null
  },
  
  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(2);
      //var sec = Math.floor(a);
      return deg + "°" + min + "'" + sec + "\""; 
    },
    
    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);
    }
  },
  
  loader: {
    loadingCount: 0,
    
    begin: function() {
      if (this.loadingCount == 0) this.onLoadStart();
      
      this.loadingCount++;
    },
    
    end: function(force) {
      if (force) {
        this.loadingCount = 0;
      }
      
      if (this.loadingCount > 0) this.loadingCount--;
      
      if (this.loadingCount == 0) this.onLoadFinish();
    },
    
    isLoading: function() {
      return (this.loadingCount > 0);
    },
    
    onLoadStart: function() {
      return;
    },
    
    onLoadFinish: function() {
      return;
    }
  },
  
  map: {
    objectTypes: [],
    markers: [],
    layerTypes: [],
    layers: [],
    layersData: [],
    layersMarkers: [],
    layersLabels: [],    
    layersTurnedOff: [],
    mapObject: null,
    engine: "unknown",
    
    beforeLoad: function() {
    },
    
    afterLoad: function() {      
    },

    load: function(divID) {
      //alert('Redefine map.load method');
    },
    toggleLayer: function(typeID, on) {
      if (on) { // toggle on
        if (this.layerTypes.length >= 3) {          
          return false;
        }        
        this.addLayer(typeID);
        this.loadLayers();
      } else { // off
        this.removeLayer(typeID);
        //this.loadLayers();
      }
      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.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;
      tourmapy.ajax.task(url, function(response) {
        new Function(response)();
        }, 'objectLoader');
    },     
    
    loadLayers: function() {    
      var layerTypes = [];
      for (var i = 0; i < this.layerTypes.length; i++) {
        if (this.layerTypes[i] != 998 && this.layerTypes[i] != 999 && !this.layersData[this.layerTypes[i]]) {
          layerTypes.push(this.layerTypes[i]);
        }
      }
      if (layerTypes.length > 0) {
        var url = "/resource/tm_layers.php?lang="+tourmapy.params.get('lang', 'cz')+"&ids=" + layerTypes.join(',') + "&e=" + this.engine + '&z=' + tourmapy.params.get('z', 8);
        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;
      for (var i = 0; i < this.layerTypes.length; i++) {
        var id = this.layerTypes[i];        
        if (!this.layersData[id] || this.layers[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] = [];       
        
        for (var j = 0; j < this.layersData[id].length; j++) {
          var turnedOff = (this.layersTurnedOff[id] && this.layersTurnedOff[id][this.layersData[id][j].bus_id]);
          //if (turnedOff) continue;
          
          var points = [];
          var display = false;
          var last = null; 
          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]);
            }
          }
        }
      } 
    },
    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;
          for (var i = 0; i < this.names.length; i++) {
            name = escape(name);
            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));
          }
        }
      },
      
      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 in this.taskList) {
        if (this.taskList[i].abort) {
         this.taskList[i].abort();
        }        
      }
      this.taskList = [];
    },
    
    /**
     * executes ajax task and optionaly notifies the loader
     */         
    task: function(url, action, id, notifyLoader) {
      if (notifyLoader == null) {
        notifyLoader = true;
      }
      if (notifyLoader) tourmapy.loader.begin();
    	var xmlHttp = this.getXmlHttpObject();
      
      if (id) {
        if (this.taskList[id]) {          
          if (this.taskList[id].abort) {
            this.taskList[id].abort();            
            delete this.taskList[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();
      		}
    	}
    	
    	if (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,
    
    get: function() {
      return this.directions;
    },
    
    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});
            
      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;
      
      if (callback) {
        this.userCallback = callback;
      } else {
        this.userCallback = null;
      }
      
      this.obj.getLocations(address, function(response) { 
        tourmapy.geocoder.geocoderCallback(response);
        });
    },
    
    isReady: function() {
      return this.ready;
    },
    
    geocoderCallback: function(response) {
    
      if (response.Status.code == 200) {      
        this.data = [];
            
        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['locality'] = p.AddressDetails.Country.AdministrativeArea.Locality.LocalityName;
          o['address'] = p.address;
          o['query'] = response.name;
          
          this.data.push(o);
        }
      }    
      
      this.ready = true;
      
      if (this.userCallback !== null) {
        this.userCallback(this.data, response.Status.code);
      }       
    }
  },
  
  /**
   * reads and sets variables in url
   */     
  params: {
  	data: new Array(),
  	display: true,
  	
  	load: function() {
      
  		var hash = location.hash;
  		if (hash.charAt(0) == '#') {
  		  hash = hash.substr(1);
      }
  		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);				
  			} 
  		}
  	},
  	
  	show: function() {
  		var out = "";
  		for (var i=0; i<this.data.length;i++) {
  			out += this.data[i][0] + '=' + this.data[i][1] + "\n";
  		}		
  		alert(out);
  	},
  	
  	get: function(param, defaultValue) {
  		for (var i=0; i<this.data.length;i++) {
  			if (this.data[i][0] == param) {
  				return unescape(this.data[i][1]);
  			}
  		}
  		
  		if (defaultValue) {
  		  return defaultValue;
      }
  		
  		return '';
  	},
  	
  	getInt: function(param, defaultValue) {
  		return parseInt(this.get(param, defaultValue));
  	},
  	
  	set: function(param, 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);
  	   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(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(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) 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;
    }	
  }
}