

// js/v8/core.js

/**

 * @module WPGMZA

 * @summary This is the core Javascript module. Some code exists in ../core.js, the functionality there will slowly be handed over to this module.

 */

(function($) {

	var core = {

		maps: [],

		events: null,

		settings: null,

		

		loadingHTML: '<div class="wpgmza-preloader"><div class="wpgmza-loader">...</div></div>',

		

		/**

		 * Override this method to add a scroll offset when using animated scroll

		 * @return number

		 */

		getScrollAnimationOffset: function() {

			return (WPGMZA.settings.scroll_animation_offset || 0);

		},

		

		/**

		 * Animated scroll, accounts for animation settings and fixed header height

		 * @return void

		 */

		animateScroll: function(element, milliseconds) {

			

			var offset = WPGMZA.getScrollAnimationOffset();

			

			if(!milliseconds)

			{

				if(WPGMZA.settings.scroll_animation_milliseconds)

					milliseconds = WPGMZA.settings.scroll_animation_milliseconds;

				else

					milliseconds = 500;

			}

			

			$("html, body").animate({

				scrollTop: $(element).offset().top - offset

			}, milliseconds);

			

		},

		

		/**

		 * @function guid

		 * @summary Utility function returns a GUID

		 * @static

		 * @return {string} The GUID

		 */

		guid: function() { // Public Domain/MIT

		  var d = new Date().getTime();

			if (typeof performance !== 'undefined' && typeof performance.now === 'function'){

				d += performance.now(); //use high-precision timer if available

			}

			return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {

				var r = (d + Math.random() * 16) % 16 | 0;

				d = Math.floor(d / 16);

				return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);

			});

		},

		

		/**

		 * @function hexOpacityToRGBA

		 * @summary Takes a hex string and opacity value and converts it to Openlayers RGBA format

		 * @param {string} colour The hex color string

		 * @param {number} opacity The opacity from 0.0 - 1.0

		 * @static

		 * @return {array} RGBA where color components are 0 - 255 and opacity is 0.0 - 1.0

		 */

		hexOpacityToRGBA: function(colour, opacity)

		{

			hex = parseInt(colour.replace(/^#/, ""), 16);

			return [

				(hex & 0xFF0000) >> 16,

				(hex & 0xFF00) >> 8,

				hex & 0xFF,

				parseFloat(opacity)

			];

		},

		

		latLngRegexp: /^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/,

		

		/**

		 * @function isLatLngString

		 * @summary Utility function returns true is string is a latitude and longitude

		 * @param str {string} The string to attempt to parse as coordinates

		 * @static

		 * @return {array} the matched latitude and longitude or null if no match

		 */

		isLatLngString: function(str)

		{

			if(typeof str != "string")

				return null;

			

			// Remove outer brackets

			if(str.match(/^\(.+\)$/))

				str = str.replace(/^\(|\)$/, "");

			

			var m = str.match(WPGMZA.latLngRegexp);

			

			if(!m)

				return null;

			

			return {

				lat: parseFloat(m[1]),

				lng: parseFloat(m[3])

			};

		},

		

		/**

		 * @function stringToLatLng

		 * @summary Utility function returns a latLng literal given a valid latLng string

		 * @param str {string} The string to attempt to parse as coordinates

		 * @static

		 * @return {object} LatLng literal

		 */

		stringToLatLng: function(str)

		{

			var result = WPGMZA.isLatLngString(str);

			

			if(!result)

				throw new Error("Not a valid latLng");

			

			return result;

		},

		

		/**

		 * @function stringToLatLng

		 * @summary Utility function returns a latLng literal given a valid latLng string

		 * @param str {string} The string to attempt to parse as coordinates

		 * @static

		 * @return {object} LatLng literal

		 */

		isHexColorString: function(str)

		{

			if(typeof str != "string")

				return false;

			

			return (str.match(/#[0-9A-F]{6}/i) ? true : false);

		},

		

		/**

		 * @function getImageDimensions

		 * @summary Utility function to get the dimensions of an image, caches results for best performance

		 * @param src {string} Image source URL

		 * @param callback {function} Callback to recieve image dimensions

		 * @static

		 * @return {void}

		 */

		imageDimensionsCache: {},

		getImageDimensions: function(src, callback)

		{

			if(WPGMZA.imageDimensionsCache[src])

			{

				callback(WPGMZA.imageDimensionsCache[src]);

				return;

			}

			

			var img = document.createElement("img");

			img.onload = function(event) {

				var result = {

					width: image.width,

					height: image.height

				};

				WPGMZA.imageDimensionsCache[src] = result;

				callback(result);

			};

			img.src = src;

		},

		

		/**

		 * @function isDeveloperMode

		 * @summary Returns true if developer mode is set

		 * @static 

		 * @return {boolean} True if developer mode is on

		 */

		isDeveloperMode: function()

		{

			return this.developer_mode || (window.Cookies && window.Cookies.get("wpgmza-developer-mode"));

		},

		

		/**

		 * @function isProVersion

		 * @summary Returns true if the Pro add-on is active

		 * @static

		 * @return {boolean} True if the Pro add-on is active

		 */

		isProVersion: function()

		{

			return (this._isProVersion == "1");

		},

		

		/**

		 * @function openMediaDialog

		 * @summary Opens the WP media dialog and returns the result to a callback

		 * @param {function} callback Callback to recieve the attachment ID as the first parameter and URL as the second

		 * @static

		 * @return {void}

		 */

		openMediaDialog: function(callback) {

			// Media upload

			var file_frame;

			

			// If the media frame already exists, reopen it.

			if ( file_frame ) {

				// Set the post ID to what we want

				file_frame.uploader.uploader.param( 'post_id', set_to_post_id );

				// Open frame

				file_frame.open();

				return;

			}

			

			// Create the media frame.

			file_frame = wp.media.frames.file_frame = wp.media({

				title: 'Select a image to upload',

				button: {

					text: 'Use this image',

				},

				multiple: false	// Set to true to allow multiple files to be selected

			});

			

			// When an image is selected, run a callback.

			file_frame.on( 'select', function() {

				// We set multiple to false so only get one image from the uploader

				attachment = file_frame.state().get('selection').first().toJSON();

				

				callback(attachment.id, attachment.url);

			});

			

			// Finally, open the modal

			file_frame.open();

		},

		

		/**

		 * @function getCurrentPosition

		 * @summary This function will get the users position, it first attempts to get

		 * high accuracy position (mobile with GPS sensors etc.), if that fails

		 * (desktops will time out) then it tries again without high accuracy

		 * enabled

		 * @static

		 * @return {object} The users position as a LatLng literal

		 */

		getCurrentPosition: function(callback)

		{

			if(!navigator.geolocation)

			{

				console.warn("No geolocation available on this device");

				return;

			}

			

			var options = {

				enableHighAccuracy: true

			};

			

			navigator.geolocation.getCurrentPosition(function(position) {

				if(callback)

					callback(position);

				

				WPGMZA.events.trigger("userlocationfound");

			},

			function(error) {

				

				options.enableHighAccuracy = false;

				

				navigator.geolocation.getCurrentPosition(function(position) {

					if(callback)

						callback(position);

					

					WPGMZA.events.trigger("userlocationfound");

				},

				function(error) {

					console.warn(error.code, error.message);

				},

				options);

				

			},

			options);

		},

		

		/**

		 * @function runCatchableTask

		 * @summary Runs a catchable task and displays a friendly error if the function throws an error

		 * @param {function} callback The function to run

		 * @param {HTMLElement} friendlyErrorContainer The container element to hold the error

		 * @static

		 * @return {void}

		 */

		runCatchableTask: function(callback, friendlyErrorContainer) {

			

			if(WPGMZA.isDeveloperMode())

				callback();

			else

				try{

					callback();

				}catch(e) {

					var friendlyError = new WPGMZA.FriendlyError(e);

					$(friendlyErrorContainer).html("");

					$(friendlyErrorContainer).append(friendlyError.element);

					$(friendlyErrorContainer).show();

				}

		},

		

		/**

		 * @function assertInstanceOf

		 * @summary

		 * This function is for checking inheritence has been setup correctly.

		 * For objects that have engine and Pro specific classes, it will automatically

		 * add the engine and pro prefix to the supplied string and if such an object

		 * exists it will test against that name rather than the un-prefix argument

		 * supplied.

		 *

		 * For example, if we are running the Pro addon with Google maps as the engine,

		 * if you supply Marker as the instance name the function will check to see

		 * if instance is an instance of GoogleProMarker

		 * @param {object} instance The object to check

		 * @param {string} instanceName The class name as a string which this object should be an instance of

		 * @static

		 * @return {void}

		 */

		assertInstanceOf: function(instance, instanceName) {

			var engine, fullInstanceName, assert;

			var pro = WPGMZA.isProVersion() ? "Pro" : "";

			

			switch(WPGMZA.settings.engine)

			{

				case "open-layers":

					engine = "OL";

					break;

				

				default:

					engine = "Google";

					break;

			}

			

			if(WPGMZA[engine + pro + instanceName])

				fullInstanceName = engine + pro + instanceName;

			else if(WPGMZA[pro + instanceName])

				fullInstanceName = pro + instanceName;

			else if(WPGMZA[engine + instanceName])

				fullInstanceName = engine + instanceName;

			else

				fullInstanceName = instanceName;

			

			assert = instance instanceof WPGMZA[fullInstanceName];

			

			if(!assert)

				throw new Error("Object must be an instance of " + fullInstanceName + " (did you call a constructor directly, rather than createInstance?)");

		},

		

		/**

		 * @function getMapByID

		 * @param {mixed} id The ID of the map to retrieve

		 * @static

		 * @return {object} The map object, or null if no such map exists

		 */

		getMapByID: function(id) {

			for(var i = 0; i < WPGMZA.maps.length; i++) {

				if(WPGMZA.maps[i].id == id)

					return WPGMZA.maps[i];

			}

			

			return null;

		},

		

		/**

		 * @function isGoogleAutocompleteSupported

		 * @summary Shorthand function to determine if the Places Autocomplete is available

		 * @static

		 * @return {boolean}

		 */

		isGoogleAutocompleteSupported: function() {

			return typeof google === 'object' && typeof google.maps === 'object' && typeof google.maps.places === 'object' && typeof google.maps.places.Autocomplete === 'function';

		},

		

		googleAPIStatus: window.wpgmza_google_api_status

	};

	

	if(window.WPGMZA)

		window.WPGMZA = $.extend(window.WPGMZA, core);

	else

		window.WPGMZA = core;

	

	for(var key in WPGMZA_localized_data)

	{

		var value = WPGMZA_localized_data[key];

		WPGMZA[key] = value;

	}

	

	$(document).ready(function(event) {

		

		// Combined script warning

		if($("script[src*='wp-google-maps.combined.js'], script[src*='wp-google-maps-pro.combined.js']").length)

			console.warn("Minified script is out of date, using combined script instead.");

		

		// Check for multiple jQuery versions

		var elements = $("script").filter(function() {

			return this.src.match(/(^|\/)jquery\.(min\.)?js(\?|$)/i);

		});



		if(elements.length > 1)

			console.warn("Multiple jQuery versions detected: ", elements);

		

		// Rest API

		WPGMZA.restAPI = WPGMZA.RestAPI.createInstance();

		

		// TODO: Move to map edit page JS

		$(document).on("click", ".wpgmza_edit_btn", function() {

			

			WPGMZA.animateScroll("#wpgmaps_tabs_markers");

			

		});

		

	});

	

	$(window).on("load", function(event) {

		

		// Geolocation warnings

		if(window.location.protocol != 'https:')

		{

			var warning = '<div class="notice notice-warning"><p>' + WPGMZA.localized_strings.unsecure_geolocation + "</p></div>";

			

			$(".wpgmza-geolocation-setting").each(function(index, el) {

				$(el).after( $(warning) );

			});

		}

		

	});

	

	

	

})(jQuery);



// js/v8/compatibility.js

/**

 * @namespace WPGMZA

 * @module Compatibility

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.Compatibility = function()

	{

		this.preventDocumentWriteGoogleMapsAPI();

	}

	

	WPGMZA.Compatibility.prototype.preventDocumentWriteGoogleMapsAPI = function()

	{

		var old = document.write;

		

		document.write = function(content)

		{

			if(content.match && content.match(/maps\.google/))

				return;

			

			old.call(document, content);

		}

	}

	

	WPGMZA.compatiblityModule = new WPGMZA.Compatibility();

	

})(jQuery);



// js/v8/css-escape.js

/**

 * @module WPGMZA.CSS

 * @namespace WPGMZA

 * @requires WPGMZA

 * @summary Polyfill for CSS.escape, with thanks to @mathias

 */



/*! https://mths.be/cssescape v1.5.1 by @mathias | MIT license */

;(function(root, factory) {

	// https://github.com/umdjs/umd/blob/master/returnExports.js

	if (typeof exports == 'object') {

		// For Node.js.

		module.exports = factory(root);

	} else if (typeof define == 'function' && define.amd) {

		// For AMD. Register as an anonymous module.

		define([], factory.bind(root, root));

	} else {

		// For browser globals (not exposing the function separately).

		factory(root);

	}

}(typeof global != 'undefined' ? global : this, function(root) {



	if (root.CSS && root.CSS.escape) {

		return root.CSS.escape;

	}



	// https://drafts.csswg.org/cssom/#serialize-an-identifier

	var cssEscape = function(value) {

		if (arguments.length == 0) {

			throw new TypeError('`CSS.escape` requires an argument.');

		}

		var string = String(value);

		var length = string.length;

		var index = -1;

		var codeUnit;

		var result = '';

		var firstCodeUnit = string.charCodeAt(0);

		while (++index < length) {

			codeUnit = string.charCodeAt(index);

			// Note: there’s no need to special-case astral symbols, surrogate

			// pairs, or lone surrogates.



			// If the character is NULL (U+0000), then the REPLACEMENT CHARACTER

			// (U+FFFD).

			if (codeUnit == 0x0000) {

				result += '\uFFFD';

				continue;

			}



			if (

				// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is

				// U+007F, […]

				(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||

				// If the character is the first character and is in the range [0-9]

				// (U+0030 to U+0039), […]

				(index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||

				// If the character is the second character and is in the range [0-9]

				// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]

				(

					index == 1 &&

					codeUnit >= 0x0030 && codeUnit <= 0x0039 &&

					firstCodeUnit == 0x002D

				)

			) {

				// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point

				result += '\\' + codeUnit.toString(16) + ' ';

				continue;

			}



			if (

				// If the character is the first character and is a `-` (U+002D), and

				// there is no second character, […]

				index == 0 &&

				length == 1 &&

				codeUnit == 0x002D

			) {

				result += '\\' + string.charAt(index);

				continue;

			}



			// If the character is not handled by one of the above rules and is

			// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or

			// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to

			// U+005A), or [a-z] (U+0061 to U+007A), […]

			if (

				codeUnit >= 0x0080 ||

				codeUnit == 0x002D ||

				codeUnit == 0x005F ||

				codeUnit >= 0x0030 && codeUnit <= 0x0039 ||

				codeUnit >= 0x0041 && codeUnit <= 0x005A ||

				codeUnit >= 0x0061 && codeUnit <= 0x007A

			) {

				// the character itself

				result += string.charAt(index);

				continue;

			}



			// Otherwise, the escaped character.

			// https://drafts.csswg.org/cssom/#escape-a-character

			result += '\\' + string.charAt(index);



		}

		return result;

	};



	if (!root.CSS) {

		root.CSS = {};

	}



	root.CSS.escape = cssEscape;

	return cssEscape;



}));



// js/v8/distance.js

/**

 * @namespace WPGMZA

 * @module Distance

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.Distance = {

		

		MILES:					true,

		KILOMETERS:				false,

		

		MILES_PER_KILOMETER:	0.621371,

		KILOMETERS_PER_MILE:	1.60934,

		

		// TODO: Implement WPGMZA.settings.distance_units

		

		/**

		 * Converts a UI distance (eg from a form control) to meters,

		 * accounting for the global units setting

		 */

		uiToMeters: function(uiDistance)

		{

			return parseFloat(uiDistance) / (WPGMZA.settings.distance_units == WPGMZA.Distance.MILES ? WPGMZA.Distance.MILES_PER_KILOMETER : 1) * 1000;

		},

		

		/**

		 * Converts a UI distance (eg from a form control) to kilometers,

		 * accounting for the global units setting

		 */

		uiToKilometers: function(uiDistance)

		{

			return WPGMZA.Distance.uiToMeters(uiDistance) * 0.001;

		},

		

		/**

		 * Converts a UI distance (eg from a form control) to miles,

		 * accounting for the global units setting

		 */

		uiToMiles: function(uiDistance)

		{

			return WPGMZA.Distance.uiToKilometers(uiDistance) * WPGMZA.Distance.MILES_PER_KILOMETER;

		},

		

		kilometersToUI: function(km)

		{

			if(WPGMZA.settings.distance_units == WPGMZA.Distance.MILES)

				return km * WPGMZA.Distance.MILES_PER_KILOMETER;

			return km;

		}

		

	};

	

})(jQuery);



// js/v8/event-dispatcher.js

/**

 * @namespace WPGMZA

 * @module EventDispatcher

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.EventDispatcher = function()

	{

		WPGMZA.assertInstanceOf(this, "EventDispatcher");

		

		this._listenersByType = [];

	}



	WPGMZA.EventDispatcher.prototype.addEventListener = function(type, listener, thisObject, useCapture)

	{

		var arr;

		

		var types = type.split(/\s+/);

		if(types.length > 1)

		{

			for(var i = 0; i < types.length; i++)

				this.addEventListener(types[i], listener, thisObject, useCapture);

			

			return;

		}

		

		if(!(listener instanceof Function))

			throw new Error("Listener must be a function");



		if(!(arr = this._listenersByType[type]))

			arr = this._listenersByType[type] = [];

			

		var obj = {

			listener: listener,

			thisObject: (thisObject ? thisObject : this),

			useCapture: (useCapture ? true : false)

			};

			

		arr.push(obj);

	}



	WPGMZA.EventDispatcher.prototype.on = WPGMZA.EventDispatcher.prototype.addEventListener;



	WPGMZA.EventDispatcher.prototype.removeEventListener = function(type, listener, thisObject, useCapture)

	{

		var arr, index, obj;



		if(!(arr = this._listenersByType[type]))

			return;

			

		if(!thisObject)

			thisObject = this;

			

		useCapture = (useCapture ? true : false);

		

		for(var i = 0; i < arr.length; i++)

		{

			obj = arr[i];

		

			if(obj.listener == listener && obj.thisObject == thisObject && obj.useCapture == useCapture)

			{

				arr.splice(i, 1);

				return;

			}

		}

	}



	WPGMZA.EventDispatcher.prototype.off = WPGMZA.EventDispatcher.prototype.removeEventListener;



	WPGMZA.EventDispatcher.prototype.hasEventListener = function(type)

	{

		return (_listenersByType[type] ? true : false);

	}



	WPGMZA.EventDispatcher.prototype.dispatchEvent = function(event)

	{

		if(!(event instanceof WPGMZA.Event))

		{

			if(typeof event == "string")

				event = new WPGMZA.Event(event);

			else

			{

				var src = event;

				event = new WPGMZA.Event();

				for(var name in src)

					event[name] = src[name];

			}

		}



		event.target = this;

			

		var path = [];

		for(var obj = this.parent; obj != null; obj = obj.parent)

			path.unshift(obj);

		

		event.phase = WPGMZA.Event.CAPTURING_PHASE;

		for(var i = 0; i < path.length && !event._cancelled; i++)

			path[i]._triggerListeners(event);

			

		if(event._cancelled)

			return;

			

		event.phase = WPGMZA.Event.AT_TARGET;

		this._triggerListeners(event);

			

		event.phase = WPGMZA.Event.BUBBLING_PHASE;

		for(i = path.length - 1; i >= 0 && !event._cancelled; i--)

			path[i]._triggerListeners(event);

		

		if(this.element)

		{

			var customEvent = {};

			

			for(var key in event)

			{

				var value = event[key];

				

				if(key == "type")

					value += ".wpgmza";

				

				customEvent[key] = value;

			}

			

			$(this.element).trigger(customEvent);

		}

	}



	WPGMZA.EventDispatcher.prototype.trigger = WPGMZA.EventDispatcher.prototype.dispatchEvent;



	WPGMZA.EventDispatcher.prototype._triggerListeners = function(event)

	{

		var arr, obj;

		

		if(!(arr = this._listenersByType[event.type]))

			return;

			

		for(var i = 0; i < arr.length; i++)

		{

			obj = arr[i];

			

			if(event.phase == WPGMZA.Event.CAPTURING_PHASE && !obj.useCapture)

				continue;

				

			obj.listener.call(arr[i].thisObject, event);

		}

	}



	WPGMZA.events = new WPGMZA.EventDispatcher();



})(jQuery);



// js/v8/event.js

/**

 * @namespace WPGMZA

 * @module Event

 * @requires WPGMZA

 */ 

(function($) {

		

	WPGMZA.Event = function(options)

	{

		if(typeof options == "string")

			this.type = options;

		

		this.bubbles		= true;

		this.cancelable		= true;

		this.phase			= WPGMZA.Event.PHASE_CAPTURE;

		this.target			= null;

		

		this._cancelled = false;

		

		if(typeof options == "object")

			for(var name in options)

				this[name] = options[name];

	}



	WPGMZA.Event.CAPTURING_PHASE		= 0;

	WPGMZA.Event.AT_TARGET				= 1;

	WPGMZA.Event.BUBBLING_PHASE			= 2;



	WPGMZA.Event.prototype.stopPropagation = function()

	{

		this._cancelled = true;

	}

	

})(jQuery);



// js/v8/friendly-error.js

/**

 * @namespace WPGMZA

 * @module FriendlyError

 * @requires WPGMZA

 */

(function($) {

	

	/*var template = '\

		<div class="notice notice-error"> \

			<p> \

			' + WPGMZA.localized_strings.friendly_error + ' \

			</p> \

			<pre style="white-space: pre-line;"></pre> \

		<div> \

		';

	

	WPGMZA.FriendlyError = function(nativeError)

	{

		if(!WPGMZA.is_admin)

		{

			this.element = $(WPGMZA.preloaderHTML);

			$(this.element).removeClass("animated");

			return;

		}

		

		$("#wpgmza-map-edit-page>.wpgmza-preloader").remove();

		

		this.element = $(template);

		this.element.find("pre").html(nativeError.message + "\r\n" + nativeError.stack + "\r\n\r\n on " + window.location.href);

	}*/

	

})(jQuery);



// js/v8/geocoder.js

/**

 * @namespace WPGMZA

 * @module Geocoder

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.Geocoder = function()

	{

		WPGMZA.assertInstanceOf(this, "Geocoder");

	}

	

	WPGMZA.Geocoder.SUCCESS			= "success";

	WPGMZA.Geocoder.ZERO_RESULTS	= "zero-results";

	WPGMZA.Geocoder.FAIL			= "fail";

	

	WPGMZA.Geocoder.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				return WPGMZA.OLGeocoder;

				break;

				

			default:

				return WPGMZA.GoogleGeocoder;

				break;

		}

	}

	

	WPGMZA.Geocoder.createInstance = function()

	{

		var constructor = WPGMZA.Geocoder.getConstructor();

		return new constructor();

	}

	

	WPGMZA.Geocoder.prototype.getLatLngFromAddress = function(options, callback)

	{

		if(WPGMZA.isLatLngString(options.address))

		{

			var parts = options.address.split(/,\s*/);

			var latLng = new WPGMZA.LatLng({

				lat: parseFloat(parts[0]),

				lng: parseFloat(parts[1])

			});

			callback([latLng], WPGMZA.Geocoder.SUCCESS);

		}

	}

	

	WPGMZA.Geocoder.prototype.getAddressFromLatLng = function(options, callback)

	{

		var latLng = new WPGMZA.LatLng(options.latLng);

		callback([latLng.toString()], WPGMZA.Geocoder.SUCCESS);

	}

	

	WPGMZA.Geocoder.prototype.geocode = function(options, callback)

	{

		if("address" in options)

			return this.getLatLngFromAddress(options, callback);

		else if("latLng" in options)

			return this.getAddressFromLatLng(options, callback);

		

		throw new Error("You must supply either a latLng or address");

	}

	

})(jQuery);



// js/v8/google-api-error-handler.js

/**

 * @namespace WPGMZA

 * @module GoogleAPIErrorHandler

 * @requires WPGMZA

 */

(function($){  



	WPGMZA.GoogleAPIErrorHandler = function() {

		

		var self = this;

		

		// Don't do anything if Google isn't the selected API

		if(WPGMZA.settings.engine != "google-maps")

			return;

		

		// Only allow on the map edit page, or front end if user has administrator role

		if(!(WPGMZA.currentPage == "map-edit" || (WPGMZA.is_admin == 0 && WPGMZA.userCanAdministrator == 1)))

			return;

		

		this.element = $(WPGMZA.html.googleMapsAPIErrorDialog);

		

		if(WPGMZA.is_admin == 1)

			this.element.find(".wpgmza-front-end-only").remove();

		

		this.errorMessageList = this.element.find("#wpgmza-google-api-error-list");

		this.templateListItem = this.element.find("li.template").remove();

		

		this.messagesAlreadyDisplayed = {};

		

		// Override error function

		var _error = console.error;

		

		console.error = function(message)

		{

			self.onErrorMessage(message);

			

			_error.apply(this, arguments);

		}

	}

	

	WPGMZA.GoogleAPIErrorHandler.prototype.onErrorMessage = function(message)

	{

		var m;

		var regexURL = /http(s)?:\/\/[^\s]+/gm;

		

		if((m = message.match(/You have exceeded your (daily )?request quota for this API/)) || (m = message.match(/This API project is not authorized to use this API/)))

		{

			var urls = message.match(regexURL);

			this.addErrorMessage(m[0], urls);

		}

		else if(m = message.match(/^Google Maps.+error: (.+)\s+(http(s?):\/\/.+)/m))

		{

			this.addErrorMessage(m[1].replace(/([A-Z])/g, " $1"), [m[2]]);

		}

	}

	

	WPGMZA.GoogleAPIErrorHandler.prototype.addErrorMessage = function(message, urls)

	{

		if(this.messagesAlreadyDisplayed[message])

			return;

		

		var li = this.templateListItem.clone();

		$(li).find(".wpgmza-message").html(message);

		

		var buttonContainer = $(li).find(".wpgmza-documentation-buttons");

		

		var buttonTemplate = $(li).find(".wpgmza-documentation-buttons>a");

		buttonTemplate.remove();

		

		if(urls && urls.length)

		{

			for(var i = 0; i < urls.length; i++)

			{

				var url = urls[i];

				var button = buttonTemplate.clone();

				var icon = "fa-external-link";

				var text = WPGMZA.localized_strings.documentation;

				

				button.attr("href", urls[i]);

				

				if(url.match(/google.+documentation/))

				{

					icon = "fa-google";

				}

				else if(url.match(/maps-no-account/))

				{

					icon = "fa-wrench"

					text = WPGMZA.localized_strings.verify_project;

				}

				else if(url.match(/console\.developers\.google/))

				{

					icon = "fa-wrench";

					text = WPGMZA.localized_strings.api_dashboard;

				}

				

				$(button).find("i").addClass(icon);

				$(button).append(text);

			}

			

			buttonContainer.append(button);

		}

		

		$(this.errorMessageList).append(li);

		

		if(!this.dialog)

			this.dialog = $(this.element).remodal();

		

		switch(this.dialog.getState())

		{

			case "open":

			case "opened":

			case "opening":

				break;

				

			default:

				this.dialog.open();

				break;

		}

		

		this.messagesAlreadyDisplayed[message] = true;

	}

	

	WPGMZA.googleAPIErrorHandler = new WPGMZA.GoogleAPIErrorHandler();



})(jQuery);



// js/v8/info-window.js

/**

 * @namespace WPGMZA

 * @module InfoWindow

 * @requires WPGMZA.EventDispatcher

 */

(function($) {

	

	WPGMZA.InfoWindow = function(mapObject)

	{

		var self = this;

		

		WPGMZA.EventDispatcher.call(this);

		

		WPGMZA.assertInstanceOf(this, "InfoWindow");

		

		if(!mapObject)

			return;

		

		this.mapObject = mapObject;

		

		if(mapObject.map)

		{

			// This has to be slightly delayed so the map initialization won't overwrite the infowindow element

			setTimeout(function() {

				self.onMapObjectAdded(event);

			}, 100);

		}

		else

			mapObject.addEventListener("added", function(event) { 

				self.onMapObjectAdded(event);

			});		

	}

	

	WPGMZA.InfoWindow.prototype = Object.create(WPGMZA.EventDispatcher.prototype);

	WPGMZA.InfoWindow.prototype.constructor = WPGMZA.InfoWindow;

	

	WPGMZA.InfoWindow.OPEN_BY_CLICK = 1;

	WPGMZA.InfoWindow.OPEN_BY_HOVER = 2;

	

	WPGMZA.InfoWindow.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				if(WPGMZA.isProVersion())

					return WPGMZA.OLProInfoWindow;

				return WPGMZA.OLInfoWindow;

				break;

			

			default:

				if(WPGMZA.isProVersion())

					return WPGMZA.GoogleProInfoWindow;

				return WPGMZA.GoogleInfoWindow;

				break;

		}

	}

	

	WPGMZA.InfoWindow.createInstance = function(mapObject)

	{

		var constructor = this.getConstructor();

		return new constructor(mapObject);

	}

	

	/**

	 * Gets the content for the info window and passes it to the specified callback - this allows for delayed loading (eg AJAX) as well as instant content

	 * @return void

	 */

	WPGMZA.InfoWindow.prototype.getContent = function(callback)

	{

		var html = "";

		

		if(this.mapObject instanceof WPGMZA.Marker)

			html = this.mapObject.address;

		

		callback(html);

	}

	

	/**

	 * Opens the info window

	 * @return boolean FALSE if the info window should not & will not open, TRUE if it will

	 */

	WPGMZA.InfoWindow.prototype.open = function(map, mapObject)

	{

		var self = this;

		

		this.mapObject = mapObject;

		

		if(WPGMZA.settings.disable_infowindows)

			return false;

		

		return true;

	}

	

	WPGMZA.InfoWindow.prototype.close = function()

	{

		

	}

	

	WPGMZA.InfoWindow.prototype.setContent = function(options)

	{

		

	}

	

	WPGMZA.InfoWindow.prototype.setOptions = function(options)

	{

		

	}

	

	/**

	 * Event listener for when the map object is added. This will cause the info window to open if the map object has infoopen set

	 * @return void

	 */

	WPGMZA.InfoWindow.prototype.onMapObjectAdded = function()

	{

		if(this.mapObject.settings.infoopen == 1)

			this.open();

	}

	

})(jQuery);



// js/v8/latlng.js

/**

 * @namespace WPGMZA

 * @module LatLng

 * @requires WPGMZA

 */

(function($) {



	/**

	 * Constructor

	 * @param mixed A latLng literal, or latitude

	 * @param mixed The latitude, where arg is a longitude

	 */

	WPGMZA.LatLng = function(arg, lng)

	{

		this._lat = 0;

		this._lng = 0;

		

		if(arguments.length == 0)

			return;

		

		if(arguments.length == 1)

		{

			// TODO: Support latlng string

			

			if(typeof arg == "string")

			{

				var m;

				

				if(!(m = arg.match(WPGMZA.LatLng.REGEXP)))

					throw new Error("Invalid LatLng string");

				

				arg = {

					lat: m[1],

					lng: m[3]

				};

			}

			

			if(typeof arg != "object" || !("lat" in arg && "lng" in arg))

				throw new Error("Argument must be a LatLng literal");

			

			this.lat = arg.lat;

			this.lng = arg.lng;

		}

		else

		{

			this.lat = arg;

			this.lng = lng;

		}

	}

	

	WPGMZA.LatLng.REGEXP = /^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/;

	

	WPGMZA.LatLng.isValid = function(obj)

	{

		if(typeof obj != "object")

			return false;

		

		if(!("lat" in obj && "lng" in obj))

			return false;

		

		return true;

	}

	

	Object.defineProperty(WPGMZA.LatLng.prototype, "lat", {

		get: function() {

			return this._lat;

		},

		set: function(val) {

			if(!$.isNumeric(val))

				throw new Error("Latitude must be numeric");

			this._lat = parseFloat( val );

		}

	});

	

	Object.defineProperty(WPGMZA.LatLng.prototype, "lng", {

		get: function() {

			return this._lng;

		},

		set: function(val) {

			if(!$.isNumeric(val))

				throw new Error("Longitude must be numeric");

			this._lng = parseFloat( val );

		}

	});

	

	WPGMZA.LatLng.prototype.toString = function()

	{

		return this._lat + ", " + this._lng;

	}

	

	WPGMZA.LatLng.prototype.toGoogleLatLng = function()

	{

		return new google.maps.LatLng({

			lat: this.lat,

			lng: this.lng

		});

	}

	

	/**

	 * @function moveByDistance

	 * @summary Moves this latLng by the specified kilometers along the given heading

	 * @return void

	 * With many thanks to Hu Kenneth - https://gis.stackexchange.com/questions/234473/get-a-lonlat-point-by-distance-or-between-2-lonlat-points

	 */

	WPGMZA.LatLng.prototype.moveByDistance = function(kilometers, heading)

	{

		var radius 		= 6371;

		

		var delta 		= parseFloat(kilometers) / radius;

		var theta 		= parseFloat(heading) / 180 * Math.PI;

		

		var phi1 		= this.lat / 180 * Math.PI;

		var lambda1 	= this.lng / 180 * Math.PI;

		

		var sinPhi1 	= Math.sin(phi1), cosPhi1 = Math.cos(phi1);

		var sinDelta	= Math.sin(delta), cosDelta = Math.cos(delta);

		var sinTheta	= Math.sin(theta), cosTheta = Math.cos(theta);

		

		var sinPhi2		= sinPhi1 * cosDelta + cosPhi1 * sinDelta * cosTheta;

		var phi2		= Math.asin(sinPhi2);

		var y			= sinTheta * sinDelta * cosPhi1;

		var x			= cosDelta - sinPhi1 * sinPhi2;

		var lambda2		= lambda1 + Math.atan2(y, x);

		

		this.lat		= phi2 * 180 / Math.PI;

		this.lng		= lambda2 * 180 / Math.PI;

	}

	

})(jQuery);



// js/v8/latlngbounds.js

/**

 * @namespace WPGMZA

 * @module LatLngBounds

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.LatLngBounds = function(southWest, northEast)

	{

		

	}

	

	WPGMZA.LatLngBounds.prototype.isInInitialState = function()

	{

		return (this.north == undefined && this.south == undefined && this.west == undefined && this.east == undefined);

	}

	

	WPGMZA.LatLngBounds.prototype.extend = function(latLng)

	{

		if(this.isInInitialState())

		{

			this.north = this.south = this.west = this.east = new WPGMZA.LatLng(latLng);

			return;

		}

		

		if(!(latLng instanceof WPGMZA.LatLng))

			latLng = new WPGMZA.LatLng(latLng);

		

		if(latLng.lat < this.north)

			this.north = latLng.lat;

		

		if(latLng.lat > this.south)

			this.south = latLng.lat;

		

		if(latLng.lng < this.west)

			this.west = latLng.lng;

		

		if(latLng.lng > this.east)

			this.east = latLng.lng;

	}

	

})(jQuery);





// js/v8/map-object.js

/**

 * @namespace WPGMZA

 * @module MapObject

 * @requires WPGMZA.EventDispatcher

 */

(function($) {

	

	WPGMZA.MapObject = function(row)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "MapObject");

		

		WPGMZA.EventDispatcher.call(this);

		

		this.id = -1;

		this.guid = WPGMZA.guid();

		this.modified = true;

		this.settings = {};

		

		if(row)

		{

			for(var name in row)

			{

				if(name == "settings")

				{

					if(row["settings"] == null)

						this["settings"] = {};

					else switch(typeof row["settings"]) {

						case "string":

							this["settings"] = JSON.parse(row[name]);

							break;

						case "object":

							this["settings"] = row[name];

							break;

						default:

							throw new Error("Don't know how to interpret settings")

							break;

					}

					

					for(var name in this.settings)

					{

						var value = this.settings[name];

						if(String(value).match(/^-?\d+$/))

							this.settings[name] = parseInt(value);

					}

				}

				else

					this[name] = row[name];

			}

		}		

	}

	

	WPGMZA.MapObject.prototype = Object.create(WPGMZA.EventDispatcher.prototype);

	WPGMZA.MapObject.prototype.constructor = WPGMZA.MapObject;

	

	WPGMZA.MapObject.prototype.parseGeometry = function(string)

	{

		var stripped, pairs, coords, results = [];

		stripped = string.replace(/[^ ,\d\.\-+e]/g, "");

		pairs = stripped.split(",");

		

		for(var i = 0; i < pairs.length; i++)

		{

			coords = pairs[i].split(" ");

			results.push({

				lat: parseFloat(coords[1]),

				lng: parseFloat(coords[0])

			});

		}

				

		return results;

	}

	

	WPGMZA.MapObject.prototype.toJSON = function()

	{

		return {

			id: this.id,

			guid: this.guid,

			settings: this.settings

		};

	}

	

})(jQuery);



// js/v8/circle.js

/**

 * @namespace WPGMZA

 * @module Circle

 * @requires WPGMZA.MapObject

 */

(function($) {

	

	var Parent = WPGMZA.MapObject;

	

	/**

	 * @class Circle

	 * @summary Represents a generic circle. <b>Please do not instantiate this object directly, use createInstance</b>

	 * @return {WPGMZA.Circle}

	 */

	WPGMZA.Circle = function(options, engineCircle)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "Circle");

		

		this.center = new WPGMZA.LatLng();

		this.radius = 100;

		

		Parent.apply(this, arguments);

	}

	

	WPGMZA.Circle.prototype = Object.create(Parent.prototype);

	WPGMZA.Circle.prototype.constructor = WPGMZA.Circle;

	

	/**

	 * @function createInstance

	 * @summary Creates an instance of a circle, <b>please always use this function rather than calling the constructor directly</b>

	 * @param {object} options Options for the object (optional)

	 */

	WPGMZA.Circle.createInstance = function(options)

	{

		var constructor;

		

		if(WPGMZA.settings.engine == "google-maps")

			constructor = WPGMZA.GoogleCircle;

		else

			constructor = WPGMZA.OLCircle;

		

		return new constructor(options);

	}

	

	/**

	 * @function getCenter

	 * @returns {WPGMZA.LatLng}

	 */

	WPGMZA.Circle.prototype.getCenter = function()

	{

		return this.center.clone();

	}

	

	/**

	 * @function setCenter

	 * @param {object|WPGMZA.LatLng} latLng either a literal or as a WPGMZA.LatLng

	 * @returns {void}

	 */

	WPGMZA.Circle.prototype.setCenter = function(latLng)

	{

		this.center.lat = latLng.lat;

		this.center.lng = latLng.lng;

	}

	

	/**

	 * @function getRadius

	 * @summary Returns the circles radius in kilometers

	 * @returns {WPGMZA.LatLng}

	 */

	WPGMZA.Circle.prototype.getRadius = function()

	{

		return this.radius;

	}

	

	/**

	 * @function setRadius

	 * @param {number} The radius

	 * @returns {void}

	 */

	WPGMZA.Circle.prototype.setRadius = function(radius)

	{

		this.radius = radius;

	}

	

	/**

	 * @function getMap

	 * @summary Returns the map that this circle is being displayed on

	 * @return {WPGMZA.Map}

	 */

	WPGMZA.Circle.prototype.getMap = function()

	{

		return this.map;

	}

	

	/**

	 * @function setMap

	 * @param {WPGMZA.Map} The target map

	 * @summary Puts this circle on a map

	 * @return {void}

	 */

	WPGMZA.Circle.prototype.setMap = function(map)

	{

		if(this.map)

			this.map.removeCircle(this);

		

		if(map)

			map.addCircle(this);

			

	}

	

})(jQuery);



// js/v8/map-settings-page.js

/**

 * @namespace WPGMZA

 * @module MapSettingsPage

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.MapSettingsPage = function()

	{

		var self = this;

		

		this.updateEngineSpecificControls();

		this.updateGDPRControls();

		

		$("select[name='wpgmza_maps_engine']").on("change", function(event) {

			self.updateEngineSpecificControls();

		});

		

		$("input[name='wpgmza_gdpr_require_consent_before_load'], input[name='wpgmza_gdpr_require_consent_before_vgm_submit'], input[name='wpgmza_gdpr_override_notice']").on("change", function(event) {

			self.updateGDPRControls();

		});

	}

	

	WPGMZA.MapSettingsPage.prototype.updateEngineSpecificControls = function()

	{

		var engine = $("select[name='wpgmza_maps_engine']").val();

		

		$("[data-required-maps-engine][data-required-maps-engine!='" + engine + "']").hide();

		$("[data-required-maps-engine='" + engine + "']").show();

	}

	

	WPGMZA.MapSettingsPage.prototype.updateGDPRControls = function()

	{

		var showNoticeControls = $("input[name='wpgmza_gdpr_require_consent_before_load']").prop("checked");

		

		var vgmCheckbox = $("input[name='wpgmza_gdpr_require_consent_before_vgm_submit']");

		

		if(vgmCheckbox.length)

			showNoticeControls = showNoticeControls || vgmCheckbox.prop("checked");

		

		var showOverrideTextarea = showNoticeControls && $("input[name='wpgmza_gdpr_override_notice']").prop("checked");

		

		if(showNoticeControls)

		{

			$("#wpgmza-gdpr-compliance-notice").show("slow");

		}

		else

		{

			$("#wpgmza-gdpr-compliance-notice").hide("slow");

		}

		

		if(showOverrideTextarea)

		{

			$("#wpgmza_gdpr_override_notice_text").show("slow");

		}

		else

		{

			$("#wpgmza_gdpr_override_notice_text").hide("slow");

		}

	}

	

	$(document).ready(function(event) {

		

		if(!window.location.href.match(/wp-google-maps-menu-settings/))

			return;

		

		WPGMZA.mapSettingsPage = new WPGMZA.MapSettingsPage();

		

	});

	

})(jQuery);



// js/v8/map-settings.js

/**

 * @namespace WPGMZA

 * @module MapSettings

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.MapSettings = function(element)

	{

		var str = element.getAttribute("data-settings");

		var json = JSON.parse(str);

		

		//var id = $(element).attr("data-map-id");

		//var json = JSON.parse(window["wpgmza_map_settings_" + id]);

		

		WPGMZA.assertInstanceOf(this, "MapSettings");

		

		for(var key in WPGMZA.settings)

		{

			var value = WPGMZA.settings[key];

			

			this[key] = value;

		}

		

		for(var key in json)

		{

			var value = json[key];

			

			if(String(value).match(/^-?\d+$/))

				value = parseInt(value);

				

			this[key] = value;

		}

	}

	

	WPGMZA.MapSettings.prototype.toOLViewOptions = function()

	{

		var options = {

			center: ol.proj.fromLonLat([-119.4179, 36.7783]),

			zoom: 4

		};

		

		function empty(name)

		{

			if(typeof self[name] == "object")

				return false;

			

			return !self[name] || !self[name].length;

		}

		

		// Start location

		if(typeof this.start_location == "string")

		{

			var coords = this.start_location.replace(/^\(|\)$/g, "").split(",");

			if(WPGMZA.isLatLngString(this.start_location))

				options.center = ol.proj.fromLonLat([

					parseFloat(coords[1]),

					parseFloat(coords[0])

				]);

			else

				console.warn("Invalid start location");

		}

		

		if(this.center)

		{

			options.center = ol.proj.fromLonLat([

				parseFloat(this.center.lng),

				parseFloat(this.center.lat)

			]);

		}

		

		// Start zoom

		if(this.zoom)

			options.zoom = parseInt(this.zoom);

		

		if(this.start_zoom)

			options.zoom = parseInt(this.start_zoom);

		

		// Zoom limits

		// TODO: This matches the Google code, so some of these could be potentially put on a parent class

		if(!empty("min_zoom"))

			options.minZoom = parseInt(this.min_zoom);

		if(!empty("max_zoom"))

			options.maxZoom = parseInt(this.max_zoom);

		

		return options;

	}

	

	WPGMZA.MapSettings.prototype.toGoogleMapsOptions = function()

	{

		var self = this;

		var latLngCoords = (this.start_location && this.start_location.length ? this.start_location.split(",") : [36.7783, -119.4179]);

		

		function empty(name)

		{

			if(typeof self[name] == "object")

				return false;

			

			return !self[name] || !self[name].length;

		}

		

		function formatCoord(coord)

		{

			if($.isNumeric(coord))

				return coord;

			return parseFloat( String(coord).replace(/[\(\)\s]/, "") );

		}

		

		var latLng = new google.maps.LatLng(

			formatCoord(latLngCoords[0]),

			formatCoord(latLngCoords[1])

		);

		

		var zoom = (this.start_zoom ? parseInt(this.start_zoom) : 4);

		

		if(!this.start_zoom && this.zoom)

			zoom = parseInt( this.zoom );

		

		var options = {

			zoom:			zoom,

			center:			latLng

		};

		

		if(!empty("center"))

			options.center = new google.maps.LatLng({

				lat: parseFloat(this.center.lat),

				lng: parseFloat(this.center.lng)

			});

		

		if(!empty("min_zoom"))

			options.minZoom = parseInt(this.min_zoom);

		if(!empty("max_zoom"))

			options.maxZoom = parseInt(this.max_zoom);

		

		// These settings are all inverted because the checkbox being set means "disabled"

		options.zoomControl				= !(this.wpgmza_settings_map_zoom == 'yes');

        options.panControl				= !(this.wpgmza_settings_map_pan == 'yes');

        options.mapTypeControl			= !(this.wpgmza_settings_map_type == 'yes');

        options.streetViewControl		= !(this.wpgmza_settings_map_streetview == 'yes');

        options.fullscreenControl		= !(this.wpgmza_settings_map_full_screen_control == 'yes');

        

        options.draggable				= !(this.wpgmza_settings_map_draggable == 'yes');

        options.disableDoubleClickZoom	= !(this.wpgmza_settings_map_clickzoom == 'yes');

        options.scrollwheel				= !(this.wpgmza_settings_map_scroll == 'yes');

		

		if(this.wpgmza_force_greedy_gestures == "greedy")

			options.gestureHandling = "greedy";

		else

			options.gestureHandling = "cooperative";

		

		switch(parseInt(this.map_type))

		{

			case 2:

				options.mapTypeId = google.maps.MapTypeId.SATELLITE;

				break;

			

			case 3:

				options.mapTypeId = google.maps.MapTypeId.HYBRID;

				break;

			

			case 4:

				options.mapTypeId = google.maps.MapTypeId.TERRAIN;

				break;

				

			default:

				options.mapTypeId = google.maps.MapTypeId.ROADMAP;

				break;

		}

		

		if(this.theme_data && this.theme_data.length > 0)

		{

			try{

				options.styles = JSON.parse(this.theme_data);

			}catch(e) {

				alert("Your theme data is not valid JSON and has been ignored");

			}

		}

		

		return options;

	}

})(jQuery);



// js/v8/map.js

/**

 * @namespace WPGMZA

 * @module Map

 * @requires WPGMZA.EventDispatcher

 */

(function($) {

	

	/**

	 * Constructor

	 * @param element to contain map

	 */

	WPGMZA.Map = function(element, options)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "Map");

		

		WPGMZA.EventDispatcher.call(this);

		

		if(!(element instanceof HTMLElement))

			throw new Error("Argument must be a HTMLElement");

		

		this.id = element.getAttribute("data-map-id");

		if(!/\d+/.test(this.id))

			throw new Error("Map ID must be an integer");

		

		WPGMZA.maps.push(this);

		this.element = element;

		this.element.wpgmzaMap = this;

		

		this.engineElement = element;

		

		this.markers = [];

		this.polygons = [];

		this.polylines = [];

		this.circles = [];

		

		this.loadSettings(options);

	}

	

	WPGMZA.Map.prototype = Object.create(WPGMZA.EventDispatcher.prototype);

	WPGMZA.Map.prototype.constructor = WPGMZA.Map;

	

	WPGMZA.Map.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				if(WPGMZA.isProVersion())

					return WPGMZA.OLProMap;

				

				return WPGMZA.OLMap;

				break;

			

			default:

				if(WPGMZA.isProVersion())

					return WPGMZA.GoogleProMap;

				

				return WPGMZA.GoogleMap;

				break;

		}

	}

	

	WPGMZA.Map.createInstance = function(element, options)

	{

		var constructor = WPGMZA.Map.getConstructor();

		return new constructor(element, options);

	}

	

	/**

	 * Loads the maps settings and sets some defaults

	 * @return void

	 */

	WPGMZA.Map.prototype.loadSettings = function(options)

	{

		var settings = new WPGMZA.MapSettings(this.element);

		var other_settings = settings.other_settings;

		

		delete settings.other_settings;

		

		if(other_settings)

			for(var key in other_settings)

				settings[key] = other_settings[key];

			

		if(options)

			for(var key in options)

				settings[key] = options[key];

			

		this.settings = settings;

	}

	

	/**

	 * This override should automatically dispatch a .wpgmza scoped event on the element

	 * TODO: Implement

	 */

	/*WPGMZA.Map.prototype.trigger = function(event)

	{

		

	}*/

	

	/**

	 * Sets options in bulk on map

	 * @return void

	 */

	WPGMZA.Map.prototype.setOptions = function(options)

	{

		for(var name in options)

			this.settings[name] = options[name];

	}

	

	/**

	 * Gets the distance between two latLngs in kilometers

	 * NB: Static function

	 * @return number

	 */

	var earthRadiusMeters = 6371;

	var piTimes360 = Math.PI / 360;

	

	function deg2rad(deg) {

	  return deg * (Math.PI/180)

	};

	

	/**

	 * This gets the distance in kilometers between two latitude / longitude points

	 * TODO: Move this to the distance class, or the LatLng class

	 * @return void

	 */

	WPGMZA.Map.getGeographicDistance = function(lat1, lon1, lat2, lon2)

	{

		var dLat = deg2rad(lat2-lat1);

		var dLon = deg2rad(lon2-lon1); 

		

		var a = 

			Math.sin(dLat/2) * Math.sin(dLat/2) +

			Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 

			Math.sin(dLon/2) * Math.sin(dLon/2); 

			

		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 

		var d = earthRadiusMeters * c; // Distance in km

		

		return d;

	}

	

	WPGMZA.Map.prototype.setCenter = function(latLng)

	{

		if(!("lat" in latLng && "lng" in latLng))

			throw new Error("Argument is not an object with lat and lng");

	}

	

	/**

	 * Sets the dimensions of the map

	 * @return void

	 */

	WPGMZA.Map.prototype.setDimensions = function(width, height)

	{

		$(this.element).css({

			width: width

		});

		

		$(this.engineElement).css({

			width: "100%",

			height: height

		});

	}

	

	/**

	 * Adds the specified marker to this map

	 * @return void

	 */

	WPGMZA.Map.prototype.addMarker = function(marker)

	{

		if(!(marker instanceof WPGMZA.Marker))

			throw new Error("Argument must be an instance of WPGMZA.Marker");

		

		marker.map = this;

		marker.parent = this;

		

		this.markers.push(marker);

		this.dispatchEvent({type: "markeradded", marker: marker});

		marker.dispatchEvent({type: "added"});

	}

	

	/**

	 * Removes the specified marker from this map

	 * @return void

	 */

	WPGMZA.Map.prototype.removeMarker = function(marker)

	{

		if(!(marker instanceof WPGMZA.Marker))

			throw new Error("Argument must be an instance of WPGMZA.Marker");

		

		if(marker.map !== this)

			throw new Error("Wrong map error");

		

		marker.map = null;

		marker.parent = null;

		

		this.markers.splice(this.markers.indexOf(marker), 1);

		this.dispatchEvent({type: "markerremoved", marker: marker});

		marker.dispatchEvent({type: "removed"});

	}

	

	WPGMZA.Map.prototype.getMarkerByID = function(id)

	{

		for(var i = 0; i < this.markers.length; i++)

		{

			if(this.markers[i].id == id)

				return this.markers[i];

		}

		

		return null;

	}

	

	WPGMZA.Map.prototype.removeMarkerByID = function(id)

	{

		var marker = this.getMarkerByID(id);

		

		if(!marker)

			return;

		

		this.removeMarker(marker);

	}

	

	/**

	 * Adds the specified polygon to this map

	 * @return void

	 */

	WPGMZA.Map.prototype.addPolygon = function(polygon)

	{

		if(!(polygon instanceof WPGMZA.Polygon))

			throw new Error("Argument must be an instance of WPGMZA.Polygon");

		

		polygon.map = this;

		

		this.polygons.push(polygon);

		this.dispatchEvent({type: "polygonadded", polygon: polygon});

	}

	

	/**

	 * Removes the specified polygon from this map

	 * @return void

	 */

	WPGMZA.Map.prototype.deletePolygon = function(polygon)

	{

		if(!(polygon instanceof WPGMZA.Polygon))

			throw new Error("Argument must be an instance of WPGMZA.Polygon");

		

		if(polygon.map !== this)

			throw new Error("Wrong map error");

		

		polygon.map = null;

		

		this.polygons.splice(this.polygons.indexOf(polygon), 1);

		this.dispatchEvent({type: "polygonremoved", polygon: polygon});

	}

	

	WPGMZA.Map.prototype.getPolygonByID = function(id)

	{

		for(var i = 0; i < this.polygons.length; i++)

		{

			if(this.polygons[i].id == id)

				return this.polygons[i];

		}

		

		return null;

	}

	

	WPGMZA.Map.prototype.deletePolygonByID = function(id)

	{

		var polygon = this.getPolygonByID(id);

		

		if(!polygon)

			return;

		

		this.deletePolygon(polygon);

	}

	

	/**

	 * Gets a polyline by ID

	 * @return void

	 */

	WPGMZA.Map.prototype.getPolylineByID = function(id)

	{

		for(var i = 0; i < this.polylines.length; i++)

		{

			if(this.polylines[i].id == id)

				return this.polylines[i];

		}

		

		return null;

	}

	

	/**

	 * Adds the specified polyline to this map

	 * @return void

	 */

	WPGMZA.Map.prototype.addPolyline = function(polyline)

	{

		if(!(polyline instanceof WPGMZA.Polyline))

			throw new Error("Argument must be an instance of WPGMZA.Polyline");

		

		polyline.map = this;

		

		this.polylines.push(polyline);

		this.dispatchEvent({type: "polylineadded", polyline: polyline});

	}

	

	/**

	 * Removes the specified polyline from this map

	 * @return void

	 */

	WPGMZA.Map.prototype.deletePolyline = function(polyline)

	{

		if(!(polyline instanceof WPGMZA.Polyline))

			throw new Error("Argument must be an instance of WPGMZA.Polyline");

		

		if(polyline.map !== this)

			throw new Error("Wrong map error");

		

		polyline.map = null;

		

		this.polylines.splice(this.polylines.indexOf(polyline), 1);

		this.dispatchEvent({type: "polylineremoved", polyline: polyline});

	}

	

	WPGMZA.Map.prototype.getPolylineByID = function(id)

	{

		for(var i = 0; i < this.polylines.length; i++)

		{

			if(this.polylines[i].id == id)

				return this.polylines[i];

		}

		

		return null;

	}

	

	WPGMZA.Map.prototype.deletePolylineByID = function(id)

	{

		var polyline = this.getPolylineByID(id);

		

		if(!polyline)

			return;

		

		this.deletePolyline(polyline);

	}

	

	/**

	 * Adds the specified circle to this map

	 * @return void

	 */

	WPGMZA.Map.prototype.addCircle = function(circle)

	{

		if(!(circle instanceof WPGMZA.Circle))

			throw new Error("Argument must be an instance of WPGMZA.Circle");

		

		circle.map = this;

		

		this.circles.push(circle);

		this.dispatchEvent({type: "circleadded", circle: circle});

	}

	

	/**

	 * Removes the specified circle from this map

	 * @return void

	 */

	WPGMZA.Map.prototype.removeCircle = function(circle)

	{

		if(!(circle instanceof WPGMZA.Circle))

			throw new Error("Argument must be an instance of WPGMZA.Circle");

		

		if(circle.map !== this)

			throw new Error("Wrong map error");

		

		circle.map = null;

		

		this.circles.splice(this.circles.indexOf(circle), 1);

		this.dispatchEvent({type: "circleremoved", circle: circle});

	}

	

	WPGMZA.Map.prototype.getCircleByID = function(id)

	{

		for(var i = 0; i < this.circles.length; i++)

		{

			if(this.circles[i].id == id)

				return this.circles[i];

		}

		

		return null;

	}

	

	WPGMZA.Map.prototype.deleteCircleByID = function(id)

	{

		var circle = this.getCircleByID(id);

		

		if(!circle)

			return;

		

		this.deleteCircle(circle);

	}

	

	/**

	 * Nudges the map viewport by the given pixel coordinates

	 * @return void

	 */

	WPGMZA.Map.prototype.nudge = function(x, y)

	{

		var pixels = this.latLngToPixels(this.getCenter());

		

		pixels.x += parseFloat(x);

		pixels.y += parseFloat(y);

		

		if(isNaN(pixels.x) || isNaN(pixels.y))

			throw new Error("Invalid coordinates supplied");

		

		var latLng = this.pixelsToLatLng(pixels);

		

		this.setCenter(latLng);

	}

	

	/**

	 * Triggered when the window resizes

	 * @return void

	 */

	WPGMZA.Map.prototype.onWindowResize = function(event)

	{

		

	}

	

	/**

	 * Listener for when the engine map div is resized

	 * @return void

	 */

	WPGMZA.Map.prototype.onElementResized = function(event)

	{

		

	}

	

	WPGMZA.Map.prototype.onBoundsChanged = function(event)

	{

		// Native events

		this.trigger("boundschanged");

		

		// Google / legacy compatibility events

		this.trigger("bounds_changed");

	}

	

	WPGMZA.Map.prototype.onIdle = function(event)

	{

		$(this.element).trigger("idle");

	}

	

	/*$(document).ready(function() {

		function createMaps()

		{

			// TODO: Test that this works for maps off screen (which borks google)

			$(".wpgmza-map").each(function(index, el) {

				if(!el.wpgmzaMap)

				{

					WPGMZA.runCatchableTask(function() {

						WPGMZA.Map.createInstance(el);

					}, el);

				}

			});

		}

		

		createMaps();

		

		// Call again each second to load AJAX maps

		setInterval(createMaps, 1000);

	});*/

})(jQuery);



// js/v8/maps-engine-dialog.js

/**

 * @namespace WPGMZA

 * @module MapsEngineDialog

 * @requires WPGMZA

 */

(function($) {

	

	WPGMZA.MapsEngineDialog = function(element)

	{

		var self = this;

		

		this.element = element;

		

		if(window.wpgmzaUnbindSaveReminder)

			window.wpgmzaUnbindSaveReminder();

		

		$(element).show();

		$(element).remodal().open();

		

		$(element).find("input:radio").on("change", function(event) {

			

			$("#wpgmza-confirm-engine").prop("disabled", false);

			

		});

		

		$("#wpgmza-confirm-engine").on("click", function(event) {

			

			self.onButtonClicked(event);

			

		});

	}

	

	WPGMZA.MapsEngineDialog.prototype.onButtonClicked = function(event)

	{

		$(event.target).prop("disabled", true);

		

		$.ajax(WPGMZA.ajaxurl, {

			method: "POST",

			data: {

				action: "wpgmza_maps_engine_dialog_set_engine",

				engine: $("[name='wpgmza_maps_engine']:checked").val()

			},

			success: function(response, status, xhr) {

				window.location.reload();

			}

		});

	}

	

	$(window).on("load", function(event) {

		

		var element = $("#wpgmza-maps-engine-dialog");

		

		if(!element.length)

			return;

		

		if(WPGMZA.settings.wpgmza_maps_engine_dialog_done)

			return;

		

		WPGMZA.mapsEngineDialog = new WPGMZA.MapsEngineDialog(element);

		

	});

	

})(jQuery);



// js/v8/marker.js

/**

 * @namespace WPGMZA

 * @module Marker

 * @requires WPGMZA

 */

(function($) {

	/**

	 * Constructor

	 * @param json to load (optional)

	 */

	WPGMZA.Marker = function(row)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "Marker");

		

		this.lat = "36.778261";

		this.lng = "-119.4179323999";

		this.address = "California";

		this.title = null;

		this.description = "";

		this.link = "";

		this.icon = "";

		this.approved = 1;

		this.pic = null;

		

		WPGMZA.MapObject.apply(this, arguments);

		

		if(row && row.heatmap)

			return; // Don't listen for these events on heatmap markers.

		

		this.on("init", function(event) {

			if(row.position)

				this.setPosition(row.position);

			

			if(row.map)

				row.map.addMarker(this);

		});

		

		this.addEventListener("added", function(event) {

			self.onAdded(event);

		});

	}

	

	WPGMZA.Marker.prototype = Object.create(WPGMZA.MapObject.prototype);

	WPGMZA.Marker.prototype.constructor = WPGMZA.Marker;

	

	/**

	 * Gets the constructor. You can use this instead of hard coding the parent class when inheriting,

	 * which is helpful for making subclasses that work with Basic only, Pro, Google, OL or a 

	 * combination of the four.

	 * @return function

	 */

	WPGMZA.Marker.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				if(WPGMZA.isProVersion())

					return WPGMZA.OLProMarker;

				return WPGMZA.OLMarker;

				break;

				

			default:

				if(WPGMZA.isProVersion())

					return WPGMZA.GoogleProMarker;

				return WPGMZA.GoogleMarker;

				break;

		}

	}

	

	WPGMZA.Marker.createInstance = function(row)

	{

		var constructor = WPGMZA.Marker.getConstructor();

		return new constructor(row);

	}

	

	WPGMZA.Marker.ANIMATION_NONE			= "0";

	WPGMZA.Marker.ANIMATION_BOUNCE			= "1";

	WPGMZA.Marker.ANIMATION_DROP			= "2";

	

	WPGMZA.Marker.prototype.onAdded = function(event)

	{

		var self = this;

		

		// this.infoWindow = WPGMZA.InfoWindow.createInstance(this);

		

		this.addEventListener("click", function(event) {

			self.onClick(event);

		});

		

		this.addEventListener("mouseover", function(event) {

			self.onMouseOver(event);

		});

		

		this.addEventListener("select", function(event) {

			self.onSelect(event);

		});

		

		if(this.map.settings.marker == this.id)

			self.trigger("select");

	}

	

	/**

	 * This function will hide the last info the user interacted with

	 * @return void

	 */

	WPGMZA.Marker.prototype.hidePreviousInteractedInfoWindow = function()

	{

		if(!this.map.lastInteractedMarker)

			return;

		

		this.map.lastInteractedMarker.infoWindow.close();

	}

	

	WPGMZA.Marker.prototype.openInfoWindow = function()

	{

		//this.hidePreviousInteractedInfoWindow();

		//this.infoWindow.open(this.map, this);

		//this.map.lastInteractedMarker = this;

	}

	

	WPGMZA.Marker.prototype.onClick = function(event)

	{

		

	}

	

	WPGMZA.Marker.prototype.onSelect = function(event)

	{

		this.openInfoWindow();

	}

	

	WPGMZA.Marker.prototype.onMouseOver = function(event)

	{

		if(this.map.settings.info_window_open_by == WPGMZA.InfoWindow.OPEN_BY_HOVER)

			this.openInfoWindow();

	}

	

	WPGMZA.Marker.prototype.getIcon = function()

	{

		function stripProtocol(url)

		{

			if(typeof url != "string")

				return url;

			

			return url.replace(/^http(s?):/, "");

		}

		

		return stripProtocol(WPGMZA.settings.default_marker_icon);

	}

	

	/**

	 * Gets the position of the marker

	 * @return object

	 */

	WPGMZA.Marker.prototype.getPosition = function()

	{

		return {

			lat: parseFloat(this.lat),

			lng: parseFloat(this.lng)

		};

	}

	

	/**

	 * Sets the position of the marker

	 * @return void

	 */

	WPGMZA.Marker.prototype.setPosition = function(latLng)

	{

		if(latLng instanceof WPGMZA.LatLng)

		{

			this.lat = latLng.lat;

			this.lng = latLng.lng;

		}

		else

		{

			this.lat = parseFloat(latLng.lat);

			this.lng = parseFloat(latLng.lng);

		}

	}

	

	/**

	 * Set the marker animation

	 * @return void

	 */

	WPGMZA.Marker.prototype.getAnimation = function(animation)

	{

		return this.settings.animation;

	}

	

	/**

	 * Set the marker animation

	 * @return void

	 */

	WPGMZA.Marker.prototype.setAnimation = function(animation)

	{

		this.settings.animation = animation;

	}

	

	/**

	 * Get the marker visibility

	 * @return void

	 */

	WPGMZA.Marker.prototype.getVisible = function(visible)

	{

		

	}

	

	/**

	 * Set the marker visibility. This is used by the store locator etc. and is not a setting

	 * @return void

	 */

	WPGMZA.Marker.prototype.setVisible = function(visible)

	{

		if(!visible && this.infoWindow)

			this.infoWindow.close();

	}

	

	WPGMZA.Marker.prototype.setMap = function(map)

	{

		if(!map)

		{

			if(this.map)

				this.map.removeMarker(this);

			

			return;

		}

		

		map.addMarker(this);

	}

	

	WPGMZA.Marker.prototype.getDraggable = function()

	{

		

	}

	

	WPGMZA.Marker.prototype.setDraggable = function(draggable)

	{

		

	}

	

	WPGMZA.Marker.prototype.setOptions = function()

	{

		

	}

	

	WPGMZA.Marker.prototype.panIntoView = function()

	{

		if(!this.map)

			throw new Error("Marker hasn't been added to a map");

		

		this.map.setCenter(this.getPosition());

	}

	

	/**

	 * Returns the marker as a JSON object

	 * @return object

	 */

	WPGMZA.Marker.prototype.toJSON = function()

	{

		var result = WPGMZA.MapObject.prototype.toJSON.call(this);

		var position = this.getPosition();

		

		$.extend(result, {

			lat: position.lat,

			lng: position.lng,

			address: this.address,

			title: this.title,

			description: this.description,

			link: this.link,

			icon: this.icon,

			pic: this.pic,

			approved: this.approved

		});

		

		return result;

	}

	

	

})(jQuery);



// js/v8/modern-store-locator-circle.js

/**

 * @namespace WPGMZA

 * @module ModernStoreLocatorCircle

 * @requires WPGMZA

 */

(function($) {

	

	/**

	 * This module is the modern store locator circle

	 * @constructor

	 */

	WPGMZA.ModernStoreLocatorCircle = function(map_id, settings) {

		var self = this;

		var map;

		

		if(WPGMZA.isProVersion())

			map = this.map = MYMAP[map_id].map;

		else

			map = this.map = MYMAP.map;

		

		this.map_id = map_id;

		this.mapElement = map.element;

		this.mapSize = {

			width:  $(this.mapElement).width(),

			height: $(this.mapElement).height()

		};

			

		this.initCanvasLayer();

		

		this.settings = {

			center: new WPGMZA.LatLng(0, 0),

			radius: 1,

			color: "#63AFF2",

			

			shadowColor: "white",

			shadowBlur: 4,

			

			centerRingRadius: 10,

			centerRingLineWidth: 3,



			numInnerRings: 9,

			innerRingLineWidth: 1,

			innerRingFade: true,

			

			numOuterRings: 7,

			

			ringLineWidth: 1,

			

			mainRingLineWidth: 2,

			

			numSpokes: 6,

			spokesStartAngle: Math.PI / 2,

			

			numRadiusLabels: 6,

			radiusLabelsStartAngle: Math.PI / 2,

			radiusLabelFont: "13px sans-serif",

			

			visible: false

		};

		

		if(settings)

			this.setOptions(settings);

	};

	

	WPGMZA.ModernStoreLocatorCircle.createInstance = function(map, settings) {

		

		if(WPGMZA.settings.engine == "google-maps")

			return new WPGMZA.GoogleModernStoreLocatorCircle(map, settings);

		else

			return new WPGMZA.OLModernStoreLocatorCircle(map, settings);

		

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.initCanvasLayer = function() {

		

	}

	

	WPGMZA.ModernStoreLocatorCircle.prototype.onResize = function(event) { 

		this.draw();

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.onUpdate = function(event) { 

		this.draw();

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.setOptions = function(options) {

		for(var name in options)

		{

			var functionName = "set" + name.substr(0, 1).toUpperCase() + name.substr(1);

			

			if(typeof this[functionName] == "function")

				this[functionName](options[name]);

			else

				this.settings[name] = options[name];

		}

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getResolutionScale = function() {

		return window.devicePixelRatio || 1;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getCenter = function() {

		return this.getPosition();

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.setCenter = function(value) {

		this.setPosition(value);

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getPosition = function() {

		return this.settings.center;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.setPosition = function(position) {

		this.settings.center = position;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getRadius = function() {

		return this.settings.radius;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.setRadius = function(radius) {

		

		if(isNaN(radius))

			throw new Error("Invalid radius");

		

		this.settings.radius = radius;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getVisible = function(visible) {

		return this.settings.visible;

	};

	

	WPGMZA.ModernStoreLocatorCircle.prototype.setVisible = function(visible) {

		this.settings.visible = visible;

	};

	

	/**

	 * This function transforms a km radius into canvas space

	 * @return number

	 */

	WPGMZA.ModernStoreLocatorCircle.prototype.getTransformedRadius = function(km)

	{

		throw new Error("Abstract function called");

	}

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getContext = function(type)

	{

		throw new Error("Abstract function called");

	}

	

	WPGMZA.ModernStoreLocatorCircle.prototype.getCanvasDimensions = function()

	{

		throw new Error("Abstract function called");

	}

	

	WPGMZA.ModernStoreLocatorCircle.prototype.validateSettings = function()

	{

		if(!WPGMZA.isHexColorString(this.settings.color))

			this.settings.color = "#63AFF2";

	}

	

	WPGMZA.ModernStoreLocatorCircle.prototype.draw = function() {

		

		this.validateSettings();

		

		var settings = this.settings;

		var canvasDimensions = this.getCanvasDimensions();

		

        var canvasWidth = canvasDimensions.width;

        var canvasHeight = canvasDimensions.height;

		

		var map = this.map;

		var resolutionScale = this.getResolutionScale();

		

		context = this.getContext("2d");

        context.clearRect(0, 0, canvasWidth, canvasHeight);



		if(!settings.visible)

			return;

		

		context.shadowColor = settings.shadowColor;

		context.shadowBlur = settings.shadowBlur;

		

		// NB: 2018/02/13 - Left this here in case it needs to be calibrated more accurately

		/*if(!this.testCircle)

		{

			this.testCircle = new google.maps.Circle({

				strokeColor: "#ff0000",

				strokeOpacity: 0.5,

				strokeWeight: 3,

				map: this.map,

				center: this.settings.center

			});

		}

		

		this.testCircle.setCenter(settings.center);

		this.testCircle.setRadius(settings.radius * 1000);*/

		

        // Reset transform

        context.setTransform(1, 0, 0, 1, 0, 0);

        

        var scale = this.getScale();

        context.scale(scale, scale);



		// Translate by world origin

		var offset = this.getWorldOriginOffset();

		context.translate(offset.x, offset.y);



        // Get center and project to pixel space

		var center = new WPGMZA.LatLng(this.settings.center);

		var worldPoint = this.getCenterPixels();

		

		var rgba = WPGMZA.hexToRgba(settings.color);

		var ringSpacing = this.getTransformedRadius(settings.radius) / (settings.numInnerRings + 1);

		

		// TODO: Implement gradients for color and opacity

		

		// Inside circle (fixed?)

        context.strokeStyle = settings.color;

		context.lineWidth = (1 / scale) * settings.centerRingLineWidth;

		

		context.beginPath();

		context.arc(

			worldPoint.x, 

			worldPoint.y, 

			this.getTransformedRadius(settings.centerRingRadius) / scale, 0, 2 * Math.PI

		);

		context.stroke();

		context.closePath();

		

		// Spokes

		var radius = this.getTransformedRadius(settings.radius) + (ringSpacing * settings.numOuterRings) + 1;

		var grad = context.createRadialGradient(0, 0, 0, 0, 0, radius);

		var rgba = WPGMZA.hexToRgba(settings.color);

		var start = WPGMZA.rgbaToString(rgba), end;

		var spokeAngle;

		

		rgba.a = 0;

		end = WPGMZA.rgbaToString(rgba);

		

		grad.addColorStop(0, start);

		grad.addColorStop(1, end);

		

		context.save();

		

		context.translate(worldPoint.x, worldPoint.y);

		context.strokeStyle = grad;

		context.lineWidth = 2 / scale;

		

		for(var i = 0; i < settings.numSpokes; i++)

		{

			spokeAngle = settings.spokesStartAngle + (Math.PI * 2) * (i / settings.numSpokes);

			

			x = Math.cos(spokeAngle) * radius;

			y = Math.sin(spokeAngle) * radius;

			

			context.setLineDash([2 / scale, 15 / scale]);

			

			context.beginPath();

			context.moveTo(0, 0);

			context.lineTo(x, y);

			context.stroke();

		}

		

		context.setLineDash([]);

		

		context.restore();

		

		// Inner ringlets

		context.lineWidth = (1 / scale) * settings.innerRingLineWidth;

		

		for(var i = 1; i <= settings.numInnerRings; i++)

		{

			var radius = i * ringSpacing;

			

			if(settings.innerRingFade)

				rgba.a = 1 - (i - 1) / settings.numInnerRings;

			

			context.strokeStyle = WPGMZA.rgbaToString(rgba);

			

			context.beginPath();

			context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);

			context.stroke();

			context.closePath();

		}

		

		// Main circle

		context.strokeStyle = settings.color;

		context.lineWidth = (1 / scale) * settings.centerRingLineWidth;

		

		context.beginPath();

		context.arc(worldPoint.x, worldPoint.y, this.getTransformedRadius(settings.radius), 0, 2 * Math.PI);

		context.stroke();

		context.closePath();

		

		// Outer ringlets

		var radius = radius + ringSpacing;

		for(var i = 0; i < settings.numOuterRings; i++)

		{

			if(settings.innerRingFade)

				rgba.a = 1 - i / settings.numOuterRings;

			

			context.strokeStyle = WPGMZA.rgbaToString(rgba);

			

			context.beginPath();

			context.arc(worldPoint.x, worldPoint.y, radius, 0, 2 * Math.PI);

			context.stroke();

			context.closePath();

		

			radius += ringSpacing;

		}

		

		// Text

		if(settings.numRadiusLabels > 0)

		{

			var m;

			var radius = this.getTransformedRadius(settings.radius);

			var clipRadius = (12 * 1.1) / scale;

			var x, y;

			

			if(m = settings.radiusLabelFont.match(/(\d+)px/))

				clipRadius = (parseInt(m[1]) / 2 * 1.1) / scale;

			

			context.font = settings.radiusLabelFont;

			context.textAlign = "center";

			context.textBaseline = "middle";

			context.fillStyle = settings.color;

			

			context.save();

			

			context.translate(worldPoint.x, worldPoint.y)

			

			for(var i = 0; i < settings.numRadiusLabels; i++)

			{

				var spokeAngle = settings.radiusLabelsStartAngle + (Math.PI * 2) * (i / settings.numRadiusLabels);

				var textAngle = spokeAngle + Math.PI / 2;

				var text = settings.radiusString;

				var width;

				

				if(Math.sin(spokeAngle) > 0)

					textAngle -= Math.PI;

				

				x = Math.cos(spokeAngle) * radius;

				y = Math.sin(spokeAngle) * radius;

				

				context.save();

				

				context.translate(x, y);

				

				context.rotate(textAngle);

				context.scale(1 / scale, 1 / scale);

				

				width = context.measureText(text).width;

				height = width / 2;

				context.clearRect(-width, -height, 2 * width, 2 * height);

				

				context.fillText(settings.radiusString, 0, 0);

				

				context.restore();

			}

			

			context.restore();

		}

	}

	

})(jQuery);



// js/v8/modern-store-locator.js

/**

 * @namespace WPGMZA

 * @module ModernStoreLocator

 * @requires WPGMZA

 */

(function($) {

	

	/**

	 * The new modern look store locator. It takes the elements

	 * from the default look and moves them into the map, wrapping

	 * in a new element so we can apply new styles.

	 * @return Object

	 */

	WPGMZA.ModernStoreLocator = function(map_id)

	{

		var self = this;

		var original;

		

		WPGMZA.assertInstanceOf(this, "ModernStoreLocator");

		

		if(WPGMZA.isProVersion())

			original = $(".wpgmza_sl_search_button[mid='" + map_id + "']").closest(".wpgmza_sl_main_div");

		else

			original = $(".wpgmza_sl_search_button").closest(".wpgmza_sl_main_div");

		

		if(!original.length)

			return;

		

		// Build / re-arrange elements

		this.element = $("<div class='wpgmza-modern-store-locator'><div class='wpgmza-inner wpgmza-modern-hover-opaque'/></div>")[0];

		

		var inner = $(this.element).find(".wpgmza-inner");

		

		var titleSearch = $(original).find("[id='nameInput_" + map_id + "']");

		if(titleSearch.length)

		{

			var placeholder = wpgmaps_localize[map_id].other_settings.store_locator_name_string;

			if(placeholder && placeholder.length)

				titleSearch.attr("placeholder", placeholder);

			inner.append(titleSearch);

		}

		

		var addressInput;

		if(WPGMZA.isProVersion())

			addressInput = $(original).find(".addressInput");

		else

			addressInput = $(original).find("#addressInput");

		

		if(wpgmaps_localize[map_id].other_settings.store_locator_query_string && wpgmaps_localize[map_id].other_settings.store_locator_query_string.length)

			addressInput.attr("placeholder", wpgmaps_localize[map_id].other_settings.store_locator_query_string);

		

		inner.append(addressInput);

		

		inner.append($(original).find("select.wpgmza_sl_radius_select"));

		// inner.append($(original).find(".wpgmza_filter_select_" + map_id));

		

		// Buttons

		this.searchButton = $(original).find( ".wpgmza_sl_search_button" );

		inner.append(this.searchButton);

		

		this.resetButton = $(original).find( ".wpgmza_sl_reset_button_div" );

		inner.append(this.resetButton);

		

		this.resetButton.on("click", function(event) {

			resetLocations(map_id);

		});

		

		this.resetButton.hide();

		

		if(WPGMZA.isProVersion())

		{

			this.searchButton.on("click", function(event) {

				if($("addressInput_" + map_id).val() == 0)

					return;

				

				self.searchButton.hide();

				self.resetButton.show();

			});

			this.resetButton.on("click", function(event) {

				self.resetButton.hide();

				self.searchButton.show();

			});

		}

		

		// Distance type

		inner.append($("#wpgmza_distance_type_" + map_id));

		

		// Categories

		var container = $(original).find(".wpgmza_cat_checkbox_holder");

		var ul = $(container).children("ul");

		var items = $(container).find("li");

		var numCategories = 0;

		

		//$(items).find("ul").remove();

		//$(ul).append(items);

		

		var icons = [];

		

		items.each(function(index, el) {

			var id = $(el).attr("class").match(/\d+/);

			

			for(var category_id in wpgmza_category_data) {

				

				if(id == category_id) {

					var src = wpgmza_category_data[category_id].image;

					var icon = $('<div class="wpgmza-chip-icon"/>');

					

					icon.css({

						"background-image": "url('" + src + "')",

						"width": $("#wpgmza_cat_checkbox_" + category_id + " + label").height() + "px"

					});

					icons.push(icon);

					

                    if(src != null && src != ""){

					   //$(el).find("label").prepend(icon);

                       $("#wpgmza_cat_checkbox_" + category_id + " + label").prepend(icon);

                    }

					

					numCategories++;

					

					break;

				}

				

			}

		});



        $(this.element).append(container);



		

		if(numCategories) {

			this.optionsButton = $('<span class="wpgmza_store_locator_options_button"><i class="fas fa-list"></i></span>');

			$(this.searchButton).before(this.optionsButton);

		}

		

		setInterval(function() {

			

			icons.forEach(function(icon) {

				var height = $(icon).height();

				$(icon).css({"width": height + "px"});

				$(icon).closest("label").css({"padding-left": height + 8 + "px"});

			});

			

			$(container).css("width", $(self.element).find(".wpgmza-inner").outerWidth() + "px");

			

		}, 1000);

		

		$(this.element).find(".wpgmza_store_locator_options_button").on("click", function(event) {

			

			if(container.hasClass("wpgmza-open"))

				container.removeClass("wpgmza-open");

			else

				container.addClass("wpgmza-open");

			

		});

		

		// Remove original element

		$(original).remove();

		

		// Event listeners

		$(this.element).find("input, select").on("focus", function() {

			$(inner).addClass("active");

		});

		

		$(this.element).find("input, select").on("blur", function() {

			$(inner).removeClass("active");

		});

	}

	

	WPGMZA.ModernStoreLocator.createInstance = function(map_id)

	{

		if(WPGMZA.settings.engine == "google-maps")

			return new WPGMZA.GoogleModernStoreLocator(map_id);

		else

			return new WPGMZA.OLModernStoreLocator(map_id);

	}

	

})(jQuery);



// js/v8/polygon.js

/**

 * @namespace WPGMZA

 * @module Polygon

 * @requires WPGMZA.MapObject

 */

(function($) {

	WPGMZA.Polygon = function(row, enginePolygon)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "Polygon");

		

		this.paths = null;

		this.title = null;

		this.name = null;

		this.link = null;

		

		WPGMZA.MapObject.apply(this, arguments);

	}

	

	WPGMZA.Polygon.prototype = Object.create(WPGMZA.MapObject.prototype);

	WPGMZA.Polygon.prototype.constructor = WPGMZA.Polygon;

	

	WPGMZA.Polygon.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				if(WPGMZA.isProVersion())

					return WPGMZA.OLProPolygon;

				return WPGMZA.OLPolygon;

				break;

			

			default:

				if(WPGMZA.isProVersion())

					return WPGMZA.GoogleProPolygon;

				return WPGMZA.GooglePolygon;

				break;

		}

	}

	

	WPGMZA.Polygon.createInstance = function(row, engineObject)

	{

		var constructor = WPGMZA.Polygon.getConstructor();

		return new constructor(row, engineObject);

	}

	

	WPGMZA.Polygon.prototype.toJSON = function()

	{

		var result = WPGMZA.MapObject.prototype.toJSON.call(this);

		

		$.extend(result, {

			name:		this.name,

			title:		this.title,

			link:		this.link,

		});

	

		return result;

	}

	

})(jQuery);



// js/v8/polyline.js

/**

 * @namespace WPGMZA

 * @module Polyline

 * @requires WPGMZA.MapObject

 */

(function($) {

	WPGMZA.Polyline = function(row, googlePolyline)

	{

		var self = this;

		

		WPGMZA.assertInstanceOf(this, "Polyline");

		

		this.title = null;

		

		WPGMZA.MapObject.apply(this, arguments);

	}

	

	WPGMZA.Polyline.prototype = Object.create(WPGMZA.MapObject.prototype);

	WPGMZA.Polyline.prototype.constructor = WPGMZA.Polyline;

	

	WPGMZA.Polyline.getConstructor = function()

	{

		switch(WPGMZA.settings.engine)

		{

			case "open-layers":

				return WPGMZA.OLPolyline;

				break;

			

			default:

				return WPGMZA.GooglePolyline;

				break;

		}

	}

	

	WPGMZA.Polyline.createInstance = function(row, engineObject)

	{

		var constructor = WPGMZA.Polyline.getConstructor();

		return new constructor(row, engineObject);

	}

	

	WPGMZA.Polyline.prototype.getPoints = function()

	{

		return this.toJSON().points;

	}

	

	WPGMZA.Polyline.prototype.toJSON = function()

	{

		var result = WPGMZA.MapObject.prototype.toJSON.call(this);

		

		result.title = this.title;

		

		return result;

	}

	

	

})(jQuery);



// js/v8/rest-api.js

/**

 * @module WPGMZA.RestAPI

 * @namespace WPGMZA

 * @requires WPGMZA

 * @summary Wrapped for the rest API

 */

(function($) {

	

	WPGMZA.RestAPI = function()

	{

		WPGMZA.RestAPI.URL = WPGMZA.resturl;

	}

	

	WPGMZA.RestAPI.createInstance = function() 

	{

		return new WPGMZA.RestAPI();

	}

	

	WPGMZA.RestAPI.prototype.call = function(route, params)

	{

		if(typeof route != "string" || !route.match(/^\//))

			throw new Error("Invalid route");

		

		$.ajax(WPGMZA.RestAPI.URL + route, params);

	}

	

})(jQuery);



// js/v8/version.js

/**

 * @namespace WPGMZA

 * @module Version

 * @requires WPGMZA

 */

(function($) {



	function isPositiveInteger(x) {

		// http://stackoverflow.com/a/1019526/11236

		return /^\d+$/.test(x);

	}



	function validateParts(parts) {

		for (var i = 0; i < parts.length; ++i) {

			if (!isPositiveInteger(parts[i])) {

				return false;

			}

		}

		return true;

	}

	

	WPGMZA.Version = function()

	{

		

	}

	

	/**

	 * Compare two software version numbers (e.g. 1.7.1)

	 * Returns:

	 *

	 *  0 if they're identical

	 *  negative if v1 < v2

	 *  positive if v1 > v2

	 *  NaN if they in the wrong format

	 *

	 *  "Unit tests": http://jsfiddle.net/ripper234/Xv9WL/28/

	 *

	 *  Taken from http://stackoverflow.com/a/6832721/11236

	 */

	WPGMZA.Version.compare = function(v1, v2)

	{

		var v1parts = v1.split('.');

		var v2parts = v2.split('.');



		// First, validate both numbers are true version numbers

		if (!validateParts(v1parts) || !validateParts(v2parts)) {

			return NaN;

		}



		for (var i = 0; i < v1parts.length; ++i) {

			if (v2parts.length === i) {

				return 1;

			}



			if (v1parts[i] === v2parts[i]) {

				continue;

			}

			if (v1parts[i] > v2parts[i]) {

				return 1;

			}

			return -1;

		}



		if (v1parts.length != v2parts.length) {

			return -1;

		}



		return 0;

	}



})(jQuery);



// js/v8/compatibility/google-ui-compatibility.js

/**

 * @namespace WPGMZA

 * @module GoogleUICompatibility

 * @requires WPGMZA

 */ 

(function($) {

	

	WPGMZA.GoogleUICompatibility = function()

	{

		var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&

				   navigator.userAgent &&

				   navigator.userAgent.indexOf('CriOS') == -1 &&

				   navigator.userAgent.indexOf('FxiOS') == -1;

		

		if(!isSafari)

		{

			var style = $("<style id='wpgmza-google-ui-compatiblity-fix'/>");

			style.html(".wpgmza_map img:not(button img) { padding:0 !important; }");

			$(document.head).append(style);

		}

	}

	

	WPGMZA.googleUICompatibility = new WPGMZA.GoogleUICompatibility();

	

})(jQuery);



// js/v8/google-maps/google-circle.js

/**

 * @namespace WPGMZA

 * @module GoogleCircle

 * @requires WPGMZA.Circle

 */

(function($) {

	

	WPGMZA.GoogleCircle = function(options, googleCircle)

	{

		var self = this;

		

		WPGMZA.Circle.call(this, options, googleCircle);

		

		if(googleCircle)

		{

			this.googleCircle = googleCircle;

		}

		else

		{

			this.googleCircle = new google.maps.Circle();

			this.googleCircle.wpgmzaCircle = this;

		}

		

		google.maps.event.addListener(this.googleCircle, "click", function() {

			self.dispatchEvent({type: "click"});

		});

		

		if(options)

		{

			var googleOptions = {};

			

			googleOptions = $.extend({}, options);

			delete googleOptions.map;

			delete googleOptions.center;

			

			if(options.center)

				googleOptions.center = new google.maps.LatLng({

					lat: options.center.lat,

					lng: options.center.lng

				});

			

			this.googleCircle.setOptions(googleOptions);

			

			if(options.map)

				options.map.addCircle(this);

		}

	}

	

	WPGMZA.GoogleCircle.prototype = Object.create(WPGMZA.Circle.prototype);

	WPGMZA.GoogleCircle.prototype.constructor = WPGMZA.GoogleCircle;

	

})(jQuery);



// js/v8/google-maps/google-geocoder.js

/**

 * @namespace WPGMZA

 * @module GoogleGeocoder

 * @requires WPGMZA.Geocoder

 */

(function($) {

	

	WPGMZA.GoogleGeocoder = function()

	{

		

	}

	

	WPGMZA.GoogleGeocoder.prototype = Object.create(WPGMZA.Geocoder.prototype);

	WPGMZA.GoogleGeocoder.prototype.constructor = WPGMZA.GoogleGeocoder;

	

	WPGMZA.GoogleGeocoder.prototype.getLatLngFromAddress = function(options, callback)

	{

		if(!options || !options.address)

			throw new Error("No address specified");

		

		if(WPGMZA.isLatLngString(options.address))

			return WPGMZA.Geocoder.prototype.getLatLngFromAddress.call(this, options, callback);

		

		if(options.country)

			options.componentRestrictions = {

				country: options.country

			};

		

		var geocoder = new google.maps.Geocoder();

		

		geocoder.geocode(options, function(results, status) {

			if(status == google.maps.GeocoderStatus.OK)

			{

				var location = results[0].geometry.location;

				var latLng = {

					lat: location.lat(),

					lng: location.lng()

				};

				

				var results = [

					{

						geometry: {

							location: latLng

						},

						latLng: latLng,

						lat: latLng.lat,

						lng: latLng.lng

					}

				];

				

				callback(results, WPGMZA.Geocoder.SUCCESS);

			}

			else

			{

				var nativeStatus = WPGMZA.Geocoder.FAIL;

				

				if(status == google.maps.GeocoderStatus.ZERO_RESULTS)

					nativeStatus = WPGMZA.Geocoder.ZERO_RESULTS;

				

				callback(null, nativeStatus);

			}

		});

	}

	

	WPGMZA.GoogleGeocoder.prototype.getAddressFromLatLng = function(options, callback)

	{

		if(!options || !options.latLng)

			throw new Error("No latLng specified");

		

		var latLng = new WPGMZA.LatLng(options.latLng);

		var geocoder = new google.maps.Geocoder();

		

		var options = $.extend(options, {

			location: {

				lat: latLng.lat,

				lng: latLng.lng

			}

		});

		delete options.latLng;

		

		geocoder.geocode(options, function(results, status) {

			

			if(status !== "OK")

				callback(null, WPGMZA.Geocoder.FAIL);

			

			if(!results || !results.length)

				callback([], WPGMZA.Geocoder.NO_RESULTS);

			

			callback([results[0].formatted_address], WPGMZA.Geocoder.SUCCESS);

			

		});

	}

	

})(jQuery);



// js/v8/google-maps/google-info-window.js

/**

 * @namespace WPGMZA

 * @module GoogleInfoWindow

 * @requires WPGMZA.InfoWindow

 * @pro-requires WPGMZA.ProInfoWindow

 */

(function($) {

	

	var Parent;

	

	WPGMZA.GoogleInfoWindow = function(mapObject)

	{

		Parent.call(this, mapObject);

		

		this.setMapObject(mapObject);

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProInfoWindow;

	else

		Parent = WPGMZA.InfoWindow;

	

	WPGMZA.GoogleInfoWindow.prototype = Object.create(Parent.prototype);

	WPGMZA.GoogleInfoWindow.prototype.constructor = WPGMZA.GoogleInfoWindow;

	

	WPGMZA.GoogleInfoWindow.prototype.setMapObject = function(mapObject)

	{

		if(mapObject instanceof WPGMZA.Marker)

			this.googleObject = mapObject.googleMarker;

		else if(mapObject instanceof WPGMZA.Polygon)

			this.googleObject = mapObject.googlePolygon;

		else if(mapObject instanceof WPGMZA.Polyline)

			this.googleObject = mapObject.googlePolyline;

	}

	

	WPGMZA.GoogleInfoWindow.prototype.createGoogleInfoWindow = function()

	{

		if(this.googleInfoWindow)

			return;

		

		this.googleInfoWindow = new google.maps.InfoWindow();

	}

	

	/**

	 * Opens the info window

	 * @return boolean FALSE if the info window should not & will not open, TRUE if it will

	 */

	WPGMZA.GoogleInfoWindow.prototype.open = function(map, mapObject)

	{

		var self = this;

		

		if(!Parent.prototype.open.call(this, map, mapObject))

			return false;

		

		this.createGoogleInfoWindow();

		this.setMapObject(mapObject);

		

		this.googleInfoWindow.open(

			this.mapObject.map.googleMap,

			this.googleObject

		);

		

		if(this.content)

			this.googleInfoWindow.setContent(this.content);

		

		//this.

		

		/*this.getContent(function(html) {

			

			// Wrap HTML with unique ID

			var guid = WPGMZA.guid();

			var html = "<div id='" + guid + "'>" + html + "</div>";

			var div, intervalID;

			

			self.googleInfoWindow.setContent(html);

			self.googleInfoWindow.open(

				self.mapObject.map.googleMap,

				self.googleObject

			);

			

			intervalID = setInterval(function(event) {

				

				div = $("#" + guid);

				

				if(div.find(".gm-style-iw").length)

				{

					div[0].wpgmzaMapObject = self.mapObject;

					

					self.dispatchEvent("infowindowopen");

					div.trigger("infowindowopen");

					clearInterval(intervalID);

				}

				

			}, 50);

			

		});*/

		

		return true;

	}

	

	WPGMZA.GoogleInfoWindow.prototype.close = function()

	{

		if(!this.googleInfoWindow)

			return;

		

		WPGMZA.InfoWindow.prototype.close.call(this);

		

		this.googleInfoWindow.close();

	}

	

	WPGMZA.GoogleInfoWindow.prototype.setContent = function(html)

	{

		Parent.prototype.setContent.call(this, html);

		

		this.content = html;

		

		this.createGoogleInfoWindow();

		

		this.googleInfoWindow.setContent(html);

	}

	

	WPGMZA.GoogleInfoWindow.prototype.setOptions = function(options)

	{

		Parent.prototype.setOptions.call(this, options);

		

		this.createGoogleInfoWindow();

		

		this.googleInfoWindow.setOptions(options);

	}

	

})(jQuery);



// js/v8/google-maps/google-map.js

/**

 * @namespace WPGMZA

 * @module GoogleMap

 * @requires WPGMZA.Map

 * @pro-requires WPGMZA.ProMap

 */

(function($) {

	var Parent;

	

	/**

	 * Constructor

	 * @param element to contain the map

	 */

	WPGMZA.GoogleMap = function(element, options)

	{

		var self = this;

		

		Parent.call(this, element, options);

		

		if(!window.google)

		{

			var status = WPGMZA.googleAPIStatus;

			var message = "Google API not loaded";

			

			if(status && status.message)

				message += " - " + status.message;

			

			if(status.code == "USER_CONSENT_NOT_GIVEN")

			{

				console.log(WPGMZA.api_consent_html);

				return;

			}

			

			throw new Error(message);

		}

		

		this.loadGoogleMap();

		

		if(options)

			this.setOptions(options);



		google.maps.event.addListener(this.googleMap, "click", function(event) {

			var wpgmzaEvent = new WPGMZA.Event("click");

			wpgmzaEvent.latLng = {

				lat: event.latLng.lat(),

				lng: event.latLng.lng()

			};

			self.dispatchEvent(wpgmzaEvent);

		});

		

		google.maps.event.addListener(this.googleMap, "rightclick", function(event) {

			var wpgmzaEvent = new WPGMZA.Event("rightclick");

			wpgmzaEvent.latLng = {

				lat: event.latLng.lat(),

				lng: event.latLng.lng()

			};

			self.dispatchEvent(wpgmzaEvent);

		});

		

		google.maps.event.addListener(this.googleMap, "dragend", function(event) {

			self.dispatchEvent("dragend");

		});

		

		google.maps.event.addListener(this.googleMap, "zoom_changed", function(event) {

			self.dispatchEvent("zoom_changed");

			self.dispatchEvent("zoomchanged");

		});

		

		// Idle event

		google.maps.event.addListener(this.googleMap, "idle", function(event) {

			self.onIdle(event);

		});

		

		// Dispatch event

		if(!WPGMZA.isProVersion())

		{

			this.dispatchEvent("created");

			WPGMZA.events.dispatchEvent({type: "mapcreated", map: this});

		}

	}

	

	// If we're running the Pro version, inherit from ProMap, otherwise, inherit from Map

	if(WPGMZA.isProVersion())

	{

		Parent = WPGMZA.ProMap;

		WPGMZA.GoogleMap.prototype = Object.create(WPGMZA.ProMap.prototype);

	}

	else

	{

		Parent = WPGMZA.Map;

		WPGMZA.GoogleMap.prototype = Object.create(WPGMZA.Map.prototype);

	}

	WPGMZA.GoogleMap.prototype.constructor = WPGMZA.GoogleMap;

	

	/**

	 * Creates the Google Maps map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.loadGoogleMap = function()

	{

		var self = this;

		var options = this.settings.toGoogleMapsOptions();

		

		this.googleMap = new google.maps.Map(this.engineElement, options);

		google.maps.event.addListener(this.googleMap, "bounds_changed", function() { 

			self.onBoundsChanged();

		});

		

		if(this.settings.bicycle == 1)

			this.enableBicycleLayer(true);

		if(this.settings.traffic == 1)

			this.enableTrafficLayer(true);

		if(this.settings.transport == 1)

			this.enablePublicTransportLayer(true);

		this.showPointsOfInterest(this.settings.show_points_of_interest);

		

		// Move the loading wheel into the map element (it has to live outside in the HTML file because it'll be overwritten by Google otherwise)

		$(this.engineElement).append($(this.element).find(".wpgmza-loader"));

	}

	

	WPGMZA.GoogleMap.prototype.setOptions = function(options)

	{

		Parent.prototype.setOptions.call(this, options);

		

		this.googleMap.setOptions(this.settings.toGoogleMapsOptions());

		

		var clone = $.extend({}, options);

		if(clone.center instanceof WPGMZA.LatLng || typeof clone.center == "object")

			clone.center = {

				lat: parseFloat(clone.center.lat),

				lng: parseFloat(clone.center.lng)

			};

		

		this.googleMap.setOptions(clone);

	}

	

	/**

	 * Adds the specified marker to this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.addMarker = function(marker)

	{

		marker.googleMarker.setMap(this.googleMap);

		

		Parent.prototype.addMarker.call(this, marker);

	}

	

	/**

	 * Removes the specified marker from this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.removeMarker = function(marker)

	{

		marker.googleMarker.setMap(null);

		

		Parent.prototype.removeMarker.call(this, marker);

	}

	

	/**

	 * Adds the specified polygon to this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.addPolygon = function(polygon)

	{

		polygon.googlePolygon.setMap(this.googleMap);

		

		Parent.prototype.addPolygon.call(this, polygon);

	}

	

	/**

	 * Removes the specified polygon from this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.removePolygon = function(polygon)

	{

		polygon.googlePolygon.setMap(null);

		

		Parent.prototype.removePolygon.call(this, polygon);

	}

	

	/**

	 * Adds the specified polyline to this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.addPolyline = function(polyline)

	{

		polyline.googlePolyline.setMap(this.googleMap);

		

		Parent.prototype.addPolyline.call(this, polyline);

	}

	

	/**

	 * Removes the specified polygon from this map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.removePolyline = function(polyline)

	{

		polyline.googlePolyline.setMap(null);

		

		Parent.prototype.removePolyline.call(this, polyline);

	}

	

	WPGMZA.GoogleMap.prototype.addCircle = function(circle)

	{

		circle.googleCircle.setMap(this.googleMap);

		

		Parent.prototype.addCircle.call(this, circle);

	}

	

	WPGMZA.GoogleMap.prototype.removeCircle = function(circle)

	{

		circle.googleCircle.setMap(null);

		

		Parent.prototype.removeCircle.call(this, circle);

	}

	

	/**

	 * Delegate for google maps getCenter

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.getCenter = function()

	{

		var latLng = this.googleMap.getCenter();

		

		return {

			lat: latLng.lat(),

			lng: latLng.lng()

		};

	}

	

	/**

	 * Delegate for google maps setCenter

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.setCenter = function(latLng)

	{

		WPGMZA.Map.prototype.setCenter.call(this, latLng);

		

		if(latLng instanceof WPGMZA.LatLng)

			this.googleMap.setCenter({

				lat: latLng.lat,

				lng: latLng.lng

			});

		else

			this.googleMap.setCenter(latLng);

	}

	

	/**

	 * Delegate for google maps setPan

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.panTo = function(latLng)

	{

		if(latLng instanceof WPGMZA.LatLng)

			this.googleMap.panTo({

				lat: latLng.lat,

				lng: latLng.lng

			});

		else

			this.googleMap.panTo(latLng);

	}

	

	/**

	 * Delegate for google maps getCenter

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.getZoom = function()

	{

		return this.googleMap.getZoom();

	}

	

	/**

	 * Delegate for google maps getZoom

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.setZoom = function(value)

	{

		if(isNaN(value))

			throw new Error("Value must not be NaN");

		

		return this.googleMap.setZoom(value);

	}

	

	/**

	 * Gets the bounds

	 * @return object

	 */

	WPGMZA.GoogleMap.prototype.getBounds = function()

	{

		var bounds = this.googleMap.getBounds();

		var northEast = bounds.getNorthEast();

		var southWest = bounds.getSouthWest();

		

		return {

			topLeft: {

				lat: northEast.lat(),

				lng: southWest.lng()

			},

			bottomRight: {

				lat: southWest.lat(),

				lng: northEast.lng()

			}

		};

	}

	

	/**

	 * Fit to given boundaries

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.fitBounds = function(southWest, northEast)

	{

		if(southWest instanceof WPGMZA.LatLng)

			southWest = {lat: southWest.lat, lng: southWest.lng};

		if(northEast instanceof WPGMZA.LatLng)

			northEast = {lat: northEast.lat, lng: northEast.lng};

		

		this.googleMap.fitBounds(southWest, northEast);

	}

	

	/**

	 * Fit the map boundaries to visible markers

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.fitBoundsToVisibleMarkers = function()

	{

		var bounds = new google.maps.LatLngBounds();

		for(var i = 0; i < this.markers.length; i++)

		{

			if(markers[i].getVisible())

				bounds.extend(markers[i].getPosition());

		}

		this.googleMap.fitBounds(bounds);

	}

	

	/**

	 * Enables / disables the bicycle layer

	 * @param enable boolean, enable or not

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.enableBicycleLayer = function(enable)

	{

		if(!this.bicycleLayer)

			this.bicycleLayer = new google.maps.BicyclingLayer();

		

		this.bicycleLayer.setMap(

			enable ? this.googleMap : null

		);

	}

	

	/**

	 * Enables / disables the bicycle layer

	 * @param enable boolean, enable or not

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.enableTrafficLayer = function(enable)

	{

		if(!this.trafficLayer)

			this.trafficLayer = new google.maps.TrafficLayer();

		

		this.trafficLayer.setMap(

			enable ? this.googleMap : null

		);

	}

	

	/**

	 * Enables / disables the bicycle layer

	 * @param enable boolean, enable or not

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.enablePublicTransportLayer = function(enable)

	{

		if(!this.publicTransportLayer)

			this.publicTransportLayer = new google.maps.TransitLayer();

		

		this.publicTransportLayer.setMap(

			enable ? this.googleMap : null

		);

	}

	

	/**

	 * Shows / hides points of interest

	 * @param show boolean, enable or not

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.showPointsOfInterest = function(show)

	{

		// TODO: This will bug the front end because there is textarea with theme data

		var text = $("textarea[name='theme_data']").val();

		

		if(!text)

			return;

		

		var styles = JSON.parse(text);

		

		styles.push({

			featureType: "poi",

			stylers: [

				{

					visibility: (show ? "on" : "off")

				}

			]

		});

		

		this.googleMap.setOptions({styles: styles});

	}

	

	/**

	 * Gets the min zoom of the map

	 * @return int

	 */

	WPGMZA.GoogleMap.prototype.getMinZoom = function()

	{

		return parseInt(this.settings.min_zoom);

	}

	

	/**

	 * Sets the min zoom of the map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.setMinZoom = function(value)

	{

		this.googleMap.setOptions({

			minZoom: value,

			maxZoom: this.getMaxZoom()

		});

	}

	

	/**

	 * Gets the min zoom of the map

	 * @return int

	 */

	WPGMZA.GoogleMap.prototype.getMaxZoom = function()

	{

		return parseInt(this.settings.max_zoom);

	}

	

	/**

	 * Sets the min zoom of the map

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.setMaxZoom = function(value)

	{

		this.googleMap.setOptions({

			minZoom: this.getMinZoom(),

			maxZoom: value

		});

	}

	

	WPGMZA.GoogleMap.prototype.latLngToPixels = function(latLng)

	{

		var map = this.googleMap;

		var nativeLatLng = new google.maps.LatLng({

			lat: parseFloat(latLng.lat),

			lng: parseFloat(latLng.lng)

		});

		var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());

		var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());

		var scale = Math.pow(2, map.getZoom());

		var worldPoint = map.getProjection().fromLatLngToPoint(nativeLatLng);

		return {

			x: (worldPoint.x - bottomLeft.x) * scale, 

			y: (worldPoint.y - topRight.y) * scale

		};

	}

	

	WPGMZA.GoogleMap.prototype.pixelsToLatLng = function(x, y)

	{

		if(y == undefined)

		{

			if("x" in x && "y" in x)

			{

				y = x.y;

				x = x.x;

			}

			else

				console.warn("Y coordinate undefined in pixelsToLatLng (did you mean to pass 2 arguments?)");

		}

		

		var map = this.googleMap;

		var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());

		var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());

		var scale = Math.pow(2, map.getZoom());

		var worldPoint = new google.maps.Point(x / scale + bottomLeft.x, y / scale + topRight.y);

		var latLng = map.getProjection().fromPointToLatLng(worldPoint);

		return {

			lat: latLng.lat(),

			lng: latLng.lng()

		};

	}

	

	/**

	 * Handle the map element resizing

	 * @return void

	 */

	WPGMZA.GoogleMap.prototype.onElementResized = function(event)

	{

		if(!this.googleMap)

			return;

		google.maps.event.trigger(this.googleMap, "resize");

	}

	

})(jQuery);



// js/v8/google-maps/google-marker.js

/**

 * @namespace WPGMZA

 * @module GoogleMarker

 * @requires WPGMZA.Marker

 * @pro-requires WPGMZA.ProMarker

 */

(function($) {

	

	var Parent;

	

	WPGMZA.GoogleMarker = function(row)

	{

		var self = this;

		

		Parent.call(this, row);

		

		var settings = {};

		if(row)

		{

			for(var name in row)

			{

				if(row[name] instanceof WPGMZA.LatLng)

				{

					settings[name] = row[name].toGoogleLatLng();

				}

				else if(row[name] instanceof WPGMZA.Map)

				{

					// Do nothing (ignore)

				}

				else

					settings[name] = row[name];

			}

		}

		

		this.googleMarker = new google.maps.Marker(settings);

		this.googleMarker.wpgmzaMarker = this;

		

		this.googleMarker.setPosition(new google.maps.LatLng({

			lat: parseFloat(this.lat),

			lng: parseFloat(this.lng)

		}));

			

		this.googleMarker.setLabel(this.settings.label);

		

		if(this.animation)

			this.googleMarker.setAnimation(this.animation);

			

		google.maps.event.addListener(this.googleMarker, "click", function() {

			self.dispatchEvent("click");

			self.dispatchEvent("select");

		});

		

		google.maps.event.addListener(this.googleMarker, "mouseover", function() {

			self.dispatchEvent("mouseover");

		});

		

		google.maps.event.addListener(this.googleMarker, "dragend", function() {

			var googleMarkerPosition = self.googleMarker.getPosition();

			

			self.setPosition({

				lat: googleMarkerPosition.lat(),

				lng: googleMarkerPosition.lng()

			});

			

			self.dispatchEvent({

				type: "dragend",

				latLng: self.getPosition()

			});

		});

		

		this.trigger("init");

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProMarker;

	else

		Parent = WPGMZA.Marker;

	WPGMZA.GoogleMarker.prototype = Object.create(Parent.prototype);

	WPGMZA.GoogleMarker.prototype.constructor = WPGMZA.GoogleMarker;

	

	WPGMZA.GoogleMarker.prototype.setLabel = function(label)

	{

		if(!label)

		{

			this.googleMarker.setLabel(null);

			return;

		}

		

		this.googleMarker.setLabel({

			text: label

		});

		

		if(!this.googleMarker.getIcon())

			this.googleMarker.setIcon(WPGMZA.settings.default_marker_icon);

	}

	

	/**

	 * Sets the position of the marker

	 * @return void

	 */

	WPGMZA.GoogleMarker.prototype.setPosition = function(latLng)

	{

		Parent.prototype.setPosition.call(this, latLng);

		this.googleMarker.setPosition({

			lat: this.lat,

			lng: this.lng

		});

	}

	

	/**

	 * Sets the position offset of a marker

	 * @return void

	 */

	WPGMZA.GoogleMarker.prototype.setOffset = function(x, y)

	{

		var self = this;

		var icon = this.googleMarker.getIcon();

		var img = new Image();

		var params;

		

		if(typeof icon == "string")

			params = {

				url: icon

			};

		else

			params = icon;

		

		img.onload = function()

		{

			var defaultAnchor = {

				x: img.width / 2,

				y: img.height

			};

			

			params.anchor = new google.maps.Point(defaultAnchor.x - x, defaultAnchor.y - y);

			

			self.googleMarker.setIcon(params);

		}

		

		img.src = params.url;

	}

	

	WPGMZA.GoogleMarker.prototype.setOptions = function(options)

	{

		this.googleMarker.setOptions(options);

	}

	

	/**

	 * Set the marker animation

	 * @return void

	 */

	WPGMZA.GoogleMarker.prototype.setAnimation = function(animation)

	{

		Parent.prototype.setAnimation.call(this, animation);

		this.googleMarker.setAnimation(animation);

	}

	

	/**

	 * Sets the visibility of the marker

	 * @return void

	 */

	WPGMZA.GoogleMarker.prototype.setVisible = function(visible)

	{

		Parent.prototype.setVisible.call(this, visible);

		

		this.googleMarker.setVisible(visible);

	}

	

	WPGMZA.GoogleMarker.prototype.setDraggable = function(draggable)

	{

		this.googleMarker.setDraggable(draggable);

	}

	

})(jQuery);



// js/v8/google-maps/google-modern-store-locator-circle.js

/**

 * @namespace WPGMZA

 * @module GoogleModernStoreLocatorCircle

 * @requires WPGMZA.ModernStoreLocatorCircle

 */

(function($) {

	

	WPGMZA.GoogleModernStoreLocatorCircle = function(map, settings)

	{

		var self = this;

		

		WPGMZA.ModernStoreLocatorCircle.call(this, map, settings);

		

		this.intervalID = setInterval(function() {

			

			var mapSize = {

				width: $(self.mapElement).width(),

				height: $(self.mapElement).height()

			};

			

			if(mapSize.width == self.mapSize.width && mapSize.height == self.mapSize.height)

				return;

			

			self.canvasLayer.resize_();

			self.canvasLayer.draw();

			

			self.mapSize = mapSize;

			

		}, 1000);

		

		$(document).bind('webkitfullscreenchange mozfullscreenchange fullscreenchange', function() {

			

			self.canvasLayer.resize_();

			self.canvasLayer.draw();

			

		});

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype = Object.create(WPGMZA.ModernStoreLocatorCircle.prototype);

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.constructor = WPGMZA.GoogleModernStoreLocatorCircle;

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.initCanvasLayer = function()

	{

		var self = this;

		

		if(this.canvasLayer)

		{

			this.canvasLayer.setMap(null);

			this.canvasLayer.setAnimate(false);

		}

		

		this.canvasLayer = new CanvasLayer({

			map: this.map.googleMap,

			resizeHandler: function(event) {

				self.onResize(event);

			},

			updateHandler: function(event) {

				self.onUpdate(event);

			},

			animate: true,

			resolutionScale: this.getResolutionScale()

        });

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.setOptions = function(options)

	{

		WPGMZA.ModernStoreLocatorCircle.prototype.setOptions.call(this, options);

		

		this.canvasLayer.scheduleUpdate();

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.setPosition = function(position)

	{

		WPGMZA.ModernStoreLocatorCircle.prototype.setPosition.call(this, position);

		

		this.canvasLayer.scheduleUpdate();

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.setRadius = function(radius)

	{

		WPGMZA.ModernStoreLocatorCircle.prototype.setRadius.call(this, radius);

		

		this.canvasLayer.scheduleUpdate();

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getTransformedRadius = function(km)

	{

		var multiplierAtEquator = 0.006395;

		var spherical = google.maps.geometry.spherical;

		

		var center = this.settings.center;

		var equator = new WPGMZA.LatLng({

			lat: 0.0,

			lng: 0.0

		});

		var latitude = new WPGMZA.LatLng({

			lat: center.lat,

			lng: 0.0

		});

		

		var offsetAtEquator = spherical.computeOffset(equator.toGoogleLatLng(), km * 1000, 90);

		var offsetAtLatitude = spherical.computeOffset(latitude.toGoogleLatLng(), km * 1000, 90);

		

		var factor = offsetAtLatitude.lng() / offsetAtEquator.lng();

		var result = km * multiplierAtEquator * factor;

		

		if(isNaN(result))

			throw new Error("here");

		

		return result;

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getCanvasDimensions = function()

	{

		return {

			width: this.canvasLayer.canvas.width,

			height: this.canvasLayer.canvas.height

		};

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getWorldOriginOffset = function()

	{

		var projection = this.map.googleMap.getProjection();

		var position = projection.fromLatLngToPoint(this.canvasLayer.getTopLeft());

		

		return {

			x: -position.x,

			y: -position.y

		};

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getCenterPixels = function()

	{

		var center = new WPGMZA.LatLng(this.settings.center);

		var projection = this.map.googleMap.getProjection();

		return projection.fromLatLngToPoint(center.toGoogleLatLng());

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getContext = function(type)

	{

		return this.canvasLayer.canvas.getContext("2d");

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.getScale = function()

	{

		return Math.pow(2, this.map.getZoom()) * this.getResolutionScale();

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.setVisible = function(visible)

	{

		WPGMZA.ModernStoreLocatorCircle.prototype.setVisible.call(this, visible);

		

		this.canvasLayer.scheduleUpdate();

	}

	

	WPGMZA.GoogleModernStoreLocatorCircle.prototype.destroy = function()

	{

		this.canvasLayer.setMap(null);

		this.canvasLayer = null;

		

		clearInterval(this.intervalID);

	}

	

})(jQuery);



// js/v8/google-maps/google-modern-store-locator.js

/**

 * @namespace WPGMZA

 * @module GoogleModernStoreLocator

 * @requires WPGMZA.ModernStoreLocator

 */

(function($) {

	

	WPGMZA.GoogleModernStoreLocator = function(map_id)

	{

		WPGMZA.ModernStoreLocator.call(this, map_id);

		

		var googleMap;

		

		if(WPGMZA.isProVersion())

			googleMap = MYMAP[map_id].map.googleMap;

		else

			googleMap = MYMAP.map.googleMap;

		

		googleMap.controls[google.maps.ControlPosition.TOP_CENTER].push(this.element);

	}

	

	WPGMZA.GoogleModernStoreLocator.prototype = Object.create(WPGMZA.ModernStoreLocator.prototype);

	WPGMZA.GoogleModernStoreLocator.prototype.constructor = WPGMZA.GoogleModernStoreLocator;

	

})(jQuery);



// js/v8/google-maps/google-polygon.js

/**

 * @namespace WPGMZA

 * @module GooglePolygon

 * @requires WPGMZA.Polygon

 * @pro-requires WPGMZA.ProPolygon

 */

(function($) {

	

	var Parent;

	

	WPGMZA.GooglePolygon = function(row, googlePolygon)

	{

		var self = this;

		

		Parent.call(this, row, googlePolygon);

		

		if(googlePolygon)

		{

			this.googlePolygon = googlePolygon;

		}

		else

		{

			this.googlePolygon = new google.maps.Polygon(this.settings);

			

			if(row && row.points)

			{

				var paths = this.parseGeometry(row.points);

				this.googlePolygon.setOptions({paths: paths});

			}

		}

		

		this.googlePolygon.wpgmzaPolygon = this;

			

		google.maps.event.addListener(this.googlePolygon, "click", function() {

			self.dispatchEvent({type: "click"});

		});

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProPolygon;

	else

		Parent = WPGMZA.Polygon;

		

	WPGMZA.GooglePolygon.prototype = Object.create(Parent.prototype);

	WPGMZA.GooglePolygon.prototype.constructor = WPGMZA.GooglePolygon;

	

	/**

	 * Returns true if the polygon is editable

	 * @return void

	 */

	WPGMZA.GooglePolygon.prototype.getEditable = function()

	{

		return this.googlePolygon.getOptions().editable;

	}

	

	/**

	 * Sets the editable state of the polygon

	 * @return void

	 */

	WPGMZA.GooglePolygon.prototype.setEditable = function(value)

	{

		this.googlePolygon.setOptions({editable: value});

	}

	

	/**

	 * Returns the polygon represented by a JSON object

	 * @return object

	 */

	WPGMZA.GooglePolygon.prototype.toJSON = function()

	{

		var result = WPGMZA.Polygon.prototype.toJSON.call(this);

		

		result.points = [];

		

		// TODO: Support holes using multiple paths

		var path = this.googlePolygon.getPath();

		for(var i = 0; i < path.getLength(); i++)

		{

			var latLng = path.getAt(i);

			result.points.push({

				lat: latLng.lat(),

				lng: latLng.lng()

			});

		}

		

		return result;

	}

	

})(jQuery);



// js/v8/google-maps/google-polyline.js

/**

 * @namespace WPGMZA

 * @module GooglePolyline

 * @requires WPGMZA.Polyline

 */

(function($) {

	

	WPGMZA.GooglePolyline = function(row, googlePolyline)

	{

		var self = this;

		

		WPGMZA.Polyline.call(this, row, googlePolyline);

		

		if(googlePolyline)

		{

			this.googlePolyline = googlePolyline;

		}

		else

		{

			this.googlePolyline = new google.maps.Polyline(this.settings);			

			this.googlePolyline.wpgmzaPolyline = this;

			

			if(row && row.points)

			{

				var path = this.parseGeometry(row.points);

				this.setPoints(path);

			}

		}

		

		google.maps.event.addListener(this.googlePolyline, "click", function() {

			self.dispatchEvent({type: "click"});

		});

	}

	

	WPGMZA.GooglePolyline.prototype = Object.create(WPGMZA.Polyline.prototype);

	WPGMZA.GooglePolyline.prototype.constructor = WPGMZA.GooglePolyline;

	

	WPGMZA.GooglePolyline.prototype.setEditable = function(value)

	{

		this.googlePolyline.setOptions({editable: value});

	}

	

	WPGMZA.GooglePolyline.prototype.setPoints = function(points)

	{

		this.googlePolyline.setOptions({path: points});

	}

	

	WPGMZA.GooglePolyline.prototype.toJSON = function()

	{

		var result = WPGMZA.Polyline.prototype.toJSON.call(this);

		

		result.points = [];

		

		var path = this.googlePolyline.getPath();

		for(var i = 0; i < path.getLength(); i++)

		{

			var latLng = path.getAt(i);

			result.points.push({

				lat: latLng.lat(),

				lng: latLng.lng()

			});

		}

		

		return result;

	}

	

})(jQuery);



// js/v8/google-maps/google-vertex-context-menu.js

/**

 * @namespace WPGMZA

 * @module GoogleVertexContextMenu

 * @requires wpgmza_api_call

 */

(function($) {

	

	if(WPGMZA.settings.engine != "google-maps")

		return;

	

	if(WPGMZA.googleAPIStatus && WPGMZA.googleAPIStatus.code == "USER_CONSENT_NOT_GIVEN")

		return;

	

	WPGMZA.GoogleVertexContextMenu = function(mapEditPage)

	{

		var self = this;

		

		this.mapEditPage = mapEditPage;

		

		this.element = document.createElement("div");

		this.element.className = "wpgmza-vertex-context-menu";

		this.element.innerHTML = "Delete";

		

		google.maps.event.addDomListener(this.element, "click", function(event) {

			self.removeVertex();

			event.preventDefault();

			event.stopPropagation();

			return false;

		});

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype = new google.maps.OverlayView();

	

	WPGMZA.GoogleVertexContextMenu.prototype.onAdd = function()

	{

		var self = this;

		var map = this.getMap();

		

		this.getPanes().floatPane.appendChild(this.element);

		this.divListener = google.maps.event.addDomListener(map.getDiv(), "mousedown", function(e) {

			if(e.target != self.element)

				self.close();

		}, true);

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype.onRemove = function()

	{

		google.maps.event.removeListener(this.divListener);

		this.element.parentNode.removeChild(this.element);

		

		this.set("position");

		this.set("path");

		this.set("vertex");

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype.open = function(map, path, vertex)

	{

		this.set('position', path.getAt(vertex));

		this.set('path', path);

		this.set('vertex', vertex);

		this.setMap(map);

		this.draw();

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype.close = function()

	{

		this.setMap(null);

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype.draw = function()

	{

		var position = this.get('position');

		var projection = this.getProjection();



		if (!position || !projection)

		  return;



		var point = projection.fromLatLngToDivPixel(position);

		this.element.style.top = point.y + 'px';

		this.element.style.left = point.x + 'px';

	}

	

	WPGMZA.GoogleVertexContextMenu.prototype.removeVertex = function()

	{

		var path = this.get('path');

		var vertex = this.get('vertex');



		if (!path || vertex == undefined) {

		  this.close();

		  return;

		}



		path.removeAt(vertex);

		this.close();

	}

	

})(jQuery);



// js/v8/open-layers/ol-circle.js

/**

 * @namespace WPGMZA

 * @module OLCircle

 * @requires WPGMZA.Circle

 */

(function($) {

	

	var Parent = WPGMZA.Circle;

	

	WPGMZA.OLCircle = function(options, olFeature)

	{

		var self = this;

		

		this.center = {lat: 0, lng: 0};

		this.radius = 0;

		

		Parent.call(this, options, olFeature);

		

		if(!this.settings.fillColor)

		{

			this.settings.fillColor = "#ff0000";

			this.settings.fillOpacity = 0.6;

		}

		

		this.olStyle = new ol.style.Style(this.getStyleFromSettings());

		

		// IMPORTANT: Please note that due to what appears to be a bug in OpenLayers, the following code MUST be exected specifically in this order, or the circle won't appear

		var vectorLayer3857 = new ol.layer.Vector({

			source: new ol.source.Vector(),

			style: this.olStyle

		});

		

		if(olFeature)

		{

			this.olFeature = olFeature;

		}

		else

		{

			var wgs84Sphere = new ol.Sphere(6378137);

			var radius = this.radius;

			var x, y;

			

			x = this.center.lng;

			y = this.center.lat;

			

			var circle4326 = ol.geom.Polygon.circular(wgs84Sphere, [x, y], radius, 64);

			var circle3857 = circle4326.clone().transform('EPSG:4326', 'EPSG:3857');

			

			vectorLayer3857.getSource().addFeature(new ol.Feature(circle3857));

		}

		

		this.layer = vectorLayer3857;

		

		options.map.olMap.addLayer(vectorLayer3857);

	}

	

	WPGMZA.OLCircle.prototype = Object.create(Parent.prototype);

	WPGMZA.OLCircle.prototype.constructor = WPGMZA.OLCircle;

	

	WPGMZA.OLCircle.prototype.getStyleFromSettings = function()

	{

		var params = {};

				

		if(this.settings.strokeOpacity)

			params.stroke = new ol.style.Stroke({

				color: WPGMZA.hexOpacityToRGBA(this.settings.strokeColor, this.settings.strokeOpacity)

			});

		

		if(this.settings.fillOpacity)

			params.fill = new ol.style.Fill({

				color: WPGMZA.hexOpacityToRGBA(this.settings.fillColor, this.settings.fillOpacity)

			});

			

		return params;

	}

	

	WPGMZA.OLCircle.prototype.updateStyleFromSettings = function()

	{

		// Re-create the style - working on it directly doesn't cause a re-render

		var params = this.getStyleFromSettings();

		this.olStyle = new ol.style.Style(params);

		this.layer.setStyle(this.olStyle);

	}

	

})(jQuery);



// js/v8/open-layers/ol-geocoder.js

/**

 * @namespace WPGMZA

 * @module OLGeocoder

 * @requires WPGMZA.Geocoder

 */

(function($) {

	

	/**

	 * @class OLGeocoder

	 * @extends Geocoder

	 * @summary OpenLayers geocoder - uses Nominatim by default

	 */

	WPGMZA.OLGeocoder = function()

	{

		

	}

	

	WPGMZA.OLGeocoder.prototype = Object.create(WPGMZA.Geocoder.prototype);

	WPGMZA.OLGeocoder.prototype.constructor = WPGMZA.OLGeocoder;

	

	/**

	 * @function getResponseFromCache

	 * @access protected

	 * @summary Tries to retrieve cached coordinates from server cache

	 * @param {string} address The street address to geocode

	 * @param {function} callback Where to send the results, as an array

	 * @return {void}

	 */

	WPGMZA.OLGeocoder.prototype.getResponseFromCache = function(address, callback)

	{

		$.ajax(WPGMZA.ajaxurl, {

			data: {

				action: "wpgmza_query_nominatim_cache",

				query: address

			},

			success: function(response, xhr, status) {

				// Legacy compatibility support

				response.lng = response.lon;

				

				callback(response);

			}

		});

	}

	

	/**

	 * @function getResponseFromNominatim

	 * @access protected

	 * @summary Queries Nominatim on the specified address

	 * @param {object} options An object containing the options for geocoding, address is a mandatory field

	 * @param {function} callback The function to send the results to, as an array

	 */

	WPGMZA.OLGeocoder.prototype.getResponseFromNominatim = function(options, callback)

	{

		var data = {

			q: options.address,

			format: "json"

		};

		

		if(options.country)

			data.countrycodes = options.country;

		

		$.ajax("https://nominatim.openstreetmap.org/search/", {

			data: data,

			success: function(response, xhr, status) {

				callback(response);

			},

			error: function(response, xhr, status) {

				callback(null, WPGMZA.Geocoder.FAIL)

			}

		});

	}

	

	/**

	 * @function cacheResponse

	 * @access protected

	 * @summary Caches a response on the server, usually after it's been returned from Nominatim

	 * @param {string} address The street address

	 * @param {object|array} response The response to cache

	 * @returns {void}

	 */

	WPGMZA.OLGeocoder.prototype.cacheResponse = function(address, response)

	{

		$.ajax(WPGMZA.ajaxurl, {

			data: {

				action: "wpgmza_store_nominatim_cache",

				query: address,

				response: JSON.stringify(response)

			},

			method: "POST"

		});

	}

	

	WPGMZA.OLGeocoder.prototype.getLatLngFromAddress = function(options, callback)

	{

		return WPGMZA.OLGeocoder.prototype.geocode(options, callback);

	}

	

	WPGMZA.OLGeocoder.prototype.getAddressFromLatLng = function(options, callback)

	{

		return WPGMZA.OLGeocoder.prototype.geocode(options, callback);

	}

	

	WPGMZA.OLGeocoder.prototype.geocode = function(options, callback)

	{

		var self = this;

		

		if(!options)

			throw new Error("Invalid options");

		

		if(options.location)

			options.latLng = new WPGMZA.LatLng(options.location);

		

		var finish, location;

		

		if(options.address)

		{

			location = options.address;

			

			finish = function(response, status)

			{

				for(var i = 0; i < response.length; i++)

				{

					response[i].geometry = {

						location: new WPGMZA.LatLng({

							lat: parseFloat(response[i].lat),

							lng: parseFloat(response[i].lon)

						})

					};

					

					response[i].latLng = {

						lat: parseFloat(response[i].lat),

						lng: parseFloat(response[i].lon)

					};

					

					// Backward compatibility

					response[i].lng = response[i].lon;

				}

				

				callback(response, status);

			}

		}

		else if(options.latLng)

		{

			location = options.latLng.toString();

			

			finish = function(response, status)

			{

				var address = response[0].display_name;

				callback([address], status);

			}

		}

		else

			throw new Error("You must supply either a latLng or address")

		

		this.getResponseFromCache(location, function(response) {

			if(response.length)

			{

				finish(response, WPGMZA.Geocoder.SUCCESS);

				return;

			}

			

			self.getResponseFromNominatim($.extend(options, {address: location}), function(response, status) {

				if(status == WPGMZA.Geocoder.FAIL)

				{

					callback(null, WPGMZA.Geocoder.FAIL);

					return;

				}

				

				if(response.length == 0)

				{

					callback([], WPGMZA.Geocoder.ZERO_RESULTS);

					return;

				}

				

				finish(response, WPGMZA.Geocoder.SUCCESS);

				

				self.cacheResponse(location, response);

			});

		});

	}

	

})(jQuery);



// js/v8/open-layers/ol-info-window.js

/**

 * @namespace WPGMZA

 * @module OLInfoWindow

 * @requires WPGMZA.InfoWindow

 * @pro-requires WPGMZA.ProInfoWindow

 */

(function($) {

	

	var Parent;

	

	WPGMZA.OLInfoWindow = function(mapObject)

	{

		var self = this;

		

		Parent.call(this, mapObject);

		

		this.element = $("<div class='ol-info-window-container ol-info-window-plain'></div>")[0];

			

		$(this.element).on("click", ".ol-info-window-close", function(event) {

			self.close();

		});

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProInfoWindow;

	else

		Parent = WPGMZA.InfoWindow;

	

	WPGMZA.OLInfoWindow.prototype = Object.create(Parent.prototype);

	WPGMZA.OLInfoWindow.prototype.constructor = WPGMZA.OLInfoWindow;

	

	/**

	 * Opens the info window

	 * TODO: This should take a mapObject, not an event

	 * @return boolean FALSE if the info window should not & will not open, TRUE if it will

	 */

	WPGMZA.OLInfoWindow.prototype.open = function(map, mapObject)

	{

		var self = this;

		var latLng = mapObject.getPosition();

		

		if(!WPGMZA.InfoWindow.prototype.open.call(this, map, mapObject))

			return false;

		

		if(this.overlay)

			this.mapObject.map.olMap.removeOverlay(this.overlay);

			

		this.overlay = new ol.Overlay({

			element: this.element

		});

		

		this.overlay.setPosition(ol.proj.fromLonLat([

			latLng.lng,

			latLng.lat

		]));

		self.mapObject.map.olMap.addOverlay(this.overlay);

		

		$(this.element).show();

		

		this.dispatchEvent("infowindowopen");

	}

	

	WPGMZA.OLInfoWindow.prototype.close = function(event)

	{

		// TODO: Why? This shouldn't have to be here. Removing the overlay should hide the element (it doesn't)

		$(this.element).hide();

		

		if(!this.overlay)

			return;

		

		WPGMZA.InfoWindow.prototype.close.call(this);

		

		this.mapObject.map.olMap.removeOverlay(this.overlay);

		this.overlay = null;

	}

	

	WPGMZA.OLInfoWindow.prototype.setContent = function(html)

	{

		$(this.element).html("<i class='fa fa-times ol-info-window-close' aria-hidden='true'></i>" + html);

	}

	

	WPGMZA.OLInfoWindow.prototype.setOptions = function(options)

	{

		if(WPGMZA.settings.developer_mode)

			console.log(options);

		

		if(options.maxWidth)

		{

			$(this.element).css({"max-width": options.maxWidth + "px"});

		}

	}

	

})(jQuery);



// js/v8/open-layers/ol-map.js

/**

 * @namespace WPGMZA

 * @module OLMap

 * @requires WPGMZA.Map

 * @pro-requires WPGMZA.ProMap

 */

(function($) {

	

	var Parent;

	

	WPGMZA.OLMap = function(element, options)

	{

		var self = this;

		

		Parent.call(this, element);

		

		this.setOptions(options);

		

		var viewOptions = this.settings.toOLViewOptions();

		

		$(this.element).html("");

		

		this.olMap = new ol.Map({

			target: $(element)[0],

			layers: [

				this.getTileLayer()

			],

			view: new ol.View(viewOptions)

		});

		

		// TODO: Re-implement using correct setting names

		// Interactions

		this.olMap.getInteractions().forEach(function(interaction) {

			

			// NB: The true and false values are flipped because these settings represent the "disabled" state when true

			if(interaction instanceof ol.interaction.DragPan)

				interaction.setActive( (this.settings.wpgmza_settings_map_draggable == "yes" ? false : true) );

			else if(interaction instanceof ol.interaction.DoubleClickZoom)

				interaction.setActive( (this.settings.wpgmza_settings_map_clickzoom ? false : true) );

			else if(interaction instanceof ol.interaction.MouseWheelZoom)

				interaction.setActive( (this.settings.wpgmza_settings_map_scroll == "yes" ? false : true) );

			

		}, this);

		

		// Controls

		this.olMap.getControls().forEach(function(control) {

			

			// NB: The true and false values are flipped because these settings represent the "disabled" state when true

			if(control instanceof ol.control.Zoom && WPGMZA.settings.wpgmza_settings_map_zoom == "yes")

				this.olMap.removeControl(control);

			

		}, this);

		

		if(WPGMZA.settings.wpgmza_settings_map_full_screen_control != "yes")

			this.olMap.addControl(new ol.control.FullScreen());

		

		// Marker layer

		this.markerLayer = new ol.layer.Vector({

			source: new ol.source.Vector({

				features: []

			})

		});

		this.olMap.addLayer(this.markerLayer);

		

		// Listen for end of pan so we can wrap longitude if needs be

		this.olMap.on("moveend", function(event) {

			self.wrapLongitude();

			self.dispatchEvent("dragend");

			self.onIdle();

		});

		

		// Listen for zoom

		this.olMap.getView().on("change:resolution", function(event) {

			self.dispatchEvent("zoom_changed");

			self.dispatchEvent("zoomchanged");

			self.onIdle();

		});

		

		// Listen for bounds changing

		this.olMap.getView().on("change", function() {

			// Wrap longitude

			self.onBoundsChanged();

		});

		self.onBoundsChanged();

		

		// Store locator center

		var marker;

		if(this.storeLocator && (marker = this.storeLocator.centerPointMarker))

		{

			this.olMap.addOverlay(marker.overlay);

			marker.setVisible(false);

		}

		

		// Right click listener

		$(this.element).on("click contextmenu", function(event) {

			

			var isRight;

			event = event || window.event;

			

			var latLng = self.pixelsToLatLng(event.offsetX, event.offsetY);

			

			if("which" in event)

				isRight = event.which == 3;

			else if("button" in event)

				isRight = event.button == 2;

			

			if(event.which == 1 || event.button == 1)

			{

				// Left

				self.trigger({

					type: "click",

					latLng: latLng

				});

				return;

			}

			

			if(!isRight)

				return;

			

			return self.onRightClick(event);

		});

		

		// Dispatch event

		if(!WPGMZA.isProVersion())

		{

			this.dispatchEvent("created");

			WPGMZA.events.dispatchEvent({type: "mapcreated", map: this});

		}

	}



	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProMap;

	else

		Parent = WPGMZA.Map;

	

	WPGMZA.OLMap.prototype = Object.create(Parent.prototype);

	WPGMZA.OLMap.prototype.constructor = WPGMZA.OLMap;

	

	WPGMZA.OLMap.prototype.getTileLayer = function()

	{

		return new ol.layer.Tile({

			source: new ol.source.OSM()

		});

	}

	

	WPGMZA.OLMap.prototype.wrapLongitude = function()

	{

		var center = this.getCenter();

		

		if(center.lng >= -180 && center.lng <= 180)

			return;

		

		center.lng = center.lng - 360 * Math.floor(center.lng / 360);

		

		if(center.lng > 180)

			center.lng -= 360;

		

		this.setCenter(center);

	}

	

	WPGMZA.OLMap.prototype.getCenter = function()

	{

		var lonLat = ol.proj.toLonLat(

			this.olMap.getView().getCenter()

		);

		return {

			lat: lonLat[1],

			lng: lonLat[0]

		};

	}

	

	WPGMZA.OLMap.prototype.setCenter = function(latLng)

	{

		var view = this.olMap.getView();

		

		WPGMZA.Map.prototype.setCenter.call(this, latLng);

		

		view.setCenter(ol.proj.fromLonLat([

			latLng.lng,

			latLng.lat

		]));

		

		this.wrapLongitude();



		this.onBoundsChanged();

	}

	

	WPGMZA.OLMap.prototype.getBounds = function()

	{

		var bounds = this.olMap.getView().calculateExtent(this.olMap.getSize());

		

		var topLeft = ol.proj.toLonLat([bounds[0], bounds[1]]);

		var bottomRight = ol.proj.toLonLat([bounds[2], bounds[3]]);

		

		return {

			topLeft: {

				lat: topLeft[1],

				lng: topLeft[0]

			},

			bottomRight: {

				lat: bottomRight[1],

				lng: bottomRight[0]

			}

		};

	}

	

	/**

	 * Fit to given boundaries

	 * @return void

	 */

	WPGMZA.OLMap.prototype.fitBounds = function(southWest, northEast)

	{

		this.olMap.getView().fitExtent(

			[southWest.lng, southWest.lat, northEast.lng, northEast.lat],

			this.olMap.getSize()

		);

	}

	

	WPGMZA.OLMap.prototype.panTo = function(latLng)

	{

		var view = this.olMap.getView();

		view.animate({

			center: ol.proj.fromLonLat([

				parseFloat(latLng.lng),

				parseFloat(latLng.lat),

			]),

			duration: 500

		});

	}

	

	WPGMZA.OLMap.prototype.getZoom = function()

	{

		return Math.round( this.olMap.getView().getZoom() ) + 1;

	}

	

	WPGMZA.OLMap.prototype.setZoom = function(value)

	{

		this.olMap.getView().setZoom(value);

	}

	

	WPGMZA.OLMap.prototype.getMinZoom = function()

	{

		return this.olMap.getView().getMinZoom();

	}

	

	WPGMZA.OLMap.prototype.setMinZoom = function(value)

	{

		this.olMap.getView().setMinZoom(value);

	}

	

	WPGMZA.OLMap.prototype.getMaxZoom = function()

	{

		return this.olMap.getView().getMaxZoom();

	}

	

	WPGMZA.OLMap.prototype.setMaxZoom = function(value)

	{

		this.olMap.getView().setMaxZoom(value);

	}

	

	WPGMZA.OLMap.prototype.setOptions = function(options)

	{

		Parent.prototype.setOptions.call(this, options);

		

		if(!this.olMap)

			return;

		

		this.olMap.getView().setProperties( this.settings.toOLViewOptions() );

	}

	

	/**

	 * TODO: Consider moving all these functions to their respective classes, same on google map (DO IT!!! It's very misleading having them here)

	 */

	WPGMZA.OLMap.prototype.addMarker = function(marker)

	{

		this.olMap.addOverlay(marker.overlay);

		

		Parent.prototype.addMarker.call(this, marker);

	}

	

	WPGMZA.OLMap.prototype.removeMarker = function(marker)

	{

		this.olMap.removeOverlay(marker.overlay);

		

		Parent.prototype.removeMarker.call(this, marker);

	}

	

	WPGMZA.OLMap.prototype.addPolygon = function(polygon)

	{

		this.olMap.addLayer(polygon.layer);

		

		Parent.prototype.addPolygon.call(this, polygon);

	}

	

	WPGMZA.OLMap.prototype.removePolygon = function(polygon)

	{

		this.olMap.removeLayer(polygon.layer);

		

		Parent.prototype.removePolygon.call(this, polygon);

	}

	

	WPGMZA.OLMap.prototype.addPolyline = function(polyline)

	{

		this.olMap.addLayer(polyline.layer);

		

		Parent.prototype.addPolyline.call(this, polyline);

	}

	

	WPGMZA.OLMap.prototype.removePolyline = function(polyline)

	{

		this.olMap.removeLayer(polyline.layer);

		

		Parent.prototype.removePolyline.call(this, polyline);

	}

	

	WPGMZA.OLMap.prototype.addCircle = function(circle)

	{

		this.olMap.addLayer(circle.layer);

		

		Parent.prototype.addCircle.call(this, circle);

	}

	

	WPGMZA.OLMap.prototype.removeCircle = function(circle)

	{

		this.olMap.removeLayer(circle.layer);

		

		Parent.prototype.removeCircle.call(this, circle);

	}

	

	WPGMZA.OLMap.prototype.pixelsToLatLng = function(x, y)

	{

		if(y == undefined)

		{

			if("x" in x && "y" in x)

			{

				y = x.y;

				x = x.x;

			}

			else

				console.warn("Y coordinate undefined in pixelsToLatLng (did you mean to pass 2 arguments?)");

		}

		

		var coord = this.olMap.getCoordinateFromPixel([x, y]);

		

		if(!coord)

			return {

				x: null,

				y: null

			};

		

		var lonLat = ol.proj.toLonLat(coord);

		return {

			lat: lonLat[1],

			lng: lonLat[0]

		};

	}

	

	WPGMZA.OLMap.prototype.latLngToPixels = function(latLng)

	{

		var coord = ol.proj.fromLonLat([latLng.lng, latLng.lat]);

		var pixel = this.olMap.getPixelFromCoordinate(coord);

		

		if(!pixel)

			return {

				x: null,

				y: null

			};

		

		return {

			x: pixel[0],

			y: pixel[1]

		};

	}

	

	WPGMZA.OLMap.prototype.enableBicycleLayer = function(value)

	{

		if(value)

		{

			if(!this.bicycleLayer)

				this.bicycleLayer = new ol.layer.Tile({

					source: new ol.source.OSM({

						url: "http://{a-c}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png"

					})

				});

				

			this.olMap.addLayer(this.bicycleLayer);

		}

		else

		{

			if(!this.bicycleLayer)

				return;

			

			this.olMap.removeLayer(this.bicycleLayer);

		}

	}

	

	WPGMZA.OLMap.prototype.onElementResized = function(event)

	{

		this.olMap.updateSize();

	}

	

	WPGMZA.OLMap.prototype.onRightClick = function(event)

	{

		if($(event.target).closest(".ol-marker, .wpgmza_modern_infowindow, .wpgmza-modern-store-locator").length)

			return true;

		

		var parentOffset = $(this.element).offset();

		var relX = event.pageX - parentOffset.left;

		var relY = event.pageY - parentOffset.top;

		var latLng = this.pixelsToLatLng(relX, relY);

		

		this.trigger({type: "rightclick", latLng: latLng});

		

		// Legacy event compatibility

		$(this.element).trigger({type: "rightclick", latLng: latLng});

		

		// Prevent menu

		event.preventDefault();

		return false;

	}

	

})(jQuery);



// js/v8/open-layers/ol-marker.js

/**

 * @namespace WPGMZA

 * @module OLMarker

 * @requires WPGMZA.Marker

 * @pro-requires WPGMZA.ProMarker

 */

(function($) {

	

	var Parent;

	

	WPGMZA.OLMarker = function(row)

	{

		var self = this;

		

		Parent.call(this, row);



		var origin = ol.proj.fromLonLat([

			parseFloat(this.lng),

			parseFloat(this.lat)

		]);

		

		this.element = $("<div class='ol-marker'><img src='" + WPGMZA.settings.default_marker_icon + "'/></div>")[0];

		

		$(this.element).on("click", function(event) {

			self.dispatchEvent("click");

			self.dispatchEvent("select");

		});



		$(this.element).on("mouseover", function(event) {

			self.dispatchEvent("mouseover");

		});

		

		this.overlay = new ol.Overlay({

			element: this.element

		});

		this.overlay.setPosition(origin);

		

		if(this.animation)

			this.setAnimation(this.animation);

		

		this.setLabel(this.settings.label);

		

		if(row.draggable)

			this.setDraggable(true);

		

		this.trigger("init");

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProMarker;

	else

		Parent = WPGMZA.Marker;

	WPGMZA.OLMarker.prototype = Object.create(Parent.prototype);

	WPGMZA.OLMarker.prototype.constructor = WPGMZA.OLMarker;

	

	WPGMZA.OLMarker.prototype.addLabel = function()

	{

		this.setLabel(this.getLabelText());

	}

	

	WPGMZA.OLMarker.prototype.setLabel = function(label)

	{

		if(!label)

		{

			if(this.label)

				$(this.element).find(".ol-marker-label").remove();

			

			return;

		}

		

		if(!this.label)

		{

			this.label = $("<div class='ol-marker-label'/>");

			$(this.element).append(this.label);

		}

		

		this.label.html(label);

	}

	

	WPGMZA.OLMarker.prototype.setVisible = function(visible)

	{

		Parent.prototype.setVisible(visible);

		

		this.overlay.getElement().style.display = (visible ? "block" : "none");

	}

	

	WPGMZA.OLMarker.prototype.setPosition = function(latLng)

	{

		Parent.prototype.setPosition.call(this, latLng);

		

		var origin = ol.proj.fromLonLat([

			parseFloat(this.lng),

			parseFloat(this.lat)

		]);

	

		this.overlay.setPosition(origin);

	}

	

	WPGMZA.OLMarker.prototype.setOffset = function(x, y)

	{

		this.element.style.position = "relative";

		this.element.style.left = x + "px";

		this.element.style.top = y + "px";

	}

	

	WPGMZA.OLMarker.prototype.setAnimation = function(anim)

	{

		Parent.prototype.setAnimation.call(this, anim);

		

		switch(anim)

		{

			case WPGMZA.Marker.ANIMATION_NONE:

				$(this.element).removeAttr("data-anim");

				break;

			

			case WPGMZA.Marker.ANIMATION_BOUNCE:

				$(this.element).attr("data-anim", "bounce");

				break;

			

			case WPGMZA.Marker.ANIMATION_DROP:

				$(this.element).attr("data-anim", "drop");

				break;

		}

	}

	

	WPGMZA.OLMarker.prototype.setDraggable = function(draggable)

	{

		var self = this;

		

		if(draggable)

		{

			var options = {

				disabled: false

			};

			

			if(!this.jQueryDraggableInitialized)

			{

				options.stop = function(event) {

					self.onDragEnd(event);

				}

			}

			

			$(this.element).draggable(options);

		}

		else

			$(this.element).draggable({disabled: true});

	}

	

	WPGMZA.OLMarker.prototype.onDragEnd = function(event)

	{

		var offset = {

			top:	parseFloat( $(this.element).css("top").match(/-?\d+/)[0] ),

			left:	parseFloat( $(this.element).css("left").match(/-?\d+/)[0] )

		};

		

		$(this.element).css({

			top: 	"0px",

			left: 	"0px"

		});

		

		var currentLatLng 		= this.getPosition();

		var pixelsBeforeDrag 	= this.map.latLngToPixels(currentLatLng);

		var pixelsAfterDrag		= {

			x: pixelsBeforeDrag.x + offset.left,

			y: pixelsBeforeDrag.y + offset.top

		};

		var latLngAfterDrag		= this.map.pixelsToLatLng(pixelsAfterDrag);

		

		this.setPosition(latLngAfterDrag);

		

		this.trigger({type: "dragend", latLng: latLngAfterDrag});

	}

	

})(jQuery);



// js/v8/open-layers/ol-modern-store-locator-circle.js

/**

 * @namespace WPGMZA

 * @module OLModernStoreLocatorCircle

 * @requires WPGMZA.ModernStoreLocatorCircle

 */

(function($) {

	

	WPGMZA.OLModernStoreLocatorCircle = function(map, settings)

	{

		WPGMZA.ModernStoreLocatorCircle.call(this, map, settings);

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype = Object.create(WPGMZA.ModernStoreLocatorCircle.prototype);

	WPGMZA.OLModernStoreLocatorCircle.prototype.constructor = WPGMZA.OLModernStoreLocatorCircle;

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.initCanvasLayer = function()

	{

		var self = this;

		var mapElement = $(this.map.element);

		var olViewportElement = mapElement.children(".ol-viewport");

		

		this.canvas = document.createElement("canvas");

		this.canvas.className = "wpgmza-ol-canvas-overlay";

		mapElement.append(this.canvas);

		

		this.renderFunction = function(event) {

			

			if(self.canvas.width != olViewportElement.width() || self.canvas.height != olViewportElement.height())

			{

				self.canvas.width = olViewportElement.width();

				self.canvas.height = olViewportElement.height();

				

				$(this.canvas).css({

					width: olViewportElement.width() + "px",

					height: olViewportElement.height() + "px"

				});

			}

			

			self.draw();

		};

		

		this.map.olMap.on("postrender", this.renderFunction);

	}



	WPGMZA.OLModernStoreLocatorCircle.prototype.getContext = function(type)

	{

		return this.canvas.getContext(type);

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.getCanvasDimensions = function()

	{

		return {

			width: this.canvas.width,

			height: this.canvas.height

		};

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.getCenterPixels = function()

	{

		var center = this.map.latLngToPixels(this.settings.center);

		

		return center;

	}

		

	WPGMZA.OLModernStoreLocatorCircle.prototype.getWorldOriginOffset = function()

	{

		return {

			x: 0,

			y: 0

		};

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.getTransformedRadius = function(km)

	{

		var center = new WPGMZA.LatLng(this.settings.center);

		var outer = new WPGMZA.LatLng(center);

		

		outer.moveByDistance(km, 90);

		

		var centerPixels = this.map.latLngToPixels(center);

		var outerPixels = this.map.latLngToPixels(outer);

		

		return Math.abs(outerPixels.x - centerPixels.x);



		if(!window.testMarker){

			window.testMarker = WPGMZA.Marker.createInstance({

				position: outer

			});

			WPGMZA.maps[0].addMarker(window.testMarker);

		}

		

		return 100;

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.getScale = function()

	{

		return 1;

	}

	

	WPGMZA.OLModernStoreLocatorCircle.prototype.destroy = function()

	{

		$(this.canvas).remove();

		

		this.map.olMap.un("postrender", this.renderFunction);

		this.map = null;

		this.canvas = null;

	}

	

})(jQuery);



// js/v8/open-layers/ol-modern-store-locator.js

/**

 * @namespace WPGMZA

 * @module OLModernStoreLocator

 * @requires WPGMZA.ModernStoreLocator

 */

(function($) {

	

	WPGMZA.OLModernStoreLocator = function(map_id)

	{

		var element;

		

		WPGMZA.ModernStoreLocator.call(this, map_id);

		

		if(WPGMZA.isProVersion())

			element = $(".wpgmza_map[data-map-id='" + map_id + "']");

		else

			element = $("#wpgmza_map");

		

		element.append(this.element);

	}

	

	WPGMZA.OLModernStoreLocator.prototype = Object.create(WPGMZA.ModernStoreLocator);

	WPGMZA.OLModernStoreLocator.prototype.constructor = WPGMZA.OLModernStoreLocator;

	

})(jQuery);



// js/v8/open-layers/ol-polygon.js

/**

 * @namespace WPGMZA

 * @module OLPolygon

 * @requires WPGMZA.Polygon

 * @pro-requires WPGMZA.ProPolygon

 */

(function($) {

	

	var Parent;

	

	WPGMZA.OLPolygon = function(row, olFeature)

	{

		var self = this;

		

		Parent.call(this, row, olFeature);

		

		this.olStyle = new ol.style.Style();

		

		if(olFeature)

		{

			this.olFeature = olFeature;

		}

		else

		{

			var coordinates = [[]];

			

			if(row && row.points)

			{

				var paths = this.parseGeometry(row.points);

				

				for(var i = 0; i < paths.length; i++)

					coordinates[0].push(ol.proj.fromLonLat([

						parseFloat(paths[i].lng),

						parseFloat(paths[i].lat)

					]));

				

				this.olStyle = new ol.style.Style(this.getStyleFromSettings());

			}

			

			this.olFeature = new ol.Feature({

				geometry: new ol.geom.Polygon(coordinates)

			});

		}

		

		this.layer = new ol.layer.Vector({

			source: new ol.source.Vector({

				features: [this.olFeature]

			}),

			style: this.olStyle

		});

		

		this.layer.getSource().getFeatures()[0].setProperties({

			wpgmzaPolygon: this

		});

	}

	

	if(WPGMZA.isProVersion())

		Parent = WPGMZA.ProPolygon;

	else

		Parent = WPGMZA.Polygon;

	

	WPGMZA.OLPolygon.prototype = Object.create(Parent.prototype);

	WPGMZA.OLPolygon.prototype.constructor = WPGMZA.OLPolygon;



	WPGMZA.OLPolygon.prototype.getStyleFromSettings = function()

	{

		var params = {};

				

		if(this.settings.strokeOpacity)

			params.stroke = new ol.style.Stroke({

				color: WPGMZA.hexOpacityToRGBA(this.settings.strokeColor, this.settings.strokeOpacity)

			});

		

		if(this.settings.fillOpacity)

			params.fill = new ol.style.Fill({

				color: WPGMZA.hexOpacityToRGBA(this.settings.fillColor, this.settings.fillOpacity)

			});

			

		return params;

	}

	

	WPGMZA.OLPolygon.prototype.updateStyleFromSettings = function()

	{

		// Re-create the style - working on it directly doesn't cause a re-render

		var params = this.getStyleFromSettings();

		this.olStyle = new ol.style.Style(params);

		this.layer.setStyle(this.olStyle);

	}

	

	WPGMZA.OLPolygon.prototype.setEditable = function(editable)

	{

		

	}

	

	WPGMZA.OLPolygon.prototype.toJSON = function()

	{

		var result = Parent.prototype.toJSON.call(this);

		var coordinates = this.olFeature.getGeometry().getCoordinates()[0];

		

		result.points = [];

		

		for(var i = 0; i < coordinates.length; i++)

		{

			var lonLat = ol.proj.toLonLat(coordinates[i]);

			var latLng = {

				lat: lonLat[1],

				lng: lonLat[0]

			};

			result.points.push(latLng);

		}

		

		return result;

	}

	

})(jQuery);



// js/v8/open-layers/ol-polyline.js

/**

 * @namespace WPGMZA

 * @module OLPolyline

 * @requires WPGMZA.Polyline

 */

(function($) {

	

	var Parent;

	

	WPGMZA.OLPolyline = function(row, olFeature)

	{

		var self = this;

		

		WPGMZA.Polyline.call(this, row);

		

		this.olStyle = new ol.style.Style();

		

		if(olFeature)

		{

			this.olFeature = olFeature;

		}

		else

		{

			var coordinates = [];

			

			if(row && row.points)

			{

				var path = this.parseGeometry(row.points);

				

				for(var i = 0; i < path.length; i++)

					coordinates.push(ol.proj.fromLonLat([

						parseFloat(path[i].lng),

						parseFloat(path[i].lat)

					]));

			}

			

			var params = this.getStyleFromSettings();

			this.olStyle = new ol.style.Style(params);

			

			this.olFeature = new ol.Feature({

				geometry: new ol.geom.LineString(coordinates)

			});

		}

		

		this.layer = new ol.layer.Vector({

			source: new ol.source.Vector({

				features: [this.olFeature]

			}),

			style: this.olStyle

		});

		

		this.layer.getSource().getFeatures()[0].setProperties({

			wpgmzaPolyling: this

		});

	}

	

	Parent = WPGMZA.Polyline;

		

	WPGMZA.OLPolyline.prototype = Object.create(Parent.prototype);

	WPGMZA.OLPolyline.prototype.constructor = WPGMZA.OLPolyline;

	

	WPGMZA.OLPolyline.prototype.getStyleFromSettings = function()

	{

		var params = {};

		

		if(this.settings.strokeOpacity)

			params.stroke = new ol.style.Stroke({

				color: WPGMZA.hexOpacityToRGBA(this.settings.strokeColor, this.settings.strokeOpacity),

				width: parseInt(this.settings.strokeWeight)

			});

			

		return params;

	}

	

	WPGMZA.OLPolyline.prototype.updateStyleFromSettings = function()

	{

		// Re-create the style - working on it directly doesn't cause a re-render

		var params = this.getStyleFromSettings();

		this.olStyle = new ol.style.Style(params);

		this.layer.setStyle(this.olStyle);

	}

	

	WPGMZA.OLPolyline.prototype.setEditable = function(editable)

	{

		

	}

	

	WPGMZA.OLPolyline.prototype.setPoints = function(points)

	{

		if(this.olFeature)

			this.layer.getSource().removeFeature(this.olFeature);

		

		var coordinates = [];

		

		for(var i = 0; i < points.length; i++)

			coordinates.push(ol.proj.fromLonLat([

				parseFloat(points[i].lng),

				parseFloat(points[i].lat)

			]));

		

		this.olFeature = new ol.Feature({

			geometry: new ol.geom.LineString(coordinates)

		});

		

		this.layer.getSource().addFeature(this.olFeature);

	}

	

	WPGMZA.OLPolyline.prototype.toJSON = function()

	{

		var result = Parent.prototype.toJSON.call(this);

		var coordinates = this.olFeature.getGeometry().getCoordinates();

		

		result.points = [];

		

		for(var i = 0; i < coordinates.length; i++)

		{

			var lonLat = ol.proj.toLonLat(coordinates[i]);

			var latLng = {

				lat: lonLat[1],

				lng: lonLat[0]

			};

			result.points.push(latLng);

		}

		

		return result;

	}

	

})(jQuery);