/*
 * GMaps -- Turn specially-marked HTML into Google Maps
 * Copyright (C) December 2005 by Fabio Cantone <f.cantone@orkestra.it>
 *
 */
(function(){
  var startdate = new Date();

  document.write( [
    '<style type="text/css">',
    'div.GMaps {',
    '  display: none;',
    '  border: 1px #888 solid;',
    '}',

    'div.GMaps 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.GMaps button.warnings {',
    '  display: block;',
    '  position: absolute;',
    '  z-index: 100000000;',
    '  bottom: 20px;',
    '  right: 0;',
    '  color: #f00;',
    '}',

    'v\\:* {',
    '  behavior:url(#default#VML);',
    '}',
    '</style>'
  ].join('\n'));

  function loadfunc() {

    if( ! window.GIcon ) {
      if( ! GMapsNamespace )
        return;
      _apiHash = undefined;
      GMapsNamespace();
    }

    function GSmallMapTypeControl() {
      GMapTypeControl.call( this, true );
    }
    GSmallMapTypeControl.prototype = new GMapTypeControl();
    GSmallMapTypeControl.prototype.constructor = GSmallMapTypeControl;
    window.GSmallMapTypeControl = GSmallMapTypeControl;

    var CtrlTable = {
      'GLargeMapControl': true,
      'GSmallMapControl': true,
      'GSmallZoomControl': true,
      'GSmallMapTypeControl': true,
      'GMapTypeControl': true,
      'GScaleControl': true
    };

    var MapTypeTable = {
      'G_MAP_TYPE' : true,
      'G_SATELLITE_TYPE' : true,
      'G_HYBRID_TYPE' : true,
      'G_KATRINA_TYPE' : true
    };

    var idmarkers = {};
    function markerForUrl( url ) {
      var matcha = /#(.*)/.exec( url );
      if( matcha )
        return idmarkers[ matcha[ 1 ] ];
      else
        return null;
    }

    function wordMap( str ) {
      var wmap = {};
      var list = str.split(' ');
      for( var j = 0; j < list.length; ++j ) {
        wmap[ list[ j ] ] = true;
      }
      return wmap;
    }

    var defaultIcon = (new GMarker()).icon;

    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 ],
              "infowindowopen",
              this,
              function(){
                GEvent.removeListener( onOpen );
                this.chainOpen( i + 1 );
              });
          this.markers[ i ].doOpen();
        }
        else {
          //alert('GMaps loadtime: ' + ( new Date() - startdate ) );
        }
      }
    };

    function InfoMarker( map, point, icon ) {
      GMarker.call( this, point, icon );
      this.map = map; // circular ref?
      this.html = null;
      this.infoZoomOffset = undefined;
      this.infoZoomLevel = undefined;
      this.infoMapType = null;
    }
    InfoMarker.prototype = new GMarker();
    InfoMarker.prototype.constructor = InfoMarker;

    InfoMarker.prototype.doOpen = function() {
      var zoom = null;
      if( this.html ) {
        this.openInfoWindowHtml( this.html );
      }
      else {
        if( this.infoZoomOffset != undefined )
          zoom = this.map.getZoomLevel() + this.infoZoomOffset;
        else if( this.infoZoomLevel != undefined )
          zoom = this.infoZoomLevel;

        if( zoom >= this.map.spec.numZoomLevels )
          zoom = this.map.spec.numZoomLevels - 1;
        else if( zoom < 0 )
          zoom = 0;

        this.showMapBlowup( zoom, this.infoMapType );
      }
    };

    function OMap( div, classes ) {
      this.div = div;
      this.classes = classes;

      /*
      this.map;
      this.marker = undefined;
      this.minX = undefined;
      this.maxX = undefined;
      this.minY = undefined;
      this.maxY = undefined;
      */
      this.mapType = undefined;
      this.pointCount = 0;
      this.center = undefined;
      this.span = undefined;
      this.explicitExtent = false;

      this.init();
    }

    OMap.prototype.logWarning = function( str ) {
      if( ! this.warningNode ) {
        this.warningVis = false;
        this.warningNode = document.createElement('ul');
        this.warningNode.className = 'warnings';
        div.appendChild( this.warningNode );

        var warnBtn = document.createElement('button');
        warnBtn.className = 'warnings';
        warnBtn.innerHTML = 'Warnings...';
        div.appendChild( warnBtn );
        GEvent.bindClick( warnBtn, this, this.toggleWarnings );
      }
      var li = document.createElement('li');
      li.innerHTML = str;
      this.warningNode.appendChild( li );
    };

    OMap.prototype.toggleWarnings = function() {
      this.warningVis = ! this.warningVis;
      this.warningNode.style.display = this.warningVis ? 'block' : 'none';
    };

    OMap.prototype.processMarkers = function( parentNode, drawline ) {
      var pointList = [];
      for( var node = parentNode.firstChild; node; node = node.nextSibling){
        switch( node.nodeName ) {
        case 'A':
          this.marker = undefined;
          var textContent = node.innerHTML.replace( /<[^>]*>/g, '' );
          var openThisMarker = /\bOPEN\b/.exec( textContent );
          textContent = textContent.replace( /\bOPEN\b/, '' );
          textContent = textContent.replace( /^\s*/, '' );
          textContent = textContent.replace( /\s*$/, '' );
          if( textContent == 'EXTENT' )
            this.explicitExtent = true;

          var matchll = /\Wll=([-.\d]*),([-.\d]*)/.exec( node.href );
          if( matchll ) {
            ++this.pointCount;
            var x = parseFloat( matchll[2] );
            var y = parseFloat( matchll[1] );
            var point = new GPoint( x, y );
            pointList.push( point );

            if( textContent == 'EXTENT' ) {
              this.center = point;
            }
            else {
              if( this.pointCount == 1 ) {
                this.minX = x;
                this.maxX = x;
                this.minY = y;
                this.maxY = y;
              }
              else {
                if( x < this.minX ) this.minX = x;
                if( x > this.maxX ) this.maxX = x;
                if( y < this.minY ) this.minY = y;
                if( y > this.maxY ) this.maxY = y;
              }

              var sym = null;
              var color = 'ORANGE';

              var matchcolor =
                  /\b(ORANGE|PURPLE|YELLOW|GREEN|BLUE|RED|AQUA|WHITE|GRAY)\b/
                  .exec( textContent );
              if( matchcolor )
                color = matchcolor[0];

              var matchsym = /\b([0-9A-Za-z]|HASH|DOLLAR|DOT|START|END)\b/.exec(textContent);
              if( matchsym )
                sym = matchsym[ 0 ];
              if( ! sym ) {
                if( ! drawline )
                  sym = 'DOT';
                else
                  continue;
              }

              var icon = new GIcon( defaultIcon );
			icon.iconSize = new GSize(20, 42);

              icon.image = '../map/icon/red-dot.png';
              // + color + '-' + sym + '.png';
              this.marker = new InfoMarker( this.map, point, icon );
              // We must zoom the map once before adding markers
              if( ! this.map.isLoaded() )
                this.map.centerAndZoom( point, 12 );
              this.map.addOverlay( this.marker );

              idmarkers[ node.id || node.name ] = this.marker;
              if( openThisMarker )
                markerOpener.addMarker( this.marker );
            }
          }
          else {
            this.logWarning( "No ll param for marker [" + node.innerHTML +
              ":" + (node.id || node.name) + "]" );
          }

          if( this.pointCount == 1 || textContent == 'EXTENT' ) {
            var matchspn = /\Wspn=([-.\d]*),([-.\d]*)/.exec( node.href );
            if( matchspn ) {
              this.span = {
                width:  parseFloat( matchspn[2] ),
                height: parseFloat( matchspn[1] )
              };
            }

            var matchtype = /\Wt=(.)/.exec( node.href );
            if( matchtype ) {
              switch( matchtype[1] ) {
                case 'k': this.mapType = G_SATELLITE_TYPE; break;
                case 'h': this.mapType = G_HYBRID_TYPE; break;
              }
            }
          }
          break;

        case 'DIV':
          if( ! this.marker ) {
            if( this.pointCount < 1 )
              this.logWarning( "div block given before any markers" );
            else
              this.logWarning( "Multiple div blocks given for one marker" );
            continue;
          }
          else {
            var infoClasses = wordMap( node.className );
            if( 'GMaps' in infoClasses ) {
              // infoWindow blowup
              var matchzoom = /ZOOMLEVEL([-+=]?)(\d+)/.exec( node.innerHTML );
              if( matchzoom ) {
                var num = parseInt( matchzoom[ 2 ] );
                if( matchzoom[ 1 ] == '-' )
                  this.marker.infoZoomOffset = num;
                else if( matchzoom[ 1 ] == '+' )
                  this.marker.infoZoomOffset = - num;
                else
                  this.marker.infoZoomLevel = num;
              }

              for( typeName in MapTypeTable ) {
                if( typeName in infoClasses ) {
                  this.marker.infoMapType = window[ typeName ];
                  break;
                }
              }
            }
            else {
              // infoWindow HTML
              var width = div.offsetWidth * 2 / 3;
              var html = node.outerHTML;
              if( ! html ) {
                html = '<div';
                var attrs = node.attributes;
                for( var j = 0; j < attrs.length; ++j )
                  html += ' ' + attrs[j].name + '="' + attrs[j].value + '"';
                html += '>' + node.innerHTML + '</div>';
              }
              this.marker.html = '<div style="max-width: ' + width + 'px">' +
                  html + '</div>';
            }
            GEvent.addListener( this.marker, 'click', this.marker.doOpen );
          }
          break;

        case 'LI':
          pointList.push( this.processMarkers( node, true )[ 0 ] );
          break;

        case 'OL':
          var params = { color: null, weight: null, opacity: null };
          for( var word in wordMap( node.className ) ) {
            var matchparam = /^(color|weight|opacity):(.*)$/.exec( word );
            if( matchparam )
              params[ matchparam[1] ] = matchparam[2];
          }
          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;
          }
          this.map.addOverlay( new GPolyline(
              this.processMarkers( node, true ),
              params.color,
              params.weight,
              params.opacity ) );
          break;

        case '#text':
        case '#comment':
          // ignore text and comments
          break;

        default:
          this.logWarning( "Unknown or misplaced node " + node.nodeName );
          break;
        }
      }
      return pointList;
    }

    OMap.prototype.init = function() {
      var divData = div.cloneNode( true );
      div.innerHTML = '';
      div.style.display = 'block';
      this.map = new GMap( div );

      for( var ctrl in CtrlTable ) {
        if( ctrl in this.classes ) {
          this.map.addControl( new window[ ctrl ]() );
        }
      }

      this.processMarkers( divData );

      if( ! this.center ) {
        if( this.minX == undefined ) {
          this.map.centerAndZoom( new GPoint(-13.91092300415039, 40.726185523600634), 10 );
        }
        else {
          this.center = new GPoint(
              (this.minX + this.maxX)/2,
              (this.minY + this.maxY)/2 );
          if( ! this.explicitExtent && this.pointCount != 1 ) {
            this.span = {
              width:  this.maxX - this.minX,
              height: this.maxY - this.minY
            };
          }
        }
      }

      for( typeName in MapTypeTable ) {
        if( typeName in this.classes ) {
          this.mapType = window[ typeName ];
          this.explicitExtent = true;
          break;
        }
      }

      if( this.pointCount == 1 || this.explicitExtent ) {
        if( this.mapType )
          this.map.setMapType( this.mapType );
      }

      if( this.span ) {
        var zoomLevel = this.map.spec.getLowestZoomLevel(
            this.center, this.span, this.map.viewSize );
        this.map.centerAndZoom( this.center, zoomLevel );
      }
    }

    // Find all divs marked as GMaps
    var divs = document.getElementsByTagName( 'div' );
    for( var i = 0; i < divs.length; ++i ) {
      var div = divs[ i ];
      var classes = wordMap( div.className );
      if( 'GMaps' in classes )
        new OMap( div, classes );
    }

    // Find all anchor tags linking to GMaps markers
    var anchors = document.getElementsByTagName( 'a' );
    for( var mi = 0; mi < anchors.length; ++mi ) {
      var marker = markerForUrl( anchors[ mi ].href );
      if( marker )
        GEvent.bindDom( anchors[ mi ], "click", marker, marker.doOpen );
    }

    // Examine current page location for a reference to a GMaps marker
    var marker = markerForUrl( document.location );
    if( marker )
      markerOpener.addMarker( marker );

    // Open all the markers we need to
    markerOpener.chainOpen();
  }

  function addOnLoad( func ) {
    if( window.onload ) {
      var oldfunc = window.onload;
      window.onload = function() { oldfunc(); func(); }
    }
    else {
      window.onload = func;
    }
  }
  window.addOnLoad = addOnLoad;

  addOnLoad( loadfunc );
})();