/*
 * GMapEZ -- 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/gmapez/ -- this is a request, not
 * a requirement.  Thanks.
 */

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

  // configuration -- if you're using your own copy of gmapez.js, you
  // may want to modify these:
  var overlaysPerStep = 25;
  var imgBase = 'http://gmapez.googlepages.com/';
  var upperImgBase = 'http://gmapez.upper.googlepages.com/';
  var shadowServer = 'http://n01se.net/shadow.cgi?src=';

  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 GMapEZ
        var divs = document.getElementsByTagName( 'div' );
        for( var i = 0; i < divs.length; ++i ) {
          var div = divs[ i ];
          if( div.className.indexOf( 'GMapEZ' ) > -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('GMapEZ 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
