<?php
/* $Id: gmap.module,v 1.40 2006/07/08 03:49:00 webgeer Exp $ */

/**
 * @file
 * GMap Filters is a module to include Google Map in a module
 *
 * GMap filter allows the insertion of a googlemap in a module.  It has
 * a page to creat a macro and then a filter to convet the macro into the
 * html and javascript code required to insert a google map.
 */


define('GMAP_LOAD_METHOD', variable_get('gmap_method','Static'));
define('GMAP_WIDTH', variable_get('gmap_default_width', '300px'));
define('GMAP_HEIGHT', variable_get('gmap_default_height', '200px'));
define('GMAP_ZOOM', variable_get('gmap_default_zoom', 3));
define('GMAP_CONTROL', variable_get('gmap_default_control', 'Small'));
//define('GMAP_ALIGNMENT', variable_get('gmap_default_allignment', ???));
define('GMAP_LONGLAT', variable_get('gmap_default_latlong', '40,0'));
define('GMAP_TYPE', variable_get('gmap_default_type', 'Map'));
define('GMAP_LINECOLOR1', variable_get('gmap_default_line1_color', '#00cc00'));
define('GMAP_LINECOLOR2', variable_get('gmap_default_line2_color', '#ff0000'));
define('GMAP_LINECOLOR3', variable_get('gmap_default_line3_color', '#0000ff'));
//define('GMAP_XMAPS', variable_get('gmap_xmaps',0));
define ('GMAP_XMAPS',0); //xmaps not currently compatible with v2 of google maps api
define('GMAP_XMAPS_SCRIPT', variable_get('gmap_xmaps_script','misc/xmaps.1c.js'));
define('GMAP_CLUSTER', variable_get('gmap_cluster',0));

define('GMAP_API_V', '2.69');

/**
 * Implementation of hook_help.
 *
 */

function gmap_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Filter to allow insertion of a google map into a node');
  }
}

/**
 * Implementation of hook_perm.
 *
 */

function gmap_perm() {
  return array('create macro');
}

/**
 *
 * Returns the html required to insert a map from a gmap associative array.
 *
 * @param $gmap
 * An associative array with the following variables set:
 *
 *  id - the id of the map every map on a page must have a unique id
 *  width - width of the map
 *  height - height of the map
 *  center - a string of the latitude, longitude of the centre of the map
 *  zoom - the zoom factor of the google map
 *  align - the alignment of the map 'right', 'left' or 'center'
 *  control - the control shown on the map 'Large', 'Small', or 'None'
 *  tcontrol - whether the type control is on the map or not: 'off' or 'on'
 *  scontrol - whether the scale control is on the map or not: 'off' or 'on'
 *  ocontrol - width and height of the overview control map: 'width,height'
 *  type - 'Map', 'Hybrid' or 'Satellite'
 *  drag - 'yes' or 'no' map is draggable.  Default is 'yes'
 *  markers - an array of associative arrays for the markers.
 *  shapes - an array of associative arrays for the overlays.
 *  track - a file containing a series of points in .plt format to be
 *  inserted into the node.
 *  feeds - an associative array of rss feeds
 *  wmss - an associative array of WMS services
 *
 *  Xmaps must be enabled for circle and polygon to work.
 *
 * @param $javascript
 * Some javascript to insert into function after drawing the map.
 * note that '{id}' will be replaced with the map id.
 *
 * @return
 * A string with the google map ready to be inserted into a node.
 *
 */
function gmap_draw_map($gmap, $javascript='') {
  global $base_url;
    _gmap_doheader();
    
    if (file_exists('misc/gxmarker.2.js')) {
      $gxmarkerjs=1;
    }
    else {
      $gxmarkerjs=0;
    }

    $clusterset=false;
    (!isset($gmap['width'])) ? $gmap['width']=GMAP_WIDTH: NULL;
    (!isset($gmap['height'])) ? $gmap['height']=GMAP_HEIGHT:NULL;
    (!isset($gmap['zoom'])) ? $gmap['zoom']=GMAP_ZOOM:NULL;
    (!isset($gmap['autozoom'])) ? $gmap['autozoom']=false:NULL;
    (!isset($gmap['align'])) ? $gmap['align']='':NULL;
    (!isset($gmap['id'])) ? $gmap['id']='map':NULL;
    (!isset($gmap['control'])) ? $gmap['control']=GMAP_CONTROL:NULL;
    (!isset($gmap['type'])) ? $gmap['type']=GMAP_TYPE:NULL;
    (!isset($gmap['tcontrol'])) ? $gmap['tcontrol']='off':NULL;
    (!isset($gmap['scontrol'])) ? $gmap['scontrol']='off':NULL;
//    (!isset($gmap['ocontrol'])) ? $gmap['ocontrol']='off':NULL;
    (!isset($gmap['drag'])) ? $gmap['drag']='yes':NULL;

    if (!isset($gmap['center'])&& isset($gmap['latlong'])){
      //backwards compatible with macros created by previous version of this program
      $gmap['center']=$gmap['latlong'];
    }
    elseif (!isset($gmap['center'])){
      $gmap['center']=GMAP_LONGLAT;
    }

    $style='width: '.gmap_todim($gmap['width']).'; height: '.gmap_todim($gmap['height']).';';


    switch (strtolower($gmap['align'])) {
      case 'left':
        $style .= ' float: left;';
        break;
      case 'right':
        $style .= ' float: right;';
        break;
      case 'center':
      case 'centre':
        $style .= ' margin-left: auto; margin-right: auto;';
    }

//    $outtext .= "\n<!--\n\n".print_r($gmap)."\n\n -->\n";
    $outtext.='<div id="'.$gmap['id'].'" style="'.$style.'" class="gmap-map"></div>
              <script type="text/javascript">
              //<![CDATA[
              //initialize global gmap variables
              var  geocoder = null;
              var mycontrol=null;
              var mytypecontrol=null;
              var '.$gmap['id'].'=null;
              var gxmarkerjs='.$gxmarkerjs.';
              var gmapMarkerLocation="/'.variable_get('gmap_markerfiles',drupal_get_path('module', 'gmap') .'/markers').'";
              var markerlink='.variable_get('gmap_on_click',0).';
              var bounds = new GLatLngBounds();
              addLoadEvent(gmap_init);  
              addLoadEvent(gmap_load_'.$gmap['id'].');
           
              function gmap_load_'.$gmap['id'].'() {
                 '.$gmap['id'].' = new GMap2($("'.$gmap['id'].'"));
                   geocoder = new GClientGeocoder();
                 ';
                 

    if (isset($gmap['wmss'])) {
      $outtext.= '
                 // WMS layers ';
      $outtext.= '
                 '.$gmap['id'].'.getMapTypes().length = 0;';
      $ic=0;
      foreach ($gmap['wmss'] as $item) {
        $map_name = eregi_replace("[^a-z0-9_-]", "_", $item['name']);
        (!isset($item['minresolution'])) ? $item['minresolution']='1': NULL;
        (!isset($item['maxresolution'])) ? $item['maxresolution']='17': NULL;
        (!isset($item['format'])) ? $item['format']='image/gif': NULL;
        (!isset($item['merczoomlevel'])) ? $item['merczoomlevel']='5': NULL;

        $outtext.= '
                 var ccol_'.$map_name.' = new GCopyrightCollection("'.$item['name'].'");
        ';
        if(isset($item['copyrights'])){
          foreach ($item['copyrights'] as $copyright) {
            (!isset($copyright['bounds'])) ? $copyright['bounds']='-190,-90,180,90': NULL;
            (!isset($copyright['minzoom'])) ? $copyright['minzoom']='1': NULL;
            (!isset($copyright['text'])) ? $copyright['text']='': NULL;
            $coords = explode(",", $copyright['bounds']);
            $outtext.= '
                 var sw_'.$ic.' = new GLatLng('.$coords[0].','.$coords[1].',false);
                 var ne_'.$ic.' = new GLatLng('.$coords[2].','.$coords[3].',false);
                 var llb_'.$ic.' = new GLatLngBounds(sw_'.$ic.',ne_'.$ic.');
                 var cr_'.$ic.' = new GCopyright(1,llb_'.$ic.','.$copyright['minzoom'].',"'.$copyright['text'].'");
                 ccol_'.$map_name.'.addCopyright(cr_'.$ic.');
            ';
            $ic++;
          }
        }
        $outtext.= '
                 var t_'.$map_name.'= new GTileLayer(ccol_'.$map_name.','.$item['minresolution'].','.$item['maxresolution'].');
                 t_'.$map_name.'.myMercZoomLevel='.$item['merczoomlevel'].';
                 t_'.$map_name.'.myBaseURL=\''.$item['url'].'\';
                 t_'.$map_name.'.myLayers=\''.$item['layers'].'\';
                 t_'.$map_name.'.myFormat=\''.$item['format'].'\';
                 t_'.$map_name.'.getTileUrl=CustomGetTileUrl;
                 ';
          if(isset($item['overlaywith'])){
            switch (strtolower($item['overlaywith'])) {
              case 'map':
                      $outtext.= "var l_".$map_name."1=[G_NORMAL_MAP.getTileLayers()[0]]; \n                 ";
                break;
              case 'hybrid':
                      //We overlay with roads only, not with the satellite imagery
                      $outtext.= "var l_".$map_name."1=[G_HYBRID_MAP.getTileLayers()[1]]; \n                 ";
                break;
              case 'satellite':
                      $outtext.= "var l_".$map_name."1=[G_SATELLITE_MAP.getTileLayers()[0]]; \n                 ";
                break;
              default:
                      $outtext.= "var l_".$map_name."1=[]; \n                 ";
            }
          }
        $outtext.= 'var l_'.$map_name.'2=[t_'.$map_name.'];';

            switch (strtolower($item['overlaywith'])) {
              case 'hybrid':
              $outtext.= '
                 var l_'.$map_name.'=l_'.$map_name.'2.concat(l_'.$map_name.'1);';
                break;
              default:
              $outtext.= '
                 var l_'.$map_name.'=l_'.$map_name.'1.concat(l_'.$map_name.'2);';
            }

        $outtext.= '                 
                 var m_'.$map_name.' = new GMapType(l_'.$map_name.', G_SATELLITE_MAP.getProjection(), "'.$item['name'].'", G_SATELLITE_MAP); 
                 '.$gmap['id'].'.addMapType(m_'.$map_name.');
                 ';
      }
      $outtext.= '
                 '.$gmap['id'].'.addMapType(G_NORMAL_MAP);
                 '.$gmap['id'].'.addMapType(G_SATELLITE_MAP);
                 '.$gmap['id'].'.addMapType(G_HYBRID_MAP);
                 ';
    }

    $outtext.= $gmap['id'].'.setCenter(new GLatLng('.$gmap['center'].'), '.$gmap['zoom'].');
                 ';

    switch (strtolower($gmap['control'])) {
      case 'small':
        $outtext .='mycontrol=new GSmallMapControl();
                '.$gmap['id'].'.addControl(mycontrol);
                 ';
        break;
      case 'large':
      $outtext .='mycontrol=new GLargeMapControl();
              '.$gmap['id'].'.addControl(mycontrol);
              ';
    }

    if (strtolower($gmap['tcontrol'])=='on' || strtolower($gmap['tcontrol'])=='yes') {
      $outtext .= $gmap['id'].'.addControl(mytypecontrol=new GMapTypeControl());
                ';
    }
    if (strtolower($gmap['scontrol'])=='on' || strtolower($gmap['scontrol'])=='yes') {
      $outtext .= $gmap['id'].'.addControl(myscalecontrol=new GScaleControl());
                ';
    }
    if (isset($gmap['ocontrol']) && strlen($gmap['ocontrol']) > 0) {
      $osize = explode(",", $gmap['ocontrol']);
      if (intval($osize[0]) > 0 and intval($osize[1]) > 0) {
        $outtext .= $gmap['id'].'.addControl(myovcontrol=new GOverviewMapControl(new GSize('.$osize[0].','.$osize[1].')));
                ';
      }
    }

    switch (strtolower($gmap['type'])) {
      case 'hybrid':
        $outtext .= $gmap['id'].".setMapType(G_HYBRID_MAP); \n";
        break;
      case 'satellite':
        $outtext .= $gmap['id'].".setMapType(G_SATELLITE_MAP); \n";
    }
    if (isset($gmap['drag']) && strtolower($gmap['drag'])=='no') {
      $outtext .= $gmap['id'].".disableDragging(); \n";
    }
    else {
      $outtext .= "keyboardhandler=new GKeyboardHandler(".$gmap['id'].");\n";
    }

    if (isset($gmap['tracks'])) {
      foreach ($gmap['tracks'] as $value) {
        $tt=$value;
        $tt['type']='line';
        $tt['points']=array();
        if($trackFH = fopen($tt['filename'], "r")) {
          while(!feof($trackFH)) {
            $line = fgets($trackFH, 4096);
            $line_exploded = explode(",", $line);
            if(count($line_exploded) == 7) {
              $tt['points'][]= trim($line_exploded[0]).','.trim($line_exploded[1]);
            }
          }
          fclose($trackFH);
        }

        $gmap['shape'][]=$tt;
        
      }
    }

    if (isset($gmap['markers'])) {
      if (GMAP_CLUSTER) {
        $outtext .="\n var clusterer = new Clusterer(".$gmap['id'].");\n";
        $clusterset=true;
      }
      $lastmarker='';
      foreach ($gmap['markers'] as $item) {
        $mymarker='';
        $mytext='';
        if (isset($item['markername'])){
          if ($item['markername']==$lastmarker) {
            $i++;
          }
          else {
            $lastmarker=$item['markername'];
            $i=1;
          }
          if (file_exists(variable_get('gmap_markerfiles','modules/gmap/markers').'/'.$item['markername'].$i.'.png')) {
            $mymarker=$base_url.'/'.variable_get('gmap_markerfiles',drupal_get_path('module', 'gmap') .'/markers').'/'.$item['markername'].$i.'.png';
          }
          elseif (file_exists(variable_get('gmap_markerfiles','modules/gmap/markers').'/'.$item['markername'].'.png')) {
            $mymarker=$base_url.'/'.variable_get('gmap_markerfiles',drupal_get_path('module', 'gmap') .'/markers').'/'.$item['markername'].'.png';
          }
        }
        if (isset($item['text']) && strlen(trim($item['text']))>0) {
          $mytext=$item['text'];
        }
        elseif (isset($item['label']) && strlen(trim($item['label']))>0) {
          $mytext=$item['label'];
        }
        $divstyle='';
        if (isset($item['winwidth']) && trim($item['winwidth']) != '') {
          $divstyle='width: '.$item['winwidth'].'; ';
        }
        if (strlen($divstyle) >0 && strlen(trim($mytext))>0) {
          $mytext = '<div class="gmap-popup" style="'.$divstyle.'">'.$mytext.'</div>';
        }
        elseif (strlen(trim($mytext))>0){
          $mytext = '<div class="gmap-popup">'.$mytext.'</div>';
        }
        else {
          $mytext='';
        }
        if (isset($item['tooltip'])) {
          $tooltip=check_plain($item['tooltip']);
        }
        else {
          $tooltip='';
        }
        if (isset($item['link'])) {
          $link=$item['link'];
        }
        else {
          $link='';
        }
        if (GMAP_CLUSTER) {
          $outtext .='clusterer.AddMarker(createGMarker(new GLatLng('.$item['point'].'),\''.$mytext.'\',\''.$mymarker."','$tooltip','$link'));\n";
          $clusterset=true;
        }
        else {
          $outtext .=$gmap['id'].'.addOverlay(createGMarker(new GLatLng('.$item['point'].'),\''.$mytext.'\',\''.$mymarker."','$tooltip','$link'));\n";
          if($gmap['autozoom']){
            $outtext .='bounds.extend(new GLatLng('.$item['point'].'));'."\n";
          }
        }
      }
    }
    if ($gmap['autozoom']){
      $outtext .= 'var center_lat = (bounds.getNorthEast().lat() + bounds.getSouthWest().lat()) / 2.0;
                   var center_lng = (bounds.getNorthEast().lng() + bounds.getSouthWest().lng()) / 2.0;
                   var center = new GLatLng(center_lat,center_lng);
                   var zoom = '.$gmap['id'].'.getBoundsZoomLevel(bounds);
                   '.$gmap['id'].'.setCenter(center,zoom);';
    }
    if (isset($gmap['shape']) && !variable_get('gmap_xmaps',0)) {
      //if xmaps is not enabled then just show the lines using straight google maps.
      foreach ($gmap['shape'] as $value) {
        $style ='';
        if (trim($value['type'])=='line') {
          if (isset($value['color'])) {
            $style .=",'".$value['color']."'";
            if (isset($value['width'])) {
              $style .=','.$value['width'];
              if (isset($value['opacity'])) {
                $style .=','.$value['opacity'];
              }
            }
          }
          $linetxt ='new GLatLng('.implode('), new GLatLng(',$value['points']).')';
//          foreach ($value['points'] as $lvalue) {
//            if (strlen($linetxt)>0) {
//              $linetxt .= ', ';
//            }
//            $linetxt .="new GPoint(".$lvalue.")";
//          }
          $outtext .="\n".$gmap['id'].".addOverlay(new GPolyline([$linetxt] $style));";
        }
      }
    }
    elseif (isset($gmap['shape'])) {
      foreach ($gmap['shape'] as $value) {
        $linestyle=array();
        $fillstyle=array();
        if (isset($value['color'])){
          $linestyle[] = 'color: "'.$value['color'].'" ';
        }
        if (isset($value['width'])){
          $linestyle[] = 'weight: '.$value['width'];
        }
        if (isset($value['opacity'])){
          $linestyle[] = 'opacity: '.$value['opacity'];
        }
        if (isset($value['pattern'])){
          $linestyle[] = 'pattern: ['.$value['pattern'].'] ';
        }
        if (isset($value['text'])){
          $linestyle[] = 'text: "'.$value['text'].'" ';
        }
        if (isset($value['fillcolor'])){
          $fillstyle[] = 'color: "'.$value['fillcolor'].'" ';
        }
        if (isset($value['fillopacity'])){
          $fillstyle[] = 'opacity: '.$value['fillopacity'];
        }
        $outtext .= 'var lineStyle = {'.implode(',',$linestyle)."};\n";
        if (count($fillstyle)>0) {
          $outtext .= 'var fillStyle = {'.implode(',',$fillstyle)."};\n";
        }
        else {
          $outtext .= "var fillStyle = null;\n";
        }

        switch ($value['type']) {
          case 'line':
            $outtext .= "var points = []; \n";

            foreach ($value['points'] as $lvalue) {
              $outtext .="points.push(new GPoint(".$lvalue."));\n";
            }
            $outtext .= $gmap['id'].".addOverlay(new XPolyline(points, lineStyle));\n";
            break;
          case 'circle':
            //create a circle by using a radius in km
            if (empty($value['sides'])){
              $value['sides']=36;
            }
            $outtext .= 'var radius = new XDistance('.$value['radius'].",XDistance.KM);\n";
            $outtext .= 'var centre = new GPoint('.$value['center'].");\n";
            $outtext .= $gmap['id'].".addOverlay(XPolygon.createRegularPolygonFromRadius(centre, radius, ".$value['sides'].", 0, lineStyle, fillStyle));\n";
            break;
          case 'rpolygon':
            if (isset($value['sides'])){
              $value['sides']=36;
            }
            $outtext .= "var centre = new GPoint(".$value['center'].");\n";
            $outtext .= $gmap['id'].".addOverlay(XPolygon.createRegularPolygonFromPoint( new GPoint(".$value['center']."), new GPoint(".$value['point']."), 36, lineStyle, fillStyle));\n";
            break;
          case 'polygon':
            $outtext .= "var points=[];\n";
            foreach ($value['points'] as $pvalue) {
              $outtext .= "points.push(new GPoint($pvalue));\n";
            }
            $outtext .= $gmap['id'].".addOverlay(new XPolygon(points, lineStyle, fillStyle));\n";
        }
      }
    }
    if (isset($gmap['feeds'])) {
//      if (GMAP_CLUSTER && !$clusterset) {
//        $outtext .="\n var clusterer = new Clusterer(".$gmap['id'].");\n";
 //     }
      foreach ($gmap['feeds'] as $item) {
        $mymarker='';
        if (isset($item['markername'])){
          if (file_exists(variable_get('gmap_markerfiles','modules/gmap/markers').'/'.$item['markername'].'.png')) {
            $mymarker=$base_url.'/'.variable_get('gmap_markerfiles',drupal_get_path('module', 'gmap') .'/markers').'/'.$item['markername'].'.png';
          }
        }
        if (isset($item['url'])) {
          $outtext.= "parseGeoRSS(".$gmap['id'].",'".$item['url']."','$mymarker');\n";
        }
      }
    }

    if (strlen($javascript)>0) {
      $javascript=str_replace('{id}',$gmap['id'],$javascript);
      $outtext .=$javascript;
    }
    $outtext .="\n } \n   //]]>
               </script>";
    return $outtext;
}

/**
 *
 * Cleans the gmap variables to prevent javascript interjection
 *
 * @param $gmap
 * A Gmap variable
 *
 * @return
 * A GMap variable with any dangerous text removed.
 *
 * This does not really do much of anything right now.
 */
function gmap_sanitize(&$gmap){
  //sanitizes the gmap variables to reduce the possibility of javascript inserts
  foreach ($gmap as $key=>$value) {
    if ($key=='id') {
      $out = array();
      preg_match('([a-zA-Z1-9_-]*)', $value, $out);
      if (strlen($out[0])==0)  {
        $out[0]='map';
      }
      $gmap[$key]=$out[0];
    }
    else {
      $gmap[$key]=str_replace(';','',$value);
    }
  }
}

/**
 *
 * Returns a variable based on .
 *
 * @param $instring
 * A string with the settings of gmap insertion in the format var=setting|var2=setting2
 *  The possible variables are
 *  id - the id of the map every map on a page must have a unique id
 *  width - width of the map
 *  height - height of the map
 *  center - a string of the longitude and latitude of the centre of the map
 *  zoom - the zoom factor of the google map
 *  align - the alignment of the map 'right', 'left' or 'center'
 *  control - the control shown on the map 'Large', 'Small', or 'None'
 *  type - 'Map', 'Hybrid' or 'Satellite'
 *  points/markers - a string of points to mark on the map with + between
 *          each point
 *  line - the line is defined by a set of points separated by a +
 *  track - Draws a line based on the points in the .plt file
 *  The following shape types require XMaps:
 *  circle - a circle based on a center point and a radius in km separated
 *          by a + and optionally can include the number of sizes.
 *  rpolygon - a regular polygon is defined by the center point and a point
 *          on the permiter separated by a +
 *  polygon - a polygon is defined by a set of points
 *
 *  Each of the shapes types can optionally have charecteristics of colour,
 *  width, opacity, pattern, text, fill colour, fill opacity.  Pattern, text
 *  and fill are all only used by xmaps.
 *      color - hexadecimal for the colour include the '#'
 *
 *
 * @return
 * A string with the google map ready to be inserted into a node.
 *
 */
function gmap_parse_macro($instring,$ver=2) {

  if (substr(trim($instring),-1)==']') {
    $instring=substr(trim($instring), 0, -1);
  }
  if (substr($instring, 0,5)=='[gmap') {
    $instring=substr($instring, 6);
  }
  $statements=explode('|', $instring);
  $j=0;

  while (isset($statements[$j])) {
    unset($tt);
    $t=explode('=', $statements[$j],2);
    switch (trim($t[0])) {
      case 'center':
        $gmap['center']=gmap_dopoint(trim($t[1]),$ver);
        break;
      case 'zoom':
        if ($ver==1) {
          $gmap['zoom'] = 18-trim($t[1]);
          if ($gmap['zoom']<1) {
             $gmap['zoom']=1;
          }
        }
        else {
          $gmap['zoom'] = trim($t[1]);
        }
        break;
      case 'points':
      case 'markers':
        unset($markername);
        if (strpos($t[1],'::')) { // note: we don't care about case starting with ':'
          list($markername,$t[1])=explode('::',$t[1],2);
        }
        unset($ttt);
        $ttt = explode('+',$t[1]);
        for ($i =0; $i<count($ttt); $i++) {
          unset($tt);
          if (strpos($ttt[$i],':')) {
            list($tt['point'],$tt['label']) = explode(':',$ttt[$i],2);
            $tt['point']=gmap_dopoint($tt['point'],$ver);
          }
          else {
            $tt['point']=gmap_dopoint($ttt[$i],$ver);
          }
          if (isset($markername)){
            $tt['markername']=$markername;
          }
          $gmap['markers'][]=$tt;
        }
        break;

      case 'track':
        $tt['color']=GMAP_LINECOLOR1;
        if (strpos($t[1],':')) { // note: we don't care about case starting with ':'
          list($configs,$t[1])=explode(':',$t[1],2);
          list($tt['color'],$tt['width'],$tt['opacity'],$tt['pattern'],$tt['text'])=explode('/',$configs);
        }
        $tt['filename'] =$t[1];
        $gmap['tracks'][] = $tt;
        break;
        
      case 'feed':
        if (strpos($t[1],'::')) { // note: we don't care about case starting with ':'
          list($markername,$t[1])=explode('::',$t[1],2);
        }
        $tt['url'] =$t[1];
        if (isset($markername)){
          $tt['markername']=$markername;
        }
        $gmap['feeds'][] = $tt;
        break;

      case 'wmss':
        unset($ttt);
        $ttt = explode('+',$t[1]);
        for ($i =0; $i<count($ttt); $i++) {
          unset($tt);
          list($tt['name'],$tt['url'],$tt['format'],$tt['layers'],$tt['minresolution'],$tt['maxresolution'],$tt['copyrights'],$tt['overlaywith'],$tt['merczoomlevel'])=explode('::',$ttt[$i]);
          $crr = explode('/',$tt['copyrights']);
          unset($tt['copyrights']);
          for ($k =0; $k<count($crr); $k++) {
            unset($cr);
            list($cr['minzoom'],$cr['bounds'],$cr['text'])=explode(':',$crr[$k]);
            $tt['copyrights'][]=$cr;
          }
          $gmap['wmss'][]=$tt;
        }
        break;

      case 'line1':
        $tt['color']=GMAP_LINECOLOR1;
      case 'line2':
        if (empty($tt['color'])){
          $tt['color']=GMAP_LINECOLOR2;
        }
      case 'line3':
        if (empty($tt['color'])){
          $tt['color']=GMAP_LINECOLOR3;
        }
      case 'line':
        if (strpos($t[1],':')) { // note: we don't care about case starting with ':'
          list($configs,$t[1])=explode(':',$t[1],2);
          list($tt['color'],$tt['width'],$tt['opacity'],$tt['pattern'],$tt['text'])=explode('/',$configs);
        }
        $tt['points'] = explode('+',$t[1]);
        if ($ver==1) {
          foreach ($tt['points'] as $key=>$value) {
            $tt['points'][$key]=gmap_dopoint($value,$ver);
          }
        }
        $tt['type'] = 'line';
        $gmap['shape'][] = $tt;
        break;

      case 'rpolygon':
        if (strpos($t[1],':')) { // note: we don't care about case starting with ':'
          list($configs,$t[1])=explode(':',$t[1],2);
          list($tt['color'],$tt['width'],$tt['opacity'],$tt['pattern'],$tt['text'],$tt['fillcolor'],$tt['fillopacity'])=explode('/',$configs);
        }
        list($tt['center'],$tt['point'],$tt['sides']) = explode('+',$t[1]);
        $tt['type']='rpolygon';
        $gmap['shape'][] = $tt;
        break;

      case 'circle':
        if (strpos($t[1],':')) { // note: we don't care about case starting with ':'
          list($configs,$t[1])=explode(':',$t[1],2);
          list($tt['color'],$tt['width'],$tt['opacity'],$tt['pattern'],$tt['text'],$tt['fillcolor'],$tt['fillopacity'])=explode('/',$configs);
        }
        list($tt['center'],$tt['radius'],$tt['sides'])= explode('+',$t[1]);
        $tt['type']='circle';
        $gmap['shape'][] = $tt;
        break;

      case 'polygon':
        $tt['color']=GMAP_LINECOLOR1;
        if (strpos(':',$t[1])) { // note: we don't care about case starting with ':'
          list($configs,$t[1])=explode(':',$t[1],2);
          list($tt['color'],$tt['width'],$tt['opacity'],$tt['pattern'],$tt['text'],$tt['fillcolor'],$tt['fillopacity'])=explode('/',$configs);
        }
        $tt['points'] = explode('+',$t[1]);
        $tt['type']='polygon';
        $gmap['shape'][] = $tt;
        break;


      default:
        $gmap[trim($t[0])] = trim($t[1]);
      }
    $j++;
  }
  gmap_sanitize($gmap);
  return $gmap;
}


/**
 *
 * Creates a map that can be interactively used to fill a form with a
 * location (latitude, longitude and zoom level)
 *
 * @param $map
 * Either a macro to use as the base map for setting a location, or an already set map associative array.
 * @param $form
 * A formset associative array.  Cannot be more than one deep.
 * @param $fields
 * An associative array for the field names.  'latitude', 'longitude'=>name of respective array, 'address' is optional.
 * @return
 * A string with the google map code to be inserted onto the page.
 *
 */
function gmap_set_location($map,&$form,$fields) {

  if (!is_array($map)){
    $map=gmap_parse_macro($map);
  }
  $zoom=$map['zoom'];

  $form[$fields['latitude']]['#id']='gmap-latitude';
  $form[$fields['latitude']]['#attributes']=array('onchange'=>'gmap_textchange('.$map['id'].');');
  $form[$fields['longitude']]['#id']='gmap-longitude';
  $form[$fields['longitude']]['#attributes']= array('onchange'=>'gmap_textchange('.$map['id'].');');
  if (isset($fields['address'])) {
    $form[$fields['address']]['#id']='gmap-address';
    $form[$fields['address']]['#attributes']= array('onchange'=>'gmap_geocodeaddress('.$map['id'].', this.value);');
  }

  if ($lonform['#default_value']!=0 || $latform['#default_value']!=0) {
    $myjava .= '{id}.panTo(new GLatLng('.$latform['#default_value'].', '.$lonform['#default_value'].'));'."\n".
             '{id}.addOverlay(thispoint=new GMarker(new GLatLng('.$latform['#default_value'].', '.$lonform['#default_value'].')));'."\n";
  }

  $myjava .=  '   GEvent.addListener({id}, "click", function(overlay, point) {
                  {id}.checkResize();
                  if (point) {
                    {id}.zoomIn();
                    {id}.zoomIn();
                    if (thispoint) {
                      {id}.removeOverlay(thispoint);
                    }
                    {id}.addOverlay(thispoint=new GMarker(point));

                    $("gmap-latitude").value=point.lat();
                    $("gmap-longitude").value=point.lng();
                    {id}.panTo(point);
                  }
                  else {
                    {id}.removeOverlay(thispoint);
                    $("gmap-latitude").value="";
                    $("gmap-longitude").value="";
                    thispoint=null;
                  }
                });
               // GEvent.addListener({id}, "dragend", function() {{id}.checkResize();} )
              ';
  $out = gmap_draw_map($map,$myjava);
  

  return $out;
}

function _gmap_doheader(){
  static $gmap_initialized = FALSE;
  if (variable_get('gmap_method','Static')=='None' || $gmap_initialized) {
    return;
  }

  theme_add_style(drupal_get_path('module','gmap'). '/gmap.css');
  drupal_add_js('misc/drupal.js');
  drupal_add_js(drupal_get_path('module','gmap'). '/gmap.js');
  $js = '<script src="http://maps.google.com/maps?file=api&v='.GMAP_API_V.'&key='.variable_get('googlemap_api_key', '').'" type="text/javascript"></script>';
  drupal_set_html_head($js);
  if (GMAP_XMAPS) {
    drupal_add_js(variable_get('gmap_xmaps_script','misc/xmaps.1c.js'));
  }
  if (GMAP_CLUSTER){
    if (file_exists('misc/Clusterer2.js')) {
      drupal_add_js('misc/Clusterer2.js');
    }
    else {
      drupal_add_js('http://www.acme.com/javascript/Clusterer2.js');
    }
  }
  if (GMAP_WMS){
    if (file_exists('misc/wms-gs.js')) {
      drupal_add_js('misc/wms-gs.js');
    } elseif (file_exists('misc/wms236.js')) {
      drupal_add_js('misc/wms236.js');
    } elseif (file_exists('misc/wms-gs-1_0_0.js')) {
      drupal_add_js('misc/wms-gs-1_0_0.js');
    } else {
      drupal_add_js('http://dist.codehaus.org/geoserver/gmaps-geoserver_scripts/wms-gs-1_0_0.js');
    }
  }

  $gmap_initialized = TRUE;
}

function _gmap_prepare($intext) {
  $out = FALSE;
  $mapexp = '/\[gmap([^\[\]]+ )* \] /x';
  $matches = array();
  preg_match_all($mapexp, $intext, $matches);
  $i = 0;

  while (isset($matches[1][$i])) {
    $out[0][$i] = $matches[0][$i];
    if ($matches[1][$i][0]=='1'){
      $ver=1;
      $matches[1][$i]=substr($matches[0][$i],1);
    }
    else {
      $ver=2;
    }
    $out[1][$i] = gmap_draw_map(gmap_parse_macro($matches[1][$i],$ver));
    $i++;
  } // endwhile process macro
  return $out;
}

function gmap_todim($instring) {
  if (strpos($instring,'px')) {
    return intval($instring).'px';
  }
  elseif (strpos($instring,'%')) {
    return intval($instring).'%';
  }
  else {
    return intval($instring).'px';
  }
}

function gmap_dopoint($point,$ver) {
  if ($ver==1) {
    $lonlat=explode(',',$point,2);
    return $lonlat[1].','.$lonlat[0];
  }
  else {
    return $point;
  }
}

function gmap_filter($op, $delta = 0, $format = -1, $text = '') {
  switch ($op) {
    case 'list':
      return (array(0 => t('GMap filter')));

    case 'name':
      return t('Google map filter');

    case 'description':
      return t('converts a google map  macro into the html required for inserting a google map.');

    case 'process':
      $gmaps=_gmap_prepare($text);   //returns an array of $tables[0] = table macro $table[1]= table html
      if ($gmaps) {                    // there are table macros in this node
        return str_replace($gmaps[0], $gmaps[1], $text);
      }
      else {
        return $text;
      }

    case 'prepare':
      return $text;

    case 'no cache':
    
      return (variable_get('gmap_method', 'Static')=='Dynamic');
  }
}

function gmap_filter_tips($delta, $format, $long = false) {

  if(user_access('create macro')) { // only display macro if user can create one
    return t('Insert Google Map macro.').'<a href="'.url('gmapmacro').'" target="_blank" >'.t('Create a macro').'</a>';
  }
  else {
    return t('Insert Google Map macro.');
  }
}

function gmap_menu($may_cache) {
  $items=array();

  if ($may_cache) {
/*    $items[] = array(
      'path' => 'map',
      'type' => MENU_ITEM_GROUPING,
      'title' => t('google maps'),
      'access' => user_access('create macro')||user_access('show user map')||user_access('show node map'),
    );  */
    $items[] = array(
      'path' => 'map/macro',
      'type' => MENU_NORMAL_ITEM,
      'title' => t('build a macro'),
      'access' => user_access('create macro'),
      'callback' => 'gmap_macro_page',
    );
  }
  if (!$may_cache && (strlen(variable_get('googlemap_api_key', '')) > 0)) {
    if (variable_get('gmap_method', 'Static')=='Static') {
      _gmap_doheader();
    }
  }
  return $items;
}

function gmap_settings() {
  //note the same google api key variable name as in the googlemap module is used
  //note the name of the variable for center of the map is latlong although the format is actually longitude, latitude

  $form['initialization']=array(
    '#type' => 'fieldset',
    '#title' => t('Google Map Initialize'),
  );
  $form['initialization']['googlemap_api_key']=array(
    '#type' => 'textfield', 
    '#title' => t('Google map API key'),
    '#default_value'=>variable_get('googlemap_api_key', ''), 
    '#size' => 50, 
    '#maxlength' => 255,
    '#description' => t('Your personal Googlemaps API key.  You must get this for each separate website at <a href="http://www.google.com/apis/maps/">Google Map API website</a>.'),
  );
  $form['initialization']['gmap_method']=array(
    '#type' => 'select',
    '#title' => t('Google map method'),
    '#options'=> array('Static'=>t('Static'), 'Dynamic'=>t('Dynamic'), 'None'=>t('None')),
    '#default_value'=>GMAP_LOAD_METHOD,
    '#description' => t('How is the Google Map initialization script run:<br /><strong>Static</strong> - Loaded on every page, <br /><strong>Dynamic</strong> - Initialization script runs only on pages with google maps, but all pages with the gmap filter will not be cached. <br /><strong>None</strong> - Google map initialization script must be loaded some other way.'),
  );
  // xmaps not compatible with v2 of the Google Map API
  /*
  $form['initialization']['gmap_xmaps']=array(
    '#type'=>'checkbox', 
    '#title'=>t('Enable Xmaps features (circles and polygons)'), 
    '#default_value'=>GMAP_XMAPS,
    '#description'=>t('Enables XMaps features. XMaps is currently a <em>beta</em> release available from <a href="http://xmaps.busmonster.com/">xmaps.busmonser.com</a>. '),
  );
  $form['initialization']['gmap_xmaps_script']=array(
    '#type' => 'textfield', 
    '#title' => t('Location of XMaps script'), 
    '#default_value'=>GMAP_XMAPS_SCRIPT,
    '#size' => 25,
    '#maxlength' => 50,
  );
  */
  $form['initialization']['gmap_cluster']=array(
    '#type'=>'checkbox', 
    '#title'=>t('Enable clusterer.js'),
    '#default_value'=>GMAP_CLUSTER,
    '#description'=>t('Enables Jef Poskanzer\'s excellent <a href="http://www.acme.com/javascript/#Clusterer">clusterer.js</a> script. (useful for a large number of markers on a single map)'),
  );
  $form['initialization']['gmap_on_click']=array(
    '#type'=>'radios',
                                                 '#title'=>t('On Click Marker'),
                                                 '#default_value'=>variable_get('gmap_on_click',0),
                                                 '#options'=>array(t('Open info window'), t('Open link')),
    '#description'=>t('For markers that have link associated with them, it can either an info window or go directly to the link.'),
  );

  $form['defaults']=array(
    '#type' => 'fieldset',
    '#title' => t('Default map settings'),
  );
  $form['defaults']['gmap_default_width']=array(
    '#type'=>'textfield',
    '#title'=>t('Default width'),
    '#default_value'=>GMAP_WIDTH,
    '#size'=>25,
    '#maxlength'=>6,
    '#description'=>t('The default width of a Google map.'),
  );
  $form['defaults']['gmap_default_height']=array(
    '#type'=>'textfield',
    '#title'=>t('Default height'),
    '#default_value'=>GMAP_HEIGHT,
    '#size'=>25,
    '#maxlength'=>6,
    '#description'=>t('The default height of a Google map.'),
  );
  $form['defaults']['gmap_default_latlong']=array(
    '#type'=>'textfield',
                                                  '#title'=>t('Default center'),
                                                  '#default_value'=>GMAP_LONGLAT,
                                                  '#description'=>'The decimal latitude,longitude of the centre of the map.  The "." is used for decimal, and "," is used to separate latitude and longitude.',
    '#size'=>50,
    '#maxlength'=>255,
    '#description'=>t('The default longitude, latitude of a Google map.'),
  );
  $form['defaults']['gmap_default_zoom']=array(
    '#type'=>'select',
    '#title'=>t('Default zoom'),
    '#default_value'=>GMAP_ZOOM,
    '#options' => drupal_map_assoc(range(0, 17)),
    '#description'=>t('The default zoom level of a Google map.'),
  );
  $form['defaults']['gmap_default_control']=array(
    '#type'=>'select',
    '#title'=>t('Default control type'),
    '#default_value'=>GMAP_CONTROL,
    '#options'=>array('None'=>t('None'),'Small'=>t('Small'),'Large'=>t('Large')),
  );
  $form['defaults']['gmap_default_type']=array(
    '#type'=>'select',
    '#title'=>t('Default map type'),
    '#default_value'=>GMAP_TYPE,
    '#options'=>array('Map'=>t('Map'),'Satellite'=>t('Satellite'),'Hybrid'=>t('Hybrid')),
  );
  $form['defaults']['gmap_default_line1_color']=array(
    '#type'=>'textfield',
    '#title'=>t('Default Line 1 Color'),
    '#default_value'=>GMAP_LINECOLOR1,
    '#size'=>25,
    '#maxlength'=>7 ,
  );
  $form['defaults']['gmap_default_line2_color']=array(
    '#type'=>'textfield',
    '#title'=>t('Default Line 2 Color'),
    '#default_value'=>GMAP_LINECOLOR2,
    '#size'=>25,
    '#maxlength'=>7 ,
  );
  $form['defaults']['gmap_default_line3_color']=array(
    '#type'=>'textfield',
    '#title'=>t('Default Line 3 Color'),
    '#default_value'=>GMAP_LINECOLOR3,
    '#size'=>25,
    '#maxlength'=>7 ,
  );

  $form['location']=array(
    '#type' => 'fieldset',
    '#title' => t('Location settings'),
  );
  $form['location']['gmap_location_map']=array(
    '#type'=>'textfield',
                                           '#title'=>t('Default location map'),
                                           '#default_value'=>variable_get('gmap_location_map','[gmap|id=locmap|center=40,0|zoom=3|width=100%|height=400px]'),
                                           '#size'=>50,
    '#description'=>t('The gmap macro for the map to be used in the location.module for setting latitude and longitude.'),
  );
  return $form;
}

function gmap_macro_page() {
  drupal_add_js(drupal_get_path('module','gmap'). '/gmapmacro.js');
  _gmap_doheader();
  return gmap_macro_form();
}

function gmap_macro_form() {
        $form['macroform'] = array(
    '#type' => 'fieldset',
    '#title' => t('Gmap macro creation'),
    '#theme' => 'gmap_macro',
  );
  $linecolors = array(GMAP_LINECOLOR1, GMAP_LINECOLOR2, GMAP_LINECOLOR3); 
  $form['macroform']['mapdiv']=array('#type'=>'markup','#value'=>'<div id="map" style="width: '.GMAP_WIDTH.'; height: '.GMAP_HEIGHT.';"></div>');
  $form['macroform']['javascript'] = array(
    '#value' => drupal_call_js('gmap_set_line_colors', $linecolors),
  );
  $form['macroform']['mapid'] = array(
    '#type' => 'textfield',
    '#id' => 'gmap-mapid',
    '#title' => t('Map id attribute'),
    '#default_value' => variable_get('gmap_default_mapid', 'map'),
    '#attributes' => array('onchange' => 'set_gmap_mapid(this.value)'),
  );
  $form['macroform']['maptype'] = array(
    '#type' => 'select', 
    '#id' => 'gmap-maptype',
    '#title' => t('Map type'), 
    '#options' => drupal_map_assoc(array('Map', 'Satellite', 'Hybrid')), 
    '#default_value' => GMAP_TYPE,
    '#required' => FALSE,
    '#attributes' => array('onchange' => 'set_gmap_type(this.value);'),
  );
  $form['macroform']['controltype'] = array(
    '#type' => 'select', 
    '#id' => 'gmap-controltype',
    '#title' => t('Controls'), 
    '#options' => drupal_map_assoc(array('None', 'Small', 'Large')), 
    '#required' => FALSE,
    '#default_value' => GMAP_CONTROL,
    '#attributes' => array('onchange' => 'set_control_type(this.value);')
  );  
  $form['macroform']['latlong'] = array(
    '#type' => 'textfield',
    '#id' => 'gmap-latlong',
    '#title' => t('The Latitude and Longitude of the centre of the map'),
    '#default_value' => GMAP_LONGLAT,
    '#size' => 50,
    '#attributes' => array('onchange' => 'set_gmap_latlong(this.value);'),
  );
  $form['macroform']['width'] = array(
    '#type' => 'textfield',
    '#id' => 'gmap-width',
    '#title' => t('Map width'),
    '#default_value' => GMAP_WIDTH,
    '#size' => 25,
    '#attributes' => array('onchange' => 'set_gmap_dimension(this, \'width\');'),
  ); 
  $form['macroform']['height'] = array(
    '#type' => 'textfield',
    '#title' => t('Map height'),
    '#id' => 'gmap-height',
    '#default_value' => GMAP_HEIGHT,
    '#size' => 25,
    '#attributes' => array('onchange' => 'set_gmap_dimension(this, \'height\');'),
  ); 
  $form['macroform']['alignment'] = array(
    '#type' => 'select', 
    '#id' => 'gmap-alignment',
    '#title' => t('Alignment'), 
    '#options' => drupal_map_assoc(array('None', 'Right', 'Left', 'Center')), 
//    '#default_value' => GMAP_ALIGNMENT,
    '#required' => FALSE,
    '#attributes' => array('onchange' => 'set_gmap_alignment(this.value);')
  );  
  $form['macroform']['clicktype'] = array(
    '#type' => 'select', 
    '#id' => 'gmap-clicktype',
    '#title' => t('What happens when you click on the map'), 
    '#options' => drupal_map_assoc(array('Points', 'Line1', 'Line2', 'Line3')), 
    '#required' => FALSE,
    '#default_value' => 'Points',
//    '#attributes' => array('onchange' => 'docontrol(this.value);')
  );    
  $form['macroform']['zoom'] = array(
    '#type' => 'select', 
    '#id' => 'gmap-zoom',
    '#title' => t('The current magnification of the map'),
    '#default_value' => GMAP_ZOOM,
    '#options' => drupal_map_assoc(range(0, 17)),
    '#attributes' => array('onchange' => 'setgmapZoom(this.value);'),
  );
  $form['macroform']['textarea'] = array(
    '#type' => 'textarea',
    '#id' => 'gmap-macrotext',
    '#title' => t('Macro text'),
  );
  return drupal_get_form('macroform', $form);
}

/**
 * Implementation of hook_views_style_plugins()
 */
function gmap_views_style_plugins() {
  return array(
    'gmap' => array(
      'name' => t('Gmap View'),
      'theme' => 'views_view_gmap',
      'needs_fields' => true,
      'validate' => 'views_ui_plugin_validate_list',
  ));
}

function gmap_get_markers(){

  static $markers='';
  if (!is_array($markers)){
    $markers=array();
    $markerdir=variable_get('gmap_markerfiles',drupal_get_path('module', 'gmap').'/markers');
    // Retrieve and sort a list of available markers
    $marker_images = file_scan_directory($markerdir, '.*\.png$',array('shadow.png','.','..'),0,true, 'filename');
    foreach ($marker_images as $marker) {
      preg_match('/markers\/([a-z]*\/*[a-zA-Z_-]+)\d*\.png/',$marker->filename,$matches);
      if ($matches) {
        //$thisfile=substr($marker->filename,strlen($markerdir)+1,-4);
        $markers[$matches[1]] = $matches[1];
      }
    }
    asort($markers);
  }
  return $markers;
}

/**
 * Display the nodes of a view in a Google Map
 */
function theme_views_view_gmap($view, $nodes) {
  $fields = _views_get_fields();
  foreach ($nodes as $node) {
    $node_data = node_load(array("nid"=>$node->nid));
    $location = $node_data->location;
    if (strlen($location['lat'])>0 && strlen($location['lon']>0)) {
      $newmarker['label'] = '';
      foreach ($view->field as $field) {
        $newmarker['label'] .= "<p>" . views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $node) . "</p>";
      }
      $newmarker['point']= $location['lat'] . ',' . $location['lon'];
      $newmarker['markername']='drupal_view';
      $thismap['markers'][]= $newmarker;
    }
  }
  $output .= gmap_draw_map($thismap);
  return $output;
}

