/*
 * SIMHMap -- Turn specially-marked HTML into Google Maps
 * Copyright (C) July 2005 - Oct 2006 by Chris Houser <chouser@n01se.net>
 *
 * This code is licensed under the GNU General Public License (GPL)
 *
 * If you use this code on a web page, please include on that page a
 * link to http://n01se.net/SIMHMap/ -- this is a request, not
 * a requirement.  Thanks.
 */

(function(){
  var startdate = new Date();

  // configuration -- if you're using your own copy of SIMHMap.js, you
  // may want to modify these:
  var overlaysPerStep = 25;
  var imgBase = '/images/';
  var upperImgBase = '/images/';
  var shadowServer = '/images/';

  if(window.ez_preload) {
    // we're already loaded
    return;
  }

  var _lastId = 0;
  function newId() {
    return 'ez_' + (++_lastId);
  }

  function loadfunc() {
    if(! GBrowserIsCompatible()) {
      if(document.getElementsByTagName) {
        // Find all divs marked as SIMHMap
        var divs = document.getElementsByTagName('div');
        for(var i = 0; i < divs.length; ++i) {
          var div = divs[ i ];
          if(div.className.indexOf('SIMHMap') > -1) {
            div.innerHTML =  [
              "<div>This map cannot be displayed.  The site's key may be ",
              "incorrect, or your browser may not compatible (see if ",
              "your browser is listed ",
              '<a href="http://maps.google.com/support/bin/answer.py?answer=16532">here</a>).</div>',
              '<div class="firefoxref"><iframe src="http://pagead2.googlesyndication.com/cpa/ads?client=ca-pub-1237864095616304&amp;cpa_choice=CAAQyaj8zwEaCIwcWMzeycafKMu293M&amp;oe=UTF-8&amp;dt=1148266564041&amp;lmt=1148266562&amp;format=180x60_as_rimg&amp;output=html&amp;region=_google_cpa_region_&amp;cc=100&amp;u_h=1024&amp;u_w=1280&amp;u_ah=1050&amp;u_aw=1400&amp;u_cd=24&amp;u_tz=-240&amp;u_his=1&amp;u_java=true&amp;u_nplug=11&amp;u_nmime=133" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" frameborder="0" height="60" scrolling="no" width="180"></iframe></div>'
              ].join('\n');
            div.style.visibility = 'visible';
            div.style.padding = '0.3em';
            div.style.background = '#eee';
            div.style.overflow = 'auto';
          }
        }
      }
      else {
        alert([
          'Your browser is not capable of displaying',
          'Google Maps on this page. Try using Firefox:',
          'http://getfirefox.com/' ].join('\n'));
      }
      return;
    }

    addOnUnload(GUnload);

    function getTotalOffset(elem) {
      point = new GPoint(0, 0);
      while(elem) {
        point.x += elem.offsetLeft;
        point.y += elem.offsetTop;
        //alert(elem.offsetTop + ' = ' + point.y);
        elem = elem.offsetParent;
      }
      return point;
    }

    function GSmallMapTypeControl() {
      GMapTypeControl.call(this, true);
    }
    GSmallMapTypeControl.prototype = new GMapTypeControl();
    GSmallMapTypeControl.prototype.constructor = GSmallMapTypeControl;
    window.GSmallMapTypeControl = GSmallMapTypeControl;

    var CtrlTable = {
      'GOverviewMapControl': true,
      'GLargeMapControl': true,
      'GSmallMapControl': true,
      'GSmallZoomControl': true,
      'GSmallMapTypeControl': true,
      'GMapTypeControl': true,
      'GMenuMapTypeControl': true,
      'GHierarchicalMapTypeControl': true,
      'GScaleControl': true
    };

    var MapTypeTable = {
      'G_NORMAL_MAP' : null,
      'G_SATELLITE_MAP' : null,
      'G_HYBRID_MAP' : null,
      'G_PHYSICAL_MAP' : null,
      'G_MAP_TYPE' : 'G_NORMAL_MAP',
      'G_SATELLITE_TYPE' : 'G_SATELLITE_MAP',
      'G_HYBRID_TYPE' : 'G_HYBRID_MAP'
    };

    var idmarkers = {};
    function markerForUrl(url) {
      var matcha = /#(.*)/.exec(url);
      if(matcha)
        return idmarkers[ matcha[ 1 ] ];
      else
        return null;
    }

    // For this event handler, "this" is the clicked anchor
    function anchorClick() {
      var marker = markerForUrl(this.href);
      if(marker) {
        if(/\bZOOM\b/.exec(this.className)) {
          var mapType = marker.mapType || marker.ezmap.map.getCurrentMapType();
          var zoomLevel;
          if(marker.span) {
            zoomLevel = mapType.getSpanZoomLevel(
              marker.point, marker.span, marker.ezmap.viewsize);
          }
          else {
            zoomLevel = marker.ezmap.map.getZoom();
          }
          marker.ezmap.map.setCenter(marker.point, zoomLevel, mapType);
        }
        marker.doOpen();
        return false;
      }
      else {
        return true;
      }
    }

    function wordMap(str) {
      var wmap = {};
      var list = str.split(' ');
      for(var j = 0; j < list.length; ++j) {
        wmap[ list[ j ] ] = true;
      }
      return wmap;
    }

    function parseParams(str, params) {
      var matchparam;
      for(var word in wordMap(str)) {
        matchparam = /^(\w+):(.*)$/.exec(word);
        if(matchparam && matchparam[1] in params) {
          params[ matchparam[1] ] = matchparam[2];
        }
      }
      return params;
    }

    var markerOpener = {
      markers: [],
      addMarker: function(marker) {
        this.markers.push(marker);
      },
      chainOpen: function(i) {
        /*
         * This is a work-around for a Google Maps bug.  If I try to open
         * all the info windows at once, only the last one succeeds.
         *
         * Otherwise, it is equivalent to:
         *   for(i = 0; i < this.markers.length; ++i)
         *     this.markers[ i ].doOpen();
         */
        i = i || 0;
        if(i < this.markers.length) {
          var onOpen = GEvent.bind(
              this.markers[ i ].ezmap.map,
              "infowindowopen",
              this,
              function(){
                GEvent.removeListener(onOpen);
                this.chainOpen(i + 1);
              });
          this.markers[ i ].doOpen(true);
        }
        else {
          //alert('SIMHMap loadtime: ' + (new Date() - startdate));
        }
      }
    };

    var laterFuncs = [];
    var lastFunc = null;
    function doNow() {
      if(laterFuncs.length > 0) {
        laterFuncs.shift().call();
        setTimeout(doNow, 1);
      }
      else {
        if(lastFunc) {
          lastFunc.call();
        }
      }
    }
    function doLater(obj, func) {
      laterFuncs.push(function() { func.call(obj); });
    }

    var MiniIcon = new GIcon();
    MiniIcon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
    MiniIcon.iconSize = new GSize(12, 20);
    MiniIcon.shadowSize = new GSize(22, 20);
    MiniIcon.iconAnchor = new GPoint(6, 20);
    MiniIcon.infoWindowAnchor = new GPoint(5, 1);

    G_DEFAULT_ICON.transparent = null;
    function EZInfoMarker(ezmap) {
      this.ezmap = ezmap;
      this.icon = G_DEFAULT_ICON;

      this.point = null;
      this.title = null;

      this.blowup = false;
      this.tabs = [];

      this.infoZoomOffset = undefined;
      this.infoZoomLevel = undefined;
      this.infoMapType = null;
    }
    EZInfoMarker.prototype = new GMarker(new GLatLng(0, 0));
    EZInfoMarker.prototype.constructor = EZInfoMarker;

    EZInfoMarker.prototype.initialize = function(map) {
      GMarker.call(
          this,
          this.point,
          {
            icon: this.icon,
            clickable: (this.tabs.length > 0 || this.blowup),
            title: this.title
          });
      GMarker.prototype.initialize.call(this, map);
    };

    EZInfoMarker.prototype.doOpen = function(autoopen) {
      if(! autoopen) {
        var body = document.body || document.getElementsByTagName('body')[0];
        if('scrollLeft' in body) {
          // http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
          var myWidth = 0, myHeight = 0;
          if(typeof(window.innerWidth) == 'number') {
            //Non-IE
            myWidth = window.innerWidth;
            myHeight = window.innerHeight;
          } else if(document.documentElement &&
              (document.documentElement.clientWidth ||
              document.documentElement.clientHeight))
          {
            //IE 6+ in 'standards compliant mode'
            myWidth = document.documentElement.clientWidth;
            myHeight = document.documentElement.clientHeight;
          } else if(document.body &&
              (document.body.clientWidth || document.body.clientHeight))
          {
            //IE 4 compatible
            myWidth = document.body.clientWidth;
            myHeight = document.body.clientHeight;
          }

          var mapdiv = this.ezmap.div;
          var totalOffset = getTotalOffset(mapdiv);
          if(totalOffset.x < body.scrollLeft ||
              totalOffset.x + mapdiv.offsetWidth > body.scrollLeft+myWidth ||
              totalOffset.y < body.scrollTop ||
              totalOffset.y + mapdiv.offsetHeight > body.scrollTop+myHeight)
          {
            //alert(totalOffset.x + ', ' + totalOffset.y);
            scrollTo(totalOffset.x, totalOffset.y);
          }
        }
      }

      var zoom = null;
      if(this.tabs.length > 0) {
        var opts = { maxWidth: this.ezmap.div.offsetWidth - 100 };
        if(this.icon) {
          this.openInfoWindowTabs(this.tabs, opts);
        }
        else {
          // "INVISIBLE" markers are never added as overlays -- GMap2
          // knows nothing about them.
          this.ezmap.map.openInfoWindowTabs(this.point, this.tabs, opts);
        }
        for(var i = 0; i < this.tabs.length; ++i) {
          this.setupInfoForm(this.tabs[ i ]);
        }
      }
      else if(this.blowup) {
        if(this.infoZoomOffset != undefined)
          zoom = this.ezmap.map.getZoom() + this.infoZoomOffset;
        else if(this.infoZoomLevel != undefined)
          zoom = this.infoZoomLevel;

        if(zoom >= this.ezmap.map.getCurrentMapType().numZoomLevels)
          zoom = this.ezmap.map.getCurrentMapType().numZoomLevels - 1;
        else if(zoom < 0)
          zoom = 0;

        this.showMapBlowup({ zoomLevel: zoom, mapType: this.infoMapType });
      }
      else {
        this.ezmap.map.closeInfoWindow();
      }
    };

    EZInfoMarker.prototype.setupInfoForm = function(tab) {
      var div, a;
      var tabElem = tab.contentElem;
      var classes = tabElem.className ? wordMap(tabElem.className) : {};
      if(classes.DirectionsToHere ||
          classes.DirectionsFromHere ||
          classes.SearchNearby)
      {
        if(tab.infoFormId) {
          div = document.getElementById(tab.infoFormId);
          div.innerHTML = '';
        }
        else {
          div = document.createElement('div');
          div.className = 'ez_infoForm';
          tab.infoFormId = div.id = newId();
          tabElem.appendChild(div);
        }
        var _this = this;
        if(classes.DirectionsToHere || classes.DirectionsFromHere) {
          div.appendChild(document.createTextNode('Get directions: '));
          if(classes.DirectionsToHere) {
            a = document.createElement('a');
            a.innerHTML = 'To here';
            a.onclick = function() { _this.showForm(tab); };
            a.href = 'javascript:void(null)';
            div.appendChild(a);
          }
          if(classes.DirectionsToHere && classes.DirectionsFromHere) {
            div.appendChild(document.createTextNode(' - '));
          }
          if(classes.DirectionsFromHere) {
            a = document.createElement('a');
            a.innerHTML = 'From here';
            a.onclick = function() {};
            a.href = 'javascript:void(null)';
            div.appendChild(a);
          }
          div.appendChild(document.createElement('br'));
        }
        if(classes.SearchNearby) {
          a = document.createElement('a');
          a.innerHTML = 'Search nearby';
          a.onclick = function() {};
          a.href = 'javascript:void(null)';
          div.appendChild(a);
        }
      }
    };

    EZInfoMarker.prototype.showForm = function(tab) {
      var div = document.getElementById(tab.infoFormId);
      div.innerHTML = '<b>Get directions:</b> To here - ';
      var a = document.createElement('a');
      a.innerHTML = 'From here';
      a.onclick = function() {};
      a.href = 'javascript:void(null)';
      div.appendChild(a);
      var x = document.createElement('div');
      x.innerHTML = 'Start address';
      div.appendChild(x);
      this.ezmap.map.getInfoWindow().reset();
    };

    function EZPolyline(color, weight, opacity) {
      this.points = [];
      this.initialize = function(map) {
        GPolyline.call(this, this.points, color, weight, opacity);
        GPolyline.prototype.initialize.call(this, map);
      };
    };
    EZPolyline.prototype = new GPolyline();
    EZPolyline.prototype.constructor = EZPolyline;


    function EZMap(div, classes) {
      this.div = div;
      this.classes = classes;
      this.divData = null;

      this.map = undefined;
      this.viewsize = null;
      this.extentMarker = null;

      this.overlayList = [];

      this.loading = null;
      this.bar = null;
      this.maxstep = 0;
      this.step = 0;
      this.oi = 0;
      this.initFrame();
    }

    EZMap.prototype.logWarning = function(str) {
      if(! this.warningNode) {
        this.warningVis = false;
        this.warningNode = document.createElement('ul');
        this.warningNode.className = 'warnings';
        this.div.appendChild(this.warningNode);

        var warnBtn = document.createElement('button');
        warnBtn.className = 'warnings';
        warnBtn.innerHTML = 'Warnings...';
        this.div.appendChild(warnBtn);
        var ezmap = this;
        warnBtn.onclick = function() { ezmap.toggleWarnings(); };
      }
      var li = document.createElement('li');
      li.innerHTML = str;
      this.warningNode.appendChild(li);
    };

    EZMap.prototype.toggleWarnings = function() {
      this.warningVis = ! this.warningVis;
      this.warningNode.style.display = this.warningVis ? 'block' : 'none';
    };

    EZMap.prototype.processMarkers = function(parentNode, polyline) {
      var lastOverlay, marker, textContent, imgs;
      var matchll, matchspn, matchtype;
      for(var node = parentNode.firstChild; node; node = node.nextSibling){
        lastOverlay = this.overlayList[ this.overlayList.length - 1 ];
        switch(node.nodeName) {
        case 'A':
          matchll = /\Wll=([-.\d]*),([-.\d]*)/.exec(node.href);
          if(matchll) {

            marker = new EZInfoMarker(this);
            marker.title = node.getAttribute('title');
            this.overlayList.push(marker);

            if(node.id || node.name)
              idmarkers[ node.id || node.name ] = marker;

            textContent = node.innerHTML.replace(/<[^>]*>/g, '');

            if(/\bOPEN\b/.exec(textContent))
              markerOpener.addMarker(marker);

            if(/\bEXTENT\b/.exec(textContent)) {
              marker.icon = null;
              this.extentMarker = marker;
            }

            if(/\bINVISIBLE\b/.exec(textContent)) {
              marker.icon = null;
            }

            marker.point = new GLatLng(
                parseFloat(matchll[1]),
                parseFloat(matchll[2]));

            if(polyline) {
              marker.icon = null;
              polyline.points.push(marker.point);
            }

            matchspn = /\Wspn=([-.\d]*),([-.\d]*)/.exec(node.href);
            if(matchspn) {
              marker.span = new GLatLng(
                  parseFloat(matchspn[1]),
                  parseFloat(matchspn[2]));
            }

            matchtype = /\Wt=(.)/.exec(node.href);
            if(matchtype) {
              switch(matchtype[1]) {
                case 'k': marker.mapType = G_SATELLITE_MAP; break;
                case 'h': marker.mapType = G_HYBRID_MAP; break;
                // XXX need a GMapType letter for use by ZOOM links
              }
            }

            // build icon
            imgs = node.getElementsByTagName('img')
            if(imgs.length < 1) {
              var matchcolor =
                  /\b(ORANGE|PURPLE|YELLOW|GREEN|BLUE|RED|AQUA|WHITE|GRAY)\b/
                  .exec(textContent);
              var matchsym =
                  /\b([0-9A-Za-z]|BLANK|HASH|DOLLAR|DOT|START|END)\b/
                  .exec(textContent);
              var matchmini = /\bMINI\b/.exec(textContent);

              if(matchcolor || matchsym || matchmini) {
                var upper = matchsym && matchsym[0].match(/^[A-Z]$/);
                marker.icon = new GIcon(matchmini ? MiniIcon : G_DEFAULT_ICON);
                marker.icon.image = [
                  upper ? upperImgBase : imgBase,
                  matchmini ? 'mini' : 'marker',
                  '-',
                  matchcolor ? matchcolor[0] : 'ORANGE',
                  '-',
                  matchmini ? 'BLANK' : (matchsym ? matchsym[0] : 'DOT'),
                  upper ? '-UPPER' : '',
                  '.png' ].join('');

                marker.icon.printImage    = marker.icon.image;
                marker.icon.mozPrintImage = marker.icon.image;
              }
            }
            else {
              marker.icon = new GIcon(G_DEFAULT_ICON, imgs[ 0 ].src);
              marker.icon.printImage    = imgs[ 0 ].src;
              marker.icon.mozPrintImage = imgs[ 0 ].src;
              marker.icon.transparent = null;
              marker.icon.iconAnchor = null;
              marker.icon.infoWindowAnchor = null;

              var params = parseParams(imgs[ 0 ].className, {
                width: null, height: null,
                iconAnchor: null, infoWindowAnchor: null, imageMap: null });

              var match;
              var width = parseInt(params.width);
              var height = parseInt(params.height);
              marker.icon.iconSize = new GSize(width, height);

              if(params.iconAnchor) {
                match = /(\d+),(\d+)/.exec(params.iconAnchor);
                if(match) {
                  marker.icon.iconAnchor = new GPoint(
                    parseInt(match[1]),
                    parseInt(match[2]));
                }
              }
              if(marker.icon.iconAnchor === null) {
                marker.icon.iconAnchor = new GPoint(
                  Math.round(width / 2),
                  Math.max(1, height - 2));
              }

              if(params.infoWindowAnchor) {
                match = /(\d+),(\d+)/.exec(params.infoWindowAnchor);
                if(match) {
                  marker.icon.infoWindowAnchor = new GPoint(
                    parseInt(match[1]),
                    parseInt(match[2]));
                }
              }
              if(marker.icon.infoWindowAnchor === null) {
                marker.icon.infoWindowAnchor = new GPoint(
                  Math.round(width / 2),
                  0);
              }

              if(params.imageMap) {
                marker.icon.imageMap = [];
                var parts = params.imageMap.split(/\s*,\s*/);
                for(var i = 0; i < parts.length; ++i) {
                  marker.icon.imageMap.push(parseInt(parts[ i ]));
                }
                //alert(marker.icon.imageMap);
              }

              if(imgs.length > 1) {
                marker.icon.shadow = imgs[ 1 ].src;
                params = parseParams(imgs[ 1 ].className, { width:0, height:0 });
                marker.icon.shadowSize = new GSize(params.width, params.height);
              }
              else {
                marker.icon.shadow = shadowServer + imgs[ 0 ].src;
                marker.icon.shadowSize = new GSize(
                    Math.floor(width + height * 0.55), height);
              }

              if(imgs.length > 2) {
                marker.icon.transparent = imgs[ 2 ].src;
              }
              if(imgs.length > 3) {
                marker.icon.printImage = imgs[ 3 ].src;
              }
              if(imgs.length > 4) {
                marker.icon.printShadow = imgs[ 4 ].src;
              }
              if(imgs.length > 5) {
                marker.icon.mozPrintShadow = imgs[ 5 ].src;
              }
            }
          }
          else {
            this.logWarning("No ll param for marker [" + node.innerHTML +
              ":" + (node.id || node.name) + "]");
          }
          break;

        case 'DIV':
          if(! lastOverlay) {
            this.logWarning("div block given before any markers");
            continue;
          }
          else {
            var infoClasses = wordMap(node.className);
            if('SIMHMap' in infoClasses) {
              // infoWindow blowup
              // XXX replace this with a nested map to allow for use
              // in tabs?
              lastOverlay.blowup = true;
              var matchzoom = /ZOOMLEVEL([-+=]?)(\d+)/.exec(node.innerHTML);
              if(matchzoom) {
                var num = parseInt(matchzoom[ 2 ]);
                if(matchzoom[ 1 ] == '-')
                  lastOverlay.infoZoomOffset = num;
                else if(matchzoom[ 1 ] == '+')
                  lastOverlay.infoZoomOffset = - num;
                else
                  lastOverlay.infoZoomLevel = num;
              }

              for(typeName in MapTypeTable) {
                if(typeName in infoClasses) {
                  if(MapTypeTable[ typeName ]) {
                    lastOverlay.infoMapType = window[ MapTypeTable[ typeName ] ];
                  }
                  else {
                    lastOverlay.infoMapType = window[ typeName ];
                  }
                  break;
                }
              }
            }
            else {
              // XXX remove title attribute from div node?
              lastOverlay.tabs.push(
                  new GInfoWindowTab(node.getAttribute('title'), node));
            }
          }
          break;

        case 'LI':
          this.processMarkers(node, polyline);
          break;

        case 'OL':
          var params = parseParams(node.className,
              { color: null, weight: null, opacity: null });
          if(params.color && ! /^#[0-9a-zA-Z]{6}$/.exec(params.color))
            this.logWarning('Polyline color should be a 6-digit' +
                ' hex color like "#123abc", not "' + params.color + '"');
          if(params.weight != null) {
            var w = parseInt(params.weight);
            if(w < 1 || isNaN(w))
              this.logWarning('Polyline weight should be an' +
                  ' interger above 0, not "' + params.weight + '"');
            params.weight = w;
          }
          if(params.opacity) {
            var o = parseFloat(params.opacity);
            if(o < 0 || o > 1 || isNaN(o))
              this.logWarning('Polyline opacity should be ' +
                  ' between 0 and 1, not "' + params.opacity + '"');
            params.opacity = o;
          }
          var newline = new EZPolyline(
              params.color,
              params.weight,
              params.opacity);
          this.overlayList.push(newline);
          this.processMarkers(node, newline);
          break;

        case '#text':
        case '#comment':
          // ignore text and comments
          break;

        default:
          this.logWarning("Unknown or misplaced node " + node.nodeName);
          break;
        }
      }
    };

    EZMap.prototype.onClick = function(overlay, point) {
      if(overlay && overlay.doOpen) {
        overlay.doOpen();
      }
      else if(point) {
        this.map.closeInfoWindow();
      }
    };

    EZMap.prototype.nextStep = function(func) {
      if(this.maxstep > 0) {
        this.bar.style.width = Math.round(this.step / this.maxstep * 100) + '%';
      }
      this.step += 1;
      doLater(this, func);
    };

    EZMap.prototype.initFrame = function() {
      this.divData = this.div;
      this.div = this.div.cloneNode(false);
      this.divData.parentNode.insertBefore(this.div, this.divData);
      this.divData.parentNode.removeChild(this.divData);
      this.div.style.visibility = 'visible';

      this.loading = document.createElement('div');
      this.loading.className = 'loadprogress';
      this.loading.style.marginTop = (this.div.offsetHeight / 3) + 'px';
      this.loading.innerHTML = '<div class="box"><div class="logo"></div><div class="trough"><div class="bar"></div></div></div>';
      this.bar = this.loading.getElementsByTagName('div')[3];
      this.div.appendChild(this.loading);

      this.nextStep(this.initParse);
    };

    EZMap.prototype.initParse = function() {
      this.processMarkers(this.divData);
      this.divData = null;

      this.map = new GMap2(this.div);
      this.map.getContainer().appendChild(this.loading);

      GEvent.bind(this.map, 'click', this, this.onClick);
      if('ScrollWheelZoom' in this.classes) {
        this.map.enableScrollWheelZoom();
      }

      if(! this.extentMarker && this.overlayList.length == 1)
        this.extentMarker = this.overlayList[ 0 ];

      // map type
      var mapType = G_NORMAL_MAP;
      if(this.extentMarker && this.extentMarker.mapType) {
        mapType = this.extentMarker.mapType;
      }
      for(typeName in MapTypeTable) {
        if(typeName in this.classes) {
          if(MapTypeTable[ typeName ]) {
            mapType = window[ MapTypeTable[ typeName ] ];
          }
          else {
            mapType = window[ typeName ];
          }
          break;
        }
      }

      // center and zoom
      this.viewsize = new GSize(this.div.offsetWidth, this.div.offsetHeight);
      var center, zoomLevel;
      if(this.extentMarker) {
        center = this.extentMarker.point;
        if(this.extentMarker.span) {
          zoomLevel = mapType.getSpanZoomLevel(
              center, this.extentMarker.span, this.viewsize);
        }
        else {
          zoomLevel = 10;
        }
      }
      else if(this.overlayList.length > 1) {
        var autoBounds;
        for(var i = 0; i < this.overlayList.length; ++i) {
          if(this.overlayList[ i ].point) {
            if(! autoBounds) {
              autoBounds = new GLatLngBounds(
                  this.overlayList[ i ].point,
                  this.overlayList[ i ].point);
            }
            else {
              autoBounds.extend(this.overlayList[ i ].point);
            }
          }
        }
        var sw = autoBounds.getSouthWest();
        var ne = autoBounds.getNorthEast();
        center = new GLatLng(
          (sw.lat() + ne.lat()) / 2,
          (sw.lng() + ne.lng()) / 2);
        zoomLevel = mapType.getBoundsZoomLevel(autoBounds, this.viewsize);
      }
      else {
        center = new GLatLng(41.075210, -85.130310);
        zoomLevel = 10;
      }

      // apply center, zoom, and map type
      this.map.setCenter(center, zoomLevel, mapType);

      this.maxstep = Math.floor(this.overlayList.length / overlaysPerStep) + 2;
      this.initOverlays();
    };

    EZMap.prototype.initOverlays = function() {
      var overlay;
      var steplimit = Math.min(this.overlayList.length, this.oi+overlaysPerStep);
      for(; this.oi < steplimit; ++this.oi) {
        overlay = this.overlayList[ this.oi ];
        if(overlay.points || overlay.icon)
          this.map.addOverlay(overlay);
        this.overlayList[ this.oi ] = null;
      }
      if(this.oi < this.overlayList.length)
        this.nextStep(this.initOverlays);
      else
        this.nextStep(this.initControls);
    };

    EZMap.prototype.initControls = function() {
      for(var ctrl in CtrlTable) {
        if(ctrl in this.classes) {
          this.map.addControl(new window[ ctrl ]());
        }
      }

      this.nextStep(this.initLoading);
    };

    EZMap.prototype.initLoading = function() {
      this.loading.parentNode.removeChild(this.loading);
      this.loading = null;
    };

    // Find all anchor tags linking to SIMHMap markers
    var anchors = document.getElementsByTagName('a');
    for(var mi = 0; mi < anchors.length; ++mi) {
      if(! anchors[ mi ].onclick) {
        anchors[ mi ].onclick = anchorClick;
      }
    }

    // Find all divs marked as SIMHMap
    var divs = document.getElementsByTagName('div');
    for(var i = 0; i < divs.length; ++i) {
      var div = divs[ i ];
      var classes = wordMap(div.className);
      if('SIMHMap' in classes) {
        new EZMap(div, classes);
      }
    }

    lastFunc = function() {
      // Examine current page location for a reference to a SIMHMap marker
      var marker = markerForUrl(document.location);
      if(marker)
        markerOpener.addMarker(marker);

      // Open all the markers we need to
      markerOpener.chainOpen();
    };

    doNow();
  }

  function chainWindowFunc(funcname, newfunc) {
    var oldfunc = window[ funcname ];
    if(oldfunc) {
      window[ funcname ] = function() { oldfunc(); newfunc(); };
    }
    else {
      window[ funcname ] = newfunc;
    }
  }

  window.addOnLoad   = function(func) { chainWindowFunc("onload",   func);};
  window.addOnUnload = function(func) { chainWindowFunc("onunload", func);};

  window.ez_preload = function() {
    if(window.G_INCOMPAT) {
      // If the key failed to validate, cause all keys to appear valid
      // and try again.
      window.G_INCOMPAT = false;
      window.GValidateKey = function(){ return true; }
      GLoad();
    }
    addOnLoad(loadfunc);
  }

  var key = '';
  var gmapversion = '2';
  (function(){
    var metas = document.getElementsByTagName('meta');
    var match;
    for(var i = 0; i < metas.length; ++i) {
      match = /gmapkey:?(.*)/.exec(metas[ i ].name);
      if(match) {
        if(! match[ 1 ]) {
          key = metas[ i ].content;
        }
        else if((new RegExp(match[ 1 ])).exec(window.location.href)) {
          key = metas[ i ].content;
          break;
        }
      }
      else if(metas[ i ].name == 'gmapversion') {
        gmapversion = metas[ i ].content;
      }
    }
  })();

  if(! window.GMap2) {
    document.write([
      '<script src="http://maps.google.com/maps?file=api&v='
      + gmapversion + '&key=' + key
      + '" type="text/javascript"></script>' ].join('\n'));
  }

  document.write([
    '<script type="text/javascript">',
    '  ez_preload();',
    '</script>',

    '<style type="text/css">',
    'div.SIMHMap {',
    '  visibility: hidden;',
    '  border: 1px #888 solid;',
    '}',

    'div.SIMHMap ul.warnings {',
    '  position: absolute;',
    '  top: 0;',
    '  left: 0;',
    '  margin: 0;',
    '  padding-right: 0.5em;',
    '  padding-left: 1.5em;',
    '  display: none;',
    '  border: 1px #888 solid;',
    '  background: #fff;',
    '  z-index: 100000000;',
    '  text-align: left;',
    '  font-family: Arial;',
    '  font-size: 9pt;',
    '  overflow: auto;',
    '}',

    'div.SIMHMap button.warnings {',
    '  display: block;',
    '  position: absolute;',
    '  z-index: 100000000;',
    '  bottom: 20px;',
    '  right: 0;',
    '  color: #f00;',
    '}',

    'div.SIMHMap .firefoxref {',
    '  text-align: center;',
    '  margin: 1em;',
    '}',

    'div.SIMHMap .loadprogress {',
    '  position: relative;',
    '  z-index: 100000000;',
    '  text-align: center;',
    '}',

    'div.SIMHMap .loadprogress .box {',
    '  font-family: Arial, sans-serif;',
    '  background: #fff;',
    '  border: 1px #bbb inset;',
    '  width: 136px;',
    '  height: 45px;',
    '  margin: auto;',
    '}',

    'div.SIMHMap .loadprogress .logo {',
    '  margin: 8px;',
    '  margin-bottom: 0;',
    '  width: 120px;',
    '  height: 28px;',
    "  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
        imgBase + "logo3.png',sizingMethod='scale');",
    '}',
    'div.SIMHMap .loadprogress .box > .logo {',
    '  position: relative;',
    '  background: url(' + imgBase + 'logo3.png) no-repeat;',
    '}',

    'div.SIMHMap .loadprogress .trough {',
    '  margin: 8px;',
    '  margin-top: -5px;',
    '  background: #ccc;',
    '  font-size: 1px;',
    '  height: 8px;',
    '  text-align: left;',
    '}',

    'div.SIMHMap .loadprogress .bar {',
    '  height: 8px;',
    '  width: 0px;',
    '  background: #55a url(' + imgBase + 'progressbar.png);',
    '}',

    'v\\:* {',
    '  behavior:url(#default#VML);',
    '}',
    '</style>'
  ].join('\n'));
})();

