
function createNamespace(n){var p=n.split('.'),b=window,s;while(s=p.shift())b=!b[s]?b[s]={}:b[s];}
createNamespace('planeteye.map');
createNamespace('planeteye.map.services');
createNamespace('planeteye.map.services.state');


// this ends up representing the state of the map/sidebar??
// not necessarily - this can be just an event..
// planeteye.state.map.categories
// planeteye.state.map.flyout
function PETabEvent() { }
PETabEvent.prototype.sidebar = "";
PETabEvent.prototype.map = "";


createNamespace('planeteye.map.data');

// create a point object matching those returned by 'pointsGet'
planeteye.map.data.CreatePoint = function(lat, lng, open) {
    var point = new Array();
    point['@lat'] = lat;
    point['@lng'] = lng;
    point['@pointid'] = 'single.location';
    point['@type'] = 'place';
    point['@category'] = 'none';
    point.open = open;
    
    return point;
};

/**
 * Create a contents object matching those returned by 'pointGetContents'
 *
 *   point.place['@id']
 *   point.place['@lat']
 *   point.place['@lng']
 *   point.place.title
 *   point.place.description['#cdata-section']
 *   point.place.address
 *   point.place.phone
 *   point.place.rating
 *   point.place.planeteyeUrl
 *   point.place.categories.category[j]['@name']
 *   point.place.categories.category[j]['@code']
 */
planeteye.map.data.CreatePointPlaceContents = function(point, title, address, description) {
    var pointContents = new Array();
    pointContents.place = new Array();
    pointContents.place['@lat'] = point['@lat'];
    pointContents.place['@lng'] = point['@lng'];
    pointContents.place['@id'] = point['@pointid'];
    pointContents.place.title = title;
    pointContents.place.description = new Array();
    pointContents.place.description['#cdata-section'] = description;
    pointContents.place.address = address;
    pointContents.place.planeteyeUrl = '';
    pointContents.place.categories = {};
    //pointContents.place.categories.category = new Array();
    
    return pointContents;
}


/**
 * Create a place object, as used by the sidebar.
 */
planeteye.map.data.CreateSidebarPlaceFromPoint = function(title, address, description, dotid) {
    var place = {};
    
    place.Title = title;
    place.CategoryName = '';
    place.Description = description;
    place.Address = address;
    place.DotId = dotid;
    
    return place;
}

/**
 * Find the location of the current user (approximately)
 */
planeteye.map.data.GetClientLocation = function(map) {
   if (typeof(navigator.geolocation) != 'undefined') {
      navigator.geolocation.getCurrentPosition(function(position) {
         var position = new GLatLng(position.coords.latitude, position.coords.longitude);
         map.setCenter(position, 13);
      });
   } else if (typeof(google.loader) != 'undefined' && typeof(google.loader.ClientLocation) != 'undefined') {
      var position = new GLatLng(google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude);
      map.setCenter(position, 11);
   }
}





/**
 * Google map constructor.
 */
function PEGoogleMap(lat, lng, zoom, mapElementId, sets, setsName, customerId, version, showAddToTpk, showLogo, showLegend, point, pointContents, hideSidebar, basePath, recenter)
{

    if (!GBrowserIsCompatible()) return;
    
    // doesn't need to match anything, but must initialize prettyPhoto for api calls below: prettyPhoto.open
    //if (eval('typeof $.prettyPhoto') === 'function') {
    $("a[rel^='match_nothing_prettyPhoto']").prettyPhoto({ theme: 'facebook' });
    //}

    
    // must be set before 'getCurrentTabAction'
    this.map_dom_object = document.getElementById(mapElementId);
    
    this.showAddToTpk = showAddToTpk !=null ? showAddToTpk : false;
    

    // we cache these values, and only read it from the DOM once, and that's here:
    this.cachedCurrentTabAction = this.getCurrentTabActionFromDOM();
    this.cachedCurrentTabColor = this.getCurrentTabColorFromDOM();
    this.cachedCurrentTabName = this.getCurrentTabNameFromDOM();

    
    // current state of the map
    this.state = {};
    this.state.showLogo = showLogo;
    this.state.showLegend = showLegend;
    // only display pins (travelpacks or a specific place) with all other categories.. not dots..
    if (sets && (sets.indexOf('map.') > -1 || sets.indexOf('place.') > -1)) {
        this.state.setsAlwaysOn = sets;
    } else {
        this.state.setsAlwaysOn = '';
    }
    this.state.setsInitial = sets;
    this.state.setsName = setsName;
    //this.state.sets = this.getCurrentTabAction() + "," + this.state.setsAlwaysOn; 
    this.state.customerId = customerId;
    this.state.version = version;
    //this.state.setsCurrent = this.getCurrentTabAction();
    if (hideSidebar && hideSidebar === true) {
        this.state.sidebarHidden = true;
    } else {
        this.state.sidebarHidden = false;
    }
   
    // do we update the webservice root to be something different for cookie/auth purposes? 
    if (basePath) {
        planeteye.map.services.state.basePath = basePath;
    }
    
    
    
    // hide the logo and legend if required legend is hidden at the point where it is dynamically created
    if(!showLogo){
        $(".aamap_myth_legend_powered").toggle();
    }
    
    // this map is displaying a custom, dynamic 
    if (point && pointContents) {
        this.state.point = point;
        this.state.pointContents = pointContents;
        
        this.state.sidebarPlace = planeteye.map.data.CreateSidebarPlaceFromPoint(pointContents.place.title, pointContents.place.address, pointContents.place.description['#cdata-section'], pointContents.place['@id']); 
    }
  
    // do we need to calculate the bounding box? 
    if (recenter || (!point && !pointContents && lat == 1000.0 && lng == 1000.0)) {
        this.state.calculateBoundingBox = true;
        
        // default to toronto
        if (lat == 1000.0 && lng == 1000.0) {
            lat = 43.65;
            lng = -79.45;
        }
    } else {
        this.state.calculateBoundingBox = false;
    }
    
   
    // set the special zoom state to false
    this.state.specialZoom = false;
    this.state.specialZoomDotId = -1;
    this.state.specialZoomLatLng = new GLatLng(0,0);
    
    // if the map is initialized in a hidden div, google can't figure out the 
    // height/width of that div, so we need to pass it in to initialize the
    // map properly..
    var h = 0;
    var w = 0;
    try {
        var height = $(this.map_dom_object).css('height');
        height = height.replace('px','');
        h = parseInt(height);
        var width = $(this.map_dom_object).css('width');
        width = width.replace('px','');
        w = parseInt(width);
    } catch (exxxxx) {
        h = 0;
        w = 0;
    }
    this.map = new GMap2(this.map_dom_object, {size:new GSize(w, h)});
    
    this._map_control_3d = new GLargeMapControl3D();
    this.map.addControl(this._map_control_3d, new GControlPosition(G_ANCHOR_TOP_LEFT,new GSize(300,0)));
    this.map.addControl(new GMapTypeControl(false));
    this.map.enableDoubleClickZoom();
    
    // only let people zoom out to level 3 - which is where the lat/lng flips and causes problems
    G_PHYSICAL_MAP.getMinimumResolution = function () { return 3 };
    G_NORMAL_MAP.getMinimumResolution = function () { return 3 };
    G_SATELLITE_MAP.getMinimumResolution = function () { return 3 };
    G_HYBRID_MAP.getMinimumResolution = function () { return 3 }; 
    
    // ensure that at least one tab is selected by default, so 'getCurrentTabAction' returns a value.
    // needs to happen here because it triggers a GEvent
    //var actionword_current = this.getActionForTab($('.aamap_maphead_onetab_current'));
    var actionword_current = this.getCurrentTabAction();
    if (!actionword_current || actionword_current == '') {
        var firsttab = $('div.peMapCategories span.map_tab a.map_tab_link').eq(0);
        this.tabOnClick(firsttab, this);
    }
    
    // current tab action is guaranteed to be set now
    this.map.addControl(new SidebarGControl(this.getCurrentTabAction(), this.state.sidebarPlace, this.state.customerId));    // planeteye sidebar
    this.state.sets = this.getCurrentTabAction() + "," + this.state.setsAlwaysOn; 
    this.state.setsCurrent = this.getCurrentTabAction();
    
    if (this.state.sidebarHidden) {
        this.gripHide();
    }
    
    
    // keep track of the map in the dom
    this.map._map_dom_object = this.map_dom_object;
   
    // reference to the PEGoogleMap 
    var thisObj = this;
    
    thisObj.setCenter(new GLatLng(lat, lng), zoom);
    //this.map.setCenter(new GLatLng(lat, lng), zoom);
    
    // when the map moves, redraw the dots
    GEvent.addListener(this.map,"planeteye.map.refresh", function() {
        thisObj.overlayUpdate(thisObj, thisObj.state.sets, thisObj.state.customerId); 
        thisObj.triggerStateUpdate(); 
    });
  
    // when the map moves, redraw the dots
    GEvent.addListener(this.map,"moveend", function() {
        thisObj.refocus();
        thisObj.overlayUpdate(thisObj, thisObj.state.sets, thisObj.state.customerId); 
        thisObj.triggerStateUpdate(); 
        
    });

    // triggered when a tab is clicked..
    GEvent.addListener(this.map, "planeteye.category.change", function(tabEvent) {
      thisObj.map.clearOverlays();
      thisObj.overlayUpdate(thisObj, tabEvent.map, thisObj.state.customerId);
      thisObj.preloadDotImagesForCurrentTab();
      thisObj.triggerStateUpdate(); 
    });

    // after zooming, clear all overlays... handle the special zoom here as well
    GEvent.addListener(this.map,"zoomend",function(cat){
        thisObj.map.clearOverlays(); 
     });

    // clicking on a tab..
    $('div.peMapCategories span.map_tab a.map_tab_link').click(function() {
        thisObj.tabOnClick($(this), thisObj);
        return false;
    });


    // clicking on the grip
    $(thisObj.map_dom_object).find('.sidebarGrip').click(function() {
        thisObj.sidebarGripClick($(this));
        return false;
    });



    // tightmode DEMO
    $('#xtightmode_470x330').click(function() {thisObj.tightmode(470, 330); return false;});
    $('#xtightmode_500x400').click(function() {thisObj.tightmode(500, 400); return false;});
    $('#xtightmode_550x350').click(function() {thisObj.tightmode(550, 350); return false;});
    $('#xtightmode_800x550').click(function() {thisObj.tightmode(800, 550); return false;});
    
  
    
    // initial dataset is dynamic, so we have to set the legend background at runtime
    this.initLegendColor($('.aamap_maphead_onetab_current'));
    this.populateLegend(this.getCurrentTabAction());

 
    // initial population of the map
    this.overlayUpdate(this, this.state.sets, this.state.customerId);  




    // initialize the list of already preloaded dot images,
    // then preload the dot images for the current tab
    //
    this.preloadedDotImages = {};
    this.preloadDotImagesForCurrentTab();


    // give the main app a headstart before we start preloading core images
    //
    setTimeout((function() {thisObj.preloadCoreImages()}), 5000);


    return;
}


/**
 * Used to calculate the center of the map, taking the sidebar into consideration.
 */
PEGoogleMap.prototype.setCenter = function(glatlng, zoom) {
    if (!this.state.sidebarHidden) {
        var bllx = glatlng;
        var px = this.map.getCurrentMapType().getProjection().fromLatLngToPixel(bllx, zoom);
        px.x -= 150;
        var allx = this.map.getCurrentMapType().getProjection().fromPixelToLatLng(px, zoom);
        this.map.setCenter(allx, zoom);
    }  else {
        this.map.setCenter(glatlng, zoom);
    }
}


/**
 * Calculating zoom focus point
 */
PEGoogleMap.prototype.virtualCenter = function(point, xOffs){
  var pixCenter = this.map.fromLatLngToDivPixel(point);
  var virtualPxCenter = new GPoint(pixCenter.x + xOffs/2, pixCenter.y);
  return this.map.fromDivPixelToLatLng(virtualPxCenter);
}
PEGoogleMap.prototype.refocus = function(){
  
  if(!this.state.sidebarHidden)
  {
    var pnt = this.virtualCenter(this.map.getCenter(),300);//sideBlock width
    this.map.setFocus(pnt);
  }else{
    this.map.setFocus(); //reset
  }
}


PEGoogleMap.prototype.preloadImage = function(img_url) {

  var p = document.createElement('img');
  p.src = img_url;
  p.style.display = 'none';

// mattm
//  $('body').after(p);
//  var d = document.documentElement;
//  d.appendChild(p);

  //p.style.display = 'none';

}

PEGoogleMap.prototype.preloadImageList = function(z) {
  for (var i=0; i < z.length; i++) {
    this.preloadImage(z[i]);
  }
}

PEGoogleMap.prototype.preloadCoreImages = function() {

  var z = [
    '/i/jsmap/arrow-next-active.gif',
    '/i/jsmap/arrow-next-inactive.gif',
    '/i/jsmap/arrow-prev-active.gif',
    '/i/jsmap/arrow-prev-inactive.gif',
    '/i/jsmap/ifenlay_bg.gif',
    '/i/jsmap/overlay-left-active.gif',
    '/i/jsmap/overlay-left-inactive.gif',
    '/i/jsmap/overlay-right-active.gif',
    '/i/jsmap/overlay-right-inactive.gif',
    '/i/jsmap/PinShadow.png',
    '/i/jsmap/pins/none-pin.png',
    //'/i/jsmap/green-pin.png',
    //'/i/jsmap/grey-pin.png',
    //'/i/jsmap/purple-pin.png',
    '/i/jsmap/pins/red-pin.png',
    //'/i/jsmap/yellow-pin.png',
  ];

  this.preloadImageList(z);

}

PEGoogleMap.prototype.preloadDotImagesForColor = function(color) {

  var z = [];

  for (var j=1; j <= 4; j++) {
    z.push('/i/jsmap/dots/' + color + 'Dot' + j + '.png');
    z.push('/i/jsmap/dots/' + color + 'Dot' + j + '-a.png');
  }

  this.preloadImageList(z);

}

PEGoogleMap.prototype.preloadDotImagesForCurrentTab = function() {

  var color = this.getCurrentTabColor();

  if (!this.preloadedDotImages[color]) {
    this.preloadedDotImages[color] = 1;
    this.preloadDotImagesForColor(color);
  }

  return;
}


/**
 * Trigger a 'planeteye.state.change' event, passing the current
 * maps state.
 *
 * This is useful for updating the url, etc.
 */
PEGoogleMap.prototype.triggerStateUpdate = function() {
    // make sure this doesn't break stuff.
    try {
        if (eval('typeof this.stateUpdated') === 'function') {
            this.stateUpdated(this.getState());
        }
    } catch (e) { }
}



/**
 * Retrieve enough information to build the url representing
 * the maps current state.
 */
PEGoogleMap.prototype.getState = function()
{
    var curstate = {};
    curstate.sidebarHidden = this.state.sidebarHidden;
    curstate.latitude = this.map.getCenter().lat().toFixed(4);
    if (!this.state.sidebarHidden) {
      var pnt = this.virtualCenter(this.map.getCenter(),300);//sideBlock width
      curstate.longitude = pnt.lng().toFixed(4);
    } else {
      curstate.longitude = this.map.getCenter().lng().toFixed(4);
    }
    curstate.zoom = this.map.getZoom();
    curstate.sets = this.state.setsInitial;
    curstate.setsName = this.state.setsName;
    curstate.customerId = this.state.customerId;
    curstate.point = this.state.point;
    curstate.pointContents = this.state.pointContents;
    curstate.setsCurrent = this.state.setsCurrent;
    curstate.version = this.state.version;
    
    return curstate;
}




/**
 * Clear all pending calls in this ajax request queue.
 */
PEGoogleMap.prototype.requestQueueClear = function(queue){
    while(queue.length>0){
        queue.pop().abort();
    }
}


/**
 * Add the given call to the request queue, so we can cancel it if necessary.
 */
PEGoogleMap.prototype.requestQueueAdd = function(type, ajaxHandle) {

    // no calls of this type yet, so create the queue
    if (!this.requestQueue[type]) { this.requestQueue[type] = new Array(); 
    
    // kill any existing calls
    } else {
        this.requestQueueClear(this.requestQueue[type]);
    }
    
    this.requestQueue[type].push(ajaxHandle);
}


/** cache dots */
PEGoogleMap.prototype.overlayCache = new Array();

/** cache ajax calls so we can cancel them if necessary */
PEGoogleMap.prototype.requestQueue = new Array(); // queue to hold the map ajax calls so we can cancel them if needed


/**
 * Initialize the map
 * TODO - remove this.
$(document).ready(function() {

  // register cleanup handler for google maps
  $(window).unload(function() {
    GUnload();
  });

  var pemap = new PEGoogleMap(43.65, -79.45, 12, 'map_canvas', 'travelpack.109', -1);
  
});
 */



/**
 * Set the legend color given a tab
 */
PEGoogleMap.prototype.initLegendColor = function(jq_new) {
  var jq_aamap = jq_new.parents('.aamap');
  var jq_current = jq_aamap.find('.aamap_maphead_onetab_current');
  
  var jq_legend_container = jq_aamap.find('.aamap_myth_legend');
  jq_legend_container.css('background-color', jq_new.css('background-color'));
}


/**
 * Executed when a category tab is clicked.
 * Passes a handle on the tab div.
 *
 * jq_new_a = span.map_tab a.map_tab_link
 */
PEGoogleMap.prototype.tabOnClick = function(jq_newtab_a, pemap) {
 
  // get a hold of the tab parents..
  var jq_aamap = jq_newtab_a.parents('.aamap');
  
  // find the span.map_tab of the current tab..
  var jq_currenttab_a = jq_aamap.find('div.peMapCategories span.map_tab a.categoryOn');
  var actionword_new = pemap.getActionForTab(jq_newtab_a);

  // turn off the old tab
  if (jq_currenttab_a.length > 0) {
    jq_currenttab_a.addClass('category');
    jq_currenttab_a.removeClass('categoryOn');
  }
  
  // turn on the new tab
  jq_newtab_a.addClass('categoryOn');
  jq_newtab_a.removeClass('category');


  
  // store the cached values for the action and color
  //
  this.cachedCurrentTabAction = this.getActionForTab(jq_newtab_a);
  this.cachedCurrentTabColor = this.getColorForTab(jq_newtab_a);
  this.cachedCurrentTabName = this.getNameForTab(jq_newtab_a);

  
  // match the legend container color to the tab color
  //
//  var jq_legend_container = jq_aamap.find('.aamap_myth_legend');
//  jq_legend_container.css('background-color', jq_new.css('background-color'));


//  this.populateLegend(actionword_new);


  pemap.triggerCategoryChangeEvent(actionword_new);
  return;
}

PEGoogleMap.prototype.gripReCenter = function() {
  var pemap = this;
  var jq_pemap = $(pemap.map_dom_object);
  var jq_ifen = jq_pemap.parents('.ifen');

  var jq_mapSidebar = jq_ifen.find('.mapSidebar');
  var jq_sidebarActual = jq_ifen.find('.sidebarActual');
  var jq_xgrip = jq_ifen.find('.sidebarGrip .x');

  var grip_margin_top = Math.round(jq_mapSidebar.height() / 2);
  jq_xgrip.css('margin-top', grip_margin_top);

  return;
}

/**
 * Show the sidebar
 */
PEGoogleMap.prototype.gripShow = function() {
  var pemap = this;
  var jq_pemap = $(pemap.map_dom_object);
  var jq_ifen = jq_pemap.parents('.ifen');

  var jq_mapSidebar = jq_ifen.find('.mapSidebar');
  var jq_sidebarActual = jq_ifen.find('.sidebarActual');
  var jq_xgrip = jq_ifen.find('.sidebarGrip .x');

  jq_mapSidebar.width(295);
  jq_sidebarActual.show();
  jq_xgrip.html('&laquo;');
  pemap.map.addControl(pemap._map_control_3d, new GControlPosition(G_ANCHOR_TOP_LEFT,new GSize(300,0)));
  
  pemap.state.sidebarHidden = false;
  
  // refresh the contents of the map now that less of it is visible
  GEvent.trigger(pemap.map,"planeteye.map.refresh");

  return;
}


/**
 * Hide the sidebar.
 */
PEGoogleMap.prototype.gripHide = function() {
  var pemap = this;
  var jq_pemap = $(pemap.map_dom_object);
  var jq_ifen = jq_pemap.parents('.ifen');

  var jq_mapSidebar = jq_ifen.find('.mapSidebar');
  var jq_sidebarActual = jq_ifen.find('.sidebarActual');
  var jq_xgrip = jq_ifen.find('.sidebarGrip .x');

  jq_mapSidebar.width(10);
  jq_sidebarActual.hide();
  jq_xgrip.html('&raquo;');
  pemap.map.addControl(pemap._map_control_3d, new GControlPosition(G_ANCHOR_TOP_LEFT,new GSize(15,0)));
  
  pemap.state.sidebarHidden = true;
  
  // refresh the contents of the map now that more of it is visible
  GEvent.trigger(pemap.map,"planeteye.map.refresh");

  return;
}

PEGoogleMap.prototype.sidebarGripClick = function(jq_grip) {
  var pemap = this;
  var jq_pemap = $(pemap.map_dom_object);
  var jq_ifen = jq_pemap.parents('.ifen');

  var jq_mapSidebar = jq_ifen.find('.mapSidebar');
  var jq_sidebarActual = jq_ifen.find('.sidebarActual');
  var jq_xgrip = jq_ifen.find('.sidebarGrip .x');

  this.gripReCenter();

  if (jq_mapSidebar.width() == 295) {
    this.gripHide();
  } else {
    this.gripShow();
  }

  return;
}

PEGoogleMap.prototype.tightmode = function(pxwidth, nheight) {
  var pemap = this;
  var jq_pemap = $(pemap.map_dom_object);
  var jq_ifen = jq_pemap.parents('.ifen');

  // hide the legend
  var jq_aamap_myth = jq_ifen.find('.aamap_myth');
  //jq_aamap_myth.hide();

  // change the width of the ifen container
  jq_ifen.width(pxwidth);

  // hide the tab icon container
  jq_tab_icon = jq_ifen.find('.aamap_maphead_onetab .q');
  jq_tab_icon.hide();



  // modify the tab label

  jq_onetab = jq_ifen.find('.aamap_maphead_onetab');
  jq_onetab__w = jq_ifen.find('.aamap_maphead_onetab .w');

  // font size
  jq_onetab.css('font-size', '10px');

  // overall tab height
  jq_onetab__w.css('padding-top', '5px');
  jq_onetab.css('height', '28px');

  // rhs spacing
  jq_onetab.css('padding-right', '10px');




  // change the height of the box

  //nheight = ifen_rnum(300, 500);

  var jq_sbar          = jq_pemap.find('.mapSidebar');
  var jq_sbar_contents = jq_pemap.find('.mapSidebar .sidebarContents');

  // an example where the given height is 339, we need to set:
  //   jq_ifen { height: 339px ; overflow: hidden }
  //   jq_pemap { height: 300px }
  //   jq_sbar { height: 300px }
  //   jq_sbar_contents { height: 240px }

  jq_ifen.css('overflow', 'hidden');
  jq_ifen.height(nheight);
  jq_pemap.height(nheight - 39);
  jq_sbar.height(nheight - 39);
  jq_sbar_contents.height(nheight - 39 - 60);

  
  // on ie6 and ie7 the border gets lost.  this function does not do browser sniffing however.
  // this function checks to see if some time later, the border has gone missing.
  // if it has, then it corrects it.
  // we should not look inside the google maps dom object, but we have to.
  // checkResize() is not called on the GMap2 object because this is an internal fix.
  //
  var jq_pemapFS = $(pemap.map_dom_object.firstChild);
  setTimeout( (function() {  
  if (jq_pemapFS.height() == (nheight - 39 + 2)) {
    jq_pemapFS.height(nheight - 39);
    jq_pemapFS.width(pxwidth - 2);
  }
  }), 600);


  this.gripHide();
  this.gripReCenter();


  // tell the google maps object that its container has changed size
  //
  pemap.map.checkResize();

  
}



/**
 * Cases to consider:
 * 1.  travelpack, i.e. set starts with 'map.' and does appear on all maps
 * 2.  category, i.e. set doesn't start with 'map.' and doesn't appear on all maps
 */
PEGoogleMap.prototype.populateLegend = function(newCurrentTabAction) {

  var color = this.getCurrentTabColor();
  var name = this.getCurrentTabName();

  var dom_ifen = $(this.map_dom_object).parents('.ifen')[0];
  var jq_hull = $(dom_ifen).find('.aamap_myth_legend_inner');

  var ifen_htmlre = function(html, mword, sub) {
    var re = new RegExp('%%' + mword + '%%');
    return html.replace(re, sub);
  }

  var html = ''
  // only display for travel packs..
  if ((this.state.setsAlwaysOn != '' || this.state.setsInitial == 'single.location') && this.state.showLegend) {
      html += ''
        + '<div class="x">'
        + '  <div class="q"><img class="v_pin" src="/i/jsmap/legend-pin.png"></div>'
        + '  <div class="w"><span>%%PLACE_NAME%%</span> location(s)</div>'
        + '  <div class="clear"></div>'
        + '</div>';
  }


  // only don't show the dot in the location tab, if there is a 'set always on' set.
  //if ((this.state.setsInitial != 'single.location' && this.state.setsInitial.indexOf('map.') == -1 && color == 'grey') || (color != 'grey')) {
  if (newCurrentTabAction && newCurrentTabAction.indexOf('map.') == -1 && newCurrentTabAction!="single.location") {
      html += ''
        + '<div class="x">'
        + '  <div class="q"><img class="v_dot" src="%%DOT_IMG%%"><div class="v_dot_num">4</div></div>'
        + '  <div class="w">Areas with a number of <span>%%TYPE%%</span></div>'
        + '  <div class="clear"></div>'
        + '</div>'

        + '<div class="clear"></div>';
  }

  var q_type = this.getCurrentTabName();

  var q_dot_img = this.ifen_new_marker__get_dot_img(color, 2, false);


  if (this.state.setsAlwaysOn != '' || this.state.setsInitial == 'single.location') {
    var q_place_name = this.getNameForTab(this.getLocationTabJq());
    if (!q_place_name) { q_place_name = 'Your'; }
    html = ifen_htmlre(html, 'PLACE_NAME', q_place_name);
  }
  
  // only don't show the dot in the location tab, if there is a 'set always on' set.
  //if ((this.state.setsInitial != 'single.location' && this.state.setsInitial.indexOf('map.') == -1 && color == 'grey') || (color != 'grey')) {
  if (newCurrentTabAction && newCurrentTabAction.indexOf('map.') == -1) {
    html = ifen_htmlre(html, 'TYPE', q_type);
    html = ifen_htmlre(html, 'DOT_IMG', q_dot_img);
  }

  jq_hull.html(html);


}


/**
 * Category tab clicked, now send events.
 * TODO - this should be dynamic, the codes should be in the html... rendering this useless.
 */
PEGoogleMap.prototype.triggerCategoryChangeEvent = function(categories) {

  PETabEvent.sidebar = categories;
  PETabEvent.map = categories + "," + this.state.setsAlwaysOn;
  this.state.setsCurrent = categories;

  GEvent.trigger(this.map,"planeteye.category.change",PETabEvent);
}


PEGoogleMap.prototype.getActionForTab = function(x) {
  return x.attr('petabcategories');
}
PEGoogleMap.prototype.getColorForTab = function(x) {
  return x.attr('petabcolor');
}
PEGoogleMap.prototype.getNameForTab = function(x) {
  return x.attr('petabname');
}
PEGoogleMap.prototype.getTabAttribute = function(x, attribute) {
  return x.attr(attribute);
}


PEGoogleMap.prototype.getCurrentTabJq = function() {
  var jq_aamap = $(this.map_dom_object).parents('.ifen');
  var jq_currenttab_a = jq_aamap.find('div.peMapCategories span.map_tab a.categoryOn');
  if (!jq_currenttab_a) {
    jq_current_tab = jq_aamap.find('.aamap_maphead_onetab_current');
  }
  return jq_currenttab_a;
}

// TODO - will this work anymore???
PEGoogleMap.prototype.getLocationTabJq = function() {
  var map_dom_object = this.map_dom_object;
  var jq_aamap = $(map_dom_object).parents('.ifen').find('.aamap');
  var jq_current_tab = jq_aamap.find('#aamap_maphead_onetab_locations');
  return jq_current_tab;
}

PEGoogleMap.prototype.getCurrentTabActionFromDOM = function() {
  return this.getActionForTab(this.getCurrentTabJq());
}
PEGoogleMap.prototype.getCurrentTabColorFromDOM = function() {
  return this.getColorForTab(this.getCurrentTabJq());
}
PEGoogleMap.prototype.getCurrentTabNameFromDOM = function() {
  return this.getNameForTab(this.getCurrentTabJq());
}

PEGoogleMap.prototype.getCurrentTabAction = function() {
  return this.cachedCurrentTabAction;
}
PEGoogleMap.prototype.getCurrentTabColor = function() {
  return this.cachedCurrentTabColor;
}
PEGoogleMap.prototype.getCurrentTabName = function() {
  return this.cachedCurrentTabName;
}



// utility function
//
// returns an int in the range [a,b] inclusive
//
function ifen_rnum(a, b) {
  // Math.floor(Math.random() * 3) gives a range of 3 values: 0, 1, 2
  // if we want [2,5] then there are four possible values; 2, 3, 4, 5
  var range = b - a + 1;
  return Math.floor(Math.random() * range) + a;
}






/**
 * Create a new pin - black square, with a white star in the middle.
 *
 * only display special pins if the map version contains 'sp' (special pins)
 */
PEGoogleMap.prototype.ifen_new_pin = function(latlng, pointid, category, color, sets, pemap) {

  var icon = new GIcon();

  
  if (!pemap.state.version || pemap.state.version.indexOf('sp') < 0) {
    category = 'none';
  }
  
  //icon.image = this.ifen_new_marker__get_pin_img();
  // if the source set is -1, i.e. it doesn't belong to a 'map', make it a red marker
  if (color && color != '') {
//      icon.image = '/i/jsmap/pins/' + color + '-pin.png';
      icon.image = this.ifen_new_marker__get_pin_img(color, false);
  } else if (category && category != '') {
//      icon.image = '/i/jsmap/pins/' + category + '-pin.png';
      icon.image = this.ifen_new_marker__get_pin_img(category, false);
  } else {
//      icon.image = '/i/jsmap/pins/none-pin.png';
      icon.image = this.ifen_new_marker__get_pin_img('none', false);
  }
  icon.iconSize = new GSize(24, 32);
  icon.shadow = '/i/jsmap/pins/PinShadow.png';
  icon.shadowSize = new GSize(44, 32);
  icon.iconAnchor = new GPoint(8, 31);
  icon.infoWindowAnchor = new GPoint(0, 0);  /* unused */

  var opts = { 
    "icon": icon,
    "labelClass": 'ifen_pin',
    "labelOffset": new GSize(0, 0),
    "labelText": ''
  };

  var marker = new LabeledMarker(latlng, opts);
  marker._dotid = pointid;
  marker._pemap = pemap;
  marker._sets = sets;
  marker._hover_off_event_timeout = null;

  return(marker);
}



/**
 * Update all overlays on the map.
 * Triggers webservice.
 */
PEGoogleMap.prototype.overlayUpdate = function(pemap, sets, customerId) {

  // this must change based on whether the sidebar is active or not. 
  var opts;
  if (!pemap.state.sidebarHidden) {
    opts = {top:20,right:20,bottom:20,left:310,disableSetCenter:true};
  } else {
    opts = {top:20,right:20,bottom:20,left:30,disableSetCenter:true};
  }
  var b = pemap.map.showBounds(pemap.map.getBounds(),opts);
  
  
  
  var sw = b.getSouthWest();
  var ne = b.getNorthEast();
  
  var east = 179.999;
  var west = -179.999;
  var north = 89.999;
  var south = -89.999;
 
  // is the whole lat range visible on the map?  (pole to pole) 
  if (!pemap.map.getBounds().isFullLat()) {
    north = ne.lat();
    south = sw.lat();
  }
  
  // is the whole lng range visible on the map?  east to west?
  if (!pemap.map.getBounds().isFullLng()) {
    east = ne.lng();
    west = sw.lng();
  }
  
  // at zoom level 2, google seems to reverse the east/west lng values
  /*
  if (pemap.map.getZoom() <= 2) {
    west = ne.lng();
    east = sw.lng();
    
    if (pemap.map.getZoom() == 1 && west > east) {
        var tmp = east;
        east = west;
        west = tmp;
    }
  }
  */

  // dealing with the dateline 
  //if (west >= 0 && east < 0) {
  /**
  if (west >= 0 && east < 0) {
    var tmp = east;
    east = west;
    west = tmp;
  }
  **/
 
  /** 
   * show the bounding box on the map.. 
   * doesn't work at zoom level >= 2 for some reason..
   */
   /**
  var pl = new GPolyline([
    new GLatLng(north, west),
    new GLatLng(north, east), 
    new GLatLng(south, east),
    new GLatLng(south, west),
    new GLatLng(north, west)
    ]
    , '#ff0000', 5);
  pemap.map.addOverlay(pl);
  **/
 
  // single webservice that returns dots and pins for the map.
  if (sets && sets != '') { 
    this.state.sets = sets;
    
    // if the user has not set a default lat/lng for the map, calculate a best fit
    // bounding box for the travelpack...  Which means we need to return 'all' elements
    // of a travelpack, not just the first few, hence appending 'all'
    if (pemap.state.calculateBoundingBox == true)
    {
        var setarray = sets.split(',');
        sets = '';
        for (var i = 0; i < setarray.length; i++) {
            if (setarray[i] != null && setarray[i] != '' && setarray[i].indexOf('map.') > -1 && setarray[i].indexOf('.all') == -1) {
                sets += setarray[i] + '.all,';
            } else {
                sets += setarray[i] + ',';
            }
        }
    }
    this.requestQueueAdd('overlaysGetForSet', planeteye.map.services.overlaysGetForSet(sets, north, south, west, east, pemap.map.getZoom(), customerId, pemap.ifen_overlays_draw_all, pemap.ifen_overlays_error, pemap));
  }
}





/**
 * Remove a particular overlay.
 */
PEGoogleMap.prototype.ifen_overlay_clear = function(pemap, type) {
    // did we display this overlay type before? 
    if (!this.overlayCache[type]) { this.overlayCache[type] = new Array(); }
    
    // remove the old overlays
    for (var i=0; i < this.overlayCache[type].length; i++) {
        pemap.map.removeOverlay(this.overlayCache[type][i]);
    }
}


/**
 * Redraw this set of overlays/markers.
 * This method will remove the old markers in this set, determined by the 'type' variable.
 *
 * This avoids removing all layers (like infowindows) if you just want to redraw a single layer.
 * 
 * There is opportunity here to only remove/add markers that need changing, instead of all of them.
 * Not sure that will save time though.
 */
PEGoogleMap.prototype.ifen_overlay_redraw = function(pemap, markers, type) {
    
    // add the new overlays
    for (var i=0; i < markers.length; i++) 
    {
        pemap.map.addOverlay(markers[i]);
    }

    // remove the old markers 
    //ifen_overlay_clear(map, type); 
    // did we display this overlay type before? 
    if (!this.overlayCache[type]) { this.overlayCache[type] = new Array(); }
    
    // remove the old overlays
    for (var i=0; i < this.overlayCache[type].length; i++) {
        pemap.map.removeOverlay(this.overlayCache[type][i]);
    }
    
    // remember these overlays (must be in this order..)
    this.overlayCache[type] = markers;
}


/**
 * Render the dots and points returned by the webservice.
 */
PEGoogleMap.prototype.ifen_overlays_draw_all = function(points, dots, pemap, datasetNames) {
    //ifen_dots_draw(dots, map, datasetNames);
    //ifen_points_draw(points, map, datasetNames);
    
    pemap.state.sets = datasetNames;
    
    if (pemap) {
        if (dots && dots.length > 0) {
            var markers = new Array();
            var specialMarker = null;
            for (var i=0; i < dots.length; i++)
            {
                // make sure this dot is defined..
                if (dots[i]) {
                    var latlng = new GLatLng(dots[i]["@lat"], dots[i]["@long"]);

                    var current_tab_action = pemap.getCurrentTabAction(pemap.map_dom_object);
                    var current_tab_color = pemap.getCurrentTabColor(pemap.map_dom_object);
                    var current_tab_name = pemap.getCurrentTabName(pemap.map_dom_object);

                    var dotid = dots[i]["@dotid"];

                    var marker = pemap.ifen_new_marker(current_tab_color, latlng, dots[i]["@count"], dotid, pemap);

                    if (marker == null) continue;

                    //map.addOverlay(marker);
                    //ifen_overlay_add(map, marker, 'dot');

                    markers.push(marker);

                    
                    var gmap = pemap.map;
                    if(pemap.state.specialZoom){
                        if(dotid == pemap.state.specialZoomDotId){
                            specialMarker = marker;
                        }
                    }
                    (function(marker, dotid, gmap) {

                        // don't need to remove old listeners, because the marker is new..
                        
                        GEvent.addListener(marker, 'click', function() {
                            marker.openIfenDow(-1, false);
                        });
                       
                        GEvent.addListener(marker, 'mouseover', function() {
                            marker.hover_color_on();
                        });

                        GEvent.addListener(marker, 'mouseout', function() {
                            marker.hover_color_off();
                        });



                        // These three events are triggered by the Sidebar:


                        var event_open      = 'planeteye.map.dot.' + dotid + '.open';
                        var event_hover_on  = 'planeteye.map.dot.' + dotid + '.hover.on';
                        var event_hover_off = 'planeteye.map.dot.' + dotid + '.hover.off';
                        
                        // remove any old listeners, because these listeners are attached to the map
                        GEvent.clearListeners(gmap, event_open);
                        GEvent.clearListeners(gmap, event_hover_on);
                        GEvent.clearListeners(gmap, event_hover_off);

                        // (1)
                        //
                        GEvent.addListener(gmap, event_open, function(n) {
                            marker.openIfenDow(n, false);
                        });

                        
                        // (2)
                        //
                        GEvent.addListener(gmap, event_hover_on, function() {
                            marker.hover_color_on();
                        });

                        
                        // (3)
                        //
                        GEvent.addListener(gmap, event_hover_off, function() {
                            marker.hover_color_off();
                        });
                    


                    })(marker, dotid, gmap);
                
                }

            }
            pemap.ifen_overlay_redraw(pemap, markers, 'dots');
            //open the special dot if needed
            if(pemap.state.specialZoom && specialMarker!=null){
                pemap.state.specialZoom = false;
                // specialMarker.openIfenDow(-1, false);
                if(pemap.state.specialZoomIsPlace){
                
                    GEvent.trigger(pemap.map,'planeteye.map.dot.' + pemap.state.specialZoomDotId + '.open', 'place.' + pemap.state.specialZoomId);
                }else{
                    GEvent.trigger(pemap.map,'planeteye.map.dot.' + pemap.state.specialZoomDotId + '.open', 'photo.' + pemap.state.specialZoomId);
                
                }
            }
        } else {
            pemap.ifen_overlay_clear(pemap, 'dots');
        }
        
        
        if ((points && points.length > 0) || pemap.state.point) {
            var markerToOpen;
            var specialMarker = null;
            // point information was passed in via the url, add this point to the array.
            if (pemap.state.point) {
                if (!points) { points = new Array(); }
                points.push(pemap.state.point);
            }
        
            // multiple locations returned from the webservice..
            if (points && points.length > 0) {
                var markers = new Array();
                var bb = null;
                
                for (var i=0; i < points.length; i++)
                {
                    var dotid = points[i]['@pointid'];
                    var latlng = new GLatLng(points[i]["@lat"], points[i]["@lng"]);
                    var category = points[i]["@category"];
                    var color = points[i]["@color"]; // belongs to this dataset
                    var sets = points[i]["@sets"]; // belongs to this dataset
                    var marker = pemap.ifen_new_pin(latlng, dotid, category, color, sets, pemap);
                    
                    // if the lat/lng/zoom or pll isn't set, and this is the first view, set a best fit bounding box
                    if (pemap.state.calculateBoundingBox == true) {
                        if (bb == null) { bb = new GLatLngBounds(latlng, latlng); }
                        else { bb.extend(latlng); }
                    }
                    
                    //map.addOverlay(marker);
                    markers.push(marker);

                    // opent this marker by default if the flag was set
                    if (points[i].open && points[i].open == true) {
                        markerToOpen = marker;
                        points[i].open = false; // turn it off so it doesn't keep popping open (TODO - remember for share link)..
                    }
                    
                    
                    // debug marker to check if the labeled marker is centered
                    //
                    //var simple_marker = new GMarker(latlng);
                    //map.addOverlay(simple_marker);
                    var gmap = pemap.map;
                    if(pemap.state.specialZoom){
                        if(dotid == pemap.state.specialZoomDotId){
                            specialMarker = marker;
                        }
                    }
                    
                    // points[i]["@pointid"] -- needed to populate info window
                    (function(marker, dotid, gmap) {
                       
                        // don't need to remove old listeners because they are attached to the marker, which is new
                         
                        GEvent.addListener(marker, 'click', function() {
                            marker.openIfenDow(-1, true);
                        });
                        
                        GEvent.addListener(marker, 'mouseover', function() {
                            marker.hover_color_on();
                        });

                        GEvent.addListener(marker, 'mouseout', function() {
                            marker.hover_color_off();
                        });
                        
                        
                        var event_open      = 'planeteye.map.dot.' + dotid + '.open';
                        var event_hover_on  = 'planeteye.map.dot.' + dotid + '.hover.on';
                        var event_hover_off = 'planeteye.map.dot.' + dotid + '.hover.off';
                        
                        // remove any old listeners, because these listeners are attached to the map
                        GEvent.clearListeners(gmap, event_open);
                        GEvent.clearListeners(gmap, event_hover_on);
                        GEvent.clearListeners(gmap, event_hover_off);

                        // (1)
                        //
                        GEvent.addListener(gmap, event_open, function(n) {
                            marker.openIfenDow(-1, true);
                        });

                        // (2)
                        //
                        GEvent.addListener(gmap, event_hover_on, function() {
                            marker.hover_color_on();
                        });

                        
                        // (3)
                        //
                        GEvent.addListener(gmap, event_hover_off, function() {
                            marker.hover_color_off();
                        });
                    
                       
                    })(marker, dotid, gmap);
                }
               
                // set the default bounding box 
                if (pemap.state.calculateBoundingBox == true && bb && bb != null) {
                    /*
                    // this doesn't work, for some strange reason..
                    var opts = {top:20,right:20,bottom:20,left:310,disableSetCenter:true};
                    var modifiedbb = pemap.map.showBounds(bb, opts);
                    
                    var z = pemap.map.getBoundsZoomLevel(modifiedbb) - 1;
                    var c = modifiedbb.getCenter();
                    */
                    
                    var z1 = pemap.map.getBoundsZoomLevel(bb) - 1;
                    var c1 = bb.getCenter();
                  
                    //pemap.map.setCenter(c1, z1);
                    pemap.setCenter(c1, z1);
                    pemap.state.calculateBoundingBox = false;
                }
               
                // open the default marker 
                if (markerToOpen) {
               
                    markerToOpen.openIfenDow(-1, true);
                }
            }

            pemap.ifen_overlay_redraw(pemap, markers, 'points');
            
            if(pemap.state.specialZoom && specialMarker!=null){
                pemap.state.specialZoom = false;
               // specialMarker.openIfenDow(-1, true);
                if(pemap.state.specialZoomIsPlace){
                    
                    GEvent.trigger(pemap.map,'planeteye.map.dot.' + pemap.state.specialZoomDotId + '.open', 'place.' + pemap.state.specialZoomId);
                    //GEvent.trigger(pemap.map,'planeteye.map.dot.place.6713.open', 'place.' + pemap.state.specialZoomId);
                }else{
                    GEvent.trigger(pemap.map,'planeteye.map.dot.' + pemap.state.specialZoomDotId + '.open', 'photo.' + pemap.state.specialZoomId);
                
                }
            }
        } else {
            pemap.ifen_overlay_clear(pemap, 'points');
        }
        
        
       
    }
}


/**
 * TODO...
 */
PEGoogleMap.prototype.ifen_overlays_error = function() { 
  //alert('overlay error');
}




/**
 * TODO... dot call failed to return.
 */
PEGoogleMap.prototype.ifen_dots_error = function(dots)
{
    //alert('dot call failed');
}



/**
 * Retrieve the pin image from the passed in category and 'is_hover' flag
 */
PEGoogleMap.prototype.ifen_new_marker__get_pin_img = function(category, is_hover) {
  var hover_word = '';

  if (is_hover) {
    hover_word = '-a';
  }

  var path = '/i/jsmap/pins/';
  var postfix = category + '-pin' + hover_word + '.png';
  //var img = path + current_tab_color + postfix;
  var img = path + postfix;
  return img;    
}


/**
 * Retrieve the dot image from the passed in tab color
 *
 * TODO - call to 'getCategories' should determine this.
 */
PEGoogleMap.prototype.ifen_new_marker__get_dot_img = function(current_tab_color, dotsize, is_hover) {

  var hover_word = '';

  if (is_hover) {
    hover_word = '-a';
  }

  var path = '/i/jsmap/dots/';
  var postfix = 'Dot' + dotsize + hover_word + '.png';

  var img = path + current_tab_color + postfix;

  return img;    
}



/**
 * Determine the css class for the dot (include hovers)
 */
PEGoogleMap.prototype.ifen_new_marker__compute_css_class = function(dotsize, dotcount, is_hover) {
  var h = '';
  var s = '';
  if (is_hover) {
    h = 'ifen_dot_hover ';
  }
  if (dotcount >= 1000) {
    s = '_small';
  }
  if (dotcount >= 10000) {
    s = '_extra_small';
  }
  return 'ifen_dot ' + h + 'ifen_dot' + dotsize + s;
}


/**
 * Create a new dot marker.
 */
PEGoogleMap.prototype.ifen_new_marker = function(current_tab_action, latlng, vnum, dotid, pemap) {

  if (vnum == 1) {
    var dotsize = 1;
    var u_iconSize = new GSize(17, 17);
    var u_iconAnchor = new GPoint(8, 9);
    var u_labelOffset = new GSize(0, 0);  /* unused */
    var number_to_show = '';  /* unused */
    
  } else if (vnum < 10) {
    var dotsize = 2;
    var u_iconSize = new GSize(27, 27);
    var u_iconAnchor = new GPoint(13, 14);
    var u_labelOffset = new GSize(-4, -9);
    var number_to_show = vnum; //ifen_rnum(2, 9);
    
  } else if (vnum < 100) {
    var dotsize = 3;
    var u_iconSize = new GSize(31, 31);
    var u_iconAnchor = new GPoint(15, 16);
    var u_labelOffset = new GSize(-7, -9);
    var number_to_show = vnum; //ifen_rnum(10, 99);
    
  } else if (vnum >= 100) {
    var dotsize = 4;
    var u_iconSize = new GSize(38, 38);
    var u_iconAnchor = new GPoint(19, 19);
    var u_labelOffset = new GSize(-11, -9);
    var number_to_show = vnum; //ifen_rnum(100, 999);
  }


  var u_image = this.ifen_new_marker__get_dot_img(current_tab_action, dotsize, false);
  var u_labelClass = this.ifen_new_marker__compute_css_class(dotsize, vnum, false);

  /* this is not used because we use our custom overlay, IfenDow instead of GInfoWindow */
  var u_infoWindowAnchor = new GPoint(0, 0);  

  var icon = new GIcon();

  icon.image = u_image;
  icon.iconSize = u_iconSize;
  icon.iconAnchor = u_iconAnchor;
  icon.infoWindowAnchor = u_infoWindowAnchor;
 
 /* ie6 is faster if you turn this stuff off
  icon.transparent = null;
  icon.shadow = null;
  icon.shadowSize = null;
  icon.infoShadowAnchor = null;
  */

  var opts = { 
    "icon": icon,
    "labelClass": u_labelClass,
    "labelOffset": u_labelOffset,
    "labelText": number_to_show
  };

  var marker = new LabeledMarker(latlng, opts);

  marker._dotsize  = dotsize;
  marker._dotcount  = vnum;
  marker._dotid = dotid;
  marker._pemap = pemap;
  marker._hover_off_event_timeout = null;

  return(marker);
}







/**
 * Constructor for the info window
 */
createNamespace('planeteye.map');

planeteye.map.InfoWindow = function(marker, dotid, dotcount, pemap, default_itemid, is_pin, sets) {
  this.x_marker = marker;
  this.x_dotid = dotid;
  this.x_dotcount = dotcount;
  this.x_pemap = pemap;
  this.x_default_itemid = default_itemid;
  this.x_perPage = 25;
  this.x_is_pin = is_pin;
  this.x_isowner = false;
  this.x_sets = sets; // sets that this object is in
}





/** DEFINE THE INFO WINDOW **/
planeteye.map.InfoWindow.prototype = new GOverlay();


planeteye.map.InfoWindow.prototype.initialize = function(map) {

  var x = document.createElement('div');
  var pane = map.getPane(G_MAP_FLOAT_PANE);

  pane.appendChild(x);


  // don't pass events down to the map, e.g. when you drag on the infowindow the map used to scroll
  //
  GEvent.bindDom(x, 'DOMMouseScroll',    this, this.stop_event_bubble);
  GEvent.bindDom(x, 'click',             this, this.stop_event_bubble);
  GEvent.bindDom(x, 'singlerightclick',  this, this.stop_event_bubble);
  GEvent.bindDom(x, 'rightclick',        this, this.stop_event_bubble);
  GEvent.bindDom(x, 'mousedown',         this, this.stop_event_bubble);
  GEvent.bindDom(x, 'mouseup',           this, this.stop_event_bubble);
  GEvent.bindDom(x, 'contextmenu',       this, this.stop_event_bubble);
  GEvent.bindDom(x, 'dblclick',          this, this.stop_event_bubble);
  GEvent.bindDom(x, 'mousedown',         this, this.stop_event_bubble);
  GEvent.bindDom(x, 'mousewheel',        this, this.stop_event_bubble);


  this.x_map = map;
  this.x_hull = x;

  this._populate_from_ws(false);
  
  return;
}

planeteye.map.InfoWindow.prototype.stop_event_bubble = function(e) {
  if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}


/**
 * Add another photo to the existing infowindows list.
 */
planeteye.map.InfoWindow.prototype._populate_add_photo = function(index, src, link,lat,lng,zoomDotId,id,otherId,mediaTypeId) {

  var photo = {
        ITEM_TYPE: 'photo',
        ITEM_PHOTO_SRC: src,
        ITEM_MORE_HREF: link,
        ITEM_LAT:  lat,
        ITEM_LNG: lng,
        ITEM_ZOOM_DOTID: zoomDotId,
        ITEM_ID: id,
        OTHER_ID: otherId,
        MEDIATYPE_ID: mediaTypeId
    };
//  this._lay_items.LS_ITEMS.push(photo);
  this._lay_items.LS_ITEMS[index] = photo;
  
  return;
}

/**
 * Add another place to the existing infowindows list, at the specified index.
 *
 * Allow user to indicate that this is the default.
 */
planeteye.map.InfoWindow.prototype._populate_add_place = function(index, title, line1, line2, line3, description, link, header, isdefaultitem,lat,lng,zoomDotId,id, otherId, thumbnailUrl) {

  var place = {
        ITEM_TYPE: 'place',
        ITEM_TITLE: title,
        ITEM_L1: line1,
        ITEM_L2: line2,
        ITEM_L3: line3,
        ITEM_DESC: description,
        ITEM_MORE_HREF: link,
        ITEM_LAT:  lat,
        ITEM_LNG: lng,
        ITEM_ZOOM_DOTID: zoomDotId,
        ITEM_ID: id,
        OTHER_ID: otherId,
        ITEM_THUMBURL: thumbnailUrl
    };
  //this._lay_items.LS_ITEMS.push(place);
  this._lay_items.LS_ITEMS[index] = place;
  
  return;
}


/**
 * Process the results of the 'get dot contents' call.
 *
 * Trigger HTML display at the conclusion of processing.
 *
 * TODO - deal with HEADER
 */
planeteye.map.InfoWindow.prototype._populate_from_ws_success = function(dotContents, infoWindow) {
  
  // this is overwritten while populating, so it's reset here..
  infoWindow._lay_current = 0;

  // initialize the arrays.. 
  if (!infoWindow._lay_items || infoWindow._lay_items == null) {
     infoWindow._lay_items = {
       LS_HEADER: 'Location',
       LS_ITEMS: [ ],
       LS_COUNT: infoWindow.x_dotcount ? infoWindow.x_dotcount : 1
     };
  }
  
  // clear the current items first... (for paging)
  if (infoWindow._lay_items && infoWindow._lay_items.LS_ITEMS) {
    infoWindow._lay_items.LS_ITEMS = [];
  }

  // populate the infowindow..  
  infoWindow._populate_from_ws_success_silent(dotContents, infoWindow);
  
}


/**
 * Process the results of the 'get dot contents' call.  
 * Loop through all the places/photos, and add them to the dot.
 *
 * Don't trigger an infowindow display.
 */
planeteye.map.InfoWindow.prototype._populate_from_ws_success_silent = function(dotContents, infoWindow) {

  // set up the bounding box for all the items in this overlay
  infoWindow.x_isowner = dotContents['@isowner'] == "true";
  this._bounds = new GLatLngBounds();
  // process the photos
  
  if (dotContents.photos && dotContents.photos instanceof Array && dotContents.photos.length > 0 ) {
  
    // webservices will always return 1 more item than requested in the page, to tell you there are more records..
    var length = dotContents.photos.length < infoWindow.x_perPage ? dotContents.photos.length : infoWindow.x_perPage;
    
    for(var x = 0; x < length; x++){
      var photo = dotContents.photos[x];
      this._bounds.extend(new GLatLng(photo["@lat"],photo["@lng"]));
      var index = (infoWindow._lay_page-1)*infoWindow.x_perPage + x;
      
      infoWindow._populate_add_photo(
        index, 
        photo.pathMedium['@path'], 
        photo.mediaTypeId === '6' ? photo.webUrl : photo.planeteyeUrl,
        photo["@lat"],
        photo["@lng"],
        photo.zoomDotId,
        photo["@id"],
        photo.otherId,
        photo.mediaTypeId
      );
      
      // do we need to jump to a specific item? 
      if (infoWindow.x_default_itemid != '-1' && infoWindow.x_default_itemid == 'photo.' + photo['@id']) {
        this._lay_current = index;
        infoWindow.x_default_itemid = '-1'; // reset, we've set the index..
      }
    }
  }
  
  // process the places 
  if (dotContents.places && dotContents.places != null && dotContents.places instanceof Array && dotContents.places.length > 0){

    // determine the page number
    if (dotContents.places['@page']) {
        infoWindow._lay_page = parseInt(dotContents.places['@page']);
    }
    
    // webservices will always return 1 more item than requested in the page, to tell you there are more records..
    var length = dotContents.places.length < infoWindow.x_perPage ? dotContents.places.length : infoWindow.x_perPage;
 
    for(var x = 0; x < length; x++){
      var place = dotContents.places[x];
      this._bounds.extend(new GLatLng(place["@lat"],place["@lng"]));
      var index = (infoWindow._lay_page-1)*infoWindow.x_perPage + x;
      
      // this method will reset the '_current_lay' property if 'x_default_itemid' is set.
      infoWindow._populate_add_place(
                index,
                place.title,
                '', // specific category
                place.address ? place.address : '', 
                '', 
                place.description['#cdata-section'], 
                place.planeteyeUrl, 
                place.categories.category && place.categories.category[0] ? place.categories.category[0]['@name'] : 'Place',
                infoWindow.x_default_itemid == 'place.' + place['@id'],
                place["@lat"],
                place["@lng"],
                place.zoomDotId,
                place["@id"],
                place.otherId,
                place.thumbnailUrl);
 
      // do we need to jump to a specific item? 
      if (infoWindow.x_default_itemid != '-1' && infoWindow.x_default_itemid == 'place.' + place['@id']) {
        this._lay_current = index;
        infoWindow.x_default_itemid = '-1'; // reset, we've set the index..
      }
    }
  }
  
  // process the results of a point 
  if (dotContents.photo) {
      infoWindow._populate_add_photo(
            0, 
            dotContents.photo.pathMedium['@path'], 
            dotContents.photo.mediaTypeId === '6' ? dotContents.photo.webUrl : dotContents.photo.planeteyeUrl,
            dotContents.photo["@lat"],
            dotContents.photo["@lng"],
            "photo."+dotContents.photo["@id"],
            dotContents.photo["@id"],
            dotContents.photo.otherId, 
            dotContents.photo.mediaTypeId);
  } else if (dotContents.place) {
      if(dotContents.place.categories.category instanceof Array){
            infoWindow._populate_add_place(0, dotContents.place.title, '', dotContents.place.address ? dotContents.place.address : '', '', dotContents.place.description['#cdata-section'], 
                    dotContents.place.planeteyeUrl, dotContents.place.categories.category ? dotContents.place.categories.category[0]['@name'] : 'Place',false, dotContents.place['@lat'],dotContents.place["@lng"],"place."+dotContents.place["@id"],dotContents.place["@id"], dotContents.place.otherId, dotContents.place.thumbnailUrl);
       }else{
             infoWindow._populate_add_place(0, dotContents.place.title, '', dotContents.place.address ? dotContents.place.address : '', '', dotContents.place.description['#cdata-section'], 
                    dotContents.place.planeteyeUrl, dotContents.place.categories.category ? dotContents.place.categories.category['@name'] : 'Place',false, dotContents.place['@lat'],dotContents.place["@lng"],"place."+dotContents.place["@id"],dotContents.place["@id"], dotContents.place.otherId, dotContents.place.thumbnailUrl);
       }
  }
  
  // trigger display
  infoWindow._display_current_lay();
}


/**
 * error..
 */
planeteye.map.InfoWindow.prototype._populate_from_ws_error = function(xhr, status, error) {
  //alert('populate_from_ws_error, ' + xhr + ", " + status + ", " + error);
  //error
}


/**
 * Call the webservices to populate the info window.
 */
planeteye.map.InfoWindow.prototype._populate_from_ws = function(issilent) {

  // single location populated from the url..
  if (this.x_dotid == 'single.location') {
    this._populate_from_ws_success(this.x_pemap.state.pointContents, this);
  } else {
  
    // is this a point?
    if (this.x_dotid.indexOf('place.') == 0 || this.x_dotid.indexOf('photo.') == 0) {
      var dotid = this.x_dotid;
      var customerId = this.x_pemap.state.customerId;
        
      this.x_pemap.requestQueueAdd('pointGetContents', planeteye.map.services.pointGetContents(dotid, customerId, this.x_pemap.state.sets, this._populate_from_ws_success, this._populate_from_ws_error, this));
      
      
    // this is a dot.
    } else {
      var dotid = this.x_dotid;
      var default_itemid = this.x_default_itemid; // do we need to open to a specific item?
      var zoom = this.x_map.getZoom();
      var customerId = this.x_pemap.state.customerId;
      var sets = this.x_pemap.state.sets; // + "," + this.x_pemap.state.setsAlwaysOn;
       
      // if this isn't defined... 
      if (!this._lay_page) 
      {
          this._lay_page = 1;
      }
      var page = this._lay_page;
     
      // we want the webservice to tell us what page we're on.
      if (default_itemid && default_itemid != '-1' && default_itemid != '') {
          page = 0;
      }
        
      var pageSize = this.x_perPage;
       
      if (issilent) {
          this.x_pemap.requestQueueAdd('dotsGetContents', planeteye.map.services.dotsGetContents(sets, dotid, default_itemid, zoom, customerId, page, pageSize, this._populate_from_ws_success_silent, this._populate_from_ws_error, this));
      } else {
          this.x_pemap.requestQueueAdd('dotsGetContents', planeteye.map.services.dotsGetContents(sets, dotid, default_itemid, zoom, customerId, page, pageSize, this._populate_from_ws_success, this._populate_from_ws_error, this));
      }
    }
  }
    
  return;
}



/**
 * String utility - replace tokens..
 */
planeteye.map.InfoWindow.prototype.ifen_htmlre = function(html, mword, sub) {
  var re = new RegExp('%%' + mword + '%%');
  return html.replace(re, sub);
}




planeteye.map.InfoWindow.prototype._htmlize_inside_place = function(
  PLACE_TITLE, PLACE_L1, PLACE_L2, PLACE_L3, PLACE_DESC,PLANETEYE_URL, PLACE_ID, OTHER_ID, PLACE_THUMBURL
) {

  var html = '';
  if (PLACE_THUMBURL && PLACE_THUMBURL != '') {
    html += '    <div class="x1"><div class="itemPhoto"><a class="thumb-link" href="%%PLACE_IMAGE_FULLURL%%"><img src="%%PLACE_IMAGE_THUMBURL%%" alt="" /></a></div><a class="place-link" href="%%PLANETEYE_URL%%" target="%%PLANETEYE_TARGET%%" rel="%%OTHER_ID%%">%%PLACE_TITLE%%</a></div>'
  } else {
    html += '    <div class="x1"><a class="place-link" href="%%PLANETEYE_URL%%" target="%%PLANETEYE_TARGET%%" rel="%%OTHER_ID%%">%%PLACE_TITLE%%</a></div>'
  }
    html += '    <div class="x2 %%MY_EXTRA_X2_CSS_CLASS%%">%%PLACE_L1%%</div>'
    + '    <div class="x3 %%MY_EXTRA_X3_CSS_CLASS%%">%%MY_PLACE_MIDDLE%%</div>'
    + '    <div class="x4">%%PLACE_DESC%%</div>'
  ;

  //PLACE_L1 = 'City Park';
  //PLACE_L3 = 'Bla, Blabla';

  // L2 <br> L3
  //
  var my_place_middle = PLACE_L2;
  if (PLACE_L3 != '') {
    if (my_place_middle != '') my_place_middle += '<br>';
    my_place_middle += PLACE_L3;
  }

  var my_extra_x2_css_class = '';
  if (PLACE_L1 === '') {
    my_extra_x2_css_class = 'x2_hidden';
  }

  var my_extra_x3_css_class = '';
  if (my_place_middle === '') {
    my_extra_x3_css_class = 'x3_hidden';
  }


  html = this.ifen_htmlre(html, 'PLACE_TITLE', PLACE_TITLE);
  html = this.ifen_htmlre(html, 'MY_EXTRA_X2_CSS_CLASS', my_extra_x2_css_class);
  html = this.ifen_htmlre(html, 'PLACE_L1', PLACE_L1);
  html = this.ifen_htmlre(html, 'MY_EXTRA_X3_CSS_CLASS', my_extra_x3_css_class);
  html = this.ifen_htmlre(html, 'MY_PLACE_MIDDLE', my_place_middle);
  html = this.ifen_htmlre(html, 'PLACE_DESC', PLACE_DESC);
  html = this.ifen_htmlre(html, 'OTHER_ID', PLACE_ID + ',' + OTHER_ID);
  if (PLACE_THUMBURL && PLACE_THUMBURL != '') {
    html = this.ifen_htmlre(html, 'PLACE_IMAGE_THUMBURL', PLACE_THUMBURL);
    // display the LARGER size
    html = this.ifen_htmlre(html, 'PLACE_IMAGE_FULLURL', PLACE_THUMBURL.replace('users/3/','users/7/'));
  }
  
  if (!PLANETEYE_URL || PLANETEYE_URL === '' || PLANETEYE_URL === '#') {
    html = this.ifen_htmlre(html, 'PLANETEYE_TARGET', ''); 
    html = this.ifen_htmlre(html, 'PLANETEYE_URL', 'javascript:void(0);'); 
  } else {
    html = this.ifen_htmlre(html, 'PLANETEYE_URL', PLANETEYE_URL); 
    html = this.ifen_htmlre(html, 'PLANETEYE_TARGET', '_blank'); 
  }
 
  return html;
}

planeteye.map.InfoWindow.prototype._htmlize_inside_photo = function(
  PHOTO_SRC,PLANETEYE_URL,PHOTO_ID,OTHER_ID,KIND
) {

  var html = ''
    + '    <div class="q"><a class="%%KIND%%-link" href="%%PLANETEYE_URL%%" target="_blank" rel="%%OTHER_ID%%"><img class="w" src="%%PHOTO_SRC%%"></a></div>'
  ;

  html = this.ifen_htmlre(html, 'PHOTO_SRC', PHOTO_SRC);
  html = this.ifen_htmlre(html, 'PLANETEYE_URL', PLANETEYE_URL);
  html = this.ifen_htmlre(html, 'PHOTO_ID', PHOTO_ID);
  html = this.ifen_htmlre(html, 'OTHER_ID', PHOTO_ID + ',' + OTHER_ID);
  html = this.ifen_htmlre(html, 'KIND', KIND);
  return html;
}
/**
 * Renders out a data object as created by the 
 * _create_lay calls
 */
planeteye.map.InfoWindow.prototype._htmlize = function(q) {

  var marker = this.x_marker;
  var map    = this.x_map;
  var hull   = this.x_hull;
  var dotid  = this.x_dotid;
  var is_pin = this.x_is_pin;
  var isowner = this.x_isowner;
  var version = this.x_pemap.state.version;
  var inset = this.x_sets != '';     // is this marker in a set (i.e. in a travelpack?)
  
  // if isowner, pins: remove button, dots: add button in dots (check for placeid in points array?)
  // if !isowner, pins: 'save to my maps', dots: add button
  var show_add_to_map = !is_pin || (is_pin && !isowner) || (is_pin && !inset);
  
  var thingTitle;
  
  if (q.THING_COUNT > 1) {
    thingTitle = "%%THING_TYPE%% <span class='q'>%%THING_CURRENT%%</span> / <span class='w'>%%THING_COUNT%%</span>"
  } else {
    if (q.THING_TYPE == "Location"){
        thingTitle = "Location Details"
    } else {
        thingTitle = "%%THING_TYPE%% Details"
    }
  }
  
  var thingAddRemove = "";

  if (isowner) {
      thingAddRemove = '      <a href="#" class="ifenlay_add_or_remove_link">%%THING_ADD_OR_REMOVE%%</a>'
  } else {
     thingAddRemove = '      <a href="#" class="ifenlay_add_or_remove_link" style="display:none" />'
  }
  
  var html = '' 
    + '<div class="ifenlay">'

    + '  <div class="ifenlay_majoritas">'

    + '    <div class="ifenlay_vtitle">'
    + thingTitle
    + '    </div>'

    + '    <div class="ifenlay_vclose">'
    + '    <a href="#">close</a>'
    + '    </div>'

    + '    <div class="ifenlay_vbuttons">'
    + '      <a href="#" class="ifenlay_button %%MY_EXTRA_BUTTON_CLASS_LEFT%%">p</a>'
    + '      <a href="#" class="ifenlay_button %%MY_EXTRA_BUTTON_CLASS_RIGHT%%">n</a>'
    + '      <div class="clear"></div>'
    + '    </div>'


    + '    <div class="clear"></div>'
    + '  </div> <!-- /ifenlay_majoritas -->'


    + '  <div class="ifenlay_inside">'
    + '  <div class="ifenlay_inside_inner">'

    + '  %%MY_HTML_INSIDE%%'

    + '  </div> <!-- /ifenlay_inside_inner -->'
    + '  </div> <!-- /ifenlay_inside -->'

    + '  <div class="ifenlay_finalis">'
    + '    <div class="q"><div class="w"></div></div>'
    + '    <div class="e">'
    + '      <a href="#" class="ifenlay_full_link %%THING_MORE_HREF_CLASS%%">%%MY_MORE_TEXT%%</a>'
    + thingAddRemove
    + '      <div class="clear"></div>'
    + '    </div>'
    + '  </div> <!-- /ifenlay_finalis -->'

    + '</div> <!-- /ifenlay -->'
  ;


  var html_inside = '';
  var add_remove_text = show_add_to_map ? 'add to my map' : 'remove from my map';
var more_text = 'zoom in';
  if (q.KIND === 'photo' || q.KIND === 'video') {
    
    html_inside = this._htmlize_inside_photo(
      q.V.PHOTO_SRC,q.THING_MORE_HREF,q.V.ITEM_ID,q.V.OTHER_ID, q.KIND
    );
  }

  if (q.KIND === 'place') {
    
    html_inside = this._htmlize_inside_place(
      q.V.PLACE_TITLE, q.V.PLACE_L1, q.V.PLACE_L2, q.V.PLACE_L3, q.V.PLACE_DESC,q.THING_MORE_HREF, q.V.ITEM_ID, q.V.OTHER_ID, q.V.ITEM_THUMBURL
    );
  }



  // add special css classes to the buttons to indicate their status
  //
  var extra_button_class_left = '';
  var extra_button_class_right = '';

  // if there is only one item in total
  //
  if (q.THING_COUNT == 1) {

    extra_button_class_left  = 'ifenlay_button_hidden';
    extra_button_class_right = 'ifenlay_button_hidden';

  } else {

    // there is more than one item in total
    //

    // this is the first item
    if (q.THING_CURRENT == 1) {
      extra_button_class_left = 'ifenlay_button_left_inactive';
    } else {
      extra_button_class_left = 'ifenlay_button_left_active';
    }

    // this is the last item
    if (q.THING_CURRENT == q.THING_COUNT) {
      extra_button_class_right = 'ifenlay_button_right_inactive';
    } else {
      extra_button_class_right = 'ifenlay_button_right_active';
    }

  }


  // if there is no 'more' link, hide it..
  // if there is no more link, chances are we can't add it to a travelpack, so hide it..
  // if this is a 'place map' (pm), don't display the 'add to travelpack' links.
  
  // TODO: We really need to not add the link to the dom instead of just adding "" for the add remove text
  var more_href_class = '';
  if (!q.THING_MORE_HREF || q.THING_MORE_HREF == '') {
 //   more_href_class = 'ifenlay_full_link_hidden';
    add_remove_text = '';
  } else if (this.x_pemap.state.version && this.x_pemap.state.version.indexOf('pm') >= 0) {
    add_remove_text = '';
  }

  if(!this.x_pemap.showAddToTpk){
    add_remove_text = '';
  }

  html = this.ifen_htmlre(html, 'THING_TYPE', q.THING_TYPE);
  html = this.ifen_htmlre(html, 'THING_CURRENT', q.THING_CURRENT);
  html = this.ifen_htmlre(html, 'THING_COUNT', q.THING_COUNT);
  html = this.ifen_htmlre(html, 'THING_MORE_HREF', q.THING_MORE_HREF);
  html = this.ifen_htmlre(html, 'THING_MORE_HREF_CLASS', more_href_class);

  html = this.ifen_htmlre(html, 'MY_EXTRA_BUTTON_CLASS_LEFT', extra_button_class_left);
  html = this.ifen_htmlre(html, 'MY_EXTRA_BUTTON_CLASS_RIGHT', extra_button_class_right);
  
  html = this.ifen_htmlre(html, 'MY_MORE_TEXT', more_text);
  
  html = this.ifen_htmlre(html, 'MY_HTML_INSIDE', html_inside);
  
  html = this.ifen_htmlre(html, 'THING_ADD_OR_REMOVE', add_remove_text);



  hull.innerHTML = html;



  var close_link = $(hull).find('.ifenlay_vclose a')[0];
  var zoom_link = $(hull).find('.ifenlay_full_link')[0];
  var add_or_remove_link = $(hull).find('.ifenlay_add_or_remove_link')[0];
  var bounds = this._bounds;
  var pemap = this.x_pemap;
  
  
  GEvent.addDomListener(zoom_link,'click', function(e){
        var z = 15;
        // set the special state for zooming in
        pemap.state.specialZoom = true;
        pemap.state.specialZoomDotId = q.V.ITEM_ZOOM_DOTID;
        pemap.state.specialZoomIsPlace = q.KIND=='place' ? true :false;
        pemap.state.specialZoomId = q.V.ITEM_ID;
        // set the zooming variables
        
        //map.setCenter(new GLatLng(q.V.ITEM_LAT,q.V.ITEM_LNG),z);
        pemap.setCenter(new GLatLng(q.V.ITEM_LAT,q.V.ITEM_LNG),z);
        // show the overlay again
        
        if (e.preventDefault) e.preventDefault();
         return false;
  });
  
  
  /**
   * Process an add or remove from travelpack click.
   */
  GEvent.addDomListener(add_or_remove_link,'click', function(e){
        var pointId = q.KIND + '.' + q.V.ITEM_ID; 

        // this user currently owns this travelpack..
        if (isowner) {
            // if this is a marker (not a dot), it should have a remove link
            if (is_pin) {
                // my tpk AND not in it
                planeteye.map.services.pointRemoveFromSet(pemap.state.sets, pointId, pemap.state.customerId, function() { GEvent.trigger(pemap.map,"planeteye.map.refresh"); } );
            } else {
                // my tpk AND not in it
                planeteye.map.services.pointAddToSet(pemap.state.sets, pointId, pemap.state.customerId, function() { GEvent.trigger(pemap.map,"planeteye.map.refresh"); } );
            }
            // don't jump to the top of the screen -- need both lines for cross-platform
            marker.closeIfenDow();
            
            // remove the marker and 
            pemap.map.removeOverlay(marker);
            pemap.overlayUpdate(pemap, pemap.state.sets, pemap.state.customerId); 
            
        } else {
        
            // not my tpk OR my tpk and not in it - so, save this map, and add this place.
            window.location.href = $('.save-link').attr('href') + "&add=" + pointId; //__doPostBack('SaveMap', pointId);
        }
        
        // don't jump to the top of the screen -- need both lines for cross-platform
        if (e.preventDefault) e.preventDefault();
        return false;
  });
  
  
  GEvent.addDomListener(close_link, 'click', function(e) {

    marker.closeIfenDow();

    // don't jump to the top of the screen -- need both lines for cross-platform
    if (e.preventDefault) e.preventDefault();
    return false;
  });


  this._register_lr_handlers();


  return;
}


planeteye.map.InfoWindow.prototype._register_lr_handlers = function() {

  var hull   = this.x_hull;
  var t_pemap  = this.x_pemap;


  try {
    // check to see if there is a 'pre' function for opening media
    if (eval('typeof t_pemap.mediaOpen') === 'function') {
        var link = $(hull).find('.photo-link')[0];
        if (link) {
            $(link).click(function() {
                //var ids = this.rel.split(',');
                //return t_pemap.mediaOpen(ids[0], ids[1]);
                
                // open in prettyphoto instead
                $.prettyPhoto.open([$('img', this).attr('src').replace('users/4','users/1')], [], ['<a href="' + this.href + '">credits</a>'], 0);
                return false;
            });
        }
    }
    // open videos in a prettyPhoto lightbox
    var link2 = $(hull).find('.video-link, .thumb-link')[0];
    if (link2) {
        $(link2).click(function() {
            $.prettyPhoto.open([this.href], [''], [''], 0);
            return false;
        });
    }
  } catch (e) { }
  
  try {
    // check to see if there is a 'pre' function for opening a place
    if (eval('typeof t_pemap.placeOpen') === 'function') {
        var link = $(hull).find('.place-link')[0];
        if (link) {
            $(link).click(function() {
                var ids = this.rel.split(',');
                return t_pemap.placeOpen(ids[0], ids[1]);
            });
        }
    }
  } catch (e) { }
  
  
  var b_left = $(hull).find('.ifenlay_button_left_active')[0];
  var b_right = $(hull).find('.ifenlay_button_right_active')[0];

  if (b_left) {
    this._register_left_handler(b_left);
  }
  if (b_right) {
    this._register_right_handler(b_right);
  }


  var l_no = $(hull).find('.ifenlay_button_left_inactive')[0];
  var r_no = $(hull).find('.ifenlay_button_right_inactive')[0];

  if (l_no) {
    this._register_handler_disabled(l_no);
  }

  if (r_no) {
    this._register_handler_disabled(r_no);
  }


  return;
}



planeteye.map.InfoWindow.prototype._register_handler_disabled = function(button) {

  (function (button) {

    GEvent.addDomListener(button, 'click', function(e) {
      // don't jump to the top of the screen -- need both lines for cross-platform
      if (e.preventDefault) e.preventDefault();
      return false;
    });

  })(button);

}


planeteye.map.InfoWindow.prototype._register_left_handler = function(button) {

  (function (ifendow, button) {

    GEvent.addDomListener(button, 'click', function(e) {

      ifendow._go_left();

      // don't jump to the top of the screen -- need both lines for cross-platform
      if (e.preventDefault) e.preventDefault();
      return false;
    });

  })(this, button);

}

planeteye.map.InfoWindow.prototype._register_right_handler = function(button) {

  (function (ifendow, button) {

    GEvent.addDomListener(button, 'click', function(e) {

      ifendow._go_right();

      // don't jump to the top of the screen -- need both lines for cross-platform
      if (e.preventDefault) e.preventDefault();
      return false;
    });

  })(this, button);

}


/**
 * Display the previous item.
 *
 * Also deals with paging.
 */
planeteye.map.InfoWindow.prototype._go_left = function() {

  var z = this._lay_items;
  var n = this._lay_current;
  var p = this._lay_page;

  var len = z.LS_ITEMS.length;

  // at the beginning of the list
  if (n == 0) {
    // do nothing.. at the beginning of the list
    
  // are we at the edge of a page?
  } else if (((n-1) % this.x_perPage) == 0) {
    this._lay_page --;
    this._lay_current --;
    
    // is the next item not defined?  i.e. the page hasn't been populated yet?
    if (!z.LS_ITEMS[this._lay_current]) {
      this._populate_from_ws(true);
    } else {
        this._display_current_lay();
    }
    
  // move normally through the list
  } else {
    this._lay_current --;
    this._display_current_lay();
  }

  return;
}

/**
 * Display the next item.
 *
 * Also deals with paging.
 */
planeteye.map.InfoWindow.prototype._go_right = function() {

  var z = this._lay_items;
  var n = this._lay_current;
  var p = this._lay_page;

  var len = z.LS_ITEMS.length;

  // is this the last element in the list?  
  if ((n+1) >= z.LS_COUNT) {
    // do nothing, we're at the end of the list
   
  // are we at the edge of a page?
  } else if (((n+1) % this.x_perPage) == 0) {
    this._lay_page ++;
    this._lay_current ++;
  
    // is the next item not defined?  i.e. the page hasn't been populated yet?
    if (!z.LS_ITEMS[this._lay_current]) {
      this._populate_from_ws(true);
      // silent yes, but should still call 'display_current_lay' at the end... just not reset the arrays..
    } else {
      this._display_current_lay();
    }
    
  // we are free to move to the next item
  } else {
    this._lay_current ++;
    this._display_current_lay();
  }

  return;
}

/**
 * Call to populate the info window overlay with data that has been saved in 
 *  this.lay_items the item at index this.lay_current will be shown first.
 */
planeteye.map.InfoWindow.prototype._display_current_lay = function() {

  
  //var ltype = this._lay_type;
  if (this._lay_items && this._lay_items.LS_ITEMS && this._lay_items.LS_ITEMS.length > 0)
  {
    var ltype = this._lay_items.LS_ITEMS[this._lay_current].ITEM_TYPE;

    if (ltype === 'place') {
      this._display_current_lay_place();
    
    } else if (ltype === 'photo') {
      this._display_current_lay_photo();
      
    }
  }

  return;
}
/**
 * Helper method called by  display_curent_lay that handles a place data object
 * as it has been determined in the previous call it resides at index this._lay_current
 */
planeteye.map.InfoWindow.prototype._display_current_lay_place = function() {

  
  var z = this._lay_items;
  var n = this._lay_current;

  var p = z.LS_ITEMS[n];
  var KIND = 'place';
  var THING_TYPE = z.LS_HEADER;
  var THING_CURRENT = n + 1;
  //var THING_COUNT = z.LS_ITEMS.length;
  var THING_COUNT = z.LS_COUNT;
  var THING_MORE_HREF = p.ITEM_MORE_HREF;
  var V = {};
  V.PLACE_TITLE = p.ITEM_TITLE;
  V.PLACE_L1 = p.ITEM_L1;
  V.PLACE_L2 = p.ITEM_L2;
  V.PLACE_L3 = p.ITEM_L3;
  V.PLACE_DESC = p.ITEM_DESC;
  V.ITEM_LAT = p.ITEM_LAT;
  V.ITEM_LNG = p.ITEM_LNG;
  V.ITEM_ZOOM_DOTID = p.ITEM_ZOOM_DOTID;
  V.ITEM_ID = p.ITEM_ID;
  V.OTHER_ID = p.OTHER_ID;
  V.ITEM_THUMBURL = p.ITEM_THUMBURL;
  var k = {
    KIND: KIND,
    THING_TYPE: THING_TYPE,
    THING_CURRENT: THING_CURRENT,
    THING_COUNT: THING_COUNT,
    THING_MORE_HREF: THING_MORE_HREF,
    V: V
  }

  this._htmlize(k);

  return;
}
/**
 * Helper method called by  display_curent_lay that handles a photo data object
 * as it has been determined in the previous call it resides at index this._lay_current
 */
planeteye.map.InfoWindow.prototype._display_current_lay_photo = function(photos, index) {

  
  var z = this._lay_items;
  var n = this._lay_current;

  var p = z.LS_ITEMS[n];

  var KIND = p.MEDIATYPE_ID === '6' ? 'video' : 'photo';
  var THING_TYPE = z.LS_HEADER;
  var THING_CURRENT = n + 1;
  //var THING_COUNT = z.LS_ITEMS.length;
  var THING_COUNT = z.LS_COUNT;
  var THING_MORE_HREF = p.ITEM_MORE_HREF;
  var V = {};
  V.PHOTO_SRC = p.ITEM_PHOTO_SRC;
  V.ITEM_LAT = p.ITEM_LAT;
  V.ITEM_LNG = p.ITEM_LNG;
  V.ITEM_ZOOM_DOTID = p.ITEM_ZOOM_DOTID;
  V.ITEM_ID = p.ITEM_ID;
  V.OTHER_ID = p.OTHER_ID;
  V.MEDIATYPE_ID = p.MEDIATYPE_ID;
  var k = {
    KIND: KIND,
    THING_TYPE: THING_TYPE,
    THING_CURRENT: THING_CURRENT,
    THING_COUNT: THING_COUNT,
    THING_MORE_HREF: THING_MORE_HREF,
    V: V
  }

  this._htmlize(k);

  return;
}


planeteye.map.InfoWindow.prototype.remove = function() {

  var marker = this.x_marker;
  var map    = this.x_map;
  var hull   = this.x_hull;

  hull.parentNode.removeChild(hull);

}

planeteye.map.InfoWindow.prototype.copy = function() {

  var marker = this.x_marker;
  var map    = this.x_map;
  var hull   = this.x_hull;
  
  return new IfenDow(marker);
}

planeteye.map.InfoWindow.prototype.redraw = function(force) {

  if (!force) return;

  var marker = this.x_marker;
  var map    = this.x_map;
  var hull   = this.x_hull;

  var pixel_at_marker = map.fromLatLngToDivPixel(marker.getPoint());

  var px = pixel_at_marker.x;
  var py = pixel_at_marker.y;
  
  var topoffset = (this.x_is_pin ? 242 : 251);

  hull.style.left = (px - 26) + 'px';
  hull.style.top  = (py - topoffset) + 'px';
 
  hull.className = 'ifendow';

}


GMarker.prototype.IfenDow = null;

GMarker.prototype.openIfenDow = function(default_itemid, is_pin) {

  var marker   = this;
  var map      = marker._pemap.map;
  if (marker.InfoWindow != null){
    // hide the infowindow
    marker.InfoWindow = new planeteye.map.InfoWindow(marker, marker._dotid, marker._dotcount, marker._pemap, default_itemid, is_pin, marker._sets);
  
      // track the open windows, and close the old ones
      marker._pemap.ifen_overlay_redraw(marker._pemap, [ marker.InfoWindow ], 'infowindow');
      ifendow_pan_if_necessary(marker, map, marker.InfoWindow);
      return false;
  }
    
  marker.InfoWindow = new planeteye.map.InfoWindow(marker, marker._dotid, marker._dotcount, marker._pemap, default_itemid, is_pin, marker._sets);
  
  // track the open windows, and close the old ones
  marker._pemap.ifen_overlay_redraw(marker._pemap, [ marker.InfoWindow ], 'infowindow');
  //map.addOverlay(marker.InfoWindow);

  ifendow_pan_if_necessary(marker, map, marker.InfoWindow);

//  ifendow_register_destruction(marker);

}

GMarker.prototype.closeIfenDow = function() {

  var marker   = this;
  var map      = marker._pemap.map;

  if (marker.InfoWindow == null) return;

// not necessary..
//  marker._pemap.ifen_overlay_clear(marker._pemap, 'infowindow');
  map.removeOverlay(marker.InfoWindow);
  marker.InfoWindow = null;
  
}


/*
When the map moves, or the sidebar calls hover on/off, the dot may or
may not be available anymore, due to timing differences.  This
function is a guard against that, and uses the div frame to know
whether or not the marker can still be styled.
*/
GMarker.prototype.has_expired = function() {

  var marker = this;

  if (marker.div_ === null) {
    return true;
  }

  return false;
}

/* 
The hover uses a timeout to decide whether or not to turn off the
hover.  It so happens that when you mouse into a child element of the
binding element, you get duplicate events to turn the hover on then
off in rapid succession.  Using a timeout solves the flickering you
get by only turning off on the last hover off request.
*/
GMarker.prototype.hover_color_on = function() {

  var marker = this;

  if (marker._hover_off_event_timeout !== null) {
    clearTimeout(marker._hover_off_event_timeout);
  }

  marker._hover_off_event_timeout = null;

  marker.hover_color__aux(true);


  return;
}
GMarker.prototype.hover_color_off = function() {

  // the hover should fade away when you mouse away from the dot, but in ie6's case,
  // because of bad events, fade away when you're really sure (extend the timeout).
  //
  var ie6 = false;
  var uagent = navigator.userAgent.toLowerCase();
  if ((uagent.indexOf('msie 6') != -1) && (uagent.indexOf('msie 7') == -1)) {
    ie6 = true;
  }
  var ms = 10;
  if (ie6) ms = 200;


  var marker = this;

  if (marker._hover_off_event_timeout !== null) {
    clearTimeout(marker._hover_off_event_timeout);
  }

  marker._hover_off_event_timeout = setTimeout(
    (function () {
      marker.hover_color__aux(false);
    }),
    ms
  );


  return;
}

GMarker.prototype.hover_color__aux = function(hover_flag) {

  var marker   = this;
  var map      = marker._pemap.map;
  var dotsize  = marker._dotsize;
  var dotcount = marker._dotcount;

  if (marker.has_expired()) return;  // guard against timing differences where this marker can't be styled

  var current_tab_color = marker._pemap.getCurrentTabColor(map._map_dom_object);

  // if this is a dot
  if (marker._dotsize) {
      var img = marker._pemap.ifen_new_marker__get_dot_img(current_tab_color, dotsize, hover_flag);
      marker.setImage(img);
      marker.div_.className = marker._pemap.ifen_new_marker__compute_css_class(dotsize, dotcount, hover_flag);
      
  // if this is a pin
  } else {
      var img = marker._pemap.ifen_new_marker__get_pin_img('none', hover_flag);
      marker.setImage(img);
  }

}

function ifendow_pan_if_necessary(marker, map, ifendow) {

  // sidebar
  var sx = map._sidebar_dom_object.clientWidth;
  var sy = map._sidebar_dom_object.clientHeight;

  // marker
  var point = map.fromLatLngToContainerPixel(marker.getPoint());
  var ox = point.x - sx;
  var oy = point.y;

  // map
  var tx = map._map_dom_object.clientWidth - sx;
  var ty = map._map_dom_object.clientHeight;

  // ifendow bounding box, an approximation
  var ix = 250;
  var iy = 245;

  //console.info('sidebar  ' + sx + ' ' + sy);
  //console.info('marker   ' + ox + ' ' + oy);
  //console.info('map      ' + tx + ' ' + ty);
  //console.info('ifendow  ' + ix + ' ' + iy);

  //console.info(ox + ' + ' + ix + ' = ' + (ox + ix) + ' < ' + tx);
  //console.info(oy + ' - ' + iy + ' = ' + (oy - iy) + ' > ' + 0);

  if ((ox + ix < tx) && (oy - iy > 0)) {
    // good
  } else {
    //console.info('need pan');
    ifendow_pan(marker, map, ifendow);
  }

  return;  
}

function ifendow_pan(marker, map, ifendow) {

  var point = map.fromLatLngToDivPixel(marker.getPoint());

  /* offset it so the overlay is visible */
  point.y -= 100;

  var latlng = map.fromDivPixelToLatLng(point);

  map.panTo(latlng);
}

function ifendow_register_destruction(marker) {

  setTimeout(
    (function () {
      marker.closeIfenDow();
    }),
    2000
  );

}


