--- /dev/null
+<?php
+
+/**
+ * Gaslight Media Members Database
+ * Get Points of Interest (POI) for NearMe by AJAX
+ *
+ * PHP version 5.5
+ *
+ * @category glmWordPressPlugin
+ * @package glmMembersDatabase
+ * @author Chuck Scott <cscott@gaslightmedia.com>
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version 0.1
+ */
+
+
+// Load Members Data Class
+require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataMembers.php';
+
+/*
+ * This class exports the currently selected members list
+ * to a printable HTML file, to a CSV file, or otherwise.
+ */
+class GlmMembersAdmin_ajax_getPOI extends GlmDataMembers
+{
+
+ /**
+ * WordPress Database Object
+ *
+ * @var $wpdb
+ * @access public
+ */
+ public $wpdb;
+ /**
+ * Plugin Configuration Data
+ *
+ * @var $config
+ * @access public
+ */
+ public $config;
+
+ /*
+ * Constructor
+ *
+ * This contructor sets up this model. At this time that only includes
+ * storing away the WordPress data object.
+ *
+ * @return object Class object
+ *
+ */
+ public function __construct ($wpdb, $config)
+ {
+
+ // Save WordPress Database object
+ $this->wpdb = $wpdb;
+
+ // Save plugin configuration object
+ $this->config = $config;
+
+ parent::__construct(false, false);
+
+ }
+
+ /*
+ * Perform Model Action
+ *
+ * This modelAction takes an AJAX request to bump a member detail page views
+ * counter in the database for the current day, week and month and updates
+ * the database accordingly.
+ *
+ * This model action does not return, it simply does it's work then calls die();
+ *
+ * @param $actionData
+ *
+ * Echos JSON string as response and does not return
+ */
+ public function modelAction ($actionData = false)
+ {
+
+ $ret = false;
+ $poiAreas = false; // False means to search anywhere
+
+ // Get any search string
+ $searchText = html_entity_decode( filter_input(INPUT_GET, 'searchText', FILTER_SANITIZE_STRING), ENT_QUOTES | ENT_XML1);
+
+ $request = filter_input(INPUT_GET, 'request', FILTER_SANITIZE_STRING);
+
+ // Get the target LAT/LON area
+ $latMax = filter_input(INPUT_GET, 'latMax', FILTER_VALIDATE_FLOAT);
+ $latMin = filter_input(INPUT_GET, 'latMin', FILTER_VALIDATE_FLOAT);
+ $lonMax = filter_input(INPUT_GET, 'lonMax', FILTER_VALIDATE_FLOAT);
+ $lonMin = filter_input(INPUT_GET, 'lonMin', FILTER_VALIDATE_FLOAT);
+
+
+ // If this is not an "anywhere" search, then build the lat/lon areas
+ switch($request) {
+
+ case 'onMap';
+
+ // Check for good LAT/LON data
+ if ($latMax && $latMin && $lonMax && $lonMin) {
+
+ // Get the previous target LAT/LON area
+ $lastLatMax = filter_input(INPUT_GET, 'lastLatMax', FILTER_VALIDATE_FLOAT);
+ $lastLatMin = filter_input(INPUT_GET, 'lastLatMin', FILTER_VALIDATE_FLOAT);
+ $lastLonMax = filter_input(INPUT_GET, 'lastLonMax', FILTER_VALIDATE_FLOAT);
+ $lastLonMin = filter_input(INPUT_GET, 'lastLonMin', FILTER_VALIDATE_FLOAT);
+
+ // If this is the first load, request all POIs within map bounds
+ if ($lastLatMax == false) {
+
+ // Add entire map as area to update
+ $poiAreas[] = array(
+ 'area' => 'Entire Map',
+ 'latMax' => $latMax,
+ 'latMin' => $latMin,
+ 'lonMax' => $lonMax,
+ 'lonMin' => $lonMin
+ );
+
+ // Otherwise request only those POIs in the new map areas
+ } else {
+
+ // Check for good last LAT/LON data
+ if ($lastLatMax && $lastLatMin && $lastLonMax && $lastLonMin) {
+
+ // Check if there's new area at the top of the map
+ if ($latMax > $lastLatMax) {
+ $poiAreas[] = array(
+ 'area' => 'New at top',
+ 'latMax' => $latMax,
+ 'latMin' => $lastLatMax,
+ 'lonMax' => $lonMax,
+ 'lonMin' => $lonMin
+ );
+ }
+
+ // Check if there's new area at the bottom of the map
+ if ($latMin < $lastLatMin) {
+ $poiAreas[] = array(
+ 'area' => 'New at bottom',
+ 'latMax' => $lastLatMin,
+ 'latMin' => $latMin,
+ 'lonMax' => $lonMax,
+ 'lonMin' => $lonMin
+ );
+ }
+
+ // Check if there's new area at the left of the map
+ if ($lonMin < $lastLonMin) {
+ $poiAreas[] = array(
+ 'area' => 'New at left',
+ 'latMax' => $latMax,
+ 'latMin' => $latMin,
+ 'lonMax' => $lastLonMin,
+ 'lonMin' => $lonMin
+ );
+ }
+
+ // Check if there's new area at the right of the map
+ if ($lonMax > $lastLonMax) {
+ $poiAreas[] = array(
+ 'area' => 'New at right',
+ 'latMax' => $latMax,
+ 'latMin' => $latMin,
+ 'lonMax' => $lonMax,
+ 'lonMin' => $lastLonMax
+ );
+ }
+
+ }
+
+ }
+
+ }
+
+ break;
+
+ case 'anywhere':
+ break;
+
+ default:
+ $request = 'onMap';
+ break;
+
+ }
+
+ /*
+ * Get map items
+ *
+ * (NOTE that while this is for a front-end feature, that feature gets
+ * data via AJAX, which is processed by the admin controller.)
+ *
+ * The array supplied is in the following standardized format. This format is
+ * used for all generic map items aggregated from multiple sources.
+ *
+ * 'request' What type of request is being made - Request only
+ * 'filter' A string filter to use to limit results - Request only
+ * 'area' A set of lat/lon areas to get results for (if request = 'onMap') - Request only
+ * If false then get all results without regard as to where they are.
+ * 'sources' An array of source information for what type of item and which add-on - Each source adds one entry here
+ * 'mapItems' Array of new map items to return to NearMe - Each source adds one or more map items here
+ *
+ * array(
+ * 'request' => {'onMap', 'anywhere'}
+ * 'filter' => {some search string}
+ * 'area' => array(
+ * // First area to search
+ * array(
+ * 'latMax' => {lat at North end of area},
+ * 'latMin' => {lat at South end of area},
+ * 'lonMax' => {lon at East end of area},
+ * 'lonMin' => {lon at West end of area}
+ * ),
+ * // Second area to search
+ * array(
+ * 'latMax' => {lat at North end of area},
+ * 'latMin' => {lat at South end of area},
+ * 'lonMax' => {lon at East end of area},
+ * 'lonMin' => {lon at West end of area}
+ * ),
+ * // Additional areas
+ * ),
+ * 'sources' = array(
+ * {addOn Slug} => array(
+ * 'addOn' => {addOn Slug},
+ * 'type' => {Name to use for the source - Needs to match the 'type' element in map items added by the addOn )
+ * ),
+ * // Additional sources
+ * ),
+ * 'mapItems' => array(
+ * array(
+ * 'type' => {type of item, i.e. 'member', 'event', ...},
+ * 'id' => {ID of item for reference},
+ * 'lat' => {Latitude},
+ * 'lon' => {Longitude},
+ * 'name' => {Name of item},
+ * 'loc_name' => {Name of Location},
+ * 'addr1' => {Address line 1 of location},
+ * 'addr2' => {Address line 2 of location},
+ * 'city' => {City of location},
+ * 'state' => {State code of location},
+ * 'zip' => {Postal Code of location},
+ * 'phone' => {Contact phone number},
+ * 'email' => {Contact E-Mail address},
+ * 'url' => {URL of item},
+ * 'region' => {Region name, if available},
+ * 'categories' => {array of categories - i.e. array( 0 = array( id => {id}, name => {name}), ... )
+ * 'descr' => {Description},
+ * 'short_descr' => {Short Description},
+ * 'detail_page' => {URL of detail page, if available},
+ * 'dates' => {Text stating date, dates, or date range},
+ * 'times' => {Text stating time, times, or time range}
+ * )
+ * )
+ * )
+ */
+ $poiData = apply_filters(
+ 'glm-hook-list-map-items-by-latlon',
+ array(
+ 'request' => $request,
+ 'filter' => $searchText,
+ 'area' => $poiAreas,
+ 'sources' => array(),
+ 'mapItems' => array()
+ )
+ );
+
+ header('Content-type:application/json;charset=utf-8', true);
+ echo json_encode($poiData);
+
+ wp_die();
+
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Gaslight Media Members Database
+ * NearMe Page
+ *
+ * PHP version 5.5
+ *
+ * @category glmWordPressPlugin
+ * @package glmMembersDatabase
+ * @author Chuck Scott <cscott@gaslightmedia.com>
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version 0.1
+ */
+
+/*
+ * This class performs the work for displaying members packages.
+ */
+class GlmMembersFront_nearme_index
+{
+
+ /**
+ * WordPress Database Object
+ *
+ * @var $wpdb
+ * @access public
+ */
+ public $wpdb;
+ /**
+ * Plugin Configuration Data
+ *
+ * @var $config
+ * @access public
+ */
+ public $config;
+
+ /*
+ * Constructor
+ *
+ * This contructor sets up this model. At this time that only includes
+ * storing away the WordPress data object.
+ *
+ * @return object Class object
+ *
+ */
+ public function __construct ($wpdb, $config)
+ {
+
+ // Save WordPress Database object
+ $this->wpdb = $wpdb;
+
+ // Save plugin configuration object
+ $this->config = $config;
+
+ // Enqueue the Marker Clusterer Script
+ wp_register_script(
+ 'glm-members-admin-google-maps-marker-clusterer',
+ GLM_MEMBERS_PLUGIN_URL . 'js/googleMapsMarkerClusterer/markerclustererplus.js',
+ array(
+ 'jquery'
+ ),
+ GLM_MEMBERS_PLUGIN_VERSION
+ );
+ wp_enqueue_script('glm-members-admin-google-maps-marker-clusterer', false, array('jquery'), false, true);
+
+ }
+
+ /*
+ * Perform Model Action
+ *
+ * This method does the work for this model and returns any resulting data
+ *
+ * @return array Status and data array
+ *
+ * 'status'
+ *
+ * True if successfull and false if there was a fatal failure.
+ *
+ * 'menuItemRedirect'
+ *
+ * If not false, provides a menu item the controller should
+ * execute after this one. Normally if this is used, there would also be a
+ * modelRedirect value supplied as well.
+ *
+ * 'modelRedirect'
+ *
+ * If not false, provides an action the controller should execute after
+ * this one.
+ *
+ * 'view'
+ *
+ * A suggested view name that the contoller should use instead of the
+ * default view for this model or false to indicate that the default view
+ * should be used.
+ *
+ * 'data'
+ *
+ * Data that the model is returning for use in merging with the view to
+ * produce output.
+ *
+ */
+ public function modelAction ($actionData = false)
+ {
+
+ $status = true;
+ $pointsOfInterest = array();
+ $havePOI = false; // Have Points of Interest
+
+ // Set default map center
+ $mapCenterLat = $this->config['settings']['maps_default_lat'];
+ $mapCenterLon = $this->config['settings']['maps_default_lon'];
+
+ // Set default reference location
+ $refLat = $mapCenterLat;
+ $refLon = $mapCenterLon;
+
+ // Set default map zoom
+ $mapZoom = 14; // Need to add this to management ******
+
+ // Check for user cookie - Cookie will be set by ajax request from map so we can save the last user map status - Overides defaults
+ if (isset($_COOKIE) && isset($_COOKIE[GLM_MEMBERS_NEARME_PLUGIN_SLUG])) {
+ $cookie = $_COOKIE[GLM_MEMBERS_NEARME_PLUGIN_SLUG];
+ $mapCenterLat = $cookie['mapCenterLat'];
+ $mapCenterLon = $cookie['mapCenterLon'];
+ $refLat = $cookie['refLat'];
+ $refLon = $cookie['refLon'];
+ $mapZoom = $cookie['mapZoom'];
+ }
+
+ // Check for specified map center, reference location, and zoom - Overides cookies and defaults
+ $mapCenterLat = $this->checkMapRequest('mapCenterLat', $mapCenterLat);
+ $mapCenterLon = $this->checkMapRequest('mapCenterLon', $mapCenterLon);
+ $refLat = $this->checkMapRequest('refLat', $refLat);
+ $refLon = $this->checkMapRequest('refLon', $refLon);
+ $mapZoom = $this->checkMapRequest('mapZoom', $mapZoom);
+
+ $view = 'index.html';
+
+ // Compile template data
+ $templateData = array(
+ 'siteBaseUrl' => GLM_MEMBERS_SITE_BASE_URL,
+ 'pointsOfInterest' => $pointsOfInterest,
+ 'havePOI' => $havePOI,
+ 'mapCenterLat' => $mapCenterLat,
+ 'mapCenterLon' => $mapCenterLon,
+ 'refLat' => $refLat,
+ 'refLon' => $refLon,
+ 'mapZoom' => $mapZoom
+ );
+
+ // Return status, suggested view, and data to controller - also return any modified settings
+ return array(
+ 'status' => $status,
+ 'menuItemRedirect' => false,
+ 'modelRedirect' => false,
+ 'view' => 'front/nearme/'.$view,
+ 'data' => $templateData
+ );
+
+ }
+
+ /*
+ * Check for a submitted value for map defaults
+ *
+ * @param $param string Name of the request parameter
+ * @param $default float/integer Default value to return if none was sumbitted
+ *
+ * @return float/integer Value to use
+ */
+ private function checkMapRequest ($param, $default)
+ {
+ // If there's no request array, return default
+ if (!isset($_REQUEST)) {
+ return $default;
+ }
+
+ // If the request parameter doesn't exist, return default
+ if (!isset($_REQUEST[$param])) {
+ return $default;
+ }
+
+ // If it's not a valid number, return default
+ $val = ($_REQUEST[$param] - 0);
+ if ($val == 0) {
+ return $default;
+ }
+
+ // Return the parameter value
+ return $val;
+
+ }
+
+
+}
+
+?>
\ No newline at end of file
--- /dev/null
+{include file='front/nearme/header.html'}
+
+ <script src="http://maps.googleapis.com/maps/api/js?&key={$settings.google_maps_api_key}"></script>
+ <script type="text/javascript">var enableDraggable = true;</script>
+
+ <div>
+ Display only items matching this text:
+
+ <a id="glmNearMeSearchAnywhere" class="button button-secondary glm-member-button-small">Search All Areas</a>
+ <input id="glmNearMeSearchText" type="text">
+ </div>
+
+ <p><!-- just adding space here temporarily --></p>
+
+ <div style="height: 2em;">
+ <b>My Location:</b>
+ <input id="glmMyLocationFollowMe" class="glmMapMyLocationType" type="radio" name="myLocationType" value="track" checked="checked"> Follow Me
+ <input id="glmMyLocationSet" class="glmMapMyLocationType" type="radio" name="myLocationType" value="set"> Manual Map Selection
+
+ <a id="glmMapSetMyLocation" class="button button-secondary glm-member-button-small glm-hidden">Set My Location Here</a>
+ <a id="glmMapGoToMyLocation" class="button button-secondary glm-member-button-small">Return to My Location</a>
+ <a id="glmMapBack" class="button button-secondary glm-member-button-small glm-hidden">Back to previous map location</a>
+ <span id="glmMapReload" class="glm-hidden glm-right">Loading ...</span>
+ </div>
+ <div id="glm-nearmeMap" class="glm-map-tall">(map loads here)</div>
+ <div id="glm-mapLegend" style="margin: .4em;">
+ <b>Show:</b>
+ </div>
+ <div>
+ My Location <img src="{$assetsUrl}/MapIcons/standard/woman.png"> (drag to change)
+ <span id="glm-mapClusterLegend" class="glm-right">
+ Clustered locations - Click to view:
+ <img style="width: 26px; height: auto;" src="{$baseUrl}/js/googleMapsMarkerClusterer/images/m1.png">
+ <img style="width: 28px; height: auto;" src="{$baseUrl}/js/googleMapsMarkerClusterer/images/m2.png">
+ <img style="width: 33px; height: auto;" src="{$baseUrl}/js/googleMapsMarkerClusterer/images/m3.png">
+ <img style="width: 39px; height: auto;" src="{$baseUrl}/js/googleMapsMarkerClusterer/images/m4.png">
+ <img style="width: 45px; height: auto;" src="{$baseUrl}/js/googleMapsMarkerClusterer/images/m5.png">
+ </span>
+ </div>
+ <div id="nearMe-detail"><!-- Detail for map item goes here when clicked. --></div>
+ <div id="nearMe-favorites">
+ <h4>My Favorites</h4>
+ <div>
+ <!-List of favorites goes here -->
+ </div>
+ </div>
+
+ <div id="nearMe-mouseover-template" class="glm-hidden">
+ { name } - Click for more
+ </div>
+
+ <script type="text/javascript">
+ jQuery(document).ready(function($) {
+
+ /*
+ * Google Maps
+ * API reference: https://developers.google.com/maps/documentation/javascript/reference
+ */
+
+ // Settings
+ var doDetailSection = false; // Show detail section below map
+ var doPOIListSection = false; // Show a list of map points of interest below map
+ var defZoom = {$mapZoom}; // Default zoom for starting up or when returning to selected location
+ var doMapBubble = true; // Show detail on map bubbles in map
+ var clusterGridSize = 30; // Area in pixels over which clustering may take place. Smaller number = more smaller clusters.
+ var clusterMaxZoom = 14; // Maximum map zoom where clustering may take place. Higher number is smaller map area. Set to 1 to effectively dissable clustering.
+ var clusterMinSize = 3; // Minimum number of markers that may be in a cluster.
+ var mapLat = {$mapCenterLat}; // Set default map Lat - Default for the current Website
+ var mapLon = {$mapCenterLon}; // Set default map Lon - Default for the current Website
+ var refLat = {$refLat}; // Default location for My Location marker - Lat
+ var refLon = {$refLon}; // Default location for My Location marker - Lon
+ var maxAnywhereZoom = 14; // Max zoom in when doing anywhere search
+ var boundsTimeout = 1500; // Time to wait after a map move or zoom before trying to load new POIs in milliseconds
+ var trackPositionInterval = 60000; // Time interval for getting user's current geolocation - 1 Min
+ var highAccuracyPoisition = false; // Request high-accuracy user geolocation from user's device
+ var postionTimeout = 15000; // Maximum amount of time we'll wait for geolocation result - 15 Sec
+ var geolocationFailCount = 2; // Allow geolocation to fail this many times before switching to manual location settings ('set')
+ var trackToConsole = false; // Send debug/progress messages to developers console (Firefox)
+
+ // Set default - Need to make this configurable
+ var mapLocation = new google.maps.LatLng(mapLat, mapLon);
+ var refLocation = new google.maps.LatLng(refLat, refLon);
+ var map;
+ var geocoder;
+ var marker;
+ var latMax = false;
+ var latMin = false;
+ var lonMax = false;
+ var lonMin = false;
+ var lastLatMax = false;
+ var lastLatMin = false;
+ var lastLonMax = false;
+ var lastLonMin = false;
+ var boundsDelayTimer = false;
+ var markers = []; // Object with marker objects attached by key name
+ var markerId = 1;
+ var markerClusterer = false;
+ var myMarker = false;
+ var myCurrentLocation = false;
+ var canReadUserLocation = true;
+ var savedMapView = [];
+ var mapViewStack = [];
+ var curZoom = false;
+ var curCenter = false;
+ var poiTypes = [];
+ var dontPushPos = false;
+ var mapBubble = false;
+ var mapTitle = false;
+ var categories = [];
+ var availPushPins = [
+ 'standard/blue.png',
+ 'standard/green.png',
+ 'standard/lightblue.png',
+ 'standard/orange.png',
+ 'standard/pink.png',
+ 'standard/purple.png',
+ 'standard/red.png',
+ 'standard/yellow.png'
+ ]
+ var assignedPushPins = [];
+ var delQueue = [];
+ var dragging = false;
+ var trackingType = 'track';
+ var trackingTimer;
+ var getLocationIngoredTimer;
+ var geolocationFailCounter = 0;
+ var stopGeolocation = false;
+
+ function initMap() {
+
+ if (trackToConsole) { console.log('GLM NearMe: initMap()'); }
+
+ map = new google.maps.Map(document.getElementById('glm-nearmeMap'), {
+ zoom: defZoom,
+ disableDefaultUI: false,
+ mapTypeId: google.maps.MapTypeId.MAP
+ });
+ var geocoder = new google.maps.Geocoder();
+
+ markerClusterer = new MarkerClusterer(map, [], {
+ imagePath: '{$baseUrl}/js/googleMapsMarkerClusterer/images/m',
+ gridSize: clusterGridSize,
+ maxZoom: clusterMaxZoom,
+ minimumClusterSize: clusterMinSize
+ });
+
+ // Create a moveable marker for our currently saved reference location
+ myMarker = new google.maps.Marker({
+ map: map,
+ position: refLocation,
+ draggable: enableDraggable,
+ animation: google.maps.Animation.DROP,
+ icon: '{$assetsUrl}/MapIcons/standard/woman.png'
+ });
+
+ // Get initial user location, but wait a bit for things to get seup.
+ google.maps.event.addListenerOnce(map, 'idle', function(){
+ window.setTimeout(getUserLocation, 1000);
+
+ // Start with map centered on reference location
+ map.setCenter(mapLocation);
+
+ // Save the initail map position
+ curZoom = map.getZoom();
+ curCenter = map.getCenter();
+
+ });
+
+ google.maps.event.addListener(myMarker, 'dragend', function(event) {
+// Save My location stuff here
+ });
+
+ // Add lister to capture map position and zoom before dragging
+ map.addListener('dragstart', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: Drag map started'); }
+
+ // Close map bubble if used
+ closeMapBubble();
+
+ });
+
+ // On first load get all POIs for current bounds - Don't push position
+ dontPushPos = true;
+ checkNewPOIs(boundsTimeout * 2); // Give things a bit more time to be ready before gettign POIs the first time.
+
+ // Add a click listener for this marker
+ myMarker.addListener('mouseover', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: myMarker mouseover'); }
+
+ if (dragging) {
+ return;
+ }
+ closeMapBubble();
+ closeMapTitle();
+ mapTitle = new google.maps.InfoWindow({
+ content: 'My Selected Location'
+ });
+ mapTitle.open(map, this);
+
+ });
+
+ myMarker.addListener('mouseout', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: myMarker mouseout'); }
+
+ if (dragging) {
+ return;
+ }
+ mapTitle.close();
+ checkNewPOIs();
+ });
+
+ myMarker.addListener('dragstart', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: myMaker dragstart'); }
+
+ dragging = true;
+ mapTitle.close();
+ window.clearTimeout(boundsDelayTimer);
+ });
+
+ myMarker.addListener('dragend', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: myMarker dragend'); }
+
+ dragging = false;
+ checkNewPOIs();
+ });
+
+ // Request update for points of interest when map dragging stops.
+ map.addListener('dragend', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: map dragend'); }
+
+ checkNewPOIs();
+ });
+
+ // Request update for points of interest when map zoom changes.
+ map.addListener('zoom_changed', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: map zoom changed'); }
+
+ // Close map bubble if used
+ closeMapBubble();
+
+ checkNewPOIs();
+ });
+
+ }
+
+ // Watch for Reference Map Position request by user
+ $('#glmMapGoToMyLocation').on('click', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: User GoToMyLocation clicked'); }
+
+ goToPosition(myMarker.getPosition());
+ return false;
+ });
+
+ function goToPosition(position) {
+ map.setCenter(position);
+ map.setZoom(defZoom)
+ checkNewPOIs();
+ }
+
+
+ // Watch for request to set My Selected Location to center of map
+ $('#glmMapSetMyLocation').on('click', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: User SetMyLocation clicked'); }
+
+ myMarker.setPosition(map.getCenter());
+ return false;
+ });
+
+ // Watch for Previous Map Position request by user
+ $('#glmMapBack').on('click', function() {
+
+ if (trackToConsole) { console.log('GLM NearMe: User MapBack clicked'); }
+
+ // If there's any previous map positions on the stack
+ if (mapViewStack.length) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Moving to previous map position'); }
+
+ dontPushPos = true;
+
+ // Get the previons position data
+ var poppedPos = mapViewStack.pop();
+
+ // Set the map to the popped position
+ map.panTo(poppedPos.center);
+ map.setZoom(poppedPos.zoom);
+
+ // If there's nothing left on the stack, remove back button
+ if (!mapViewStack.length) {
+
+ if (trackToConsole) { console.log('GLM NearMe: No previous map postion'); }
+
+ $('#glmMapBack').addClass('glm-hidden');
+ }
+
+ checkNewPOIs();
+
+ }
+
+ return false;
+ });
+
+ // Redo search when Search Anywhere button is clicked.
+ $('#glmNearMeSearchAnywhere').click('change', function() {
+ getBoundsPOIs(true, true);
+ });
+
+ // Trigger text search for POIs when search text is changed
+ $('#glmNearMeSearchText').catchEnter().on('enterkey', function() {
+ getBoundsPOIs(true);
+ });
+
+ // Schedule a check for new POIs
+ function checkNewPOIs(timeout = boundsTimeout) {
+
+ if (trackToConsole) { console.log('GLM NearMe: checkNewPOIs()'); }
+
+ // Reset the delay timer to make sure nothing happens from an earlier dragend
+ window.clearTimeout(boundsDelayTimer);
+
+ // Set timer to get POIs
+ boundsDelayTimer = window.setTimeout(getBoundsPOIs, timeout);
+
+ }
+
+ /*
+ * Get new bounds for map and check for POI changes
+ *
+ * type 'onMap' or 'anywhere'
+ * searchText A string that should match any returned results
+ * resetPOIs true to clear all POIs and reload them all for current map bounds
+ * Not needed for type 'anywhere' - full reset is assumned
+ */
+ function getBoundsPOIs(resetPOIs = false, anywhere = false) {
+
+ if (trackToConsole) { console.log('GLM NearMe: getBoundsPOIs() '); }
+
+ var searchText = $('#glmNearMeSearchText').val();
+ var request = 'onMap';
+ if (anywhere) {
+ request = 'anywhere';
+
+ // If we're currently in "Follow Me" mode, switch to manual
+ if (trackingType == 'track') {
+ setMyLocationToManual();
+ }
+
+ // Then turn off checked
+ $('#glmNearMeSearchAnywhere').attr("checked", false);
+ }
+
+
+ // Get current map view data
+ mapBounds = map.getBounds();
+ ne = mapBounds.getNorthEast();
+ sw = mapBounds.getSouthWest();
+ latMax = ne.lat();
+ latMin = sw.lat();
+ lonMax = ne.lng();
+ lonMin = sw.lng();
+
+ // If "reset" requested, clear last position values
+ if (resetPOIs) {
+ lastLatMax = false;
+ lastLatMin = false;
+ lastLonMax = false;
+ lastLonMin = false;
+ }
+
+ // Check if there's been no movement - Don't do anything.
+ if ( latMax == lastLatMax && latMin == lastLatMin && lonMax == lastLonMax && lonMin == lastLonMin ) {
+
+ if (trackToConsole) { console.log('GLM NearMe: No map change, aborting getBoundsPOIs()'); }
+
+ hideLoadingMsg();
+ return;
+ }
+
+ showLoadingMsg();
+
+ // If there is a previous map position, push that on the stack - unless we've been told not to
+ if (dontPushPos) {
+ dontPushPos = false;
+ } else {
+
+ // Push the previous position on the stack
+ mapViewStack.push({
+ zoom: curZoom,
+ center: curCenter
+ });
+
+ // Show the back button
+ $('#glmMapBack').removeClass('glm-hidden');
+
+ // Clear the don't push on stack flag
+ dontPushPos = false;
+ }
+
+ // Request any POIs in new areas of the map
+ $.getJSON(
+ '{$ajaxUrl}?action=glm_members_admin_ajax&glm_action=getPOI&request='+request+'&latMax='+latMax+'&latMin='+latMin+'&lonMax='+lonMax+'&lonMin='+lonMin+'&lastLatMax='+lastLatMax+'&lastLatMin='+lastLatMin+'&lastLonMax='+lastLonMax+'&lastLonMin='+lastLonMin,
+ 'searchText='+searchText,
+ function( newPOIs ) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Requesting newPOIs'); }
+
+ // If resetPOIs is requested, clear all clusterer markers, all map markers, and our own markers array.
+ if (resetPOIs) {
+
+ markerClusterer.clearMarkers();
+ markerClusterer.repaint();
+
+// map.markers = [];
+ markers = [];
+// markerClusterer.redraw();
+
+
+ // Otherwise remove only markers that are off our map
+ } else {
+
+ // Delete the POIs that are off our map
+ for (key in markers) {
+
+ // If reset is requested or this marker is not within our map bounds. remove the marker and delete it
+ if (markers[key].getPosition().lat() > latMax || markers[key].getPosition().lat() < latMin ||
+ markers[key].getPosition().lng() > lonMax || markers[key].getPosition().lng() < lonMin) {
+
+ // Add this marker to the delete queue
+ delQueue.push(key);
+ }
+
+ }
+
+ if (trackToConsole) { console.log('GLM NearMe: '+Object.keys(delQueue).length+' markers in delete queue'); }
+
+ // Remove markers in the delete queue from the clusterer and markers array - can't do this in loop above
+ if (Object.keys(delQueue).length) {
+ $.each( delQueue, function(key, value) {
+
+ // Remove the marker from the clusterer
+ markerClusterer.removeMarker(markers[value]);
+
+ // Remove the marker from the map
+ markers[value].setMap(null);
+
+ // Delete our marker
+ delete markers[value];
+
+ });
+ }
+ delQueue = []; // Clear delete queue
+
+ }
+
+ if (trackToConsole) { console.log('GLM NearMe: Processing '+Object.keys(newPOIs.sources).length+' POI sources'); }
+
+ // Add all sources (members, events, ...) to the assignedPushPins array
+ $.each( newPOIs.sources, function(key, value) {
+
+ if (!assignedPushPins[value.type]) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Adding POI type '+value.type); }
+
+ // Pull a new pushpin off the available stack and assign that to this type
+ pushPin = availPushPins.pop();
+ pushPinData = { pinFile: pushPin, pinOn: true };
+ assignedPushPins[value.type] = pushPinData;
+
+ // Add the new pushpin to the map legend
+ $('#glm-mapLegend').append('<input data-type="'+value.type+'" class="glm-type-select" type="checkbox" checked><img src="{$assetsUrl}/MapIcons/'+pushPin+'"> '+value.type+' ');
+
+ }
+ });
+
+ // Re-bind click on ping type for when a pin type is turned on or off by the user
+ // Note that .upbind().on() prevents the event from being bound multiple times from multiple map moves.
+ $('.glm-type-select').unbind('click').on('click', function() {
+
+ // Get the type and current visibility state
+ pinType = $(this).attr('data-type');
+ pinVisible = $(this).prop('checked');
+
+ if (trackToConsole) { console.log('Pin type "'+pinType+' set to '+pinVisible); }
+
+ // Save visibility to the specified assignedPushPins element
+ assignedPushPins[pinType].pinOn = pinVisible;
+
+ // loop through all current markers and set visibility for any of that type
+ for (key in markers) {
+ if (markers[key].type == pinType) {
+
+ // Set marker visibility
+ markers[key].setVisible(pinVisible);
+
+ // If not visible, remove from clusterer. If visible, add back to clusterer
+ if (pinVisible) {
+ markerClusterer.addMarker(markers[key]);
+ } else {
+ markerClusterer.removeMarker(markers[key]);
+ }
+ }
+
+ }
+
+ });
+
+ // If we have new POIs
+ if (newPOIs.mapItems.length > 0) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Processing '+Object.keys(newPOIs.mapItems).length+' new POIs'); }
+
+ $.each( newPOIs.mapItems, function(key, value) {
+
+ var markerKey = value.type+'_'+value.id+'_'+value.lat+'_'+value.lon;
+
+ // Create marker - add some other information we'll need for each marker
+ value.position = new google.maps.LatLng(value.lat, value.lon);
+ marker = new google.maps.Marker({
+ position: value.position,
+ map: map,
+ icon: '{$assetsUrl}/MapIcons/' + assignedPushPins[value.type].pinFile,
+ type: value.type,
+ visible: assignedPushPins[value.type].pinOn,
+ id: value.type+value.id
+ })
+
+ // Add a click listener for this marker
+ marker.addListener('mouseover', function() {
+
+ template = $('#nearMe-mouseover-template').html();
+ template = template.replace(/{ name }/g, value.name);
+ closeMapBubble();
+ closeMapTitle();
+
+ mapTitle = new google.maps.InfoWindow({
+ content: template
+ });
+
+ mapTitle.open(map, this);
+
+ });
+
+ marker.addListener('mouseout', function() {
+ mapTitle.close();
+ });
+
+ // Add a click listener for this marker
+ marker.addListener('click', function() {
+
+
+ var infotext = '<h4 style="white-space: nowrap;">'+value.name+'</h4>';
+ var stateComma = '';
+
+ if (value.short_descr) {
+ infotext += '<p>'+value.short_descr+'</p>';
+ }
+ if (value.loc_name) {
+ infotext += '<p>'+value.name+'</p>';
+ }
+ if (value.addr1) {
+ infotext += value.addr1+'<br>';
+ }
+ if (value.addr2) {
+ infotext += value.addr2+'<br>';
+ }
+ if (value.city) {
+ infotext += value.city;
+ stateComma = ', ';
+ }
+ if (value.state) {
+ infotext += stateComma+value.state;
+ }
+ if (value.zip) {
+ infotext += ' '+value.zip;
+ }
+ if (value.city+value.state+value.zip) {
+ infotext += '<br>';
+ }
+ if (value.phone) {
+ infotext += '<b>Phone: </b>'+value.phone+'<br>';
+ }
+ if (value.email) {
+ infotext += '<b>E-Mail: </b><a href="mailto:'+value.email+'">'+value.email+'</a><br>';
+ }
+ if (value.region) {
+ infotext += '<b>Region: </b>'+value.region+'<br>';
+ }
+ if (value.url) {
+ infotext += '<a href="'+value.url+'">Website<br>';
+ }
+ if (value.detail_page) {
+ infotext += '<a href="'+value.detail_page+'">More Information</a></<br>';
+ }
+ if (value.dates) {
+ infotext += '<b>Dates: </b>'+value.dates+'<br>';
+ }
+ if (value.times) {
+ infotext += '<b>Times: </b>'+value.times+'<br>';
+ }
+
+
+ if (doDetailSection) {
+ $('#nearMe-detail').html(infotext);
+ }
+
+ if (doMapBubble) {
+
+ // Close any other existing bubble or title.
+ closeMapBubble();
+ closeMapTitle();
+
+ // Show a map info bubble for this marker
+ mapBubble = new google.maps.InfoWindow({
+ content: infotext
+ });
+ mapBubble.open(map, this);
+
+ // Check for new POIs in case the map moved when displaying the map bubble
+ checkNewPOIs();
+
+ }
+
+ });
+
+ // Save this marker to our markers array
+ markers[marker.id] = marker;
+
+ // Also if this marker type is turned on, add marker to Clusterer
+ if (assignedPushPins[marker.type].pinOn) {
+ markerClusterer.addMarker(marker);
+ }
+
+ });
+ }
+
+ // If there's a request to find things anywhere
+ if (request == 'anywhere' && Object.keys(markers).length) {
+
+ allBounds = new google.maps.LatLngBounds();
+
+ // Find the total extent of all current markers.
+ for (key in markers) {
+ allBounds.extend(markers[key].getPosition());
+ }
+
+ map.fitBounds(allBounds);
+
+ // Enforce minimum zoom
+ var zoom = map.getZoom();
+ if (zoom > maxAnywhereZoom) {
+ map.setZoom(maxAnywhereZoom);
+ }
+
+
+ }
+
+ // Remove the loading message
+ hideLoadingMsg();
+
+ }
+ );
+
+ // Save current bounds as last bounds
+ lastLatMax = latMax;
+ lastLatMin = latMin;
+ lastLonMax = lonMax;
+ lastLonMin = lonMin;
+
+ // Save the current center position and zoom to push on stack before next move
+ curZoom = map.getZoom();
+ curCenter = map.getCenter();
+
+ }
+
+ // Handle tracking type selection
+ $('.glmMapMyLocationType').on('change', function() {
+
+ trackingType = $(this).val();
+ switch (trackingType) {
+ case 'track':
+ setMyLocationToTrack();
+ break;
+ case 'set':
+ setMyLocationToManual();
+ break;
+
+ }
+
+ });
+
+ // Try HTML5 geolocation to get user's current location
+ function getUserLocation() {
+
+ if (trackToConsole) { console.log('GLM NearMe: getUserLocation()'); }
+
+ if (!canReadUserLocation) {
+ if (trackToConsole) { console.log('GLM NearMe: Aborting getUserLocation() - canReadUserLocation = false'); }
+ return;
+ }
+
+ stopGeolocation = false;
+ clearGeolocationTimers();
+
+ // Set timer to catch when user ignores postion request or selects Not Now or geolocation not available
+ getLocationIngoredTimer = window.setTimeout( function() {
+ setMyLocationToManual();
+ return;
+ }, postionTimeout);
+
+
+ // If we can get the user's location
+ if (navigator.geolocation) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Processing Geolocation'); }
+
+ // Get the user's location
+ navigator.geolocation.getCurrentPosition(function(position) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Have geolocation as '+position.coords.latitude+', '+position.coords.longitude); }
+
+ if (stopGeolocation) {
+
+ if (trackToConsole) { console.log('GLM NearMe: geolocation stopped'); }
+
+ clearGeolocationTimers();
+ return;
+ }
+
+ // Save their current location
+ myCurrentLocation = {
+ lat: position.coords.latitude,
+ lng: position.coords.longitude
+ };
+
+ // Set return to this function at the defined interval
+ trackingTimer = window.setTimeout(getUserLocation, trackPositionInterval);
+
+ // Say that we are able to read the location and set things up to continue
+ canReadUserLocation = true;
+ window.clearTimeout(getLocationIngoredTimer);
+ geolocationFailCounter = 0;
+
+ // Set My Location to new lat/lon
+ myMarker.setPosition(myCurrentLocation);
+
+ // If current location is not on map, recenter map on location
+ if (!map.getBounds().contains(myMarker.getPosition())) {
+ map.setCenter(myCurrentLocation);
+ getBoundsPOIs();
+ }
+
+ // If we can't get the location, then switch to manual
+ },
+ function(err) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Geolocation error = ' + err.code + ': ' + err.message); }
+
+ if (stopGeolocation) {
+
+ if (trackToConsole) { console.log('GLM NearMe: geolocation stopped'); }
+
+ clearGeolocationTimers();
+ return;
+ }
+
+ // Bump fail counter and check if we've reached the end of our patience.
+ if (++geolocationFailCounter == geolocationFailCount) {
+
+ if (trackToConsole) { console.log('GLM NearMe: Geolocation fail counter exceeded'); }
+
+ setMyLocationToManual();
+ } else {
+
+ if (trackToConsole) { console.log('GLM NearMe: Geolocation fail #'+(geolocationFailCounter-1)); }
+
+ // Start the get location timer again window.clearTimeout(trackingTimer);
+ trackingTimer = window.setTimeout(getUserLocation, trackPositionInterval);
+ }
+
+ },
+ {
+ enableHighAccuracy: highAccuracyPoisition,
+ timeout: postionTimeout,
+ maximumAge: trackPositionInterval - 100 // Allows cached position resulting from other requests during interval
+
+ });
+
+ }
+ }
+
+ function setMyLocationToTrack() {
+
+ stopGeolocation = false;
+ canReadUserLocation = true;
+ getUserLocation();
+ $('#glmMapSetMyLocation').addClass('glm-hidden');
+
+ }
+
+ function clearGeolocationTimers() {
+ window.clearTimeout(trackingTimer);
+ window.clearTimeout(getLocationIngoredTimer);
+ }
+
+ function setMyLocationToManual() {
+
+ if (trackToConsole) { console.log('GLM NearMe: My Location set to manual'); }
+
+ stopGeolocation = true;
+ clearGeolocationTimers();
+
+ // Dissable tracking and change tracking radio buttons to 'set'
+ canReadUserLocation = false;
+ trackingType = 'set';
+ $('#glmMyLocationSet').prop('checked', true);
+
+ // Let user know that we're doing this.
+ alert('Your "My Location" setting has been changed to "Manual Map Selection".'+"\n\n"
+ +'You either selected "Manual Map Selection" or your are doing a search with "Anywhere" selected.'+"\n\n"
+ +'You may use the "Set My Location Here" button to select the center of the current map as your "My Location" and return to that location as desired.'+"\n\n"
+ +'You may return to automatically tracking your location by selecting "Follow Me" at any time.');
+
+ $('#glmMapSetMyLocation').removeClass('glm-hidden');
+
+ }
+
+ function showLoadingMsg() {
+ $('#glmMapReload').removeClass('glm-hidden');
+ }
+ function hideLoadingMsg() {
+ $('#glmMapReload').addClass('glm-hidden');
+ }
+
+ function closeMapBubble() {
+ if (mapBubble) {
+ mapBubble.close();
+ }
+ }
+ function closeMapTitle() {
+ if (mapTitle) {
+ mapTitle.close();
+ }
+ }
+
+ // Get it all started
+ initMap();
+
+ }); // jquery
+
+ (function($) { $.fn.catchEnter = function(sel) {
+ return this.each(function() {
+ $(this).on('keyup',sel,function(e){
+ if(e.keyCode == 13)
+ $(this).trigger("enterkey");
+ })
+ });
+ };
+ })(jQuery);
+
+ </script>
+
+{include file='front/footer.html'}
\ No newline at end of file