# Source: 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.draw = function() {

    		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 = Math.pow(2, map.getZoom()) * resolutionScale;

            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);