Skip to content
Snippets Groups Projects
gmap.module 43.4 KiB
Newer Older
<?php
// $Id$

/**
 * @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.
 */

// Current minimum version: 2.113
// Minimum version last changed on: June 9 2008
// Reason: G_SATELLITE_3D_MAP support in gmap_addons. See http://code.google.com/apis/earth/.
//
// See http://groups.google.com/group/Google-Maps-API/web/api-version-changes
// for details on using other version numbers.
define('GMAP_API_VERSION', '2.115');

/**
 * Get the defaults for a gmap.
 */
function gmap_defaults() {
  $defaults = array(
    'width' => '300px',
    'height' => '200px',
    'zoom' => 3,
    'maxzoom' => 14,
    'controltype' => 'Small',
    'align' => 'None',
    'latlong' => '40,0',
    'maptype' => 'Map',
    'mtc' => 'standard',
    'baselayers' => array('Map', 'Satellite', 'Hybrid'),
    'styles' => array(
      'line_default' => array('0000ff', 5, 45, '', 0, 0),
      'poly_default' => array('000000', 3, 25, 'ff0000', 45),
    ),
    'line_colors' => array('#00cc00', '#ff0000', '#0000ff'),
  );
  $defaults['behavior'] = array();
  $m = array();
  $behaviors = module_invoke_all('gmap', 'behaviors', $m);
  foreach ($behaviors as $k => $v) {
    $defaults['behavior'][$k] = $v['default'];
  }
  $defaults = array_merge($defaults, variable_get('gmap_default', array()));
  return $defaults;
}

/**
 * Implementation of hook_theme().
 */
function gmap_theme() {
  return array(
    'views_view_gmap' => array('arguments' => array('element')),
    'gmap_views_marker_label' => array('arguments' => array('element')),
    'gmap_marker_popup' => array('arguments' => array('label')),
    'gmap_overlay_edit' => array('arguments' => array('element')),
    'gmap_macrotext' => array('arguments' => array('element')),
    'gmap_dimension' => array('arguments' =>  array('element')),
    'gmap_address' => array('arguments' => array('element')),
    'gmap_align' => array('arguments' => array('element')),
Brandon Bergren's avatar
Brandon Bergren committed
    'gmap_style' => array('arguments' => array('element')),
/**
 * Implementation of hook_gmap().
 */
function gmap_gmap($op, &$map) {
  switch ($op) {
    case 'macro':
      return array(
        'points' => array(
          'multiple' => TRUE,
        ),
        'markers' => array(
          'multiple' => TRUE,
        ),
        'feed' => array(
          'multiple' => TRUE,
        ),
        'style' => array(
          'multiple' => TRUE,
        ),
      );
    case 'pre_theme_map':
      $path = drupal_get_path('module', 'gmap') .'/js/';
      // Activate markers if needed.
Brandon Bergren's avatar
Brandon Bergren committed
      if ((isset($map['behavior']['dynmarkers']) && $map['behavior']['dynmarkers']) || !empty($map['markers'])) {
        static $header_set = FALSE;
        if (!$header_set) {
          $header_set = TRUE;
          // If the user is using private download method, it's up to them to get the path set up.
          if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) {
            $markerpath = variable_get('gmap_private_markerfile', '');
            if (empty($markerpath) || !file_exists($markerpath)) {
              drupal_set_message(t('GMap marker file settings are not configured properly for Private download method, markers will not work!'), 'error');
            }
            else {
              drupal_add_js($markerpath, 'module', 'header', FALSE, TRUE, FALSE);
            }
          }
          // With public method, we can handle all bookkeeping ourselves.
          else {
            $markerpath = file_create_path('js');
            if (!$markerpath || !file_exists("$markerpath/gmap_markers.js")) {
              gmap_regenerate_markers();
              $markerpath = file_create_path('js');
            }
            drupal_add_js("$markerpath/gmap_markers.js", 'module', 'header', FALSE, TRUE, FALSE);
        drupal_add_js($path .'icon.js');
        drupal_add_js($path .'marker.js');
        drupal_add_js($path . variable_get('gmap_mm_type', 'gmap') .'_marker.js');
      }
Brandon Bergren's avatar
Brandon Bergren committed
      if (isset($map['behavior']['locpick']) && $map['behavior']['locpick']) {
        drupal_add_js($path .'locpick.js');
      }
Brandon Bergren's avatar
Brandon Bergren committed
      if (!empty($map['markers']) || !empty($map['lines'])) {
        drupal_add_js($path .'markerloader_static.js');
      }
Brandon Bergren's avatar
Brandon Bergren committed
      if (!empty($map['shapes'])) {
        drupal_add_js($path .'shapeloader_static.js');
        drupal_add_js($path .'gmap_shapes.js');
      }
Brandon Bergren's avatar
Brandon Bergren committed
      if (isset($map['feed']) && is_array($map['feed'])) {
        drupal_add_js($path .'markerloader_georss.js');
      }
      break;
    case 'macro_multiple':
      return array('points', 'markers', 'feed', 'circle', 'rpolygon', 'polygon', 'line', 'style');
    case 'behaviors':
      return array(
        'locpick' => array(
          'title' => t('Location chooser'),
          'default' => FALSE,
          'help' => t('Used to activate location choosing using a gmap.'),
          'internal' => TRUE,
        ),
        'nodrag' => array(
          'title' => t('Disable dragging'),
          'default' => FALSE,
          'help' => t('Remove the ability for the user to drag the map. If dragging is disabled, keyboard shortcuts are implicitly disabled.'),
        ),
        'nokeyboard' => array(
          'title' => t('Disable keyboard'),
          'default' => TRUE,
          'help' => t('Disable the keyboard shortcuts.'),
        ),
        'nomousezoom' => array(
          'title' => t('Disable mousezoom'),
          'default' => FALSE,
          'help' => t('Disable using the scroll wheel to zoom the map.'),
        ),
        'nocontzoom' => array(
          'title' => t('Disable Continuous Zoom'),
          'default' => FALSE,
          'help' => t('Disable dynamically resizing images while waiting for tiles to load when zooming.'),
        ),
        'autozoom' => array(
          'title' => t('Use AutoZoom'),
          'default' => FALSE,
          'help' => t('Automatically zoom the map to fit all markers when markers are added.'),
        ),
        'dynmarkers' => array(
          'title' => t('Unconditionally enable marker interface'),
          'default' => FALSE,
          'help' => t('Load the marker loader system even if no markers to load are detected. Useful if you are injecting markers from somewhere else.'),
        ),
        'overview' => array(
          'title' => t('Enable Overview Map'),
          'default' => FALSE,
          'help' => t('Enable the "overview map" in the bottom right corner.'),
          'previewable' => TRUE,
        ),
/*        'notype' => array(
          'title' => t('Disable map type control'),
          'default' => FALSE,
          'help' => t('Removes the map type control from the upper right corner. Recommended for very narrow maps.'),
          'previewable' => TRUE,
        ), */
        'collapsehack' => array(
          'title' => t('Work around bugs when maps appear in collapsible fieldsets'),
          'default' => FALSE,
          'help' => t('Enabling this will work around some issues that can occur when maps appear inside collapsible fieldsets.'),
        ),
        // Note to myself, who keeps forgetting what a scale control actually IS.:
        // |------------  1mi ------------|
        'scale' => array(
          'title' => t('Add scale control to map.'),
          'default' => FALSE,
          'help' => t('Adds a scale control to the map in the default position.'),
          'previewable' => TRUE,
        ),
      );
      break;

    case 'baselayers':
      $map['Google']['Map'] = array(
        'title' => t('Map: Standard street map.'),
        'default' => TRUE,
        'help' => t('The standard default street map. Internal name: G_NORMAL_MAP'),
      );
      $map['Google']['Satellite'] = array(
        'title' => t('Satellite: Standard satellite map.'),
        'default' => TRUE,
        'help' => t('Satellite view without street overlay. Internal name: G_SATELLITE_MAP'),
      );
      $map['Google']['Hybrid'] = array(
        'title' => t('Hybrid: Hybrid satellite map.'),
        'default' => TRUE,
        'help' => t('Satellite view with street overlay. Internal name: G_HYBRID_MAP'),
      );
      $map['Google']['Physical'] = array(
        'title' => t('Terrain: Physical feature map.'),
        'default' => FALSE,
        'help' => t('Map with physical data (terrain, vegetation.) Internal name: G_PHYSICAL_MAP'),
      );
      break;
  }
}

/**
 * Set up the HTML header for GMap.
 */
function _gmap_doheader() {
  static $gmap_initialized = FALSE;
  if ($gmap_initialized) {
    return;
  }
  $gmap_path = drupal_get_path('module', 'gmap');
  drupal_add_css($gmap_path .'/gmap.css');
  drupal_add_js($gmap_path .'/js/gmap.js');
  $mm = variable_get('gmap_mm_type', 'gmap');
  if ($mm=='clusterer') {
    drupal_add_js($gmap_path .'/js/icon.js');
    drupal_add_js($gmap_path .'/thirdparty/Clusterer2.js');
  }
  drupal_add_js($gmap_path .'/js/marker.js');
  drupal_add_js($gmap_path .'/js/'. $mm .'_marker.js');
  $mms = variable_get('gmap_markermanager', array());
  if (empty($mms[$mm])) {
    $mms[$mm] = array();
  }
  drupal_add_js(array('gmap_markermanager' => $mms[$mm]), 'setting');
// @@@
drupal_add_js($gmap_path .'/js/poly.js');

  global $language;
  $query = array(
    'file' => 'api',
    'v' => variable_get('gmap_api_version', GMAP_API_VERSION),
    'key' => gmap_get_key(),
    'hl' => $language->language,
  );
  drupal_set_html_head('<script src="'. check_url(url('http://maps.google.com/maps', array('query' => $query))) .'" type="text/javascript"></script>');

  $gmap_initialized = TRUE;
}

/**
 * Parse a macro style definition.
 * Example: #111111/1/100/#111111/1
 */
function _gmap_parse_style($style) {
  if (strpos($style, '/') === FALSE) {
    // Style ref.
    return $style;
  }
  $styles = explode('/', $style);

  // @@@ Todo: Fix up old xmaps stuff. Possibly detect by looking for array length 7?

  // Strip off # signs, they get added by code.
  if (isset($styles[0]) && substr($styles[0], 0, 1) == '#') {
    $styles[0] = substr($styles[0], 1);
  }
  if (isset($styles[3]) && substr($styles[3], 0, 1) == '#') {
    $styles[3] = substr($styles[3], 1);
  }

  // Assume anything > 0 and < 1.1 was an old representation.
  if ($styles[2] > 0 && $styles[2] < 1.1) {
    $styles[2] = $styles[2] * 100;
  }
  if (isset($styles[4])) {
    if ($styles[4] > 0 && $styles[4] < 1.1) {
      $styles[4] = $styles[4] * 100;
    }
  }

  return $styles;
}

/**
 * Convert a macro string into a GMap array.
 *
 * @param $instring
 *   Macro to process.
 * @param $ver
 *   Version to treat macro as.
 *   Set to 1 when processing very old macros, otherwise leave as is.
 * @return
 *   A GMap array.
 */
function gmap_parse_macro($instring, $ver = 2) {
  // Get a list of keys that are "multiple."
  $m = array();
  $multiple = module_invoke_all('gmap', 'macro_multiple', $m);

  // Remove leading and trailing tags
  if (substr(trim($instring), -1)==']') {
    $instring = substr(trim($instring), 0, -1);
  }
  if (substr($instring, 0, 5)=='[gmap') {
    $instring = substr($instring, 6);
  }

  // Chop the macro into an array
  $temp = explode('|', $instring);
  $m = array();
  foreach ($temp as $row) {
    $offset = strpos($row, '=');
    if ($offset !== FALSE) {
      $k = trim(substr($row, 0, $offset));
      $r = trim(substr($row, $offset+1));
      if (in_array($k, $multiple)) {
        // Things that can appear multiple times
        if (!isset($m[$k])) {
          $m[$k] = array();
        }
        $m[$k][] = $r;
      }
      else {
        $m[$k] = $r;
      }
    }
  }

  // Synonyms
    $m['maptype'] = $m['type'];
    unset($m['type']);
  }
Brandon Bergren's avatar
Brandon Bergren committed
  if (isset($m['control'])) {
    $m['controltype'] = $m['control'];
    unset($m['control']);
  }

  if (isset($m['feed']) && is_array($m['feed'])) {
    foreach ($m['feed'] as $k => $v) {
      $temp = explode('::', $v);
      // Normalize url
      if (substr($temp[1], 0, 1) == '/') {
        $temp[1] = substr($temp[1], 1);
      }
      $temp[1] = url($temp[1]);
      $m['feed'][$k] = array(
        'markername' => $temp[0],
        'url' => $temp[1],
      );
    }
  }

  // Add custom styles.
  if (isset($m['style']) && is_array($m['style'])) {
    foreach ($m['style'] as $k => $v) {
      $temp = explode(':', $v);
      $m['styles'][$temp[0]] = _gmap_parse_style($temp[1]);
    }
    unset($m['style']);
  }

  // Merge points and markers
  if (!isset($m['points']) || !is_array($m['points'])) $m['points'] = array();
  if (!isset($m['markers']) || !is_array($m['markers'])) $m['markers'] = array();
  $m['markers-temp'] = array_merge($m['points'], $m['markers']);
  unset($m['points']);
  unset($m['markers']);

  // all shapes in 1 array
  if (isset($m['circle']) && is_array($m['circle'])) {
    foreach ($m['circle'] as $shape) {
      $s = array('type' => 'circle');
      $cp = strpos($shape, ':');
      if ($cp !== FALSE) {
        $stylestr = substr($shape, 0, $cp);
        $s['style'] = _gmap_parse_style($stylestr);
        $shape = substr($shape, $cp+1);
      }
      $tmp = explode('+', $shape);
      $s['radius'] = $tmp[1] ? $tmp[1] : 100;
      if ($tmp[2]) $s['numpoints'] = trim($tmp[2]);
      $tmp = _gmap_str2coord($tmp[0]);
      $s['center'] = $tmp[0];
      $m['shapes'][] = $s;
    }
    unset($m['circle']);
  }
  // Fixup legacy lines.
  if (isset($m['line1'])) {
    $def = gmap_defaults();
    if (!isset($m['line'])) $m['line'] = array();
    $m['line'][] = $def['line_colors'][0] .':'. $m['line1'];
    unset($m['line1']);
  }
  if (isset($m['line2'])) {
    $def = gmap_defaults();
    if (!isset($m['line'])) $m['line'] = array();
    $m['line'][] = $def['line_colors'][1] .':'. $m['line3'];
    unset($m['line2']);
  }
  if (isset($m['line3'])) {
    $def = gmap_defaults();
    if (!isset($m['line'])) $m['line'] = array();
    $m['line'][] = $def['line_colors'][2] .':'. $m['line3'];
    unset($m['line3']);
  }

  if (isset($m['line']) && is_array($m['line'])) {
    foreach ($m['line'] as $shape) {
      $s = array('type' => 'line');
      $cp = strpos($shape, ':');
      if ($cp != FALSE) {
        $stylestr = substr($shape, 0, $cp);
        $s['style'] = _gmap_parse_style($stylestr);
        $shape = substr($shape, $cp+1);
      }
      $s['points'] = _gmap_str2coord($shape);
      $m['shapes'][] = $s;
    }
    unset($m['line']);
  }
  if (isset($m['rpolygon']) && is_array($m['rpolygon'])) {
    foreach ($m['rpolygon'] as $shape) {
      $s = array('type' => 'rpolygon');
      $cp = strpos($shape, ':');
      if ($cp !== FALSE) {
        $stylestr = substr($shape, 0, $cp);
        $s['style'] = _gmap_parse_style($stylestr);
        $shape = substr($shape, $cp+1);
      }
      $tmp = explode('+', $shape);
      if ($tmp[2]) {
        $s['numpoints'] = (int)trim($tmp[2]);
        $tmp = array_slice($tmp, 0, 2);
      }
      $shape = implode('+', $tmp);
      $tmp = _gmap_str2coord($shape);
      $s['center'] = $tmp[0];
      $s['point2'] = $tmp[1];
      $m['shapes'][] = $s;
    }
    unset($m['rpolygon']);
  }
  if (isset($m['polygon']) && is_array($m['polygon'])) {
    foreach ($m['polygon'] as $shape) {
      $s = array('type' => 'polygon');
      $cp = strpos($shape, ':');
      if ($cp !== FALSE) {
        $stylestr = substr($shape, 0, $cp);
        $s['style'] = _gmap_parse_style($stylestr);
        $shape = substr($shape, $cp+1);
      }
      $s['points'] = _gmap_str2coord($shape);
      $m['shapes'][] = $s;
    }
    unset($m['polygon']);
  }

  // Version 1 -> 2 conversion
  if ($ver == 1) {
    // Zoom is flipped
      $m['zoom'] = 18 - $m['zoom'];
      if ($m['zoom'] < 1) {
        $m['zoom'] = 1;
      }
    }
  }

  // Center -> latitude and longitude
  if (isset($m['center']) && $m['center']) {
    list($m['latitude'], $m['longitude']) = explode(',', $m['center']);
    unset($m['center']);
  }

  // Behavior
  if (isset($m['behaviour'])) {
    $m['behavior'] = $m['behaviour'];
    unset($m['behaviour']);
  }
  if (isset($m['behavior'])) {
    $sep = ' ';
    if (strpos($m['behavior'], ',') !== FALSE) {
      // In some places, commas were used to seperate behaviors.
      // This was originally an accident, but it's easy enough to support.
      $sep = ',';
    }
    $m['behavior-temp'] = explode($sep, $m['behavior']);
    $m['behavior'] = array();
    foreach ($m['behavior-temp'] as $v) {
      $m['behavior'][substr($v, 1)] = (substr($v, 0, 1) == '+') ? TRUE : FALSE;
    }
    unset($m['behavior-temp']);
  }

  // tcontrol now is mtc.
  if (isset($m['tcontrol'])) {
    if (strtolower(trim($m['tcontrol'])) == 'on') {
      $m['mtc'] = 'standard';
    }
    else {
      $m['mtc'] = 'none';
    }
    unset($m['tcontrol']);
  }

  // notype also controls mtc.
  if (isset($m['behavior']['notype'])) {
    if ($m['behavior']['notype']) {
      $m['mtc']= 'none';
    }
    unset($m['behavior']['notype']);
  }

  // Stuff that was converted to behavior flags

  // Scale control.
  if (isset($m['scontrol'])) {
    if (strtolower(trim($m['scontrol'])) == 'on') {
      $m['behavior']['scale'] = TRUE;
    }
    else {
      $m['behavior']['scale'] = FALSE;
    }
    unset($m['scontrol']);
  }

  // Draggability.
    if (strtolower(trim($m['drag'])) == 'yes') {
      $m['behavior']['nodrag'] = FALSE;
    }
    else {
      $m['behavior']['nodrag'] = TRUE;
    }
    unset($m['drag']);
  }

  // Markers fixup
  foreach ($m['markers-temp'] as $t) {
    unset($markername);
    // Named?
    if (strpos($t, '::')) { // Single : gets handled below.
      list($markername, $t) = explode('::', $t, 2);
    }
    // Break down into points
    $points = explode('+', $t);
    $offset = -1;
    foreach ($points as $point) {
      $marker = array();
      $offset++;
      $marker['options'] = array();
      // Labelled?
      // @@@ Gmap allows both a tooltip and a popup, how to represent?
      if (strpos($point, ':')) {
        list($point, $marker['text']) = explode(':', $point, 2);
        $marker['text'] = theme('gmap_marker_popup', $marker['text']);
      }
      if (strpos($point, '%')) {
        list($point, $addons) = explode('%', $point, 2);
        $motemp = explode('%', $addons);
        foreach ($motemp as $option) {
          $marker['options'][trim($option)] = TRUE;
        }
      }
      list($marker['latitude'], $marker['longitude']) = explode(',', $point, 2);
      // Named markers get an offset too.
      if (isset($markername)) {
        $marker['markername'] = $markername;
        $marker['offset'] = $offset;
      }
      $m['markers'][] = $marker;
    }
  }
  unset($m['markers-temp']);

  // Assign an id if one wasn't specified.
    $m['id'] = gmap_get_auto_mapid();
  }

  // The macro can now be manipulated by reference.
  foreach (module_implements('gmap') as $module) {
    $additions = call_user_func_array($module .'_gmap', array('parse_macro', &$m));
    if (!empty($additions)) {
      foreach ($additions as $k => $v) {
        $m[$k] = $v;
      }
    }
  }
  return $m;
}

/**
 * Parse "x.xxxxx , y.yyyyyy (+ x.xxxxx, y.yyyyy ...)" into an array of points.
 */
function _gmap_str2coord($str) {
  // Explode along + axis
  $arr = explode('+', $str);
  // Explode along , axis
  $points = array();
  foreach ($arr as $pt) {
    list($lat, $lon) = explode(',', $pt);
    $points[] = array((float)trim($lat), (float)trim($lon));
  }
  return $points;
}

/**
 * Theme a marker popup.
 * This will get called for markers embedded in macros.
 */
function theme_gmap_marker_popup($label) {
  return $label;
}

/**
 * Location chooser utility function.
 *
 * Creates a map that can be interactively used to fill a form with a
 * location (latitude, longitude and zoom level).
 *
 * Note: This is a utility function designed for location.module, there is no
 * guarantee it will not be removed eventually.
 *
 * @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) {
  static $ctr = 0;
  $ctr++;
  if (!is_array($map)) {
    $map = array_merge(gmap_defaults(), gmap_parse_macro($map));
  }
  $id = 'loc'. $ctr;
  $map['id'] = $id;

  // This is a locpick map.
  $map['behavior']['locpick'] = TRUE;

  $element = array(
    '#type' => 'gmap',
    '#map' => $map['id'],
    '#settings' => $map,
  );

  $form[$fields['latitude']]['#map']=$id;
  gmap_widget_setup($form[$fields['latitude']], 'locpick_latitude');

  $form[$fields['longitude']]['#map']=$id;
  gmap_widget_setup($form[$fields['longitude']], 'locpick_longitude');

  if (isset($fields['address'])) {
    $form[$fields['address']]['#map'] = $id;
    gmap_widget_setup($form[$fields['address']], 'locpick_address');
  }
  return theme('gmap', $element);
}

/**
 * Handle filter preparation.
 */
function _gmap_prepare($intext) {
  $out = FALSE;
  $matches = array();
  preg_match_all('/\[gmap([^\[\]]+ )* \] /x', $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;
    }
    $map = array('#settings' => gmap_parse_macro($matches[1][$i], $ver));
    $out[1][$i] = theme('gmap', $map);
    $i++;
  } // endwhile process macro
  return $out;
}

/**
 * Make sure a string is a valid css dimension.
 */
function gmap_todim($instring) {
Brandon Bergren's avatar
Brandon Bergren committed
  if (!is_string($instring)) {
    return FALSE;
  }
  $s = strtolower(trim($instring));
  $matches = array();
Brandon Bergren's avatar
Brandon Bergren committed
  if (preg_match('/^([\d.]+)\s*(em|ex|px|in|cm|mm|pt|pc|%)$/', $s, $matches)) {
    return $matches[1] . $matches[2];
  }
  else {
    return FALSE;
  }
}

/**
 * Ensure a textfield is a valid css dimension string.
 */
Brandon Bergren's avatar
Brandon Bergren committed
function gmap_dimension_validate(&$elem, &$form_state) {
  $value = gmap_todim($elem['#value']);
  if ($value) {
    // Normalize the css dimension string.
    form_set_value($elem, $value, $form_state);
  }
  else {
    form_error($elem, t('The specified value is not a valid CSS dimension.'));
  }
}

/**
 * Implementation of hook_filter().
 */
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 TRUE; // @@@ Possibly improve efficiency in the future?
  }
}

/**
 * Implementation of hook_filter_tips().
 */
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('map/macro') .'" target="_blank" >'. t('Create a macro') .'</a>';
  }
  else {
    return t('Insert Google Map macro.');
  }
}

/**
 * Implementation of hook_menu().
 */
function gmap_menu() {
  $items['admin/settings/gmap'] = array(
    'title' => 'GMap',
    'description' => 'Configure GMap settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('gmap_admin_settings'),
    'file' => 'gmap_settings_ui.inc',
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
 * Regenerate the markerdata file.
function gmap_regenerate_markers() {
  $contents = '';
  // Create the js/ within the files folder.
  $jspath = file_create_path('js');
  file_check_directory($jspath, FILE_CREATE_DIRECTORY);
  $contents .= "// GMap marker image data.\n";
  $contents .= "Drupal.gmap.iconpath = ". drupal_to_js(base_path() . drupal_get_path('module', 'gmap') .'/markers') .";\n";
  $contents .= "Drupal.gmap.icondata = ". drupal_to_js(gmap_get_icondata()) .";\n";

  file_save_data($contents, "$jspath/gmap_markers.js", FILE_EXISTS_REPLACE);
}

/**
 * Implementation of hook_elements().
 */
function gmap_elements() {
  return array(
    'gmap' => array(
      '#input' => FALSE, // This isn't a *form* input!!
      '#settings' => array_merge(gmap_defaults(), array(
        'points' => array(),
        'pointsOverlays' => array(),
        'lines' => array(),
      )),
    ),
    'gmap_macrotext' => array(
      '#input' => TRUE,
      '#gmap_newtype' => 'textarea',
      '#theme' => 'gmap_macrotext',
Brandon Bergren's avatar
Brandon Bergren committed
      '#process' => array('process_gmap_control'),
Brandon Bergren's avatar
Brandon Bergren committed
    'gmap_overlay_edit' => array('#input' => TRUE, '#process' => array('process_gmap_overlay_edit')),
    'gmap_style' => array('#input' => TRUE, '#tree' => TRUE, '#gmap_style_type' => 'poly', '#process' => array('process_gmap_style')),
    'gmap_address' => array('#input' => TRUE, '#process' => array('process_gmap_address')),
    'gmap_align' => array('#input' => TRUE, '#process' => array('process_gmap_align')),
Brandon Bergren's avatar
Brandon Bergren committed
    'gmap_latitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
    'gmap_longitude' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
    'gmap_latlon' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control')),
    'gmap_markerchooser' => array('#input' => TRUE, '#process' => array('process_gmap_markerchooser')),
Brandon Bergren's avatar
Brandon Bergren committed
    'gmap_dimension' => array('#input' => TRUE, '#gmap_newtype' => 'textfield', '#process' => array('process_gmap_control'), '#element_validate' => array('gmap_dimension_validate')),
  );
}

/**
 * Generic gmap control #process function.
 */
function process_gmap_control($element, $edit, &$form_state, $complete_form) {
  $control = substr($element['#type'], 5);
  $element['#type'] = $element['#gmap_newtype'];
  unset($element['#gmap_newtype']);
Brandon Bergren's avatar
Brandon Bergren committed
  // Attempt to run any #process handlers of our transmogrified type.
  // However, if we aren't the only #process handler, we assume someone else
  // is taking care of setting up any default handlers.
  $chain = FALSE;
  if (count($element['#process']) == 1) {
    // Unset #process so we can pull in the default.
    unset($element['#process']);
    $chain = TRUE;
  }
  // Inherit #input from the new type.
  unset($element['#input']);
  // Merge in the defaults for the target element type.
  $element += _element_info($element['#type']);
  if (isset($element['#input']) && $element['#input']) {
    if (isset($element['#process']) && $chain) {
      // Chain process.
      foreach ($element['#process'] as $process) {
        if (function_exists($process)) {
          $form = $process($element, $edit, $form_state, $complete_form);
        }
      }
    }
/**
 * Style fieldset #process function.
 */
function process_gmap_style($element) {
  $isline = ($element['#gmap_style_type'] == 'line');
  // Stroke color
  $element[0] = array(
    '#type' => 'textfield',
    '#size' => 6,
    '#maxlength' => 6,
    '#field_prefix' => '#',
    '#title' => t('Stroke color'),
    '#value' => $element['#value'][0],
    '#attributes' => array('class' => 'gmap_style'),
  );
  // Stroke weight
  $element[1] = array(
    '#type' => 'textfield',
    '#title' => t('Stroke weight'),
    '#description' => t('Thickness of line, in pixels.'),
    '#size' => 3,
    '#maxlength' => 3,
    '#field_suffix' => t('px'),
    '#value' => $element['#value'][1],
    '#attributes' => array('class' => 'gmap_style'),
  );
  // Stroke opacity
  $element[2] = array(
    '#type' => 'textfield',
    '#title' => t('Stroke opacity'),
    '#size' => 3,
    '#maxlength' => 3,
    '#field_suffix' => '%',
    '#value' => $element['#value'][2],
    '#attributes' => array('class' => 'gmap_style'),
  );
  // Fill color
  $element[3] = array(
    '#type' => 'textfield',
    '#title' => t('Fill color'),
    '#description' => t('Hex color value for fill color. Example: #<em>00AA33</em>'),
    '#size' => 6,
    '#maxlength' => 6,
    '#field_prefix' => '#',
Brandon Bergren's avatar
Brandon Bergren committed
    '#value' => $isline ? '' : $element['#value'][3],
    '#disabled' => $isline,
    '#attributes' => array('class' => 'gmap_style'),
  );
  $element[4] = array(
    '#type' => 'textfield',
    '#title' => t('Fill opacity'),
    '#description' => t('Opacity of fill, from 0 to 100%.'),
    '#size' => 3,
    '#maxlength' => 3,
    '#field_suffix' => '%',
Brandon Bergren's avatar
Brandon Bergren committed
    '#value' => $isline ? '' : $element['#value'][4],
    '#disabled' => $isline,
    '#attributes' => array('class' => 'gmap_style'),
  );
Brandon Bergren's avatar
Brandon Bergren committed
  unset($element['#input']);
  $element += _element_info('fieldset');
  return $element;
}

/**
 * Theme a gmap_style fieldset.
 */
function theme_gmap_style($element) {
  // Fieldsets print #value at the end, so we need to empty it out.
  // Otherwise, it puts "Array" at the end of the fieldset.
  $element['#value'] = '';
  return theme('fieldset', $element, $element['#children']);
}

/**
 * Overlay editor #process function.
 */
function process_gmap_overlay_edit($element) {
  // Conver the root element into a fieldset.
  $element['#type'] = 'fieldset';
Brandon Bergren's avatar
Brandon Bergren committed
  if (!isset($element['#title'])) {
    $element['#title'] = t('Overlay editor');
  }
  $element['#tree'] = TRUE;

  $element['mapclicktype'] = array(
    '#type' => 'select',
    '#title' => t('Click map'),
    '#map' => $element['#map'],
    '#options' => array(
      'Points' => t('Points'),
      'Lines' => t('Lines'),
      'Circles' => t('Circles'),
      'GPolygon' => t('Filled Polygons'),
    ),
  );
  gmap_widget_setup($element['mapclicktype'], 'overlayedit_mapclicktype');
  $element['markerclicktype'] = array(
    '#type' => 'select',
    '#title' => t('Click marker / segment'),
    '#map' => $element['#map'],
    '#options' => array(
      'Remove' => t('Remove'),
//      'updatestyle' => t('Update Styles'),
//      'removestyle' => t('Remove Styles'),
      'Edit Info' => t('Edit Info'),
    ),
  );
  gmap_widget_setup($element['markerclicktype'], 'overlayedit_markerclicktype');

  $element['marker'] = array(
    '#type' => 'select',
    '#map' => $element['#map'],
    '#options' => gmap_get_marker_titles(),
    '#title' => t('Marker'),
    '#theme' => 'gmap_overlay_edit',
  );
  gmap_widget_setup($element['marker'], 'overlayedit');