/*

var map = new BpMap(div,opts?);

0.1  - initial release
       fix applyFilter
       removed old stuff
       renamed vars for obfuscation
       added progress bar repositioning on window resize
0.11 - minor changes
       some getFilterState improvements
       clearPreviousBounds
       getPreviousBounds
       en/disableZooming()/zoomingEnabled() // added to start/endDelicateProcedure
       allowNestedDelicateProcedures(bool) // disallowed by default

todo:
	pauseOnMoveEndHandler
	resumeOnMoveEndHandler

*/
function BpMap() {
var bpLicense = false;
var bpLogoOk  = false;
function BpMap() {
	GMap2.apply(this,arguments);

	this.bpOnMoveUrl       = '';
	this.bpOnMoveCallback  = false;
	this.bpGeocodeUrl      = '';
	this.bpOnMovePadding   = 0;
	this.bpDProcLevel      = 0;
	this.bpLastAjaxRequest = null;
	this.bpAfterDownloadCallback = null;
  this.bpLatField        = 'lat';
  this.bpLngField        = 'lng';
  this.bpZoomingEnabled  = true;
  this.bpMarkerClass     = (typeof(window.BpMarker) != 'undefined' ? window.BpMarker : typeof(window.BpMarkerLight) != 'undefined' ? window.BpMarkerLight : null);
  this.bpStopDownload    = false;
  this.bpProgressBar     = null;
  this.bpCount           = 0; // for progress bars
  this.bpStep            = 10; // for progress bars
  this.bpCurrentMarker   = null;
  this.bpMarkerData      = new Array(); // for progress bars

  this.bpBootstrapDataFunc = function (bpData) { return eval(bpData) };

  this.bpOverlays = {};
  this.bpCurrentOverlayId = 0;
}

var bpMapPrototype = BpMap.prototype;

for(var bpProp in GMap2.prototype)
  bpMapPrototype[bpProp] = GMap2.prototype[bpProp];


bpMapPrototype.getGeocoder = function() {
	if(!this.bpGeocoder)
		this.bpGeocoder = new GClientGeocoder();

	return this.bpGeocoder;
};

bpMapPrototype.addMarkerByAddress = function(bpAddress,bpOnError,bpMarkerClass,bpData) {
	var bpGeocoder = this.getGeocoder();

	var bpCallback = this.bpCreateGeocoderCallback(bpAddress,bpOnError,bpMarkerClass,bpData);
	bpGeocoder.getLatLng(bpAddress,bpCallback);
};

bpMapPrototype.bpCreateGeocoderCallback = function(bpAddress,bpOnError,bpMarkerClass,bpData) {

	// setup the error handler - it can be a string, number, or function; otherwise, a sensible default
	var bpOnErrorCallback;
	if(typeof(bpOnError) == 'string' || typeof(bpOnError) == 'number') {
		bpOnErrorCallback = function() { alert(bpOnError) };
	} else if(typeof(bpOnError) != 'function') {
		bpOnErrorCallback = function() { alert(bpAddress + ' not found.') };
	} else {
		bpOnErrorCallback = bpOnError;
	}

	// setup the marker class default
	if(typeof(bpMarkerClass) != 'function')
		bpMarkerClass = GMarker;

	var bpMap = this;
	return function(bpLatlng) {
		if(!bpLatlng) {
			bpOnErrorCallback();
		} else { // we have a latlng
			var bpMarker = new bpMarkerClass(bpLatlng);
			if(bpData && typeof(bpMarker.setUserData) == 'function')
				bpMarker.setUserData(bpData);
			bpMap.addOverlay(bpMarker);
		}
	};
};

bpMapPrototype.zoomToMarkers = function(bpMargin,bpPoints,bpMaxZoom) {
  if(!bpPoints) {
	  var bpMarkers = this.getMarkers();
	  if(bpMarkers.length == 0)
	  	return;

    var bpPoints = new Array();
    for(var i = 0; i < bpMarkers.length; i++)
      bpPoints.push(bpMarkers[i].getPoint());
  } else if(bpPoints.length && typeof(bpPoints[0].getPoint) == 'function') {
    var bpP = new Array();
    for(var i = 0; i < bpPoints.length; i++)
      bpP.push(bpPoints[i].getPoint());
    bpPoints = bpP;
  }

	bpMargin = bpMargin || 0;

	var bpBounds = new GLatLngBounds(bpPoints[0]);
	for(var i = 1; i < bpPoints.length; i++) {
		bpBounds.extend(bpPoints[i]);
	}
	var bpSpan = bpBounds.toSpan();
	var bpNe = bpBounds.getNorthEast();
	var bpSw = bpBounds.getSouthWest();
	var bpNewNE = new GLatLng(bpNe.lat()+bpMargin*bpSpan.lat(),bpNe.lng()+bpMargin*bpSpan.lng());
	bpBounds.extend(bpNewNE);
	var bpNewSW = new GLatLng(bpSw.lat()-bpMargin*bpSpan.lat(),bpSw.lng()-bpMargin*bpSpan.lng());
	bpBounds.extend(bpNewSW);

	var bpLat = bpBounds.getSouthWest().lat() + .5 * bpBounds.toSpan().lat();
	var bpLng = bpBounds.getSouthWest().lng() + .5 * bpBounds.toSpan().lng();
	var bpCenter = new GLatLng(bpLat,bpLng);

	var bpZoom = this.getBoundsZoomLevel(bpBounds);
  if(arguments.length > 2 && bpZoom > bpMaxZoom)
    bpZoom = bpMaxZoom;

	this.setCenter(bpCenter,bpZoom);
};

bpMapPrototype.addOverlay = function(bpO) {
	if(typeof(bpO._bpid) == 'undefined')
		bpO._bpid = ++this.bpCurrentOverlayId;

	this.bpOverlays[bpO._bpid] = bpO;

  GMap2.prototype.addOverlay.call(this,bpO);
	
	return bpO._bpid;
};

bpMapPrototype.removeOverlay = function(bpO) {
  delete this.bpOverlays[bpO._bpid];

	GMap2.prototype.removeOverlay.call(this,bpO);
};

bpMapPrototype.clearOverlays = function() {
  var bpOverlays = this.getOverlays();
  for(var i = 0; i < bpOverlays.length; i++) {
    this.removeOverlay(bpOverlays[i]);
  }
};

bpMapPrototype.getOverlays = function() {
  var bpO = new Array();
  for(var bpProp in this.bpOverlays)
    bpO.push(this.bpOverlays[bpProp]);
  return bpO;
};

bpMapPrototype.getMarkers = function() {
	var bpA = this.getOverlays();
	var bpI = 0;
	while(bpI < bpA.length) {
		if(typeof(bpA[bpI].getIcon) != 'function') {
			bpA.splice(bpI,1);
		} else {
			bpI++;
		}
	}
	return bpA;
};

bpMapPrototype.bpOnGetDataError = function() {
	this.endDelicateProcedure();
	alert('There has been a server error.');
};

bpMapPrototype.getOnMoveUrl = function(bpQuery) {
  if(typeof(this.bpOnMoveUrl) == 'string' && bpQuery)
    return this.bpOnMoveUrl + this.bpGetQueryString();
  else
  	return this.bpOnMoveUrl;
};

bpMapPrototype.setOnMoveUrl = function(bpUrl,bpNow,bpExecuteOnZoomIn) {
	this.bpOnMoveUrl       = bpUrl;
	this.bpExecuteOnZoomIn = bpExecuteOnZoomIn;
  
  if(!this.bpOnMoveUrl) {
    if(this.bpOnMoveListener) {
      GEvent.removeListener(this.bpOnMoveListener);
      delete this.bpOnMoveListener;
    }
    return;
  }

  if(!this.bpOnMoveListener) {
    this.bpOnMoveListener = GEvent.addListener(this,'moveend',GEvent.callback(this,this.bpOnMoveEnd));
  }

  if(bpNow)
    this.bpOnMoveEnd();
};

bpMapPrototype.setOnMoveEnd = bpMapPrototype.setOnMoveUrl;
bpMapPrototype.getOnMoveEnd = bpMapPrototype.getOnMoveUrl;

bpMapPrototype.getData = function(bpUrl,bpCb,bpErr,bpPost) {
  this.bpStopDownload = false;

	this.startDelicateProcedure(this.bpProgressBar);

  if(typeof(this.onBeforeGetData) == 'function')
    this.onBeforeGetData();

	var bpCallback;
	if(typeof(bpCb) == 'function') {
	  bpCallback = GEvent.callback(this,function(){
      bpCb.apply(this,arguments);
      this.bpDoAfterGetData();
	    this.endDelicateProcedure();
	  });
	} else {
	  bpCallback = GEvent.callback(this,this.bpProcessDownload);
	}

	var bpOnError;
  if(typeof(bpErr) == 'string') {
    bpOnError = GEvent.callback(this,function() {
      alert(bpErr);
      this.endDelicateProcedure();
    });
  } else if(typeof(bpErr) == 'function') {
    bpOnError = GEvent.callback(this,function(){
      bpErr.apply(this,arguments);
      this.endDelicateProcedure();
    });
  } else {
    bpOnError = GEvent.callback(this,this.bpOnGetDataError);
  }

  if(this.bpProgressBar) {
    this.bpProgressBar.setPercentage(0);
    this.bpPositionProgressBar();
  }

	this.bpLastAjaxRequest = BpDownloadUrl(bpUrl,bpCallback,bpOnError,bpPost);
  return this.bpLastAjaxRequest;
};

bpMapPrototype.getBootstrapDataFunc = function() {
  return this.bpBootstrapDataFunc;
};

bpMapPrototype.setBootstrapDataFunc = function(bpFunc) {
  this.bpBootstrapDataFunc = bpFunc;
};

bpMapPrototype.bpProcessDownload = function(bpData) {
  try {
	  this.bpMarkerData = this.bpBootstrapDataFunc(bpData);
	} catch(e) {
    if(typeof(this.onError) == 'function')
	    this.onError(e);
	  else
      this.bpOnGetDataError();
  }
  this.bpCount = 0;

  setTimeout(GEvent.callback(this,this.bpProcessDownloadPhaseTwo),50);
};

bpMapPrototype.mapArray = function(bpArray) {
  this.bpMarkerData = bpArray;

  if(this.bpProgressBar) {
    this.bpProgressBar.setPercentage(0);
    this.bpPositionProgressBar();
  }

  this.startDelicateProcedure(this.bpProgressBar);

  this.bpCount = 0;
  setTimeout(GEvent.callback(this,this.bpProcessDownloadPhaseTwo),50);
};

bpMapPrototype.bpProcessDownloadPhaseTwo = function() {
  var bpMax = Math.min(this.bpCount + this.bpStep, this.bpMarkerData.length);

  if(!this.bpStopDownload) {
    if(!this.bpMarkerClass) {
      alert('I was not able to find what marker class you want me to use.\nPlease load either BpMarker or BpMarkerLight\n(or your own marker class which implements the proper interface)\nto continue.');
      return;
    }
    for(var i = this.bpCount; i < bpMax; i++) {
      var bpMarker = new this.bpMarkerClass(new GLatLng(this.bpMarkerData[i][this.bpLatField],this.bpMarkerData[i][this.bpLngField]));
      bpMarker.setUserData(this.bpMarkerData[i]);
      this.addOverlay(bpMarker);
    }
  } else {
    bpMax = this.bpMarkerData.length;
    this.bpStopDownload = false;
  }

  if(bpMax < this.bpMarkerData.length) {
    this.bpCount = bpMax;
    
    if(this.bpProgressBar) {
      var bpPercent = 100 * this.bpCount / this.bpMarkerData.length;
      var bpCurrent = this.bpCount + 1;
      var bpTotal   = this.bpMarkerData.length;
      this.bpProgressBar.setPercentage(bpPercent,bpCurrent,bpTotal);
    }

    setTimeout(GEvent.callback(this,this.bpProcessDownloadPhaseTwo),50);
  } else {
    this.bpCount = 0;
    this.bpMarkerData = new Array();

    if(this.bpProgressBar) {
      this.bpProgressBar.setPercentage(0);
      this.bpProgressBar.hide();
    }

	  this.endDelicateProcedure();
    this.bpDoAfterGetData();
  }
};

bpMapPrototype.setProgressBar = function(bpPb,bpOset) {
  this.bpProgressBar = bpPb;
  
  if(bpOset)
    this.bpProgressBarOffset = bpOset;
  else
    this.bpProgressBarOffset = {x:0,y:0};

  GEvent.addDomListener(window,'resize',GEvent.callbackArgs(this,this.bpPositionProgressBar,true));
};

bpMapPrototype.getProgressBar = function() {
  return this.bpProgressBar;
};

bpMapPrototype.bpPositionProgressBar = function(bpNoStartShow) {
  // we might be setting the position due to a window resize
  // in that case, we only want to show it if it's already visible
  if(!this.bpProgressBar || (bpNoStartShow && !this.bpProgressBar.isVisible()))
    return;

  var bpTop  = BpMap.bpGetPageY(this.getContainer()) + this.bpProgressBarOffset.y;
  var bpLeft = BpMap.bpGetPageX(this.getContainer()) + this.bpProgressBarOffset.x;
  this.bpProgressBar.show(bpTop,bpLeft);
};

bpMapPrototype.stopDownloading = function(bpBool) {
  if(arguments.length != 0)
    this.bpStopDownload = bpBool;

  return this.bpStopDownload;
};

bpMapPrototype.bpDoAfterGetData = function() {
	if(typeof(this.onAfterGetData) == 'function')
  	this.onAfterGetData();
};

bpMapPrototype.setCenter = function() {
	GMap2.prototype.setCenter.apply(this,arguments);

	if(!this.bpIsLinked) {
		this.bpAddLogo();
		this.bpIsLinked = true;
	}
};

bpMapPrototype.bpAddLogo = function() {
  if(!bpLicense) {
    if(!this._BpLogo) {
      if(typeof(BpLogo) == 'undefined') {
        alert('BpBrowser is required to use BpMap\nhttp://www.gmaptools.com/');
        return;
      }
      this.addControl(new BpLogo());
      this._BpLogo = true;
    }
  }
  bpLogoOk = true;
};

bpMapPrototype.bpGetDataBounds = function() {
	if(!this.bpOnMovePadding)
		return this.getBounds();

	var bpBounds = this.getBounds();
	var bpMinX = bpBounds.getSouthWest().lng();
	var bpMinY = bpBounds.getSouthWest().lat();
	var bpMaxX = bpBounds.getNorthEast().lng();
	var bpMaxY = bpBounds.getNorthEast().lat();

	bpMinX -= this.bpOnMovePadding * (bpMaxX - bpMinX);
	bpMinY -= this.bpOnMovePadding * (bpMaxY - bpMinY);
	bpMaxX += this.bpOnMovePadding * (bpMaxX - bpMinX);
	bpMaxY += this.bpOnMovePadding * (bpMaxY - bpMinY);

	return new GLatLngBounds(new GLatLng(bpMinY,bpMinX),new GLatLng(bpMaxY,bpMaxX));
};

bpMapPrototype.bpGetQueryString = function() {
	var bpBounds = this.bpGetDataBounds();
	var bpTop    = bpBounds.getNorthEast().lat();
	var bpBottom = bpBounds.getSouthWest().lat();
	var bpRight  = bpBounds.getNorthEast().lng();
	var bpLeft   = bpBounds.getSouthWest().lng();

	var bpQs = '?top=' + bpTop + '&bottom=' + bpBottom + '&left=' + bpLeft + '&right=' + bpRight;

	if(this.bpPreviousDataBounds) {
		var bpPb = this.bpPreviousDataBounds;
		var bpNotop    = bpPb.getNorthEast().lat();
		var bpNobottom = bpPb.getSouthWest().lat();
		var bpNoright  = bpPb.getNorthEast().lng();
		var bpNoleft   = bpPb.getSouthWest().lng();
		bpQs += '&notop=' + bpNotop + '&nobottom=' + bpNobottom + '&noleft=' + bpNoleft + '&noright=' + bpNoright;
	}

	return bpQs;
};

bpMapPrototype.removeUnviewableMarkers = function(bpAll) {
  var bpMarkers = this.getMarkers();
  var bpBounds = this.bpGetDataBounds();
  for(var i = 0; i < bpMarkers.length; i++) {
    if(!bpBounds.contains(bpMarkers[i].getPoint()) && (bpAll || !this.bpKeepUnviewableMarkerTest(bpMarkers[i])))
      this.removeOverlay(bpMarkers[i]);
  }
};

bpMapPrototype.setKeepUnviewableMarkerTest = function(bpTestFunc) {
	this.bpKeepUnviewableMarkerTest = bpTestFunc;
};

bpMapPrototype.getKeepUnviewableMarkerTest = function() {
	return this.bpKeepUnviewableMarkerTest;
};

bpMapPrototype.bpIsZoomIn = function() {
  if(!this.bpPreviousDataBounds || !this.bpPreviousCenter)
    return false;

  if(this.getCenter().equals(this.bpPreviousCenter) && this.bpPreviousDataBounds.containsBounds(this.getBounds()))
    return true;

  return false;
};

bpMapPrototype.allowNestedDelicateProcedures = function(bpBool) {
  this.bpAllowNestedDelicateProcedures = bpBool;
};

bpMapPrototype.bpOnMoveEnd = function() {
  if(!this.bpOnMoveUrl)
    return;
  
  if(this.bpDProcLevel > 0 && !this.bpAllowNestedDelicateProcedures)
    return;
  
	this.startDelicateProcedure();

  this.removeUnviewableMarkers();

  var bpCalled = false;
  var bpIsBigMove = (!this.bpPreviousDataBounds || !this.bpPreviousDataBounds.containsBounds(this.getBounds()));
  if((!this.bpIsZoomIn() && bpIsBigMove) || this.bpExecuteOnZoomIn) {
	  var bpUrl = this.getOnMoveUrl(true);
    if(typeof(bpUrl) == 'function') {
      bpUrl.call(this);
    } else {
      var bpCb  = this.bpOnMoveCallback;
      var bpErr = this.onError;
      this.bpLastAjaxRequest = this.getData(bpUrl,bpCb,bpErr);
  	}
  	this.bpSetPreviousState();
    bpCalled = this.bpOnMoveCallback;
  } else {
    this.bpDoAfterGetData();
    if(this.bpIsZoomIn())
  	  this.bpSetPreviousState();
    else
      this.bpSetPreviousState(true); // small move, so don't update the data bounds
  }

  if(!bpCalled || this.bpCallEndDP)
    this.endDelicateProcedure();
};

bpMapPrototype.bpSetPreviousState = function(bpIgnoreBounds) {
	this.bpPreviousCenter = this.getCenter();
  
  if(!bpIgnoreBounds)
  	this.bpPreviousDataBounds = this.bpGetDataBounds();
};

bpMapPrototype.getPreviousBounds = function() {
  return this.bpPreviousDataBounds;
};

// takes 3 args: 
// 1) an id which will match marker.getId(); - this will be false if you're searching for the id in the getUserData object
// 2) an id which will match marker.getUserData()[id_field]; and
// 3) an id field to use, defaults to 'id'
bpMapPrototype.getMarkerById = function(bpId,bpUid,bpFieldName) {
  var bpIdField = bpFieldName || 'id';
  var bpMarkers = this.getMarkers();
  for(var i = 0; i < bpMarkers.length; i++) {
    if(bpId && bpMarkers[i].getId() == bpId)
      return bpMarkers[i];
    else if(!bpId && bpMarkers[i].getUserData()[bpIdField] == bpUid)
      return bpMarkers[i];
  }
  return null;
};

bpMapPrototype.getLastAjaxRequest = function() {
	return this.bpLastAjaxRequest;
};

bpMapPrototype.getMarkerClass = function() {
	return this.bpMarkerClass;
};

bpMapPrototype.setMarkerClass = function(bpClassName) {
	this.bpMarkerClass = bpClassName;
};

bpMapPrototype.getOnMovePadding = function() {
	return this.bpOnMovePadding;
};

bpMapPrototype.setOnMovePadding = function(bpPadding) {
	this.bpOnMovePadding = bpPadding;
};

bpMapPrototype.getOnMoveCallback = function() {
	return this.bpOnMoveCallback;
};

bpMapPrototype.setOnMoveCallback = function(bpCb,bpRecursive) {
	this.bpOnMoveCallback = bpCb;
  this.bpCallEndDP = !bpRecursive;
};

bpMapPrototype.getLatitudeFieldName = function() {
	return this.bpLatField;
};

bpMapPrototype.setLatitudeFieldName = function(bpName) {
	this.bpLatField = bpName;
};

bpMapPrototype.getLongitudeFieldName = function() {
	return this.bpLngField;
};

bpMapPrototype.setLongitudeFieldName = function(bpName) {
	this.bpLngField = bpName;
};

bpMapPrototype.getFilterForm = function() {
	return this.bpFilterForm;
};

bpMapPrototype.bpCreateFilterCallback = function(bpInput) {
  return GEvent.callback(this,function() {
    this.applyFilter(bpInput);
  });
};

bpMapPrototype.setFilterForm = function(bpForm,bpOnClick) {
	this.startDelicateProcedure();
	this.bpFilterForm = bpForm;
  this.bpCustomFilterFormCallback = bpOnClick;

	if(bpForm) {
    var bpE = bpForm.elements;
		for(var i = 0; i < bpE.length; i++) {
			if(bpE[i].name.indexOf('bpfilter_') == 0) {
				if(bpE[i].type == 'checkbox' || bpE[i].type == 'radio') {
					bpE[i].onclick = this.bpCreateFilterCallback(bpE[i]);
				} else {
					alert('Not written yet for this input type: ' + bpForm.elements[i].type);
				}
			}
		}
	}

	this.bpCreateFilterConfig();
	this.endDelicateProcedure();
};

bpMapPrototype.getFilterConfig = function() {
	if(!this.bpFilterConfig)
		this.bpCreateFilterConfig();

	return this.bpFilterConfig;
};

bpMapPrototype.bpCreateFilterConfig = function() {
	if(this.bpFilterConfig)
		return;

	if(!this.bpFilterForm)
		return;

	this.bpFilterConfig = {
		fields: new Array()
	};

	var bpE = this.bpFilterForm.elements;
	for(var i = 0; i < bpE.length; i++) {
		if((bpE[i].type == 'checkbox' || bpE[i].type == 'radio') && bpE[i].name.indexOf('bpfilter_') == 0) {
			var bpName = bpE[i].name.replace(/bpfilter_/,'');
			if(!this.bpFilterConfig[bpName]) {
				this.bpFilterConfig.fields.push(bpName);
				this.bpFilterConfig[bpName] = new Array();
			}
			this.bpFilterConfig[bpName].push(bpE[i]);
		}
	}
};

bpMapPrototype.applyFilter = function(bpInput) {
	if(!this.bpFilterForm)
		return;

  if(BpBrowser.type == BpBrowser.MSIE)
    setTimeout(GEvent.callback(this,this.startDelicateProcedure),1);
  else
    this.startDelicateProcedure();
  setTimeout(GEvent.callback(this,function(){
    this.applyFilterPhaseTwo(bpInput);
  }),100);
};

bpMapPrototype.clearPreviousBounds = function() {
  delete this.bpPreviousDataBounds;
};

var bpReGeLe = new RegExp('^>=<=\\s+(\\S+)\\s+(\\S+)$');
var bpReGeL  = new RegExp('^>=<\\s+(\\S+)\\s+(\\S+)$');
var bpReGLe  = new RegExp('^><=\\s+(\\S+)\\s+(\\S+)$');
var bpReGL   = new RegExp('^(?:><|<>)\\s+(\\S+)\\s+(\\S+)$');
var bpReGe   = new RegExp('^>=\\s+(.+)$');
var bpReLe   = new RegExp('^<=\\s+(.+)$');
var bpReL    = new RegExp('^<\\s+(.+)$');
var bpReG    = new RegExp('^>\\s+(.+)$');
var bpReE    = new RegExp('^=\\s+(.+)$');

bpMapPrototype.bpGetAbbreviatedFilterState = function() {
  var bpConfig = this.getFilterConfig();
  if(!bpConfig)
    return '';
  
  var bpData = {};
  
  // return a list of all filter checkboxes which are checked OFF
  // type=val1|val2|val3&list_price=val1|val2|val3
  var bpFields = bpConfig.fields;
  for(var i = 0; i < bpFields.length; i++) {
    var bpInputs = bpConfig[bpFields[i]];
    for(var j = 0; j < bpInputs.length; j++) {
      if(bpInputs[j].checked)
        continue;
      if(!bpData[bpFields[i]])
        bpData[bpFields[i]] = [];
      bpData[bpFields[i]].push(j);
    }
    if(bpData[bpFields[i]])
      bpData[bpFields[i]] = bpData[bpFields[i]].join('-');
  }
  
  return BpDownloadUrl.serialize(bpData);
};

bpMapPrototype.getFilterState = function(bpInput,bpSetBounds,bpSetPrevBounds,bpAbbreviateResults) {
  if(bpAbbreviateResults)
    return this.bpGetAbbreviatedFilterState();

  // if the input is not checked, we don't need to get anything from the server
  if(false && !bpInput.checked) // commented out for now, for flexibility
    return;

  // do we have a filter form?
  var bpConfig = this.getFilterConfig();
  if(!bpConfig)
    return;

  var bpFoundChecked = new Array();
  var bpState = {};

  //
  // for each category (field) in the filter form
  //
  for(var i = 0; i < bpConfig.fields.length; i++) {
    var bpField = bpConfig.fields[i];
    var bpCount = 0;
    var bpPrevMaxValue = '';
    
    //
    // for each checkbox in a category (field)
    //
    var bpFoundUnChecked = false;
    for(var j = 0; j < bpConfig[bpField].length; j++) {
      bpFoundChecked[bpField] = bpFoundChecked[bpField] || bpConfig[bpField][j].checked;
      if(!bpConfig[bpField][j].checked) {
        bpFoundUnChecked = true;
        continue;
      }
      if(bpInput && 'bpfilter_' + bpField == bpInput.name && bpInput !== bpConfig[bpField][j])
        continue;
      var bpKey = bpField + bpCount;
      var bpVal = bpConfig[bpField][j].value;
      if(bpReGeLe.test(bpVal)) {
        if(bpPrevMaxValue == RegExp.$1) {
          bpState[bpField + (bpCount-1) + 'max'] = '<=' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
          continue;
        } else {
          bpState[bpKey + 'min'] = '>=' + RegExp.$1;
          bpState[bpKey + 'max'] = '<=' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
        }
      } else if(bpReGeL.test(bpVal)) {
        if(bpPrevMaxValue == RegExp.$1) {
          bpState[bpField + (bpCount-1) + 'max'] = '<' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
          continue;
        } else {
          bpState[bpKey + 'min'] = '>=' + RegExp.$1;
          bpState[bpKey + 'max'] = '<'  + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
        }
      } else if(bpReGLe.test(bpVal)) {
        if(bpPrevMaxValue == RegExp.$1) {
          bpState[bpField + (bpCount-1) + 'max'] = '<=' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
          continue;
        } else {
          bpState[bpKey + 'min'] = '>'  + RegExp.$1;
          bpState[bpKey + 'max'] = '<=' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
        }
      } else if(bpReGL.test(bpVal)) {
        if(bpPrevMaxValue == RegExp.$1) {
          bpState[bpField + (bpCount-1) + 'max'] = '<' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
          continue;
        } else {
          bpState[bpKey + 'min'] = '>' + RegExp.$1;
          bpState[bpKey + 'max'] = '<' + RegExp.$2;
          bpPrevMaxValue = RegExp.$2;
        }
      } else if(bpReGe.test(bpVal)) {
        bpState[bpKey + 'min'] = '>=' + RegExp.$1;
        bpPrevMaxValue = '';
      } else if(bpReLe.test(bpVal)) {
        bpState[bpKey + 'min'] = '<=' + RegExp.$1;
        bpPrevMaxValue = '';
      } else if(bpReL.test(bpVal)) {
        bpState[bpKey + 'min'] = '<' + RegExp.$1;
        bpPrevMaxValue = '';
      } else if(bpReG.test(bpVal)) {
        bpState[bpKey + 'min'] = '>' + RegExp.$1;
        bpPrevMaxValue = '';
      } else if(bpReE.test(bpVal)) {
        bpState[bpKey + 'min'] = '=' + RegExp.$1;
        bpPrevMaxValue = '';
      } else {
        alert('failed to match anything: ' + bpVal);
      }

      bpCount++;
    }
    //
    // if we didn't find an unchecked box for this category, throw out the category
    //
    if(!bpFoundUnChecked) {
      var bpReKeyTest = new RegExp('^' + bpField + '\\d+m(?:in|ax)$');
      for(var bpProp in bpState)
        if(bpReKeyTest.test(bpProp))
          delete bpState[bpProp];
    }
  }

  for(var i = 0; i < bpConfig.fields.length; i++)
    if(!bpFoundChecked[bpConfig.fields[i]])
      return;

  if(bpSetBounds) {
    var bpB = this.getBounds();
    var bpNe = bpB.getNorthEast();
    var bpSw = bpB.getSouthWest();
    bpState.top    = bpNe.lat();
    bpState.bottom = bpSw.lat();
    bpState.left   = bpSw.lng();
    bpState.right  = bpNe.lng();
  }

  if(bpSetPrevBounds && this.bpPreviousDataBounds) {
    var bpNep = this.bpPreviousDataBounds.getNorthEast();
    var bpSwp = this.bpPreviousDataBounds.getSouthWest();
    bpState.notop    = bpNep.lat();
    bpState.nobottom = bpSwp.lat();
    bpState.noleft   = bpSwp.lng();
    bpState.noright  = bpNep.lng();
  }

  return bpState;
};

bpMapPrototype.applyFilterPhaseTwo = function(bpInput) {
  if(typeof(this.onBeforeApplyFilter) == 'function')
    this.onBeforeApplyFilter();
  
  if(typeof(this.bpCustomFilterFormCallback) == 'function') {
    this.bpCustomFilterFormCallback(bpInput);
  } else {
  	var bpMarkers = this.getMarkers();
	  var bpFilterConfig = this.getFilterConfig();
	  for(var i = 0; i < bpMarkers.length; i++) {
      if(bpInput && !bpInput.checked) { // there's an input that's just been clicked - BpMarker will report on only that input
        if(!bpMarkers[i].applyFilter(bpInput,bpFilterConfig))
          bpMarkers[i].hide()
      } else if(bpInput) { // only check invisible markers
        if(!bpMarkers[i].isVisible() && bpMarkers[i].applyFilter(bpInput,bpFilterConfig))
          bpMarkers[i].show();
      } else {
        if(bpMarkers[i].applyFilter(null,bpFilterConfig))
          bpMarkers[i].show();
        else
          bpMarkers[i].hide();
      }
	  }
  }

  if(typeof(this.onAfterApplyFilter) == 'function')
    this.onAfterApplyFilter();

	setTimeout(GEvent.callback(this,this.endDelicateProcedure),100);
};

bpMapPrototype.applyMarkers = function(bpCb,bpView) {
//  this.startDelicateProcedure();
  if(BpBrowser.type == BpBrowser.MSIE)
    setTimeout(GEvent.callback(this,this.startDelicateProcedure),1);
  else
    this.startDelicateProcedure();
  setTimeout(GEvent.callback(this,function(){
    this.applyMarkersPhaseTwo(bpCb,bpView);
  }),100);
};

bpMapPrototype.applyMarkersPhaseTwo = function(bpCb,bpView) {
	var bpMarkers = this.getMarkers();

	for(var i = 0; i < bpMarkers.length; i++)
    bpCb.call(bpMarkers[i],bpView);

  setTimeout(GEvent.callback(this,this.endDelicateProcedure),100);
};

bpMapPrototype.enableZooming = function() {
  if(this.bpZoomEndListener) {
    GEvent.removeListener(this.bpZoomEndListener);
    delete this.bpZoomEndListener;
    delete this.bpOkZoomLevel;
  }
};

bpMapPrototype.disableZooming = function() {
  this.bpOkZoomLevel = this.getZoom();
  this.bpZoomEndListener = GEvent.addListener(this,'zoomend',GEvent.callback(this,this.bpOnZoomEnd));
};

bpMapPrototype.bpOnZoomEnd = function(bpOldZoom,bpNewZoom) {
  if(!this.zoomingEnabled() && bpNewZoom != this.bpOkZoomLevel)
    this.setZoom(this.bpOkZoomLevel);
};

bpMapPrototype.zoomingEnabled = function() {
  return this.bpZoomEndListener ? false : true;
};

bpMapPrototype.startDelicateProcedure = function(bpNoshow) {
  if(!bpNoshow)
  	setTimeout(GEvent.callback(this,this.showLoadingMessage),1);

  if(this.bpFilterForm) {
    var bpE = this.bpFilterForm.elements;
    for(var i = 0; i < bpE.length; i++)
      bpE[i].disabled = true;
  }

  if(this.bpDProcLevel == 0) {
  	this.bpDraggingWasEnabled = this.draggingEnabled();
    this.bpZoomingWasEnabled  = this.zoomingEnabled();
  }

	this.bpDProcLevel++;

	this.disableDragging();
	this.disableZooming();
  document.body.style.cursor = 'wait';
};

bpMapPrototype.clearDelicateProcedures = function() {
  this.bpDProcLevel = 1;
  this.endDelicateProcedure();
};

bpMapPrototype.endDelicateProcedure = function() {
  if(this.bpDProcLevel == 0)
    return;

	this.bpDProcLevel--;
	if(this.bpDProcLevel == 0) {
		setTimeout(GEvent.callback(this,this.hideLoadingMessage),1);
    if(this.bpDraggingWasEnabled)
  		this.enableDragging();
    if(this.bpZoomingWasEnabled)
  		this.enableZooming();

    if(this.bpFilterForm) {
      var bpE = this.bpFilterForm.elements;
      for(var i = 0; i < bpE.length; i++)
        bpE[i].disabled = false;
    }

    delete this.bpDraggingWasEnabled;
    delete this.bpZoomingWasEnabled;
    document.body.style.cursor = 'default';
	}
};

bpMapPrototype.setLoadingMessage = function(bpDiv,bpOffset) {
	this.bpLoadingDiv = bpDiv;
  if(bpOffset)
    this.bpLoadingOffset = bpOffset;
};

bpMapPrototype.getLoadingMessage = function() {
	return this.bpLoadingDiv;
};

bpMapPrototype.showLoadingMessage = function() {
	if(!this.bpLoadingDiv)
		return;

  var bpDiv = this.bpLoadingDiv;
  if(this.bpLoadingOffset) {
    var bpX = BpMap.bpGetPageX(this.getContainer());
    var bpY = BpMap.bpGetPageY(this.getContainer());
    bpDiv.style.left = (bpX + this.bpLoadingOffset.x) + 'px';
    bpDiv.style.top  = (bpY + this.bpLoadingOffset.y) + 'px';
  }
	bpDiv.style.display = '';
	bpDiv.style.visibility = 'visible';
};

bpMapPrototype.hideLoadingMessage = function() {
	if(!this.bpLoadingDiv)
		return;

	this.bpLoadingDiv.style.display = 'none';
	this.bpLoadingDiv.style.visibility = 'hidden';
};

bpMapPrototype.isUnderEvent = function(bpE) {
  if(!bpE || typeof(bpE.clientX) == 'undefined')
    return false;

  return this.isUnderPagePixel(bpE.clientX,bpE.clientY);
};

bpMapPrototype.isUnderPagePixel = function(bpX,bpY) {
  bpX += BpMap.bpGetScrollX(window,true);
  bpY += BpMap.bpGetScrollY(window,true);

	var bpDiv  = this.getContainer();
	var bpMinX = BpMap.bpGetPageX(bpDiv);
	var bpMaxX = bpMinX + this.getSize().width;
	var bpMinY = BpMap.bpGetPageY(bpDiv);
	var bpMaxY = bpMinY + this.getSize().height;

	if(bpX >= bpMinX && bpX < bpMaxX && bpY >= bpMinY && bpY < bpMaxY) {
		return true;
	}
	return false;
};

bpMapPrototype.bpGetMapCenterPagePixel = function() {
  var bpContainer = this.getContainer();
	var bpX = Math.round(BpMap.bpGetPageX(bpContainer) + (BpMap.bpGetWidth(bpContainer)/2));
	var bpY = Math.round(BpMap.bpGetPageY(bpContainer) + (BpMap.bpGetHeight(bpContainer)/2));
	return new GPoint(bpX,bpY);
};

bpMapPrototype.fromPagePixelToLatLng = function(bpPoint) {
	var bpProjection = this.getCurrentMapType().getProjection();

	var bpCenterPagePixel = this.bpGetMapCenterPagePixel();
	var bpX = bpCenterPagePixel.x - bpPoint.x;
	var bpY = bpCenterPagePixel.y - bpPoint.y;
	var bpOffsetPagePixel = new GPoint(bpX,bpY);
	var bpCenterTilePixel = bpProjection.fromLatLngToPixel(this.getCenter(),this.getZoom());

	bpCenterTilePixel.x -= bpOffsetPagePixel.x;
	bpCenterTilePixel.y -= bpOffsetPagePixel.y;

	return bpProjection.fromPixelToLatLng(bpCenterTilePixel,this.getZoom());
};

bpMapPrototype.fromLatLngToPagePixel = function(bpLatlng) {
	var bpProjection = this.getCurrentMapType().getProjection();
	
	var bpLatLngTilePixel = bpProjection.fromLatLngToPixel(bpLatlng,this.getZoom());
	var bpCenterTilePixel = bpProjection.fromLatLngToPixel(this.getCenter(),this.getZoom());

	var bpX = bpLatLngTilePixel.x - bpCenterTilePixel.x;
	var bpY = bpLatLngTilePixel.y - bpCenterTilePixel.y;

  var bpContainer = this.getContainer();
	bpX += BpMap.bpGetPageX(bpContainer);
	bpY += BpMap.bpGetPageY(bpContainer);

	bpX += Math.round(BpMap.bpGetWidth(bpContainer)/2);
	bpY += Math.round(BpMap.bpGetHeight(bpContainer)/2);
	
	return new GPoint(bpX,bpY);
};

BpMap.bpGetPageX = function(bpDiv) {
  var bpX = 0;
  while (bpDiv) {
    if(typeof(bpDiv.offsetLeft) != 'undefined')
      bpX += bpDiv.offsetLeft;
    bpDiv = bpDiv.offsetParent;
  }
  return bpX;
};
BpMap.bpGetPageY = function(bpDiv) {
  var bpY = 0;
  while (bpDiv) {
    if (typeof(bpDiv.offsetTop) != 'undefined') bpY += bpDiv.offsetTop;
    bpDiv = bpDiv.offsetParent;
  }
  return bpY;
};
BpMap.bpGetHeight = function(bpDiv) {
  if(typeof(bpDiv.offsetHeight) != 'undefined' && typeof(bpDiv.style.height) == 'string') {
    return bpDiv.offsetHeight;
  }else if(typeof(bpDiv.style.pixelHeight) != 'undefined') {
    return bpDiv.style.pixelHeight;
  }
  return 0;
};
BpMap.bpGetWidth = function(bpDiv) {
  if(typeof(bpDiv.offsetWidth) != 'undefined')
    return bpDiv.offsetWidth;
  else if(typeof(bpDiv.style.pixelWidth) != 'undefined')
    return bpDiv.style.pixelWidth;
  return 0;
};
BpMap.bpGetScrollX = function() {
  if(document.documentElement && document.documentElement.scrollLeft)
    return document.documentElement.scrollLeft;
  else if(document.body && typeof(document.body.scrollLeft) != 'undefined')
    return document.body.scrollLeft;
  return 0;
};
BpMap.bpGetScrollY = function () {
  if(document.documentElement && document.documentElement.scrollTop)
    return document.documentElement.scrollTop;
  else if(document.body && typeof(document.body.scrollTop) != 'undefined')
    return document.body.scrollTop;
  return 0;
};

window.BpMap = BpMap;
}
BpMap();
