/**
 * GSliderControl class
 * Copyright (c) 2007, Viktor Szule, viktor.szule@gmail.com
 *
 * Version: 0.66
 *
 * Tested widh: 
 *      - Internet Explorer 7
 *      - Internet Explorer 6
 *      - Netscape 9.0b1
 *      - FireFox 2.0.0.4
 *      - Opera 9.21
 *      - Safari 3.0.2
 *
 * Limitations:
 *      - Opacity is not working correctly with Internet Explore 6 or older versions
 */

function initGSlider() {
  	var controlParams = { x: 10, y: 10, opacity: .8 };
    var zoomBoxParams = { fillColor: '#89bb88', borderColor: '#537a52', opacity: .3, borderWidth: 2, enabled: true };
	map.addControl(new GSliderControl(controlParams, zoomBoxParams));
}
 
/** 
 * Constructor for GSliderControl
 * @param {opts_controlParams} Named optional arguments:
 *   opts_controlParams.x {Number} x position of control
 *   opts_controlParams.y {Number} y position of control
 *   opts_controlParams.opacity {Number} opacity of control
 *   opts_controlParams.maxZoom {Number} maximum number of zoom level (default 19)
 *   opts_controlParams.minZoom {Number} minimum number of zoom level (default 1)
 * @param {opts_zoomBoxParams} Named optional arguments:
 *   opts_zoomBoxParams.fillColor {String} x fill color of zoomBox
 *   opts_zoomBoxParams.borderColor {String} border color of zoomBox
 *   opts_zoomBoxParams.opacity {Number} opacity of control
 *   opts_zoomBoxParams.borderWidth {Number} border width in pixel
 *   opts_zoomBoxParams.enabled {Boolean} enable or disable the control
 */

function GSliderControl(opts_controlParams, opts_zoomBoxParams) { 
  this.controlParams = { 
    x: 17,
    y: 17,
    opacity: 1,
    maxZoom: 19,
    minZoom: 1
  };
  
  var mt = map.getMapTypes();
  var that = this;
  for (var i=0; i<mt.length; i++) {
    mt[i].getMinimumResolution = function() {return that.controlParams.minZoom;};
    mt[i].getMaximumResolution = function() {return that.controlParams.maxZoom;};
  }
  
  for (var s in opts_controlParams) {
    this.controlParams[s] = opts_controlParams[s];
  }
  
  var opacity = opts_controlParams.opacity;
  if (getBrowserType_() == 5) opacity = 1;
  
  this.zoomBoxParams = {
    fillColor: '#8888BB',
    borderColor: '#4444BB',
    opacity: .3,
    borderWidth: 2,
    enabled: true
  };
  
  for (var s in opts_zoomBoxParams) {
    this.zoomBoxParams[s] = opts_zoomBoxParams[s];
  }
  
  this.globals = {
    maxZoom: 19
  };
};

/**
 * Declare GSliderControl
 */
GSliderControl.prototype = new GControl();
GSliderControl.prototype.setSlider_ = function(zoom) {
  var 
    top = this.getKnobTop_(zoom),
    t = parseInt(this.knob.style.top);
  this.slide.top = top;
  this.knob_a = t;
  this.knob_b = top;
  if (t < top) this.knob_c = 1;
  else this.knob_c = -1;
  clearTimeout(this.to2);
  this.movingSpeed = 1;
  this.accelerateKnob = true;
  this.knobOldY = this.knob_a;
  this.len = Math.abs(this.knob_a - this.knob_b);
  this.animeKnob_();
};

/** Calculate y position of knob from zoom level.
 *  @param {Number} zoom level
 *  @return {Number} calculated y position
 */
GSliderControl.prototype.getKnobTop_ = function(zoom) {
  return this.globals.sliderLength - Math.round(this.globals.sliderLength/this.globals.maxZoom*zoom);
};

/**  Anime moving of knob. Its parameters:  
 *    this.knob_a - knob's actual y position  
 *    this.knob_b - destination y position  
 *    this.knob_c - knob y destination
 */
GSliderControl.prototype.animeKnob_ = function() {
  if (this.len == 0) return;
  if (this.len > 0) {
    var y = Math.abs(5*Math.cos(Math.PI * 1.1 / 2 / this.len * Math.abs(this.knob_a - this.knobOldY))) + 1;
    this.movingSpeed = y;
  } else if (this.accelerateKnob) this.movingSpeed++;
  this.knob_a+= this.knob_c * this.movingSpeed;
  this.knob.style.top = this.knob_a + 'px';
  if ((this.knob_b >= this.knob_a && this.knob_c == 1) || (this.knob_b <= this.knob_a && this.knob_c == -1)) {
    var me = this;
    this.to2 = me.objectSetTimeout_(this, this.animeKnob_, 0);
  } else {
    this.knob.style.top = this.knob_b + 'px';
    }
};

/**
 * Function called when "zoomend" event is captured on map.
 */
GSliderControl.prototype.setAnimeKnob_ = function(zoomSign) {
  var top = this.getKnobTop_(map.getZoom() + zoomSign);
  this.slide.top = top;
  this.knob_a = parseInt(this.knob.style.top);
  this.knob_b = top;
  this.knob_c = zoomSign*-1;  // invert zoomSign
  clearTimeout(this.to2);
  this.accelerateKnob = false;
  this.animeKnob_();
};

/**
 * Call a function or evaluate an expression after a specified number of
 * milliseconds.
 *
 * Equivalent to the standard window.setTimeout function, but the given
 * function executes as a method of this instance. So the function passed to
 * objectSetTimeout can contain references to this.
 *    objectSetTimeout(this, function() { alert(this.x) }, 1000);
 *
 * @param {Object} object  The target object.
 * @param {Function} command  The command to run.
 * @param {Number} milliseconds  The delay.
 * @return {Boolean} Success.
 */
GSliderControl.prototype.objectSetTimeout_ = function(object, command, milliseconds) {
  return window.setTimeout(function() {
    command.call(object);
  }, milliseconds);
};

/**
 * Function called when "dragend" event is captured on GSliderControl.
 */
GSliderControl.prototype.setZoom_ = function(mode) {
  var G = this.globals;
  this.removeZoomBox_(0);
  if (this.slide.top != 0) map.setZoom(Math.round(G.maxZoom - this.slide.top*G.maxZoom/G.sliderLength));
  var knobTop = this.getKnobTop_(map.getZoom());
  this.knob.style.top = knobTop + 'px';
  this.slide.top = knobTop;
};

/**
 * Clear polygons of zoomBox
 * @param {Number} type of clearing
 */
GSliderControl.prototype.removeZoomBox_ = function(type) {
    if (type != 1) if (this.overLay1) map.removeOverlay(this.overLay1);
    if (this.overLay2) map.removeOverlay(this.overLay2);
    if (this.overLay3) map.removeOverlay(this.overLay3);
    if (this.overLay4) map.removeOverlay(this.overLay4);
    if (this.overLay5) map.removeOverlay(this.overLay5);
};

/**
 * Function called when "mousemove" event is captured on knob.
 * Draw ZoomBox control on map.
 */
GSliderControl.prototype.setZoomBox_ = function() {
  if (!this.zoomBoxParams.enabled) return;

  var
    G = this.globals,
    Z = this.zoomBoxParams,
    mapZoom = map.getZoom(),
    top = parseInt(this.knob.style.top),
    z = Math.round(G.maxZoom - top * G.maxZoom / G.sliderLength);

  if (top <= this.getKnobTop_(mapZoom) && z >= mapZoom) {
    var 
      m = Math.pow(2,(z - mapZoom + 1)),
      x = Math.round(map.getSize().width / m) + 1,
      y = Math.round(map.getSize().height / m) + 1,
      polyPoints = Array(),
      P = G_NORMAL_MAP.getProjection(),
      mapCenter = P.fromLatLngToPixel(map.getCenter(), mapZoom),
      X = mapCenter.x,
      Y = mapCenter.y;
      
    if (this.overLay1) map.removeOverlay(this.overLay1);
    
    var knobOffset = Math.round(preloaded[1].height / 2);
    
    // GPolygon is not working correctly at zoom level 0 and 1 so the following
    // code is more complicated than should be... :(
    for (var i = 0;i<10;i++) {
      var x1 = X + Math.round(i * x / 10);
      if (x1 < X+x) polyPoints.push(P.fromPixelToLatLng(new GPoint(x1,Y-y),mapZoom));
    }
    for (var i = 0;i<=20;i++) {
      polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x,Y-y + Math.round(i * y / 10)),mapZoom));
    }
    for (var i = 0;i<=20;i++) {
      polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x - Math.round(i * x / 10),Y+y),mapZoom));
    }
     for (var i = 0;i<=20;i++) {
      polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x,Y+y - Math.round(i * y / 10)),mapZoom));
    }
    for (var i = 0;i<=10;i++) {
      polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x + Math.round(i * x / 10),Y-y),mapZoom));
    }
    
    polyPoints.push(P.fromPixelToLatLng(new GPoint(X,Y-y),mapZoom));
    
    this.overLay1 = new GPolygon(polyPoints,Z.borderColor,Z.borderWidth,1,Z.fillColor,Z.opacity);
    map.addOverlay(this.overLay1);
    if (y > 7 && x > 7) {
      if ((m > 4) || (mapZoom > 0  && m > 2)) {
        polyPoints.length = 0;
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-knobOffset,Y-y),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+knobOffset,Y-y),mapZoom));    
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X,Y-y-knobOffset),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-knobOffset,Y-y),mapZoom));
        if (this.overLay2) map.removeOverlay(this.overLay2);
        this.overLay2 = new GPolygon(polyPoints,Z.borderColor,Z.borderWidth,1,Z.borderColor,1);
        map.addOverlay(this.overLay2);
 
        polyPoints.length = 0;
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-knobOffset,Y+y),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+knobOffset,Y+y),mapZoom));    
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X,Y+y+knobOffset),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-knobOffset,Y+y),mapZoom));
        if (this.overLay3) map.removeOverlay(this.overLay3);
        this.overLay3 = new GPolygon(polyPoints,Z.borderColor,Z.borderWidth,1,Z.borderColor,1);
        map.addOverlay(this.overLay3);
    
        polyPoints.length = 0;
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x,Y-knobOffset),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x,Y+knobOffset),mapZoom));    
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x-knobOffset,Y),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X-x,Y-knobOffset),mapZoom));
        if (this.overLay4) map.removeOverlay(this.overLay4);
        this.overLay4 = new GPolygon(polyPoints,Z.borderColor,Z.borderWidth,1,Z.borderColor,1);
        map.addOverlay(this.overLay4);
    
        polyPoints.length = 0;
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x,Y-knobOffset),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x,Y+knobOffset),mapZoom));    
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x+knobOffset,Y),mapZoom));
        polyPoints.push(P.fromPixelToLatLng(new GPoint(X+x,Y-knobOffset),mapZoom));
        if (this.overLay5) map.removeOverlay(this.overLay5);
        this.overLay5 = new GPolygon(polyPoints,Z.borderColor,Z.borderWidth,1,Z.borderColor,1);
        map.addOverlay(this.overLay5);
      } else this.removeZoomBox_(1);
    } else this.removeZoomBox_(1);
  } else {
    this.removeZoomBox_(0);
  }
};

/**
 * Is called by GMap2's addOverlay method. 
 * @param {GMap2} map The map that has had this GSliderControl added to it.
 * @return {DOM Object} Div that holds the GSliderControl
 */ 
GSliderControl.prototype.initialize = function(map) {
  //----------------------------------------
  // Declaration of private controls [START]
  //----------------------------------------

  /* utility functions in DragZoomUtil.namespace */
  var GSliderUtils = {};

  /** Constructor for GZoomInControl
   *  @param {opts_other} Named optional arguments:
   *    @opts_other.x {Number} x position of GSliderControl.
   *    @opts_other.y {Number} y position of GSliderControl.
   *    @opts_other.constolStyle {String} style of GSliderControl.
   */
  function GZoomInControl(controlParams) {
    this.controlParams = controlParams;
  };

  /**
   * Declare GZoomInControl
   */
  GZoomInControl.prototype = new GControl();

  /**
   * Is called by GMap2's addOverlay method. 
   * @param {GMap2} map The map that has had this GZoomInControl added to it.
   * @return {DOM Object} Div that holds the GZoomInControl
   */ 
  GZoomInControl.prototype.initialize = function(map) {
    var container;
    if (getBrowserType_() == 5) {
      container = document.createElement("div"); 
      container.style.height = preloaded[3].height + 'px';
      container.style.width = preloaded[3].width + 'px';
      container.style.filter = getLoaderStyle_('plus.png');
      container.style.backgroundImage = "none";
    } else {
      container = preloaded[3];
      var opacity = this.controlParams.opacity;
      container.style['-moz-opacity'] = opacity;
      container.style['opacity'] = opacity;
      container.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
    }
    
    GEvent.addDomListener(container, 'click', function() { map.zoomIn(); });
    map.getContainer().appendChild(container);
    return container;
  };
  
  /**
   * Required by GMaps API for controls. 
   * @return {GControlPosition} Default location for control
   */
  GZoomInControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(this.controlParams.x, this.controlParams.y)); 
  };
  
  /** Constructor for GZoomOutControl
   *    @param {opts_other} Named optional arguments:
   *      @opts_other.x {Number} x position of GSliderControl.
   *      @opts_other.y {Number} y position of GSliderControl.
   *      @opts_other.constolStyle {String} style of GSliderControl.
   */
  function GZoomOutControl(controlParams) {
    this.controlParams = controlParams; 
  };

  /**
   * Declare GZoomOutControl
   */
  GZoomOutControl.prototype = new GControl();

  /**
   * Is called by GMap2's addOverlay method. 
   * @param {GMap2} map The map that has had this GZoomOutControl added to it.
   * @return {DOM Object} Div that holds the GZoomOutControl
   */
  GZoomOutControl.prototype.initialize = function(map) {
    if (getBrowserType_() == 5) {
      c = document.createElement("div"); 
      c.style.height = preloaded[2].height + 'px';
      c.style.width = preloaded[2].width + 'px';
      c.style.filter = getLoaderStyle_('minus.png');
    } else {
      c = preloaded[2];
      var opacity = this.controlParams.opacity;
      c.style['-moz-opacity'] = opacity;
      c.style['opacity'] = opacity;
      c.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
    }
    GEvent.addDomListener(c, 'click', function() { map.zoomOut(); });
    map.getContainer().appendChild(c);
    return c;
  };        

  /**
   * Required by GMaps API for controls. 
   * @return {GControlPosition} Default location for control
   */
  GZoomOutControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(this.controlParams.x, this.controlParams.y + preloaded[3].height + preloaded[0].height)); 
  };
  //--------------------------------------
  // Declaration of private controls [END]
  //--------------------------------------
  this.globals.sliderLength = 0;
  this.draging = false;

  var 
    that = this,
    container = document.createElement('div');
  
  this.knobHeight = preloaded[1].height;
  if (getBrowserType_() == 5) {
    this.knob = document.createElement("div"); 
    this.knob.style.height = this.knobHeight + 'px';
    this.knob.style.width = preloaded[1].width;
    this.knob.style.filter = getLoaderStyle_('knob.png');
  } else {
    this.knob = preloaded[1];
  }
  container.innerHTML='<img src="/images/graphics/maps/slider.png" style="background:url(/images/graphics/maps/slider.png);">';
  var opacity = this.controlParams.opacity;
  if (getBrowserType_() != 5) {
    container.style['-moz-opacity'] = opacity;
    container.style['opacity'] = opacity;
    container.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
  }
  container.appendChild(this.knob);
  this.slide = new GDraggableObject(this.knob, {container:container});
  this.setSlider_(map.getZoom());
  map.getContainer().appendChild(container);
  
  // calculate height of moving area of knob
  this.globals.sliderLength = preloaded[0].height - this.knobHeight;
  GSliderUtils.knobStepY = Math.round(this.globals.sliderLength / (this.globals.maxZoom + 1));
  // init knob position
  this.setZoom_();
  GEvent.addListener(map, 'zoomend', function(a,b) {that.setSlider_(b);});
  GEvent.addListener(map, 'zoomstart', function(a,b,c) {that.setAnimeKnob_(a);});
  GEvent.addListener(this.slide, 'dragstart', function() {that.draging = true;});
  GEvent.addListener(this.slide, 'dragend', function() {that.setZoom_();});
  GEvent.addListener(this.slide, 'move', function() {that.setZoomBox_();});
  var bt = getBrowserType_();
  if (bt == 0 || bt == 2 || bt == 5) {
    GEvent.addDomListener(container, 'click', function(e) {that.setKnobY_(e.offsetY);});
  } else {
    GEvent.addDomListener(container, 'click', function(e) {that.setKnobY_(e.layerY);});
  }
  
  // Add ZoomIn and ZoomOut controls to map overlay
  map.addControl(new GZoomInControl(this.controlParams));
  map.addControl(new GZoomOutControl(this.controlParams));
  return container;
};
 
/** Set y position of knob
 *  @param {Number} y coordinate of knob
 */
GSliderControl.prototype.setKnobY_ = function(y) {
  if (this.draging == false) {
    var G = this.globals;
    var zoom = Math.round(G.maxZoom - y * G.maxZoom / G.sliderLength);
    this.setSlider_(zoom);
    map.setZoom(zoom);
    this.setSlider_(map.getZoom());
  } else this.draging = false;
};

/**
 * Required by GMaps API for controls. 
 * @return {GControlPosition} Default location for control
 */
GSliderControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(this.controlParams.x, this.controlParams.y + preloaded[3].height)); 
};

/**
 * Return a string which include the style of transparent png images.
 */
function getLoaderStyle_(name) {
  return "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/graphics/maps/" + name + "', sizingMethod='image');";
};

/** Get version number of IE
 *  @return {Number} version of IE
 */
function getInternetExplorerVersion_() {
  var rv = -1;
  if (navigator.appName == 'Microsoft Internet Explorer') {
    if (new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})').exec(navigator.userAgent) != null) rv = parseFloat( RegExp.$1 );
  }
  return rv;
};

/**
 * Detect brwoser.
 * @return {Number} browser type
 */
function getBrowserType_() {
  var agent = navigator.userAgent.toLowerCase();
  if (agent.indexOf('msie') > -1) {              // IE
    if (getInternetExplorerVersion_() < 7) {
      return 5;                                  // IE6, IE5 or older
    } else {
      return 0;                                  // IE7
    }
  }
  if (agent.indexOf('firefox') > -1) return 1;   // Firefox
  if (agent.indexOf('opera') > -1) return 2;     // Opera
  if (agent.indexOf('navigator') > -1) return 3; // Netscape
  if (agent.indexOf('safari') > -1) return 4;    // Safari
  return -1;
};

/* Image preloading */
var preloaded = new Array();
GSliderControl.imgPreLoader = function(func) {
    this.callback = func;
    this.loadedImages = 0;
    this.images = new Array('/images/graphics/maps/slider.png', '/images/graphics/maps/knob.png', '/images/graphics/maps/minus.png', '/images/graphics/maps/plus.png');
    for(var i=0; i<this.images.length; i++){
      	preloaded[i] = new Image();
      	preloaded[i].onload = this.imgOnLoad;
      	preloaded[i].src = this.images[i];
    }
};

GSliderControl.imgOnLoad = function() {
    GSliderControl.loadedImages++;
    if (GSliderControl.loadedImages == GSliderControl.images.length) {GSliderControl.callback()}
}