﻿/*
Copyright 2005-2009 James Tolley - http://www.bitperfect.com | http://www.gmaptools.com
All rights reserved.
This software is released under the terms of the LGPL v3.
*/


(function(){

function BpMap(div, opts) {
  if (typeof div == 'string') {
    div = document.getElementById(div);
  }

  this._bp = {};

  GMap2.call(this, div, opts);

  setupGetOverlays(this);
  setupZoomTo(this);
  setupMapArray(this);

  this.addBitPerfectLogo();
}

for (var prop in GMap2.prototype)
  BpMap.prototype[prop] = GMap2.prototype[prop];


BpMap.prototype.removeBitPerfectLogo = function() {
  this.removeControl(this._bp._logo);
};

BpMap.prototype.addBitPerfectLogo = function() {
  if (!this._bp._logo) {
    this._bp._logo = new BpLogoControl();
  }
  this.addControl(this._bp._logo);
};

(function(){

var browsers = ['chrome','opera','msie','safari','firefox','mozilla'];
var oses = ['x11;','macintosh','windows'];

function browser(ua){
  this.CHROME = 0;
  this.OPERA = 1;
  this.MSIE = 2;
  this.SAFARI = 3;
  this.FIREFOX = 4;
  this.MOZILLA = 5;
  this.X11 = 0;
  this.MAC = 1;
  this.WINDOWS = 2;
  this.type = -1;
  this.os = -1;
  this.version = 0;
  ua = ua.toLowerCase();
  for (var i = 0; i < browsers.length; i++) {
    if (ua.indexOf(browsers[i]) != -1) {
      this.type = i;
      var version_re = new RegExp(browsers[i] + '[ /]?([0-9]+)');
      if (version_re.exec(ua)) {
        this.version = parseFloat(RegExp.$1);
      }
      break;
    }
  }
  for (var i = 0; i < oses.length; i++) {
    if (ua.indexOf(oses[i]) != -1) {
      this.os = i;
      break
    }
  }
}

window.BP_BROWSER = window.BpBrowser = new browser(navigator.userAgent);

})();

(function(){

function BpLogoControl(){
  GControl.apply(this,arguments);
}

BpLogoControl.prototype = new GControl();

BpLogoControl.prototype.initialize = function(map) {
  var img = document.createElement('img');
  img.setAttribute('border', '0');

  var src = 'http://www.gmaptools.com/images/bplogo.png';
  if (BP_BROWSER.type == BP_BROWSER.MSIE && BP_BROWSER.version <= 6) {
    img.setAttribute('src', 'http://www.gmaptools.com/images/clear.gif');
    img.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=crop; src=" + src + ")";
  }
  else {
    img.setAttribute('src', src);
  }

  try {
    img.style.cursor = 'pointer';
  }
  catch (e) {
    img.style.cursor = 'hand';
  }

  var a = document.createElement("a");
  a.setAttribute('title', 'Click to visit GMapTools.com');
  a.setAttribute('href', 'http://www.gmaptools.com/maplink');
  a.appendChild(img);

  var div = document.createElement("div");
  div.appendChild(a);
  map.getContainer().appendChild(div);
  return div;
};

BpLogoControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(4, 35));
};

window.BpLogoControl = BpLogoControl;

})();

(function(){
var bpId = 0;
function BpControl(bpMsg,bpSize,bpCorner,bpClassName) {
    if(!bpMsg)    bpMsg    = 'Loading...';
    if(!bpSize)   bpSize   = new GSize(100,100);
    if(!bpCorner) bpCorner = G_ANCHOR_TOP_LEFT;

    this.bpMsg       = bpMsg;
    this.bpSize      = bpSize;
    this.bpCorner    = bpCorner;
    this.bpClassName = bpClassName;

    this.bpId     = ++bpId;
    this.bpDivId  = 'bpcontrol' + this.bpId;
}

BpControl.prototype = new GControl();

var bpProto = BpControl.prototype;

bpProto.getDivId = function() {
    return this.bpDivId;
};

bpProto.getDiv = function() {
    return this.bpDiv;
};

bpProto.getContent = function() {
    return this.bpDiv.innerHTML;
};

bpProto.setContent = function(bpMsg) {
    this.bpDiv.innerHTML = bpMsg;
};

bpProto.setContentNode = function(bpNode) {
    var bpDiv = this.getDiv();
    while (bpDiv.firstChild)
        bpDiv.removeChild(bpDiv.firstChild);

    bpDiv.appendChild = bpNode;
};

bpProto.show = function() {
    this.bpDiv.style.display = '';
};

bpProto.hide = function() {
    this.bpDiv.style.display = 'none';
};

bpProto.getClassName = function() {
    return this.bpClassName;
};

bpProto.setClassName = function(bpClassName) {
    if(!this.bpClassName)
        return;

    this.bpClassName = bpClassName;
    this.bpDiv.className = bpClassName;
};

bpProto.initialize = function(bpMap) {
    var bpDiv = document.createElement('div');
    bpDiv.setAttribute('id',this.bpId);

    this.bpDiv = bpDiv;

    var bpStyle = bpDiv.style;
    bpStyle.display = 'none';

    if(this.bpClassName) {
        bpDiv.className = this.bpClassName;
    } else if (typeof(this.setStyle) == 'function') {
        this.setStyle();
    } else {
        bpStyle.border = '1px solid black';
        bpStyle.backgroundColor = 'white';
        bpStyle.fontWeight   = 'bold';
        bpStyle.paddingLeft  = '3px';
        bpStyle.paddingRight = '3px';
    }
  
    bpDiv.innerHTML = this.bpMsg;
    bpMap.getContainer().appendChild(bpDiv);
    this._map = bpMap;
    return bpDiv;
};

bpProto.isVisible = function() {
    return this.bpDiv.style.display != 'none';
};

bpProto.getDefaultPosition = function() {
    return new GControlPosition(this.bpCorner,this.bpSize);
};

bpProto.printable  = function() { return false; };
bpProto.selectable = function() { return false; };

window.BpControl = BpControl;
})();
/*
  Copyright 2005-2009 James Tolley - http://www.bitperfect.com | http://www.gmaptools.com
  All rights reserved.
  This software is released under the LGPL v3. (http://www.gnu.org/licenses/lgpl.html)
*/

/**
 * @fileOverview setupGetOverlays(GMap2) sets up GMap.getOverlays(), GMap2.getOverlay(), GMap2.removeOverlays(), and GOverlay.getId()
 * @author James Tolley, http://bitperfect.com, http://gmaptools.com
 * @version 0.21
 * @example
 * var in_view_markers = map.getOverlays(GMarker, true);
 * var id = overlay.getId();
 * var overlay = map.getOverlay(id);
 * var o2 = map.getOverlay('mlsid', '1234'); // field and value
 * map.removeOverlays([GPolygon, GPolyline]); // remove polys
 */

(function(){

/**
 * GMap2 method: Get some or all of the overlays on a map.
 * @public
 * @name getOverlays
 * @function
 * @param {Array|Function} [types] An array of constructors (or a single constructor) of the types of overlays to get.
 * @param {Boolean|Null} [inview=null] Include only overlays inside or outside the current map view (null includes all.)
 * @param {Boolean} [include_ignored=false] Include "ignored" overlays (like the tooltip overlay).
 * @returns {Array} An array of mapped overlays.
 * @example
 * map.getOverlays(GMarker, true); // in-view markers
 * map.getOverlays([GPolyline, GPolygon]); // all polys
 * map.getOverlays(false, null, true); // all overlays, including "ignored" overlays, like BpLabeledPoly's polys.
 */
function getOverlays(types, inview, include_ignored) {
  if (types && !(types instanceof Array)) { // requesting a single type
    types = [types];
  }

  var a = [];
  var overlays = this._bp._overlays;
  var bounds = this.getBounds();
  for (var id in overlays) {
    var overlay = overlays[id];
    var type_ok = !types;
    if (types instanceof Array) {
      for (var i = 0; i < types.length; i++) {
        if (overlay instanceof types[i]) {
          type_ok = true;
          break;
        }
      }
    }
    // inview: not boolean? include everything; true include inview only; false, include off-view only
    // only use this feature with overlays that have a getLatLng method; otherwise, include them if we're checking this
    var inview_ok = !(typeof inview == 'boolean' && typeof overlay.getLatLng == 'function');
    if (!inview_ok) {
      if (inview) { // true: must be in view
        inview_ok = bounds.containsLatLng(overlay.getLatLng());
      }
      else { // false: must be off-view
        inview_ok = !bounds.containsLatLng(overlay.getLatLng());
      }
    }
    if (type_ok && inview_ok && (!overlay._bp._ignore || include_ignored)) {
      a.push(overlay);
    }
  }
  return a;
}

/**
 * GMap2 method: Remove overlays from the map. This should be used instead of clearOverlays.
 * @public
 * @name removeOverlays
 * @function
 * @param {Array} [types] An array of constructors (or a single constructor) of the types of overlays to remove.
 * @param {Boolean|Null} [inview=null] Include only overlays inside (true) or outside (false) the current map view (A value of null will return all overlays.) This only pertains to GMarker overlays.
 * @param {Boolean} [include_ignored=false] Include "ignored" overlays (like the tooltip overlay).
 * @example
 * map.removeOverlays(GMarker, true); // in-view markers
 * map.removeOverlays([GPolyline,GPolygon]); // all polys
 */
function removeOverlays() {
  var overlays = [];

  // are we getting an array of overlays to remove?
  if (arguments.length > 0 && arguments[0] instanceof Array && arguments[0].length > 0 && typeof arguments[0][0] !== 'function') {
    overlays = arguments[0];
  }
  // get overlays specified by the arguments
  else {
    overlays = this.getOverlays.apply(this, arguments);
  }

  for (var i = 0; i < overlays.length; i++) {
    this.removeOverlay(overlays[i]);
  }
};

/**
 * GMap2 method: Get a specific overlay based on its id or user data.
 * @name getOverlay
 * @function
 * @public
 * @param id_or_fieldname The id for the desired overlay, or the user data field to search.
 * @param [fieldvalue] The value of the user data field, which will indicate the desired overlay.
 * @returns {GOverlay} The overlay desired, or the undefined value if the overlay could not be found.
 */
function getOverlay(id_or_fieldname, fieldvalue) {
  var overlays = this.getOverlays();

  for (var i = 0; i < overlays.length; i++) {
    if (arguments.length > 1) {
      if (typeof overlays[i].getUserData === 'undefined') {
        return;
      }
      if (overlays[i].getUserData()[id_or_fieldname] == fieldvalue) { // TODO "==" too loose?
        return overlays[i];
      }
    }
    else if (overlays[i]._bp._id == id_or_fieldname) {
      return overlays[i];
    }
  }
}

/**
 * GOverlay method: Get a unique id for this overlay, available after the overlay has been added to the map.
 * @name getId
 * @function
 * @public
 * @name getId
 * @function
 * @returns {Number} A unique id for this overlay, for this execution of the application.
 * @example
 * var marker = new GMarker(map.getCenter());
 * map.addOverlay(marker); // the id is created here
 * var id = marker.getId();
 */
function getId() {
  if (!this._bp) {
    this._bp = {};
  }

  if (typeof this._bp._id == 'number') {
    return this._bp._id
  }
  else {
    return false;
  }
}

/**
 * Setup GMap2.getOverlay, GMap2.getOverlays, GMap2.removeOverlays, GMap2.addOverlays, and GOverlay.getId, 
 * @public
 * @name setupGetOverlays
 * @function
 * @param {GMap2} map The map to which these methods are to be added.
 * @example
 * setupGetOverlays(map);
 */
function setupGetOverlays(map) {
  if(typeof map.getOverlays !== 'undefined') {
    return;
  }

  if (typeof map._bp === 'undefined') {
    map._bp = {};
  }
  map._bp._overlays = {};
  map._bp._nextOverlayId = 1;

  GEvent.addListener(map, 'addoverlay', function(overlay) {
    if (!overlay._bp) {
      overlay._bp = {};
    }

    if(overlay === this.getInfoWindow()) {
      return;
    }

    if(!overlay._bp._id) {
      overlay._bp._id = this._bp._nextOverlayId++;
    }

    this._bp._overlays[overlay._bp._id] = overlay;
  });

  GEvent.addListener(map, 'removeoverlay', function(overlay) {
    if(!overlay._bp || !overlay._bp._id) {
      return;
    }

    delete this._bp._overlays[overlay._bp._id];
  });

  GEvent.addListener(map, 'clearoverlays', function() {
    this._bp._overlays = {};
  });

  if (typeof map.getOverlays === 'undefined') {
    map.getOverlays = getOverlays;
  }

  if (typeof map.getOverlay === 'undefined') {
    map.getOverlay = getOverlay;
  }

  if (typeof map.removeOverlays === 'undefined') {
    map.removeOverlays = removeOverlays;
  }

  if (typeof GOverlay.prototype.getId === 'undefined') {
    GOverlay.prototype.getId = getId
  }
}

window.setupGetOverlays = setupGetOverlays;

})();

function setupZoomTo(map) {
  if(typeof(map.zoomTo) == 'function') {
    return;
  }

  map.zoomTo = function(margin, points, maxZoom) {
    // if there are no points specified, use the mapped markers' points
    if (!points) {
      var m = this.getOverlays([GMarker,GPolyline,GPolygon]);
      if(m.length == 0) {
        return; // return if we don't know where to zoom
      }

      // get the points of the markers
      var points = [];
      for(var i = 0; i < m.length; i++) {
        points.push(m[i].getPoint());
      }
    }
    else if (points.length && (points[0].getPoint || points[0].getLatLng)) {
      // there are markers specified, so get their points
      var p = [];
      for(var i = 0; i < points.length; i++) {
        if (points[i].getPoint) {
          p.push(points[i].getPoint());
        }
        else {
          p.push(points[i].getLatLng());
        }
      }
      points = p;
    }
    else if (points.length && points[0].getVertexCount) {
      
    }

    if(!points[0]) {
      return;
    }

    margin = margin || 0; // set the default (a percentage of the span)

    // create the bounds to zoom to
    var bounds = new GLatLngBounds(points[0]);
    for(var i = 1; i < points.length; i++) {
      bounds.extend(points[i]);
    }

    var zoom = this.getBoundsZoomLevel(bounds);
    if(arguments.length > 2 && zoom > maxZoom) {
      zoom = maxZoom;
    }

    this.setCenter(bounds.getCenter(), zoom);
  };
}

function setupMapArray(map) {

  function doMapArray() {
    if (this._mapArrayConfig.queue.length == 0)
      return; // interrupted

    var key = this._mapArrayConfig.queue[0];
    var config = this._mapArrayConfig.todo[key];

    var maxIndex = Math.min(config.startIndex + config.step, config.dataArray.length);

    var tt = typeof(this.getTooltip) == 'function' ? this.getTooltip() : null;
    for(var i = config.startIndex; i < maxIndex; i++) {
      var marker = config.mkMarker.call(this, config.dataArray[i]);
      this.addOverlay(marker);
      if (tt)
        marker.setTooltip(tt, config.dataArray[i].title);
    }

    if(maxIndex < config.dataArray.length) {
      config.startIndex = maxIndex;
      var total_mapped_count = maxIndex + map._mapArrayConfig.totalMappedCount;
      var total_count = map._mapArrayConfig.totalMappedCount + map._mapArrayConfig.totalCount;
      if (config.progressbar) {
        config.progressbar.updateLoader(config.step);
      }
      config.onstep.call(this, total_mapped_count, total_count);
      setTimeout(GEvent.callback(this, doMapArray), 1);
    }
    else {
      delete this._mapArrayConfig.todo[key];
      this._mapArrayConfig.queue.shift(); // remove this key from the queue

      // if there are other requests to process, process them - don't call ondone
      map._mapArrayConfig.totalMappedCount += maxIndex;
      if (this._mapArrayConfig.queue.length > 0) {
        total_mapped_count = map._mapArrayConfig.totalMappedCount;
        total_count = map._mapArrayConfig.totalMappedCount + map._mapArrayConfig.totalCount;
        if (config.progressbar) {
          config.progressbar.updateLoader(config.step);
        }
        config.onstep.call(this, total_mapped_count, total_count);
        setTimeout(GEvent.callback(this, doMapArray), 1);
      }
      // last request? call config.ondone
      else {
        total_count = map._mapArrayConfig.totalMappedCount;
        if (config.progressbar) {
          config.progressbar.updateLoader(config.step);
        }
        config.onstep.call(this, total_count, total_count);
        if (config.progressbar) {
          config.progressbar.remove();
        }
        config.ondone.call(this, total_count);
        // reset everything to the default
        map.stopMapArray();
      }
    }
  }

  function defaultMkMarker(d, tt) {
    var lat     = parseFloat(d.lat);
    var lng     = parseFloat(d.lng);
    var latlng  = new GLatLng(lat, lng)
    var marker  = new GMarker(latlng);
    if (marker.setUserData) marker.setUserData(d);
    return marker;
  }

  map.stopMapArray = function() {
    this._mapArrayConfig = { todo: {}, queue: [], totalMappedCount: 0, completedArrays: [], totalCount: 0 };
  };
  map.stopMapArray(); // also good for initialization

  map.mapArray = function(dataArray, args, key) {
    if (arguments.length < 3)
      key = 'one-and-only';

    if (typeof(this._mapArrayConfig.todo[key]) == 'undefined')
      this._mapArrayConfig.todo[key] = {};
    else // only one request per key at a time
      return false;

    if (arguments.length < 2)
      args = {};

    // store the config
    this._mapArrayConfig.todo[key].dataArray   = dataArray;
    this._mapArrayConfig.todo[key].step        = args.step        ? args.step         : 1;
    this._mapArrayConfig.todo[key].mkMarker    = args.mkMarker    ? args.mkMarker     : defaultMkMarker;
    this._mapArrayConfig.todo[key].onstep      = args.onstep      ? args.onstep       : function(){};
    this._mapArrayConfig.todo[key].ondone      = args.ondone      ? args.ondone       : function(){};
    this._mapArrayConfig.todo[key].progressbar = args.progressbar ? args.progressbar  : false;
    this._mapArrayConfig.todo[key].startIndex  = 0;

    this._mapArrayConfig.queue.push(key);
    this._mapArrayConfig.totalCount += dataArray.length;

    if (this._mapArrayConfig.queue.length == 1) {
      if (args.progressbar) {
        args.progressbar.start(dataArray.length);
      }
      setTimeout(GEvent.callback(this, doMapArray), 1);
    }

    return true;
  };
}

(function() {

var id = 0;

function BpLabel(latlng, html, hide, anchor) {
  GOverlay.apply(this, arguments);

  this._latlng  = latlng  || new GLatLng(0,0);
  this._html    = html    || '';
  this._hide    = hide    || false;
  this._anchor  = anchor  || 'center';

  this._id = ++id;

  this._pane = G_MAP_MARKER_PANE;
  this._cursor = false;
  this._opacity = 80;
  this._userData = {};
  this._className = false;
  this._stale_size = true; // we don't know our size yet
}

BpLabel.prototype = new GOverlay(new GLatLng(0,0));

BpLabel.prototype.getAnchor = function() {
  return this._anchor;
};

BpLabel.prototype.setAnchor = function(anchor) {
  if ((anchor == 'center' || anchor == 'nw') && this._anchor != anchor) {
    this._anchor = anchor;
    this.redraw();
  }
};

// returns only if it's mapped or was mapped and has not changed html or className since.
BpLabel.prototype.getSize = function() {
  if (!this._stale_size) {
    return this._size;
  }

  if (!this._div) {
    return false;
  }

  if(this.isVisible()) {
    var width = this._div.offsetWidth || this._div.style.pixelWidth || 0;
    var height = this._div.offsetHeight || this._div.style.pixelHeight || 0;
    this._size = new GSize(width, height);
    this._stale_size = false;
  }
  else if (this.isMapped()) {
    // put it off-map
    var top  = this._div.style.top;
    var left = this._div.style.left;
    this._div.style.top   = -screen.height + 'px';
    this._div.style.left  = -screen.width  + 'px';

    // show it
    this._div.style.display = '';

    // measure it,
    var width = this._div.offsetWidth || this._div.style.pixelWidth || 0;
    var height = this._div.offsetHeight || this._div.style.pixelHeight || 0;
    this._size = new GSize(width, height);
    this._stale_size = false;

    // hide it
    this._div.style.display = 'none';

    //move it back
    this._div.style.top   = top;
    this._div.style.left  = left;
  }
  // not yet mapped, or the html or className changed since it's been mapped, then return false
  if (this._stale_size) {
    return false;
  }

  return this._size;
};

BpLabel.prototype.getWidth = function() {
  var size = this.getSize();
  if (size) {
    return size.width;
  }
  return false;
};

BpLabel.prototype.getHeight = function() {
  var size = this.getSize();
  if (size) {
    return size.height;
  }
  return false;
};

BpLabel.prototype.setClassName = function(cName) {
  this._stale_size = true;

  this._className = cName;

  if (this._styled) {
    this.removeStyle();
  }

  this._div.className = this._className;

  this.getSize();
};

BpLabel.prototype.getClassName = function() {
  return this._className;
};

BpLabel.defaultStyle = {
  border: '1px solid black',
  backgroundColor: 'white',
  filter: 'alpha(opacity:80)',
  KHTMLOpacity: 0.8,
  MozOpacity: 0.8,
  opacity: 0.8,
  fontWeight: 'bold',
  whiteSpace: 'nowrap',
  paddingRight: '3px',
  paddingLeft: '3px'
};

BpLabel.prototype.addStyle = function() {
  if (this._div) {
    var style = BpLabel.defaultStyle;
    for (var key in style) {
      this._div.style[key] = style[key];
    }

    this._styled = true;
  }
};

BpLabel.prototype.removeStyle = function() {
  if (this._div) {
    var style = BpLabel.defaultStyle;
    for (var key in style) {
      this._div.style[key] = '';
    }

    this._styled = false;
  }
};

BpLabel.prototype.initialize = function(map) {
  this._map = map;

  // if we're doing this for the first time...
  if(!this._div) {
    this._div = document.createElement('div');
    this._div.style.position = 'absolute';

    if (this._className) {
      this._div.className = this._className;
    }
    else {
      this.addStyle();
    }

    this._div.style.zIndex = GOverlay.getZIndex(this.getLatLng().lat());
    this._div.innerHTML = this._html;
  }

  if (this._cursor) {
    this.setCursor(this._cursor);
  }

  this.setOpacity(this._opacity);
  var forgetZIndex = typeof this._zIndex === 'undefined';
  this.setZIndex(this.getZIndex(), forgetZIndex);

  this._div.style.display = 'none';
  map.getPane(this._pane).appendChild(this._div);
  this.getSize();
  this.redraw();

  if (!this._hide) {
    this._div.style.display = '';
  }

  return this._div;
};

BpLabel.prototype.remove = function() {
  if (this._div) {
    this._div.parentNode.removeChild(this._div);
  }
  delete this._map;
};

// this is a bare-bones implementation which anchors on the NW corner
BpLabel.prototype.redraw = function() {
  if (!this.isMapped()) {
    return;
  }

  var divPx = this._map.fromLatLngToDivPixel(this._latlng);

  if (this._anchor == 'center') {
    var size = this.getSize();
    if (size) {
      divPx.x -= Math.round(size.width/2);
      divPx.y -= Math.round(size.height/2);
    }
  }

  this._div.style.left = divPx.x + 'px';
  this._div.style.top  = divPx.y + 'px';
};

// latlng, html, opts (not userData, pane, cursor, opacity, zIndex)
BpLabel.prototype.copy = function() {
  return new BpLabel(this._latlng, this._html, this._hide, this._anchor);
};

BpLabel.prototype.getHtml = function() {
  return this._html;
};

BpLabel.prototype.setHtml = function(html) {
  this._stale_size = true;

  this._html = html;
  if (this._div) {
    this._div.innerHTML = this._html;
  }

  this.getSize();
};

BpLabel.prototype.isMapped = function() {
  return this._map ? true : false;
};

BpLabel.prototype.getMap = function() {
  return this._map;
};

BpLabel.prototype.show = function() {
  this._hide = false;
  if (this._div) {
    this._div.style.display = '';
  }
};

BpLabel.prototype.hide = function() {
  this._hide = true;
  if (this._div) {
    this._div.style.display = 'none';
  }
};

BpLabel.prototype.isVisible = function() {
  if (!this.isMapped() || !this._div || !this._div.parentNode) {
    return false;
  }

  return this._div.style.display != 'none';
};

BpLabel.prototype.isHidden = function() {
  return !this.isVisible();
};

BpLabel.prototype.getUserData = function() {
  return this._userData;
};

BpLabel.prototype.setUserData = function(userData) {
  this._userData = userData;
};

BpLabel.prototype.getZIndex = function() {
  if (! this._div) {
    if (typeof(this._zIndex) != 'undefined') {
      return this._zIndex;
    }
    else {
      return GOverlay.getZIndex(this.getLatLng().lat());
    }
  }

  return this._div.style.zIndex;
};

BpLabel.prototype.setZIndex = function(zIndex, forget) {
  if (arguments.length == 0) {
    this.resetZIndex();
    return;
  }

  if (!forget) {
    this._zIndex = zIndex;
  }

  if (this._div) {
    this._div.style.zIndex = zIndex;
  }
};

BpLabel.prototype.resetZIndex = function() {
  delete this._zIndex;
  var zIndex = GOverlay.getZIndex(this.getLatLng().lat());
  this.setZIndex(zIndex, true);
};

BpLabel.prototype.setHighZIndex = function() {
  var zIndex = GOverlay.getZIndex(-91);
  this.setZIndex(zIndex);
};

BpLabel.prototype.getOpacity = function() {
  return this._opacity;
};

BpLabel.prototype.setOpacity = function(opacity) {
  if(opacity < 0)   opacity = 0;
  if(opacity > 100) opacity = 100;

  this._opacity = opacity;

  if(this._div) {
    this._div.style.filter       = 'alpha(opacity:' + this._opacity + ')';
    this._div.style.KHTMLOpacity = this._opacity / 100;
    this._div.style.MozOpacity   = this._opacity / 100;
    this._div.style.opacity      = this._opacity / 100;
  }
};

BpLabel.prototype.getCursor = function() {
  return this._cursor;
};

BpLabel.prototype.setCursor = function(cursor) {
  this._cursor = cursor;

  if(this._div) {
    this._div.style.cursor = this._cursor;
  }
};

BpLabel.prototype.getPane = function() {
  return this._pane;
};

// only works before the overlay is added to the map
BpLabel.prototype.setPane = function(pane) {
  this._pane = pane;
};

BpLabel.prototype.getDiv = function() {
  return this._div;
};

BpLabel.prototype.getLatLng = function() {
  return this._latlng;
};

BpLabel.prototype.setLatLng = function(latlng) {
  this._latlng = latlng;
  if (typeof this._zIndex === 'undefined') {
    this.resetZIndex();
  }
  this.redraw();
};

BpLabel.prototype.getId = function() {
  return this._id;
};

BpLabel.prototype.getDiv = function() {
  return this._div;
};

BpLabel.prototype.draggingEnabled = function() {
  if (this._draggable) {
    return this._draggable.enabled();
  }
};

BpLabel.prototype.dragging = function() {
  if (this._draggable) {
    return this._draggable.dragging();
  }
};

BpLabel.prototype.disableDragging = function() {
  if (this._draggable) {
    this._draggable.disable();
    return this._draggable;
  }
};

BpLabel.prototype.enableDragging = function() {
  if (this._draggable) {
    this._draggable.enable();
    return this._draggable;
  }

  var label = this;
  var map = label.getMap();
  var draggable = new GDraggableObject(label.getDiv());
  this._draggable = draggable;
  if (label.getCursor()) {
    draggable.setDraggableCursor(label.getCursor());
    draggable.setDraggingCursor('move');
  }

  this._zoomend_listener = GEvent.addListener(map, 'zoomend', function() {
    var point = map.fromLatLngToDivPixel(label.getLatLng());
    if (label.getAnchor() == 'center') {
      point.x -= Math.ceil(label.getWidth() / 2);
      point.y -= Math.ceil(label.getHeight() / 2);
    }
    label._draggable.moveTo(point);
  });

  this._dragend_listener = GEvent.addListener(draggable, 'dragend', function() {
    var left = parseInt(label.getDiv().style.left, 10);
    var top = parseInt(label.getDiv().style.top, 10);

    if (label.getAnchor() == 'center') {
      if (!label.getSize()) {
        return; // we're off the map
      }
      left += Math.ceil(label.getWidth() / 2);
      top += Math.ceil(label.getHeight() / 2);
    }

    var point = new GPoint(left, top);
    var latlng = map.fromDivPixelToLatLng(point);
    label.setLatLng(latlng);
  });

  return draggable;
};

BpLabel.prototype.getDraggableObject = function() {
  return this._draggable;
};

window.BpLabel = BpLabel;
})();

(function() {

function BpTooltip(latlng, html) {
  // the true argument will initially hide the label
  BpLabel.call(this, latlng, html, true, 'nw');

  this.setPane(G_MAP_FLOAT_SHADOW_PANE);
  this._bp = {};
  this._bp._ignore = true;
}

BpTooltip.prototype = new BpLabel();

BpTooltip.prototype.initialize = function(map) {
  BpLabel.prototype.initialize.call(this, map);

  // setup the code to reposition the tooltip on zoom
  if (typeof this._onZoomListener == 'undefined') {
    this._onZoomListener = GEvent.addListener(map, 'zoomend', GEvent.callback(this, this.onZoomEnd));
  }

  return this._div;
};

BpTooltip.prototype.onZoomEnd = function() {
  if(this._marker) {
    this.redraw();
  }
};

BpTooltip.prototype.copy = function() {
  return new BpTooltip(this._latlng, this._html);
};

BpTooltip.prototype.setMarker = function(marker) {
  this._marker = marker;
  if(!marker) {
    this.hide();
    this.setHtml('');
  }
  else if (typeof marker.getTooltipHtml === 'function') {
    this.setHtml(marker.getTooltipHtml());
  }
};

BpTooltip.prototype.getMarker = function() {
  return this._marker;
};

// if there's no marker, there's nothing to do
// always force; when the marker moves, we move
BpTooltip.prototype.redraw = function(force) {
  var m = this.getMarker();
  if (m) {
//    this.setHtml( m.getTooltipHtml() );
    m.redraw();
    var point = m.getTooltipPoint();

    this._div.style.top  = point.y + 'px';
    this._div.style.left = point.x + 'px';
  }
};

BpTooltip.prototype.remove = function() {
  if (this._onZoomListener) {
    GEvent.removeListener(this._onZoomListener);
    delete this._onZoomListener;
  }

  BpLabel.prototype.remove.call(this);
};

if (!window.BpTooltip) {
  window.BpTooltip = BpTooltip;
}

})();

if (!window.setupSetTooltip) {

  function mk_bp(m) {
    if (typeof m._bp === 'undefined') {
      m._bp = {};
    }
  }

  window.setupSetTooltip = function(constructor) {
    if (typeof(constructor) != 'function') {
      constructor = GMarker;
    }

    if (typeof constructor.prototype.setTooltip == 'function') {
      return;
    }

    constructor.prototype.removeTooltip = function(){
      mk_bp(this);
      if (this._bp._ttOverListener) {
        GEvent.removeListener(this._bp._ttOverListener);
        delete this._bp._ttOverListener;
        GEvent.removeListener(this._bp._ttOutListener);
        delete this._bp._ttOutListener;
        var tt = this.getTooltip();
        if(tt.getMarker() === this) {
          tt.setMarker();
        }
        delete this._bp._tt;
      }
    }

    constructor.prototype.setTooltip = function(tt, html) {
      mk_bp(this);
      // remove tooltip
      if(!tt && this.getTooltip()) {
        this.removeTooltip();
        return;
      }

      this._bp._tt = tt;
      this._bp._ttHtml = html;
  
      var mouseOverHandler = function() {
        var tt = this.getTooltip();
        if(tt) {
          tt.setMarker(this);
//          tt.setHtml(this._bp._ttHtml);
          tt.redraw();
          tt.show();
        }
      };

      this._bp._ttOverListener = GEvent.addListener(this, 'mouseover', mouseOverHandler);

      var mouseOutHandler = function() {
        var tt = this.getTooltip();
        if(tt && !this._bp._ttMaintain) {
          tt.setMarker();
        }
      };

      this._bp._ttOutListener = GEvent.addListener(this, 'mouseout', mouseOutHandler);
    };

    constructor.prototype.getTooltip = function() {
      mk_bp(this);
      return this._bp._tt;
    };

    constructor.prototype.getTooltipHtml = function() {
      mk_bp(this);
      return this._bp._ttHtml;
    };

    constructor.prototype.setTooltipHtml = function(html) {
      mk_bp(this);
      this._bp._ttHtml = html;
    };

    constructor.prototype.getTooltipPoint = function() {
      mk_bp(this);
      var margin = 5; // px to the right (or left) of the icon

      var tt = this.getTooltip();
      var map = tt.getMap();
      var pixel = map.fromLatLngToDivPixel(this.getLatLng());
      var icon = this.getIcon();
      pixel.x -= icon.iconAnchor.x;
      pixel.x += icon.iconSize.width;
      pixel.x += margin;

      pixel.y -= icon.iconAnchor.y;
      pixel.y += 0.5 * icon.iconSize.height;
      pixel.y -= 0.5 * tt.getHeight();

      // how many pixels are between the margin and the right side of the map?
      var nePixel = map.fromLatLngToDivPixel(map.getBounds().getNorthEast());
      var ttWidth = tt.getWidth();
      if(nePixel.x < pixel.x + ttWidth) {
        pixel.x -= 2 * margin;
        pixel.x -= icon.iconSize.width;
        pixel.x -= ttWidth;
      }
      return pixel;
    };

    constructor.prototype.showTooltip = function() {
      mk_bp(this);
      var tt = this.getTooltip();
      if(!tt) {
        return;
      }
      tt.setMarker(this);
      tt.redraw();
      tt.show();
    };
  
    constructor.prototype.hideTooltip = function() {
      mk_bp(this);
      var tt = this.getTooltip();
      if(!tt) {
        return;
      }
      tt.hide();
    };
  
    constructor.prototype.setMaintainTooltip = function(bool) {
      mk_bp(this);
      this._bp._ttMaintain = bool;
      if(bool) {
        this.showTooltip();
      }
      else {
        this.hideTooltip();
      }
    };
  }
  if (GMarker) {
    setupSetTooltip(GMarker);
  }
  else {
    alert('BpTooltip should be loaded after the Google Maps API');
  }
  setupSetTooltip.version = 0.12;
}

window.BpMap = BpMap;
})();


