/* @Version 5.8 */

//=======================//
//==== Map functions ====//
//=======================//

	window.cspm_global_object = {};
	
	var map_layout = {};
	var heatmap = {}; //@since 3.3	
	var all_map_markers_objs = {}; //@since 5.0
	var map_visited_markers = {}; //@since 5.8
	var map_visited_viewports = {}; //@since 5.8

	/**
	 * This will validate the latitude & longitude 
	 * @since 3.6 
	 */	 
	function cspm_validate_latLng(lat, lng){

		if(lat < -90 || lat > 90 || lat.toString().indexOf(',') != -1 || isNaN(Math.floor(lat))){
			return false;
		}else if(lng < -180 || lng > 180 || lng.toString().indexOf(',') != -1 || isNaN(Math.floor(lng))){
			return false;
		}else if(lat == "" || lng == ""){
			return false;
		}else return new google.maps.LatLng(lat, lng);
				
	}
	
	/**
	 * Load map options
	 *
	 * @light_map, Declare the light map in order to use the apropriate options for this type of map.
	 * @latLng, The center point of the map.
	 * @zoom, The default zoom of the map.
	 *
     * @updated 5.8
	 */
	function cspm_load_map_options(map_id, light_map, latLng, zoom){
      
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		const latlng = (latLng != null) ? latLng.split(',') : progress_map_vars.map_script_args[map_script_id]['center'].split(',');
		
		if(!cspm_validate_latLng(latlng[0], latlng[1])){
			
			const title = 'The map center is incorrect!<br /><br />';
			const message  = 'Edit your map. Click on the menu <strong>\"Map settings\"</strong> and ...</strong><br />';
				message += '<strong>1.</strong> Make sure the Laitude & Longitude are comma separated.<br />';
				message += '<strong>2.</strong> Make sure the Latitude ranges between -90 and 90 degrees.<br />';
				message += '<strong>3.</strong> Make sure the Longitude ranges between -180 and 180 degrees.<br />';
				message += '<strong>4.</strong> Make sure the Latitude coordinate is written first, followed by the Longitude.<br />';
				message += '<strong>5.</strong> Make sure to replace the comma in the Latitude and/or the Longitude by a dot.';
			cspm_popup_msg(map_id, 'fatal_error', title, message);
		
		}
		
		const zoom_value = (zoom != null) ? parseInt(zoom) : parseInt(progress_map_vars.map_script_args[map_script_id]['zoom']);
		const max_zoom_value = parseInt(progress_map_vars.map_script_args[map_script_id]['max_zoom']);
		const min_zoom_value = parseInt(progress_map_vars.map_script_args[map_script_id]['min_zoom']);		
		
        const zoom_on_doubleclick = (progress_map_vars.map_script_args[map_script_id]['zoom_on_doubleclick'] == 'true') ? true : false;
   
		const default_options = {		
            mapId: map_id, //@since 5.8
			center:[latlng[0], latlng[1]],
			maxZoom: max_zoom_value,
			minZoom: min_zoom_value,				
			mapTypeControl: (progress_map_vars.map_script_args[map_script_id]['mapTypeControl'] == 'false') ? false : true,
			mapTypeControlOptions: {
				style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR,
				position: google.maps.ControlPosition.TOP_RIGHT,
				mapTypeIds: [
					google.maps.MapTypeId.ROADMAP,
					google.maps.MapTypeId.SATELLITE,
					google.maps.MapTypeId.TERRAIN,
					google.maps.MapTypeId.HYBRID
				]				
			},
			streetViewControl: (progress_map_vars.map_script_args[map_script_id]['streetViewControl'] == 'false') ? false : true,	
			streetViewControlOptions: {
				position: google.maps.ControlPosition.RIGHT_TOP  
			},
			gestureHandling: progress_map_vars.map_script_args[map_script_id]['gestureHandling'], //@since 5.6.5
			disableDoubleClickZoom: zoom_on_doubleclick,
			backgroundColor: progress_map_vars.map_script_args[map_script_id]['map_background'],
			clickableIcons: (progress_map_vars.map_script_args[map_script_id]['clickableIcons'] == 'false') ? false : true,
			rotateControl: (progress_map_vars.map_script_args[map_script_id]['rotateControl'] == 'false') ? false : true,
			rotateControlOptions: {
				position: google.maps.ControlPosition.RIGHT_TOP,			
			},
			scaleControl: (progress_map_vars.map_script_args[map_script_id]['scaleControl'] == 'false') ? false : true,			
			fullscreenControl: (progress_map_vars.map_script_args[map_script_id]['fullscreenControl'] == 'false') ? false : true,
			/*fullscreenControlOptions:{
				position: google.maps.ControlPosition.BOTTOM_LEFT
			}*/
			controlSize: progress_map_vars.map_script_args[map_script_id]['controlSize'], //@since 4.8			
		};
        
        let zoom_options = {
            zoomControl: false,
        };
        
		if(progress_map_vars.map_script_args[map_script_id]['zoomControl'] == 'true' 
           && progress_map_vars.map_script_args[map_script_id]['zoomControlType'] == 'default'
          ){
			zoom_options = {
				zoomControl: true,
				zoomControlOptions:{
					//position: google.maps.ControlPosition.LEFT_BOTTOM,
				},
			};
		}
        
        const autofit = progress_map_vars.map_script_args[map_script_id]['autofit']; //@since 5.8
        
        if(autofit !== 'true'){
            zoom_options = jQuery.extend({}, zoom_options, {zoom: zoom_value});
        } //@since 5.8 | Add zoom parameter only when "Autofit" is disabled to facilitate a smooth map transition from the default zoom to the autofit setting.
		
		const map_options = jQuery.extend({}, default_options, zoom_options);

		return map_options;
		
	}					
	
	/**
	 * Set the initial map style
	 * @since 2.4
     * @udpated 5.8
	 */
	function cspm_initial_map_style(map_style, custom_style_status){
			
		if(map_style == 'custom_style' && custom_style_status == false){
			return {mapTypeId: google.maps.MapTypeId.ROADMAP};
		}else if(map_style == 'custom_style'){
			return {mapTypeId: "custom_style"};
		}else if(map_style == 'SATELLITE'){
			return {mapTypeId: google.maps.MapTypeId.SATELLITE};
		}else if(map_style == 'TERRAIN'){				
			return {mapTypeId: google.maps.MapTypeId.TERRAIN};
		}else if(map_style == 'HYBRID'){				
			return {mapTypeId: google.maps.MapTypeId.HYBRID};
		}
        
	}
	
	var post_ids_and_categories = {};
	var post_lat_lng_coords = {};
	var post_ids_and_child_status = {}
	var post_id_and_child_ids = {} //@since 5.8

	/**
	 * Create Marker Object
	 *
	 * @Since 2.5 
	 * @Updated 3.5 | 4.0 | 5.8
	 */
	function cspm_new_pin_object(map_id, pin_options){

		const post_id = pin_options.post_id;
		const post_categories = pin_options.post_categories;
		const lat = pin_options.coordinates.lat;
		const lng = pin_options.coordinates.lng;
		const marker_img = pin_options.icon.url;
		const marker_size = pin_options.icon.size;
		const is_child = pin_options.is_child;
		const media = pin_options.media;
		const address = pin_options.coordinates.address; //@since 5.4
		const zindex = pin_options.zindex; //@since 5.8
        
        const parent_post_id = String(post_id).includes('-') 
            ? String(post_id).split('-')[0] 
            : post_id; //@since 5.8 | In case of child markers, get the parent post ID!
        
		if(!cspm_validate_latLng(lat, lng))
			return false;		

		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		 
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		post_lat_lng_coords[map_id][post_id] = lat+'_'+lng;
	
		/**
		 * Create an object of that post_id and its categories/status for the faceted search */
		 
		post_lat_lng_coords[map_id]['post_id_'+post_id] = {};
		post_ids_and_child_status[map_id][lat+'_'+lng] = is_child;
		
		/**
		 * Get the current post categories */
		 
		const post_category_ids = (post_categories != '') ? post_categories.split(',') : '';
		
		/**
		 * Collect an object of all posts in the map and their categories
		 * Useful for the faceted search & the search form */
		 
		post_lat_lng_coords[map_id]['post_id_'+post_id][0] = post_category_ids;
		
		/**
		 * By default the marker image is the default Google map red marker */	
        
        if(progress_map_vars.map_script_args[map_script_id]['defaultMarker'] === 'customize'){
            marker_icon = cspm_marker_icon_obj(map_id, marker_img, marker_size); //@since 5.8
        }
        
		/**
		 * Marker title
		 * @since 4.0
         * @edited 5.8 | User "parent_post_id" to get titles so that child markers has also titles! */
		
		const title = (typeof progress_map_vars['titles_'+map_id] !== 'undefined' 
                       && typeof progress_map_vars['titles_'+map_id][parent_post_id] !== 'undefined'
                      ) ? progress_map_vars['titles_'+map_id][parent_post_id] : ''; 

		/**
		 * Marker labels
		 * @since 4.0 */
								
		const marker_label_obj = cspm_marker_label_obj(map_id, post_id);
		let marker_label = {};
		
		if(cspm_object_size(marker_label_obj) > 0){
			
			marker_label = { label: marker_label_obj };
		
			/**
			 * Marker label origin */

			if(typeof marker_label_obj.top !== 'undefined' && !isNaN(marker_label_obj.top) && typeof marker_label_obj.left !== 'undefined' && !isNaN(marker_label_obj.left))
				marker_icon = jQuery.extend({}, marker_icon, { labelOrigin: new google.maps.Point(parseInt(marker_label_obj.top), parseInt(marker_label_obj.left)) });			
		
		}
		
		/**
		 * Build the marker object */

		let pin_options_obj = { 
			optimized: false, 
			icon: marker_icon, 
			id: post_id, 
			post_id: post_id, 
			is_child: is_child,
			parent_post_id: parent_post_id, //@since 5.8
			media: media,
			title: title, //@since 4.0
			map_id: map_id, //@since 4.6
            zIndex: zindex, //@since 5.8
		};
		
		pin_options_obj = jQuery.extend({}, pin_options_obj, marker_label);
	
		const pin_object = {
			latLng: [lat, lng], 
			tag: ['post_id__'+ post_id, 'to_cluster'],
			id: post_id +'_'+ is_child,
			options: pin_options_obj,
		};	
		
		return pin_object;									
	
	}
    
    /**
     * This will set and return the marker icon object
     * @since 5.8 */

    function cspm_marker_icon_obj(map_id, marker_img, marker_size){

		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		 
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
        
        if(marker_img === '')
            return '';
        
        let marker_icon = {
            url: marker_img,
        };

        /**
         * Marker image size with retina support */
        
        const isRetina = (progress_map_vars.map_script_args[map_script_id]['retinaSupport'] === 'true' && !cspm_is_svg(marker_img));
        
        const marker_img_width = isRetina
            ? parseInt(marker_size.split('x')[0])/2 
            : parseInt(marker_size.split('x')[0]);

        const marker_img_height = isRetina
            ? parseInt(marker_size.split('x')[1])/2 
            : parseInt(marker_size.split('x')[1]);

        /**
         * Marker image anchor point */

        let anchor_point = null;
        
        const anchor_y = marker_img_height/2;
        const anchor_x = marker_img_width/2;	
        
        const markerAnchorPointOption = progress_map_vars.map_script_args[map_script_id]['marker_anchor_point_option'];
        const markerAnchorPoint = progress_map_vars.map_script_args[map_script_id]['marker_anchor_point'];
        
        if(markerAnchorPointOption === 'auto'){				
            anchor_point = new google.maps.Point(anchor_x, anchor_y);
        }else if(markerAnchorPointOption === 'manual'){
            if(isRetina){
                anchor_point = new google.maps.Point(
                    parseInt(markerAnchorPoint.split(',')[0]/2), 
                    parseInt(markerAnchorPoint.split(',')[1]/2)
                );
            }else{ 
                anchor_point = new google.maps.Point(
                    parseInt(markerAnchorPoint.split(',')[0]), 
                    parseInt(markerAnchorPoint.split(',')[1])
                );
            }
        }

        /**
         * Marker icon */

        if(marker_img_width > 0 && marker_img_height > 0){
            marker_icon = jQuery.extend({}, marker_icon, {					
                anchor: anchor_point,
                scaledSize: new google.maps.Size(marker_img_width, marker_img_height),
                size: new google.maps.Size(marker_img_width, marker_img_height),
            });
        }

        return marker_icon;
        
    }
	
	/**
	 * This will return the marker label object.
	 *
	 * @since 4.0
     * @edited 5.8
	 */
	function cspm_marker_label_obj(map_id, post_id){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		 
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		const marker_labels = progress_map_vars.map_script_args[map_script_id]['marker_labels']; //@since 4.0
        
        post_id = String(post_id).includes('-') ? String(post_id).split('-')[0] : post_id; //@since 5.8 | Make sure child markers also has the marker label of the parent!

		if(marker_labels == 'yes' 
           && typeof progress_map_vars['marker_labels_'+map_id] !== 'undefined' 
           && typeof progress_map_vars['marker_labels_'+map_id][post_id] !== 'undefined' 
           && typeof progress_map_vars['marker_labels_'+map_id][post_id]['hide'] !== 'undefined' 
           && progress_map_vars['marker_labels_'+map_id][post_id]['hide'].toString() == 'no'
           && progress_map_vars['marker_labels_'+map_id][post_id]['text'].toString() != ''
          ){

			return {
				text: progress_map_vars['marker_labels_'+map_id][post_id]['text'].toString(),
				color: progress_map_vars['marker_labels_'+map_id][post_id]['color'].toString(),
				fontFamily: progress_map_vars['marker_labels_'+map_id][post_id]['fontFamily'].toString(),
				fontSize: progress_map_vars['marker_labels_'+map_id][post_id]['fontSize'].toString(),
				fontWeight: progress_map_vars['marker_labels_'+map_id][post_id]['fontWeight'].toString(),
				top: parseInt(progress_map_vars['marker_labels_'+map_id][post_id]['top']),
				left: parseInt(progress_map_vars['marker_labels_'+map_id][post_id]['left']),
			};
				
		}else return {}; 
	
	}
	
	/**
	 * This will check if an image is an SVG icon
	 *
	 * @since 4.0
	 */
	function cspm_is_svg(file){
		const ext = file.substr( (file.lastIndexOf('.') +1) );
		return (ext == 'svg') ? true : false;		
	}
	
	/**
	 * This will set a label for each marker on the map.
	 *
	 * Note: This will prevent an issue where marker labels stay visible when markers are not in the viewport of the map!
	 *
	 * @since 4.0
     * @updated 5.8
	 */
	function cspm_set_marker_labels_visibility(plugin_map, map_id){

		let r = jQuery.Deferred();
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
		 
		const marker_labels = progress_map_vars.map_script_args[map_script_id]['marker_labels'];

        if(marker_labels == 'no'){
            r.resolve();
            return r;
        }
        
		plugin_map.gmap3({
			get: {
				name: 'marker',
				tag: 'to_cluster',
				all: true,
				callback: function(markers){

					for(let i = 0; i < markers.length; i++){	
						
						let marker = markers[i];
						let post_id = parseInt(marker.post_id);

						if(post_id != '' && post_id != 'user_location' && !isNaN(post_id)){
													
							let latLng = marker.position;
							let lat = latLng.lat();
							let lng = latLng.lng();

							// if the marker is within the viewport of the map
							if(cspm_map_bounds_contains_marker(plugin_map, lat, lng) 
                               && marker.getMap() != null 
                               && marker.getVisible() === true //@edited 5.8
                              ){
								
								if(marker.getLabel() == null){
									let marker_label = cspm_marker_label_obj(map_id, post_id);
									if(cspm_object_size(marker_label) > 0)
										marker.setLabel(marker_label);
								}
								
							}else marker.setLabel(null);
							
						}
						
					}
				
					r.resolve(); 
																												
				}
			}
		});
		
		return r;
		
	}
	
	/**
	 * Clustering markers 
     * @updated 5.8 */

    var markerCluster = {}; //@sine 5.8

    function cspm_clustering(plugin_map, map_id, markers){ //@edited 5.8
        
        if(typeof markers !== 'object')
            return;
        
        /**
         * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
         * @since 3.0 */
        const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

        const mapObject = plugin_map.gmap3('get');
        const light_map = (jQuery('#cspm_carousel_'+map_id).length > 0) ? false : true //@since 5.8
        const max_zoom = parseInt(progress_map_vars.map_script_args[map_script_id]['max_zoom']);

        const objs = markers;

        /**
         * Detach all markers from the map to gain in performance. Clustering will attach them again!
         * @since 5.8 */

        for(let i = 0; i < objs.length; i++){
            let marker = objs[i];                    
            marker.setMap(null);
        }

        /**
         * Remove invisible markers from the markers object
         * @since 5.8 */

        for(let i = 0; i < objs.length; i++){
            let marker = objs[i];                    
            if(marker.getVisible() === false){                            
                objs.splice(i, 1); // If the marker is not visible, remove it from the markers object                            
                i--; // Decrement the index to adjust for the removed element
            }
        }

        /**
         * Start Clustering 
         * @edited 5.8 */

        if(objs && typeof markerClusterer.MarkerClusterer !== 'undefined'){ //@edited 5.8

            markerCluster[map_id] = new markerClusterer.MarkerClusterer({
                map: mapObject,
                markers: objs,
                algorithm: new markerClusterer.SuperClusterAlgorithm({ 
                    maxZoom: max_zoom, 
                    radius: parseInt(progress_map_vars.map_script_args[map_script_id]['grid_size'])
                }),
                renderer: {
                    render({ count, position }, stats){

                        const clusters_style = progress_map_vars.map_script_args[map_script_id]['clusters_style'];

                        let icon_obj = {};

                        /**
                         * Colored clusters */

                        if(clusters_style == 'colored'){

                            const clusters_colors_array = [
                                progress_map_vars.map_script_args[map_script_id]['colored_clusters_start_color'],
                                progress_map_vars.map_script_args[map_script_id]['colored_clusters_middle_color'],
                                progress_map_vars.map_script_args[map_script_id]['colored_clusters_end_color']
                            ].filter(color => color !== null && color.trim() !== '');

                            const stops = parseInt(progress_map_vars.map_script_args[map_script_id]['colored_clusters_stops']);

                            const custom_gradient = gradstop({
                                stops: stops,
                                inputFormat: 'rgb',
                                colorArray: clusters_colors_array
                            });

                            const getColorByMarkerCount = function(markerCount, colors){
                                const minMarkers = 2; // Minimum number of markers for the first color
                                const colorIndex = Math.floor(Math.min(markerCount - minMarkers, colors.length - 1));
                                return colors[colorIndex]; // Return the color from the colors array
                            }

                            const clusters_size = parseInt(progress_map_vars.map_script_args[map_script_id]['colored_clusters_size']);                            

                            const color = getColorByMarkerCount(count, custom_gradient);

                            const svgString = '<svg fill="'+color+'" width="'+clusters_size+'" height="'+clusters_size+'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">'+
                                '<circle cx="20" cy="20" r="20" opacity=".3"/>' +
                                '<circle cx="20" cy="20" r="16" opacity=".9"/>' +
                            '</svg>';
                            
                            const cluster_svg = window.btoa(svgString.trim());

                            icon_obj = {
                                url: 'data:image/svg+xml;base64,'+cluster_svg,
                                scaledSize: new google.maps.Size(clusters_size, clusters_size),
                                anchor: new google.maps.Point(clusters_size / 2, clusters_size / 2),
                            };

                        /**
                         * Image Clusters */

                        }else{

                            const interpolateImage = function(count, stats){

                                let imageUrl, imageSize, imageAnchor;

                                if(count < stats.clusters.markers.min + (stats.clusters.markers.max - stats.clusters.markers.min) / 3){                                                						

                                    const small_cluster_size = progress_map_vars.map_script_args[map_script_id]['small_cluster_size'];                                                
                                    const small_cluster_height = parseInt(small_cluster_size.split('x')[0]);
                                    const small_cluster_width = parseInt(small_cluster_size.split('x')[1]);

                                    imageUrl = progress_map_vars.map_script_args[map_script_id]['small_cluster_icon'];
                                    imageSize = new google.maps.Size(small_cluster_width, small_cluster_height);
                                    imageAnchor = new google.maps.Point(small_cluster_width/2, small_cluster_height/2);

                                }else if(count < stats.clusters.markers.min + (2 * (stats.clusters.markers.max - stats.clusters.markers.min)) / 3){

                                    const medium_cluster_size = progress_map_vars.map_script_args[map_script_id]['medium_cluster_size']
                                    const medium_cluster_height = parseInt(medium_cluster_size.split('x')[0]);
                                    const medium_cluster_width = parseInt(medium_cluster_size.split('x')[1]);

                                    imageUrl = progress_map_vars.map_script_args[map_script_id]['medium_cluster_icon'];
                                    imageSize = new google.maps.Size(medium_cluster_width, medium_cluster_height);
                                    imageAnchor = new google.maps.Point(medium_cluster_width/2, medium_cluster_height/2);

                                }else{

                                    const big_cluster_size = progress_map_vars.map_script_args[map_script_id]['big_cluster_size'];
                                    const big_cluster_height = parseInt(big_cluster_size.split('x')[0]);
                                    const big_cluster_width = parseInt(big_cluster_size.split('x')[1]);

                                    imageUrl = progress_map_vars.map_script_args[map_script_id]['big_cluster_icon'];
                                    imageSize = new google.maps.Size(big_cluster_width, big_cluster_height);
                                    imageAnchor = new google.maps.Point(big_cluster_width/2, big_cluster_height/2);

                                }

                                return { imageUrl, imageSize, imageAnchor };

                            }

                            const image = interpolateImage(count, stats);

                            icon_obj = {
                                url: image.imageUrl,
                                scaledSize: image.imageSize,
                                anchor: image.imageAnchor,
                            };

                        }

                        /**
                         * Create cluster (marker) */

                        const clusterCount = (progress_map_vars.map_script_args[map_script_id]['clusters_count_abbreviating'] == 'yes') 
                            ? String(cspm_compact_number(count)) 
                            : String(count);

                        return new google.maps.Marker({
                            position,
                            title: progress_map_vars.cluster_text,
                            icon: icon_obj,
                            label: {
                                text: clusterCount,
                                color: progress_map_vars.map_script_args[map_script_id]['cluster_text_color'],
                                fontFamily: 'monospace',
                                fontSize: '12px',
                                fontWeight: 'bold',
                            },
                            zIndex: Number(google.maps.Marker.MAX_ZINDEX) +  count, // adjust zIndex to be above other markers
                        });

                    }
                },
            }); //@edited 5.8	

            let cluster_xhr;				

            const main_hex_color = (typeof progress_map_vars.map_script_args['initial']['main_hex_color'] !== 'undefined') 
                ? progress_map_vars.map_script_args['initial']['main_hex_color'] : "#189AC9"; //@since 4.7

            /**
             * Draw a convex hull around the cluster area
             * @since 5.8 */

            google.maps.event.addListener(markerCluster[map_id], 'clusteringend', function(cluster){                            

                markerCluster[map_id].clusters.forEach(function(cluster) {

                    /**
                     * Return if the cluster contains only one marker. This will prevent adding "mouseover" ...
                     * ... listener to all markers.
                     * Clusters with only one marker are considered simple markers, not clusters.
                     * Note: The new "MarkerClusterer" API treats a cluster as a marker. ...
                     * ... To distinguish between clusters and markers on the map, ...
                     * ... we check the number of markers in "cluster.markers". If it's only one, it's a simple marker, ...
                     * ... if it's more than one, it's a cluster! */

                    if(cluster.markers.length === 1) 
                        return; 

                    cluster.marker.addListener('mouseover', function(){

                        if(typeof cluster.markers !== 'undefined' 
                           && cluster.markers.length > 2 // Draw a polygon if the cluster contains more than 2 markers
                           && typeof getConvexHull === 'function' 
                          ){

                            const convexHull = getConvexHull(cspm_get_markers_positions(cluster.markers));

                            plugin_map.gmap3({
                                clear:{
                                    tag: 'cluster_area',
                                },
                                polygon:{
                                    options:{
                                        paths: convexHull,
                                        strokeColor: main_hex_color,
                                        strokeOpacity: 0.5,
                                        strokeWeight: 3,
                                        fillColor: main_hex_color,
                                        fillOpacity: 0.3,
                                    },
                                    tag: 'cluster_area',
                                }
                            });

                        }
                        
                    });

                    cluster.marker.addListener('mouseout', function(){
                        plugin_map.gmap3({
                            clear:{
                                tag: 'cluster_area',
                            }								  
                        });
                    });

                });

            });

            /**
             * On cluster click, Hide and show overlays depending on markers positions */

            google.maps.event.addListener(markerCluster[map_id], 'click', function(cluster){

                if(typeof NProgress !== 'undefined'){
                    NProgress.configure({
                      parent: 'div#codespacing_progress_map_div_'+map_id,
                      showSpinner: false
                    });				
                    NProgress.start();
                }		

                jQuery('div.cspm_top_element').hide(); //@since 5.8

                /**
                 * Remove the polygon around the cluster area
                 * @since 3.6 */

                plugin_map.gmap3({
                    clear:{
                        tag: 'cluster_area',
                    }								  
                });

                /**
                 * Show locations inside the cluster */

                let current_zoom = mapObject.getZoom();	// Note: Must get the current zoom before "idle" event!
                let isIdle = false; // Check if map "idle" event was fired!
                let r = jQuery.Deferred(); // Check if map "idle" event was completed!

                google.maps.event.addListenerOnce(mapObject, 'idle', function(){

                    if(typeof NProgress !== 'undefined')
                        NProgress.set(0.5);

                    isIdle = true;

                    /**
                     * Fallback: Increase the map zoom level when the cluster radius/grid-size is set to a high value (500 or more) ...
                     * ... and the cluster expands to its maximum extent such that clicking on it ...
                     * ... does not zoom in to display child clusters within it! */

                    if(current_zoom < max_zoom && current_zoom == mapObject.getZoom()){
                        mapObject.setZoom(current_zoom + 1);
                    }

                    /**
                     * Fallback: Show map elements that are supposed to be visible on map but prevent removing the modal ...
                     * ... that appears when clustering reaches max zoom and that multiple markers with same location are still inside the cluster! */

                    /*setTimeout(function(){
                        if(current_zoom < max_zoom || (progress_map_vars.map_script_args[map_script_id]['initial_map_style'] == 'TERRAIN' && current_zoom < 15)){
                            //cspm_zoom_in_and_out(plugin_map);
                            google.maps.event.trigger(mapObject, 'idle');
                        }
                    }, 500);*/

                    /**
                     * Display the list of markers in the same place */

                    if(current_zoom >= max_zoom || (progress_map_vars.map_script_args[map_script_id]['initial_map_style'] == 'TERRAIN' && current_zoom >= 15)){

                        let cluster_markers = cluster.markers; //@edited 5.8

                        current_zoom = mapObject.getZoom();	

                        // Get cluster position and convert it to XY
                        const scale = Math.pow(2, current_zoom);
                        const nw = new google.maps.LatLng(mapObject.getBounds().getNorthEast().lat(), mapObject.getBounds().getSouthWest().lng());
                        const worldCoordinateNW = mapObject.getProjection().fromLatLngToPoint(nw);
                        const worldCoordinate = mapObject.getProjection().fromLatLngToPoint(cluster.position); //@edited 5.8
                        const pixelOffset = new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
                        const mapposition = plugin_map.position();

                        let count_li = 0;						

                        const clustred_post_ids = []; //@since 2.5
                        const is_child_clustered_posts = {}; //@since 5.8

                        if(typeof cluster_markers !== 'undefined'){

                            for(let i = 0; i < cluster_markers.length; i++ ){                                            
                                if(cluster_markers[i].getVisible() === true){ //@edited 5.8												
                                    count_li++;
                                    let marker_post_id = cluster_markers[i].id; //@since 5.8	
                                    clustred_post_ids.push(marker_post_id); //@since 2.5									
                                    is_child_clustered_posts[marker_post_id] = cluster_markers[i].is_child; //@since 5.8																					
                                }
                            }

                            const implode_clustred_post_ids = clustred_post_ids.join().replace(',', ''); //@since 3.5										

                            setTimeout(function(){

                                const cluster_id = jQuery('div.cluster_posts_widget_'+map_id).attr('data-cluster-id');

                                if(cluster_id == implode_clustred_post_ids)
                                    return; // Prevent re-loading the same data if already exists!

                                jQuery('div.cluster_posts_widget_'+map_id).html('<div class="cspm_infobox_spinner cspm_border_top_after_hex"></div></div>');

                                if(count_li > 0){

                                    jQuery('div.cluster_posts_widget_'+map_id).removeClass('flipOutX');
                                    jQuery('div.cluster_posts_widget_'+map_id).addClass('cspm_animated flipInX').css('display', 'block');
                                    jQuery('div.cluster_posts_widget_'+map_id).css({
                                        left: (((pixelOffset.x / 2) - mapposition.left) - 70) + 'px', 
                                        top: (((pixelOffset.y / 2) + mapposition.top) - 20) + 'px' //@edited 5.8
                                    });	

                                    jQuery('div.cluster_posts_widget_'+map_id).attr('data-cluster-id', implode_clustred_post_ids);

                                    if(cluster_xhr && cluster_xhr.readystate != 4){
                                        cluster_xhr.abort();
                                    }

                                    cluster_xhr = jQuery.post(
                                        progress_map_vars.ajax_url,
                                        {
                                            action: 'cspm_load_clustred_markers_list',
                                            post_ids: clustred_post_ids,
                                            is_child_clustered_posts: is_child_clustered_posts, //@since 5.8
                                            map_object_id: progress_map_vars.map_script_args[map_script_id]['map_object_id'],
                                            map_settings: progress_map_vars.map_script_args[map_script_id]['map_settings'],																	
                                            light_map: light_map
                                        },
                                        function(data){	

                                            jQuery('div.cluster_posts_widget_'+map_id).html(data);

                                            /**
                                             * Call custom scroll bar for infowindow 

                                            if(typeof jQuery('div.cluster_posts_widget_'+map_id).mCustomScrollbar === 'function'){

                                                jQuery('div.cluster_posts_widget_'+map_id).mCustomScrollbar("destroy");
                                                cspm_apply_custom_scrollabr('div.cluster_posts_widget_'+map_id);

                                            } */                                                     

                                            //cspm_destroy_csutom_scrollbar('div.cluster_posts_widget_'+map_id);
                                            //cspm_apply_custom_scrollabr('div.cluster_posts_widget_'+map_id);

                                            jQuery(document).on('click', 'div.cluster_posts_widget_'+map_id+' ul li', function(){ //@edited 5.6.3
                                                const post_id = jQuery(this).attr('data-post-id');											
                                                const is_child = jQuery(this).attr('data-is-child'); //@since 5.8											
                                                if(!light_map){ 
                                                    const carousel_item = '.cspm_carousel_item[data-map-id='+map_id+'][data-post-id='+post_id+'][data-is-child='+is_child+']'; //@since 5.8
                                                    cspm_slide_to_carousel_item(map_id, post_id, true); //@edited 5.8
                                                    cspm_apply_carousel_item_hover_style(carousel_item, map_id); //@edited 5.8
                                                }
                                            });

                                        }
                                    );												

                                }else jQuery('div.cluster_posts_widget_'+map_id).css({'display':'none'});

                                //google.maps.event.trigger(mapObject, 'idle'); //@since 5.8 | Fallback: Show marker elements (e.g "Labels") that are supposed to be visible!

                            }, 1000);

                        }													

                    }

                    r.resolve().then(function(){

                        /**
                         * End the Progress Bar Loader */

                        if(typeof NProgress !== 'undefined')
                            NProgress.done();

                    });

                });

                /**
                 * Fallback: Ensure that the map "idle" event is triggered. This event may not be triggered when ...
                 * ... clicking on a cluster (with a big radius/grid-size value) already centered within the map viewport! */

                setTimeout(function(){
                    if(r.state() === 'pending' && !isIdle){
                        google.maps.event.trigger(mapObject, 'idle');                                        
                    }
                }, 1000);

            });

        }
        
        return markerCluster[map_id];

    }

    /**
     * This will cause the map to zoom-in and out, helping to reveal any elements that ...
     * ... may have been hidden while interacting with it, such as during clustering or dragging actions.
     * @edited 5.8 
     */
	function cspm_zoom_in_and_out(plugin_map){
		const mapObject = plugin_map.gmap3('get');
        if(typeof mapObject.getZoom === 'function'){ //@since 5.8
            mapObject.setZoom(mapObject.getZoom() - 1);
            mapObject.setZoom(mapObject.getZoom() + 1);
        }
	}

    /**
     * This will clear all clusters from the map and reset "markerClusterer"
     * @edited 5.8
     */
	function cspm_simple_clustering(plugin_map, map_id){
        if(typeof markerCluster[map_id] !== 'undefined'){
            plugin_map.gmap3({
                get: {
                    name: 'marker',
                    tag: 'to_cluster',
                    all: true,
                    callback: function(objs){
                        if(typeof markerCluster[map_id].clearMarkers === 'function'){
                            markerCluster[map_id].clearMarkers();  
                        }
                        cspm_clustering(plugin_map, map_id, objs);
                    }
                }
            });
        }
	}
	
	/**
	 * Animate the selected marker 
	 */
	function cspm_animate_marker(plugin_map, map_id, post_id){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		plugin_map.gmap3({
			get: {
				name: 'marker',
				tag: 'post_id__'+post_id,
				callback: function(marker){
				
					if(typeof marker !== 'undefined' && marker.getVisible() === true){ //@edited 5.8
						
						const is_child = marker.is_child;	
						const marker_infobox = 'div.cspm_infobox_container[data-post-id='+post_id+'][data-map-id='+map_id+']';

						if(progress_map_vars.map_script_args[map_script_id]['markerAnimation'] == 'pulsating_circle'){
								
							const mapObject = plugin_map.gmap3('get');
	
							// Get marker position and convert it to XY
							const scale = Math.pow(2, mapObject.getZoom());
							const nw = new google.maps.LatLng(mapObject.getBounds().getNorthEast().lat(), mapObject.getBounds().getSouthWest().lng());
							const worldCoordinateNW = mapObject.getProjection().fromLatLngToPoint(nw);
							const worldCoordinate = mapObject.getProjection().fromLatLngToPoint(marker.position);
							const pixelOffset = new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
							const mapposition = plugin_map.position();
		
							jQuery('div#pulsating_holder.'+map_id+'_pulsating').css({'display':'block', 'left':(pixelOffset.x + mapposition.left - 15 + 'px'), 'top':(pixelOffset.y + mapposition.top - 18 + 'px')});
							setTimeout(function(){
								jQuery('div#pulsating_holder.'+map_id+'_pulsating').css('display', 'none');
							},1500);
							
						}else if(progress_map_vars.map_script_args[map_script_id]['markerAnimation'] == 'bouncing_marker'){
						 								
							marker.setAnimation(google.maps.Animation.BOUNCE);
							setTimeout(function(){
								marker.setAnimation(null);
							},1500);
							
						}else if(progress_map_vars.map_script_args[map_script_id]['markerAnimation'] == 'flushing_infobox'){						
							
							jQuery('div.cspm_infobox_container[data-map-id='+map_id+']').removeClass('cspm_animated flash');
							setTimeout(function(){								
								jQuery(marker_infobox).addClass('cspm_animated flash');
								jQuery(marker_infobox).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){
									jQuery(marker_infobox).removeClass('flash');
								});
							}, 600);
							
						}

					}
					
				}
			}
		});
	
	}

	/**
     * Zoom-in function
     * @updated 5.8 
     */
	function cspm_zoom_in(map_id, selector, plugin_map){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

        jQuery(document).on('click', selector, function(){ //@edited 5.8
			
			const map = plugin_map.gmap3("get");
			const current_zoom = map.getZoom();
			
			if(current_zoom < progress_map_vars.map_script_args[map_script_id]['max_zoom'])
	    		map.setZoom(current_zoom + 1);

			jQuery('div[class^=cluster_posts_widget]').removeClass('flipInX');
			jQuery('div[class^=cluster_posts_widget]').addClass('cspm_animated flipOutX');

		});
		
	}

	/**
     * Zoom-out function
     * @updated 5.8
     */
	function cspm_zoom_out(map_id, selector, plugin_map){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		jQuery(document).on('click', selector, function(){ //@edited 5.8
            
			const map = plugin_map.gmap3("get");
			const current_zoom = map.getZoom();
			
			if(current_zoom > progress_map_vars.map_script_args[map_script_id]['min_zoom'])
				map.setZoom(current_zoom - 1);

			jQuery('div[class^=cluster_posts_widget]').removeClass('flipInX');
			jQuery('div[class^=cluster_posts_widget]').addClass('cspm_animated flipOutX');
			
		});
		
	}
	
//============================//
//==== Carousel functions ====//
//============================//

	/**
	 * Initialize carousel
	 * @updated 5.8
	 */

    var cspm_carousel = {};
    var virtual_carousel_outputs = []; //@since 5.8
    var virtual_carousel_outputs_indexes = []; //@since 5.8

    function cspm_init_carousel(map_id){        
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		const carousel_id = map_id;

		if(progress_map_vars.map_script_args[map_script_id]['show_carousel'] == 'true' 
           && (['fullscreen-map', 'fit-in-map'].includes(map_layout[map_id]) === false)
          ){
            
            /**
             * [MarkerInteractionOccured] | Listen the marker hover!
             *
             * Note: Before the carousel event "slideChangeTransitionEnd", listen for the "cspmMarkerInteraction" event. ...
             * ... If the event occurs, refrain from moving the carousel; otherwise, proceed with the movement. ...
             * ... This helps ensure that when hovering over a marker, the map stays in place instead of being ...
             * ... centered due to carousel movement. */
                
            let MarkerInteractionOccured = false;

            document.addEventListener('cspmMarkerInteraction', function(event) {
                MarkerInteractionOccured = event.detail.isOccured ? true : false;
            });
		
            /**
             * Swiper Carousel
             * @since 5.8 */
            
            const direction = ([
                'mr-cl', 
                'ml-cr'
            ].includes(map_layout[map_id])) ? 'vertical' : 'horizontal';
            
            const autoplayDelay = parseInt(progress_map_vars.map_script_args[map_script_id]['carousel_auto']);
            
            let fixedSlideSize, fixedSlideWidth, fixedSlideHeight;
                        
            if(progress_map_vars.map_script_args[map_script_id]['items_view'] == 'listview'){ 
                fixedSlideWidth = parseInt(progress_map_vars.map_script_args[map_script_id]['horizontal_item_width']);
                fixedSlideHeight = parseInt(progress_map_vars.map_script_args[map_script_id]['horizontal_item_height']);
                fixedSlideSize = (['mr-cl', 'ml-cr'].includes(map_layout[map_id]) === false) ? fixedSlideWidth : fixedSlideHeight;										
            }else if(progress_map_vars.map_script_args[map_script_id]['items_view'] == 'gridview'){ 
                fixedSlideWidth = parseInt(progress_map_vars.map_script_args[map_script_id]['vertical_item_width']);
                fixedSlideHeight = parseInt(progress_map_vars.map_script_args[map_script_id]['vertical_item_height']);                
                fixedSlideSize = (['mr-cl', 'ml-cr'].includes(map_layout[map_id]) === false) ? fixedSlideWidth : fixedSlideHeight;
            }
                        
            let breakpointWidth = parseInt(progress_map_vars.map_script_args[map_script_id]['carousel_breakpoint_width']);
            let itemsBreakpointWidth = parseInt(progress_map_vars.map_script_args[map_script_id]['carousel_items_breakpoint_width']);
            
            let default_parameters = {  
                direction: direction,                
                initialSlide: 0,                
                slideToClickedSlide: true,
                slidesPerGroup: parseInt(progress_map_vars.map_script_args[map_script_id]['carousel_scroll']),
                pagination: false,
                scrollbar: false,
                hashNavigation: false,
                navigation: {
                    nextEl: '.cspm-swiper-button-next',
                    prevEl: '.cspm-swiper-button-prev',
                },                
                breakpointsBase: 'window',
                breakpoints: {
                    // 0px: Extra Small & Small Devices
                    0: {
                        direction: 'horizontal',
                        slidesPerView: 1,
                    },
                }, 
                roundLengths: true,
                autoHeight: true,
                virtual:{
                    enabled: true,
                    slides: virtual_carousel_outputs[map_id],  
                    addSlidesAfter: 1,
                    addSlidesBefore: 1,
                },
                virtualTranslate: false,
                setWrapperSize: false,                
                watchSlidesVisibility: true,
                watchOverflow: true,                
                watchSlidesProgress: true,            
                on: {
                    afterInit: function(swiper){      

                        cspm_carousel_items_load_callback(carousel_id);      
                        
                        if(['map-tglc-top', 'map-tglc-bottom'].includes(map_layout[map_id]) === false){
                            
                            jQuery('#cspm_carousel_container[data-map-id='+map_id+']').fadeIn(50, function(){
                                
                                let hashSlideTo = false;
                                let fullHash = window.location.hash;
                                let carousel_items_hash = progress_map_vars.map_script_args[map_script_id]['carousel_items_hash'];
                                
                                if(progress_map_vars.map_script_args[map_script_id]['hash_navigation'] === 'yes' 
                                   && fullHash.startsWith('#'+carousel_items_hash)
                                  ){
                                    setTimeout(function(){                                        
                                        let post_id = fullHash.substring(carousel_items_hash.length+1); // Get the hash value (post ID) without the '#item' symbol
                                        let item_index = virtual_carousel_outputs_indexes[map_id].indexOf(String(post_id));
                                        if(item_index !== -1){
                                            cspm_slide_to_carousel_item(map_id, post_id, false);
                                            hashSlideTo = true;   
                                        } 
                                    }, 100);
                                } // Hash navigation
                                
                                if(!hashSlideTo){
                                    const carousel_item = '.cspm_carousel_item[data-map-id='+map_id+'][data-index=0]';
                                    cspm_apply_carousel_item_hover_style(carousel_item, map_id); // Apply carousel first item hover style                                                                 
                                }
                                
                                if(autoplayDelay > 0){
                                    setTimeout(function(){
                                        swiper.autoplay.start();
                                    }, (autoplayDelay * 1000));
                                } // Fallback if autoplay didn't start!  
                                
                            });  
                            
                        }
                        
                    },
                    update: function(swiper){                        
                        cspm_carousel_items_load_callback(carousel_id);
                    },
                    transitionEnd: function(swiper){    
                        cspm_carousel_items_load_callback(carousel_id);
                    },
                    slideChangeTransitionEnd: function(swiper){      
                        // Pan/Center the map to item's related marker
                        if(!MarkerInteractionOccured){
                            if(typeof swiper.activeIndex !== 'undefined'){
                                const item_index = Math.floor(swiper.activeIndex);
                                cspm_request_carousel_item_related_marker(map_id, item_index);
                            }
                        }else MarkerInteractionOccured = false; // After each marker (hover) interaction, set to FALSE!    
                    },
                    slideChange: function(swiper){     
                        // Apply carousel first item hover style
                        if(typeof swiper.activeIndex !== 'undefined'){                            
                            const index = Math.floor(swiper.activeIndex);
                            const carousel_item = '.cspm_carousel_item[data-map-id='+map_id+'][data-index='+index+']';
                            cspm_apply_carousel_item_hover_style(carousel_item, map_id);
                        }
                    },
                    click: function(swiper, event){
                        if(typeof swiper.clickedIndex !== 'undefined'){
                            // Pan/Center the map to item's related marker
                            const item_index = swiper.clickedIndex;
                            cspm_request_carousel_item_related_marker(map_id, item_index); 
                        }
                    },
                    autoplay: function(){
                        // Prevent the map from panning by triggering the custom event "cspmMarkerInteraction"
                        const mapMovementEvent = new CustomEvent('cspmMarkerInteraction', {
                            detail: {
                                isOccured: true
                            }
                        });
                        document.dispatchEvent(mapMovementEvent);
                    },
                    breakpoint: function(swiper, breakpointParams){
                        if(['mr-cl', 'ml-cr'].includes(map_layout[map_id]) === true){
                            swiper.emit('resize');  
                        }
                    },
                    resize: function(swiper){                                                
                        
                        if(!jQuery('.cspm-swiper[data-map-id='+map_id+']').is(':visible'))
                            return; // This will prevent the carousel from resizing before it is displayed inside a tab!
                        
                        let activeIndex = Math.floor(swiper.activeIndex);
                      
                        let screenWidth = window.innerWidth;
                        
                        let swiperWidth = jQuery('.cspm-swiper[data-map-id='+map_id+']').innerWidth() - 20;
                        let swiperHeight = jQuery('.cspm-swiper[data-map-id='+map_id+']').innerHeight() - 20;
                        let swiperSize = (['mr-cl', 'ml-cr'].includes(map_layout[map_id]) === false) ? swiperWidth : swiperHeight;

                        let countSlidesPerView = 1;
                        
                        if(screenWidth > breakpointWidth){
                            countSlidesPerView = (swiperSize / fixedSlideSize);
                        }else if(screenWidth > itemsBreakpointWidth && screenWidth < breakpointWidth){
                             countSlidesPerView = (swiperWidth / fixedSlideWidth);
                        }else if(screenWidth < itemsBreakpointWidth){
                            countSlidesPerView = 1;
                        }
                        
                        swiper.params.slidesPerView = countSlidesPerView;                        
                        swiper.params.spaceBetween = (screenWidth > breakpointWidth) ? Math.round(countSlidesPerView * 10) : 10;
                        
                        let changeDirectionTo = (screenWidth <= breakpointWidth) ? 'horizontal' : direction;
                        
                        if(['mr-cl', 'ml-cr'].includes(map_layout[map_id]) === true){
                            
                            let slides = swiper.slides;
                            
                            let SlidePosition = (screenWidth <= breakpointWidth) 
                                ? {top: 0} 
                                : (swiper.rtl === true) 
                                    ? {right: 0} 
                                    : {left: 0};   
                            
                            for(let i = 0; i < slides.length; i++){    
                                jQuery(slides[i]).css(SlidePosition);
                                if(screenWidth <= breakpointWidth){
                                    jQuery(slides[i]).css({'height': 'auto'});
                                    if(swiper.rtl === true){
                                        jQuery(slides[i]).css({'margin-right': '0'});
                                        jQuery(slides[i]).css({'top': '0'});             
                                    }
                                }else{
                                    jQuery(slides[i]).css({'width': '100%'});
                                    if(swiper.rtl === true){                                        
                                        jQuery(slides[i]).css({'margin-left': '0'});
                                    }
                                }
                            }
                            
                        } // Workaround to reset slides position & dimension when changing direction
                         
                        if(swiper.rtl === true){
                            if(swiper.params.direction == 'horizontal'){
                                swiper.rtlTranslate = true;  
                            }else{
                                swiper.rtlTranslate = false;
                            }
                        }
                        
                        swiper.slideTo(activeIndex); 
                        
                        swiper.changeDirection(changeDirectionTo, false);                           
                        swiper.update();  

                    }                    
                },
            };

            default_parameters.breakpoints[breakpointWidth] = {direction: direction}; // Extend breakpoints
                               
            let rewind_param = {rewind: true};
                               
            /*let rewind_param = (progress_map_vars.map_script_args[map_script_id]['carousel_behavior'] === 'rewind')
                ? {rewind: true} : {loop: true};*/
            
            let speed_param = (progress_map_vars.map_script_args[map_script_id]['carousel_animation'] === 'fast')
                ? {speed: 200} : {speed: 600};
            
            let mousewheel_param = (progress_map_vars.map_script_args[map_script_id]['scrollwheel_carousel'] === 'true')
				? {mousewheel: true} : {};
            
            let allowTouchMove_param = (progress_map_vars.map_script_args[map_script_id]['touchswipe_carousel'] === 'true')
				? {allowTouchMove: true, grabCursor: false,} : {allowTouchMove: false};
            
            let autoplay_param = (autoplayDelay > 0)
                ? {autoplay: {
                    delay: (autoplayDelay * 1000), 
                    pauseOnMouseEnter: true,
                    disableOnInteraction: true,
                    waitForTransition: true,
                }} : {autoplay: false};
            
            let carousel_parameters = jQuery.extend({}, 
                default_parameters, 
                rewind_param,
                speed_param,
                mousewheel_param,
                allowTouchMove_param,
                autoplay_param
            );
            
            cspm_carousel[map_id] = new Swiper('.cspm-swiper[data-map-id='+map_id+']', carousel_parameters);
                    
            if(progress_map_vars.map_script_args[map_script_id]['carousel_mode'] === 'rtl'){
                cspm_carousel[map_id].changeLanguageDirection('rtl');
                cspm_carousel[map_id].emit('resize');  
            }             
            
        }else return false;	

    }

	/**
	 * Load Items in the screenview
	 * @since 5.8 */	 

	function cspm_carousel_items_load_callback(carousel_id){
	
		const map_id = carousel_id;

		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
        
        const carousel_items_object = progress_map_vars['carousel_items_'+map_script_id];
		
        const query_post_ids = progress_map_vars['query_post_ids_'+map_script_id];
        
        for(let key in carousel_items_object){
            
            if(carousel_items_object.hasOwnProperty(key)){
      
                let post_id = String(key).includes('-') ? String(key).split('-')[0] : key;
                
                if(jQuery.inArray(parseInt(post_id), query_post_ids) !== -1){
                    
                    /**
                     * Get item content */

                    let carousel_item_content = carousel_items_object[post_id]; 

                    let carousel_items_hash = progress_map_vars.map_script_args[map_script_id]['carousel_items_hash']; //@since 5.8

                    /**
                     * Check if the requested items already exist, then, ...
                     * ... add item content to its container */

                    let parent_selector = '.cspm_carousel_item[data-map-id='+map_id+'][data-post-id='+post_id+']';

                    if(jQuery(parent_selector).has('div.cspm_spinner').length > 0){
                        jQuery(parent_selector).addClass('cspm_animated fadeIn').html(carousel_item_content)
                        jQuery(parent_selector).parent().attr('data-hash', carousel_items_hash + post_id); //@since 5.8 | swiper hash navigation!
                    }

                    /**
                     * Find item's childs and assign to them the same content of the parent
                     * Note: Doing so can allow us to load child items even when parent item is not available in the carousel! */

                    let child_selector = '.cspm_carousel_item[data-map-id='+map_id+'][data-post-id^='+post_id+'-][data-is-child^="yes"]';

                    if(jQuery(child_selector).has('div.cspm_spinner').length > 0){
                        jQuery(child_selector).each(function(i){
                            jQuery(this).addClass('cspm_animated fadeIn').html(carousel_item_content);
                            jQuery(this).parent().attr('data-hash', carousel_items_hash + post_id + '-' + i); //@since 5.8 | swiper hash navigation!
                        });
                    }
                    
                }
                
            }
			
		}
		
	}					
		
    /**
     * This will center the map on its related item in the carousel (the first postioned item)
     @edited 5.8 */

	function cspm_request_carousel_item_related_marker(map_id, item_index, target_map = true){

        if(typeof map_id === 'undefined' && typeof item_index === 'undefined')
            return;
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';		
		
        const connect_carousel_with_map = (progress_map_vars.map_script_args[map_script_id]['connect_carousel_with_map'] === 'yes') ? true : false;
		
        const post_id = jQuery('.cspm_carousel_item[data-map-id='+map_id+'][data-index='+item_index+']').attr('data-post-id');

		if(post_id){

			cspm_apply_carousel_item_hover_style('.cspm_carousel_item[data-map-id='+map_id+'][data-post-id='+post_id+']', map_id);													
			
			if(connect_carousel_with_map == true && target_map == true){
				
				const item_latlng = jQuery('.cspm_carousel_item[data-map-id='+map_id+'][data-post-id='+post_id+']').attr('data-latlng');
	
				if(item_latlng && typeof item_latlng !== 'undefined'){
	
					const split_item_latlng = item_latlng.split('_');
					const this_lat = split_item_latlng[0].replace(/\"/g, '');
					const this_lng = split_item_latlng[1].replace(/\"/g, '');
									
					const plugin_map = jQuery('div#codespacing_progress_map_div_'+map_id);
                    
                    cspm_center_map_at_point(plugin_map, map_id, this_lat, this_lng, 'zoom');

                    setTimeout(function(){
                        cspm_animate_marker(plugin_map, map_id, post_id);
                    }, 200);	
                
                }		
				
			}
            
            /**
             * Set hash navigation 
             * @since 5.8 */
            
            if(progress_map_vars.map_script_args[map_script_id]['hash_navigation'] === 'yes'){
                let carousel_items_hash = progress_map_vars.map_script_args[map_script_id]['carousel_items_hash'];
                window.location.hash = carousel_items_hash + post_id;
            }
			
		}
				
	}    

	/**
	 * Slide to carousel item by its index			
	 * @edited 5.8 */

	function cspm_slide_to_carousel_item(map_id, post_id, mapMovementOccured = false){
				
        /**
         * Trigger a custom event when map-related movement occurs */
        
        if(mapMovementOccured){
            const mapMovementEvent = new CustomEvent('cspmMarkerInteraction', {
                detail: {
                    isOccured: true
                }
            });
            document.dispatchEvent(mapMovementEvent);
        }
        
        /** 
         * Slide to carousel item */
        
        const carousel = cspm_carousel[map_id];
        
        const item_index = virtual_carousel_outputs_indexes[map_id].indexOf(String(post_id)); //@since 5.8
        
        if(typeof carousel !== 'undefined' && item_index !== -1)
			carousel.slideTo(item_index); /*carousel.slideToLoop(item_index);*/
		
		return false;
		
	}
	
	/**
	 * Apply hover style for the first and/or the selected item in the carousel
	 * @edited 5.8 */

	function cspm_apply_carousel_item_hover_style(item_selector, map_id){								
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		jQuery('.cspm_carousel_item[data-map-id='+map_id+']').removeClass('cspm_carousel_first_item').css({
            'background-color':progress_map_vars.map_script_args[map_script_id]['items_background']
        });
        
		jQuery(item_selector).addClass('cspm_carousel_first_item').css({
            'background-color':progress_map_vars.map_script_args[map_script_id]['items_hover_background']
        });	
		
	}    
	
    /**
     * This will fill the carousel items with their content
     @edited 5.8 */

	function cspm_carousel_item_HTML(atts){

		let carousel_item = '';							
		
		if(typeof atts !== 'object')
			return carousel_item;
			
		const map_id = atts.map_id;
		const post_id = atts.post_id;
		const is_child = atts.is_child;
		const index = atts.index;
		const latLng = atts.latLng;

		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
        
        let item_width, item_height, item_css;
        
        const items_background = progress_map_vars.map_script_args[map_script_id]['items_background'];
        
		if(progress_map_vars.map_script_args[map_script_id]['items_view'] == 'listview'){ 
			item_width = parseInt(progress_map_vars.map_script_args[map_script_id]['horizontal_item_width']);										
			item_height = parseInt(progress_map_vars.map_script_args[map_script_id]['horizontal_item_height']);
			item_css = progress_map_vars.map_script_args[map_script_id]['horizontal_item_css'];
		}else if(progress_map_vars.map_script_args[map_script_id]['items_view'] == 'gridview'){ 
			item_width = parseInt(progress_map_vars.map_script_args[map_script_id]['vertical_item_width']);
			item_height = parseInt(progress_map_vars.map_script_args[map_script_id]['vertical_item_height']);
			item_css = progress_map_vars.map_script_args[map_script_id]['vertical_item_css'];
		}

        carousel_item = '<div class="cspm_carousel_item cspm_border_radius cspm_border_shadow" value="'+index+'" data-map-id="'+map_id+'" data-post-id="'+post_id+'" data-is-child="'+is_child+'" data-index="'+index+'" data-latlng="'+latLng+'" style="background-color:'+items_background+'; '+item_css+'">';
			carousel_item += '<div class="cspm_spinner cspm_border_top_before_hex"></div>';
		carousel_item += '</div>';
		
		return carousel_item;

	}    
	
    /**
     * This will rewrite the carousel by removing and adding requested items
     * @edited 5.8 */

	function cspm_rewrite_carousel(map_id, show_carousel, posts_to_retrieve){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		if(show_carousel == 'yes' 
           && progress_map_vars.map_script_args[map_script_id]['show_carousel'] == 'true' 
           && ['fullscreen-map', 'fit-in-map'].includes(map_layout[map_id]) === false
          ){
                
            if(typeof NProgress !== 'undefined'){
                NProgress.configure({
                  parent: 'div#cspm_carousel_container[data-map-id='+map_id+']',
                  showSpinner: true
                });				
                NProgress.start();  
                NProgress.set(0.5);                  
            }				
            
			let carousel = cspm_carousel[map_id];

            if(typeof carousel !== 'undefined')
                carousel.removeAllSlides();

            const carousel_length = cspm_object_size(posts_to_retrieve);

            let count_loop = 0;
            let carousel_new_items = [];
            let carousel_appended_items = [];

            const sync_carousel_to_viewport = progress_map_vars.map_script_args[map_script_id]['sync_carousel_to_viewport'];
            const plugin_map = jQuery('div#codespacing_progress_map_div_'+map_id);
            const markers_on_viewport = cspm_get_markers_on_viewport(plugin_map, map_id);
            
            const query_post_ids = progress_map_vars['query_post_ids_'+map_script_id];
            
            virtual_carousel_outputs[map_id] = [];
            virtual_carousel_outputs_indexes[map_id] = [];

            let rewrite_deferred = jQuery.Deferred();

            for(let c = 0; c < carousel_length; c++){

                const post_id = posts_to_retrieve[c];
                
                if(jQuery.inArray(parseInt(post_id), query_post_ids) !== -1){
                    
                    const post_latLng = post_lat_lng_coords[map_id][post_id];
                    const is_child = post_ids_and_child_status[map_id][post_latLng];
                    const index = count_loop;

                    const carousel_item = cspm_carousel_item_HTML({
                        map_id: map_id,
                        post_id: post_id,
                        is_child: is_child,
                        index: index,
                        latLng: post_latLng,
                    });							

                    carousel_new_items.push(carousel_item);
                    carousel_appended_items.push(String(post_id));

                    virtual_carousel_outputs_indexes[map_id].push(String(post_id));                                                

                    count_loop++;

                    /**
                     * Append the post child markers to the carousel 
                     * NOTE: On carousel first/init load, we need to append the child markers to the carousel. ...
                     * ... On carousel rewrite (especially when carousel and map viewport are connected), we must check if a child marker isn't already appended to prevent appending double entries!
                     * @since 5.8 */

                    if(typeof post_id_and_child_ids[map_id][post_id] !== 'undefined' 
                       && post_id_and_child_ids[map_id][post_id].length > 0
                      ){

                        for(let j = 0; j < post_id_and_child_ids[map_id][post_id].length; j++){

                            const child_post_id = post_id_and_child_ids[map_id][post_id][j];

                            if(jQuery.inArray(child_post_id, carousel_appended_items) === -1){ //Check if not yet appended!

                                if(sync_carousel_to_viewport == 'no'
                                  || (sync_carousel_to_viewport == 'yes' && jQuery.inArray(child_post_id, markers_on_viewport) !== -1)
                                ){ // Make sure to not load child items when they're outside the map's viewport (only when sync. carousel to viewport is enabled!)

                                    const post_latLng = post_lat_lng_coords[map_id][child_post_id];
                                    const is_child = post_ids_and_child_status[map_id][post_latLng];
                                    const index = count_loop;

                                    const child_carousel_item = cspm_carousel_item_HTML({
                                        map_id: map_id,
                                        post_id: child_post_id,
                                        is_child: is_child,
                                        index: index,
                                        latLng: post_latLng,
                                    });	

                                    carousel_new_items.push(child_carousel_item);
                                    carousel_appended_items.push(String(child_post_id));

                                    virtual_carousel_outputs_indexes[map_id].push(String(child_post_id));                                                

                                    count_loop++;

                                }

                            }

                        }

                    }
                    
                }

            }

            virtual_carousel_outputs[map_id] = carousel_new_items;

            rewrite_deferred.resolve();

            rewrite_deferred.done(function(){
                
                if(typeof carousel !== 'undefined'){
                    carousel.virtual.slides = []; // Clear slides
                    carousel.virtual.cache = []; // Clear cache
                    carousel.virtual.slides = carousel_new_items; // Add new slides
                    carousel.virtual.update(true); // Force virtual slides to update
                    carousel.update(); // Update carousel                    
                }else{
                    cspm_init_carousel(map_id); // Init in case of "Search map" type!
                }

                if(typeof NProgress !== 'undefined') 
                    NProgress.done();

            });

            return count_loop;
			
		}else return posts_to_retrieve.length;

	}    
	
//================================//
//==== End Carousel functions ====//
//================================//

	function cspm_fullscreen_map(map_id){
		var screenWidth = window.innerWidth;
		var screenHeight = window.innerHeight;
		jQuery('div.codespacing_progress_map_area[data-map-id='+map_id+']').css({height : screenHeight, width : screenWidth});
	    jQuery('#codespacing_progress_map_div_'+map_id+'[data-map-id='+map_id+']').css({height : screenHeight, width : screenWidth}); //@since 5.8
	}
		
	function cspm_carousel_width(map_id){
		var carouselWidth = jQuery('div#codespacing_progress_map_div_'+map_id).width();
		carouselWidth = parseInt(carouselWidth - 40);
		var carouselHalf = parseInt((-0) - (carouselWidth/ 2));
		jQuery('div.cspm_carousel_on_top[data-map-id='+map_id+']').css({width : carouselWidth, 'margin-left' : carouselHalf+'px'});
	}

	function cspm_fitIn_map(map_id){
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
		var parentHeight = jQuery('div.codespacing_progress_map_area[data-map-id='+map_id+']').parent().height();
		if(parentHeight == 0){ 
            parentHeight = progress_map_vars.map_script_args[map_script_id]['layout_fixed_height'];
        }
		jQuery('div.codespacing_progress_map_area[data-map-id='+map_id+']').css({height : parentHeight});
		jQuery('#codespacing_progress_map_div_'+map_id+'[data-map-id='+map_id+']').css({height : parentHeight}); //@since 5.8
	}

	/**
	 * Returns the distance, in meters, between two LatLngs */

	function cspm_compute_distance_between(origin_lat, origin_lng, destination_lat, destination_lng){
		const from = new google.maps.LatLng(origin_lat, origin_lng);
		const to = new google.maps.LatLng(destination_lat, destination_lng);
		const distance = google.maps.geometry.spherical.computeDistanceBetween(from, to);
		return distance;
	}
	
	/**
	 * Display user position on the map
	 *
	 * @since 2.8
	 * @updated 3.2 [added retina support for user marker icon]
	 * @updated 4.0 | 5.8
	 */
	function cspm_geolocate(plugin_map, map_id, show_user, user_marker_icon, marker_size, user_circle, zoom, show_error){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		const mapObject = plugin_map.gmap3("get");

		// Marker image size with Retina support
		const marker_img_width = (progress_map_vars.map_script_args[map_script_id]['retinaSupport'] == "true" && !cspm_is_svg(user_marker_icon)) 
            ? parseInt(marker_size.split('x')[0])/2 
            : parseInt(marker_size.split('x')[0]);
        
		const marker_img_height = (progress_map_vars.map_script_args[map_script_id]['retinaSupport'] == "true" && !cspm_is_svg(user_marker_icon)) 
            ? parseInt(marker_size.split('x')[1])/2 
            : parseInt(marker_size.split('x')[1]);

		// Marker image anchor point
		const anchor_y = marker_img_height/2;
		const anchor_x = marker_img_width/2;	
		let anchor_point = null;

		anchor_point = new google.maps.Point(anchor_x, anchor_y);
		
		/**
		 * Marker icon */
		
		let marker_icon = {
			url: user_marker_icon,
		}
		
		if(marker_img_width > 0 && marker_img_height > 0){
			marker_icon = jQuery.extend({}, marker_icon, {					
				anchor: anchor_point,
				scaledSize: new google.maps.Size(marker_img_width, marker_img_height)
			});
		}
		
		/**
		 * Geolocate */
		 
		plugin_map.gmap3({
			getgeoloc:{
				callback: function(latLng){
                    if(latLng){
					  			                    
                        if(typeof mapObject.panTo === 'function')
                           mapObject.panTo(latLng);
                    
                        google.maps.event.addListenerOnce(mapObject, 'idle', function(){                       	

                            if(typeof mapObject.setZoom === 'function')
                                mapObject.setZoom(parseInt(zoom));
								
                            /**
                             * Add a marker indicating the user location */

                            if(show_user){

                                plugin_map.gmap3({
                                    marker:{
                                        latLng: latLng,
                                        tag: ['cspm_user_marker_'+map_id, 'to_cluster'],
                                        options:{
                                            icon: marker_icon,
                                            post_id: 'user_location',
                                            id: 'user_location',													
                                        },
                                        events:{
                                            click: function(marker, event, elements){

                                                /**
                                                 * Nearby points of interets.
                                                 * Add the coordinates of this marker to the list of Proximities ...
                                                 * ... in order to use them (latLng) to display nearby points of interest ...
                                                 * ... of that marker 
                                                 * @since 3.2 */

                                                if(progress_map_vars.map_script_args[map_script_id]['nearby_places_option'] == 'true'){

                                                    jQuery('select.cspm_proximities_list[data-map-id='+map_id+'] option.cspm_proximity_name[data-map-id='+map_id+']').attr('data-marker-latlng', marker.position).attr('data-marker-post-id', 'user_location'); //@since 5.6.3 || Fix an issue when selecting a marker before opening the proximities list. "Tail.select" inherit all select options attributes!!							
                                                    jQuery('li.cspm_proximity_name[data-map-id='+map_id+']').attr('data-marker-latlng', marker.position).attr('data-marker-post-id', 'user_location').removeClass('selected');	

                                                    cspm_popup_msg(map_id, 'success', '', progress_map_vars.new_marker_selected_msg);

                                                }
                                            }
                                        },																																																		
                                    },											
                                    circle:{
                                        options:{
                                            center: latLng,
                                            radius : parseInt(user_circle*1000),
                                            fillColor : progress_map_vars.map_script_args[map_script_id]['user_circle_fillColor'],
                                            fillOpacity: progress_map_vars.map_script_args[map_script_id]['user_circle_fillOpacity'],
                                            strokeColor : progress_map_vars.map_script_args[map_script_id]['user_circle_strokeColor'],
                                            strokeOpacity: progress_map_vars.map_script_args[map_script_id]['user_circle_strokeOpacity'],
                                            strokeWeight: parseInt(progress_map_vars.map_script_args[map_script_id]['user_circle_strokeWeight']),
                                            editable: false,
                                        },
                                    }
                                });

                            }	
																							
				        });
                        
				    }else if(show_error){
					
                        /**
                         * More info at https://support.google.com/maps/answer/152197
                         * Deprecate Geolocation API: https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only */

                        var title = progress_map_vars.geoErrorTitle;
                        var message = progress_map_vars.geoErrorMsg;

                        if(!cspm_popup_msg(map_id, 'error', title, '<br />' + message))
                            alert(title + '\n\n' + message);

                        console.log('PROGRESS MAP: ' + progress_map_vars.geoDeprecateMsg);
					  
				    }
				}
			},							
		});
	}
	
	/**
	 * Get User marker on the map
	 *
	 * @since 2.8
	 */
	function cspm_get_user_marker(plugin_map, map_id){
		plugin_map.gmap3({
		  get: {
			name: 'marker',
			tag: 'cspm_user_marker_'+map_id,
			callback: function(objs){
				if(objs)
				  return objs;
			}
		  }
		});
	}
	
	/**
	 * This will detect if the Street View panorama is activated
	 * @since 2.7
	 */
	function cspm_is_panorama_active(plugin_map){
		const mapObject = plugin_map.gmap3("get");
		if(typeof mapObject.getStreetView === "function"){
			const streetView = mapObject.getStreetView();
			return streetView.getVisible();
		}else return false;
	}

	function cspm_center_map_at_point(plugin_map, map_id, latitude, longitude, effect){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		const map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		const mapObject = plugin_map.gmap3("get");
		
		if(typeof mapObject !== 'undefined'){
			
			const current_zoom = (typeof mapObject.getZoom === 'function') ? mapObject.getZoom() : 12;
			const latLng = new google.maps.LatLng(latitude, longitude);
			
            let carousel_map_zoom;
            
			if(typeof progress_map_vars.map_script_args[map_script_id]['carousel_map_zoom'] !== 'undefined'){
				carousel_map_zoom = progress_map_vars.map_script_args[map_script_id]['carousel_map_zoom'];
			}else carousel_map_zoom = current_zoom;
			
			if(effect == 'zoom' && current_zoom != parseInt(carousel_map_zoom))
				mapObject.setZoom(parseInt(carousel_map_zoom));
			else if(effect == 'resize')
				google.maps.event.trigger(mapObject, 'resize');

			setTimeout(function(){
				if(typeof mapObject.panTo === 'function')
					mapObject.panTo(latLng);
			}, 200);
			
		}
			
	}

	function cspm_map_bounds_contains_marker(plugin_map, latitude, longitude){
		
		var mapObject = plugin_map.gmap3('get');
		
		if(typeof mapObject === 'undefined')
			return false;
			
		var myLatlng = new google.maps.LatLng(latitude, longitude);
		
		if(typeof mapObject.getBounds === 'function'){
			return mapObject.getBounds().contains(myLatlng);		
		}else return false;
		
	}
	
	/**
	 * Computes whether the given point lies inside the specified polygon
	 */
	function cspm_polygon_contains_marker(polygon_paths, marker_lat, marker_lng){

		var marker_latLng = new google.maps.LatLng(marker_lat, marker_lng);
		
		return google.maps.geometry.poly.containsLocation(marker_latLng, polygon_paths);

	}
	
//===================================//
//==== Start Infoboxes functions ====//
//===================================//

	var cspm_infoboxes = {};
	
	/**
	 * Set infobox settings
	 *
	 * @updated 5.3 | 5.8
	 */
	function cspm_infobox(plugin_map, map_id, marker, infobox_options){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		var post_id = parseInt(marker.post_id);
		var icon_height = (typeof marker.icon === 'undefined' || typeof marker.icon.size === 'undefined' || typeof marker.icon.size.height === 'undefined') ? 38 : marker.icon.size.height;

		var infobox_type = infobox_options.type;
		var infobox_display_event = infobox_options.display_event;
		var infobox_external_link = infobox_options.link_target;
		var remove_infobox_on_mouseout = infobox_options.remove_on_mouseout;
		var show_close_btn = infobox_options.show_close_btn; //@since 5.3
		
		var map_type = infobox_options.map_type; //@since 5.3
		
		var panOnOpen = (infobox_display_event == 'onload' || infobox_display_event == 'onzoom') ? false : true; //@since 5.3
			if(map_type == 'light_map') panOnOpen = true; //@since 5.3
        
        const mapObject = plugin_map.gmap3('get'); //@since 5.8
        
        return new SnazzyInfoWindow({
			map: mapObject,
			marker: marker,
			content: progress_map_vars['infoboxes_'+map_id][post_id],
			wrapperClass: 'cspm_marker_infobox cspm_marker_overlay',
			panOnOpen: panOnOpen, //@edited 5.3
			closeOnMapClick: (infobox_display_event != 'onload' && infobox_display_event != 'onzoom') ? true : false, //@edited 5.3
			openOnMarkerMouseOver: (infobox_display_event == 'onhover') ? true : false,
			closeOnMarkerMouseOut: ((infobox_display_event != 'onload' && infobox_display_event != 'onzoom') && remove_infobox_on_mouseout == 'true') ? true : false, //@edited 5.3
			openOnMarkerClick: (infobox_display_event == 'onclick') ? true : false,
			closeWhenOthersOpen: (infobox_display_event != 'onload' && infobox_display_event != 'onzoom') ? true : false, //@edited 5.3
			showCloseButton: (show_close_btn == 'true') ? true : false, //@edited 5.3
			pointer: '10px',
			borderRadius: (infobox_type == 'rounded_bubble') ? '50%' : '2px',
			backgroundColor: 'transparent',
			padding: '0px',
			border: false,
			offset: {
				top: '-'+(icon_height+2)+'px',
			},
			shadow: {
				h: '0px',
				v: '1px',
				blur: '4px',
				spread: '-1px',
				opacity: 0.2,
				color: '#000'
			},
			callbacks: {				
                open: function(){
                    
                    /**
                     * Add "z-index", "is-child" & "data-carousel-item-post-id" to the infobox so we can target ...
                     * ... the appropriate item in the carousel on infobox hover
                     * @since 5.8 */
                    
                    if(typeof this.getWrapper !== 'undefined'){
                        if(typeof marker.getZIndex === 'function'){
                            jQuery(this.getWrapper()).css({'z-index' : marker.getZIndex()});
                        }
					    jQuery(this.getWrapper()).find('.cspm_infobox_content_container').attr('data-is-child', marker.is_child);
				        jQuery(this.getWrapper()).find('.cspm_infobox_content_container').attr('data-carousel-item-post-id', marker.id);                        
                    }
                    
					/**
					 * Fix an issue with the maps where it wasn't possible to open the single post modal.
					 * @since 5.6.3 */
					
                    jQuery('a.cspm_popup_single_post').on('click', function(e){ // Don't use $(document).on(event, handler, ... !!!
                        e.preventDefault(); 
                        var iframe_url = jQuery(this).attr('href');
                        if(typeof iframe_url !== 'undefined' && iframe_url != ''){
                            cspm_open_single_post_modal(iframe_url);		
                        }	
                    });	
                    
                    /**
                     * Fix an issue with the maps where it wasn't possible to open the "Nearby places" map inside a modal
                     * @since 5.6.3 */

                    jQuery('a.cspm_popup_nearby_palces_map').on('click', function(e){ // Don't use $(document).on(event, handler, ... !!!
                        e.preventDefault();			            
                        var iframe_url = progress_map_vars.nearby_places_page_url;
                        var post_id = jQuery(this).attr('data-post-id');
                        if(typeof iframe_url !== 'undefined' && iframe_url != '' && typeof post_id !== 'undefined' && post_id != ''){
                            cspm_open_single_post_modal(iframe_url+'?post_id='+post_id);		
                        }
                    });	
			
				},
                afterOpen: function(){
                        
					/**
					 * Fix an issue with "Single post" maps where it wasn't possible to ...
					 *... open the modal.
					 * @since 4.6
					 * @edited 5.3 [fixed and issue where some infoboxes open the link in a new window/tab] */
					
					jQuery('body').remove('#cspm_single_post_modal'); //@since 5.3
					 
					setTimeout(function(){
						if(jQuery('#cspm_single_post_modal').length == 0){ 
							jQuery('body').append('<div id="cspm_single_post_modal"></div>');
						}	
					}, 100);
                    
                },                
				beforeClose: function(){
					
					/**
					 * Make sure not to close the infobox when opening the marker menu.
					 * This will fix a conflict between multiple SnazzyInfowindow instances regarding the option "closeWhenOthersOpen".
					 * When [@cspm_marker_menus_status] is empty or equal to "closed", this means a marker menu will be rendred on the map, ...
					 * ... therfore, we can use this information to ignore "closeWhenOthersOpen"!
					 * @since 5.5 */

					if(typeof cspm_marker_menus_status[map_id] !== 'undefined' && cspm_marker_menus_status[map_id] == 'opened'){
						return false;
					}
					
				}
			}
		});	

    }
	
	function cspm_draw_multiple_infoboxes(plugin_map, map_id, infobox_options){

		var r = jQuery.Deferred();
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		var infobox_display_event = infobox_options.display_event; //@since 5.3

		plugin_map.gmap3({
			get: {
				name: 'marker',
				tag: 'to_cluster',
				all:  true,
				callback: function(objs) {
        
					for(var i = 0; i < objs.length; i++){	
						
						var id = objs[i].id;
						var this_marker_object = objs[i];														
						var post_id = parseInt(objs[i].post_id);

						if(post_id != '' && post_id != 'user_location' && !isNaN(post_id)){
													
							var latLng = objs[i].position;
							
							// Convert the LatLng object to array
							var lat = latLng.lat();
							var lng = latLng.lng();
							
							/**
							 * Load infobox on specific zoom level 
							 * @since 5.3 */
							 
							var load_by_reaching_zoom_level = false; //@since 5.3
							if(infobox_display_event == 'onzoom' && typeof infobox_options.display_zoom_level !== 'undefined'){
								var mapObject = plugin_map.gmap3('get');
								var current_zoom = mapObject.getZoom();								
								if(current_zoom >= infobox_options.display_zoom_level)
									load_by_reaching_zoom_level = true;
							} //@since 5.3
							
							// ==========================

							// if the marker is within the viewport of the map
							if(	cspm_map_bounds_contains_marker(plugin_map, lat, lng) 
								&& objs[i].getMap() != null 
								&& objs[i].getVisible() === true //@edited 5.8
								&& (infobox_display_event == 'onload' || (infobox_display_event == 'onzoom' && load_by_reaching_zoom_level)) //@since 5.3
							){

								if(typeof cspm_infoboxes[map_id][id] === 'undefined'){
									cspm_infoboxes[map_id][id] = cspm_infobox(plugin_map, map_id, this_marker_object, infobox_options);
									cspm_infoboxes[map_id][id].open();
								}else{
									if(!cspm_infoboxes[map_id][id].isOpen())
										cspm_infoboxes[map_id][id].open();
								}
								
							}else{
									
								if(typeof cspm_infoboxes[map_id][id] === 'object' && cspm_infoboxes[map_id][id].isOpen())
									cspm_infoboxes[map_id][id].close();			
									
							}
							
						}
						
					}
				
					r.resolve(); 
																												
				}
			}
		});
		
		return r;
		
	}

    function cspm_draw_single_infobox(plugin_map, map_id, marker_obj, infobox_options){

		var r = jQuery.Deferred();

		var id = marker_obj.id;
		var post_id = parseInt(marker_obj.post_id);
				
		if(typeof cspm_infoboxes[map_id][id] === 'undefined'){
			
			cspm_infoboxes[map_id][id] = cspm_infobox(plugin_map, map_id, marker_obj, infobox_options);
			cspm_infoboxes[map_id][id].open();
				
			r.resolve(); 
																												
		}
		
		return r;

	}
    
    /**
     * This will scroll to the associated carousel item of the infobox the user hovers over
     * @edited 5.8 */

	function cspm_connect_carousel_and_infobox(){
		
		setTimeout(function(){
			jQuery('div.cspm_infobox_container[data-move-carousel=true] div.cspm_infobox_content_container').on('mouseenter', function(){
				const map_id = jQuery(this).attr('data-map-id');
				const post_id = jQuery(this).attr('data-post-id');
				const carousel_item_post_id = jQuery(this).attr('data-carousel-item-post-id'); //@since 5.8 | The real ID that may also contain the index of the child marker (e.g. 4854-0)!
                const is_child = jQuery(this).attr('data-is-child'); //@since 5.8	            
				const carousel = jQuery(this).attr('data-show-carousel');				
				if(carousel == 'true'){
					cspm_slide_to_carousel_item(map_id, carousel_item_post_id, true); //@edited 5.8	
                    let carousel_item = '.cspm_carousel_item[data-map-id='+map_id+'][data-post-id='+carousel_item_post_id+'][data-is-child='+is_child+']';
                    cspm_apply_carousel_item_hover_style(carousel_item, map_id); //@edited 5.8
				}
			});
		}, 1000);
		
	}
	
//=================================//
//==== End Infoboxes functions ====//
//=================================//
	
//=======================================//
//==== Start Marker Popups functions ====//
//=======================================//

	/**
	 * Marker Popups
	 * @since 4.0 */
	 
	var cspm_marker_popups = {};

	function cspm_popup(plugin_map, map_id, marker, popup_options){
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
	
		var post_id = parseInt(marker.post_id);
		var icon_height = (typeof marker.icon === 'undefined' || typeof marker.icon.size === 'undefined' || typeof marker.icon.size.height === 'undefined') ? 38 : marker.icon.size.height;
		var icon_width = (typeof marker.icon === 'undefined' || typeof marker.icon.size === 'undefined' || typeof marker.icon.size.width === 'undefined') ? 35 : marker.icon.size.width;
		
		var placement = popup_options.placement.toString();
		var pointer = (placement == 'left' || placement == 'right') ? '7px' : '5px';
		var offsetTop = '0px';
		
		if(placement == 'left' || placement == 'right'){
			offsetTop = '-'+(icon_height/2)+'px';
		}else if(placement == 'top'){
			offsetTop = '-'+(icon_height-7)+'px';
		}
		
		var content = '<div class="cspm_marker_popup_content" style="font-weight:'+popup_options.fontWeight+'">' + 
			progress_map_vars['marker_popups_'+map_id][post_id] + 
		'</div>';
		
		return new SnazzyInfoWindow({
			map: plugin_map.gmap3('get'),
			marker: marker,
			content: content,
			wrapperClass: 'cspm_marker_popup cspm_marker_overlay',
			panOnOpen: false,
			closeOnMapClick: false,
			openOnMarkerMouseOver: true,
			closeOnMarkerMouseOut: false,
			openOnMarkerClick: true,
			closeWhenOthersOpen: false,
			showCloseButton: false,
			pointer: pointer,
			borderRadius: '2px',
			backgroundColor: popup_options.bgColor,
			fontColor: popup_options.color,
			fontSize: popup_options.fontSize,
			padding: '5px 7px',
			border: false,
			placement: placement,
			offset: {
				top: offsetTop,
			},
			shadow: {
				h: '3px',
				v: '3px',
				blur: '0px',
				spread: '-1px',
				opacity: 0.15,
				color: '#000'
			},
		});	
	
	}
	
	function cspm_draw_multiple_popups(plugin_map, map_id, infobox_options){
	
		var r = jQuery.Deferred();
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
	
		plugin_map.gmap3({
			get: {
				name: 'marker',
				tag: 'to_cluster',
				all:  true,
				callback: function(objs) {	
                
					for(var i = 0; i < objs.length; i++){	
						
						var id = objs[i].id;
						var this_marker_object = objs[i];														
						var post_id = parseInt(objs[i].post_id);
	
						if(post_id != '' && post_id != 'user_location' && !isNaN(post_id) && progress_map_vars['marker_popups_'+map_id][post_id] != ''){
													
							var latLng = objs[i].position;
							
							// Convert the LatLng object to array
							var lat = latLng.lat();
							var lng = latLng.lng();
	
							// if the marker is within the viewport of the map
							if(cspm_map_bounds_contains_marker(plugin_map, lat, lng) && objs[i].getMap() != null && objs[i].getVisible() === true){ //@edited 5.8
	
								if(typeof cspm_marker_popups[map_id][id] === 'undefined'){
									cspm_marker_popups[map_id][id] = cspm_popup(plugin_map, map_id, this_marker_object, infobox_options);
									cspm_marker_popups[map_id][id].open();
								}else{
									if(!cspm_marker_popups[map_id][id].isOpen())
										cspm_marker_popups[map_id][id].open();
								}
								
							}else{
									
								if(typeof cspm_marker_popups[map_id][id] === 'object' && cspm_marker_popups[map_id][id].isOpen())
									cspm_marker_popups[map_id][id].close();			
									
							}
							
						}
						
					}
				
					r.resolve(); 
																												
				}
			}
		});
		
		return r;
		
	}
	
//=====================================//
//==== End Marker Popups functions ====//
//=====================================//
	
//=====================================//
//==== Start Marker Menu functions ====//
//=====================================//

	/**
	 * Marker Menu
	 * @since 5.5 */
	
	var cspm_marker_menus = {};
	var cspm_marker_menus_status = {};
	
	function cspm_marker_menu(plugin_map, map_id, marker, menu_options){
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
	
		var post_id = parseInt(marker.post_id);
		var icon_height = (typeof marker.icon === 'undefined' || typeof marker.icon.size === 'undefined' || typeof marker.icon.size.height === 'undefined') ? 38 : marker.icon.size.height;
		var icon_width = (typeof marker.icon === 'undefined' || typeof marker.icon.size === 'undefined' || typeof marker.icon.size.width === 'undefined') ? 35 : marker.icon.size.width;
		
		var placement = menu_options.placement.toString();
		var pointer = (placement == 'left' || placement == 'right') ? '7px' : '5px';
		var offsetTop = '0px';
		
		if(placement == 'left' || placement == 'right'){
			offsetTop = '-'+(icon_height/2)+'px';
		}else if(placement == 'top'){
			offsetTop = '-'+(icon_height-7)+'px';
		}
		
		var content = '<div class="cspm_marker_menu_content" style="font-weight:'+menu_options.fontWeight+'">' + 
			progress_map_vars['marker_menus_'+map_id][post_id] + 
		'</div>';
		
		return cspm_marker_menus[map_id] = new SnazzyInfoWindow({
			map: plugin_map.gmap3('get'),
			marker: marker,
			post_id: post_id,
			content: content,
			wrapperClass: 'cspm_marker_menu cspm_marker_overlay',
			//maxWidth: '50',
			//maxHeight: '50',
			panOnOpen: true,
			edgeOffset: {
			  top: 50,
			  right: 50,
			  bottom: 50,
			  left: 50
			},
			closeOnMapClick: (menu_options.close_map_click == 'yes') ? true : false,
			openOnMarkerMouseOver: (menu_options.mouse_enter == 'yes') ? true : false,
			closeOnMarkerMouseOut: (menu_options.mouse_out == 'yes') ? true : false,
			openOnMarkerClick: false,
			closeWhenOthersOpen: true,
			showCloseButton: (menu_options.close_btn == 'yes') ? true : false,
			pointer: pointer,
			borderRadius: '2px',
			backgroundColor: menu_options.bgColor,
			fontColor: menu_options.color,
			fontSize: menu_options.fontSize,
			padding: '10px 17px',
			border: false,
			placement: placement,
			offset: {
				top: offsetTop,
			},
			shadow: {
				h: '3px',
				v: '3px',
				blur: '0px',
				spread: '-1px',
				opacity: 0.15,
				color: '#000'
			},
			callbacks: {
                open: function(){
					
					/**
					 * Fix an issue with the maps where it wasn't possible to open the single post modal.
					 * @since 5.6.3 */
					
                    jQuery('a.cspm_popup_single_post').on('click', function(e){ // Don't use $(document).on(event, handler, ... !!!
                        e.preventDefault(); 
                        var iframe_url = jQuery(this).attr('href');
                        if(typeof iframe_url !== 'undefined' && iframe_url != ''){
                            cspm_open_single_post_modal(iframe_url);		
                        }	
                    });	
                    
                    /**
                     * Fix an issue with the maps where it wasn't possible to open the "Nearby places" map inside a modal
                     * @since 5.6.3 */

                    jQuery('a.cspm_popup_nearby_palces_map').on('click', function(e){ // Don't use $(document).on(event, handler, ... !!!
                        e.preventDefault();			            
                        var iframe_url = progress_map_vars.nearby_places_page_url;
                        var post_id = jQuery(this).attr('data-post-id');
                        if(typeof iframe_url !== 'undefined' && iframe_url != '' && typeof post_id !== 'undefined' && post_id != ''){
                            cspm_open_single_post_modal(iframe_url+'?post_id='+post_id);		
                        }
                    });	
			
				},
				afterOpen: function(){
					
					/**
					 * The marker menu status well help us to prevent closing other SnazzyInfowindow instances.
					 * Other SI instances can use the status in the "beforeClose" callback */
					 
					cspm_marker_menus_status[map_id] = 'opened';
					setTimeout(function(){
						cspm_marker_menus_status[map_id] = '';
					}, 100);
			
					/**
					 * Fix an issue with "Single post" maps where it wasn't possible to ...
					 *... open the modal.
					 * @since 4.6
					 * @edited 5.3 [fixed and issue where some marker menus open the link in a new window/tab] */
					
					jQuery('body').remove('#cspm_single_post_modal'); //@since 5.3
					 
					setTimeout(function(){
						if(jQuery('#cspm_single_post_modal').length == 0){ 
							jQuery('body').append('<div id="cspm_single_post_modal"></div>');
						}	
					}, 100);
			
					/**
					 * Nearby points of interest 
					 * @since 5.5 */
					 
					if(menu_options.proximities == 'true'){
			
						var latLng = marker.position;
						var post_id = marker.post_id;
			
						jQuery('a.cspm_prepare_for_proximity').click(function(e){ // Don't use $(document).on(event, handler, ... !!!
							e.preventDefault();
				            jQuery('select.cspm_proximities_list[data-map-id='+map_id+'] option.cspm_proximity_name[data-map-id='+map_id+']').attr('data-marker-latlng', latLng).attr('data-marker-post-id', post_id); //@since 5.6.3 || Fix an issue when selecting a marker before opening the proximities list. "Tail.select" inherit all select options attributes!!							
                            jQuery('li.cspm_proximity_name[data-map-id='+map_id+']').attr('data-marker-latlng', latLng).attr('data-marker-post-id', post_id).removeClass('selected');																
							cspm_popup_msg(map_id, 'success', '', menu_options.proximities_msg);
							if(jQuery('div.cspm_proximities_container_'+map_id).is(':hidden')){
								jQuery('div.cspm_proximities_btn[data-map-id='+map_id+']').trigger('click');
							}
							if(typeof cspm_marker_menus[map_id] === 'object'){
								cspm_marker_menus[map_id].close(); // Close the marker menu
							}
						});
						
					}
					
					/**
					 * Open the post/location media modal
					 * @since 5.5 */
						
					marker.media['post_id'] = marker.post_id; // Add post ID to media array | @since 4.6													
		
					jQuery('a.cspm_popup_marker_media').click(function(e){ // Don't use $(document).on(event, handler, ... !!!						
                        e.preventDefault();
						if(typeof marker.media !== 'undefined' && typeof marker.media.format !== 'undefined'){
							if(marker.media.format != 'link' && marker.media.format != 'nearby_places'){
                                cspm_open_media_modal(marker.media, map_id, 'yes');
							}else return false;
						}
					});	
					
					if(jQuery.inArray(marker.media.format, ['link', 'nearby_places']) > -1 
					|| cspm_is_media_empty(marker.media, map_id)
					){
						jQuery('a.cspm_popup_marker_media').parent('.cspm_marker_menu_item').hide();	
					} // hide the media modal link if the post format is not a media type or media files are empty!
					
					/**
					 * Open the directions URL
					 * @since 5.5 */
						
					jQuery('a.cspm_open_directions').click(function(e){ // Don't use $(document).on(event, handler, ... !!!
						e.preventDefault();				
						if(typeof marker.position !== 'undefined'){
							var travel_mode = jQuery(this).attr('data-travel-mode');
							var directions_url = 'https://www.google.com/maps/dir/?api=1&origin=&destination='+marker.position.lat()+','+marker.position.lng()+'&travelmode='+travel_mode;									
							window.open(directions_url);
						}else return false;
					});			
										
				},				
				afterClose: function(){
					if(typeof cspm_marker_menus[map_id] === 'object'){
						cspm_marker_menus[map_id].destroy();
						cspm_marker_menus[map_id] = '';
						cspm_marker_menus_status[map_id] = 'closed';
					}
				}
			}
		});	
	
	}
	
	function cspm_open_marker_menu(plugin_map, map_id, marker, menu_options){
		
		if(typeof cspm_marker_menus !== 'undefined'){
			
			if(typeof cspm_marker_menus[map_id] === 'object'){
				cspm_marker_menus[map_id].close(); // Close other marker menus
			} //@since 5.6.2
							
			/**
			 * Open marker menu 
			 * @since 5.5 */
			
			cspm_marker_menus[map_id] = cspm_marker_menu(plugin_map, map_id, marker, menu_options);
			cspm_marker_menus[map_id].open();
		
		}
	
	}
	
//===================================//
//==== End Marker Menu functions ====//
//===================================//
	
	/** 
	 * Count the number of visible markers in the map
	 * @since 2.5
	 */
	function cspm_nbr_of_visible_markers(plugin_map){
		
		var count_posts = 0;
		
		plugin_map.gmap3({
			get: {
				name: 'marker',
				tag: 'to_cluster',
				all:  true,
				callback: function(objs) {	
                    for(var i = 0; i < objs.length; i++){	
						if(objs[i].getVisible() === true){ //@edited 5.8
							count_posts++;
						}
					}
				}
			}
		});		
		
		return count_posts;
		
	}
	
	function cspm_set_markers_visibility(plugin_map, map_id, value, j, post_ids_and_categories, posts_to_retrieve, retrieve_posts){

		if(retrieve_posts == true){
			
			// @value: Refers to the category ID
			if(value != null){
				// Show markers comparing with the category ID (faceted search case)
				plugin_map.gmap3({
					get: {
						name: 'marker',
						tag: 'to_cluster',
						all: true,
						callback: function(objs){
							if(objs){
								
								jQuery.each(objs, function(i, obj){

									if(typeof obj.post_id !== 'undefined' && obj.post_id != 'user_location' && jQuery.inArray(value, post_ids_and_categories['post_id_'+obj.post_id][0]) > -1){
									
										var obj_post_id = parseInt(obj.post_id);
									
										if(typeof obj.setVisible === 'function'){
											obj.setVisible(true);
											cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'show');
										}
										
										if(jQuery.inArray(parseInt(obj.post_id), posts_to_retrieve) === -1){
											posts_to_retrieve[j] = parseInt(obj.post_id);
											j++;
										}	
											
									}
									
									/**
									 * Show the user's marker for GeoTargeting
									 * @since 2.7.4 */
									 
									if(obj.post_id == 'user_location' && typeof obj.setVisible === 'function')
										obj.setVisible(true);
									
								});
								
							}
				
                        }
					}
				});
				
			}else{
				
				/**
				 * Show markers within the search area/radius (Search form case) */
				 
				plugin_map.gmap3({
					get: {
						name: "marker",
						tag: 'to_cluster',
						all: true,
						callback: function(objs){
							if(objs){
								
								jQuery.each(objs, function(i, obj){
									
									if(typeof obj.post_id !== 'undefined'){
										
										var obj_post_id = parseInt(obj.post_id);
									
										if(jQuery.inArray(obj_post_id, posts_to_retrieve) > -1){
										
											if(typeof obj.setVisible === 'function'){
												obj.setVisible(true);												
												cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'show');
											}
											
										}
											
									}
									
								});
								
							}
							
						}
					}
				});
			
			}
		
		/**
		 * Show all markers */
		 
		}else{
			
			plugin_map.gmap3({
				get: {
					name: "marker",
					tag: 'to_cluster',
					all: true,
					callback: function(objs){
						
						if(objs){
							
							jQuery.each(objs, function(i, obj){
								
								if(typeof obj.post_id !== 'undefined'){
									
									var obj_post_id = parseInt(obj.post_id);
									
									if(typeof obj.setVisible === 'function'){ 
										obj.setVisible(true);
										cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'show');
									}
									
									if(typeof obj.post_id !== 'undefined' && obj.post_id != 'user_location')										
										posts_to_retrieve[j] = parseInt(obj.post_id);
										
									j++;
									
								}
								
							});
							
						}
						
					}
				}
			});

		}
		
		return posts_to_retrieve;
		
	}

	/**
	 * Hide all visible markers on the map
	 *
	 * @since 2.5
	 */
	function cspm_hide_all_markers(plugin_map, map_id){
		
		var r = jQuery.Deferred();
		
		plugin_map.gmap3({
			get: {
				name: 'marker',	
				tag: 'to_cluster',			
				all: true,	
				callback: function(objs){
                    
					jQuery.each(objs, function(i, obj){
						if(typeof obj.setVisible === 'function' && obj.post_id != 'user_location'){
							obj.setVisible(false);							
							cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'hide');
						}
					});

					r.resolve();
                    
				}
			}
		});
				
		return r;
		
	}
	
	/**
	 * Show all visible markers in the map.
	 * Note: Make sure to display only markers that are currently available on the map ...
	 * ... and exclude markers hidden by a filter and/or search request!
	 *
	 * @since 3.7
	 */
	function cspm_show_all_markers(plugin_map, map_id){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		var r = jQuery.Deferred();
		
		plugin_map.gmap3({
			get: {
				name: 'marker',	
				tag: 'to_cluster',			
				all: true,	
				callback: function(objs){
                
					jQuery.each(objs, function(i, obj){
						if(typeof obj.setVisible === 'function' && obj.post_id != 'user_location'){
							
							/**
							 * Show all markers and exclude those hidden by a filter/search request */
							 
							if(typeof cspm_global_object.posts_to_retrieve[map_id] !== 'undefined' && jQuery.inArray(parseInt(obj.post_id), cspm_global_object.posts_to_retrieve[map_id]) !== -1){
								obj.setVisible(true);
								cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'show');
							
							/**
							 * Show all markers but only if it isn't a search map type! */
							 
							}else if(typeof cspm_global_object.posts_to_retrieve[map_id] === 'undefined' && progress_map_vars.map_script_args[map_script_id]['map_type'] != 'search_map'){
								obj.setVisible(true);	
								cspm_toggle_marker_connected_layers(plugin_map, map_id, obj, 'show');							
							}
							
						}
					});
			
					r.resolve();
				}
			}
		});
				
		return r;
		
	}
	
	/**
	 * Return locations on the current viewport of the map
	 *
	 * @since 5.0 
	 */
	function cspm_get_markers_on_viewport(plugin_map, map_id){

		var markers_on_viewport = [];
		
		plugin_map.gmap3({
			get: {
				name: 'marker',	
				tag: 'to_cluster',			
				all: true,	
				callback: function(objs){
					jQuery.each(objs, function(i, obj){
						if(typeof obj.setVisible === 'function' && obj.getVisible() === true && obj.post_id != 'user_location'){ //@edited 5.8
							if(cspm_map_bounds_contains_marker(plugin_map, obj.position.lat(), obj.position.lng())){
								markers_on_viewport.push(String(obj.post_id));
							}
						}
					});
				}
			}
		});
		
		return markers_on_viewport;
		
	}
	
	
	/**
	 * Show/Hide all marker's connected layers
	 *
	 * @since 4.6
	 */
	function cspm_toggle_marker_connected_layers(plugin_map, map_id, marker_obj, toggle){
		
		if(typeof plugin_map === 'undefined' 
		|| typeof marker_obj === 'undefined' 
		|| typeof toggle === 'undefined')
			return;
      	
		var map_id = (typeof map_id === 'undefined' && typeof marker_obj.map_id !== 'undefined') ? marker_obj.map_id : map_id;
		
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

		if(typeof marker_obj.post_id !== 'undefined'){
			
			var post_id =  marker_obj.post_id ;
					
			/**
			 * KML Layers */
			
			var use_kml = progress_map_vars.map_script_args[map_script_id]['use_kml'];
			
			if(use_kml == 'true'){

				setTimeout(function(){																		
					plugin_map.gmap3({
						exec: {
							name: 'kmllayer',
							tag: 'kmlConnectedPost_' + post_id,
							all: true,
							func: function(data){
								if(typeof data.object !== 'undefined' && typeof data.object.setMap === 'function'){
									if(toggle == 'show')
										data.object.setMap(plugin_map.gmap3('get'));
									else data.object.setMap(null);
								}
							}
						}
					});
				}, 100);
				
			}
								
			/**
			 * Ground Overlays */
			
			var ground_overlays_option = progress_map_vars.map_script_args[map_script_id]['ground_overlays_option'];

			if(ground_overlays_option == 'true'){

				setTimeout(function(){																		
					plugin_map.gmap3({
						exec: {
							name: 'groundoverlay',
							all: true,
							func: function(data){
								var original_opacity = data.object.original_opacity;
								var connected_post = data.object.connectedPost;
								if(connected_post == post_id){																									
									if(typeof data.object !== 'undefined' && typeof data.object.setOpacity === 'function'){
										if(toggle == 'show')
											data.object.setOpacity(original_opacity);
										else data.object.setOpacity(0);
									}
								}
							}
						}
					});
				}, 100);

			}
								
			/**
			 * Polylines */
			
			var draw_polyline = progress_map_vars.map_script_args[map_script_id]['draw_polyline'];
			
			if(draw_polyline == 'true'){

				setTimeout(function(){																		
					plugin_map.gmap3({
						exec: {
							name: 'polyline',
							tag: 'polylineConnectedPost_' + post_id,
							all: true,
							func: function(data){
								if(typeof data.object !== 'undefined' && typeof data.object.setVisible === 'function'){
									if(toggle == 'show')
										data.object.setVisible(true);
									else data.object.setVisible(false);
								}
							}
						}
					});
				}, 100);
				
			}
								
			/**
			 * Polygons */
			
			var draw_polygon = progress_map_vars.map_script_args[map_script_id]['draw_polygon'];
			
			if(draw_polygon == 'true'){

				setTimeout(function(){																		
					plugin_map.gmap3({
						exec: {
							name: 'polygon',
							tag: 'polygonConnectedPost_' + post_id,
							all: true,
							func: function(data){
								if(typeof data.object !== 'undefined' && typeof data.object.setVisible === 'function'){
									if(toggle == 'show')
										data.object.setVisible(true);
									else data.object.setVisible(false);
								}
							}
						}
					});
				}, 100);
				
			}
								
		}
																				
	}
	 
	/**
	 * Get the bounds of a polygon, polyline ...
	 * [@latLngs] Array of latLng coordinates [[1,2],[1,2],[1,2],...]
	 *
	 * @since 3.0
	 */
	function cspm_get_latLngs_bounds(latLngs){

		var bounds = new google.maps.LatLngBounds();
		var i;
		
		var LatLngsCoords = [];
		
		jQuery.each(latLngs, function(i, latLngs){
			var lat = latLngs[0];
			var lng = latLngs[1];
			LatLngsCoords.push(new google.maps.LatLng(lat, lng));
		});

		for (i = 0; i < LatLngsCoords.length; i++) {
		  bounds.extend(LatLngsCoords[i]);
		}
		
		return bounds;		
		
	}
	
	/**
	 * Get the center of a polygon, polyline ...
	 * [@latLngs] Array of latLng coordinates [[1,2],[1,2],[1,2],...]
	 *
	 * @since 3.0
	 */
	function cspm_get_latLngs_center(latLngs){

		var bounds = cspm_get_latLngs_bounds(latLngs)
		
		return bounds.getCenter();		
		
	}
	
	/**
	 * Check if bounds (polygon, polyline) are empty ...
	 * [@latLngs] Array of latLng coordinates [[1,2],[1,2],[1,2],...]
	 *
	 * @since 3.0
	 */
	function cspm_are_bounds_empty(latLngs){

		var bounds = cspm_get_latLngs_bounds(latLngs)
		
		return bounds.isEmpty();		
		
	}
	
	
    /**
     * Return Array of markers latitude & longitude 
     * 
     * @since 5.8
     */
    function cspm_get_markers_positions(markers_object){
        
        let markersPositions = [];
        
        /**
         * Loop through each marker in the markers object */
        
        for(let i = 0; i < markers_object.length; i++){
            
            /**
             * Get the position of the current marker */
            
            let markerPosition = markers_object[i].getPosition();

            /**
             * Check if the marker position is valid */
            
            if(markerPosition){
                
                /**
                 * Extract latitude and longitude from the marker position */
                
                let lat = markerPosition.lat();
                let lng = markerPosition.lng();

                /**
                 * Create an object containing latitude and longitude */
                
                let position = { lat: lat, lng: lng };

                /**
                 * Push the position object to the array */
                
                markersPositions.push(position);
            
            }
            
        }
        
        return markersPositions;
        
    }


	/**
	 * Zoom to address
	 *
	 * @since 3.0
	 * @updated 3.9
	 */
	function cspm_zoom_to_address(map_id, plugin_map, address, autofit, map_zoom){

		plugin_map.gmap3({
			getlatlng:{
				address: address,
				callback: function(results, status){

					if(status == 'OK'){
						
						if(!results || typeof results[0].geometry.location === 'undefined')
							return;
					
						var mapObject = plugin_map.gmap3("get");
						
						if(!autofit){
							mapObject.setZoom(parseInt(map_zoom));
							mapObject.panTo(results[0].geometry.location);
						}else{
							if(typeof results[0].geometry.bounds !== 'undefined'){
								mapObject.fitBounds(results[0].geometry.bounds);
							}else{
								mapObject.panTo(results[0].geometry.location);
							}
						}
					
					}else{
						var api_status_codes = progress_map_vars.geocoding_api_status_codes; //@since 3.9
						cspm_api_status_info(map_id, api_status_codes, status, 'Geocoding API'); //@since 3.9
					}
					
				}
			}
		});	
		
	}
	
	function cspm_recenter_map(plugin_map, map_id){
        
		/**
		 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
		 * @since 3.0 */
		var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
		
		var autofit = progress_map_vars.map_script_args[map_script_id]['autofit'];
		
		if(autofit == 'true'){
			
			plugin_map.gmap3({
				get: {
					name: 'marker',
					all:  true,										
				},
				autofit:{}
			});	//@since 4.6
					
		}else{
			
			var map_center = progress_map_vars.map_script_args[map_script_id]['center'].split(',');
			
			if(typeof map_center !== 'undefined'){
				
				var zoom = parseInt(progress_map_vars.map_script_args[map_script_id]['zoom']);
		
				var mapObject = plugin_map.gmap3("get");
						
				var latLng = new google.maps.LatLng(map_center[0], map_center[1]);
				
				mapObject.setZoom(zoom);
	
				setTimeout(function(){
					if(typeof mapObject.panTo === 'function')
						mapObject.panTo(latLng);
				}, 200);
				
			}else console.log('PROGRESS MAP: There was a problem centring the map. Check your initial map center coordinates in "Progress Map => Map settings => Map center", then, try again.');
		
		}
		
	}
	
	/**
	 * This will open the single post page inside a modal/popup
	 *
	 * @since 3.6
	 * @updated 5.4 | 5.6.3
	 */
	function cspm_open_single_post_modal(iframe_url){
		
        if(jQuery('#cspm_single_post_modal').length == 0){ 
            jQuery('body').append('<div id="cspm_single_post_modal"></div>');
        } //@since 5.6.3
        
		var modal_state = jQuery('#cspm_single_post_modal').iziModal('getState'); //@since 5.4
		
		if(typeof iframe_url === 'undefined' || iframe_url == '' || modal_state == 'opened' || modal_state == 'opening') //@edited 5.4
			return false;
		
		var modal_width = progress_map_vars.map_script_args['initial']['modal_width']; //@since 4.6
		var modal_width_unit = progress_map_vars.map_script_args['initial']['modal_width_unit']; //@since 4.6
		var modal_height = progress_map_vars.map_script_args['initial']['modal_height']; //@since 4.6
		var modal_openFullscreen = progress_map_vars.map_script_args['initial']['modal_openFullscreen']; //@since 4.6
		var modal_allow_fullscreen = progress_map_vars.map_script_args['initial']['modal_allow_fullscreen']; //@since 4.7
		var modal_top_margin = progress_map_vars.map_script_args['initial']['modal_top_margin']; //@since 5.5
		var modal_bottom_margin = progress_map_vars.map_script_args['initial']['modal_bottom_margin']; //@since 5.5
		
		jQuery('#cspm_single_post_modal').iziModal({
			title: '<br>',
			headerColor: progress_map_vars.map_script_args['initial']['main_rgb_color'],
			width: parseInt(modal_width) + modal_width_unit,
			top: parseInt(modal_top_margin),
			bottom: parseInt(modal_bottom_margin),
			borderBottom: true,
			padding: 0,
			radius: 2,
			zindex: 99999,
			closeOnEscape: true,
			overlay: true,
			overlayClose: true,
			transitionIn: 'fadeInDown',
			iframe: true,
			iframeHeight: parseInt(modal_height),
			iframeURL: iframe_url,
			fullscreen: (modal_allow_fullscreen == 'no') ? false : true,
			openFullscreen: (modal_openFullscreen == 'no') ? false : true,
			bodyOverflow: true,
			onClosed: function(modal){
				jQuery('#cspm_single_post_modal').iziModal('destroy');					
			}
		});
		
		jQuery('#cspm_single_post_modal').iziModal('open');
		
	}
	
	/**
	 * This will display a modal containing the post media elements
	 *
	 * @ince 3.5
	 * @updated 3.6 [Open the single post page link]
	 * @updated 4.6 [Open the nearby map page]
	 * @updated 5.5 [Added new attribute "allow_marker_click" to prevent opening media files on marker click event]
	 * @updated 5.8 [Open a custom/outer link]
	 */
	function cspm_open_media_modal(media, map_id, allow_marker_click){

        if(cspm_object_size(media) == 0 || cspm_is_media_empty(media, map_id))
			return;
		
		var format = media.format;

        /**
		 * Open the single post page if the format is "Link"
		 * @since 3.6 */
		 
		if(format == 'link' && typeof media.link !== 'undefined' && media.link != ''){
			cspm_open_single_post_modal(media.link);
			return;
		}

		/**
		 * Open outer link in a new window if the format is "Custom Link"
		 * @since 5.8 */
		 
		if(format == 'custom_link' && typeof media.custom_url !== 'undefined' && media.custom_url != ''){
			window.open(media.custom_url, '_blank');
			return;
		}
		
		/**
		 * Open the nearby palces map if the format is "Nearby Places Map"
		 * @since 4.6 */
		 
		if(format == 'nearby_places' && typeof media.post_id !== 'undefined' && media.post_id != ''){
			if(progress_map_vars.nearby_places_page_url != ''){
				cspm_open_single_post_modal(progress_map_vars.nearby_places_page_url+'?post_id='+media.post_id);
			}
			return;
		}
		
		/**
		 * Return if marker click is disabled 
		 * @since 5.5 */
		 
		if(allow_marker_click == 'no')
			return;
        
        /**
         * Build the modal 
         * @since 5.6.3 */
        
		var modal_class_name = 'div.cspm_location_modal';	   

        if(jQuery(modal_class_name).length == 0){
            jQuery('body').append('<div class="cspm_location_modal"></div>');
        }
        
		/**
		 * This will output the modal content based on the media format.
		 * @since 3.5 */
		 
		var cspm_modal_output = function(media, map_id){
			
			var format = media.format;
			var gallery = media.gallery;
			var image = media.image;
			var embed = media.embed;
			var audio = media.audio;
			
			var output = '';
			
			/**
			 * Media container */
			
			output += '<div class="cspm_modal_media_container" data-map-id="'+map_id+'">';
				
				/**
				 * Close modal button */
				 
				output += '<a href="javascript:void(0)" class="cspm_close_modal cspm_modal_action_btn" data-map-id="'+map_id+'">';
					output += '<img src="'+progress_map_vars.plugin_url+'img/svg/close_modal.svg" />';
				output += '</a>';
		
				if(format == 'all' || format == 'gallery'){
					
					/**
					 * Media preview area */
					 
					output += '<div class="cspm_modal_carousel_preview cspm_border_top_radius" data-map-id="'+map_id+'"></div>';
					
					/**
					 * Carousel media items container */
					
					if(format == 'all' || format == 'gallery'){
						
						output += '<div class="cspm_modal_carousel_container" data-map-id="'+map_id+'">';
						
							/**
							 * Prev & Next buttons */
						 
							output += '<div class="cspm_modal_carousel_prev cspm_border_right_radius cspm_border_shadow cspm_modal_action_btn" data-map-id="'+map_id+'">';
								output += '<img src="'+progress_map_vars.plugin_url+'img/svg/prev.svg" />';
							output += '</div>';
							
							output += '<div class="cspm_modal_carousel_next cspm_border_left_radius cspm_border_shadow cspm_modal_action_btn" data-map-id="'+map_id+'">';
								output += '<img src="'+progress_map_vars.plugin_url+'img/svg/next.svg" />';
							output += '</div>';
							
							/**
							 * Carousel media items/thumbs */
						 
							output += '<div class="cspm_modal_carousel" data-map-id="'+map_id+'">';
							
								var index = 0;
								
								/**
								 * Gallery */
								
								if(cspm_object_size(gallery) > 0){
									
									jQuery.each(gallery, function(i, img){
										if(cspm_object_size(img) && img.preview != '' && img.thumb != ''){
											index = i;
											output += '<div class="cspm_modal_carousel_item" data-index="'+index+'" data-type="image" data-src="'+img.preview+'" data-full-src="'+img.fullscreen+'">';
												output += '<img class="cspm_border_radius cspm_border_shadow" src="'+img.thumb+'" />';
											output += '</div>';
										}
									});
									
								}
								
								/**
								 * Image */
								 
								if(format == 'all' && cspm_object_size(image) > 0){
									output += '<div class="cspm_modal_carousel_item" data-index="'+index+'" data-type="image" data-src="'+image.preview+'" data-full-src="'+image.fullscreen+'">';
										output += '<img class="cspm_border_radius cspm_border_shadow" src="'+image.thumb+'" />';
									output += '</div>';
								}
								
								/**
								 * Audio */
								 
								if(format == 'all' && audio != ''){
									output += '<div class="cspm_modal_carousel_item cspm_thumb_audio cspm_border_radius cspm_border_shadow" data-index="'+(++index)+'" data-type="audio" data-src="'+audio+'" data-full-src="">';
										output += '<img src="'+progress_map_vars.plugin_url+'img/svg/audio.svg" />';
									output += '</div>';
								}
								
								/**
								 * Embed */
								 
								if(format == 'all' && cspm_object_size(embed) > 0){ 
									
									if(typeof embed.html != 'undefined' && embed.html != ''){
										
										var embed_title = embed.title;
										var embed_type = embed.type;
										var embed_thumb = embed.thumb;
										var embed_provider = embed.provider;
										var embed_html = embed.html;
										
										var thumb_url = (embed_thumb != '') ? embed_thumb : progress_map_vars.plugin_url+'img/svg/video.svg';
										var thumb_conatiner_class = (embed_thumb != '') ? 'cspm_thumb_embed' : 'cspm_default_thumb_embed cspm_border_radius cspm_border_shadow';
										var thumb_img_class =  (embed_thumb != '') ? 'cspm_border_radius cspm_border_shadow' : '';
										
										output += '<div class="cspm_modal_carousel_item '+thumb_conatiner_class+'" data-index="'+(++index)+'" data-type="embed" data-src="'+embed_html+'" data-full-src="">';
											output += '<img src="'+thumb_url+'" class="'+thumb_img_class+'" title="'+embed_title+'" alt="'+embed_title+'" />';
										output += '</div>';
										
									}
								
								}
								
							output += '</div>';
								
						output += '</div>';
					
					}

				}else{
							
					/**
					 * Image */
					 
					if(format == 'image' && cspm_object_size(image) > 0)
						output += '<div class="cspm_modal_media_img cspm_border_radius" data-map-id="'+map_id+'">'+media_format_output(format, image.single, image.fullscreen)+'</div>';
					
					/**
					 * Audio */
					 
					if(format == 'audio' && audio != '')
						output += '<div class="cspm_modal_media_audio cspm_border_radius cspm_border_shadow" data-map-id="'+map_id+'">'+media_format_output(format, audio, '')+'</div>';
					
					/**
					 * Embed */
					 
					if(format == 'embed' && cspm_object_size(embed) > 0){

						if(typeof embed.html != 'undefined' && embed.html != '')
							output += '<div class="cspm_modal_media_embed cspm_border_radius cspm_border_shadow" data-map-id="'+map_id+'">'+media_format_output(format, embed.html, '')+'</div>';						
							
					}
					
				}
					
			output += '</div>';
			
			return output;
			
		}
		
		/**
		 * Get the media output based on its type.
		 * @since 3.5 */
		 
		var media_format_output = function(type, src, full_src){
			
			var output = '';
			
			/**
			 * Image */
			 
			if(type == 'image'){

				output += '<img src="'+src+'" />';
				output += '<a href="'+full_src+'" target="_blank" class="cspm_open_fullscreen_img cspm_modal_action_btn">';
					output += '<img src="'+progress_map_vars.plugin_url+'img/svg/fullscreen.svg" />';
				output += '</a>';
			
			/**
			 * Audio */
			 
			}else if(type == 'audio'){
		
				output += '<audio class="cspm_audio_player" preload="none" controls controlsList="nodownload" autoplay="true" style="width:100% !important; margin:0 !important;">';
					output += '<source src="'+src+'">';
				output += '</audio>';
			
			/**
			 * Embed */
			 
			}else if(type == 'embed'){
				
				var iframe_HTML = src.replace(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
				
				output += '<div class="cspm_embed_container">';
					output += iframe_HTML;
				output += '</div>';								
				
			}
			
			return output;
						
		}
		
		/**
		 * This will display the selected media from the carousel in the preview area.
		 * This works only for the format 'All' & 'Gallery'!
		 * @since 3.5 */
		 
		var preview_media_output = function(type, src, fullscreen_size){
			
			var output = media_format_output(type, src, fullscreen_size);
			
			jQuery('div.cspm_modal_carousel_preview[data-map-id='+map_id+']').fadeOut('slow', function(){
				
				jQuery(this).attr('data-type', type).html(output).fadeIn('fast');
				
				if(type == 'audio')
					jQuery('.cspm_audio_player').mediaelementplayer();
			
			});
			
		}

		/**
		 * Initialize the modal
         * @edited 5.6.3 */

		if(jQuery(modal_class_name).length > 0){
            jQuery(modal_class_name).iziModal({
                background: '#fefefe',
                overlayClose: true,
                focusInput: false,
                borderBottom: false,
                width: 150,
                zindex: 9999,
                restoreDefaultContent: true,
                transitionIn: 'fadeInDown',
            });
        }
				
		/**
		 * Modal output */
		
		var modal_HTML = cspm_modal_output(media, map_id);
		
		/**
		 * On opening, start loading the modal */
		 
		jQuery(modal_class_name).on('opening', function(e, modal){
			jQuery(modal_class_name).iziModal('startLoading');		
		});
		
		/**
		 * When opend, set the modal content */
		 
		jQuery(modal_class_name).on('opened', function(e, modal){
			
			jQuery(modal_class_name).iziModal('setContent', modal_HTML);			
				jQuery('.cspm_modal_action_btn').hide();
			
			/**
			 * Gallery & All format */
			 
			if(format == 'all' || format == 'gallery'){

				/** 
				 * Initialize the carousel */
				 
				var cspm_modal_carousel = new Siema({
					
					selector: 'div.cspm_modal_carousel[data-map-id='+map_id+']',
					loop: true,
					perPage: {
						100: 1, 300: 3, 400: 3, 
						500: 4, 600: 5, 700: 5,
					},	
						
					/**
					 * On init, show the first carousel item on the preview area */
															
					onInit: function(){		
						var type = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index=0]').attr('data-type');
						var src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index=0]').attr('data-src');
						var full_src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index=0]').attr('data-full-src');
						preview_media_output(type, src, full_src);
					},	
					
					/**
					 * On change, get the current slide index an show the media on the preview area */
																
					onChange: function(){						 
						var index = this.currentSlide;
						var type = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-type');
						var src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-src');							
						var full_src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-full-src');
						preview_media_output(type, src, full_src);
					}
					
				});
				
				/**
				 * Detect the click on the carousel items and display the selected item in the preview area */
				 
                jQuery(document).on('click', 'div.cspm_modal_carousel[data-map-id='+map_id+'] div.cspm_modal_carousel_item', function(){ //@edited 5.6.3
					var index = jQuery(this).attr('data-index');
					var type = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-type');
					var src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-src');
					var full_src = jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] div[data-index='+index+']').attr('data-full-src');
					preview_media_output(type, src, full_src);
				});	
		
				/**
				 * Detect the click on the previous & next btns and display the current item in the preview area */
				 				
                jQuery(document).on('click', 'div.cspm_modal_carousel_prev[data-map-id='+map_id+']', function(){ //@edited 5.6.3
					cspm_modal_carousel.prev(cspm_modal_carousel.perPage); 
				});
				
                jQuery(document).on('click', 'div.cspm_modal_carousel_next[data-map-id='+map_id+']', function(){ //@edited 5.6.3
					cspm_modal_carousel.next(cspm_modal_carousel.perPage);
				});	
		
				/**
				 * Stop the modal loading when all images loaded & display the modal btns/links */
				 
				jQuery('div.cspm_modal_carousel[data-map-id='+map_id+'] .cspm_modal_carousel_item').imagesLoaded().done(function(instance){
					jQuery(modal_class_name).iziModal('stopLoading');
					jQuery(modal_class_name).iziModal('setWidth', 700);										
					cspm_modal_carousel.resizeHandler(); // Resize the carousel	
					jQuery('.cspm_modal_action_btn').show();									
				});
			
			/**
			 * Image format */
			 		
			}else if(format == 'image'){
				
				jQuery('div.cspm_modal_media_img[data-map-id='+map_id+']').imagesLoaded().done(function(instance){
					
					jQuery(modal_class_name).iziModal('stopLoading');
					jQuery(modal_class_name).iziModal('setWidth', 700);	
					jQuery('.cspm_modal_action_btn').show();						
				
				}).fail(function(instance){
					
					jQuery(modal_class_name).iziModal('close');
					
					if(!cspm_popup_msg(map_id, 'error', 'Oops!', progress_map_vars.broken_img_msg))
						alert('Oops! '+progress_map_vars.broken_img_msg);
				
				});
			
			/**
			 * Audio & Video format */
			 	
			}else{
				jQuery(modal_class_name).iziModal('stopLoading');
				jQuery(modal_class_name).iziModal('setWidth', (format == 'audio') ? 400 : 700);												
				jQuery('.cspm_modal_action_btn').show();	
			}
			
			if(format == 'audio'){
				jQuery(modal_class_name).css({'min-height': 'auto'});
				jQuery('.cspm_audio_player').mediaelementplayer();
			}

		});
	
		/**
		 * On closed, remove the modal and destroy it */
			 
		jQuery(modal_class_name).on('closed', function(e, modal){
			jQuery(modal_class_name).remove().iziModal('destroy');
		});

		/**
		 * Open the modal */
			 
		jQuery(modal_class_name).iziModal('open');
		
		/**
		 * Close the modal */
		
        jQuery(document).on('click', 'a.cspm_close_modal', function(){ //@edited 5.6.3
			jQuery(modal_class_name).iziModal('close');
		});
		
	}
	
	/**
	 * This will check if the post media is available based on its format
	 *
	 * @since 3.5
	 * @updated 3.6 | 5.5 | 5.8
	 */
	function cspm_is_media_empty(media, map_id){

		var format = media.format;
		var gallery = media.gallery;
		var image = media.image;
		var embed = media.embed;
		var audio = media.audio;
		var link = media.link; //@since 3.6
		var custom_url = media.custom_url; //@since 5.8

		if((format == 'all' 
			&& (cspm_object_size(gallery) > 0 
				|| cspm_object_size(image) > 0 
				|| audio.length > 0 //@edited 5.5
				|| (typeof embed.html !== 'undefined' && embed.html.length > 0) //@edited 5.5
			))
		|| (format == 'link' && link != '') //@since 3.6
		|| (format == 'gallery' && cspm_object_size(gallery) > 0)
		|| (format == 'image' && cspm_object_size(image) > 0)
		|| (format == 'audio' && audio.length > 0) //@edited 5.5
		|| (format == 'embed' && (typeof embed.html !== 'undefined' && embed.html.length > 0)) //@edited 5.5
		|| (format == 'nearby_places') 
        || (format == 'custom_link' && custom_url != '') //@since 5.8
          ){
			
			return false;
			
		}else return true;
			
	}
	
	
	/**
	 * This will open a message that informs the user that if they click ...
	 * ... on the marker, media will be displayed.
	 *
	 * @since 3.5
	 * @updated 5.5
	 */
	function cspm_open_media_message(media, map_id){

		if(cspm_object_size(media) == 0)
			return;
			
		if(typeof media.format === 'undefined' || media.format == '')
			return;

		var format = media.format;
		var gallery = media.gallery;
		var image = media.image;
		var embed = media.embed;
		var audio = media.audio;
		
		/**
		 * Don't show popup message for markers with media type format & marker click is disabled 
		 * @since 5.5 */
		 
		if(jQuery.inArray(format, ['link', 'nearby_places']) === -1 && media.marker_click == 'no')
			return;
			
		if(!cspm_is_media_empty(media, map_id)){
			var message = progress_map_vars.media_format_msg['format_'+format+'_msg'];
			if(message != ''){
				cspm_popup_msg(map_id, 'info', '',  message); 
			}
		}
		
	}
	
	
	/**
	 * This will close the post media message
	 *
	 * @since 3.5
	 */
	function cspm_close_media_message(map_id){
		
		cspm_close_popup_msg(map_id, 'info');
 	
	}
	
	
	/**
	 * This will pop-up a (success, error, info or warning) message
	 * [@type] Accepts "success", "info", "warning", "error".
	 *
	 * @since 3.5 
	 */
	function cspm_popup_msg(map_id, type, title, message){
		
		if(typeof iziToast === 'undefined'){
			
			/**
			 * A fallback if something went wrong with iziToast */
			 
			if(type == 'error')
				return false; // Errors will be displayed using alert()!
				
			var message_type = type.replace('success', 'green').replace('info', 'blue').replace('warning', 'orange').replace('error', 'red').replace('fatal_error', 'red');
			
			var map_message_widget = jQuery('div.cspm_map_'+message_type+'_msg_widget[data-map-id='+map_id+']');
			
			map_message_widget.text(message).removeClass('fadeOut').addClass('cspm_animated fadeIn').css({'display':'block'});
			
			setTimeout(function(){ 
				map_message_widget.removeClass('fadeIn').addClass('fadeOut');
				setTimeout(function(){
					map_message_widget.css({'display':'none'});
				}, 100);
			}, 3000);
			
		}else{
						
			var default_settings = {
				class: 'cspm_'+type+'_popup_'+map_id,				
				message: message,
				animateInside: true,
				titleSize: 13,
				messageSize: 13,						
				maxWidth: 450,
			}
			
			var custom_settings = {
				success: { id: 'cspm_success_popup_'+map_id, position: 'topRight', transitionIn: 'fadeInLeft', timeout: 3000, displayMode: 1, close: true, },
				info: { position: 'topLeft', transitionIn: 'fadeIn', timeout: 3000, displayMode: 2, close: false, },
				warning: { id: 'cspm_warning_popup_'+map_id, position: 'center', transitionIn: 'fadeInUp', timeout: 3000, toastOnce: true, close: true, },			
				error: { id: 'cspm_error_popup_'+map_id, title: title, icon: '', position: 'center', transitionIn: 'fadeInUp', timeout: 15000, displayMode: 1, close: true, },
				fatal_error: { id: 'cspm_fatal_error_popup_'+map_id, title: title, icon: '', position: 'center', transitionIn: 'fadeInUp', timeout: false, displayMode: 1, close: true, drag: false, }, //@edited 5.6.5
				detailed_warning: { id: 'cspm_warning_popup_'+map_id, title: title, position: 'center', transitionIn: 'fadeInUp', timeout: 10000, displayMode: 1, close: true, }, //@since 5.2
			};
			
			var popup_settings = jQuery.extend({}, default_settings, custom_settings[type]);
	
			if(type == 'success')
				iziToast.success(popup_settings);
			
			else if(type == 'error' || type == 'fatal_error')
				iziToast.error(popup_settings);
			
			else if(type == 'warning' || type == 'detailed_warning')
				iziToast.warning(popup_settings);
			
			else iziToast.info(popup_settings);
			
		}
		
		return true;
		
	}
	
	
	/**
	 * This will close the poped-up message
	 *
	 * @since 3.5 
	 */
	function cspm_close_popup_msg(map_id, type){
		
		if(typeof iziToast === 'undefined')
			return false;	
					
		jQuery('.cspm_'+type+'_popup_'+map_id).each(function(i, message){
			iziToast.hide({
				transitionOut: 'fadeOutUp'
			}, message);
		});
		
		return true;
 	
	}
	
	
	/**
	 * This will display a marker/icon on the top-right corner of a "Ground Overlay" ...
	 * ... which we will use to hide & show the image.
	 *
	 * @since 3.5 
	 */	 
	function cspm_groundoverlay_callback(map_id, plugin_map, overlay){

		if(typeof map_id === 'undefined' || typeof plugin_map != 'object' || typeof overlay != 'object')
			return;
		
		var visibility = overlay.visibility;
				
		if(visibility == 'always' || visibility == 'marker_connected')
			return;

		var ne_latLng = overlay.getBounds().getNorthEast();
		
		var opacity = overlay.opacity;
		var original_opacity = overlay.original_opacity;									
		
		var show_icon = overlay.show_icon;
		var hide_icon = overlay.hide_icon;
		var icon_position = (overlay.icon_position && overlay.icon_position != '') ? overlay.icon_position : ne_latLng;

		var marker_icon = (opacity == 0) ? show_icon : hide_icon;

		plugin_map.gmap3({
			marker:{
				latLng: icon_position,
				tag: 'cspm_groundoverlay_marker_'+map_id,
				options:{
					icon: {
						url: marker_icon,
					},
					marker_id: 'groundoverlay_marker',
				},
				events: {
					click: function(marker, event, elements){
						if(overlay.getOpacity() == 0){
							marker.setIcon(hide_icon);
							overlay.setOpacity(original_opacity);
							cspm_close_popup_msg(map_id, 'info');														
						}else{														
							marker.setIcon(show_icon);
							overlay.setOpacity(0);
							cspm_close_popup_msg(map_id, 'info');	
						}
					},
					mouseover: function(){
						cspm_popup_msg(map_id, 'info', '',  progress_map_vars.hide_img_overlay_msg);
					},
					mouseout: function(){
						cspm_close_popup_msg(map_id, 'info');
					}
				}
			}
		});
	
	}
	
	
	/**
	 * This will display a GMaps API status message to allow debugging errors.
	 *
	 * @since 3.9
	 */
	function cspm_api_status_info(map_id, api_status_codes, current_status, api_string_name){
		
		if(typeof map_id === 'undefined' || typeof api_status_codes === 'undefined' || typeof current_status === 'undefined')
			return;
							
		var message = '';
		var msg_type = '';
		
		if(typeof api_status_codes.warning[current_status] !== 'undefined'){
			message = api_status_codes.warning[current_status];
			msg_type = 'warning';
			
		}else if(typeof api_status_codes.error[current_status] !== 'undefined'){
			message = '<br />' + api_status_codes.error[current_status];
			msg_type = 'error';
			
		}else if(typeof api_status_codes.console[current_status] !== 'undefined'){
			message = api_status_codes.console[current_status];
			msg_type = 'console';
		}
		
		if(message != ''){
			message = message.replace(/&quot;/g, '"').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
			if(msg_type !== 'console')
				cspm_popup_msg(map_id, msg_type, current_status, message);
			else console.warn('PROGRESS MAP, "' + api_string_name + '": (' + current_status + ') ' + message);
		}
		
	}
	
	
	/** 
	 * This will show error messages when Google Maps fails to load.
	 * @since 5.6.5
	 */
	function cspm_check_gmaps_failure(map_id){

		var GCC_link = '<a href="https://console.cloud.google.com/apis/dashboard" target="_blank">Google Cloud Console</a>';
		var required_apis_list = '<ul><li>Maps JavaScript API</li><li>Directions API</li><li>Distance Matrix API</li><li>Geocoding API</li><li>Places API</li></ul>';
								
		if(window.console && console.error){
			
			var original_console_errors = console.error;
			
			/**
			 * Overwrite original console error to read errors and show our custom messages */
			 
			console.error = function(){	
					
				if(typeof arguments[0].indexOf === 'function'){
					
					var title = ''; var message = ''; var note = '';
					
					/**
					 * Missing an API Key */
					 
					if(arguments[0].indexOf('You are using this API without a key') != -1 
					|| arguments[0].indexOf('MissingKeyMapError') != -1){
						
						title = 'The API Key is missing';
						
						message  = 'You are using this map without an API Key. ';
						message += 'Please make sure to add an API Key in the map settings.<br /><br />';
						message += 'If you already have an API Key, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/insert-the-google-maps-api-key/" target="_blank">link</a> to see where to use it. ';
						message += 'if you did not create any API Key, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a> for more details. ';
						message += 'If you are new to Google Maps Platform, visit this <a href="https://developers.google.com/maps/gmp-get-started" target="_blank">link</a> to get started.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API Key added, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Deleted API project from GCC */
					 
					}else if(arguments[0].indexOf('DeletedApiProjectMapError') != -1){
						
						title = 'API project may have been deleted';
						
						message  = 'Your API project may have been deleted from your ' + GCC_link + '. ';
						message += 'Please check the project for which you generated the API key that\'s included in your map settings. ';
						message += 'You can <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">create a new API project and get a new key</a> in the Cloud Console. ';
						message += 'Once created, please make sure to <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/insert-the-google-maps-api-key/" target="_blank">add the new API Key in the map settings</a>. <br /><br />';
						message += 'If you are new to Google Maps Platform, visit this <a href="https://developers.google.com/maps/gmp-get-started" target="_blank">link</a> to get started.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API Key added, it may take up to 5 minutes for settings to take effect!';
						
					/**
					 * API Key not authorised to use "Directions API" */
					 
					}else if(arguments[0].indexOf('Directions Service: This API key is not authorized to use this service or API') != -1){
						
						title = 'The API Key is not authorised to use "Directions API"';
						
						message  = 'The API key you provided in your map setting is not authorized to use the "Directions API". ';
						message += 'Please check the API restrictions settings of your API key in your ' + GCC_link +' to ensure that the "Directions API" is correctly specified in the list of enabled APIs. ';
						message += 'See <a href="https://console.cloud.google.com/project/_/apiui/credential?_ga=2.51736993.949578656.1620363005-1066915472.1487262090" target=_blank">API keys in the Cloud Console</a>. <br /><br />';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API authorised, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * API Key not authorised to use "Distance Matrix API" */
					 
					}else if(arguments[0].indexOf('Distance Matrix Service: This API key is not authorized to use this service or API') != -1){
						
						title = 'The API Key is not authorised to use "Distance Matrix API"';
						
						message  = 'The API key you provided in your map setting is not authorized to use the "Distance Matrix API". ';
						message += 'Please check the API restrictions settings of your API key in your ' + GCC_link +' to ensure that the "Distance Matrix API" is correctly specified in the list of enabled APIs. ';
						message += 'See <a href="https://console.cloud.google.com/project/_/apiui/credential?_ga=2.51736993.949578656.1620363005-1066915472.1487262090" target=_blank">API keys in the Cloud Console</a>. <br /><br />';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API authorised, it may take up to 5 minutes for settings to take effect!';
						
					/**
					 * API Key not authorised to use "Geocoding API" */
					 
					}else if(arguments[0].indexOf('Geocoding Service: This API key is not authorized to use this service or API') != -1){
						
						title = 'The API Key is not authorised to use "Geocoding API"';
						
						message  = 'The API key you provided in your map setting is not authorized to use the "Geocoding API". ';
						message += 'Please check the API restrictions settings of your API key in your ' + GCC_link +' to ensure that the "Geocoding API" is correctly specified in the list of enabled APIs. ';
						message += 'See <a href="https://console.cloud.google.com/project/_/apiui/credential?_ga=2.51736993.949578656.1620363005-1066915472.1487262090" target=_blank">API keys in the Cloud Console</a>. <br /><br />';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API authorised, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * API Key not authorised to use "Places API" */
					 
					}else if(arguments[0].indexOf('Places API error: ApiTargetBlockedMapError') != -1){
						
						title = 'The API Key is not authorised to use "Places API"';
						
						message  = 'The API key you provided in your map setting is not authorized to use the "Places API". ';
						message += 'Please check the API restrictions settings of your API key in your ' + GCC_link +' to ensure that the "Places API" is correctly specified in the list of enabled APIs. ';
						message += 'See <a href="https://console.cloud.google.com/project/_/apiui/credential?_ga=2.51736993.949578656.1620363005-1066915472.1487262090" target=_blank">API keys in the Cloud Console</a>. <br /><br />';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API authorised, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * API Key not authorised to use required APIs */
					 
					}else if(arguments[0].indexOf('ApiTargetBlockedMapError') != -1){
						
						title = 'The API Key is not authorised to use Maps APIs';
						message = 'The API key you provided in your map setting is not authorized to use all Google Maps APIs required by this plugin. Please check the API restrictions settings of your API key in your ' + GCC_link +' to ensure that all of the required APIs you need to use are correctly specified in the list of enabled APIs. See <a href="https://console.cloud.google.com/project/_/apiui/credential?_ga=2.51736993.949578656.1620363005-1066915472.1487262090" target=_blank">API keys in the Cloud Console</a>. <br /><br />Here\'s the list of all required APIs: ' + required_apis_list + 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>';
						note = '<br /><br /><u><strong>Note:</strong></u> Once APIs authorised, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Invalid API Key */
					 
					}else if(arguments[0].indexOf('InvalidKeyMapError') != -1){
						
						title = 'Invalid Google Maps API Key';
						
						message  = 'The <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/insert-the-google-maps-api-key/" target="_blank">API key included in the plugin</a> is not found in your Google Cloud Console or is incorrect. ';
						message += 'Please make sure you are using a correct API key. You can generate a new API key in the '+ GCC_link +'. <br /><br />';
						message += 'For more infos about generating an API Key, visit this <a href="https://developers.google.com/maps/gmp-get-started#api-key" target="_blank">link</a>';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API Key corrected, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * "Geocoding API" not enabled */
					 
					}else if(arguments[0].indexOf('Geocoding Service: This API project is not authorized to use this API.') != -1){
						
						title = 'Missing required API: "Geocoding API"';
						message = 'Please make sure to enable the "Geocoding API" from your ' + GCC_link + '. For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>.';
						note = '<br /><br /><u><strong>Note:</strong></u> Once API enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * "Directions API" not enabled */
					 
					}else if(arguments[0].indexOf('Directions Service: This API project is not authorized to use this API.') != -1){
						
						title = 'Missing required API: "Directions API"';
						
						message  = 'Please make sure to enable the "Directions API" from your ' + GCC_link + '. ';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * "Distance Matrix API" not enabled */
					 
					}else if(arguments[0].indexOf('Distance Matrix Service: This API project is not authorized to use this API.') != -1){
						
						title = 'Missing required API: "Distance Matrix API"';
						
						message  = 'Please make sure to enable the "Distance Matrix API" from your ' + GCC_link + '. ';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * "Places API" not enabled */
					 					
					}else if(arguments[0].indexOf('Places API error: ApiNotActivatedMapError') != -1){
						
						title = 'Missing required API: "Places API"';
						
						message  = 'Please make sure to enable the "Places API" from your ' + GCC_link + '. ';
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Required API(s) not enabled */
					 
					}else if(arguments[0].indexOf('ApiNotActivatedMapError') != -1){
						
						title = 'Missing required API(s)';
						
						message  = 'Please make sure to enable all the required APIs from your ' + GCC_link + '. ' + required_apis_list;
						message += 'For more info about fixing this error, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a>.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Billing not enabled */
					 
					}else if(arguments[0].indexOf('BillingNotEnabledMapError') != -1 
						|| arguments[0].indexOf('BillingNotEnabledMapError') != -1 
						|| arguments[0].indexOf('You must enable Billing on the Google Cloud Project at') != -1){
						
						title = 'Client or project billing not enabled';
						
						message  = 'You have not enabled billing on your project. ';
						message += 'You must enable Billing on the Google Cloud Project associated to this client ID or in your project which is causing this error <a href="https://console.cloud.google.com/project/_/billing/enable?_ga=2.218824337.949578656.1620363005-1066915472.1487262090" target="_blank">here</a>.<br /><br />';
						message += 'If you are new to Google Maps Platform, visit this <a href="https://developers.google.com/maps/gmp-get-started" target="_blank">link</a> to get started.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once billing enabled, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Expired API Key */
					 
					}else if(arguments[0].indexOf('ExpiredKeyMapError') != -1){
						
						title = 'Expired API Key';
						
						message  = 'The API key included in the map settings has expired or is not recognized by the system. ';
						message += 'You may receive this error after creating a new API key if you try to use the key before it is recognized by the system. ';
						message += 'Wait a few minutes and try again, or you may need to generate a new API key in your ' + GCC_link + '<br /><br />';
						message += '- To create an API Key, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a> for all details.<br />';
						message += '- To use the API Key with your map, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/insert-the-google-maps-api-key/" target="_blank">link</a> to see where to use it.<br />';
						message += '- If you are new to Google Maps Platform, visit this <a href="https://developers.google.com/maps/gmp-get-started" target="_blank">link</a> to get started.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API Key created, it may take up to 5 minutes for settings to take effect!';
					
					/**
					 * Invalid Client ID */
					 
					}else if(arguments[0].indexOf('InvalidClientIdMapError') != -1){
						
						title = 'Invalid Client ID';
						
						message  = 'The client ID included in the script element that loads the API is invalid, or expired. ';
						message += 'Please make sure you are using your client ID correctly. ';
						message += 'The client ID should start with "gme-" prefix. ';
						message += 'If you see this error even when using your client ID correctly, the client ID may have expired.<br /><br />';						
						message += 'If you do not have a Premium Plan or Maps APIs for Work license, you need to use a <strong>key</strong> parameter with your API key instead of the <strong>client</strong> parameter.<br /><br />';
						message += 'See the guide to <a href="https://developers.google.com/maps/documentation/javascript/get-api-key#premium-auth" target="_blank">Premium Plan authentication</a>.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> If you don\'t now how to fix this error, contact your Google Account Manager.<br /><br />';
					
					/**
					 * Project Denied */
					 
					}else if(arguments[0].indexOf('ProjectDeniedMapError') != -1){
						
						title = 'Project Denied';
						
						message  = 'Your request has not been completed. You may be able to find the more details about the error in the Cloud Console.<br /><br />';
						message += 'See your ' + GCC_link;
						
						note = '<br /><br /><u><strong>Note:</strong></u> If you don\'t now how to fix this error, contact your Google Account Manager.<br /><br />';
					
					/**
					 * Referer Denied */
					 
					}else if(arguments[0].indexOf('RefererDeniedMapError') != -1){
						
						title = 'Referer Denied';
						
						message  = 'Your application was blocked for non-compliance with the Google Maps Platform Terms of Service, following several email notifications. ';
						message += 'To appeal the block and have your implementation reviewed, please complete <a href="https://docs.google.com/forms/d/e/1FAIpQLSdndZBL6qgP4iDUx8M7i5_yyf1AVji5OEag8qERRJ6vyqK7Kw/viewform" target="_blank">this form</a>. You will receive a response via email within a few business days.';
						
						note = '';
					
					/**
					 * Referer Not Allowed */
					 
					}else if(arguments[0].indexOf('RefererNotAllowedMapError') != -1){
						
						title = 'Referer Not Allowed';
						
						message  = 'The current URL loading the Maps JavaScript API has not been added to the list of allowed referrers. Please check the referrer settings of your API key in your ' + GCC_link + '.<br /><br />';
						message += 'See the Maps JavaScript API and <a href="https://developers.google.com/maps/documentation/javascript/get-api-key#restrict_key" target="_blank">Restricting API keys.</a>';
						
						note = '';
					
					/**
					 * Over Quota Error */
					 
					}else if(arguments[0].indexOf('OverQuotaMapError') != -1){
						
						title = 'Over Quota';
						
						message  = 'The number of requests has exceeded the usage limits for the Maps JavaScript API. Your app\'s requests will work again at the next daily quota reset.<br /><br />';
						message += 'If you are NOT the website owner, there are no steps you can take to fix this error. However, you may want to notify the site owner if possible.<br /><br />';
						message += 'For more details, see the guide to <a href="https://developers.google.com/maps/documentation/javascript/usage" target="_blank">usage limits</a>. The page also explains how you can get higher usage limits.';
						
						note = '';
						
					/**
					 * API Project Error */
					 
					}else if(arguments[0].indexOf('ApiProjectMapError') != -1){
						
						title = 'API Project Map Error';
						
						message  = 'Either the provided API key or the API project with which it is associated, could not be resolved. This error may be temporary. If this error message persists you may need to get a new API key or create a new API project.<br /><br />';
						message += 'If you are NOT the website owner, there are no steps you can take to fix this error. However, you may want to notify the site owner if possible.<br /><br />';
						message += '- To create an API Key, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/create-an-api-key-activate-the-required-apis/" target="_blank">link</a> for all details.<br />';
						message += '- To use the API Key with your map, visit this <a href="https://www.docs.progress-map.com/cspm_guide/first-steps/insert-the-google-maps-api-key/" target="_blank">link</a> to see where to use it.<br />';
						message += '- If you are new to Google Maps Platform, visit this <a href="https://developers.google.com/maps/gmp-get-started" target="_blank">link</a> to get started.';
						
						note = '<br /><br /><u><strong>Note:</strong></u> Once API Key created, it may take up to 5 minutes for settings to take effect!';
					
					} 
					
					if(message != ''){
						cspm_popup_msg(map_id, 'fatal_error', title, '<br />' + message + note);
					}
				
				}
				
				Array.prototype.unshift.call(arguments);
				
				/**
				 * Rewrite original console error by re-adding errors to browser console */
				 
				if(original_console_errors.apply){
					original_console_errors.apply(console, arguments); // Do this for normal browsers
				}else{
					var message = Array.prototype.slice.apply(arguments).join(' ');
					original_console_errors(message); // Do this for IE
				}
				
				/**
				 * End the Progress Bar Loader */
					
				if(typeof NProgress !== 'undefined')
					NProgress.done();
					
			}
			
		}
						
	}
	
		
	/**
	 * Customize element scrollbar
	 * @since 5.6
     * @updated 5.8 */
	
	function cspm_apply_custom_scrollabr(selector){
		if(typeof jQuery(selector).mCustomScrollbar === 'function'){
            jQuery(selector).mCustomScrollbar({
                autoHideScrollbar:false,
                mouseWheel:{
                    enable: true,
                    preventDefault: true,
                },
                theme: 'dark-2',
                live: 'on',
            });
        }
	}

	/**
	 * Destroy custome scrollbar
	 * @since 5.8 */
	
    function cspm_destroy_csutom_scrollbar(selector){
        if(typeof jQuery(selector).mCustomScrollbar === 'function'){
            jQuery(selector).mCustomScrollbar("destroy");
        }
    }


	/**
	 * Find if a polygon is clockwise or counter-clockwise.
	 * Return TRUE if Clockwise (-negative sum) & FALSE if Counter-clockwise (+positive sum).
	 *
	 * Note that the area of a convex polygon is defined to be positive if the points are ...
	 * ... arranged in a counterclockwise order, and negative if they are in clockwise order!
	 *
	 * @since 5.3
	 */
	function cspm_is_clockwise(vertices){		
		var sum = 0;
		for(var i=0; i<vertices.length-1; i++){
			var current = vertices[i],
				next = vertices[i+1];
			sum += (next[0] - current[0]) * (next[1] + current[1]);
		}
		return sum < 0;
	}
		
//=========================//
//==== Other functions ====//
//=========================//
	
    /**
     * Convert numbers into a more readable format like "10k"
     * @since 5.8 */
    function cspm_compact_number(num){
        return Intl.NumberFormat('en-US', {
          notation: "compact",
          maximumFractionDigits: 1
        }).format(num);
    }

	// Remove duplicated emlements from an array
	// @since 2.5
	function cspm_remove_array_duplicates(array){
		var new_array = [];
		var i = 0;
		jQuery.each(array, function(index, element){
			if(jQuery.inArray(element, new_array) === -1){
				new_array[i] = element;	
				i++;
			}
		});
		return new_array;
	}

	function cspm_object_size(obj){
			
		var size = 0, key;
		for (key in obj) {
			if (obj.hasOwnProperty(key)) size++;
		}
		return size;
					
	}

	/*function cspm_strpos(haystack, needle, offset) {
		
	  // From: http://phpjs.org/functions
	  // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	  // +   improved by: Onno Marsman
	  // +   bugfixed by: Daniel Esteban
	  // +   improved by: Brett Zamir (http://brett-zamir.me)
	  // *     example 1: strpos('Kevin van Zonneveld', 'e', 5);
	  // *     returns 1: 14
	  var i = (haystack + '').indexOf(needle, (offset || 0));
	  return i === -1 ? false : i;
	  
	}*/

	/*function cspm_deg2rad(angle) {
		
	  // From: http://phpjs.org/functions
	  // +   original by: Enrique Gonzalez
	  // +     improved by: Thomas Grainger (http://graingert.co.uk)
	  // *     example 1: deg2rad(45);
	  // *     returns 1: 0.7853981633974483
	  return angle * .017453292519943295; // (angle / 180) * Math.PI;
	
	}*/
	
	/**
	 * Return the difference of two arrays
	 * @since 5.0 
	 */
	 function cspm_arrayDiff(arrayA, arrayB){
		return arrayA.concat(arrayB).filter(function (e, i, array) {
			// Check if the element is appearing only once
			return array.indexOf(e) === array.lastIndexOf(e);
		});
	}	

	/**
	 * Replace all SVG images with inline SVG
	 * @since 3.8 */
		 
	function cspm_convert_img_to_svg(){
		
		jQuery('img.cspm_svg').each(function(){
			
			var $img = jQuery(this);
			var imgID = $img.attr('id');
			var imgClass = $img.attr('class');
			var imgURL = $img.attr('src');
			var imgStyle = $img.attr('style');
		
			jQuery.get(imgURL, function(data) {
				// Get the SVG tag, ignore the rest
				var $svg = jQuery(data).find('svg');
		
				// Add replaced image's ID to the new SVG
				if(typeof imgID !== 'undefined') {
					$svg = $svg.attr('id', imgID);
				}
				// Add replaced image's classes to the new SVG
				if(typeof imgClass !== 'undefined') {
					$svg = $svg.attr('class', imgClass);
				}
				// Add replaced image's style to the new SVG
				if(typeof imgStyle !== 'undefined') {
					$svg = $svg.attr('style', imgStyle);
				}
		
				// Remove any invalid XML tags as per http://validator.w3.org
				$svg = $svg.removeAttr('xmlns:a');
		
				// Check if the viewport is set, if the viewport is not set the SVG wont't scale.
				if(!$svg.attr('viewBox') && $svg.attr('height') && $svg.attr('width')) {
					$svg.attr('viewBox', '0 0 ' + $svg.attr('height') + ' ' + $svg.attr('width'))
				}
		
				// Replace image with new SVG
				$img.replaceWith($svg);
		
			}, 'xml');
		
		});
	}
	
	/**
	 * This will toggle the map sidebar
	 * @since 5.6.5 
	 */
	
	function cspm_toggle_map_sidebar(map_id, content_id, $this){
		
		if(typeof $this !== 'object' || typeof map_id === 'undefined' || typeof content_id === 'undefined')
			return;

        var action = $this.attr('data-action');
		var side = $this.attr('data-side');		
		
		if(typeof action === 'undefined' || typeof side === 'undefined')
			return;
		
		if(jQuery('.cspm_sidebar.'+side+'[data-map-id='+map_id+'][data-content-id='+content_id+']').length > 0){
			jQuery('.cspm_sidebar.'+side+'[data-map-id='+map_id+'][data-content-id='+content_id+']').trigger('sidebar:'+action);
		}
		
		return false;
		
	}
	
 	var posts_to_retrieve = {};
	
	/**
	 * Save the post_ids in the global object */
	 
	cspm_global_object.posts_to_retrieve = {};

    (function($){
		        
		/**
		 * Map sidebars 
		 * @since 5.6.5 */
		 
		var cspm_sidebars = ['left', 'top', 'right', 'bottom'];	
		
		for(var i = 0; i < cspm_sidebars.length; ++i){
			var cspm_side = cspm_sidebars[i];
			if($('.cspm_sidebar.'+cspm_side).length > 0){
				$('.cspm_sidebar.'+cspm_side).sidebar({side: cspm_side});
			}
		}
			 
		$(document).on('click', '.cspm_toggle_map_side', function(){
			var map_id = $(this).attr('data-map-id');
            var content_id = $(this).attr('data-content-id');
			cspm_toggle_map_sidebar(map_id, content_id, $(this));
		});		
				
		/**
		 * Customize Checkbox and Radio buttons
		 * @since 1.0 */
		
		$.each($('form.faceted_search_form'), function(i, obj){
			
			var map_id = $(this).attr('data-map-id');
					
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

			if(typeof $('form.faceted_search_form[data-map-id='+map_id+'] input').iCheck === 'function'){

				if(progress_map_vars.map_script_args[map_script_id]['faceted_search_input_skin'] == 'line'){
					
					var skin_color = (progress_map_vars.map_script_args[map_script_id]['faceted_search_input_color'] != 'black') ? '-' + progress_map_vars.map_script_args[map_script_id]['faceted_search_input_color'] : '';
					
					$('form.faceted_search_form[data-map-id='+map_id+'] input').each(function(){
						
						var self = $(this),
						  label = self.next(),
						  label_text = label.text();
						
						label.remove();
						self.iCheck({
							checkboxClass: 'icheckbox_line' + skin_color,
							radioClass: 'iradio_line' + skin_color,
							insert: '<div class="icheck_line-icon"></div>' + label_text,
							inheritClass: true
						});
					
					});
					
				}else{
					
					if(progress_map_vars.map_script_args[map_script_id]['faceted_search_input_skin'] != 'polaris' && progress_map_vars.map_script_args[map_script_id]['faceted_search_input_skin'] != 'futurico')
						var skin_color = (progress_map_vars.map_script_args[map_script_id]['faceted_search_input_color'] != 'black') ? '-' + progress_map_vars.map_script_args[map_script_id]['faceted_search_input_color'] : '';
					else var skin_color = '';

					$('form.faceted_search_form[data-map-id='+map_id+'] input').iCheck({
						checkboxClass: 'icheckbox_' + progress_map_vars.map_script_args[map_script_id]['faceted_search_input_skin'] + skin_color,
						radioClass: 'iradio_' + progress_map_vars.map_script_args[map_script_id]['faceted_search_input_skin'] + skin_color,
						increaseArea: '20%',
						inheritClass: true
					});
				
				}
		
			}
		
		});
	
		/**
		 * Faceted search
		 * @edited 5.5 */
		
        $(document).on('click', 'div.faceted_search_btn', function(){ //@edited 5.6.3

			var map_id = $(this).attr('id');
		
			$('div.cspm_top_element:not(.faceted_search_container_'+map_id+')').hide();
			
			$('div.faceted_search_container_'+map_id).slideToggle(0, 'easeOutCirc', function(){
				
				if($(this).is(':visible')){
					
					/**
					 * Call custom scroll bar for faceted search list
                     * @edited 5.8 */
                    
                    cspm_destroy_csutom_scrollbar('div.faceted_search_container_'+map_id+' form.faceted_search_form ul');
					cspm_apply_custom_scrollabr('div.faceted_search_container_'+map_id+' form.faceted_search_form ul');							
					
				}
				
			});			
			
		});

        $(document).on('click', 'a[class^=reset_map_list]', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@added 5.5
			
			var map_id = $(this).attr('id');

			var icheck_selector = $('form#faceted_search_form_'+map_id+' input');
			
			if(typeof icheck_selector.iCheck === 'function')
				icheck_selector.iCheck('uncheck');
			
			$(this).hide();
			
			/**
			 * Reset nearby points of interest */
			
			$('a.cspm_reset_proximities[data-map-id='+map_id+']').trigger('click'); //@edited 5.5
			
		});
		
		var change_event = (typeof $('form.faceted_search_form input').iCheck === 'function') ? 'ifChanged' : 'change';
		
		$('form.faceted_search_form input').on(change_event, function(event){
			
			var map_id = $(this).attr('data-map-id');
			var show_carousel = $(this).attr('data-show-carousel');
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
					
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
			
			var multi_taxonomy_option = progress_map_vars.map_script_args[map_script_id]['faceted_search_multi_taxonomy_option'];
				
			if(typeof NProgress !== 'undefined'){
				
				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				
				NProgress.start();
				
			}
			
			/**
			 * Hide all markers */
			 
			cspm_hide_all_markers(plugin_map, map_id).done(function(){
				
				if(typeof NProgress !== 'undefined')
					NProgress.set(0.5);
				
				if(multi_taxonomy_option == 'false')
					$('a.reset_map_list_'+map_id).show(); //@edited 5.5
				
				posts_to_retrieve[map_id] = [];
				var retrieved_posts = [];
				var i = 0;
				var j = 0;
				var num_checked = 0;
				var count_posts = 0;					
				
				/**
				 * Loop throught checkboxes/radios and get the one(s) selected, then, display related markers */
						
				$('div.faceted_search_container_'+map_id+' form.faceted_search_form input').each(function(){
	
					if($(this).prop('checked') == true){ 
						
						num_checked++;

						var value = $(this).val();

						/**
						 * Loop throught post_ids and check its relation with the current category
						 * Then show markers within the selected category */
						 
						retrieved_posts = cspm_remove_array_duplicates(retrieved_posts.concat(cspm_set_markers_visibility(plugin_map, map_id, value, j, post_lat_lng_coords[map_id], posts_to_retrieve[map_id], true)));

						cspm_simple_clustering(plugin_map, map_id);
						
						i++;
	
					}
					
				});
				
				posts_to_retrieve[map_id] = retrieved_posts;
				
				/**
				 * Show all markers when there is no checked category */
				 
				if(num_checked == 0){
					
					var j = 0;
			
					if(progress_map_vars.map_script_args[map_script_id]['map_type'] == 'search_map'){
						cspm_hide_all_markers(plugin_map, map_id);
                        cspm_simple_clustering(plugin_map, map_id); //@since 5.8
					}else{
						cspm_set_markers_visibility(plugin_map, map_id, null, j, post_lat_lng_coords[map_id], posts_to_retrieve[map_id], false);
						cspm_simple_clustering(plugin_map, map_id);
					}
						
				}
									
				/**
				 * Center the map on the first post in the array 
				 * @since 2.8
				 * @updated 2.8.2 */
											
				var marker_objs_length = count_posts = cspm_object_size(posts_to_retrieve[map_id]);

				if(progress_map_vars.map_script_args[map_script_id]['faceted_search_drag_map'] == 'autofit'){
					
					setTimeout(function(){
						
						var markers_bounds = new google.maps.LatLngBounds();
						
						plugin_map.gmap3({
							get: {
								name: 'marker',
								tag: 'to_cluster',
								all: true,
								callback: function(markers){
                                
									if(!markers)
										return;
									
									var visible_markers = 0;
										
									$.each(markers, function(i, marker){
										
										var marker_id = marker.id;
										
										if(typeof marker !== 'undefined' && marker.getVisible() === true && marker_id != 'user_location'){ //@edited 5.8
											markers_bounds.extend(marker.getPosition());
											visible_markers++;
										}

										if(i == (markers.length-1)){
											setTimeout(function(){								
												if(visible_markers > 0){
													plugin_map.gmap3('get').fitBounds(markers_bounds);
												}else{
													cspm_recenter_map(plugin_map, map_id);
												}
												cspm_zoom_in_and_out(plugin_map);						
											}, 100);
										}

									});

								}
							}
						});
						
					}, 100);
				
				}else if(progress_map_vars.map_script_args[map_script_id]['faceted_search_drag_map'] == 'drag'){
					
					if(marker_objs_length > 0){
						var first_post_latlng = post_lat_lng_coords[map_id][posts_to_retrieve[map_id][0]].split('_');
						cspm_center_map_at_point(plugin_map, map_id, first_post_latlng[0], first_post_latlng[1], 'zoom');
					}
					
				}
								
				/**
				 * Save the post_ids in the global object.
				 * This is usefull when using the plugin with an extension */
				 
				cspm_global_object.posts_to_retrieve[map_id] = posts_to_retrieve[map_id];																											
	
				if(progress_map_vars.map_script_args[map_script_id]['show_posts_count'] == "yes")
					$('span.the_count_'+map_id).empty().html(count_posts);

				/**
				 * Rewrite Carousel */
				
				if(progress_map_vars.map_script_args[map_script_id]['sync_carousel_to_viewport'] == 'no'){ //@since 5.0
					setTimeout(function(){
						cspm_rewrite_carousel(map_id, show_carousel, posts_to_retrieve[map_id]); //@since 5.8
					}, 100);
				}
			
				/**
				 * Hide the Countries list & Remove hover style on Countries list just in case a user ...
				 * ... already choosed a country then decided they want to filter the map
				 * @edited 5.5 */
				
				if($('div.countries_container_'+map_id).length){
					$('li.cspm_country_name[data-map-id='+map_id+']').removeClass('selected');
					$('div.countries_container_'+map_id).hide();
				}
						
				/**
				 * Reset nearby points of interest */
				
				$('a.cspm_reset_proximities[data-map-id='+map_id+']').trigger('click'); //@edited 5.5
			
				/**
				 * End the Progress Bar Loader */
					
				if(typeof NProgress !== 'undefined')
					NProgress.done();
				
			});
				
		});
	
		// @Facetd search ====						
		
		// Search form request
	
		/**
		 * Customize the text box "radius" to slider
		 * @edited 5.6 */
		
		if(typeof $("input.cspm_sf_slider_range").ionRangeSlider === 'function'){
				
			$("input.cspm_sf_slider_range").ionRangeSlider({
				type: 'single',
				skin: 'round', //@since 5.6
				grid: true,
			});
		
		}
		
		/**
		 * Load the search form to the screen
		 * @edited 5.6.3 */
		 
        $(document).on('click', 'div.search_form_btn', function(event){ //@edited 5.6.3

			var map_id = $(this).attr('id');
								
			$('div.cspm_top_element:not(.search_form_container_'+map_id+')').hide();
			
            $('div.search_form_container_'+map_id).slideToggle(0, 'easeOutCirc', function(){
                if($(this).is(':visible')){
                    if(typeof event.originalEvent !== 'undefined'){
                        $('form#search_form_'+map_id+' input[name=cspm_address]').trigger('focus'); //@edited 5.6.3											
                    }
                }
            });
					
		});
		
		/**
		 * Submit the search form data */
		
        $(document).on('click', 'a.cspm_geotarget_search_btn', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@since 5.5
			
			var map_id = $(this).attr('data-map-id');
				        
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

			if(typeof NProgress !== 'undefined'){
				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				NProgress.start();			
			}
					
			var saved_latLng = $('a.cspm_geotarget_search_btn[data-map-id='+map_id+']').attr('data-latLng'); //@edited 5.5
			
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);

			plugin_map.gmap3({
			  getgeoloc:{
				callback : function(latLng){
				  if(latLng){
					
					var lat = latLng.lat();
					var lng = latLng.lng();
					
					if(saved_latLng != lat+','+lng){

						plugin_map.gmap3({
						  getaddress:{
							latLng: latLng,
							callback: function(results){
								var address = (results && results[1]) ? results && results[1].formatted_address : latLng.lat()+','+latLng.lng();
								$('form#search_form_'+map_id+' input[name=cspm_address]').val(address);
								var message = '<strong>Your address:</strong> '+address;
								message += '<br /><strong>Your position:</strong> '+latLng.lat()+','+latLng.lng();
								cspm_popup_msg(map_id, 'info', '',  message);
								$('a.cspm_geotarget_search_btn[data-map-id='+map_id+']').attr('data-latLng', lat+','+lng); //@edited 5.5
							}
						  }
						});	
						
					}
					
				  }else{
					
					/**
					 * More info at https://support.google.com/maps/answer/152197
					 * Deprecate Geolocation API: https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only */
						
					var title = progress_map_vars.geoErrorTitle;
					var message = progress_map_vars.geoErrorMsg;
					
					if(!cspm_popup_msg(map_id, 'error', title, '<br />' + message))
						alert(title + '\n\n' + message);
					
					console.log('PROGRESS MAP: ' + progress_map_vars.geoDeprecateMsg);
					  
				  }
				}
			  }
			});
			
			if(typeof NProgress !== 'undefined')
				NProgress.done();
				
		});
		 
        $(document).on('click', 'a[class^=cspm_submit_search_form_]', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@since 5.5
			
			var map_id = $(this).attr('data-map-id');
			var open_btn = $(this).attr('data-open-btn'); //@since 5.6.5 | We use this to determine if the search form is displayed outside the map
			
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

			if(typeof NProgress !== 'undefined'){
				
				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
			
				NProgress.start();
			
			}
			
			cspm_close_popup_msg(map_id, 'error');				
			
			var show_carousel = $(this).attr('data-show-carousel');
			var address = progress_map_vars.map_script_args[map_script_id]['before_search_address'] + $('form#search_form_'+map_id+' input[name=cspm_address]').val() + progress_map_vars.map_script_args[map_script_id]['after_search_address'];
			
			var draw_circle = progress_map_vars.map_script_args[map_script_id]['sf_circle_option'] == 'true' ? true : false;
			var edit_circle = progress_map_vars.map_script_args[map_script_id]['sf_edit_circle'] == 'true' ? true : false;
			
			/**
			 * Distance in KM or Miles */
			
			var decimals_distance = $('form#search_form_'+map_id+' input[name=cspm_decimals_distance]').val();
			
			if(decimals_distance != '' && typeof e.isTrigger !== 'undefined'){
				var distance = decimals_distance;
			}else var distance = $('form#search_form_'+map_id+' input[name=cspm_distance]').val();
			 
			var distance_unit = $('form#search_form_'+map_id+' input[name=cspm_distance_unit]').val();
			
				/**
				 * [@distance_in_meters] Distance converted to meters */
				 
				if(distance_unit == "metric")
					var distance_in_meters = distance * 1000; // 1KM = 1000Meter
				else var distance_in_meters = (distance * 1.60934) * 1000; // 1Mile = 1.60934KM = 1609.34Meter
			
			/**
			 * Min & Max Distances in KM or Miles */
			 
			var min_distance = $('form#search_form_'+map_id+' input[name=cspm_distance]').attr('data-min');
			var max_distance = $('form#search_form_'+map_id+' input[name=cspm_distance]').attr('data-max');
			
				/**
				 * [@min_distance_in_meters] Min Distance converted to meters
				 * [@max_distance_in_meters] Max Distance converted to meters */
				 
				if(distance_unit == "metric"){
					var min_distance_in_meters = min_distance * 1000; // 1KM = 1000Meter
					var max_distance_in_meters = max_distance * 1000; // 1KM = 1000Meter
				}else{
					var min_distance_in_meters = (min_distance * 1.60934) * 1000; // 1Mile = 1.60934KM = 1609.34Meter
					var max_distance_in_meters = (max_distance * 1.60934) * 1000; // 1Mile = 1.60934KM = 1609.34Meter
				}

			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
			
			var posts_in_search = {};				
			posts_in_search[map_id] = [];

			var geocoder = new google.maps.Geocoder();
			
			/**
			 * Convert our address to Lat & Long */
			 
			geocoder.geocode({ 'address': address }, function (results, status) {
				
				/**
				 * If the address is found */
				 
				if(status == google.maps.GeocoderStatus.OK){
					
					var latLng = results[0].geometry.location; //@since 5.6.5
					var latitude = latLng.lat(); //@edited 5.6.5
					var longitude = latLng.lng(); //@edited 5.6.5
									
					plugin_map.gmap3({
						get: {
							name: 'marker',
							tag: 'to_cluster',
							all:  true,
							callback: function(objs) {
                            
								var j = 0;
								
								/**
								 * Get all markers inside the radius of our address */
								 
								$.each(objs, function(i, obj) {									
									
									var marker_id = obj.id;
									
									/**
									 * Convert the LatLng object to array */
									 
									var post_latlng = obj.position;
									
									/**
									 * Compute the distance and save the post_id */

									if(marker_id != 'user_location' && cspm_compute_distance_between(latitude, longitude, post_latlng.lat(), post_latlng.lng()) < distance_in_meters){
										posts_in_search[map_id][j] = parseInt(marker_id);													
										j++;
									}
									
								});
								
								/**
								 * If one or more posts were found within the radius area */
								 
								if(cspm_object_size(posts_in_search[map_id]) > 0){
									
									if(open_btn == 'yes'){
										$('div.search_form_container_'+map_id).hide();
									} //@edited 5.6.5 | Will make sure not to hide the search form when displayed outside the map

									/**
									 * Hide all markers */
									 
									cspm_hide_all_markers(plugin_map, map_id).done(function(){
				
										/**
										 * Center the map in our address position */
										 
										cspm_center_map_at_point(plugin_map, map_id, latitude, longitude, 'zoom');										
						
										/**
										 * Loop throught post_ids and check the post relation with the current category
										 * Then show markers within the selected category */
										 
										cspm_set_markers_visibility(plugin_map, map_id, null, 0, post_lat_lng_coords[map_id], posts_in_search[map_id], true);
										cspm_simple_clustering(plugin_map, map_id);

										/**
										 * Save the post_ids in the global object
										 * This is usefull when using the plugin with an extension */
										 
										cspm_global_object.posts_to_retrieve[map_id] = posts_in_search[map_id];																																						
										
										if(draw_circle){
											
											var circle_radius = distance_in_meters;										

											/**
											 * [@circle_mouseover_count] | Count the mouseover events so we can decide when to display the ...
											 * ... pop-up message that informs the user that they can reize the circle.
											 * @since 3.5 */
											
											var circle_mouseover_count = 0;
											
											plugin_map.gmap3({
												clear: {
													tag: "search_circle",
													all: true
												},
												circle:{
													tag: 'search_circle',
													options:{
														center: [latitude, longitude],
														radius : circle_radius, // The radius in meters on the Earth's surface
														fillColor : progress_map_vars.map_script_args[map_script_id]['fillColor'],
														fillOpacity: progress_map_vars.map_script_args[map_script_id]['fillOpacity'],
														strokeColor : progress_map_vars.map_script_args[map_script_id]['strokeColor'],
														strokeOpacity: progress_map_vars.map_script_args[map_script_id]['strokeOpacity'],
														strokeWeight: parseInt(progress_map_vars.map_script_args[map_script_id]['strokeWeight']),
														editable: edit_circle,
													},
													callback: function(circle){
														plugin_map.gmap3('get').fitBounds(circle.getBounds());														
													},
													events:{
														mouseover: function(circle){
															
															/**
															 * Inform the user that they can resize the circle ...
															 * ... to show more or less results.
															 * @since 3.5 */
															
															if(edit_circle && (circle_mouseover_count == 0 || circle_mouseover_count == 6)){
																
																var message = progress_map_vars.resize_circle_msg;
																cspm_popup_msg(map_id, 'info', '',  message);
																
																if(circle_mouseover_count == 6)
																	circle_mouseover_count = 1;
																	
															}
															
															circle_mouseover_count++;
															
														},
														mouseout: function(circle){
																														
															/**
															 * Close the info message
															 * @since 3.5 */
															
															if(edit_circle)
																cspm_close_popup_msg(map_id, 'info');

														},
														radius_changed: function(circle){
															
															if(edit_circle){
																															
																/**
																 * Close the info message
																 * @since 3.5 */
															
																cspm_close_popup_msg(map_id, 'info');

																/**
																 * Get circle radius */
																 
																var radius_in_meters = circle.getRadius();
																
																/**
																 * Compare circle radius to min and max distances and redraw circle if needs to */
																
																var readable_distance_unit = (distance_unit == 'metric') ? 'Km' : 'Miles';
																
																if(radius_in_meters > max_distance_in_meters){
																	
																	circle.setRadius(max_distance_in_meters);
																	
																	var message = progress_map_vars.circle_reached_max_msg+' ('+max_distance+readable_distance_unit+')';
																	cspm_popup_msg(map_id, 'warning', '', message);
																	
																}else if(radius_in_meters < min_distance_in_meters){
																	
																	circle.setRadius(min_distance_in_meters);
																	
																	var message = progress_map_vars.circle_reached_min_msg+' ('+min_distance+readable_distance_unit+')';
																	cspm_popup_msg(map_id, 'warning', '', message);
																	
																}
															
																setTimeout(function(){
																
																	var radius_in_meters = circle.getRadius();

																	if(distance_unit == "metric")
																		var radius_in_distance_unit = (radius_in_meters / 1000);
																	else var radius_in_distance_unit = (radius_in_meters / 1609.34);
																																
																	$('form#search_form_'+map_id+' input[name=cspm_decimals_distance]').attr('value', radius_in_distance_unit);
																				
																	var distance_slider = $('input.cspm_sf_slider_range[data-map-id='+map_id+']').data('ionRangeSlider');

																	distance_slider.update({
																		from: radius_in_distance_unit
																	});

																	cspm_hide_all_markers(plugin_map, map_id).done(function(){
																		$('a.cspm_submit_search_form_'+map_id).trigger('click'); //@edited 5.5
																		cspm_zoom_in_and_out(plugin_map);
																	});
																	
																}, 500);
																
															}
																												
														}
													}
												}											
											});
											
										}
										
										/**
										 * Show the reset button */
										 
										$('a.cspm_reset_search_form_'+map_id).show(); //@edited 5.5
				
										/**
										 * Reload post count value */
										 
										if(progress_map_vars.map_script_args[map_script_id]['show_posts_count'] == "yes")
											$('span.the_count_'+map_id).empty().html(posts_in_search[map_id].length);
				
										/**
										 * Rewrite Carousel */
				
										if(progress_map_vars.map_script_args[map_script_id]['sync_carousel_to_viewport'] == 'no'){ //@since 5.0
											setTimeout(function(){
												cspm_rewrite_carousel(map_id, show_carousel, posts_in_search[map_id]); //@since 5.8
											}, 500);
										}
			
									});
								
								/**
								 * No post found */
								 	
								}else{
									
									var message = progress_map_vars.map_script_args[map_script_id]['no_location_found_msg'];
									cspm_popup_msg(map_id, 'warning', '', message);		
									
									/**
									 * If circle resized but no locations found */
									 
									if(decimals_distance != '' && draw_circle && edit_circle){
										
										$('form#search_form_'+map_id+' input[name=cspm_decimals_distance]').val('');							
				
										/**
										 * Reload post count value */
										 
										if(progress_map_vars.map_script_args[map_script_id]['show_posts_count'] == "yes")
											$('span.the_count_'+map_id).empty().html(posts_in_search[map_id].length);
				
										/**
										 * Rewrite Carousel */
				
										if(progress_map_vars.map_script_args[map_script_id]['sync_carousel_to_viewport'] == 'no'){ //@since 5.0
											setTimeout(function(){
												cspm_rewrite_carousel(map_id, show_carousel, posts_in_search[map_id]); //@since 5.8
											}, 500);
										}
			
									}
		
								}

							}
						}
					});
				
				/**
				 * The address is not found */
				 
				}else{
					
					if(status == google.maps.GeocoderStatus.ZERO_RESULTS){
										
						var title = progress_map_vars.map_script_args[map_script_id]['bad_address_msg'];
						
						var sug_1 = progress_map_vars.map_script_args[map_script_id]['bad_address_sug_1'];
						var sug_2 = progress_map_vars.map_script_args[map_script_id]['bad_address_sug_2'];
						var sug_3 = progress_map_vars.map_script_args[map_script_id]['bad_address_sug_3'];										
						
						var message = '<br />' + sug_1 + '<br />' + sug_2 + '<br />' + sug_3;
							
						if(!cspm_popup_msg(map_id, 'error', title, message))
							alert(title + '\n\n' + sug_1 + '\n' + sug_2 + '\n' + sug_3);

					}else{
						var api_status_codes = progress_map_vars.geocoding_api_status_codes; //@since 3.9
						cspm_api_status_info(map_id, api_status_codes, status, 'Geocoding API'); //@since 3.9
					}
					
				}
			
				/**
				 * Hide the Countries list & Remove hover style on Countries list just in case a user ...
				 * ... already choosed a country then decided they want to search the map
				 * @edited 5.5 */
				
				if($('div.countries_container_'+map_id).length){
					$('li.cspm_country_name[data-map-id='+map_id+']').removeClass('selected');
					$('div.countries_container_'+map_id).hide();
				}
			
				/**
				 * Reset nearby points of interest */
				
				$('a.cspm_reset_proximities[data-map-id='+map_id+']').trigger('click'); //@edited 5.5
			
				if(typeof NProgress !== 'undefined')
					NProgress.done();
				
			});
			
		});
		
		/**
		 * Reset the search form & the map */
		 
        $(document).on('click', 'a[class^=cspm_reset_search_form]', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@since 5.5
			
			var map_id = $(this).attr('data-map-id');
					
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

			if(typeof NProgress !== 'undefined'){

				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				
				NProgress.start();
				
			}
							
			var show_carousel = $(this).attr('data-show-carousel');
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
			
			posts_to_retrieve[map_id] = [];
			
			if(progress_map_vars.map_script_args[map_script_id]['map_type'] == 'search_map'){
				cspm_hide_all_markers(plugin_map, map_id);
                cspm_simple_clustering(plugin_map, map_id); //@since 5.8
			}else{
				cspm_set_markers_visibility(plugin_map, map_id, null, 0, post_lat_lng_coords[map_id], posts_to_retrieve[map_id], false);
				cspm_simple_clustering(plugin_map, map_id);
			}
			
			/**
			 * Save the post_ids in the global object
			 * This is usefull when using the plugin with an extension */
			 
			cspm_global_object.posts_to_retrieve[map_id] = posts_to_retrieve[map_id];																											
										
			if(progress_map_vars.map_script_args[map_script_id]['show_posts_count'] == "yes")
				$('span.the_count_'+map_id).empty().html(posts_to_retrieve[map_id].length);
			
			if(typeof NProgress !== 'undefined')
				NProgress.set(0.5);
								
			plugin_map.gmap3({
				clear: {
					tag: "search_circle",
					all: true
				},
			});
        	
			cspm_recenter_map(plugin_map, map_id);						
			cspm_zoom_in_and_out(plugin_map); // Fix issue whith clusters when current map center equals initial map center
			
			$('form#search_form_'+map_id+' input#cspm_address_'+map_id).val('').trigger('focus'); //@edited 5.6.3
				
			/**
			 * Rewrite Carousel */
				
			if(progress_map_vars.map_script_args[map_script_id]['sync_carousel_to_viewport'] == 'no'){ //@since 5.0
				setTimeout(function(){
					cspm_rewrite_carousel(map_id, show_carousel, posts_to_retrieve[map_id]); //@since 5.8
				}, 500);
			}
						
			/**
			 * Hide the Countries list & Remove hover style on Countries list just in case a user ...
			 * ... already choosed a country then decided they want to search the map
			 * @edited 5.5 */
			
			if($('div.countries_container_'+map_id).length){
				$('li.cspm_country_name[data-map-id='+map_id+']').removeClass('selected');
				$('div.countries_container_'+map_id).hide();
			}
			
			/**
			 * Reset nearby points of interest */
			
			$('a.cspm_reset_proximities[data-map-id='+map_id+']').trigger('click'); //@edited 5.5
			
			$(this).removeClass('fadeIn').hide('fast', function(){
				if(typeof NProgress !== 'undefined')
					NProgress.done();	
			});
					
		});
		
		// @Search form request
		
		
		// Toogle the carousel
				
        $(document).on('click', 'div.toggle-button', function(){ //@edited 5.8
			
			var map_id = $(this).attr('data-map-id');
			
			$('div#cspm_carousel_container[data-map-id='+map_id+']').slideToggle('fast'); //@edited 5.8
			
		});								
		
		// Frontend Form @since 2.6.3
		
        $(document).on('click', 'button#cspm_search_btn', function(){ //@edited 5.6.3
			
			var map_id = $(this).attr('data-map-id');
			var address = $('input[name=cspm_search_address]').val();

			var plugin_map = $('div#cspm_frontend_form_'+map_id);
			
			plugin_map.gmap3({
				clear: {
					name:"marker",
					all: true
				},
			});

			var geocoder = new google.maps.Geocoder();
			
			// Convert our address to Lat & Lng
			geocoder.geocode({ 'address': address }, function (results, status) {
				
				// If the address was found
				if (status == google.maps.GeocoderStatus.OK) {

					var latitude = results[0].geometry.location.lat();
					var longitude = results[0].geometry.location.lng();
					
					setTimeout(function(){
						// Center the map in our address position
						cspm_center_map_at_point(plugin_map, map_id, latitude, longitude, 'zoom');
						
						plugin_map.gmap3({							
							marker:{
							  latLng:results[0].geometry.location,
							  options:{
							  	draggable: true,
							  }
							}
						});
					}, 500);
					
				}else{
					var api_status_codes = progress_map_vars.geocoding_api_status_codes; //@since 3.9
					cspm_api_status_info(map_id, api_status_codes, status, 'Geocoding API'); //@since 3.9
				}
				
			});
			
		});
		
        $(document).on('keypress', 'input#cspm_search_address', function(e){ //@edited 5.6.3
			if (e.keyCode == 13) {
				e.preventDefault();
			}
		});
				  
        $(document).on('click', 'button#cspm_get_pinpoint', function(){ //@edited 5.6.3
			
			var map_id = $(this).attr('data-map-id');
							
			var plugin_map = $('div#cspm_frontend_form_'+map_id);
			
			plugin_map.gmap3({
			  get: {
				name:"marker",
				callback: function(marker){
					if(marker){
						$("input#cspm_latitude").val(marker.getPosition().lat());
						$("input#cspm_longitude").val(marker.getPosition().lng());
					}
				}
			  }
			});
			
		});
				
		// @Frontend Form		
					
		/**
		 * Geolocalization
		 *
		 * @since 2.8
		 * @updated 5.6.5 */
		
        $(document).on('click', 'div.cspm_geotarget_btn', function(){ //@edited 5.6.3
			
			var map_id = $(this).attr('data-map-id');
				        
			/**
			 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
			 * @since 3.0 */
			var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';

			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
			
			cspm_geolocate(
				plugin_map, 
				map_id, 
				progress_map_vars.map_script_args[map_script_id]['show_user'], 
				progress_map_vars.map_script_args[map_script_id]['user_marker_icon'], 
				progress_map_vars.map_script_args[map_script_id]['user_marker_size'], 
				progress_map_vars.map_script_args[map_script_id]['user_circle'], 
				progress_map_vars.map_script_args[map_script_id]['user_map_zoom'], 
				true
			); //@edited 5.6.5
			
		});
		
		
		/**
		 * Zoom to country
		 *
		 * @since 3.0 
		 * @updated 5.6 | 5.6.3
		 */
		
		var countries_list = {}; //@edited 5.6.3
		
        $(document).on('click', 'div.countries_btn', function(){ //@edited 5.6.3

			var map_id = $(this).attr('data-map-id');
								
			$('div.cspm_top_element:not(.countries_container_'+map_id+')').hide();
			
			$('div.countries_container_'+map_id).slideToggle(0, 'easeOutCirc', function(){
				
				var $countries_container = $(this);
				
				if($(this).is(':visible')){
					
					var search_input = $(this).attr('data-search-input');
					var show_flag = $(this).attr('data-show-flag');
		
					if(typeof countries_list[map_id] === 'undefined'){

						countries_list[map_id] = tail.select('.cspm_countries_list[data-map-id='+map_id+']', {
							classNames: 'cspm_custom_tailselect',
							multiple: false,
							multiShowCount: false,
							multiSelectAll: false,
							startOpen: true,
							stayOpen: true,
							openAbove: false,
							height: 215,
							search: (search_input == 'yes') ? ((show_flag == 'only') ? false : true) : false,
							searchMarked: true,
							width: (show_flag == 'only') ? 55 : null, 
							deselect: true,
							hideSelected: true,
							cbLoopItem: function(item, group, search){
								// Create LI element
								var li = document.createElement('li');
								// Add option attributes to the LI element
								if(item.option.hasAttributes()){
								   var attrs = item.option.attributes;
								   for(var i = attrs.length - 1; i >= 0; i--) {
									   li.setAttribute(attrs[i].name, attrs[i].value);
								   }
								}
								// Get the element data
								var item_key = item.key;
								var country_name = li.getAttribute('data-country-name');
								var country_code = li.getAttribute('data-country-code');
								// Assign element class names
								li.className = 'dropdown-option cspm_dropdown_no_icon' + (item.selected ? ' selected' : '') + (item.disabled ? ' disabled' : '');
								if(search && search.length > 0 && this.con.searchMarked){
									country_name = country_name.replace(new RegExp('(' + search + ')', 'i'), '<mark>$1</mark>');
								}
								li.innerHTML = '<div class="item-inner">';							
									if(jQuery.inArray(show_flag, ['true', 'only']) !== -1){
										 li.innerHTML += '<div class="cspm_flg_icon cspm_'+country_code+'"></div>';
									}
									if(show_flag != 'only'){
										li.innerHTML += country_name;
									}
								li.innerHTML += '</div>';													
								return li;
							},
							cbComplete: function(){
								$countries_container.find('input.search-input[type=text]').attr('placeholder', progress_map_vars.type_to_search);								
								cspm_apply_custom_scrollabr('.cspm_custom_tailselect.tail-select .select-dropdown .dropdown-inner');
							}
						}).on('change', function(item, state){
							
							if(state != 'select')
								return;
	
							var country_name = $(item.option).attr('data-country-name');
							var country_code = $(item.option).attr('data-country-code');
							
							var custom_label = '';
							
							if(jQuery.inArray(show_flag, ['true', 'only']) !== -1)
								custom_label = '<div class="cspm_flg_icon cspm_'+country_code+'"></div>';
							
							if(show_flag != 'only')
								custom_label += country_name;
								
							countries_list[map_id].updateLabel(custom_label);
				 
							$('div.countries_container_'+map_id).hide();
							
							if(typeof NProgress !== 'undefined'){
								NProgress.configure({
								  parent: 'div#codespacing_progress_map_div_'+map_id,
								  showSpinner: true
								});				
								NProgress.start();
							}								
						
							/**
							 * [@map_script_id] | Used only to get map options from the PHP option wp_localize_script()
							 * @since 3.0 */
							var map_script_id = (typeof progress_map_vars.map_script_args[map_id] !== 'undefined') ? map_id : 'initial';
							
							var zoom_or_autofit = progress_map_vars.map_script_args[map_script_id]['country_zoom_or_autofit'];
							
							var autofit = (zoom_or_autofit == 'autofit') ? true : false;
							
							var map_zoom = progress_map_vars.map_script_args[map_script_id]['country_zoom_level'];
						
							var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
											
							if(typeof NProgress !== 'undefined')
								NProgress.set(0.5);
				
							cspm_zoom_to_address(map_id, plugin_map, country_name, autofit, map_zoom);
				
							if(typeof NProgress !== 'undefined')
								NProgress.done();
							
						});											
					
					}
					
				}
				
			});			
					
		});
		 
		
		/**
		 * Recenter the map
		 *
		 * @since 3.0 
		 */
		
        $(document).on('click', 'div.cspm_recenter_map_btn', function(){ //@edited 5.6.3

			var map_id = $(this).attr('data-map-id');
		
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
			
			if(typeof NProgress !== 'undefined'){
				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				NProgress.start();
			}								
        	
			cspm_recenter_map(plugin_map, map_id);						
			cspm_zoom_in_and_out(plugin_map); // Fix issue whith clusters when current map center equals initial map center
			
			/**
			 * Hide the Countries list & Remove hover style on Countries list just in case a user ...
			 * ... already choosed a country then decided they want to recenter the map
			 * @edited 5.5 */
			
			if($('div.countries_container_'+map_id).length){
				$('li.cspm_country_name[data-map-id='+map_id+']').removeClass('selected');
				$('div.countries_container_'+map_id).hide();
			}
			
			if(typeof NProgress !== 'undefined')
				NProgress.done();	

		});
		
		
		/**
		 * Init Directions Display */
		 
		var DirectionsDisplay = {};
		
		/**
		 * Display nearby points of interest */
		 
		var cspm_find_nearby_places = function(listObj, listItemObj, proximity_id, callback){

			var map_id = listItemObj.attr('data-map-id');

			var plugin_map = jQuery('div#codespacing_progress_map_div_'+map_id);
			var mapObject = plugin_map.gmap3('get');
			
			/**
			 * Get all data needed to get proximities */
			
			var $elementObj = $('div.cspm_proximities_container_'+map_id).find('li.cspm_proximity_name[data-map-id='+map_id+'][data-proximity-id='+proximity_id+']');
			
			//var proximity_id = $elementObj.attr('data-proximity-id');
			var proximity_name = $elementObj.attr('data-proximity-name');				
			var distance_unit = $elementObj.attr('data-distance-unit');	
			var radius = $elementObj.attr('data-radius');
			var initial_radius = $elementObj.attr('data-initial-radius');
			var travel_mode = $elementObj.attr('data-travel-mode');	
			var draw_circle_attr = $elementObj.attr('data-draw-circle');
				var draw_circle = (draw_circle_attr == 'true') ? true : false;
			var edit_circle_attr = $elementObj.attr('data-edit-circle');
				var edit_circle = (edit_circle_attr == 'true') ? true : false;
			var marker_type = $elementObj.attr('data-marker-type');
			var target_marker_latlng = $elementObj.attr('data-marker-latlng');			        		

			/**
			 * Min & Max Radius in meters */
			
			var min_radius = $elementObj.attr('data-min-radius');
			var max_radius = $elementObj.attr('data-max-radius');	
			
				/**
				 * [@radius_in_distance_unit] Radius converted to Km or Miles
				 * [@min_radius_in_distance_unit] Min Radius converted to Km or Miles
				 * [@max_radius_in_distance_unit] Max Radius converted to Km or Miles */
													
				if(distance_unit == "METRIC"){
					var radius_in_distance_unit = (radius / 1000); 					
					var min_radius_in_distance_unit = (min_radius / 1000);
					var max_radius_in_distance_unit = (max_radius / 1000);
				}else{
					var radius_in_distance_unit = (radius / 1609.34);
						radius_in_distance_unit = radius_in_distance_unit / 1000;					
					var min_radius_in_distance_unit = (min_radius / 1609.34);
						min_radius_in_distance_unit = min_radius_in_distance_unit / 1000;
					var max_radius_in_distance_unit = (max_radius / 1609.34);
						max_radius_in_distance_unit = max_radius_in_distance_unit / 1000;
				}
				
			var min_radius_attempt = $elementObj.attr('data-min-radius-attempt');
			var max_radius_attempt = $elementObj.attr('data-max-radius-attempt');
			
			var hasResults =  false;
			
			/**
			 * No marker coordinates has been found */
			 
			if(target_marker_latlng == '' || typeof target_marker_latlng === 'undefined'){
				
				var message = progress_map_vars.no_marker_selected_msg;
				cspm_popup_msg(map_id, 'warning', '', message);				
				
			/**
			 * Proceed when marker coordinates are available */
			 	
			}else{

				if(typeof NProgress !== 'undefined'){
	
					NProgress.configure({
					  parent: 'div#codespacing_progress_map_div_'+map_id,
					  showSpinner: true
					});				
					
					NProgress.start();
					
				}								
				
				/**
				 * Convert marker latLng string to array */
				 
				var location_latlng = target_marker_latlng.replace('(', '').replace(')', '').split(',');
				
				if(typeof location_latlng[0] === 'undefined' && typeof location_latlng[1] === 'undefined'){
					
					if(typeof NProgress !== 'undefined')
							NProgress.done();
					
					return;
					
				}
			
				/**
				 * Clear already displayed proximity markers */
				 
				plugin_map.gmap3({
					clear: {
						tag: ['cspm_nearby_point_'+map_id, 'cspm_nearby_circle_'+map_id]
					}
				});	
				
				/**
				 * Prepare the map to display new markers and routes */
				
				var DirectionsRendererOptions = {
					suppressMarkers: true,
					suppressInfoWindows: true,
					draggable: false,
					preserveViewport: false,
					suppressBicyclingLayer: true,
				};
				
				if(DirectionsDisplay[map_id] != null) {
					DirectionsDisplay[map_id].setMap(null);
					DirectionsDisplay[map_id] = null;
				}
			
				DirectionsDisplay[map_id] = new google.maps.DirectionsRenderer(DirectionsRendererOptions);
				DirectionsDisplay[map_id].setMap(mapObject);
				
				/**
				 * New request for places */
				 	
				var request = {
				  location: new google.maps.LatLng(location_latlng[0], location_latlng[1]),
				  radius: radius,	  
				  types: [proximity_id], /* https://developers.google.com/places/documentation/supported_types?hl=fr */
				};
	
				var service = new google.maps.places.PlacesService(mapObject);
				
				var cspm_existing_places = {};
					cspm_existing_places[map_id] = new Array();
							
				if(typeof NProgress !== 'undefined')
					NProgress.set(0.5);
				
				var markers_bounds = new google.maps.LatLngBounds();

				service.nearbySearch(request, function(results, status, pagination){
			
					if(status === google.maps.places.PlacesServiceStatus.OK){
				
						/**
						 * Hide the proximities list
						 * @edited 5.5 */
						
						if($('div.cspm_proximities_container_'+map_id).is(':visible'))
							$('div.cspm_proximities_container_'+map_id).slideToggle();

						if(results.length > 0){

							/**
							 * [@marker_mouseover_count] | Count the mouseover events so we can decide when to display the ...
							 * ... pop-up message that informs the user that they can display place details by clicking on the marker.
							 * @since 3.5 */
							
							var marker_mouseover_count = 0;
											
							/**
							 * List all places */
							
							for (var i = 0; i < results.length; i++){
									
								var this_place_id = results[i].place_id;
								var this_place_location = results[i].geometry.location;
								var this_place_name = results[i].name;
										
								if(jQuery.inArray( this_place_id, cspm_existing_places[map_id] ) == -1 && jQuery.inArray( proximity_id, results[i].types ) != -1){
									
									cspm_existing_places[map_id].push(this_place_id);
									
									/**
									 * Add this nearby point of interest as new marker on the map */
									
									var marker_icon = (marker_type == 'default') ? '' : progress_map_vars.plugin_url+'img/nearby/pointers/'+proximity_id+'-pin.png';
									
									plugin_map.gmap3({ 
										marker:{
											latLng: this_place_location,
											title: this_place_name,
											tag: 'cspm_nearby_point_'+map_id,
											options:{
												icon: marker_icon, //@edited 5.4
												animation: google.maps.Animation.DROP,
												opacity: 1,
												placeID: this_place_id,
												placeName: this_place_name, 
											},
											callback: function(marker){	
												markers_bounds.extend(marker.getPosition());
											},
											events:{
												mouseover: function(marker){
															
													/**
													 * Inform the user that they can display the route ...
													 * ... and the place details.
													 * @since 3.5 */
															
													if(marker_mouseover_count == 0 || marker_mouseover_count == 10){
														
														var message = progress_map_vars.show_nearby_details_msg;
														cspm_popup_msg(map_id, 'info', '',  message);
														
														if(marker_mouseover_count == 10)
															marker_mouseover_count = 1;
															
													}
													
													marker_mouseover_count++;
														
												},
												mouseout: function(marker){
																												
													/**
													 * Close the info message
													 * @since 3.5 */
													
													cspm_close_popup_msg(map_id, 'info');

												},
												click: function(marker, event, context){
													
													var origin = new google.maps.LatLng(location_latlng[0], location_latlng[1]);
													var destination = marker.position;
													var travel_mode = $elementObj.attr('data-travel-mode');	
													
													$('a.cspm_switch_np_travel_mode[data-map-id='+map_id+']').attr('data-origin', origin).attr('data-destination', destination); //@edited 5.5
													
													/**
													 * Get route */
													 
													plugin_map.gmap3({ 
														getroute:{
															options:{
																origin: origin,
																destination: destination,
																travelMode: google.maps.DirectionsTravelMode[travel_mode.toUpperCase()],
																unitSystem: google.maps.UnitSystem[distance_unit.toUpperCase()],
															},
															callback: function(response, status){

																if(typeof response !== 'undefined' && status === 'OK'){
																
																	/**
																	 *  Render the route on the map */
																	 
																	DirectionsDisplay[map_id].setDirections(response);						
																	 
																	var route = response.routes[0];
																			
																	var distance = route.legs[0].distance.text;
																	var duration = route.legs[0].duration.text;		
		
																	var place_name = marker.placeName;
																	
																	/**
																	 * Get place details */
																	 
																	service.getDetails({
																		
																		placeId: marker.placeID,
																		fields: [
																			//'name', 
																			//'photo', 
																			//'rating', 
																			//'user_ratings_total', 
																			'place_id', 
																			//'formatted_address',
																			//'plus_code',
																			//'geometry',
																			//'website',
																			//'international_phone_number',
																			'url',
																			//'type',
																			//'review'
																		] //@since 5.2
																																		
																	}, function(place, status){

																		if(status === google.maps.places.PlacesServiceStatus.OK){
																			
																			var origin_latLng = origin.toString().replace('(', '').replace(')', '');
																			var destination_latLng = destination.toString().replace('(', '').replace(')', '');
																			
																			var infowindow_content = 
																				'<div style="padding:10px 5px; margin-bottom:5px; border-bottom:1px solid #eee; text-align:center;">'+
																					'<strong><a href="'+place.url+'" target="_blank">'+place_name+'</a></strong>'+
																				'</div>'+
																				'<div style="padding:5px; text-align:center;">'+
																					'<span style="text-align:center; padding:5px; margin-right:15px;"><img src="'+progress_map_vars.plugin_url+'img/svg/route_distance.svg" style="width:15px; margin-right:5px" /><span class="cspm_infowindow_distance">'+distance+'</span></span>'+
																					'<span style="text-align:center; padding:5px;"><img src="'+progress_map_vars.plugin_url+'img/svg/clock.svg" style="width:15px; margin-right:5px" /><span class="cspm_infowindow_duration">'+duration+'</span></span>'+
																					'<span style="text-align:center; padding:5px;"><img src="'+progress_map_vars.plugin_url+'img/svg/directions.svg" style="width:15px; margin-right:5px" /><span class="cspm_infowindow_directions">'+
																						'<a href="https://www.google.com/maps/dir/?api=1&origin='+origin_latLng+'&destination='+destination_latLng+'&destination_place_id='+place.place_id+'&travelmode='+travel_mode.toLowerCase()+'" target="_blank">Directions</a>'+
																					'</span></span>'+
																				'</div>';
																			
																			infowindow = plugin_map.gmap3({get: {name: "infowindow", id: "nearby_place"}});
																			
																			if(infowindow){
																			  infowindow.open(mapObject, marker);
																			  infowindow.setContent(infowindow_content);
																			}else{
																				plugin_map.gmap3({
																					infowindow: {
																						anchor: marker, 
																						id: "nearby_place",
																						options:{
																							content: infowindow_content
																						}
																					}
																				});
																			}
																			
																		}else{
																			var api_status_codes = progress_map_vars.places_api_status_codes; //@since 3.9
																			cspm_api_status_info(map_id, api_status_codes, status, 'Places API'); //@since 3.9
																		}
																		
																	});
																	
																}else{
																	var api_status_codes = progress_map_vars.directions_api_status_codes; //@since 3.9
																	cspm_api_status_info(map_id, api_status_codes, status, 'Directions API'); //@since 3.9
																}

																if(typeof NProgress !== 'undefined')
																	NProgress.done();
						
																return;
													
															}
															
														}
													});
	
												}
											}
											
										}
									});
				
								}
								
							}
							
							if(pagination.hasNextPage){

								pagination.nextPage();
								
							}else{
								
								/**
								 * Draw a circle around the nearby points of interest */

								if(!draw_circle){
									
									plugin_map.gmap3('get').fitBounds(markers_bounds);
									 											
									if(typeof NProgress !== 'undefined')
										NProgress.done();
										
								}else{
									
									setTimeout(function(){
											
										/**
										 * [@circle_mouseover_count] | Count the mouseover events so we can decide when to display the ...
										 * ... pop-up message that informs the user that they can reize the circle.
										 * @since 3.5 */
										
										var circle_mouseover_count = 0;
						
										var main_hex_color = (typeof progress_map_vars.map_script_args['initial']['main_hex_color'] !== 'undefined') 
											? progress_map_vars.map_script_args['initial']['main_hex_color'] : "#189AC9"; //@since 4.7
										 
										plugin_map.gmap3({
											circle:{
												tag: 'cspm_nearby_circle_'+map_id,
												options:{
													center: [location_latlng[0], location_latlng[1]],
													radius: parseInt(radius),
													fillColor: main_hex_color,
													fillOpacity: 0.05,
													strokeColor: main_hex_color,
													strokeOpacity: 0.5,
													strokeWeight: 5,
													editable: edit_circle,
												},										
												callback: function(circle){
	
													plugin_map.gmap3('get').fitBounds(markers_bounds);
													
													if(typeof NProgress !== 'undefined')
														NProgress.done();
												
												},										
												events:{
													mouseover: function(circle){
														
														/**
														 * Inform the user that they can resize the circle ...
														 * ... to show more or less results.
														 * @since 3.5 */
															
														if(edit_circle && (circle_mouseover_count == 0 || circle_mouseover_count == 6)){
															
															var message = progress_map_vars.resize_circle_msg;
															cspm_popup_msg(map_id, 'info', '',  message);
															
															if(circle_mouseover_count == 6)
																circle_mouseover_count = 1;
																
														}
														
														circle_mouseover_count++;
														
													},
													mouseout: function(circle){
																													
														/**
														 * Close the info message
														 * @since 3.5 */
														
														if(edit_circle)
															cspm_close_popup_msg(map_id, 'info');

													},
													radius_changed: function(circle){
														
														if(edit_circle){
															
															/**
															 * Get circle radius */
															 
															var circle_radius_in_meters = circle.getRadius();
																	
															/**
															 * Compare circle radius to min and max distances and redraw circle if needs to */
															
															var readable_distance_unit = (distance_unit == 'METRIC') ? 'Km' : 'Miles';
															
															/**
															 * Circle should not be more than max radius */
															 
															if(circle_radius_in_meters > max_radius){
																
																circle.setRadius(parseInt(max_radius));
																
																$elementObj.attr('data-radius', max_radius).attr('data-max-radius-attempt', parseInt(max_radius_attempt)+1);
																
																var reached_edge = 'max';
																				
																var message = progress_map_vars.max_search_radius_msg+' ('+Math.floor(max_radius_in_distance_unit)+readable_distance_unit+')';
																cspm_popup_msg(map_id, 'warning', '', message);							
																
															/**
															 * Circle should not be less than max radius */
															 
															}else if(circle_radius_in_meters < min_radius){
																
																circle.setRadius(parseInt(min_radius));
																
																$elementObj.attr('data-radius', min_radius).attr('data-min-radius-attempt', parseInt(min_radius_attempt)+1);
															
																var reached_edge = 'min';
																				
																var message = progress_map_vars.min_search_radius_msg+' ('+Math.floor(min_radius_in_distance_unit)+readable_distance_unit+')';
																cspm_popup_msg(map_id, 'warning', '', message);							
															
															/**
															 * Circle is between min radius & max radius */
															 
															}else{
																
																var reached_edge = '';
																	
																$elementObj.attr('data-radius', circle_radius_in_meters).attr('data-min-radius-attempt', parseInt(0)).attr('data-max-radius-attempt', parseInt(0));
																
															}
															
															/**
															 * Research for nearby points of interest */
								 
															setTimeout(function(){
																
																var min_radius_attempt = $elementObj.attr('data-min-radius-attempt');
																var max_radius_attempt = $elementObj.attr('data-max-radius-attempt');
																
																if((min_radius_attempt == 0 && max_radius_attempt == 0) || (reached_edge == 'min' && min_radius_attempt <= 1) || (reached_edge == 'max' && max_radius_attempt <= 1)){
																	listObj.options.all('unselect');
																	setTimeout(function(){
																		$elementObj.trigger('click');
																	}, 10);
																}
															
															}, 500);
															
														}
																											
													}
												}
											}
											
										});
									
									}, 500);
									
								}
									
							}
					
						}
								
					}else{
											
						var api_status_codes = progress_map_vars.places_api_status_codes; //@since 3.9
						cspm_api_status_info(map_id, api_status_codes, status, 'Places API'); //@since 3.9
															
						/**
						 * Back to the initial situation */
						 
					 	$elementObj.attr('data-radius', initial_radius).attr('data-min-radius-attempt', 0).attr('data-max-radius-attempt', 0);
					 	listObj.options.all('unselect');
						
						if(typeof NProgress !== 'undefined')						
							NProgress.done();
									
						return;
						
					}

				});
				
				/**
				 * Display the reset button */
				
				setTimeout(function(){
					$('a.cspm_reset_proximities[data-map-id='+map_id+']').show(); //@edited 5.5
				},1000);	
				
				hasResults = true;			
				
			}
			
			callback(hasResults);

		}
		
		/**
		 * Nearby points of interest
		 *
		 * @since 3.2
		 * @updated 5.2 [Added support for "fields" attribute in the function "getDetails()"]
		 * @updated 5.6 | 5.6.3
		 */
		
		var proximities_list = {}; //@edited 5.6.3
		
        $(document).on('click', 'div.cspm_proximities_btn', function(){ //@edited 5.6.3

			var map_id = $(this).attr('data-map-id');
		
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
		
			$('div.cspm_top_element:not(.cspm_proximities_container_'+map_id+')').hide();
			
			$('div.cspm_proximities_container_'+map_id).slideToggle(0, 'easeOutCirc', function(){
				
				if($(this).is(':visible')){
					
					var show_proximity_icon = $(this).attr('data-show-proximity-icon');
					
					if(typeof proximities_list[map_id] === 'undefined'){
						
						proximities_list[map_id] = tail.select('.cspm_proximities_list[data-map-id='+map_id+']', {
							classNames: 'cspm_custom_tailselect',
							multiple: false,
							multiShowCount: false,
							multiSelectAll: false,
							startOpen: false,
							stayOpen: false,
							openAbove: false,
							height: 195,						
							width: 195, 
							search: false,
							deselect: true,
							hideSelected: true,
							cbLoopItem: function(item, group, search){
								// Create LI element
								var li = document.createElement('li');
								// Add option attributes to the LI element
								if(item.option.hasAttributes()){
								   var attrs = item.option.attributes;
								   for(var i = attrs.length - 1; i >= 0; i--) {
									   li.setAttribute(attrs[i].name, attrs[i].value);
								   }
								}
								// Get the element data
								var item_key = item.key;
								var proximity_id = li.getAttribute('data-proximity-id');
								var proximity_name = li.getAttribute('data-proximity-name');
								// Assign element class names
								li.className = 'dropdown-option cspm_dropdown_no_icon' + (item.selected ? ' selected' : '') + (item.disabled ? ' disabled' : '');
								li.className += ' cspm_proximity_name'; // Required!
								li.innerHTML = '<div class="item-inner">';							
									if(jQuery.inArray(show_proximity_icon, ['true', 'only']) !== -1){
										 li.innerHTML += '<div class="cspm_np_icon cspm_'+proximity_id+'"></div>';
									}
									if(show_proximity_icon != 'only'){
										li.innerHTML += proximity_name;
									}
								li.innerHTML += '</div>';	
								return li;
							},
							cbComplete: function(){
								cspm_apply_custom_scrollabr('.cspm_custom_tailselect.tail-select .select-dropdown .dropdown-inner');                                
                            }
						}).on('change', function(item, state){						
	
							if(state != 'select')
								return;
								
							var proximity_id = $(item.option).attr('data-proximity-id');
							var proximity_name = $(item.option).attr('data-proximity-name');

							var custom_label = '';
							
							if(jQuery.inArray(show_proximity_icon, ['true', 'only']) !== -1)
								custom_label = '<div class="cspm_np_icon cspm_'+proximity_id+'"></div>';
							
							if(show_proximity_icon != 'only')
								custom_label += proximity_name;
							
							cspm_find_nearby_places(proximities_list[map_id], $(item.option), proximity_id, function(hasResults){
								if(!hasResults){		
									proximities_list[map_id].options.all('unselect');
								}else{
									proximities_list[map_id].updateLabel(custom_label);
								}
							});
						
						});
								
					}
					
				}
				
			});			
			
		});		
		
		
		/**
		 * Switch the travel mode
		 * @since 3.2
		 * @updated 3.8 */
		
        $(document).on('click', 'a.cspm_switch_np_travel_mode', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@since 5.5
			
			var map_id = $(this).attr('data-map-id');
			var travel_mode = $(this).attr('data-travel-mode');
			var origin = $(this).attr('data-origin');
			var destination = $(this).attr('data-destination');
			var distance_unit = $(this).attr('data-distance-unit');
			
			if($(this).hasClass('active'))
				return;
		
			$.each($('a.cspm_switch_np_travel_mode[data-map-id='+map_id+'] svg.cspm_svg'), function(index, element){
				$(this).removeClass('cspm_svg_white').addClass('cspm_svg_colored');
			}); //@edited 5.5
			
			$('a.cspm_switch_np_travel_mode[data-map-id='+map_id+']').removeClass('active cspm_bg_hex_hover'); //@edited 5.5
			$(this).addClass('active cspm_bg_hex_hover');			
			
			var this_icon = $('a.cspm_switch_np_travel_mode[data-map-id='+map_id+'][data-travel-mode='+travel_mode+'] svg.cspm_svg'); //@edited 5.5
			this_icon.removeClass('cspm_svg_colored').addClass('cspm_svg_white');
			
			var plugin_map = jQuery('div#codespacing_progress_map_div_'+map_id);
			var mapObject = plugin_map.gmap3('get');
				
			$('li.cspm_proximity_name[data-map-id='+map_id+']').attr('data-travel-mode', travel_mode);
						
			if(origin == '' && destination == '')
				return;
				
			/**
			 * Convert marker latLng string to array */
			 
			var origin_latlng = origin.replace('(', '').replace(')', '').split(',');
			
			if(typeof origin_latlng[0] === 'undefined' && typeof origin_latlng[1] === 'undefined'){
				
				if(typeof NProgress !== 'undefined')
						NProgress.done();
				
				return;
				
			}
				
			/**
			 * Convert destination latLng string to array */
			 
			var destination_latlng = destination.replace('(', '').replace(')', '').split(',');
			
			if(typeof destination_latlng[0] === 'undefined' && typeof destination_latlng[1] === 'undefined'){
				
				if(typeof NProgress !== 'undefined')
						NProgress.done();
				
				return;
				
			}
						
			if(typeof NProgress !== 'undefined'){

				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				
				NProgress.start();
				
			}								
		
			/**
			 * Get route */
														
			plugin_map.gmap3({ 
				getroute:{
					options:{
						origin: new google.maps.LatLng(origin_latlng[0], origin_latlng[1]),
						destination: new google.maps.LatLng(destination_latlng[0], destination_latlng[1]),
						travelMode: google.maps.DirectionsTravelMode[travel_mode.toUpperCase()],
						unitSystem: google.maps.UnitSystem[distance_unit.toUpperCase()],
					},
					callback: function(response, status){
							
						if(typeof response !== 'undefined' && status === 'OK'){
						
							/**
							 *  Render the route on the map */
							 
							DirectionsDisplay[map_id].setDirections(response);						
							 
							var route = response.routes[0];
									
							var distance = route.legs[0].distance.text;
							var duration = route.legs[0].duration.text;		

							$('span.cspm_infowindow_distance').text(distance);
							$('span.cspm_infowindow_duration').text(duration);
							
							var directions_url = $('span.cspm_infowindow_directions a').attr('href');
							
							if(typeof directions_url !== 'undefined'){
								var directions_url_array = directions_url.split('&');								
								var new_directions_url = directions_url_array[0]+'&'+directions_url_array[1]+'&'+directions_url_array[2]+'&'+directions_url_array[3]+'&travelmode='+travel_mode.toLowerCase();
								$('span.cspm_infowindow_directions a').attr('href', new_directions_url);
							}
						
						}else{
							var api_status_codes = progress_map_vars.directions_api_status_codes; //@since 3.9
							cspm_api_status_info(map_id, api_status_codes, status, 'Directions API'); //@since 3.9
						}

						if(typeof NProgress !== 'undefined')
							NProgress.done();

						return;
		
					}
					
				}
				
			});
	
		});
		
		/**
		 * Reset proximities list */
		
        $(document).on('click', 'a.cspm_reset_proximities', function(e){ //@edited 5.6.3
			
			e.preventDefault(); //@since 5.5
			
			var map_id = $(this).attr('data-map-id');

			if(typeof NProgress !== 'undefined'){

				NProgress.configure({
				  parent: 'div#codespacing_progress_map_div_'+map_id,
				  showSpinner: true
				});				
				
				NProgress.start();
				
			}		
			
			var plugin_map = jQuery('div#codespacing_progress_map_div_'+map_id);
			var mapObject = plugin_map.gmap3('get');
			
			$(this).hide();
		
			/**
			 * Remove selected class name and previous marker latLng from the list items */
			 
			$('li.cspm_proximity_name[data-map-id='+map_id+']').attr('data-marker-latlng', '');
			
			if(typeof proximities_list[map_id] !== 'undefined'){	                
				proximities_list[map_id].options.all('unselect');
				proximities_list[map_id].updateLabel(proximities_list[map_id].con.placeholder);
			}
			
			/**
			 * Clear already displayed proximity markers */
			 
			plugin_map.gmap3({
				clear: {
					tag: ['cspm_nearby_point_'+map_id, 'cspm_nearby_circle_'+map_id]
				}
			});	
				
			/**
			 * Clear already displayed routes */
			
			if(typeof DirectionsDisplay[map_id] !== 'undefined')
				DirectionsDisplay[map_id].setMap(null);
				
			if(typeof NProgress !== 'undefined')						
				NProgress.done();
																		
		});
					
		/**
		 * Heatmap Layer
		 *
		 * @since 3.3
         * @edited 5.8 */
		
        $(document).on('click', 'div.cspm_heatmap_btn', function(){ //@edited 5.6.3
			
			var map_id = $(this).attr('data-map-id');			
			var toggle_marker_option = $(this).attr('data-toggle-markers');
				
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
			var mapObject = plugin_map.gmap3('get');
			
			/**
			 * Hide heatmap layer */
			 
			if(heatmap[map_id].getMap()){

				$(this).removeClass('cspm_active_btn cspm_bg_rgb_hover');
				$(this).find('svg.cspm_svg').removeClass('cspm_svg_white').addClass('cspm_svg_colored');
				
				heatmap[map_id].setMap(null);
				
				if(toggle_marker_option == 'yes'){
					cspm_show_all_markers(plugin_map, map_id);
				}
				
			/**
			 * Show heatmap layer */
			 
			}else{

				$(this).addClass('cspm_active_btn cspm_bg_rgb_hover');
				$(this).find('svg.cspm_svg').removeClass('cspm_svg_colored').addClass('cspm_svg_white');
				
				heatmap[map_id].setMap(mapObject);
				
				if(toggle_marker_option == 'yes'){				
					cspm_hide_all_markers(plugin_map, map_id);
				}
				
			}
            
            cspm_simple_clustering(plugin_map, map_id); //@since 5.8
            cspm_zoom_in_and_out(plugin_map); //@since 5.8
			
		});
		
		/**
		 * Open the single post inside a modal
		 * @since 3.6 */
				
        $(document).on('click', 'a.cspm_popup_single_post', function(e){ //@edited 5.6.3
			e.preventDefault(); 
			var iframe_url = $(this).attr('href');
			if(typeof iframe_url !== 'undefined' && iframe_url != ''){
				cspm_open_single_post_modal(iframe_url);		
			}	
		});		
		
		/**
		 * Open the "Nearby places" map inside a modal
		 * @since 4.6 */
					
        $(document).on('click', 'a.cspm_popup_nearby_palces_map', function(e){ //@edited 5.6.3
			e.preventDefault();			            
			var iframe_url = progress_map_vars.nearby_places_page_url;
			var post_id = $(this).attr('data-post-id');
			if(typeof iframe_url !== 'undefined' && iframe_url != '' && typeof post_id !== 'undefined' && post_id != ''){
				cspm_open_single_post_modal(iframe_url+'?post_id='+post_id);		
			}
		});	

		/**
		 * Replace all SVG images with inline SVG
		 * @since 3.8
		 * @edited 5.6.5 */
		
		cspm_convert_img_to_svg();		
		
		/**
		 * KML Layers List
		 *
		 * @since 5.6
         * @updated 5.6.3
		 */	
		
		var kml_layers_list = {}; //@edited 5.6.3
			
        $(document).on('click', 'div.kml_list_btn', function(){ //@edited 5.6.3

			var map_id = $(this).attr('data-map-id');
		
			var plugin_map = $('div#codespacing_progress_map_div_'+map_id);
							
			$('div.cspm_top_element:not(.kml_list_container_'+map_id+')').hide();
			
			$('div.kml_list_container_'+map_id).slideToggle(0, 'easeOutCirc', function(){
				
				var $kml_list_container = $(this);
				
				if($(this).is(':visible')){
					
					/**
					 * Display a list with all available KML Layers ...
					 * ... and show/Hide the selected KML Layer(s) */
					
					if(typeof kml_layers_list[map_id] === 'undefined'){
						 
						kml_layers_list[map_id] = tail.select('.cspm_kml_layer_list[data-map-id='+map_id+']', {
							classNames: 'cspm_custom_tailselect',
							multiple: true,
							multiShowCount: true,
							multiSelectAll: true,
							startOpen: true,
							stayOpen: true,
							openAbove: false,
							local: 'en',
							cbComplete: function(){
								$kml_list_container.find(".tail-all").html(progress_map_vars.all);
								$kml_list_container.find(".tail-none").html(progress_map_vars.none);							
								cspm_apply_custom_scrollabr('.cspm_custom_tailselect.tail-select .select-dropdown .dropdown-inner');
							}						
						}).on('change', function(item, state){
							kml_layers_list[map_id].disable(false);
							plugin_map.gmap3({
								get: {
									name: 'kmllayer',
									tag: item.key,
									callback: function(layer){
										if(typeof layer !== 'undefined' 
										&& typeof layer.setMap === 'function'){
											if(state == 'select'){
												layer.setMap(plugin_map.gmap3('get'));
												layer.setOptions({preserveViewport: false});
											}else if(state == 'unselect'){
												layer.setMap(null);
											}
										}
										kml_layers_list[map_id].enable(false);
									}
								}
							});	
						});
						
					}
					
				}
				
			});			
					
		});
        
	})(jQuery);
